From 5deac1ad5edb345a385519919c07584431c101e8 Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Thu, 24 Sep 2026 18:07:33 +0200 Subject: [PATCH 01/50] codex-setup-temporary-github-auth: specify assisted setup and bot PAT creation --- specs/CATALOG.md | 24 + specs/catalog.json | 64 ++ specs/guided-bot-pat-onboarding.md | 553 ++++++++++++++++++ ...up-configuration-credentials-and-doctor.md | 6 + ...at-permission-guidance-and-verification.md | 7 +- .../temporary-setup-operator-authorization.md | 475 +++++++++++++++ 6 files changed, 1128 insertions(+), 1 deletion(-) create mode 100644 specs/guided-bot-pat-onboarding.md create mode 100644 specs/temporary-setup-operator-authorization.md diff --git a/specs/CATALOG.md b/specs/CATALOG.md index f04bee2e9..dd655c277 100644 --- a/specs/CATALOG.md +++ b/specs/CATALOG.md @@ -17,6 +17,8 @@ debt or convert unknown historic intent into a design decision. | `execution-lifecycle` | Implemented | Shared GitHub Action lifecycle from event admission through durable user-facing results | [Execution admission, queueing, routing, and result publication](./execution-admission-queue-and-publication.md) + 3 companion | 84 paths · 2026-09-16 | | `architecture-quality-hardening` | Implemented | Close verified concurrency, error-contract, context-coupling, fan-out, setup/doctor, and provider-policy risks in dependency order | [Architecture quality and scalability hardening](./architecture-quality-and-scalability-hardening.md) + 1 companion | 72 paths · 2026-09-16 | | `setup-and-doctor` | Implemented | Plan, validate, provision, and audit a repository installation without exposing credentials | [Setup, configuration, credentials, and doctor](./setup-configuration-credentials-and-doctor.md) + 2 companion | 83 paths · 2026-09-24 | +| `guided-bot-pat-onboarding` | Proposed | Guide creation of the persistent workflow PAT using GitHub's official form, verify bot identity and grants, and install the approved Actions Secret | [Guided bot PAT onboarding](./guided-bot-pat-onboarding.md) | 12 paths · 2026-09-24 | +| `temporary-setup-operator-authorization` | Proposed | Guide creation of the one-run operator PAT through GitHub's official form, verify final setup access, and report user-owned deletion accurately | [Assisted setup PAT creation](./temporary-setup-operator-authorization.md) | 12 paths · 2026-09-24 | | `issue-start-and-sdd-readiness` | Implemented | Start every admitted issue with one explicit signal and publish a validated SDD before eligible Action-managed branch work | [Uniform issue start and pre-branch SDD readiness](./issue-start-and-branch-readiness.md) + 1 companion | 51 paths · 2026-09-17 | | `managed-issue-lifecycle` | As-built baseline | Convert typed issues into traceable work branches, project state, and lifecycle state | [Managed issue and branch lifecycle](./managed-issue-and-branch-lifecycle.md) | 31 paths · 2026-09-17 | | `comment-automation` | Implemented | Admit only explicit commands or exact mentions, then route them while protecting repository mutations | [Comment automation and authorization](./comment-automation-and-authorization.md) | 61 paths · 2026-09-21 | @@ -108,6 +110,28 @@ debt or convert unknown historic intent into a design decision. - Tests: [`src/application/policies/__tests__/setup_questionnaire_policy.test.ts`](../src/application/policies/__tests__/setup_questionnaire_policy.test.ts) · [`src/application/policies/__tests__/setup_configuration_policy.test.ts`](../src/application/policies/__tests__/setup_configuration_policy.test.ts) · [`src/application/policies/__tests__/setup_token_permission_policy.test.ts`](../src/application/policies/__tests__/setup_token_permission_policy.test.ts) · [`src/application/policies/__tests__/setup_doctor_message_catalog.test.ts`](../src/application/policies/__tests__/setup_doctor_message_catalog.test.ts) · [`src/application/policies/__tests__/setup_doctor_report_policy.test.ts`](../src/application/policies/__tests__/setup_doctor_report_policy.test.ts) · [`src/application/usecases/setup/__tests__/setup_questionnaire_controller.test.ts`](../src/application/usecases/setup/__tests__/setup_questionnaire_controller.test.ts) · [`src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_token_permissions_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_token_permissions_use_case.test.ts) · [`src/application/usecases/setup/__tests__/doctor_use_case.test.ts`](../src/application/usecases/setup/__tests__/doctor_use_case.test.ts) · [`src/application/usecases/setup/__tests__/merge_queue_readiness_use_case.test.ts`](../src/application/usecases/setup/__tests__/merge_queue_readiness_use_case.test.ts) · [`src/application/usecases/actions/__tests__/initial_setup_use_case.test.ts`](../src/application/usecases/actions/__tests__/initial_setup_use_case.test.ts) · [`src/application/usecases/actions/__tests__/setup_resource_provisioning.test.ts`](../src/application/usecases/actions/__tests__/setup_resource_provisioning.test.ts) · [`src/infrastructure/__tests__/setup_workspace_adapter.test.ts`](../src/infrastructure/__tests__/setup_workspace_adapter.test.ts) · [`src/infrastructure/__tests__/setup_remote_credential_health_adapter.test.ts`](../src/infrastructure/__tests__/setup_remote_credential_health_adapter.test.ts) · [`src/infrastructure/__tests__/setup_token_permission_query_adapter.test.ts`](../src/infrastructure/__tests__/setup_token_permission_query_adapter.test.ts) · [`src/infrastructure/composition/__tests__/setup_token_permissions_composition_root.test.ts`](../src/infrastructure/composition/__tests__/setup_token_permissions_composition_root.test.ts) · [`src/data/repository/__tests__/repository_variables_repository.test.ts`](../src/data/repository/__tests__/repository_variables_repository.test.ts) · [`src/cli/__tests__/setup_presenters.test.ts`](../src/cli/__tests__/setup_presenters.test.ts) · [`src/cli/__tests__/setup_prompt_rendering.test.ts`](../src/cli/__tests__/setup_prompt_rendering.test.ts) · [`src/cli/__tests__/setup_token_permission_presenter.test.ts`](../src/cli/__tests__/setup_token_permission_presenter.test.ts) · [`src/__tests__/cli.test.ts`](../src/__tests__/cli.test.ts) · [`src/cli/__tests__/setup_terminal_driver.test.ts`](../src/cli/__tests__/setup_terminal_driver.test.ts) · [`src/architecture/__tests__/setup_doctor_boundaries.test.ts`](../src/architecture/__tests__/setup_doctor_boundaries.test.ts) · [`src/tooling/__tests__/documentation_pat_exception_policy.test.ts`](../src/tooling/__tests__/documentation_pat_exception_policy.test.ts) - User documentation: [`README.md`](../README.md) · [`docs/how-to-use.mdx`](../docs/how-to-use.mdx) · [`docs/configuration.mdx`](../docs/configuration.mdx) · [`docs/configuration-checklist.mdx`](../docs/configuration-checklist.mdx) · [`docs/authentication.mdx`](../docs/authentication.mdx) · [`docs/development/architecture.mdx`](../docs/development/architecture.mdx) · [`docs/security-operations/operations/provisioning.mdx`](../docs/security-operations/operations/provisioning.mdx) · [`docs/security-operations/operations/troubleshooting.mdx`](../docs/security-operations/operations/troubleshooting.mdx) · [`docs/security-operations/security/credentials.mdx`](../docs/security-operations/security/credentials.mdx) · [`docs/single-actions/workflow-and-cli.mdx`](../docs/single-actions/workflow-and-cli.mdx) · [`docs/security-operations/operations/verification.mdx`](../docs/security-operations/operations/verification.mdx) +### `guided-bot-pat-onboarding` — Guided bot PAT onboarding + +- Owner: Copilot maintainers +- Last verified: 2026-09-24 +- Specifications: [`specs/guided-bot-pat-onboarding.md`](./guided-bot-pat-onboarding.md) +- Workflows: Not applicable for this capability. +- Entrypoints: [`src/cli/commands/setup.ts`](../src/cli/commands/setup.ts) +- Core code: [`src/application/policies/setup_token_permission_policy.ts`](../src/application/policies/setup_token_permission_policy.ts) · [`src/application/usecases/setup/setup_credentials_use_case.ts`](../src/application/usecases/setup/setup_credentials_use_case.ts) · [`src/cli/setup_credential_prompt_adapter.ts`](../src/cli/setup_credential_prompt_adapter.ts) · [`src/data/repository/repository_variables_repository.ts`](../src/data/repository/repository_variables_repository.ts) +- Tests: [`src/application/policies/__tests__/setup_token_permission_policy.test.ts`](../src/application/policies/__tests__/setup_token_permission_policy.test.ts) · [`src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts) · [`src/cli/__tests__/setup_presenters.test.ts`](../src/cli/__tests__/setup_presenters.test.ts) · [`src/data/repository/__tests__/repository_variables_repository.test.ts`](../src/data/repository/__tests__/repository_variables_repository.test.ts) +- User documentation: [`docs/authentication.mdx`](../docs/authentication.mdx) · [`docs/how-to-use.mdx`](../docs/how-to-use.mdx) · [`docs/security-operations/operations/troubleshooting.mdx`](../docs/security-operations/operations/troubleshooting.mdx) + +### `temporary-setup-operator-authorization` — Assisted setup PAT creation + +- Owner: Copilot maintainers +- Last verified: 2026-09-24 +- Specifications: [`specs/temporary-setup-operator-authorization.md`](./temporary-setup-operator-authorization.md) +- Workflows: Not applicable for this capability. +- Entrypoints: [`src/cli/commands/setup.ts`](../src/cli/commands/setup.ts) +- Core code: [`src/cli/setup_credential_prompt_adapter.ts`](../src/cli/setup_credential_prompt_adapter.ts) · [`src/application/usecases/setup/setup_token_permissions_use_case.ts`](../src/application/usecases/setup/setup_token_permissions_use_case.ts) · [`src/infrastructure/setup_token_permission_query_adapter.ts`](../src/infrastructure/setup_token_permission_query_adapter.ts) · [`src/utils/setup_files.ts`](../src/utils/setup_files.ts) +- Tests: [`src/cli/__tests__/setup_presenters.test.ts`](../src/cli/__tests__/setup_presenters.test.ts) · [`src/application/usecases/setup/__tests__/setup_token_permissions_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_token_permissions_use_case.test.ts) · [`src/infrastructure/__tests__/setup_token_permission_query_adapter.test.ts`](../src/infrastructure/__tests__/setup_token_permission_query_adapter.test.ts) · [`src/utils/__tests__/setup_files.test.ts`](../src/utils/__tests__/setup_files.test.ts) +- User documentation: [`docs/how-to-use.mdx`](../docs/how-to-use.mdx) · [`docs/authentication.mdx`](../docs/authentication.mdx) · [`docs/security-operations/operations/troubleshooting.mdx`](../docs/security-operations/operations/troubleshooting.mdx) + ### `issue-start-and-sdd-readiness` — Uniform issue start and pre-branch SDD readiness - Owner: Copilot maintainers diff --git a/specs/catalog.json b/specs/catalog.json index cd808f8d8..3ae63b295 100644 --- a/specs/catalog.json +++ b/specs/catalog.json @@ -744,6 +744,70 @@ "docs/security-operations/operations/verification.mdx" ] }, + { + "id": "guided-bot-pat-onboarding", + "title": "Guided bot PAT onboarding", + "status": "proposed", + "scope": "Guide creation of the persistent workflow PAT using GitHub's official form, verify bot identity and grants, and install the approved Actions Secret", + "owner": "Copilot maintainers", + "lastVerified": "2026-09-24", + "specs": [ + "specs/guided-bot-pat-onboarding.md" + ], + "workflows": [], + "entrypoints": [ + "src/cli/commands/setup.ts" + ], + "code": [ + "src/application/policies/setup_token_permission_policy.ts", + "src/application/usecases/setup/setup_credentials_use_case.ts", + "src/cli/setup_credential_prompt_adapter.ts", + "src/data/repository/repository_variables_repository.ts" + ], + "tests": [ + "src/application/policies/__tests__/setup_token_permission_policy.test.ts", + "src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts", + "src/cli/__tests__/setup_presenters.test.ts", + "src/data/repository/__tests__/repository_variables_repository.test.ts" + ], + "documentation": [ + "docs/authentication.mdx", + "docs/how-to-use.mdx", + "docs/security-operations/operations/troubleshooting.mdx" + ] + }, + { + "id": "temporary-setup-operator-authorization", + "title": "Assisted setup PAT creation", + "status": "proposed", + "scope": "Guide creation of the one-run operator PAT through GitHub's official form, verify final setup access, and report user-owned deletion accurately", + "owner": "Copilot maintainers", + "lastVerified": "2026-09-24", + "specs": [ + "specs/temporary-setup-operator-authorization.md" + ], + "workflows": [], + "entrypoints": [ + "src/cli/commands/setup.ts" + ], + "code": [ + "src/cli/setup_credential_prompt_adapter.ts", + "src/application/usecases/setup/setup_token_permissions_use_case.ts", + "src/infrastructure/setup_token_permission_query_adapter.ts", + "src/utils/setup_files.ts" + ], + "tests": [ + "src/cli/__tests__/setup_presenters.test.ts", + "src/application/usecases/setup/__tests__/setup_token_permissions_use_case.test.ts", + "src/infrastructure/__tests__/setup_token_permission_query_adapter.test.ts", + "src/utils/__tests__/setup_files.test.ts" + ], + "documentation": [ + "docs/how-to-use.mdx", + "docs/authentication.mdx", + "docs/security-operations/operations/troubleshooting.mdx" + ] + }, { "id": "issue-start-and-sdd-readiness", "title": "Uniform issue start and pre-branch SDD readiness", diff --git a/specs/guided-bot-pat-onboarding.md b/specs/guided-bot-pat-onboarding.md new file mode 100644 index 000000000..720c44e82 --- /dev/null +++ b/specs/guided-bot-pat-onboarding.md @@ -0,0 +1,553 @@ +# Guided Bot PAT Onboarding + +- Status: Draft — guided form is viable; runtime expiry and identity UX need review +- Date: 2026-09-24 +- Catalog capability ID: `guided-bot-pat-onboarding` +- Last verified: Not applicable; prospective change +- Owners: Copilot maintainers and setup operators +- Scope: guide creation and installation of the workflow/bot PAT when operator and bot are different GitHub accounts +- Related issues/PRs: none; no Action dogfooding for this design +- Required review gates: product UX, architecture, testing, documentation, credential security, GitHub form compatibility +- Open decisions blocking readiness: runtime PAT expiration default/rotation owner; behavior when organization PAT approval is pending; supported non-interactive identity assertion + +## 1. Executive summary + +`copilot setup` already asks for two separate credentials: an operator setup PAT +and a workflow PAT owned by the Action's bot account. After the operator PAT +and setup plan are accepted, the proposed guided path builds an official GitHub +fine-grained PAT creation URL from the final **bot** permission plan. The user +reviews the browser account and repository, generates a PAT, and enters it in +Copilot's masked prompt. Copilot checks its identity and grants. The operator +credential installs it as GitHub Actions Secret `PAT`; it remains active for +future workflow runs. + +This uses [GitHub's documented PAT URL parameters](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#pre-filling-fine-grained-personal-access-token-details-using-url-parameters). +The URL does **not** choose an individual repository or press Generate for the +user. It also cannot switch the browser's GitHub account. Therefore the UI +calls this **guided creation**, not automatic PAT issuance. There is no local +account manager or browser credential collection in this scope. + +```text +operator PAT: guided link -> user creates PAT -> verify operator -> setup plan +bot PAT: final runtime grants -> guided link -> user switches browser to bot + -> user selects repository and creates PAT -> verify bot -> Secret PAT +``` + +Text equivalent: the operator and bot each create their own PAT on GitHub; +Copilot checks the returned credential's identity and permissions and uses +each one only for its defined role. + +## 2. Problem, current behavior, evidence, and feasibility + +### 2.1 Problem + +The setup owner often has a work or personal GitHub account, while the Action +uses a separate service account. The existing terminal permission table helps +configure both PATs but leaves the user to navigate GitHub's form and enter +many grants. A browser signed into the wrong account can produce a syntactically +valid PAT for the wrong identity. The bot PAT is especially consequential +because it persists as `PAT` for Actions rather than expiring at the end of +setup. + +### 2.2 Observed repository behavior + +1. `src/cli/commands/setup.ts` resolves the operator PAT before running the + wizard, shows permission requirements, and later collects the separate + workflow PAT. +2. `src/application/policies/setup_token_permission_policy.ts` derives the + workflow PAT grants from the final selected features and storage policy. +3. `src/application/usecases/setup/setup_credentials_use_case.ts` requires a + supplied or re-entered workflow PAT for permission audit, even if remote + Secret `PAT` already exists. GitHub Secrets cannot reveal stored values. +4. `src/data/repository/repository_variables_repository.ts` writes validated + Secret values to the selected repository or organization scope. +5. `docs/authentication.mdx` recommends a dedicated bot account for an + organization and explains bot/self-event behavior and runtime grants. + +### 2.3 External primary evidence + +| Question | GitHub source | Contract consequence | +|---|---|---| +| What can a PAT URL prefill? | [PAT management: supported query parameters](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#supported-query-parameters) | `name`, `description`, `target_name`, `expires_in`, and permission levels. `expires_in` is 1–366 days or `none`, subject to owner policy. | +| Can it select the repository or browser identity? | [PAT creation steps](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-fine-grained-personal-access-token) and [supported query parameters](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#supported-query-parameters) | The documented URL has no individual-repository selector. The user selects repository access and confirms the active account in GitHub. | +| Can a user switch browser accounts? | [GitHub account switcher](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/switching-between-accounts) | Yes. The browser may prompt for an account when following a link. CLI identity selection does not set that browser state. | +| Can Copilot create or delete the PAT through an owner API? | [PAT management](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens), [organization PAT API](https://docs.github.com/en/rest/orgs/personal-access-tokens) | No documented owner PAT creation/deletion API found. Organization access management is not a user PAT minting flow. | + +### 2.4 Viability conclusion + +The official URL provides a supported, useful first release for **both** PAT +roles. It removes repetitive form entry and preserves GitHub's login, 2FA, +account switching, and final consent. It does not provide fully automatic PAT +creation or revocation. Replaying private GitHub web requests or sharing user +cookies is not required for this release and is not a stable product contract. +The temporary operator authorization proposal is in +[`temporary-setup-operator-authorization.md`](./temporary-setup-operator-authorization.md); +this SDD covers the bot's persistent runtime PAT. + +### 2.5 Retrospective classification + +Not applicable: this SDD specifies a proposed user journey, with current +behavior identified above. + +## 3. Actors, surfaces, and terminology + +| Actor | Goal | Entry point | Visible surfaces | +|---|---|---|---| +| Setup operator | configure repo/org and install Secret | `copilot setup` | permission tables, links, plan, result | +| Bot account owner | issue runtime PAT | GitHub PAT form | account switcher, permission form, PAT value | +| Organization admin | approve PAT/org resources if policy requires | GitHub Settings | pending/approved state | +| GitHub Action | use bot PAT after setup | workflows | jobs, PRs, issues | + +**Operator PAT** is the setup-only token, preferably short lived. **Bot PAT** +is a fine-grained personal access token issued by the bot account and stored as +Secret `PAT`. **Expected bot identity** is a GitHub account resolved to its +immutable numeric user ID before accepting a PAT. **Guided link** is a URL to +GitHub's own form, never a bearer credential. **Installed** means the Secret +API accepted the value; it does not prove a later workflow has run. + +## 4. Goals, non-goals, and fixed invariants + +### 4.1 Goals + +1. Setup MUST generate the bot PAT form link from the final runtime permission + plan and clearly distinguish it from the earlier operator link. +2. Interactive setup MUST offer guided creation (recommended) or the existing + manual PAT input at the workflow credential step. +3. In guided mode, the bot PAT MUST be checked against an explicitly chosen + expected bot user ID, repository/organization access, and selected-feature + permissions before Secret `PAT` is written. +4. Setup MUST report bot PAT installation separately from local disposal and + later workflow health. +5. The existing manual and non-interactive PAT inputs MUST remain available. + +### 4.2 Non-goals + +1. Managing several GitHub accounts or credential stores on the device. +2. Switching a GitHub browser, Git, or `gh` account automatically. +3. Creating or revoking a PAT through undocumented website requests. +4. Replacing the Action's PAT with an App installation token, or deleting the + bot PAT after setup; it is needed by later Action runs. +5. Automatically opening a browser, managing the clipboard, or adding a local + account store in the first release. + +### 4.3 Fixed invariants + +1. The generated URL contains no PAT, cookie, session key, 2FA data, or secret. + Only documented query parameters and a fixed GitHub host are allowed. +2. The user MUST select the individual repository in GitHub and confirm the + active browser account; URL `target_name` sets only resource owner. +3. Neither the browser login nor a typed bot username proves the PAT's owner. + In guided mode, Copilot MUST call GitHub `/user` with the supplied bot PAT + and compare the immutable user ID to the expected account before any + Secret write. Legacy manual/unattended inputs retain their current audit + until a separately reviewed compatibility migration extends this binding. +4. Operator PAT and bot PAT are never interchanged. The bot PAT never becomes + a local setup authority; the operator PAT never becomes Secret `PAT`. +5. A successfully installed bot PAT remains active. Local memory disposal is + not described as remote revocation. No PAT value enters config, files, + logs, URLs, or account metadata. +6. `--yes` cannot select the bot identity, accept missing grants, or generate + a PAT on behalf of the user. +7. The guided URL is printed in full as plain terminal text outside a bordered + box; no URL shortener or browser opener is required. + +## 5. Current versus proposed product journey + +| Stage | Current | Proposed | User effect | +|---|---|---|---| +| Operator access | permission table + masked prompt | official prefilled link + existing prompt | less manual form setup | +| Bot identity | implicit in docs and PAT value | ask expected bot login; resolve and display ID | wrong account detected | +| Bot grants | exact table before prompt | table + guided/manual choice and link from same policy result | fewer transcription errors | +| Bot browser | no explicit check | tell user to select bot in GitHub account switcher | handles separate accounts | +| Secret installation | validate and write `PAT` | same, with identity and scope result | clear Action owner | +| Completion | setup summary | setup summary distinguishes Secret from local PAT | accurate lifecycle | + +```mermaid +sequenceDiagram + participant U as Setup operator + participant C as Copilot CLI + participant G as GitHub + participant S as Actions Secret + C->>U: Show operator PAT link and masked prompt + U->>G: Create operator PAT in GitHub + U->>C: Enter operator PAT + C->>G: Verify operator and build final setup plan + C->>U: Show bot account, runtime grants, and link + U->>G: Switch to bot account and complete 2FA if asked + U->>G: Select repository and generate bot PAT + U->>C: Enter bot PAT in masked prompt + C->>G: Verify bot ID and grants with bot PAT + C->>S: Store PAT using operator authority + C->>U: Report Secret result and active bot identity +``` + +Text equivalent: each user-owned PAT is generated inside GitHub. Copilot checks +the resulting identities, uses the operator PAT to configure the repository, +and stores the bot PAT in the selected Actions Secret scope. + +## 6. Functional behavior and state model + +### 6.1 Normal path + +1. Complete the separate [operator PAT journey](./temporary-setup-operator-authorization.md), + including its local preflight and final permission audit. Do not reuse the + operator token as the Action credential. +2. Once setup choices and remote targets are final, use the existing workflow + permission policy to build the bot PAT link. Its name/description identify + purpose and repository; `target_name` is the repository owner; `expires_in` + is a reviewed runtime expiration; each permission uses GitHub's documented + query name and level. +3. At the existing workflow PAT collection point, present the final runtime + permission table and offer `Create with GitHub guidance` (recommended) or + `I already have a PAT`. In guided mode, ask for the expected bot login, + resolve its immutable GitHub ID, and display both. Print the full URL on + its own line outside `renderBox`; do not auto-open a browser. The user + switches to that account in GitHub, selects the target repository, reviews + the form, generates the PAT, and pastes it into the masked prompt. +4. Verify `/user` with the exact supplied PAT, compare immutable IDs, and run + the existing role-specific permission audit. An `Unverifiable` write stays + `Unverifiable` and follows the established acknowledgement policy. +5. After plan confirmation, use the operator credential to install the bot + PAT as repository or organization Secret `PAT`. Print Secret scope, expected + bot identity, successful setup facts, and any remaining health checks. +6. Release the local PAT value after use. The bot PAT remains in GitHub for + future Action runs and needs an owner-managed renewal before its expiration. + +### 6.2 Alternatives + +- Choosing `Enter PAT manually` keeps the existing masked prompts, permission + checks, and Secret provisioning. The current path does not bind a bot ID; + the CLI must not claim that it does. +- A user may decline the guided link and use the docs directly. Setup still + runs the existing permission audit without claiming bot identity binding. +- Non-interactive setup keeps supplied values and existing validation for the + first release. It never opens a browser or infers the bot from the operator + token. A later, explicit expected-bot-ID input and migration are required + before identity binding can become mandatory there. +- `--dry-run` generates a plan without a PAT; it may display an example link + only when its permission set is complete and clearly marked provisional. +- If organization policy requires approval, setup stops before writing Secret + `PAT` until target access is verified. It reports `pending approval` only + when GitHub provides explicit evidence; otherwise it reports unavailable + access and says approval is one possible cause. +- If the bot is the same account as the operator, explain event self-suppression + and enforce the existing guarded-approval identity restriction. + +### 6.3 State machine + +| State | Entered when | User-visible meaning | Next | Owner | +|---|---|---|---|---| +| `operator-pat-needed` | bootstrap grants known | create/paste operator PAT | `operator-verified`, `cancelled` | user | +| `operator-verified` | identity/grants accepted | finish plan choices | `bot-pat-needed` | CLI/user | +| `bot-pat-needed` | final grants and expected bot ID known | open GitHub as bot, create PAT | `bot-pat-verified`, `blocked`, `cancelled` | bot user | +| `bot-pat-verified` | ID and grants accepted | Secret ready to write after approval | `secret-writing` | operator | +| `secret-writing` | remote call started | provisioning | `installed`, `partial`, `blocked` | CLI | +| `installed` | Secret API accepted | runtime PAT is stored | terminal | operator | +| `partial` | Secret write succeeded but later setup failed | PAT may be active | inspect/retry | operator | + +Retries do not generate or store a second PAT automatically. A changed final +permission plan invalidates the previously shown URL and requires a new link. +If setup is canceled before Secret write, a PAT the user already generated may +remain active at GitHub; the CLI instructs the user to delete it. A crash after +Secret write requires read-first reconciliation of Secret **name and scope**; +GitHub cannot return the stored value. This is not a reason to claim failure +or to overwrite the Secret without a new approved value. + +## 7. User-facing configuration + +| Input | Type | Recommended default | Allowed values | Scope/persistence | +|---|---|---|---|---| +| PAT creation help | interactive choice | guided | `guided`, `manual` | one setup run; not saved | +| Operator PAT expiry | integer days | `1` for a one-run token | defined by companion operator SDD | link only | +| Bot PAT expiry | integer days | proposed `90`, subject to review and org policy | 1–366 per GitHub, no `none` in generated link | link only; not a new config Secret | +| Expected bot | GitHub login and resolved ID | explicit selection | one valid GitHub user | one run; non-secret display | +| Secret scope | existing setup storage policy | repository | repository or organization as already supported | approved setup plan | + +The final runtime permission policy is the sole source of URL grants. The +existing `--workflow-pat`/`--secret PAT=...` values override interactive input +and retain their current permission validation; they cannot silently enter +guided mode without an expected bot identity. The bot expiry default is +an open product decision; it cannot be shipped as a fixed value until rotation +ownership is documented. No link may select `none` silently. Invalid owner, +permission name/level, URL length, account, or scope blocks link generation. +There is no migration of existing PAT Secrets or account state. Role separation, +no embedded secret, and exact identity checking within guided mode are not +configurable. + +Recommended example: use `copilot setup`, follow the earlier operator guidance, +then choose guided bot creation after the approved plan. Alternative: create +the bot PAT manually from GitHub Settings and enter it into the existing prompt. + +## 8. Clean Architecture design + +### 8.1 Responsibilities and dependency direction + +| Boundary | Owns | Must not own/import | +|---|---|---| +| Domain/pure policy | role, owner, expiry, grant-to-URL mapping and identity match | HTTP, browser, terminal, tokens | +| Application | guide role-specific creation, verify supplied PAT, hand off bot PAT | GitHub DTOs, process opening | +| Ports | resolve GitHub identity, inspect permissions, write Secret | private web sessions | +| Adapters | official URL encoder, GitHub identity/permission queries, existing Secret API | product decisions | +| Composition | wire current setup stages and conditional guidance | duplicate permission rules | +| Presentation | permission table, link, masked prompt, state summary | PAT mutation | + +```mermaid +flowchart LR + C[Setup CLI] --> U[Guided PAT use case] + U --> P[Existing permission policy] + U --> L[Official URL builder] + U --> I[GitHub identity port] + U --> A[Existing permission audit] + U --> S[Existing Secret writer] +``` + +Text equivalent: setup presents the guide; a pure builder encodes the existing +permission plan into GitHub's URL; application logic verifies the token's +identity and grants; the existing Secret writer installs the runtime PAT. + +### 8.2 Contracts, state, and trust boundaries + +- `PatRole` is `operator` or `workflow-bot`; each has a separate permission + plan, expected identity, lifetime guidance, and cleanup owner. +- URL builder accepts only normalized `{name, description, targetName, + expiresIn, permissions}` and returns a URL pinned to + `https://github.com/settings/personal-access-tokens/new`. +- URL mapping is versioned against GitHub's documented parameter vocabulary. + `target_name` is an owner slug; the repository name is descriptive only and + cannot be misrepresented as selected repository access. +- In guided mode, bot token identity comes from GitHub `/user` using that + token. Compare numeric user IDs and verify repository access, then invoke + the existing permission audit. +- No durable local state is introduced. The remote Secret is the only durable + bot PAT copy Copilot creates. Browser state is owned by GitHub, not Copilot. + +### 8.3 Executable architecture constraints + +Pure URL builder imports no HTTP, filesystem, terminal, or Secret adapter. +Contract tests MUST compare each emitted permission query name to GitHub's +documented set and reject unknown grants. Setup architecture tests MUST show +that no generated URL contains a token or cookie, no guided Secret write +precedes bot identity verification, and no operator token is used as runtime +`PAT`. + +## 9. UI/UX and content contract + +The CLI first shows the role, expected account, repository, permission purpose, +remaining action, and cleanup ownership. The following English example matches +the current CLI language; it is illustrative. The URL is plain, complete, and +outside the bordered permission summary so it remains copyable. + +```text +Workflow PAT · 2 of 2 Repository: vypdev/copilot +The setup PAT is not the Action's runtime credential. +Choose how to provide the bot PAT: + 1) Create with GitHub guidance (recommended) + 2) I already have a PAT +Select [1]: +Expected bot account: vypbot +Expected account resolved: @vypbot (GitHub ID 5678) + +Action required: In GitHub, switch to vypbot and complete 2FA if asked. +Select only vypdev/copilot; review the prepared permissions, then Generate. +PAT creation URL: +https://github.com/settings/personal-access-tokens/new?name=...&target_name=vypdev&expires_in=...&... +Workflow PAT (hidden): +``` + +| State | Representative first visible text | Primary action | +|---|---|---| +| Pending | `Waiting for a PAT created by vypbot. No Secret has been written.` | open PAT link | +| Action required | `Before generating, check that GitHub is using vypbot. Select only vypdev/copilot, then generate the PAT.` | check browser account | +| Blocked | `The supplied PAT belongs to efrain, not vypbot. No Secret was written. Delete that PAT in GitHub and create one while signed in as vypbot.` | create correct PAT | +| Partial | `Secret PAT was updated, but later setup steps failed. The bot PAT may already be active. Inspect the setup report before retrying.` | inspect report | +| Complete | `Secret PAT is installed for vypdev/copilot. Verified owner: vypbot (ID 5678). The local value was discarded; the GitHub Secret remains active.` | run doctor/health check | + +Error content follows impact, cause, action, retained state. Do not say a +typed account name or URL proves the browser account. No Github issue, PR, +check, comment, or label is created solely for this flow. English follows the +current setup CLI; later locales use the message catalog. Text status does not +depend on color or emoji; tables and instructions wrap on narrow terminals, +while the raw URL stays complete on its own line. Limit to +one guided block per role and one final summary; no polling notifications. +Escape untrusted usernames and descriptions in terminal and URL content. + +## 10. Failure, recovery, and cleanup + +| Failure | Impact | Retained facts | Retry | Action | Cleanup | +|---|---|---|---|---|---| +| Wrong browser account | PAT belongs to another user | no Secret write | new PAT | switch account in GitHub | user deletes wrong PAT | +| Wrong resource owner/repo | PAT lacks target access | no Secret write | correct form | select target repo and owner | user deletes wrong PAT | +| Org PAT pending or inaccessible | Action cannot use it yet | no Secret write | after access is verified | inspect approval with org admin or choose permitted account; label pending only with evidence | user owns token | +| Missing/unverifiable grant | setup blocked by established audit policy | permission table | corrected PAT | inspect settings/acknowledge only where allowed | user deletes obsolete PAT | +| Secret write fails | Action keeps prior Secret or none | known Secret name/scope | setup retry | repair operator rights | local value discarded after run | +| Secret write succeeds, later step fails | bot PAT may be active | Secret name/scope and completed steps | idempotent retry | inspect report | do not delete runtime PAT | +| User cancels after PAT creation | PAT may remain active in GitHub | no local value retained | new setup | delete unused PAT | user-owned deletion | + +GitHub's Secret API cannot return the previous value, so a failed update cannot +be rolled back by reading it. `copilot doctor`/health inspection is separate +from Secret-write success. The terminal states these facts without exposing +the PAT. + +## 11. Security, permissions, and privacy + +1. GitHub owns password, 2FA, browser account selection, PAT generation, and + organization approval. Copilot handles only the resulting PAT value entered + in a masked prompt. +2. URL grants are derived from current permission policy, not from CLI prose. + Strongest required level wins; no unrelated grant is added for convenience. +3. For guided mode, verify bot numeric user ID with the bot PAT itself; + compare it to a separately resolved expected identity. A login string or + claimed role is insufficient. Do not describe legacy validation as this + stronger identity check. +4. Operator and bot PATs remain separate in memory and at API boundaries. Bot + PAT is written only as Secret `PAT`, never to a Variable or local file. +5. The new guided path never puts a PAT in command arguments, stdout, URL, + logs, telemetry, diagnostics, or fixtures. Existing command-line PAT flags + remain for compatibility but should warn about process-list/history exposure. + Discarding process memory is not revocation. +6. The runtime PAT requires a renewal plan before expiry. No one-run cleanup + may revoke it while the Action depends on it. + +## 12. Observability and operational UX + +Record role, GitHub user ID/login, target repo, permission result statuses, +Secret name/scope, and setup outcome only. No token value or raw provider +response is recorded. Confirmed `pending approval` is distinct from a failed +PAT; unknown access must not be mislabeled as pending. +The CLI reports after identity verification and after Secret write, without +repeated prompts. A user can inspect the GitHub Secret **name** and later Action +health but not the Secret value. Failures include an actionable GitHub Settings +link and the retained state. Rate-limited identity or permission checks yield +a bounded retry message, never a false success. + +## 13. Compatibility, migration, rollout, and rollback + +Existing `--token`, `PERSONAL_ACCESS_TOKEN`, `--workflow-pat`, and +`--secret PAT=...` remain accepted with current precedence. Existing Secrets +are not modified by the link feature alone; a new value is installed only +after the normal plan confirmation and audit. Initial rollout adds guided +links and bot ID binding in interactive guided setup. Manual and unattended +inputs keep current behavior, with an explicit warning that bot identity is +not bound. Non-interactive bot ID binding follows only after an explicit input +contract and migration policy are settled. Rollback hides the links +and returns to current manual instructions; already installed bot PATs remain +active until their owner rotates or revokes them. + +## 14. Testing strategy and numeric budget + +The implementation floor is **36 distinct bot-specific cases**, derived from +the runtime permission plan, wrong-account and wrong-repository states, Secret +write outcomes, and compatibility modes. Shared URL builder cases in the +operator SDD are not counted again here. + +| Area | Minimum cases | Key behavior | +|---|---:|---| +| Domain/configuration/pure URL mapping | 8 | bot-only permission keys/levels, owner, expiry, invalid grants | +| State/application/idempotency | 7 | plan change, cancellation, re-entry, retry, Secret partial state | +| Provider adapters/contracts | 5 | token `/user`, ID mismatch, repository access, Secret response | +| Setup/permissions/schema | 5 | operator/bot role separation, final policy, org scope, existing Secret | +| UI/accessibility/localization | 5 | pending/action/blocked/partial/complete and narrow output | +| Integration/security/migration | 6 | no URL secrets, no wrong-token write, manual/non-interactive compatibility | +| **Total** | **36** | No double counting | + +Repository-wide thresholds remain; the new pure mapping and identity policy +target 100% branch coverage, and changed setup modules target at least 95% +lines/statements and 90% branches/functions. Use deterministic GitHub fakes, +not live PAT creation in CI. Contract fixtures assert parsed URL parameters +and semantic CLI copy, not snapshots alone. Manual acceptance evidence must +include two browser accounts, 2FA handled by GitHub, wrong-account rejection, +repository selection, Secret installation, and post-write partial failure. +Test PATs are deleted by their owners after the controlled exercise. + +## 15. Documentation and discoverability + +| Audience | Artifact | Required content | Validation | +|---|---|---|---| +| New user | `README.md`, `docs/how-to-use.mdx` | two PAT roles, guided links, browser account choice | navigation/link test | +| Setup operator | `docs/authentication.mdx`, `docs/configuration.mdx` | exact grants, repo selection, expiry, Secret scope | permission fixture | +| Operator | `docs/security-operations/operations/troubleshooting.mdx` | wrong account, org approval, cleanup, partial Secret write, renewal | recovery fixture | +| Contributor | `docs/development/architecture.mdx`, this SDD | URL builder, identity binding, trust boundary | architecture test | + +Docs must explicitly say the link does not select a repository or create the +PAT, and the bot PAT remains active after setup. + +## 16. Acceptance scenarios + +1. Given the selected setup features, each role receives a documented PAT URL + with exactly its own needed permission levels and no token material. +2. Given local-only choices change, the operator link changes before a PAT is + requested; final remote inspection revalidates its grants. +3. Given a bot account chosen by name, Copilot resolves and displays its + immutable ID before accepting the bot PAT. +4. Given the browser uses another account, a PAT created there fails `/user` + ID comparison and no Secret write occurs. +5. Given the correct bot account but wrong repository selection, setup blocks + before Secret write and names the correction. +6. Given an organization PAT pending approval or otherwise unable to reach + the target, setup does not present the Action as ready; it names pending + approval only when provider evidence supports it. +7. Given accepted bot identity and grants, operator authority installs Secret + `PAT` at the approved scope; the final report says it remains active. +8. Given an existing Secret, the CLI does not claim to know its value and + requests re-entry for the existing permission audit. +9. Given Secret write success followed by another setup failure, output + reports the partial result and does not revoke the bot PAT. +10. Given cancellation after GitHub created a PAT but before Secret write, + the CLI instructs the user to delete the unused PAT in GitHub. +11. Given a legacy manual or non-interactive PAT input, current setup continues + to work with its existing permission audit; no bot identity verification + is claimed until the planned migration is implemented. +12. Pending, action, blocked, partial, and complete CLI states are readable + without color and accurately distinguish local disposal from GitHub Secret. + +## 17. Requirements traceability + +| Requirement | Owner | Verification | Documentation | +|---|---|---|---| +| Role-specific link (§4.1, §6.1) | policy + URL builder | scenarios 1–2 | how-to-use/authentication | +| Bot ID and grants (§4.3, §6.1) | identity port + existing audit | scenarios 3–6 | authentication | +| Secret lifecycle (§4.3, §10) | existing credential/Secret use cases | scenarios 7–10 | setup/troubleshooting | +| Compatibility (§6.2, §13) | setup CLI | scenario 11 | CLI guide | +| Truthful UX (§9) | presenter | scenario 12 | how-to-use | + +## 18. Implementation sequence + +1. Resolve operator pre-auth planning and bot PAT expiration policy. Validate + the official permission-key mapping with GitHub's current documentation. +2. Implement a pure, role-specific URL builder and contract tests using the + existing permission policy; keep URL generation separate from token input. +3. Add expected bot ID resolution and exact-token `/user` comparison before + Secret collection/provisioning. +4. Integrate guided links and state messages with the existing CLI prompts and + setup plan; preserve manual/non-interactive paths. +5. Add Secret partial-state tests, docs, architecture checks, controlled + two-account UX evidence, coverage, and specification validation. + +## 19. Definition of Done + +- [ ] Open decisions are resolved before Ready for implementation. +- [ ] Every MUST has an acceptance scenario and test or evidence. +- [ ] URLs use only documented GitHub parameters and never contain secrets. +- [ ] Guided operator and bot IDs/grants are verified with their actual + credentials; legacy paths are labeled accurately. +- [ ] Secret scope, persistence, partial writes, and cleanup are truthful. +- [ ] Architecture checks, 36-case budget, and coverage targets pass. +- [ ] CLI primary states pass accessibility, sanitization, and locale review. +- [ ] User, setup, operator, and contributor docs are complete and linked. +- [ ] Catalog evidence and generated `specs/CATALOG.md` are current and + `pnpm run validate:specifications` passes. +- [ ] Controlled two-account acceptance evidence is recorded without PATs. + +## 20. References and decisions + +- Related specs: [temporary setup operator authorization](./temporary-setup-operator-authorization.md), + [setup baseline](./setup-configuration-credentials-and-doctor.md), and + [PAT permission guidance](./setup-pat-permission-guidance-and-verification.md). +- Primary sources: [PAT form and URL templates](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens), + [browser account switcher](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/switching-between-accounts), + [organization PAT endpoints](https://docs.github.com/en/rest/orgs/personal-access-tokens). +- Decision: use GitHub's documented guided form for both PATs. Local account + profiles are unnecessary for this flow; verify each guided PAT's actual + owner rather than trusting the browser or local CLI account. Legacy paths + keep their current checks pending a migration. Automatic bot PAT creation + needs a future supported GitHub API and is not claimed here. diff --git a/specs/setup-configuration-credentials-and-doctor.md b/specs/setup-configuration-credentials-and-doctor.md index daf9b1db6..588e4d92e 100644 --- a/specs/setup-configuration-credentials-and-doctor.md +++ b/specs/setup-configuration-credentials-and-doctor.md @@ -70,6 +70,12 @@ but unusable, overwrite hand-maintained files, or expose credentials. [`architecture-quality-and-scalability-hardening.md`](./architecture-quality-and-scalability-hardening.md). Role-specific PAT guidance and safe permission evidence are specified in [`setup-pat-permission-guidance-and-verification.md`](./setup-pat-permission-guidance-and-verification.md). + Assisted operator PAT creation is proposed in + [`temporary-setup-operator-authorization.md`](./temporary-setup-operator-authorization.md), + not an implemented setup path. + Guided creation of the separate persistent bot PAT is proposed in + [`guided-bot-pat-onboarding.md`](./guided-bot-pat-onboarding.md), + without a local account manager. Transactional rollback across local and GitHub writes requires a separate design. ## 3. Actors, surfaces, and terminology diff --git a/specs/setup-pat-permission-guidance-and-verification.md b/specs/setup-pat-permission-guidance-and-verification.md index 2a81afc26..a5ff0a8e0 100644 --- a/specs/setup-pat-permission-guidance-and-verification.md +++ b/specs/setup-pat-permission-guidance-and-verification.md @@ -109,7 +109,12 @@ transient response. ### 4.2 Non-goals -1. Setup does not enumerate, create, edit, rotate, or revoke GitHub PATs. +1. The implemented permission-guidance flow does not enumerate, create, edit, + rotate, or revoke GitHub PATs. The proposed guided operator PAT flow and + its explicit GitHub deletion responsibility are documented in + [`temporary-setup-operator-authorization.md`](./temporary-setup-operator-authorization.md). + The separate proposed guided workflow PAT is covered by + [`guided-bot-pat-onboarding.md`](./guided-bot-pat-onboarding.md). 2. Setup does not prove write access by creating temporary labels, branches, files, Variables, Secrets, comments, projects, or workflow runs. 3. Existing remote Secret values remain unavailable. Credential-health evidence diff --git a/specs/temporary-setup-operator-authorization.md b/specs/temporary-setup-operator-authorization.md new file mode 100644 index 000000000..1d111d5d2 --- /dev/null +++ b/specs/temporary-setup-operator-authorization.md @@ -0,0 +1,475 @@ +# Assisted Setup PAT Creation + +- Status: Draft — the guided flow is feasible; pre-auth planning and cleanup UX need review +- Date: 2026-09-24 +- Catalog capability ID: `temporary-setup-operator-authorization` +- Last verified: Not applicable; prospective change +- Owners: Copilot maintainers and setup operators +- Scope: guide creation, verification, use, and user-owned deletion of the operator PAT for one `copilot setup` run +- Related issues/PRs: none; no Action dogfooding for this design +- Required review gates: product UX, architecture, testing, documentation, security, GitHub form compatibility +- Open decisions blocking readiness: exact local-only preflight questions; whether the initial link is provisional for remote-dependent grants; handling a plan that needs additional grants + +## 1. Executive summary + +Interactive `copilot setup` will offer **Create with GitHub guidance** (the +recommended choice) or **I already have a PAT** immediately before requesting +the operator credential. Guidance prints an official, prefilled GitHub +fine-grained PAT URL for the selected repository owner and currently known +permissions. The user chooses the browser account, selects the individual +repository, reviews the form, generates the PAT, and pastes it into the existing +masked prompt. Copilot verifies access, runs setup, discards its local value, +and tells the user to delete the PAT in GitHub. A one-day expiry is a safety +backstop, **not** proof of deletion or revocation. + +The companion [bot PAT SDD](./guided-bot-pat-onboarding.md) covers the second, +persistent token installed as Actions Secret `PAT` after the setup plan is +known. Both roles share one URL-building contract, but not a credential or +lifecycle. + +```text +resolve repository -> local preflight -> choose guided/manual operator PAT + -> GitHub form -> masked input -> verify -> plan and final grant audit + -> guide/verify bot PAT -> install Secret -> apply setup -> cleanup reminder +``` + +Text equivalent: the terminal guides two separate PATs during one setup run; +GitHub owns authentication and issuance; Copilot verifies and uses each token +only for its role; the operator deletes the temporary PAT in GitHub. + +## 2. Problem, current behavior, evidence, and feasibility + +### 2.1 Problem + +The first-time operator sees a large permission table but must navigate to the +correct GitHub form and transcribe every grant. The same browser may contain a +personal and a bot account. Merely disposing of the PAT in local memory does +not remove it from GitHub. + +### 2.2 Observed repository behavior + +1. `src/cli/commands/setup.ts` resolves the repository, prints the bootstrap + setup-PAT table, and requests the setup PAT **before** the questionnaire. +2. `buildSetupPatPermissionRequirements()` has conditional rows because the + final features and remote state are not yet known. The approved plan is + re-audited with `buildConfiguredSetupPatPermissionRequirements()`. +3. `SetupCredentialPromptAdapter` uses a masked terminal input. The setup PAT + is not installed as runtime Secret `PAT`. +4. `SetupCredentialsUseCase` gathers the distinct workflow PAT later, after + the plan, and GitHub cannot reveal existing Secret values. + +### 2.3 External primary evidence + +| Question | Official source | Decision | +|---|---|---| +| Can the form be prepared? | [GitHub PAT URL parameters](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#pre-filling-fine-grained-personal-access-token-details-using-url-parameters) | Use documented `name`, `description`, `target_name`, `expires_in`, and permission levels. Validate names and levels. | +| Can the URL select one repository? | [GitHub PAT creation steps](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-fine-grained-personal-access-token) | No documented individual-repository URL parameter; the user must select it in GitHub. | +| Who handles the account and 2FA? | [GitHub browser account switcher](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/switching-between-accounts) | GitHub owns the browser session and account choice; local Git/`gh` identity is not evidence. | +| Can this link create or delete the PAT? | [GitHub PAT management](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens) | No. The user generates and deletes it in GitHub Settings. No documented owner PAT mint/revoke API was found. | + +### 2.4 Viability decision + +**Guided creation is feasible; automatic PAT issuance and destruction are not +claimed.** Do not replay private website requests, read cookies, capture 2FA, +or call the link an authorization grant. A future GitHub App design would use a +different credential and requires its own endpoint and revocation proof; it is +outside this SDD and is not displayed as an available terminal choice. + +### 2.5 Retrospective classification + +Not applicable: this is a proposed extension to the observed setup flow. + +## 3. Actors, surfaces, and terminology + +| Actor | Goal | Entry point | Visible surfaces | +|---|---|---|---| +| Setup operator | configure one repository | `copilot setup` | permission table, choice, URL, masked prompt, result | +| GitHub | authenticate and issue PAT | official form | account switcher, 2FA, repository selector, Generate | +| Bot owner | issue the separate runtime PAT | later setup step | [bot PAT journey](./guided-bot-pat-onboarding.md) | + +**Operator PAT** means the human's one-run setup credential. **Guided** means +the CLI prepares a form URL; the user still creates the PAT. **Discarded** means +the CLI no longer retains the value. **Deleted/revoked** means GitHub has +invalidated it; this flow cannot infer that from local disposal. **Provisional +link** means later remote inspection may require a new grant. + +## 4. Goals, non-goals, and fixed invariants + +### 4.1 Goals + +1. Interactive setup MUST offer guided creation or existing manual PAT input + without introducing a second setup command. +2. The link MUST include only known, needed grants from the same permission + policy as the terminal table; unknown remote-dependent grants MUST be + disclosed as provisional, not silently added. +3. The actual operator PAT MUST pass existing identity, repository-access, and + final-plan permission checks before dependent mutation. Guided setup MUST + show its authenticated account and ask the operator to confirm that this is + the account intended to configure the repository. +4. The final terminal result MUST distinguish local disposal from GitHub + deletion and provide a concrete deletion action. + +### 4.2 Non-goals + +1. Automatic browser login, 2FA, PAT generation, or PAT deletion. +2. A local multi-account manager or storing browser credentials. +3. Replacing the bot PAT or changing Action runtime authentication; the + companion SDD covers assisted creation of that separate PAT. +4. Automatically opening the browser, managing the clipboard, shortening URLs, + or adding account-profile persistence in the first release. +5. Dogfooding this repository's issue/Action workflow for this design. + +### 4.3 Fixed invariants + +1. The URL host/path are fixed to + `https://github.com/settings/personal-access-tokens/new`; it contains no + PAT, cookie, 2FA code, callback secret, or arbitrary URL input. +2. `target_name` selects only resource owner. The CLI MUST tell the user to + select the individual repository and check the active browser account. +3. The CLI MUST NOT say a PAT was created, deleted, or revoked by Copilot. +4. The operator PAT MUST NOT become Actions Secret `PAT`; the bot PAT MUST NOT + become operator setup authority. +5. `--yes`, non-interactive mode, and dry-run MUST NOT trigger browser actions, + generate a PAT, or silently accept new permissions. + +## 5. Current versus proposed product journey + +| Stage | Current | Proposed | User effect | +|---|---|---|---| +| Before setup PAT | bootstrap permission table | brief local preflight, table, guided/manual choice | purpose and account are clear | +| GitHub | user navigates form and transcribes grants | documented prefilled URL; user selects repo and generates | less repetitive form work | +| After paste | identity/access and grant audit | same audit; display actual account | wrong token found before setup | +| After plan | final grant audit | same audit; if link was provisional, explain correction | no silent overgrant | +| Completion | local PAT not stored | explicit GitHub deletion reminder and link | honest cleanup | + +```mermaid +sequenceDiagram + participant U as Operator + participant C as Copilot CLI + participant G as GitHub + C->>U: Show permission summary and guided/manual choice + C->>U: Print official PAT URL and repository instruction + U->>G: Choose account, complete 2FA if required, select repo, Generate + U->>C: Paste PAT into masked prompt + C->>G: Verify account, repository, and grants + C->>U: Show actual account and request confirmation + C->>U: Confirm plan; explain any newly required grant + C->>G: Apply approved setup + C->>U: Report local disposal and user-owned GitHub deletion +``` + +Text equivalent: the user creates a PAT on GitHub, the CLI verifies and uses it +for setup, then explicitly asks the user to delete it on GitHub. + +## 6. Functional behavior and state model + +### 6.1 Normal path + +1. Resolve repository. Collect only local configuration choices that affect + permissions before asking for the setup PAT. This preflight MUST reuse the + existing questionnaire policy, not fork a second configuration model. +2. Render the existing permission summary and `Create with GitHub guidance` + (recommended) / `I already have a PAT`. If a supplied `--token` or + `PERSONAL_ACCESS_TOKEN` exists, retain existing precedence and skip the + interactive choice. +3. In guided mode, build the official URL from known selected grants. If a + remote-dependent grant cannot be determined before authorization, label + the link **provisional** and identify the possible later correction. A + permission whose URL name/level is unknown blocks link generation and + offers manual configuration with the existing table. +4. Print the full URL on its own terminal line, outside `renderBox`; give + account/repository instructions. Do not auto-open the browser. Accept the + PAT only through the existing masked prompt. +5. Inspect the supplied PAT. Show the actual GitHub account and grant report. + In guided mode, ask the operator to confirm that account before mutation; + reject a decline or invalid target access. After plan confirmation, run the final policy + audit; if a previously unknown grant is required, block dependent mutation + and explain how to correct or replace the PAT. +6. Continue to the separate bot PAT journey. On success, failure, or + cancellation after PAT creation, remind the user to delete the setup PAT + in GitHub. Never claim deletion was verified. + +### 6.2 Alternatives + +- Manual uses the existing masked prompt and permission audit without a link. +- Existing supplied-token and unattended paths remain unchanged; no new prompt + or browser action occurs. A cleanup reminder MAY be shown, but the CLI + cannot know who created or owns a supplied token. +- Dry-run shows a plan and can explain the future PAT requirement, but never + creates a credential or opens GitHub. +- If the user cancels before pasting, no local PAT value exists; a PAT they may + already have generated in GitHub remains their deletion responsibility. +- If GitHub organization approval is required, setup waits for verified target + access; call it `pending approval` only with explicit provider evidence. + +### 6.3 State machine + +| State | Entered when | Visible meaning | Next | Owner | +|---|---|---|---|---| +| `choice` | no supplied PAT | guided/manual decision | `form-ready`, `manual-input`, `cancelled` | operator | +| `form-ready` | URL validated | GitHub action required | `pat-entered`, `cancelled` | operator | +| `pat-entered` | masked value received | verification in progress | `verified`, `blocked` | CLI | +| `verified` | current grants accepted and account confirmed | plan and final audit | `setup-running`, `blocked` | CLI/operator | +| `setup-running` | plan approved | apply setup | `complete`, `partial` | CLI | +| `complete` | setup finished | delete operator PAT in GitHub | terminal | operator | +| `partial` | some changes applied | inspect report, then delete PAT | retry/terminal | operator | + +Re-entering the same PAT does not create another one. A changed plan invalidates +the old link. A crash cannot guarantee GitHub deletion; restart and recovery +instructions must not imply otherwise. + +## 7. User-facing configuration + +| Input | Type | Recommended default | Allowed values | Scope/persistence | +|---|---|---|---|---| +| PAT help choice | interactive enum | guided | `guided`, `manual` | one run; not saved | +| Generated expiry | integer days | `1` | documented 1–366; first release fixes link at 1 | URL only; GitHub policy may override | +| Repository | existing Git remote identity | current repo | verified owner/repo | one run | +| Supplied operator token | existing secret input | none | current CLI/env precedence | memory only | + +No new account or PAT configuration is persisted. Wrong owner, invalid grant, +URL length outside a reviewed terminal bound, and contradictory preflight +choices block link generation. Existing `--token` and environment precedence +remain; `--yes` does not choose an identity or waive checks. Expiry, host, +secret-free URL, and role separation are not configurable in this first +release. The recommended example is interactive guided setup; the meaningful +alternative is manual PAT creation and masked input. + +## 8. Clean Architecture design + +### 8.1 Responsibilities and direction + +| Boundary | Owns | Must not own/import | +|---|---|---| +| Pure policy | permission-plan-to-URL mapping, role, validation | browser, HTTP, terminal, token values | +| Application | guided choice, final audit, cleanup message state | process/browser APIs, GitHub DTOs | +| Ports | secret input, identity/grant inspection, presentation | private website sessions | +| Adapters | GitHub query mapping and terminal rendering | permission decisions | +| Composition | connect existing setup stages | duplicate policy tables | + +```mermaid +flowchart LR + E[Setup entrypoint] --> A[Guided PAT flow] + A --> P[Existing permission policy] + A --> B[Pure GitHub URL builder] + A --> V[GitHub identity and grant audit] + A --> T[Terminal presenter] +``` + +Text equivalent: setup orchestrates a guided choice, derives the URL from the +existing policy, verifies the pasted PAT through existing GitHub ports, and +renders status in the terminal. + +### 8.2 Contracts, state, and trust boundaries + +- The URL builder receives `{role, owner, name, description, expiresIn, + permissions}` and emits only documented query parameters. It rejects + duplicate/conflicting grants and unsupported scope/level pairs. The bot SDD + reuses this contract with different role and expiry. +- GitHub web authentication, 2FA, account switching, repository selection, + PAT generation, and deletion stay entirely in GitHub. +- No durable local token or browser session state is introduced. The remote + setup mutations remain governed by the existing approved plan. +- Token identity and permission responses are untrusted provider evidence; + presentation escapes account names and never prints raw responses. + +### 8.3 Executable constraints + +Architecture tests forbid the pure builder from importing terminal, HTTP, +filesystem, or browser modules. Contract tests parse every emitted query key +and level against GitHub's documented set. Security tests reject token/cookie +material in URLs and logs and prove no setup mutation precedes final grant +acceptance. + +## 9. Terminal UI and content contract + +The current CLI is English; this example is illustrative and follows its +existing text-first styling. Preserve one primary action per state. + +```text +Setup PAT · 1 of 2 Repository: vypdev/copilot +Choose how to provide the setup PAT: + 1) Create with GitHub guidance (recommended) + 2) I already have a PAT +Select [1]: + +Action required: Create the PAT in GitHub as the account that will configure +vypdev/copilot. The link fills known permissions, but does not select a repo. +In GitHub, select only vypdev/copilot, review the grants, then Generate. +PAT creation URL: +https://github.com/settings/personal-access-tokens/new?name=...&target_name=vypdev&expires_in=1&... +Setup PAT (hidden): +``` + +If remote-dependent permissions remain unknown, insert `Provisional link: +setup will check the final plan and may require you to adjust this PAT` before +the URL. The URL is printed as an unwrapped plain line outside a bordered box; +terminal auto-linking is optional, never required. Do not copy it to clipboard +or open a browser automatically. + +| State | First visible text | Next action | +|---|---|---| +| Pending | `Waiting for a setup PAT. No setup changes have started.` | create/paste PAT | +| Action required | `Check the GitHub account and select only vypdev/copilot before Generate.` | complete GitHub form | +| Blocked | `Setup has not changed the repository: this PAT lacks Contents write required by the final plan. Update or replace it, then retry.` | correct PAT | +| Partial | `Some setup changes were applied. The operator PAT may still be active in GitHub. Inspect the setup report, then delete the PAT.` | inspect/delete | +| Complete | `Setup complete. The operator PAT was discarded locally, not deleted from GitHub. Delete it in GitHub Settings.` | delete PAT | + +Errors follow impact, cause, action, retained state. Status uses words, not +color/emoji alone. Narrow terminals keep choices and instructions readable; +the URL stays copyable. English message catalog is the initial source; later +locales follow existing fallback policy. Escape untrusted repository/account +names. No issue, PR, comment, label, or check is created by this UI. + +## 10. Failure, recovery, and cleanup + +| Condition | Impact | Retained fact | Retry/action | Cleanup | +|---|---|---|---|---| +| Wrong browser account | PAT belongs to an unintended user | no mutation before guided account confirmation | decline, switch in GitHub, recreate if needed | user deletes wrong PAT | +| Wrong repo/owner | PAT lacks target access | no dependent mutation | select correct repo in GitHub | user deletes unused PAT | +| Final plan adds grant | setup cannot proceed safely | plan and audit result | update PAT or create a new one | user deletes obsolete PAT | +| Unknown form parameter | no safe guided URL | manual path remains | use table/manual form | no generated PAT | +| User cancels after GitHub generation | PAT may remain active | no local value | delete in GitHub | user-owned | +| Setup partially applies | repo may be changed | report of completed steps | inspect before retry | delete operator PAT only after no retry needs it | +| GitHub deletion not confirmed | PAT may remain valid until expiry | local disposal only | open PAT Settings and delete | do not claim revoked | + +The cleanup URL points to GitHub PAT Settings, not to a destructive endpoint. +The CLI cannot identify or delete the exact PAT from the supplied value. A +one-day expiry still permits use until expiry and may be shortened by policy. + +## 11. Security, permissions, and privacy + +1. Least-privilege grants come from the same setup policy used by the final + permission audit. The link never adds every conditional grant by default. +2. No password, cookie, browser profile, 2FA code, or PAT appears in a URL, + config file, telemetry, logs, or GitHub issue. Masked input is retained. +3. Existing command-line PAT flags remain for compatibility; guided mode does + not put token values in process arguments and docs should warn about those + legacy flags exposing values in shell history/process inspection. +4. Private GitHub website requests are not a supported authentication + contract; no browser scraping is added. + +## 12. Observability and operational UX + +Show role, target repository, actual authenticated login, access result, +whether the link was provisional, and setup/cleanup status. Do not record token +values or raw API payloads. A failed check includes one next action and known +retained state. Limit output to one choice, one guidance block, existing audit +report, and one final cleanup reminder; no polling or notifications. + +## 13. Compatibility, migration, rollout, and rollback + +The manual prompt, `--token`, `PERSONAL_ACCESS_TOKEN`, `--non-interactive`, +`--yes`, and dry-run continue to work with existing precedence. The first +release adds only interactive guidance and local preflight needed for its URL. +No repository schema, Secret, or account-store migration occurs. Rollback +hides guidance and returns to the current prompt; PATs already generated by +users remain their responsibility. User docs must not imply the guided path +exists until implemented. + +## 14. Testing strategy and numeric budget + +The minimum is **30 distinct cases**, derived from two UI choices, bootstrap +versus final grants, provider identity/scope, cancellation, and cleanup truth. + +| Area | Cases | Risk covered | +|---|---:|---| +| Pure URL/configuration policy | 7 | key/level mapping, expiry, owner, encoding, rejected grants | +| State/application/idempotency | 6 | choice, preflight, final-plan change, retry, cancel | +| Provider/permission contracts | 4 | identity, repo access, missing grant, unknown response | +| Setup/compatibility | 4 | manual, supplied token, unattended, dry-run | +| Terminal/accessibility/localization | 5 | pending, action, blocked, partial, complete; narrow URL | +| Integration/security | 4 | no URL secret, no early mutation, cleanup truth, wrong account | +| **Total** | **30** | Distinct tests, no double counting | + +Existing repository-wide gates remain. New pure URL policy targets 100% +branch coverage; changed setup code targets at least 95% lines/statements and +90% branches/functions. Use deterministic GitHub fakes, no real PATs in CI; +assert parsed query parameters and semantic UI text, not snapshots alone. +Human UX evidence includes a narrow terminal, browser account switcher, 2FA +handled by GitHub, repo selector, and guided/manual fallback. No live token +value enters test evidence. + +## 15. Documentation and discoverability + +| Audience | Artifact | Required content | Validation | +|---|---|---|---| +| New user | `README.md`, `docs/how-to-use.mdx` | two roles and guided/manual normal path | navigation/link check | +| Setup owner | `docs/authentication.mdx`, `docs/configuration.mdx` | URL limits, preflight, exact permissions, account/repo choice | policy fixture | +| Operator | `docs/security-operations/operations/troubleshooting.mdx` | wrong account, changed grants, cancellation, deletion | recovery fixture | +| Contributor | `docs/development/architecture.mdx`, this SDD | shared URL builder and trust boundary | architecture test | + +Docs are updated with implementation, not ahead of it. Examples must match +CLI fixtures and clearly distinguish setup-PAT deletion from persistent bot +Secret renewal. + +## 16. Acceptance scenarios + +1. Given interactive setup without a supplied token, the CLI offers guided + creation and manual input; guided is the default. +2. Given guided choice, the CLI prints a documented GitHub URL, repository + instruction, and hidden PAT prompt; it does not open a browser. +3. Given local feature choices, URL permissions match known selected grants; + unknown remote-dependent grants are labeled provisional, not overgranted. +4. Given an unintended browser account, the CLI displays the actual login and + the operator declines it; given a wrong repo, access verification fails. + Either way, setup blocks before dependent mutation. +5. Given a final plan requiring an additional grant, the CLI explains the + correction and blocks mutation until the PAT passes re-audit. +6. Given manual, supplied-token, unattended, or dry-run paths, their existing + behavior is preserved without surprise browser action. +7. Given cancellation after the user generated a PAT, the CLI warns that it + may remain active and links to GitHub Settings. +8. Given partial setup, the CLI reports completed changes separately from + token cleanup and does not claim rollback. +9. Given complete setup, the CLI says local value discarded, GitHub deletion + still required; it never says revoked without evidence. +10. Given an unsupported URL permission, no misleading link is shown and the + manual permission table remains available. +11. All primary states remain readable without color at narrow width and the + full URL is copyable. + +## 17. Requirements traceability + +| Requirement | Owner | Test/evidence | Documentation | +|---|---|---|---| +| Guided/manual choice (§4.1) | setup CLI + presenter | scenarios 1–2, 6 | how-to-use | +| Exact/provisional grants (§4.1–4.3) | permission policy + URL builder | scenarios 3, 5, 10 | authentication/configuration | +| Actual token audit (§4.1) | existing permission use case | scenarios 4–5 | troubleshooting | +| Cleanup truth (§4.1–4.3) | setup result presenter | scenarios 7–9 | authentication/troubleshooting | +| Accessible UI (§9) | terminal renderer | scenario 11 | how-to-use | + +## 18. Implementation sequence + +1. Settle the pre-auth local questions and provisional-link rule using the + current permission policy; record provider parameter mapping. +2. Build one pure URL policy shared with the bot SDD and its contract tests. +3. Integrate the guided/manual choice and raw URL output before the masked + operator PAT prompt, preserving supplied-token paths. +4. Add final-audit correction and honest cleanup states, plus failure tests. +5. Update user/architecture/recovery docs, coverage, UX evidence, and catalog + validation before enabling guidance. + +## 19. Definition of Done + +- [ ] Readiness-blocking preflight and provisional-link decisions are settled. +- [ ] Every MUST maps to acceptance and verification. +- [ ] Only documented GitHub URL parameters are emitted; URL contains no secret. +- [ ] Account/repo/permissions are checked through the supplied PAT. +- [ ] Manual, supplied-token, unattended, dry-run, and `--yes` behavior remain safe. +- [ ] Cancellation, partial setup, and deletion wording are accurate. +- [ ] Architecture, 30-case floor, coverage, security, and narrow-terminal UX pass. +- [ ] User, setup, operator, contributor docs and navigation are updated. +- [ ] Catalog evidence and generated `specs/CATALOG.md` are current; + `pnpm run validate:specifications` passes. + +## 20. References and decisions + +- Related: [guided bot PAT onboarding](./guided-bot-pat-onboarding.md), + [setup baseline](./setup-configuration-credentials-and-doctor.md), and + [PAT permission guidance](./setup-pat-permission-guidance-and-verification.md). +- Primary sources: [GitHub PAT form and URL parameters](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens), + [GitHub browser account switcher](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/switching-between-accounts). +- Decision: ship guided PAT creation for both roles; do not present automatic + PAT issuance, website request replay, or a local account manager as part of + this product. A future App token is a separate credential and proposal. From ee61a379487e44e602c98db85be0b8450ab007f2 Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Thu, 24 Sep 2026 18:50:22 +0200 Subject: [PATCH 02/50] codex-setup-temporary-github-auth: guide setup and bot PAT creation --- README.md | 4 + build/cli/index.js | 300 +++++++++++++++++- docs/authentication.mdx | 28 ++ docs/configuration.mdx | 9 + docs/development/architecture.mdx | 11 + docs/how-to-use.mdx | 12 + .../operations/troubleshooting.mdx | 12 + specs/CATALOG.md | 16 +- specs/catalog.json | 15 + specs/guided-bot-pat-onboarding.md | 19 +- .../temporary-setup-operator-authorization.md | 39 ++- src/__tests__/cli.test.ts | 16 +- .../setup_pat_creation_url_policy.test.ts | 82 +++++ .../policies/setup_pat_creation_url_policy.ts | 75 +++++ .../ports/setup_pat_identity_ports.ts | 9 + ...ded_workflow_pat_identity_use_case.test.ts | 22 ++ ...y_guided_workflow_pat_identity_use_case.ts | 18 ++ src/cli/__tests__/setup_presenters.test.ts | 108 +++++++ src/cli/commands/setup.ts | 65 +++- src/cli/setup_credential_prompt_adapter.ts | 82 ++++- ...etup_github_identity_query_adapter.test.ts | 29 ++ .../setup_github_identity_query_adapter.ts | 45 +++ 22 files changed, 977 insertions(+), 39 deletions(-) create mode 100644 src/application/policies/__tests__/setup_pat_creation_url_policy.test.ts create mode 100644 src/application/policies/setup_pat_creation_url_policy.ts create mode 100644 src/application/ports/setup_pat_identity_ports.ts create mode 100644 src/application/usecases/setup/__tests__/verify_guided_workflow_pat_identity_use_case.test.ts create mode 100644 src/application/usecases/setup/verify_guided_workflow_pat_identity_use_case.ts create mode 100644 src/infrastructure/__tests__/setup_github_identity_query_adapter.test.ts create mode 100644 src/infrastructure/setup_github_identity_query_adapter.ts diff --git a/README.md b/README.md index 4c1e3a05b..812e6e263 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,10 @@ on the exact existing branch and push normal commits, but do not create, rename, delete, replace, or force-push managed branches. The setup PAT entered by the operator is separate from the workflow `PAT` Secret. +Interactive setup can guide creation of both via GitHub's prefilled PAT form: +the operator creates a temporary setup token, and the bot account creates the +persistent workflow token. GitHub handles account switching, 2FA, repository +selection, and final creation; Copilot never creates or revokes either token. Use `copilot setup --dry-run` to inspect the plan before making local or remote changes. See the complete [How to use](https://docs.page/vypdev/copilot/how-to-use) guide and [Authentication](https://docs.page/vypdev/copilot/authentication). diff --git a/build/cli/index.js b/build/cli/index.js index efe4de95c..f8a87a50e 100755 --- a/build/cli/index.js +++ b/build/cli/index.js @@ -47703,6 +47703,87 @@ function effectiveIssueFormLabels(configuration) { } +/***/ }), + +/***/ 54718: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.UnsupportedSetupPatLinkError = void 0; +exports.buildSetupPatCreationUrl = buildSetupPatCreationUrl; +const PAT_FORM = 'https://github.com/settings/personal-access-tokens/new'; +const QUERY_PERMISSIONS = { + repository: { + Metadata: 'metadata', + Contents: 'contents', + Secrets: 'secrets', + Variables: 'actions_variables', + Issues: 'issues', + Actions: 'actions', + Administration: 'administration', + Workflows: 'workflows', + 'Pull requests': 'pull_requests', + }, + organization: { + Secrets: 'organization_secrets', + Variables: 'organization_actions_variables', + 'Issue Types': 'issue_types', + Projects: 'organization_projects', + Members: 'members', + }, +}; +class UnsupportedSetupPatLinkError extends Error { + constructor(permissions) { + super(`GitHub's fine-grained PAT form cannot prefill: ${permissions.join(', ')}.`); + this.permissions = permissions; + this.name = 'UnsupportedSetupPatLinkError'; + } +} +exports.UnsupportedSetupPatLinkError = UnsupportedSetupPatLinkError; +/** Builds only documented GitHub form fields; never accepts credential material. */ +function buildSetupPatCreationUrl(input) { + if (!/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/.test(input.owner) + || !/^[A-Za-z0-9._-]{1,100}$/.test(input.repository) + || !Number.isInteger(input.expiresIn) + || input.expiresIn < 1 + || input.expiresIn > 366) { + throw new Error('Invalid PAT form owner, repository, or expiration.'); + } + const grants = new Map(); + const unsupported = []; + for (const item of input.requirements) { + if (item.role !== input.role) + throw new Error('PAT permission role does not match the requested form.'); + if (item.applicability !== 'required') + continue; + const key = QUERY_PERMISSIONS[item.scope][item.permission]; + if (!key || (key === 'metadata' && item.level !== 'read') + || (key === 'workflows' && item.level !== 'write')) { + unsupported.push(`${item.scope} ${item.permission} ${item.level}`); + continue; + } + if (grants.get(key) !== 'write') + grants.set(key, item.level); + } + if (unsupported.length > 0) + throw new UnsupportedSetupPatLinkError(unsupported); + const url = new URL(PAT_FORM); + url.searchParams.set('name', `Copilot ${input.role === 'setup' ? 'setup' : 'bot'} ${input.repository}`.slice(0, 40)); + url.searchParams.set('description', `Copilot ${input.role === 'setup' ? 'repository setup' : 'GitHub Action'} for ${input.owner}/${input.repository}`); + url.searchParams.set('target_name', input.owner); + url.searchParams.set('expires_in', String(input.expiresIn)); + for (const [key, level] of [...grants].sort(([left], [right]) => left.localeCompare(right))) { + url.searchParams.set(key, level); + } + const result = url.toString(); + if (result.length > 2048) + throw new Error('PAT form URL exceeds the supported terminal length; create the PAT manually.'); + return result; +} + + /***/ }), /***/ 6009: @@ -55707,6 +55788,32 @@ function unavailableRemoteConfiguration() { } +/***/ }), + +/***/ 35697: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.VerifyGuidedWorkflowPatIdentityUseCase = void 0; +const application_error_1 = __nccwpck_require__(75999); +/** Binds a guided runtime PAT to the bot account chosen before token entry. */ +class VerifyGuidedWorkflowPatIdentityUseCase { + constructor(identities) { + this.identities = identities; + } + async execute(expected, workflowToken) { + const actual = await this.identities.identify(workflowToken); + if (actual.id !== expected.id) { + throw new application_error_1.ApplicationError('authorization.credential-invalid', `The workflow PAT belongs to @${actual.login}, not the selected bot @${expected.login}. No Secret was written. Delete the unintended PAT in GitHub and create one as @${expected.login}.`); + } + return expected; + } +} +exports.VerifyGuidedWorkflowPatIdentityUseCase = VerifyGuidedWorkflowPatIdentityUseCase; + + /***/ }), /***/ 73572: @@ -65118,6 +65225,9 @@ const setup_credential_prompt_adapter_1 = __nccwpck_require__(93232); const setup_workflow_update_prompt_adapter_1 = __nccwpck_require__(84473); const setup_token_permission_presenter_1 = __nccwpck_require__(63206); const setup_token_permissions_composition_root_1 = __nccwpck_require__(64132); +const setup_pat_creation_url_policy_1 = __nccwpck_require__(54718); +const setup_github_identity_query_adapter_1 = __nccwpck_require__(56098); +const verify_guided_workflow_pat_identity_use_case_1 = __nccwpck_require__(35697); function registerSetupCommand(program) { program .command('setup') @@ -65158,6 +65268,7 @@ function registerSetupCommand(program) { const tokenPermissions = (0, setup_token_permissions_composition_root_1.createSetupTokenPermissionsUseCase)(); const workflowPrompt = new setup_workflow_update_prompt_adapter_1.SetupWorkflowUpdatePromptAdapter(terminal); const cwd = process.cwd(); + let setupMutationStarted = false; try { if (!options.nonInteractive && !terminal) { (0, logger_1.logError)('Interactive setup requires a terminal. Use --non-interactive with explicit configuration.'); @@ -65182,6 +65293,18 @@ function registerSetupCommand(program) { const setupPatPermissions = (0, setup_token_permission_policy_1.buildSetupPatPermissionRequirements)(); permissionPresenter.showRequirements('setup', setupPatPermissions); let token = (0, setup_files_1.getSetupToken)(cwd, options.token); + let setupPatAccount; + if (!token && !options.nonInteractive && !options.dryRun) { + try { + credentialPrompt.configureSetupPatGuide((0, setup_pat_creation_url_policy_1.buildSetupPatCreationUrl)({ + role: 'setup', owner: gitInfo.owner, repository: gitInfo.repo, expiresIn: 1, + requirements: setupPatPermissions, + })); + } + catch { + (0, logger_1.logInfo)('A guided setup PAT link is unavailable for this repository or permission set. Enter a manually created PAT using the table above.'); + } + } if (!token && !options.nonInteractive && !options.dryRun) token = await credentialPrompt.requestSetupPat(); if (!token && !options.dryRun) { @@ -65205,8 +65328,17 @@ function registerSetupCommand(program) { || (permissionReport.confirmationRequired && await credentialPrompt.confirmUnverifiableTokenPermissions(permissionReport)); if (!permissionAccepted || permissionReport.identityStatus !== 'valid') { + if (credentialPrompt.usedGuidedSetupPat) + credentialPrompt.showUpdatedSetupPatLink((0, setup_pat_creation_url_policy_1.buildSetupPatCreationUrl)({ + role: 'setup', owner: gitInfo.owner, repository: gitInfo.repo, expiresIn: 1, + requirements: setupPatPermissions, + }), 'bootstrap'); throw new application_error_1.ApplicationError('authorization.credential-invalid', 'The setup PAT has missing or unconfirmed required access. Grant or explicitly confirm the permissions shown above and retry.'); } + if (!await credentialPrompt.confirmGuidedSetupAccount(permissionReport.account)) { + throw new application_error_1.ApplicationError('authorization.credential-invalid', 'The setup PAT belongs to an unintended account. Revoke it in GitHub and retry with the correct account.'); + } + setupPatAccount = permissionReport.account; } (0, logger_1.logInfo)(options.dryRun ? '🧭 Building a dry-run setup plan...' : '🧭 Building your setup plan...'); const auditConfiguredSetupPat = async (configuration, remoteConfiguration) => { @@ -65223,6 +65355,11 @@ function registerSetupCommand(program) { || (permissionReport.confirmationRequired && await credentialPrompt.confirmUnverifiableTokenPermissions(permissionReport)); if (!permissionAccepted || permissionReport.identityStatus !== 'valid') { + if (credentialPrompt.usedGuidedSetupPat) + credentialPrompt.showUpdatedSetupPatLink((0, setup_pat_creation_url_policy_1.buildSetupPatCreationUrl)({ + role: 'setup', owner: gitInfo.owner, repository: gitInfo.repo, expiresIn: 1, + requirements: configuredSetupPatPermissions, + }), 'final'); return { status: 'blocked', errors: [ 'The setup PAT has missing or unconfirmed access required by the approved setup plan. Grant or explicitly confirm the permissions shown above and retry.', ] }; @@ -65278,6 +65415,22 @@ function registerSetupCommand(program) { (0, logger_1.logInfo)('✅ Dry run complete. No files or GitHub resources were changed.'); return; } + const workflowTokenPermissions = (0, setup_token_permission_policy_1.buildWorkflowPatPermissionRequirements)(configuration, remoteConfiguration); + const githubIdentities = new setup_github_identity_query_adapter_1.SetupGithubIdentityQueryAdapter(); + if (!options.nonInteractive && !options.workflowPat && !options.secret?.PAT) { + try { + const workflowPatGuide = (0, setup_pat_creation_url_policy_1.buildSetupPatCreationUrl)({ + role: 'workflow', owner: gitInfo.owner, repository: gitInfo.repo, expiresIn: 90, + requirements: workflowTokenPermissions, + }); + credentialPrompt.configureWorkflowPatGuide(workflowPatGuide, login => githubIdentities.resolve(login, token ?? '')); + } + catch (error) { + if (!(error instanceof setup_pat_creation_url_policy_1.UnsupportedSetupPatLinkError)) + throw error; + (0, logger_1.logInfo)('A guided fine-grained bot PAT link is unavailable for one or more required permissions. Use the permission table and manual path; review whether a classic PAT is required for this plan.'); + } + } const credentials = await (0, setup_credentials_composition_root_1.createSetupCredentialsUseCase)(credentialPrompt, permissionPresenter).collect({ owner: gitInfo.owner, repository: gitInfo.repo, @@ -65287,15 +65440,34 @@ function registerSetupCommand(program) { secretStoragePolicy: configuration.storage.secrets, ref: configuration.repository.mainBranch, remoteConfiguration, - workflowTokenPermissions: (0, setup_token_permission_policy_1.buildWorkflowPatPermissionRequirements)(configuration, remoteConfiguration), + workflowTokenPermissions, }); + const guidedBotIdentity = credentialPrompt.guidedWorkflowBotIdentity; + if (guidedBotIdentity && credentials.collection.workflowPat) { + const verifiedBot = await new verify_guided_workflow_pat_identity_use_case_1.VerifyGuidedWorkflowPatIdentityUseCase(githubIdentities) + .execute(guidedBotIdentity, credentials.collection.workflowPat.value); + (0, logger_1.logInfo)(`✅ Workflow PAT owner verified as @${verifiedBot.login} (GitHub account ID ${verifiedBot.id}).`); + if (setupPatAccount?.toLowerCase() === verifiedBot.login.toLowerCase()) { + (0, logger_1.logInfo)('The workflow PAT and setup PAT use the same GitHub account. If this account authors PRs, bot-generated events and guarded self-approval may not behave as intended; use a dedicated bot account where required.'); + } + } (0, logger_1.logInfo)('⚙️ Applying the approved setup plan...'); const params = (0, setup_policy_1.buildSetupParams)(options, gitInfo, token ?? '', configuration, credentials.collection, approvedWorkflowFiles, remoteConfiguration); if (!params) return; - await (0, local_action_1.runLocalAction)(params); + setupMutationStarted = true; + const actionResults = await (0, local_action_1.runLocalAction)(params); + if (actionResults.some(actionResult => !actionResult.success || actionResult.errors.length > 0)) { + (0, logger_1.logInfo)('Setup reported failures or partial completion. If a bot PAT was supplied, its Secret may already have been written; inspect the result and GitHub Secret name/scope before retrying or revoking it.'); + process.exitCode = 1; + } } catch (error) { + if (credentialPrompt.guidedWorkflowBotIdentity) { + (0, logger_1.logInfo)(setupMutationStarted + ? 'Setup may be partially applied. Inspect the GitHub Secret before deleting or replacing the bot PAT.' + : 'No setup mutation started. If you generated an unused bot PAT in GitHub, delete it there; Copilot cannot revoke it.'); + } if (error instanceof setup_credential_prompt_adapter_1.SetupTerminalCancelledError) { (0, logger_1.logInfo)('Setup cancelled. No changes were applied.'); process.exitCode = 130; @@ -65305,6 +65477,7 @@ function registerSetupCommand(program) { process.exitCode = 1; } finally { + credentialPrompt.showSetupPatCleanupReminder(); terminal?.close(); } }); @@ -65964,10 +66137,48 @@ class SetupCredentialPromptAdapter { this.terminal = terminal; this.credentialValues = credentialValues; this.confirmUnverifiableWritePermissions = confirmUnverifiableWritePermissions; + this.guidedSetup = false; + } + configureSetupPatGuide(url) { this.setupPatGuide = url; } + get usedGuidedSetupPat() { return this.guidedSetup; } + configureWorkflowPatGuide(url, resolveIdentity) { + this.workflowPatGuide = url; + this.resolveBotIdentity = resolveIdentity; + } + get guidedWorkflowBotIdentity() { return this.guidedBotIdentity; } + async confirmGuidedSetupAccount(account) { + if (!this.guidedSetup || !this.terminal) + return true; + if (!account || !/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/.test(account)) + return false; + console.log(`GitHub authenticated the setup PAT as @${account}.`); + return (await this.readChoice('Is this the account you intended to configure with?', ['yes', 'no'], 'yes')) === 'yes'; + } + showSetupPatCleanupReminder() { + if (!this.guidedSetup) + return; + console.log((0, setup_prompt_rendering_1.renderBox)('The setup PAT was not revoked automatically. After setup finishes or is cancelled, delete it in GitHub → Settings → Developer settings → Personal access tokens. Ending this process does not remove the token from GitHub.', 'Revoke temporary setup PAT', 33)); + console.log('https://github.com/settings/personal-access-tokens'); + } + showUpdatedSetupPatLink(url, stage) { + if (!this.guidedSetup) + return; + console.log((0, setup_prompt_rendering_1.renderBox)(stage === 'bootstrap' + ? 'The setup PAT did not pass the initial access check; no setup plan has been applied. Review its grants in GitHub or create a replacement with this link, then rerun. Select only the intended repository in GitHub.' + : 'The selected plan requires access this PAT did not prove; no plan mutation has started. Update its grants in GitHub or create a replacement with this link, then rerun. Select only the intended repository in GitHub.', stage === 'bootstrap' ? 'Setup PAT access needs attention' : 'Setup PAT permissions changed', 33)); + console.log(url); } async requestSetupPat() { if (!this.terminal) return undefined; + if (this.setupPatGuide) { + this.guidedSetup = (await this.readChoice('How would you like to provide the setup PAT?', ['guided link', 'manual PAT'], 'guided link')) === 'guided link'; + if (this.guidedSetup) { + console.log((0, setup_prompt_rendering_1.renderBox)('Provisional link: Open this GitHub link in your browser, sign in as the account configuring this repository, complete any 2FA or SSO, and review the prefilled fine-grained permissions. GitHub owns token creation; Copilot never handles your web session. Select ONLY this repository manually. The permissions may need updating after the setup questionnaire.', 'Create setup PAT in GitHub', 33)); + console.log(this.setupPatGuide); + console.log('Copy the one-time token from GitHub and paste it below. It is hidden and used only for this setup run.'); + } + } console.log((0, setup_prompt_rendering_1.renderBox)('Enter a GitHub setup PAT. It is used in memory for this run only and is never stored. The workflow PAT is a different bot-account token and is requested separately.', 'Setup PAT', 33)); return this.readSecret('Setup PAT'); } @@ -66007,9 +66218,32 @@ class SetupCredentialPromptAdapter { console.log((0, setup_prompt_rendering_1.renderBox)('The workflow PAT is not the setup PAT. Runtime credentials are stored remotely as GitHub Actions Secrets. GitHub never reveals existing Secret values; health is checked through the repository workflow.', 'Workflow credentials', 33)); console.log(`Credential options: ${requirements.map((requirement) => requirement.name).join(', ')}`); } - requestWorkflowPat(requirement, current) { + async requestWorkflowPat(requirement, current) { + if (this.terminal && !this.credentialValues[requirement.name]?.trim() && this.workflowPatGuide) { + const guided = (await this.readChoice('How would you like to provide the bot workflow PAT?', ['guided link', 'manual PAT'], 'guided link')) === 'guided link'; + if (guided) { + const login = await this.readBotLogin(); + const identity = await this.resolveBotIdentity(login); + this.guidedBotIdentity = identity; + console.log(`Expected bot account resolved: @${identity.login} (GitHub account ID ${identity.id}).`); + console.log((0, setup_prompt_rendering_1.renderBox)(`Open this link in a separate/private browser session, sign in as @${login} (the bot account), and complete its 2FA or SSO. Review every grant and select ONLY the intended repository manually. GitHub creates the PAT; Copilot does not store bot web credentials. The suggested expiry is 90 days—renew the token and update the Actions Secret before then.`, 'Create bot PAT in GitHub', 33)); + console.log(this.workflowPatGuide); + console.log('Copy the one-time bot token and paste it below. It will be validated before any Secret is written.'); + } + } return this.requestSecretForRequirement(requirement, current, 'workflow PAT owned by the bot account'); } + async readBotLogin() { + while (true) { + const result = await this.terminal.readText('Expected GitHub bot login (without @): '); + if (result.kind !== 'value') + throw new SetupTerminalCancelledError(); + const login = result.value.trim(); + if (/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/.test(login)) + return login; + console.log((0, setup_prompt_rendering_1.color)('Enter a valid GitHub account login.', 33)); + } + } requestApiKey(requirement, current) { return this.requestSecretForRequirement(requirement, current, `${requirement.provider ?? 'provider'} API key`); } @@ -82549,6 +82783,66 @@ function safeMessage(error) { } +/***/ }), + +/***/ 56098: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.SetupGithubIdentityQueryAdapter = void 0; +const LOGIN_PATTERN = /^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/; +class SetupGithubIdentityQueryAdapter { + constructor(fetcher = fetch, timeoutMs = 10000) { + this.fetcher = fetcher; + this.timeoutMs = timeoutMs; + } + async resolve(login, setupToken) { + if (!LOGIN_PATTERN.test(login)) + throw new Error('Enter a valid GitHub bot account login.'); + return this.request(`https://api.github.com/users/${encodeURIComponent(login)}`, setupToken); + } + identify(token) { + return this.request('https://api.github.com/user', token); + } + async request(url, token) { + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), this.timeoutMs); + try { + const response = await this.fetcher(url, { + method: 'GET', + headers: { + Authorization: `Bearer ${token}`, + Accept: 'application/vnd.github+json', + 'X-GitHub-Api-Version': '2022-11-28', + }, + signal: controller.signal, + }); + if (!response.ok) + throw new Error('GitHub could not verify the selected bot account or token identity. No Secret was written.'); + const body = await response.json(); + if (!body || typeof body !== 'object' || Array.isArray(body)) + throw new Error('GitHub returned an invalid identity. No Secret was written.'); + const { id, login } = body; + if (typeof id !== 'number' || !Number.isSafeInteger(id) || id <= 0 || typeof login !== 'string' || !LOGIN_PATTERN.test(login)) { + throw new Error('GitHub returned an invalid identity. No Secret was written.'); + } + return { id, login }; + } + catch (error) { + if (error instanceof Error && error.message.includes('No Secret was written.')) + throw error; + throw Object.assign(new Error('GitHub identity verification failed. Check network access and retry; no Secret was written.'), { cause: error }); + } + finally { + clearTimeout(timeout); + } + } +} +exports.SetupGithubIdentityQueryAdapter = SetupGithubIdentityQueryAdapter; + + /***/ }), /***/ 1489: diff --git a/docs/authentication.mdx b/docs/authentication.mdx index f9f7e7d9e..271b28fb3 100644 --- a/docs/authentication.mdx +++ b/docs/authentication.mdx @@ -12,6 +12,34 @@ For [guarded PR approval](/pull-requests/guarded-approval), this same runtime PA The setup PAT and workflow PAT may have different owners and permissions. Do not paste the workflow PAT into the setup prompt unless you intentionally want the same token to perform both roles. +## Assisted creation in the terminal + +When `copilot setup` needs a PAT interactively, it offers a guided link (the +default) or manual entry. The setup link contains only bootstrap permissions +known before the questionnaire; it is **provisional**. If the final plan needs +additional grants, setup stops before applying it and prints a corrected link. +Update the PAT in GitHub or create a replacement, then rerun setup. Guided +setup shows the account returned by GitHub and asks you to confirm it. + +After the plan, the bot link uses the selected workflow permissions. Enter the +expected bot login first: setup resolves its GitHub numeric ID, then checks the +PAT's own `/user` identity against that ID before any Secret write. A manual or +non-interactive PAT retains the existing permission audit but does **not** gain +this extra identity binding. A wrong bot account blocks installation. + +Both links use GitHub's [documented fine-grained PAT form](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens). +They do not sign you in, complete 2FA, generate or revoke a token, or choose +an individual repository. Check the active browser account, select only the +target repository, and review the final GitHub form. A guided setup PAT uses a +one-day suggested expiry; delete it yourself in [GitHub PAT Settings](https://github.com/settings/personal-access-tokens) +afterward. The bot PAT uses a 90-day suggested expiry, may be shortened by +organization policy, and remains in Actions Secret `PAT`; arrange renewal +before it expires. If guarded approval requires `Checks`, GitHub's fine-grained +PAT cannot prefill or provide that permission. Setup omits the guided bot link +for that plan; review a compatible manually created credential and the +permission audit instead. Existing command-line token flags also remain, but +putting a PAT in a command can expose it in shell history or process listings. + ## Permission tables in `copilot setup` Immediately before each hidden PAT prompt, interactive setup prints a diff --git a/docs/configuration.mdx b/docs/configuration.mdx index 010db4746..eb7557110 100644 --- a/docs/configuration.mdx +++ b/docs/configuration.mdx @@ -189,6 +189,15 @@ The immutable questionnaire first inspects the repository and reports repository Organization storage is available only for organization-owned repositories and requires organization Actions permissions on the setup PAT. If only one class should be global, set that class to `organization` and leave the other at `repository`. `--skip-secrets` and `--skip-variables` disable their respective setup operations without changing the other class. +Interactive PAT guidance does not add configuration keys: it is a one-run +choice at each hidden prompt. The initial setup-PAT form link contains only +bootstrap grants and may need correction after the questionnaire. The bot-PAT +link is built from the final workflow permission policy and suggests a 90-day +expiry; the bot account owner must renew it and replace Secret `PAT` before +expiration. Manual and non-interactive token inputs keep their existing +precedence and validation. See [authentication](/authentication) for the +GitHub-owned creation, account verification, and deletion steps. + `--non-interactive` constructs no terminal and resolves only defaults, config, flags, and explicit external inputs. `--yes` approves the final plan but never invents a missing token, credential, target, or storage prerequisite. There is diff --git a/docs/development/architecture.mdx b/docs/development/architecture.mdx index 89f35034c..45017f580 100644 --- a/docs/development/architecture.mdx +++ b/docs/development/architecture.mdx @@ -160,6 +160,17 @@ The setup policy is intentionally split by responsibility: - `setup_resource_provisioning.ts` owns grouping and port calls for Variables and Secrets. +Assisted PAT creation uses the existing permission policy as its only grant +source. The pure `setup_pat_creation_url_policy.ts` maps required grants to +documented GitHub form parameters and rejects unsupported grants; it never +receives token material. `setup.ts` wires the terminal choice and hidden input +to that policy. In guided bot mode, the identity query adapter resolves the +chosen login through GitHub and identifies the supplied PAT through `/user`; +the application use case compares immutable numeric IDs before local setup +can write Secrets. GitHub owns the browser session, 2FA, token generation, and +deletion. Manual and unattended inputs retain the prior audit without the new +bot-ID assertion. + PAT permission validation follows the same dependency rule. The application `SetupTokenPermissionsUseCase` validates identity before invoking the narrow `SetupTokenPermissionQueryPort`; the infrastructure adapter performs only safe diff --git a/docs/how-to-use.mdx b/docs/how-to-use.mdx index f56f11790..12c07b291 100644 --- a/docs/how-to-use.mdx +++ b/docs/how-to-use.mdx @@ -38,6 +38,18 @@ If the checkout does not include the compiled `build/` folder (e.g. it is gitign Once installed, the `copilot` command is available globally. Repository-dependent commands such as `copilot setup`, `copilot doctor`, `copilot check-progress`, `copilot think`, and `copilot do` must be run **from the root of the target repository**. The `copilot upgrade`, `copilot --version`, and help flows can run from any directory. Commands that access GitHub accept `--token` or `PERSONAL_ACCESS_TOKEN` from the environment. `copilot setup` and `copilot doctor` securely prompt for the setup PAT when run interactively; no `.env` file is read or created. `copilot setup --dry-run` is the only setup mode that can run without a token. See [CLI commands](/single-actions/workflow-and-cli). +Interactive `copilot setup` offers a guided GitHub link or manual entry for each +PAT. The first link prepares a short-lived **setup PAT** for the person +configuring the repository; the second, after the setup plan, prepares the +**workflow PAT** for the bot account. Open each link in the appropriate GitHub +account, complete GitHub's sign-in/2FA, **select only the intended repository** +on the form, review the grants, and paste the generated value into the hidden +terminal prompt. The links prefill fields; they neither create a PAT nor select +an individual repository. The setup PAT is suggested for one day and must be +deleted by you in GitHub after the run. The bot PAT is suggested for 90 days, +remains active as Actions Secret `PAT`, and needs renewal before expiry. See +[authentication](/authentication) for permission and recovery details. + If you previously installed Copilot from a local checkout, installing the published package with pnpm switches the same `copilot` command to the published package. Check which executable and package are active: diff --git a/docs/security-operations/operations/troubleshooting.mdx b/docs/security-operations/operations/troubleshooting.mdx index decf97f22..a407f7b2c 100644 --- a/docs/security-operations/operations/troubleshooting.mdx +++ b/docs/security-operations/operations/troubleshooting.mdx @@ -9,6 +9,18 @@ If guarded PR approval is missing, run `copilot doctor` and inspect the ordered This guide helps you resolve common issues you might encounter while using Copilot. Expand the section that matches your problem. +If the guided setup PAT belongs to the wrong account, decline the account +confirmation, delete the unintended PAT in [GitHub PAT Settings](https://github.com/settings/personal-access-tokens), +and rerun setup in the correct browser account. If the final permission table +requires more than the provisional link, use the corrected link printed by +setup and rerun; no approved setup mutation has started. A guided bot PAT +created while signed into another account fails the numeric-ID check before +the Secret is written. Delete that unused PAT and create one as the chosen bot. +If setup fails after applying changes, inspect the Actions Secret name and +scope before revoking or replacing the bot PAT: it may already be active. +Cancelling setup never revokes either PAT. Delete an unused setup PAT in GitHub; +renew an installed bot PAT before its suggested 90-day expiry. + **Setup cancellation:** `Ctrl-C` or end-of-input intentionally exits 130 and diff --git a/specs/CATALOG.md b/specs/CATALOG.md index dd655c277..1eb2b5483 100644 --- a/specs/CATALOG.md +++ b/specs/CATALOG.md @@ -17,8 +17,8 @@ debt or convert unknown historic intent into a design decision. | `execution-lifecycle` | Implemented | Shared GitHub Action lifecycle from event admission through durable user-facing results | [Execution admission, queueing, routing, and result publication](./execution-admission-queue-and-publication.md) + 3 companion | 84 paths · 2026-09-16 | | `architecture-quality-hardening` | Implemented | Close verified concurrency, error-contract, context-coupling, fan-out, setup/doctor, and provider-policy risks in dependency order | [Architecture quality and scalability hardening](./architecture-quality-and-scalability-hardening.md) + 1 companion | 72 paths · 2026-09-16 | | `setup-and-doctor` | Implemented | Plan, validate, provision, and audit a repository installation without exposing credentials | [Setup, configuration, credentials, and doctor](./setup-configuration-credentials-and-doctor.md) + 2 companion | 83 paths · 2026-09-24 | -| `guided-bot-pat-onboarding` | Proposed | Guide creation of the persistent workflow PAT using GitHub's official form, verify bot identity and grants, and install the approved Actions Secret | [Guided bot PAT onboarding](./guided-bot-pat-onboarding.md) | 12 paths · 2026-09-24 | -| `temporary-setup-operator-authorization` | Proposed | Guide creation of the one-run operator PAT through GitHub's official form, verify final setup access, and report user-owned deletion accurately | [Assisted setup PAT creation](./temporary-setup-operator-authorization.md) | 12 paths · 2026-09-24 | +| `guided-bot-pat-onboarding` | Proposed | Guide creation of the persistent workflow PAT using GitHub's official form, verify bot identity and grants, and install the approved Actions Secret | [Guided bot PAT onboarding](./guided-bot-pat-onboarding.md) | 22 paths · 2026-09-24 | +| `temporary-setup-operator-authorization` | Proposed | Guide creation of the one-run operator PAT through GitHub's official form, verify final setup access, and report user-owned deletion accurately | [Assisted setup PAT creation](./temporary-setup-operator-authorization.md) | 17 paths · 2026-09-24 | | `issue-start-and-sdd-readiness` | Implemented | Start every admitted issue with one explicit signal and publish a validated SDD before eligible Action-managed branch work | [Uniform issue start and pre-branch SDD readiness](./issue-start-and-branch-readiness.md) + 1 companion | 51 paths · 2026-09-17 | | `managed-issue-lifecycle` | As-built baseline | Convert typed issues into traceable work branches, project state, and lifecycle state | [Managed issue and branch lifecycle](./managed-issue-and-branch-lifecycle.md) | 31 paths · 2026-09-17 | | `comment-automation` | Implemented | Admit only explicit commands or exact mentions, then route them while protecting repository mutations | [Comment automation and authorization](./comment-automation-and-authorization.md) | 61 paths · 2026-09-21 | @@ -117,9 +117,9 @@ debt or convert unknown historic intent into a design decision. - Specifications: [`specs/guided-bot-pat-onboarding.md`](./guided-bot-pat-onboarding.md) - Workflows: Not applicable for this capability. - Entrypoints: [`src/cli/commands/setup.ts`](../src/cli/commands/setup.ts) -- Core code: [`src/application/policies/setup_token_permission_policy.ts`](../src/application/policies/setup_token_permission_policy.ts) · [`src/application/usecases/setup/setup_credentials_use_case.ts`](../src/application/usecases/setup/setup_credentials_use_case.ts) · [`src/cli/setup_credential_prompt_adapter.ts`](../src/cli/setup_credential_prompt_adapter.ts) · [`src/data/repository/repository_variables_repository.ts`](../src/data/repository/repository_variables_repository.ts) -- Tests: [`src/application/policies/__tests__/setup_token_permission_policy.test.ts`](../src/application/policies/__tests__/setup_token_permission_policy.test.ts) · [`src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts) · [`src/cli/__tests__/setup_presenters.test.ts`](../src/cli/__tests__/setup_presenters.test.ts) · [`src/data/repository/__tests__/repository_variables_repository.test.ts`](../src/data/repository/__tests__/repository_variables_repository.test.ts) -- User documentation: [`docs/authentication.mdx`](../docs/authentication.mdx) · [`docs/how-to-use.mdx`](../docs/how-to-use.mdx) · [`docs/security-operations/operations/troubleshooting.mdx`](../docs/security-operations/operations/troubleshooting.mdx) +- Core code: [`src/application/policies/setup_token_permission_policy.ts`](../src/application/policies/setup_token_permission_policy.ts) · [`src/application/policies/setup_pat_creation_url_policy.ts`](../src/application/policies/setup_pat_creation_url_policy.ts) · [`src/application/ports/setup_pat_identity_ports.ts`](../src/application/ports/setup_pat_identity_ports.ts) · [`src/application/usecases/setup/verify_guided_workflow_pat_identity_use_case.ts`](../src/application/usecases/setup/verify_guided_workflow_pat_identity_use_case.ts) · [`src/infrastructure/setup_github_identity_query_adapter.ts`](../src/infrastructure/setup_github_identity_query_adapter.ts) · [`src/application/usecases/setup/setup_credentials_use_case.ts`](../src/application/usecases/setup/setup_credentials_use_case.ts) · [`src/cli/setup_credential_prompt_adapter.ts`](../src/cli/setup_credential_prompt_adapter.ts) · [`src/data/repository/repository_variables_repository.ts`](../src/data/repository/repository_variables_repository.ts) +- Tests: [`src/application/policies/__tests__/setup_token_permission_policy.test.ts`](../src/application/policies/__tests__/setup_token_permission_policy.test.ts) · [`src/application/policies/__tests__/setup_pat_creation_url_policy.test.ts`](../src/application/policies/__tests__/setup_pat_creation_url_policy.test.ts) · [`src/application/usecases/setup/__tests__/verify_guided_workflow_pat_identity_use_case.test.ts`](../src/application/usecases/setup/__tests__/verify_guided_workflow_pat_identity_use_case.test.ts) · [`src/infrastructure/__tests__/setup_github_identity_query_adapter.test.ts`](../src/infrastructure/__tests__/setup_github_identity_query_adapter.test.ts) · [`src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts) · [`src/cli/__tests__/setup_presenters.test.ts`](../src/cli/__tests__/setup_presenters.test.ts) · [`src/data/repository/__tests__/repository_variables_repository.test.ts`](../src/data/repository/__tests__/repository_variables_repository.test.ts) +- User documentation: [`README.md`](../README.md) · [`docs/authentication.mdx`](../docs/authentication.mdx) · [`docs/how-to-use.mdx`](../docs/how-to-use.mdx) · [`docs/configuration.mdx`](../docs/configuration.mdx) · [`docs/development/architecture.mdx`](../docs/development/architecture.mdx) · [`docs/security-operations/operations/troubleshooting.mdx`](../docs/security-operations/operations/troubleshooting.mdx) ### `temporary-setup-operator-authorization` — Assisted setup PAT creation @@ -128,9 +128,9 @@ debt or convert unknown historic intent into a design decision. - Specifications: [`specs/temporary-setup-operator-authorization.md`](./temporary-setup-operator-authorization.md) - Workflows: Not applicable for this capability. - Entrypoints: [`src/cli/commands/setup.ts`](../src/cli/commands/setup.ts) -- Core code: [`src/cli/setup_credential_prompt_adapter.ts`](../src/cli/setup_credential_prompt_adapter.ts) · [`src/application/usecases/setup/setup_token_permissions_use_case.ts`](../src/application/usecases/setup/setup_token_permissions_use_case.ts) · [`src/infrastructure/setup_token_permission_query_adapter.ts`](../src/infrastructure/setup_token_permission_query_adapter.ts) · [`src/utils/setup_files.ts`](../src/utils/setup_files.ts) -- Tests: [`src/cli/__tests__/setup_presenters.test.ts`](../src/cli/__tests__/setup_presenters.test.ts) · [`src/application/usecases/setup/__tests__/setup_token_permissions_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_token_permissions_use_case.test.ts) · [`src/infrastructure/__tests__/setup_token_permission_query_adapter.test.ts`](../src/infrastructure/__tests__/setup_token_permission_query_adapter.test.ts) · [`src/utils/__tests__/setup_files.test.ts`](../src/utils/__tests__/setup_files.test.ts) -- User documentation: [`docs/how-to-use.mdx`](../docs/how-to-use.mdx) · [`docs/authentication.mdx`](../docs/authentication.mdx) · [`docs/security-operations/operations/troubleshooting.mdx`](../docs/security-operations/operations/troubleshooting.mdx) +- Core code: [`src/cli/setup_credential_prompt_adapter.ts`](../src/cli/setup_credential_prompt_adapter.ts) · [`src/application/policies/setup_pat_creation_url_policy.ts`](../src/application/policies/setup_pat_creation_url_policy.ts) · [`src/application/usecases/setup/setup_token_permissions_use_case.ts`](../src/application/usecases/setup/setup_token_permissions_use_case.ts) · [`src/infrastructure/setup_token_permission_query_adapter.ts`](../src/infrastructure/setup_token_permission_query_adapter.ts) · [`src/utils/setup_files.ts`](../src/utils/setup_files.ts) +- Tests: [`src/cli/__tests__/setup_presenters.test.ts`](../src/cli/__tests__/setup_presenters.test.ts) · [`src/application/policies/__tests__/setup_pat_creation_url_policy.test.ts`](../src/application/policies/__tests__/setup_pat_creation_url_policy.test.ts) · [`src/application/usecases/setup/__tests__/setup_token_permissions_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_token_permissions_use_case.test.ts) · [`src/infrastructure/__tests__/setup_token_permission_query_adapter.test.ts`](../src/infrastructure/__tests__/setup_token_permission_query_adapter.test.ts) · [`src/utils/__tests__/setup_files.test.ts`](../src/utils/__tests__/setup_files.test.ts) +- User documentation: [`README.md`](../README.md) · [`docs/how-to-use.mdx`](../docs/how-to-use.mdx) · [`docs/authentication.mdx`](../docs/authentication.mdx) · [`docs/configuration.mdx`](../docs/configuration.mdx) · [`docs/development/architecture.mdx`](../docs/development/architecture.mdx) · [`docs/security-operations/operations/troubleshooting.mdx`](../docs/security-operations/operations/troubleshooting.mdx) ### `issue-start-and-sdd-readiness` — Uniform issue start and pre-branch SDD readiness diff --git a/specs/catalog.json b/specs/catalog.json index 3ae63b295..8f55a2009 100644 --- a/specs/catalog.json +++ b/specs/catalog.json @@ -760,19 +760,29 @@ ], "code": [ "src/application/policies/setup_token_permission_policy.ts", + "src/application/policies/setup_pat_creation_url_policy.ts", + "src/application/ports/setup_pat_identity_ports.ts", + "src/application/usecases/setup/verify_guided_workflow_pat_identity_use_case.ts", + "src/infrastructure/setup_github_identity_query_adapter.ts", "src/application/usecases/setup/setup_credentials_use_case.ts", "src/cli/setup_credential_prompt_adapter.ts", "src/data/repository/repository_variables_repository.ts" ], "tests": [ "src/application/policies/__tests__/setup_token_permission_policy.test.ts", + "src/application/policies/__tests__/setup_pat_creation_url_policy.test.ts", + "src/application/usecases/setup/__tests__/verify_guided_workflow_pat_identity_use_case.test.ts", + "src/infrastructure/__tests__/setup_github_identity_query_adapter.test.ts", "src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts", "src/cli/__tests__/setup_presenters.test.ts", "src/data/repository/__tests__/repository_variables_repository.test.ts" ], "documentation": [ + "README.md", "docs/authentication.mdx", "docs/how-to-use.mdx", + "docs/configuration.mdx", + "docs/development/architecture.mdx", "docs/security-operations/operations/troubleshooting.mdx" ] }, @@ -792,19 +802,24 @@ ], "code": [ "src/cli/setup_credential_prompt_adapter.ts", + "src/application/policies/setup_pat_creation_url_policy.ts", "src/application/usecases/setup/setup_token_permissions_use_case.ts", "src/infrastructure/setup_token_permission_query_adapter.ts", "src/utils/setup_files.ts" ], "tests": [ "src/cli/__tests__/setup_presenters.test.ts", + "src/application/policies/__tests__/setup_pat_creation_url_policy.test.ts", "src/application/usecases/setup/__tests__/setup_token_permissions_use_case.test.ts", "src/infrastructure/__tests__/setup_token_permission_query_adapter.test.ts", "src/utils/__tests__/setup_files.test.ts" ], "documentation": [ + "README.md", "docs/how-to-use.mdx", "docs/authentication.mdx", + "docs/configuration.mdx", + "docs/development/architecture.mdx", "docs/security-operations/operations/troubleshooting.mdx" ] }, diff --git a/specs/guided-bot-pat-onboarding.md b/specs/guided-bot-pat-onboarding.md index 720c44e82..e1d46e172 100644 --- a/specs/guided-bot-pat-onboarding.md +++ b/specs/guided-bot-pat-onboarding.md @@ -1,6 +1,6 @@ # Guided Bot PAT Onboarding -- Status: Draft — guided form is viable; runtime expiry and identity UX need review +- Status: Draft — guided implementation in progress; controlled GitHub UX and full test budget remain unverified - Date: 2026-09-24 - Catalog capability ID: `guided-bot-pat-onboarding` - Last verified: Not applicable; prospective change @@ -8,7 +8,7 @@ - Scope: guide creation and installation of the workflow/bot PAT when operator and bot are different GitHub accounts - Related issues/PRs: none; no Action dogfooding for this design - Required review gates: product UX, architecture, testing, documentation, credential security, GitHub form compatibility -- Open decisions blocking readiness: runtime PAT expiration default/rotation owner; behavior when organization PAT approval is pending; supported non-interactive identity assertion +- Open decisions blocking readiness: controlled GitHub UX, organization approval evidence, non-interactive identity extension, and full test-budget evidence ## 1. Executive summary @@ -259,16 +259,16 @@ or to overwrite the Secret without a new approved value. |---|---|---|---|---| | PAT creation help | interactive choice | guided | `guided`, `manual` | one setup run; not saved | | Operator PAT expiry | integer days | `1` for a one-run token | defined by companion operator SDD | link only | -| Bot PAT expiry | integer days | proposed `90`, subject to review and org policy | 1–366 per GitHub, no `none` in generated link | link only; not a new config Secret | +| Bot PAT expiry | integer days | `90`, with bot owner responsible for renewal before expiry | 1–366 per GitHub, no `none` in generated link | link only; not a new config Secret | | Expected bot | GitHub login and resolved ID | explicit selection | one valid GitHub user | one run; non-secret display | | Secret scope | existing setup storage policy | repository | repository or organization as already supported | approved setup plan | The final runtime permission policy is the sole source of URL grants. The existing `--workflow-pat`/`--secret PAT=...` values override interactive input and retain their current permission validation; they cannot silently enter -guided mode without an expected bot identity. The bot expiry default is -an open product decision; it cannot be shipped as a fixed value until rotation -ownership is documented. No link may select `none` silently. Invalid owner, +guided mode without an expected bot identity. The bot account owner must renew +the suggested 90-day PAT and replace Secret `PAT` before expiration; the +organization may impose a shorter limit. No link may select `none` silently. Invalid owner, permission name/level, URL length, account, or scope blocks link generation. There is no migration of existing PAT Secrets or account state. Role separation, no embedded secret, and exact identity checking within guided mode are not @@ -551,3 +551,10 @@ PAT, and the bot PAT remains active after setup. owner rather than trusting the browser or local CLI account. Legacy paths keep their current checks pending a migration. Automatic bot PAT creation needs a future supported GitHub API and is not claimed here. +- Implementation snapshot (2026-09-24): guided/manual bot prompt, final-plan + URL, expected login resolution before token entry, numeric-ID comparison + before Secret mutation, and a 90-day suggested expiry are implemented + locally. Unsupported `Checks` in guarded plans suppresses the fine-grained + link and directs users to manual credential compatibility review. Human + two-account/2FA acceptance, organization approval evidence, and the full + numeric test budget remain open review gates. diff --git a/specs/temporary-setup-operator-authorization.md b/specs/temporary-setup-operator-authorization.md index 1d111d5d2..be58c52fb 100644 --- a/specs/temporary-setup-operator-authorization.md +++ b/specs/temporary-setup-operator-authorization.md @@ -1,6 +1,6 @@ # Assisted Setup PAT Creation -- Status: Draft — the guided flow is feasible; pre-auth planning and cleanup UX need review +- Status: Draft — guided implementation in progress; controlled GitHub UX and full test budget remain unverified - Date: 2026-09-24 - Catalog capability ID: `temporary-setup-operator-authorization` - Last verified: Not applicable; prospective change @@ -8,7 +8,7 @@ - Scope: guide creation, verification, use, and user-owned deletion of the operator PAT for one `copilot setup` run - Related issues/PRs: none; no Action dogfooding for this design - Required review gates: product UX, architecture, testing, documentation, security, GitHub form compatibility -- Open decisions blocking readiness: exact local-only preflight questions; whether the initial link is provisional for remote-dependent grants; handling a plan that needs additional grants +- Open decisions blocking readiness: controlled GitHub UX and test-budget evidence; the initial link is provisional and a changed final plan requires correction and rerun ## 1. Executive summary @@ -28,7 +28,7 @@ known. Both roles share one URL-building contract, but not a credential or lifecycle. ```text -resolve repository -> local preflight -> choose guided/manual operator PAT +resolve repository -> provisional bootstrap grants -> choose guided/manual operator PAT -> GitHub form -> masked input -> verify -> plan and final grant audit -> guide/verify bot PAT -> install Secret -> apply setup -> cleanup reminder ``` @@ -136,7 +136,7 @@ link** means later remote inspection may require a new grant. | Stage | Current | Proposed | User effect | |---|---|---|---| -| Before setup PAT | bootstrap permission table | brief local preflight, table, guided/manual choice | purpose and account are clear | +| Before setup PAT | bootstrap permission table | provisional required-grant link, table, guided/manual choice | purpose and account are clear | | GitHub | user navigates form and transcribes grants | documented prefilled URL; user selects repo and generates | less repetitive form work | | After paste | identity/access and grant audit | same audit; display actual account | wrong token found before setup | | After plan | final grant audit | same audit; if link was provisional, explain correction | no silent overgrant | @@ -165,9 +165,9 @@ for setup, then explicitly asks the user to delete it on GitHub. ### 6.1 Normal path -1. Resolve repository. Collect only local configuration choices that affect - permissions before asking for the setup PAT. This preflight MUST reuse the - existing questionnaire policy, not fork a second configuration model. +1. Resolve repository. Use the existing bootstrap permission policy before the + questionnaire: only its required rows enter the initial URL. The link MUST + be labeled provisional because local and remote choices are not yet known. 2. Render the existing permission summary and `Create with GitHub guidance` (recommended) / `I already have a PAT`. If a supplied `--token` or `PERSONAL_ACCESS_TOKEN` exists, retain existing precedence and skip the @@ -228,8 +228,8 @@ instructions must not imply otherwise. | Supplied operator token | existing secret input | none | current CLI/env precedence | memory only | No new account or PAT configuration is persisted. Wrong owner, invalid grant, -URL length outside a reviewed terminal bound, and contradictory preflight -choices block link generation. Existing `--token` and environment precedence +URL length outside a reviewed terminal bound, and contradictory permission +grants block link generation. Existing `--token` and environment precedence remain; `--yes` does not choose an identity or waive checks. Expiry, host, secret-free URL, and role separation are not configurable in this first release. The recommended example is interactive guided setup; the meaningful @@ -361,7 +361,7 @@ report, and one final cleanup reminder; no polling or notifications. The manual prompt, `--token`, `PERSONAL_ACCESS_TOKEN`, `--non-interactive`, `--yes`, and dry-run continue to work with existing precedence. The first -release adds only interactive guidance and local preflight needed for its URL. +release adds only interactive guidance and a provisional bootstrap URL. No repository schema, Secret, or account-store migration occurs. Rollback hides guidance and returns to the current prompt; PATs already generated by users remain their responsibility. User docs must not imply the guided path @@ -375,7 +375,7 @@ versus final grants, provider identity/scope, cancellation, and cleanup truth. | Area | Cases | Risk covered | |---|---:|---| | Pure URL/configuration policy | 7 | key/level mapping, expiry, owner, encoding, rejected grants | -| State/application/idempotency | 6 | choice, preflight, final-plan change, retry, cancel | +| State/application/idempotency | 6 | choice, provisional link, final-plan change, retry, cancel | | Provider/permission contracts | 4 | identity, repo access, missing grant, unknown response | | Setup/compatibility | 4 | manual, supplied token, unattended, dry-run | | Terminal/accessibility/localization | 5 | pending, action, blocked, partial, complete; narrow URL | @@ -395,7 +395,7 @@ value enters test evidence. | Audience | Artifact | Required content | Validation | |---|---|---|---| | New user | `README.md`, `docs/how-to-use.mdx` | two roles and guided/manual normal path | navigation/link check | -| Setup owner | `docs/authentication.mdx`, `docs/configuration.mdx` | URL limits, preflight, exact permissions, account/repo choice | policy fixture | +| Setup owner | `docs/authentication.mdx`, `docs/configuration.mdx` | URL limits, provisional grants, exact permissions, account/repo choice | policy fixture | | Operator | `docs/security-operations/operations/troubleshooting.mdx` | wrong account, changed grants, cancellation, deletion | recovery fixture | | Contributor | `docs/development/architecture.mdx`, this SDD | shared URL builder and trust boundary | architecture test | @@ -409,8 +409,8 @@ Secret renewal. creation and manual input; guided is the default. 2. Given guided choice, the CLI prints a documented GitHub URL, repository instruction, and hidden PAT prompt; it does not open a browser. -3. Given local feature choices, URL permissions match known selected grants; - unknown remote-dependent grants are labeled provisional, not overgranted. +3. Given initial bootstrap grants, the URL contains only required grants; + later feature- or remote-dependent grants are labeled provisional, not overgranted. 4. Given an unintended browser account, the CLI displays the actual login and the operator declines it; given a wrong repo, access verification fails. Either way, setup blocks before dependent mutation. @@ -441,8 +441,8 @@ Secret renewal. ## 18. Implementation sequence -1. Settle the pre-auth local questions and provisional-link rule using the - current permission policy; record provider parameter mapping. +1. Use the bootstrap required-grant policy for a provisional initial link; + record provider parameter mapping and require final-plan re-audit. 2. Build one pure URL policy shared with the bot SDD and its contract tests. 3. Integrate the guided/manual choice and raw URL output before the masked operator PAT prompt, preserving supplied-token paths. @@ -452,7 +452,7 @@ Secret renewal. ## 19. Definition of Done -- [ ] Readiness-blocking preflight and provisional-link decisions are settled. +- [x] Provisional bootstrap-link decision is settled; final audit requires correction and rerun when grants change. - [ ] Every MUST maps to acceptance and verification. - [ ] Only documented GitHub URL parameters are emitted; URL contains no secret. - [ ] Account/repo/permissions are checked through the supplied PAT. @@ -473,3 +473,8 @@ Secret renewal. - Decision: ship guided PAT creation for both roles; do not present automatic PAT issuance, website request replay, or a local account manager as part of this product. A future App token is a separate credential and proposal. +- Implementation snapshot (2026-09-24): the pure URL builder, interactive + guided/manual prompt, account confirmation, final-audit correction link, and + GitHub deletion reminder are implemented locally. The URL never selects a + repository. Controlled browser acceptance and the numeric test budget remain + review gates; do not infer remote PAT deletion from this implementation. diff --git a/src/__tests__/cli.test.ts b/src/__tests__/cli.test.ts index 3b62cd2cd..0733b1a3f 100644 --- a/src/__tests__/cli.test.ts +++ b/src/__tests__/cli.test.ts @@ -15,7 +15,7 @@ jest.mock('child_process', () => ({ })); jest.mock('../actions/local_action', () => ({ - runLocalAction: jest.fn().mockResolvedValue(undefined), + runLocalAction: jest.fn().mockResolvedValue([]), })); jest.mock('../utils/logger', () => ({ @@ -112,7 +112,7 @@ describe('CLI', () => { ? 'a'.repeat(40) : 'https://github.com/test-owner/test-repo.git', )); - (runLocalAction as jest.Mock).mockResolvedValue(undefined); + (runLocalAction as jest.Mock).mockResolvedValue([]); mockIsIssue.mockResolvedValue(true); consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(() => {}); @@ -489,6 +489,18 @@ describe('CLI', () => { expect(params[INPUT_KEYS.SINGLE_ACTION]).toBe(ACTIONS.INITIAL_SETUP); }); + it('reports partial application when the local setup action returns a failed result', async () => { + (runLocalAction as jest.Mock).mockResolvedValueOnce([{ success: false, errors: [] }]); + await program.parseAsync([ + 'node', 'cli', 'setup', '--token', 'ghp_abcdefghijklmnopqrstuvwxyz12', + '--skip-secrets', '--non-interactive', '--pr-approval-mode', 'off', '--yes', + ]); + const { logInfo } = require('../utils/logger'); + expect(runLocalAction).toHaveBeenCalledTimes(1); + expect(logInfo).toHaveBeenCalledWith(expect.stringContaining('Secret may already have been written')); + expect(process.exitCode).toBe(1); + }); + it.each([ { ready: false, identityStatus: 'valid' as const }, { ready: true, identityStatus: 'invalid' as const }, diff --git a/src/application/policies/__tests__/setup_pat_creation_url_policy.test.ts b/src/application/policies/__tests__/setup_pat_creation_url_policy.test.ts new file mode 100644 index 000000000..c1b555789 --- /dev/null +++ b/src/application/policies/__tests__/setup_pat_creation_url_policy.test.ts @@ -0,0 +1,82 @@ +import { buildSetupPatCreationUrl, UnsupportedSetupPatLinkError } from '../setup_pat_creation_url_policy'; +import type { SetupTokenPermissionRequirement } from '../../../domain/setup_token_permissions'; + +const permission = ( + role: 'setup' | 'workflow', + scope: 'repository' | 'organization', + name: string, + level: 'read' | 'write', + applicability: 'required' | 'conditional' = 'required', +): SetupTokenPermissionRequirement => ({ + id: `${role}.${scope}.${name}`, role, scope, permission: name, level, applicability, + reason: 'test', probe: 'metadata', +}); + +describe('buildSetupPatCreationUrl', () => { + it('fills only required setup grants, owner, role, and one-day expiry', () => { + const url = new URL(buildSetupPatCreationUrl({ + role: 'setup', owner: 'vypdev', repository: 'copilot', expiresIn: 1, + requirements: [ + permission('setup', 'repository', 'Metadata', 'read'), + permission('setup', 'repository', 'Contents', 'read'), + permission('setup', 'repository', 'Secrets', 'write', 'conditional'), + ], + })); + expect(`${url.origin}${url.pathname}`).toBe('https://github.com/settings/personal-access-tokens/new'); + expect(Object.fromEntries(url.searchParams)).toEqual({ + name: 'Copilot setup copilot', + description: 'Copilot repository setup for vypdev/copilot', + target_name: 'vypdev', expires_in: '1', contents: 'read', metadata: 'read', + }); + expect(url.searchParams.has('repository')).toBe(false); + }); + + it('maps repository and organization bot grants without conflating scopes', () => { + const url = new URL(buildSetupPatCreationUrl({ + role: 'workflow', owner: 'vypdev', repository: 'copilot', expiresIn: 90, + requirements: [ + permission('workflow', 'repository', 'Variables', 'read'), + permission('workflow', 'repository', 'Pull requests', 'write'), + permission('workflow', 'organization', 'Variables', 'read'), + permission('workflow', 'organization', 'Projects', 'write'), + permission('workflow', 'organization', 'Members', 'read'), + ], + })); + expect(url.searchParams.get('actions_variables')).toBe('read'); + expect(url.searchParams.get('organization_actions_variables')).toBe('read'); + expect(url.searchParams.get('organization_projects')).toBe('write'); + expect(url.searchParams.get('pull_requests')).toBe('write'); + expect(url.searchParams.get('members')).toBe('read'); + expect(url.searchParams.get('expires_in')).toBe('90'); + }); + + it('keeps the strongest duplicate grant', () => { + const url = new URL(buildSetupPatCreationUrl({ + role: 'setup', owner: 'vypdev', repository: 'copilot', expiresIn: 1, + requirements: [permission('setup', 'repository', 'Contents', 'write'), permission('setup', 'repository', 'Contents', 'read')], + })); + expect(url.searchParams.get('contents')).toBe('write'); + }); + + it('rejects unsupported Checks instead of producing an incomplete guarded link', () => { + expect(() => buildSetupPatCreationUrl({ + role: 'workflow', owner: 'vypdev', repository: 'copilot', expiresIn: 90, + requirements: [permission('workflow', 'repository', 'Checks', 'read')], + })).toThrow(UnsupportedSetupPatLinkError); + }); + + it.each(['bad/owner', '', 'a'.repeat(40)])('rejects unsafe or invalid owner %s', owner => { + expect(() => buildSetupPatCreationUrl({ role: 'setup', owner, repository: 'copilot', expiresIn: 1, requirements: [] })).toThrow(); + }); + + it.each([0, 367, 1.5])('rejects invalid expiration %s', expiresIn => { + expect(() => buildSetupPatCreationUrl({ role: 'setup', owner: 'vypdev', repository: 'copilot', expiresIn, requirements: [] })).toThrow(); + }); + + it('rejects a mixed-role permission list', () => { + expect(() => buildSetupPatCreationUrl({ + role: 'setup', owner: 'vypdev', repository: 'copilot', expiresIn: 1, + requirements: [permission('workflow', 'repository', 'Contents', 'read')], + })).toThrow('role'); + }); +}); diff --git a/src/application/policies/setup_pat_creation_url_policy.ts b/src/application/policies/setup_pat_creation_url_policy.ts new file mode 100644 index 000000000..e37ddd3fc --- /dev/null +++ b/src/application/policies/setup_pat_creation_url_policy.ts @@ -0,0 +1,75 @@ +import type { SetupTokenPermissionRequirement, SetupTokenPermissionScope } from '../../domain/setup_token_permissions'; + +const PAT_FORM = 'https://github.com/settings/personal-access-tokens/new'; + +const QUERY_PERMISSIONS: Readonly>>> = { + repository: { + Metadata: 'metadata', + Contents: 'contents', + Secrets: 'secrets', + Variables: 'actions_variables', + Issues: 'issues', + Actions: 'actions', + Administration: 'administration', + Workflows: 'workflows', + 'Pull requests': 'pull_requests', + }, + organization: { + Secrets: 'organization_secrets', + Variables: 'organization_actions_variables', + 'Issue Types': 'issue_types', + Projects: 'organization_projects', + Members: 'members', + }, +}; + +export class UnsupportedSetupPatLinkError extends Error { + constructor(readonly permissions: readonly string[]) { + super(`GitHub's fine-grained PAT form cannot prefill: ${permissions.join(', ')}.`); + this.name = 'UnsupportedSetupPatLinkError'; + } +} + +/** Builds only documented GitHub form fields; never accepts credential material. */ +export function buildSetupPatCreationUrl(input: Readonly<{ + role: 'setup' | 'workflow'; + owner: string; + repository: string; + expiresIn: number; + requirements: readonly SetupTokenPermissionRequirement[]; +}>): string { + if (!/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/.test(input.owner) + || !/^[A-Za-z0-9._-]{1,100}$/.test(input.repository) + || !Number.isInteger(input.expiresIn) + || input.expiresIn < 1 + || input.expiresIn > 366) { + throw new Error('Invalid PAT form owner, repository, or expiration.'); + } + + const grants = new Map(); + const unsupported: string[] = []; + for (const item of input.requirements) { + if (item.role !== input.role) throw new Error('PAT permission role does not match the requested form.'); + if (item.applicability !== 'required') continue; + const key = QUERY_PERMISSIONS[item.scope][item.permission]; + if (!key || (key === 'metadata' && item.level !== 'read') + || (key === 'workflows' && item.level !== 'write')) { + unsupported.push(`${item.scope} ${item.permission} ${item.level}`); + continue; + } + if (grants.get(key) !== 'write') grants.set(key, item.level); + } + if (unsupported.length > 0) throw new UnsupportedSetupPatLinkError(unsupported); + + const url = new URL(PAT_FORM); + url.searchParams.set('name', `Copilot ${input.role === 'setup' ? 'setup' : 'bot'} ${input.repository}`.slice(0, 40)); + url.searchParams.set('description', `Copilot ${input.role === 'setup' ? 'repository setup' : 'GitHub Action'} for ${input.owner}/${input.repository}`); + url.searchParams.set('target_name', input.owner); + url.searchParams.set('expires_in', String(input.expiresIn)); + for (const [key, level] of [...grants].sort(([left], [right]) => left.localeCompare(right))) { + url.searchParams.set(key, level); + } + const result = url.toString(); + if (result.length > 2_048) throw new Error('PAT form URL exceeds the supported terminal length; create the PAT manually.'); + return result; +} diff --git a/src/application/ports/setup_pat_identity_ports.ts b/src/application/ports/setup_pat_identity_ports.ts new file mode 100644 index 000000000..508374bed --- /dev/null +++ b/src/application/ports/setup_pat_identity_ports.ts @@ -0,0 +1,9 @@ +export interface SetupGithubIdentity { + readonly id: number; + readonly login: string; +} + +export interface SetupGithubIdentityQueryPort { + resolve(login: string, setupToken: string): Promise; + identify(token: string): Promise; +} diff --git a/src/application/usecases/setup/__tests__/verify_guided_workflow_pat_identity_use_case.test.ts b/src/application/usecases/setup/__tests__/verify_guided_workflow_pat_identity_use_case.test.ts new file mode 100644 index 000000000..98eb5a011 --- /dev/null +++ b/src/application/usecases/setup/__tests__/verify_guided_workflow_pat_identity_use_case.test.ts @@ -0,0 +1,22 @@ +import { VerifyGuidedWorkflowPatIdentityUseCase } from '../verify_guided_workflow_pat_identity_use_case'; + +describe('VerifyGuidedWorkflowPatIdentityUseCase', () => { + const identities = { + resolve: jest.fn(), + identify: jest.fn(), + }; + beforeEach(() => jest.clearAllMocks()); + + it('accepts matching immutable IDs even when the login casing differs', async () => { + identities.identify.mockResolvedValue({ id: 42, login: 'vypbot' }); + await expect(new VerifyGuidedWorkflowPatIdentityUseCase(identities) + .execute({ id: 42, login: 'VypBot' }, 'workflow-token')).resolves.toEqual({ id: 42, login: 'VypBot' }); + expect(identities.identify).toHaveBeenCalledWith('workflow-token'); + }); + + it('rejects another account without leaking either token', async () => { + identities.identify.mockResolvedValue({ id: 99, login: 'operator' }); + await expect(new VerifyGuidedWorkflowPatIdentityUseCase(identities) + .execute({ id: 42, login: 'vypbot' }, 'workflow-token')).rejects.toThrow('not the selected bot'); + }); +}); diff --git a/src/application/usecases/setup/verify_guided_workflow_pat_identity_use_case.ts b/src/application/usecases/setup/verify_guided_workflow_pat_identity_use_case.ts new file mode 100644 index 000000000..720b73286 --- /dev/null +++ b/src/application/usecases/setup/verify_guided_workflow_pat_identity_use_case.ts @@ -0,0 +1,18 @@ +import type { SetupGithubIdentity, SetupGithubIdentityQueryPort } from '../../ports/setup_pat_identity_ports'; +import { ApplicationError } from '../../errors/application_error'; + +/** Binds a guided runtime PAT to the bot account chosen before token entry. */ +export class VerifyGuidedWorkflowPatIdentityUseCase { + constructor(private readonly identities: SetupGithubIdentityQueryPort) {} + + async execute(expected: SetupGithubIdentity, workflowToken: string): Promise { + const actual = await this.identities.identify(workflowToken); + if (actual.id !== expected.id) { + throw new ApplicationError( + 'authorization.credential-invalid', + `The workflow PAT belongs to @${actual.login}, not the selected bot @${expected.login}. No Secret was written. Delete the unintended PAT in GitHub and create one as @${expected.login}.`, + ); + } + return expected; + } +} diff --git a/src/cli/__tests__/setup_presenters.test.ts b/src/cli/__tests__/setup_presenters.test.ts index 2f07b517d..eeeecb69e 100644 --- a/src/cli/__tests__/setup_presenters.test.ts +++ b/src/cli/__tests__/setup_presenters.test.ts @@ -237,6 +237,114 @@ describe('setup presenters and prompt-specific adapters', () => { log.mockRestore(); }); + it('guides setup PAT creation, confirms the authenticated account, and gives an honest cleanup reminder', async () => { + const log = jest.spyOn(console, 'log').mockImplementation(); + try { + const input = terminal([ + { kind: 'value', value: '' }, + { kind: 'value', value: 'setup-token' }, + { kind: 'value', value: '' }, + ]); + const adapter = new SetupCredentialPromptAdapter(input, {}); + adapter.configureSetupPatGuide('https://github.com/settings/personal-access-tokens/new?expires_in=1'); + await expect(adapter.requestSetupPat()).resolves.toBe('setup-token'); + await expect(adapter.confirmGuidedSetupAccount('operator')).resolves.toBe(true); + adapter.showSetupPatCleanupReminder(); + const output = log.mock.calls.flat().join('\n'); + expect(output).toContain('https://github.com/settings/personal-access-tokens/new?expires_in=1'); + expect(output).toContain('Provisional'); + expect(output).toContain('@operator'); + expect(output).toContain('not revoked automatically'); + expect(output).not.toContain('setup-token'); + expect(input.readSecret).toHaveBeenCalledTimes(1); + } finally { log.mockRestore(); } + }); + + it('keeps manual setup PAT choice free of link and cleanup claims', async () => { + const log = jest.spyOn(console, 'log').mockImplementation(); + try { + const adapter = new SetupCredentialPromptAdapter(terminal([ + { kind: 'value', value: '2' }, + { kind: 'value', value: 'manual-token' }, + ]), {}); + adapter.configureSetupPatGuide('https://github.com/settings/personal-access-tokens/new'); + await expect(adapter.requestSetupPat()).resolves.toBe('manual-token'); + adapter.showSetupPatCleanupReminder(); + const output = log.mock.calls.flat().join('\n'); + expect(output).not.toContain('https://github.com/settings/personal-access-tokens/new'); + expect(output).not.toContain('not revoked automatically'); + } finally { log.mockRestore(); } + }); + + it('distinguishes an initial PAT failure from a blocked final plan', async () => { + const log = jest.spyOn(console, 'log').mockImplementation(); + try { + const adapter = new SetupCredentialPromptAdapter(terminal([ + { kind: 'value', value: '1' }, { kind: 'value', value: 'setup-token' }, + ]), {}); + adapter.configureSetupPatGuide('https://github.com/settings/personal-access-tokens/new'); + await adapter.requestSetupPat(); + log.mockClear(); + adapter.showUpdatedSetupPatLink('https://github.com/settings/personal-access-tokens/new?contents=read', 'bootstrap'); + expect(log.mock.calls.flat().join('\n')).toContain('no setup plan has been applied'); + log.mockClear(); + adapter.showUpdatedSetupPatLink('https://github.com/settings/personal-access-tokens/new?contents=write', 'final'); + expect(log.mock.calls.flat().join('\n')).toContain('no plan mutation has started'); + } finally { log.mockRestore(); } + }); + + it('rejects an unintended setup account before continuing', async () => { + const log = jest.spyOn(console, 'log').mockImplementation(); + try { + const adapter = new SetupCredentialPromptAdapter(terminal([ + { kind: 'value', value: '1' }, + { kind: 'value', value: 'setup-token' }, + { kind: 'value', value: '2' }, + ]), {}); + adapter.configureSetupPatGuide('https://github.com/settings/personal-access-tokens/new'); + await adapter.requestSetupPat(); + await expect(adapter.confirmGuidedSetupAccount('wrong-account')).resolves.toBe(false); + } finally { log.mockRestore(); } + }); + + it('resolves the intended bot ID before accepting a guided workflow PAT', async () => { + const log = jest.spyOn(console, 'log').mockImplementation(); + try { + const input = terminal([ + { kind: 'value', value: '' }, + { kind: 'value', value: 'bad/login' }, + { kind: 'value', value: 'vypbot' }, + { kind: 'value', value: 'bot-token' }, + ]); + const resolve = jest.fn(async () => ({ id: 42, login: 'vypbot' })); + const adapter = new SetupCredentialPromptAdapter(input, {}); + adapter.configureWorkflowPatGuide('https://github.com/settings/personal-access-tokens/new?expires_in=90', resolve); + const requirement = { name: 'PAT', kind: 'workflowPat' as const, description: 'Runtime token' }; + await expect(adapter.requestWorkflowPat(requirement)).resolves.toEqual({ name: 'PAT', value: 'bot-token' }); + expect(resolve).toHaveBeenCalledWith('vypbot'); + expect(adapter.guidedWorkflowBotIdentity).toEqual({ id: 42, login: 'vypbot' }); + const output = log.mock.calls.flat().join('\n'); + expect(output).toContain('GitHub account ID 42'); + expect(output).toContain('https://github.com/settings/personal-access-tokens/new?expires_in=90'); + expect(output).not.toContain('bot-token'); + } finally { log.mockRestore(); } + }); + + it('manual bot PAT entry does not assert a guided bot identity', async () => { + const log = jest.spyOn(console, 'log').mockImplementation(); + try { + const adapter = new SetupCredentialPromptAdapter(terminal([ + { kind: 'value', value: '2' }, { kind: 'value', value: 'manual-bot-token' }, + ]), {}); + const resolve = jest.fn(); + adapter.configureWorkflowPatGuide('https://github.com/settings/personal-access-tokens/new', resolve); + await expect(adapter.requestWorkflowPat({ name: 'PAT', kind: 'workflowPat', description: 'Runtime token' })) + .resolves.toEqual({ name: 'PAT', value: 'manual-bot-token' }); + expect(resolve).not.toHaveBeenCalled(); + expect(adapter.guidedWorkflowBotIdentity).toBeUndefined(); + } finally { log.mockRestore(); } + }); + it('supports explicit existing-credential choices and propagates interrupted secret input', async () => { const log = jest.spyOn(console, 'log').mockImplementation(); const requirement = { name: 'PAT', kind: 'workflowPat' as const, description: 'Runtime token' }; diff --git a/src/cli/commands/setup.ts b/src/cli/commands/setup.ts index 9aa76fbe6..4cdacc806 100644 --- a/src/cli/commands/setup.ts +++ b/src/cli/commands/setup.ts @@ -33,6 +33,9 @@ import { SetupCredentialPromptAdapter, SetupTerminalCancelledError } from '../se import { SetupWorkflowUpdatePromptAdapter } from '../setup_workflow_update_prompt_adapter'; import { ConsoleSetupTokenPermissionPresenter } from '../setup_token_permission_presenter'; import { createSetupTokenPermissionsUseCase } from '../../infrastructure/composition/setup_token_permissions_composition_root'; +import { buildSetupPatCreationUrl, UnsupportedSetupPatLinkError } from '../../application/policies/setup_pat_creation_url_policy'; +import { SetupGithubIdentityQueryAdapter } from '../../infrastructure/setup_github_identity_query_adapter'; +import { VerifyGuidedWorkflowPatIdentityUseCase } from '../../application/usecases/setup/verify_guided_workflow_pat_identity_use_case'; export function registerSetupCommand(program: Command): void { program @@ -74,6 +77,7 @@ export function registerSetupCommand(program: Command): void { const tokenPermissions = createSetupTokenPermissionsUseCase(); const workflowPrompt = new SetupWorkflowUpdatePromptAdapter(terminal); const cwd = process.cwd(); + let setupMutationStarted = false; try { if (!options.nonInteractive && !terminal) { logError('Interactive setup requires a terminal. Use --non-interactive with explicit configuration.'); @@ -98,6 +102,17 @@ export function registerSetupCommand(program: Command): void { const setupPatPermissions = buildSetupPatPermissionRequirements(); permissionPresenter.showRequirements('setup', setupPatPermissions); let token = getSetupToken(cwd, options.token); + let setupPatAccount: string | undefined; + if (!token && !options.nonInteractive && !options.dryRun) { + try { + credentialPrompt.configureSetupPatGuide(buildSetupPatCreationUrl({ + role: 'setup', owner: gitInfo.owner, repository: gitInfo.repo, expiresIn: 1, + requirements: setupPatPermissions, + })); + } catch { + logInfo('A guided setup PAT link is unavailable for this repository or permission set. Enter a manually created PAT using the table above.'); + } + } if (!token && !options.nonInteractive && !options.dryRun) token = await credentialPrompt.requestSetupPat(); if (!token && !options.dryRun) { logError('🛑 Setup requires PERSONAL_ACCESS_TOKEN with a valid token.'); @@ -120,11 +135,19 @@ export function registerSetupCommand(program: Command): void { || (permissionReport.confirmationRequired && await credentialPrompt.confirmUnverifiableTokenPermissions(permissionReport)); if (!permissionAccepted || permissionReport.identityStatus !== 'valid') { + if (credentialPrompt.usedGuidedSetupPat) credentialPrompt.showUpdatedSetupPatLink(buildSetupPatCreationUrl({ + role: 'setup', owner: gitInfo.owner, repository: gitInfo.repo, expiresIn: 1, + requirements: setupPatPermissions, + }), 'bootstrap'); throw new ApplicationError( 'authorization.credential-invalid', 'The setup PAT has missing or unconfirmed required access. Grant or explicitly confirm the permissions shown above and retry.', ); } + if (!await credentialPrompt.confirmGuidedSetupAccount(permissionReport.account)) { + throw new ApplicationError('authorization.credential-invalid', 'The setup PAT belongs to an unintended account. Revoke it in GitHub and retry with the correct account.'); + } + setupPatAccount = permissionReport.account; } logInfo(options.dryRun ? '🧭 Building a dry-run setup plan...' : '🧭 Building your setup plan...'); const auditConfiguredSetupPat = async ( @@ -143,6 +166,10 @@ export function registerSetupCommand(program: Command): void { || (permissionReport.confirmationRequired && await credentialPrompt.confirmUnverifiableTokenPermissions(permissionReport)); if (!permissionAccepted || permissionReport.identityStatus !== 'valid') { + if (credentialPrompt.usedGuidedSetupPat) credentialPrompt.showUpdatedSetupPatLink(buildSetupPatCreationUrl({ + role: 'setup', owner: gitInfo.owner, repository: gitInfo.repo, expiresIn: 1, + requirements: configuredSetupPatPermissions, + }), 'final'); return { status: 'blocked', errors: [ 'The setup PAT has missing or unconfirmed access required by the approved setup plan. Grant or explicitly confirm the permissions shown above and retry.', ] }; @@ -200,6 +227,20 @@ export function registerSetupCommand(program: Command): void { logInfo('✅ Dry run complete. No files or GitHub resources were changed.'); return; } + const workflowTokenPermissions = buildWorkflowPatPermissionRequirements(configuration, remoteConfiguration); + const githubIdentities = new SetupGithubIdentityQueryAdapter(); + if (!options.nonInteractive && !options.workflowPat && !options.secret?.PAT) { + try { + const workflowPatGuide = buildSetupPatCreationUrl({ + role: 'workflow', owner: gitInfo.owner, repository: gitInfo.repo, expiresIn: 90, + requirements: workflowTokenPermissions, + }); + credentialPrompt.configureWorkflowPatGuide(workflowPatGuide, login => githubIdentities.resolve(login, token ?? '')); + } catch (error) { + if (!(error instanceof UnsupportedSetupPatLinkError)) throw error; + logInfo('A guided fine-grained bot PAT link is unavailable for one or more required permissions. Use the permission table and manual path; review whether a classic PAT is required for this plan.'); + } + } const credentials = await createSetupCredentialsUseCase(credentialPrompt, permissionPresenter).collect({ owner: gitInfo.owner, repository: gitInfo.repo, @@ -209,8 +250,17 @@ export function registerSetupCommand(program: Command): void { secretStoragePolicy: configuration.storage.secrets, ref: configuration.repository.mainBranch, remoteConfiguration, - workflowTokenPermissions: buildWorkflowPatPermissionRequirements(configuration, remoteConfiguration), + workflowTokenPermissions, }); + const guidedBotIdentity = credentialPrompt.guidedWorkflowBotIdentity; + if (guidedBotIdentity && credentials.collection.workflowPat) { + const verifiedBot = await new VerifyGuidedWorkflowPatIdentityUseCase(githubIdentities) + .execute(guidedBotIdentity, credentials.collection.workflowPat.value); + logInfo(`✅ Workflow PAT owner verified as @${verifiedBot.login} (GitHub account ID ${verifiedBot.id}).`); + if (setupPatAccount?.toLowerCase() === verifiedBot.login.toLowerCase()) { + logInfo('The workflow PAT and setup PAT use the same GitHub account. If this account authors PRs, bot-generated events and guarded self-approval may not behave as intended; use a dedicated bot account where required.'); + } + } logInfo('⚙️ Applying the approved setup plan...'); const params = buildSetupParams( options, @@ -222,8 +272,18 @@ export function registerSetupCommand(program: Command): void { remoteConfiguration, ); if (!params) return; - await runLocalAction(params); + setupMutationStarted = true; + const actionResults = await runLocalAction(params); + if (actionResults.some(actionResult => !actionResult.success || actionResult.errors.length > 0)) { + logInfo('Setup reported failures or partial completion. If a bot PAT was supplied, its Secret may already have been written; inspect the result and GitHub Secret name/scope before retrying or revoking it.'); + process.exitCode = 1; + } } catch (error) { + if (credentialPrompt.guidedWorkflowBotIdentity) { + logInfo(setupMutationStarted + ? 'Setup may be partially applied. Inspect the GitHub Secret before deleting or replacing the bot PAT.' + : 'No setup mutation started. If you generated an unused bot PAT in GitHub, delete it there; Copilot cannot revoke it.'); + } if (error instanceof SetupTerminalCancelledError) { logInfo('Setup cancelled. No changes were applied.'); process.exitCode = 130; @@ -232,6 +292,7 @@ export function registerSetupCommand(program: Command): void { logError(toApplicationError(error, 'workflow.failed', 'Setup failed.')); process.exitCode = 1; } finally { + credentialPrompt.showSetupPatCleanupReminder(); terminal?.close(); } }); diff --git a/src/cli/setup_credential_prompt_adapter.ts b/src/cli/setup_credential_prompt_adapter.ts index 45630cd3a..716e3badd 100644 --- a/src/cli/setup_credential_prompt_adapter.ts +++ b/src/cli/setup_credential_prompt_adapter.ts @@ -7,6 +7,7 @@ import type { SetupCredentialValue, } from '../domain/setup'; import type { SetupTokenPermissionReport } from '../domain/setup_token_permissions'; +import type { SetupGithubIdentity } from '../application/ports/setup_pat_identity_ports'; import { color, renderBox, statusIcon } from './setup_prompt_rendering'; export class SetupTerminalCancelledError extends Error { @@ -17,14 +18,68 @@ export class SetupTerminalCancelledError extends Error { } export class SetupCredentialPromptAdapter implements SetupCredentialPromptPort { + private setupPatGuide?: string; + private workflowPatGuide?: string; + private resolveBotIdentity?: (login: string) => Promise; + private guidedSetup = false; + private guidedBotIdentity?: SetupGithubIdentity; + constructor( private readonly terminal: TerminalDriver | undefined, private readonly credentialValues: Readonly>, private readonly confirmUnverifiableWritePermissions = false, ) {} + configureSetupPatGuide(url: string): void { this.setupPatGuide = url; } + get usedGuidedSetupPat(): boolean { return this.guidedSetup; } + configureWorkflowPatGuide(url: string, resolveIdentity: (login: string) => Promise): void { + this.workflowPatGuide = url; + this.resolveBotIdentity = resolveIdentity; + } + get guidedWorkflowBotIdentity(): SetupGithubIdentity | undefined { return this.guidedBotIdentity; } + + async confirmGuidedSetupAccount(account?: string): Promise { + if (!this.guidedSetup || !this.terminal) return true; + if (!account || !/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/.test(account)) return false; + console.log(`GitHub authenticated the setup PAT as @${account}.`); + return (await this.readChoice('Is this the account you intended to configure with?', ['yes', 'no'], 'yes')) === 'yes'; + } + + showSetupPatCleanupReminder(): void { + if (!this.guidedSetup) return; + console.log(renderBox( + 'The setup PAT was not revoked automatically. After setup finishes or is cancelled, delete it in GitHub → Settings → Developer settings → Personal access tokens. Ending this process does not remove the token from GitHub.', + 'Revoke temporary setup PAT', + 33, + )); + console.log('https://github.com/settings/personal-access-tokens'); + } + + showUpdatedSetupPatLink(url: string, stage: 'bootstrap' | 'final'): void { + if (!this.guidedSetup) return; + console.log(renderBox( + stage === 'bootstrap' + ? 'The setup PAT did not pass the initial access check; no setup plan has been applied. Review its grants in GitHub or create a replacement with this link, then rerun. Select only the intended repository in GitHub.' + : 'The selected plan requires access this PAT did not prove; no plan mutation has started. Update its grants in GitHub or create a replacement with this link, then rerun. Select only the intended repository in GitHub.', + stage === 'bootstrap' ? 'Setup PAT access needs attention' : 'Setup PAT permissions changed', + 33, + )); + console.log(url); + } + async requestSetupPat(): Promise { if (!this.terminal) return undefined; + if (this.setupPatGuide) { + this.guidedSetup = (await this.readChoice('How would you like to provide the setup PAT?', ['guided link', 'manual PAT'], 'guided link')) === 'guided link'; + if (this.guidedSetup) { + console.log(renderBox( + 'Provisional link: Open this GitHub link in your browser, sign in as the account configuring this repository, complete any 2FA or SSO, and review the prefilled fine-grained permissions. GitHub owns token creation; Copilot never handles your web session. Select ONLY this repository manually. The permissions may need updating after the setup questionnaire.', + 'Create setup PAT in GitHub', 33, + )); + console.log(this.setupPatGuide); + console.log('Copy the one-time token from GitHub and paste it below. It is hidden and used only for this setup run.'); + } + } console.log(renderBox( 'Enter a GitHub setup PAT. It is used in memory for this run only and is never stored. The workflow PAT is a different bot-account token and is requested separately.', 'Setup PAT', @@ -73,13 +128,38 @@ export class SetupCredentialPromptAdapter implements SetupCredentialPromptPort { console.log(`Credential options: ${requirements.map((requirement) => requirement.name).join(', ')}`); } - requestWorkflowPat( + async requestWorkflowPat( requirement: SetupCredentialRequirement, current?: SetupCredentialCheck, ): Promise { + if (this.terminal && !this.credentialValues[requirement.name]?.trim() && this.workflowPatGuide) { + const guided = (await this.readChoice('How would you like to provide the bot workflow PAT?', ['guided link', 'manual PAT'], 'guided link')) === 'guided link'; + if (guided) { + const login = await this.readBotLogin(); + const identity = await this.resolveBotIdentity!(login); + this.guidedBotIdentity = identity; + console.log(`Expected bot account resolved: @${identity.login} (GitHub account ID ${identity.id}).`); + console.log(renderBox( + `Open this link in a separate/private browser session, sign in as @${login} (the bot account), and complete its 2FA or SSO. Review every grant and select ONLY the intended repository manually. GitHub creates the PAT; Copilot does not store bot web credentials. The suggested expiry is 90 days—renew the token and update the Actions Secret before then.`, + 'Create bot PAT in GitHub', 33, + )); + console.log(this.workflowPatGuide); + console.log('Copy the one-time bot token and paste it below. It will be validated before any Secret is written.'); + } + } return this.requestSecretForRequirement(requirement, current, 'workflow PAT owned by the bot account'); } + private async readBotLogin(): Promise { + while (true) { + const result = await this.terminal!.readText('Expected GitHub bot login (without @): '); + if (result.kind !== 'value') throw new SetupTerminalCancelledError(); + const login = result.value.trim(); + if (/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/.test(login)) return login; + console.log(color('Enter a valid GitHub account login.', 33)); + } + } + requestApiKey( requirement: SetupCredentialRequirement, current?: SetupCredentialCheck, diff --git a/src/infrastructure/__tests__/setup_github_identity_query_adapter.test.ts b/src/infrastructure/__tests__/setup_github_identity_query_adapter.test.ts new file mode 100644 index 000000000..a719f3c35 --- /dev/null +++ b/src/infrastructure/__tests__/setup_github_identity_query_adapter.test.ts @@ -0,0 +1,29 @@ +import { SetupGithubIdentityQueryAdapter } from '../setup_github_identity_query_adapter'; + +describe('SetupGithubIdentityQueryAdapter', () => { + it('uses the operator token to resolve expected identity and the workflow PAT to identify its owner', async () => { + const fetcher = jest.fn() + .mockResolvedValueOnce({ ok: true, json: async () => ({ id: 42, login: 'vypbot' }) }) + .mockResolvedValueOnce({ ok: true, json: async () => ({ id: 42, login: 'vypbot' }) }); + const adapter = new SetupGithubIdentityQueryAdapter(fetcher as unknown as typeof fetch); + await expect(adapter.resolve('vypbot', 'setup-token')).resolves.toEqual({ id: 42, login: 'vypbot' }); + await expect(adapter.identify('workflow-token')).resolves.toEqual({ id: 42, login: 'vypbot' }); + expect(fetcher.mock.calls[0][0]).toBe('https://api.github.com/users/vypbot'); + expect(fetcher.mock.calls[0][1].headers.Authorization).toBe('Bearer setup-token'); + expect(fetcher.mock.calls[1][0]).toBe('https://api.github.com/user'); + expect(fetcher.mock.calls[1][1].headers.Authorization).toBe('Bearer workflow-token'); + }); + + it('rejects malformed logins before a request', async () => { + const fetcher = jest.fn(); + const adapter = new SetupGithubIdentityQueryAdapter(fetcher as unknown as typeof fetch); + await expect(adapter.resolve('bad/login', 'setup-token')).rejects.toThrow('valid GitHub'); + expect(fetcher).not.toHaveBeenCalled(); + }); + + it('rejects an unverified or malformed response without returning provider text', async () => { + const fetcher = jest.fn().mockResolvedValue({ ok: true, json: async () => ({ id: '42', login: 'vypbot' }) }); + await expect(new SetupGithubIdentityQueryAdapter(fetcher as unknown as typeof fetch) + .identify('workflow-token')).rejects.toThrow('invalid identity'); + }); +}); diff --git a/src/infrastructure/setup_github_identity_query_adapter.ts b/src/infrastructure/setup_github_identity_query_adapter.ts new file mode 100644 index 000000000..fba9a584f --- /dev/null +++ b/src/infrastructure/setup_github_identity_query_adapter.ts @@ -0,0 +1,45 @@ +import type { SetupGithubIdentity, SetupGithubIdentityQueryPort } from '../application/ports/setup_pat_identity_ports'; + +const LOGIN_PATTERN = /^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/; + +export class SetupGithubIdentityQueryAdapter implements SetupGithubIdentityQueryPort { + constructor(private readonly fetcher: typeof fetch = fetch, private readonly timeoutMs = 10_000) {} + + async resolve(login: string, setupToken: string): Promise { + if (!LOGIN_PATTERN.test(login)) throw new Error('Enter a valid GitHub bot account login.'); + return this.request(`https://api.github.com/users/${encodeURIComponent(login)}`, setupToken); + } + + identify(token: string): Promise { + return this.request('https://api.github.com/user', token); + } + + private async request(url: string, token: string): Promise { + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), this.timeoutMs); + try { + const response = await this.fetcher(url, { + method: 'GET', + headers: { + Authorization: `Bearer ${token}`, + Accept: 'application/vnd.github+json', + 'X-GitHub-Api-Version': '2022-11-28', + }, + signal: controller.signal, + }); + if (!response.ok) throw new Error('GitHub could not verify the selected bot account or token identity. No Secret was written.'); + const body: unknown = await response.json(); + if (!body || typeof body !== 'object' || Array.isArray(body)) throw new Error('GitHub returned an invalid identity. No Secret was written.'); + const { id, login } = body as Record; + if (typeof id !== 'number' || !Number.isSafeInteger(id) || id <= 0 || typeof login !== 'string' || !LOGIN_PATTERN.test(login)) { + throw new Error('GitHub returned an invalid identity. No Secret was written.'); + } + return { id, login }; + } catch (error) { + if (error instanceof Error && error.message.includes('No Secret was written.')) throw error; + throw Object.assign(new Error('GitHub identity verification failed. Check network access and retry; no Secret was written.'), { cause: error }); + } finally { + clearTimeout(timeout); + } + } +} From 3c8dd1fb88481de3ebac341bc26c5efb4090879e Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Thu, 24 Sep 2026 19:06:44 +0200 Subject: [PATCH 03/50] codex-setup-temporary-github-auth: cover guided PAT audit failures --- build/cli/index.js | 1 + src/__tests__/cli.test.ts | 57 +++++++++++++++++++ .../policies/setup_pat_creation_url_policy.ts | 1 + 3 files changed, 59 insertions(+) diff --git a/build/cli/index.js b/build/cli/index.js index f8a87a50e..92ae3905d 100755 --- a/build/cli/index.js +++ b/build/cli/index.js @@ -47731,6 +47731,7 @@ const QUERY_PERMISSIONS = { Variables: 'organization_actions_variables', 'Issue Types': 'issue_types', Projects: 'organization_projects', + // GitHub's PAT form documents this organization permission as "members". Members: 'members', }, }; diff --git a/src/__tests__/cli.test.ts b/src/__tests__/cli.test.ts index 0733b1a3f..87d6745f0 100644 --- a/src/__tests__/cli.test.ts +++ b/src/__tests__/cli.test.ts @@ -453,6 +453,63 @@ describe('CLI', () => { describe('setup', () => { // Token check: hasValidSetupToken/setupEnvFileExists and message variants are covered in // setup_files.test.ts and initial_setup_use_case.test.ts. + it('offers the guided setup PAT link and a repair link when its initial audit fails', async () => { + const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); + const terminal = { + readText: jest.fn().mockResolvedValue({ kind: 'value', value: '' }), + readSecret: jest.fn().mockResolvedValue({ kind: 'value', value: 'github_pat_guided_setup_test_token' }), + close: jest.fn(), + }; + const createTerminal = jest.spyOn(terminalDriver, 'createInteractiveTerminalDriver') + .mockReturnValue(terminal as unknown as ReturnType); + mockTokenPermissionInspect.mockResolvedValueOnce({ + role: 'setup', identityStatus: 'valid', identityMessage: 'verified', + ready: false, confirmationRequired: false, checks: [], + }); + + try { + await program.parseAsync(['node', 'cli', 'setup']); + + expect(terminal.readText).toHaveBeenCalledWith(expect.stringContaining('How would you like to provide the setup PAT?')); + expect(terminal.readSecret).toHaveBeenCalledWith('Setup PAT'); + expect(consoleLogSpy.mock.calls.flat().join('\n')).toContain('Setup PAT access needs attention'); + expect(consoleLogSpy.mock.calls.flat().join('\n')).toContain('Revoke temporary setup PAT'); + expect(runLocalAction).not.toHaveBeenCalled(); + expect(process.exitCode).toBe(1); + expect(terminal.close).toHaveBeenCalledTimes(1); + } finally { + createTerminal.mockRestore(); + } + }); + + it('stops before planning if the guided setup PAT belongs to an unintended account', async () => { + const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); + const terminal = { + readText: jest.fn() + .mockResolvedValueOnce({ kind: 'value', value: '' }) + .mockResolvedValueOnce({ kind: 'value', value: '2' }), + readSecret: jest.fn().mockResolvedValue({ kind: 'value', value: 'github_pat_guided_setup_test_token' }), + close: jest.fn(), + }; + const createTerminal = jest.spyOn(terminalDriver, 'createInteractiveTerminalDriver') + .mockReturnValue(terminal as unknown as ReturnType); + mockTokenPermissionInspect.mockResolvedValueOnce({ + role: 'setup', identityStatus: 'valid', identityMessage: 'verified', + account: 'wrong-account', ready: true, confirmationRequired: false, checks: [], + }); + + try { + await program.parseAsync(['node', 'cli', 'setup']); + + expect(terminal.readText).toHaveBeenCalledWith(expect.stringContaining('Is this the account you intended to configure with?')); + expect(runLocalAction).not.toHaveBeenCalled(); + expect(process.exitCode).toBe(1); + expect(terminal.close).toHaveBeenCalledTimes(1); + } finally { + createTerminal.mockRestore(); + } + }); + it('calls runLocalAction with INITIAL_SETUP', async () => { await program.parseAsync([ 'node', diff --git a/src/application/policies/setup_pat_creation_url_policy.ts b/src/application/policies/setup_pat_creation_url_policy.ts index e37ddd3fc..0898deb6f 100644 --- a/src/application/policies/setup_pat_creation_url_policy.ts +++ b/src/application/policies/setup_pat_creation_url_policy.ts @@ -19,6 +19,7 @@ const QUERY_PERMISSIONS: Readonly Date: Fri, 25 Sep 2026 12:30:09 +0200 Subject: [PATCH 04/50] codex-setup-temporary-github-auth: Guide setup PAT grants from preflight intent --- README.md | 2 +- build/cli/index.js | 329 +++++++++-- docs/authentication.mdx | 13 +- docs/configuration.mdx | 7 +- docs/development/architecture.mdx | 7 +- docs/how-to-use.mdx | 12 +- .../operations/troubleshooting.mdx | 6 +- specs/CATALOG.md | 12 +- specs/catalog.json | 13 +- specs/guided-bot-pat-onboarding.md | 59 +- .../temporary-setup-operator-authorization.md | 511 ++++++++++++------ src/__tests__/cli.test.ts | 62 ++- .../__tests__/setup_pat_intent_policy.test.ts | 102 ++++ .../setup_questionnaire_policy.test.ts | 42 ++ .../policies/setup_pat_intent_policy.ts | 47 ++ .../policies/setup_questionnaire_policy.ts | 46 +- .../policies/setup_token_permission_policy.ts | 33 +- .../__tests__/setup_wizard_use_case.test.ts | 20 +- .../usecases/setup/setup_wizard_use_case.ts | 65 ++- src/cli/commands/setup.ts | 107 +++- src/cli/setup_credential_prompt_adapter.ts | 40 +- src/cli/setup_question_renderer.ts | 9 + src/domain/setup_questionnaire.ts | 3 + 23 files changed, 1251 insertions(+), 296 deletions(-) create mode 100644 src/application/policies/__tests__/setup_pat_intent_policy.test.ts create mode 100644 src/application/policies/setup_pat_intent_policy.ts diff --git a/README.md b/README.md index 812e6e263..7a6328c97 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ delete, replace, or force-push managed branches. The setup PAT entered by the operator is separate from the workflow `PAT` Secret. Interactive setup can guide creation of both via GitHub's prefilled PAT form: -the operator creates a temporary setup token, and the bot account creates the +picks the permission-affecting setup options first, then the operator creates a temporary setup token, and the bot account creates the persistent workflow token. GitHub handles account switching, 2FA, repository selection, and final creation; Copilot never creates or revokes either token. Use `copilot setup --dry-run` to inspect the plan before making local or remote diff --git a/build/cli/index.js b/build/cli/index.js index 92ae3905d..00cabc677 100755 --- a/build/cli/index.js +++ b/build/cli/index.js @@ -47785,6 +47785,60 @@ function buildSetupPatCreationUrl(input) { } +/***/ }), + +/***/ 30748: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.fixedSetupPatIntentQuestionIds = fixedSetupPatIntentQuestionIds; +exports.setupPatIntentNeedsOwnerKind = setupPatIntentNeedsOwnerKind; +exports.setupPatIntentOwnerConflict = setupPatIntentOwnerConflict; +const setup_token_permission_policy_1 = __nccwpck_require__(99590); +/** Local inputs with explicit precedence are decisions, not questions. */ +function fixedSetupPatIntentQuestionIds(overrides, skipVariables, skipSecrets) { + const fixed = []; + for (const feature of ['issues', 'pullRequests']) { + if (overrides.features?.[feature] !== undefined) + fixed.push(`features.${feature}`); + } + if (overrides.issueWorkflows?.enabled !== undefined) + fixed.push('issueWorkflows.enabled'); + if (overrides.pullRequestApproval?.mode !== undefined) + fixed.push('pullRequestApproval.mode'); + if (overrides.projects?.ids !== undefined) + fixed.push('projects.ids'); + if (overrides.createInitialTag !== undefined) + fixed.push('createInitialTag'); + if (skipVariables || overrides.manageRepositoryVariables !== undefined) + fixed.push('manageRepositoryVariables'); + if (skipSecrets || overrides.manageRepositorySecrets !== undefined) + fixed.push('manageRepositorySecrets'); + for (const kind of ['variables', 'secrets']) { + if (overrides.storage?.[kind]?.defaultScope !== undefined) + fixed.push(`storage.${kind}.defaultScope`); + if (overrides.storage?.[kind]?.preserveExisting !== undefined) + fixed.push(`storage.${kind}.preserveExisting`); + } + return fixed; +} +function setupPatIntentNeedsOwnerKind(configuration) { + return (0, setup_token_permission_policy_1.buildSetupPatIntentPermissionRequirements)(configuration, 'Organization') + .some(requirement => requirement.scope === 'organization') + || (configuration.manageRepositorySecrets && configuration.storage.secrets.preserveExisting) + || (configuration.manageRepositoryVariables && configuration.storage.variables.preserveExisting); +} +function setupPatIntentOwnerConflict(configuration, ownerKind) { + return ownerKind === 'User' && ((configuration.manageRepositorySecrets && (configuration.storage.secrets.defaultScope === 'organization' + || Object.values(configuration.storage.secrets.overrides).includes('organization'))) + || (configuration.manageRepositoryVariables && (configuration.storage.variables.defaultScope === 'organization' + || Object.values(configuration.storage.variables.overrides).includes('organization'))) + || configuration.projects.ids.trim().length > 0); +} + + /***/ }), /***/ 6009: @@ -47794,6 +47848,7 @@ function buildSetupPatCreationUrl(input) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createSetupQuestionnaire = createSetupQuestionnaire; +exports.createSetupPermissionIntentQuestionnaire = createSetupPermissionIntentQuestionnaire; exports.createSetupReviewState = createSetupReviewState; exports.transitionSetupQuestionnaire = transitionSetupQuestionnaire; exports.enterSetupConfirmation = enterSetupConfirmation; @@ -47804,10 +47859,26 @@ const setup_configuration_defaults_1 = __nccwpck_require__(23381); const issue_workflow_profile_1 = __nccwpck_require__(26744); const AGENT_PROVIDERS = ['codex', 'opencode', 'cursor']; const MODEL_PROVIDERS = ['openai', 'anthropic', 'google', 'openrouter', 'opencode', 'local']; +const PERMISSION_INTENT_QUESTION_IDS = new Set([ + 'features.issues', 'features.pullRequests', 'issueWorkflows.enabled', + 'pullRequestApproval.mode', 'projects.ids', 'createInitialTag', + 'manageRepositoryVariables', 'manageRepositorySecrets', + 'storage.variables.defaultScope', 'storage.variables.preserveExisting', + 'storage.secrets.defaultScope', 'storage.secrets.preserveExisting', +]); function createSetupQuestionnaire(configuration, context = {}) { const draft = (0, setup_configuration_clone_policy_1.cloneSetupConfiguration)(configuration); - const question = questions(draft, false, context)[0]; - return { stateId: question.stateId, draft, question, terminal: 'collecting', configureIndependently: false }; + const question = questions(draft, false, context, 'full')[0]; + return question + ? { stateId: question.stateId, draft, question, terminal: 'collecting', configureIndependently: false, phase: 'full' } + : { stateId: 'review', draft, terminal: 'review', configureIndependently: false, phase: 'full' }; +} +function createSetupPermissionIntentQuestionnaire(configuration, context = {}) { + const draft = (0, setup_configuration_clone_policy_1.cloneSetupConfiguration)(configuration); + const question = questions(draft, false, context, 'permission-intent')[0]; + return question + ? { stateId: question.stateId, draft, question, terminal: 'collecting', configureIndependently: false, phase: 'permission-intent', answeredQuestionIds: [] } + : { stateId: 'review', draft, terminal: 'review', configureIndependently: false, phase: 'permission-intent', answeredQuestionIds: [] }; } function createSetupReviewState(configuration) { return { @@ -47826,6 +47897,8 @@ function transitionSetupQuestionnaire(state, event, context = {}) { draft: (0, setup_configuration_clone_policy_1.cloneSetupConfiguration)(state.draft), terminal: 'cancelled', configureIndependently: state.configureIndependently, + phase: state.phase, + answeredQuestionIds: state.answeredQuestionIds, }; } const parsed = parseAnswer(state.question, event.value); @@ -47840,7 +47913,8 @@ function transitionSetupQuestionnaire(state, event, context = {}) { ? Boolean(parsed.value) : state.configureIndependently; const draft = applyAnswer(state.draft, state.question, parsed.value); - const nextQuestions = questions(draft, configureIndependently, context); + const answeredQuestionIds = [...(state.answeredQuestionIds ?? []), state.question.id]; + const nextQuestions = questions(draft, configureIndependently, context, state.phase ?? 'full'); const nextIndex = nextQuestions.findIndex((question) => question.id === state.question?.id); const next = nextQuestions[nextIndex + 1]; return next @@ -47850,8 +47924,10 @@ function transitionSetupQuestionnaire(state, event, context = {}) { question: next, terminal: 'collecting', configureIndependently, + phase: state.phase, + answeredQuestionIds, } - : { stateId: 'review', draft, terminal: 'review', configureIndependently }; + : { stateId: 'review', draft, terminal: 'review', configureIndependently, phase: state.phase, answeredQuestionIds }; } function enterSetupConfirmation(state) { if (state.terminal !== 'review') @@ -47887,8 +47963,10 @@ function setupQuestionnaireStateLabel(stateId) { cancelled: 'Cancelled', })[stateId]; } -function questions(draft, independently, context) { - return definitions().filter((definition) => definition.applies?.(draft, independently, context) ?? true) +function questions(draft, independently, context, phase) { + return definitions().filter((definition) => (phase === 'full' || PERMISSION_INTENT_QUESTION_IDS.has(definition.id)) + && !context.skipQuestionIds?.includes(definition.id) + && (definition.applies?.(draft, independently, context) ?? true)) .map((definition) => toQuestion(definition, draft, context)); } function definitions() { @@ -48161,6 +48239,13 @@ function applyAnswer(configuration, question, value) { const draft = (0, setup_configuration_clone_policy_1.cloneSetupConfiguration)(configuration); if (question.id === 'agents.configureIndependently') return draft; + if (question.id === 'features.issues' && value === false) { + draft.features.issues = false; + draft.features.release = false; + draft.features.hotfix = false; + draft.issueWorkflows = (0, issue_workflow_profile_1.createIssueWorkflowProfile)([]); + return draft; + } if (question.id === 'features.pullRequests' && value === false) { draft.features.pullRequests = false; draft.pullRequestApproval = { ...draft.pullRequestApproval, mode: 'off' }; @@ -48352,6 +48437,8 @@ function unverifiable(requirement, message) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildSetupPatPermissionRequirements = buildSetupPatPermissionRequirements; exports.buildConfiguredSetupPatPermissionRequirements = buildConfiguredSetupPatPermissionRequirements; +exports.buildSetupPatIntentPermissionRequirements = buildSetupPatIntentPermissionRequirements; +exports.buildSetupPatIntentUncertainty = buildSetupPatIntentUncertainty; exports.buildWorkflowPatPermissionRequirements = buildWorkflowPatPermissionRequirements; exports.normalizePermissionRequirements = normalizePermissionRequirements; const setup_configuration_plan_1 = __nccwpck_require__(87770); @@ -48389,6 +48476,28 @@ function buildSetupPatPermissionRequirements() { * selected setup operation or its read-only preflight. */ function buildConfiguredSetupPatPermissionRequirements(configuration, remote) { + return buildSetupPatRequirements(configuration, remote?.ownerType === 'Organization', remote); +} +/** Grants justified by local choices alone; remote-only conditions stay unresolved. */ +function buildSetupPatIntentPermissionRequirements(configuration, ownerKind) { + return buildSetupPatRequirements(configuration, ownerKind === 'Organization'); +} +function buildSetupPatIntentUncertainty(configuration, ownerKind) { + const unknown = []; + if (configuration.manageRepositorySecrets) { + unknown.push('Existing managed Secrets may require repository Actions write for credential-health checks. A confirmed missing health workflow may also require repository Contents write and Workflows write.'); + } + if (ownerKind === 'Organization') { + for (const kind of ['secrets', 'variables']) { + const managed = kind === 'secrets' ? configuration.manageRepositorySecrets : configuration.manageRepositoryVariables; + if (managed && configuration.storage[kind].preserveExisting && configuration.storage[kind].defaultScope === 'repository') { + unknown.push(`Inherited organization ${kind} may require organization ${kind === 'secrets' ? 'Secrets' : 'Variables'} write after inventory inspection.`); + } + } + } + return unknown; +} +function buildSetupPatRequirements(configuration, organization, remote) { const repositorySecretNames = (0, setup_credential_requirement_policy_1.buildSetupCredentialRequirements)(configuration) .map(credential => credential.name); const repositoryVariableNames = (0, setup_configuration_plan_1.buildSetupRepositoryVariables)(configuration) @@ -48409,7 +48518,6 @@ function buildConfiguredSetupPatPermissionRequirements(configuration, remote) { const needsCredentialHealth = configuration.manageRepositorySecrets && hasExistingCredential; const needsCredentialHealthBootstrap = needsCredentialHealth && remote?.credentialHealthWorkflow === 'missing'; - const organization = remote?.ownerType === 'Organization'; return normalizePermissionRequirements([ requirement({ role: 'setup', scope: 'repository', permission: 'Metadata', level: 'read', reason: 'Resolve repository identity and visibility.', probe: 'metadata' }), requirement({ role: 'setup', scope: 'repository', permission: 'Contents', level: 'read', reason: 'Inspect installed workflows and repository files.', probe: 'contents' }), @@ -55586,6 +55694,7 @@ exports.SetupTokenPermissionsUseCase = SetupTokenPermissionsUseCase; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SetupWizardUseCase = void 0; +exports.buildInitialSetupConfiguration = buildInitialSetupConfiguration; const application_error_1 = __nccwpck_require__(75999); const setup_configuration_policy_1 = __nccwpck_require__(56637); const setup_questionnaire_policy_1 = __nccwpck_require__(6009); @@ -55597,24 +55706,9 @@ class SetupWizardUseCase { this.dependencies = dependencies; } async execute(request) { - const effectiveOverrides = request.mode === 'non-interactive' - && request.overrides?.repositoryAgentGuidance?.agentsPointer === undefined - ? { - ...request.overrides, - repositoryAgentGuidance: { - ...request.overrides?.repositoryAgentGuidance, - agentsPointer: 'create-if-missing', - }, - } - : request.overrides; - const defaults = (0, setup_configuration_policy_1.mergeSetupConfiguration)((0, setup_configuration_policy_1.mergeSetupConfiguration)((0, setup_configuration_policy_1.createDefaultSetupConfiguration)(), { pullRequestApproval: pull_request_approval_policy_1.DEFAULT_PULL_REQUEST_APPROVAL_POLICY }), { - ...effectiveOverrides, - ...(request.skipRepositoryVariables ? { manageRepositoryVariables: false } : {}), - ...(request.skipRepositorySecrets ? { manageRepositorySecrets: false } : {}), - }); - if (defaults.features.pullRequests === false && effectiveOverrides?.pullRequestApproval?.mode === undefined) { - defaults.pullRequestApproval = { ...defaults.pullRequestApproval, mode: 'off' }; - } + const defaults = buildInitialSetupConfiguration(request); + const effectiveOverrides = request.overrides; + const initial = request.permissionIntent ? (0, setup_configuration_clone_policy_1.cloneSetupConfiguration)(request.permissionIntent.draft) : defaults; let remoteConfiguration; if (request.remoteTarget) { try { @@ -55624,18 +55718,19 @@ class SetupWizardUseCase { remoteConfiguration = unavailableRemoteConfiguration(); } } - const defaultValidationErrors = (0, setup_configuration_policy_1.validateSetupConfiguration)(defaults, { allowIncompleteApproval: true }); + const defaultValidationErrors = (0, setup_configuration_policy_1.validateSetupConfiguration)(initial, { allowIncompleteApproval: true }); if (defaultValidationErrors.length > 0) { throw new application_error_1.ApplicationError('configuration.invalid', `Invalid setup configuration:\n${defaultValidationErrors.map((error) => `- ${error}`).join('\n')}`); } const context = { ...(remoteConfiguration ? { remote: remoteConfiguration } : {}), - variableNames: (0, setup_configuration_policy_1.buildSetupRepositoryVariables)(defaults).map((variable) => variable.name), - secretNames: (0, setup_configuration_policy_1.buildSetupCredentialRequirements)(defaults).map((requirement) => requirement.name), + variableNames: (0, setup_configuration_policy_1.buildSetupRepositoryVariables)(initial).map((variable) => variable.name), + secretNames: (0, setup_configuration_policy_1.buildSetupCredentialRequirements)(initial).map((requirement) => requirement.name), + ...(request.permissionIntent ? { skipQuestionIds: request.permissionIntent.answeredQuestionIds } : {}), }; const questionnaire = request.mode === 'interactive' - ? await this.collectInteractive(defaults, context) - : (0, setup_questionnaire_policy_1.createSetupReviewState)(defaults); + ? await this.collectInteractive(initial, context) + : (0, setup_questionnaire_policy_1.createSetupReviewState)(initial); if (questionnaire.terminal === 'cancelled') { return { status: 'cancelled', @@ -55776,6 +55871,27 @@ class SetupWizardUseCase { } } exports.SetupWizardUseCase = SetupWizardUseCase; +function buildInitialSetupConfiguration(request) { + const effectiveOverrides = request.mode === 'non-interactive' + && request.overrides?.repositoryAgentGuidance?.agentsPointer === undefined + ? { + ...request.overrides, + repositoryAgentGuidance: { + ...request.overrides?.repositoryAgentGuidance, + agentsPointer: 'create-if-missing', + }, + } + : request.overrides; + const defaults = (0, setup_configuration_policy_1.mergeSetupConfiguration)((0, setup_configuration_policy_1.mergeSetupConfiguration)((0, setup_configuration_policy_1.createDefaultSetupConfiguration)(), { pullRequestApproval: pull_request_approval_policy_1.DEFAULT_PULL_REQUEST_APPROVAL_POLICY }), { + ...effectiveOverrides, + ...(request.skipRepositoryVariables ? { manageRepositoryVariables: false } : {}), + ...(request.skipRepositorySecrets ? { manageRepositorySecrets: false } : {}), + }); + if (defaults.features.pullRequests === false && effectiveOverrides?.pullRequestApproval?.mode === undefined) { + defaults.pullRequestApproval = { ...defaults.pullRequestApproval, mode: 'off' }; + } + return defaults; +} /** An unavailable read is explicit, never an authoritative empty inventory. */ function unavailableRemoteConfiguration() { return { @@ -65210,6 +65326,9 @@ const cli_context_1 = __nccwpck_require__(21307); const setup_policy_1 = __nccwpck_require__(28732); const setup_config_file_1 = __nccwpck_require__(11196); const setup_1 = __nccwpck_require__(36888); +const setup_wizard_use_case_1 = __nccwpck_require__(43433); +const setup_questionnaire_policy_1 = __nccwpck_require__(6009); +const setup_pat_intent_policy_1 = __nccwpck_require__(30748); const setup_configuration_policy_1 = __nccwpck_require__(56637); const setup_token_permission_policy_1 = __nccwpck_require__(99590); const setup_credentials_composition_root_1 = __nccwpck_require__(69084); @@ -65291,19 +65410,78 @@ function registerSetupCommand(program) { return; } (0, logger_1.logInfo)(`📦 Repository: ${gitInfo.owner}/${gitInfo.repo}`); - const setupPatPermissions = (0, setup_token_permission_policy_1.buildSetupPatPermissionRequirements)(); + const overrides = loadSetupOverrides(options); + let setupPatPermissions = (0, setup_token_permission_policy_1.buildSetupPatPermissionRequirements)(); permissionPresenter.showRequirements('setup', setupPatPermissions); let token = (0, setup_files_1.getSetupToken)(cwd, options.token); let setupPatAccount; + let permissionIntent; + let assertedOwnerKind; if (!token && !options.nonInteractive && !options.dryRun) { - try { - credentialPrompt.configureSetupPatGuide((0, setup_pat_creation_url_policy_1.buildSetupPatCreationUrl)({ - role: 'setup', owner: gitInfo.owner, repository: gitInfo.repo, expiresIn: 1, - requirements: setupPatPermissions, - })); - } - catch { - (0, logger_1.logInfo)('A guided setup PAT link is unavailable for this repository or permission set. Enter a manually created PAT using the table above.'); + if (await credentialPrompt.chooseSetupPatMethod() === 'guided') { + const fixedQuestionIds = (0, setup_pat_intent_policy_1.fixedSetupPatIntentQuestionIds)(overrides, Boolean(options.skipVariables), Boolean(options.skipSecrets)); + let draft = (0, setup_wizard_use_case_1.buildInitialSetupConfiguration)({ + mode: 'interactive', overrides, + skipRepositoryVariables: Boolean(options.skipVariables), + skipRepositorySecrets: Boolean(options.skipSecrets), + }); + while (true) { + const context = { skipQuestionIds: fixedQuestionIds }; + const collector = new setup_1.SetupQuestionnaireController(terminal, new setup_question_renderer_1.ConsoleSetupQuestionRenderer('permission-intent')); + const intent = await collector.collect((0, setup_questionnaire_policy_1.createSetupPermissionIntentQuestionnaire)(draft, context), context); + if (intent.terminal === 'cancelled') + throw new setup_credential_prompt_adapter_1.SetupTerminalCancelledError(); + draft = intent.draft; + const ownerKind = (0, setup_pat_intent_policy_1.setupPatIntentNeedsOwnerKind)(draft) + ? await credentialPrompt.chooseSetupOwnerKind() : 'User'; + if (ownerKind === 'unknown') { + (0, logger_1.logInfo)('Owner type was not confirmed. Use the manual PAT table, or check whether the GitHub owner is an organization before retrying guided setup.'); + credentialPrompt.useManualSetupPat(); + break; + } + if ((0, setup_pat_intent_policy_1.setupPatIntentOwnerConflict)(draft, ownerKind)) { + (0, logger_1.logInfo)('This plan selects organization storage or Projects, but the owner was declared a personal account. Revise the choices or use the manual PAT path.'); + } + const intentErrors = (0, setup_configuration_policy_1.validateSetupConfiguration)(draft, { allowIncompleteApproval: true }); + if (intentErrors.length > 0) { + (0, logger_1.logInfo)(`The selected local configuration needs correction before a guided link can be generated:\n${intentErrors.map(item => ` - ${item}`).join('\n')}`); + } + const preview = (0, setup_token_permission_policy_1.buildSetupPatIntentPermissionRequirements)(draft, ownerKind); + (0, logger_1.logInfo)('Permission intent:'); + (0, logger_1.logInfo)(` Initial tag: ${draft.createInitialTag ? 'yes' : 'no'}; issue workflows: ${draft.features.issues ? draft.issueWorkflows.enabled.join(', ') || 'none' : 'disabled'}; PR approval: ${draft.pullRequestApproval.mode}`); + (0, logger_1.logInfo)(` Secrets: ${draft.manageRepositorySecrets ? draft.storage.secrets.defaultScope : 'off'}; Variables: ${draft.manageRepositoryVariables ? draft.storage.variables.defaultScope : 'off'}; Projects: ${draft.projects.ids.trim() || 'none'}`); + permissionPresenter.showRequirements('setup', preview); + const uncertain = (0, setup_token_permission_policy_1.buildSetupPatIntentUncertainty)(draft, ownerKind); + if (uncertain.length) + (0, logger_1.logInfo)(`May need after GitHub inspection:\n${uncertain.map(item => ` - ${item}`).join('\n')}`); + const decision = await credentialPrompt.reviewSetupPatIntent(); + if (decision === 'manual') { + credentialPrompt.useManualSetupPat(); + break; + } + if (decision === 'revise') + continue; + if ((0, setup_pat_intent_policy_1.setupPatIntentOwnerConflict)(draft, ownerKind) || intentErrors.length > 0) { + throw new application_error_1.ApplicationError('configuration.invalid', 'Correct the reported setup intent or local --config/flags, then retry guided setup. No PAT was requested.'); + } + try { + const url = (0, setup_pat_creation_url_policy_1.buildSetupPatCreationUrl)({ + role: 'setup', owner: gitInfo.owner, repository: gitInfo.repo, expiresIn: 1, + requirements: preview, + }); + credentialPrompt.configureSetupPatGuide(url); + setupPatPermissions = preview; + assertedOwnerKind = ownerKind; + permissionIntent = { draft, answeredQuestionIds: [...new Set([...fixedQuestionIds, ...(intent.answeredQuestionIds ?? [])])] }; + } + catch (error) { + if (!(error instanceof setup_pat_creation_url_policy_1.UnsupportedSetupPatLinkError)) + throw error; + (0, logger_1.logInfo)('A guided setup PAT link is unavailable for this owner or permission set. Enter a manually created PAT using the table above.'); + credentialPrompt.useManualSetupPat(); + } + break; + } } } if (!token && !options.nonInteractive && !options.dryRun) @@ -65345,6 +65523,21 @@ function registerSetupCommand(program) { const auditConfiguredSetupPat = async (configuration, remoteConfiguration) => { const configuredSetupPatPermissions = (0, setup_token_permission_policy_1.buildConfiguredSetupPatPermissionRequirements)(configuration, remoteConfiguration); permissionPresenter.showRequirements('setup', configuredSetupPatPermissions); + if (assertedOwnerKind && remoteConfiguration && remoteConfiguration.ownerType !== 'Unknown' + && remoteConfiguration.ownerType !== assertedOwnerKind) { + (0, logger_1.logInfo)(`The owner was declared ${assertedOwnerKind}, but GitHub reports ${remoteConfiguration.ownerType}. The guided link is no longer valid for this plan.`); + if (credentialPrompt.usedGuidedSetupPat) + credentialPrompt.showUpdatedSetupPatLink((0, setup_pat_creation_url_policy_1.buildSetupPatCreationUrl)({ + role: 'setup', owner: gitInfo.owner, repository: gitInfo.repo, expiresIn: 1, + requirements: configuredSetupPatPermissions, + }), 'final', setupPatPermissionDelta(setupPatPermissions, configuredSetupPatPermissions)); + return { status: 'blocked', errors: ['Repository owner type differs from the pre-PAT selection. Rerun setup with the correct owner type and PAT.'] }; + } + if (credentialPrompt.usedGuidedSetupPat) { + const removed = setupPatPermissionDelta(configuredSetupPatPermissions, setupPatPermissions); + if (removed.length) + (0, logger_1.logInfo)(`The final plan no longer requires grants suggested earlier: ${removed.join(', ')}. Your PAT may have excess access; replace it in GitHub if least privilege is required.`); + } if (!token) return { status: 'accepted' }; const permissionReport = await tokenPermissions.inspect({ @@ -65360,7 +65553,7 @@ function registerSetupCommand(program) { credentialPrompt.showUpdatedSetupPatLink((0, setup_pat_creation_url_policy_1.buildSetupPatCreationUrl)({ role: 'setup', owner: gitInfo.owner, repository: gitInfo.repo, expiresIn: 1, requirements: configuredSetupPatPermissions, - }), 'final'); + }), 'final', setupPatPermissionDelta(setupPatPermissions, configuredSetupPatPermissions)); return { status: 'blocked', errors: [ 'The setup PAT has missing or unconfirmed access required by the approved setup plan. Grant or explicitly confirm the permissions shown above and retry.', ] }; @@ -65381,10 +65574,10 @@ function registerSetupCommand(program) { mergeQueueReadiness: (0, setup_doctor_composition_root_1.createSetupMergeQueueReadinessUseCase)(), approvalReadiness: new setup_approval_readiness_adapter_1.GithubSetupApprovalReadinessAdapter(), }); - const overrides = loadSetupOverrides(options); const result = await wizard.execute({ mode: options.nonInteractive ? 'non-interactive' : 'interactive', overrides, + ...(permissionIntent ? { permissionIntent } : {}), skipRepositoryVariables: Boolean(options.skipVariables), skipRepositorySecrets: Boolean(options.skipSecrets), previewOnly: Boolean(options.dryRun), @@ -65496,6 +65689,14 @@ function collectSecret(value, previous) { function collectApprovalCheck(value, previous) { return [...previous, value]; } +function setupPatPermissionDelta(before, after) { + const previous = new Map(before.filter(item => item.applicability === 'required') + .map(item => [`${item.scope}:${item.permission.toLowerCase()}`, item.level])); + return after.filter(item => item.applicability === 'required' + && (previous.get(`${item.scope}:${item.permission.toLowerCase()}`) === undefined + || (previous.get(`${item.scope}:${item.permission.toLowerCase()}`) === 'read' && item.level === 'write'))) + .map(item => `${item.scope} ${item.permission} ${item.level}`); +} function loadSetupOverrides(options) { const fromFile = options.config ? (0, setup_config_file_1.loadSetupConfigurationOverrides)(options.config) : {}; const fromFlags = {}; @@ -66139,9 +66340,29 @@ class SetupCredentialPromptAdapter { this.credentialValues = credentialValues; this.confirmUnverifiableWritePermissions = confirmUnverifiableWritePermissions; this.guidedSetup = false; + this.setupMethodChosen = false; } configureSetupPatGuide(url) { this.setupPatGuide = url; } get usedGuidedSetupPat() { return this.guidedSetup; } + async chooseSetupPatMethod() { + if (!this.terminal) + return 'manual'; + this.setupMethodChosen = true; + this.guidedSetup = (await this.readChoice('How would you like to provide the setup PAT?', ['guided link', 'manual PAT'], 'guided link')) === 'guided link'; + return this.guidedSetup ? 'guided' : 'manual'; + } + useManualSetupPat() { this.guidedSetup = false; this.setupPatGuide = undefined; this.setupMethodChosen = true; } + async chooseSetupOwnerKind() { + if (!this.terminal) + return 'unknown'; + const choice = await this.readChoice('Is the GitHub repository owner an organization or a personal account?', ['organization', 'personal account', 'not sure']); + return choice === 'organization' ? 'Organization' : choice === 'personal account' ? 'User' : 'unknown'; + } + async reviewSetupPatIntent() { + if (!this.terminal) + return 'manual'; + return await this.readChoice('Review these intended grants before opening GitHub. Continue, revise choices, or enter a PAT manually?', ['continue', 'revise', 'manual']); + } configureWorkflowPatGuide(url, resolveIdentity) { this.workflowPatGuide = url; this.resolveBotIdentity = resolveIdentity; @@ -66156,30 +66377,37 @@ class SetupCredentialPromptAdapter { return (await this.readChoice('Is this the account you intended to configure with?', ['yes', 'no'], 'yes')) === 'yes'; } showSetupPatCleanupReminder() { - if (!this.guidedSetup) + if (!this.guidedSetup || !this.setupPatGuide) return; console.log((0, setup_prompt_rendering_1.renderBox)('The setup PAT was not revoked automatically. After setup finishes or is cancelled, delete it in GitHub → Settings → Developer settings → Personal access tokens. Ending this process does not remove the token from GitHub.', 'Revoke temporary setup PAT', 33)); console.log('https://github.com/settings/personal-access-tokens'); } - showUpdatedSetupPatLink(url, stage) { + showUpdatedSetupPatLink(url, stage, delta) { if (!this.guidedSetup) return; console.log((0, setup_prompt_rendering_1.renderBox)(stage === 'bootstrap' ? 'The setup PAT did not pass the initial access check; no setup plan has been applied. Review its grants in GitHub or create a replacement with this link, then rerun. Select only the intended repository in GitHub.' : 'The selected plan requires access this PAT did not prove; no plan mutation has started. Update its grants in GitHub or create a replacement with this link, then rerun. Select only the intended repository in GitHub.', stage === 'bootstrap' ? 'Setup PAT access needs attention' : 'Setup PAT permissions changed', 33)); + if (delta?.length) + console.log(delta.map(item => ` - ${item}`).join('\n')); console.log(url); } async requestSetupPat() { if (!this.terminal) return undefined; - if (this.setupPatGuide) { + if (this.setupPatGuide && !this.setupMethodChosen) { this.guidedSetup = (await this.readChoice('How would you like to provide the setup PAT?', ['guided link', 'manual PAT'], 'guided link')) === 'guided link'; if (this.guidedSetup) { - console.log((0, setup_prompt_rendering_1.renderBox)('Provisional link: Open this GitHub link in your browser, sign in as the account configuring this repository, complete any 2FA or SSO, and review the prefilled fine-grained permissions. GitHub owns token creation; Copilot never handles your web session. Select ONLY this repository manually. The permissions may need updating after the setup questionnaire.', 'Create setup PAT in GitHub', 33)); + console.log((0, setup_prompt_rendering_1.renderBox)('Provisional link: Open this GitHub link in your browser, sign in as the account configuring this repository, complete any 2FA or SSO, and review the prefilled fine-grained permissions. GitHub owns token creation; Copilot never handles your web session. Change All repositories to Only select repositories and select ONLY this repository. Remote inspection may require a corrected token later.', 'Create setup PAT in GitHub', 33)); console.log(this.setupPatGuide); console.log('Copy the one-time token from GitHub and paste it below. It is hidden and used only for this setup run.'); } } + if (this.setupMethodChosen && this.guidedSetup && this.setupPatGuide) { + console.log((0, setup_prompt_rendering_1.renderBox)('Open this GitHub link as the account configuring this repository; complete any 2FA or SSO. Review the prefilled grants. Change All repositories to Only select repositories and select ONLY this repository. GitHub creates the PAT; Copilot does not handle your browser session. Remote inspection may require a corrected token later.', 'Create setup PAT in GitHub', 33)); + console.log(this.setupPatGuide); + console.log('Copy the one-time token from GitHub and paste it below. It is hidden and used only for this setup run.'); + } console.log((0, setup_prompt_rendering_1.renderBox)('Enter a GitHub setup PAT. It is used in memory for this run only and is never stored. The workflow PAT is a different bot-account token and is requested separately.', 'Setup PAT', 33)); return this.readSecret('Setup PAT'); } @@ -66284,11 +66512,11 @@ class SetupCredentialPromptAdapter { const result = await this.terminal.readText([ label, ...lines, - `Select 1-${choices.length} ${(0, setup_prompt_rendering_1.color)(`[${choices.indexOf(defaultValue) + 1}]`, 90)}: `, + `Select 1-${choices.length}${defaultValue ? ` ${(0, setup_prompt_rendering_1.color)(`[${choices.indexOf(defaultValue) + 1}]`, 90)}` : ''}: `, ].join('\n')); if (result.kind !== 'value') throw new SetupTerminalCancelledError(); - if (!result.value.trim()) + if (!result.value.trim() && defaultValue) return defaultValue; const index = Number(result.value) - 1; if (Number.isInteger(index) && choices[index]) @@ -66616,7 +66844,14 @@ exports.ConsoleSetupQuestionRenderer = void 0; const setup_prompt_rendering_1 = __nccwpck_require__(83434); const setup_questionnaire_policy_1 = __nccwpck_require__(6009); class ConsoleSetupQuestionRenderer { + constructor(phase = 'full') { + this.phase = phase; + } showIntroduction() { + if (this.phase === 'permission-intent') { + console.log((0, setup_prompt_rendering_1.renderBox)('First, choose the setup options that affect your temporary PAT permissions. These answers will carry into the full wizard and will not be asked again. No GitHub changes happen in this step.', 'Setup PAT permission intent')); + return; + } console.log((0, setup_prompt_rendering_1.renderBox)('This wizard configures repository workflows, GitHub Actions resources, AI agents, and operational defaults.\n\nThe setup PAT is used in memory only. Runtime credentials are collected separately after the plan is approved.', 'Copilot Setup')); } showState(stateId) { diff --git a/docs/authentication.mdx b/docs/authentication.mdx index 271b28fb3..65f8d42c2 100644 --- a/docs/authentication.mdx +++ b/docs/authentication.mdx @@ -15,8 +15,14 @@ The setup PAT and workflow PAT may have different owners and permissions. Do not ## Assisted creation in the terminal When `copilot setup` needs a PAT interactively, it offers a guided link (the -default) or manual entry. The setup link contains only bootstrap permissions -known before the questionnaire; it is **provisional**. If the final plan needs +default) or manual entry. In guided mode it first asks the setup choices that +determine PAT permissions: issue workflows, initial tag, Secret and Variable +management and storage scope, PR approval mode, and Projects. Choices already +fixed by flags or `--config` are not asked. You review the resulting grants +before the link appears; these answers carry into the full wizard without being +asked twice. The link is still **provisional** for facts that require GitHub +inspection, such as existing Secrets, inherited organization resources, and a +missing credential-health workflow. If the final plan needs additional grants, setup stops before applying it and prints a corrected link. Update the PAT in GitHub or create a replacement, then rerun setup. Guided setup shows the account returned by GitHub and asks you to confirm it. @@ -29,7 +35,8 @@ this extra identity binding. A wrong bot account blocks installation. Both links use GitHub's [documented fine-grained PAT form](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens). They do not sign you in, complete 2FA, generate or revoke a token, or choose -an individual repository. Check the active browser account, select only the +an individual repository. Check the active browser account, change **All +repositories** to **Only select repositories**, select only the target repository, and review the final GitHub form. A guided setup PAT uses a one-day suggested expiry; delete it yourself in [GitHub PAT Settings](https://github.com/settings/personal-access-tokens) afterward. The bot PAT uses a 90-day suggested expiry, may be shortened by diff --git a/docs/configuration.mdx b/docs/configuration.mdx index eb7557110..ef64f6629 100644 --- a/docs/configuration.mdx +++ b/docs/configuration.mdx @@ -185,13 +185,14 @@ storage: OPENAI_API_KEY: organization ``` -The immutable questionnaire first inspects the repository and reports repository-scoped resources, organization resources available to that repository, repository visibility, and access errors. It then asks separately about Secret and Variable storage. Each accepted answer creates a fresh configuration snapshot; defaults, overrides, prior answers, and the result do not share mutable nested references. Repository resources take precedence over organization resources. With `preserveExisting: true`, an effective organization resource is inherited instead of being shadowed by a new repository value; add a name under `storage.secrets.overrides` or `storage.variables.overrides` when a repository-specific value is intentional. +Guided setup asks the permission-affecting Secret and Variable management and default-scope questions before the setup PAT is created. After token authentication, the remaining questionnaire inspects the repository and reports repository-scoped resources, organization resources available to that repository, visibility, and access errors. Remote-dependent inherited-resource overrides are asked then; they may require a corrected PAT. Each accepted answer creates a fresh configuration snapshot; defaults, overrides, prior answers, and the result do not share mutable nested references. Repository resources take precedence over organization resources. With `preserveExisting: true`, an effective organization resource is inherited instead of being shadowed by a new repository value; add a name under `storage.secrets.overrides` or `storage.variables.overrides` when a repository-specific value is intentional. Organization storage is available only for organization-owned repositories and requires organization Actions permissions on the setup PAT. If only one class should be global, set that class to `organization` and leave the other at `repository`. `--skip-secrets` and `--skip-variables` disable their respective setup operations without changing the other class. Interactive PAT guidance does not add configuration keys: it is a one-run -choice at each hidden prompt. The initial setup-PAT form link contains only -bootstrap grants and may need correction after the questionnaire. The bot-PAT +choice at each hidden prompt. The setup-PAT form link contains grants derived +from reviewed local intent; remote-only conditions are disclosed separately +and may require correction after inspection. The bot-PAT link is built from the final workflow permission policy and suggests a 90-day expiry; the bot account owner must renew it and replace Secret `PAT` before expiration. Manual and non-interactive token inputs keep their existing diff --git a/docs/development/architecture.mdx b/docs/development/architecture.mdx index 45017f580..1111b8e4a 100644 --- a/docs/development/architecture.mdx +++ b/docs/development/architecture.mdx @@ -152,6 +152,7 @@ The setup policy is intentionally split by responsibility: resolution and effective-resource preservation. - `setup_configuration_clone_policy.ts` owns reference-isolated configuration copies. - `setup_questionnaire_policy.ts` owns setup states, questions, transitions, and answers. +- `setup_pat_intent_policy.ts` identifies setup choices fixed by local inputs and owner-kind conflicts. - `setup_configuration_plan.ts` owns the reviewable provisioning plan. - `setup_token_permission_policy.ts` owns the setup/workflow PAT permission catalogs, conditional capability projection, strongest-level normalization, @@ -161,7 +162,11 @@ The setup policy is intentionally split by responsibility: and Secrets. Assisted PAT creation uses the existing permission policy as its only grant -source. The pure `setup_pat_creation_url_policy.ts` maps required grants to +source. Guided setup runs a permission-intent phase of the same questionnaire +before token entry, projects local choices through the setup permission policy, +and carries the draft and answered IDs into the remaining questionnaire. The +projection leaves remote-only grants unresolved for the final audit. The pure +`setup_pat_creation_url_policy.ts` maps required grants to documented GitHub form parameters and rejects unsupported grants; it never receives token material. `setup.ts` wires the terminal choice and hidden input to that policy. In guided bot mode, the identity query adapter resolves the diff --git a/docs/how-to-use.mdx b/docs/how-to-use.mdx index 12c07b291..cb1ba6fac 100644 --- a/docs/how-to-use.mdx +++ b/docs/how-to-use.mdx @@ -40,9 +40,13 @@ Once installed, the `copilot` command is available globally. Repository-dependen Interactive `copilot setup` offers a guided GitHub link or manual entry for each PAT. The first link prepares a short-lived **setup PAT** for the person -configuring the repository; the second, after the setup plan, prepares the +configuring the repository. Before showing it, guided setup asks only the local +choices that affect its grants, shows an exact permission preview and notes +what remains unknown until GitHub is inspected. The later questionnaire reuses +those answers. The second link, after the setup plan, prepares the **workflow PAT** for the bot account. Open each link in the appropriate GitHub -account, complete GitHub's sign-in/2FA, **select only the intended repository** +account, complete GitHub's sign-in/2FA, change **All repositories** to **Only +select repositories**, and **select only the intended repository** on the form, review the grants, and paste the generated value into the hidden terminal prompt. The links prefill fields; they neither create a PAT nor select an individual repository. The setup PAT is suggested for one day and must be @@ -124,14 +128,14 @@ The complete command reference, including every supported option, is in [Workflo copilot setup ``` - Before applying the plan, the wizard securely asks for the setup PAT. For automation, pass it explicitly or through the environment: + In guided interactive setup, answer the short permission-intent questions and review the proposed grants before creating the setup PAT in GitHub. The wizard then securely asks for the token and continues with the remaining plan questions. For automation, pass it explicitly or through the environment: ```bash PERSONAL_ACCESS_TOKEN=your_setup_pat copilot setup --non-interactive --yes --skip-secrets # or: copilot setup --token your_setup_pat ``` - The wizard shows a reviewable plan and asks for confirmation. Its forward-only questionnaire keeps defaults and every answer immutable; cancel and rerun if you need to revise an earlier stage. `Ctrl-C` or end-of-input exits 130 with no writes, while declining the final plan exits 0 with no writes. Use `copilot setup --dry-run` to inspect the plan without a token or changes. + The wizard shows a reviewable plan and asks for confirmation. You may revise the pre-PAT intent before opening GitHub; after entering the PAT, the remaining questionnaire is forward-only. Cancel and rerun to revise an earlier stage. `Ctrl-C` or end-of-input exits 130 with no writes before application, while declining the final plan exits 0 with no writes. Use `copilot setup --dry-run` to inspect the plan without a token or changes. In automation, `--non-interactive` creates no terminal. `--yes` approves only the final plan: it does not supply a missing setup PAT, workflow credential, provider credential, target, organization prerequisite, or permission acknowledgement. If every required read is verified or positively operationally usable but safe probes cannot prove required writes, inspect the PAT settings first and pass the separate `--confirm-unverifiable-write-permissions` flag. It never bypasses missing or unusable unverifiable read access. diff --git a/docs/security-operations/operations/troubleshooting.mdx b/docs/security-operations/operations/troubleshooting.mdx index a407f7b2c..73f9b80d1 100644 --- a/docs/security-operations/operations/troubleshooting.mdx +++ b/docs/security-operations/operations/troubleshooting.mdx @@ -12,8 +12,10 @@ This guide helps you resolve common issues you might encounter while using Copil If the guided setup PAT belongs to the wrong account, decline the account confirmation, delete the unintended PAT in [GitHub PAT Settings](https://github.com/settings/personal-access-tokens), and rerun setup in the correct browser account. If the final permission table -requires more than the provisional link, use the corrected link printed by -setup and rerun; no approved setup mutation has started. A guided bot PAT +requires more than the reviewed local-intent link, inspect the named grant +delta, use the corrected link printed by setup, and rerun; no approved setup +mutation has started. If the final plan removes grants, your existing PAT may +have excess access; replace it for strict least privilege. A guided bot PAT created while signed into another account fails the numeric-ID check before the Secret is written. Delete that unused PAT and create one as the chosen bot. If setup fails after applying changes, inspect the Actions Secret name and diff --git a/specs/CATALOG.md b/specs/CATALOG.md index 1eb2b5483..9620242fa 100644 --- a/specs/CATALOG.md +++ b/specs/CATALOG.md @@ -17,8 +17,8 @@ debt or convert unknown historic intent into a design decision. | `execution-lifecycle` | Implemented | Shared GitHub Action lifecycle from event admission through durable user-facing results | [Execution admission, queueing, routing, and result publication](./execution-admission-queue-and-publication.md) + 3 companion | 84 paths · 2026-09-16 | | `architecture-quality-hardening` | Implemented | Close verified concurrency, error-contract, context-coupling, fan-out, setup/doctor, and provider-policy risks in dependency order | [Architecture quality and scalability hardening](./architecture-quality-and-scalability-hardening.md) + 1 companion | 72 paths · 2026-09-16 | | `setup-and-doctor` | Implemented | Plan, validate, provision, and audit a repository installation without exposing credentials | [Setup, configuration, credentials, and doctor](./setup-configuration-credentials-and-doctor.md) + 2 companion | 83 paths · 2026-09-24 | -| `guided-bot-pat-onboarding` | Proposed | Guide creation of the persistent workflow PAT using GitHub's official form, verify bot identity and grants, and install the approved Actions Secret | [Guided bot PAT onboarding](./guided-bot-pat-onboarding.md) | 22 paths · 2026-09-24 | -| `temporary-setup-operator-authorization` | Proposed | Guide creation of the one-run operator PAT through GitHub's official form, verify final setup access, and report user-owned deletion accurately | [Assisted setup PAT creation](./temporary-setup-operator-authorization.md) | 17 paths · 2026-09-24 | +| `guided-bot-pat-onboarding` | Proposed | Guide creation of the persistent workflow PAT using GitHub's official form, verify bot identity and grants, and install the approved Actions Secret | [Guided bot PAT onboarding](./guided-bot-pat-onboarding.md) | 22 paths · 2026-09-25 | +| `temporary-setup-operator-authorization` | Proposed | Guide creation of the one-run operator PAT through GitHub's official form, verify final setup access, and report user-owned deletion accurately | [Assisted setup PAT creation](./temporary-setup-operator-authorization.md) | 26 paths · 2026-09-25 | | `issue-start-and-sdd-readiness` | Implemented | Start every admitted issue with one explicit signal and publish a validated SDD before eligible Action-managed branch work | [Uniform issue start and pre-branch SDD readiness](./issue-start-and-branch-readiness.md) + 1 companion | 51 paths · 2026-09-17 | | `managed-issue-lifecycle` | As-built baseline | Convert typed issues into traceable work branches, project state, and lifecycle state | [Managed issue and branch lifecycle](./managed-issue-and-branch-lifecycle.md) | 31 paths · 2026-09-17 | | `comment-automation` | Implemented | Admit only explicit commands or exact mentions, then route them while protecting repository mutations | [Comment automation and authorization](./comment-automation-and-authorization.md) | 61 paths · 2026-09-21 | @@ -113,7 +113,7 @@ debt or convert unknown historic intent into a design decision. ### `guided-bot-pat-onboarding` — Guided bot PAT onboarding - Owner: Copilot maintainers -- Last verified: 2026-09-24 +- Last verified: 2026-09-25 - Specifications: [`specs/guided-bot-pat-onboarding.md`](./guided-bot-pat-onboarding.md) - Workflows: Not applicable for this capability. - Entrypoints: [`src/cli/commands/setup.ts`](../src/cli/commands/setup.ts) @@ -124,12 +124,12 @@ debt or convert unknown historic intent into a design decision. ### `temporary-setup-operator-authorization` — Assisted setup PAT creation - Owner: Copilot maintainers -- Last verified: 2026-09-24 +- Last verified: 2026-09-25 - Specifications: [`specs/temporary-setup-operator-authorization.md`](./temporary-setup-operator-authorization.md) - Workflows: Not applicable for this capability. - Entrypoints: [`src/cli/commands/setup.ts`](../src/cli/commands/setup.ts) -- Core code: [`src/cli/setup_credential_prompt_adapter.ts`](../src/cli/setup_credential_prompt_adapter.ts) · [`src/application/policies/setup_pat_creation_url_policy.ts`](../src/application/policies/setup_pat_creation_url_policy.ts) · [`src/application/usecases/setup/setup_token_permissions_use_case.ts`](../src/application/usecases/setup/setup_token_permissions_use_case.ts) · [`src/infrastructure/setup_token_permission_query_adapter.ts`](../src/infrastructure/setup_token_permission_query_adapter.ts) · [`src/utils/setup_files.ts`](../src/utils/setup_files.ts) -- Tests: [`src/cli/__tests__/setup_presenters.test.ts`](../src/cli/__tests__/setup_presenters.test.ts) · [`src/application/policies/__tests__/setup_pat_creation_url_policy.test.ts`](../src/application/policies/__tests__/setup_pat_creation_url_policy.test.ts) · [`src/application/usecases/setup/__tests__/setup_token_permissions_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_token_permissions_use_case.test.ts) · [`src/infrastructure/__tests__/setup_token_permission_query_adapter.test.ts`](../src/infrastructure/__tests__/setup_token_permission_query_adapter.test.ts) · [`src/utils/__tests__/setup_files.test.ts`](../src/utils/__tests__/setup_files.test.ts) +- Core code: [`src/cli/setup_credential_prompt_adapter.ts`](../src/cli/setup_credential_prompt_adapter.ts) · [`src/application/policies/setup_pat_intent_policy.ts`](../src/application/policies/setup_pat_intent_policy.ts) · [`src/application/policies/setup_questionnaire_policy.ts`](../src/application/policies/setup_questionnaire_policy.ts) · [`src/application/policies/setup_token_permission_policy.ts`](../src/application/policies/setup_token_permission_policy.ts) · [`src/application/policies/setup_pat_creation_url_policy.ts`](../src/application/policies/setup_pat_creation_url_policy.ts) · [`src/application/usecases/setup/setup_wizard_use_case.ts`](../src/application/usecases/setup/setup_wizard_use_case.ts) · [`src/application/usecases/setup/setup_token_permissions_use_case.ts`](../src/application/usecases/setup/setup_token_permissions_use_case.ts) · [`src/infrastructure/setup_token_permission_query_adapter.ts`](../src/infrastructure/setup_token_permission_query_adapter.ts) · [`src/utils/setup_files.ts`](../src/utils/setup_files.ts) +- Tests: [`src/cli/__tests__/setup_presenters.test.ts`](../src/cli/__tests__/setup_presenters.test.ts) · [`src/__tests__/cli.test.ts`](../src/__tests__/cli.test.ts) · [`src/application/policies/__tests__/setup_pat_intent_policy.test.ts`](../src/application/policies/__tests__/setup_pat_intent_policy.test.ts) · [`src/application/policies/__tests__/setup_questionnaire_policy.test.ts`](../src/application/policies/__tests__/setup_questionnaire_policy.test.ts) · [`src/application/policies/__tests__/setup_token_permission_policy.test.ts`](../src/application/policies/__tests__/setup_token_permission_policy.test.ts) · [`src/application/policies/__tests__/setup_pat_creation_url_policy.test.ts`](../src/application/policies/__tests__/setup_pat_creation_url_policy.test.ts) · [`src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_token_permissions_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_token_permissions_use_case.test.ts) · [`src/infrastructure/__tests__/setup_token_permission_query_adapter.test.ts`](../src/infrastructure/__tests__/setup_token_permission_query_adapter.test.ts) · [`src/utils/__tests__/setup_files.test.ts`](../src/utils/__tests__/setup_files.test.ts) - User documentation: [`README.md`](../README.md) · [`docs/how-to-use.mdx`](../docs/how-to-use.mdx) · [`docs/authentication.mdx`](../docs/authentication.mdx) · [`docs/configuration.mdx`](../docs/configuration.mdx) · [`docs/development/architecture.mdx`](../docs/development/architecture.mdx) · [`docs/security-operations/operations/troubleshooting.mdx`](../docs/security-operations/operations/troubleshooting.mdx) ### `issue-start-and-sdd-readiness` — Uniform issue start and pre-branch SDD readiness diff --git a/specs/catalog.json b/specs/catalog.json index 8f55a2009..ce0735918 100644 --- a/specs/catalog.json +++ b/specs/catalog.json @@ -750,7 +750,7 @@ "status": "proposed", "scope": "Guide creation of the persistent workflow PAT using GitHub's official form, verify bot identity and grants, and install the approved Actions Secret", "owner": "Copilot maintainers", - "lastVerified": "2026-09-24", + "lastVerified": "2026-09-25", "specs": [ "specs/guided-bot-pat-onboarding.md" ], @@ -792,7 +792,7 @@ "status": "proposed", "scope": "Guide creation of the one-run operator PAT through GitHub's official form, verify final setup access, and report user-owned deletion accurately", "owner": "Copilot maintainers", - "lastVerified": "2026-09-24", + "lastVerified": "2026-09-25", "specs": [ "specs/temporary-setup-operator-authorization.md" ], @@ -802,14 +802,23 @@ ], "code": [ "src/cli/setup_credential_prompt_adapter.ts", + "src/application/policies/setup_pat_intent_policy.ts", + "src/application/policies/setup_questionnaire_policy.ts", + "src/application/policies/setup_token_permission_policy.ts", "src/application/policies/setup_pat_creation_url_policy.ts", + "src/application/usecases/setup/setup_wizard_use_case.ts", "src/application/usecases/setup/setup_token_permissions_use_case.ts", "src/infrastructure/setup_token_permission_query_adapter.ts", "src/utils/setup_files.ts" ], "tests": [ "src/cli/__tests__/setup_presenters.test.ts", + "src/__tests__/cli.test.ts", + "src/application/policies/__tests__/setup_pat_intent_policy.test.ts", + "src/application/policies/__tests__/setup_questionnaire_policy.test.ts", + "src/application/policies/__tests__/setup_token_permission_policy.test.ts", "src/application/policies/__tests__/setup_pat_creation_url_policy.test.ts", + "src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts", "src/application/usecases/setup/__tests__/setup_token_permissions_use_case.test.ts", "src/infrastructure/__tests__/setup_token_permission_query_adapter.test.ts", "src/utils/__tests__/setup_files.test.ts" diff --git a/specs/guided-bot-pat-onboarding.md b/specs/guided-bot-pat-onboarding.md index e1d46e172..29fe884fd 100644 --- a/specs/guided-bot-pat-onboarding.md +++ b/specs/guided-bot-pat-onboarding.md @@ -1,12 +1,12 @@ # Guided Bot PAT Onboarding - Status: Draft — guided implementation in progress; controlled GitHub UX and full test budget remain unverified -- Date: 2026-09-24 +- Date: 2026-09-25 - Catalog capability ID: `guided-bot-pat-onboarding` - Last verified: Not applicable; prospective change - Owners: Copilot maintainers and setup operators - Scope: guide creation and installation of the workflow/bot PAT when operator and bot are different GitHub accounts -- Related issues/PRs: none; no Action dogfooding for this design +- Related issues/PRs: [PR #402](https://github.com/vypdev/copilot/pull/402); no Action dogfooding for this design - Required review gates: product UX, architecture, testing, documentation, credential security, GitHub form compatibility - Open decisions blocking readiness: controlled GitHub UX, organization approval evidence, non-interactive identity extension, and full test-budget evidence @@ -25,10 +25,14 @@ This uses [GitHub's documented PAT URL parameters](https://docs.github.com/en/au The URL does **not** choose an individual repository or press Generate for the user. It also cannot switch the browser's GitHub account. Therefore the UI calls this **guided creation**, not automatic PAT issuance. There is no local -account manager or browser credential collection in this scope. +account manager or browser credential collection in this scope. The new +pre-PAT permission-intent questions in the [operator SDD](./temporary-setup-operator-authorization.md) +are for the *setup* PAT only: they seed the same setup plan that later produces +the bot's exact runtime permission set. They MUST NOT cause the bot URL or PAT +to be requested before the final plan and remote facts are known. ```text -operator PAT: guided link -> user creates PAT -> verify operator -> setup plan +operator PAT: local intent -> guided link -> user creates PAT -> verify -> final setup plan bot PAT: final runtime grants -> guided link -> user switches browser to bot -> user selects repository and creates PAT -> verify bot -> Secret PAT ``` @@ -155,7 +159,7 @@ API accepted the value; it does not prove a later workflow has run. | Stage | Current | Proposed | User effect | |---|---|---|---| -| Operator access | permission table + masked prompt | official prefilled link + existing prompt | less manual form setup | +| Operator access | permission table + masked prompt | local permission-intent review, then official prefilled link + existing prompt | setup grants are prepared before GitHub | | Bot identity | implicit in docs and PAT value | ask expected bot login; resolve and display ID | wrong account detected | | Bot grants | exact table before prompt | table + guided/manual choice and link from same policy result | fewer transcription errors | | Bot browser | no explicit check | tell user to select bot in GitHub account switcher | handles separate accounts | @@ -168,7 +172,7 @@ sequenceDiagram participant C as Copilot CLI participant G as GitHub participant S as Actions Secret - C->>U: Show operator PAT link and masked prompt + C->>U: Ask setup permission intent, then show operator PAT link and masked prompt U->>G: Create operator PAT in GitHub U->>C: Enter operator PAT C->>G: Verify operator and build final setup plan @@ -190,10 +194,14 @@ and stores the bot PAT in the selected Actions Secret scope. ### 6.1 Normal path 1. Complete the separate [operator PAT journey](./temporary-setup-operator-authorization.md), - including its local preflight and final permission audit. Do not reuse the + including its local preflight, reuse of early answers, authenticated remote + inspection, and final permission audit. If that audit requires a corrected + operator PAT, resolve it before presenting the bot link. Do not reuse the operator token as the Action credential. 2. Once setup choices and remote targets are final, use the existing workflow - permission policy to build the bot PAT link. Its name/description identify + permission policy to build the bot PAT link. Do not project the setup PAT's + preflight grants into the bot role: identical feature answers can imply + different setup and runtime levels. Its name/description identify purpose and repository; `target_name` is the repository owner; `expires_in` is a reviewed runtime expiration; each permission uses GitHub's documented query name and level. @@ -226,6 +234,9 @@ and stores the bot PAT in the selected Actions Secret scope. before identity binding can become mandatory there. - `--dry-run` generates a plan without a PAT; it may display an example link only when its permission set is complete and clearly marked provisional. +- Revising a pre-PAT setup answer after operator authorization invalidates + the operator link and requires its final audit before the bot URL is shown; + it never silently reuses an earlier bot URL. - If organization policy requires approval, setup stops before writing Secret `PAT` until target access is verified. It reports `pending approval` only when GitHub provides explicit evidence; otherwise it reports unavailable @@ -238,7 +249,7 @@ and stores the bot PAT in the selected Actions Secret scope. | State | Entered when | User-visible meaning | Next | Owner | |---|---|---|---|---| | `operator-pat-needed` | bootstrap grants known | create/paste operator PAT | `operator-verified`, `cancelled` | user | -| `operator-verified` | identity/grants accepted | finish plan choices | `bot-pat-needed` | CLI/user | +| `operator-verified` | identity/grants accepted | reuse preflight intent, finish plan and resolve any operator grant correction | `bot-pat-needed`, `blocked` | CLI/user | | `bot-pat-needed` | final grants and expected bot ID known | open GitHub as bot, create PAT | `bot-pat-verified`, `blocked`, `cancelled` | bot user | | `bot-pat-verified` | ID and grants accepted | Secret ready to write after approval | `secret-writing` | operator | | `secret-writing` | remote call started | provisioning | `installed`, `partial`, `blocked` | CLI | @@ -264,6 +275,10 @@ or to overwrite the Secret without a new approved value. | Secret scope | existing setup storage policy | repository | repository or organization as already supported | approved setup plan | The final runtime permission policy is the sole source of URL grants. The +operator preflight's temporary intent snapshot is consumed by the final setup +configuration, not a second bot-specific questionnaire or persistent account +profile. Any later change to a permission-driving choice requires both +role-specific plans to be recalculated before the relevant link is used. The existing `--workflow-pat`/`--secret PAT=...` values override interactive input and retain their current permission validation; they cannot silently enter guided mode without an expected bot identity. The bot account owner must renew @@ -318,6 +333,10 @@ identity and grants; the existing Secret writer installs the runtime PAT. - In guided mode, bot token identity comes from GitHub `/user` using that token. Compare numeric user IDs and verify repository access, then invoke the existing permission audit. +- The bot URL builder consumes only the finalized workflow-role grant set. A + contract test compares that set with the link after preflight choices are + reused and remote facts are incorporated; no setup-only Secret, Variable, + or health-workflow grant can leak into the bot link by role confusion. - No durable local state is introduced. The remote Secret is the only durable bot PAT copy Copilot creates. Browser state is owned by GitHub, not Copilot. @@ -348,7 +367,8 @@ Expected bot account: vypbot Expected account resolved: @vypbot (GitHub ID 5678) Action required: In GitHub, switch to vypbot and complete 2FA if asked. -Select only vypdev/copilot; review the prepared permissions, then Generate. +GitHub may initially select All repositories: change to Only select repositories +and select vypdev/copilot; review the prepared permissions, then Generate. PAT creation URL: https://github.com/settings/personal-access-tokens/new?name=...&target_name=vypdev&expires_in=...&... Workflow PAT (hidden): @@ -442,7 +462,7 @@ operator SDD are not counted again here. | Area | Minimum cases | Key behavior | |---|---:|---| -| Domain/configuration/pure URL mapping | 8 | bot-only permission keys/levels, owner, expiry, invalid grants | +| Domain/configuration/pure URL mapping | 8 | bot-only permission keys/levels, owner, expiry, invalid grants, no setup-only grant leakage after preflight | | State/application/idempotency | 7 | plan change, cancellation, re-entry, retry, Secret partial state | | Provider adapters/contracts | 5 | token `/user`, ID mismatch, repository access, Secret response | | Setup/permissions/schema | 5 | operator/bot role separation, final policy, org scope, existing Secret | @@ -475,8 +495,9 @@ PAT, and the bot PAT remains active after setup. 1. Given the selected setup features, each role receives a documented PAT URL with exactly its own needed permission levels and no token material. -2. Given local-only choices change, the operator link changes before a PAT is - requested; final remote inspection revalidates its grants. +2. Given pre-PAT local intent choices, only the operator link is generated + before authorization; the answers are reused in the final plan, and only + then is the bot runtime link generated from final grants and remote facts. 3. Given a bot account chosen by name, Copilot resolves and displays its immutable ID before accepting the bot PAT. 4. Given the browser uses another account, a PAT created there fails `/user` @@ -505,6 +526,7 @@ PAT, and the bot PAT remains active after setup. | Requirement | Owner | Verification | Documentation | |---|---|---|---| | Role-specific link (§4.1, §6.1) | policy + URL builder | scenarios 1–2 | how-to-use/authentication | +| Setup-preflight handoff (§1, §6.1) | wizard + role-specific permission policies | scenario 2 and no cross-role-grant fixture | how-to-use/architecture | | Bot ID and grants (§4.3, §6.1) | identity port + existing audit | scenarios 3–6 | authentication | | Secret lifecycle (§4.3, §10) | existing credential/Secret use cases | scenarios 7–10 | setup/troubleshooting | | Compatibility (§6.2, §13) | setup CLI | scenario 11 | CLI guide | @@ -512,8 +534,9 @@ PAT, and the bot PAT remains active after setup. ## 18. Implementation sequence -1. Resolve operator pre-auth planning and bot PAT expiration policy. Validate - the official permission-key mapping with GitHub's current documentation. +1. Reuse the operator SDD's local pre-auth planning and preserve bot PAT + generation after the final setup audit. Validate the official + permission-key mapping with GitHub's current documentation. 2. Implement a pure, role-specific URL builder and contract tests using the existing permission policy; keep URL generation separate from token input. 3. Add expected bot ID resolution and exact-token `/user` comparison before @@ -558,3 +581,9 @@ PAT, and the bot PAT remains active after setup. link and directs users to manual credential compatibility review. Human two-account/2FA acceptance, organization approval evidence, and the full numeric test budget remain open review gates. +- Implementation update (2026-09-25): the operator permission-intent preflight + now seeds the same final setup draft, but the bot URL is still built only + after final plan review and setup-PAT audit. The bot URL continues to use + its separate workflow-role policy; no bot-account browser session or token + is created by Copilot. Controlled browser acceptance and the full numeric + test budget remain open gates. diff --git a/specs/temporary-setup-operator-authorization.md b/specs/temporary-setup-operator-authorization.md index be58c52fb..4dc443993 100644 --- a/specs/temporary-setup-operator-authorization.md +++ b/specs/temporary-setup-operator-authorization.md @@ -1,26 +1,36 @@ # Assisted Setup PAT Creation -- Status: Draft — guided implementation in progress; controlled GitHub UX and full test budget remain unverified -- Date: 2026-09-24 +- Status: Draft — permission-intent preflight implemented locally; controlled GitHub UX and full test budget remain unverified +- Date: 2026-09-25 - Catalog capability ID: `temporary-setup-operator-authorization` - Last verified: Not applicable; prospective change - Owners: Copilot maintainers and setup operators -- Scope: guide creation, verification, use, and user-owned deletion of the operator PAT for one `copilot setup` run -- Related issues/PRs: none; no Action dogfooding for this design +- Scope: collect setup permission intent before the operator PAT link, then guide creation, verification, use, and user-owned deletion for one `copilot setup` run +- Related issues/PRs: [PR #402](https://github.com/vypdev/copilot/pull/402); no Action dogfooding for this design - Required review gates: product UX, architecture, testing, documentation, security, GitHub form compatibility -- Open decisions blocking readiness: controlled GitHub UX and test-budget evidence; the initial link is provisional and a changed final plan requires correction and rerun +- Open decisions blocking readiness: controlled browser UX and full test-budget evidence; remote-only facts cannot be known before authenticated inspection, so the link discloses residual uncertainty ## 1. Executive summary Interactive `copilot setup` will offer **Create with GitHub guidance** (the -recommended choice) or **I already have a PAT** immediately before requesting -the operator credential. Guidance prints an official, prefilled GitHub -fine-grained PAT URL for the selected repository owner and currently known -permissions. The user chooses the browser account, selects the individual -repository, reviews the form, generates the PAT, and pastes it into the existing -masked prompt. Copilot verifies access, runs setup, discards its local value, -and tells the user to delete the PAT in GitHub. A one-day expiry is a safety -backstop, **not** proof of deletion or revocation. +recommended choice) or **I already have a PAT** before requesting the operator +credential. Guided mode first asks only the setup choices that determine PAT +permissions, reusing answers from the existing questionnaire and local +configuration. It shows a reviewable permission preview, then prints an +official GitHub fine-grained PAT URL with every *locally determined* required +grant preselected; it does not add all conditional grants for convenience. +The user chooses the browser account, selects the individual repository, +reviews the form, generates the PAT, and pastes it into the existing masked +prompt. Copilot verifies access, completes the plan and final audit, runs +setup, discards its local value, and tells the user to delete the PAT in +GitHub. A one-day expiry is a safety backstop, **not** proof of deletion or +revocation. + +Authenticated repository/organization inventory and credential-health +workflow status are unavailable before the first PAT. The preview MUST name +those unresolved grants, and a later verified need MUST block dependent +mutation and produce a corrected link. This is a bounded exception to the +one-link goal, not permission to request every possible grant up front. The companion [bot PAT SDD](./guided-bot-pat-onboarding.md) covers the second, persistent token installed as Actions Secret `PAT` after the setup plan is @@ -28,9 +38,11 @@ known. Both roles share one URL-building contract, but not a credential or lifecycle. ```text -resolve repository -> provisional bootstrap grants -> choose guided/manual operator PAT - -> GitHub form -> masked input -> verify -> plan and final grant audit - -> guide/verify bot PAT -> install Secret -> apply setup -> cleanup reminder +resolve repository -> choose guided/manual -> collect permission-affecting intent + -> review exact known grants and remote unknowns -> prefilled GitHub form + -> masked PAT input -> verify -> complete plan and final grant audit + -> correct link if remote facts add grants -> guide/verify bot PAT + -> install Secret -> apply setup -> cleanup reminder ``` Text equivalent: the terminal guides two separate PATs during one setup run; @@ -41,10 +53,12 @@ only for its role; the operator deletes the temporary PAT in GitHub. ### 2.1 Problem -The first-time operator sees a large permission table but must navigate to the -correct GitHub form and transcribe every grant. The same browser may contain a -personal and a bot account. Merely disposing of the PAT in local memory does -not remove it from GitHub. +The first-time operator sees a large permission table but the current guided +URL contains only `metadata=read` and `contents=read`: it is built before the +questionnaire and filters out every conditional row. The operator must still +enter the other needed permissions manually or replace the PAT after the +final audit. The same browser may contain a personal and a bot account. +Merely disposing of the PAT in local memory does not remove it from GitHub. ### 2.2 Observed repository behavior @@ -57,6 +71,10 @@ not remove it from GitHub. is not installed as runtime Secret `PAT`. 4. `SetupCredentialsUseCase` gathers the distinct workflow PAT later, after the plan, and GitHub cannot reveal existing Secret values. +5. `buildSetupPatCreationUrl()` serializes only `required` rows. The initial + setup call supplies the bootstrap table, where only Metadata and Contents + are required; the other ten rows are conditional. `loadSetupOverrides()` + and the interactive questionnaire currently run after setup-PAT entry. ### 2.3 External primary evidence @@ -67,13 +85,22 @@ not remove it from GitHub. | Who handles the account and 2FA? | [GitHub browser account switcher](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/switching-between-accounts) | GitHub owns the browser session and account choice; local Git/`gh` identity is not evidence. | | Can this link create or delete the PAT? | [GitHub PAT management](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens) | No. The user generates and deletes it in GitHub Settings. No documented owner PAT mint/revoke API was found. | +Controlled browser prefill check on 2026-09-25: a test URL with all twelve +documented setup-table grants displayed eight repository and four organization +permissions at the requested levels for `vypdev`. GitHub initially selected +**All repositories**; changing to **Only select repositories** and selecting +`vypdev/copilot` retained all twelve grants. No PAT was generated. This proves +form prefill and repository-selector behavior for that account/session, not +token issuance, permission sufficiency, or universal organization policy. + ### 2.4 Viability decision -**Guided creation is feasible; automatic PAT issuance and destruction are not -claimed.** Do not replay private website requests, read cookies, capture 2FA, -or call the link an authorization grant. A future GitHub App design would use a -different credential and requires its own endpoint and revocation proof; it is -outside this SDD and is not displayed as an available terminal choice. +**Guided creation and preselected permissions are feasible; exact one-pass +least privilege cannot be guaranteed from unauthenticated local intent alone.** +Do not replay private website requests, read cookies, capture 2FA, or call the +link an authorization grant. A future GitHub App design would use a different +credential and requires its own endpoint and revocation proof; it is outside +this SDD and is not displayed as an available terminal choice. ### 2.5 Retrospective classification @@ -90,8 +117,10 @@ Not applicable: this is a proposed extension to the observed setup flow. **Operator PAT** means the human's one-run setup credential. **Guided** means the CLI prepares a form URL; the user still creates the PAT. **Discarded** means the CLI no longer retains the value. **Deleted/revoked** means GitHub has -invalidated it; this flow cannot infer that from local disposal. **Provisional -link** means later remote inspection may require a new grant. +invalidated it; this flow cannot infer that from local disposal. +**Permission-intent preflight** means the short, pre-PAT portion of the setup +questionnaire that determines locally knowable grants. **Provisional link** +means authenticated remote facts may require a corrected grant after entry. ## 4. Goals, non-goals, and fixed invariants @@ -99,14 +128,20 @@ link** means later remote inspection may require a new grant. 1. Interactive setup MUST offer guided creation or existing manual PAT input without introducing a second setup command. -2. The link MUST include only known, needed grants from the same permission - policy as the terminal table; unknown remote-dependent grants MUST be - disclosed as provisional, not silently added. -3. The actual operator PAT MUST pass existing identity, repository-access, and +2. Guided mode MUST collect, review, and reuse all locally knowable + permission-affecting choices **before** generating the setup PAT URL. The + URL MUST include the resulting required grants from the same policy as + the terminal table and final audit. No selected choice may be silently + reverted or asked a second time in the main questionnaire. +3. Unknown remote-dependent grants MUST be listed beside the preview and + omitted by default, not silently overgranted. A changed plan or verified + remote need MUST invalidate the old link and block dependent mutation until + the supplied PAT passes the recalculated audit. +4. The actual operator PAT MUST pass existing identity, repository-access, and final-plan permission checks before dependent mutation. Guided setup MUST show its authenticated account and ask the operator to confirm that this is the account intended to configure the repository. -4. The final terminal result MUST distinguish local disposal from GitHub +5. The final terminal result MUST distinguish local disposal from GitHub deletion and provide a concrete deletion action. ### 4.2 Non-goals @@ -131,15 +166,18 @@ link** means later remote inspection may require a new grant. become operator setup authority. 5. `--yes`, non-interactive mode, and dry-run MUST NOT trigger browser actions, generate a PAT, or silently accept new permissions. +6. Preflight answers are operator intent, not GitHub facts or authorization. + Unknown owner type, remote inventory, approval, and workflow status MUST + never be fabricated from defaults or treated as proven by a user answer. ## 5. Current versus proposed product journey | Stage | Current | Proposed | User effect | |---|---|---|---| -| Before setup PAT | bootstrap permission table | provisional required-grant link, table, guided/manual choice | purpose and account are clear | +| Before setup PAT | bootstrap permission table and two-grant provisional link | guided/manual choice, short permission-intent preflight, reviewed grants and unresolved remote needs | needed local grants are preselected | | GitHub | user navigates form and transcribes grants | documented prefilled URL; user selects repo and generates | less repetitive form work | | After paste | identity/access and grant audit | same audit; display actual account | wrong token found before setup | -| After plan | final grant audit | same audit; if link was provisional, explain correction | no silent overgrant | +| After plan | final grant audit | same audit; show exact delta and corrected URL only if a choice changed or remote evidence adds a grant | no silent overgrant or mutation | | Completion | local PAT not stored | explicit GitHub deletion reminder and link | honest cleanup | ```mermaid @@ -147,51 +185,102 @@ sequenceDiagram participant U as Operator participant C as Copilot CLI participant G as GitHub - C->>U: Show permission summary and guided/manual choice + C->>U: Offer guided/manual choice + C->>U: Ask permission-affecting setup choices and show grant preview C->>U: Print official PAT URL and repository instruction U->>G: Choose account, complete 2FA if required, select repo, Generate U->>C: Paste PAT into masked prompt C->>G: Verify account, repository, and grants C->>U: Show actual account and request confirmation - C->>U: Confirm plan; explain any newly required grant + C->>U: Reuse preflight choices, confirm plan, explain any grant delta C->>G: Apply approved setup C->>U: Report local disposal and user-owned GitHub deletion ``` -Text equivalent: the user creates a PAT on GitHub, the CLI verifies and uses it -for setup, then explicitly asks the user to delete it on GitHub. +Text equivalent: the CLI first collects and reviews permission-driving local +choices, the user creates the prepared PAT on GitHub, the CLI verifies remote +facts and any grant change before using it for setup, then explicitly asks the +user to delete it on GitHub. ## 6. Functional behavior and state model ### 6.1 Normal path -1. Resolve repository. Use the existing bootstrap permission policy before the - questionnaire: only its required rows enter the initial URL. The link MUST - be labeled provisional because local and remote choices are not yet known. -2. Render the existing permission summary and `Create with GitHub guidance` - (recommended) / `I already have a PAT`. If a supplied `--token` or - `PERSONAL_ACCESS_TOKEN` exists, retain existing precedence and skip the - interactive choice. -3. In guided mode, build the official URL from known selected grants. If a - remote-dependent grant cannot be determined before authorization, label - the link **provisional** and identify the possible later correction. A - permission whose URL name/level is unknown blocks link generation and - offers manual configuration with the existing table. -4. Print the full URL on its own terminal line, outside `renderBox`; give - account/repository instructions. Do not auto-open the browser. Accept the - PAT only through the existing masked prompt. -5. Inspect the supplied PAT. Show the actual GitHub account and grant report. - In guided mode, ask the operator to confirm that account before mutation; - reject a decline or invalid target access. After plan confirmation, run the final policy - audit; if a previously unknown grant is required, block dependent mutation - and explain how to correct or replace the PAT. -6. Continue to the separate bot PAT journey. On success, failure, or - cancellation after PAT creation, remind the user to delete the setup PAT - in GitHub. Never claim deletion was verified. +1. Resolve repository and the existing local setup inputs (`--config`, CLI + flags, and defaults) without network mutation. If `--token` or + `PERSONAL_ACCESS_TOKEN` is supplied, retain existing precedence and skip + guided preflight. Otherwise offer `Create with GitHub guidance` + (recommended) or `I already have a PAT`. +2. Guided mode asks only the permission-driving setup choices not already + fixed by local inputs, using the same questionnaire definitions and + validation, in their normal order. It creates a one-run intent draft that + the later full questionnaire MUST consume without repeating those answers. + The question set is the dependency closure of the existing permission + policy: agent/model or other choices join preflight only when they change + whether a managed resource or scope exists, not merely its value or name. + The operator can explicitly revise the draft before PAT creation; after + creation, a revision requires a new permission comparison before mutation. +3. Render the intended choices, an exact required-grant preview, and a + separate **May need after GitHub inspection** list. Require explicit review + of this preview; `--yes` does not waive it. Compute grants using the same + permission policy as the final audit, projected over locally known facts. + Include Metadata read and Contents read even when no optional capability + is selected. Never turn every conditional row into a required row. +4. Build the official URL from that reviewed required-grant set. Each + permission MUST use its documented query name and level; a missing mapping + blocks guided link generation and leaves manual setup available. If a + remote-only grant is unresolved, label the link **provisional** and name + the exact potential grant and trigger beside it. Print the complete URL + outside `renderBox` with account/repository instructions; do not open the + browser automatically. Accept the PAT only through the masked prompt. +5. Inspect the supplied PAT, show the actual GitHub account and grant report, + and confirm the intended account. Authenticated remote inspection then + verifies owner type, managed-resource inventory, and health-workflow state. + Complete the remaining setup questions without re-asking preflight choices. + Recompute the final grants from the actual configuration and remote facts + before any dependent mutation. A missing grant or changed intent invalidates + the previous link; show the permission delta and a corrected URL, require + an audited replacement/corrected PAT, and remind the user to delete any + obsolete PAT. A plan that merely removes grants MUST NOT claim the existing + token was least-privileged; explain that the user may replace it before + continuing. +6. Continue to the separate bot PAT journey only after final setup-PAT access + is accepted. On success, failure, or cancellation after PAT creation, + remind the user to delete the setup PAT in GitHub. Never claim deletion + was verified. + +The preflight question-to-grant contract is based on the current final setup +permission policy. It MUST be derived from configuration fields, not a second +hard-coded permission table in the terminal adapter: + +| Pre-PAT intent question or local input | Grant projected into the URL when selected | Still unknown until GitHub inspection | +|---|---|---| +| Repository owner kind (`organization` or `personal`), asked only if an organization grant is a candidate | Enables valid organization grants for an asserted organization owner; never by itself adds a grant | Actual owner kind and organization PAT policy | +| Create initial tag? | Repository Contents write instead of read | Whether tag creation is ultimately needed | +| Manage Actions Secrets and their requested default scope, preservation, and known explicit overrides? | Repository Secrets write for selected managed names/inventory; organization Secrets write when an organization target or inventory is definitely selected | Existing effective scopes, inherited names, and conditional organization inventory | +| Manage Actions Variables and their requested default scope, preservation, and known explicit overrides? | Repository Variables write for selected managed names/inventory; organization Variables write when an organization target or inventory is definitely selected | Existing effective scopes, inherited names, and conditional organization inventory | +| Enable issue workflow types? | Repository Issues write; organization Issue Types write if owner is an organization | Verified owner kind | +| Enable release/hotfix or guarded PR approval? | Repository Administration read | Final branch-rule readiness | +| Configure organization Project IDs? | Organization Projects write when owner is an organization and IDs are selected | Project access and ownership | +| Remote condition shown, not asked: existing managed Secrets need credential-health validation; workflow is confirmed missing on the selected branch | No grant from an unverified assertion; explain potential Actions write and, only for confirmed missing workflow, Contents write plus Workflows write | Authenticated inventory and independent selected-ref workflow proof | + +The last row is **not** a second questionnaire about facts the operator may +not know. It is a preview of remote-only conditions; the CLI MUST NOT ask the +operator to attest to workflow presence or Secret inventory as if that proved +it. Individual resource overrides that depend on inherited remote names stay +in the post-auth questionnaire and may require a corrected link. If the +operator cannot identify whether the owner is an organization when an +organization grant is a candidate, the CLI explains how to check it and +offers the manual path rather than guessing a URL. An explicit organization +storage/project choice with a declared personal owner is rejected before URL +generation. The target owner and selected repository are verified after the +PAT is entered. ### 6.2 Alternatives - Manual uses the existing masked prompt and permission audit without a link. +- A guided user may cancel or revise the preflight before GitHub. No remote + resource changes occur and no PAT exists unless the user generated one. - Existing supplied-token and unattended paths remain unchanged; no new prompt or browser action occurs. A cleanup reminder MAY be shown, but the CLI cannot know who created or owns a supplied token. @@ -201,21 +290,29 @@ for setup, then explicitly asks the user to delete it on GitHub. already have generated in GitHub remains their deletion responsibility. - If GitHub organization approval is required, setup waits for verified target access; call it `pending approval` only with explicit provider evidence. +- If the preflight predicts a need that the final plan does not have, the CLI + displays the excess grant and offers replacement guidance; it never silently + describes the earlier URL as the exact final least-privilege plan. ### 6.3 State machine | State | Entered when | Visible meaning | Next | Owner | |---|---|---|---|---| -| `choice` | no supplied PAT | guided/manual decision | `form-ready`, `manual-input`, `cancelled` | operator | -| `form-ready` | URL validated | GitHub action required | `pat-entered`, `cancelled` | operator | +| `choice` | no supplied PAT | guided/manual decision | `intent-collecting`, `manual-input`, `cancelled` | operator | +| `intent-collecting` | guided selected | only grant-driving setup choices are being asked | `intent-review`, `cancelled` | operator | +| `intent-review` | local choices projected | review exact grants and remote unknowns | `form-ready`, `intent-collecting`, `cancelled` | operator | +| `form-ready` | reviewed URL validated | GitHub action required | `pat-entered`, `cancelled` | operator | | `pat-entered` | masked value received | verification in progress | `verified`, `blocked` | CLI | -| `verified` | current grants accepted and account confirmed | plan and final audit | `setup-running`, `blocked` | CLI/operator | +| `verified` | current grants accepted and account confirmed | reuse intent, finish plan and final audit | `setup-running`, `grant-correction`, `blocked` | CLI/operator | +| `grant-correction` | final evidence/choice changed grants | no dependent mutation; correct or replace PAT | `pat-entered`, `cancelled` | operator | | `setup-running` | plan approved | apply setup | `complete`, `partial` | CLI | | `complete` | setup finished | delete operator PAT in GitHub | terminal | operator | | `partial` | some changes applied | inspect report, then delete PAT | retry/terminal | operator | -Re-entering the same PAT does not create another one. A changed plan invalidates -the old link. A crash cannot guarantee GitHub deletion; restart and recovery +Re-entering the same PAT does not create another one. Re-running preflight with +the same inputs yields the same grant set and URL; stale or out-of-order +answers cannot modify a reviewed draft. A changed plan invalidates the old +link. A crash cannot guarantee GitHub deletion; restart and recovery instructions must not imply otherwise. ## 7. User-facing configuration @@ -223,17 +320,29 @@ instructions must not imply otherwise. | Input | Type | Recommended default | Allowed values | Scope/persistence | |---|---|---|---|---| | PAT help choice | interactive enum | guided | `guided`, `manual` | one run; not saved | +| Permission-intent answers | existing bounded setup fields | existing CLI/config/default values; ask only unset or revisable fields | existing feature, workflow, tag, storage, and Project validators | one run; frozen for later questionnaire, not saved | +| Owner-kind assertion | interactive enum when an organization grant is possible | no assumed value; ask operator | `organization`, `personal` (or choose manual if unknown) | one run; verified after PAT, not saved | | Generated expiry | integer days | `1` | documented 1–366; first release fixes link at 1 | URL only; GitHub policy may override | | Repository | existing Git remote identity | current repo | verified owner/repo | one run | | Supplied operator token | existing secret input | none | current CLI/env precedence | memory only | +Precedence remains existing CLI flags over `--config` over setup defaults; +interactive intent changes override only the corresponding defaulted draft +field for this run. `--skip-secrets` and `--skip-variables` override both +the draft and URL projection. A reviewed choice is snapshotted into the main +questionnaire rather than reread from a changed file. Invalid cross-field +combinations (personal owner with organization storage or organization +Projects, disabled issues with enabled issue workflow types, unsupported URL +permission/level) block link generation with a specific recovery action. No new account or PAT configuration is persisted. Wrong owner, invalid grant, URL length outside a reviewed terminal bound, and contradictory permission grants block link generation. Existing `--token` and environment precedence remain; `--yes` does not choose an identity or waive checks. Expiry, host, -secret-free URL, and role separation are not configurable in this first -release. The recommended example is interactive guided setup; the meaningful -alternative is manual PAT creation and masked input. +secret-free URL, role separation, and omission of unverified remote-only grants +are not configurable in this release. The recommended example is interactive +guided setup; the meaningful alternative is manual PAT creation and masked +input. Existing configuration files need no migration; a supplied PAT follows +the current path without a hidden preflight. ## 8. Clean Architecture design @@ -241,8 +350,8 @@ alternative is manual PAT creation and masked input. | Boundary | Owns | Must not own/import | |---|---|---| -| Pure policy | permission-plan-to-URL mapping, role, validation | browser, HTTP, terminal, token values | -| Application | guided choice, final audit, cleanup message state | process/browser APIs, GitHub DTOs | +| Pure policy | project local intent to required/remote-unknown grants; permission-plan-to-URL mapping, role, validation | browser, HTTP, terminal, token values | +| Application | guided choice, intent snapshot/reuse, grant-delta comparison, final audit, cleanup message state | process/browser APIs, GitHub DTOs | | Ports | secret input, identity/grant inspection, presentation | private website sessions | | Adapters | GitHub query mapping and terminal rendering | permission decisions | | Composition | connect existing setup stages | duplicate policy tables | @@ -250,15 +359,16 @@ alternative is manual PAT creation and masked input. ```mermaid flowchart LR E[Setup entrypoint] --> A[Guided PAT flow] - A --> P[Existing permission policy] - A --> B[Pure GitHub URL builder] + A --> I[Permission-intent preflight] + I --> P[Existing permission policy] + P --> B[Pure GitHub URL builder] A --> V[GitHub identity and grant audit] A --> T[Terminal presenter] ``` -Text equivalent: setup orchestrates a guided choice, derives the URL from the -existing policy, verifies the pasted PAT through existing GitHub ports, and -renders status in the terminal. +Text equivalent: setup collects only permission-affecting local intent before +deriving a link from the existing policy, then verifies the pasted PAT and +remote facts, reuses the intent in the full wizard, and renders any grant delta. ### 8.2 Contracts, state, and trust boundaries @@ -266,6 +376,13 @@ renders status in the terminal. permissions}` and emits only documented query parameters. It rejects duplicate/conflicting grants and unsupported scope/level pairs. The bot SDD reuses this contract with different role and expiry. +- A preflight projection accepts normalized local setup overrides and bounded + questionnaire answers, returns `{draft, requiredGrants, unresolvedTriggers}`, + and has no provider token or GitHub DTO. The same draft is consumed by the + full wizard; no second hard-coded permission matrix or duplicate prompts. +- The final audit compares normalized grants by role, scope, permission, and + strongest level. A grant added or upgraded is a blocking delta until a new + audited PAT is supplied; a removed grant is disclosed as possible excess. - GitHub web authentication, 2FA, account switching, repository selection, PAT generation, and deletion stay entirely in GitHub. - No durable local token or browser session state is introduced. The remote @@ -275,11 +392,13 @@ renders status in the terminal. ### 8.3 Executable constraints -Architecture tests forbid the pure builder from importing terminal, HTTP, -filesystem, or browser modules. Contract tests parse every emitted query key -and level against GitHub's documented set. Security tests reject token/cookie -material in URLs and logs and prove no setup mutation precedes final grant -acceptance. +Architecture tests forbid the pure projection and URL builder from importing +terminal, HTTP, filesystem, or browser modules. Contract tests parse every +emitted query key and level against GitHub's documented set. Setup contract +tests prove preflight fields are the same normalized fields consumed by the +wizard, with no duplicated question or privilege table. Security tests reject +token/cookie material in URLs and logs and prove no setup mutation precedes +final grant acceptance. ## 9. Terminal UI and content contract @@ -293,28 +412,57 @@ Choose how to provide the setup PAT: 2) I already have a PAT Select [1]: -Action required: Create the PAT in GitHub as the account that will configure -vypdev/copilot. The link fills known permissions, but does not select a repo. -In GitHub, select only vypdev/copilot, review the grants, then Generate. +Before creating the PAT, choose what setup will configure. Existing --config +and CLI selections are shown as defaults and will be reused later. +Enable issue workflows? [Yes]: Yes +Create/update Actions Secrets? [Yes]: Yes +Secrets storage? [Repository]: Repository +Create/update Actions Variables? [Yes]: Yes +Variables storage? [Repository]: Repository +Enable guarded approval or release/hotfix? [No from --config]: No +Create an initial tag? [Yes]: No +Organization-owned repository? [No answer yet]: Yes +Configure organization Projects? [No]: No + +Review before opening GitHub: + Required now: Metadata read, Contents read, repository Secrets write, + repository Variables write, Issues write, organization Issue Types write. + May be needed after GitHub inspection: Actions write for existing managed + Secrets; Contents write + Workflows write only if the health workflow is + independently confirmed missing; organization Secret/Variable access + if preservation resolves to that scope. +Confirm these choices and permission preview? [No]: Yes + +Action required: Open GitHub as the account configuring vypdev/copilot. +GitHub initially selects All repositories: change to Only select repositories +and select vypdev/copilot. Review the prefilled grants, then Generate. PAT creation URL: https://github.com/settings/personal-access-tokens/new?name=...&target_name=vypdev&expires_in=1&... Setup PAT (hidden): ``` -If remote-dependent permissions remain unknown, insert `Provisional link: -setup will check the final plan and may require you to adjust this PAT` before -the URL. The URL is printed as an unwrapped plain line outside a bordered box; -terminal auto-linking is optional, never required. Do not copy it to clipboard -or open a browser automatically. +This example is illustrative: only fields that actually affect the selected +grant set are asked, and their defaults reflect existing setup inputs. The +remote-only list makes the link **provisional**, not broken. The URL is printed +as an unwrapped plain line outside a bordered box; terminal auto-linking is +optional, never required. Do not copy it to clipboard or open a browser +automatically. | State | First visible text | Next action | |---|---|---| -| Pending | `Waiting for a setup PAT. No setup changes have started.` | create/paste PAT | -| Action required | `Check the GitHub account and select only vypdev/copilot before Generate.` | complete GitHub form | -| Blocked | `Setup has not changed the repository: this PAT lacks Contents write required by the final plan. Update or replace it, then retry.` | correct PAT | +| Pending | `Collecting setup choices that determine the PAT permissions. No GitHub changes have started.` | answer/review intent | +| Action required | `GitHub prefilled six grants. Change All repositories to Only select repositories → vypdev/copilot before Generate.` | complete GitHub form | +| Blocked | `Setup has not changed the repository: authenticated inspection found an existing managed Secret, so Actions write is required. Create a replacement PAT with the corrected link and retry; delete the obsolete PAT in GitHub.` | correct PAT | | Partial | `Some setup changes were applied. The operator PAT may still be active in GitHub. Inspect the setup report, then delete the PAT.` | inspect/delete | | Complete | `Setup complete. The operator PAT was discarded locally, not deleted from GitHub. Delete it in GitHub Settings.` | delete PAT | +If a later choice removes a grant, say `The PAT may have more access than this +plan needs; review or replace it before continuing` rather than claiming exact +least privilege. If preflight is cancelled, say no repository changes started +and remind the user that any PAT already generated in GitHub remains theirs +to delete. If GitHub rejects an owner/permission combination, return to intent +review or the manual path; never suggest a hidden URL parameter as a fix. + Errors follow impact, cause, action, retained state. Status uses words, not color/emoji alone. Narrow terminals keep choices and instructions readable; the URL stays copyable. English message catalog is the initial source; later @@ -326,8 +474,11 @@ names. No issue, PR, comment, label, or check is created by this UI. | Condition | Impact | Retained fact | Retry/action | Cleanup | |---|---|---|---|---| | Wrong browser account | PAT belongs to an unintended user | no mutation before guided account confirmation | decline, switch in GitHub, recreate if needed | user deletes wrong PAT | +| Preflight cancelled or invalid | no link or remote mutation | local inputs only | revise choices or use manual path | delete any already generated PAT in GitHub | +| Owner assertion differs from verified owner | organization grants may be invalid or omitted | authenticated owner type; no dependent mutation | revise intent and create corrected PAT | delete obsolete PAT | | Wrong repo/owner | PAT lacks target access | no dependent mutation | select correct repo in GitHub | user deletes unused PAT | -| Final plan adds grant | setup cannot proceed safely | plan and audit result | update PAT or create a new one | user deletes obsolete PAT | +| Remote inspection or changed plan adds grant | setup cannot proceed safely | intent draft, actual remote facts, grant delta | create a replacement PAT with corrected URL and re-audit | user deletes obsolete PAT | +| Final plan removes grant | token may exceed least privilege | final grant comparison | replace PAT or explicitly continue under existing audit policy | user owns excess-token cleanup | | Unknown form parameter | no safe guided URL | manual path remains | use table/manual form | no generated PAT | | User cancels after GitHub generation | PAT may remain active | no local value | delete in GitHub | user-owned | | Setup partially applies | repo may be changed | report of completed steps | inspect before retry | delete operator PAT only after no retry needs it | @@ -341,6 +492,8 @@ one-day expiry still permits use until expiry and may be shortened by policy. 1. Least-privilege grants come from the same setup policy used by the final permission audit. The link never adds every conditional grant by default. + The CLI must identify GitHub's initial **All repositories** selection as a + separate, manual scope decision; `target_name` is not repository scoping. 2. No password, cookie, browser profile, 2FA code, or PAT appears in a URL, config file, telemetry, logs, or GitHub issue. Masked input is retained. 3. Existing command-line PAT flags remain for compatibility; guided mode does @@ -348,56 +501,70 @@ one-day expiry still permits use until expiry and may be shortened by policy. legacy flags exposing values in shell history/process inspection. 4. Private GitHub website requests are not a supported authentication contract; no browser scraping is added. +5. Preflight is local-only and uses the same bounded validators as setup. + Operator-declared owner kind cannot authorize organization operations; + authenticated inspection and the final audit remain mandatory. ## 12. Observability and operational UX -Show role, target repository, actual authenticated login, access result, -whether the link was provisional, and setup/cleanup status. Do not record token -values or raw API payloads. A failed check includes one next action and known -retained state. Limit output to one choice, one guidance block, existing audit -report, and one final cleanup reminder; no polling or notifications. +Show role, target repository, reviewed intent, exact prefilled grants, remote +unknowns, actual authenticated login, any grant delta, access result, and +setup/cleanup status. Do not record token values or raw API payloads. A failed +check includes one next action and known retained state. Limit output to one +choice, one intent review, one guidance block, existing audit report, and one +final cleanup reminder; no polling, comments, or notifications. ## 13. Compatibility, migration, rollout, and rollback The manual prompt, `--token`, `PERSONAL_ACCESS_TOKEN`, `--non-interactive`, -`--yes`, and dry-run continue to work with existing precedence. The first -release adds only interactive guidance and a provisional bootstrap URL. +`--yes`, and dry-run continue to work with existing precedence. The current +guided implementation prints a provisional bootstrap-only URL; the proposed +revision adds an interactive local preflight and grants selected by intent. No repository schema, Secret, or account-store migration occurs. Rollback -hides guidance and returns to the current prompt; PATs already generated by -users remain their responsibility. User docs must not imply the guided path -exists until implemented. +hides the new preflight and returns to the existing guided/manual prompt; +PATs already generated by users remain their responsibility. Documentation +must distinguish shipped bootstrap-only behavior from this proposed behavior +until implementation is released. ## 14. Testing strategy and numeric budget -The minimum is **30 distinct cases**, derived from two UI choices, bootstrap -versus final grants, provider identity/scope, cancellation, and cleanup truth. +The minimum is **48 distinct cases**, derived from permission-intent branching, +local/remote evidence separation, exact URL grants, changed-plan correction, +identity/scope, cancellation, and cleanup truth. | Area | Cases | Risk covered | |---|---:|---| -| Pure URL/configuration policy | 7 | key/level mapping, expiry, owner, encoding, rejected grants | -| State/application/idempotency | 6 | choice, provisional link, final-plan change, retry, cancel | -| Provider/permission contracts | 4 | identity, repo access, missing grant, unknown response | -| Setup/compatibility | 4 | manual, supplied token, unattended, dry-run | -| Terminal/accessibility/localization | 5 | pending, action, blocked, partial, complete; narrow URL | -| Integration/security | 4 | no URL secret, no early mutation, cleanup truth, wrong account | -| **Total** | **30** | Distinct tests, no double counting | - -Existing repository-wide gates remain. New pure URL policy targets 100% -branch coverage; changed setup code targets at least 95% lines/statements and -90% branches/functions. Use deterministic GitHub fakes, no real PATs in CI; -assert parsed query parameters and semantic UI text, not snapshots alone. -Human UX evidence includes a narrow terminal, browser account switcher, 2FA -handled by GitHub, repo selector, and guided/manual fallback. No live token -value enters test evidence. +| Pure intent/URL/configuration policy | 12 | every local grant trigger and scope/level, dedupe, invalid owner/permission, encoding | +| State/application/idempotency | 10 | preflight review/revision, draft reuse, stale answers, added/removed grant, retry/cancel | +| Provider/permission contracts | 5 | owner/identity, repository access, remote inventory and health evidence, unknown response | +| Setup/compatibility | 6 | manual, supplied token, unattended, dry-run, CLI/config/default precedence, skip flags | +| Terminal/accessibility/localization | 7 | pending, review, action, blocked, partial, complete, narrow full URL and scope warning | +| Integration/security | 8 | no URL secret, no early mutation, no automatic all-conditionals, wrong account, remote unknown, cleanup truth | +| **Total** | **48** | Distinct tests, no double counting | + +Existing repository-wide gates remain. New pure preflight/projection and URL +policies target 100% branch coverage; changed setup code targets at least 95% +lines/statements and 90% branches/functions. Use deterministic GitHub fakes, +fixed clock and no real PATs in CI. Contract tests compare the URL's parsed +permission set with the reviewed preview and final policy fixtures; semantic +UI assertions accompany, rather than rely only on, snapshots. Required +coverage includes the exact six-grant example above, the twelve-grant +GitHub-form compatibility case, no optional grants selected, organization +versus personal owner, preflight choice reuse, remote-only grant correction, +and a plan that removes access. Human UX evidence includes a narrow terminal, +browser account switcher, 2FA handled by GitHub, explicit All-to-selected +repository change, and guided/manual fallback. The 2026-09-25 browser test +is prefill evidence only; final acceptance does not require dogfooding or a +live token value in test evidence. ## 15. Documentation and discoverability | Audience | Artifact | Required content | Validation | |---|---|---|---| -| New user | `README.md`, `docs/how-to-use.mdx` | two roles and guided/manual normal path | navigation/link check | -| Setup owner | `docs/authentication.mdx`, `docs/configuration.mdx` | URL limits, provisional grants, exact permissions, account/repo choice | policy fixture | -| Operator | `docs/security-operations/operations/troubleshooting.mdx` | wrong account, changed grants, cancellation, deletion | recovery fixture | -| Contributor | `docs/development/architecture.mdx`, this SDD | shared URL builder and trust boundary | architecture test | +| New user | `README.md`, `docs/how-to-use.mdx` | two roles, pre-PAT choices, guided/manual normal path | navigation/link check | +| Setup owner | `docs/authentication.mdx`, `docs/configuration.mdx` | question-to-permission mapping, URL limits, remote unknowns, exact grants, All-to-selected repository step | policy fixture | +| Operator | `docs/security-operations/operations/troubleshooting.mdx` | wrong account, changed/removed grants, correction, cancellation, deletion | recovery fixture | +| Contributor | `docs/development/architecture.mdx`, this SDD | local intent snapshot, shared policy/URL builder, trust boundary | architecture test | Docs are updated with implementation, not ahead of it. Examples must match CLI fixtures and clearly distinguish setup-PAT deletion from persistent bot @@ -409,56 +576,78 @@ Secret renewal. creation and manual input; guided is the default. 2. Given guided choice, the CLI prints a documented GitHub URL, repository instruction, and hidden PAT prompt; it does not open a browser. -3. Given initial bootstrap grants, the URL contains only required grants; - later feature- or remote-dependent grants are labeled provisional, not overgranted. -4. Given an unintended browser account, the CLI displays the actual login and +3. Given guided mode and local defaults/flags/config, the CLI asks only + permission-affecting choices that are not fixed by those inputs, shows an + exact grant preview before the URL, and reuses answers in the full wizard. +4. Given selected Secret/Variable provisioning, issue workflows, initial tag, + release/hotfix/guarded approval, and organization Projects, the URL + contains exactly the corresponding strongest-level grants; disabling + those capabilities omits their grants. +5. Given remote-only Secret inventory or health-workflow uncertainty, the + preview labels the corresponding possible grants provisional and does not + add them merely because they are conditional in the bootstrap table. +6. Given an unintended browser account, the CLI displays the actual login and the operator declines it; given a wrong repo, access verification fails. Either way, setup blocks before dependent mutation. -5. Given a final plan requiring an additional grant, the CLI explains the - correction and blocks mutation until the PAT passes re-audit. -6. Given manual, supplied-token, unattended, or dry-run paths, their existing +7. Given a final plan requiring an additional or upgraded grant, the CLI + explains the exact delta, provides a corrected link, and blocks mutation + until a replacement/corrected PAT passes re-audit. A removed grant is + disclosed as possible excess access. +8. Given manual, supplied-token, unattended, or dry-run paths, their existing behavior is preserved without surprise browser action. -7. Given cancellation after the user generated a PAT, the CLI warns that it - may remain active and links to GitHub Settings. -8. Given partial setup, the CLI reports completed changes separately from +9. Given cancellation during preflight or after GitHub generated a PAT, the + CLI reports no mutation and, when relevant, warns that the PAT may remain + active and links to GitHub Settings. +10. Given partial setup, the CLI reports completed changes separately from token cleanup and does not claim rollback. -9. Given complete setup, the CLI says local value discarded, GitHub deletion +11. Given complete setup, the CLI says local value discarded, GitHub deletion still required; it never says revoked without evidence. -10. Given an unsupported URL permission, no misleading link is shown and the - manual permission table remains available. -11. All primary states remain readable without color at narrow width and the +12. Given an unsupported URL permission or contradictory owner/scope choice, + no misleading link is shown and the manual permission table remains + available. +13. Given GitHub initially selects All repositories, the CLI explicitly + instructs the operator to select only the target repository; neither + `target_name` nor a locally selected repository is presented as proof of + that GitHub form choice. +14. All primary states remain readable without color at narrow width and the full URL is copyable. ## 17. Requirements traceability | Requirement | Owner | Test/evidence | Documentation | |---|---|---|---| -| Guided/manual choice (§4.1) | setup CLI + presenter | scenarios 1–2, 6 | how-to-use | -| Exact/provisional grants (§4.1–4.3) | permission policy + URL builder | scenarios 3, 5, 10 | authentication/configuration | -| Actual token audit (§4.1) | existing permission use case | scenarios 4–5 | troubleshooting | -| Cleanup truth (§4.1–4.3) | setup result presenter | scenarios 7–9 | authentication/troubleshooting | -| Accessible UI (§9) | terminal renderer | scenario 11 | how-to-use | +| Guided/manual choice (§4.1) | setup CLI + presenter | scenarios 1–2, 8 | how-to-use | +| Intent collection/reuse (§4.1, §6.1) | questionnaire + pure projection | scenarios 3–4, 9 | how-to-use/configuration | +| Exact/provisional grants (§4.1–4.3) | permission policy + URL builder | scenarios 4–5, 7, 12–13 | authentication/configuration | +| Actual token audit (§4.1) | existing permission use case | scenarios 6–7 | troubleshooting | +| Cleanup truth (§4.1–4.3) | setup result presenter | scenarios 9–11 | authentication/troubleshooting | +| Accessible UI (§9) | terminal renderer | scenario 14 | how-to-use | ## 18. Implementation sequence -1. Use the bootstrap required-grant policy for a provisional initial link; - record provider parameter mapping and require final-plan re-audit. -2. Build one pure URL policy shared with the bot SDD and its contract tests. -3. Integrate the guided/manual choice and raw URL output before the masked - operator PAT prompt, preserving supplied-token paths. -4. Add final-audit correction and honest cleanup states, plus failure tests. +1. Define one permission-intent projection over the existing setup fields, + normalized override precedence, and required-versus-remote-unknown grants. +2. Split/reuse the existing questionnaire so permission-driving local answers + occur before the setup PAT and are not repeated after authenticated remote + inspection. Keep manual and supplied-token paths unchanged. +3. Feed the reviewed projection to the existing pure URL builder, compare + parsed link grants to preview fixtures, and make All-to-selected repository + instructions unavoidable. +4. Reconcile owner kind, remote inventory, and health-workflow evidence with + the final plan; show grant deltas and block mutation until re-audit. 5. Update user/architecture/recovery docs, coverage, UX evidence, and catalog - validation before enabling guidance. + validation before enabling the new flow; do not dogfood this repository's + Issue/Action workflow. ## 19. Definition of Done -- [x] Provisional bootstrap-link decision is settled; final audit requires correction and rerun when grants change. +- [x] Pre-PAT local intent and remote-only provisional-grant boundary are specified; final audit still requires correction when grants change. - [ ] Every MUST maps to acceptance and verification. - [ ] Only documented GitHub URL parameters are emitted; URL contains no secret. - [ ] Account/repo/permissions are checked through the supplied PAT. - [ ] Manual, supplied-token, unattended, dry-run, and `--yes` behavior remain safe. - [ ] Cancellation, partial setup, and deletion wording are accurate. -- [ ] Architecture, 30-case floor, coverage, security, and narrow-terminal UX pass. +- [ ] Architecture, 48-case floor, coverage, security, and narrow-terminal UX pass. - [ ] User, setup, operator, contributor docs and navigation are updated. - [ ] Catalog evidence and generated `specs/CATALOG.md` are current; `pnpm run validate:specifications` passes. @@ -473,8 +662,18 @@ Secret renewal. - Decision: ship guided PAT creation for both roles; do not present automatic PAT issuance, website request replay, or a local account manager as part of this product. A future App token is a separate credential and proposal. -- Implementation snapshot (2026-09-24): the pure URL builder, interactive - guided/manual prompt, account confirmation, final-audit correction link, and - GitHub deletion reminder are implemented locally. The URL never selects a - repository. Controlled browser acceptance and the numeric test budget remain - review gates; do not infer remote PAT deletion from this implementation. +- Implementation snapshot (2026-09-25): the guided pre-PAT phase reuses the + normal questionnaire definitions and validation, skips fields fixed by + flags/config, and passes its in-memory draft and answered IDs to the main + wizard. The reviewed local choices project through the same setup permission + policy used by the final audit. Owner kind is explicitly asked when an + organization grant or inherited-resource access is possible; remote-only + health and inventory conditions are disclosed rather than granted by guess. + The terminal prints the exact URL only after review, and instructs the user + to switch GitHub's All repositories selection to Only select repositories. + Final audits still block missing grants and print a corrected link with + added-grant delta; removed grants are flagged as possible excess access. + The bot URL remains after the final plan. No PAT is generated or revoked by + Copilot; the URL never selects a repository. Browser prefill of twelve + grants was checked without minting a PAT. Controlled browser acceptance, + full numeric test budget, and security review remain open gates. diff --git a/src/__tests__/cli.test.ts b/src/__tests__/cli.test.ts index 87d6745f0..a53d2da67 100644 --- a/src/__tests__/cli.test.ts +++ b/src/__tests__/cli.test.ts @@ -456,7 +456,8 @@ describe('CLI', () => { it('offers the guided setup PAT link and a repair link when its initial audit fails', async () => { const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); const terminal = { - readText: jest.fn().mockResolvedValue({ kind: 'value', value: '' }), + isInteractive: () => true, + readText: jest.fn(async (prompt: string) => ({ kind: 'value', value: prompt.includes('repository owner an organization') || prompt.includes('Review these intended grants') ? '1' : '' })), readSecret: jest.fn().mockResolvedValue({ kind: 'value', value: 'github_pat_guided_setup_test_token' }), close: jest.fn(), }; @@ -472,8 +473,12 @@ describe('CLI', () => { expect(terminal.readText).toHaveBeenCalledWith(expect.stringContaining('How would you like to provide the setup PAT?')); expect(terminal.readSecret).toHaveBeenCalledWith('Setup PAT'); - expect(consoleLogSpy.mock.calls.flat().join('\n')).toContain('Setup PAT access needs attention'); - expect(consoleLogSpy.mock.calls.flat().join('\n')).toContain('Revoke temporary setup PAT'); + const output = consoleLogSpy.mock.calls.flat().join('\n'); + expect(output).toContain('Setup PAT access needs attention'); + expect(output).toContain('Revoke temporary setup PAT'); + expect(output).toContain('contents=write'); + expect(output).toContain('issue_types=write'); + expect(output).toContain('Only select repositories'); expect(runLocalAction).not.toHaveBeenCalled(); expect(process.exitCode).toBe(1); expect(terminal.close).toHaveBeenCalledTimes(1); @@ -482,12 +487,59 @@ describe('CLI', () => { } }); - it('stops before planning if the guided setup PAT belongs to an unintended account', async () => { + it('keeps manual PAT entry free of pre-PAT questions and guided cleanup claims', async () => { const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); const terminal = { + isInteractive: () => true, + readText: jest.fn().mockResolvedValue({ kind: 'value', value: '2' }), + readSecret: jest.fn().mockResolvedValue({ kind: 'value', value: 'ghp_manual_setup_test_token' }), + close: jest.fn(), + }; + const createTerminal = jest.spyOn(terminalDriver, 'createInteractiveTerminalDriver') + .mockReturnValue(terminal as unknown as ReturnType); + mockTokenPermissionInspect.mockResolvedValueOnce({ + role: 'setup', identityStatus: 'valid', identityMessage: 'verified', + ready: false, confirmationRequired: false, checks: [], + }); + try { + await program.parseAsync(['node', 'cli', 'setup']); + expect(terminal.readText).toHaveBeenCalledTimes(1); + expect(terminal.readSecret).toHaveBeenCalledWith('Setup PAT'); + expect(consoleLogSpy.mock.calls.flat().join('\n')).not.toContain('Revoke temporary setup PAT'); + expect(runLocalAction).not.toHaveBeenCalled(); + } finally { + createTerminal.mockRestore(); + } + }); + + it('exits cleanly when permission intent is cancelled before a GitHub link exists', async () => { + const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); + const terminal = { + isInteractive: () => true, readText: jest.fn() .mockResolvedValueOnce({ kind: 'value', value: '' }) - .mockResolvedValueOnce({ kind: 'value', value: '2' }), + .mockResolvedValueOnce({ kind: 'end-of-input' }), + readSecret: jest.fn(), + close: jest.fn(), + }; + const createTerminal = jest.spyOn(terminalDriver, 'createInteractiveTerminalDriver') + .mockReturnValue(terminal as unknown as ReturnType); + try { + await program.parseAsync(['node', 'cli', 'setup']); + expect(terminal.readSecret).not.toHaveBeenCalled(); + expect(consoleLogSpy.mock.calls.flat().join('\n')).not.toContain('Revoke temporary setup PAT'); + expect(runLocalAction).not.toHaveBeenCalled(); + expect(process.exitCode).toBe(130); + } finally { + createTerminal.mockRestore(); + } + }); + + it('stops before planning if the guided setup PAT belongs to an unintended account', async () => { + const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); + const terminal = { + isInteractive: () => true, + readText: jest.fn(async (prompt: string) => ({ kind: 'value', value: prompt.includes('Is this the account you intended') ? '2' : prompt.includes('repository owner an organization') || prompt.includes('Review these intended grants') ? '1' : '' })), readSecret: jest.fn().mockResolvedValue({ kind: 'value', value: 'github_pat_guided_setup_test_token' }), close: jest.fn(), }; diff --git a/src/application/policies/__tests__/setup_pat_intent_policy.test.ts b/src/application/policies/__tests__/setup_pat_intent_policy.test.ts new file mode 100644 index 000000000..7cf491048 --- /dev/null +++ b/src/application/policies/__tests__/setup_pat_intent_policy.test.ts @@ -0,0 +1,102 @@ +import { createDefaultSetupConfiguration } from '../setup_configuration_policy'; +import { fixedSetupPatIntentQuestionIds, setupPatIntentNeedsOwnerKind, setupPatIntentOwnerConflict } from '../setup_pat_intent_policy'; +import { buildSetupPatIntentPermissionRequirements, buildSetupPatIntentUncertainty } from '../setup_token_permission_policy'; +import { buildSetupPatCreationUrl } from '../setup_pat_creation_url_policy'; + +const grants = (configuration: ReturnType, owner: 'Organization' | 'User') => + buildSetupPatIntentPermissionRequirements(configuration, owner).map(item => `${item.scope}:${item.permission}:${item.level}`); + +describe('setup PAT permission intent', () => { + it('turns selected local operations into exact repository and organization grants', () => { + const configuration = createDefaultSetupConfiguration(); + configuration.projects.ids = 'PVT_example'; + configuration.storage.secrets.defaultScope = 'organization'; + expect(grants(configuration, 'Organization')).toEqual(expect.arrayContaining([ + 'repository:Metadata:read', 'repository:Contents:write', 'repository:Secrets:write', + 'repository:Variables:write', 'repository:Issues:write', 'repository:Administration:read', + 'organization:Secrets:write', 'organization:Issue Types:write', 'organization:Projects:write', + ])); + expect(grants(configuration, 'Organization')).not.toContain('repository:Actions:write'); + expect(grants(configuration, 'Organization')).not.toContain('repository:Workflows:write'); + }); + + it('reduces to metadata and contents read when all optional setup operations are off', () => { + const configuration = createDefaultSetupConfiguration(); + configuration.createInitialTag = false; + configuration.manageRepositorySecrets = false; + configuration.manageRepositoryVariables = false; + configuration.features.issues = false; + configuration.features.release = false; + configuration.features.hotfix = false; + configuration.pullRequestApproval = { ...configuration.pullRequestApproval, mode: 'off' }; + expect(grants(configuration, 'User')).toEqual(['repository:Metadata:read', 'repository:Contents:read']); + expect(setupPatIntentNeedsOwnerKind(configuration)).toBe(false); + }); + + it('separates remote-only credential health and inherited inventory from required grants', () => { + const configuration = createDefaultSetupConfiguration(); + const unknown = buildSetupPatIntentUncertainty(configuration, 'Organization'); + expect(unknown.join(' ')).toContain('Actions write'); + expect(unknown.join(' ')).toContain('Workflows write'); + expect(unknown.join(' ')).toContain('organization Secrets write'); + expect(unknown.join(' ')).toContain('organization Variables write'); + expect(grants(configuration, 'Organization')).not.toContain('repository:Actions:write'); + expect(grants(configuration, 'Organization')).not.toContain('organization:Secrets:write'); + }); + + it('flags contradictory personal ownership and explicit organization targets', () => { + const configuration = createDefaultSetupConfiguration(); + configuration.storage.variables.defaultScope = 'organization'; + expect(setupPatIntentOwnerConflict(configuration, 'User')).toBe(true); + expect(setupPatIntentOwnerConflict(configuration, 'Organization')).toBe(false); + configuration.storage.variables.defaultScope = 'repository'; + configuration.projects.ids = 'PVT_example'; + expect(setupPatIntentOwnerConflict(configuration, 'User')).toBe(true); + }); + + it('does not ask choices fixed by config or skip flags', () => { + const fixed = fixedSetupPatIntentQuestionIds({ + features: { issues: false }, + issueWorkflows: { enabled: [] }, + storage: { variables: { defaultScope: 'organization' } }, + createInitialTag: false, + }, true, true); + expect(fixed).toEqual(expect.arrayContaining([ + 'features.issues', 'issueWorkflows.enabled', 'storage.variables.defaultScope', + 'createInitialTag', 'manageRepositoryVariables', 'manageRepositorySecrets', + ])); + }); + + it('projects the reviewed grants to documented URL parameters without selecting a repository', () => { + const configuration = createDefaultSetupConfiguration(); + configuration.features.issues = false; + configuration.features.release = false; + configuration.features.hotfix = false; + configuration.pullRequestApproval = { ...configuration.pullRequestApproval, mode: 'off' }; + const requirements = buildSetupPatIntentPermissionRequirements(configuration, 'User'); + const url = new URL(buildSetupPatCreationUrl({ role: 'setup', owner: 'vypdev', repository: 'copilot', expiresIn: 1, requirements })); + expect(Object.fromEntries(url.searchParams)).toEqual(expect.objectContaining({ + metadata: 'read', contents: 'write', secrets: 'write', actions_variables: 'write', + })); + expect(url.searchParams.has('issues')).toBe(false); + expect(url.searchParams.has('repository')).toBe(false); + }); + + it('prefills the six grants in the reviewed organization example', () => { + const configuration = createDefaultSetupConfiguration(); + configuration.createInitialTag = false; + configuration.features.release = false; + configuration.features.hotfix = false; + configuration.issueWorkflows.enabled = ['feature']; + configuration.pullRequestApproval = { ...configuration.pullRequestApproval, mode: 'off' }; + const requirements = buildSetupPatIntentPermissionRequirements(configuration, 'Organization'); + expect(requirements.map(item => `${item.scope}:${item.permission}:${item.level}`)).toEqual([ + 'repository:Metadata:read', 'repository:Contents:read', 'repository:Secrets:write', + 'repository:Variables:write', 'repository:Issues:write', 'organization:Issue Types:write', + ]); + const url = new URL(buildSetupPatCreationUrl({ role: 'setup', owner: 'vypdev', repository: 'copilot', expiresIn: 1, requirements })); + expect(Object.fromEntries([...url.searchParams].filter(([key]) => !['name', 'description', 'target_name', 'expires_in'].includes(key)))).toEqual({ + actions_variables: 'write', contents: 'read', issue_types: 'write', issues: 'write', metadata: 'read', secrets: 'write', + }); + }); +}); diff --git a/src/application/policies/__tests__/setup_questionnaire_policy.test.ts b/src/application/policies/__tests__/setup_questionnaire_policy.test.ts index 0c1a47db4..ddb23378e 100644 --- a/src/application/policies/__tests__/setup_questionnaire_policy.test.ts +++ b/src/application/policies/__tests__/setup_questionnaire_policy.test.ts @@ -1,6 +1,7 @@ import { createDefaultSetupConfiguration } from '../setup_configuration_policy'; import { createSetupQuestionnaire, + createSetupPermissionIntentQuestionnaire, createSetupReviewState, enterSetupConfirmation, finishSetupQuestionnaire, @@ -10,6 +11,47 @@ import { import type { SetupQuestionnaireContext, SetupQuestionnaireState } from '../../../domain/setup_questionnaire'; describe('setup questionnaire policy', () => { + it('collects only permission-driving questions and reuses their answers in the full wizard', () => { + const defaults = createDefaultSetupConfiguration(); + const intentContext = { skipQuestionIds: ['createInitialTag', 'manageRepositorySecrets'] }; + let state = createSetupPermissionIntentQuestionnaire(defaults, intentContext); + const visited: string[] = []; + while (state.terminal === 'collecting') { + visited.push(state.question!.id); + const value = state.question!.id === 'features.pullRequests' ? 'no' : ''; + state = transitionSetupQuestionnaire(state, { kind: 'answer', value }, intentContext); + } + expect(visited).toContain('features.issues'); + expect(visited).toContain('issueWorkflows.enabled'); + expect(visited).not.toContain('pullRequestApproval.mode'); + expect(visited).not.toContain('createInitialTag'); + expect(visited).not.toContain('manageRepositorySecrets'); + expect(visited).not.toContain('agents.findings.model'); + expect(state.draft.features.pullRequests).toBe(false); + const full = createSetupQuestionnaire(state.draft, { skipQuestionIds: [...intentContext.skipQuestionIds, ...(state.answeredQuestionIds ?? [])] }); + expect(full.question?.id).not.toBe('features.issues'); + expect(full.draft.features.pullRequests).toBe(false); + }); + + it('uses the current draft when permission intent is revised', () => { + const first = createSetupPermissionIntentQuestionnaire(createDefaultSetupConfiguration()); + const changed = transitionSetupQuestionnaire(first, { kind: 'answer', value: 'no' }); + const revised = createSetupPermissionIntentQuestionnaire(changed.draft); + expect(revised.question?.defaultValue).toBe(false); + expect(revised.phase).toBe('permission-intent'); + }); + + it('drops release and hotfix intent when issue automation is turned off', () => { + const state = transitionSetupQuestionnaire( + createSetupPermissionIntentQuestionnaire(createDefaultSetupConfiguration()), + { kind: 'answer', value: 'no' }, + ); + expect(state.draft.features.issues).toBe(false); + expect(state.draft.features.release).toBe(false); + expect(state.draft.features.hotfix).toBe(false); + expect(state.draft.issueWorkflows.enabled).toEqual([]); + }); + it('walks the declared applicable sections in deterministic order', () => { const visited: string[] = []; let state = createSetupQuestionnaire(createDefaultSetupConfiguration()); diff --git a/src/application/policies/setup_pat_intent_policy.ts b/src/application/policies/setup_pat_intent_policy.ts new file mode 100644 index 000000000..e2dd7be0d --- /dev/null +++ b/src/application/policies/setup_pat_intent_policy.ts @@ -0,0 +1,47 @@ +import type { SetupConfiguration } from '../../domain/setup'; +import type { SetupConfigurationOverrides } from './setup_configuration_policy'; +import { buildSetupPatIntentPermissionRequirements } from './setup_token_permission_policy'; + +/** Local inputs with explicit precedence are decisions, not questions. */ +export function fixedSetupPatIntentQuestionIds( + overrides: SetupConfigurationOverrides, + skipVariables: boolean, + skipSecrets: boolean, +): string[] { + const fixed: string[] = []; + for (const feature of ['issues', 'pullRequests'] as const) { + if (overrides.features?.[feature] !== undefined) fixed.push(`features.${feature}`); + } + if (overrides.issueWorkflows?.enabled !== undefined) fixed.push('issueWorkflows.enabled'); + if (overrides.pullRequestApproval?.mode !== undefined) fixed.push('pullRequestApproval.mode'); + if (overrides.projects?.ids !== undefined) fixed.push('projects.ids'); + if (overrides.createInitialTag !== undefined) fixed.push('createInitialTag'); + if (skipVariables || overrides.manageRepositoryVariables !== undefined) fixed.push('manageRepositoryVariables'); + if (skipSecrets || overrides.manageRepositorySecrets !== undefined) fixed.push('manageRepositorySecrets'); + for (const kind of ['variables', 'secrets'] as const) { + if (overrides.storage?.[kind]?.defaultScope !== undefined) fixed.push(`storage.${kind}.defaultScope`); + if (overrides.storage?.[kind]?.preserveExisting !== undefined) fixed.push(`storage.${kind}.preserveExisting`); + } + return fixed; +} + +export function setupPatIntentNeedsOwnerKind(configuration: Readonly): boolean { + return buildSetupPatIntentPermissionRequirements(configuration, 'Organization') + .some(requirement => requirement.scope === 'organization') + || (configuration.manageRepositorySecrets && configuration.storage.secrets.preserveExisting) + || (configuration.manageRepositoryVariables && configuration.storage.variables.preserveExisting); +} + +export function setupPatIntentOwnerConflict(configuration: Readonly, ownerKind: 'Organization' | 'User'): boolean { + return ownerKind === 'User' && ( + (configuration.manageRepositorySecrets && ( + configuration.storage.secrets.defaultScope === 'organization' + || Object.values(configuration.storage.secrets.overrides).includes('organization') + )) + || (configuration.manageRepositoryVariables && ( + configuration.storage.variables.defaultScope === 'organization' + || Object.values(configuration.storage.variables.overrides).includes('organization') + )) + || configuration.projects.ids.trim().length > 0 + ); +} diff --git a/src/application/policies/setup_questionnaire_policy.ts b/src/application/policies/setup_questionnaire_policy.ts index 9976f049f..5ce792891 100644 --- a/src/application/policies/setup_questionnaire_policy.ts +++ b/src/application/policies/setup_questionnaire_policy.ts @@ -13,6 +13,13 @@ import { ISSUE_WORKFLOW_KINDS, ISSUE_WORKFLOW_CATALOG, createIssueWorkflowProfil const AGENT_PROVIDERS = ['codex', 'opencode', 'cursor'] as const; const MODEL_PROVIDERS = ['openai', 'anthropic', 'google', 'openrouter', 'opencode', 'local'] as const; +const PERMISSION_INTENT_QUESTION_IDS = new Set([ + 'features.issues', 'features.pullRequests', 'issueWorkflows.enabled', + 'pullRequestApproval.mode', 'projects.ids', 'createInitialTag', + 'manageRepositoryVariables', 'manageRepositorySecrets', + 'storage.variables.defaultScope', 'storage.variables.preserveExisting', + 'storage.secrets.defaultScope', 'storage.secrets.preserveExisting', +]); interface QuestionDefinition { readonly stateId: SetupQuestion['stateId']; @@ -29,8 +36,21 @@ export function createSetupQuestionnaire( context: SetupQuestionnaireContext = {}, ): SetupQuestionnaireState { const draft = cloneSetupConfiguration(configuration); - const question = questions(draft, false, context)[0]; - return { stateId: question.stateId, draft, question, terminal: 'collecting', configureIndependently: false }; + const question = questions(draft, false, context, 'full')[0]; + return question + ? { stateId: question.stateId, draft, question, terminal: 'collecting', configureIndependently: false, phase: 'full' } + : { stateId: 'review', draft, terminal: 'review', configureIndependently: false, phase: 'full' }; +} + +export function createSetupPermissionIntentQuestionnaire( + configuration: SetupConfiguration, + context: SetupQuestionnaireContext = {}, +): SetupQuestionnaireState { + const draft = cloneSetupConfiguration(configuration); + const question = questions(draft, false, context, 'permission-intent')[0]; + return question + ? { stateId: question.stateId, draft, question, terminal: 'collecting', configureIndependently: false, phase: 'permission-intent', answeredQuestionIds: [] } + : { stateId: 'review', draft, terminal: 'review', configureIndependently: false, phase: 'permission-intent', answeredQuestionIds: [] }; } export function createSetupReviewState(configuration: SetupConfiguration): SetupQuestionnaireState { @@ -54,6 +74,8 @@ export function transitionSetupQuestionnaire( draft: cloneSetupConfiguration(state.draft), terminal: 'cancelled', configureIndependently: state.configureIndependently, + phase: state.phase, + answeredQuestionIds: state.answeredQuestionIds, }; } const parsed = parseAnswer(state.question, event.value); @@ -68,7 +90,8 @@ export function transitionSetupQuestionnaire( ? Boolean(parsed.value) : state.configureIndependently; const draft = applyAnswer(state.draft, state.question, parsed.value); - const nextQuestions = questions(draft, configureIndependently, context); + const answeredQuestionIds = [...(state.answeredQuestionIds ?? []), state.question.id]; + const nextQuestions = questions(draft, configureIndependently, context, state.phase ?? 'full'); const nextIndex = nextQuestions.findIndex((question) => question.id === state.question?.id); const next = nextQuestions[nextIndex + 1]; return next @@ -78,8 +101,10 @@ export function transitionSetupQuestionnaire( question: next, terminal: 'collecting', configureIndependently, + phase: state.phase, + answeredQuestionIds, } - : { stateId: 'review', draft, terminal: 'review', configureIndependently }; + : { stateId: 'review', draft, terminal: 'review', configureIndependently, phase: state.phase, answeredQuestionIds }; } export function enterSetupConfirmation(state: SetupQuestionnaireState): SetupQuestionnaireState { @@ -124,8 +149,12 @@ function questions( draft: SetupConfiguration, independently: boolean, context: SetupQuestionnaireContext, + phase: 'full' | 'permission-intent', ): SetupQuestion[] { - return definitions().filter((definition) => definition.applies?.(draft, independently, context) ?? true) + return definitions().filter((definition) => + (phase === 'full' || PERMISSION_INTENT_QUESTION_IDS.has(definition.id)) + && !context.skipQuestionIds?.includes(definition.id) + && (definition.applies?.(draft, independently, context) ?? true)) .map((definition) => toQuestion(definition, draft, context)); } @@ -412,6 +441,13 @@ function applyAnswer( ): SetupConfiguration { const draft = cloneSetupConfiguration(configuration); if (question.id === 'agents.configureIndependently') return draft; + if (question.id === 'features.issues' && value === false) { + draft.features.issues = false; + draft.features.release = false; + draft.features.hotfix = false; + draft.issueWorkflows = createIssueWorkflowProfile([]); + return draft; + } if (question.id === 'features.pullRequests' && value === false) { draft.features.pullRequests = false; draft.pullRequestApproval = { ...draft.pullRequestApproval, mode: 'off' }; diff --git a/src/application/policies/setup_token_permission_policy.ts b/src/application/policies/setup_token_permission_policy.ts index b1e98ade1..4dd2e39c9 100644 --- a/src/application/policies/setup_token_permission_policy.ts +++ b/src/application/policies/setup_token_permission_policy.ts @@ -63,6 +63,38 @@ export function buildSetupPatPermissionRequirements(): SetupTokenPermissionRequi export function buildConfiguredSetupPatPermissionRequirements( configuration: Readonly, remote?: Readonly, +): SetupTokenPermissionRequirement[] { + return buildSetupPatRequirements(configuration, remote?.ownerType === 'Organization', remote); +} + +/** Grants justified by local choices alone; remote-only conditions stay unresolved. */ +export function buildSetupPatIntentPermissionRequirements( + configuration: Readonly, + ownerKind: 'Organization' | 'User', +): SetupTokenPermissionRequirement[] { + return buildSetupPatRequirements(configuration, ownerKind === 'Organization'); +} + +export function buildSetupPatIntentUncertainty(configuration: Readonly, ownerKind: 'Organization' | 'User'): string[] { + const unknown: string[] = []; + if (configuration.manageRepositorySecrets) { + unknown.push('Existing managed Secrets may require repository Actions write for credential-health checks. A confirmed missing health workflow may also require repository Contents write and Workflows write.'); + } + if (ownerKind === 'Organization') { + for (const kind of ['secrets', 'variables'] as const) { + const managed = kind === 'secrets' ? configuration.manageRepositorySecrets : configuration.manageRepositoryVariables; + if (managed && configuration.storage[kind].preserveExisting && configuration.storage[kind].defaultScope === 'repository') { + unknown.push(`Inherited organization ${kind} may require organization ${kind === 'secrets' ? 'Secrets' : 'Variables'} write after inventory inspection.`); + } + } + } + return unknown; +} + +function buildSetupPatRequirements( + configuration: Readonly, + organization: boolean, + remote?: Readonly, ): SetupTokenPermissionRequirement[] { const repositorySecretNames = buildSetupCredentialRequirements(configuration) .map(credential => credential.name); @@ -86,7 +118,6 @@ export function buildConfiguredSetupPatPermissionRequirements( const needsCredentialHealth = configuration.manageRepositorySecrets && hasExistingCredential; const needsCredentialHealthBootstrap = needsCredentialHealth && remote?.credentialHealthWorkflow === 'missing'; - const organization = remote?.ownerType === 'Organization'; return normalizePermissionRequirements([ requirement({ role: 'setup', scope: 'repository', permission: 'Metadata', level: 'read', reason: 'Resolve repository identity and visibility.', probe: 'metadata' }), diff --git a/src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts b/src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts index 353cc98b8..b71b30f79 100644 --- a/src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts +++ b/src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts @@ -1,4 +1,4 @@ -import { SetupWizardUseCase } from '../setup_wizard_use_case'; +import { buildInitialSetupConfiguration, SetupWizardUseCase } from '../setup_wizard_use_case'; import { buildSetupCredentialRequirements, buildSetupRepositoryVariables, @@ -31,6 +31,24 @@ function dependencies(overrides: Record = {}) { } describe('SetupWizardUseCase', () => { + it('starts the main questionnaire from reviewed permission intent and skips its answered questions', async () => { + const draft = buildInitialSetupConfiguration({ mode: 'interactive', overrides: { pullRequestApproval: { mode: 'off' } } }); + draft.createInitialTag = false; + draft.manageRepositorySecrets = false; + const collect = jest.fn(async (state, _context) => createSetupReviewState(state.draft)); + const result = await new SetupWizardUseCase(dependencies({ collector: { collect } })).execute({ + mode: 'interactive', overrides: { pullRequestApproval: { mode: 'off' } }, + permissionIntent: { draft, answeredQuestionIds: ['createInitialTag', 'manageRepositorySecrets', 'features.issues'] }, + }); + expect(result.status).toBe('completed'); + if (result.status === 'completed') { + expect(result.configuration.createInitialTag).toBe(false); + expect(result.configuration.manageRepositorySecrets).toBe(false); + } + expect(collect.mock.calls[0][0].question?.id).not.toBe('features.issues'); + expect(collect.mock.calls[0][1].skipQuestionIds).toEqual(['createInitialTag', 'manageRepositorySecrets', 'features.issues']); + }); + it('requires an explicit exact CI producer in non-interactive guarded setup', async () => { await expect(new SetupWizardUseCase(dependencies()).execute({ mode: 'non-interactive' })) .rejects.toThrow('guarded/recommend mode requires 1–8 exact test checks'); diff --git a/src/application/usecases/setup/setup_wizard_use_case.ts b/src/application/usecases/setup/setup_wizard_use_case.ts index 75d141133..f87e412c1 100644 --- a/src/application/usecases/setup/setup_wizard_use_case.ts +++ b/src/application/usecases/setup/setup_wizard_use_case.ts @@ -10,6 +10,7 @@ import type { } from '../../ports/setup_wizard_ports'; import { ApplicationError } from '../../errors/application_error'; import type { SetupConfiguration, SetupPlan, SetupRemoteConfiguration } from '../../../domain/setup'; +import type { SetupQuestionnaireContext } from '../../../domain/setup_questionnaire'; import { buildSetupCredentialRequirements, buildSetupRepositoryVariables, @@ -45,6 +46,7 @@ export interface SetupWizardRequest { repository: string; token: string; }; + permissionIntent?: { draft: SetupConfiguration; answeredQuestionIds: readonly string[] }; } export type SetupWizardResult = @@ -92,27 +94,9 @@ export class SetupWizardUseCase { constructor(private readonly dependencies: SetupWizardDependencies) {} async execute(request: SetupWizardRequest): Promise { - const effectiveOverrides = request.mode === 'non-interactive' - && request.overrides?.repositoryAgentGuidance?.agentsPointer === undefined - ? { - ...request.overrides, - repositoryAgentGuidance: { - ...request.overrides?.repositoryAgentGuidance, - agentsPointer: 'create-if-missing' as const, - }, - } - : request.overrides; - const defaults = mergeSetupConfiguration( - mergeSetupConfiguration(createDefaultSetupConfiguration(), { pullRequestApproval: DEFAULT_PULL_REQUEST_APPROVAL_POLICY }), - { - ...effectiveOverrides, - ...(request.skipRepositoryVariables ? { manageRepositoryVariables: false } : {}), - ...(request.skipRepositorySecrets ? { manageRepositorySecrets: false } : {}), - }, - ); - if (defaults.features.pullRequests === false && effectiveOverrides?.pullRequestApproval?.mode === undefined) { - defaults.pullRequestApproval = { ...defaults.pullRequestApproval, mode: 'off' }; - } + const defaults = buildInitialSetupConfiguration(request); + const effectiveOverrides = request.overrides; + const initial = request.permissionIntent ? cloneSetupConfiguration(request.permissionIntent.draft) : defaults; let remoteConfiguration: SetupRemoteConfiguration | undefined; if (request.remoteTarget) { try { @@ -125,21 +109,22 @@ export class SetupWizardUseCase { remoteConfiguration = unavailableRemoteConfiguration(); } } - const defaultValidationErrors = validateSetupConfiguration(defaults, { allowIncompleteApproval: true }); + const defaultValidationErrors = validateSetupConfiguration(initial, { allowIncompleteApproval: true }); if (defaultValidationErrors.length > 0) { throw new ApplicationError( 'configuration.invalid', `Invalid setup configuration:\n${defaultValidationErrors.map((error) => `- ${error}`).join('\n')}`, ); } - const context = { + const context: SetupQuestionnaireContext = { ...(remoteConfiguration ? { remote: remoteConfiguration } : {}), - variableNames: buildSetupRepositoryVariables(defaults).map((variable) => variable.name), - secretNames: buildSetupCredentialRequirements(defaults).map((requirement) => requirement.name), + variableNames: buildSetupRepositoryVariables(initial).map((variable) => variable.name), + secretNames: buildSetupCredentialRequirements(initial).map((requirement) => requirement.name), + ...(request.permissionIntent ? { skipQuestionIds: request.permissionIntent.answeredQuestionIds } : {}), }; const questionnaire = request.mode === 'interactive' - ? await this.collectInteractive(defaults, context) - : createSetupReviewState(defaults); + ? await this.collectInteractive(initial, context) + : createSetupReviewState(initial); if (questionnaire.terminal === 'cancelled') { return { status: 'cancelled', @@ -294,6 +279,32 @@ export class SetupWizardUseCase { } } +export function buildInitialSetupConfiguration(request: Pick): SetupConfiguration { + const effectiveOverrides = request.mode === 'non-interactive' + && request.overrides?.repositoryAgentGuidance?.agentsPointer === undefined + ? { + ...request.overrides, + repositoryAgentGuidance: { + ...request.overrides?.repositoryAgentGuidance, + agentsPointer: 'create-if-missing' as const, + }, + } + : request.overrides; + const defaults = mergeSetupConfiguration( + mergeSetupConfiguration(createDefaultSetupConfiguration(), { pullRequestApproval: DEFAULT_PULL_REQUEST_APPROVAL_POLICY }), + { + ...effectiveOverrides, + ...(request.skipRepositoryVariables ? { manageRepositoryVariables: false } : {}), + ...(request.skipRepositorySecrets ? { manageRepositorySecrets: false } : {}), + }, + ); + if (defaults.features.pullRequests === false && effectiveOverrides?.pullRequestApproval?.mode === undefined) { + defaults.pullRequestApproval = { ...defaults.pullRequestApproval, mode: 'off' }; + } + return defaults; +} + /** An unavailable read is explicit, never an authoritative empty inventory. */ function unavailableRemoteConfiguration(): SetupRemoteConfiguration { return { diff --git a/src/cli/commands/setup.ts b/src/cli/commands/setup.ts index 4cdacc806..079f432a4 100644 --- a/src/cli/commands/setup.ts +++ b/src/cli/commands/setup.ts @@ -7,14 +7,20 @@ import { getGitInfo, isInsideGitRepo } from '../../cli_context'; import { buildSetupParams } from './setup_policy'; import { loadSetupConfigurationOverrides } from '../setup_config_file'; import { SetupQuestionnaireController, SetupWizardUseCase } from '../../application/usecases/setup'; +import { buildInitialSetupConfiguration } from '../../application/usecases/setup/setup_wizard_use_case'; +import { createSetupPermissionIntentQuestionnaire } from '../../application/policies/setup_questionnaire_policy'; +import { fixedSetupPatIntentQuestionIds, setupPatIntentNeedsOwnerKind, setupPatIntentOwnerConflict } from '../../application/policies/setup_pat_intent_policy'; import { SETUP_FEATURE_DESCRIPTIONS, buildSetupCredentialRequirements, effectiveIssueWorkflowFeatures, + validateSetupConfiguration, } from '../../application/policies/setup_configuration_policy'; import { buildConfiguredSetupPatPermissionRequirements, buildSetupPatPermissionRequirements, + buildSetupPatIntentPermissionRequirements, + buildSetupPatIntentUncertainty, buildWorkflowPatPermissionRequirements, } from '../../application/policies/setup_token_permission_policy'; import type { SetupConfigurationOverrides } from '../../application/policies/setup_configuration_policy'; @@ -36,6 +42,7 @@ import { createSetupTokenPermissionsUseCase } from '../../infrastructure/composi import { buildSetupPatCreationUrl, UnsupportedSetupPatLinkError } from '../../application/policies/setup_pat_creation_url_policy'; import { SetupGithubIdentityQueryAdapter } from '../../infrastructure/setup_github_identity_query_adapter'; import { VerifyGuidedWorkflowPatIdentityUseCase } from '../../application/usecases/setup/verify_guided_workflow_pat_identity_use_case'; +import type { SetupTokenPermissionRequirement } from '../../domain/setup_token_permissions'; export function registerSetupCommand(program: Command): void { program @@ -99,18 +106,73 @@ export function registerSetupCommand(program: Command): void { return; } logInfo(`📦 Repository: ${gitInfo.owner}/${gitInfo.repo}`); - const setupPatPermissions = buildSetupPatPermissionRequirements(); + const overrides = loadSetupOverrides(options); + let setupPatPermissions = buildSetupPatPermissionRequirements(); permissionPresenter.showRequirements('setup', setupPatPermissions); let token = getSetupToken(cwd, options.token); let setupPatAccount: string | undefined; + let permissionIntent: { draft: SetupConfiguration; answeredQuestionIds: readonly string[] } | undefined; + let assertedOwnerKind: 'Organization' | 'User' | undefined; if (!token && !options.nonInteractive && !options.dryRun) { - try { - credentialPrompt.configureSetupPatGuide(buildSetupPatCreationUrl({ - role: 'setup', owner: gitInfo.owner, repository: gitInfo.repo, expiresIn: 1, - requirements: setupPatPermissions, - })); - } catch { - logInfo('A guided setup PAT link is unavailable for this repository or permission set. Enter a manually created PAT using the table above.'); + if (await credentialPrompt.chooseSetupPatMethod() === 'guided') { + const fixedQuestionIds = fixedSetupPatIntentQuestionIds(overrides, Boolean(options.skipVariables), Boolean(options.skipSecrets)); + let draft = buildInitialSetupConfiguration({ + mode: 'interactive', overrides, + skipRepositoryVariables: Boolean(options.skipVariables), + skipRepositorySecrets: Boolean(options.skipSecrets), + }); + while (true) { + const context = { skipQuestionIds: fixedQuestionIds }; + const collector = new SetupQuestionnaireController(terminal!, new ConsoleSetupQuestionRenderer('permission-intent')); + const intent = await collector.collect(createSetupPermissionIntentQuestionnaire(draft, context), context); + if (intent.terminal === 'cancelled') throw new SetupTerminalCancelledError(); + draft = intent.draft; + const ownerKind = setupPatIntentNeedsOwnerKind(draft) + ? await credentialPrompt.chooseSetupOwnerKind() : 'User'; + if (ownerKind === 'unknown') { + logInfo('Owner type was not confirmed. Use the manual PAT table, or check whether the GitHub owner is an organization before retrying guided setup.'); + credentialPrompt.useManualSetupPat(); + break; + } + if (setupPatIntentOwnerConflict(draft, ownerKind)) { + logInfo('This plan selects organization storage or Projects, but the owner was declared a personal account. Revise the choices or use the manual PAT path.'); + } + const intentErrors = validateSetupConfiguration(draft, { allowIncompleteApproval: true }); + if (intentErrors.length > 0) { + logInfo(`The selected local configuration needs correction before a guided link can be generated:\n${intentErrors.map(item => ` - ${item}`).join('\n')}`); + } + const preview = buildSetupPatIntentPermissionRequirements(draft, ownerKind); + logInfo('Permission intent:'); + logInfo(` Initial tag: ${draft.createInitialTag ? 'yes' : 'no'}; issue workflows: ${draft.features.issues ? draft.issueWorkflows.enabled.join(', ') || 'none' : 'disabled'}; PR approval: ${draft.pullRequestApproval.mode}`); + logInfo(` Secrets: ${draft.manageRepositorySecrets ? draft.storage.secrets.defaultScope : 'off'}; Variables: ${draft.manageRepositoryVariables ? draft.storage.variables.defaultScope : 'off'}; Projects: ${draft.projects.ids.trim() || 'none'}`); + permissionPresenter.showRequirements('setup', preview); + const uncertain = buildSetupPatIntentUncertainty(draft, ownerKind); + if (uncertain.length) logInfo(`May need after GitHub inspection:\n${uncertain.map(item => ` - ${item}`).join('\n')}`); + const decision = await credentialPrompt.reviewSetupPatIntent(); + if (decision === 'manual') { + credentialPrompt.useManualSetupPat(); + break; + } + if (decision === 'revise') continue; + if (setupPatIntentOwnerConflict(draft, ownerKind) || intentErrors.length > 0) { + throw new ApplicationError('configuration.invalid', 'Correct the reported setup intent or local --config/flags, then retry guided setup. No PAT was requested.'); + } + try { + const url = buildSetupPatCreationUrl({ + role: 'setup', owner: gitInfo.owner, repository: gitInfo.repo, expiresIn: 1, + requirements: preview, + }); + credentialPrompt.configureSetupPatGuide(url); + setupPatPermissions = preview; + assertedOwnerKind = ownerKind; + permissionIntent = { draft, answeredQuestionIds: [...new Set([...fixedQuestionIds, ...(intent.answeredQuestionIds ?? [])])] }; + } catch (error) { + if (!(error instanceof UnsupportedSetupPatLinkError)) throw error; + logInfo('A guided setup PAT link is unavailable for this owner or permission set. Enter a manually created PAT using the table above.'); + credentialPrompt.useManualSetupPat(); + } + break; + } } } if (!token && !options.nonInteractive && !options.dryRun) token = await credentialPrompt.requestSetupPat(); @@ -156,6 +218,19 @@ export function registerSetupCommand(program: Command): void { ): Promise<{ status: 'accepted' } | { status: 'blocked'; errors: readonly string[] }> => { const configuredSetupPatPermissions = buildConfiguredSetupPatPermissionRequirements(configuration, remoteConfiguration); permissionPresenter.showRequirements('setup', configuredSetupPatPermissions); + if (assertedOwnerKind && remoteConfiguration && remoteConfiguration.ownerType !== 'Unknown' + && remoteConfiguration.ownerType !== assertedOwnerKind) { + logInfo(`The owner was declared ${assertedOwnerKind}, but GitHub reports ${remoteConfiguration.ownerType}. The guided link is no longer valid for this plan.`); + if (credentialPrompt.usedGuidedSetupPat) credentialPrompt.showUpdatedSetupPatLink(buildSetupPatCreationUrl({ + role: 'setup', owner: gitInfo.owner, repository: gitInfo.repo, expiresIn: 1, + requirements: configuredSetupPatPermissions, + }), 'final', setupPatPermissionDelta(setupPatPermissions, configuredSetupPatPermissions)); + return { status: 'blocked', errors: ['Repository owner type differs from the pre-PAT selection. Rerun setup with the correct owner type and PAT.'] }; + } + if (credentialPrompt.usedGuidedSetupPat) { + const removed = setupPatPermissionDelta(configuredSetupPatPermissions, setupPatPermissions); + if (removed.length) logInfo(`The final plan no longer requires grants suggested earlier: ${removed.join(', ')}. Your PAT may have excess access; replace it in GitHub if least privilege is required.`); + } if (!token) return { status: 'accepted' }; const permissionReport = await tokenPermissions.inspect({ role: 'setup', owner: gitInfo.owner, repository: gitInfo.repo, token, @@ -169,7 +244,7 @@ export function registerSetupCommand(program: Command): void { if (credentialPrompt.usedGuidedSetupPat) credentialPrompt.showUpdatedSetupPatLink(buildSetupPatCreationUrl({ role: 'setup', owner: gitInfo.owner, repository: gitInfo.repo, expiresIn: 1, requirements: configuredSetupPatPermissions, - }), 'final'); + }), 'final', setupPatPermissionDelta(setupPatPermissions, configuredSetupPatPermissions)); return { status: 'blocked', errors: [ 'The setup PAT has missing or unconfirmed access required by the approved setup plan. Grant or explicitly confirm the permissions shown above and retry.', ] }; @@ -190,10 +265,10 @@ export function registerSetupCommand(program: Command): void { mergeQueueReadiness: createSetupMergeQueueReadinessUseCase(), approvalReadiness: new GithubSetupApprovalReadinessAdapter(), }); - const overrides = loadSetupOverrides(options); const result = await wizard.execute({ mode: options.nonInteractive ? 'non-interactive' : 'interactive', overrides, + ...(permissionIntent ? { permissionIntent } : {}), skipRepositoryVariables: Boolean(options.skipVariables), skipRepositorySecrets: Boolean(options.skipSecrets), previewOnly: Boolean(options.dryRun), @@ -311,6 +386,18 @@ function collectApprovalCheck(value: string, previous: string[]): string[] { return [...previous, value]; } +function setupPatPermissionDelta( + before: readonly SetupTokenPermissionRequirement[], + after: readonly SetupTokenPermissionRequirement[], +): string[] { + const previous = new Map(before.filter(item => item.applicability === 'required') + .map(item => [`${item.scope}:${item.permission.toLowerCase()}`, item.level])); + return after.filter(item => item.applicability === 'required' + && (previous.get(`${item.scope}:${item.permission.toLowerCase()}`) === undefined + || (previous.get(`${item.scope}:${item.permission.toLowerCase()}`) === 'read' && item.level === 'write'))) + .map(item => `${item.scope} ${item.permission} ${item.level}`); +} + function loadSetupOverrides(options: { config?: string; agent?: string; diff --git a/src/cli/setup_credential_prompt_adapter.ts b/src/cli/setup_credential_prompt_adapter.ts index 716e3badd..efed6cde2 100644 --- a/src/cli/setup_credential_prompt_adapter.ts +++ b/src/cli/setup_credential_prompt_adapter.ts @@ -22,6 +22,7 @@ export class SetupCredentialPromptAdapter implements SetupCredentialPromptPort { private workflowPatGuide?: string; private resolveBotIdentity?: (login: string) => Promise; private guidedSetup = false; + private setupMethodChosen = false; private guidedBotIdentity?: SetupGithubIdentity; constructor( @@ -32,6 +33,22 @@ export class SetupCredentialPromptAdapter implements SetupCredentialPromptPort { configureSetupPatGuide(url: string): void { this.setupPatGuide = url; } get usedGuidedSetupPat(): boolean { return this.guidedSetup; } + async chooseSetupPatMethod(): Promise<'guided' | 'manual'> { + if (!this.terminal) return 'manual'; + this.setupMethodChosen = true; + this.guidedSetup = (await this.readChoice('How would you like to provide the setup PAT?', ['guided link', 'manual PAT'], 'guided link')) === 'guided link'; + return this.guidedSetup ? 'guided' : 'manual'; + } + useManualSetupPat(): void { this.guidedSetup = false; this.setupPatGuide = undefined; this.setupMethodChosen = true; } + async chooseSetupOwnerKind(): Promise<'Organization' | 'User' | 'unknown'> { + if (!this.terminal) return 'unknown'; + const choice = await this.readChoice('Is the GitHub repository owner an organization or a personal account?', ['organization', 'personal account', 'not sure']); + return choice === 'organization' ? 'Organization' : choice === 'personal account' ? 'User' : 'unknown'; + } + async reviewSetupPatIntent(): Promise<'continue' | 'revise' | 'manual'> { + if (!this.terminal) return 'manual'; + return await this.readChoice('Review these intended grants before opening GitHub. Continue, revise choices, or enter a PAT manually?', ['continue', 'revise', 'manual']) as 'continue' | 'revise' | 'manual'; + } configureWorkflowPatGuide(url: string, resolveIdentity: (login: string) => Promise): void { this.workflowPatGuide = url; this.resolveBotIdentity = resolveIdentity; @@ -46,7 +63,7 @@ export class SetupCredentialPromptAdapter implements SetupCredentialPromptPort { } showSetupPatCleanupReminder(): void { - if (!this.guidedSetup) return; + if (!this.guidedSetup || !this.setupPatGuide) return; console.log(renderBox( 'The setup PAT was not revoked automatically. After setup finishes or is cancelled, delete it in GitHub → Settings → Developer settings → Personal access tokens. Ending this process does not remove the token from GitHub.', 'Revoke temporary setup PAT', @@ -55,7 +72,7 @@ export class SetupCredentialPromptAdapter implements SetupCredentialPromptPort { console.log('https://github.com/settings/personal-access-tokens'); } - showUpdatedSetupPatLink(url: string, stage: 'bootstrap' | 'final'): void { + showUpdatedSetupPatLink(url: string, stage: 'bootstrap' | 'final', delta?: readonly string[]): void { if (!this.guidedSetup) return; console.log(renderBox( stage === 'bootstrap' @@ -64,22 +81,31 @@ export class SetupCredentialPromptAdapter implements SetupCredentialPromptPort { stage === 'bootstrap' ? 'Setup PAT access needs attention' : 'Setup PAT permissions changed', 33, )); + if (delta?.length) console.log(delta.map(item => ` - ${item}`).join('\n')); console.log(url); } async requestSetupPat(): Promise { if (!this.terminal) return undefined; - if (this.setupPatGuide) { + if (this.setupPatGuide && !this.setupMethodChosen) { this.guidedSetup = (await this.readChoice('How would you like to provide the setup PAT?', ['guided link', 'manual PAT'], 'guided link')) === 'guided link'; if (this.guidedSetup) { console.log(renderBox( - 'Provisional link: Open this GitHub link in your browser, sign in as the account configuring this repository, complete any 2FA or SSO, and review the prefilled fine-grained permissions. GitHub owns token creation; Copilot never handles your web session. Select ONLY this repository manually. The permissions may need updating after the setup questionnaire.', + 'Provisional link: Open this GitHub link in your browser, sign in as the account configuring this repository, complete any 2FA or SSO, and review the prefilled fine-grained permissions. GitHub owns token creation; Copilot never handles your web session. Change All repositories to Only select repositories and select ONLY this repository. Remote inspection may require a corrected token later.', 'Create setup PAT in GitHub', 33, )); console.log(this.setupPatGuide); console.log('Copy the one-time token from GitHub and paste it below. It is hidden and used only for this setup run.'); } } + if (this.setupMethodChosen && this.guidedSetup && this.setupPatGuide) { + console.log(renderBox( + 'Open this GitHub link as the account configuring this repository; complete any 2FA or SSO. Review the prefilled grants. Change All repositories to Only select repositories and select ONLY this repository. GitHub creates the PAT; Copilot does not handle your browser session. Remote inspection may require a corrected token later.', + 'Create setup PAT in GitHub', 33, + )); + console.log(this.setupPatGuide); + console.log('Copy the one-time token from GitHub and paste it below. It is hidden and used only for this setup run.'); + } console.log(renderBox( 'Enter a GitHub setup PAT. It is used in memory for this run only and is never stored. The workflow PAT is a different bot-account token and is requested separately.', 'Setup PAT', @@ -212,7 +238,7 @@ export class SetupCredentialPromptAdapter implements SetupCredentialPromptPort { private async readChoice( label: string, choices: readonly string[], - defaultValue: string, + defaultValue?: string, ): Promise { while (true) { const lines = choices.map((choice, index) => @@ -220,10 +246,10 @@ export class SetupCredentialPromptAdapter implements SetupCredentialPromptPort { const result = await this.terminal!.readText([ label, ...lines, - `Select 1-${choices.length} ${color(`[${choices.indexOf(defaultValue) + 1}]`, 90)}: `, + `Select 1-${choices.length}${defaultValue ? ` ${color(`[${choices.indexOf(defaultValue) + 1}]`, 90)}` : ''}: `, ].join('\n')); if (result.kind !== 'value') throw new SetupTerminalCancelledError(); - if (!result.value.trim()) return defaultValue; + if (!result.value.trim() && defaultValue) return defaultValue; const index = Number(result.value) - 1; if (Number.isInteger(index) && choices[index]) return choices[index]; console.log(color('Select one of the listed options.', 33)); diff --git a/src/cli/setup_question_renderer.ts b/src/cli/setup_question_renderer.ts index d2f637f19..f9a97a2b5 100644 --- a/src/cli/setup_question_renderer.ts +++ b/src/cli/setup_question_renderer.ts @@ -4,7 +4,16 @@ import { color, renderBox } from './setup_prompt_rendering'; import { setupQuestionnaireStateLabel } from '../application/policies/setup_questionnaire_policy'; export class ConsoleSetupQuestionRenderer implements SetupQuestionRenderer { + constructor(private readonly phase: 'full' | 'permission-intent' = 'full') {} + showIntroduction(): void { + if (this.phase === 'permission-intent') { + console.log(renderBox( + 'First, choose the setup options that affect your temporary PAT permissions. These answers will carry into the full wizard and will not be asked again. No GitHub changes happen in this step.', + 'Setup PAT permission intent', + )); + return; + } console.log(renderBox( 'This wizard configures repository workflows, GitHub Actions resources, AI agents, and operational defaults.\n\nThe setup PAT is used in memory only. Runtime credentials are collected separately after the plan is approved.', 'Copilot Setup', diff --git a/src/domain/setup_questionnaire.ts b/src/domain/setup_questionnaire.ts index 297739b10..100617420 100644 --- a/src/domain/setup_questionnaire.ts +++ b/src/domain/setup_questionnaire.ts @@ -38,6 +38,8 @@ export interface SetupQuestionnaireState { readonly validation?: string; readonly terminal: 'collecting' | 'review' | 'confirmation' | 'completed' | 'cancelled'; readonly configureIndependently: boolean; + readonly phase?: 'full' | 'permission-intent'; + readonly answeredQuestionIds?: readonly string[]; } export type SetupQuestionnaireEvent = @@ -49,4 +51,5 @@ export interface SetupQuestionnaireContext { readonly remote?: SetupRemoteConfiguration; readonly variableNames?: readonly string[]; readonly secretNames?: readonly string[]; + readonly skipQuestionIds?: readonly string[]; } From 0edb64e1c473310430d8f1e058dd40b11a7ac871 Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Mon, 28 Sep 2026 00:51:22 +0200 Subject: [PATCH 05/50] codex-setup-temporary-github-auth: cover guided setup PAT flows and final audits --- src/__tests__/cli.test.ts | 338 +++++++++++++++++- .../setup_pat_creation_url_policy.test.ts | 9 + .../__tests__/setup_pat_intent_policy.test.ts | 44 +++ .../setup_questionnaire_policy.test.ts | 17 + src/cli/__tests__/setup_presenters.test.ts | 73 ++++ ...etup_github_identity_query_adapter.test.ts | 19 + 6 files changed, 498 insertions(+), 2 deletions(-) diff --git a/src/__tests__/cli.test.ts b/src/__tests__/cli.test.ts index a53d2da67..d8e2f3a97 100644 --- a/src/__tests__/cli.test.ts +++ b/src/__tests__/cli.test.ts @@ -73,7 +73,7 @@ jest.mock('../infrastructure/composition/setup_token_permissions_composition_roo createSetupTokenPermissionsUseCase: () => ({ inspect: mockTokenPermissionInspect }), })); -const mockRemoteConfigurationInspect = jest.fn().mockResolvedValue({ +const defaultRemoteConfiguration = { ownerType: 'User', repositoryVisibility: 'private', repositorySecrets: [], @@ -85,9 +85,13 @@ const mockRemoteConfigurationInspect = jest.fn().mockResolvedValue({ organizationAccess: 'not_applicable', organizationSecretsAccess: 'not_applicable', organizationVariablesAccess: 'not_applicable', +}; +const mockRemoteConfigurationInspect = jest.fn().mockResolvedValue(defaultRemoteConfiguration); +const mockSetupCredentialsCollect = jest.fn().mockResolvedValue({ + collection: { apiKeys: [] }, checks: [], existingSecretNames: [], }); jest.mock('../infrastructure/composition/setup_credentials_composition_root', () => ({ - createSetupCredentialsUseCase: () => ({ collect: jest.fn().mockResolvedValue({ collection: { apiKeys: [] }, checks: [], existingSecretNames: [] }) }), + createSetupCredentialsUseCase: () => ({ collect: mockSetupCredentialsCollect }), createSetupRemoteConfigurationReadPort: () => ({ inspect: mockRemoteConfigurationInspect, }), @@ -100,6 +104,7 @@ describe('CLI', () => { beforeEach(() => { jest.clearAllMocks(); + mockSetupCredentialsCollect.mockResolvedValue({ collection: { apiKeys: [] }, checks: [], existingSecretNames: [] }); process.exitCode = undefined; process.env.AGENT_PROVIDER = 'opencode'; process.env.AGENT_MODEL = 'test-model'; @@ -453,6 +458,335 @@ describe('CLI', () => { describe('setup', () => { // Token check: hasValidSetupToken/setupEnvFileExists and message variants are covered in // setup_files.test.ts and initial_setup_use_case.test.ts. + beforeEach(() => { + const setupCommand = program.commands.find(command => command.name() === 'setup')!; + for (const option of setupCommand.options) { + setupCommand.setOptionValue(option.attributeName(), option.defaultValue); + } + }); + const guidedTerminal = (answer?: (prompt: string) => string | undefined) => ({ + isInteractive: () => true, + readText: jest.fn(async (prompt: string) => ({ kind: 'value' as const, value: answer?.(prompt) + ?? (prompt.includes('repository owner an organization') ? '2' + : prompt.includes('Review these intended grants') ? '1' : '') })), + readSecret: jest.fn().mockResolvedValue({ kind: 'value', value: 'github_pat_guided_setup_test_token' }), + close: jest.fn(), + }); + + const acceptedSetupPatReport = () => ({ + role: 'setup' as const, identityStatus: 'valid' as const, identityMessage: 'verified', + account: 'operator', ready: true, confirmationRequired: false, checks: [], + }); + + it('carries guided intent through the final audit and prepares the separate bot link', async () => { + const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); + const promptModule = require('../cli/setup_credential_prompt_adapter') as typeof import('../cli/setup_credential_prompt_adapter'); + const input = guidedTerminal(); + const createTerminal = jest.spyOn(terminalDriver, 'createInteractiveTerminalDriver') + .mockReturnValue(input as unknown as ReturnType); + const botGuide = jest.spyOn(promptModule.SetupCredentialPromptAdapter.prototype, 'configureWorkflowPatGuide'); + mockTokenPermissionInspect.mockResolvedValueOnce(acceptedSetupPatReport()); + try { + await program.parseAsync(['node', 'cli', 'setup', '--yes', '--pr-approval-mode', 'off', '--skip-secrets']); + expect(mockTokenPermissionInspect).toHaveBeenCalledTimes(2); + expect(mockTokenPermissionInspect.mock.calls[0][0].requirements).toEqual(expect.arrayContaining([ + expect.objectContaining({ scope: 'repository', permission: 'Contents', level: 'write', applicability: 'required' }), + ])); + expect(botGuide).toHaveBeenCalledTimes(1); + expect(runLocalAction).toHaveBeenCalledTimes(1); + expect(process.exitCode).toBeUndefined(); + } finally { + botGuide.mockRestore(); + createTerminal.mockRestore(); + } + }); + + it('falls back to manual PAT entry when the owner kind cannot be confirmed', async () => { + const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); + const input = guidedTerminal(prompt => prompt.includes('repository owner an organization') ? '3' : ''); + const createTerminal = jest.spyOn(terminalDriver, 'createInteractiveTerminalDriver') + .mockReturnValue(input as unknown as ReturnType); + mockTokenPermissionInspect.mockResolvedValueOnce({ ...acceptedSetupPatReport(), ready: false }); + try { + await program.parseAsync(['node', 'cli', 'setup']); + const { logInfo } = require('../utils/logger'); + expect(logInfo).toHaveBeenCalledWith(expect.stringContaining('Owner type was not confirmed')); + expect(input.readText).not.toHaveBeenCalledWith(expect.stringContaining('Review these intended grants')); + expect(input.readSecret).toHaveBeenCalledWith('Setup PAT'); + expect(consoleLogSpy.mock.calls.flat().join('\n')).not.toContain('Revoke temporary setup PAT'); + expect(process.exitCode).toBe(1); + } finally { createTerminal.mockRestore(); } + }); + + it('allows the operator to choose manual entry after reviewing local intent', async () => { + const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); + const input = guidedTerminal(prompt => prompt.includes('Review these intended grants') ? '3' + : prompt.includes('repository owner an organization') ? '2' : ''); + const createTerminal = jest.spyOn(terminalDriver, 'createInteractiveTerminalDriver') + .mockReturnValue(input as unknown as ReturnType); + mockTokenPermissionInspect.mockResolvedValueOnce({ ...acceptedSetupPatReport(), ready: false }); + try { + await program.parseAsync(['node', 'cli', 'setup']); + expect(input.readSecret).toHaveBeenCalledWith('Setup PAT'); + expect(consoleLogSpy.mock.calls.flat().join('\n')).not.toContain('Revoke temporary setup PAT'); + expect(process.exitCode).toBe(1); + } finally { createTerminal.mockRestore(); } + }); + + it('revises permission intent before the link and drops the initial-tag write grant', async () => { + const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); + let reviews = 0; + let tags = 0; + const input = guidedTerminal(prompt => { + if (prompt.includes('repository owner an organization')) return '2'; + if (prompt.includes('Review these intended grants')) return ++reviews === 1 ? '2' : '1'; + if (prompt.includes('Create v1.0.0')) return ++tags === 2 ? 'no' : ''; + return ''; + }); + const createTerminal = jest.spyOn(terminalDriver, 'createInteractiveTerminalDriver') + .mockReturnValue(input as unknown as ReturnType); + mockTokenPermissionInspect.mockResolvedValueOnce({ ...acceptedSetupPatReport(), ready: false }); + try { + await program.parseAsync(['node', 'cli', 'setup']); + expect(reviews).toBe(2); + expect(tags).toBe(2); + expect(mockTokenPermissionInspect.mock.calls[0][0].requirements).toEqual(expect.arrayContaining([ + expect.objectContaining({ permission: 'Contents', level: 'read' }), + ])); + expect(consoleLogSpy.mock.calls.flat().join('\n')).toContain('contents=read'); + expect(process.exitCode).toBe(1); + } finally { createTerminal.mockRestore(); } + }); + + it('blocks a personal owner paired with organization storage before requesting a PAT', async () => { + const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); + const input = guidedTerminal(); + const createTerminal = jest.spyOn(terminalDriver, 'createInteractiveTerminalDriver') + .mockReturnValue(input as unknown as ReturnType); + try { + await program.parseAsync(['node', 'cli', 'setup', '--secrets-scope', 'organization']); + const { logInfo } = require('../utils/logger'); + expect((logInfo as jest.Mock).mock.calls.flat()).toContainEqual(expect.stringContaining('declared a personal account')); + expect(input.readSecret).not.toHaveBeenCalled(); + expect(runLocalAction).not.toHaveBeenCalled(); + expect(process.exitCode).toBe(1); + } finally { createTerminal.mockRestore(); } + }); + + it('reports invalid fixed local configuration before making a guided PAT link', async () => { + const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); + const configFile = require('../cli/setup_config_file') as typeof import('../cli/setup_config_file'); + const loadConfig = jest.spyOn(configFile, 'loadSetupConfigurationOverrides') + .mockReturnValue({ repository: { mainBranch: 'invalid branch' } }); + const input = guidedTerminal(); + const createTerminal = jest.spyOn(terminalDriver, 'createInteractiveTerminalDriver') + .mockReturnValue(input as unknown as ReturnType); + try { + await program.parseAsync(['node', 'cli', 'setup', '--config', 'invalid-local-config.yml', '--pr-approval-mode', 'off']); + const { logInfo } = require('../utils/logger'); + expect((logInfo as jest.Mock).mock.calls.flat()).toContainEqual(expect.stringContaining('configuration needs correction')); + expect(input.readSecret).not.toHaveBeenCalled(); + expect(process.exitCode).toBe(1); + } finally { loadConfig.mockRestore(); createTerminal.mockRestore(); } + }); + + it('accepts a minimal personal-repository intent without asking the owner kind', async () => { + const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); + const input = guidedTerminal(prompt => { + if (prompt.includes('Issue automation:') || prompt.includes('Pull request automation:') + || prompt.includes('Create v1.0.0') || prompt.includes('Create/update GitHub Actions Variables?') + || prompt.includes('Validate and provision required GitHub Actions Secrets?')) return 'no'; + if (prompt.includes('Review these intended grants')) return '1'; + return ''; + }); + const createTerminal = jest.spyOn(terminalDriver, 'createInteractiveTerminalDriver') + .mockReturnValue(input as unknown as ReturnType); + mockTokenPermissionInspect.mockResolvedValueOnce({ ...acceptedSetupPatReport(), ready: false }); + try { + await program.parseAsync(['node', 'cli', 'setup']); + expect(input.readText.mock.calls.some(([prompt]) => String(prompt).includes('repository owner an organization'))).toBe(false); + expect(mockTokenPermissionInspect.mock.calls[0][0].requirements.map((item: SetupTokenPermissionRequirement) => item.permission)) + .toEqual(['Metadata', 'Contents']); + expect(process.exitCode).toBe(1); + } finally { createTerminal.mockRestore(); } + }); + + it('falls back to manual entry when the setup PAT form cannot express a grant', async () => { + const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); + const urlPolicy = require('../application/policies/setup_pat_creation_url_policy') as typeof import('../application/policies/setup_pat_creation_url_policy'); + const buildLink = jest.spyOn(urlPolicy, 'buildSetupPatCreationUrl') + .mockImplementation(() => { throw new urlPolicy.UnsupportedSetupPatLinkError(['repository Unsupported write']); }); + const input = guidedTerminal(); + const createTerminal = jest.spyOn(terminalDriver, 'createInteractiveTerminalDriver') + .mockReturnValue(input as unknown as ReturnType); + mockTokenPermissionInspect.mockResolvedValueOnce({ ...acceptedSetupPatReport(), ready: false }); + try { + await program.parseAsync(['node', 'cli', 'setup']); + const { logInfo } = require('../utils/logger'); + expect(logInfo).toHaveBeenCalledWith(expect.stringContaining('guided setup PAT link is unavailable')); + expect(input.readSecret).toHaveBeenCalledWith('Setup PAT'); + expect(consoleLogSpy.mock.calls.flat().join('\n')).not.toContain('Revoke temporary setup PAT'); + expect(process.exitCode).toBe(1); + } finally { buildLink.mockRestore(); createTerminal.mockRestore(); } + }); + + it('does not turn an unexpected setup-link failure into a misleading manual fallback', async () => { + const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); + const urlPolicy = require('../application/policies/setup_pat_creation_url_policy') as typeof import('../application/policies/setup_pat_creation_url_policy'); + const buildLink = jest.spyOn(urlPolicy, 'buildSetupPatCreationUrl') + .mockImplementation(() => { throw new Error('unexpected link failure'); }); + const input = guidedTerminal(); + const createTerminal = jest.spyOn(terminalDriver, 'createInteractiveTerminalDriver') + .mockReturnValue(input as unknown as ReturnType); + try { + await program.parseAsync(['node', 'cli', 'setup']); + expect(input.readSecret).not.toHaveBeenCalled(); + expect(process.exitCode).toBe(1); + } finally { buildLink.mockRestore(); createTerminal.mockRestore(); } + }); + + it('retains setup progress when the final bot PAT form is unsupported', async () => { + const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); + const urlPolicy = require('../application/policies/setup_pat_creation_url_policy') as typeof import('../application/policies/setup_pat_creation_url_policy'); + const original = urlPolicy.buildSetupPatCreationUrl; + const buildLink = jest.spyOn(urlPolicy, 'buildSetupPatCreationUrl') + .mockImplementation(input => input.role === 'workflow' + ? (() => { throw new urlPolicy.UnsupportedSetupPatLinkError(['repository Checks read']); })() + : original(input)); + const input = guidedTerminal(); + const createTerminal = jest.spyOn(terminalDriver, 'createInteractiveTerminalDriver') + .mockReturnValue(input as unknown as ReturnType); + mockTokenPermissionInspect.mockResolvedValueOnce(acceptedSetupPatReport()); + try { + await program.parseAsync(['node', 'cli', 'setup', '--yes', '--pr-approval-mode', 'off', '--skip-secrets']); + const { logInfo } = require('../utils/logger'); + expect(logInfo).toHaveBeenCalledWith(expect.stringContaining('guided fine-grained bot PAT link is unavailable')); + expect(runLocalAction).toHaveBeenCalledTimes(1); + } finally { buildLink.mockRestore(); createTerminal.mockRestore(); } + }); + + it('blocks a guided plan when GitHub reports a different owner kind', async () => { + const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); + const input = guidedTerminal(prompt => prompt.includes('repository owner an organization') ? '1' : undefined); + const createTerminal = jest.spyOn(terminalDriver, 'createInteractiveTerminalDriver') + .mockReturnValue(input as unknown as ReturnType); + mockTokenPermissionInspect.mockResolvedValueOnce(acceptedSetupPatReport()); + try { + await program.parseAsync(['node', 'cli', 'setup', '--yes', '--pr-approval-mode', 'off', '--skip-secrets']); + const { logInfo } = require('../utils/logger'); + expect(logInfo).toHaveBeenCalledWith(expect.stringContaining('GitHub reports User')); + expect(consoleLogSpy.mock.calls.flat().join('\n')).toContain('Setup PAT permissions changed'); + expect(mockTokenPermissionInspect).toHaveBeenCalledTimes(1); + expect(runLocalAction).not.toHaveBeenCalled(); + expect(process.exitCode).toBe(1); + } finally { createTerminal.mockRestore(); } + }); + + it('shows the corrected grants and blocks before mutation when the final PAT audit fails', async () => { + const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); + const input = guidedTerminal(); + const createTerminal = jest.spyOn(terminalDriver, 'createInteractiveTerminalDriver') + .mockReturnValue(input as unknown as ReturnType); + mockTokenPermissionInspect + .mockResolvedValueOnce(acceptedSetupPatReport()) + .mockResolvedValueOnce({ ...acceptedSetupPatReport(), ready: false }); + try { + await program.parseAsync(['node', 'cli', 'setup', '--yes', '--pr-approval-mode', 'off', '--skip-secrets']); + expect(mockTokenPermissionInspect).toHaveBeenCalledTimes(2); + expect(consoleLogSpy.mock.calls.flat().join('\n')).toContain('Setup PAT permissions changed'); + expect(runLocalAction).not.toHaveBeenCalled(); + expect(process.exitCode).toBe(1); + } finally { createTerminal.mockRestore(); } + }); + + it('lists remote-only grants added after discovering an existing Secret', async () => { + const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); + const input = guidedTerminal(); + const createTerminal = jest.spyOn(terminalDriver, 'createInteractiveTerminalDriver') + .mockReturnValue(input as unknown as ReturnType); + mockRemoteConfigurationInspect.mockResolvedValueOnce({ + ...defaultRemoteConfiguration, + repositorySecrets: ['PAT'], + }); + mockTokenPermissionInspect + .mockResolvedValueOnce(acceptedSetupPatReport()) + .mockResolvedValueOnce({ ...acceptedSetupPatReport(), ready: false }); + try { + await program.parseAsync(['node', 'cli', 'setup', '--yes', '--pr-approval-mode', 'off']); + const output = consoleLogSpy.mock.calls.flat().join('\n'); + expect(output).toContain('Setup PAT permissions changed'); + expect(output).toContain('repository Actions write'); + expect(runLocalAction).not.toHaveBeenCalled(); + expect(process.exitCode).toBe(1); + } finally { createTerminal.mockRestore(); } + }); + + it('verifies the guided bot PAT account before applying setup and warns on account reuse', async () => { + const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); + const promptModule = require('../cli/setup_credential_prompt_adapter') as typeof import('../cli/setup_credential_prompt_adapter'); + const identityModule = require('../infrastructure/setup_github_identity_query_adapter') as typeof import('../infrastructure/setup_github_identity_query_adapter'); + const input = guidedTerminal(); + const createTerminal = jest.spyOn(terminalDriver, 'createInteractiveTerminalDriver') + .mockReturnValue(input as unknown as ReturnType); + const botIdentity = jest.spyOn(promptModule.SetupCredentialPromptAdapter.prototype, 'guidedWorkflowBotIdentity', 'get') + .mockReturnValue({ id: 42, login: 'operator' }); + const identify = jest.spyOn(identityModule.SetupGithubIdentityQueryAdapter.prototype, 'identify') + .mockResolvedValue({ id: 42, login: 'operator' }); + mockSetupCredentialsCollect.mockResolvedValueOnce({ + collection: { apiKeys: [], workflowPat: { name: 'PAT', value: 'bot-pat' } }, checks: [], existingSecretNames: [], + }); + mockTokenPermissionInspect.mockResolvedValueOnce(acceptedSetupPatReport()); + try { + await program.parseAsync(['node', 'cli', 'setup', '--yes', '--pr-approval-mode', 'off', '--skip-secrets']); + const { logInfo } = require('../utils/logger'); + expect(identify).toHaveBeenCalledWith('bot-pat'); + expect(logInfo).toHaveBeenCalledWith(expect.stringContaining('Workflow PAT owner verified as @operator')); + expect(logInfo).toHaveBeenCalledWith(expect.stringContaining('same GitHub account')); + expect(runLocalAction).toHaveBeenCalledTimes(1); + } finally { identify.mockRestore(); botIdentity.mockRestore(); createTerminal.mockRestore(); } + }); + + it('does not mutate when the guided bot PAT identity check fails', async () => { + const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); + const promptModule = require('../cli/setup_credential_prompt_adapter') as typeof import('../cli/setup_credential_prompt_adapter'); + const identityModule = require('../infrastructure/setup_github_identity_query_adapter') as typeof import('../infrastructure/setup_github_identity_query_adapter'); + const input = guidedTerminal(); + const createTerminal = jest.spyOn(terminalDriver, 'createInteractiveTerminalDriver') + .mockReturnValue(input as unknown as ReturnType); + const botIdentity = jest.spyOn(promptModule.SetupCredentialPromptAdapter.prototype, 'guidedWorkflowBotIdentity', 'get') + .mockReturnValue({ id: 42, login: 'bot-account' }); + const identify = jest.spyOn(identityModule.SetupGithubIdentityQueryAdapter.prototype, 'identify') + .mockResolvedValue({ id: 43, login: 'wrong-account' }); + mockSetupCredentialsCollect.mockResolvedValueOnce({ + collection: { apiKeys: [], workflowPat: { name: 'PAT', value: 'bot-pat' } }, checks: [], existingSecretNames: [], + }); + mockTokenPermissionInspect.mockResolvedValueOnce(acceptedSetupPatReport()); + try { + await program.parseAsync(['node', 'cli', 'setup', '--yes', '--pr-approval-mode', 'off', '--skip-secrets']); + const { logInfo } = require('../utils/logger'); + expect(logInfo).toHaveBeenCalledWith(expect.stringContaining('No setup mutation started')); + expect(runLocalAction).not.toHaveBeenCalled(); + expect(process.exitCode).toBe(1); + } finally { identify.mockRestore(); botIdentity.mockRestore(); createTerminal.mockRestore(); } + }); + + it('warns that a guided bot PAT may be stored if applying setup fails', async () => { + const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); + const promptModule = require('../cli/setup_credential_prompt_adapter') as typeof import('../cli/setup_credential_prompt_adapter'); + const input = guidedTerminal(); + const createTerminal = jest.spyOn(terminalDriver, 'createInteractiveTerminalDriver') + .mockReturnValue(input as unknown as ReturnType); + const botIdentity = jest.spyOn(promptModule.SetupCredentialPromptAdapter.prototype, 'guidedWorkflowBotIdentity', 'get') + .mockReturnValue({ id: 42, login: 'bot-account' }); + mockTokenPermissionInspect.mockResolvedValueOnce(acceptedSetupPatReport()); + (runLocalAction as jest.Mock).mockRejectedValueOnce(new Error('setup failed after mutation started')); + try { + await program.parseAsync(['node', 'cli', 'setup', '--yes', '--pr-approval-mode', 'off', '--skip-secrets']); + const { logInfo } = require('../utils/logger'); + expect(logInfo).toHaveBeenCalledWith(expect.stringContaining('Setup may be partially applied')); + expect(process.exitCode).toBe(1); + } finally { botIdentity.mockRestore(); createTerminal.mockRestore(); } + }); it('offers the guided setup PAT link and a repair link when its initial audit fails', async () => { const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); const terminal = { diff --git a/src/application/policies/__tests__/setup_pat_creation_url_policy.test.ts b/src/application/policies/__tests__/setup_pat_creation_url_policy.test.ts index c1b555789..f89ac8d2b 100644 --- a/src/application/policies/__tests__/setup_pat_creation_url_policy.test.ts +++ b/src/application/policies/__tests__/setup_pat_creation_url_policy.test.ts @@ -65,6 +65,15 @@ describe('buildSetupPatCreationUrl', () => { })).toThrow(UnsupportedSetupPatLinkError); }); + it.each([ + ['Metadata', 'write'], ['Workflows', 'read'], + ] as const)('rejects an unsupported %s %s access level', (name, level) => { + expect(() => buildSetupPatCreationUrl({ + role: 'setup', owner: 'vypdev', repository: 'copilot', expiresIn: 1, + requirements: [permission('setup', 'repository', name, level)], + })).toThrow(UnsupportedSetupPatLinkError); + }); + it.each(['bad/owner', '', 'a'.repeat(40)])('rejects unsafe or invalid owner %s', owner => { expect(() => buildSetupPatCreationUrl({ role: 'setup', owner, repository: 'copilot', expiresIn: 1, requirements: [] })).toThrow(); }); diff --git a/src/application/policies/__tests__/setup_pat_intent_policy.test.ts b/src/application/policies/__tests__/setup_pat_intent_policy.test.ts index 7cf491048..420a2492c 100644 --- a/src/application/policies/__tests__/setup_pat_intent_policy.test.ts +++ b/src/application/policies/__tests__/setup_pat_intent_policy.test.ts @@ -67,6 +67,50 @@ describe('setup PAT permission intent', () => { ])); }); + it('recognizes fixed approval, Projects, and preservation values without treating defaults as fixed', () => { + expect(fixedSetupPatIntentQuestionIds({}, false, false)).toEqual([]); + expect(fixedSetupPatIntentQuestionIds({ + pullRequestApproval: { mode: 'off' }, projects: { ids: '' }, + storage: { secrets: { preserveExisting: false }, variables: { preserveExisting: true } }, + }, false, false)).toEqual(expect.arrayContaining([ + 'pullRequestApproval.mode', 'projects.ids', + 'storage.secrets.preserveExisting', 'storage.variables.preserveExisting', + ])); + }); + + it('asks owner kind for possible inherited resources even with no definite organization grant', () => { + const configuration = createDefaultSetupConfiguration(); + configuration.features.issues = false; + configuration.features.release = false; + configuration.features.hotfix = false; + configuration.pullRequestApproval = { ...configuration.pullRequestApproval, mode: 'off' }; + expect(grants(configuration, 'Organization').some(item => item.startsWith('organization:'))).toBe(false); + expect(setupPatIntentNeedsOwnerKind(configuration)).toBe(true); + configuration.manageRepositorySecrets = false; + configuration.manageRepositoryVariables = false; + expect(setupPatIntentNeedsOwnerKind(configuration)).toBe(false); + }); + + it('omits unresolved remote conditions when management is disabled or owner is personal', () => { + const configuration = createDefaultSetupConfiguration(); + configuration.manageRepositorySecrets = false; + expect(buildSetupPatIntentUncertainty(configuration, 'User')).toEqual([]); + expect(buildSetupPatIntentUncertainty(configuration, 'Organization')).toEqual([ + expect.stringContaining('organization Variables write'), + ]); + configuration.manageRepositoryVariables = false; + expect(buildSetupPatIntentUncertainty(configuration, 'Organization')).toEqual([]); + }); + + it('does not predict organization inventory for explicitly organization-scoped defaults', () => { + const configuration = createDefaultSetupConfiguration(); + configuration.storage.secrets.defaultScope = 'organization'; + configuration.storage.variables.preserveExisting = false; + expect(buildSetupPatIntentUncertainty(configuration, 'Organization')).toEqual([ + expect.stringContaining('Actions write'), + ]); + }); + it('projects the reviewed grants to documented URL parameters without selecting a repository', () => { const configuration = createDefaultSetupConfiguration(); configuration.features.issues = false; diff --git a/src/application/policies/__tests__/setup_questionnaire_policy.test.ts b/src/application/policies/__tests__/setup_questionnaire_policy.test.ts index ddb23378e..0e331e2cb 100644 --- a/src/application/policies/__tests__/setup_questionnaire_policy.test.ts +++ b/src/application/policies/__tests__/setup_questionnaire_policy.test.ts @@ -52,6 +52,23 @@ describe('setup questionnaire policy', () => { expect(state.draft.issueWorkflows.enabled).toEqual([]); }); + it('enters review immediately when the permission-intent phase has no open questions', () => { + const ids = [ + 'features.issues', 'features.pullRequests', 'issueWorkflows.enabled', 'pullRequestApproval.mode', + 'projects.ids', 'createInitialTag', 'manageRepositoryVariables', 'manageRepositorySecrets', + 'storage.variables.defaultScope', 'storage.variables.preserveExisting', + 'storage.secrets.defaultScope', 'storage.secrets.preserveExisting', + ]; + const state = createSetupPermissionIntentQuestionnaire(createDefaultSetupConfiguration(), { skipQuestionIds: ids }); + expect(state).toEqual(expect.objectContaining({ terminal: 'review', phase: 'permission-intent', answeredQuestionIds: [] })); + }); + + it('supports a legacy collecting state without an explicit phase', () => { + const { phase: _phase, ...legacy } = createSetupQuestionnaire(createDefaultSetupConfiguration()); + const next = transitionSetupQuestionnaire(legacy, { kind: 'answer', value: '' }); + expect(next.question?.id).toBe('features.pullRequests'); + }); + it('walks the declared applicable sections in deterministic order', () => { const visited: string[] = []; let state = createSetupQuestionnaire(createDefaultSetupConfiguration()); diff --git a/src/cli/__tests__/setup_presenters.test.ts b/src/cli/__tests__/setup_presenters.test.ts index eeeecb69e..d7008fc18 100644 --- a/src/cli/__tests__/setup_presenters.test.ts +++ b/src/cli/__tests__/setup_presenters.test.ts @@ -260,6 +260,56 @@ describe('setup presenters and prompt-specific adapters', () => { } finally { log.mockRestore(); } }); + it('keeps missing-terminal setup choices on the manual path', async () => { + const adapter = new SetupCredentialPromptAdapter(undefined, {}); + await expect(adapter.chooseSetupPatMethod()).resolves.toBe('manual'); + await expect(adapter.chooseSetupOwnerKind()).resolves.toBe('unknown'); + await expect(adapter.reviewSetupPatIntent()).resolves.toBe('manual'); + await expect(adapter.requestSetupPat()).resolves.toBeUndefined(); + await expect(adapter.confirmGuidedSetupAccount()).resolves.toBe(true); + }); + + it.each([ + ['1', 'Organization'], ['2', 'User'], ['3', 'unknown'], + ] as const)('requires an explicit owner-kind selection %s', async (selection, expected) => { + const log = jest.spyOn(console, 'log').mockImplementation(); + try { + const input = terminal([{ kind: 'value', value: '' }, { kind: 'value', value: selection }]); + await expect(new SetupCredentialPromptAdapter(input, {}).chooseSetupOwnerKind()).resolves.toBe(expected); + expect(input.readText).toHaveBeenCalledTimes(2); + } finally { log.mockRestore(); } + }); + + it('reviews intent explicitly, supports revision, and can fall back to manual input', async () => { + const log = jest.spyOn(console, 'log').mockImplementation(); + try { + const input = terminal([ + { kind: 'value', value: '1' }, + { kind: 'value', value: '2' }, + { kind: 'value', value: '3' }, + { kind: 'value', value: 'manual-token' }, + ]); + const adapter = new SetupCredentialPromptAdapter(input, {}); + await expect(adapter.chooseSetupPatMethod()).resolves.toBe('guided'); + await expect(adapter.reviewSetupPatIntent()).resolves.toBe('revise'); + await expect(adapter.reviewSetupPatIntent()).resolves.toBe('manual'); + adapter.configureSetupPatGuide('https://github.com/settings/personal-access-tokens/new'); + adapter.useManualSetupPat(); + await expect(adapter.requestSetupPat()).resolves.toBe('manual-token'); + adapter.showSetupPatCleanupReminder(); + expect(adapter.usedGuidedSetupPat).toBe(false); + expect(log.mock.calls.flat().join('\n')).not.toContain('Revoke temporary setup PAT'); + } finally { log.mockRestore(); } + }); + + it('rejects an invalid authenticated setup account without prompting', async () => { + const input = terminal([{ kind: 'value', value: '1' }]); + const adapter = new SetupCredentialPromptAdapter(input, {}); + await adapter.chooseSetupPatMethod(); + await expect(adapter.confirmGuidedSetupAccount('bad/account')).resolves.toBe(false); + expect(input.readText).toHaveBeenCalledTimes(1); + }); + it('keeps manual setup PAT choice free of link and cleanup claims', async () => { const log = jest.spyOn(console, 'log').mockImplementation(); try { @@ -290,6 +340,17 @@ describe('setup presenters and prompt-specific adapters', () => { log.mockClear(); adapter.showUpdatedSetupPatLink('https://github.com/settings/personal-access-tokens/new?contents=write', 'final'); expect(log.mock.calls.flat().join('\n')).toContain('no plan mutation has started'); + log.mockClear(); + adapter.showUpdatedSetupPatLink('https://github.com/settings/personal-access-tokens/new?contents=write', 'final', ['repository Contents write']); + expect(log.mock.calls.flat().join('\n')).toContain('repository Contents write'); + } finally { log.mockRestore(); } + }); + + it('does not show a correction link before guided mode is selected', () => { + const log = jest.spyOn(console, 'log').mockImplementation(); + try { + new SetupCredentialPromptAdapter(undefined, {}).showUpdatedSetupPatLink('https://github.com/settings/personal-access-tokens/new', 'final'); + expect(log).not.toHaveBeenCalled(); } finally { log.mockRestore(); } }); @@ -330,6 +391,18 @@ describe('setup presenters and prompt-specific adapters', () => { } finally { log.mockRestore(); } }); + it('propagates cancellation before a bot login can be resolved', async () => { + const log = jest.spyOn(console, 'log').mockImplementation(); + try { + const adapter = new SetupCredentialPromptAdapter(terminal([ + { kind: 'value', value: '1' }, { kind: 'cancel' }, + ]), {}); + adapter.configureWorkflowPatGuide('https://github.com/settings/personal-access-tokens/new', jest.fn()); + await expect(adapter.requestWorkflowPat({ name: 'PAT', kind: 'workflowPat', description: 'Runtime token' })) + .rejects.toBeInstanceOf(SetupTerminalCancelledError); + } finally { log.mockRestore(); } + }); + it('manual bot PAT entry does not assert a guided bot identity', async () => { const log = jest.spyOn(console, 'log').mockImplementation(); try { diff --git a/src/infrastructure/__tests__/setup_github_identity_query_adapter.test.ts b/src/infrastructure/__tests__/setup_github_identity_query_adapter.test.ts index a719f3c35..9dd47c269 100644 --- a/src/infrastructure/__tests__/setup_github_identity_query_adapter.test.ts +++ b/src/infrastructure/__tests__/setup_github_identity_query_adapter.test.ts @@ -26,4 +26,23 @@ describe('SetupGithubIdentityQueryAdapter', () => { await expect(new SetupGithubIdentityQueryAdapter(fetcher as unknown as typeof fetch) .identify('workflow-token')).rejects.toThrow('invalid identity'); }); + + it.each([ + { ok: false, json: async () => ({ message: 'sensitive provider text' }) }, + { ok: true, json: async () => null }, + { ok: true, json: async () => [] }, + ])('rejects non-success and non-object identities without leaking provider content', async response => { + const fetcher = jest.fn().mockResolvedValue(response); + await expect(new SetupGithubIdentityQueryAdapter(fetcher as unknown as typeof fetch) + .identify('workflow-token')).rejects.toThrow('No Secret was written'); + }); + + it('wraps network failures while preserving the cause privately', async () => { + const failure = new Error('sensitive provider text'); + const fetcher = jest.fn().mockRejectedValue(failure); + await expect(new SetupGithubIdentityQueryAdapter(fetcher as unknown as typeof fetch) + .identify('workflow-token')).rejects.toMatchObject({ + message: expect.stringContaining('network access'), cause: failure, + }); + }); }); From eebf179ea50b8682c19a0b444f6aad5b4d19f84c Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Mon, 28 Sep 2026 09:19:19 +0200 Subject: [PATCH 06/50] codex-setup-temporary-github-auth: close remaining setup PAT patch coverage gaps --- src/__tests__/cli.test.ts | 77 +++++++++++++++++-- .../setup_pat_creation_url_policy.test.ts | 14 ++++ .../__tests__/setup_pat_intent_policy.test.ts | 4 + .../setup_questionnaire_policy.test.ts | 13 ++++ .../policies/setup_pat_creation_url_policy.ts | 5 +- src/cli/commands/setup.ts | 6 +- 6 files changed, 108 insertions(+), 11 deletions(-) diff --git a/src/__tests__/cli.test.ts b/src/__tests__/cli.test.ts index d8e2f3a97..ae8e6eaf4 100644 --- a/src/__tests__/cli.test.ts +++ b/src/__tests__/cli.test.ts @@ -611,6 +611,21 @@ describe('CLI', () => { } finally { createTerminal.mockRestore(); } }); + it('describes an explicitly empty issue-workflow selection as none', async () => { + const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); + const configFile = require('../cli/setup_config_file') as typeof import('../cli/setup_config_file'); + const loadConfig = jest.spyOn(configFile, 'loadSetupConfigurationOverrides') + .mockReturnValue({ issueWorkflows: { enabled: [] }, pullRequestApproval: { mode: 'off' } }); + const input = guidedTerminal(); + const createTerminal = jest.spyOn(terminalDriver, 'createInteractiveTerminalDriver') + .mockReturnValue(input as unknown as ReturnType); + try { + await program.parseAsync(['node', 'cli', 'setup', '--config', 'empty-issue-workflows.yml']); + const { logInfo } = require('../utils/logger'); + expect(logInfo).toHaveBeenCalledWith(expect.stringContaining('issue workflows: none')); + } finally { loadConfig.mockRestore(); createTerminal.mockRestore(); } + }); + it('falls back to manual entry when the setup PAT form cannot express a grant', async () => { const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); const urlPolicy = require('../application/policies/setup_pat_creation_url_policy') as typeof import('../application/policies/setup_pat_creation_url_policy'); @@ -665,6 +680,26 @@ describe('CLI', () => { } finally { buildLink.mockRestore(); createTerminal.mockRestore(); } }); + it('surfaces an unexpected bot-link failure instead of silently entering manual mode', async () => { + const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); + const urlPolicy = require('../application/policies/setup_pat_creation_url_policy') as typeof import('../application/policies/setup_pat_creation_url_policy'); + const original = urlPolicy.buildSetupPatCreationUrl; + const buildLink = jest.spyOn(urlPolicy, 'buildSetupPatCreationUrl') + .mockImplementation(input => { + if (input.role === 'workflow') throw new Error('unexpected bot link failure'); + return original(input); + }); + const input = guidedTerminal(); + const createTerminal = jest.spyOn(terminalDriver, 'createInteractiveTerminalDriver') + .mockReturnValue(input as unknown as ReturnType); + mockTokenPermissionInspect.mockResolvedValueOnce(acceptedSetupPatReport()); + try { + await program.parseAsync(['node', 'cli', 'setup', '--yes', '--pr-approval-mode', 'off', '--skip-secrets']); + expect(runLocalAction).not.toHaveBeenCalled(); + expect(process.exitCode).toBe(1); + } finally { buildLink.mockRestore(); createTerminal.mockRestore(); } + }); + it('blocks a guided plan when GitHub reports a different owner kind', async () => { const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); const input = guidedTerminal(prompt => prompt.includes('repository owner an organization') ? '1' : undefined); @@ -721,7 +756,23 @@ describe('CLI', () => { } finally { createTerminal.mockRestore(); } }); - it('verifies the guided bot PAT account before applying setup and warns on account reuse', async () => { + it('warns about excess organization grants if remote owner type cannot be resolved', async () => { + const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); + const input = guidedTerminal(prompt => prompt.includes('repository owner an organization') ? '1' : undefined); + const createTerminal = jest.spyOn(terminalDriver, 'createInteractiveTerminalDriver') + .mockReturnValue(input as unknown as ReturnType); + mockRemoteConfigurationInspect.mockResolvedValueOnce({ ...defaultRemoteConfiguration, ownerType: 'Unknown' }); + mockTokenPermissionInspect.mockResolvedValueOnce(acceptedSetupPatReport()); + try { + await program.parseAsync(['node', 'cli', 'setup', '--yes', '--pr-approval-mode', 'off', '--skip-secrets']); + const { logInfo } = require('../utils/logger'); + expect(logInfo).toHaveBeenCalledWith(expect.stringContaining('PAT may have excess access')); + } finally { createTerminal.mockRestore(); } + }); + + it.each([ + ['operator', true], ['separate-bot', false], + ])('verifies the guided bot PAT account @%s before applying setup', async (login, reusedAccount) => { const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); const promptModule = require('../cli/setup_credential_prompt_adapter') as typeof import('../cli/setup_credential_prompt_adapter'); const identityModule = require('../infrastructure/setup_github_identity_query_adapter') as typeof import('../infrastructure/setup_github_identity_query_adapter'); @@ -729,9 +780,9 @@ describe('CLI', () => { const createTerminal = jest.spyOn(terminalDriver, 'createInteractiveTerminalDriver') .mockReturnValue(input as unknown as ReturnType); const botIdentity = jest.spyOn(promptModule.SetupCredentialPromptAdapter.prototype, 'guidedWorkflowBotIdentity', 'get') - .mockReturnValue({ id: 42, login: 'operator' }); + .mockReturnValue({ id: 42, login }); const identify = jest.spyOn(identityModule.SetupGithubIdentityQueryAdapter.prototype, 'identify') - .mockResolvedValue({ id: 42, login: 'operator' }); + .mockResolvedValue({ id: 42, login }); mockSetupCredentialsCollect.mockResolvedValueOnce({ collection: { apiKeys: [], workflowPat: { name: 'PAT', value: 'bot-pat' } }, checks: [], existingSecretNames: [], }); @@ -740,8 +791,9 @@ describe('CLI', () => { await program.parseAsync(['node', 'cli', 'setup', '--yes', '--pr-approval-mode', 'off', '--skip-secrets']); const { logInfo } = require('../utils/logger'); expect(identify).toHaveBeenCalledWith('bot-pat'); - expect(logInfo).toHaveBeenCalledWith(expect.stringContaining('Workflow PAT owner verified as @operator')); - expect(logInfo).toHaveBeenCalledWith(expect.stringContaining('same GitHub account')); + expect(logInfo).toHaveBeenCalledWith(expect.stringContaining(`Workflow PAT owner verified as @${login}`)); + expect((logInfo as jest.Mock).mock.calls.flat().some((message: unknown) => String(message).includes('same GitHub account'))) + .toBe(reusedAccount); expect(runLocalAction).toHaveBeenCalledTimes(1); } finally { identify.mockRestore(); botIdentity.mockRestore(); createTerminal.mockRestore(); } }); @@ -787,6 +839,21 @@ describe('CLI', () => { expect(process.exitCode).toBe(1); } finally { botIdentity.mockRestore(); createTerminal.mockRestore(); } }); + + it('reports partial completion when an action succeeds but returns errors', async () => { + const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); + const input = guidedTerminal(); + const createTerminal = jest.spyOn(terminalDriver, 'createInteractiveTerminalDriver') + .mockReturnValue(input as unknown as ReturnType); + mockTokenPermissionInspect.mockResolvedValueOnce(acceptedSetupPatReport()); + (runLocalAction as jest.Mock).mockResolvedValueOnce([{ success: true, errors: ['partial failure'] }]); + try { + await program.parseAsync(['node', 'cli', 'setup', '--yes', '--pr-approval-mode', 'off', '--skip-secrets']); + const { logInfo } = require('../utils/logger'); + expect(logInfo).toHaveBeenCalledWith(expect.stringContaining('partial completion')); + expect(process.exitCode).toBe(1); + } finally { createTerminal.mockRestore(); } + }); it('offers the guided setup PAT link and a repair link when its initial audit fails', async () => { const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); const terminal = { diff --git a/src/application/policies/__tests__/setup_pat_creation_url_policy.test.ts b/src/application/policies/__tests__/setup_pat_creation_url_policy.test.ts index f89ac8d2b..b536538ca 100644 --- a/src/application/policies/__tests__/setup_pat_creation_url_policy.test.ts +++ b/src/application/policies/__tests__/setup_pat_creation_url_policy.test.ts @@ -58,6 +58,20 @@ describe('buildSetupPatCreationUrl', () => { expect(url.searchParams.get('contents')).toBe('write'); }); + it('keeps even the largest valid owner, repository, and grant set within a practical terminal URL', () => { + const grants = [ + ...['Metadata', 'Contents', 'Secrets', 'Variables', 'Issues', 'Actions', 'Administration', 'Workflows', 'Pull requests'] + .map(name => permission('workflow', 'repository', name, name === 'Metadata' ? 'read' : 'write')), + ...['Secrets', 'Variables', 'Issue Types', 'Projects', 'Members'] + .map(name => permission('workflow', 'organization', name, 'write')), + ]; + const url = buildSetupPatCreationUrl({ + role: 'workflow', owner: 'a'.repeat(39), repository: 'r'.repeat(100), expiresIn: 366, + requirements: grants, + }); + expect(url.length).toBeLessThan(2_048); + }); + it('rejects unsupported Checks instead of producing an incomplete guarded link', () => { expect(() => buildSetupPatCreationUrl({ role: 'workflow', owner: 'vypdev', repository: 'copilot', expiresIn: 90, diff --git a/src/application/policies/__tests__/setup_pat_intent_policy.test.ts b/src/application/policies/__tests__/setup_pat_intent_policy.test.ts index 420a2492c..3303875f1 100644 --- a/src/application/policies/__tests__/setup_pat_intent_policy.test.ts +++ b/src/application/policies/__tests__/setup_pat_intent_policy.test.ts @@ -87,6 +87,10 @@ describe('setup PAT permission intent', () => { expect(grants(configuration, 'Organization').some(item => item.startsWith('organization:'))).toBe(false); expect(setupPatIntentNeedsOwnerKind(configuration)).toBe(true); configuration.manageRepositorySecrets = false; + expect(setupPatIntentNeedsOwnerKind(configuration)).toBe(true); + configuration.storage.variables.preserveExisting = false; + expect(setupPatIntentNeedsOwnerKind(configuration)).toBe(false); + configuration.storage.variables.preserveExisting = true; configuration.manageRepositoryVariables = false; expect(setupPatIntentNeedsOwnerKind(configuration)).toBe(false); }); diff --git a/src/application/policies/__tests__/setup_questionnaire_policy.test.ts b/src/application/policies/__tests__/setup_questionnaire_policy.test.ts index 0e331e2cb..2ab079678 100644 --- a/src/application/policies/__tests__/setup_questionnaire_policy.test.ts +++ b/src/application/policies/__tests__/setup_questionnaire_policy.test.ts @@ -63,6 +63,19 @@ describe('setup questionnaire policy', () => { expect(state).toEqual(expect.objectContaining({ terminal: 'review', phase: 'permission-intent', answeredQuestionIds: [] })); }); + it('enters the full review immediately when all questions are already fixed', () => { + const configuration = createDefaultSetupConfiguration(); + const ids: string[] = []; + let state = createSetupQuestionnaire(configuration); + while (state.terminal === 'collecting') { + ids.push(state.question!.id); + state = transitionSetupQuestionnaire(state, { kind: 'answer', value: '' }); + } + expect(createSetupQuestionnaire(configuration, { skipQuestionIds: ids })).toEqual( + expect.objectContaining({ terminal: 'review', phase: 'full' }), + ); + }); + it('supports a legacy collecting state without an explicit phase', () => { const { phase: _phase, ...legacy } = createSetupQuestionnaire(createDefaultSetupConfiguration()); const next = transitionSetupQuestionnaire(legacy, { kind: 'answer', value: '' }); diff --git a/src/application/policies/setup_pat_creation_url_policy.ts b/src/application/policies/setup_pat_creation_url_policy.ts index 0898deb6f..eae809762 100644 --- a/src/application/policies/setup_pat_creation_url_policy.ts +++ b/src/application/policies/setup_pat_creation_url_policy.ts @@ -70,7 +70,6 @@ export function buildSetupPatCreationUrl(input: Readonly<{ for (const [key, level] of [...grants].sort(([left], [right]) => left.localeCompare(right))) { url.searchParams.set(key, level); } - const result = url.toString(); - if (result.length > 2_048) throw new Error('PAT form URL exceeds the supported terminal length; create the PAT manually.'); - return result; + // Owner/repository lengths and the finite permission map bound this URL well below terminal limits. + return url.toString(); } diff --git a/src/cli/commands/setup.ts b/src/cli/commands/setup.ts index 079f432a4..244193e92 100644 --- a/src/cli/commands/setup.ts +++ b/src/cli/commands/setup.ts @@ -165,7 +165,7 @@ export function registerSetupCommand(program: Command): void { credentialPrompt.configureSetupPatGuide(url); setupPatPermissions = preview; assertedOwnerKind = ownerKind; - permissionIntent = { draft, answeredQuestionIds: [...new Set([...fixedQuestionIds, ...(intent.answeredQuestionIds ?? [])])] }; + permissionIntent = { draft, answeredQuestionIds: [...new Set([...fixedQuestionIds, ...intent.answeredQuestionIds!])] }; } catch (error) { if (!(error instanceof UnsupportedSetupPatLinkError)) throw error; logInfo('A guided setup PAT link is unavailable for this owner or permission set. Enter a manually created PAT using the table above.'); @@ -221,7 +221,7 @@ export function registerSetupCommand(program: Command): void { if (assertedOwnerKind && remoteConfiguration && remoteConfiguration.ownerType !== 'Unknown' && remoteConfiguration.ownerType !== assertedOwnerKind) { logInfo(`The owner was declared ${assertedOwnerKind}, but GitHub reports ${remoteConfiguration.ownerType}. The guided link is no longer valid for this plan.`); - if (credentialPrompt.usedGuidedSetupPat) credentialPrompt.showUpdatedSetupPatLink(buildSetupPatCreationUrl({ + credentialPrompt.showUpdatedSetupPatLink(buildSetupPatCreationUrl({ role: 'setup', owner: gitInfo.owner, repository: gitInfo.repo, expiresIn: 1, requirements: configuredSetupPatPermissions, }), 'final', setupPatPermissionDelta(setupPatPermissions, configuredSetupPatPermissions)); @@ -310,7 +310,7 @@ export function registerSetupCommand(program: Command): void { role: 'workflow', owner: gitInfo.owner, repository: gitInfo.repo, expiresIn: 90, requirements: workflowTokenPermissions, }); - credentialPrompt.configureWorkflowPatGuide(workflowPatGuide, login => githubIdentities.resolve(login, token ?? '')); + credentialPrompt.configureWorkflowPatGuide(workflowPatGuide, login => githubIdentities.resolve(login, token!)); } catch (error) { if (!(error instanceof UnsupportedSetupPatLinkError)) throw error; logInfo('A guided fine-grained bot PAT link is unavailable for one or more required permissions. Use the permission table and manual path; review whether a classic PAT is required for this plan.'); From 49123847e75f52eefae47fcbf7ae5a8abc85f759 Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Mon, 28 Sep 2026 09:21:33 +0200 Subject: [PATCH 07/50] codex-setup-temporary-github-auth: refresh CLI bundle for setup PAT coverage cleanup --- build/cli/index.js | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/build/cli/index.js b/build/cli/index.js index 00cabc677..151b042e7 100755 --- a/build/cli/index.js +++ b/build/cli/index.js @@ -47778,10 +47778,8 @@ function buildSetupPatCreationUrl(input) { for (const [key, level] of [...grants].sort(([left], [right]) => left.localeCompare(right))) { url.searchParams.set(key, level); } - const result = url.toString(); - if (result.length > 2048) - throw new Error('PAT form URL exceeds the supported terminal length; create the PAT manually.'); - return result; + // Owner/repository lengths and the finite permission map bound this URL well below terminal limits. + return url.toString(); } @@ -65472,7 +65470,7 @@ function registerSetupCommand(program) { credentialPrompt.configureSetupPatGuide(url); setupPatPermissions = preview; assertedOwnerKind = ownerKind; - permissionIntent = { draft, answeredQuestionIds: [...new Set([...fixedQuestionIds, ...(intent.answeredQuestionIds ?? [])])] }; + permissionIntent = { draft, answeredQuestionIds: [...new Set([...fixedQuestionIds, ...intent.answeredQuestionIds])] }; } catch (error) { if (!(error instanceof setup_pat_creation_url_policy_1.UnsupportedSetupPatLinkError)) @@ -65526,11 +65524,10 @@ function registerSetupCommand(program) { if (assertedOwnerKind && remoteConfiguration && remoteConfiguration.ownerType !== 'Unknown' && remoteConfiguration.ownerType !== assertedOwnerKind) { (0, logger_1.logInfo)(`The owner was declared ${assertedOwnerKind}, but GitHub reports ${remoteConfiguration.ownerType}. The guided link is no longer valid for this plan.`); - if (credentialPrompt.usedGuidedSetupPat) - credentialPrompt.showUpdatedSetupPatLink((0, setup_pat_creation_url_policy_1.buildSetupPatCreationUrl)({ - role: 'setup', owner: gitInfo.owner, repository: gitInfo.repo, expiresIn: 1, - requirements: configuredSetupPatPermissions, - }), 'final', setupPatPermissionDelta(setupPatPermissions, configuredSetupPatPermissions)); + credentialPrompt.showUpdatedSetupPatLink((0, setup_pat_creation_url_policy_1.buildSetupPatCreationUrl)({ + role: 'setup', owner: gitInfo.owner, repository: gitInfo.repo, expiresIn: 1, + requirements: configuredSetupPatPermissions, + }), 'final', setupPatPermissionDelta(setupPatPermissions, configuredSetupPatPermissions)); return { status: 'blocked', errors: ['Repository owner type differs from the pre-PAT selection. Rerun setup with the correct owner type and PAT.'] }; } if (credentialPrompt.usedGuidedSetupPat) { @@ -65617,7 +65614,7 @@ function registerSetupCommand(program) { role: 'workflow', owner: gitInfo.owner, repository: gitInfo.repo, expiresIn: 90, requirements: workflowTokenPermissions, }); - credentialPrompt.configureWorkflowPatGuide(workflowPatGuide, login => githubIdentities.resolve(login, token ?? '')); + credentialPrompt.configureWorkflowPatGuide(workflowPatGuide, login => githubIdentities.resolve(login, token)); } catch (error) { if (!(error instanceof setup_pat_creation_url_policy_1.UnsupportedSetupPatLinkError)) From cfa69dd7455a413d1e491c7b7314b2a6affe538b Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Mon, 28 Sep 2026 10:39:30 +0200 Subject: [PATCH 08/50] codex-setup-temporary-github-auth: clarify Checks limitation for guided bot PATs --- docs/authentication.mdx | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/docs/authentication.mdx b/docs/authentication.mdx index 65f8d42c2..67b152c71 100644 --- a/docs/authentication.mdx +++ b/docs/authentication.mdx @@ -41,11 +41,16 @@ target repository, and review the final GitHub form. A guided setup PAT uses a one-day suggested expiry; delete it yourself in [GitHub PAT Settings](https://github.com/settings/personal-access-tokens) afterward. The bot PAT uses a 90-day suggested expiry, may be shortened by organization policy, and remains in Actions Secret `PAT`; arrange renewal -before it expires. If guarded approval requires `Checks`, GitHub's fine-grained -PAT cannot prefill or provide that permission. Setup omits the guided bot link -for that plan; review a compatible manually created credential and the -permission audit instead. Existing command-line token flags also remain, but -putting a PAT in a command can expose it in shell history or process listings. +before it expires. GitHub [lists the Checks API among fine-grained PAT +limitations](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#fine-grained-personal-access-tokens-limitations) +and does not list `Checks` in its [supported PAT form +permissions](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#repository-permissions). +If guarded approval requires `Checks` read, setup cannot offer a guided +fine-grained bot PAT link for that plan. Do not assume that creating a +fine-grained PAT manually will bypass this limitation: use a compatible +credential and confirm its access with the permission audit. Existing +command-line token flags also remain, but putting a PAT in a command can +expose it in shell history or process listings. ## Permission tables in `copilot setup` From 3d797c44268c38ff1c89c49a0d855711db9cb241 Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Mon, 28 Sep 2026 10:46:49 +0200 Subject: [PATCH 09/50] codex-setup-temporary-github-auth: explain conflicting Checks PAT guidance and manual option --- docs/authentication.mdx | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/docs/authentication.mdx b/docs/authentication.mdx index 67b152c71..4f78b3921 100644 --- a/docs/authentication.mdx +++ b/docs/authentication.mdx @@ -41,16 +41,17 @@ target repository, and review the final GitHub form. A guided setup PAT uses a one-day suggested expiry; delete it yourself in [GitHub PAT Settings](https://github.com/settings/personal-access-tokens) afterward. The bot PAT uses a 90-day suggested expiry, may be shortened by organization policy, and remains in Actions Secret `PAT`; arrange renewal -before it expires. GitHub [lists the Checks API among fine-grained PAT -limitations](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#fine-grained-personal-access-tokens-limitations) -and does not list `Checks` in its [supported PAT form -permissions](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#repository-permissions). -If guarded approval requires `Checks` read, setup cannot offer a guided -fine-grained bot PAT link for that plan. Do not assume that creating a -fine-grained PAT manually will bypass this limitation: use a compatible -credential and confirm its access with the permission audit. Existing -command-line token flags also remain, but putting a PAT in a command can -expose it in shell history or process listings. +before it expires. GitHub's documentation is inconsistent: its [PAT limitations +list](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#fine-grained-personal-access-tokens-limitations) +calls out the Checks API, while the [check-runs endpoint +reference](https://docs.github.com/en/rest/checks/runs#list-check-runs-for-a-git-reference) +lists fine-grained PATs with `Checks` read. The documented [PAT form +parameters](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#repository-permissions) +do not include `Checks`, so setup cannot prefill a guided bot link for guarded +approval. If your manual fine-grained PAT form offers `Checks` read, select it; +otherwise use a compatible credential. In either case, confirm access with the +permission audit. Existing command-line token flags also remain, but putting +a PAT in a command can expose it in shell history or process listings. ## Permission tables in `copilot setup` From 665576b362f6d05b0de0ed785b7c223b2254166b Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Mon, 28 Sep 2026 11:00:36 +0200 Subject: [PATCH 10/50] codex-setup-temporary-github-auth: cover Projects-only setup PAT owner selection --- src/__tests__/cli.test.ts | 23 +++++++++++++++++++ .../__tests__/setup_pat_intent_policy.test.ts | 21 +++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/src/__tests__/cli.test.ts b/src/__tests__/cli.test.ts index ae8e6eaf4..fa874ab6c 100644 --- a/src/__tests__/cli.test.ts +++ b/src/__tests__/cli.test.ts @@ -611,6 +611,29 @@ describe('CLI', () => { } finally { createTerminal.mockRestore(); } }); + it('asks the owner kind and includes Projects when no other organization grant is selected', async () => { + const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); + const configFile = require('../cli/setup_config_file') as typeof import('../cli/setup_config_file'); + const loadConfig = jest.spyOn(configFile, 'loadSetupConfigurationOverrides').mockReturnValue({ + createInitialTag: false, + features: { issues: false, release: false, hotfix: false }, + projects: { ids: 'PVT_example' }, + }); + const input = guidedTerminal(prompt => prompt.includes('repository owner an organization') ? '1' : undefined); + const createTerminal = jest.spyOn(terminalDriver, 'createInteractiveTerminalDriver') + .mockReturnValue(input as unknown as ReturnType); + mockTokenPermissionInspect.mockResolvedValueOnce({ ...acceptedSetupPatReport(), ready: false }); + try { + await program.parseAsync(['node', 'cli', 'setup', '--config', 'projects-only.yml', + '--skip-variables', '--skip-secrets', '--pr-approval-mode', 'off']); + expect(input.readText.mock.calls.some(([prompt]) => String(prompt).includes('repository owner an organization'))).toBe(true); + expect(mockTokenPermissionInspect.mock.calls[0][0].requirements.filter((item: SetupTokenPermissionRequirement) => item.scope === 'organization')) + .toEqual([expect.objectContaining({ permission: 'Projects', level: 'write' })]); + expect(input.readSecret).toHaveBeenCalledWith('Setup PAT'); + expect(process.exitCode).toBe(1); + } finally { loadConfig.mockRestore(); createTerminal.mockRestore(); } + }); + it('describes an explicitly empty issue-workflow selection as none', async () => { const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); const configFile = require('../cli/setup_config_file') as typeof import('../cli/setup_config_file'); diff --git a/src/application/policies/__tests__/setup_pat_intent_policy.test.ts b/src/application/policies/__tests__/setup_pat_intent_policy.test.ts index 3303875f1..6987aadcd 100644 --- a/src/application/policies/__tests__/setup_pat_intent_policy.test.ts +++ b/src/application/policies/__tests__/setup_pat_intent_policy.test.ts @@ -95,6 +95,27 @@ describe('setup PAT permission intent', () => { expect(setupPatIntentNeedsOwnerKind(configuration)).toBe(false); }); + it('asks owner kind for Projects even when all other organization grants are disabled', () => { + const configuration = createDefaultSetupConfiguration(); + configuration.createInitialTag = false; + configuration.features.issues = false; + configuration.features.release = false; + configuration.features.hotfix = false; + configuration.pullRequestApproval = { ...configuration.pullRequestApproval, mode: 'off' }; + configuration.manageRepositorySecrets = false; + configuration.manageRepositoryVariables = false; + configuration.projects.ids = 'PVT_example'; + + expect(grants(configuration, 'Organization').filter(item => item.startsWith('organization:'))) + .toEqual(['organization:Projects:write']); + expect(setupPatIntentNeedsOwnerKind(configuration)).toBe(true); + expect(setupPatIntentOwnerConflict(configuration, 'User')).toBe(true); + expect(setupPatIntentOwnerConflict(configuration, 'Organization')).toBe(false); + + configuration.projects.ids = ' '; + expect(setupPatIntentNeedsOwnerKind(configuration)).toBe(false); + }); + it('omits unresolved remote conditions when management is disabled or owner is personal', () => { const configuration = createDefaultSetupConfiguration(); configuration.manageRepositorySecrets = false; From c26800f430bc7708d36fe21c72da5ca91a1653ef Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Mon, 28 Sep 2026 14:02:54 +0200 Subject: [PATCH 11/50] codex-setup-temporary-github-auth: isolate readline from raw setup prompts --- build/cli/index.js | 18 +++-- ...terminal_driver_stream_integration.test.ts | 68 +++++++++++++++++++ src/cli/setup_terminal_driver.ts | 19 +++--- 3 files changed, 90 insertions(+), 15 deletions(-) create mode 100644 src/cli/__tests__/setup_terminal_driver_stream_integration.test.ts diff --git a/build/cli/index.js b/build/cli/index.js index 151b042e7..a4e9538e9 100755 --- a/build/cli/index.js +++ b/build/cli/index.js @@ -66916,7 +66916,6 @@ class NodeTerminalDriver { if (!interactiveTerminalAvailable()) { throw new Error('An interactive terminal is required.'); } - this.readline = (0, promises_1.createInterface)({ input: node_process_1.stdin, output: node_process_1.stdout }); } isInteractive() { return !this.closed; @@ -66924,6 +66923,8 @@ class NodeTerminalDriver { async readText(prompt) { if (this.closed) return { kind: 'end-of-input' }; + const readline = (0, promises_1.createInterface)({ input: node_process_1.stdin, output: node_process_1.stdout }); + this.readline = readline; const abort = new AbortController(); let interrupted = false; let ended = false; @@ -66935,10 +66936,10 @@ class NodeTerminalDriver { ended = true; abort.abort(); }; - this.readline.once('SIGINT', onInterrupt); - this.readline.once('close', onClose); + readline.once('SIGINT', onInterrupt); + readline.once('close', onClose); try { - return { kind: 'value', value: await this.readline.question(prompt, { signal: abort.signal }) }; + return { kind: 'value', value: await readline.question(prompt, { signal: abort.signal }) }; } catch (error) { if (interrupted) @@ -66948,8 +66949,11 @@ class NodeTerminalDriver { throw error; } finally { - this.readline.off('SIGINT', onInterrupt); - this.readline.off('close', onClose); + readline.off('SIGINT', onInterrupt); + readline.off('close', onClose); + readline.close(); + if (this.readline === readline) + this.readline = undefined; } } async readSecret(prompt) { @@ -67081,7 +67085,7 @@ class NodeTerminalDriver { if (this.closed) return; this.closed = true; - this.readline.close(); + this.readline?.close(); } } exports.NodeTerminalDriver = NodeTerminalDriver; diff --git a/src/cli/__tests__/setup_terminal_driver_stream_integration.test.ts b/src/cli/__tests__/setup_terminal_driver_stream_integration.test.ts new file mode 100644 index 000000000..7b833f8d4 --- /dev/null +++ b/src/cli/__tests__/setup_terminal_driver_stream_integration.test.ts @@ -0,0 +1,68 @@ +import { spawn } from 'node:child_process'; +import { resolve } from 'node:path'; +import { pathToFileURL } from 'node:url'; + +describe('NodeTerminalDriver stream transitions', () => { + it('covers every input-type transition without echoing secrets', async () => { + const driverUrl = pathToFileURL(resolve(__dirname, '../setup_terminal_driver.ts')).href; + const source = ` + delete process.env.JEST_WORKER_ID; + Object.defineProperty(process.stdin, 'isTTY', { value: true }); + Object.defineProperty(process.stdout, 'isTTY', { value: true }); + process.stdin.setRawMode = () => {}; + const { NodeTerminalDriver } = await import(${JSON.stringify(driverUrl)}); + const driver = new NodeTerminalDriver(); + // T T M M S S T S M T covers every adjacent text/multi-select/secret pair. + const results = [ + await driver.readText('text1> '), + await driver.readText('text2> '), + await driver.readMultiSelect('multi1>', ['All', 'feature — Feature'], ['feature']), + await driver.readMultiSelect('multi2>', ['All', 'feature — Feature'], ['feature']), + await driver.readSecret('secret1>'), + await driver.readSecret('secret2>'), + await driver.readText('text3> '), + await driver.readSecret('secret3>'), + await driver.readMultiSelect('multi3>', ['All', 'feature — Feature'], ['feature']), + await driver.readText('text4> '), + ]; + driver.close(); + console.log('RESULT:', JSON.stringify(results.map((result, index) => + [4, 5, 7].includes(index) && result.kind === 'value' + ? { kind: result.kind, length: result.value.length } : result))); + `; + const child = spawn(process.execPath, ['--experimental-strip-types', '--input-type=module', '-e', source], { + stdio: ['pipe', 'pipe', 'pipe'], + }); + const prompts = ['text1> ', 'text2> ', 'multi1>', 'multi2>', 'secret1>:', 'secret2>:', + 'text3> ', 'secret3>:', 'multi3>', 'text4> ']; + const answers = ['one\n', 'two\n', '\n', '\n', 'dummy-one\n', 'dummy-two\n', + 'three\n', 'dummy-three\n', '\n', 'four\n']; + let output = ''; + let errorOutput = ''; + let answered = 0; + child.stdout.on('data', (chunk: Buffer) => { + output += chunk.toString(); + while (answered < prompts.length && output.includes(prompts[answered])) { + child.stdin.write(answers[answered]); + answered += 1; + } + }); + child.stderr.on('data', (chunk: Buffer) => { errorOutput += chunk.toString(); }); + const exitCode = await new Promise((resolveExit, reject) => { + const timeout = setTimeout(() => child.kill(), 10_000); + child.once('error', reject); + child.once('close', code => { + clearTimeout(timeout); + resolveExit(code); + }); + }); + + expect(exitCode).toBe(0); + expect(answered).toBe(prompts.length); + expect(output).toContain('RESULT:'); + expect(output).toContain('"value":"four"'); + expect(output).toContain('"length":11'); + expect(output).not.toContain('dummy-'); + expect(errorOutput).not.toContain('Error:'); + }); +}); diff --git a/src/cli/setup_terminal_driver.ts b/src/cli/setup_terminal_driver.ts index 6a1168f33..7ac2bcacf 100644 --- a/src/cli/setup_terminal_driver.ts +++ b/src/cli/setup_terminal_driver.ts @@ -11,14 +11,13 @@ export function createInteractiveTerminalDriver(): TerminalDriver | undefined { } export class NodeTerminalDriver implements TerminalDriver { - private readonly readline: Interface; + private readline?: Interface; private closed = false; constructor() { if (!interactiveTerminalAvailable()) { throw new Error('An interactive terminal is required.'); } - this.readline = createInterface({ input: stdin, output: stdout }); } isInteractive(): boolean { @@ -27,6 +26,8 @@ export class NodeTerminalDriver implements TerminalDriver { async readText(prompt: string): Promise { if (this.closed) return { kind: 'end-of-input' }; + const readline = createInterface({ input: stdin, output: stdout }); + this.readline = readline; const abort = new AbortController(); let interrupted = false; let ended = false; @@ -38,17 +39,19 @@ export class NodeTerminalDriver implements TerminalDriver { ended = true; abort.abort(); }; - this.readline.once('SIGINT', onInterrupt); - this.readline.once('close', onClose); + readline.once('SIGINT', onInterrupt); + readline.once('close', onClose); try { - return { kind: 'value', value: await this.readline.question(prompt, { signal: abort.signal }) }; + return { kind: 'value', value: await readline.question(prompt, { signal: abort.signal }) }; } catch (error) { if (interrupted) return { kind: 'cancel' }; if (ended || this.closed || isAbortError(error)) return { kind: 'end-of-input' }; throw error; } finally { - this.readline.off('SIGINT', onInterrupt); - this.readline.off('close', onClose); + readline.off('SIGINT', onInterrupt); + readline.off('close', onClose); + readline.close(); + if (this.readline === readline) this.readline = undefined; } } @@ -154,7 +157,7 @@ export class NodeTerminalDriver implements TerminalDriver { close(): void { if (this.closed) return; this.closed = true; - this.readline.close(); + this.readline?.close(); } } From 13a3d9f1d3fd6acb57008d3a69e3bea06a85bb55 Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Mon, 28 Sep 2026 14:55:11 +0200 Subject: [PATCH 12/50] codex-setup-temporary-github-auth: add staged terminal journey and on-demand PAT details --- build/cli/index.js | 236 +++++++++++++++++- docs/authentication.mdx | 7 +- docs/development/architecture.mdx | 7 + docs/how-to-use.mdx | 14 +- specs/CATALOG.md | 20 +- specs/catalog.json | 13 +- specs/guided-bot-pat-onboarding.md | 15 ++ ...up-configuration-credentials-and-doctor.md | 50 +++- .../temporary-setup-operator-authorization.md | 29 +++ src/__tests__/cli.test.ts | 37 +++ .../policies/setup_journey_policy.ts | 48 ++++ .../setup_permission_summary_policy.ts | 17 ++ .../__tests__/setup_journey_use_case.test.ts | 63 +++++ .../usecases/setup/setup_journey_use_case.ts | 46 ++++ .../__tests__/setup_doctor_boundaries.test.ts | 10 + .../__tests__/setup_journey_presenter.test.ts | 40 +++ src/cli/__tests__/setup_presenters.test.ts | 29 +++ .../setup_token_permission_presenter.test.ts | 9 + src/cli/commands/setup.ts | 47 +++- src/cli/setup_credential_prompt_adapter.ts | 21 +- src/cli/setup_journey_presenter.ts | 27 ++ src/cli/setup_token_permission_presenter.ts | 21 ++ 22 files changed, 774 insertions(+), 32 deletions(-) create mode 100644 src/application/policies/setup_journey_policy.ts create mode 100644 src/application/policies/setup_permission_summary_policy.ts create mode 100644 src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts create mode 100644 src/application/usecases/setup/setup_journey_use_case.ts create mode 100644 src/cli/__tests__/setup_journey_presenter.test.ts create mode 100644 src/cli/setup_journey_presenter.ts diff --git a/build/cli/index.js b/build/cli/index.js index a4e9538e9..e93695b03 100755 --- a/build/cli/index.js +++ b/build/cli/index.js @@ -47703,6 +47703,45 @@ function effectiveIssueFormLabels(configuration) { } +/***/ }), + +/***/ 53289: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.SETUP_JOURNEY_STAGES = void 0; +exports.buildSetupJourneyView = buildSetupJourneyView; +exports.SETUP_JOURNEY_STAGES = [ + 'repository', 'choices', 'setup-pat', 'plan', 'credentials', 'apply', +]; +const labels = { + repository: 'Repository', + choices: 'Setup choices', + 'setup-pat': 'Setup PAT', + plan: 'Plan', + credentials: 'Bot PAT & credentials', + apply: 'Apply', +}; +function buildSetupJourneyView(repository, stage, mutationStarted, outcome) { + const position = exports.SETUP_JOURNEY_STAGES.indexOf(stage); + return { + repository: [...repository].map(character => { + const codePoint = character.codePointAt(0); + return codePoint < 32 || (codePoint >= 127 && codePoint <= 159) ? '?' : character; + }).join('').slice(0, 120), + position: position + 1, + total: exports.SETUP_JOURNEY_STAGES.length, + current: labels[stage], + complete: exports.SETUP_JOURNEY_STAGES.slice(0, position).map(item => labels[item]), + pending: exports.SETUP_JOURNEY_STAGES.slice(position + 1).map(item => labels[item]), + ...(outcome ? { outcome } : {}), + mutationStarted, + }; +} + + /***/ }), /***/ 54718: @@ -47837,6 +47876,25 @@ function setupPatIntentOwnerConflict(configuration, ownerKind) { } +/***/ }), + +/***/ 10267: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.summarizeSetupPermissions = summarizeSetupPermissions; +/** A lossless required-grant view of the same requirements used for URL creation. */ +function summarizeSetupPermissions(requirements) { + return { + required: requirements.filter(item => item.applicability === 'required') + .map(item => `${item.permission} ${item.level} (${item.scope})`), + conditionalCount: requirements.filter(item => item.applicability === 'conditional').length, + }; +} + + /***/ }), /***/ 6009: @@ -55570,6 +55628,60 @@ function isAcceptedCredentialCheck(requirement, check) { } +/***/ }), + +/***/ 8419: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.SetupJourneyUseCase = void 0; +const setup_journey_policy_1 = __nccwpck_require__(53289); +/** Tracks semantic milestones, independently of the CLI's rendering. */ +class SetupJourneyUseCase { + constructor(repository, presenter) { + this.repository = repository; + this.presenter = presenter; + this.stage = 'repository'; + this.mutationStarted = false; + } + advance(stage) { + if (this.outcome) + throw new Error('Cannot advance a finished setup journey.'); + const next = setup_journey_policy_1.SETUP_JOURNEY_STAGES.indexOf(stage); + if (next < setup_journey_policy_1.SETUP_JOURNEY_STAGES.indexOf(this.stage)) + throw new Error('Setup journey cannot move backwards.'); + if (next === setup_journey_policy_1.SETUP_JOURNEY_STAGES.indexOf(this.stage)) + return; + this.stage = stage; + this.present(); + } + markMutationStarted() { + if (this.stage !== 'apply' || this.outcome) + throw new Error('Setup mutation must start in the apply stage.'); + this.mutationStarted = true; + this.present(); + } + finish(outcome) { + if (this.outcome) + return; + if (outcome === 'complete' && (this.stage !== 'apply' || !this.mutationStarted)) { + throw new Error('Setup cannot be complete before applying the plan.'); + } + if (outcome === 'partial' && !this.mutationStarted) { + throw new Error('Setup cannot be partial before mutation starts.'); + } + this.outcome = outcome; + this.present(); + } + present() { + this.presenter.present((0, setup_journey_policy_1.buildSetupJourneyView)(this.repository, this.stage, this.mutationStarted, this.outcome)); + } +} +exports.SetupJourneyUseCase = SetupJourneyUseCase; + + /***/ }), /***/ 41644: @@ -65346,6 +65458,8 @@ const setup_token_permissions_composition_root_1 = __nccwpck_require__(64132); const setup_pat_creation_url_policy_1 = __nccwpck_require__(54718); const setup_github_identity_query_adapter_1 = __nccwpck_require__(56098); const verify_guided_workflow_pat_identity_use_case_1 = __nccwpck_require__(35697); +const setup_journey_use_case_1 = __nccwpck_require__(8419); +const setup_journey_presenter_1 = __nccwpck_require__(20462); function registerSetupCommand(program) { program .command('setup') @@ -65382,11 +65496,12 @@ function registerSetupCommand(program) { ...(options.workflowPat ? { PAT: options.workflowPat } : {}), ...options.secret, }, Boolean(options.confirmUnverifiableWritePermissions)); - const permissionPresenter = new setup_token_permission_presenter_1.ConsoleSetupTokenPermissionPresenter(); + const permissionPresenter = new setup_token_permission_presenter_1.ConsoleSetupTokenPermissionPresenter(options.nonInteractive ? 'full' : 'summary'); const tokenPermissions = (0, setup_token_permissions_composition_root_1.createSetupTokenPermissionsUseCase)(); const workflowPrompt = new setup_workflow_update_prompt_adapter_1.SetupWorkflowUpdatePromptAdapter(terminal); const cwd = process.cwd(); let setupMutationStarted = false; + let journey; try { if (!options.nonInteractive && !terminal) { (0, logger_1.logError)('Interactive setup requires a terminal. Use --non-interactive with explicit configuration.'); @@ -65408,10 +65523,17 @@ function registerSetupCommand(program) { return; } (0, logger_1.logInfo)(`📦 Repository: ${gitInfo.owner}/${gitInfo.repo}`); + if (!options.nonInteractive) { + journey = new setup_journey_use_case_1.SetupJourneyUseCase(`${gitInfo.owner}/${gitInfo.repo}`, new setup_journey_presenter_1.ConsoleSetupJourneyPresenter()); + journey.advance('choices'); + } const overrides = loadSetupOverrides(options); let setupPatPermissions = (0, setup_token_permission_policy_1.buildSetupPatPermissionRequirements)(); - permissionPresenter.showRequirements('setup', setupPatPermissions); let token = (0, setup_files_1.getSetupToken)(cwd, options.token); + if (token || options.nonInteractive) + permissionPresenter.showDetailedRequirements('setup', setupPatPermissions); + else + permissionPresenter.showRequirements('setup', setupPatPermissions); let setupPatAccount; let permissionIntent; let assertedOwnerKind; @@ -65435,6 +65557,7 @@ function registerSetupCommand(program) { if (ownerKind === 'unknown') { (0, logger_1.logInfo)('Owner type was not confirmed. Use the manual PAT table, or check whether the GitHub owner is an organization before retrying guided setup.'); credentialPrompt.useManualSetupPat(); + permissionPresenter.showDetailedRequirements('setup', setupPatPermissions); break; } if ((0, setup_pat_intent_policy_1.setupPatIntentOwnerConflict)(draft, ownerKind)) { @@ -65445,6 +65568,7 @@ function registerSetupCommand(program) { (0, logger_1.logInfo)(`The selected local configuration needs correction before a guided link can be generated:\n${intentErrors.map(item => ` - ${item}`).join('\n')}`); } const preview = (0, setup_token_permission_policy_1.buildSetupPatIntentPermissionRequirements)(draft, ownerKind); + journey?.advance('setup-pat'); (0, logger_1.logInfo)('Permission intent:'); (0, logger_1.logInfo)(` Initial tag: ${draft.createInitialTag ? 'yes' : 'no'}; issue workflows: ${draft.features.issues ? draft.issueWorkflows.enabled.join(', ') || 'none' : 'disabled'}; PR approval: ${draft.pullRequestApproval.mode}`); (0, logger_1.logInfo)(` Secrets: ${draft.manageRepositorySecrets ? draft.storage.secrets.defaultScope : 'off'}; Variables: ${draft.manageRepositoryVariables ? draft.storage.variables.defaultScope : 'off'}; Projects: ${draft.projects.ids.trim() || 'none'}`); @@ -65452,9 +65576,15 @@ function registerSetupCommand(program) { const uncertain = (0, setup_token_permission_policy_1.buildSetupPatIntentUncertainty)(draft, ownerKind); if (uncertain.length) (0, logger_1.logInfo)(`May need after GitHub inspection:\n${uncertain.map(item => ` - ${item}`).join('\n')}`); - const decision = await credentialPrompt.reviewSetupPatIntent(); + let decision; + do { + decision = await credentialPrompt.reviewSetupPatIntent(); + if (decision === 'details') + permissionPresenter.showDetailedRequirements('setup', preview); + } while (decision === 'details'); if (decision === 'manual') { credentialPrompt.useManualSetupPat(); + permissionPresenter.showDetailedRequirements('setup', setupPatPermissions); break; } if (decision === 'revise') @@ -65477,11 +65607,20 @@ function registerSetupCommand(program) { throw error; (0, logger_1.logInfo)('A guided setup PAT link is unavailable for this owner or permission set. Enter a manually created PAT using the table above.'); credentialPrompt.useManualSetupPat(); + permissionPresenter.showDetailedRequirements('setup', setupPatPermissions); } break; } } + else { + journey?.advance('setup-pat'); + permissionPresenter.showDetailedRequirements('setup', setupPatPermissions); + } } + if (options.dryRun && !token) + journey?.advance('plan'); + if (!token && !options.dryRun) + journey?.advance('setup-pat'); if (!token && !options.nonInteractive && !options.dryRun) token = await credentialPrompt.requestSetupPat(); if (!token && !options.dryRun) { @@ -65493,6 +65632,7 @@ function registerSetupCommand(program) { return; } if (token) { + journey?.advance('setup-pat'); const permissionReport = await tokenPermissions.inspect({ role: 'setup', owner: gitInfo.owner, @@ -65516,6 +65656,7 @@ function registerSetupCommand(program) { throw new application_error_1.ApplicationError('authorization.credential-invalid', 'The setup PAT belongs to an unintended account. Revoke it in GitHub and retry with the correct account.'); } setupPatAccount = permissionReport.account; + journey?.advance('plan'); } (0, logger_1.logInfo)(options.dryRun ? '🧭 Building a dry-run setup plan...' : '🧭 Building your setup plan...'); const auditConfiguredSetupPat = async (configuration, remoteConfiguration) => { @@ -65581,6 +65722,7 @@ function registerSetupCommand(program) { ...(token ? { remoteTarget: { owner: gitInfo.owner, repository: gitInfo.repo, token } } : {}), }); if (result.status === 'cancelled') { + journey?.finish('cancelled'); if (result.reason !== 'questionnaire-cancelled') { (0, logger_1.logInfo)('⏭️ Setup cancelled. No changes were applied.'); } @@ -65589,6 +65731,7 @@ function registerSetupCommand(program) { return; } if (result.status === 'blocked') { + journey?.finish('blocked'); (0, logger_1.logError)(new application_error_1.ApplicationError(result.reason === 'setup-permissions-unavailable' ? 'authorization.credential-invalid' : 'provider.unavailable', `${result.reason === 'setup-permissions-unavailable' ? 'Setup is blocked by missing or unconfirmed PAT permissions:' : 'Setup is blocked by unavailable remote storage:'}\n${result.errors.map(error => `- ${error}`).join('\n')}`)); @@ -65603,9 +65746,11 @@ function registerSetupCommand(program) { ? workflowComparisons.filter(comparison => comparison.status === 'changed').map(comparison => comparison.file) : []; if (options.dryRun) { + journey?.finish('dry-run'); (0, logger_1.logInfo)('✅ Dry run complete. No files or GitHub resources were changed.'); return; } + journey?.advance('credentials'); const workflowTokenPermissions = (0, setup_token_permission_policy_1.buildWorkflowPatPermissionRequirements)(configuration, remoteConfiguration); const githubIdentities = new setup_github_identity_query_adapter_1.SetupGithubIdentityQueryAdapter(); if (!options.nonInteractive && !options.workflowPat && !options.secret?.PAT) { @@ -65614,12 +65759,13 @@ function registerSetupCommand(program) { role: 'workflow', owner: gitInfo.owner, repository: gitInfo.repo, expiresIn: 90, requirements: workflowTokenPermissions, }); - credentialPrompt.configureWorkflowPatGuide(workflowPatGuide, login => githubIdentities.resolve(login, token)); + credentialPrompt.configureWorkflowPatGuide(workflowPatGuide, login => githubIdentities.resolve(login, token), workflowTokenPermissions); } catch (error) { if (!(error instanceof setup_pat_creation_url_policy_1.UnsupportedSetupPatLinkError)) throw error; (0, logger_1.logInfo)('A guided fine-grained bot PAT link is unavailable for one or more required permissions. Use the permission table and manual path; review whether a classic PAT is required for this plan.'); + permissionPresenter.showDetailedRequirements('workflow', workflowTokenPermissions); } } const credentials = await (0, setup_credentials_composition_root_1.createSetupCredentialsUseCase)(credentialPrompt, permissionPresenter).collect({ @@ -65643,17 +65789,26 @@ function registerSetupCommand(program) { } } (0, logger_1.logInfo)('⚙️ Applying the approved setup plan...'); + journey?.advance('apply'); const params = (0, setup_policy_1.buildSetupParams)(options, gitInfo, token ?? '', configuration, credentials.collection, approvedWorkflowFiles, remoteConfiguration); - if (!params) + if (!params) { + journey?.finish('blocked'); return; + } setupMutationStarted = true; + journey?.markMutationStarted(); const actionResults = await (0, local_action_1.runLocalAction)(params); if (actionResults.some(actionResult => !actionResult.success || actionResult.errors.length > 0)) { + journey?.finish('partial'); (0, logger_1.logInfo)('Setup reported failures or partial completion. If a bot PAT was supplied, its Secret may already have been written; inspect the result and GitHub Secret name/scope before retrying or revoking it.'); process.exitCode = 1; } + else { + journey?.finish('complete'); + } } catch (error) { + journey?.finish(setupMutationStarted ? 'partial' : error instanceof setup_credential_prompt_adapter_1.SetupTerminalCancelledError ? 'cancelled' : 'blocked'); if (credentialPrompt.guidedWorkflowBotIdentity) { (0, logger_1.logInfo)(setupMutationStarted ? 'Setup may be partially applied. Inspect the GitHub Secret before deleting or replacing the bot PAT.' @@ -66324,6 +66479,7 @@ exports.DryRunSetupPlanConfirmation = DryRunSetupPlanConfirmation; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SetupCredentialPromptAdapter = exports.SetupTerminalCancelledError = void 0; const setup_prompt_rendering_1 = __nccwpck_require__(83434); +const setup_token_permission_presenter_1 = __nccwpck_require__(63206); class SetupTerminalCancelledError extends Error { constructor() { super('Setup input was cancelled.'); @@ -66358,11 +66514,12 @@ class SetupCredentialPromptAdapter { async reviewSetupPatIntent() { if (!this.terminal) return 'manual'; - return await this.readChoice('Review these intended grants before opening GitHub. Continue, revise choices, or enter a PAT manually?', ['continue', 'revise', 'manual']); + return await this.readChoice('Review these intended grants before opening GitHub. Continue, revise choices, view full permission table, or enter a PAT manually?', ['continue', 'revise', 'manual', 'details']); } - configureWorkflowPatGuide(url, resolveIdentity) { + configureWorkflowPatGuide(url, resolveIdentity, requirements) { this.workflowPatGuide = url; this.resolveBotIdentity = resolveIdentity; + this.workflowPatRequirements = requirements; } get guidedWorkflowBotIdentity() { return this.guidedBotIdentity; } async confirmGuidedSetupAccount(account) { @@ -66446,7 +66603,14 @@ class SetupCredentialPromptAdapter { } async requestWorkflowPat(requirement, current) { if (this.terminal && !this.credentialValues[requirement.name]?.trim() && this.workflowPatGuide) { - const guided = (await this.readChoice('How would you like to provide the bot workflow PAT?', ['guided link', 'manual PAT'], 'guided link')) === 'guided link'; + let choice; + do { + choice = await this.readChoice('How would you like to provide the bot workflow PAT?', ['guided link', 'manual PAT', 'view full permission table'], 'guided link'); + if (choice === 'view full permission table' && this.workflowPatRequirements) { + console.log((0, setup_token_permission_presenter_1.renderSetupTokenPermissionRequirements)('workflow', this.workflowPatRequirements)); + } + } while (choice === 'view full permission table'); + const guided = choice === 'guided link'; if (guided) { const login = await this.readBotLogin(); const identity = await this.resolveBotIdentity(login); @@ -66456,6 +66620,9 @@ class SetupCredentialPromptAdapter { console.log(this.workflowPatGuide); console.log('Copy the one-time bot token and paste it below. It will be validated before any Secret is written.'); } + else if (this.workflowPatRequirements) { + console.log((0, setup_token_permission_presenter_1.renderSetupTokenPermissionRequirements)('workflow', this.workflowPatRequirements)); + } } return this.requestSecretForRequirement(requirement, current, 'workflow PAT owned by the bot account'); } @@ -66610,6 +66777,42 @@ function doctorCheckLabel(id, catalog = (0, setup_doctor_message_catalog_1.resol } +/***/ }), + +/***/ 20462: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.ConsoleSetupJourneyPresenter = void 0; +exports.renderSetupJourney = renderSetupJourney; +const setup_prompt_rendering_1 = __nccwpck_require__(83434); +class ConsoleSetupJourneyPresenter { + present(view) { + console.log(renderSetupJourney(view)); + } +} +exports.ConsoleSetupJourneyPresenter = ConsoleSetupJourneyPresenter; +function renderSetupJourney(view, maximumWidth) { + const state = view.outcome === 'complete' ? 'Complete: setup applied successfully.' + : view.outcome === 'dry-run' ? 'Complete: dry run only; no changes were applied.' + : view.outcome === 'partial' ? 'Partial: application started; inspect the result before retrying.' + : view.outcome === 'blocked' ? 'Blocked: setup cannot continue.' + : view.outcome === 'cancelled' ? 'Cancelled: setup stopped.' + : view.mutationStarted ? 'Applying the approved plan; changes may already exist.' + : 'No changes have been applied.'; + return (0, setup_prompt_rendering_1.renderBox)([ + `Repository: ${view.repository}`, + `Stage ${view.position}/${view.total} · ${view.current}`, + `Complete: ${view.complete.join(' → ') || 'none'}`, + `Now: ${view.current}`, + `Next: ${view.pending.join(' → ') || 'none'}`, + state, + ].join('\n'), 'Copilot setup', 36, maximumWidth); +} + + /***/ }), /***/ 33441: @@ -67103,12 +67306,22 @@ function isAbortError(error) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.ConsoleSetupTokenPermissionPresenter = void 0; +exports.renderSetupTokenPermissionSummary = renderSetupTokenPermissionSummary; exports.renderSetupTokenPermissionRequirements = renderSetupTokenPermissionRequirements; exports.renderSetupTokenPermissionReport = renderSetupTokenPermissionReport; const node_process_1 = __nccwpck_require__(97742); const setup_prompt_rendering_1 = __nccwpck_require__(83434); +const setup_permission_summary_policy_1 = __nccwpck_require__(10267); class ConsoleSetupTokenPermissionPresenter { + constructor(mode = 'full') { + this.mode = mode; + } showRequirements(role, requirements) { + console.log(this.mode === 'summary' + ? renderSetupTokenPermissionSummary(role, requirements) + : renderSetupTokenPermissionRequirements(role, requirements)); + } + showDetailedRequirements(role, requirements) { console.log(renderSetupTokenPermissionRequirements(role, requirements)); } showReport(report) { @@ -67116,6 +67329,13 @@ class ConsoleSetupTokenPermissionPresenter { } } exports.ConsoleSetupTokenPermissionPresenter = ConsoleSetupTokenPermissionPresenter; +function renderSetupTokenPermissionSummary(role, requirements, maximumWidth = node_process_1.stdout.columns ?? 120) { + const summary = (0, setup_permission_summary_policy_1.summarizeSetupPermissions)(requirements); + return (0, setup_prompt_rendering_1.renderBox)([ + `Required now: ${summary.required.join(' · ') || 'none'}`, + `Conditional permissions: ${summary.conditionalCount}. View the full table for reasons and triggers.`, + ].join('\n'), `${roleTitle(role)} PAT permission summary`, 36, maximumWidth); +} function renderSetupTokenPermissionRequirements(role, requirements, maximumWidth = node_process_1.stdout.columns ?? 120) { const rows = maximumWidth >= 88 ? renderWideRequirements(requirements) diff --git a/docs/authentication.mdx b/docs/authentication.mdx index 4f78b3921..235b9f9b4 100644 --- a/docs/authentication.mdx +++ b/docs/authentication.mdx @@ -20,7 +20,12 @@ determine PAT permissions: issue workflows, initial tag, Secret and Variable management and storage scope, PR approval mode, and Projects. Choices already fixed by flags or `--config` are not asked. You review the resulting grants before the link appears; these answers carry into the full wizard without being -asked twice. The link is still **provisional** for facts that require GitHub +asked twice. The terminal first shows a short summary of required grants; +choose **view full permission table** at review to inspect every grant, reason, +and condition, then return to the same review without repeating setup choices. +Choosing manual PAT entry shows the full table directly. The later bot PAT +prompt has its own full-table option based on the finalized workflow grants, +not on the temporary setup PAT. The link is still **provisional** for facts that require GitHub inspection, such as existing Secrets, inherited organization resources, and a missing credential-health workflow. If the final plan needs additional grants, setup stops before applying it and prints a corrected link. diff --git a/docs/development/architecture.mdx b/docs/development/architecture.mdx index 1111b8e4a..674f196c9 100644 --- a/docs/development/architecture.mdx +++ b/docs/development/architecture.mdx @@ -8,6 +8,13 @@ Guarded PR approval uses a separate trusted observer entrypoint, a pure domain d The repository separates semantic application ports from provider-specific adapters. +Interactive setup milestones are tracked by the application-layer +`SetupJourneyUseCase`; its pure stage view model is rendered by the terminal +adapter. The terminal does not decide whether a PAT is valid or which grant is +required. Compact summaries project the same requirement objects used for the +guided URL and the full permission table. The setup command advances stages +only at existing audited boundaries; non-interactive setup keeps its own output. + GitHub conversation output crosses a closed publication boundary. Capabilities produce typed `none`, `reply`, `status`, `transition`, or `inline-finding` intents; the publication layer maps only reviewed semantic payloads and can diff --git a/docs/how-to-use.mdx b/docs/how-to-use.mdx index cb1ba6fac..332af6df1 100644 --- a/docs/how-to-use.mdx +++ b/docs/how-to-use.mdx @@ -44,7 +44,19 @@ configuring the repository. Before showing it, guided setup asks only the local choices that affect its grants, shows an exact permission preview and notes what remains unknown until GitHub is inspected. The later questionnaire reuses those answers. The second link, after the setup plan, prepares the -**workflow PAT** for the bot account. Open each link in the appropriate GitHub +**workflow PAT** for the bot account. + +The interactive terminal also shows your current phase: Repository → Setup +choices → Setup PAT → Plan → Bot PAT & credentials → Apply. These are milestones, +not a percentage or a fixed number of questions. Before Apply, it says that no +changes have been made; after Apply starts, a failed run is marked partial so +you can inspect what was installed. The initial and guided PAT views show a +compact list of required grants. Choose **view full permission table** during +review to see reasons and conditional grants without restarting the questions; +manual PAT entry shows that table directly. Supplied-token and unattended paths +retain the full table. This display never includes token values. + +Open each link in the appropriate GitHub account, complete GitHub's sign-in/2FA, change **All repositories** to **Only select repositories**, and **select only the intended repository** on the form, review the grants, and paste the generated value into the hidden diff --git a/specs/CATALOG.md b/specs/CATALOG.md index 9620242fa..0f2d764d2 100644 --- a/specs/CATALOG.md +++ b/specs/CATALOG.md @@ -16,9 +16,9 @@ debt or convert unknown historic intent into a design decision. | `bugbot-review-state-reconciliation` | Implemented | Reconcile review snapshots, findings, threads, comments, and check conclusions | [Bugbot review-state reconciliation](./bugbot-review-state-reconciliation.md) | 57 paths · 2026-09-24 | | `execution-lifecycle` | Implemented | Shared GitHub Action lifecycle from event admission through durable user-facing results | [Execution admission, queueing, routing, and result publication](./execution-admission-queue-and-publication.md) + 3 companion | 84 paths · 2026-09-16 | | `architecture-quality-hardening` | Implemented | Close verified concurrency, error-contract, context-coupling, fan-out, setup/doctor, and provider-policy risks in dependency order | [Architecture quality and scalability hardening](./architecture-quality-and-scalability-hardening.md) + 1 companion | 72 paths · 2026-09-16 | -| `setup-and-doctor` | Implemented | Plan, validate, provision, and audit a repository installation without exposing credentials | [Setup, configuration, credentials, and doctor](./setup-configuration-credentials-and-doctor.md) + 2 companion | 83 paths · 2026-09-24 | -| `guided-bot-pat-onboarding` | Proposed | Guide creation of the persistent workflow PAT using GitHub's official form, verify bot identity and grants, and install the approved Actions Secret | [Guided bot PAT onboarding](./guided-bot-pat-onboarding.md) | 22 paths · 2026-09-25 | -| `temporary-setup-operator-authorization` | Proposed | Guide creation of the one-run operator PAT through GitHub's official form, verify final setup access, and report user-owned deletion accurately | [Assisted setup PAT creation](./temporary-setup-operator-authorization.md) | 26 paths · 2026-09-25 | +| `setup-and-doctor` | Implemented | Plan, validate, provision, and audit a repository installation without exposing credentials | [Setup, configuration, credentials, and doctor](./setup-configuration-credentials-and-doctor.md) + 2 companion | 89 paths · 2026-09-28 | +| `guided-bot-pat-onboarding` | Proposed | Guide creation of the persistent workflow PAT using GitHub's official form, verify bot identity and grants, and install the approved Actions Secret | [Guided bot PAT onboarding](./guided-bot-pat-onboarding.md) | 25 paths · 2026-09-25 | +| `temporary-setup-operator-authorization` | Proposed | Guide creation of the one-run operator PAT through GitHub's official form, verify final setup access, and report user-owned deletion accurately | [Assisted setup PAT creation](./temporary-setup-operator-authorization.md) | 28 paths · 2026-09-25 | | `issue-start-and-sdd-readiness` | Implemented | Start every admitted issue with one explicit signal and publish a validated SDD before eligible Action-managed branch work | [Uniform issue start and pre-branch SDD readiness](./issue-start-and-branch-readiness.md) + 1 companion | 51 paths · 2026-09-17 | | `managed-issue-lifecycle` | As-built baseline | Convert typed issues into traceable work branches, project state, and lifecycle state | [Managed issue and branch lifecycle](./managed-issue-and-branch-lifecycle.md) | 31 paths · 2026-09-17 | | `comment-automation` | Implemented | Admit only explicit commands or exact mentions, then route them while protecting repository mutations | [Comment automation and authorization](./comment-automation-and-authorization.md) | 61 paths · 2026-09-21 | @@ -102,12 +102,12 @@ debt or convert unknown historic intent into a design decision. ### `setup-and-doctor` — Setup, configuration, credentials, and doctor - Owner: Copilot maintainers -- Last verified: 2026-09-24 +- Last verified: 2026-09-28 - Specifications: [`specs/setup-configuration-credentials-and-doctor.md`](./setup-configuration-credentials-and-doctor.md) · [`specs/setup-doctor-architecture-hardening.md`](./setup-doctor-architecture-hardening.md) · [`specs/setup-pat-permission-guidance-and-verification.md`](./setup-pat-permission-guidance-and-verification.md) - Workflows: [`setup/workflows/agent-cli-provisioning.yml`](../setup/workflows/agent-cli-provisioning.yml) · [`setup/workflows/copilot_credential_health.yml`](../setup/workflows/copilot_credential_health.yml) - Entrypoints: [`src/cli/commands/setup.ts`](../src/cli/commands/setup.ts) · [`src/cli/commands/doctor.ts`](../src/cli/commands/doctor.ts) -- Core code: [`src/domain/setup.ts`](../src/domain/setup.ts) · [`src/domain/setup_questionnaire.ts`](../src/domain/setup_questionnaire.ts) · [`src/domain/setup_token_permissions.ts`](../src/domain/setup_token_permissions.ts) · [`src/application/ports/setup_terminal_ports.ts`](../src/application/ports/setup_terminal_ports.ts) · [`src/application/ports/setup_wizard_ports.ts`](../src/application/ports/setup_wizard_ports.ts) · [`src/application/ports/setup_token_permission_ports.ts`](../src/application/ports/setup_token_permission_ports.ts) · [`src/application/policies/setup_token_permission_evidence_policy.ts`](../src/application/policies/setup_token_permission_evidence_policy.ts) · [`src/application/policies/setup_token_permission_policy.ts`](../src/application/policies/setup_token_permission_policy.ts) · [`src/application/policies/setup_questionnaire_policy.ts`](../src/application/policies/setup_questionnaire_policy.ts) · [`src/application/policies/setup_configuration_plan.ts`](../src/application/policies/setup_configuration_plan.ts) · [`src/application/policies/setup_configuration_storage_policy.ts`](../src/application/policies/setup_configuration_storage_policy.ts) · [`src/application/policies/setup_doctor_message_catalog.ts`](../src/application/policies/setup_doctor_message_catalog.ts) · [`src/application/policies/setup_doctor_report_policy.ts`](../src/application/policies/setup_doctor_report_policy.ts) · [`src/application/usecases/setup/setup_wizard_use_case.ts`](../src/application/usecases/setup/setup_wizard_use_case.ts) · [`src/application/usecases/setup/setup_questionnaire_controller.ts`](../src/application/usecases/setup/setup_questionnaire_controller.ts) · [`src/application/usecases/setup/setup_credentials_use_case.ts`](../src/application/usecases/setup/setup_credentials_use_case.ts) · [`src/application/usecases/setup/setup_token_permissions_use_case.ts`](../src/application/usecases/setup/setup_token_permissions_use_case.ts) · [`src/application/usecases/setup/doctor_use_case.ts`](../src/application/usecases/setup/doctor_use_case.ts) · [`src/application/usecases/setup/merge_queue_readiness_use_case.ts`](../src/application/usecases/setup/merge_queue_readiness_use_case.ts) · [`src/application/usecases/actions/initial_setup_workflow.ts`](../src/application/usecases/actions/initial_setup_workflow.ts) · [`src/application/usecases/actions/setup_resource_provisioning.ts`](../src/application/usecases/actions/setup_resource_provisioning.ts) · [`src/application/ports/message_catalog_ports.ts`](../src/application/ports/message_catalog_ports.ts) · [`src/application/usecases/localization/resolve_message_catalog_use_case.ts`](../src/application/usecases/localization/resolve_message_catalog_use_case.ts) · [`src/application/policies/setup_configuration_validation.ts`](../src/application/policies/setup_configuration_validation.ts) · [`src/infrastructure/setup_workspace_adapter.ts`](../src/infrastructure/setup_workspace_adapter.ts) · [`src/data/repository/repository_variables_repository.ts`](../src/data/repository/repository_variables_repository.ts) · [`src/infrastructure/github/ports/github_repository_variables_protocol.ts`](../src/infrastructure/github/ports/github_repository_variables_protocol.ts) · [`src/infrastructure/setup_remote_credential_health_adapter.ts`](../src/infrastructure/setup_remote_credential_health_adapter.ts) · [`src/infrastructure/setup_credential_validation_adapter.ts`](../src/infrastructure/setup_credential_validation_adapter.ts) · [`src/infrastructure/setup_token_permission_query_adapter.ts`](../src/infrastructure/setup_token_permission_query_adapter.ts) · [`src/cli/setup_terminal_driver.ts`](../src/cli/setup_terminal_driver.ts) · [`src/cli/setup_question_renderer.ts`](../src/cli/setup_question_renderer.ts) · [`src/cli/setup_plan_presenter.ts`](../src/cli/setup_plan_presenter.ts) · [`src/cli/setup_doctor_presenter.ts`](../src/cli/setup_doctor_presenter.ts) · [`src/cli/setup_prompt_rendering.ts`](../src/cli/setup_prompt_rendering.ts) · [`src/cli/setup_credential_prompt_adapter.ts`](../src/cli/setup_credential_prompt_adapter.ts) · [`src/cli/setup_token_permission_presenter.ts`](../src/cli/setup_token_permission_presenter.ts) · [`src/infrastructure/composition/setup_credentials_composition_root.ts`](../src/infrastructure/composition/setup_credentials_composition_root.ts) · [`src/infrastructure/composition/setup_token_permissions_composition_root.ts`](../src/infrastructure/composition/setup_token_permissions_composition_root.ts) · [`src/infrastructure/composition/setup_doctor_composition_root.ts`](../src/infrastructure/composition/setup_doctor_composition_root.ts) · [`scripts/coverage-budgets.json`](../scripts/coverage-budgets.json) · [`scripts/documentation_pat_exception_policy.cjs`](../scripts/documentation_pat_exception_policy.cjs) · [`scripts/validate-documentation-contract.cjs`](../scripts/validate-documentation-contract.cjs) -- Tests: [`src/application/policies/__tests__/setup_questionnaire_policy.test.ts`](../src/application/policies/__tests__/setup_questionnaire_policy.test.ts) · [`src/application/policies/__tests__/setup_configuration_policy.test.ts`](../src/application/policies/__tests__/setup_configuration_policy.test.ts) · [`src/application/policies/__tests__/setup_token_permission_policy.test.ts`](../src/application/policies/__tests__/setup_token_permission_policy.test.ts) · [`src/application/policies/__tests__/setup_doctor_message_catalog.test.ts`](../src/application/policies/__tests__/setup_doctor_message_catalog.test.ts) · [`src/application/policies/__tests__/setup_doctor_report_policy.test.ts`](../src/application/policies/__tests__/setup_doctor_report_policy.test.ts) · [`src/application/usecases/setup/__tests__/setup_questionnaire_controller.test.ts`](../src/application/usecases/setup/__tests__/setup_questionnaire_controller.test.ts) · [`src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_token_permissions_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_token_permissions_use_case.test.ts) · [`src/application/usecases/setup/__tests__/doctor_use_case.test.ts`](../src/application/usecases/setup/__tests__/doctor_use_case.test.ts) · [`src/application/usecases/setup/__tests__/merge_queue_readiness_use_case.test.ts`](../src/application/usecases/setup/__tests__/merge_queue_readiness_use_case.test.ts) · [`src/application/usecases/actions/__tests__/initial_setup_use_case.test.ts`](../src/application/usecases/actions/__tests__/initial_setup_use_case.test.ts) · [`src/application/usecases/actions/__tests__/setup_resource_provisioning.test.ts`](../src/application/usecases/actions/__tests__/setup_resource_provisioning.test.ts) · [`src/infrastructure/__tests__/setup_workspace_adapter.test.ts`](../src/infrastructure/__tests__/setup_workspace_adapter.test.ts) · [`src/infrastructure/__tests__/setup_remote_credential_health_adapter.test.ts`](../src/infrastructure/__tests__/setup_remote_credential_health_adapter.test.ts) · [`src/infrastructure/__tests__/setup_token_permission_query_adapter.test.ts`](../src/infrastructure/__tests__/setup_token_permission_query_adapter.test.ts) · [`src/infrastructure/composition/__tests__/setup_token_permissions_composition_root.test.ts`](../src/infrastructure/composition/__tests__/setup_token_permissions_composition_root.test.ts) · [`src/data/repository/__tests__/repository_variables_repository.test.ts`](../src/data/repository/__tests__/repository_variables_repository.test.ts) · [`src/cli/__tests__/setup_presenters.test.ts`](../src/cli/__tests__/setup_presenters.test.ts) · [`src/cli/__tests__/setup_prompt_rendering.test.ts`](../src/cli/__tests__/setup_prompt_rendering.test.ts) · [`src/cli/__tests__/setup_token_permission_presenter.test.ts`](../src/cli/__tests__/setup_token_permission_presenter.test.ts) · [`src/__tests__/cli.test.ts`](../src/__tests__/cli.test.ts) · [`src/cli/__tests__/setup_terminal_driver.test.ts`](../src/cli/__tests__/setup_terminal_driver.test.ts) · [`src/architecture/__tests__/setup_doctor_boundaries.test.ts`](../src/architecture/__tests__/setup_doctor_boundaries.test.ts) · [`src/tooling/__tests__/documentation_pat_exception_policy.test.ts`](../src/tooling/__tests__/documentation_pat_exception_policy.test.ts) +- Core code: [`src/domain/setup.ts`](../src/domain/setup.ts) · [`src/domain/setup_questionnaire.ts`](../src/domain/setup_questionnaire.ts) · [`src/domain/setup_token_permissions.ts`](../src/domain/setup_token_permissions.ts) · [`src/application/ports/setup_terminal_ports.ts`](../src/application/ports/setup_terminal_ports.ts) · [`src/application/ports/setup_wizard_ports.ts`](../src/application/ports/setup_wizard_ports.ts) · [`src/application/ports/setup_token_permission_ports.ts`](../src/application/ports/setup_token_permission_ports.ts) · [`src/application/policies/setup_token_permission_evidence_policy.ts`](../src/application/policies/setup_token_permission_evidence_policy.ts) · [`src/application/policies/setup_token_permission_policy.ts`](../src/application/policies/setup_token_permission_policy.ts) · [`src/application/policies/setup_questionnaire_policy.ts`](../src/application/policies/setup_questionnaire_policy.ts) · [`src/application/policies/setup_configuration_plan.ts`](../src/application/policies/setup_configuration_plan.ts) · [`src/application/policies/setup_configuration_storage_policy.ts`](../src/application/policies/setup_configuration_storage_policy.ts) · [`src/application/policies/setup_doctor_message_catalog.ts`](../src/application/policies/setup_doctor_message_catalog.ts) · [`src/application/policies/setup_doctor_report_policy.ts`](../src/application/policies/setup_doctor_report_policy.ts) · [`src/application/policies/setup_journey_policy.ts`](../src/application/policies/setup_journey_policy.ts) · [`src/application/policies/setup_permission_summary_policy.ts`](../src/application/policies/setup_permission_summary_policy.ts) · [`src/application/usecases/setup/setup_wizard_use_case.ts`](../src/application/usecases/setup/setup_wizard_use_case.ts) · [`src/application/usecases/setup/setup_journey_use_case.ts`](../src/application/usecases/setup/setup_journey_use_case.ts) · [`src/application/usecases/setup/setup_questionnaire_controller.ts`](../src/application/usecases/setup/setup_questionnaire_controller.ts) · [`src/application/usecases/setup/setup_credentials_use_case.ts`](../src/application/usecases/setup/setup_credentials_use_case.ts) · [`src/application/usecases/setup/setup_token_permissions_use_case.ts`](../src/application/usecases/setup/setup_token_permissions_use_case.ts) · [`src/application/usecases/setup/doctor_use_case.ts`](../src/application/usecases/setup/doctor_use_case.ts) · [`src/application/usecases/setup/merge_queue_readiness_use_case.ts`](../src/application/usecases/setup/merge_queue_readiness_use_case.ts) · [`src/application/usecases/actions/initial_setup_workflow.ts`](../src/application/usecases/actions/initial_setup_workflow.ts) · [`src/application/usecases/actions/setup_resource_provisioning.ts`](../src/application/usecases/actions/setup_resource_provisioning.ts) · [`src/application/ports/message_catalog_ports.ts`](../src/application/ports/message_catalog_ports.ts) · [`src/application/usecases/localization/resolve_message_catalog_use_case.ts`](../src/application/usecases/localization/resolve_message_catalog_use_case.ts) · [`src/application/policies/setup_configuration_validation.ts`](../src/application/policies/setup_configuration_validation.ts) · [`src/infrastructure/setup_workspace_adapter.ts`](../src/infrastructure/setup_workspace_adapter.ts) · [`src/data/repository/repository_variables_repository.ts`](../src/data/repository/repository_variables_repository.ts) · [`src/infrastructure/github/ports/github_repository_variables_protocol.ts`](../src/infrastructure/github/ports/github_repository_variables_protocol.ts) · [`src/infrastructure/setup_remote_credential_health_adapter.ts`](../src/infrastructure/setup_remote_credential_health_adapter.ts) · [`src/infrastructure/setup_credential_validation_adapter.ts`](../src/infrastructure/setup_credential_validation_adapter.ts) · [`src/infrastructure/setup_token_permission_query_adapter.ts`](../src/infrastructure/setup_token_permission_query_adapter.ts) · [`src/cli/setup_terminal_driver.ts`](../src/cli/setup_terminal_driver.ts) · [`src/cli/setup_question_renderer.ts`](../src/cli/setup_question_renderer.ts) · [`src/cli/setup_journey_presenter.ts`](../src/cli/setup_journey_presenter.ts) · [`src/cli/setup_plan_presenter.ts`](../src/cli/setup_plan_presenter.ts) · [`src/cli/setup_doctor_presenter.ts`](../src/cli/setup_doctor_presenter.ts) · [`src/cli/setup_prompt_rendering.ts`](../src/cli/setup_prompt_rendering.ts) · [`src/cli/setup_credential_prompt_adapter.ts`](../src/cli/setup_credential_prompt_adapter.ts) · [`src/cli/setup_token_permission_presenter.ts`](../src/cli/setup_token_permission_presenter.ts) · [`src/infrastructure/composition/setup_credentials_composition_root.ts`](../src/infrastructure/composition/setup_credentials_composition_root.ts) · [`src/infrastructure/composition/setup_token_permissions_composition_root.ts`](../src/infrastructure/composition/setup_token_permissions_composition_root.ts) · [`src/infrastructure/composition/setup_doctor_composition_root.ts`](../src/infrastructure/composition/setup_doctor_composition_root.ts) · [`scripts/coverage-budgets.json`](../scripts/coverage-budgets.json) · [`scripts/documentation_pat_exception_policy.cjs`](../scripts/documentation_pat_exception_policy.cjs) · [`scripts/validate-documentation-contract.cjs`](../scripts/validate-documentation-contract.cjs) +- Tests: [`src/application/policies/__tests__/setup_questionnaire_policy.test.ts`](../src/application/policies/__tests__/setup_questionnaire_policy.test.ts) · [`src/application/policies/__tests__/setup_configuration_policy.test.ts`](../src/application/policies/__tests__/setup_configuration_policy.test.ts) · [`src/application/policies/__tests__/setup_token_permission_policy.test.ts`](../src/application/policies/__tests__/setup_token_permission_policy.test.ts) · [`src/application/policies/__tests__/setup_doctor_message_catalog.test.ts`](../src/application/policies/__tests__/setup_doctor_message_catalog.test.ts) · [`src/application/policies/__tests__/setup_doctor_report_policy.test.ts`](../src/application/policies/__tests__/setup_doctor_report_policy.test.ts) · [`src/application/usecases/setup/__tests__/setup_questionnaire_controller.test.ts`](../src/application/usecases/setup/__tests__/setup_questionnaire_controller.test.ts) · [`src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_token_permissions_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_token_permissions_use_case.test.ts) · [`src/application/usecases/setup/__tests__/doctor_use_case.test.ts`](../src/application/usecases/setup/__tests__/doctor_use_case.test.ts) · [`src/application/usecases/setup/__tests__/merge_queue_readiness_use_case.test.ts`](../src/application/usecases/setup/__tests__/merge_queue_readiness_use_case.test.ts) · [`src/application/usecases/actions/__tests__/initial_setup_use_case.test.ts`](../src/application/usecases/actions/__tests__/initial_setup_use_case.test.ts) · [`src/application/usecases/actions/__tests__/setup_resource_provisioning.test.ts`](../src/application/usecases/actions/__tests__/setup_resource_provisioning.test.ts) · [`src/infrastructure/__tests__/setup_workspace_adapter.test.ts`](../src/infrastructure/__tests__/setup_workspace_adapter.test.ts) · [`src/infrastructure/__tests__/setup_remote_credential_health_adapter.test.ts`](../src/infrastructure/__tests__/setup_remote_credential_health_adapter.test.ts) · [`src/infrastructure/__tests__/setup_token_permission_query_adapter.test.ts`](../src/infrastructure/__tests__/setup_token_permission_query_adapter.test.ts) · [`src/infrastructure/composition/__tests__/setup_token_permissions_composition_root.test.ts`](../src/infrastructure/composition/__tests__/setup_token_permissions_composition_root.test.ts) · [`src/data/repository/__tests__/repository_variables_repository.test.ts`](../src/data/repository/__tests__/repository_variables_repository.test.ts) · [`src/cli/__tests__/setup_presenters.test.ts`](../src/cli/__tests__/setup_presenters.test.ts) · [`src/cli/__tests__/setup_journey_presenter.test.ts`](../src/cli/__tests__/setup_journey_presenter.test.ts) · [`src/cli/__tests__/setup_prompt_rendering.test.ts`](../src/cli/__tests__/setup_prompt_rendering.test.ts) · [`src/cli/__tests__/setup_token_permission_presenter.test.ts`](../src/cli/__tests__/setup_token_permission_presenter.test.ts) · [`src/__tests__/cli.test.ts`](../src/__tests__/cli.test.ts) · [`src/cli/__tests__/setup_terminal_driver.test.ts`](../src/cli/__tests__/setup_terminal_driver.test.ts) · [`src/architecture/__tests__/setup_doctor_boundaries.test.ts`](../src/architecture/__tests__/setup_doctor_boundaries.test.ts) · [`src/tooling/__tests__/documentation_pat_exception_policy.test.ts`](../src/tooling/__tests__/documentation_pat_exception_policy.test.ts) - User documentation: [`README.md`](../README.md) · [`docs/how-to-use.mdx`](../docs/how-to-use.mdx) · [`docs/configuration.mdx`](../docs/configuration.mdx) · [`docs/configuration-checklist.mdx`](../docs/configuration-checklist.mdx) · [`docs/authentication.mdx`](../docs/authentication.mdx) · [`docs/development/architecture.mdx`](../docs/development/architecture.mdx) · [`docs/security-operations/operations/provisioning.mdx`](../docs/security-operations/operations/provisioning.mdx) · [`docs/security-operations/operations/troubleshooting.mdx`](../docs/security-operations/operations/troubleshooting.mdx) · [`docs/security-operations/security/credentials.mdx`](../docs/security-operations/security/credentials.mdx) · [`docs/single-actions/workflow-and-cli.mdx`](../docs/single-actions/workflow-and-cli.mdx) · [`docs/security-operations/operations/verification.mdx`](../docs/security-operations/operations/verification.mdx) ### `guided-bot-pat-onboarding` — Guided bot PAT onboarding @@ -117,8 +117,8 @@ debt or convert unknown historic intent into a design decision. - Specifications: [`specs/guided-bot-pat-onboarding.md`](./guided-bot-pat-onboarding.md) - Workflows: Not applicable for this capability. - Entrypoints: [`src/cli/commands/setup.ts`](../src/cli/commands/setup.ts) -- Core code: [`src/application/policies/setup_token_permission_policy.ts`](../src/application/policies/setup_token_permission_policy.ts) · [`src/application/policies/setup_pat_creation_url_policy.ts`](../src/application/policies/setup_pat_creation_url_policy.ts) · [`src/application/ports/setup_pat_identity_ports.ts`](../src/application/ports/setup_pat_identity_ports.ts) · [`src/application/usecases/setup/verify_guided_workflow_pat_identity_use_case.ts`](../src/application/usecases/setup/verify_guided_workflow_pat_identity_use_case.ts) · [`src/infrastructure/setup_github_identity_query_adapter.ts`](../src/infrastructure/setup_github_identity_query_adapter.ts) · [`src/application/usecases/setup/setup_credentials_use_case.ts`](../src/application/usecases/setup/setup_credentials_use_case.ts) · [`src/cli/setup_credential_prompt_adapter.ts`](../src/cli/setup_credential_prompt_adapter.ts) · [`src/data/repository/repository_variables_repository.ts`](../src/data/repository/repository_variables_repository.ts) -- Tests: [`src/application/policies/__tests__/setup_token_permission_policy.test.ts`](../src/application/policies/__tests__/setup_token_permission_policy.test.ts) · [`src/application/policies/__tests__/setup_pat_creation_url_policy.test.ts`](../src/application/policies/__tests__/setup_pat_creation_url_policy.test.ts) · [`src/application/usecases/setup/__tests__/verify_guided_workflow_pat_identity_use_case.test.ts`](../src/application/usecases/setup/__tests__/verify_guided_workflow_pat_identity_use_case.test.ts) · [`src/infrastructure/__tests__/setup_github_identity_query_adapter.test.ts`](../src/infrastructure/__tests__/setup_github_identity_query_adapter.test.ts) · [`src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts) · [`src/cli/__tests__/setup_presenters.test.ts`](../src/cli/__tests__/setup_presenters.test.ts) · [`src/data/repository/__tests__/repository_variables_repository.test.ts`](../src/data/repository/__tests__/repository_variables_repository.test.ts) +- Core code: [`src/application/policies/setup_token_permission_policy.ts`](../src/application/policies/setup_token_permission_policy.ts) · [`src/application/policies/setup_permission_summary_policy.ts`](../src/application/policies/setup_permission_summary_policy.ts) · [`src/application/policies/setup_pat_creation_url_policy.ts`](../src/application/policies/setup_pat_creation_url_policy.ts) · [`src/application/ports/setup_pat_identity_ports.ts`](../src/application/ports/setup_pat_identity_ports.ts) · [`src/application/usecases/setup/verify_guided_workflow_pat_identity_use_case.ts`](../src/application/usecases/setup/verify_guided_workflow_pat_identity_use_case.ts) · [`src/infrastructure/setup_github_identity_query_adapter.ts`](../src/infrastructure/setup_github_identity_query_adapter.ts) · [`src/application/usecases/setup/setup_credentials_use_case.ts`](../src/application/usecases/setup/setup_credentials_use_case.ts) · [`src/cli/setup_credential_prompt_adapter.ts`](../src/cli/setup_credential_prompt_adapter.ts) · [`src/cli/setup_journey_presenter.ts`](../src/cli/setup_journey_presenter.ts) · [`src/data/repository/repository_variables_repository.ts`](../src/data/repository/repository_variables_repository.ts) +- Tests: [`src/application/policies/__tests__/setup_token_permission_policy.test.ts`](../src/application/policies/__tests__/setup_token_permission_policy.test.ts) · [`src/application/policies/__tests__/setup_pat_creation_url_policy.test.ts`](../src/application/policies/__tests__/setup_pat_creation_url_policy.test.ts) · [`src/application/usecases/setup/__tests__/verify_guided_workflow_pat_identity_use_case.test.ts`](../src/application/usecases/setup/__tests__/verify_guided_workflow_pat_identity_use_case.test.ts) · [`src/infrastructure/__tests__/setup_github_identity_query_adapter.test.ts`](../src/infrastructure/__tests__/setup_github_identity_query_adapter.test.ts) · [`src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts) · [`src/cli/__tests__/setup_presenters.test.ts`](../src/cli/__tests__/setup_presenters.test.ts) · [`src/cli/__tests__/setup_journey_presenter.test.ts`](../src/cli/__tests__/setup_journey_presenter.test.ts) · [`src/data/repository/__tests__/repository_variables_repository.test.ts`](../src/data/repository/__tests__/repository_variables_repository.test.ts) - User documentation: [`README.md`](../README.md) · [`docs/authentication.mdx`](../docs/authentication.mdx) · [`docs/how-to-use.mdx`](../docs/how-to-use.mdx) · [`docs/configuration.mdx`](../docs/configuration.mdx) · [`docs/development/architecture.mdx`](../docs/development/architecture.mdx) · [`docs/security-operations/operations/troubleshooting.mdx`](../docs/security-operations/operations/troubleshooting.mdx) ### `temporary-setup-operator-authorization` — Assisted setup PAT creation @@ -128,8 +128,8 @@ debt or convert unknown historic intent into a design decision. - Specifications: [`specs/temporary-setup-operator-authorization.md`](./temporary-setup-operator-authorization.md) - Workflows: Not applicable for this capability. - Entrypoints: [`src/cli/commands/setup.ts`](../src/cli/commands/setup.ts) -- Core code: [`src/cli/setup_credential_prompt_adapter.ts`](../src/cli/setup_credential_prompt_adapter.ts) · [`src/application/policies/setup_pat_intent_policy.ts`](../src/application/policies/setup_pat_intent_policy.ts) · [`src/application/policies/setup_questionnaire_policy.ts`](../src/application/policies/setup_questionnaire_policy.ts) · [`src/application/policies/setup_token_permission_policy.ts`](../src/application/policies/setup_token_permission_policy.ts) · [`src/application/policies/setup_pat_creation_url_policy.ts`](../src/application/policies/setup_pat_creation_url_policy.ts) · [`src/application/usecases/setup/setup_wizard_use_case.ts`](../src/application/usecases/setup/setup_wizard_use_case.ts) · [`src/application/usecases/setup/setup_token_permissions_use_case.ts`](../src/application/usecases/setup/setup_token_permissions_use_case.ts) · [`src/infrastructure/setup_token_permission_query_adapter.ts`](../src/infrastructure/setup_token_permission_query_adapter.ts) · [`src/utils/setup_files.ts`](../src/utils/setup_files.ts) -- Tests: [`src/cli/__tests__/setup_presenters.test.ts`](../src/cli/__tests__/setup_presenters.test.ts) · [`src/__tests__/cli.test.ts`](../src/__tests__/cli.test.ts) · [`src/application/policies/__tests__/setup_pat_intent_policy.test.ts`](../src/application/policies/__tests__/setup_pat_intent_policy.test.ts) · [`src/application/policies/__tests__/setup_questionnaire_policy.test.ts`](../src/application/policies/__tests__/setup_questionnaire_policy.test.ts) · [`src/application/policies/__tests__/setup_token_permission_policy.test.ts`](../src/application/policies/__tests__/setup_token_permission_policy.test.ts) · [`src/application/policies/__tests__/setup_pat_creation_url_policy.test.ts`](../src/application/policies/__tests__/setup_pat_creation_url_policy.test.ts) · [`src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_token_permissions_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_token_permissions_use_case.test.ts) · [`src/infrastructure/__tests__/setup_token_permission_query_adapter.test.ts`](../src/infrastructure/__tests__/setup_token_permission_query_adapter.test.ts) · [`src/utils/__tests__/setup_files.test.ts`](../src/utils/__tests__/setup_files.test.ts) +- Core code: [`src/cli/setup_credential_prompt_adapter.ts`](../src/cli/setup_credential_prompt_adapter.ts) · [`src/cli/setup_journey_presenter.ts`](../src/cli/setup_journey_presenter.ts) · [`src/application/policies/setup_pat_intent_policy.ts`](../src/application/policies/setup_pat_intent_policy.ts) · [`src/application/policies/setup_questionnaire_policy.ts`](../src/application/policies/setup_questionnaire_policy.ts) · [`src/application/policies/setup_token_permission_policy.ts`](../src/application/policies/setup_token_permission_policy.ts) · [`src/application/policies/setup_pat_creation_url_policy.ts`](../src/application/policies/setup_pat_creation_url_policy.ts) · [`src/application/usecases/setup/setup_wizard_use_case.ts`](../src/application/usecases/setup/setup_wizard_use_case.ts) · [`src/application/usecases/setup/setup_token_permissions_use_case.ts`](../src/application/usecases/setup/setup_token_permissions_use_case.ts) · [`src/infrastructure/setup_token_permission_query_adapter.ts`](../src/infrastructure/setup_token_permission_query_adapter.ts) · [`src/utils/setup_files.ts`](../src/utils/setup_files.ts) +- Tests: [`src/cli/__tests__/setup_presenters.test.ts`](../src/cli/__tests__/setup_presenters.test.ts) · [`src/cli/__tests__/setup_journey_presenter.test.ts`](../src/cli/__tests__/setup_journey_presenter.test.ts) · [`src/__tests__/cli.test.ts`](../src/__tests__/cli.test.ts) · [`src/application/policies/__tests__/setup_pat_intent_policy.test.ts`](../src/application/policies/__tests__/setup_pat_intent_policy.test.ts) · [`src/application/policies/__tests__/setup_questionnaire_policy.test.ts`](../src/application/policies/__tests__/setup_questionnaire_policy.test.ts) · [`src/application/policies/__tests__/setup_token_permission_policy.test.ts`](../src/application/policies/__tests__/setup_token_permission_policy.test.ts) · [`src/application/policies/__tests__/setup_pat_creation_url_policy.test.ts`](../src/application/policies/__tests__/setup_pat_creation_url_policy.test.ts) · [`src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_token_permissions_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_token_permissions_use_case.test.ts) · [`src/infrastructure/__tests__/setup_token_permission_query_adapter.test.ts`](../src/infrastructure/__tests__/setup_token_permission_query_adapter.test.ts) · [`src/utils/__tests__/setup_files.test.ts`](../src/utils/__tests__/setup_files.test.ts) - User documentation: [`README.md`](../README.md) · [`docs/how-to-use.mdx`](../docs/how-to-use.mdx) · [`docs/authentication.mdx`](../docs/authentication.mdx) · [`docs/configuration.mdx`](../docs/configuration.mdx) · [`docs/development/architecture.mdx`](../docs/development/architecture.mdx) · [`docs/security-operations/operations/troubleshooting.mdx`](../docs/security-operations/operations/troubleshooting.mdx) ### `issue-start-and-sdd-readiness` — Uniform issue start and pre-branch SDD readiness diff --git a/specs/catalog.json b/specs/catalog.json index ce0735918..8c318d4a6 100644 --- a/specs/catalog.json +++ b/specs/catalog.json @@ -644,7 +644,7 @@ "status": "implemented", "scope": "Plan, validate, provision, and audit a repository installation without exposing credentials", "owner": "Copilot maintainers", - "lastVerified": "2026-09-24", + "lastVerified": "2026-09-28", "specs": [ "specs/setup-configuration-credentials-and-doctor.md", "specs/setup-doctor-architecture-hardening.md", @@ -672,7 +672,10 @@ "src/application/policies/setup_configuration_storage_policy.ts", "src/application/policies/setup_doctor_message_catalog.ts", "src/application/policies/setup_doctor_report_policy.ts", + "src/application/policies/setup_journey_policy.ts", + "src/application/policies/setup_permission_summary_policy.ts", "src/application/usecases/setup/setup_wizard_use_case.ts", + "src/application/usecases/setup/setup_journey_use_case.ts", "src/application/usecases/setup/setup_questionnaire_controller.ts", "src/application/usecases/setup/setup_credentials_use_case.ts", "src/application/usecases/setup/setup_token_permissions_use_case.ts", @@ -691,6 +694,7 @@ "src/infrastructure/setup_token_permission_query_adapter.ts", "src/cli/setup_terminal_driver.ts", "src/cli/setup_question_renderer.ts", + "src/cli/setup_journey_presenter.ts", "src/cli/setup_plan_presenter.ts", "src/cli/setup_doctor_presenter.ts", "src/cli/setup_prompt_rendering.ts", @@ -711,6 +715,7 @@ "src/application/policies/__tests__/setup_doctor_report_policy.test.ts", "src/application/usecases/setup/__tests__/setup_questionnaire_controller.test.ts", "src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts", + "src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts", "src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts", "src/application/usecases/setup/__tests__/setup_token_permissions_use_case.test.ts", "src/application/usecases/setup/__tests__/doctor_use_case.test.ts", @@ -723,6 +728,7 @@ "src/infrastructure/composition/__tests__/setup_token_permissions_composition_root.test.ts", "src/data/repository/__tests__/repository_variables_repository.test.ts", "src/cli/__tests__/setup_presenters.test.ts", + "src/cli/__tests__/setup_journey_presenter.test.ts", "src/cli/__tests__/setup_prompt_rendering.test.ts", "src/cli/__tests__/setup_token_permission_presenter.test.ts", "src/__tests__/cli.test.ts", @@ -760,12 +766,14 @@ ], "code": [ "src/application/policies/setup_token_permission_policy.ts", + "src/application/policies/setup_permission_summary_policy.ts", "src/application/policies/setup_pat_creation_url_policy.ts", "src/application/ports/setup_pat_identity_ports.ts", "src/application/usecases/setup/verify_guided_workflow_pat_identity_use_case.ts", "src/infrastructure/setup_github_identity_query_adapter.ts", "src/application/usecases/setup/setup_credentials_use_case.ts", "src/cli/setup_credential_prompt_adapter.ts", + "src/cli/setup_journey_presenter.ts", "src/data/repository/repository_variables_repository.ts" ], "tests": [ @@ -775,6 +783,7 @@ "src/infrastructure/__tests__/setup_github_identity_query_adapter.test.ts", "src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts", "src/cli/__tests__/setup_presenters.test.ts", + "src/cli/__tests__/setup_journey_presenter.test.ts", "src/data/repository/__tests__/repository_variables_repository.test.ts" ], "documentation": [ @@ -802,6 +811,7 @@ ], "code": [ "src/cli/setup_credential_prompt_adapter.ts", + "src/cli/setup_journey_presenter.ts", "src/application/policies/setup_pat_intent_policy.ts", "src/application/policies/setup_questionnaire_policy.ts", "src/application/policies/setup_token_permission_policy.ts", @@ -813,6 +823,7 @@ ], "tests": [ "src/cli/__tests__/setup_presenters.test.ts", + "src/cli/__tests__/setup_journey_presenter.test.ts", "src/__tests__/cli.test.ts", "src/application/policies/__tests__/setup_pat_intent_policy.test.ts", "src/application/policies/__tests__/setup_questionnaire_policy.test.ts", diff --git a/specs/guided-bot-pat-onboarding.md b/specs/guided-bot-pat-onboarding.md index 29fe884fd..e065d450a 100644 --- a/specs/guided-bot-pat-onboarding.md +++ b/specs/guided-bot-pat-onboarding.md @@ -351,6 +351,21 @@ precedes bot identity verification, and no operator token is used as runtime ## 9. UI/UX and content contract +The [setup journey presentation contract](./setup-configuration-credentials-and-doctor.md#9-uiux-and-content-contract) +also covers the later `Bot PAT & credentials` phase. Show a compact, +role-labelled runtime-grant summary before asking how to obtain the bot PAT; +the user can view the full policy table on demand in guided mode, while manual +mode retains the complete table. The summary and detailed rows MUST use the +same final workflow-role permission objects, never the operator preview. +The phase remains active until credential collection and identity validation +finish; successful Secret installation is only reported after the apply step. +If a later apply fails after a Secret write, report partial state rather than +claiming the credential was discarded or the setup was complete. No bot token +value appears in the phase view. Add three bot-specific cases to the journey +budget: final role separation, detail-to-review without repeated identity +input, and partial Secret-write wording. The six-stage model and terminal +accessibility tests are shared with the setup baseline, not counted twice. + The CLI first shows the role, expected account, repository, permission purpose, remaining action, and cleanup ownership. The following English example matches the current CLI language; it is illustrative. The URL is plain, complete, and diff --git a/specs/setup-configuration-credentials-and-doctor.md b/specs/setup-configuration-credentials-and-doctor.md index 588e4d92e..7310a8b2e 100644 --- a/specs/setup-configuration-credentials-and-doctor.md +++ b/specs/setup-configuration-credentials-and-doctor.md @@ -2,9 +2,9 @@ - Status: Implemented — automated architecture, UX, documentation, and coverage gates complete; controlled live GitHub permission-path evidence remains external - Date: 2026-09-11 -- Last updated: 2026-09-24 +- Last updated: 2026-09-28 - Catalog capability ID: `setup-and-doctor` -- Last verified: 2026-09-24 +- Last verified: 2026-09-28 (automated journey/presentation gates; live GitHub path remains external) - Owners: Copilot maintainers - Scope: interactive/non-interactive installation planning, file and resource provisioning, credential validation, and read-only diagnosis - Related issues/PRs: merge-queue readiness SDD; architecture quality and @@ -241,6 +241,52 @@ asset parity. ## 9. UI/UX and content contract +### Interactive journey presentation (2026-09-28 amendment) + +Interactive `copilot setup` MUST show a bounded, text-first six-stage journey: +`Repository → Setup choices → Setup PAT → Plan → Bot PAT & credentials → Apply`. +The active stage is named in words, previously completed stages are marked +complete, and later stages remain pending. A stage number describes position, +not a percentage or a count of questions. Show the journey at meaningful +transitions, not after every answer. Never mark `Apply` complete until the +action reports success; failure after application begins is **Partial**, not +`No changes`. Before application begins, say `No changes have been applied`. +Cancellation or a blocked audit does not advance the journey. Dry-run ends +after plan review with an explicit `No changes` result; unattended input keeps +its existing non-interactive output rather than receiving interactive prompts. + +```text +Copilot setup · owner/repo +Stage 2/6 · Setup choices +Complete: Repository +Now: Setup choices +Next: Setup PAT → Plan → Bot PAT & credentials → Apply +No changes have been applied. +``` + +Text equivalent: the named current phase follows repository detection; all +other phases are explicitly complete or pending, and no remote mutation has +started. In a narrow terminal, each status remains on its own wrapped line. +Icons and color may reinforce the state but MUST NOT be its only carrier. +The journey is a view of existing setup state, not a new questionnaire or +source of permission truth. The application boundary owns stage ordering and +transition validity; the terminal adapter owns width, wrapping, and ANSI. +Do not persist phase state or print credentials. Detailed permission tables +remain available on explicit request and for manual/unattended paths; the +interactive guided review defaults to an exact compact grant summary. + +The presentation introduces no new flags or persisted configuration. It has +no effect on GitHub Actions, issues, PRs, comments, or checks. Rollback removes +the stage renderer and restores the existing table-first presentation without +changing saved setup state. The amendment adds a minimum **12 distinct tests**: +four pure transition/view-model cases, three terminal width/color cases, +three guided/manual detail cases, and two end-to-end dry-run/partial-state +cases. Changed presentation code targets 95% line and 90% branch coverage. +Acceptance requires stage ordering, accurate no-change/partial claims, no +duplicate intent questions, exact summary-to-table grants, and secret-free +output in both wide and narrow no-color terminals. User and contributor docs +MUST describe the phases and where the full permission table can be opened. + ```markdown Pending: **Inspecting existing Copilot resources.** No changes have been made. Action required: **The workflow `PAT` Secret is missing.** Add or enter it to enable release workflows. diff --git a/specs/temporary-setup-operator-authorization.md b/specs/temporary-setup-operator-authorization.md index 4dc443993..826f3b507 100644 --- a/specs/temporary-setup-operator-authorization.md +++ b/specs/temporary-setup-operator-authorization.md @@ -402,6 +402,35 @@ final grant acceptance. ## 9. Terminal UI and content contract +The [setup journey presentation contract](./setup-configuration-credentials-and-doctor.md#9-uiux-and-content-contract) +applies across both PAT roles. Before interactive guided intent, show the +bootstrap grants compactly, not the complete conditional table. After choices, +show every currently required grant and a count of conditional/remote-unknown +grants. The review choices are `Continue`, `Revise choices`, `View full permission +table`, and `Enter a PAT manually`. Selecting detail prints the same policy +requirements with reasons and returns to review **without rerunning questions**. +Manual entry shows the full bootstrap table immediately; supplied-token and +non-interactive paths keep the existing table and audit. The raw GitHub URL +remains on one copyable line outside a box. The active journey stage stays +`Setup PAT` until the entered credential passes the initial audit; a failed +audit or cancellation cannot make it look complete. + +```text +Stage 3/6 · Setup PAT +Required now: Metadata read · Contents write · Secrets write (repository) +May need after GitHub inspection: 2 conditional grants +No changes have been applied. +1) Continue 2) Revise choices 3) View full permission table 4) Enter a PAT manually +``` + +Text equivalent: the URL will contain the exact required grants in the +summary, while two remote-dependent grants are unresolved; the user can +inspect reasons before accepting. Labels, counts, and detailed rows derive +from the same requirement objects and may not be edited independently. +Add five operator-specific cases to the journey budget: detail returns to +review, manual shows full table, revision changes the summary, cancellation +preserves the no-change state, and narrow no-color output remains readable. + The current CLI is English; this example is illustrative and follows its existing text-first styling. Preserve one primary action per state. diff --git a/src/__tests__/cli.test.ts b/src/__tests__/cli.test.ts index fa874ab6c..efc8afca1 100644 --- a/src/__tests__/cli.test.ts +++ b/src/__tests__/cli.test.ts @@ -513,6 +513,7 @@ describe('CLI', () => { expect(logInfo).toHaveBeenCalledWith(expect.stringContaining('Owner type was not confirmed')); expect(input.readText).not.toHaveBeenCalledWith(expect.stringContaining('Review these intended grants')); expect(input.readSecret).toHaveBeenCalledWith('Setup PAT'); + expect(consoleLogSpy.mock.calls.flat().join('\n')).toContain('Setup PAT permissions required'); expect(consoleLogSpy.mock.calls.flat().join('\n')).not.toContain('Revoke temporary setup PAT'); expect(process.exitCode).toBe(1); } finally { createTerminal.mockRestore(); } @@ -533,6 +534,20 @@ describe('CLI', () => { } finally { createTerminal.mockRestore(); } }); + it('shows the full permission table immediately for manual setup PAT entry', async () => { + const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); + const input = guidedTerminal(prompt => prompt.includes('How would you like to provide the setup PAT?') ? '2' : ''); + const createTerminal = jest.spyOn(terminalDriver, 'createInteractiveTerminalDriver') + .mockReturnValue(input as unknown as ReturnType); + mockTokenPermissionInspect.mockResolvedValueOnce({ ...acceptedSetupPatReport(), ready: false }); + try { + await program.parseAsync(['node', 'cli', 'setup']); + const output = consoleLogSpy.mock.calls.flat().join('\n'); + expect(output).toContain('Setup PAT permissions required'); + expect(output).toContain('Stage 3/6 · Setup PAT'); + } finally { createTerminal.mockRestore(); } + }); + it('revises permission intent before the link and drops the initial-tag write grant', async () => { const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); let reviews = 0; @@ -558,6 +573,28 @@ describe('CLI', () => { } finally { createTerminal.mockRestore(); } }); + it('shows setup permission details on demand without repeating the intent questionnaire', async () => { + const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); + let reviews = 0; + const input = guidedTerminal(prompt => { + if (prompt.includes('repository owner an organization')) return '2'; + if (prompt.includes('Review these intended grants')) return ++reviews === 1 ? '4' : '1'; + return ''; + }); + const createTerminal = jest.spyOn(terminalDriver, 'createInteractiveTerminalDriver') + .mockReturnValue(input as unknown as ReturnType); + mockTokenPermissionInspect.mockResolvedValueOnce({ ...acceptedSetupPatReport(), ready: false }); + try { + await program.parseAsync(['node', 'cli', 'setup']); + expect(reviews).toBe(2); + expect(input.readText.mock.calls.filter(([prompt]) => String(prompt).includes('Create v1.0.0'))).toHaveLength(1); + const output = consoleLogSpy.mock.calls.flat().join('\n'); + expect(output).toContain('Setup PAT permission summary'); + expect(output).toContain('Setup PAT permissions required'); + expect(output).toContain('Stage 3/6 · Setup PAT'); + } finally { createTerminal.mockRestore(); } + }); + it('blocks a personal owner paired with organization storage before requesting a PAT', async () => { const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); const input = guidedTerminal(); diff --git a/src/application/policies/setup_journey_policy.ts b/src/application/policies/setup_journey_policy.ts new file mode 100644 index 000000000..8b463e468 --- /dev/null +++ b/src/application/policies/setup_journey_policy.ts @@ -0,0 +1,48 @@ +export const SETUP_JOURNEY_STAGES = [ + 'repository', 'choices', 'setup-pat', 'plan', 'credentials', 'apply', +] as const; + +export type SetupJourneyStage = typeof SETUP_JOURNEY_STAGES[number]; +export type SetupJourneyOutcome = 'complete' | 'dry-run' | 'cancelled' | 'blocked' | 'partial'; + +const labels: Readonly> = { + repository: 'Repository', + choices: 'Setup choices', + 'setup-pat': 'Setup PAT', + plan: 'Plan', + credentials: 'Bot PAT & credentials', + apply: 'Apply', +}; + +export interface SetupJourneyView { + readonly repository: string; + readonly position: number; + readonly total: number; + readonly current: string; + readonly complete: readonly string[]; + readonly pending: readonly string[]; + readonly outcome?: SetupJourneyOutcome; + readonly mutationStarted: boolean; +} + +export function buildSetupJourneyView( + repository: string, + stage: SetupJourneyStage, + mutationStarted: boolean, + outcome?: SetupJourneyOutcome, +): SetupJourneyView { + const position = SETUP_JOURNEY_STAGES.indexOf(stage); + return { + repository: [...repository].map(character => { + const codePoint = character.codePointAt(0)!; + return codePoint < 32 || (codePoint >= 127 && codePoint <= 159) ? '?' : character; + }).join('').slice(0, 120), + position: position + 1, + total: SETUP_JOURNEY_STAGES.length, + current: labels[stage], + complete: SETUP_JOURNEY_STAGES.slice(0, position).map(item => labels[item]), + pending: SETUP_JOURNEY_STAGES.slice(position + 1).map(item => labels[item]), + ...(outcome ? { outcome } : {}), + mutationStarted, + }; +} diff --git a/src/application/policies/setup_permission_summary_policy.ts b/src/application/policies/setup_permission_summary_policy.ts new file mode 100644 index 000000000..356135bc0 --- /dev/null +++ b/src/application/policies/setup_permission_summary_policy.ts @@ -0,0 +1,17 @@ +import type { SetupTokenPermissionRequirement } from '../../domain/setup_token_permissions'; + +export interface SetupPermissionSummary { + readonly required: readonly string[]; + readonly conditionalCount: number; +} + +/** A lossless required-grant view of the same requirements used for URL creation. */ +export function summarizeSetupPermissions( + requirements: readonly SetupTokenPermissionRequirement[], +): SetupPermissionSummary { + return { + required: requirements.filter(item => item.applicability === 'required') + .map(item => `${item.permission} ${item.level} (${item.scope})`), + conditionalCount: requirements.filter(item => item.applicability === 'conditional').length, + }; +} diff --git a/src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts b/src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts new file mode 100644 index 000000000..9efd9ed2a --- /dev/null +++ b/src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts @@ -0,0 +1,63 @@ +import { SetupJourneyUseCase } from '../setup_journey_use_case'; + +describe('setup journey', () => { + it('keeps ordered milestones and does not imply changes before apply', () => { + const present = jest.fn(); + const journey = new SetupJourneyUseCase('owner/repo', { present }); + journey.advance('choices'); + journey.advance('setup-pat'); + journey.advance('plan'); + expect(present.mock.calls.map(([view]) => view.current)).toEqual(['Setup choices', 'Setup PAT', 'Plan']); + expect(present.mock.lastCall?.[0]).toMatchObject({ + complete: ['Repository', 'Setup choices', 'Setup PAT'], + pending: ['Bot PAT & credentials', 'Apply'], mutationStarted: false, + }); + }); + + it('rejects backwards progress and false completion', () => { + const journey = new SetupJourneyUseCase('owner/repo', { present: jest.fn() }); + journey.advance('plan'); + expect(() => journey.advance('choices')).toThrow('backwards'); + expect(() => journey.finish('complete')).toThrow('before applying'); + expect(() => journey.finish('partial')).toThrow('before mutation'); + expect(() => journey.markMutationStarted()).toThrow('apply stage'); + }); + + it('distinguishes dry-run, blocked, cancelled, and post-mutation partial state', () => { + for (const outcome of ['dry-run', 'blocked', 'cancelled'] as const) { + const present = jest.fn(); + const journey = new SetupJourneyUseCase('owner/repo', { present }); + journey.advance('plan'); + journey.finish(outcome); + expect(present.mock.lastCall?.[0]).toMatchObject({ outcome, mutationStarted: false }); + expect(() => journey.advance('apply')).toThrow('finished'); + } + const present = jest.fn(); + const journey = new SetupJourneyUseCase('owner/repo', { present }); + journey.advance('apply'); + journey.markMutationStarted(); + journey.finish('partial'); + expect(present.mock.lastCall?.[0]).toMatchObject({ outcome: 'partial', mutationStarted: true }); + }); + + it('reports completion only after mutation starts and ignores duplicate finish', () => { + const present = jest.fn(); + const journey = new SetupJourneyUseCase('owner/repo', { present }); + journey.advance('apply'); + journey.markMutationStarted(); + expect(present.mock.lastCall?.[0].mutationStarted).toBe(true); + expect(present.mock.lastCall?.[0].outcome).toBeUndefined(); + journey.finish('complete'); + journey.finish('blocked'); + expect(present.mock.lastCall?.[0].outcome).toBe('complete'); + }); + + it('ignores repeated advances', () => { + const present = jest.fn(); + const journey = new SetupJourneyUseCase('owner/repo', { present }); + journey.advance('choices'); + journey.advance('choices'); + expect(present).toHaveBeenCalledTimes(1); + expect(present.mock.lastCall?.[0]).toMatchObject({ current: 'Setup choices', complete: ['Repository'] }); + }); +}); diff --git a/src/application/usecases/setup/setup_journey_use_case.ts b/src/application/usecases/setup/setup_journey_use_case.ts new file mode 100644 index 000000000..766917595 --- /dev/null +++ b/src/application/usecases/setup/setup_journey_use_case.ts @@ -0,0 +1,46 @@ +import { buildSetupJourneyView, SETUP_JOURNEY_STAGES } from '../../policies/setup_journey_policy'; +import type { SetupJourneyOutcome, SetupJourneyStage, SetupJourneyView } from '../../policies/setup_journey_policy'; + +export interface SetupJourneyPresenterPort { + present(view: SetupJourneyView): void; +} + +/** Tracks semantic milestones, independently of the CLI's rendering. */ +export class SetupJourneyUseCase { + private stage: SetupJourneyStage = 'repository'; + private outcome?: SetupJourneyOutcome; + private mutationStarted = false; + + constructor(private readonly repository: string, private readonly presenter: SetupJourneyPresenterPort) {} + + advance(stage: SetupJourneyStage): void { + if (this.outcome) throw new Error('Cannot advance a finished setup journey.'); + const next = SETUP_JOURNEY_STAGES.indexOf(stage); + if (next < SETUP_JOURNEY_STAGES.indexOf(this.stage)) throw new Error('Setup journey cannot move backwards.'); + if (next === SETUP_JOURNEY_STAGES.indexOf(this.stage)) return; + this.stage = stage; + this.present(); + } + + markMutationStarted(): void { + if (this.stage !== 'apply' || this.outcome) throw new Error('Setup mutation must start in the apply stage.'); + this.mutationStarted = true; + this.present(); + } + + finish(outcome: SetupJourneyOutcome): void { + if (this.outcome) return; + if (outcome === 'complete' && (this.stage !== 'apply' || !this.mutationStarted)) { + throw new Error('Setup cannot be complete before applying the plan.'); + } + if (outcome === 'partial' && !this.mutationStarted) { + throw new Error('Setup cannot be partial before mutation starts.'); + } + this.outcome = outcome; + this.present(); + } + + private present(): void { + this.presenter.present(buildSetupJourneyView(this.repository, this.stage, this.mutationStarted, this.outcome)); + } +} diff --git a/src/architecture/__tests__/setup_doctor_boundaries.test.ts b/src/architecture/__tests__/setup_doctor_boundaries.test.ts index 974957015..86512f6f2 100644 --- a/src/architecture/__tests__/setup_doctor_boundaries.test.ts +++ b/src/architecture/__tests__/setup_doctor_boundaries.test.ts @@ -36,12 +36,22 @@ describe('setup and doctor architecture boundaries', () => { 'src/application/policies/merge_queue_message_catalog.ts', 'src/application/policies/setup_doctor_message_catalog.ts', 'src/application/policies/setup_doctor_report_policy.ts', + 'src/application/policies/setup_journey_policy.ts', + 'src/application/policies/setup_permission_summary_policy.ts', ]) { const source = read(file); expect(source).not.toMatch(/from ['"]node:|\/cli\/|\/infrastructure\/|octokit|Execution/); } }); + it('keeps setup journey decisions in the application and terminal rendering in the CLI', () => { + const journey = read('src/application/usecases/setup/setup_journey_use_case.ts'); + const renderer = read('src/cli/setup_journey_presenter.ts'); + expect(journey).not.toMatch(/from ['"]node:|\/cli\/|\/infrastructure\/|console\.|process\./u); + expect(renderer).toContain('renderBox('); + expect(renderer).not.toMatch(/buildSetupPatCreationUrl|buildWorkflowPatPermissionRequirements/u); + }); + it('resolves one doctor catalog and reuses it through readiness and presentation', () => { const doctor = read('src/application/usecases/setup/doctor_use_case.ts'); const command = read('src/cli/commands/doctor.ts'); diff --git a/src/cli/__tests__/setup_journey_presenter.test.ts b/src/cli/__tests__/setup_journey_presenter.test.ts new file mode 100644 index 000000000..cb973a2b8 --- /dev/null +++ b/src/cli/__tests__/setup_journey_presenter.test.ts @@ -0,0 +1,40 @@ +import { buildSetupJourneyView } from '../../application/policies/setup_journey_policy'; +import { renderSetupJourney } from '../setup_journey_presenter'; + +describe('setup journey presenter', () => { + it('shows semantic status without relying on color or icons', () => { + const output = renderSetupJourney(buildSetupJourneyView('owner/repo', 'setup-pat', false), 80); + expect(output).toContain('Stage 3/6 · Setup PAT'); + expect(output).toContain('Complete: Repository → Setup choices'); + expect(output).toContain('No changes have been applied.'); + }); + + it('keeps narrow output readable and distinguishes partial from complete', () => { + const partial = renderSetupJourney(buildSetupJourneyView('owner/repo', 'apply', true, 'partial'), 40); + expect(partial).toContain('Partial: application started'); + expect(partial).not.toContain('No changes have been applied.'); + expect(partial.split('\n').every(line => line.length <= 42)).toBe(true); + const complete = renderSetupJourney(buildSetupJourneyView('owner/repo', 'apply', true, 'complete'), 80); + expect(complete).toContain('Complete: setup applied successfully.'); + }); + + it('states that dry-run applies no changes', () => { + const output = renderSetupJourney(buildSetupJourneyView('owner/repo', 'plan', false, 'dry-run'), 80); + expect(output).toContain('dry run only; no changes were applied'); + }); + + it('renders the active mutation state and the first-stage pending list', () => { + const applying = renderSetupJourney(buildSetupJourneyView('owner/repo', 'apply', true), 80); + expect(applying).toContain('changes may already exist'); + const starting = renderSetupJourney(buildSetupJourneyView('owner/repo', 'repository', false), 80); + expect(starting).toContain('Complete: none'); + expect(starting).toContain('Next: Setup choices'); + }); + + it('does not echo terminal control characters from a repository label', () => { + const output = renderSetupJourney(buildSetupJourneyView('owner/\u001b[31mrepo', 'choices', false), 80); + expect(output).not.toContain('\u001b[31m'); + expect(output).toContain('owner/?[31mrepo'); + expect(renderSetupJourney(buildSetupJourneyView('owner/\u007f\u0080repo', 'choices', false), 80)).toContain('owner/??repo'); + }); +}); diff --git a/src/cli/__tests__/setup_presenters.test.ts b/src/cli/__tests__/setup_presenters.test.ts index d7008fc18..459728c33 100644 --- a/src/cli/__tests__/setup_presenters.test.ts +++ b/src/cli/__tests__/setup_presenters.test.ts @@ -302,6 +302,12 @@ describe('setup presenters and prompt-specific adapters', () => { } finally { log.mockRestore(); } }); + it('offers the full setup permission table as a review action', async () => { + const input = terminal([{ kind: 'value', value: '4' }]); + await expect(new SetupCredentialPromptAdapter(input, {}).reviewSetupPatIntent()).resolves.toBe('details'); + expect(input.readText).toHaveBeenCalledWith(expect.stringContaining('view full permission table')); + }); + it('rejects an invalid authenticated setup account without prompting', async () => { const input = terminal([{ kind: 'value', value: '1' }]); const adapter = new SetupCredentialPromptAdapter(input, {}); @@ -391,6 +397,29 @@ describe('setup presenters and prompt-specific adapters', () => { } finally { log.mockRestore(); } }); + it('shows bot permission details on demand without asking for the bot identity twice', async () => { + const log = jest.spyOn(console, 'log').mockImplementation(); + try { + const input = terminal([ + { kind: 'value', value: '3' }, + { kind: 'value', value: '1' }, + { kind: 'value', value: 'vypbot' }, + { kind: 'value', value: 'bot-token' }, + ]); + const resolve = jest.fn(async () => ({ id: 42, login: 'vypbot' })); + const adapter = new SetupCredentialPromptAdapter(input, {}); + adapter.configureWorkflowPatGuide('https://github.com/settings/personal-access-tokens/new', resolve, [{ + id: 'workflow.repository.contents', role: 'workflow', scope: 'repository', permission: 'Contents', + level: 'write', applicability: 'required', reason: 'Manage branches.', probe: 'contents', + }]); + await expect(adapter.requestWorkflowPat({ name: 'PAT', kind: 'workflowPat', description: 'Runtime token' })) + .resolves.toEqual({ name: 'PAT', value: 'bot-token' }); + expect(resolve).toHaveBeenCalledTimes(1); + expect(input.readText.mock.calls.filter(([prompt]) => String(prompt).includes('Expected GitHub bot login'))).toHaveLength(1); + expect(log.mock.calls.flat().join('\n')).toContain('Workflow PAT permissions required'); + } finally { log.mockRestore(); } + }); + it('propagates cancellation before a bot login can be resolved', async () => { const log = jest.spyOn(console, 'log').mockImplementation(); try { diff --git a/src/cli/__tests__/setup_token_permission_presenter.test.ts b/src/cli/__tests__/setup_token_permission_presenter.test.ts index 818f9d9af..c941b546a 100644 --- a/src/cli/__tests__/setup_token_permission_presenter.test.ts +++ b/src/cli/__tests__/setup_token_permission_presenter.test.ts @@ -1,6 +1,7 @@ import { renderSetupTokenPermissionReport, renderSetupTokenPermissionRequirements, + renderSetupTokenPermissionSummary, } from '../setup_token_permission_presenter'; import type { SetupTokenPermissionRequirement } from '../../domain/setup_token_permissions'; @@ -15,6 +16,14 @@ const secrets: SetupTokenPermissionRequirement = { }; describe('setup token permission presenter', () => { + it('summarizes only required URL grants and counts conditional rows without changing policy', () => { + const output = renderSetupTokenPermissionSummary('setup', [metadata, secrets], 80); + expect(output).toContain('Required now: Metadata read (repository)'); + expect(output).toContain('Conditional permissions: 1'); + expect(output).not.toContain('Provision Actions Secrets.'); + expect(renderSetupTokenPermissionRequirements('setup', [metadata, secrets])).toContain('Provision Actions Secrets.'); + expect(renderSetupTokenPermissionSummary('setup', [], 80)).toContain('Required now: none'); + }); it('renders the requirement matrix before setup PAT input', () => { const output = renderSetupTokenPermissionRequirements('setup', [metadata, secrets], 120); expect(output).toContain('Setup PAT permissions required'); diff --git a/src/cli/commands/setup.ts b/src/cli/commands/setup.ts index 244193e92..9d70b8f7e 100644 --- a/src/cli/commands/setup.ts +++ b/src/cli/commands/setup.ts @@ -43,6 +43,8 @@ import { buildSetupPatCreationUrl, UnsupportedSetupPatLinkError } from '../../ap import { SetupGithubIdentityQueryAdapter } from '../../infrastructure/setup_github_identity_query_adapter'; import { VerifyGuidedWorkflowPatIdentityUseCase } from '../../application/usecases/setup/verify_guided_workflow_pat_identity_use_case'; import type { SetupTokenPermissionRequirement } from '../../domain/setup_token_permissions'; +import { SetupJourneyUseCase } from '../../application/usecases/setup/setup_journey_use_case'; +import { ConsoleSetupJourneyPresenter } from '../setup_journey_presenter'; export function registerSetupCommand(program: Command): void { program @@ -80,11 +82,12 @@ export function registerSetupCommand(program: Command): void { ...(options.workflowPat ? { PAT: options.workflowPat } : {}), ...options.secret, }, Boolean(options.confirmUnverifiableWritePermissions)); - const permissionPresenter = new ConsoleSetupTokenPermissionPresenter(); + const permissionPresenter = new ConsoleSetupTokenPermissionPresenter(options.nonInteractive ? 'full' : 'summary'); const tokenPermissions = createSetupTokenPermissionsUseCase(); const workflowPrompt = new SetupWorkflowUpdatePromptAdapter(terminal); const cwd = process.cwd(); let setupMutationStarted = false; + let journey: SetupJourneyUseCase | undefined; try { if (!options.nonInteractive && !terminal) { logError('Interactive setup requires a terminal. Use --non-interactive with explicit configuration.'); @@ -106,10 +109,15 @@ export function registerSetupCommand(program: Command): void { return; } logInfo(`📦 Repository: ${gitInfo.owner}/${gitInfo.repo}`); + if (!options.nonInteractive) { + journey = new SetupJourneyUseCase(`${gitInfo.owner}/${gitInfo.repo}`, new ConsoleSetupJourneyPresenter()); + journey.advance('choices'); + } const overrides = loadSetupOverrides(options); let setupPatPermissions = buildSetupPatPermissionRequirements(); - permissionPresenter.showRequirements('setup', setupPatPermissions); let token = getSetupToken(cwd, options.token); + if (token || options.nonInteractive) permissionPresenter.showDetailedRequirements('setup', setupPatPermissions); + else permissionPresenter.showRequirements('setup', setupPatPermissions); let setupPatAccount: string | undefined; let permissionIntent: { draft: SetupConfiguration; answeredQuestionIds: readonly string[] } | undefined; let assertedOwnerKind: 'Organization' | 'User' | undefined; @@ -132,6 +140,7 @@ export function registerSetupCommand(program: Command): void { if (ownerKind === 'unknown') { logInfo('Owner type was not confirmed. Use the manual PAT table, or check whether the GitHub owner is an organization before retrying guided setup.'); credentialPrompt.useManualSetupPat(); + permissionPresenter.showDetailedRequirements('setup', setupPatPermissions); break; } if (setupPatIntentOwnerConflict(draft, ownerKind)) { @@ -142,15 +151,21 @@ export function registerSetupCommand(program: Command): void { logInfo(`The selected local configuration needs correction before a guided link can be generated:\n${intentErrors.map(item => ` - ${item}`).join('\n')}`); } const preview = buildSetupPatIntentPermissionRequirements(draft, ownerKind); + journey?.advance('setup-pat'); logInfo('Permission intent:'); logInfo(` Initial tag: ${draft.createInitialTag ? 'yes' : 'no'}; issue workflows: ${draft.features.issues ? draft.issueWorkflows.enabled.join(', ') || 'none' : 'disabled'}; PR approval: ${draft.pullRequestApproval.mode}`); logInfo(` Secrets: ${draft.manageRepositorySecrets ? draft.storage.secrets.defaultScope : 'off'}; Variables: ${draft.manageRepositoryVariables ? draft.storage.variables.defaultScope : 'off'}; Projects: ${draft.projects.ids.trim() || 'none'}`); permissionPresenter.showRequirements('setup', preview); const uncertain = buildSetupPatIntentUncertainty(draft, ownerKind); if (uncertain.length) logInfo(`May need after GitHub inspection:\n${uncertain.map(item => ` - ${item}`).join('\n')}`); - const decision = await credentialPrompt.reviewSetupPatIntent(); + let decision: Awaited>; + do { + decision = await credentialPrompt.reviewSetupPatIntent(); + if (decision === 'details') permissionPresenter.showDetailedRequirements('setup', preview); + } while (decision === 'details'); if (decision === 'manual') { credentialPrompt.useManualSetupPat(); + permissionPresenter.showDetailedRequirements('setup', setupPatPermissions); break; } if (decision === 'revise') continue; @@ -170,11 +185,17 @@ export function registerSetupCommand(program: Command): void { if (!(error instanceof UnsupportedSetupPatLinkError)) throw error; logInfo('A guided setup PAT link is unavailable for this owner or permission set. Enter a manually created PAT using the table above.'); credentialPrompt.useManualSetupPat(); + permissionPresenter.showDetailedRequirements('setup', setupPatPermissions); } break; } + } else { + journey?.advance('setup-pat'); + permissionPresenter.showDetailedRequirements('setup', setupPatPermissions); } } + if (options.dryRun && !token) journey?.advance('plan'); + if (!token && !options.dryRun) journey?.advance('setup-pat'); if (!token && !options.nonInteractive && !options.dryRun) token = await credentialPrompt.requestSetupPat(); if (!token && !options.dryRun) { logError('🛑 Setup requires PERSONAL_ACCESS_TOKEN with a valid token.'); @@ -185,6 +206,7 @@ export function registerSetupCommand(program: Command): void { return; } if (token) { + journey?.advance('setup-pat'); const permissionReport = await tokenPermissions.inspect({ role: 'setup', owner: gitInfo.owner, @@ -210,6 +232,7 @@ export function registerSetupCommand(program: Command): void { throw new ApplicationError('authorization.credential-invalid', 'The setup PAT belongs to an unintended account. Revoke it in GitHub and retry with the correct account.'); } setupPatAccount = permissionReport.account; + journey?.advance('plan'); } logInfo(options.dryRun ? '🧭 Building a dry-run setup plan...' : '🧭 Building your setup plan...'); const auditConfiguredSetupPat = async ( @@ -275,6 +298,7 @@ export function registerSetupCommand(program: Command): void { ...(token ? { remoteTarget: { owner: gitInfo.owner, repository: gitInfo.repo, token } } : {}), }); if (result.status === 'cancelled') { + journey?.finish('cancelled'); if (result.reason !== 'questionnaire-cancelled') { logInfo('⏭️ Setup cancelled. No changes were applied.'); } @@ -282,6 +306,7 @@ export function registerSetupCommand(program: Command): void { return; } if (result.status === 'blocked') { + journey?.finish('blocked'); logError(new ApplicationError( result.reason === 'setup-permissions-unavailable' ? 'authorization.credential-invalid' : 'provider.unavailable', `${result.reason === 'setup-permissions-unavailable' @@ -299,9 +324,11 @@ export function registerSetupCommand(program: Command): void { ? workflowComparisons.filter(comparison => comparison.status === 'changed').map(comparison => comparison.file) : []; if (options.dryRun) { + journey?.finish('dry-run'); logInfo('✅ Dry run complete. No files or GitHub resources were changed.'); return; } + journey?.advance('credentials'); const workflowTokenPermissions = buildWorkflowPatPermissionRequirements(configuration, remoteConfiguration); const githubIdentities = new SetupGithubIdentityQueryAdapter(); if (!options.nonInteractive && !options.workflowPat && !options.secret?.PAT) { @@ -310,10 +337,11 @@ export function registerSetupCommand(program: Command): void { role: 'workflow', owner: gitInfo.owner, repository: gitInfo.repo, expiresIn: 90, requirements: workflowTokenPermissions, }); - credentialPrompt.configureWorkflowPatGuide(workflowPatGuide, login => githubIdentities.resolve(login, token!)); + credentialPrompt.configureWorkflowPatGuide(workflowPatGuide, login => githubIdentities.resolve(login, token!), workflowTokenPermissions); } catch (error) { if (!(error instanceof UnsupportedSetupPatLinkError)) throw error; logInfo('A guided fine-grained bot PAT link is unavailable for one or more required permissions. Use the permission table and manual path; review whether a classic PAT is required for this plan.'); + permissionPresenter.showDetailedRequirements('workflow', workflowTokenPermissions); } } const credentials = await createSetupCredentialsUseCase(credentialPrompt, permissionPresenter).collect({ @@ -337,6 +365,7 @@ export function registerSetupCommand(program: Command): void { } } logInfo('⚙️ Applying the approved setup plan...'); + journey?.advance('apply'); const params = buildSetupParams( options, gitInfo, @@ -346,14 +375,22 @@ export function registerSetupCommand(program: Command): void { approvedWorkflowFiles, remoteConfiguration, ); - if (!params) return; + if (!params) { + journey?.finish('blocked'); + return; + } setupMutationStarted = true; + journey?.markMutationStarted(); const actionResults = await runLocalAction(params); if (actionResults.some(actionResult => !actionResult.success || actionResult.errors.length > 0)) { + journey?.finish('partial'); logInfo('Setup reported failures or partial completion. If a bot PAT was supplied, its Secret may already have been written; inspect the result and GitHub Secret name/scope before retrying or revoking it.'); process.exitCode = 1; + } else { + journey?.finish('complete'); } } catch (error) { + journey?.finish(setupMutationStarted ? 'partial' : error instanceof SetupTerminalCancelledError ? 'cancelled' : 'blocked'); if (credentialPrompt.guidedWorkflowBotIdentity) { logInfo(setupMutationStarted ? 'Setup may be partially applied. Inspect the GitHub Secret before deleting or replacing the bot PAT.' diff --git a/src/cli/setup_credential_prompt_adapter.ts b/src/cli/setup_credential_prompt_adapter.ts index efed6cde2..b84a9565a 100644 --- a/src/cli/setup_credential_prompt_adapter.ts +++ b/src/cli/setup_credential_prompt_adapter.ts @@ -9,6 +9,8 @@ import type { import type { SetupTokenPermissionReport } from '../domain/setup_token_permissions'; import type { SetupGithubIdentity } from '../application/ports/setup_pat_identity_ports'; import { color, renderBox, statusIcon } from './setup_prompt_rendering'; +import type { SetupTokenPermissionRequirement } from '../domain/setup_token_permissions'; +import { renderSetupTokenPermissionRequirements } from './setup_token_permission_presenter'; export class SetupTerminalCancelledError extends Error { constructor() { @@ -24,6 +26,7 @@ export class SetupCredentialPromptAdapter implements SetupCredentialPromptPort { private guidedSetup = false; private setupMethodChosen = false; private guidedBotIdentity?: SetupGithubIdentity; + private workflowPatRequirements?: readonly SetupTokenPermissionRequirement[]; constructor( private readonly terminal: TerminalDriver | undefined, @@ -45,13 +48,14 @@ export class SetupCredentialPromptAdapter implements SetupCredentialPromptPort { const choice = await this.readChoice('Is the GitHub repository owner an organization or a personal account?', ['organization', 'personal account', 'not sure']); return choice === 'organization' ? 'Organization' : choice === 'personal account' ? 'User' : 'unknown'; } - async reviewSetupPatIntent(): Promise<'continue' | 'revise' | 'manual'> { + async reviewSetupPatIntent(): Promise<'continue' | 'revise' | 'manual' | 'details'> { if (!this.terminal) return 'manual'; - return await this.readChoice('Review these intended grants before opening GitHub. Continue, revise choices, or enter a PAT manually?', ['continue', 'revise', 'manual']) as 'continue' | 'revise' | 'manual'; + return await this.readChoice('Review these intended grants before opening GitHub. Continue, revise choices, view full permission table, or enter a PAT manually?', ['continue', 'revise', 'manual', 'details']) as 'continue' | 'revise' | 'manual' | 'details'; } - configureWorkflowPatGuide(url: string, resolveIdentity: (login: string) => Promise): void { + configureWorkflowPatGuide(url: string, resolveIdentity: (login: string) => Promise, requirements?: readonly SetupTokenPermissionRequirement[]): void { this.workflowPatGuide = url; this.resolveBotIdentity = resolveIdentity; + this.workflowPatRequirements = requirements; } get guidedWorkflowBotIdentity(): SetupGithubIdentity | undefined { return this.guidedBotIdentity; } @@ -159,7 +163,14 @@ export class SetupCredentialPromptAdapter implements SetupCredentialPromptPort { current?: SetupCredentialCheck, ): Promise { if (this.terminal && !this.credentialValues[requirement.name]?.trim() && this.workflowPatGuide) { - const guided = (await this.readChoice('How would you like to provide the bot workflow PAT?', ['guided link', 'manual PAT'], 'guided link')) === 'guided link'; + let choice: string; + do { + choice = await this.readChoice('How would you like to provide the bot workflow PAT?', ['guided link', 'manual PAT', 'view full permission table'], 'guided link'); + if (choice === 'view full permission table' && this.workflowPatRequirements) { + console.log(renderSetupTokenPermissionRequirements('workflow', this.workflowPatRequirements)); + } + } while (choice === 'view full permission table'); + const guided = choice === 'guided link'; if (guided) { const login = await this.readBotLogin(); const identity = await this.resolveBotIdentity!(login); @@ -171,6 +182,8 @@ export class SetupCredentialPromptAdapter implements SetupCredentialPromptPort { )); console.log(this.workflowPatGuide); console.log('Copy the one-time bot token and paste it below. It will be validated before any Secret is written.'); + } else if (this.workflowPatRequirements) { + console.log(renderSetupTokenPermissionRequirements('workflow', this.workflowPatRequirements)); } } return this.requestSecretForRequirement(requirement, current, 'workflow PAT owned by the bot account'); diff --git a/src/cli/setup_journey_presenter.ts b/src/cli/setup_journey_presenter.ts new file mode 100644 index 000000000..ee83f34a0 --- /dev/null +++ b/src/cli/setup_journey_presenter.ts @@ -0,0 +1,27 @@ +import type { SetupJourneyPresenterPort } from '../application/usecases/setup/setup_journey_use_case'; +import type { SetupJourneyView } from '../application/policies/setup_journey_policy'; +import { renderBox } from './setup_prompt_rendering'; + +export class ConsoleSetupJourneyPresenter implements SetupJourneyPresenterPort { + present(view: SetupJourneyView): void { + console.log(renderSetupJourney(view)); + } +} + +export function renderSetupJourney(view: SetupJourneyView, maximumWidth?: number): string { + const state = view.outcome === 'complete' ? 'Complete: setup applied successfully.' + : view.outcome === 'dry-run' ? 'Complete: dry run only; no changes were applied.' + : view.outcome === 'partial' ? 'Partial: application started; inspect the result before retrying.' + : view.outcome === 'blocked' ? 'Blocked: setup cannot continue.' + : view.outcome === 'cancelled' ? 'Cancelled: setup stopped.' + : view.mutationStarted ? 'Applying the approved plan; changes may already exist.' + : 'No changes have been applied.'; + return renderBox([ + `Repository: ${view.repository}`, + `Stage ${view.position}/${view.total} · ${view.current}`, + `Complete: ${view.complete.join(' → ') || 'none'}`, + `Now: ${view.current}`, + `Next: ${view.pending.join(' → ') || 'none'}`, + state, + ].join('\n'), 'Copilot setup', 36, maximumWidth); +} diff --git a/src/cli/setup_token_permission_presenter.ts b/src/cli/setup_token_permission_presenter.ts index d8792d945..831dd3169 100644 --- a/src/cli/setup_token_permission_presenter.ts +++ b/src/cli/setup_token_permission_presenter.ts @@ -7,9 +7,18 @@ import type { SetupTokenRole, } from '../domain/setup_token_permissions'; import { renderBox } from './setup_prompt_rendering'; +import { summarizeSetupPermissions } from '../application/policies/setup_permission_summary_policy'; export class ConsoleSetupTokenPermissionPresenter implements SetupTokenPermissionPresenterPort { + constructor(private readonly mode: 'full' | 'summary' = 'full') {} + showRequirements(role: SetupTokenRole, requirements: readonly SetupTokenPermissionRequirement[]): void { + console.log(this.mode === 'summary' + ? renderSetupTokenPermissionSummary(role, requirements) + : renderSetupTokenPermissionRequirements(role, requirements)); + } + + showDetailedRequirements(role: SetupTokenRole, requirements: readonly SetupTokenPermissionRequirement[]): void { console.log(renderSetupTokenPermissionRequirements(role, requirements)); } @@ -18,6 +27,18 @@ export class ConsoleSetupTokenPermissionPresenter implements SetupTokenPermissio } } +export function renderSetupTokenPermissionSummary( + role: SetupTokenRole, + requirements: readonly SetupTokenPermissionRequirement[], + maximumWidth = stdout.columns ?? 120, +): string { + const summary = summarizeSetupPermissions(requirements); + return renderBox([ + `Required now: ${summary.required.join(' · ') || 'none'}`, + `Conditional permissions: ${summary.conditionalCount}. View the full table for reasons and triggers.`, + ].join('\n'), `${roleTitle(role)} PAT permission summary`, 36, maximumWidth); +} + export function renderSetupTokenPermissionRequirements( role: SetupTokenRole, requirements: readonly SetupTokenPermissionRequirement[], From 66b9df2b2ba539f08eae7edbb1c201afaeefd0b5 Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Mon, 28 Sep 2026 16:07:44 +0200 Subject: [PATCH 13/50] codex-setup-temporary-github-auth: Clarify setup PAT choice review progress --- build/cli/index.js | 56 +++++++++++++--- docs/authentication.mdx | 6 ++ docs/how-to-use.mdx | 8 +++ ...up-configuration-credentials-and-doctor.md | 33 ++++++++++ .../temporary-setup-operator-authorization.md | 31 ++++++++- src/__tests__/cli.test.ts | 65 ++++++++++++++++++- .../policies/setup_journey_policy.ts | 3 + .../__tests__/setup_journey_use_case.test.ts | 33 ++++++++++ .../usecases/setup/setup_journey_use_case.ts | 16 ++++- .../__tests__/setup_journey_presenter.test.ts | 9 +++ src/cli/__tests__/setup_presenters.test.ts | 27 +++++++- src/cli/commands/setup.ts | 9 ++- src/cli/setup_credential_prompt_adapter.ts | 9 ++- src/cli/setup_journey_presenter.ts | 5 +- src/cli/setup_question_renderer.ts | 21 +++++- 15 files changed, 306 insertions(+), 25 deletions(-) diff --git a/build/cli/index.js b/build/cli/index.js index e93695b03..b79f5115d 100755 --- a/build/cli/index.js +++ b/build/cli/index.js @@ -47724,7 +47724,7 @@ const labels = { credentials: 'Bot PAT & credentials', apply: 'Apply', }; -function buildSetupJourneyView(repository, stage, mutationStarted, outcome) { +function buildSetupJourneyView(repository, stage, mutationStarted, outcome, choiceReviewPass = 1) { const position = exports.SETUP_JOURNEY_STAGES.indexOf(stage); return { repository: [...repository].map(character => { @@ -47738,6 +47738,7 @@ function buildSetupJourneyView(repository, stage, mutationStarted, outcome) { pending: exports.SETUP_JOURNEY_STAGES.slice(position + 1).map(item => labels[item]), ...(outcome ? { outcome } : {}), mutationStarted, + choiceReviewPass, }; } @@ -55645,6 +55646,7 @@ class SetupJourneyUseCase { this.presenter = presenter; this.stage = 'repository'; this.mutationStarted = false; + this.choiceReviewPass = 1; } advance(stage) { if (this.outcome) @@ -55657,6 +55659,16 @@ class SetupJourneyUseCase { this.stage = stage; this.present(); } + /** The only deliberate backwards transition: revisit local choices before PAT entry. */ + revisitChoices() { + if (this.stage !== 'setup-pat' || this.outcome || this.mutationStarted) { + throw new Error('Setup choices can be revisited only from pre-PAT review.'); + } + this.choiceReviewPass += 1; + this.stage = 'choices'; + this.present(); + return this.choiceReviewPass; + } markMutationStarted() { if (this.stage !== 'apply' || this.outcome) throw new Error('Setup mutation must start in the apply stage.'); @@ -55676,7 +55688,7 @@ class SetupJourneyUseCase { this.present(); } present() { - this.presenter.present((0, setup_journey_policy_1.buildSetupJourneyView)(this.repository, this.stage, this.mutationStarted, this.outcome)); + this.presenter.present((0, setup_journey_policy_1.buildSetupJourneyView)(this.repository, this.stage, this.mutationStarted, this.outcome, this.choiceReviewPass)); } } exports.SetupJourneyUseCase = SetupJourneyUseCase; @@ -65545,9 +65557,10 @@ function registerSetupCommand(program) { skipRepositoryVariables: Boolean(options.skipVariables), skipRepositorySecrets: Boolean(options.skipSecrets), }); + let choiceReviewPass = 1; while (true) { const context = { skipQuestionIds: fixedQuestionIds }; - const collector = new setup_1.SetupQuestionnaireController(terminal, new setup_question_renderer_1.ConsoleSetupQuestionRenderer('permission-intent')); + const collector = new setup_1.SetupQuestionnaireController(terminal, new setup_question_renderer_1.ConsoleSetupQuestionRenderer('permission-intent', choiceReviewPass)); const intent = await collector.collect((0, setup_questionnaire_policy_1.createSetupPermissionIntentQuestionnaire)(draft, context), context); if (intent.terminal === 'cancelled') throw new setup_credential_prompt_adapter_1.SetupTerminalCancelledError(); @@ -65568,6 +65581,8 @@ function registerSetupCommand(program) { (0, logger_1.logInfo)(`The selected local configuration needs correction before a guided link can be generated:\n${intentErrors.map(item => ` - ${item}`).join('\n')}`); } const preview = (0, setup_token_permission_policy_1.buildSetupPatIntentPermissionRequirements)(draft, ownerKind); + if (choiceReviewPass > 1) + (0, logger_1.logInfo)('Choice review complete. Returning to setup PAT permission review.'); journey?.advance('setup-pat'); (0, logger_1.logInfo)('Permission intent:'); (0, logger_1.logInfo)(` Initial tag: ${draft.createInitialTag ? 'yes' : 'no'}; issue workflows: ${draft.features.issues ? draft.issueWorkflows.enabled.join(', ') || 'none' : 'disabled'}; PR approval: ${draft.pullRequestApproval.mode}`); @@ -65587,8 +65602,10 @@ function registerSetupCommand(program) { permissionPresenter.showDetailedRequirements('setup', setupPatPermissions); break; } - if (decision === 'revise') + if (decision === 'revise') { + choiceReviewPass = journey?.revisitChoices() ?? choiceReviewPass + 1; continue; + } if ((0, setup_pat_intent_policy_1.setupPatIntentOwnerConflict)(draft, ownerKind) || intentErrors.length > 0) { throw new application_error_1.ApplicationError('configuration.invalid', 'Correct the reported setup intent or local --config/flags, then retry guided setup. No PAT was requested.'); } @@ -66514,7 +66531,14 @@ class SetupCredentialPromptAdapter { async reviewSetupPatIntent() { if (!this.terminal) return 'manual'; - return await this.readChoice('Review these intended grants before opening GitHub. Continue, revise choices, view full permission table, or enter a PAT manually?', ['continue', 'revise', 'manual', 'details']); + const choice = await this.readChoice('Review these intended grants before opening GitHub. What would you like to do?', ['continue to GitHub', 'review all setup choices again', 'view full permission table', 'enter a PAT manually']); + if (choice === 'review all setup choices again') + return 'revise'; + if (choice === 'view full permission table') + return 'details'; + if (choice === 'enter a PAT manually') + return 'manual'; + return 'continue'; } configureWorkflowPatGuide(url, resolveIdentity, requirements) { this.workflowPatGuide = url; @@ -66795,6 +66819,7 @@ class ConsoleSetupJourneyPresenter { } exports.ConsoleSetupJourneyPresenter = ConsoleSetupJourneyPresenter; function renderSetupJourney(view, maximumWidth) { + const revisitingChoices = view.current === 'Setup choices' && view.choiceReviewPass > 1; const state = view.outcome === 'complete' ? 'Complete: setup applied successfully.' : view.outcome === 'dry-run' ? 'Complete: dry run only; no changes were applied.' : view.outcome === 'partial' ? 'Partial: application started; inspect the result before retrying.' @@ -66804,9 +66829,9 @@ function renderSetupJourney(view, maximumWidth) { : 'No changes have been applied.'; return (0, setup_prompt_rendering_1.renderBox)([ `Repository: ${view.repository}`, - `Stage ${view.position}/${view.total} · ${view.current}`, + `Stage ${view.position}/${view.total} · ${view.current}${revisitingChoices ? ` · review pass ${view.choiceReviewPass}` : ''}`, `Complete: ${view.complete.join(' → ') || 'none'}`, - `Now: ${view.current}`, + `Now: ${revisitingChoices ? 'reviewing saved setup choices' : view.current}`, `Next: ${view.pending.join(' → ') || 'none'}`, state, ].join('\n'), 'Copilot setup', 36, maximumWidth); @@ -67044,12 +67069,25 @@ exports.ConsoleSetupQuestionRenderer = void 0; const setup_prompt_rendering_1 = __nccwpck_require__(83434); const setup_questionnaire_policy_1 = __nccwpck_require__(6009); class ConsoleSetupQuestionRenderer { - constructor(phase = 'full') { + constructor(phase = 'full', choiceReviewPass = 1) { this.phase = phase; + this.choiceReviewPass = choiceReviewPass; } showIntroduction() { if (this.phase === 'permission-intent') { - console.log((0, setup_prompt_rendering_1.renderBox)('First, choose the setup options that affect your temporary PAT permissions. These answers will carry into the full wizard and will not be asked again. No GitHub changes happen in this step.', 'Setup PAT permission intent')); + console.log((0, setup_prompt_rendering_1.renderBox)(this.choiceReviewPass > 1 + ? [ + `Reviewing your setup choices again (pass ${this.choiceReviewPass}).`, + 'This is the same setup run. Your answers are saved as defaults.', + 'Press Enter to keep each answer, or enter a new value.', + 'After this pass you return to the setup PAT permission review.', + 'No setup changes have been applied.', + ].join('\n') + : [ + 'First, choose the setup options that affect your temporary PAT permissions.', + 'These answers carry into the later full wizard and are not asked there again', + 'unless you choose to review them here. No GitHub changes happen in this step.', + ].join('\n'), this.choiceReviewPass > 1 ? 'Review saved setup choices' : 'Setup PAT permission intent')); return; } console.log((0, setup_prompt_rendering_1.renderBox)('This wizard configures repository workflows, GitHub Actions resources, AI agents, and operational defaults.\n\nThe setup PAT is used in memory only. Runtime credentials are collected separately after the plan is approved.', 'Copilot Setup')); diff --git a/docs/authentication.mdx b/docs/authentication.mdx index 235b9f9b4..6913ec01c 100644 --- a/docs/authentication.mdx +++ b/docs/authentication.mdx @@ -32,6 +32,12 @@ additional grants, setup stops before applying it and prints a corrected link. Update the PAT in GitHub or create a replacement, then rerun setup. Guided setup shows the account returned by GitHub and asks you to confirm it. +If you choose **Review all setup choices again** at the permission preview, +the terminal clearly marks a second pass over your saved answers. Press Enter +to keep an answer, or change it; afterward you return to the same PAT review +with permissions recalculated. No PAT link has been accepted and no setup +mutation has started merely because you revisited these choices. + After the plan, the bot link uses the selected workflow permissions. Enter the expected bot login first: setup resolves its GitHub numeric ID, then checks the PAT's own `/user` identity against that ID before any Secret write. A manual or diff --git a/docs/how-to-use.mdx b/docs/how-to-use.mdx index 332af6df1..fd06aa5c8 100644 --- a/docs/how-to-use.mdx +++ b/docs/how-to-use.mdx @@ -56,6 +56,14 @@ review to see reasons and conditional grants without restarting the questions; manual PAT entry shows that table directly. Supplied-token and unattended paths retain the full table. This display never includes token values. +At the setup PAT permission review, **Review all setup choices again** starts +another pass over the same in-memory answers. The terminal marks it as a review +pass, temporarily returns to the Setup choices phase, and explains that Enter +keeps each previous answer. When the pass ends, you return to the setup PAT +permission summary with any changed grants recalculated. This is not a new +setup run; no repository changes occur during these passes. The later full +wizard still reuses these answers instead of asking them again. + Open each link in the appropriate GitHub account, complete GitHub's sign-in/2FA, change **All repositories** to **Only select repositories**, and **select only the intended repository** diff --git a/specs/setup-configuration-credentials-and-doctor.md b/specs/setup-configuration-credentials-and-doctor.md index 7310a8b2e..8286e2877 100644 --- a/specs/setup-configuration-credentials-and-doctor.md +++ b/specs/setup-configuration-credentials-and-doctor.md @@ -189,6 +189,12 @@ by policy when irrelevant; there is no legacy state alias or back-navigation mode. After questionnaire completion, credential validation and provisioning remain separate application flows. +The pre-PAT intent review may start a **new questionnaire pass** over the +current in-memory draft. Each pass remains forward-only; this explicit review +loop is not an implicit reset or back-navigation inside a questionnaire. The +journey presentation reopens `Setup choices` only before PAT entry and returns +to `Setup PAT` when that pass finishes. + Cancellation before confirmation writes nothing. Partial remote provisioning retains successful facts and reports remaining work; retries MUST preserve valid existing resources and avoid duplicate shadowing. @@ -275,6 +281,33 @@ Do not persist phase state or print credentials. Detailed permission tables remain available on explicit request and for manual/unattended paths; the interactive guided review defaults to an exact compact grant summary. +If the operator chooses to review intent again, the journey MUST visibly +reopen `Setup choices`, label the review pass, and mark `Setup PAT` pending +until the repeated questions finish. This is the only backwards journey +transition and is allowed only before PAT entry and before mutation. The +terminal MUST explain that existing answers remain as defaults, Enter keeps +them, the flow returns to PAT review afterward, and no setup changes have +been applied. It MUST NOT reuse the first-pass introduction. On completion, +show an explicit return to `Setup PAT` and recalculate the permission preview. +The later full wizard still does not re-ask the pre-PAT answers. No fixed +question counter or percentage is displayed because the set is conditional. + +```text +Copilot setup · owner/repo +Stage 2/6 · Setup choices · review pass 2 +Complete: Repository +Now: reviewing saved setup choices +Next: Setup PAT → Plan → Bot PAT & credentials → Apply +No changes have been applied. +``` + +Text equivalent: the operator deliberately returned to a second pass over +saved choices, will reach PAT review afterward, and has not begun mutation. +This amendment adds at least **eight distinct cases** beyond the original +journey budget: three transition/guard cases, two introduction and narrow +no-color presentation cases, and three CLI return/cancellation/permission +preview integration cases. The existing coverage thresholds remain. + The presentation introduces no new flags or persisted configuration. It has no effect on GitHub Actions, issues, PRs, comments, or checks. Rollback removes the stage renderer and restores the existing table-first presentation without diff --git a/specs/temporary-setup-operator-authorization.md b/specs/temporary-setup-operator-authorization.md index 826f3b507..ca3c50943 100644 --- a/specs/temporary-setup-operator-authorization.md +++ b/specs/temporary-setup-operator-authorization.md @@ -315,6 +315,14 @@ answers cannot modify a reviewed draft. A changed plan invalidates the old link. A crash cannot guarantee GitHub deletion; restart and recovery instructions must not imply otherwise. +Choosing `Review all setup choices again` from `intent-review` starts an +explicit second (or later) pass over the existing draft, not a fresh setup +run. The user is told this before the first repeated question. Previously +answered values remain defaults; local flags/config still fix their original +fields. This loop never creates a PAT, inspects GitHub, or applies setup. Its +journey stage reopens `Setup choices`, then returns to `Setup PAT` with a newly +computed grant preview. Cancellation ends the run with no setup mutation. + ## 7. User-facing configuration | Input | Type | Recommended default | Allowed values | Scope/persistence | @@ -406,8 +414,9 @@ The [setup journey presentation contract](./setup-configuration-credentials-and- applies across both PAT roles. Before interactive guided intent, show the bootstrap grants compactly, not the complete conditional table. After choices, show every currently required grant and a count of conditional/remote-unknown -grants. The review choices are `Continue`, `Revise choices`, `View full permission -table`, and `Enter a PAT manually`. Selecting detail prints the same policy +grants. The review choices are `Continue to GitHub`, `Review all setup choices +again`, `View full permission table`, and `Enter a PAT manually`, in precisely +that numbered order. Selecting detail prints the same policy requirements with reasons and returns to review **without rerunning questions**. Manual entry shows the full bootstrap table immediately; supplied-token and non-interactive paths keep the existing table and audit. The raw GitHub URL @@ -431,6 +440,24 @@ Add five operator-specific cases to the journey budget: detail returns to review, manual shows full table, revision changes the summary, cancellation preserves the no-change state, and narrow no-color output remains readable. +When choice 2 is selected, the terminal MUST show a transition message before +repeating any question: + +```text +Reviewing your setup choices again (pass 2). +This is the same setup run. Your answers are saved as defaults; press Enter +to keep one or enter a new value. After this pass you return to the setup PAT +permission review. No setup changes have been applied. +Stage 2/6 · Setup choices · review pass 2 +``` + +Text equivalent: this is a deliberate second pass over saved answers, with +no repository mutation, followed by a return to the PAT grant review. After +the pass, print `Choice review complete. Returning to setup PAT permission +review.` before the recalculated preview. No new command, persisted setting, +browser action, or account state is introduced. The review counter is one-run +presentation state; it cannot be used as authorization or grant evidence. + The current CLI is English; this example is illustrative and follows its existing text-first styling. Preserve one primary action per state. diff --git a/src/__tests__/cli.test.ts b/src/__tests__/cli.test.ts index efc8afca1..5b1bf8e61 100644 --- a/src/__tests__/cli.test.ts +++ b/src/__tests__/cli.test.ts @@ -521,7 +521,7 @@ describe('CLI', () => { it('allows the operator to choose manual entry after reviewing local intent', async () => { const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); - const input = guidedTerminal(prompt => prompt.includes('Review these intended grants') ? '3' + const input = guidedTerminal(prompt => prompt.includes('Review these intended grants') ? '4' : prompt.includes('repository owner an organization') ? '2' : ''); const createTerminal = jest.spyOn(terminalDriver, 'createInteractiveTerminalDriver') .mockReturnValue(input as unknown as ReturnType); @@ -568,17 +568,76 @@ describe('CLI', () => { expect(mockTokenPermissionInspect.mock.calls[0][0].requirements).toEqual(expect.arrayContaining([ expect.objectContaining({ permission: 'Contents', level: 'read' }), ])); - expect(consoleLogSpy.mock.calls.flat().join('\n')).toContain('contents=read'); + const output = consoleLogSpy.mock.calls.flat().join('\n'); + expect(output).toContain('contents=read'); + expect(output).toContain('Stage 2/6 · Setup choices · review pass 2'); + expect(output).toContain('This is the same setup run. Your answers are saved as defaults'); + const { logInfo } = require('../utils/logger'); + expect(logInfo).toHaveBeenCalledWith('Choice review complete. Returning to setup PAT permission review.'); + const firstPatReview = output.indexOf('Stage 3/6 · Setup PAT'); + const choiceReview = output.indexOf('Stage 2/6 · Setup choices · review pass 2'); + const returnedPatReview = output.indexOf('Stage 3/6 · Setup PAT', choiceReview + 1); + expect(firstPatReview).toBeLessThan(choiceReview); + expect(choiceReview).toBeLessThan(returnedPatReview); expect(process.exitCode).toBe(1); } finally { createTerminal.mockRestore(); } }); + it('keeps fixed flag choices out of every review pass', async () => { + const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); + let reviews = 0; + const input = guidedTerminal(prompt => { + if (prompt.includes('repository owner an organization')) return '2'; + if (prompt.includes('Review these intended grants')) return ++reviews === 1 ? '2' : '1'; + return ''; + }); + const createTerminal = jest.spyOn(terminalDriver, 'createInteractiveTerminalDriver') + .mockReturnValue(input as unknown as ReturnType); + mockTokenPermissionInspect.mockResolvedValueOnce({ ...acceptedSetupPatReport(), ready: false }); + try { + await program.parseAsync(['node', 'cli', 'setup', '--skip-secrets', '--pr-approval-mode', 'off']); + expect(reviews).toBe(2); + expect(input.readText.mock.calls.some(([prompt]) => String(prompt).includes('Validate and provision required GitHub Actions Secrets?'))).toBe(false); + expect(input.readText.mock.calls.some(([prompt]) => String(prompt).includes('Bot PR approval mode'))).toBe(false); + expect(consoleLogSpy.mock.calls.flat().join('\n')).toContain('review pass 2'); + } finally { createTerminal.mockRestore(); } + }); + + it('cancels safely during a repeated choice pass without requesting a PAT', async () => { + const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); + let revisiting = false; + const terminal = { + isInteractive: () => true, + readText: jest.fn(async (prompt: string) => { + if (prompt.includes('Review these intended grants')) { + revisiting = true; + return { kind: 'value' as const, value: '2' }; + } + if (revisiting && prompt.includes('Issue automation:')) return { kind: 'end-of-input' as const }; + return { kind: 'value' as const, value: prompt.includes('repository owner an organization') ? '2' : '' }; + }), + readSecret: jest.fn(), + close: jest.fn(), + }; + const createTerminal = jest.spyOn(terminalDriver, 'createInteractiveTerminalDriver') + .mockReturnValue(terminal as unknown as ReturnType); + try { + await program.parseAsync(['node', 'cli', 'setup']); + const output = consoleLogSpy.mock.calls.flat().join('\n'); + expect(output).toContain('Stage 2/6 · Setup choices · review pass 2'); + expect(output).toContain('Cancelled: setup stopped.'); + expect(terminal.readSecret).not.toHaveBeenCalled(); + expect(runLocalAction).not.toHaveBeenCalled(); + expect(process.exitCode).toBe(130); + } finally { createTerminal.mockRestore(); } + }); + it('shows setup permission details on demand without repeating the intent questionnaire', async () => { const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); let reviews = 0; const input = guidedTerminal(prompt => { if (prompt.includes('repository owner an organization')) return '2'; - if (prompt.includes('Review these intended grants')) return ++reviews === 1 ? '4' : '1'; + if (prompt.includes('Review these intended grants')) return ++reviews === 1 ? '3' : '1'; return ''; }); const createTerminal = jest.spyOn(terminalDriver, 'createInteractiveTerminalDriver') diff --git a/src/application/policies/setup_journey_policy.ts b/src/application/policies/setup_journey_policy.ts index 8b463e468..152e66c58 100644 --- a/src/application/policies/setup_journey_policy.ts +++ b/src/application/policies/setup_journey_policy.ts @@ -23,6 +23,7 @@ export interface SetupJourneyView { readonly pending: readonly string[]; readonly outcome?: SetupJourneyOutcome; readonly mutationStarted: boolean; + readonly choiceReviewPass: number; } export function buildSetupJourneyView( @@ -30,6 +31,7 @@ export function buildSetupJourneyView( stage: SetupJourneyStage, mutationStarted: boolean, outcome?: SetupJourneyOutcome, + choiceReviewPass = 1, ): SetupJourneyView { const position = SETUP_JOURNEY_STAGES.indexOf(stage); return { @@ -44,5 +46,6 @@ export function buildSetupJourneyView( pending: SETUP_JOURNEY_STAGES.slice(position + 1).map(item => labels[item]), ...(outcome ? { outcome } : {}), mutationStarted, + choiceReviewPass, }; } diff --git a/src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts b/src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts index 9efd9ed2a..2bd141239 100644 --- a/src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts +++ b/src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts @@ -60,4 +60,37 @@ describe('setup journey', () => { expect(present).toHaveBeenCalledTimes(1); expect(present.mock.lastCall?.[0]).toMatchObject({ current: 'Setup choices', complete: ['Repository'] }); }); + + it('reopens only pre-PAT choices and returns to PAT review without resetting the run', () => { + const present = jest.fn(); + const journey = new SetupJourneyUseCase('owner/repo', { present }); + journey.advance('choices'); + journey.advance('setup-pat'); + expect(journey.revisitChoices()).toBe(2); + expect(present.mock.lastCall?.[0]).toMatchObject({ + current: 'Setup choices', choiceReviewPass: 2, complete: ['Repository'], + pending: ['Setup PAT', 'Plan', 'Bot PAT & credentials', 'Apply'], mutationStarted: false, + }); + journey.advance('setup-pat'); + expect(present.mock.lastCall?.[0]).toMatchObject({ current: 'Setup PAT', choiceReviewPass: 2 }); + expect(journey.revisitChoices()).toBe(3); + }); + + it('rejects a review loop outside pre-PAT review or after cancellation', () => { + const journey = new SetupJourneyUseCase('owner/repo', { present: jest.fn() }); + expect(() => journey.revisitChoices()).toThrow('pre-PAT'); + journey.advance('setup-pat'); + journey.finish('cancelled'); + expect(() => journey.revisitChoices()).toThrow('pre-PAT'); + const later = new SetupJourneyUseCase('owner/repo', { present: jest.fn() }); + later.advance('plan'); + expect(() => later.revisitChoices()).toThrow('pre-PAT'); + }); + + it('cannot reopen choices once application has begun', () => { + const journey = new SetupJourneyUseCase('owner/repo', { present: jest.fn() }); + journey.advance('apply'); + journey.markMutationStarted(); + expect(() => journey.revisitChoices()).toThrow('pre-PAT'); + }); }); diff --git a/src/application/usecases/setup/setup_journey_use_case.ts b/src/application/usecases/setup/setup_journey_use_case.ts index 766917595..3ea1e28a4 100644 --- a/src/application/usecases/setup/setup_journey_use_case.ts +++ b/src/application/usecases/setup/setup_journey_use_case.ts @@ -10,6 +10,7 @@ export class SetupJourneyUseCase { private stage: SetupJourneyStage = 'repository'; private outcome?: SetupJourneyOutcome; private mutationStarted = false; + private choiceReviewPass = 1; constructor(private readonly repository: string, private readonly presenter: SetupJourneyPresenterPort) {} @@ -22,6 +23,17 @@ export class SetupJourneyUseCase { this.present(); } + /** The only deliberate backwards transition: revisit local choices before PAT entry. */ + revisitChoices(): number { + if (this.stage !== 'setup-pat' || this.outcome || this.mutationStarted) { + throw new Error('Setup choices can be revisited only from pre-PAT review.'); + } + this.choiceReviewPass += 1; + this.stage = 'choices'; + this.present(); + return this.choiceReviewPass; + } + markMutationStarted(): void { if (this.stage !== 'apply' || this.outcome) throw new Error('Setup mutation must start in the apply stage.'); this.mutationStarted = true; @@ -41,6 +53,8 @@ export class SetupJourneyUseCase { } private present(): void { - this.presenter.present(buildSetupJourneyView(this.repository, this.stage, this.mutationStarted, this.outcome)); + this.presenter.present(buildSetupJourneyView( + this.repository, this.stage, this.mutationStarted, this.outcome, this.choiceReviewPass, + )); } } diff --git a/src/cli/__tests__/setup_journey_presenter.test.ts b/src/cli/__tests__/setup_journey_presenter.test.ts index cb973a2b8..3fa384af0 100644 --- a/src/cli/__tests__/setup_journey_presenter.test.ts +++ b/src/cli/__tests__/setup_journey_presenter.test.ts @@ -23,6 +23,15 @@ describe('setup journey presenter', () => { expect(output).toContain('dry run only; no changes were applied'); }); + it('labels the second choice pass and stays readable at narrow width without color', () => { + const output = renderSetupJourney(buildSetupJourneyView('owner/repo', 'choices', false, undefined, 2), 48); + expect(output).toContain('Stage 2/6 · Setup choices · review pass 2'); + expect(output).toContain('Now: reviewing saved setup choices'); + expect(output).toContain('Next: Setup PAT'); + expect(output).toContain('No changes have been applied.'); + expect(output.split('\n').every(line => line.length <= 50)).toBe(true); + }); + it('renders the active mutation state and the first-stage pending list', () => { const applying = renderSetupJourney(buildSetupJourneyView('owner/repo', 'apply', true), 80); expect(applying).toContain('changes may already exist'); diff --git a/src/cli/__tests__/setup_presenters.test.ts b/src/cli/__tests__/setup_presenters.test.ts index 459728c33..72d046d11 100644 --- a/src/cli/__tests__/setup_presenters.test.ts +++ b/src/cli/__tests__/setup_presenters.test.ts @@ -43,6 +43,22 @@ describe('setup presenters and prompt-specific adapters', () => { log.mockRestore(); }); + it('distinguishes the first permission-intent pass from a deliberate second pass', () => { + const log = jest.spyOn(console, 'log').mockImplementation(); + try { + new ConsoleSetupQuestionRenderer('permission-intent').showIntroduction(); + expect(log.mock.calls.flat().join('\n')).toContain('later full wizard'); + log.mockClear(); + new ConsoleSetupQuestionRenderer('permission-intent', 2).showIntroduction(); + const output = log.mock.calls.flat().join('\n'); + expect(output).toContain('Reviewing your setup choices again (pass 2)'); + expect(output).toContain('same setup run'); + expect(output).toContain('Press Enter to keep each answer'); + expect(output).toContain('return to the setup PAT permission review'); + expect(output).not.toContain('First, choose'); + } finally { log.mockRestore(); } + }); + it('renders every doctor presentation label in the resolved repository locale', () => { const catalog = resolveStaticSetupDoctorCatalog('es-ES'); const rendered = renderDoctorReport(buildDoctorReport([ @@ -286,7 +302,7 @@ describe('setup presenters and prompt-specific adapters', () => { const input = terminal([ { kind: 'value', value: '1' }, { kind: 'value', value: '2' }, - { kind: 'value', value: '3' }, + { kind: 'value', value: '4' }, { kind: 'value', value: 'manual-token' }, ]); const adapter = new SetupCredentialPromptAdapter(input, {}); @@ -303,11 +319,18 @@ describe('setup presenters and prompt-specific adapters', () => { }); it('offers the full setup permission table as a review action', async () => { - const input = terminal([{ kind: 'value', value: '4' }]); + const input = terminal([{ kind: 'value', value: '3' }]); await expect(new SetupCredentialPromptAdapter(input, {}).reviewSetupPatIntent()).resolves.toBe('details'); expect(input.readText).toHaveBeenCalledWith(expect.stringContaining('view full permission table')); }); + it('lists PAT review actions in the same order as the numbered menu', async () => { + const input = terminal([{ kind: 'value', value: '3' }]); + await new SetupCredentialPromptAdapter(input, {}).reviewSetupPatIntent(); + const prompt = String(input.readText.mock.calls[0][0]); + expect(prompt).toMatch(/1\) continue to GitHub[\s\S]*2\) review all setup choices again[\s\S]*3\) view full permission table[\s\S]*4\) enter a PAT manually/u); + }); + it('rejects an invalid authenticated setup account without prompting', async () => { const input = terminal([{ kind: 'value', value: '1' }]); const adapter = new SetupCredentialPromptAdapter(input, {}); diff --git a/src/cli/commands/setup.ts b/src/cli/commands/setup.ts index 9d70b8f7e..6077e9c29 100644 --- a/src/cli/commands/setup.ts +++ b/src/cli/commands/setup.ts @@ -129,9 +129,10 @@ export function registerSetupCommand(program: Command): void { skipRepositoryVariables: Boolean(options.skipVariables), skipRepositorySecrets: Boolean(options.skipSecrets), }); + let choiceReviewPass = 1; while (true) { const context = { skipQuestionIds: fixedQuestionIds }; - const collector = new SetupQuestionnaireController(terminal!, new ConsoleSetupQuestionRenderer('permission-intent')); + const collector = new SetupQuestionnaireController(terminal!, new ConsoleSetupQuestionRenderer('permission-intent', choiceReviewPass)); const intent = await collector.collect(createSetupPermissionIntentQuestionnaire(draft, context), context); if (intent.terminal === 'cancelled') throw new SetupTerminalCancelledError(); draft = intent.draft; @@ -151,6 +152,7 @@ export function registerSetupCommand(program: Command): void { logInfo(`The selected local configuration needs correction before a guided link can be generated:\n${intentErrors.map(item => ` - ${item}`).join('\n')}`); } const preview = buildSetupPatIntentPermissionRequirements(draft, ownerKind); + if (choiceReviewPass > 1) logInfo('Choice review complete. Returning to setup PAT permission review.'); journey?.advance('setup-pat'); logInfo('Permission intent:'); logInfo(` Initial tag: ${draft.createInitialTag ? 'yes' : 'no'}; issue workflows: ${draft.features.issues ? draft.issueWorkflows.enabled.join(', ') || 'none' : 'disabled'}; PR approval: ${draft.pullRequestApproval.mode}`); @@ -168,7 +170,10 @@ export function registerSetupCommand(program: Command): void { permissionPresenter.showDetailedRequirements('setup', setupPatPermissions); break; } - if (decision === 'revise') continue; + if (decision === 'revise') { + choiceReviewPass = journey?.revisitChoices() ?? choiceReviewPass + 1; + continue; + } if (setupPatIntentOwnerConflict(draft, ownerKind) || intentErrors.length > 0) { throw new ApplicationError('configuration.invalid', 'Correct the reported setup intent or local --config/flags, then retry guided setup. No PAT was requested.'); } diff --git a/src/cli/setup_credential_prompt_adapter.ts b/src/cli/setup_credential_prompt_adapter.ts index b84a9565a..fe10d47b9 100644 --- a/src/cli/setup_credential_prompt_adapter.ts +++ b/src/cli/setup_credential_prompt_adapter.ts @@ -50,7 +50,14 @@ export class SetupCredentialPromptAdapter implements SetupCredentialPromptPort { } async reviewSetupPatIntent(): Promise<'continue' | 'revise' | 'manual' | 'details'> { if (!this.terminal) return 'manual'; - return await this.readChoice('Review these intended grants before opening GitHub. Continue, revise choices, view full permission table, or enter a PAT manually?', ['continue', 'revise', 'manual', 'details']) as 'continue' | 'revise' | 'manual' | 'details'; + const choice = await this.readChoice( + 'Review these intended grants before opening GitHub. What would you like to do?', + ['continue to GitHub', 'review all setup choices again', 'view full permission table', 'enter a PAT manually'], + ); + if (choice === 'review all setup choices again') return 'revise'; + if (choice === 'view full permission table') return 'details'; + if (choice === 'enter a PAT manually') return 'manual'; + return 'continue'; } configureWorkflowPatGuide(url: string, resolveIdentity: (login: string) => Promise, requirements?: readonly SetupTokenPermissionRequirement[]): void { this.workflowPatGuide = url; diff --git a/src/cli/setup_journey_presenter.ts b/src/cli/setup_journey_presenter.ts index ee83f34a0..c4d3605c8 100644 --- a/src/cli/setup_journey_presenter.ts +++ b/src/cli/setup_journey_presenter.ts @@ -9,6 +9,7 @@ export class ConsoleSetupJourneyPresenter implements SetupJourneyPresenterPort { } export function renderSetupJourney(view: SetupJourneyView, maximumWidth?: number): string { + const revisitingChoices = view.current === 'Setup choices' && view.choiceReviewPass > 1; const state = view.outcome === 'complete' ? 'Complete: setup applied successfully.' : view.outcome === 'dry-run' ? 'Complete: dry run only; no changes were applied.' : view.outcome === 'partial' ? 'Partial: application started; inspect the result before retrying.' @@ -18,9 +19,9 @@ export function renderSetupJourney(view: SetupJourneyView, maximumWidth?: number : 'No changes have been applied.'; return renderBox([ `Repository: ${view.repository}`, - `Stage ${view.position}/${view.total} · ${view.current}`, + `Stage ${view.position}/${view.total} · ${view.current}${revisitingChoices ? ` · review pass ${view.choiceReviewPass}` : ''}`, `Complete: ${view.complete.join(' → ') || 'none'}`, - `Now: ${view.current}`, + `Now: ${revisitingChoices ? 'reviewing saved setup choices' : view.current}`, `Next: ${view.pending.join(' → ') || 'none'}`, state, ].join('\n'), 'Copilot setup', 36, maximumWidth); diff --git a/src/cli/setup_question_renderer.ts b/src/cli/setup_question_renderer.ts index f9a97a2b5..ba2a01f60 100644 --- a/src/cli/setup_question_renderer.ts +++ b/src/cli/setup_question_renderer.ts @@ -4,13 +4,28 @@ import { color, renderBox } from './setup_prompt_rendering'; import { setupQuestionnaireStateLabel } from '../application/policies/setup_questionnaire_policy'; export class ConsoleSetupQuestionRenderer implements SetupQuestionRenderer { - constructor(private readonly phase: 'full' | 'permission-intent' = 'full') {} + constructor( + private readonly phase: 'full' | 'permission-intent' = 'full', + private readonly choiceReviewPass = 1, + ) {} showIntroduction(): void { if (this.phase === 'permission-intent') { console.log(renderBox( - 'First, choose the setup options that affect your temporary PAT permissions. These answers will carry into the full wizard and will not be asked again. No GitHub changes happen in this step.', - 'Setup PAT permission intent', + this.choiceReviewPass > 1 + ? [ + `Reviewing your setup choices again (pass ${this.choiceReviewPass}).`, + 'This is the same setup run. Your answers are saved as defaults.', + 'Press Enter to keep each answer, or enter a new value.', + 'After this pass you return to the setup PAT permission review.', + 'No setup changes have been applied.', + ].join('\n') + : [ + 'First, choose the setup options that affect your temporary PAT permissions.', + 'These answers carry into the later full wizard and are not asked there again', + 'unless you choose to review them here. No GitHub changes happen in this step.', + ].join('\n'), + this.choiceReviewPass > 1 ? 'Review saved setup choices' : 'Setup PAT permission intent', )); return; } From 2404561d0948650314718e5876ece1db81c450e5 Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Mon, 28 Sep 2026 20:01:46 +0200 Subject: [PATCH 14/50] codex-setup-temporary-github-auth: add local web assistant with isolated authorization gates --- .gitattributes | 2 + README.md | 10 + build/cli/index.js | 1726 ++++++++++++++--- build/github_action/index.js | 39 +- build/web/assets/index-DBy8dnu-.js | 2 + build/web/assets/index-lEwj5VdR.css | 1 + build/web/index.html | 14 + docs/authentication.mdx | 18 + docs/configuration-checklist.mdx | 5 +- docs/configuration.mdx | 11 +- docs/dependency-rules.md | 8 + docs/development/architecture.mdx | 32 + docs/how-to-use.mdx | 29 + .../operations/provisioning.mdx | 6 + .../operations/troubleshooting.mdx | 10 + package.json | 11 +- pnpm-lock.yaml | 593 ++++++ scripts/render-web-setup-component.cjs | 26 + scripts/smoke-test-npm-package.cjs | 12 +- scripts/validate-build.cjs | 2 +- scripts/validate-npm-package.cjs | 13 + specs/CATALOG.md | 16 +- specs/catalog.json | 92 + specs/guided-bot-pat-onboarding.md | 4 + specs/local-web-setup-assistant.md | 936 +++++++++ ...up-configuration-credentials-and-doctor.md | 4 + .../temporary-setup-operator-authorization.md | 4 + src/__tests__/cli.test.ts | 196 +- src/__tests__/cli_context_root.test.ts | 31 + src/application/contracts/web_setup_view.ts | 29 + .../setup_interaction_cancelled_error.ts | 7 + .../setup_questionnaire_policy.test.ts | 6 + .../setup_remote_facts_policy.test.ts | 37 + .../setup_token_permission_policy.test.ts | 14 + .../policies/merge_setup_overrides_policy.ts | 30 + .../policies/setup_configuration_plan.ts | 24 +- .../policies/setup_questionnaire_policy.ts | 1 + .../policies/setup_remote_facts_policy.ts | 23 + .../policies/setup_token_permission_policy.ts | 13 + ...udit_configured_setup_pat_use_case.test.ts | 111 ++ .../prepare_setup_pat_intent_use_case.test.ts | 128 ++ ...erify_setup_pat_bootstrap_use_case.test.ts | 72 + .../verify_web_setup_apply_use_case.test.ts | 147 ++ .../audit_configured_setup_pat_use_case.ts | 71 + .../prepare_setup_pat_intent_use_case.ts | 122 ++ .../verify_setup_pat_bootstrap_use_case.ts | 48 + .../setup/verify_web_setup_apply_use_case.ts | 84 + .../__tests__/web_setup_boundaries.test.ts | 113 ++ .../__tests__/setup_apply_snapshot.test.ts | 68 + .../__tests__/setup_command_options.test.ts | 98 + src/cli/__tests__/setup_session_guard.test.ts | 61 + src/cli/__tests__/web_setup_adapters.test.ts | 366 ++++ src/cli/__tests__/web_setup_bridge.test.ts | 101 + .../__tests__/web_setup_browser_open.test.ts | 29 + .../web_setup_browser_session.test.ts | 72 + .../__tests__/web_setup_components.test.ts | 137 ++ src/cli/__tests__/web_setup_palette.test.ts | 49 + src/cli/__tests__/web_setup_server.test.ts | 304 +++ .../__tests__/web_setup_ui_helpers.test.ts | 49 + src/cli/commands/setup.ts | 497 ++--- src/cli/setup_apply_snapshot.ts | 42 + src/cli/setup_command_options.ts | 127 ++ src/cli/setup_credential_prompt_adapter.ts | 9 +- src/cli/setup_session_guard.ts | 49 + src/cli/web_setup_adapters.ts | 171 ++ src/cli/web_setup_bridge.ts | 118 ++ src/cli/web_setup_server.ts | 207 ++ src/cli_context.ts | 9 +- ...token_permissions_composition_root.test.ts | 9 + .../setup_credentials_composition_root.ts | 5 +- .../specification_catalog_validator.cjs | 4 +- .../validate_specification_catalog.test.ts | 10 + web/index.html | 13 + web/src/App.svelte | 67 + web/src/components/ActionButton.svelte | 11 + web/src/components/ChoicePrompt.svelte | 9 + web/src/components/ContextPanel.svelte | 16 + web/src/components/CredentialPrompt.svelte | 23 + web/src/components/PlanPrompt.svelte | 19 + web/src/components/PromptCard.svelte | 26 + web/src/components/QuestionPrompt.svelte | 24 + web/src/components/ResultPanel.svelte | 18 + web/src/components/SetupHeader.svelte | 9 + web/src/components/SetupIntro.svelte | 13 + web/src/components/SetupSidebar.svelte | 19 + web/src/components/StatusBanner.svelte | 17 + web/src/components/ThemeSwitch.svelte | 11 + web/src/components/WaitingPanel.svelte | 1 + web/src/lib/githubLink.ts | 10 + web/src/lib/questionAnswer.ts | 25 + web/src/main.ts | 5 + web/src/session/setupSession.ts | 117 ++ web/src/style.css | 6 + web/src/styles/controls.css | 28 + web/src/styles/feedback.css | 18 + web/src/styles/foundation.css | 15 + web/src/styles/layout.css | 50 + web/src/styles/responsive.css | 3 + web/src/styles/tokens.css | 27 + web/tsconfig.json | 15 + web/vite.config.mts | 9 + 101 files changed, 7480 insertions(+), 668 deletions(-) create mode 100644 .gitattributes create mode 100644 build/web/assets/index-DBy8dnu-.js create mode 100644 build/web/assets/index-lEwj5VdR.css create mode 100644 build/web/index.html create mode 100644 scripts/render-web-setup-component.cjs create mode 100644 specs/local-web-setup-assistant.md create mode 100644 src/__tests__/cli_context_root.test.ts create mode 100644 src/application/contracts/web_setup_view.ts create mode 100644 src/application/errors/setup_interaction_cancelled_error.ts create mode 100644 src/application/policies/__tests__/setup_remote_facts_policy.test.ts create mode 100644 src/application/policies/merge_setup_overrides_policy.ts create mode 100644 src/application/policies/setup_remote_facts_policy.ts create mode 100644 src/application/usecases/setup/__tests__/audit_configured_setup_pat_use_case.test.ts create mode 100644 src/application/usecases/setup/__tests__/prepare_setup_pat_intent_use_case.test.ts create mode 100644 src/application/usecases/setup/__tests__/verify_setup_pat_bootstrap_use_case.test.ts create mode 100644 src/application/usecases/setup/__tests__/verify_web_setup_apply_use_case.test.ts create mode 100644 src/application/usecases/setup/audit_configured_setup_pat_use_case.ts create mode 100644 src/application/usecases/setup/prepare_setup_pat_intent_use_case.ts create mode 100644 src/application/usecases/setup/verify_setup_pat_bootstrap_use_case.ts create mode 100644 src/application/usecases/setup/verify_web_setup_apply_use_case.ts create mode 100644 src/architecture/__tests__/web_setup_boundaries.test.ts create mode 100644 src/cli/__tests__/setup_apply_snapshot.test.ts create mode 100644 src/cli/__tests__/setup_command_options.test.ts create mode 100644 src/cli/__tests__/setup_session_guard.test.ts create mode 100644 src/cli/__tests__/web_setup_adapters.test.ts create mode 100644 src/cli/__tests__/web_setup_bridge.test.ts create mode 100644 src/cli/__tests__/web_setup_browser_open.test.ts create mode 100644 src/cli/__tests__/web_setup_browser_session.test.ts create mode 100644 src/cli/__tests__/web_setup_components.test.ts create mode 100644 src/cli/__tests__/web_setup_palette.test.ts create mode 100644 src/cli/__tests__/web_setup_server.test.ts create mode 100644 src/cli/__tests__/web_setup_ui_helpers.test.ts create mode 100644 src/cli/setup_apply_snapshot.ts create mode 100644 src/cli/setup_command_options.ts create mode 100644 src/cli/setup_session_guard.ts create mode 100644 src/cli/web_setup_adapters.ts create mode 100644 src/cli/web_setup_bridge.ts create mode 100644 src/cli/web_setup_server.ts create mode 100644 web/index.html create mode 100644 web/src/App.svelte create mode 100644 web/src/components/ActionButton.svelte create mode 100644 web/src/components/ChoicePrompt.svelte create mode 100644 web/src/components/ContextPanel.svelte create mode 100644 web/src/components/CredentialPrompt.svelte create mode 100644 web/src/components/PlanPrompt.svelte create mode 100644 web/src/components/PromptCard.svelte create mode 100644 web/src/components/QuestionPrompt.svelte create mode 100644 web/src/components/ResultPanel.svelte create mode 100644 web/src/components/SetupHeader.svelte create mode 100644 web/src/components/SetupIntro.svelte create mode 100644 web/src/components/SetupSidebar.svelte create mode 100644 web/src/components/StatusBanner.svelte create mode 100644 web/src/components/ThemeSwitch.svelte create mode 100644 web/src/components/WaitingPanel.svelte create mode 100644 web/src/lib/githubLink.ts create mode 100644 web/src/lib/questionAnswer.ts create mode 100644 web/src/main.ts create mode 100644 web/src/session/setupSession.ts create mode 100644 web/src/style.css create mode 100644 web/src/styles/controls.css create mode 100644 web/src/styles/feedback.css create mode 100644 web/src/styles/foundation.css create mode 100644 web/src/styles/layout.css create mode 100644 web/src/styles/responsive.css create mode 100644 web/src/styles/tokens.css create mode 100644 web/tsconfig.json create mode 100644 web/vite.config.mts diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..46e0da3dd --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Vite bundles are generated, minified artifacts; inspect their Svelte sources instead. +build/web/assets/*.js -diff -whitespace diff --git a/README.md b/README.md index 7a6328c97..6c4964c10 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,8 @@ pnpm add --global @vypdev/copilot copilot --version cd /path/to/your/repository copilot setup +# Optional local visual assistant, in the same repository: +copilot setup --web ``` `@vypdev/copilot` contains both the `copilot` CLI and the compiled GitHub Action. @@ -68,6 +70,14 @@ Use `copilot setup --dry-run` to inspect the plan before making local or remote changes. See the complete [How to use](https://docs.page/vypdev/copilot/how-to-use) guide and [Authentication](https://docs.page/vypdev/copilot/authentication). +`--web` opens an ephemeral, loopback-only setup page with a six-stage progress +rail, plan review, separate masked inputs for the two PAT roles, and a +System/Light/Dark theme control. If the browser does not open, use the local +URL printed in the terminal. The page does not create PATs: GitHub owns the +form, account switch, 2FA, and token issuance. You must explicitly approve +the plan and Apply; `--web` cannot be combined with unattended approval or +secret-bearing command-line flags. The terminal wizard remains the default. + ### Manual workflow integration (advanced) You can integrate the Action manually when the CLI setup flow is not suitable: diff --git a/build/cli/index.js b/build/cli/index.js index b79f5115d..ddbe2a627 100755 --- a/build/cli/index.js +++ b/build/cli/index.js @@ -39178,6 +39178,25 @@ function runAtApplicationErrorBoundary(operation) { } +/***/ }), + +/***/ 38313: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.SetupInteractionCancelledError = void 0; +/** Shared cancellation signal for terminal and browser setup presenters. */ +class SetupInteractionCancelledError extends Error { + constructor() { + super('Setup input was cancelled.'); + this.name = 'SetupInteractionCancelledError'; + } +} +exports.SetupInteractionCancelledError = SetupInteractionCancelledError; + + /***/ }), /***/ 79966: @@ -44266,6 +44285,42 @@ function boundedMergeQueueDiagnostic(value) { } +/***/ }), + +/***/ 39267: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.mergeSetupOverrides = mergeSetupOverrides; +/** Explicit CLI flags override only their fields; file-only settings remain intact. */ +function mergeSetupOverrides(fileOverrides, flagOverrides) { + return { + ...fileOverrides, + ...flagOverrides, + features: { ...fileOverrides.features, ...flagOverrides.features }, + agents: { ...fileOverrides.agents, ...flagOverrides.agents }, + repository: { ...fileOverrides.repository, ...flagOverrides.repository }, + ai: { ...fileOverrides.ai, ...flagOverrides.ai }, + pullRequestApproval: { + ...fileOverrides.pullRequestApproval, + ...flagOverrides.pullRequestApproval, + coverage: { ...fileOverrides.pullRequestApproval?.coverage, ...flagOverrides.pullRequestApproval?.coverage }, + }, + projects: { ...fileOverrides.projects, ...flagOverrides.projects }, + issueWorkflows: { ...fileOverrides.issueWorkflows, ...flagOverrides.issueWorkflows }, + repositoryAgentGuidance: { ...fileOverrides.repositoryAgentGuidance, ...flagOverrides.repositoryAgentGuidance }, + storage: { + ...fileOverrides.storage, + ...flagOverrides.storage, + secrets: { ...fileOverrides.storage?.secrets, ...flagOverrides.storage?.secrets, overrides: { ...fileOverrides.storage?.secrets?.overrides, ...flagOverrides.storage?.secrets?.overrides } }, + variables: { ...fileOverrides.storage?.variables, ...flagOverrides.storage?.variables, overrides: { ...fileOverrides.storage?.variables?.overrides, ...flagOverrides.storage?.variables?.overrides } }, + }, + }; +} + + /***/ }), /***/ 97890: @@ -46321,6 +46376,7 @@ function normalizeSetupConfigurationLocales(configuration) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildSetupCredentialRequirements = void 0; exports.buildSetupPlan = buildSetupPlan; +exports.setupPlanGuardPaths = setupPlanGuardPaths; exports.buildSetupRepositoryVariables = buildSetupRepositoryVariables; exports.buildSetupActionInputs = buildSetupActionInputs; const pull_request_description_1 = __nccwpck_require__(45315); @@ -46370,6 +46426,31 @@ function buildSetupPlan(configuration, mergeQueueReadiness = [], approvalReadine warnings: buildSetupWarnings(configuration), }; } +/** Actual checkout destinations covered by a web Apply drift check. + * The presentation plan uses package-source labels for workflows/forms; + * comparing those labels as checkout paths would silently miss local edits. + */ +function setupPlanGuardPaths(plan) { + const selected = plan.selectedFiles.map(file => { + if (file.startsWith('workflows/')) + return `.github/${file}`; + if (file.startsWith('ISSUE_TEMPLATE/')) + return `.github/${file}`; + if (file === 'pull_request_template.md') + return '.github/pull_request_template.md'; + if (file === 'AGENTS.md (managed pointer only)') + return 'AGENTS.md'; + return file; + }); + // Deselected managed assets can be retired to setup-backups during Apply. + const retiredCandidates = [ + ...['config.yml', ...issue_workflow_profile_1.ISSUE_WORKFLOW_KINDS.map(kind => issue_workflow_profile_1.ISSUE_WORKFLOW_CATALOG[kind].formFile)] + .map(file => `.github/ISSUE_TEMPLATE/${file}`), + ...['release_workflow.yml', 'hotfix_workflow.yml', 'copilot_deployment_orchestration.yml'] + .map(file => `.github/workflows/${file}`), + ]; + return [...new Set([...selected, ...retiredCandidates])].sort(); +} function buildSetupRepositoryVariables(configuration) { const variables = []; const add = (name, value) => { @@ -48358,6 +48439,8 @@ function applyAnswer(configuration, question, value) { } function parseWorkflowSelection(raw) { const normalized = raw.trim().toLowerCase(); + if (normalized === 'none') + return { value: [] }; if (!normalized || normalized === 'all') return { value: [...issue_workflow_profile_1.ISSUE_WORKFLOW_KINDS] }; const requested = normalized.split(',').map(item => item.trim()).filter(Boolean) @@ -48407,6 +48490,38 @@ function projectLabel(field) { } +/***/ }), + +/***/ 92567: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.sameSetupRemoteFacts = sameSetupRemoteFacts; +/** Compare the semantic GitHub facts used by setup, not object/response ordering. */ +function sameSetupRemoteFacts(left, right) { + const variables = (items) => items + .map(item => JSON.stringify([item.name, item.value])).sort(); + const normalize = (facts) => ({ + ownerType: facts.ownerType, + repositoryId: facts.repositoryId, + repositoryVisibility: facts.repositoryVisibility, + repositorySecrets: [...facts.repositorySecrets].sort(), + repositorySecretsAccess: facts.repositorySecretsAccess, + organizationSecrets: [...facts.organizationSecrets].sort(), + repositoryVariables: variables(facts.repositoryVariables), + repositoryVariablesAccess: facts.repositoryVariablesAccess, + organizationVariables: variables(facts.organizationVariables), + organizationAccess: facts.organizationAccess, + organizationSecretsAccess: facts.organizationSecretsAccess, + organizationVariablesAccess: facts.organizationVariablesAccess, + credentialHealthWorkflow: facts.credentialHealthWorkflow, + }); + return JSON.stringify(normalize(left)) === JSON.stringify(normalize(right)); +} + + /***/ }), /***/ 65640: @@ -48496,6 +48611,7 @@ exports.buildSetupPatPermissionRequirements = buildSetupPatPermissionRequirement exports.buildConfiguredSetupPatPermissionRequirements = buildConfiguredSetupPatPermissionRequirements; exports.buildSetupPatIntentPermissionRequirements = buildSetupPatIntentPermissionRequirements; exports.buildSetupPatIntentUncertainty = buildSetupPatIntentUncertainty; +exports.requiredSetupPatPermissionDelta = requiredSetupPatPermissionDelta; exports.buildWorkflowPatPermissionRequirements = buildWorkflowPatPermissionRequirements; exports.normalizePermissionRequirements = normalizePermissionRequirements; const setup_configuration_plan_1 = __nccwpck_require__(87770); @@ -48554,6 +48670,15 @@ function buildSetupPatIntentUncertainty(configuration, ownerKind) { } return unknown; } +/** Required grants newly introduced (or upgraded) after the provisional review. */ +function requiredSetupPatPermissionDelta(before, after) { + const previous = new Map(before.filter(item => item.applicability === 'required') + .map(item => [`${item.scope}:${item.permission.toLowerCase()}`, item.level])); + return after.filter(item => item.applicability === 'required' + && (previous.get(`${item.scope}:${item.permission.toLowerCase()}`) === undefined + || (previous.get(`${item.scope}:${item.permission.toLowerCase()}`) === 'read' && item.level === 'write'))) + .map(item => `${item.scope} ${item.permission} ${item.level}`); +} function buildSetupPatRequirements(configuration, organization, remote) { const repositorySecretNames = (0, setup_credential_requirement_policy_1.buildSetupCredentialRequirements)(configuration) .map(credential => credential.name); @@ -54804,6 +54929,64 @@ function buildDraftPrompt(context, snapshot, plan, answers, currentSdd) { } +/***/ }), + +/***/ 60830: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.AuditConfiguredSetupPatUseCase = void 0; +const setup_pat_creation_url_policy_1 = __nccwpck_require__(54718); +const setup_token_permission_policy_1 = __nccwpck_require__(99590); +/** Rechecks the final plan without granting permission based on the browser preview. */ +class AuditConfiguredSetupPatUseCase { + constructor(context, ports) { + this.context = context; + this.ports = ports; + } + async audit(configuration, remote) { + const required = (0, setup_token_permission_policy_1.buildConfiguredSetupPatPermissionRequirements)(configuration, remote); + this.ports.presenter.showRequirements('setup', required); + if (this.context.assertedOwnerKind && remote && remote.ownerType !== 'Unknown' + && remote.ownerType !== this.context.assertedOwnerKind) { + this.ports.showOwnerMismatch(this.context.assertedOwnerKind, remote.ownerType); + this.showCorrectedLink(required); + return { status: 'blocked', errors: ['Repository owner type differs from the pre-PAT selection. Rerun setup with the correct owner type and PAT.'] }; + } + if (this.context.guided) { + const removed = (0, setup_token_permission_policy_1.requiredSetupPatPermissionDelta)(required, this.context.provisionalRequirements); + if (removed.length) + this.ports.showExcessGrants(removed); + } + if (!this.context.token) + return { status: 'accepted' }; + const report = await this.ports.permissions.inspect({ + role: 'setup', owner: this.context.owner, repository: this.context.repository, + token: this.context.token, requirements: required, + }); + this.ports.presenter.showReport(report); + const accepted = report.ready || (report.confirmationRequired && await this.ports.confirmUnverifiable(report)); + if (!accepted || report.identityStatus !== 'valid') { + if (this.context.guided) + this.showCorrectedLink(required); + return { status: 'blocked', errors: [ + 'The setup PAT has missing or unconfirmed access required by the approved setup plan. Grant or explicitly confirm the permissions shown above and retry.', + ] }; + } + return { status: 'accepted' }; + } + showCorrectedLink(required) { + this.ports.showUpdatedLink((0, setup_pat_creation_url_policy_1.buildSetupPatCreationUrl)({ + role: 'setup', owner: this.context.owner, repository: this.context.repository, + expiresIn: 1, requirements: required, + }), (0, setup_token_permission_policy_1.requiredSetupPatPermissionDelta)(this.context.provisionalRequirements, required)); + } +} +exports.AuditConfiguredSetupPatUseCase = AuditConfiguredSetupPatUseCase; + + /***/ }), /***/ 87328: @@ -55398,6 +55581,99 @@ function uniqueTargets(targets) { } +/***/ }), + +/***/ 69277: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.PrepareSetupPatIntentUseCase = void 0; +const application_error_1 = __nccwpck_require__(75999); +const setup_interaction_cancelled_error_1 = __nccwpck_require__(38313); +const setup_configuration_policy_1 = __nccwpck_require__(56637); +const setup_questionnaire_policy_1 = __nccwpck_require__(6009); +const setup_pat_intent_policy_1 = __nccwpck_require__(30748); +const setup_token_permission_policy_1 = __nccwpck_require__(99590); +const setup_pat_creation_url_policy_1 = __nccwpck_require__(54718); +const setup_wizard_use_case_1 = __nccwpck_require__(43433); +/** Frontend-neutral preparation; GitHub still issues the PAT in its own UI. */ +class PrepareSetupPatIntentUseCase { + constructor(ports) { + this.ports = ports; + } + async execute(request) { + const fixedQuestionIds = (0, setup_pat_intent_policy_1.fixedSetupPatIntentQuestionIds)(request.overrides, request.skipRepositoryVariables, request.skipRepositorySecrets); + let draft = (0, setup_wizard_use_case_1.buildInitialSetupConfiguration)({ + mode: 'interactive', overrides: request.overrides, + skipRepositoryVariables: request.skipRepositoryVariables, + skipRepositorySecrets: request.skipRepositorySecrets, + }); + let pass = 1; + while (true) { + const context = { skipQuestionIds: fixedQuestionIds }; + const intent = await this.ports.collect((0, setup_questionnaire_policy_1.createSetupPermissionIntentQuestionnaire)(draft, context), context, pass); + if (intent.terminal === 'cancelled') + throw new setup_interaction_cancelled_error_1.SetupInteractionCancelledError(); + draft = intent.draft; + const ownerKind = (0, setup_pat_intent_policy_1.setupPatIntentNeedsOwnerKind)(draft) ? await this.ports.chooseOwnerKind() : 'User'; + if (ownerKind === 'unknown') { + this.ports.onManual('owner-unknown'); + return { kind: 'manual' }; + } + const ownerConflict = (0, setup_pat_intent_policy_1.setupPatIntentOwnerConflict)(draft, ownerKind); + const errors = (0, setup_configuration_policy_1.validateSetupConfiguration)(draft, { allowIncompleteApproval: true }); + const requirements = (0, setup_token_permission_policy_1.buildSetupPatIntentPermissionRequirements)(draft, ownerKind); + this.ports.advanceToSetupPat(); + this.ports.showPreview({ + draft, requirements, uncertain: (0, setup_token_permission_policy_1.buildSetupPatIntentUncertainty)(draft, ownerKind), + ownerConflict, errors, pass, + }); + let decision; + do { + decision = await this.ports.review(); + if (decision === 'details') + this.ports.showDetails(requirements); + } while (decision === 'details'); + if (decision === 'manual') { + this.ports.onManual('chosen'); + return { kind: 'manual' }; + } + if (decision === 'revise') { + pass = this.ports.revisitChoices(); + continue; + } + if (ownerConflict || errors.length > 0) { + throw new application_error_1.ApplicationError('configuration.invalid', 'Correct the reported setup intent or local --config/flags, then retry guided setup. No PAT was requested.'); + } + try { + return { + kind: 'guided', + url: (0, setup_pat_creation_url_policy_1.buildSetupPatCreationUrl)({ + role: 'setup', owner: request.owner, repository: request.repository, expiresIn: 1, + requirements, + }), + requirements, + ownerKind, + permissionIntent: { + draft, + answeredQuestionIds: [...new Set([...fixedQuestionIds, ...(intent.answeredQuestionIds ?? [])])], + }, + }; + } + catch (error) { + if (!(error instanceof setup_pat_creation_url_policy_1.UnsupportedSetupPatLinkError)) + throw error; + this.ports.onManual('unsupported'); + return { kind: 'manual' }; + } + } + } +} +exports.PrepareSetupPatIntentUseCase = PrepareSetupPatIntentUseCase; + + /***/ }), /***/ 67438: @@ -56053,6 +56329,113 @@ class VerifyGuidedWorkflowPatIdentityUseCase { exports.VerifyGuidedWorkflowPatIdentityUseCase = VerifyGuidedWorkflowPatIdentityUseCase; +/***/ }), + +/***/ 23388: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.VerifySetupPatBootstrapUseCase = void 0; +const application_error_1 = __nccwpck_require__(75999); +const setup_pat_creation_url_policy_1 = __nccwpck_require__(54718); +/** Initial read-only gate shared by terminal and browser setup presentations. */ +class VerifySetupPatBootstrapUseCase { + constructor(ports) { + this.ports = ports; + } + async execute(request) { + const report = await this.ports.permissions.inspect({ + role: 'setup', owner: request.owner, repository: request.repository, + token: request.token, requirements: request.requirements, + }); + this.ports.presenter.showReport(report); + const accepted = report.ready + || (report.confirmationRequired && await this.ports.confirmUnverifiable(report)); + if (!accepted || report.identityStatus !== 'valid') { + if (request.guided) + this.ports.showCorrectedLink((0, setup_pat_creation_url_policy_1.buildSetupPatCreationUrl)({ + role: 'setup', owner: request.owner, repository: request.repository, + expiresIn: 1, requirements: request.requirements, + })); + throw new application_error_1.ApplicationError('authorization.credential-invalid', 'The setup PAT has missing or unconfirmed required access. Grant or explicitly confirm the permissions shown above and retry.'); + } + if (!await this.ports.confirmAccount(report.account)) { + throw new application_error_1.ApplicationError('authorization.credential-invalid', 'The setup PAT belongs to an unintended account. Revoke it in GitHub and retry with the correct account.'); + } + return report.account; + } +} +exports.VerifySetupPatBootstrapUseCase = VerifySetupPatBootstrapUseCase; + + +/***/ }), + +/***/ 5303: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.VerifyWebSetupApplyUseCase = void 0; +const application_error_1 = __nccwpck_require__(75999); +const setup_interaction_cancelled_error_1 = __nccwpck_require__(38313); +const setup_remote_facts_policy_1 = __nccwpck_require__(92567); +/** Authorizes one web Apply against the facts the operator actually reviewed. */ +class VerifyWebSetupApplyUseCase { + constructor(ports) { + this.ports = ports; + } + async execute(request) { + const decision = await this.ports.confirm(); + if (decision === undefined) + return 'cancelled'; + if (decision === 'stop') + return 'declined'; + this.assertActive(); + const current = this.ports.readRepositoryFacts(); + const expected = request.repository; + if (!current || current.owner !== expected.owner || current.repository !== expected.repository + || current.checkoutRoot !== expected.checkoutRoot || current.branch !== expected.branch + || current.head !== expected.head) { + throw new application_error_1.ApplicationError('configuration.invalid', 'The repository identity changed during setup. No mutation started; restart and review a new plan.'); + } + if (!this.ports.fileSnapshotMatches(expected.checkoutRoot, request.selectedFiles, request.fileSnapshot)) { + throw new application_error_1.ApplicationError('configuration.invalid', 'Selected repository files changed since plan review. No mutation started; restart and review a new plan.'); + } + let remote = await this.ports.remote.inspect(expected.owner, expected.repository, request.setupToken); + this.assertActive(); + let credentialHealthWorkflow = 'unavailable'; + try { + credentialHealthWorkflow = await this.ports.remote.inspectCredentialHealthWorkflow?.(expected.owner, expected.repository, request.setupToken, request.configuration.repository.mainBranch) ?? 'unavailable'; + } + catch { /* Unknown selected-ref state must not inherit a provisional value. */ } + this.assertActive(); + remote = { ...remote, credentialHealthWorkflow }; + if (!(0, setup_remote_facts_policy_1.sameSetupRemoteFacts)(remote, request.approvedRemote)) { + throw new application_error_1.ApplicationError('configuration.invalid', 'GitHub repository facts changed since plan review. No mutation started; restart and review a new plan.'); + } + const audit = await this.ports.permissionAudit.audit(request.configuration, remote); + this.assertActive(); + if (audit.status === 'blocked') { + throw new application_error_1.ApplicationError('authorization.credential-invalid', 'Setup PAT access changed since plan review. No mutation started; correct the PAT and review a new plan.'); + } + return 'approved'; + } + assertActive() { + const state = this.ports.sessionState(); + if (state === 'cancelled') { + throw new setup_interaction_cancelled_error_1.SetupInteractionCancelledError(); + } + if (state === 'ended') { + throw new application_error_1.ApplicationError('configuration.invalid', 'The local setup session expired during final checks. No mutation started; start a new run and review a fresh plan.'); + } + } +} +exports.VerifyWebSetupApplyUseCase = VerifyWebSetupApplyUseCase; + + /***/ }), /***/ 73572: @@ -65446,18 +65829,18 @@ const setup_files_1 = __nccwpck_require__(59126); const logger_1 = __nccwpck_require__(91151); const cli_context_1 = __nccwpck_require__(21307); const setup_policy_1 = __nccwpck_require__(28732); -const setup_config_file_1 = __nccwpck_require__(11196); +const setup_command_options_1 = __nccwpck_require__(99254); const setup_1 = __nccwpck_require__(36888); -const setup_wizard_use_case_1 = __nccwpck_require__(43433); -const setup_questionnaire_policy_1 = __nccwpck_require__(6009); -const setup_pat_intent_policy_1 = __nccwpck_require__(30748); +const setup_configuration_plan_1 = __nccwpck_require__(87770); +const prepare_setup_pat_intent_use_case_1 = __nccwpck_require__(69277); +const audit_configured_setup_pat_use_case_1 = __nccwpck_require__(60830); +const verify_setup_pat_bootstrap_use_case_1 = __nccwpck_require__(23388); const setup_configuration_policy_1 = __nccwpck_require__(56637); const setup_token_permission_policy_1 = __nccwpck_require__(99590); const setup_credentials_composition_root_1 = __nccwpck_require__(69084); const setup_doctor_composition_root_1 = __nccwpck_require__(56360); const setup_workspace_adapter_1 = __nccwpck_require__(5729); const setup_approval_readiness_adapter_1 = __nccwpck_require__(78572); -const issue_workflow_profile_1 = __nccwpck_require__(26744); const application_error_1 = __nccwpck_require__(75999); const setup_terminal_driver_1 = __nccwpck_require__(5462); const setup_question_renderer_1 = __nccwpck_require__(89481); @@ -65470,8 +65853,15 @@ const setup_token_permissions_composition_root_1 = __nccwpck_require__(64132); const setup_pat_creation_url_policy_1 = __nccwpck_require__(54718); const setup_github_identity_query_adapter_1 = __nccwpck_require__(56098); const verify_guided_workflow_pat_identity_use_case_1 = __nccwpck_require__(35697); +const verify_web_setup_apply_use_case_1 = __nccwpck_require__(5303); const setup_journey_use_case_1 = __nccwpck_require__(8419); +const setup_journey_policy_1 = __nccwpck_require__(53289); const setup_journey_presenter_1 = __nccwpck_require__(20462); +const web_setup_bridge_1 = __nccwpck_require__(21518); +const setup_apply_snapshot_1 = __nccwpck_require__(84136); +const setup_session_guard_1 = __nccwpck_require__(53104); +const web_setup_server_1 = __nccwpck_require__(63080); +const web_setup_adapters_1 = __nccwpck_require__(60574); function registerSetupCommand(program) { program .command('setup') @@ -65484,10 +65874,11 @@ function registerSetupCommand(program) { .option('--agent-guidance ', 'Generated agent guidance mode (prompt|create-if-missing|disabled)') .option('--config ', 'YAML or JSON file with setup overrides') .option('--pr-approval-mode ', 'PR bot approval: recommend (new setup default), guarded, or off') - .option('--pr-approval-check ', 'Exact test producer name|source-App-ID|workflow-name; repeat for multiple checks', collectApprovalCheck, []) + .option('--pr-approval-check ', 'Exact test producer name|source-App-ID|workflow-name; repeat for multiple checks', setup_command_options_1.collectApprovalCheck, []) .option('--pr-approval-coverage-check ', 'Exact selected check that enforces the coverage budget') .option('--pr-approval-attest-producer', 'Confirm exact check/App/workflow identity and a coverage-enforcing CI step', false) .option('--non-interactive', 'Use defaults and config-file values without prompting', false) + .option('--web', 'Run the optional local browser setup assistant (127.0.0.1 only)', false) .option('--yes', 'Apply the plan without the final confirmation prompt', false) .option('--confirm-unverifiable-write-permissions', 'Confirm that required PAT write permissions shown as Unverifiable were configured exactly as displayed', false) .option('--dry-run', 'Show the setup plan without changing files or GitHub', false) @@ -65497,28 +65888,31 @@ function registerSetupCommand(program) { .option('--secrets-scope ', 'Default Secret scope (repository|organization)') .option('--variables-visibility ', 'Organization Variable visibility (selected|private|all)') .option('--secrets-visibility ', 'Organization Secret visibility (selected|private|all)') - .option('--variable-scope ', 'Per-variable scope override; repeat as needed', collectScope, {}) - .option('--secret-scope ', 'Per-secret scope override; repeat as needed', collectScope, {}) + .option('--variable-scope ', 'Per-variable scope override; repeat as needed', setup_command_options_1.collectScope, {}) + .option('--secret-scope ', 'Per-secret scope override; repeat as needed', setup_command_options_1.collectScope, {}) .option('--update-workflows', 'Allow setup-managed workflows already in the repository to be updated', false) .option('--workflow-pat ', 'Workflow PAT for the bot account (prefer the hidden interactive prompt)') - .option('--secret ', 'Secret value for non-interactive setup; repeat for each API key', collectSecret, {}) + .option('--secret ', 'Secret value for non-interactive setup; repeat for each API key', setup_command_options_1.collectSecret, {}) .action(async (options) => { - const terminal = options.nonInteractive ? undefined : (0, setup_terminal_driver_1.createInteractiveTerminalDriver)(); - const credentialPrompt = new setup_credential_prompt_adapter_1.SetupCredentialPromptAdapter(terminal, { + const terminal = options.nonInteractive || options.web ? undefined : (0, setup_terminal_driver_1.createInteractiveTerminalDriver)(); + const webBridge = options.web ? new web_setup_bridge_1.WebSetupBridge('Resolving repository…') : undefined; + let webServer; + const credentialPrompt = webBridge ? new web_setup_adapters_1.WebSetupCredentialPrompt(webBridge) : new setup_credential_prompt_adapter_1.SetupCredentialPromptAdapter(terminal, { ...(options.workflowPat ? { PAT: options.workflowPat } : {}), ...options.secret, }, Boolean(options.confirmUnverifiableWritePermissions)); - const permissionPresenter = new setup_token_permission_presenter_1.ConsoleSetupTokenPermissionPresenter(options.nonInteractive ? 'full' : 'summary'); + const permissionPresenter = webBridge ? new web_setup_adapters_1.WebSetupPermissionPresenter(webBridge) + : new setup_token_permission_presenter_1.ConsoleSetupTokenPermissionPresenter(options.nonInteractive ? 'full' : 'summary'); const tokenPermissions = (0, setup_token_permissions_composition_root_1.createSetupTokenPermissionsUseCase)(); - const workflowPrompt = new setup_workflow_update_prompt_adapter_1.SetupWorkflowUpdatePromptAdapter(terminal); + const workflowPrompt = webBridge ? new web_setup_adapters_1.WebSetupWorkflowUpdatePrompt(webBridge) : new setup_workflow_update_prompt_adapter_1.SetupWorkflowUpdatePromptAdapter(terminal); const cwd = process.cwd(); let setupMutationStarted = false; + let releaseSetupGuard; let journey; try { - if (!options.nonInteractive && !terminal) { - (0, logger_1.logError)('Interactive setup requires a terminal. Use --non-interactive with explicit configuration.'); - process.exitCode = 1; - return; + if (options.web && (options.nonInteractive || options.yes || options.token || options.workflowPat + || Object.keys(options.secret ?? {}).length || options.confirmUnverifiableWritePermissions)) { + throw new application_error_1.ApplicationError('configuration.invalid', '--web cannot be combined with --non-interactive, --yes, --token, --workflow-pat, --secret, or --confirm-unverifiable-write-permissions. Use the browser for these decisions or run copilot setup in the terminal.'); } (0, logger_1.logInfo)('🔍 Checking we are inside a git repository...'); if (!(0, cli_context_1.isInsideGitRepo)(cwd)) { @@ -65535,13 +65929,48 @@ function registerSetupCommand(program) { return; } (0, logger_1.logInfo)(`📦 Repository: ${gitInfo.owner}/${gitInfo.repo}`); + releaseSetupGuard = (0, setup_session_guard_1.acquireSetupSessionGuard)(cwd); + const checkoutRoot = webBridge ? (0, cli_context_1.getGitRepositoryRoot)(cwd) : cwd; + const initialBranch = webBridge ? (0, cli_context_1.getCurrentBranch)() : undefined; + const initialHead = webBridge ? (0, cli_context_1.getCurrentHeadSha)() : undefined; + if (webBridge && !initialHead) { + throw new application_error_1.ApplicationError('configuration.invalid', 'The current Git revision could not be verified. No local setup session started.'); + } + if (webBridge) { + webBridge.setRepository(`${gitInfo.owner}/${gitInfo.repo}`); + webBridge.setJourney((0, setup_journey_policy_1.buildSetupJourneyView)(`${gitInfo.owner}/${gitInfo.repo}`, 'repository', false)); + webServer = await (0, web_setup_server_1.startWebSetupServer)(webBridge); + (0, logger_1.logInfo)(`🌐 Local setup assistant: ${webServer.url}`); + (0, logger_1.logInfo)('If the browser does not open, copy this URL into a browser on this computer. The terminal setup remains available with copilot setup.'); + (0, web_setup_server_1.openWebSetupBrowser)(webServer.url); + } if (!options.nonInteractive) { - journey = new setup_journey_use_case_1.SetupJourneyUseCase(`${gitInfo.owner}/${gitInfo.repo}`, new setup_journey_presenter_1.ConsoleSetupJourneyPresenter()); + journey = new setup_journey_use_case_1.SetupJourneyUseCase(`${gitInfo.owner}/${gitInfo.repo}`, webBridge ? new web_setup_adapters_1.WebSetupJourneyPresenter(webBridge) : new setup_journey_presenter_1.ConsoleSetupJourneyPresenter()); + if (webBridge) { + const target = await webBridge.ask({ kind: 'confirm', title: 'Confirm this repository', + description: `This local checkout resolves to ${gitInfo.owner}/${gitInfo.repo} on branch ${initialBranch}. Confirm the target before configuring PAT access or files.`, + choices: ['Yes, this is my repository', 'Stop and choose another checkout'] }); + if (target === undefined) + throw new setup_credential_prompt_adapter_1.SetupTerminalCancelledError(); + if (target !== 'Yes, this is my repository') { + journey.finish('cancelled'); + return; + } + } journey.advance('choices'); } - const overrides = loadSetupOverrides(options); + const overrides = (0, setup_command_options_1.loadSetupOverrides)(options); let setupPatPermissions = (0, setup_token_permission_policy_1.buildSetupPatPermissionRequirements)(); let token = (0, setup_files_1.getSetupToken)(cwd, options.token); + if (webBridge && token) { + const choice = await webBridge.ask({ kind: 'choice', title: 'An environment setup PAT is available', + description: 'Its value stays in the CLI process and is never sent to this page. Exiting Copilot cannot unset your parent shell variable.', + choices: ['Use the environment PAT', 'Create or enter a different PAT'] }); + if (choice === undefined) + throw new setup_credential_prompt_adapter_1.SetupTerminalCancelledError(); + if (choice !== 'Use the environment PAT') + token = undefined; + } if (token || options.nonInteractive) permissionPresenter.showDetailedRequirements('setup', setupPatPermissions); else @@ -65551,82 +65980,49 @@ function registerSetupCommand(program) { let assertedOwnerKind; if (!token && !options.nonInteractive && !options.dryRun) { if (await credentialPrompt.chooseSetupPatMethod() === 'guided') { - const fixedQuestionIds = (0, setup_pat_intent_policy_1.fixedSetupPatIntentQuestionIds)(overrides, Boolean(options.skipVariables), Boolean(options.skipSecrets)); - let draft = (0, setup_wizard_use_case_1.buildInitialSetupConfiguration)({ - mode: 'interactive', overrides, + const prepared = await new prepare_setup_pat_intent_use_case_1.PrepareSetupPatIntentUseCase({ + collect: (initial, context, pass) => (webBridge + ? new web_setup_adapters_1.WebSetupQuestionnaireCollector(webBridge, pass) + : new setup_1.SetupQuestionnaireController(terminal, new setup_question_renderer_1.ConsoleSetupQuestionRenderer('permission-intent', pass))) + .collect(initial, context), + chooseOwnerKind: () => credentialPrompt.chooseSetupOwnerKind(), + review: () => credentialPrompt.reviewSetupPatIntent(), + showPreview: ({ draft, requirements, uncertain, ownerConflict, errors, pass }) => { + if (ownerConflict) + (0, logger_1.logInfo)('This plan selects organization storage or Projects, but the owner was declared a personal account. Revise the choices or use the manual PAT path.'); + if (errors.length) + (0, logger_1.logInfo)(`The selected local configuration needs correction before a guided link can be generated:\n${errors.map(item => ` - ${item}`).join('\n')}`); + if (pass > 1) + (0, logger_1.logInfo)('Choice review complete. Returning to setup PAT permission review.'); + (0, logger_1.logInfo)('Permission intent:'); + (0, logger_1.logInfo)(` Initial tag: ${draft.createInitialTag ? 'yes' : 'no'}; issue workflows: ${draft.features.issues ? draft.issueWorkflows.enabled.join(', ') || 'none' : 'disabled'}; PR approval: ${draft.pullRequestApproval.mode}`); + (0, logger_1.logInfo)(` Secrets: ${draft.manageRepositorySecrets ? draft.storage.secrets.defaultScope : 'off'}; Variables: ${draft.manageRepositoryVariables ? draft.storage.variables.defaultScope : 'off'}; Projects: ${draft.projects.ids.trim() || 'none'}`); + webBridge?.message(`Permission preview: issue workflows ${draft.features.issues ? draft.issueWorkflows.enabled.join(', ') || 'none' : 'disabled'}; PR approval ${draft.pullRequestApproval.mode}; Secrets ${draft.manageRepositorySecrets ? draft.storage.secrets.defaultScope : 'off'}; Variables ${draft.manageRepositoryVariables ? draft.storage.variables.defaultScope : 'off'}; Projects ${draft.projects.ids.trim() || 'none'}.`, 'info'); + permissionPresenter.showRequirements('setup', requirements); + if (uncertain.length) + (0, logger_1.logInfo)(`May need after GitHub inspection:\n${uncertain.map(item => ` - ${item}`).join('\n')}`); + }, + showDetails: requirements => permissionPresenter.showDetailedRequirements('setup', requirements), + onManual: reason => { + if (reason === 'owner-unknown') + (0, logger_1.logInfo)('Owner type was not confirmed. Use the manual PAT table, or check whether the GitHub owner is an organization before retrying guided setup.'); + if (reason === 'unsupported') + (0, logger_1.logInfo)('A guided setup PAT link is unavailable for this owner or permission set. Enter a manually created PAT using the table above.'); + credentialPrompt.useManualSetupPat(); + permissionPresenter.showDetailedRequirements('setup', setupPatPermissions); + }, + advanceToSetupPat: () => { journey?.advance('setup-pat'); }, + revisitChoices: () => journey.revisitChoices(), + }).execute({ + owner: gitInfo.owner, repository: gitInfo.repo, overrides, skipRepositoryVariables: Boolean(options.skipVariables), skipRepositorySecrets: Boolean(options.skipSecrets), }); - let choiceReviewPass = 1; - while (true) { - const context = { skipQuestionIds: fixedQuestionIds }; - const collector = new setup_1.SetupQuestionnaireController(terminal, new setup_question_renderer_1.ConsoleSetupQuestionRenderer('permission-intent', choiceReviewPass)); - const intent = await collector.collect((0, setup_questionnaire_policy_1.createSetupPermissionIntentQuestionnaire)(draft, context), context); - if (intent.terminal === 'cancelled') - throw new setup_credential_prompt_adapter_1.SetupTerminalCancelledError(); - draft = intent.draft; - const ownerKind = (0, setup_pat_intent_policy_1.setupPatIntentNeedsOwnerKind)(draft) - ? await credentialPrompt.chooseSetupOwnerKind() : 'User'; - if (ownerKind === 'unknown') { - (0, logger_1.logInfo)('Owner type was not confirmed. Use the manual PAT table, or check whether the GitHub owner is an organization before retrying guided setup.'); - credentialPrompt.useManualSetupPat(); - permissionPresenter.showDetailedRequirements('setup', setupPatPermissions); - break; - } - if ((0, setup_pat_intent_policy_1.setupPatIntentOwnerConflict)(draft, ownerKind)) { - (0, logger_1.logInfo)('This plan selects organization storage or Projects, but the owner was declared a personal account. Revise the choices or use the manual PAT path.'); - } - const intentErrors = (0, setup_configuration_policy_1.validateSetupConfiguration)(draft, { allowIncompleteApproval: true }); - if (intentErrors.length > 0) { - (0, logger_1.logInfo)(`The selected local configuration needs correction before a guided link can be generated:\n${intentErrors.map(item => ` - ${item}`).join('\n')}`); - } - const preview = (0, setup_token_permission_policy_1.buildSetupPatIntentPermissionRequirements)(draft, ownerKind); - if (choiceReviewPass > 1) - (0, logger_1.logInfo)('Choice review complete. Returning to setup PAT permission review.'); - journey?.advance('setup-pat'); - (0, logger_1.logInfo)('Permission intent:'); - (0, logger_1.logInfo)(` Initial tag: ${draft.createInitialTag ? 'yes' : 'no'}; issue workflows: ${draft.features.issues ? draft.issueWorkflows.enabled.join(', ') || 'none' : 'disabled'}; PR approval: ${draft.pullRequestApproval.mode}`); - (0, logger_1.logInfo)(` Secrets: ${draft.manageRepositorySecrets ? draft.storage.secrets.defaultScope : 'off'}; Variables: ${draft.manageRepositoryVariables ? draft.storage.variables.defaultScope : 'off'}; Projects: ${draft.projects.ids.trim() || 'none'}`); - permissionPresenter.showRequirements('setup', preview); - const uncertain = (0, setup_token_permission_policy_1.buildSetupPatIntentUncertainty)(draft, ownerKind); - if (uncertain.length) - (0, logger_1.logInfo)(`May need after GitHub inspection:\n${uncertain.map(item => ` - ${item}`).join('\n')}`); - let decision; - do { - decision = await credentialPrompt.reviewSetupPatIntent(); - if (decision === 'details') - permissionPresenter.showDetailedRequirements('setup', preview); - } while (decision === 'details'); - if (decision === 'manual') { - credentialPrompt.useManualSetupPat(); - permissionPresenter.showDetailedRequirements('setup', setupPatPermissions); - break; - } - if (decision === 'revise') { - choiceReviewPass = journey?.revisitChoices() ?? choiceReviewPass + 1; - continue; - } - if ((0, setup_pat_intent_policy_1.setupPatIntentOwnerConflict)(draft, ownerKind) || intentErrors.length > 0) { - throw new application_error_1.ApplicationError('configuration.invalid', 'Correct the reported setup intent or local --config/flags, then retry guided setup. No PAT was requested.'); - } - try { - const url = (0, setup_pat_creation_url_policy_1.buildSetupPatCreationUrl)({ - role: 'setup', owner: gitInfo.owner, repository: gitInfo.repo, expiresIn: 1, - requirements: preview, - }); - credentialPrompt.configureSetupPatGuide(url); - setupPatPermissions = preview; - assertedOwnerKind = ownerKind; - permissionIntent = { draft, answeredQuestionIds: [...new Set([...fixedQuestionIds, ...intent.answeredQuestionIds])] }; - } - catch (error) { - if (!(error instanceof setup_pat_creation_url_policy_1.UnsupportedSetupPatLinkError)) - throw error; - (0, logger_1.logInfo)('A guided setup PAT link is unavailable for this owner or permission set. Enter a manually created PAT using the table above.'); - credentialPrompt.useManualSetupPat(); - permissionPresenter.showDetailedRequirements('setup', setupPatPermissions); - } - break; + if (prepared.kind === 'guided') { + credentialPrompt.configureSetupPatGuide(prepared.url); + setupPatPermissions = [...prepared.requirements]; + assertedOwnerKind = prepared.ownerKind; + permissionIntent = prepared.permissionIntent; } } else { @@ -65634,7 +66030,7 @@ function registerSetupCommand(program) { permissionPresenter.showDetailedRequirements('setup', setupPatPermissions); } } - if (options.dryRun && !token) + if (options.dryRun && !token && !webBridge) journey?.advance('plan'); if (!token && !options.dryRun) journey?.advance('setup-pat'); @@ -65650,81 +66046,41 @@ function registerSetupCommand(program) { } if (token) { journey?.advance('setup-pat'); - const permissionReport = await tokenPermissions.inspect({ - role: 'setup', - owner: gitInfo.owner, - repository: gitInfo.repo, - token, - requirements: setupPatPermissions, - }); - permissionPresenter.showReport(permissionReport); - const permissionAccepted = permissionReport.ready - || (permissionReport.confirmationRequired - && await credentialPrompt.confirmUnverifiableTokenPermissions(permissionReport)); - if (!permissionAccepted || permissionReport.identityStatus !== 'valid') { - if (credentialPrompt.usedGuidedSetupPat) - credentialPrompt.showUpdatedSetupPatLink((0, setup_pat_creation_url_policy_1.buildSetupPatCreationUrl)({ - role: 'setup', owner: gitInfo.owner, repository: gitInfo.repo, expiresIn: 1, - requirements: setupPatPermissions, - }), 'bootstrap'); - throw new application_error_1.ApplicationError('authorization.credential-invalid', 'The setup PAT has missing or unconfirmed required access. Grant or explicitly confirm the permissions shown above and retry.'); - } - if (!await credentialPrompt.confirmGuidedSetupAccount(permissionReport.account)) { - throw new application_error_1.ApplicationError('authorization.credential-invalid', 'The setup PAT belongs to an unintended account. Revoke it in GitHub and retry with the correct account.'); - } - setupPatAccount = permissionReport.account; + setupPatAccount = await new verify_setup_pat_bootstrap_use_case_1.VerifySetupPatBootstrapUseCase({ + permissions: tokenPermissions, + presenter: permissionPresenter, + confirmUnverifiable: report => credentialPrompt.confirmUnverifiableTokenPermissions(report), + confirmAccount: account => credentialPrompt.confirmGuidedSetupAccount(account), + showCorrectedLink: url => credentialPrompt.showUpdatedSetupPatLink(url, 'bootstrap'), + }).execute({ owner: gitInfo.owner, repository: gitInfo.repo, token, + requirements: setupPatPermissions, guided: credentialPrompt.usedGuidedSetupPat }); journey?.advance('plan'); } (0, logger_1.logInfo)(options.dryRun ? '🧭 Building a dry-run setup plan...' : '🧭 Building your setup plan...'); - const auditConfiguredSetupPat = async (configuration, remoteConfiguration) => { - const configuredSetupPatPermissions = (0, setup_token_permission_policy_1.buildConfiguredSetupPatPermissionRequirements)(configuration, remoteConfiguration); - permissionPresenter.showRequirements('setup', configuredSetupPatPermissions); - if (assertedOwnerKind && remoteConfiguration && remoteConfiguration.ownerType !== 'Unknown' - && remoteConfiguration.ownerType !== assertedOwnerKind) { - (0, logger_1.logInfo)(`The owner was declared ${assertedOwnerKind}, but GitHub reports ${remoteConfiguration.ownerType}. The guided link is no longer valid for this plan.`); - credentialPrompt.showUpdatedSetupPatLink((0, setup_pat_creation_url_policy_1.buildSetupPatCreationUrl)({ - role: 'setup', owner: gitInfo.owner, repository: gitInfo.repo, expiresIn: 1, - requirements: configuredSetupPatPermissions, - }), 'final', setupPatPermissionDelta(setupPatPermissions, configuredSetupPatPermissions)); - return { status: 'blocked', errors: ['Repository owner type differs from the pre-PAT selection. Rerun setup with the correct owner type and PAT.'] }; - } - if (credentialPrompt.usedGuidedSetupPat) { - const removed = setupPatPermissionDelta(configuredSetupPatPermissions, setupPatPermissions); - if (removed.length) - (0, logger_1.logInfo)(`The final plan no longer requires grants suggested earlier: ${removed.join(', ')}. Your PAT may have excess access; replace it in GitHub if least privilege is required.`); - } - if (!token) - return { status: 'accepted' }; - const permissionReport = await tokenPermissions.inspect({ - role: 'setup', owner: gitInfo.owner, repository: gitInfo.repo, token, - requirements: configuredSetupPatPermissions, - }); - permissionPresenter.showReport(permissionReport); - const permissionAccepted = permissionReport.ready - || (permissionReport.confirmationRequired - && await credentialPrompt.confirmUnverifiableTokenPermissions(permissionReport)); - if (!permissionAccepted || permissionReport.identityStatus !== 'valid') { - if (credentialPrompt.usedGuidedSetupPat) - credentialPrompt.showUpdatedSetupPatLink((0, setup_pat_creation_url_policy_1.buildSetupPatCreationUrl)({ - role: 'setup', owner: gitInfo.owner, repository: gitInfo.repo, expiresIn: 1, - requirements: configuredSetupPatPermissions, - }), 'final', setupPatPermissionDelta(setupPatPermissions, configuredSetupPatPermissions)); - return { status: 'blocked', errors: [ - 'The setup PAT has missing or unconfirmed access required by the approved setup plan. Grant or explicitly confirm the permissions shown above and retry.', - ] }; - } - return { status: 'accepted' }; - }; + const auditConfiguredSetupPat = new audit_configured_setup_pat_use_case_1.AuditConfiguredSetupPatUseCase({ + owner: gitInfo.owner, repository: gitInfo.repo, token, + provisionalRequirements: setupPatPermissions, assertedOwnerKind, + guided: credentialPrompt.usedGuidedSetupPat, + }, { + permissions: tokenPermissions, + presenter: permissionPresenter, + confirmUnverifiable: report => credentialPrompt.confirmUnverifiableTokenPermissions(report), + showOwnerMismatch: (asserted, actual) => (0, logger_1.logInfo)(`The owner was declared ${asserted}, but GitHub reports ${actual}. The guided link is no longer valid for this plan.`), + showExcessGrants: grants => (0, logger_1.logInfo)(`The final plan no longer requires grants suggested earlier: ${grants.join(', ')}. Your PAT may have excess access; replace it in GitHub if least privilege is required.`), + showUpdatedLink: (url, grants) => credentialPrompt.showUpdatedSetupPatLink(url, 'final', grants), + }); const remoteConfigurationReader = (0, setup_credentials_composition_root_1.createSetupRemoteConfigurationReadPort)(); const wizard = new setup_1.SetupWizardUseCase({ - ...(terminal ? { - collector: new setup_1.SetupQuestionnaireController(terminal, new setup_question_renderer_1.ConsoleSetupQuestionRenderer()), + ...(terminal || webBridge ? { + collector: webBridge ? new web_setup_adapters_1.WebSetupQuestionnaireCollector(webBridge) + : new setup_1.SetupQuestionnaireController(terminal, new setup_question_renderer_1.ConsoleSetupQuestionRenderer()), } : {}), - planPresenter: new setup_plan_presenter_1.ConsoleSetupPlanPresenter(), + planPresenter: webBridge ? new web_setup_adapters_1.WebSetupPlanPresenter(webBridge) : new setup_plan_presenter_1.ConsoleSetupPlanPresenter(), confirmation: options.dryRun ? new setup_confirmation_adapter_1.DryRunSetupPlanConfirmation() - : new setup_confirmation_adapter_1.SetupPlanConfirmationAdapter(terminal, Boolean(options.yes)), - finalPermissionAudit: { audit: auditConfiguredSetupPat }, + : webBridge ? new web_setup_adapters_1.WebSetupPlanConfirmation(webBridge) + : new setup_confirmation_adapter_1.SetupPlanConfirmationAdapter(terminal, Boolean(options.yes)), + finalPermissionAudit: auditConfiguredSetupPat, remoteConfiguration: remoteConfigurationReader, mergeQueueReadiness: (0, setup_doctor_composition_root_1.createSetupMergeQueueReadinessUseCase)(), approvalReadiness: new setup_approval_readiness_adapter_1.GithubSetupApprovalReadinessAdapter(), @@ -65756,6 +66112,8 @@ function registerSetupCommand(program) { return; } const { configuration, remoteConfiguration } = result; + const guardedFiles = webBridge ? (0, setup_configuration_plan_1.setupPlanGuardPaths)(result.plan) : undefined; + const webApplySnapshot = guardedFiles ? (0, setup_apply_snapshot_1.captureSetupApplySnapshot)(checkoutRoot, guardedFiles) : undefined; const credentialRequirements = (0, setup_configuration_policy_1.buildSetupCredentialRequirements)(configuration); const workflowComparisons = new setup_workspace_adapter_1.SetupDoctorWorkspaceQueryAdapter().compareWorkflows((0, setup_configuration_policy_1.effectiveIssueWorkflowFeatures)(configuration), configuration); const updateWorkflows = await workflowPrompt.confirmWorkflowUpdates(workflowComparisons, Boolean(options.updateWorkflows)); @@ -65763,6 +66121,8 @@ function registerSetupCommand(program) { ? workflowComparisons.filter(comparison => comparison.status === 'changed').map(comparison => comparison.file) : []; if (options.dryRun) { + if (webBridge) + journey?.advance('plan'); journey?.finish('dry-run'); (0, logger_1.logInfo)('✅ Dry run complete. No files or GitHub resources were changed.'); return; @@ -65785,7 +66145,7 @@ function registerSetupCommand(program) { permissionPresenter.showDetailedRequirements('workflow', workflowTokenPermissions); } } - const credentials = await (0, setup_credentials_composition_root_1.createSetupCredentialsUseCase)(credentialPrompt, permissionPresenter).collect({ + const credentials = await (0, setup_credentials_composition_root_1.createSetupCredentialsUseCase)(credentialPrompt, permissionPresenter, webBridge ? { allowPreApplyHealthWorkflow: false } : undefined).collect({ owner: gitInfo.owner, repository: gitInfo.repo, setupToken: token ?? '', @@ -65805,6 +66165,42 @@ function registerSetupCommand(program) { (0, logger_1.logInfo)('The workflow PAT and setup PAT use the same GitHub account. If this account authors PRs, bot-generated events and guarded self-approval may not behave as intended; use a dedicated bot account where required.'); } } + if (webBridge) { + if (!remoteConfiguration || !guardedFiles || !webApplySnapshot || !initialBranch || !initialHead || !token) { + throw new application_error_1.ApplicationError('configuration.invalid', 'The approved setup evidence is incomplete. No mutation started; restart and review a new plan.'); + } + const authorization = await new verify_web_setup_apply_use_case_1.VerifyWebSetupApplyUseCase({ + confirm: async () => { + const answer = await webBridge.ask({ kind: 'confirm', title: 'Apply this setup now?', + description: 'This is the final approval. Local files and selected GitHub resources may change. A partial result may require inspection before retrying.', + choices: ['Apply setup', 'Stop without applying'] }); + return answer === undefined ? undefined : answer === 'Apply setup' ? 'apply' : 'stop'; + }, + readRepositoryFacts: () => { + const current = (0, cli_context_1.getGitInfo)(); + return 'error' in current ? undefined : { + owner: current.owner, repository: current.repo, checkoutRoot: (0, cli_context_1.getGitRepositoryRoot)(cwd), + branch: (0, cli_context_1.getCurrentBranch)(), head: (0, cli_context_1.getCurrentHeadSha)() ?? '', + }; + }, + fileSnapshotMatches: setup_apply_snapshot_1.setupApplySnapshotMatches, + remote: remoteConfigurationReader, + permissionAudit: auditConfiguredSetupPat, + sessionState: () => webBridge.snapshot().outcome === 'cancelled' ? 'cancelled' + : webBridge.snapshot().outcome ? 'ended' : 'active', + }).execute({ + repository: { owner: gitInfo.owner, repository: gitInfo.repo, checkoutRoot, + branch: initialBranch, head: initialHead }, + selectedFiles: guardedFiles, fileSnapshot: webApplySnapshot, approvedRemote: remoteConfiguration, + configuration, setupToken: token, + }); + if (authorization === 'cancelled') + throw new setup_credential_prompt_adapter_1.SetupTerminalCancelledError(); + if (authorization === 'declined') { + journey?.finish('cancelled'); + return; + } + } (0, logger_1.logInfo)('⚙️ Applying the approved setup plan...'); journey?.advance('apply'); const params = (0, setup_policy_1.buildSetupParams)(options, gitInfo, token ?? '', configuration, credentials.collection, approvedWorkflowFiles, remoteConfiguration); @@ -65842,148 +66238,20 @@ function registerSetupCommand(program) { finally { credentialPrompt.showSetupPatCleanupReminder(); terminal?.close(); + if (webBridge && webServer) { + const outcome = webBridge.snapshot().journey?.outcome ?? (process.exitCode ? 'blocked' : 'cancelled'); + webBridge.finish(outcome, outcome === 'complete' + ? 'Setup completed. Delete the temporary setup PAT in GitHub; keep the bot PAT while its Secret is in use.' + : outcome === 'dry-run' ? 'Dry run complete. No files or GitHub resources changed.' + : outcome === 'partial' ? 'Setup may be partial. Inspect GitHub resources and run copilot doctor before retrying.' + : 'No further setup changes will be applied. Any PAT already created in GitHub still exists until you delete it there.'); + (0, logger_1.logInfo)('The local browser page shows the result. Choose “Close local session” there, or stop this command with Ctrl+C.'); + await webServer.closed; + } + releaseSetupGuard?.(); } }); } -function collectSecret(value, previous) { - const separator = value.indexOf('='); - if (separator <= 0) - throw new Error('--secret must use NAME=VALUE syntax.'); - const name = value.slice(0, separator).trim(); - const secret = value.slice(separator + 1); - if (!/^[A-Z][A-Z0-9_]*$/.test(name) || !secret) - throw new Error('--secret must use a non-empty NAME=VALUE with an uppercase secret name.'); - return { ...previous, [name]: secret }; -} -function collectApprovalCheck(value, previous) { - return [...previous, value]; -} -function setupPatPermissionDelta(before, after) { - const previous = new Map(before.filter(item => item.applicability === 'required') - .map(item => [`${item.scope}:${item.permission.toLowerCase()}`, item.level])); - return after.filter(item => item.applicability === 'required' - && (previous.get(`${item.scope}:${item.permission.toLowerCase()}`) === undefined - || (previous.get(`${item.scope}:${item.permission.toLowerCase()}`) === 'read' && item.level === 'write'))) - .map(item => `${item.scope} ${item.permission} ${item.level}`); -} -function loadSetupOverrides(options) { - const fromFile = options.config ? (0, setup_config_file_1.loadSetupConfigurationOverrides)(options.config) : {}; - const fromFlags = {}; - if (options.prApprovalMode || options.prApprovalCheck?.length || options.prApprovalCoverageCheck || options.prApprovalAttestProducer) { - if (options.prApprovalMode && !['off', 'recommend', 'guarded'].includes(options.prApprovalMode)) { - throw new Error('--pr-approval-mode must be guarded, recommend, or off.'); - } - const checks = options.prApprovalCheck?.map(value => { - const [name, appId, workflowName] = value.split('|').map(item => item.trim()); - return { name, sourceAppId: Number(appId), workflowName }; - }); - fromFlags.pullRequestApproval = { - ...(options.prApprovalMode ? { mode: options.prApprovalMode } : {}), - ...(checks?.length ? { testChecks: checks } : {}), - ...(options.prApprovalAttestProducer ? { producerAttested: true } : {}), - ...(options.prApprovalCoverageCheck ? { coverage: { mode: 'check', checkName: options.prApprovalCoverageCheck } } : {}), - }; - } - if (options.agent) { - if (!['codex', 'opencode', 'cursor'].includes(options.agent)) { - throw new Error('--agent must be one of: codex, opencode, cursor.'); - } - fromFlags.agents = Object.fromEntries(['planner', 'findings', 'reviewer', 'fixer', 'tester'].map(task => [task, { provider: options.agent }])); - } - if (options.features) { - if (options.features.trim().toLowerCase() === 'all') { - fromFlags.features = Object.fromEntries(Object.keys(setup_configuration_policy_1.SETUP_FEATURE_DESCRIPTIONS).map(feature => [feature, true])); - } - else { - const requested = options.features.split(',').map(feature => feature.trim()).filter(Boolean); - const unknown = requested.filter(feature => !Object.prototype.hasOwnProperty.call(setup_configuration_policy_1.SETUP_FEATURE_DESCRIPTIONS, feature)); - if (unknown.length > 0) - throw new Error(`Unknown setup feature(s): ${unknown.join(', ')}.`); - fromFlags.features = Object.fromEntries(Object.keys(setup_configuration_policy_1.SETUP_FEATURE_DESCRIPTIONS).map(feature => [feature, requested.includes(feature)])); - } - } - if (options.issueWorkflows) { - const raw = options.issueWorkflows.trim().toLowerCase(); - const requested = raw === 'all' ? [...issue_workflow_profile_1.ISSUE_WORKFLOW_KINDS] : raw.split(',').map(item => item.trim()).filter(Boolean); - const unknown = requested.filter(item => !issue_workflow_profile_1.ISSUE_WORKFLOW_KINDS.includes(item)); - if (unknown.length > 0) - throw new Error(`Unknown issue workflow(s): ${unknown.join(', ')}.`); - if (new Set(requested).size !== requested.length) - throw new Error('Issue workflow selection cannot contain duplicates.'); - fromFlags.issueWorkflows = { enabled: requested }; - } - if (options.agentGuidance) { - const mode = options.agentGuidance.trim().toLowerCase(); - if (!['prompt', 'create-if-missing', 'disabled'].includes(mode)) - throw new Error('--agent-guidance must be prompt, create-if-missing, or disabled.'); - fromFlags.repositoryAgentGuidance = { agentsPointer: mode, enabled: mode !== 'disabled' }; - } - const storage = {}; - if (options.variablesScope || options.variablesVisibility || Object.keys(options.variableScope ?? {}).length > 0) { - storage.variables = { - ...(options.variablesScope ? { defaultScope: parseScope(options.variablesScope, '--variables-scope') } : {}), - ...(options.variablesVisibility ? { organizationVisibility: parseVisibility(options.variablesVisibility, '--variables-visibility') } : {}), - ...(Object.keys(options.variableScope ?? {}).length > 0 ? { overrides: options.variableScope } : {}), - }; - } - if (options.secretsScope || options.secretsVisibility || Object.keys(options.secretScope ?? {}).length > 0) { - storage.secrets = { - ...(options.secretsScope ? { defaultScope: parseScope(options.secretsScope, '--secrets-scope') } : {}), - ...(options.secretsVisibility ? { organizationVisibility: parseVisibility(options.secretsVisibility, '--secrets-visibility') } : {}), - ...(Object.keys(options.secretScope ?? {}).length > 0 ? { overrides: options.secretScope } : {}), - }; - } - if (Object.keys(storage).length > 0) - fromFlags.storage = storage; - return mergeSetupOverrides(fromFile, fromFlags); -} -function mergeSetupOverrides(fileOverrides, flagOverrides) { - return { - ...fileOverrides, - ...flagOverrides, - features: { ...fileOverrides.features, ...flagOverrides.features }, - agents: { ...fileOverrides.agents, ...flagOverrides.agents }, - repository: { ...fileOverrides.repository, ...flagOverrides.repository }, - ai: { ...fileOverrides.ai, ...flagOverrides.ai }, - pullRequestApproval: { - ...fileOverrides.pullRequestApproval, - ...flagOverrides.pullRequestApproval, - coverage: { ...fileOverrides.pullRequestApproval?.coverage, ...flagOverrides.pullRequestApproval?.coverage }, - }, - projects: { ...fileOverrides.projects, ...flagOverrides.projects }, - issueWorkflows: { ...fileOverrides.issueWorkflows, ...flagOverrides.issueWorkflows }, - repositoryAgentGuidance: { ...fileOverrides.repositoryAgentGuidance, ...flagOverrides.repositoryAgentGuidance }, - storage: { - ...fileOverrides.storage, - ...flagOverrides.storage, - secrets: { ...fileOverrides.storage?.secrets, ...flagOverrides.storage?.secrets, overrides: { ...fileOverrides.storage?.secrets?.overrides, ...flagOverrides.storage?.secrets?.overrides } }, - variables: { ...fileOverrides.storage?.variables, ...flagOverrides.storage?.variables, overrides: { ...fileOverrides.storage?.variables?.overrides, ...flagOverrides.storage?.variables?.overrides } }, - }, - }; -} -function collectScope(value, previous) { - const separator = value.indexOf('='); - if (separator <= 0) - throw new Error('Scope overrides must use NAME=repository or NAME=organization syntax.'); - const name = value.slice(0, separator).trim(); - const scope = value.slice(separator + 1).trim().toLowerCase(); - if (!/^[A-Z][A-Z0-9_]*$/.test(name) || !['repository', 'organization'].includes(scope)) { - throw new Error('Scope overrides must use an uppercase NAME and repository or organization scope.'); - } - return { ...previous, [name]: scope }; -} -function parseScope(value, flag) { - const normalized = value.trim().toLowerCase(); - if (normalized !== 'repository' && normalized !== 'organization') - throw new Error(`${flag} must be repository or organization.`); - return normalized; -} -function parseVisibility(value, flag) { - const normalized = value.trim().toLowerCase(); - if (!['all', 'private', 'selected'].includes(normalized)) - throw new Error(`${flag} must be selected, private, or all.`); - return normalized; -} /***/ }), @@ -66141,6 +66409,191 @@ function registerUpgradeCommand(program) { } +/***/ }), + +/***/ 84136: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.captureSetupApplySnapshot = captureSetupApplySnapshot; +exports.setupApplySnapshotMatches = setupApplySnapshotMatches; +const node_crypto_1 = __nccwpck_require__(6005); +const node_fs_1 = __nccwpck_require__(87561); +const node_path_1 = __nccwpck_require__(49411); +/** Captures only the selected setup paths. Missing files are part of the snapshot. */ +function captureSetupApplySnapshot(repositoryRoot, selectedFiles) { + const root = (0, node_path_1.resolve)(repositoryRoot); + const result = {}; + for (const name of [...new Set(selectedFiles)].sort()) { + const path = (0, node_path_1.resolve)(root, name); + const inside = (0, node_path_1.relative)(root, path); + if ((0, node_path_1.isAbsolute)(name) || !inside || inside === '..' || inside.startsWith(`..${node_path_1.sep}`)) { + throw new Error('The setup plan contains a path outside the repository.'); + } + // A lexically in-repository path can still escape through a parent symlink. + let prefix = root; + for (const segment of inside.split(node_path_1.sep)) { + prefix = (0, node_path_1.resolve)(prefix, segment); + try { + if ((0, node_fs_1.lstatSync)(prefix).isSymbolicLink()) + throw new Error(`Setup path ${name} traverses a symbolic link.`); + } + catch (cause) { + if (cause && typeof cause === 'object' && 'code' in cause && cause.code === 'ENOENT') + break; + throw cause; + } + } + try { + const stat = (0, node_fs_1.lstatSync)(path); + if (stat.isFile() && stat.size <= 5 * 1024 * 1024) { + result[name] = `file:${(0, node_crypto_1.createHash)('sha256').update((0, node_fs_1.readFileSync)(path)).digest('hex')}`; + } + else + throw new Error(`Cannot safely snapshot setup file ${name}.`); + } + catch (cause) { + if (cause && typeof cause === 'object' && 'code' in cause && cause.code === 'ENOENT') + result[name] = 'missing'; + else + throw cause; + } + } + return result; +} +function setupApplySnapshotMatches(repositoryRoot, selectedFiles, expected) { + const current = captureSetupApplySnapshot(repositoryRoot, selectedFiles); + return JSON.stringify(current) === JSON.stringify(expected); +} + + +/***/ }), + +/***/ 99254: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.collectSecret = collectSecret; +exports.collectApprovalCheck = collectApprovalCheck; +exports.loadSetupOverrides = loadSetupOverrides; +exports.collectScope = collectScope; +const setup_config_file_1 = __nccwpck_require__(11196); +const setup_configuration_policy_1 = __nccwpck_require__(56637); +const merge_setup_overrides_policy_1 = __nccwpck_require__(39267); +const issue_workflow_profile_1 = __nccwpck_require__(26744); +function collectSecret(value, previous) { + const separator = value.indexOf('='); + if (separator <= 0) + throw new Error('--secret must use NAME=VALUE syntax.'); + const name = value.slice(0, separator).trim(); + const secret = value.slice(separator + 1); + if (!/^[A-Z][A-Z0-9_]*$/.test(name) || !secret) + throw new Error('--secret must use a non-empty NAME=VALUE with an uppercase secret name.'); + return { ...previous, [name]: secret }; +} +function collectApprovalCheck(value, previous) { + return [...previous, value]; +} +function loadSetupOverrides(options) { + const fromFile = options.config ? (0, setup_config_file_1.loadSetupConfigurationOverrides)(options.config) : {}; + const fromFlags = {}; + if (options.prApprovalMode || options.prApprovalCheck?.length || options.prApprovalCoverageCheck || options.prApprovalAttestProducer) { + if (options.prApprovalMode && !['off', 'recommend', 'guarded'].includes(options.prApprovalMode)) { + throw new Error('--pr-approval-mode must be guarded, recommend, or off.'); + } + const checks = options.prApprovalCheck?.map(value => { + const [name, appId, workflowName] = value.split('|').map(item => item.trim()); + return { name, sourceAppId: Number(appId), workflowName }; + }); + fromFlags.pullRequestApproval = { + ...(options.prApprovalMode ? { mode: options.prApprovalMode } : {}), + ...(checks?.length ? { testChecks: checks } : {}), + ...(options.prApprovalAttestProducer ? { producerAttested: true } : {}), + ...(options.prApprovalCoverageCheck ? { coverage: { mode: 'check', checkName: options.prApprovalCoverageCheck } } : {}), + }; + } + if (options.agent) { + if (!['codex', 'opencode', 'cursor'].includes(options.agent)) { + throw new Error('--agent must be one of: codex, opencode, cursor.'); + } + fromFlags.agents = Object.fromEntries(['planner', 'findings', 'reviewer', 'fixer', 'tester'].map(task => [task, { provider: options.agent }])); + } + if (options.features) { + if (options.features.trim().toLowerCase() === 'all') { + fromFlags.features = Object.fromEntries(Object.keys(setup_configuration_policy_1.SETUP_FEATURE_DESCRIPTIONS).map(feature => [feature, true])); + } + else { + const requested = options.features.split(',').map(feature => feature.trim()).filter(Boolean); + const unknown = requested.filter(feature => !Object.prototype.hasOwnProperty.call(setup_configuration_policy_1.SETUP_FEATURE_DESCRIPTIONS, feature)); + if (unknown.length > 0) + throw new Error(`Unknown setup feature(s): ${unknown.join(', ')}.`); + fromFlags.features = Object.fromEntries(Object.keys(setup_configuration_policy_1.SETUP_FEATURE_DESCRIPTIONS).map(feature => [feature, requested.includes(feature)])); + } + } + if (options.issueWorkflows) { + const raw = options.issueWorkflows.trim().toLowerCase(); + const requested = raw === 'all' ? [...issue_workflow_profile_1.ISSUE_WORKFLOW_KINDS] : raw.split(',').map(item => item.trim()).filter(Boolean); + const unknown = requested.filter(item => !issue_workflow_profile_1.ISSUE_WORKFLOW_KINDS.includes(item)); + if (unknown.length > 0) + throw new Error(`Unknown issue workflow(s): ${unknown.join(', ')}.`); + if (new Set(requested).size !== requested.length) + throw new Error('Issue workflow selection cannot contain duplicates.'); + fromFlags.issueWorkflows = { enabled: requested }; + } + if (options.agentGuidance) { + const mode = options.agentGuidance.trim().toLowerCase(); + if (!['prompt', 'create-if-missing', 'disabled'].includes(mode)) + throw new Error('--agent-guidance must be prompt, create-if-missing, or disabled.'); + fromFlags.repositoryAgentGuidance = { agentsPointer: mode, enabled: mode !== 'disabled' }; + } + const storage = {}; + if (options.variablesScope || options.variablesVisibility || Object.keys(options.variableScope ?? {}).length > 0) { + storage.variables = { + ...(options.variablesScope ? { defaultScope: parseScope(options.variablesScope, '--variables-scope') } : {}), + ...(options.variablesVisibility ? { organizationVisibility: parseVisibility(options.variablesVisibility, '--variables-visibility') } : {}), + ...(Object.keys(options.variableScope ?? {}).length > 0 ? { overrides: options.variableScope } : {}), + }; + } + if (options.secretsScope || options.secretsVisibility || Object.keys(options.secretScope ?? {}).length > 0) { + storage.secrets = { + ...(options.secretsScope ? { defaultScope: parseScope(options.secretsScope, '--secrets-scope') } : {}), + ...(options.secretsVisibility ? { organizationVisibility: parseVisibility(options.secretsVisibility, '--secrets-visibility') } : {}), + ...(Object.keys(options.secretScope ?? {}).length > 0 ? { overrides: options.secretScope } : {}), + }; + } + if (Object.keys(storage).length > 0) + fromFlags.storage = storage; + return (0, merge_setup_overrides_policy_1.mergeSetupOverrides)(fromFile, fromFlags); +} +function collectScope(value, previous) { + const separator = value.indexOf('='); + if (separator <= 0) + throw new Error('Scope overrides must use NAME=repository or NAME=organization syntax.'); + const name = value.slice(0, separator).trim(); + const scope = value.slice(separator + 1).trim().toLowerCase(); + if (!/^[A-Z][A-Z0-9_]*$/.test(name) || !['repository', 'organization'].includes(scope)) { + throw new Error('Scope overrides must use an uppercase NAME and repository or organization scope.'); + } + return { ...previous, [name]: scope }; +} +function parseScope(value, flag) { + const normalized = value.trim().toLowerCase(); + if (normalized !== 'repository' && normalized !== 'organization') + throw new Error(`${flag} must be repository or organization.`); + return normalized; +} +function parseVisibility(value, flag) { + const normalized = value.trim().toLowerCase(); + if (!['all', 'private', 'selected'].includes(normalized)) + throw new Error(`${flag} must be selected, private, or all.`); + return normalized; +} + + /***/ }), /***/ 11196: @@ -66497,13 +66950,9 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SetupCredentialPromptAdapter = exports.SetupTerminalCancelledError = void 0; const setup_prompt_rendering_1 = __nccwpck_require__(83434); const setup_token_permission_presenter_1 = __nccwpck_require__(63206); -class SetupTerminalCancelledError extends Error { - constructor() { - super('Setup input was cancelled.'); - this.name = 'SetupTerminalCancelledError'; - } -} -exports.SetupTerminalCancelledError = SetupTerminalCancelledError; +const setup_interaction_cancelled_error_1 = __nccwpck_require__(38313); +/** @deprecated Use the presentation-neutral cancellation signal in new adapters. */ +exports.SetupTerminalCancelledError = setup_interaction_cancelled_error_1.SetupInteractionCancelledError; class SetupCredentialPromptAdapter { constructor(terminal, credentialValues, confirmUnverifiableWritePermissions = false) { this.terminal = terminal; @@ -66610,7 +67059,7 @@ class SetupCredentialPromptAdapter { `Confirm that the PAT was configured exactly as shown above? ${(0, setup_prompt_rendering_1.color)('[N]', 90)}: `, ].join('\n')); if (result.kind !== 'value') - throw new SetupTerminalCancelledError(); + throw new exports.SetupTerminalCancelledError(); const value = result.value.normalize('NFKC').trim().toLowerCase(); if (!value || ['n', 'no', 'false', '0'].includes(value)) return false; @@ -66654,7 +67103,7 @@ class SetupCredentialPromptAdapter { while (true) { const result = await this.terminal.readText('Expected GitHub bot login (without @): '); if (result.kind !== 'value') - throw new SetupTerminalCancelledError(); + throw new exports.SetupTerminalCancelledError(); const login = result.value.trim(); if (/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/.test(login)) return login; @@ -66691,7 +67140,7 @@ class SetupCredentialPromptAdapter { async readSecret(label) { const result = await this.terminal.readSecret(label); if (result.kind !== 'value') - throw new SetupTerminalCancelledError(); + throw new exports.SetupTerminalCancelledError(); return result.value.trim(); } async readChoice(label, choices, defaultValue) { @@ -66703,7 +67152,7 @@ class SetupCredentialPromptAdapter { `Select 1-${choices.length}${defaultValue ? ` ${(0, setup_prompt_rendering_1.color)(`[${choices.indexOf(defaultValue) + 1}]`, 90)}` : ''}: `, ].join('\n')); if (result.kind !== 'value') - throw new SetupTerminalCancelledError(); + throw new exports.SetupTerminalCancelledError(); if (!result.value.trim() && defaultValue) return defaultValue; const index = Number(result.value) - 1; @@ -67132,6 +67581,78 @@ function formatDefault(value) { } +/***/ }), + +/***/ 53104: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.acquireSetupSessionGuard = acquireSetupSessionGuard; +const node_crypto_1 = __nccwpck_require__(6005); +const node_child_process_1 = __nccwpck_require__(17718); +const node_fs_1 = __nccwpck_require__(87561); +const node_os_1 = __nccwpck_require__(70612); +const node_path_1 = __nccwpck_require__(49411); +/** One cooperative setup process per canonical checkout; no credential is stored in the lock. */ +function acquireSetupSessionGuard(cwd) { + const top = (0, node_child_process_1.execFileSync)('git', ['-C', cwd, 'rev-parse', '--show-toplevel'], { encoding: 'utf8' }).trim(); + const repository = (0, node_fs_1.realpathSync)(top); + const hash = (0, node_crypto_1.createHash)('sha256').update(repository).digest('hex').slice(0, 32); + const lockPath = (0, node_path_1.join)((0, node_os_1.tmpdir)(), `copilot-setup-${hash}.lock`); + const record = { pid: process.pid, nonce: (0, node_crypto_1.randomBytes)(16).toString('hex'), repository }; + for (let attempt = 0; attempt < 2; attempt += 1) { + try { + const fd = (0, node_fs_1.openSync)(lockPath, 'wx', 0o600); + try { + (0, node_fs_1.writeFileSync)(fd, JSON.stringify(record)); + } + finally { + (0, node_fs_1.closeSync)(fd); + } + return () => { + try { + const current = JSON.parse((0, node_fs_1.readFileSync)(lockPath, 'utf8')); + if (current.pid === record.pid && current.nonce === record.nonce && current.repository === record.repository) + (0, node_fs_1.unlinkSync)(lockPath); + } + catch { /* Missing or replaced lock is not ours to remove. */ } + }; + } + catch (cause) { + if (!cause || typeof cause !== 'object' || !('code' in cause) || cause.code !== 'EEXIST') + throw cause; + let existing; + try { + existing = JSON.parse((0, node_fs_1.readFileSync)(lockPath, 'utf8')); + } + catch { + throw new Error('A setup lock exists but cannot be verified. Inspect it before retrying.'); + } + if (!Number.isSafeInteger(existing.pid) || existing.pid <= 0 || existing.repository !== repository || !existing.nonce) { + throw setupLockError('A setup lock has unexpected contents. Inspect it before retrying.', cause); + } + try { + process.kill(existing.pid, 0); + throw setupLockError(`Another setup process (${existing.pid}) is active for this checkout. Finish or stop it before starting a second setup.`, cause); + } + catch (checkError) { + if (!checkError || typeof checkError !== 'object' || !('code' in checkError) || checkError.code !== 'ESRCH') + throw checkError; + } + // Recover only a verified dead owner and only if the lock has not changed meanwhile. + if ((0, node_fs_1.existsSync)(lockPath) && (0, node_fs_1.readFileSync)(lockPath, 'utf8') === JSON.stringify(existing)) + (0, node_fs_1.unlinkSync)(lockPath); + } + } + throw new Error('Could not acquire the local setup lock.'); +} +function setupLockError(message, cause) { + return Object.assign(new Error(message), { cause }); +} + + /***/ }), /***/ 5462: @@ -67505,6 +68026,570 @@ class SetupWorkflowUpdatePromptAdapter { exports.SetupWorkflowUpdatePromptAdapter = SetupWorkflowUpdatePromptAdapter; +/***/ }), + +/***/ 60574: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.WebSetupCredentialPrompt = exports.WebSetupJourneyPresenter = exports.WebSetupPermissionPresenter = exports.WebSetupWorkflowUpdatePrompt = exports.WebSetupPlanConfirmation = exports.WebSetupPlanPresenter = exports.WebSetupQuestionnaireCollector = void 0; +const setup_questionnaire_policy_1 = __nccwpck_require__(6009); +const setup_interaction_cancelled_error_1 = __nccwpck_require__(38313); +const web_setup_bridge_1 = __nccwpck_require__(21518); +class WebSetupQuestionnaireCollector { + constructor(bridge, pass = 1) { + this.bridge = bridge; + this.pass = pass; + } + async collect(initial, context) { + let state = initial; + while (state.terminal === 'collecting' && state.question) { + if (state.validation) + this.bridge.message(state.validation, 'warning'); + const value = await this.bridge.ask({ + kind: 'question', title: (0, setup_questionnaire_policy_1.setupQuestionnaireStateLabel)(state.stateId), + question: state.question, phase: state.phase ?? 'full', pass: this.pass, + }); + state = (0, setup_questionnaire_policy_1.transitionSetupQuestionnaire)(state, value === undefined ? { kind: 'cancel' } : { kind: 'answer', value }, context); + } + return state; + } +} +exports.WebSetupQuestionnaireCollector = WebSetupQuestionnaireCollector; +class WebSetupPlanPresenter { + constructor(bridge) { + this.bridge = bridge; + } + present(plan) { + this.bridge.message(`Plan ready: ${plan.selectedFiles.length} files, ${plan.variables.length} Variables and ${plan.requiredSecrets.length} Secret names. Review it before continuing.`); + } +} +exports.WebSetupPlanPresenter = WebSetupPlanPresenter; +class WebSetupPlanConfirmation { + constructor(bridge) { + this.bridge = bridge; + } + async confirm(plan) { + const response = await this.bridge.ask({ kind: 'plan', title: 'Review your setup plan', plan: (0, web_setup_bridge_1.toWebSetupPlan)(plan) }); + return { kind: response === undefined ? 'cancelled' : response === 'approve' ? 'approved' : 'declined' }; + } +} +exports.WebSetupPlanConfirmation = WebSetupPlanConfirmation; +class WebSetupWorkflowUpdatePrompt { + constructor(bridge) { + this.bridge = bridge; + } + async confirmWorkflowUpdates(comparisons, forcedByFlag) { + const changed = comparisons.filter(item => item.status === 'changed' || item.status === 'unmanaged'); + if (!changed.length) + return false; + if (forcedByFlag) + return true; + const answer = await this.bridge.ask({ + kind: 'confirm', title: 'Update existing workflows?', + description: changed.map(item => `${item.destination} (${item.status})`).join('\n'), + choices: ['Keep existing', 'Update setup-managed workflows'], + }); + if (answer === undefined) + throw new setup_interaction_cancelled_error_1.SetupInteractionCancelledError(); + return answer === 'Update setup-managed workflows'; + } +} +exports.WebSetupWorkflowUpdatePrompt = WebSetupWorkflowUpdatePrompt; +class WebSetupPermissionPresenter { + constructor(bridge) { + this.bridge = bridge; + } + showRequirements(role, requirements) { this.bridge.requirements(role, requirements); } + showDetailedRequirements(role, requirements) { this.bridge.requirements(role, requirements); } + showReport(report) { this.bridge.report(report); } +} +exports.WebSetupPermissionPresenter = WebSetupPermissionPresenter; +class WebSetupJourneyPresenter { + constructor(bridge) { + this.bridge = bridge; + } + present(view) { this.bridge.setJourney(view); } +} +exports.WebSetupJourneyPresenter = WebSetupJourneyPresenter; +class WebSetupCredentialPrompt { + constructor(bridge) { + this.bridge = bridge; + this.guidedSetup = false; + } + get usedGuidedSetupPat() { return this.guidedSetup; } + get guidedWorkflowBotIdentity() { return this.botIdentity; } + configureSetupPatGuide(url) { this.setupGuide = url; } + useManualSetupPat() { this.guidedSetup = false; this.setupGuide = undefined; } + async chooseSetupPatMethod() { + this.guidedSetup = await this.choice('How will you provide your setup PAT?', ['Guided GitHub link', 'Manual PAT']) === 'Guided GitHub link'; + return this.guidedSetup ? 'guided' : 'manual'; + } + async chooseSetupOwnerKind() { + const answer = await this.choice('What kind of GitHub account owns this repository?', ['Organization', 'Personal account', 'Not sure']); + return answer === 'Organization' ? 'Organization' : answer === 'Personal account' ? 'User' : 'unknown'; + } + async reviewSetupPatIntent() { + const answer = await this.choice('Review these provisional setup PAT grants', ['Continue to GitHub', 'Review setup choices again', 'View full permission table', 'Enter a PAT manually']); + return answer === 'Review setup choices again' ? 'revise' : answer === 'View full permission table' ? 'details' + : answer === 'Enter a PAT manually' ? 'manual' : 'continue'; + } + async requestSetupPat() { + return this.secret('Temporary setup PAT', 'Use the operator account in GitHub. Complete 2FA there, switch to Only select repositories, select this repository, and copy the generated token here. This token is for this run only; delete it in GitHub afterwards.', this.guidedSetup ? this.setupGuide : undefined); + } + async confirmGuidedSetupAccount(account) { + if (!this.guidedSetup) + return true; + if (!account) + return false; + return await this.choice(`GitHub authenticated the setup PAT as @${account}. Is that the intended operator account?`, ['Yes, continue', 'No, stop']) === 'Yes, continue'; + } + showUpdatedSetupPatLink(url, stage, delta) { + this.bridge.message(`Setup PAT ${stage === 'final' ? 'permissions changed' : 'access failed'}. No setup mutation started. ${delta?.join(', ') ?? ''} Create a corrected PAT using the updated GitHub link.`, 'warning', url); + } + showSetupPatCleanupReminder() { + if (this.guidedSetup) + this.bridge.message('Delete the temporary setup PAT in GitHub Settings after this run. Closing Copilot does not revoke it.', 'warning', 'https://github.com/settings/personal-access-tokens'); + } + async confirmUnverifiableTokenPermissions(report) { + const writes = report.checks.filter(item => item.applicability === 'required' && item.level === 'write' && item.status === 'unverifiable'); + if (!report.confirmationRequired || writes.length === 0) + return false; + return await this.choice('GitHub cannot safely prove these write grants without a mutation. Confirm they are configured exactly as shown.', ['No, stop', 'Yes, I checked them']) === 'Yes, I checked them'; + } + configureWorkflowPatGuide(url, resolveIdentity, requirements) { + this.workflowGuide = url; + this.resolveBot = resolveIdentity; + this.workflowRequirements = requirements; + } + explainCredentialSeparation(requirements) { + this.bridge.message(`The bot PAT is separate from your setup PAT. Runtime credentials (${requirements.map(item => item.name).join(', ')}) become GitHub Actions Secrets; existing Secret values cannot be read back. This browser flow will not dispatch or install a credential-health workflow before Apply. Re-enter an existing bot PAT so its grants can be audited.`, 'info'); + } + async requestWorkflowPat(requirement, current) { + let guide; + let botInfo = ''; + if (this.workflowGuide) { + const method = await this.choice('How will you provide the bot PAT?', ['Guided GitHub link', 'Manual PAT']); + if (method === 'Guided GitHub link') { + const login = await this.text('Expected GitHub bot login', 'Enter the bot account login, without @. We will verify its numeric account ID against the token.'); + if (!login || !/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/.test(login)) + throw new Error('Enter a valid GitHub bot login.'); + this.botIdentity = await this.resolveBot(login); + guide = this.workflowGuide; + botInfo = `Expected bot account: @${this.botIdentity.login} (GitHub ID ${this.botIdentity.id}). Open GitHub as this account, not the setup operator. `; + } + else if (this.workflowRequirements) + this.bridge.requirements('workflow', this.workflowRequirements); + } + const value = await this.secret(`${requirement.name} — bot account PAT`, `${botInfo}Use the bot account, select only the intended repository and review all grants. Suggested expiry is 90 days. ${current ? `Existing Secret: ${current.status}; its value cannot be read back.` : ''}`, guide); + return value ? { name: requirement.name, value } : undefined; + } + async requestApiKey(requirement, current) { + const value = await this.secret(`${requirement.name} — ${requirement.provider ?? 'provider'} API key`, current?.message, undefined, Boolean(requirement.alternativeGroups?.length)); + return value ? { name: requirement.name, value } : undefined; + } + async chooseExistingCredential(requirement, check) { + const answer = await this.choice(`Existing ${requirement.name}: ${check.status}`, ['keep', 'replace', 'skip'], check.message); + return answer; + } + showCredentialChecks(checks) { + this.bridge.message(checks.map(item => `${item.name}: ${item.status} — ${item.message}`).join('\n'), checks.some(item => item.status === 'invalid') ? 'warning' : 'success'); + } + async choice(title, choices, description) { + const answer = await this.bridge.ask({ kind: 'choice', title, choices, description }); + if (answer === undefined) + throw new setup_interaction_cancelled_error_1.SetupInteractionCancelledError(); + if (!choices.includes(answer)) + throw new Error('Invalid setup choice.'); + return answer; + } + async text(title, description) { + const answer = await this.bridge.ask({ kind: 'text', title, description }); + if (answer === undefined) + throw new setup_interaction_cancelled_error_1.SetupInteractionCancelledError(); + return answer.trim(); + } + async secret(title, description, link, optional = false) { + const answer = await this.bridge.ask({ kind: 'secret', title, description, optional, link }); + if (answer === undefined) + throw new setup_interaction_cancelled_error_1.SetupInteractionCancelledError(); + return answer.trim(); + } +} +exports.WebSetupCredentialPrompt = WebSetupCredentialPrompt; + + +/***/ }), + +/***/ 21518: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.WebSetupBridge = void 0; +exports.toWebSetupPlan = toWebSetupPlan; +const node_crypto_1 = __nccwpck_require__(6005); +/** A one-run, in-memory handoff. Values submitted by the browser are never part of a view. */ +class WebSetupBridge { + constructor(repository) { + this.revision = 0; + this.subscribers = new Set(); + this.takeoverTicket = (0, node_crypto_1.randomBytes)(32).toString('hex'); + this.bootstrapped = false; + this.view = { revision: 0, repository }; + } + snapshot() { return this.view; } + setRepository(repository) { this.publish({ repository }); } + subscribe(listener) { + this.subscribers.add(listener); + return () => this.subscribers.delete(listener); + } + bootstrap() { + if (!this.controller) + this.controller = (0, node_crypto_1.randomBytes)(32).toString('hex'); + // A second tab starts read-only. Its explicit takeover rotates the controller capability. + const first = !this.bootstrapped; + this.bootstrapped = true; + return { controller: first, ...(first ? { capability: this.controller } : {}), takeoverTicket: this.takeoverTicket }; + } + takeOver(ticket) { + if (!sameCapability(ticket, this.takeoverTicket)) + return undefined; + this.controller = (0, node_crypto_1.randomBytes)(32).toString('hex'); + this.takeoverTicket = (0, node_crypto_1.randomBytes)(32).toString('hex'); + this.publish({ message: { tone: 'info', text: 'Control moved to this tab. The previous tab is now read-only.' } }); + return this.controller; + } + isController(capability) { + return Boolean(this.controller && sameCapability(capability, this.controller)); + } + async ask(prompt) { + if (this.pending || this.view.outcome) + throw new Error('A setup decision is already pending or the session has ended.'); + const revision = this.revision + 1; + this.publish({ prompt, promptRevision: revision }); + return new Promise(resolve => { this.pending = { revision, resolve }; }); + } + answer(revision, value) { + if (!this.pending || this.pending.revision !== revision || this.view.outcome) + return false; + const prompt = this.view.prompt; + if (prompt && (prompt.kind === 'choice' || prompt.kind === 'confirm') && !prompt.choices.includes(value)) + return false; + const pending = this.pending; + this.pending = undefined; + this.lastAnsweredRevision = revision; + this.publish({ prompt: undefined, promptRevision: undefined }); + pending.resolve(value); + return true; + } + wasAnswered(revision) { return this.lastAnsweredRevision === revision; } + cancel() { + if (this.view.journey?.mutationStarted || this.view.outcome) + return false; + const pending = this.pending; + this.pending = undefined; + this.publish({ prompt: undefined, promptRevision: undefined, outcome: 'cancelled', message: { tone: 'warning', text: 'Setup cancelled before applying further changes. Any PAT created at GitHub still exists until you delete it there.' } }); + pending?.resolve(undefined); + return true; + } + setJourney(journey) { this.publish({ journey }); } + message(text, tone = 'info', link) { + this.publish({ message: { tone, text, ...(link ? { link } : {}) } }); + } + requirements(role, requirements) { + this.publish({ permissions: { role, requirements, report: undefined } }); + } + report(report) { + this.publish({ permissions: { role: report.role, requirements: this.view.permissions?.requirements, report } }); + } + finish(outcome, text) { + if (this.view.outcome) + return; + this.pending?.resolve(undefined); + this.pending = undefined; + this.publish({ prompt: undefined, promptRevision: undefined, outcome, message: { tone: outcome === 'complete' || outcome === 'dry-run' ? 'success' : 'warning', text } }); + } + publish(change) { + this.revision += 1; + this.view = { ...this.view, ...change, revision: this.revision }; + for (const listener of this.subscribers) + listener(this.view); + } +} +exports.WebSetupBridge = WebSetupBridge; +function sameCapability(provided, expected) { + if (!/^[a-f0-9]{64}$/.test(provided)) + return false; + return (0, node_crypto_1.timingSafeEqual)(Buffer.from(provided, 'hex'), Buffer.from(expected, 'hex')); +} +function toWebSetupPlan(plan) { + return { + files: plan.selectedFiles, + workflows: plan.workflowFiles, + variables: plan.variables.map(variable => variable.name), + secrets: plan.requiredSecrets, + warnings: plan.warnings, + }; +} + + +/***/ }), + +/***/ 63080: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.startWebSetupServer = startWebSetupServer; +exports.openWebSetupBrowser = openWebSetupBrowser; +const node_http_1 = __nccwpck_require__(88849); +const promises_1 = __nccwpck_require__(93977); +const node_path_1 = __nccwpck_require__(49411); +const node_child_process_1 = __nccwpck_require__(17718); +const MAX_BODY_BYTES = 8192; +const MAX_ANSWER_LENGTH = 4096; +const CSP = "default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' data:; connect-src 'self'; base-uri 'none'; form-action 'self'; frame-ancestors 'none'"; +/** Transport only: setup policy and credential decisions live behind the bridge. */ +async function startWebSetupServer(bridge, assets = (0, node_path_1.join)(__dirname, '..', 'web')) { + const assetRoot = await (0, promises_1.realpath)(assets); + if (!(await (0, promises_1.realpath)((0, node_path_1.join)(assetRoot, 'index.html'))).startsWith(`${assetRoot}${node_path_1.sep}`)) { + throw new Error('Local setup index must be inside its packaged asset directory.'); + } + const indexHtml = (await (0, promises_1.readFile)((0, node_path_1.join)(assetRoot, 'index.html'))).toString('utf8'); + const allowedAssets = new Set([...indexHtml.matchAll(/(?:\.\/)?(assets\/[A-Za-z0-9._-]+\.(?:js|css))/g)] + .map(match => match[1])); + if (allowedAssets.size < 2) + throw new Error('Local setup web assets are incomplete. Reinstall Copilot or use terminal setup.'); + for (const asset of allowedAssets) { + const packagedPath = await (0, promises_1.realpath)((0, node_path_1.join)(assetRoot, asset)); + if (!packagedPath.startsWith(`${assetRoot}${node_path_1.sep}`)) + throw new Error('Local setup asset escapes its packaged directory.'); + await (0, promises_1.readFile)(packagedPath); + } + let closeResolver = () => undefined; + const closed = new Promise(resolveClosed => { closeResolver = resolveClosed; }); + let closing = false; + let idleTimer; + let resultTimer; + const armIdle = () => { + if (idleTimer) + clearTimeout(idleTimer); + idleTimer = setTimeout(() => { + if (!bridge.snapshot().journey?.mutationStarted && !bridge.snapshot().outcome) { + bridge.finish('blocked', 'This local setup session expired after 30 minutes without a decision. Start a new setup run; GitHub PATs are not revoked automatically.'); + } + }, 30 * 60 * 1000); + }; + const server = (0, node_http_1.createServer)(async (request, response) => { + const address = server.address(); + const origin = `http://127.0.0.1:${typeof address === 'object' && address ? address.port : 0}`; + const host = `127.0.0.1:${typeof address === 'object' && address ? address.port : 0}`; + response.setHeader('Content-Security-Policy', CSP); + response.setHeader('X-Content-Type-Options', 'nosniff'); + response.setHeader('Referrer-Policy', 'no-referrer'); + response.setHeader('Cache-Control', 'no-store'); + response.setHeader('Cross-Origin-Resource-Policy', 'same-origin'); + response.setHeader('X-Frame-Options', 'DENY'); + try { + if (request.headers.host !== host || request.headers['x-forwarded-host'] || request.headers.forwarded + || request.headers['x-forwarded-proto'] || request.headers['sec-fetch-site'] === 'cross-site') { + respond(response, 403, { error: 'Invalid local host or request context.' }); + return; + } + if (request.method === 'POST' && (request.headers.origin !== origin + || (request.headers.referer && !request.headers.referer.startsWith(`${origin}/`)))) { + respond(response, 403, { error: 'Invalid request origin.' }); + return; + } + if (request.method === 'GET' && request.url === '/api/bootstrap') { + respond(response, 200, bridge.bootstrap()); + return; + } + if (request.method === 'GET' && request.url === '/api/state') { + respond(response, 200, bridge.snapshot()); + return; + } + if (request.method === 'POST' && request.url === '/api/takeover') { + if (request.headers['content-type'] !== 'application/json') { + respond(response, 415, { error: 'JSON required.' }); + return; + } + const body = await readJson(request); + const ticket = typeof body.ticket === 'string' ? body.ticket : ''; + const capability = bridge.takeOver(ticket); + if (capability) + armIdle(); + respond(response, capability ? 200 : 403, capability ? { capability } : { error: 'Invalid takeover ticket.' }); + return; + } + if (request.method === 'POST' && request.url === '/api/answer') { + if (!bridge.isController(String(request.headers['x-setup-capability'] ?? ''))) { + respond(response, 403, { error: 'This tab is read-only.' }); + return; + } + if (request.headers['content-type'] !== 'application/json') { + respond(response, 415, { error: 'JSON required.' }); + return; + } + const body = await readJson(request); + if (!Number.isSafeInteger(body.revision) || body.revision <= 0 || typeof body.value !== 'string' || body.value.length > MAX_ANSWER_LENGTH) { + respond(response, 400, { error: 'Invalid answer.' }); + return; + } + if (!bridge.isController(String(request.headers['x-setup-capability'] ?? ''))) { + respond(response, 403, { error: 'Control moved to another tab.' }); + return; + } + const accepted = bridge.answer(body.revision, body.value); + const duplicate = !accepted && bridge.wasAnswered(body.revision); + if (accepted) + armIdle(); + respond(response, accepted || duplicate ? 200 : 409, accepted || duplicate ? { accepted: true, ...(duplicate ? { duplicate: true } : {}) } + : { error: 'This question changed. Refresh the current state.' }); + return; + } + if (request.method === 'POST' && request.url === '/api/cancel') { + if (!bridge.isController(String(request.headers['x-setup-capability'] ?? ''))) { + respond(response, 403, { error: 'This tab is read-only.' }); + return; + } + if (request.headers['content-type'] !== 'application/json') { + respond(response, 415, { error: 'JSON required.' }); + return; + } + await readJson(request); + if (!bridge.isController(String(request.headers['x-setup-capability'] ?? ''))) { + respond(response, 403, { error: 'Control moved to another tab.' }); + return; + } + const cancelled = bridge.cancel(); + respond(response, cancelled ? 200 : 409, cancelled ? { cancelled: true } : { error: 'This setup has already started applying or ended.' }); + return; + } + if (request.method === 'POST' && request.url === '/api/close') { + if (!bridge.isController(String(request.headers['x-setup-capability'] ?? '')) || !bridge.snapshot().outcome) { + respond(response, 403, { error: 'Only the controller can close a finished session.' }); + return; + } + if (request.headers['content-type'] !== 'application/json') { + respond(response, 415, { error: 'JSON required.' }); + return; + } + await readJson(request); + if (!bridge.isController(String(request.headers['x-setup-capability'] ?? ''))) { + respond(response, 403, { error: 'Control moved to another tab.' }); + return; + } + respond(response, 200, { closed: true }); + setImmediate(() => void close()); + return; + } + if (request.method !== 'GET') { + respond(response, 405, { error: 'Method not allowed.' }); + return; + } + const pathname = request.url ?? ''; + if (pathname !== '/' && !/^\/assets\/[A-Za-z0-9._-]+$/.test(pathname)) { + respond(response, 404, { error: 'Not found.' }); + return; + } + const relative = pathname === '/' ? 'index.html' : pathname.slice(1); + if (relative !== 'index.html' && !allowedAssets.has(relative)) { + respond(response, 404, { error: 'Not found.' }); + return; + } + const file = (0, node_path_1.resolve)(assetRoot, relative); + const realFile = await (0, promises_1.realpath)(file); + if (!realFile.startsWith(`${assetRoot}${node_path_1.sep}`)) { + respond(response, 404, { error: 'Not found.' }); + return; + } + const content = await (0, promises_1.readFile)(realFile); + const contentType = file.endsWith('.js') ? 'text/javascript; charset=utf-8' + : file.endsWith('.css') ? 'text/css; charset=utf-8' + : 'text/html; charset=utf-8'; + response.writeHead(200, { 'Content-Type': contentType }); + response.end(content); + } + catch { + if (!response.headersSent) + respond(response, 400, { error: 'Invalid local request.' }); + else + response.end(); + } + }); + server.requestTimeout = 15000; + server.headersTimeout = 15000; + server.maxRequestsPerSocket = 250; + await new Promise((resolveListen, reject) => { + server.once('error', reject); + server.listen(0, '127.0.0.1', () => { server.off('error', reject); resolveListen(); }); + }); + const address = server.address(); + if (!address || typeof address === 'string') + throw new Error('Unable to bind local setup server.'); + const url = `http://127.0.0.1:${address.port}/`; + const close = async () => { + if (closing) + return closed; + closing = true; + if (idleTimer) + clearTimeout(idleTimer); + if (hardTimer) + clearTimeout(hardTimer); + if (resultTimer) + clearTimeout(resultTimer); + unsubscribe?.(); + bridge.cancel(); + server.closeAllConnections(); + await new Promise(resolveClose => server.close(() => resolveClose())); + closeResolver(); + }; + armIdle(); + const hardTimer = setTimeout(() => { + if (!bridge.snapshot().journey?.mutationStarted && !bridge.snapshot().outcome) { + bridge.finish('blocked', 'This local setup session reached its four-hour limit. Start a new run; no prior approval can be replayed.'); + } + }, 4 * 60 * 60 * 1000); + const unsubscribe = bridge.subscribe(view => { + if (view.outcome && !resultTimer) + resultTimer = setTimeout(() => void close(), 10 * 60 * 1000); + }); + return { url, closed, close }; +} +function respond(response, status, body) { + response.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8' }); + response.end(JSON.stringify(body)); +} +async function readJson(request) { + let size = 0; + const chunks = []; + for await (const chunk of request) { + const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk); + size += buffer.length; + if (size > MAX_BODY_BYTES) + throw new Error('Body too large.'); + chunks.push(buffer); + } + const parsed = JSON.parse(Buffer.concat(chunks).toString('utf8')); + if (!parsed || Array.isArray(parsed) || typeof parsed !== 'object') + throw new Error('JSON object required.'); + return parsed; +} +function openWebSetupBrowser(url) { + const command = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'cmd' : 'xdg-open'; + const args = process.platform === 'win32' ? ['/c', 'start', '', url] : [url]; + const child = (0, node_child_process_1.spawn)(command, args, { stdio: 'ignore', detached: true, windowsHide: true }); + child.on('error', () => { }); + child.unref(); +} + + /***/ }), /***/ 21307: @@ -67518,6 +68603,7 @@ exports.getGitInfo = getGitInfo; exports.getCurrentBranch = getCurrentBranch; exports.getCurrentHeadSha = getCurrentHeadSha; exports.isInsideGitRepo = isInsideGitRepo; +exports.getGitRepositoryRoot = getGitRepositoryRoot; exports.isGitRepositoryRoot = isGitRepositoryRoot; const child_process_1 = __nccwpck_require__(32081); const node_fs_1 = __nccwpck_require__(87561); @@ -67567,10 +68653,14 @@ function isInsideGitRepo(cwd) { return false; } } +/** Canonical checkout root for plans whose file paths are repository-relative. */ +function getGitRepositoryRoot(cwd) { + const root = (0, child_process_1.execSync)('git rev-parse --show-toplevel', { cwd, stdio: 'pipe' }).toString().trim(); + return (0, node_fs_1.realpathSync)(root); +} function isGitRepositoryRoot(cwd) { try { - const root = (0, child_process_1.execSync)('git rev-parse --show-toplevel', { cwd, stdio: 'pipe' }).toString().trim(); - return (0, node_fs_1.realpathSync)(root) === (0, node_fs_1.realpathSync)(cwd); + return getGitRepositoryRoot(cwd) === (0, node_fs_1.realpathSync)(cwd); } catch { return false; @@ -81763,9 +82853,11 @@ const github_identity_client_factory_1 = __nccwpck_require__(93081); const setup_remote_credential_health_adapter_1 = __nccwpck_require__(1489); const octokit_credential_health_adapter_1 = __nccwpck_require__(41760); const setup_token_permissions_composition_root_1 = __nccwpck_require__(64132); -function createSetupCredentialsUseCase(prompt, permissionPresenter) { +function createSetupCredentialsUseCase(prompt, permissionPresenter, options = {}) { const secretNames = new repository_variables_repository_1.RepositorySecretNamesQueryRepository((0, github_identity_client_factory_1.createRepositoryVariablesClient)()); - return new setup_credentials_use_case_1.SetupCredentialsUseCase(prompt, new setup_credential_validation_adapter_1.SetupCredentialValidationAdapter(), secretNames, new setup_remote_credential_health_adapter_1.SetupRemoteCredentialHealthBootstrapAdapter(new octokit_credential_health_adapter_1.OctokitCredentialHealthClientAdapter()), (0, setup_token_permissions_composition_root_1.createSetupTokenPermissionsUseCase)(), permissionPresenter); + return new setup_credentials_use_case_1.SetupCredentialsUseCase(prompt, new setup_credential_validation_adapter_1.SetupCredentialValidationAdapter(), secretNames, options.allowPreApplyHealthWorkflow === false + ? undefined + : new setup_remote_credential_health_adapter_1.SetupRemoteCredentialHealthBootstrapAdapter(new octokit_credential_health_adapter_1.OctokitCredentialHealthClientAdapter()), (0, setup_token_permissions_composition_root_1.createSetupTokenPermissionsUseCase)(), permissionPresenter); } function createSetupRemoteConfigurationReadPort() { return new repository_variables_repository_1.SetupRemoteConfigurationQueryRepository((0, github_identity_client_factory_1.createRepositoryVariablesClient)()); @@ -91083,8 +92175,8 @@ function isInside(root, candidate) { function matchesFieldBoundary(field, relativePath) { if (field === 'specs') return /^specs\/(?!README\.md$|_template\.md$|CATALOG\.md$).+\.md$/.test(relativePath); if (field === 'workflows') return /^(?:\.github|setup)\/workflows\/.+\.ya?ml$/.test(relativePath); - if (field === 'entrypoints') return /^(?:src\/.+|action\.yml|package\.json)$/.test(relativePath); - if (field === 'code') return /^(?:src|scripts)\//.test(relativePath); + if (field === 'entrypoints') return /^(?:src\/.+|web\/src\/main\.ts|action\.yml|package\.json)$/.test(relativePath); + if (field === 'code') return /^(?:(?:src|scripts)\/|web\/src\/.+\.(?:ts|svelte|css)$)/.test(relativePath); if (field === 'tests') return /^src\/.*(?:__tests__\/.*\.test\.ts|\.test\.ts)$/.test(relativePath); if (field === 'documentation') return /^(?:docs\/.*\.(?:md|mdx)|README\.md|CONTRIBUTING\.md)$/.test(relativePath); return false; diff --git a/build/github_action/index.js b/build/github_action/index.js index 920afe545..94a7c5ecd 100644 --- a/build/github_action/index.js +++ b/build/github_action/index.js @@ -49091,6 +49091,7 @@ function normalizeSetupConfigurationLocales(configuration) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildSetupCredentialRequirements = void 0; exports.buildSetupPlan = buildSetupPlan; +exports.setupPlanGuardPaths = setupPlanGuardPaths; exports.buildSetupRepositoryVariables = buildSetupRepositoryVariables; exports.buildSetupActionInputs = buildSetupActionInputs; const pull_request_description_1 = __nccwpck_require__(45315); @@ -49140,6 +49141,31 @@ function buildSetupPlan(configuration, mergeQueueReadiness = [], approvalReadine warnings: buildSetupWarnings(configuration), }; } +/** Actual checkout destinations covered by a web Apply drift check. + * The presentation plan uses package-source labels for workflows/forms; + * comparing those labels as checkout paths would silently miss local edits. + */ +function setupPlanGuardPaths(plan) { + const selected = plan.selectedFiles.map(file => { + if (file.startsWith('workflows/')) + return `.github/${file}`; + if (file.startsWith('ISSUE_TEMPLATE/')) + return `.github/${file}`; + if (file === 'pull_request_template.md') + return '.github/pull_request_template.md'; + if (file === 'AGENTS.md (managed pointer only)') + return 'AGENTS.md'; + return file; + }); + // Deselected managed assets can be retired to setup-backups during Apply. + const retiredCandidates = [ + ...['config.yml', ...issue_workflow_profile_1.ISSUE_WORKFLOW_KINDS.map(kind => issue_workflow_profile_1.ISSUE_WORKFLOW_CATALOG[kind].formFile)] + .map(file => `.github/ISSUE_TEMPLATE/${file}`), + ...['release_workflow.yml', 'hotfix_workflow.yml', 'copilot_deployment_orchestration.yml'] + .map(file => `.github/workflows/${file}`), + ]; + return [...new Set([...selected, ...retiredCandidates])].sort(); +} function buildSetupRepositoryVariables(configuration) { const variables = []; const add = (name, value) => { @@ -65211,6 +65237,7 @@ exports.getGitInfo = getGitInfo; exports.getCurrentBranch = getCurrentBranch; exports.getCurrentHeadSha = getCurrentHeadSha; exports.isInsideGitRepo = isInsideGitRepo; +exports.getGitRepositoryRoot = getGitRepositoryRoot; exports.isGitRepositoryRoot = isGitRepositoryRoot; const child_process_1 = __nccwpck_require__(32081); const node_fs_1 = __nccwpck_require__(87561); @@ -65260,10 +65287,14 @@ function isInsideGitRepo(cwd) { return false; } } +/** Canonical checkout root for plans whose file paths are repository-relative. */ +function getGitRepositoryRoot(cwd) { + const root = (0, child_process_1.execSync)('git rev-parse --show-toplevel', { cwd, stdio: 'pipe' }).toString().trim(); + return (0, node_fs_1.realpathSync)(root); +} function isGitRepositoryRoot(cwd) { try { - const root = (0, child_process_1.execSync)('git rev-parse --show-toplevel', { cwd, stdio: 'pipe' }).toString().trim(); - return (0, node_fs_1.realpathSync)(root) === (0, node_fs_1.realpathSync)(cwd); + return getGitRepositoryRoot(cwd) === (0, node_fs_1.realpathSync)(cwd); } catch { return false; @@ -84025,8 +84056,8 @@ function isInside(root, candidate) { function matchesFieldBoundary(field, relativePath) { if (field === 'specs') return /^specs\/(?!README\.md$|_template\.md$|CATALOG\.md$).+\.md$/.test(relativePath); if (field === 'workflows') return /^(?:\.github|setup)\/workflows\/.+\.ya?ml$/.test(relativePath); - if (field === 'entrypoints') return /^(?:src\/.+|action\.yml|package\.json)$/.test(relativePath); - if (field === 'code') return /^(?:src|scripts)\//.test(relativePath); + if (field === 'entrypoints') return /^(?:src\/.+|web\/src\/main\.ts|action\.yml|package\.json)$/.test(relativePath); + if (field === 'code') return /^(?:(?:src|scripts)\/|web\/src\/.+\.(?:ts|svelte|css)$)/.test(relativePath); if (field === 'tests') return /^src\/.*(?:__tests__\/.*\.test\.ts|\.test\.ts)$/.test(relativePath); if (field === 'documentation') return /^(?:docs\/.*\.(?:md|mdx)|README\.md|CONTRIBUTING\.md)$/.test(relativePath); return false; diff --git a/build/web/assets/index-DBy8dnu-.js b/build/web/assets/index-DBy8dnu-.js new file mode 100644 index 000000000..f8a1cbfaf --- /dev/null +++ b/build/web/assets/index-DBy8dnu-.js @@ -0,0 +1,2 @@ +(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})(),typeof window<`u`&&((window.__svelte??={}).v??=new Set).add(`5`);var e=!1;function t(){e=!0}t();var n={},r=Symbol(`uninitialized`),i=`http://www.w3.org/1999/xhtml`,a=Array.isArray,o=Array.prototype.indexOf,s=Array.prototype.includes,c=Array.from,l=Object.defineProperty,u=Object.getOwnPropertyDescriptor,d=Object.getOwnPropertyDescriptors,f=Object.prototype,p=Array.prototype,m=Object.getPrototypeOf,h=Object.isExtensible,g=()=>{};function _(e){return e()}function v(e){for(var t=0;t{e=n,t=r}),resolve:e,reject:t}}var b=1024,x=2048,S=4096,ee=8192,te=16384,ne=32768,re=1<<25,ie=65536,ae=1<<18,oe=1<<19,se=1<<20,ce=1<<25,le=1<<21,ue=1<<22,de=1<<23,fe=Symbol(`$state`),pe=Symbol(`component`),me=Symbol(`legacy props`),he=Symbol(``),ge=Symbol(`attributes`),_e=Symbol(`class`),ve=Symbol(`style`),ye=Symbol(`text`),be=Symbol(`form reset`),xe=new class extends Error{name=`StaleReactionError`;message="The reaction that called `getAbortSignal()` was re-run or destroyed"},Se=!!globalThis.document?.contentType&&globalThis.document.contentType.includes(`xml`);function Ce(){console.warn(`https://svelte.dev/e/derived_inert`)}function we(e){console.warn(`https://svelte.dev/e/hydration_mismatch`)}function Te(){console.warn(`https://svelte.dev/e/select_multiple_invalid_value`)}function Ee(){console.warn(`https://svelte.dev/e/svelte_boundary_reset_noop`)}var C=!1;function De(e){C=e}var w;function T(e){if(e===null)throw we(),n;return w=e}function Oe(){return T(on(w))}function E(e){if(C){if(on(w)!==null)throw we(),n;w=e}}function ke(e=1){if(C){for(var t=e,n=w;t--;)n=on(n);w=n}}function Ae(e=!0){for(var t=0,n=w;;){if(n.nodeType===8){var r=n.data;if(r===`]`){if(t===0)return n;--t}else(r===`[`||r===`[!`||r[0]===`[`&&!isNaN(Number(r.slice(1))))&&(t+=1)}var i=on(n);e&&n.remove(),n=i}}function je(e){if(!e||e.nodeType!==8)throw we(),n;return e.data}function Me(e){return e===this.v}function Ne(e,t){return e==e?e!==t||typeof e==`object`&&!!e||typeof e==`function`:t==t}function Pe(e){return!Ne(e,this.v)}function Fe(e){throw Error(`https://svelte.dev/e/lifecycle_outside_component`)}function Ie(){throw Error(`https://svelte.dev/e/async_derived_orphan`)}function Le(e,t,n){throw Error(`https://svelte.dev/e/each_key_duplicate`)}function Re(e){throw Error(`https://svelte.dev/e/effect_in_teardown`)}function ze(){throw Error(`https://svelte.dev/e/effect_in_unowned_derived`)}function Be(e){throw Error(`https://svelte.dev/e/effect_orphan`)}function Ve(){throw Error(`https://svelte.dev/e/effect_update_depth_exceeded`)}function He(e){throw Error(`https://svelte.dev/e/props_invalid_value`)}function Ue(){throw Error(`https://svelte.dev/e/state_descriptors_fixed`)}function We(){throw Error(`https://svelte.dev/e/state_prototype_fixed`)}function Ge(){throw Error(`https://svelte.dev/e/state_unsafe_mutation`)}function Ke(){throw Error(`https://svelte.dev/e/svelte_boundary_reset_onerror`)}var D=null;function qe(e){D=e}function O(t,n=!1,r){D={p:D,i:!1,c:null,e:null,s:t,x:null,r:H,l:e&&!n?{s:null,u:null,$:[]}:null}}function k(e){var t=D,n=t.e;if(n!==null){t.e=null;for(var r of n)bn(r)}return e!==void 0&&(t.x=e),t.i=!0,D=t.p,Je(e)}function Je(e={}){return l(e,pe,{value:!0}),e}function Ye(){return!e||D!==null&&D.l===null}var Xe=[];function Ze(){var e=Xe;Xe=[],v(e)}function Qe(e){if(Xe.length===0&&!wt){var t=Xe;queueMicrotask(()=>{t===Xe&&Ze()})}Xe.push(e)}function $e(){for(;Xe.length>0;)Ze()}var et=~(x|S|b);function A(e,t){e.f=e.f&et|t}function tt(e){e.f&512||e.deps===null?A(e,b):A(e,S)}function nt(e,t,n){e.f&2048?t.add(e):e.f&4096&&n.add(e),A(e,b)}var rt=!1;function it(){rt||(rt=!0,document.addEventListener(`reset`,e=>{Promise.resolve().then(()=>{if(!e.defaultPrevented)for(let t of e.target.elements)t[be]?.()})},{capture:!0}))}function at(e){var t=B,n=H;V(null),Wn(null);try{return e()}finally{V(t),Wn(n)}}function ot(e,t,n,r=n){e.addEventListener(t,()=>at(n));let i=e[be];e[be]=i?()=>{i(),r(!0)}:()=>r(!0),it()}function st(e,t,n,r){let i=Ye()?dt:mt;var a=e.filter(e=>!e.settled),o=t.map(i);if(n.length===0&&a.length===0){r(o);return}var s=H,c=ct(),l=a.length===1?a[0].promise:a.length>1?Promise.all(a.map(e=>e.promise)):null;function u(e){if(!(s.f&16384)){c();try{r([...o,...e])}catch(e){pn(e,s)}lt()}}var d=ut();if(n.length===0){l.then(()=>u([])).finally(d);return}function f(){Promise.all(n.map(e=>pt(e))).then(u).catch(e=>pn(e,s)).finally(d)}l?l.then(()=>{c(),f(),lt()}):f()}function ct(){var e=H,t=B,n=D,r=j;return function(i=!0){Wn(e),V(t),qe(n),i&&!(e.f&16384)&&(r?.activate(),r?.apply())}}function lt(e=!0){Wn(null),V(null),qe(null),e&&j?.deactivate()}function ut(){var e=H,t=e.b,n=j,r=!!t?.is_rendered();return t?.update_pending_count(1,n),n.increment(r,e),()=>{t?.update_pending_count(-1,n),n.decrement(r,e)}}function dt(e){var t=2|x;return H!==null&&(H.f|=oe),{ctx:D,deps:null,effects:null,equals:Me,f:t,fn:e,reactions:null,rv:0,v:r,wv:0,parent:H,ac:null}}var ft=Symbol(`obsolete`);function pt(e,t,n){let i=H;i===null&&Ie();var a=void 0,o=Vt(r),s=!B,c=new Set;return En(()=>{var t=H,n=y();a=n.promise;try{Promise.resolve(e()).then(n.resolve,e=>{e!==xe&&n.reject(e)}).finally(lt)}catch(e){n.reject(e),lt()}var r=j;if(s){if(t.f&32768)var l=ut();if(i.b?.is_rendered())r.async_deriveds.get(t)?.reject(ft);else for(let e of c.values())e.reject(ft);c.add(n),r.async_deriveds.set(t,n)}let u=(e,t=void 0)=>{l?.(),c.delete(n),t!==ft&&(r.activate(),t?(o.f|=de,Gt(o,t)):(o.f&8388608&&(o.f^=de),Gt(o,e)),r.deactivate())};n.promise.then(u,e=>u(null,e||`unknown`))}),vn(()=>{for(let e of c)e.reject(ft)}),new Promise(e=>{function t(n){function r(){n===a?e(o):t(a)}n.then(r,r)}t(a)})}function mt(e){let t=dt(e);return t.equals=Pe,t}function ht(e){var t=e.effects;if(t!==null){e.effects=null;for(var n=0;n{t.ac.abort(xe),t.ac=null}),t.fn!==null&&(t.teardown=g),ar(t,0),An(t))}function yt(e){if(e.effects!==null)for(let t of e.effects)t.teardown&&t.fn!==null&&or(t)}var bt=null,j=null,xt=null,St=null,Ct=null,wt=!1,Tt=!1,Et=null,Dt=null,Ot=0,kt=1,At=class e{id=kt++;#e=!1;linked=!0;#t=null;#n=null;async_deriveds=new Map;current=new Map;previous=new Map;#r=new Set;#i=new Set;#a=0;#o=new Map;#s=null;#c=[];#l=[];#u=new Set;#d=new Set;#f=new Map;#p=new Set;is_fork=!1;#m=!1;constructor(){bt===null?bt=this:(bt.#n=this,this.#t=bt),bt=this}#h(){if(this.is_fork)return!0;for(let n of this.#o.keys()){for(var e=n,t=!1;e.parent!==null;){if(this.#f.has(e)){t=!0;break}e=e.parent}if(!t)return!0}return!1}skip_effect(e){this.#f.has(e)||this.#f.set(e,{d:[],m:[]}),this.#p.delete(e)}unskip_effect(e,t=e=>this.schedule(e)){var n=this.#f.get(e);if(n){this.#f.delete(e);for(var r of n.d)A(r,x),t(r);for(r of n.m)A(r,S),t(r)}this.#p.add(e)}#g(){var e=[];for(let i of this.#c)if(!(i.f&16384||!(i.f&6144))){for(var t=i,n=!1;t.parent!==null;){t=t.parent;var r=t.f;if(r&96){if(!(r&1024)){n=!0;break}t.f^=b}}n||e.push(t)}return this.#c=[],e}#_(){this.#e=!0;for(let e of this.#u)this.#d.delete(e),A(e,x),this.schedule(e);for(let e of this.#d)A(e,S),this.schedule(e);this.apply();for(var t=Et=[],n=[],r=Dt=[];this.#c.length>0;){Ot++>1e3&&(this.#S(),Mt());for(let e of this.#g())try{this.#v(e,t,n)}catch(t){throw Lt(e),this.#h()||this.discard(),t}}if(j=null,r.length>0){var i=e.ensure();for(let e of r)i.schedule(e)}if(Et=null,Dt=null,this.#h()){this.#x(n),this.#x(t);for(let[e,t]of this.#f)It(e,t);r.length>0&&j.#_();return}let a=this.#y();if(a){this.#x(n),this.#x(t),a.#b(this);return}this.#u.clear(),this.#d.clear();for(let e of this.#r)e(this);this.#r.clear(),xt=this,Pt(n),Pt(t),xt=null,this.#s?.resolve();var o=j;if(this.#a===0&&(this.#c.length===0||o!==null)&&this.#S(),this.#c.length>0){if(o!==null){for(let e of this.#c)o.#c.push(e);this.#c=[]}else o=this}o!==null&&(zt.clear(),o.#_())}#v(e,t,n){e.f^=b;for(var r=e.first;r!==null;){var i=r.f,a=!!(i&96);if(!(a&&i&1024||i&8192||this.#f.has(r))&&r.fn!==null){a?r.f^=b:i&4?t.push(r):er(r)&&(i&16&&this.#d.add(r),or(r));var o=r.first;if(o!==null){r=o;continue}}for(;r!==null;){var s=r.next;if(s!==null){r=s;break}r=r.parent}}}#y(){for(var e=this.#t;e!==null;){if(!e.is_fork){for(let[t,[,n]]of this.current)if(e.current.has(t)&&!n)return e}e=e.#t}return null}#b(e){for(let[t,n]of e.current)!this.previous.has(t)&&e.previous.has(t)&&this.previous.set(t,e.previous.get(t)),this.current.set(t,n);for(let[t,n]of e.async_deriveds){let e=this.async_deriveds.get(t);e&&n.promise.then(e.resolve).catch(e.reject)}e.async_deriveds.clear(),this.transfer_effects(e.#u,e.#d);let t=e=>{var n=e.reactions;if(n!==null&&!(e.f&2&&!(e.f&6144)))for(let e of n){var r=e.f;if(r&2)t(e);else{var i=e;r&4194320&&!this.async_deriveds.has(i)&&(this.#d.delete(i),A(i,x),this.schedule(i))}}};for(let e of this.current.keys())t(e);this.oncommit(()=>e.discard()),e.#S(),j=this,this.#_()}#x(e){for(var t=0;t{this.#m=!1,this.linked&&this.flush()}))}transfer_effects(e,t){for(let t of e)this.#u.add(t);for(let e of t)this.#d.add(e);e.clear(),t.clear()}oncommit(e){this.#r.add(e)}ondiscard(e){this.#i.add(e)}settled(){return(this.#s??=y()).promise}static ensure(){if(j===null){let t=j=new e;!Tt&&!wt&&Qe(()=>{t.#e||t.flush()})}return j}apply(){St=null}schedule(e){if(Ct=e,e.b?.is_pending&&e.f&16777228&&!(e.f&32768)){e.b.defer_effect(e);return}this.#c.push(e)}#S(){if(this.linked){var e=this.#t,t=this.#n;e===null||(e.#n=t),t===null?bt=e:t.#t=e,this.linked=!1}}};function jt(e){var t=wt;wt=!0;try{var n;for(e&&(j!==null&&!j.is_fork&&j.flush(),n=e());;){if($e(),j===null)return n;j.flush()}}finally{wt=t}}function Mt(){try{Ve()}catch(e){pn(e,Ct)}}var Nt=null;function Pt(e){var t=e.length;if(t!==0){for(var n=0;n0)){zt.clear();for(let e of Nt){if(e.f&24576)continue;let t=[e],n=e.parent;for(;n!==null;)Nt.has(n)&&(Nt.delete(n),t.push(n)),n=n.parent;for(let e=t.length-1;e>=0;e--){let n=t[e];n.f&24576||or(n)}}Nt.clear()}}Nt=null}}function Ft(e){j.schedule(e)}function It(e,t){if(!(e.f&32&&e.f&1024)){e.f&2048?t.d.push(e):e.f&4096&&t.m.push(e),A(e,b);for(var n=e.first;n!==null;)It(n,t),n=n.next}}function Lt(e){A(e,b);for(var t=e.first;t!==null;)Lt(t),t=t.next}var Rt=new Set,zt=new Map,Bt=!1;function Vt(e,t){return{f:0,v:e,reactions:null,equals:Me,rv:0,wv:0}}function Ht(e,t){let n=Vt(e,t);return Kn(n),n}function M(t,n=!1,r=!0){let i=Vt(t);return n||(i.equals=Pe),e&&r&&D!==null&&D.l!==null&&(D.l.s??=[]).push(i),i}function N(e,t,n=!1){return B!==null&&(!Un||B.f&131072)&&Ye()&&B.f&4325394&&(Gn===null||!Gn.has(e))&&Ge(),Gt(e,n?Yt(t):t,Dt)}var Ut=null,Wt=0;function Gt(e,t,n=null){if(!e.equals(t)){Vn?zt.set(e,t):zt.has(e)||zt.set(e,e.v);var r=At.ensure();if(r.capture(e,t),e.f&2){let t=e;e.f&2048&>(t),St===null&&tt(t)}e.wv=$n(),Ut=null,Wt=0,Jt(e,x,n),Ut=null,Ye()&&H!==null&&H.f&1024&&!(H.f&96)&&(qn===null?Jn([e]):qn.push(e)),!r.is_fork&&Rt.size>0&&!Bt&&Kt()}return t}function Kt(){Bt=!1;for(let e of Rt){e.f&1024&&A(e,S);let t;try{t=er(e)}catch{t=!0}t&&or(e)}Rt.clear()}function qt(e){N(e,e.v+1)}function Jt(e,t,n){var r=e.reactions;if(r!==null){var i=Ye(),a=r.length;if(Wt+=a,Wt>1e5&&Ut===null&&(Ut=new Set),Ut!==null){if(Ut.has(e))return;Ut.add(e)}for(var o=0;o{if(Zn===c)return e();var t=B,n=Zn;V(null),Qn(c);var r=e();return V(t),Qn(n),r};return i&&n.set(`length`,Ht(e.length,s)),new Proxy(e,{defineProperty(e,t,r){(!(`value`in r)||r.configurable===!1||r.enumerable===!1||r.writable===!1)&&Ue();var i=n.get(t);return i===void 0?l(()=>{var e=Ht(r.value,s);return n.set(t,e),e}):N(i,r.value,!0),!0},deleteProperty(e,t){var i=n.get(t);if(i===void 0){if(t in e){let e=l(()=>Ht(r,s));n.set(t,e),qt(o)}}else N(i,r),qt(o);return!0},get(t,i,a){if(i===fe)return e;var o=n.get(i),c=i in t;if(o===void 0&&(!c||u(t,i)?.writable)&&(o=l(()=>Ht(Yt(c?t[i]:r),s)),n.set(i,o)),o!==void 0){var d=G(o);return d===r?void 0:d}return Reflect.get(t,i,a)},getOwnPropertyDescriptor(e,t){this.has?.(e,t);var i=Reflect.getOwnPropertyDescriptor(e,t),a=n.get(t);if(a!==void 0){var o=G(a);if(o===r)return;if(i&&`value`in i)i.value=o;else return{enumerable:!0,configurable:!0,value:o,writable:!0}}return i},has(e,t){if(t===fe)return!0;var i=n.get(t),a=i!==void 0&&i.v!==r||Reflect.has(e,t);return(i!==void 0||H!==null&&(!a||u(e,t)?.writable))&&(i===void 0&&(i=l(()=>Ht(a?Yt(e[t]):r,s)),n.set(t,i)),G(i)===r)?!1:a},set(e,t,a,c){var d=n.get(t),f=t in e;if(i&&t===`length`)for(var p=a;pHt(r,s)),n.set(p+``,m)):N(m,r)}if(d===void 0)(!f||u(e,t)?.writable)&&(d=l(()=>Ht(void 0,s)),N(d,Yt(a)),n.set(t,d));else{f=d.v!==r;var h=l(()=>Yt(a));N(d,h)}var g=Reflect.getOwnPropertyDescriptor(e,t);if(g?.set&&g.set.call(c,a),!f){if(i&&typeof t==`string`){var _=n.get(`length`),v=Number(t);Number.isInteger(v)&&v>=_.v&&N(_,v+1)}qt(o)}return!0},ownKeys(e){G(o);var t=Reflect.ownKeys(e).filter(e=>{var t=n.get(e);return t===void 0||t.v!==r});for(var[i,a]of n)a.v!==r&&!(i in e)&&t.push(i);return t},setPrototypeOf(){We()}})}function Xt(e){try{if(typeof e==`object`&&e&&fe in e)return e[fe]}catch{}return e}function Zt(e,t){return Object.is(Xt(e),Xt(t))}var Qt,$t,en,tn;function nn(){if(Qt===void 0){Qt=window,$t=/Firefox/.test(navigator.userAgent);var e=Element.prototype,t=Node.prototype,n=Text.prototype;en=u(t,`firstChild`).get,tn=u(t,`nextSibling`).get,h(e)&&(e[_e]=void 0,e[ge]=null,e[ve]=void 0,e.__e=void 0),h(n)&&(n[ye]=void 0)}}function rn(e=``){return document.createTextNode(e)}function an(e){return en.call(e)}function on(e){return tn.call(e)}function P(e,t){if(!C)return an(e);var n=an(w);if(n===null)n=w.appendChild(rn());else if(t&&n.nodeType!==3){var r=rn();return n?.before(r),T(r),r}return t&&dn(n),T(n),n}function sn(e,t=!1){if(!C){var n=an(e);return n instanceof Comment&&n.data===``?on(n):n}if(t){if(w?.nodeType!==3){var r=rn();return w?.before(r),T(r),r}dn(w)}return w}function F(e,t=!1){if(!C)return an(e);var n=P(e,t);return E(e),n}function I(e,t=1,n=!1){let r=C?w:e;for(var i;t--;)i=r,r=on(r);if(!C)return r;if(n){if(r?.nodeType!==3){var a=rn();return r===null?i?.after(a):r.before(a),T(a),a}dn(r)}return T(r),r}function cn(e){e.textContent=``}function ln(){return!1}function un(e,t,n){return t==null||t===`http://www.w3.org/1999/xhtml`?n?document.createElement(e,{is:n}):document.createElement(e):n?document.createElementNS(t,e,{is:n}):document.createElementNS(t,e)}function dn(e){if(e.nodeValue.length<65536)return;let t=e.nextSibling;for(;t!==null&&t.nodeType===3;)t.remove(),e.nodeValue+=t.nodeValue,t=e.nextSibling}function fn(e){var t=H;if(t===null)return B.f|=de,e;if(!(t.f&32768)&&!(t.f&4))throw e;pn(e,t)}function pn(e,t){if(!(t!==null&&t.f&16384)){for(;t!==null;){if(t.f&128&&!(t.f&33570816)){if(!(t.f&32768))throw e;try{t.b.error(e);return}catch(t){e=t}}t=t.parent}throw e}}function mn(e){H===null&&(B===null&&Be(e),ze()),Vn&&Re(e)}function hn(e,t){var n=t.last;n===null?t.last=t.first=e:(n.next=e,e.prev=n,t.last=e)}function gn(e,t){var n=H;n!==null&&n.f&8192&&(e|=ee);var r={ctx:D,deps:null,nodes:null,f:e|x|512,first:null,fn:t,last:null,next:null,parent:n,b:n&&n.b,prev:null,teardown:null,wv:0,ac:null};j?.register_created_effect(r);var i=r;if(e&4)Et===null?At.ensure().schedule(r):Et.push(r);else if(t!==null){try{or(r)}catch(e){throw z(r),e}i.deps===null&&i.teardown===null&&i.nodes===null&&i.first===i.last&&!(i.f&524288)&&(i=i.first,e&16&&e&65536&&i!==null&&(i.f|=ie))}if(i!==null&&(i.parent=n,n!==null&&hn(i,n),B!==null&&B.f&2&&!(e&64))){var a=B;(a.effects??=[]).push(i)}return r}function _n(){return B!==null&&!Un}function vn(e){let t=gn(8,null);return A(t,b),t.teardown=e,t}function yn(e){mn(`$effect`);var t=H.f;if(!B&&t&32&&D!==null&&!D.i){var n=D;(n.e??=[]).push(e)}else return bn(e)}function bn(e){return gn(4|se,e)}function xn(e){return mn(`$effect.pre`),gn(8|se,e)}function Sn(e){At.ensure();let t=gn(64|oe,e);return(e={})=>new Promise(n=>{e.outro?Pn(t,()=>{z(t),n(void 0)}):(z(t),n(void 0))})}function Cn(e){return gn(4,e)}function wn(e,t){var n=D,r={effect:null,ran:!1,deps:e};n.l.$.push(r),r.effect=Dn(()=>{if(e(),!r.ran){r.ran=!0;var n=H;try{Wn(n.parent),K(t)}finally{Wn(n)}}})}function Tn(){var e=D;Dn(()=>{for(var t of e.l.$){t.deps();var n=t.effect;n.f&1024&&n.deps!==null&&A(n,S),er(n)&&or(n),t.ran=!1}})}function En(e){return gn(ue|oe,e)}function Dn(e,t=0){return gn(8|t,e)}function L(e,t=[],n=[],r=[]){st(r,t,n,t=>{gn(8,()=>{e(...t.map(G))})})}function On(e,t=0){return gn(16|t,e)}function R(e){return gn(32|oe,e)}function kn(e){var t=e.teardown;if(t!==null){let n=Vn,r=B;Hn(!0),V(null);try{t.call(null)}catch(t){pn(t,e.parent)}finally{Hn(n),V(r)}}}function An(e,t=!1){var n=e.first;for(e.first=e.last=null;n!==null;){let e=n.ac;e!==null&&at(()=>{e.abort(xe)});var r=n.next;n.f&64?n.parent=null:z(n,t),n=r}}function jn(e){for(var t=e.first;t!==null;){var n=t.next;t.f&32||z(t),t=n}}function z(e,t=!0){var n=!1;(t||e.f&262144)&&e.nodes!==null&&e.nodes.end!==null&&(Mn(e.nodes.start,e.nodes.end),n=!0),e.f|=re,An(e,t&&!n),ar(e,0);var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)e.stop();kn(e),e.f^=re,e.f|=te;var i=e.parent;i!==null&&i.first!==null&&Nn(e),e.next=e.prev=e.teardown=e.ctx=e.deps=e.fn=e.nodes=e.ac=e.b=null}function Mn(e,t){for(;e!==null;){var n=e===t?null:on(e);e.remove(),e=n}}function Nn(e){var t=e.parent,n=e.prev,r=e.next;n!==null&&(n.next=r),r!==null&&(r.prev=n),t!==null&&(t.first===e&&(t.first=r),t.last===e&&(t.last=n))}function Pn(e,t,n=!0){var r=[];e.f|=256,Fn(e,r,!0);var i=()=>{n&&z(e),t&&t()},a=r.length;if(a>0){var o=()=>--a||i();for(var s of r)s.out(o)}else i()}function Fn(e,t,n){if(!(e.f&8192)){e.f^=ee;var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)(e.is_global||n)&&t.push(e);for(var i=e.first;i!==null;){var a=i.next;if(!(i.f&64)){var o=!!(i.f&65536)||!!(i.f&32)&&!!(e.f&16);Fn(i,t,o?n:!1)}i=a}}}function In(e){e.f&=-257,Ln(e,!0)}function Ln(e,t){if(!(e.f&256)&&e.f&8192){e.f^=ee,e.f&1024||(A(e,x),At.ensure().schedule(e));for(var n=e.first;n!==null;){var r=n.next,i=!!(n.f&65536)||!!(n.f&32);Ln(n,i?t:!1),n=r}var a=e.nodes&&e.nodes.t;if(a!==null)for(let e of a)(e.is_global||t)&&e.in()}}function Rn(e,t){if(e.nodes)for(var n=e.nodes.start,r=e.nodes.end;n!==null;){var i=n===r?null:on(n);t.append(n),n=i}}var zn=null,Bn=!1,Vn=!1;function Hn(e){Vn=e}var B=null,Un=!1;function V(e){B=e}var H=null;function Wn(e){H=e}var Gn=null;function Kn(e){B!==null&&(B.f&2097152||B.f&2)&&(Gn??=new Set).add(e)}var U=null,W=0,qn=null;function Jn(e){qn=e}var Yn=1,Xn=0,Zn=Xn;function Qn(e){Zn=e}function $n(){return++Yn}function er(e){var t=e.f;if(t&2048)return!0;if(t&4096){for(var n=e.deps,r=n.length,i=0;ie.wv)return!0}t&512&&St===null&&A(e,b)}return!1}function tr(e,t,n=!0){var r=e.reactions;if(r!==null&&!(Gn!==null&&Gn.has(e)))for(var i=0;i{e.ac.abort(xe)}),e.ac=null);try{e.f|=le;var u=e.fn,d=u();e.f|=ne;var f=rr(e);if(Ye()&&qn!==null&&!Un&&f!==null&&!(e.f&6146))for(var p=0;p0)for(t.length=W+U.length,r=0;r{c.ac.abort(xe),c.ac=null,A(c,x)}),vt(c),ar(c,0)}}function ar(e,t){var n=e.deps;if(n!==null)for(var r=t;r{_r=!1,gr=null}));var o=0,s=gr===e&&e[dr];if(s){var c=i.indexOf(s);if(c!==-1&&(t===document||t===window)){e[dr]=t;return}var u=i.indexOf(t);if(u===-1)return;c<=u&&(o=c)}if(a=i[o]||e.target,a!==t){l(e,`currentTarget`,{configurable:!0,get(){return a||n}});var d=B,f=H;V(null),Wn(null);try{for(var p,m=[];a!==null&&a!==t;){try{var h=a[dr]?.[r];h!=null&&(!a.disabled||e.target===a)&&h.call(a,e)}catch(e){p?m.push(e):p=e}if(e.cancelBubble)break;o++,a=o{throw e});throw p}}finally{e[dr]=t,delete e.currentTarget,V(d),Wn(f)}}}var yr=globalThis?.window?.trustedTypes&&globalThis.window.trustedTypes.createPolicy(`svelte-trusted-html`,{createHTML:e=>e});function br(e){return yr?.createHTML(e)??e}function xr(e){var t=un(`template`);return t.innerHTML=br(e.replaceAll(``,``)),t.content}function Sr(e,t){var n=H;n.nodes===null&&(n.nodes={start:e,end:t,a:null,t:null})}function J(e,t){var n=!!(t&1),r=!!(t&2),i,a=!e.startsWith(``);return()=>{if(C)return Sr(w,null),w;i===void 0&&(i=xr(a?e:``+e),n||(i=an(i)));var t=r||$t?document.importNode(i,!0):i.cloneNode(!0);if(n){var o=an(t),s=t.lastChild;Sr(o,s)}else Sr(t,t);return t}}function Y(e,t){if(C){var n=H;(!(n.f&32768)||n.nodes.end===null)&&(n.nodes.end=w),Oe();return}e!==null&&e.before(t)}[...`allowfullscreen.async.autofocus.autoplay.checked.controls.default.disabled.formnovalidate.indeterminate.inert.ismap.loop.multiple.muted.nomodule.novalidate.open.playsinline.readonly.required.reversed.seamless.selected.webkitdirectory.defer.disablepictureinpicture.disableremoteplayback`.split(`.`)];var Cr=[`touchstart`,`touchmove`];function wr(e){return Cr.includes(e)}function Tr(e){let t=0,n=Vt(0),r;return()=>{_n()&&(G(n),Dn(()=>(t===0&&(r=K(()=>e(()=>qt(n)))),t+=1,()=>{Qe(()=>{--t,t===0&&(r?.(),r=void 0,qt(n))})})))}}var Er=ie|oe;function Dr(e,t,n,r){new Or(e,t,n,r)}var Or=class{parent;is_pending=!1;transform_error;#e;#t=C?w:null;#n;#r;#i;#a=null;#o=null;#s=null;#c=null;#l=0;#u=0;#d=!1;#f=new Set;#p=new Set;#m=null;#h=Tr(()=>(this.#m=Vt(this.#l),()=>{this.#m=null}));constructor(e,t,n,r){this.#e=e,this.#n=t,this.#r=e=>{var t=H;t.b=this,t.f|=128,n(e)},this.parent=H.b,this.transform_error=r??this.parent?.transform_error??(e=>e),this.#i=On(()=>{if(C){let e=this.#t;Oe();let t=e.data===`[!`;if(e.data.startsWith(`[?`)){let t=JSON.parse(e.data.slice(2));this.#_(t)}else t?this.#y():this.#g()}else this.#b()},Er),C&&(this.#e=w)}#g(){try{this.#a=R(()=>this.#r(this.#e))}catch(e){this.error(e)}}#_(e){let t=this.#n.failed,{reset:n,invoke_onerror:r}=this.#v(e);Qe(r),t&&(this.#s=R(()=>{t(this.#e,()=>e,()=>n)}))}#v(e){var t=!1,n=!1;let r=()=>{if(t){Ee();return}t=!0,n&&Ke(),this.#s!==null&&Pn(this.#s,()=>{this.#s=null}),this.#S(()=>{this.#b()})};return{reset:r,invoke_onerror:()=>{try{n=!0,this.#n.onerror?.(e,r),n=!1}catch(e){pn(e,this.#i&&this.#i.parent)}}}}#y(){let e=this.#n.pending;e&&(this.is_pending=!0,this.#o=R(()=>e(this.#e)),Qe(()=>{var e=this.#c=document.createDocumentFragment(),t=rn(),n=!1;if(e.append(t),this.#a=this.#S(()=>{try{return R(()=>this.#r(t))}catch(e){try{this.error(e),n=!0}catch(e){pn(e,this.#i.parent)}return null}}),this.#a===null){this.#c=null,n&&this.#x(j);return}this.#u===0&&(this.#e.before(e),this.#c=null,Pn(this.#o,()=>{this.#o=null}),this.#x(j))}))}#b(){try{if(this.is_pending=this.has_pending_snippet(),this.#u=0,this.#l=0,this.#a=R(()=>{this.#r(this.#e)}),this.#u>0){var e=this.#c=document.createDocumentFragment();Rn(this.#a,e);let t=this.#n.pending;this.#o=R(()=>t(this.#e))}else this.#x(j)}catch(e){this.error(e)}}#x(e){this.is_pending=!1,e.transfer_effects(this.#f,this.#p)}defer_effect(e){nt(e,this.#f,this.#p)}is_rendered(){return!this.is_pending&&(!this.parent||this.parent.is_rendered())}has_pending_snippet(){return!!this.#n.pending}#S(e){var t=H,n=B,r=D;Wn(this.#i),V(this.#i),qe(this.#i.ctx);try{return At.ensure(),e()}finally{Wn(t),V(n),qe(r)}}#C(e,t){if(!this.has_pending_snippet()){this.parent&&this.parent.#C(e,t);return}this.#u+=e,this.#u===0&&(this.#x(t),this.#o&&Pn(this.#o,()=>{this.#o=null}),this.#c&&=(this.#e.before(this.#c),null))}update_pending_count(e,t){this.#C(e,t),this.#l+=e,!(!this.#m||this.#d)&&(this.#d=!0,Qe(()=>{this.#d=!1,this.#m&&Gt(this.#m,this.#l)}))}get_effect_pending(){return this.#h(),G(this.#m)}error(e){if(!this.#n.onerror&&!this.#n.failed)throw e;j?.is_fork?(this.#a&&j.skip_effect(this.#a),this.#o&&j.skip_effect(this.#o),this.#s&&j.skip_effect(this.#s),j.oncommit(()=>{this.#w(e)})):this.#w(e)}#w(e){this.#a&&=(z(this.#a),null),this.#o&&=(z(this.#o),null),this.#s&&=(z(this.#s),null),C&&(T(this.#t),ke(),T(Ae()));let t=this.#n.failed,n=e=>{let{reset:n,invoke_onerror:r}=this.#v(e);r(),t&&(this.#s=this.#S(()=>{try{return R(()=>{var r=H;r.b=this,r.f|=128,t(this.#e,()=>e,()=>n)})}catch(e){return pn(e,this.#i.parent),null}}))};Qe(()=>{var t;try{t=this.transform_error(e)}catch(e){pn(e,this.#i&&this.#i.parent);return}typeof t==`object`&&t&&typeof t.then==`function`?t.then(n,e=>pn(e,this.#i&&this.#i.parent)):n(t)})}};function X(e,t){var n=t==null?``:typeof t==`object`?`${t}`:t;n!==(e[ye]??=e.nodeValue)&&(e[ye]=n,e.nodeValue=`${n}`)}function kr(e,t){return jr(e,t)}var Ar=new Map;function jr(e,{target:t,anchor:r,props:i={},events:a,context:o,intro:s=!0,transformError:l}){nn();var u=void 0,d=Sn(()=>{var s=r??t.appendChild(rn());Dr(s,{pending:()=>{}},t=>{O({});var r=D;if(o&&(r.c=o),a&&(i.$$events=a),C&&Sr(t,null),u=e(t,i)||Je(),C&&(H.nodes.end=w,w===null||w.nodeType!==8||w.data!==`]`))throw we(),n;k()},l);var d=new Set,f=e=>{for(var n=0;n{for(var e of d)for(let r of[t,document]){var n=Ar.get(r),i=n.get(e);--i==0?(r.removeEventListener(e,vr),n.delete(e),n.size===0&&Ar.delete(r)):n.set(e,i)}pr.delete(f),s!==r&&s.parentNode?.removeChild(s)}});return Mr.set(u,d),u}var Mr=new WeakMap,Nr=class{anchor;#e=new Map;#t=new Map;#n=new Map;#r=new Set;#i=!0;constructor(e,t=!0){this.anchor=e,this.#i=t}#a=e=>{if(this.#e.has(e)){var t=this.#e.get(e),n=this.#t.get(t);if(n)In(n),this.#r.delete(t);else{var r=this.#n.get(t);r&&(In(r.effect),this.#t.set(t,r.effect),this.#n.delete(t),r.fragment.lastChild.remove(),this.anchor.before(r.fragment),n=r.effect)}for(let[t,n]of this.#e){if(this.#e.delete(t),t===e)break;let r=this.#n.get(n);r&&(z(r.effect),this.#n.delete(n))}for(let[e,r]of this.#t){if(e===t||this.#r.has(e))continue;let i=()=>{if(Array.from(this.#e.values()).includes(e)){var t=document.createDocumentFragment();Rn(r,t),t.append(rn()),this.#n.set(e,{effect:r,fragment:t})}else z(r);this.#r.delete(e),this.#t.delete(e)};this.#i||!n?(this.#r.add(e),Pn(r,i,!1)):i()}}};#o=e=>{this.#e.delete(e);let t=Array.from(this.#e.values());for(let[e,n]of this.#n)t.includes(e)||(z(n.effect),this.#n.delete(e))};ensure(e,t){var n=j,r=ln();if(t&&!this.#t.has(e)&&!this.#n.has(e)){if(r){var i=document.createDocumentFragment(),a=rn();i.append(a),this.#n.set(e,{effect:R(()=>t(a)),fragment:i})}else this.#t.set(e,R(()=>t(this.anchor)))}if(this.#e.set(n,e),r){for(let[t,r]of this.#t)t===e?n.unskip_effect(r):n.skip_effect(r);for(let[t,r]of this.#n)t===e?n.unskip_effect(r.effect):n.skip_effect(r.effect);n.oncommit(this.#a),n.ondiscard(this.#o)}else C&&(this.anchor=w),this.#a(n)}};function Pr(t){D===null&&Fe(`onMount`),e&&D.l!==null?Fr(D).m.push(t):yn(()=>{let e=K(t);if(typeof e==`function`)return e})}function Fr(e){var t=e.l;return t.u??={a:[],b:[],m:[]}}function Z(e,t,n=!1){var r;C&&(r=w,Oe());var i=new Nr(e),a=n?ie:0;function o(e,t){if(C){var n=je(r);if(e!==parseInt(n.substring(1))){var a=Ae();T(a),i.anchor=a,De(!1),i.ensure(e,t),De(!0);return}}i.ensure(e,t)}On(()=>{var e=!1;t((t,n=0)=>{e=!0,o(n,t)}),e||o(-1,null)},a)}var Ir=Symbol(`NaN`);function Lr(e,t,n){C&&Oe();var r=new Nr(e),i=!Ye();On(()=>{var e=t();e!==e&&(e=Ir),i&&typeof e==`object`&&e&&(e={}),r.ensure(e,n)})}function Rr(e,t){return t}function zr(e,t,n){for(var r=[],i=t.length,a,o=t.length,s=0;s{if(a){if(a.pending.delete(n),a.done.add(n),a.pending.size===0){var t=e.outrogroups;Br(e,c(a.done)),t.delete(a),t.size===0&&(e.outrogroups=null)}}else--o},!1)}if(o===0){var l=r.length===0&&n!==null&&e.pending.size===0;if(l){var u=n,d=u.parentNode;cn(d),d.append(u),e.items.clear()}Br(e,t,!l)}else a={pending:new Set(t),done:new Set},(e.outrogroups??=new Set).add(a)}function Br(e,t,n=!0){var r;if(e.pending.size>0){r=new Set;for(let t of e.pending.values())for(let n of t)r.add(e.items.get(n).e)}for(var i=0;i{var e=n();return a(e)?e:e==null?[]:c(e)}),p,m=new Map,h=!0;function g(e){v.effect.f&16384||(v.pending.delete(e),v.fallback=d,Wr(v,p,s,t,r),d!==null&&(p.length===0?d.f&33554432?(d.f^=ce,Kr(d,null,s)):In(d):Pn(d,()=>{d=null})))}function _(e){v.pending.delete(e)}var v={effect:On(()=>{p=G(f);var e=p.length;let a=!1;C&&je(s)===`[!`!=(e===0)&&(s=Ae(),T(s),De(!1),a=!0);for(var c=new Set,u=j,v=ln(),y=0;yo(s)):(d=R(()=>o(Vr??=rn())),d.f|=ce)),e>c.size&&Le(``,``,``),C&&e>0&&T(Ae()),!h){if(m.set(u,c),v){for(let[e,t]of l)c.has(e)||u.skip_effect(t.e);u.oncommit(g),u.ondiscard(_)}else g(u)}a&&De(!0),G(f)}),flags:t,items:l,pending:m,outrogroups:null,fallback:d};h=!1,C&&(s=w)}function Ur(e){for(;e!==null&&!(e.f&32);)e=e.next;return e}function Wr(e,t,n,r,i){var a=!!(r&8),o=t.length,s=e.items,l=Ur(e.effect.first),u,d=null,f,p=[],m=[],h,g,_,v;if(a)for(v=0;v0){var re=r&4&&o===0?n:null;if(a){for(v=0;v{if(f!==void 0)for(_ of f)_.nodes?.a?.apply()})}function Gr(e,t,n,r,i,a,o,s){var c=o&1?o&16?Vt(n):M(n,!1,!1):null,l=o&2?Vt(i):null;return{v:c,i:l,e:R(()=>(a(t,c??n,l??i,s),()=>{e.delete(r)}))}}function Kr(e,t,n){if(e.nodes)for(var r=e.nodes.start,i=e.nodes.end,a=t&&!(t.f&33554432)?t.nodes.start:n;r!==null;){var o=on(r);if(a.before(r),r===i)return;r=o}}function qr(e,t,n){t===null?e.effect.first=n:t.next=n,n===null?e.effect.last=t:n.prev=t}function Jr(e,t){let n=null,r=C;var i;if(C){n=w;for(var a=an(document.head);a!==null&&(a.nodeType!==8||a.data!==e);)a=on(a);if(a===null)De(!1);else{var o=on(a);a.remove(),T(o)}}C||(i=document.head.appendChild(rn()));try{On(()=>{var e=R(()=>t(i));e.f|=ae,C||(e.nodes===null?e.nodes={start:i,end:i,a:null,t:null}:e.nodes.end=i)})}finally{r&&(De(!0),T(n))}}function Yr(e){var t,n,r=``;if(typeof e==`string`||typeof e==`number`)r+=e;else if(typeof e==`object`){if(Array.isArray(e)){var i=e.length;for(t=0;t=0;){var s=o+a;(o===0||Qr.includes(r[o-1]))&&(s===r.length||Qr.includes(r[s]))?r=(o===0?``:r.substring(0,o))+r.substring(s+1):o=s}}return r===``?null:r}function ei(e,t,n,r,i,a){var o=e[_e];if(C||o!==n||o===void 0){var s=$r(n,r,a);(!C||s!==e.getAttribute(`class`))&&(s==null?e.removeAttribute(`class`):t?e.className=s:e.setAttribute(`class`,s)),e[_e]=n}else if(a&&i!==a)for(var c in a){var l=!!a[c];(i==null||l!==!!i[c])&&e.classList.toggle(c,l)}return a}function ti(e,t){t?e.hasAttribute(`selected`)||e.setAttribute(`selected`,``):e.removeAttribute(`selected`)}function ni(e,t){var n=e.__defaultValue,r=e.multiple,i=r?n??[]:null;if(!r||a(i)){var o=e.selectedIndex,s=t&&r?new Set(e.selectedOptions):null;for(var c of e.options){var l=oi(c);ti(c,r?i.includes(l):Zt(l,n))}if(t){if(s!==null)for(c of e.options){var u=s.has(c);c.selected!==u&&(c.selected=u)}else e.selectedIndex!==o&&(e.selectedIndex=o)}}}function ri(e,t,n=!1){if(e.multiple){if(t==null)return;if(!a(t))return Te();for(var r of e.options)r.selected=t.includes(oi(r));return}for(r of e.options)if(Zt(oi(r),t)){r.selected=!0;return}(!n||t!==void 0)&&(e.selectedIndex=-1)}function ii(e){var t=new MutationObserver(t=>{t.every(si)||(`__defaultValue`in e&&ni(e,!1),`__value`in e&&ri(e,e.__value))});t.observe(e,{childList:!0,subtree:!0,attributes:!0,attributeFilter:[`value`]}),vn(()=>{t.disconnect()})}function ai(e,t,n=t){var r=new WeakSet,i=!0;ot(e,`change`,t=>{var i=t?`[selected]`:`:checked`,a;if(e.multiple)a=[].map.call(e.querySelectorAll(i),oi);else{var o=e.querySelector(i)??e.querySelector(`option:not([disabled])`);a=o&&oi(o)}n(a),e.__value=a,j!==null&&r.add(j)}),Cn(()=>{var a=t();if(e===document.activeElement){var o=j;if(r.has(o))return}if(ri(e,a,i),i&&a===void 0){var s=e.querySelector(`:checked`);s!==null&&(a=oi(s),n(a))}e.__value=a,i=!1})}function oi(e){return`__value`in e?e.__value:e.value}function si(e){if(e.target.closest(`selectedcontent`)!==null)return!0;if(e.type===`childList`){var t=[...e.addedNodes,...e.removedNodes];return t.length>0&&t.every(e=>e.nodeName===`SELECTEDCONTENT`)}return!1}var ci=Symbol(`is custom element`),li=Symbol(`is html`),ui=Se?`link`:`LINK`;function di(e){if(C){var t=!1,n=()=>{if(!t){if(t=!0,e.hasAttribute(`value`)){var n=e.value;Q(e,`value`,null),e.value=n}if(e.hasAttribute(`checked`)){var r=e.checked;Q(e,`checked`,null),e.checked=r}}};e[be]=n,Qe(n),it()}}function fi(e,t){var n=pi(e);n.checked!==(n.checked=t??void 0)&&(e.checked=t)}function Q(e,t,n,r){var i=pi(e);C&&(i[t]=e.getAttribute(t),t===`src`||t===`srcset`||t===`href`&&e.nodeName===ui)||i[t]!==(i[t]=n)&&(t===`loading`&&(e[he]=n),n==null?e.removeAttribute(t):typeof n!=`string`&&hi(e).has(t)?e[t]=n:e.setAttribute(t,n))}function pi(e){return e[ge]??={[ci]:e.nodeName.includes(`-`),[li]:e.namespaceURI===i}}var mi=new Map;function hi(e){var t=e.getAttribute(`is`)||e.nodeName,n=mi.get(t);if(n)return n;mi.set(t,n=new Set);for(var r,i=e,a=Element.prototype;a!==i;){for(var o in r=d(i),r)r[o].set&&o!==`innerHTML`&&o!==`textContent`&&o!==`innerText`&&n.add(o);i=m(i)}return n}function gi(e,t,n=t){var r=new WeakSet;ot(e,`input`,async i=>{var a=i?e.defaultValue:e.value;if(a=_i(e)?vi(a):a,n(a),j!==null&&r.add(j),await sr(),a!==(a=t())){var o=e.selectionStart,s=e.selectionEnd,c=e.value.length;if(e.value=a??``,s!==null){var l=e.value.length;o===s&&s===c&&l>c?(e.selectionStart=l,e.selectionEnd=l):(e.selectionStart=o,e.selectionEnd=Math.min(s,l))}}}),(C&&e.defaultValue!==e.value||K(t)==null&&e.value)&&(n(_i(e)?vi(e.value):e.value),j!==null&&r.add(j)),Dn(()=>{var n=t();if(e===document.activeElement){var i=j;if(r.has(i))return}_i(e)&&n===vi(e.value)||(e.type!==`date`||n||e.value)&&n!==e.value&&(e.value=n??``)})}function _i(e){var t=e.type;return t===`number`||t===`range`}function vi(e){return e===``?null:+e}function yi(e=!1){let t=D,n=t.l.u;if(!n)return;let r=()=>q(t.s);if(e){let e=0,n={},i=dt(()=>{let r=!1,i=t.s;for(let e in i)i[e]!==n[e]&&(n[e]=i[e],r=!0);return r&&e++,e});r=()=>G(i)}n.b.length&&xn(()=>{bi(t,r),v(n.b)}),yn(()=>{let e=K(()=>n.m.map(_));return()=>{for(let t of e)typeof t==`function`&&t()}}),n.a.length&&yn(()=>{bi(t,r),v(n.a)})}function bi(e,t){if(e.l.s)for(let t of e.l.s)G(t);t()}function xi(e,t,n){if(e==null)return t(void 0),n&&n(void 0),g;let r=K(()=>e.subscribe(t,n));return r.unsubscribe?()=>r.unsubscribe():r}var Si=[];function Ci(e,t=g){let n=null,r=new Set;function i(t){if(Ne(e,t)&&(e=t,n)){let t=!Si.length;for(let t of r)t[1](),Si.push(t,e);if(t){for(let e=0;e{r.delete(c),r.size===0&&n&&(n(),n=null)}}return{set:i,update:a,subscribe:o}}function wi(e){let t;return xi(e,e=>t=e)(),t}var Ti=!1,Ei=Symbol(`unmounted`);function Di(e,t,n){let r=n[t]??={store:null,source:M(void 0),unsubscribe:g};if(r.store!==e&&!(Ei in n)){if(r.unsubscribe(),r.store=e??null,e==null)r.source.v=void 0,r.unsubscribe=g;else{var i=!0;r.unsubscribe=xi(e,e=>{i?r.source.v=e:N(r.source,e)}),i=!1}}return e&&Ei in n?wi(e):G(r.source)}function Oi(){let e={};function t(){vn(()=>{for(var t in e)e[t].unsubscribe();l(e,Ei,{enumerable:!1,value:!0})})}return[e,t]}function ki(e){var t=Ti;try{return Ti=!1,[e(),Ti]}finally{Ti=t}}function $(t,n,r,i){var a=!e||!!(r&2),o=!!(r&8),s=!!(r&16),c=i,l=!0,d=void 0,f=()=>s&&a?(d??=dt(i),G(d)):(l&&(l=!1,c=s?K(i):i),c);let p;if(o){var m=fe in t||me in t;p=u(t,n)?.set??(m&&n in t?e=>t[n]=e:void 0)}var h,g=!1;o?[h,g]=ki(()=>t[n]):h=t[n],h===void 0&&i!==void 0&&(h=f(),p&&(a&&He(n),p(h)));var _=a?()=>{var e=t[n];return e===void 0?f():(l=!0,e)}:()=>{var e=t[n];return e!==void 0&&(c=void 0),e===void 0?c:e};if(a&&!(r&4))return _;if(p){var v=t.$$legacy;return(function(e,t){return arguments.length>0?((!a||!t||v||g)&&p(t?_():e),e):_()})}var y=!1,b=(r&1?dt:mt)(()=>(y=!1,_()));o&&G(b);var x=H;return(function(e,t){if(arguments.length>0){let n=t?G(b):a&&o?Yt(e):e;return N(b,n),y=!0,c!==void 0&&(c=n),e}return Vn&&y||x.f&16384?b.v:G(b)})}function Ai(){let e=Ci({controller:!1,busy:!1,error:``}),t,n=``,r={controller:!1,busy:!1,error:``},i=!1;function a(t){r={...r,...t},e.set(r)}async function o(e=!1){if(!i){i=!0;try{let t=await fetch(`/api/state`,{cache:`no-store`});if(!t.ok)throw Error(`The local setup session is unavailable.`);a({view:await t.json(),...e?{}:{error:``}})}catch{a({error:`Connection lost. The CLI may have stopped. Check the terminal before trying again.`})}finally{i=!1}}}async function s(){try{let e=await fetch(`/api/bootstrap`,{cache:`no-store`});if(!e.ok)throw Error(`Could not join this local session.`);let r=await e.json();t=r.capability,n=r.takeoverTicket,a({controller:r.controller,error:``}),await o()}catch{a({error:`Could not connect to the local setup session. Check the terminal.`})}}async function c(e,n,r=!0){let i=await fetch(e,{method:`POST`,cache:`no-store`,headers:{"Content-Type":`application/json`,...r&&t?{"X-Setup-Capability":t}:{}},body:JSON.stringify(n)}),a=await i.json();if(!i.ok)throw Error(String(a.error??`The request was rejected.`));return a}async function l(e,t){if(!r.busy&&r.controller&&r.view?.promptRevision===e){a({busy:!0,error:``});try{await c(`/api/answer`,{revision:e,value:t}),await o()}catch(e){let t=e instanceof Error?e.message:`Could not submit this answer.`;a({error:t}),/read-only|Control moved/.test(t)?await s():await o(!0)}finally{a({busy:!1})}}}async function u(){if(r.controller&&!r.busy){a({busy:!0,error:``});try{await c(`/api/cancel`,{}),await o()}catch(e){let t=e instanceof Error?e.message:`Cancellation failed.`;a({error:t}),/read-only|Control moved/.test(t)&&await s()}finally{a({busy:!1})}}}async function d(){try{let e=await c(`/api/takeover`,{ticket:n},!1);t=String(e.capability),a({controller:!0,error:``}),await o()}catch(e){a({error:e instanceof Error?e.message:`Takeover failed.`}),await s()}}async function f(){try{await c(`/api/close`,{})}catch{}}return{subscribe:e.subscribe,connect:s,refresh:o,submit:l,cancel:u,takeOver:d,close:f}}var ji=J(`
  • `),Mi=J(``);function Ni(e,t){O(t,!1);let n=$(t,`journey`,8),r=[`Repository`,`Setup choices`,`Setup PAT`,`Plan`,`Bot PAT & credentials`,`Apply`];yi();var i=Mi(),a=I(P(i),4);Hr(a,5,()=>r,Rr,(e,t,r)=>{var i=ji();let a;var o=P(i),s=F(o,!0),c=F(I(o,2),!0);E(i),L(e=>{Q(i,`aria-current`,(q(n()),K(()=>n()?.position===r+1?`step`:void 0))),a=ei(i,1,``,null,a,{current:n()?.position===r+1,completed:(n()?.position??0)>r+1}),X(s,e),X(c,G(t))},[()=>(q(n()),K(()=>(n()?.position??0)>r+1?`✓`:String(r+1).padStart(2,`0`)))]),Y(e,i)}),E(a),ke(2),E(i),Y(e,i),k()}var Pi=J(`
    `);function Fi(e,t){O(t,!1);let n=M(`system`);wn(()=>G(n),()=>{document.documentElement.dataset.theme=G(n)}),Tn();var r=Pi(),i=P(r);let a;var o=I(i,2);let s;var c=I(o,2);let l;E(r),L(()=>{Q(i,`aria-pressed`,G(n)===`system`),a=ei(i,1,``,null,a,{active:G(n)===`system`}),Q(o,`aria-pressed`,G(n)===`light`),s=ei(o,1,``,null,s,{active:G(n)===`light`}),Q(c,`aria-pressed`,G(n)===`dark`),l=ei(c,1,``,null,l,{active:G(n)===`dark`})}),mr(`click`,i,()=>N(n,`system`)),mr(`click`,o,()=>N(n,`light`)),mr(`click`,c,()=>N(n,`dark`)),Y(e,r),k()}hr([`click`]);var Ii=J(`
    LOCAL SESSION
    `);function Li(e,t){let n=$(t,`repository`,8);var r=Ii(),i=P(r),a=F(I(P(i),2),!0);E(i);var o=I(i,2);Fi(I(P(o)),{}),E(o),E(r),L(()=>X(a,n()??`Connecting…`)),Y(e,r)}var Ri=J(`

    `,1);function zi(e,t){O(t,!1);let n=M(),r=$(t,`view`,8);wn(()=>q(r()),()=>{N(n,r()?.outcome===`complete`?`Setup complete.`:r()?.outcome===`dry-run`?`Preview complete.`:r()?.outcome===`cancelled`?`Setup cancelled.`:r()?.outcome?`Setup needs attention.`:r()?.prompt?.title??`Preparing your setup…`)}),Tn(),yi();var i=Ri(),a=sn(i),o=I(P(a)),s=F(I(o));E(a);var c=I(a,2),l=F(c,!0),u=F(I(c,2),!0);L((e,t)=>{X(o,` ${e??``} `),X(s,`${t??``} / 06`),X(l,G(n)),X(u,(q(r()),K(()=>r()?.outcome?`The terminal contains the detailed result. Review the next steps below before closing this session.`:`One decision at a time. Your choices shape the permissions, plan and credentials needed for this repository.`)))},[()=>(q(r()),K(()=>r()?.journey?.current?.toUpperCase()??`GETTING READY`)),()=>(q(r()),K(()=>String(r()?.journey?.position??1).padStart(2,`0`)))]),Y(e,i),k()}function Bi(e){try{let t=new URL(e??``);return t.protocol===`https:`&&t.hostname===`github.com`&&(t.pathname===`/settings/personal-access-tokens`||t.pathname.startsWith(`/settings/personal-access-tokens/`))?t.toString():void 0}catch{return}}var Vi=J(``),Hi=J(``);function Ui(e,t){let n=$(t,`label`,8),r=$(t,`variant`,8,`primary`),i=$(t,`disabled`,8,!1),a=$(t,`arrow`,8,!1),o=$(t,`onClick`,8);var s=Hi(),c=P(s,!0),l=I(c),u=e=>{Y(e,Vi())};Z(l,e=>{a()&&e(u)}),E(s),L(()=>{ei(s,1,Zr(r())),s.disabled=i(),X(c,n())}),mr(`click`,s,function(...e){o()?.apply(this,e)}),Y(e,s)}hr([`click`]);var Wi=J(`Open GitHub link ↗`),Gi=J(`

    `);function Ki(e,t){O(t,!1);let n=M(),r=$(t,`tone`,8,`info`),i=$(t,`title`,8),a=$(t,`message`,8),o=$(t,`link`,8,void 0),s=$(t,`actionLabel`,8,void 0),c=$(t,`onAction`,8,void 0);wn(()=>q(o()),()=>{N(n,Bi(o()))}),Tn(),yi();var l=Gi(),u=P(l),d=F(u,!0),f=I(u),p=F(f,!0),m=I(f,2),h=e=>{var t=Wi();L(()=>Q(t,`href`,G(n))),Y(e,t)};Z(m,e=>{G(n)&&e(h)});var g=I(m,2),_=e=>{Ui(e,{get label(){return s()},variant:`secondary`,get onClick(){return c()}})};Z(g,e=>{s()&&c()&&e(_)}),E(l),L(()=>{ei(l,1,`banner ${r()??``}`),Q(l,`role`,r()===`error`?`alert`:`status`),X(d,i()),X(p,a())}),Y(e,l),k()}function qi(e){let t=String(e.question.defaultValue??``),n=t.split(`,`).map(e=>e.trim()).filter(Boolean);return{value:t,selected:e.question.kind===`multi-select`?(e.question.choices??[]).filter(e=>e!==`All`&&n.includes(e.split(` — `)[0])):e.question.kind===`scope-overrides`?n:[]}}function Ji(e,t){return t===`All`?e.includes(`All`)?[]:[`All`]:e.includes(t)?e.filter(e=>e!==t):[...e.filter(e=>e!==`All`),t]}function Yi(e,t,n){return e.question.kind===`scope-overrides`||e.question.kind===`multi-select`?n.length?n.join(`,`):`none`:t}var Xi=J(`PERMISSION PREVIEW`),Zi=J(`
    `),Qi=J(``),$i=J(``),ea=J(``),ta=J(`
    `),na=J(``),ra=J(`

    `,1);function ia(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`controller`,8),i=$(t,`busy`,8),a=$(t,`onSubmit`,8),o=qi(n()),s=M(o.value),c=M(o.selected);yi();var l=ra(),u=sn(l),d=P(u),f=F(d,!0),p=I(d),m=e=>{Y(e,Xi())};Z(p,e=>{q(n()),K(()=>n().phase===`permission-intent`)&&e(m)}),E(u);var h=I(u,2),g=e=>{var t=Zi(),i=P(t);let a;var o=I(i);let c;E(t),L(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),Q(i,`aria-pressed`,G(s)===`yes`||G(s)===`true`),i.disabled=!r(),a=ei(i,1,``,null,a,{selected:G(s)===`yes`||G(s)===`true`}),Q(o,`aria-pressed`,G(s)===`no`||G(s)===`false`),o.disabled=!r(),c=ei(o,1,``,null,c,{selected:G(s)===`no`||G(s)===`false`})}),mr(`click`,i,()=>N(s,`yes`)),mr(`click`,o,()=>N(s,`no`)),Y(e,t)},_=e=>{var t=$i();Hr(t,5,()=>(q(n()),K(()=>n().question.choices??[])),Rr,(e,t)=>{var n=Qi(),r=F(n,!0),i={};L(()=>{X(r,G(t)),i!==(i=G(t))&&(n.value=(n.__value=i)??``)}),Y(e,n)}),E(t),ii(t),L(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),t.disabled=!r()}),ai(t,()=>G(s),e=>N(s,e)),Y(e,t)},v=e=>{var t=ta();Hr(t,5,()=>(q(n()),K(()=>n().question.kind===`multi-select`?n().question.choices??[]:n().question.allowedNames??[])),Rr,(e,t)=>{var n=ea(),i=P(n);di(i);var a=F(I(i),!0);E(n),L(e=>{fi(i,e),i.disabled=!r(),X(a,G(t))},[()=>(G(c),G(t),K(()=>G(c).includes(G(t))))]),mr(`change`,i,()=>N(c,Ji(G(c),G(t)))),Y(e,n)}),E(t),L(()=>Q(t,`aria-label`,(q(n()),K(()=>n().question.label)))),Y(e,t)},y=e=>{var t=na();di(t),L(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),Q(t,`type`,(q(n()),K(()=>n().question.kind===`number`?`number`:`text`))),Q(t,`min`,(q(n()),K(()=>n().question.kind===`number`?0:void 0))),t.disabled=!r()}),gi(t,()=>G(s),e=>N(s,e)),Y(e,t)};Z(h,e=>{q(n()),K(()=>n().question.kind===`boolean`)?e(g):(q(n()),K(()=>n().question.kind===`choice`)?e(_,1):(q(n()),K(()=>n().question.kind===`multi-select`||n().question.kind===`scope-overrides`)?e(v,2):e(y,-1)))});var b=I(h,2),x=F(b),S=I(b,2);{let e=mt(()=>!r()||i());Ui(S,{label:`Continue`,arrow:!0,onClick:()=>a()(Yi(n(),G(s),G(c))),get disabled(){return G(e)}})}L((e,t)=>{X(f,e),X(x,`Suggested answer: ${t??``} · You can review choices again before creating your setup PAT.`)},[()=>(q(n()),K(()=>n().question.label.replace(` (Space toggles, Enter confirms)`,``))),()=>(q(n()),K(()=>String(n().question.defaultValue)||`none`))]),Y(e,l),k()}hr([`click`,`change`]);var aa=J(``),oa=J(`
    `);function sa(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`controller`,8),i=$(t,`busy`,8),a=$(t,`onSubmit`,8);yi();var o=oa();Hr(o,5,()=>(q(n()),K(()=>n().choices)),Rr,(e,t)=>{var n=aa(),o=F(P(n),!0);ke(),E(n),L(()=>{n.disabled=!r()||i(),X(o,G(t))}),mr(`click`,n,()=>a()(G(t))),Y(e,n)}),E(o),Y(e,o),k()}hr([`click`]);var ca=J(`Open the official GitHub PAT form

    Check the signed-in account, select Only select repositories, then select this repository. GitHub handles 2FA and creates the PAT.

    `,1),la=J(`

    Sent only to this local process. It will not be shown again or saved in browser storage.

    `),ua=J(` `,1);function da(e,t){O(t,!1);let n=M(),r=$(t,`prompt`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8),s=M(``);function c(){let e=G(s);r().kind===`secret`&&N(s,``),o()(e)}wn(()=>q(r()),()=>{N(n,Bi(r().link))}),Tn(),yi();var l=ua(),u=sn(l),d=e=>{var t=ca(),r=sn(t);ke(),L(()=>Q(r,`href`,G(n))),Y(e,t)};Z(u,e=>{G(n)&&e(d)});var f=I(u,2),p=F(f,!0),m=I(f,2);di(m);var h=I(m,2),g=e=>{Y(e,la())};Z(h,e=>{q(r()),K(()=>r().kind===`secret`)&&e(g)});var _=I(h,2);{let e=mt(()=>(q(i()),q(a()),q(r()),G(s),K(()=>!i()||a()||!r().optional&&!G(s).trim())));Ui(_,{label:`Continue`,arrow:!0,onClick:c,get disabled(){return G(e)}})}L(()=>{X(p,(q(r()),K(()=>r().kind===`secret`?`Paste the value here`:`Your answer`))),Q(m,`type`,(q(r()),K(()=>r().kind===`secret`?`password`:`text`))),m.disabled=!i(),Q(m,`placeholder`,(q(r()),K(()=>r().kind===`secret`?`Hidden after submission`:`Type your answer`)))}),gi(m,()=>G(s),e=>N(s,e)),Y(e,l),k()}var fa=J(`
  • `),pa=J(`

      `),ma=J(`

      Before you continue

        `),ha=J(`

        Review exactly what this run may change. The bot PAT and any additional credentials are collected next.

        `,1);function ga(e,t){O(t,!1);let n=M(),r=$(t,`prompt`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8);wn(()=>q(r()),()=>{N(n,[{title:`Files`,items:r().plan.files},{title:`Workflows`,items:r().plan.workflows},{title:`Variables`,items:r().plan.variables},{title:`Secret names`,items:r().plan.secrets}])}),Tn(),yi();var s=ha(),c=I(sn(s),2);Hr(c,5,()=>G(n),Rr,(e,t)=>{var n=pa(),r=P(n),i=P(r),a=F(I(i),!0);E(r);var o=I(r);Hr(o,5,()=>(G(t),K(()=>G(t).items)),Rr,(e,t)=>{var n=fa(),r=F(n,!0);L(()=>X(r,G(t))),Y(e,n)}),E(o),E(n),L(()=>{X(i,`${G(t),K(()=>G(t).title)??``} `),X(a,(G(t),K(()=>G(t).items.length)))}),Y(e,n)}),E(c);var l=I(c,2),u=e=>{var t=ma(),n=I(P(t));Hr(n,5,()=>(q(r()),K(()=>r().plan.warnings)),Rr,(e,t)=>{var n=fa(),r=F(n,!0);L(()=>X(r,G(t))),Y(e,n)}),E(n),E(t),Y(e,t)};Z(l,e=>{q(r()),K(()=>r().plan.warnings.length)&&e(u)});var d=I(l,2),f=P(d);{let e=mt(()=>!i()||a());Ui(f,{label:`Stop here`,variant:`secondary`,onClick:()=>o()(`decline`),get disabled(){return G(e)}})}var p=I(f);{let e=mt(()=>!i()||a());Ui(p,{label:`Approve this plan`,arrow:!0,onClick:()=>o()(`approve`),get disabled(){return G(e)}})}E(d),Y(e,s),k()}var _a=J(`

        `),va=J(`
        CURRENT DECISION
        `);function ya(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`revision`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8);yi();var s=va(),c=P(s),l=F(I(P(c)));E(c);var u=I(c,2),d=e=>{var t=_a(),r=F(t,!0);L(()=>X(r,(q(n()),K(()=>n().description)))),Y(e,t)};Z(u,e=>{q(n()),K(()=>`description`in n()&&n().description)&&e(d)});var f=I(u,2),p=e=>{ia(e,{get prompt(){return n()},get controller(){return i()},get busy(){return a()},get onSubmit(){return o()}})},m=e=>{sa(e,{get prompt(){return n()},get controller(){return i()},get busy(){return a()},get onSubmit(){return o()}})},h=e=>{da(e,{get prompt(){return n()},get controller(){return i()},get busy(){return a()},get onSubmit(){return o()}})},g=e=>{ga(e,{get prompt(){return n()},get controller(){return i()},get busy(){return a()},get onSubmit(){return o()}})};Z(f,e=>{q(n()),K(()=>n().kind===`question`)?e(p):(q(n()),K(()=>n().kind===`choice`||n().kind===`confirm`)?e(m,1):(q(n()),K(()=>n().kind===`text`||n().kind===`secret`)?e(h,2):(q(n()),K(()=>n().kind===`plan`)&&e(g,3))))}),E(s),L(()=>X(l,`SESSION ${r()??``}`)),Y(e,s),k()}var ba=J(` `),xa=J(`
      • `),Sa=J(`

          Conditional grants depend on the choices and GitHub facts. A final audit runs before setup changes.
          `),Ca=J(`

          Permissions follow your choices

          We’ll show the exact grants before you create each PAT. Nothing is created just by opening this page.

          `),wa=J(``);function Ta(e,t){O(t,!1);let n=$(t,`view`,8);yi();var r=wa(),i=P(r),a=F(I(P(i),3),!0);E(i);var o=I(i,2),s=e=>{var t=Sa(),r=I(P(t)),i=F(r),a=I(r),o=F(a,!0),s=I(a,2);Hr(s,5,()=>(q(n()),K(()=>n().permissions.report?.checks??n().permissions.requirements??[])),Rr,(e,t)=>{var n=xa(),r=P(n),i=P(r),a=I(i),o=F(a),s=F(I(a),!0);E(r);var c=I(r),l=P(c,!0),u=I(l),d=e=>{var n=ba(),r=F(n,!0);L(()=>X(r,(G(t),K(()=>G(t).status)))),Y(e,n)};Z(u,e=>{`status`in G(t)&&e(d)}),E(c),E(n),L(()=>{X(i,`${G(t),K(()=>G(t).permission)??``} `),X(o,`${G(t),K(()=>G(t).scope)??``} · ${G(t),K(()=>G(t).applicability)??``}`),X(s,(G(t),K(()=>G(t).reason))),X(l,(G(t),K(()=>G(t).level)))}),Y(e,n)}),E(s),ke(2),E(t),L(()=>{X(i,`${q(n()),K(()=>n().permissions.role===`setup`?`Setup PAT`:`Bot PAT`)??``} access`),X(o,(q(n()),K(()=>n().permissions.report?`Read-only access check`:`Provisional least-privilege grants`)))}),Y(e,t)},c=e=>{Y(e,Ca())};Z(o,e=>{q(n()),K(()=>n().permissions)?e(s):e(c,-1)}),E(r),L(()=>X(a,(q(n()),K(()=>n().repository)))),Y(e,r),k()}var Ea=J(`

          `);function Da(e,t){O(t,!1);let n=M(),r=M(),i=$(t,`outcome`,8),a=$(t,`controller`,8),o=$(t,`onClose`,8);wn(()=>q(i()),()=>{N(n,i()===`complete`?`Your configuration was applied`:i()===`dry-run`?`No changes were made`:i()===`cancelled`||i()===`blocked`?`No setup changes started`:`Check partial changes before retrying`)}),wn(()=>q(i()),()=>{N(r,i()===`complete`?`The temporary setup PAT is not revoked automatically. Delete it in GitHub after confirming your setup. Keep the bot PAT until you rotate the installed Secret.`:i()===`partial`?`A bot Secret write or another change may have succeeded. Inspect GitHub and run copilot doctor before replacing or deleting its PAT.`:`Copilot did not begin applying setup changes. PATs you created in GitHub still exist until you delete them there.`)}),Tn();var s=Ea(),c=P(s),l=F(c,!0),u=I(c),d=F(u,!0),f=I(u),p=F(f,!0),m=I(f,2),h=e=>{Ui(e,{label:`Close local session`,get onClick(){return o()}})};Z(m,e=>{a()&&e(h)}),E(s),L(()=>{X(l,i()===`complete`?`✓`:`!`),X(d,G(n)),X(p,G(r))}),Y(e,s),k()}var Oa=J(`

          Working on the next step

          The local process is checking your answers and preparing the next decision. Keep this page open.

          `);function ka(e){Y(e,Oa())}var Aa=J(``),ja=J(`
          `),Ma=J(``),Na=J(`
          `,1),Pa=J(`
          LOCALHOST ONLY NO CLOUD SETUP ACCOUNT GITHUB OWNS PAT ISSUANCE
          `);function Fa(e,t){O(t,!1);let n=()=>Di(a,`$session`,r),[r,i]=Oi(),a=Ai();Pr(()=>{a.connect();let e=window.setInterval(()=>{n().view?.outcome||a.refresh()},900);return()=>window.clearInterval(e)});async function o(){window.confirm(`Cancel this local setup session? PATs already created in GitHub will still exist.`)&&await a.cancel()}async function s(e){let t=n().view?.promptRevision;t!==void 0&&await a.submit(t,e)}yi();var c=Pa();Jr(`16t12jp`,e=>{Y(e,Aa())});var l=P(c);{let e=mt(()=>n().view?.journey);Ni(l,{get journey(){return G(e)}})}var u=I(l,2),d=P(u);{let e=mt(()=>n().view?.repository);Li(d,{get repository(){return G(e)}})}var f=I(d,2),p=P(f);zi(p,{get view(){return n().view}});var m=I(p,2),h=e=>{var t=ja(),r=I(P(t));E(t),L(()=>X(r,` Reviewing saved choices — pass ${n().view.journey.choiceReviewPass??``}. This is the same setup run, not a restart.`)),Y(e,t)};Z(m,e=>{n().view?.journey?.choiceReviewPass&&n().view.journey.choiceReviewPass>1&&!n().view.outcome&&e(h)});var g=I(m,2),_=e=>{Ki(e,{tone:`warning`,title:`Read-only tab`,message:`Another tab controls this session. You can review progress here or explicitly take over.`,actionLabel:`Take control in this tab`,get onAction(){return a.takeOver}})};Z(g,e=>{!n().controller&&n().view&&e(_)});var v=I(g,2),y=e=>{Ki(e,{tone:`error`,title:`Needs attention`,get message(){return n().error}})};Z(v,e=>{n().error&&e(y)});var b=I(v,2),x=e=>{{let t=mt(()=>n().view.message.tone===`success`?`Checked`:n().view.message.tone===`warning`?`Please note`:n().view.message.tone===`error`?`Needs attention`:`Progress update`);Ki(e,{get tone(){return n().view.message.tone},get title(){return G(t)},get message(){return n().view.message.text},get link(){return n().view.message.link}})}};Z(b,e=>{n().view?.message&&e(x)});var S=I(b,2),ee=e=>{Da(e,{get outcome(){return n().view.outcome},get controller(){return n().controller},get onClose(){return a.close}})},te=e=>{var t=Na(),r=sn(t),i=P(r);Lr(i,()=>n().view.promptRevision,e=>{ya(e,{get prompt(){return n().view.prompt},get revision(){return n().view.revision},get controller(){return n().controller},get busy(){return n().busy},onSubmit:s})}),Ta(I(i,2),{get view(){return n().view}}),E(r);var a=I(r,2),c=e=>{var t=Ma();L(()=>t.disabled=n().busy),mr(`click`,t,o),Y(e,t)};Z(a,e=>{n().controller&&n().view.journey?.current!==`Apply`&&e(c)}),Y(e,t)},ne=e=>{ka(e,{})};Z(S,e=>{n().view?.outcome?e(ee):n().view?.prompt?e(te,1):e(ne,-1)}),ke(2),E(f),E(u),E(c),Y(e,c),k(),i()}hr([`click`]),kr(Fa,{target:document.getElementById(`app`)}); \ No newline at end of file diff --git a/build/web/assets/index-lEwj5VdR.css b/build/web/assets/index-lEwj5VdR.css new file mode 100644 index 000000000..38607f317 --- /dev/null +++ b/build/web/assets/index-lEwj5VdR.css @@ -0,0 +1 @@ +:root{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light dark;--page:#f6f8f7;--side:#102423;--side-line:#31504b;--side-text:#e8f5f0;--surface:#fff;--surface-soft:#f3f7f5;--line:#d7e3df;--control-line:#748b82;--text:#18312c;--muted:#58736b;--accent:#176e5e;--accent-strong:#075743;--accent-tint:#dff4e9;--focus:#966000;--warn:#76510d;--warn-bg:#fff6df;--error:#a73434;--error-bg:#fff0ec;--shadow:0 18px 50px #1e403414;font-family:Inter,ui-sans-serif,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}@media (prefers-color-scheme:dark){:root{--lightningcss-light: ;--lightningcss-dark:initial}:root:not([data-theme=light]){--page:#0d1716;--side:#10201d;--side-line:#294a41;--side-text:#eaf7f1;--surface:#182722;--surface-soft:#20342d;--line:#355247;--control-line:#688f7f;--text:#eaf5ee;--muted:#adccbc;--accent:#7ed6ac;--accent-strong:#a4edc2;--accent-tint:#234b38;--focus:#ffca6a;--warn:#ffdd8a;--warn-bg:#463b21;--error:#ffc0b7;--error-bg:#4a2b2a;--shadow:0 18px 50px #00000024}}:root[data-theme=dark]{--page:#0d1716;--side:#10201d;--side-line:#294a41;--side-text:#eaf7f1;--surface:#182722;--surface-soft:#20342d;--line:#355247;--control-line:#688f7f;--text:#eaf5ee;--muted:#adccbc;--accent:#7ed6ac;--accent-strong:#a4edc2;--accent-tint:#234b38;--focus:#ffca6a;--warn:#ffdd8a;--warn-bg:#463b21;--error:#ffc0b7;--error-bg:#4a2b2a;--shadow:0 18px 50px #00000024}:root[data-theme=light]{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light}:root[data-theme=dark]{--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}*{box-sizing:border-box}body{background:var(--page);color:var(--text);margin:0}button,input,select{font:inherit}button{cursor:pointer}button:disabled{cursor:not-allowed;opacity:.5}:focus-visible{outline:3px solid var(--focus);outline-offset:3px}a{color:var(--accent-strong);text-underline-offset:3px}.card,.context-card{background:var(--surface);border:1px solid var(--line);box-shadow:var(--shadow);border-radius:15px}.primary{border:1px solid var(--accent-strong);background:var(--accent-strong);color:var(--side);border-radius:8px;min-height:43px;padding:12px 18px;font-size:12px;font-weight:800}:root[data-theme=light] .primary,:root:not([data-theme=dark]) .primary{color:#fff}@media (prefers-color-scheme:dark){:root:not([data-theme=light]) .primary{color:#0d2419}}.primary span{margin-left:18px}.primary:hover:not(:disabled){filter:brightness(1.1)}.secondary{border:1px solid var(--control-line);color:var(--text);background:var(--surface-soft);border-radius:8px;padding:11px 16px;font-size:12px;font-weight:700}@media (prefers-reduced-motion:reduce){*,:before,:after{scroll-behavior:auto!important;transition-duration:.01ms!important;animation-duration:.01ms!important}}.shell{grid-template-columns:minmax(250px,288px) minmax(0,1fr);min-height:100vh;display:grid}.sidebar{background:var(--side);color:var(--side-text);flex-direction:column;height:100vh;padding:34px 28px;display:flex;position:sticky;top:0}.brand{letter-spacing:-.035em;align-items:center;gap:13px;display:flex}.brand-mark{color:#0c3021;background:#75d9a0;border-radius:11px;place-items:center;width:37px;height:37px;font-size:26px;line-height:1;display:grid}.brand strong{font-size:23px;line-height:1;display:block}.brand small{letter-spacing:.23em;color:#aac8bd;margin-top:5px;font-size:9px;font-weight:800;display:block}.rail-caption{color:#9dbab0;letter-spacing:.18em;margin:78px 0 22px 7px;font-size:10px;font-weight:800}.steps{margin:0;padding:0;list-style:none;position:relative}.steps:before{content:"";background:var(--side-line);width:1px;position:absolute;top:22px;bottom:22px;left:19px}.steps li{color:#a6c2b7;border-radius:10px;align-items:center;gap:16px;min-height:55px;padding:8px 12px 8px 1px;font-size:13px;font-weight:600;display:flex;position:relative}.steps li.current{color:#fff;background:#25443b}.steps li.completed{color:#dbf2e4}.step-index{border:1px solid var(--side-line);background:var(--side);letter-spacing:.04em;border-radius:50%;flex:0 0 37px;place-items:center;height:37px;font-size:11px;font-weight:800;display:grid}.steps .current .step-index{color:#102b1d;background:#80dba8;border-color:#80dba8}.steps .completed .step-index{color:#b9f8c9;background:#204c37;border-color:#45966b;font-size:15px}.sidebar-note{border:1px solid var(--side-line);background:#ffffff09;border-radius:13px;gap:13px;margin-top:auto;padding:19px 16px;display:flex}.sidebar-note>span{color:#8fe1ae;font-size:20px}.sidebar-note strong{font-size:12px}.sidebar-note p{color:#afcabe;margin:6px 0 0;font-size:11px;line-height:1.6}.main{min-width:0}.topbar{border-bottom:1px solid var(--line);background:var(--surface);justify-content:space-between;align-items:center;gap:16px;height:80px;padding:0 clamp(24px,4vw,70px);display:flex}.breadcrumb{align-items:center;gap:12px;min-width:0;font-size:12px;display:flex}.breadcrumb span:first-child{color:var(--muted);letter-spacing:.14em;font-size:10px;font-weight:800}.breadcrumb span:nth-child(2){color:var(--muted)}.breadcrumb strong{white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.top-actions{flex-shrink:0;align-items:center;gap:18px;display:flex}.local-pill{color:var(--accent-strong);background:var(--accent-tint);letter-spacing:.08em;white-space:nowrap;border-radius:6px;padding:8px 11px;font-size:10px;font-weight:800}.pulse-dot{background:currentColor;border-radius:50%;width:6px;height:6px;margin-right:5px;display:inline-block}.theme-switch{border:1px solid var(--line);background:var(--surface-soft);border-radius:8px;padding:3px;display:flex}.theme-switch button{color:var(--muted);background:0 0;border:0;border-radius:5px;min-width:31px;height:27px;font-size:11px}.theme-switch button.active{background:var(--surface);color:var(--text);font-weight:800;box-shadow:0 1px 4px #0000001f}.content{max-width:1320px;margin:0 auto;padding:52px clamp(24px,4vw,70px) 35px}.eyebrow{color:var(--accent-strong);letter-spacing:.18em;align-items:center;gap:10px;font-size:10px;font-weight:900;display:flex}.eyebrow-line{background:var(--accent);width:21px;height:2px}.eyebrow-count{color:var(--muted);letter-spacing:.09em;margin-left:8px}h1{letter-spacing:-.045em;max-width:860px;margin:15px 0 13px;font-size:clamp(30px,3vw,45px);line-height:1.15}.lede{color:var(--muted);max-width:700px;margin:0 0 30px;font-size:14px;line-height:1.65}.workspace-grid{grid-template-columns:minmax(0,1.65fr) minmax(230px,.8fr);align-items:start;gap:19px;display:grid}.context-column{gap:17px;display:grid}.context-card{box-shadow:none;padding:25px}.context-icon{background:var(--accent-tint);width:32px;height:32px;color:var(--accent-strong);border-radius:8px;place-items:center;font-size:19px;display:grid}.context-card h2{letter-spacing:-.015em;margin:17px 0 7px;font-size:14px}.context-card p,.context-card>small{color:var(--muted);margin:0 0 12px;font-size:12px;line-height:1.65;display:block}.context-card code{background:var(--surface-soft);overflow-wrap:anywhere;border-radius:6px;padding:10px;font-size:11px;display:block}.permissions ul{max-height:270px;margin:8px 0 13px;padding:0;list-style:none;overflow:auto}.permissions li{border-bottom:1px solid var(--line);justify-content:space-between;gap:10px;padding:9px 0;font-size:11px;display:flex}.permissions li small{color:var(--muted);margin-top:3px;display:block}.permissions li strong{color:var(--accent-strong);text-transform:uppercase;font-size:9px}footer{color:var(--muted);opacity:.85;letter-spacing:.11em;margin-top:40px;font-size:9px;font-weight:700}footer span{margin:0 8px}.decision-card{min-height:360px;padding:clamp(25px,3vw,40px)}.card-header{justify-content:space-between;align-items:center;gap:12px;margin-bottom:29px;display:flex}.card-kicker{color:var(--accent-strong);letter-spacing:.17em;font-size:10px;font-weight:900}.revision{color:var(--muted);letter-spacing:.08em;font-size:10px}.description{white-space:pre-line;color:var(--muted);margin-top:0;font-size:13px;line-height:1.65}.question-heading{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:10px;margin-bottom:15px;display:flex}.question-heading h2,.decision-card>label{margin:0 0 10px;font-size:15px;font-weight:700;line-height:1.4;display:block}.phase-tag{color:var(--accent-strong);background:var(--accent-tint);letter-spacing:.07em;border-radius:5px;padding:6px 8px;font-size:9px;font-weight:900}input[type=text],input[type=password],input[type=number],select{border:1px solid var(--control-line);background:var(--surface-soft);width:100%;min-height:46px;color:var(--text);border-radius:8px;padding:10px 13px}input[type=checkbox]{accent-color:var(--accent);width:17px;height:17px}.field-help{color:var(--muted);margin:14px 0 24px;font-size:12px;line-height:1.6}.segmented{gap:9px;display:flex}.segmented button{border:1px solid var(--control-line);color:var(--text);background:var(--surface-soft);border-radius:8px;flex:1;padding:13px;font-weight:700}.segmented button.selected{border-color:var(--accent);background:var(--accent-tint);color:var(--accent-strong)}.check-grid{gap:7px;max-height:280px;display:grid;overflow-y:auto}.check-option{background:var(--surface-soft);border:1px solid var(--control-line);border-radius:7px;align-items:center;gap:10px;padding:10px 13px;font-size:12px;display:flex}.choice-list{gap:9px;display:grid}.choice-card{text-align:left;background:var(--surface-soft);width:100%;min-height:52px;color:var(--text);border:1px solid var(--control-line);border-radius:8px;justify-content:space-between;align-items:center;padding:13px 15px;font-size:13px;font-weight:650;display:flex}.choice-card:hover:not(:disabled){border-color:var(--accent);background:var(--accent-tint)}.github-link{background:var(--accent-tint);border:1px solid var(--accent);border-radius:8px;margin:0 0 12px;padding:14px;font-size:13px;font-weight:800;text-decoration:none;display:block}.github-link span{float:right}.plan-sections{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;display:grid}.plan-sections>div,.plan-warnings{background:var(--surface-soft);border:1px solid var(--line);border-radius:8px;padding:13px}.plan-sections h3,.plan-warnings h3{justify-content:space-between;margin:0 0 8px;font-size:12px;display:flex}.plan-sections h3 span{color:var(--accent-strong)}.plan-sections ul,.plan-warnings ul{overflow-wrap:anywhere;max-height:120px;margin:0;padding-left:18px;font-size:11px;line-height:1.7;overflow:auto}.plan-warnings{color:var(--warn);background:var(--warn-bg);margin-top:10px}.button-row{justify-content:space-between;gap:10px;margin-top:18px;display:flex}.review-pass,.banner{white-space:pre-line;border-radius:8px;margin:0 0 20px;padding:14px 18px;font-size:12px;line-height:1.5}.review-pass{color:var(--accent-strong);background:var(--accent-tint);border:1px solid var(--accent)}.review-pass span{margin-right:8px;font-weight:800}.banner{background:var(--surface-soft);border:1px solid var(--line)}.banner p{margin:5px 0 0}.banner.warning{color:var(--warn);background:var(--warn-bg);border-color:var(--warn)}.banner.error{color:var(--error);background:var(--error-bg);border-color:var(--error)}.banner.success{color:var(--accent-strong);background:var(--accent-tint);border-color:var(--accent)}.banner button{margin-top:12px}.cancel-link{color:var(--muted);background:0 0;border:0;margin-top:18px;padding:5px 0;font-size:12px;text-decoration:underline}.result-card,.waiting-card{max-width:750px;padding:36px}.result-icon{background:var(--accent-tint);width:43px;height:43px;color:var(--accent-strong);border-radius:50%;place-items:center;font-size:22px;display:grid}.result-card h2,.waiting-card h2{margin:18px 0 10px;font-size:21px}.result-card p,.waiting-card p{color:var(--muted);font-size:13px;line-height:1.6}.result-links{flex-wrap:wrap;align-items:center;gap:20px;margin:22px 0;font-size:12px;display:flex}.result-links code{background:var(--surface-soft);border-radius:5px;padding:8px}.spinner{border:3px solid var(--line);border-top-color:var(--accent);border-radius:50%;width:25px;height:25px;animation:1s linear infinite spin}@keyframes spin{to{transform:rotate(360deg)}}@media (width<=1100px){.workspace-grid{grid-template-columns:1fr}.context-column{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (width<=780px){.shell{display:block}.sidebar{height:auto;padding:16px 20px;position:static}.rail-caption,.sidebar-note{display:none}.steps{gap:4px;margin-top:18px;display:flex;overflow-x:auto}.steps:before{display:none}.steps li{flex:none;gap:6px;min-height:37px;padding:4px 7px;font-size:11px}.step-index{flex-basis:26px;width:26px;height:26px}.topbar{flex-wrap:wrap;height:auto;min-height:65px;padding:12px 20px}.content{padding:28px 20px}}@media (width<=540px){.context-column,.plan-sections{grid-template-columns:1fr}.top-actions{justify-content:space-between;width:100%}.decision-card{padding:22px}.breadcrumb{max-width:100%}h1{font-size:29px}} diff --git a/build/web/index.html b/build/web/index.html new file mode 100644 index 000000000..6159c500e --- /dev/null +++ b/build/web/index.html @@ -0,0 +1,14 @@ + + + + + + + Copilot · Setup studio + + + + +
          + + diff --git a/docs/authentication.mdx b/docs/authentication.mdx index 6913ec01c..0fbc48bd1 100644 --- a/docs/authentication.mdx +++ b/docs/authentication.mdx @@ -12,6 +12,24 @@ For [guarded PR approval](/pull-requests/guarded-approval), this same runtime PA The setup PAT and workflow PAT may have different owners and permissions. Do not paste the workflow PAT into the setup prompt unless you intentionally want the same token to perform both roles. +`copilot setup --web` offers the same guided or manual PAT choices in a local +browser page. It has separate masked inputs for the temporary operator PAT and +the bot PAT, displays the relevant grants next to each step, and verifies +identity and access through the same setup use cases as the terminal. The +browser never calls GitHub with a PAT directly or stores PAT values. GitHub's +own tab handles account switching, 2FA, repository selection, and creation. +The local page cannot prove that a browser extension or another process under +your OS user cannot see the value while you paste it. Afterward, delete the +temporary setup PAT in GitHub yourself; do not delete the bot PAT while the +installed Actions Secret still depends on it. + +The web assistant does not dispatch or temporarily install a credential-health +workflow before you press **Apply setup**. Existing Secret values cannot be +read back: re-enter the bot PAT to check its grants, and treat any preserved +optional provider Secret marked `unverifiable` as unknown until a later +`copilot doctor`/workflow check. The terminal setup retains its existing +credential-health behavior. + ## Assisted creation in the terminal When `copilot setup` needs a PAT interactively, it offers a guided link (the diff --git a/docs/configuration-checklist.mdx b/docs/configuration-checklist.mdx index 96e80c7ad..23df65da8 100644 --- a/docs/configuration-checklist.mdx +++ b/docs/configuration-checklist.mdx @@ -20,7 +20,10 @@ If guarded PR approval is selected, confirm the exact test/coverage producer tup ## Credentials -- [ ] Before entering each PAT, the setup terminal table matches the intended repository/organization target, access level, selected features, and storage scope. +- [ ] If using `copilot setup --web`, the repository shown in the local page is the intended checkout, the signed-in GitHub account and single selected repository are checked separately on each PAT form, and the final plan plus Apply step are explicitly reviewed. +- [ ] System/Light/Dark mode is readable in the current browser; theme choice does not change permission, credential, or setup policy. + +- [ ] Before entering each PAT, the setup terminal table or local web permission panel matches the intended repository/organization target, access level, selected features, and storage scope. - [ ] After entry, every `❌ Missing` required permission has been corrected; required unverifiable reads have been retried; every `? Unverifiable` required write has been compared manually with the PAT settings and explicitly acknowledged without treating it as a pass. - [ ] A publicly readable endpoint has not been mistaken for PAT evidence. After valid identity, only the exact successful public-repository read may be operationally usable while still shown as `Unverifiable`; public organization Members, Issue Types and writes never gain that exception. Members read needs a verified, active self-membership response for the selected organization. - [ ] If the `PAT` Secret already exists, its value has been re-entered (or supplied again to unattended setup) and the full workflow-PAT permission report has completed; credential-health success alone is not treated as permission evidence. diff --git a/docs/configuration.mdx b/docs/configuration.mdx index ef64f6629..33cd43b61 100644 --- a/docs/configuration.mdx +++ b/docs/configuration.mdx @@ -190,8 +190,15 @@ Guided setup asks the permission-affecting Secret and Variable management and de Organization storage is available only for organization-owned repositories and requires organization Actions permissions on the setup PAT. If only one class should be global, set that class to `organization` and leave the other at `repository`. `--skip-secrets` and `--skip-variables` disable their respective setup operations without changing the other class. Interactive PAT guidance does not add configuration keys: it is a one-run -choice at each hidden prompt. The setup-PAT form link contains grants derived -from reviewed local intent; remote-only conditions are disclosed separately +choice at each hidden prompt. The setup-PAT form link uses the same grant +policy whether shown in the terminal or through `copilot setup --web`. Web +mode is also a one-run presentation choice: it adds no stored +theme, browser account, host, port, or credential setting. Non-secret flags +and `--config` keep their normal precedence; fields fixed by them are skipped +by the questionnaire. A supplied environment setup PAT is never consumed +silently in the web mode; the browser asks whether to use it without seeing +its value. `--web` rejects unattended approval and secret-bearing flags. +The link reflects reviewed local intent; remote-only conditions are disclosed separately and may require correction after inspection. The bot-PAT link is built from the final workflow permission policy and suggests a 90-day expiry; the bot account owner must renew it and replace Secret `PAT` before diff --git a/docs/dependency-rules.md b/docs/dependency-rules.md index 4874e20e3..731bd9fa0 100644 --- a/docs/dependency-rules.md +++ b/docs/dependency-rules.md @@ -19,6 +19,14 @@ entrypoint Inner behavior reaches outer details only through contracts owned by the appropriate inner boundary. +The local setup browser is an outer presentation adapter. Its Svelte files +may import type-only redacted view contracts from `src/application/contracts` +but cannot import provider adapters, mutation use cases, Node HTTP, or PAT +permission tables. `src/cli/web_setup_server.ts` owns loopback transport and +static assets; `src/cli/web_setup_adapters.ts` maps semantic browser decisions +to the existing application ports. Domain/application policy modules must not +import the browser, Vite, Node HTTP, or terminal renderers. + ## Current physical layers ### Pure model and policy subset diff --git a/docs/development/architecture.mdx b/docs/development/architecture.mdx index 674f196c9..10c7327b2 100644 --- a/docs/development/architecture.mdx +++ b/docs/development/architecture.mdx @@ -183,6 +183,38 @@ can write Secrets. GitHub owns the browser session, 2FA, token generation, and deletion. Manual and unattended inputs retain the prior audit without the new bot-ID assertion. +The optional `setup --web` presentation compiles Svelte/Vite into packaged +`build/web` assets. The CLI serves those assets from a loopback-only Node HTTP +server; the browser sends bounded semantic answers to an in-memory bridge. +The bridge exposes the existing questionnaire policy state, credential ports, +`SetupWizardUseCase`, permission audits, and local Action application, rather +than parsing terminal output. It returns redacted views only: token values are +never serialized to the page or persisted in browser storage. The server +checks exact Host/Origin, a per-controller capability, prompt revision, +content type and body size, sets a restrictive CSP, and serves only bundled +asset paths. Before web Apply, the CLI rechecks repository identity, selected +file hashes, remote facts and final setup PAT access. The terminal path remains +the default and neither Action nor Bugbot API bundles import Svelte runtime. +The shared pre-PAT intent, initial and configured-PAT audits, and final web Apply decisions +live in application use cases with semantic ports; the command wires concrete +presenters, Git/remote readers, and the mutation entrypoint. CLI flag/file +merging uses a pure application policy behind a CLI parsing adapter. Import +graph tests reject transitive dependencies from these use cases into CLI, +infrastructure, Action, or browser code, and browser components can import +only redacted application contracts as types. + +Inside `web/src`, `App.svelte` is only the page shell. The session client owns +same-origin bootstrap, polling and revision-bound commands; it holds no pasted +PAT in a store. Presenters in `components/` render progress, prompts, context, +status and results from redacted views and callbacks, without network or +provider imports. A new prompt kind goes in its presenter, not the shell. +`style.css` imports layered palette, foundation, layout, controls, feedback +and responsive styles; shared card/button/banner patterns and semantic color +tokens cover both light and dark themes. The browser architecture test guards +these dependencies and file-size budgets. The application-level setup-session +coordinator extraction and full web acceptance budget remain open per the +[web setup SDD](../../specs/local-web-setup-assistant.md). + PAT permission validation follows the same dependency rule. The application `SetupTokenPermissionsUseCase` validates identity before invoking the narrow `SetupTokenPermissionQueryPort`; the infrastructure adapter performs only safe diff --git a/docs/how-to-use.mdx b/docs/how-to-use.mdx index fd06aa5c8..d0d8ff046 100644 --- a/docs/how-to-use.mdx +++ b/docs/how-to-use.mdx @@ -38,6 +38,35 @@ If the checkout does not include the compiled `build/` folder (e.g. it is gitign Once installed, the `copilot` command is available globally. Repository-dependent commands such as `copilot setup`, `copilot doctor`, `copilot check-progress`, `copilot think`, and `copilot do` must be run **from the root of the target repository**. The `copilot upgrade`, `copilot --version`, and help flows can run from any directory. Commands that access GitHub accept `--token` or `PERSONAL_ACCESS_TOKEN` from the environment. `copilot setup` and `copilot doctor` securely prompt for the setup PAT when run interactively; no `.env` file is read or created. `copilot setup --dry-run` is the only setup mode that can run without a token. See [CLI commands](/single-actions/workflow-and-cli). +Prefer a visual walkthrough? Run `copilot setup --web` from the repository +root. The CLI starts a short-lived page on `127.0.0.1` and opens your browser; +if opening fails, paste the printed local URL into a browser on this computer. +The page shows the same six setup stages, keeps the review pass visibly part +of the current run, presents permissions and the final plan, and asks for a +separate final **Apply setup** approval. Use the System/Light/Dark control in +the top bar to choose a readable palette; the choice lasts only in that tab. +PAT values are submitted to the local process through masked fields and are +not restored after refreshing the page. A second tab is read-only until you +explicitly take control there. The terminal remains the default with +`copilot setup`. + +The web mode still opens GitHub's official form in a separate tab for each +PAT; it cannot pick your GitHub account, complete 2FA, select an individual +repository, generate a PAT, or revoke it. Confirm the account and repository +on GitHub yourself. If `PERSONAL_ACCESS_TOKEN` is available in your +environment, the web page asks whether to use it without revealing its value; +closing Copilot cannot unset the parent shell variable. `--web` cannot be +combined with `--non-interactive`, `--yes`, `--token`, `--workflow-pat`, +`--secret`, or `--confirm-unverifiable-write-permissions`: provide those +decisions in the browser or use the terminal setup instead. `--dry-run --web` +shows a no-change preview. + +Before its final Apply approval, the web mode performs read-only GitHub +inspection and PAT checks; it does not dispatch or temporarily install a +credential-health workflow. Re-enter an existing bot PAT for its grant audit. +An optional existing provider Secret that you choose to keep may remain +`unverifiable`; check runtime health with `copilot doctor` after installation. + Interactive `copilot setup` offers a guided GitHub link or manual entry for each PAT. The first link prepares a short-lived **setup PAT** for the person configuring the repository. Before showing it, guided setup asks only the local diff --git a/docs/security-operations/operations/provisioning.mdx b/docs/security-operations/operations/provisioning.mdx index f870d88dc..6392a3a44 100644 --- a/docs/security-operations/operations/provisioning.mdx +++ b/docs/security-operations/operations/provisioning.mdx @@ -4,6 +4,12 @@ description: Pinned installation and verification of the selected agent CLI. --- # CLI provisioning +For local repository onboarding, `copilot setup --web` serves the precompiled +assistant from the installed CLI package on `127.0.0.1`; it does not start a +Vite development server, download browser assets, or provision an agent CLI. +If the browser opener is unavailable, use the loopback URL printed in the +terminal. The default `copilot setup` remains a fully terminal-driven path. + When running in GitHub Actions, the Action provisions and verifies only the selected runtime. `AGENT_PROVISIONING=auto` reuses any available operator-owned executable without replacing it. If the default Codex or OpenCode executable is diff --git a/docs/security-operations/operations/troubleshooting.mdx b/docs/security-operations/operations/troubleshooting.mdx index 73f9b80d1..383bcd0bf 100644 --- a/docs/security-operations/operations/troubleshooting.mdx +++ b/docs/security-operations/operations/troubleshooting.mdx @@ -23,6 +23,16 @@ scope before revoking or replacing the bot PAT: it may already be active. Cancelling setup never revokes either PAT. Delete an unused setup PAT in GitHub; renew an installed bot PAT before its suggested 90-day expiry. +For `copilot setup --web`, an inaccessible page or failed browser launch does +not imply a setup failure: open the exact `127.0.0.1` URL printed by the CLI. +If the local bind or bundled assets fail, stop and use `copilot setup` in the +terminal. A second browser tab is read-only until you select **Take control +in this tab**; the former tab then cannot submit decisions. A rejected stale +answer means the page should refresh to the current decision, not replay it. +If the page closes during Apply, inspect the terminal result and run +`copilot doctor` before retrying; do not assume that completed local or remote +writes were rolled back. No local session shutdown revokes a PAT in GitHub. + **Setup cancellation:** `Ctrl-C` or end-of-input intentionally exits 130 and diff --git a/package.json b/package.json index a9bfc72db..337d56ffa 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "files": [ "action.yml", "build/cli/index.js", + "build/web/", "build/github_action/index.js", "build/api/index.js", "build/api/src/", @@ -48,7 +49,9 @@ "node": ">=24" }, "scripts": { - "build": "node scripts/clean-generated-bundles.cjs && ncc build src/actions/github_action.ts -o build/github_action && ncc build src/cli.ts -o build/cli && ncc build src/api.ts -o build/api && node scripts/prepare-generated-bundles.cjs && chmod +x build/cli/index.js", + "build": "node scripts/clean-generated-bundles.cjs && pnpm run build:web && ncc build src/actions/github_action.ts -o build/github_action && ncc build src/cli.ts -o build/cli && ncc build src/api.ts -o build/api && node scripts/prepare-generated-bundles.cjs && chmod +x build/cli/index.js", + "build:web": "vite build --config web/vite.config.mts", + "check:web": "svelte-check --tsconfig web/tsconfig.json && vite build --config web/vite.config.mts", "validate:build": "node scripts/validate-build.cjs", "validate:npm-package": "node scripts/validate-npm-package.cjs", "smoke:npm-package": "node scripts/smoke-test-npm-package.cjs", @@ -84,13 +87,17 @@ }, "devDependencies": { "@eslint/js": "^10.0.1", + "@sveltejs/vite-plugin-svelte": "^7.3.1", "@types/jest": "^30.0.0", "@types/node": "^22.9.1", "@vercel/ncc": "^0.36.1", "eslint": "^10.10.0", "jest": "^30.5.1", + "svelte": "^5.57.1", + "svelte-check": "^4.7.6", "ts-jest": "^29.4.5", "typescript": "^5.2.2", - "typescript-eslint": "^8.70.0" + "typescript-eslint": "^8.70.0", + "vite": "^8.3.1" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 77da4790a..6cb3a48dc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -42,6 +42,9 @@ importers: '@eslint/js': specifier: ^10.0.1 version: 10.0.1(eslint@10.10.0) + '@sveltejs/vite-plugin-svelte': + specifier: ^7.3.1 + version: 7.3.1(svelte@5.57.1(@typescript-eslint/types@8.70.0))(vite@8.3.1(@types/node@22.20.1)) '@types/jest': specifier: ^30.0.0 version: 30.0.0 @@ -57,6 +60,12 @@ importers: jest: specifier: ^30.5.1 version: 30.5.1(@types/node@22.20.1) + svelte: + specifier: ^5.57.1 + version: 5.57.1(@typescript-eslint/types@8.70.0) + svelte-check: + specifier: ^4.7.6 + version: 4.7.6(picomatch@4.0.5)(svelte@5.57.1(@typescript-eslint/types@8.70.0))(typescript@5.9.3) ts-jest: specifier: ^29.4.5 version: 29.4.12(@babel/core@7.29.7)(@jest/transform@30.5.1)(@jest/types@30.5.1)(babel-jest@30.5.1(@babel/core@7.29.7))(jest-util@30.5.1)(jest@30.5.1(@types/node@22.20.1))(typescript@5.9.3) @@ -66,6 +75,9 @@ importers: typescript-eslint: specifier: ^8.70.0 version: 8.70.0(eslint@10.10.0)(typescript@5.9.3) + vite: + specifier: ^8.3.1 + version: 8.3.1(@types/node@22.20.1) packages: @@ -454,6 +466,9 @@ packages: '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + '@jridgewell/sourcemap-codec@1.6.0': + resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} + '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} @@ -521,6 +536,9 @@ packages: '@octokit/types@18.0.0': resolution: {integrity: sha512-l6bAF43PNxkJp6g+W4PjoUSSkxHomXw2nOum5CTftJz1NlV3vu93NImgOYtLf6CbBUb5j+fiuzW0PPQ5JTSvZA==} + '@oxc-project/types@0.151.0': + resolution: {integrity: sha512-J1yXrIlNDZVzE3ada310xeAw7nH8yCAyLPuUIsjKatFPmfn5bS1oW+cM+QsGOtVWd5nhSpbwZWx/rue+r5Z+PA==} + '@parcel/watcher-android-arm64@2.6.0': resolution: {integrity: sha512-trgpLSCKRC/huFjXX/Smh+0sWe4+YtKfktIToiMl59ghz7z+qkH6kMvNnUbLyRs9N11t8l4svSCs1+5B3rOAhA==} engines: {node: '>= 10.0.0'} @@ -605,6 +623,99 @@ packages: resolution: {integrity: sha512-SEeaJLb3qBNF/OaXnaR1NmmBbFYk1zC0ZH/52fATcRPLFg/p791YrcyFFy44Bo9sLaGuSuLp5Q6axbb/O+v/RA==} engines: {node: ^14.18.0 || >=16.0.0} + '@rolldown/binding-android-arm-eabi@1.2.11': + resolution: {integrity: sha512-A5kXfGKvKWWZE0TtPrfsvT+q4Y5d1QG8gGUzpYjGydM+fARM9MuX90PrXYXe0XbsDVgyxxNzHo6giCj90bsFNw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@rolldown/binding-android-arm64@1.2.11': + resolution: {integrity: sha512-z6cTycz+iJ4PVkuL4HHW4DfTfoeU/2nqYYuSOrTmH7yHK5Y0LCOnA03V4ZNxavyVaU1oOqUgIg2klN/s+USGOA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.2.11': + resolution: {integrity: sha512-jShvqNtP6vDC6/A5JOAzbVV+DkgHqhl/ScVCJEbt+TUY6QYz7YnXcrg3sLtFBniro0f/Ld50ZwCWA6f7KYD1nQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.2.11': + resolution: {integrity: sha512-f2i2xiNWq1Z1l2++q2fuhZRdLAT3aqxD6vRNm1RAxpUoBcdqNB3C0s1Bt+K+PbEx2F5F4gQp6hqKkphCY/xF9w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.2.11': + resolution: {integrity: sha512-4Ir5FSOKIAMr4r0kExpt1s3bMgzJU3rA45AYOHtQpls0oNeqcYBKrWMlckrYH4KCfGLfkfn1tN1dmZPMVsdXow==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.2.11': + resolution: {integrity: sha512-/gnRDM+39BROzAN/k1OZjDPnDMcZxB/0EUxKjONO5yVkNEvlsoMDrxGNKgZi/ttFriS2gwlDNzB65pvNbFOXIQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.2.11': + resolution: {integrity: sha512-PFaK8HwvAHbaKbBcDNQihjMKYvFnA5hiENx/l5tphTDz1E0WFp32l0A7aq7lyUwGsRw/xSrNIy/gIK4thrSCrw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@rolldown/binding-linux-arm64-musl@1.2.11': + resolution: {integrity: sha512-AskzJUIKRLPxkruR1wLKewGbOw+EYfU/9lOrBFj4AFrEA8hPpKFnODWNu2WLaNs0QNkEb9QIJufmVZZIL/bJlg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@rolldown/binding-linux-ppc64-gnu@1.2.11': + resolution: {integrity: sha512-qlUGAheh2yh8afH7QBgx0PrRHN85hKnNd78x8MeMhXivuevgd8vgf6/CstOzmNKY/lLTHvNTrPy98cLnAugzJw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + + '@rolldown/binding-linux-s390x-gnu@1.2.11': + resolution: {integrity: sha512-secpEad+0vCbSfn8upFySkDskv+bGPk3THSDS9Y89yc4rb4kzqHp8Dmyd9BkQW4SnhNXBZCl/6CrO//hZahNJQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + + '@rolldown/binding-linux-x64-gnu@1.2.11': + resolution: {integrity: sha512-mOVBT3dPpkWm8XBWPmU4bf+U6dYDLeMo/9ojUmis4N0L5uu10qra5vOyngZ7/PSdoE4G9KvRt4bloRxNjLas7A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@rolldown/binding-linux-x64-musl@1.2.11': + resolution: {integrity: sha512-Is78i9A8Ui4SqcxUwFJ9uMmjDn58IbVTjFWYdQestFEgeuEmHMLGNriXnVJKkwG2YiZjw8cP0zCTyDMdDGtOOg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@rolldown/binding-openharmony-arm64@1.2.11': + resolution: {integrity: sha512-dUCXneZ87INUMyQ0D+C0HrEBNUPNXHaPmU5GTjyKTJEiussw9Kaj5Ln8UztPe4epV/ffvgNBEadksdYhmW6xJA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-win32-arm64-msvc@1.2.11': + resolution: {integrity: sha512-jByxb6qfd+bH1xUd0qnfFnb17i9sWBPY2tOavJ0l3tdr3OTu+Kvtm8cd/JV5nFt657b1VqGltxg9olOEfofXWw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.2.11': + resolution: {integrity: sha512-/PzKqzAJ03i19oy2ItPvyvaVjOjBCNnfaJs8yvUdGBKmiESgnrJSQ2awd81QzFbbnAmu7YO9ZnJrDCb9VSJPRA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + '@sinclair/typebox@0.34.52': resolution: {integrity: sha512-XiMQh7qqVlxZzcVD+kkGMNGMzcTrDMLWI7S4x7z1MkCkbDPrekpZXEUK0eZqZFMuHQg2a2DZOcDIh9o5v3Gonw==} @@ -614,6 +725,22 @@ packages: '@sinonjs/fake-timers@15.4.0': resolution: {integrity: sha512-DsG+8/LscQIQg68J6Ef3dv10u6nVyetYn923s3/sus5eaGfTo1of5WMZSLf0UJc9KDuKPilPH0UDJCjvNbDNCA==} + '@sveltejs/acorn-typescript@1.0.13': + resolution: {integrity: sha512-wgKggnhZVL9Bfx1OaKKTrYY9BFRk6C8UAkQNUcIv1+llzYrIqy+RZm5HPKzn0NpEBvTVhTqB4kQyllZywsRBRQ==} + peerDependencies: + acorn: ^8.9.0 + + '@sveltejs/load-config@0.2.3': + resolution: {integrity: sha512-VT3qmUb8pRV2QrZjd8iAmtg8lf4W0TIjZbvXtz5MKei/q96teWZgGJyyidJzOjzZzvdq616eSRVeMYIQChUTAQ==} + engines: {node: '>= 18.0.0'} + + '@sveltejs/vite-plugin-svelte@7.3.1': + resolution: {integrity: sha512-ZPsLN8B1e/En+Ak5s4V7srFDT532oS0qieLsQwu63NGKsS+iAjoO2Js1BochlHlglcU+Pt7WAO3C5Ee+4f6gVA==} + engines: {node: ^20.19 || ^22.12 || >=24} + peerDependencies: + svelte: ^5.46.4 + vite: ^8.0.0-beta.7 || ^8.0.0 + '@tybys/wasm-util@0.10.3': resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} @@ -888,6 +1015,14 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + aria-query@5.3.1: + resolution: {integrity: sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g==} + engines: {node: '>= 0.4'} + + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} + babel-jest@30.5.1: resolution: {integrity: sha512-ge1xUVZS91ml09YRMgRGgeKJ4YJpcOiuwteAxFBYLugQyp7cRw+hHej6Ho0vPjvLrjq60bb7JPHH9LAMA1/krA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -985,6 +1120,10 @@ packages: resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} engines: {node: '>=10'} + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + ci-info@4.4.0: resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} engines: {node: '>=8'} @@ -1000,6 +1139,10 @@ packages: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + co@4.6.0: resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} @@ -1061,6 +1204,9 @@ packages: resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} engines: {node: '>=8'} + devalue@5.9.4: + resolution: {integrity: sha512-sPAT4pztbu6586/hrhOnMKS17IJrvg12mXiSPSS3W5qDeN2RGgvZ0diZCm31dBbnevfVmujNO3IM2wrS4Y2Rhg==} + eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} @@ -1120,6 +1266,9 @@ packages: jiti: optional: true + esm-env@1.2.2: + resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==} + espree@11.2.0: resolution: {integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} @@ -1133,6 +1282,14 @@ packages: resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} engines: {node: '>=0.10'} + esrap@2.4.0: + resolution: {integrity: sha512-CQPcneEHw/czj32uwQC52nYnax8H4SXt25FCM+LIwGZ49cMIxpOj3bP8wmzcI+Z0HIy5KkClaPhRLfCySzwHPA==} + peerDependencies: + '@typescript-eslint/types': ^8.2.0 + peerDependenciesMeta: + '@typescript-eslint/types': + optional: true + esrecurse@4.3.0: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} engines: {node: '>=4.0'} @@ -1203,6 +1360,11 @@ packages: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} @@ -1301,6 +1463,9 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} + is-reference@3.0.3: + resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} + is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} @@ -1518,9 +1683,82 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + lightningcss-android-arm64@1.33.0: + resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.33.0: + resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.33.0: + resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.33.0: + resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.33.0: + resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.33.0: + resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.33.0: + resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.33.0: + resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.33.0: + resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.33.0: + resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.33.0: + resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.33.0: + resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} + engines: {node: '>= 12.0.0'} + lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + locate-character@3.0.0: + resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} + locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} @@ -1542,6 +1780,12 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + magic-string@1.4.2: + resolution: {integrity: sha512-vG+rjFRj1PqdIBozIxAGMjPlOhaVe+GXpbttY/iSK7rGcJRMlwNJO7dcUwmUqkymsFLJiNGI06t4D7Fr7yRC9g==} + make-dir@4.0.0: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} @@ -1571,9 +1815,18 @@ packages: resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} engines: {node: '>=16 || 14 >=14.17'} + mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + nanoid@3.3.19: + resolution: {integrity: sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + napi-postinstall@0.3.4: resolution: {integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} @@ -1603,6 +1856,10 @@ packages: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} + obug@2.2.1: + resolution: {integrity: sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q==} + engines: {node: '>=12.20.0'} + onetime@5.1.2: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} @@ -1665,6 +1922,10 @@ packages: resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} engines: {node: '>=12'} + picomatch@4.0.7: + resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} + engines: {node: '>=12'} + pirates@4.0.7: resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} engines: {node: '>= 6'} @@ -1673,6 +1934,10 @@ packages: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} engines: {node: '>=8'} + postcss@8.5.28: + resolution: {integrity: sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==} + engines: {node: ^10 || ^12 || >=14} + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -1702,6 +1967,10 @@ packages: react-is@19.3.0: resolution: {integrity: sha512-UpMYezM4v5/18F28aC66AEsjXIgE02kyEMH6yLdgLXu/UTfa1Ntwck/nNLrbqJsEXW7gPb0coNO9FQse9WTovA==} + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -1714,6 +1983,15 @@ packages: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} + rolldown@1.2.11: + resolution: {integrity: sha512-qpSwIyz0jHQq5qXBTNxFmE6664rJ7O+4TvPFOiOaBSrz8IOHc1koKKSqTM2H6u1UG1+TveuC6vaDHKXFOvb1Kw==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + sade@1.8.1: + resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} + engines: {node: '>=6'} + semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -1746,6 +2024,10 @@ packages: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} @@ -1801,6 +2083,18 @@ packages: resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} engines: {node: '>=10'} + svelte-check@4.7.6: + resolution: {integrity: sha512-t2scM//ZuVbSY/T2w6FSBw1v9s2NEmh/g+sy1lqtosW5ylBV5AF4wFb1Ts9Kf3MbfPDUDJDZ9L436YT0SPTdvw==} + engines: {node: '>= 18.0.0'} + hasBin: true + peerDependencies: + svelte: ^4.0.0 || ^5.0.0-next.0 + typescript: ^5.0.0 || ^6.0.0 + + svelte@5.57.1: + resolution: {integrity: sha512-Uqj49lWKB+iSSnneuwiYYJ7MZgkB+eXr0LXBhv4uDuAkXqnWmq65Sxflfvp0Lc6MdKjMUxGaeOKWJqz5SNiVIA==} + engines: {node: '>=18'} + synckit@0.11.13: resolution: {integrity: sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg==} engines: {node: ^14.18.0 || >=16.0.0} @@ -1915,6 +2209,57 @@ packages: resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} engines: {node: '>=10.12.0'} + vite@8.3.1: + resolution: {integrity: sha512-/bvH9E9tmCXRGp2uXY3WbOldqpTwFkbha/8ANaEQ6VkxhH60KyqLwgZq6lG2y+4uT55x9+9eUHMpQ7uGnOCKjA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.7.1 + esbuild: ^0.27.0 || ^0.28.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitefu@1.1.3: + resolution: {integrity: sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + vite: + optional: true + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -1966,6 +2311,9 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + zimmerframe@1.1.5: + resolution: {integrity: sha512-msJxIvYDYcoNL+PJsu+7qmpDWsYmAxTY+2TNYXXF0hzBzBk0BMecOqDOG/EckUoKCuKwObfbugIl8QpqHDXeFA==} + snapshots: '@actions/core@2.0.3': @@ -2500,6 +2848,8 @@ snapshots: '@jridgewell/sourcemap-codec@1.5.5': {} + '@jridgewell/sourcemap-codec@1.6.0': {} + '@jridgewell/trace-mapping@0.3.31': dependencies: '@jridgewell/resolve-uri': 3.1.2 @@ -2578,6 +2928,8 @@ snapshots: dependencies: '@octokit/openapi-types': 29.0.1 + '@oxc-project/types@0.151.0': {} + '@parcel/watcher-android-arm64@2.6.0': optional: true @@ -2639,6 +2991,53 @@ snapshots: '@pkgr/core@0.3.6': {} + '@rolldown/binding-android-arm-eabi@1.2.11': + optional: true + + '@rolldown/binding-android-arm64@1.2.11': + optional: true + + '@rolldown/binding-darwin-arm64@1.2.11': + optional: true + + '@rolldown/binding-darwin-x64@1.2.11': + optional: true + + '@rolldown/binding-freebsd-x64@1.2.11': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.2.11': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.2.11': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.2.11': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.2.11': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.2.11': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.2.11': + optional: true + + '@rolldown/binding-linux-x64-musl@1.2.11': + optional: true + + '@rolldown/binding-openharmony-arm64@1.2.11': + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.2.11': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.2.11': + optional: true + + '@rolldown/pluginutils@1.0.1': {} + '@sinclair/typebox@0.34.52': {} '@sinonjs/commons@3.0.1': @@ -2649,6 +3048,21 @@ snapshots: dependencies: '@sinonjs/commons': 3.0.1 + '@sveltejs/acorn-typescript@1.0.13(acorn@8.18.0)': + dependencies: + acorn: 8.18.0 + + '@sveltejs/load-config@0.2.3': {} + + '@sveltejs/vite-plugin-svelte@7.3.1(svelte@5.57.1(@typescript-eslint/types@8.70.0))(vite@8.3.1(@types/node@22.20.1))': + dependencies: + deepmerge: 4.3.1 + magic-string: 1.4.2 + obug: 2.2.1 + svelte: 5.57.1(@typescript-eslint/types@8.70.0) + vite: 8.3.1(@types/node@22.20.1) + vitefu: 1.1.3(vite@8.3.1(@types/node@22.20.1)) + '@tybys/wasm-util@0.10.3': dependencies: tslib: 2.8.1 @@ -2917,6 +3331,10 @@ snapshots: argparse@2.0.1: {} + aria-query@5.3.1: {} + + axobject-query@4.1.0: {} + babel-jest@30.5.1(@babel/core@7.29.7): dependencies: '@babel/core': 7.29.7 @@ -3039,6 +3457,10 @@ snapshots: char-regex@1.0.2: {} + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + ci-info@4.4.0: {} cjs-module-lexer@2.2.1: {} @@ -3051,6 +3473,8 @@ snapshots: strip-ansi: 6.0.1 wrap-ansi: 7.0.0 + clsx@2.1.1: {} + co@4.6.0: {} collect-v8-coverage@1.0.3: {} @@ -3087,6 +3511,8 @@ snapshots: detect-newline@3.1.0: {} + devalue@5.9.4: {} + eastasianwidth@0.2.0: {} electron-to-chromium@1.5.406: {} @@ -3157,6 +3583,8 @@ snapshots: transitivePeerDependencies: - supports-color + esm-env@1.2.2: {} + espree@11.2.0: dependencies: acorn: 8.18.0 @@ -3169,6 +3597,12 @@ snapshots: dependencies: estraverse: 5.3.0 + esrap@2.4.0(@typescript-eslint/types@8.70.0): + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + optionalDependencies: + '@typescript-eslint/types': 8.70.0 + esrecurse@4.3.0: dependencies: estraverse: 5.3.0 @@ -3250,6 +3684,9 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 + fsevents@2.3.3: + optional: true + gensync@1.0.0-beta.2: {} get-caller-file@2.0.5: {} @@ -3327,6 +3764,10 @@ snapshots: dependencies: is-extglob: 2.1.1 + is-reference@3.0.3: + dependencies: + '@types/estree': 1.0.9 + is-stream@2.0.1: {} isexe@2.0.0: {} @@ -3753,8 +4194,59 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + lightningcss-android-arm64@1.33.0: + optional: true + + lightningcss-darwin-arm64@1.33.0: + optional: true + + lightningcss-darwin-x64@1.33.0: + optional: true + + lightningcss-freebsd-x64@1.33.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.33.0: + optional: true + + lightningcss-linux-arm64-gnu@1.33.0: + optional: true + + lightningcss-linux-arm64-musl@1.33.0: + optional: true + + lightningcss-linux-x64-gnu@1.33.0: + optional: true + + lightningcss-linux-x64-musl@1.33.0: + optional: true + + lightningcss-win32-arm64-msvc@1.33.0: + optional: true + + lightningcss-win32-x64-msvc@1.33.0: + optional: true + + lightningcss@1.33.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.33.0 + lightningcss-darwin-arm64: 1.33.0 + lightningcss-darwin-x64: 1.33.0 + lightningcss-freebsd-x64: 1.33.0 + lightningcss-linux-arm-gnueabihf: 1.33.0 + lightningcss-linux-arm64-gnu: 1.33.0 + lightningcss-linux-arm64-musl: 1.33.0 + lightningcss-linux-x64-gnu: 1.33.0 + lightningcss-linux-x64-musl: 1.33.0 + lightningcss-win32-arm64-msvc: 1.33.0 + lightningcss-win32-x64-msvc: 1.33.0 + lines-and-columns@1.2.4: {} + locate-character@3.0.0: {} + locate-path@5.0.0: dependencies: p-locate: 4.1.0 @@ -3773,6 +4265,14 @@ snapshots: dependencies: yallist: 3.1.1 + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + + magic-string@1.4.2: + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + make-dir@4.0.0: dependencies: semver: 7.8.5 @@ -3795,8 +4295,12 @@ snapshots: minipass@7.1.3: {} + mri@1.2.0: {} + ms@2.1.3: {} + nanoid@3.3.19: {} + napi-postinstall@0.3.4: {} natural-compare@1.4.0: {} @@ -3815,6 +4319,8 @@ snapshots: dependencies: path-key: 3.1.1 + obug@2.2.1: {} + onetime@5.1.2: dependencies: mimic-fn: 2.1.0 @@ -3875,12 +4381,20 @@ snapshots: picomatch@4.0.5: {} + picomatch@4.0.7: {} + pirates@4.0.7: {} pkg-dir@4.2.0: dependencies: find-up: 4.1.0 + postcss@8.5.28: + dependencies: + nanoid: 3.3.19 + picocolors: 1.1.1 + source-map-js: 1.2.1 + prelude-ls@1.2.1: {} pretty-format@30.4.1: @@ -3909,6 +4423,8 @@ snapshots: react-is@19.3.0: {} + readdirp@4.1.2: {} + require-directory@2.1.1: {} resolve-cwd@3.0.0: @@ -3917,6 +4433,31 @@ snapshots: resolve-from@5.0.0: {} + rolldown@1.2.11: + dependencies: + '@oxc-project/types': 0.151.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm-eabi': 1.2.11 + '@rolldown/binding-android-arm64': 1.2.11 + '@rolldown/binding-darwin-arm64': 1.2.11 + '@rolldown/binding-darwin-x64': 1.2.11 + '@rolldown/binding-freebsd-x64': 1.2.11 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.11 + '@rolldown/binding-linux-arm64-gnu': 1.2.11 + '@rolldown/binding-linux-arm64-musl': 1.2.11 + '@rolldown/binding-linux-ppc64-gnu': 1.2.11 + '@rolldown/binding-linux-s390x-gnu': 1.2.11 + '@rolldown/binding-linux-x64-gnu': 1.2.11 + '@rolldown/binding-linux-x64-musl': 1.2.11 + '@rolldown/binding-openharmony-arm64': 1.2.11 + '@rolldown/binding-win32-arm64-msvc': 1.2.11 + '@rolldown/binding-win32-x64-msvc': 1.2.11 + + sade@1.8.1: + dependencies: + mri: 1.2.0 + semver@6.3.1: {} semver@7.8.5: {} @@ -3935,6 +4476,8 @@ snapshots: slash@3.0.0: {} + source-map-js@1.2.1: {} + source-map@0.6.1: {} sprintf-js@1.0.3: {} @@ -3988,6 +4531,39 @@ snapshots: dependencies: has-flag: 4.0.0 + svelte-check@4.7.6(picomatch@4.0.5)(svelte@5.57.1(@typescript-eslint/types@8.70.0))(typescript@5.9.3): + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + '@sveltejs/load-config': 0.2.3 + chokidar: 4.0.3 + fdir: 6.5.0(picomatch@4.0.5) + picocolors: 1.1.1 + sade: 1.8.1 + svelte: 5.57.1(@typescript-eslint/types@8.70.0) + typescript: 5.9.3 + transitivePeerDependencies: + - picomatch + + svelte@5.57.1(@typescript-eslint/types@8.70.0): + dependencies: + '@jridgewell/remapping': 2.3.5 + '@jridgewell/sourcemap-codec': 1.6.0 + '@sveltejs/acorn-typescript': 1.0.13(acorn@8.18.0) + '@types/estree': 1.0.9 + acorn: 8.18.0 + aria-query: 5.3.1 + axobject-query: 4.1.0 + clsx: 2.1.1 + devalue: 5.9.4 + esm-env: 1.2.2 + esrap: 2.4.0(@typescript-eslint/types@8.70.0) + is-reference: 3.0.3 + locate-character: 3.0.0 + magic-string: 0.30.21 + zimmerframe: 1.1.5 + transitivePeerDependencies: + - '@typescript-eslint/types' + synckit@0.11.13: dependencies: '@pkgr/core': 0.3.6 @@ -4109,6 +4685,21 @@ snapshots: '@types/istanbul-lib-coverage': 2.0.6 convert-source-map: 2.0.0 + vite@8.3.1(@types/node@22.20.1): + dependencies: + lightningcss: 1.33.0 + picomatch: 4.0.7 + postcss: 8.5.28 + rolldown: 1.2.11 + tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 22.20.1 + fsevents: 2.3.3 + + vitefu@1.1.3(vite@8.3.1(@types/node@22.20.1)): + optionalDependencies: + vite: 8.3.1(@types/node@22.20.1) + which@2.0.2: dependencies: isexe: 2.0.0 @@ -4161,3 +4752,5 @@ snapshots: yargs-parser: 21.1.1 yocto-queue@0.1.0: {} + + zimmerframe@1.1.5: {} diff --git a/scripts/render-web-setup-component.cjs b/scripts/render-web-setup-component.cjs new file mode 100644 index 000000000..6f4629f6d --- /dev/null +++ b/scripts/render-web-setup-component.cjs @@ -0,0 +1,26 @@ +const { resolve } = require('node:path'); + +/** Test-only semantic render of the configured Svelte/Vite component tree. */ +async function main() { + const [name, encodedProps] = process.argv.slice(2); + if (!/^[A-Z][A-Za-z]+$/.test(name) || !encodedProps) { + throw new Error('A named component and props are required.'); + } + const { createServer } = await import('vite'); + const server = await createServer({ + configFile: resolve(__dirname, '../web/vite.config.mts'), + server: { middlewareMode: true }, appType: 'custom', logLevel: 'silent', + }); + try { + const component = await server.ssrLoadModule(`/src/components/${name}.svelte`); + // Use the same Svelte SSR runtime as the Vite-transformed component. + const { render } = await server.ssrLoadModule('svelte/server'); + const props = JSON.parse(encodedProps); + for (const key of ['onSubmit', 'onClose', 'onAction']) props[key] = async () => undefined; + process.stdout.write(render(component.default, { props }).body); + } finally { + await server.close(); + } +} + +main().catch(error => { console.error(error); process.exitCode = 1; }); diff --git a/scripts/smoke-test-npm-package.cjs b/scripts/smoke-test-npm-package.cjs index 2c7f13f1c..ef9ea1b5d 100644 --- a/scripts/smoke-test-npm-package.cjs +++ b/scripts/smoke-test-npm-package.cjs @@ -35,11 +35,18 @@ try { const packageRoot = path.join(extractedDirectory, 'package'); const packageJson = JSON.parse(fs.readFileSync(path.join(packageRoot, 'package.json'), 'utf8')); const cliPath = path.join(packageRoot, 'build', 'cli', 'index.js'); + const webIndexPath = path.join(packageRoot, 'build', 'web', 'index.html'); const bugbotApiPath = path.join(packageRoot, 'build', 'api', 'index.js'); const version = execFileSync(process.execPath, [cliPath, '--version'], { encoding: 'utf8' }).trim(); const help = execFileSync(process.execPath, [cliPath, '--help'], { encoding: 'utf8' }); const bugbotApi = require(bugbotApiPath); const cliBundle = fs.readFileSync(cliPath, 'utf8'); + for (const runtime of ['github_action', 'api']) { + const bundle = fs.readFileSync(path.join(packageRoot, 'build', runtime, 'index.js'), 'utf8'); + if (bundle.includes('Local setup web assets are incomplete') || bundle.includes('Control moved to another tab.')) { + throw new Error(`Packaged ${runtime} runtime must not include the local web setup server.`); + } + } if (packageJson.name !== '@vypdev/copilot') { throw new Error(`packaged name is ${packageJson.name}, expected @vypdev/copilot.`); @@ -47,6 +54,9 @@ try { if (!fs.existsSync(path.join(packageRoot, 'build', 'api', 'src', 'api.d.ts'))) { throw new Error('packaged Bugbot API is missing its TypeScript declarations.'); } + if (!fs.existsSync(webIndexPath) || !fs.readFileSync(webIndexPath, 'utf8').includes('/assets/')) { + throw new Error('Packaged local web setup assets are missing or incomplete.'); + } if (version !== packageJson.version) { throw new Error(`CLI reported ${version}, expected ${packageJson.version}.`); } @@ -54,7 +64,7 @@ try { throw new Error('packaged CLI help does not expose the copilot executable.'); } const setupHelp = execFileSync(process.execPath, [cliPath, 'setup', '--help'], { encoding: 'utf8' }); - for (const option of ['--issue-workflows ', '--agent-guidance ', '--non-interactive']) { + for (const option of ['--issue-workflows ', '--agent-guidance ', '--non-interactive', '--web']) { if (!setupHelp.includes(option)) throw new Error(`packaged setup CLI is missing ${option}.`); } for (const publicExport of ['BugbotReviewService', 'evaluateBugbotFindings', 'buildBugbotAnalytics']) { diff --git a/scripts/validate-build.cjs b/scripts/validate-build.cjs index cf722515a..8df3b8f62 100644 --- a/scripts/validate-build.cjs +++ b/scripts/validate-build.cjs @@ -1,6 +1,6 @@ const { spawnSync } = require('node:child_process'); -const buildPaths = ['build/cli', 'build/github_action', 'build/api']; +const buildPaths = ['build/cli', 'build/github_action', 'build/api', 'build/web']; function runGit(args) { const result = spawnSync('git', args, { encoding: 'utf8' }); diff --git a/scripts/validate-npm-package.cjs b/scripts/validate-npm-package.cjs index 545449114..20c557fc3 100644 --- a/scripts/validate-npm-package.cjs +++ b/scripts/validate-npm-package.cjs @@ -34,6 +34,7 @@ if (packageJson.exports?.['./bugbot']?.default !== './build/api/index.js' const requiredPackageFiles = [ 'action.yml', 'build/cli/index.js', + 'build/web/', 'build/github_action/index.js', 'build/api/index.js', 'build/api/src/', @@ -52,6 +53,7 @@ for (const requiredFile of requiredPackageFiles) { const requiredRepositoryFiles = [ 'action.yml', 'build/cli/index.js', + 'build/web/index.html', 'build/github_action/index.js', 'build/api/index.js', 'build/api/src/api.d.ts', @@ -106,6 +108,17 @@ try { } } + const webIndex = fs.readFileSync(path.join(repositoryRoot, 'build/web/index.html'), 'utf8'); + const referencedAssets = [...webIndex.matchAll(/(?:\.\/)?(assets\/[A-Za-z0-9._-]+\.(?:js|css))/g)] + .map(match => `build/web/${match[1]}`); + if (referencedAssets.length < 2) error('local web setup index must reference packaged JS and CSS assets.'); + for (const asset of referencedAssets) { + if (!packageFiles.has(asset)) error(`npm package is missing referenced web asset ${asset}.`); + } + if ([...packageFiles].some(file => file.startsWith('build/web/') && file.endsWith('.map'))) { + error('npm package must not include web source maps.'); + } + const forbiddenFile = [...packageFiles].find((file) => { const normalized = file.toLowerCase(); return normalized === '.env' diff --git a/specs/CATALOG.md b/specs/CATALOG.md index 0f2d764d2..c68f35ddc 100644 --- a/specs/CATALOG.md +++ b/specs/CATALOG.md @@ -16,7 +16,8 @@ debt or convert unknown historic intent into a design decision. | `bugbot-review-state-reconciliation` | Implemented | Reconcile review snapshots, findings, threads, comments, and check conclusions | [Bugbot review-state reconciliation](./bugbot-review-state-reconciliation.md) | 57 paths · 2026-09-24 | | `execution-lifecycle` | Implemented | Shared GitHub Action lifecycle from event admission through durable user-facing results | [Execution admission, queueing, routing, and result publication](./execution-admission-queue-and-publication.md) + 3 companion | 84 paths · 2026-09-16 | | `architecture-quality-hardening` | Implemented | Close verified concurrency, error-contract, context-coupling, fan-out, setup/doctor, and provider-policy risks in dependency order | [Architecture quality and scalability hardening](./architecture-quality-and-scalability-hardening.md) + 1 companion | 72 paths · 2026-09-16 | -| `setup-and-doctor` | Implemented | Plan, validate, provision, and audit a repository installation without exposing credentials | [Setup, configuration, credentials, and doctor](./setup-configuration-credentials-and-doctor.md) + 2 companion | 89 paths · 2026-09-28 | +| `setup-and-doctor` | Implemented | Plan, validate, provision, and audit a repository installation without exposing credentials | [Setup, configuration, credentials, and doctor](./setup-configuration-credentials-and-doctor.md) + 2 companion | 90 paths · 2026-09-28 | +| `local-web-setup-assistant` | Proposed | Offer a packaged, loopback-only Svelte setup interface over the existing setup engine with role-separated PAT guidance and revision-bound application | [Local web setup assistant](./local-web-setup-assistant.md) | 71 paths · 2026-09-28 | | `guided-bot-pat-onboarding` | Proposed | Guide creation of the persistent workflow PAT using GitHub's official form, verify bot identity and grants, and install the approved Actions Secret | [Guided bot PAT onboarding](./guided-bot-pat-onboarding.md) | 25 paths · 2026-09-25 | | `temporary-setup-operator-authorization` | Proposed | Guide creation of the one-run operator PAT through GitHub's official form, verify final setup access, and report user-owned deletion accurately | [Assisted setup PAT creation](./temporary-setup-operator-authorization.md) | 28 paths · 2026-09-25 | | `issue-start-and-sdd-readiness` | Implemented | Start every admitted issue with one explicit signal and publish a validated SDD before eligible Action-managed branch work | [Uniform issue start and pre-branch SDD readiness](./issue-start-and-branch-readiness.md) + 1 companion | 51 paths · 2026-09-17 | @@ -107,9 +108,20 @@ debt or convert unknown historic intent into a design decision. - Workflows: [`setup/workflows/agent-cli-provisioning.yml`](../setup/workflows/agent-cli-provisioning.yml) · [`setup/workflows/copilot_credential_health.yml`](../setup/workflows/copilot_credential_health.yml) - Entrypoints: [`src/cli/commands/setup.ts`](../src/cli/commands/setup.ts) · [`src/cli/commands/doctor.ts`](../src/cli/commands/doctor.ts) - Core code: [`src/domain/setup.ts`](../src/domain/setup.ts) · [`src/domain/setup_questionnaire.ts`](../src/domain/setup_questionnaire.ts) · [`src/domain/setup_token_permissions.ts`](../src/domain/setup_token_permissions.ts) · [`src/application/ports/setup_terminal_ports.ts`](../src/application/ports/setup_terminal_ports.ts) · [`src/application/ports/setup_wizard_ports.ts`](../src/application/ports/setup_wizard_ports.ts) · [`src/application/ports/setup_token_permission_ports.ts`](../src/application/ports/setup_token_permission_ports.ts) · [`src/application/policies/setup_token_permission_evidence_policy.ts`](../src/application/policies/setup_token_permission_evidence_policy.ts) · [`src/application/policies/setup_token_permission_policy.ts`](../src/application/policies/setup_token_permission_policy.ts) · [`src/application/policies/setup_questionnaire_policy.ts`](../src/application/policies/setup_questionnaire_policy.ts) · [`src/application/policies/setup_configuration_plan.ts`](../src/application/policies/setup_configuration_plan.ts) · [`src/application/policies/setup_configuration_storage_policy.ts`](../src/application/policies/setup_configuration_storage_policy.ts) · [`src/application/policies/setup_doctor_message_catalog.ts`](../src/application/policies/setup_doctor_message_catalog.ts) · [`src/application/policies/setup_doctor_report_policy.ts`](../src/application/policies/setup_doctor_report_policy.ts) · [`src/application/policies/setup_journey_policy.ts`](../src/application/policies/setup_journey_policy.ts) · [`src/application/policies/setup_permission_summary_policy.ts`](../src/application/policies/setup_permission_summary_policy.ts) · [`src/application/usecases/setup/setup_wizard_use_case.ts`](../src/application/usecases/setup/setup_wizard_use_case.ts) · [`src/application/usecases/setup/setup_journey_use_case.ts`](../src/application/usecases/setup/setup_journey_use_case.ts) · [`src/application/usecases/setup/setup_questionnaire_controller.ts`](../src/application/usecases/setup/setup_questionnaire_controller.ts) · [`src/application/usecases/setup/setup_credentials_use_case.ts`](../src/application/usecases/setup/setup_credentials_use_case.ts) · [`src/application/usecases/setup/setup_token_permissions_use_case.ts`](../src/application/usecases/setup/setup_token_permissions_use_case.ts) · [`src/application/usecases/setup/doctor_use_case.ts`](../src/application/usecases/setup/doctor_use_case.ts) · [`src/application/usecases/setup/merge_queue_readiness_use_case.ts`](../src/application/usecases/setup/merge_queue_readiness_use_case.ts) · [`src/application/usecases/actions/initial_setup_workflow.ts`](../src/application/usecases/actions/initial_setup_workflow.ts) · [`src/application/usecases/actions/setup_resource_provisioning.ts`](../src/application/usecases/actions/setup_resource_provisioning.ts) · [`src/application/ports/message_catalog_ports.ts`](../src/application/ports/message_catalog_ports.ts) · [`src/application/usecases/localization/resolve_message_catalog_use_case.ts`](../src/application/usecases/localization/resolve_message_catalog_use_case.ts) · [`src/application/policies/setup_configuration_validation.ts`](../src/application/policies/setup_configuration_validation.ts) · [`src/infrastructure/setup_workspace_adapter.ts`](../src/infrastructure/setup_workspace_adapter.ts) · [`src/data/repository/repository_variables_repository.ts`](../src/data/repository/repository_variables_repository.ts) · [`src/infrastructure/github/ports/github_repository_variables_protocol.ts`](../src/infrastructure/github/ports/github_repository_variables_protocol.ts) · [`src/infrastructure/setup_remote_credential_health_adapter.ts`](../src/infrastructure/setup_remote_credential_health_adapter.ts) · [`src/infrastructure/setup_credential_validation_adapter.ts`](../src/infrastructure/setup_credential_validation_adapter.ts) · [`src/infrastructure/setup_token_permission_query_adapter.ts`](../src/infrastructure/setup_token_permission_query_adapter.ts) · [`src/cli/setup_terminal_driver.ts`](../src/cli/setup_terminal_driver.ts) · [`src/cli/setup_question_renderer.ts`](../src/cli/setup_question_renderer.ts) · [`src/cli/setup_journey_presenter.ts`](../src/cli/setup_journey_presenter.ts) · [`src/cli/setup_plan_presenter.ts`](../src/cli/setup_plan_presenter.ts) · [`src/cli/setup_doctor_presenter.ts`](../src/cli/setup_doctor_presenter.ts) · [`src/cli/setup_prompt_rendering.ts`](../src/cli/setup_prompt_rendering.ts) · [`src/cli/setup_credential_prompt_adapter.ts`](../src/cli/setup_credential_prompt_adapter.ts) · [`src/cli/setup_token_permission_presenter.ts`](../src/cli/setup_token_permission_presenter.ts) · [`src/infrastructure/composition/setup_credentials_composition_root.ts`](../src/infrastructure/composition/setup_credentials_composition_root.ts) · [`src/infrastructure/composition/setup_token_permissions_composition_root.ts`](../src/infrastructure/composition/setup_token_permissions_composition_root.ts) · [`src/infrastructure/composition/setup_doctor_composition_root.ts`](../src/infrastructure/composition/setup_doctor_composition_root.ts) · [`scripts/coverage-budgets.json`](../scripts/coverage-budgets.json) · [`scripts/documentation_pat_exception_policy.cjs`](../scripts/documentation_pat_exception_policy.cjs) · [`scripts/validate-documentation-contract.cjs`](../scripts/validate-documentation-contract.cjs) -- Tests: [`src/application/policies/__tests__/setup_questionnaire_policy.test.ts`](../src/application/policies/__tests__/setup_questionnaire_policy.test.ts) · [`src/application/policies/__tests__/setup_configuration_policy.test.ts`](../src/application/policies/__tests__/setup_configuration_policy.test.ts) · [`src/application/policies/__tests__/setup_token_permission_policy.test.ts`](../src/application/policies/__tests__/setup_token_permission_policy.test.ts) · [`src/application/policies/__tests__/setup_doctor_message_catalog.test.ts`](../src/application/policies/__tests__/setup_doctor_message_catalog.test.ts) · [`src/application/policies/__tests__/setup_doctor_report_policy.test.ts`](../src/application/policies/__tests__/setup_doctor_report_policy.test.ts) · [`src/application/usecases/setup/__tests__/setup_questionnaire_controller.test.ts`](../src/application/usecases/setup/__tests__/setup_questionnaire_controller.test.ts) · [`src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_token_permissions_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_token_permissions_use_case.test.ts) · [`src/application/usecases/setup/__tests__/doctor_use_case.test.ts`](../src/application/usecases/setup/__tests__/doctor_use_case.test.ts) · [`src/application/usecases/setup/__tests__/merge_queue_readiness_use_case.test.ts`](../src/application/usecases/setup/__tests__/merge_queue_readiness_use_case.test.ts) · [`src/application/usecases/actions/__tests__/initial_setup_use_case.test.ts`](../src/application/usecases/actions/__tests__/initial_setup_use_case.test.ts) · [`src/application/usecases/actions/__tests__/setup_resource_provisioning.test.ts`](../src/application/usecases/actions/__tests__/setup_resource_provisioning.test.ts) · [`src/infrastructure/__tests__/setup_workspace_adapter.test.ts`](../src/infrastructure/__tests__/setup_workspace_adapter.test.ts) · [`src/infrastructure/__tests__/setup_remote_credential_health_adapter.test.ts`](../src/infrastructure/__tests__/setup_remote_credential_health_adapter.test.ts) · [`src/infrastructure/__tests__/setup_token_permission_query_adapter.test.ts`](../src/infrastructure/__tests__/setup_token_permission_query_adapter.test.ts) · [`src/infrastructure/composition/__tests__/setup_token_permissions_composition_root.test.ts`](../src/infrastructure/composition/__tests__/setup_token_permissions_composition_root.test.ts) · [`src/data/repository/__tests__/repository_variables_repository.test.ts`](../src/data/repository/__tests__/repository_variables_repository.test.ts) · [`src/cli/__tests__/setup_presenters.test.ts`](../src/cli/__tests__/setup_presenters.test.ts) · [`src/cli/__tests__/setup_journey_presenter.test.ts`](../src/cli/__tests__/setup_journey_presenter.test.ts) · [`src/cli/__tests__/setup_prompt_rendering.test.ts`](../src/cli/__tests__/setup_prompt_rendering.test.ts) · [`src/cli/__tests__/setup_token_permission_presenter.test.ts`](../src/cli/__tests__/setup_token_permission_presenter.test.ts) · [`src/__tests__/cli.test.ts`](../src/__tests__/cli.test.ts) · [`src/cli/__tests__/setup_terminal_driver.test.ts`](../src/cli/__tests__/setup_terminal_driver.test.ts) · [`src/architecture/__tests__/setup_doctor_boundaries.test.ts`](../src/architecture/__tests__/setup_doctor_boundaries.test.ts) · [`src/tooling/__tests__/documentation_pat_exception_policy.test.ts`](../src/tooling/__tests__/documentation_pat_exception_policy.test.ts) +- Tests: [`src/application/policies/__tests__/setup_questionnaire_policy.test.ts`](../src/application/policies/__tests__/setup_questionnaire_policy.test.ts) · [`src/application/policies/__tests__/setup_configuration_policy.test.ts`](../src/application/policies/__tests__/setup_configuration_policy.test.ts) · [`src/application/policies/__tests__/setup_token_permission_policy.test.ts`](../src/application/policies/__tests__/setup_token_permission_policy.test.ts) · [`src/application/policies/__tests__/setup_remote_facts_policy.test.ts`](../src/application/policies/__tests__/setup_remote_facts_policy.test.ts) · [`src/application/policies/__tests__/setup_doctor_message_catalog.test.ts`](../src/application/policies/__tests__/setup_doctor_message_catalog.test.ts) · [`src/application/policies/__tests__/setup_doctor_report_policy.test.ts`](../src/application/policies/__tests__/setup_doctor_report_policy.test.ts) · [`src/application/usecases/setup/__tests__/setup_questionnaire_controller.test.ts`](../src/application/usecases/setup/__tests__/setup_questionnaire_controller.test.ts) · [`src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_token_permissions_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_token_permissions_use_case.test.ts) · [`src/application/usecases/setup/__tests__/doctor_use_case.test.ts`](../src/application/usecases/setup/__tests__/doctor_use_case.test.ts) · [`src/application/usecases/setup/__tests__/merge_queue_readiness_use_case.test.ts`](../src/application/usecases/setup/__tests__/merge_queue_readiness_use_case.test.ts) · [`src/application/usecases/actions/__tests__/initial_setup_use_case.test.ts`](../src/application/usecases/actions/__tests__/initial_setup_use_case.test.ts) · [`src/application/usecases/actions/__tests__/setup_resource_provisioning.test.ts`](../src/application/usecases/actions/__tests__/setup_resource_provisioning.test.ts) · [`src/infrastructure/__tests__/setup_workspace_adapter.test.ts`](../src/infrastructure/__tests__/setup_workspace_adapter.test.ts) · [`src/infrastructure/__tests__/setup_remote_credential_health_adapter.test.ts`](../src/infrastructure/__tests__/setup_remote_credential_health_adapter.test.ts) · [`src/infrastructure/__tests__/setup_token_permission_query_adapter.test.ts`](../src/infrastructure/__tests__/setup_token_permission_query_adapter.test.ts) · [`src/infrastructure/composition/__tests__/setup_token_permissions_composition_root.test.ts`](../src/infrastructure/composition/__tests__/setup_token_permissions_composition_root.test.ts) · [`src/data/repository/__tests__/repository_variables_repository.test.ts`](../src/data/repository/__tests__/repository_variables_repository.test.ts) · [`src/cli/__tests__/setup_presenters.test.ts`](../src/cli/__tests__/setup_presenters.test.ts) · [`src/cli/__tests__/setup_journey_presenter.test.ts`](../src/cli/__tests__/setup_journey_presenter.test.ts) · [`src/cli/__tests__/setup_prompt_rendering.test.ts`](../src/cli/__tests__/setup_prompt_rendering.test.ts) · [`src/cli/__tests__/setup_token_permission_presenter.test.ts`](../src/cli/__tests__/setup_token_permission_presenter.test.ts) · [`src/__tests__/cli.test.ts`](../src/__tests__/cli.test.ts) · [`src/cli/__tests__/setup_terminal_driver.test.ts`](../src/cli/__tests__/setup_terminal_driver.test.ts) · [`src/architecture/__tests__/setup_doctor_boundaries.test.ts`](../src/architecture/__tests__/setup_doctor_boundaries.test.ts) · [`src/tooling/__tests__/documentation_pat_exception_policy.test.ts`](../src/tooling/__tests__/documentation_pat_exception_policy.test.ts) - User documentation: [`README.md`](../README.md) · [`docs/how-to-use.mdx`](../docs/how-to-use.mdx) · [`docs/configuration.mdx`](../docs/configuration.mdx) · [`docs/configuration-checklist.mdx`](../docs/configuration-checklist.mdx) · [`docs/authentication.mdx`](../docs/authentication.mdx) · [`docs/development/architecture.mdx`](../docs/development/architecture.mdx) · [`docs/security-operations/operations/provisioning.mdx`](../docs/security-operations/operations/provisioning.mdx) · [`docs/security-operations/operations/troubleshooting.mdx`](../docs/security-operations/operations/troubleshooting.mdx) · [`docs/security-operations/security/credentials.mdx`](../docs/security-operations/security/credentials.mdx) · [`docs/single-actions/workflow-and-cli.mdx`](../docs/single-actions/workflow-and-cli.mdx) · [`docs/security-operations/operations/verification.mdx`](../docs/security-operations/operations/verification.mdx) +### `local-web-setup-assistant` — Local web setup assistant + +- Owner: Copilot maintainers +- Last verified: 2026-09-28 +- Specifications: [`specs/local-web-setup-assistant.md`](./local-web-setup-assistant.md) +- Workflows: Not applicable for this capability. +- Entrypoints: [`src/cli/commands/setup.ts`](../src/cli/commands/setup.ts) · [`package.json`](../package.json) · [`web/src/main.ts`](../web/src/main.ts) +- Core code: [`web/src/App.svelte`](../web/src/App.svelte) · [`web/src/session/setupSession.ts`](../web/src/session/setupSession.ts) · [`web/src/components/ActionButton.svelte`](../web/src/components/ActionButton.svelte) · [`web/src/components/ContextPanel.svelte`](../web/src/components/ContextPanel.svelte) · [`web/src/components/PromptCard.svelte`](../web/src/components/PromptCard.svelte) · [`web/src/components/QuestionPrompt.svelte`](../web/src/components/QuestionPrompt.svelte) · [`web/src/components/StatusBanner.svelte`](../web/src/components/StatusBanner.svelte) · [`web/src/lib/questionAnswer.ts`](../web/src/lib/questionAnswer.ts) · [`web/src/styles/tokens.css`](../web/src/styles/tokens.css) · [`web/src/style.css`](../web/src/style.css) · [`src/application/contracts/web_setup_view.ts`](../src/application/contracts/web_setup_view.ts) · [`src/application/errors/setup_interaction_cancelled_error.ts`](../src/application/errors/setup_interaction_cancelled_error.ts) · [`src/application/policies/merge_setup_overrides_policy.ts`](../src/application/policies/merge_setup_overrides_policy.ts) · [`src/application/policies/setup_remote_facts_policy.ts`](../src/application/policies/setup_remote_facts_policy.ts) · [`src/application/usecases/setup/prepare_setup_pat_intent_use_case.ts`](../src/application/usecases/setup/prepare_setup_pat_intent_use_case.ts) · [`src/application/usecases/setup/audit_configured_setup_pat_use_case.ts`](../src/application/usecases/setup/audit_configured_setup_pat_use_case.ts) · [`src/application/usecases/setup/verify_web_setup_apply_use_case.ts`](../src/application/usecases/setup/verify_web_setup_apply_use_case.ts) · [`src/application/usecases/setup/verify_setup_pat_bootstrap_use_case.ts`](../src/application/usecases/setup/verify_setup_pat_bootstrap_use_case.ts) · [`src/cli_context.ts`](../src/cli_context.ts) · [`src/cli/setup_command_options.ts`](../src/cli/setup_command_options.ts) · [`src/cli/web_setup_bridge.ts`](../src/cli/web_setup_bridge.ts) · [`src/cli/web_setup_adapters.ts`](../src/cli/web_setup_adapters.ts) · [`src/cli/web_setup_server.ts`](../src/cli/web_setup_server.ts) · [`src/cli/setup_apply_snapshot.ts`](../src/cli/setup_apply_snapshot.ts) · [`src/cli/setup_session_guard.ts`](../src/cli/setup_session_guard.ts) · [`src/application/usecases/setup/setup_wizard_use_case.ts`](../src/application/usecases/setup/setup_wizard_use_case.ts) · [`src/application/usecases/setup/setup_journey_use_case.ts`](../src/application/usecases/setup/setup_journey_use_case.ts) · [`src/application/usecases/setup/setup_credentials_use_case.ts`](../src/application/usecases/setup/setup_credentials_use_case.ts) · [`src/application/policies/setup_questionnaire_policy.ts`](../src/application/policies/setup_questionnaire_policy.ts) · [`src/application/policies/setup_token_permission_policy.ts`](../src/application/policies/setup_token_permission_policy.ts) · [`src/application/policies/setup_configuration_plan.ts`](../src/application/policies/setup_configuration_plan.ts) · [`src/application/policies/setup_pat_creation_url_policy.ts`](../src/application/policies/setup_pat_creation_url_policy.ts) · [`src/infrastructure/composition/setup_credentials_composition_root.ts`](../src/infrastructure/composition/setup_credentials_composition_root.ts) · [`scripts/validate-npm-package.cjs`](../scripts/validate-npm-package.cjs) · [`scripts/render-web-setup-component.cjs`](../scripts/render-web-setup-component.cjs) +- Tests: [`src/__tests__/cli.test.ts`](../src/__tests__/cli.test.ts) · [`src/__tests__/cli_context_root.test.ts`](../src/__tests__/cli_context_root.test.ts) · [`src/cli/__tests__/web_setup_bridge.test.ts`](../src/cli/__tests__/web_setup_bridge.test.ts) · [`src/cli/__tests__/web_setup_adapters.test.ts`](../src/cli/__tests__/web_setup_adapters.test.ts) · [`src/cli/__tests__/web_setup_palette.test.ts`](../src/cli/__tests__/web_setup_palette.test.ts) · [`src/cli/__tests__/web_setup_ui_helpers.test.ts`](../src/cli/__tests__/web_setup_ui_helpers.test.ts) · [`src/cli/__tests__/web_setup_browser_session.test.ts`](../src/cli/__tests__/web_setup_browser_session.test.ts) · [`src/cli/__tests__/web_setup_server.test.ts`](../src/cli/__tests__/web_setup_server.test.ts) · [`src/cli/__tests__/web_setup_browser_open.test.ts`](../src/cli/__tests__/web_setup_browser_open.test.ts) · [`src/cli/__tests__/setup_apply_snapshot.test.ts`](../src/cli/__tests__/setup_apply_snapshot.test.ts) · [`src/cli/__tests__/setup_session_guard.test.ts`](../src/cli/__tests__/setup_session_guard.test.ts) · [`src/cli/__tests__/setup_command_options.test.ts`](../src/cli/__tests__/setup_command_options.test.ts) · [`src/cli/__tests__/web_setup_components.test.ts`](../src/cli/__tests__/web_setup_components.test.ts) · [`src/architecture/__tests__/web_setup_boundaries.test.ts`](../src/architecture/__tests__/web_setup_boundaries.test.ts) · [`src/application/usecases/setup/__tests__/prepare_setup_pat_intent_use_case.test.ts`](../src/application/usecases/setup/__tests__/prepare_setup_pat_intent_use_case.test.ts) · [`src/application/usecases/setup/__tests__/audit_configured_setup_pat_use_case.test.ts`](../src/application/usecases/setup/__tests__/audit_configured_setup_pat_use_case.test.ts) · [`src/application/usecases/setup/__tests__/verify_web_setup_apply_use_case.test.ts`](../src/application/usecases/setup/__tests__/verify_web_setup_apply_use_case.test.ts) · [`src/application/usecases/setup/__tests__/verify_setup_pat_bootstrap_use_case.test.ts`](../src/application/usecases/setup/__tests__/verify_setup_pat_bootstrap_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts) · [`src/application/policies/__tests__/setup_questionnaire_policy.test.ts`](../src/application/policies/__tests__/setup_questionnaire_policy.test.ts) · [`src/application/policies/__tests__/setup_token_permission_policy.test.ts`](../src/application/policies/__tests__/setup_token_permission_policy.test.ts) · [`src/architecture/__tests__/setup_doctor_boundaries.test.ts`](../src/architecture/__tests__/setup_doctor_boundaries.test.ts) +- User documentation: [`README.md`](../README.md) · [`docs/how-to-use.mdx`](../docs/how-to-use.mdx) · [`docs/authentication.mdx`](../docs/authentication.mdx) · [`docs/configuration.mdx`](../docs/configuration.mdx) · [`docs/configuration-checklist.mdx`](../docs/configuration-checklist.mdx) · [`docs/development/architecture.mdx`](../docs/development/architecture.mdx) · [`docs/dependency-rules.md`](../docs/dependency-rules.md) · [`docs/security-operations/operations/troubleshooting.mdx`](../docs/security-operations/operations/troubleshooting.mdx) · [`docs/security-operations/operations/provisioning.mdx`](../docs/security-operations/operations/provisioning.mdx) + ### `guided-bot-pat-onboarding` — Guided bot PAT onboarding - Owner: Copilot maintainers diff --git a/specs/catalog.json b/specs/catalog.json index 8c318d4a6..07ce45b3c 100644 --- a/specs/catalog.json +++ b/specs/catalog.json @@ -711,6 +711,7 @@ "src/application/policies/__tests__/setup_questionnaire_policy.test.ts", "src/application/policies/__tests__/setup_configuration_policy.test.ts", "src/application/policies/__tests__/setup_token_permission_policy.test.ts", + "src/application/policies/__tests__/setup_remote_facts_policy.test.ts", "src/application/policies/__tests__/setup_doctor_message_catalog.test.ts", "src/application/policies/__tests__/setup_doctor_report_policy.test.ts", "src/application/usecases/setup/__tests__/setup_questionnaire_controller.test.ts", @@ -750,6 +751,97 @@ "docs/security-operations/operations/verification.mdx" ] }, + { + "id": "local-web-setup-assistant", + "title": "Local web setup assistant", + "status": "proposed", + "scope": "Offer a packaged, loopback-only Svelte setup interface over the existing setup engine with role-separated PAT guidance and revision-bound application", + "owner": "Copilot maintainers", + "lastVerified": "2026-09-28", + "specs": [ + "specs/local-web-setup-assistant.md" + ], + "workflows": [], + "entrypoints": [ + "src/cli/commands/setup.ts", + "package.json", + "web/src/main.ts" + ], + "code": [ + "web/src/App.svelte", + "web/src/session/setupSession.ts", + "web/src/components/ActionButton.svelte", + "web/src/components/ContextPanel.svelte", + "web/src/components/PromptCard.svelte", + "web/src/components/QuestionPrompt.svelte", + "web/src/components/StatusBanner.svelte", + "web/src/lib/questionAnswer.ts", + "web/src/styles/tokens.css", + "web/src/style.css", + "src/application/contracts/web_setup_view.ts", + "src/application/errors/setup_interaction_cancelled_error.ts", + "src/application/policies/merge_setup_overrides_policy.ts", + "src/application/policies/setup_remote_facts_policy.ts", + "src/application/usecases/setup/prepare_setup_pat_intent_use_case.ts", + "src/application/usecases/setup/audit_configured_setup_pat_use_case.ts", + "src/application/usecases/setup/verify_web_setup_apply_use_case.ts", + "src/application/usecases/setup/verify_setup_pat_bootstrap_use_case.ts", + "src/cli_context.ts", + "src/cli/setup_command_options.ts", + "src/cli/web_setup_bridge.ts", + "src/cli/web_setup_adapters.ts", + "src/cli/web_setup_server.ts", + "src/cli/setup_apply_snapshot.ts", + "src/cli/setup_session_guard.ts", + "src/application/usecases/setup/setup_wizard_use_case.ts", + "src/application/usecases/setup/setup_journey_use_case.ts", + "src/application/usecases/setup/setup_credentials_use_case.ts", + "src/application/policies/setup_questionnaire_policy.ts", + "src/application/policies/setup_token_permission_policy.ts", + "src/application/policies/setup_configuration_plan.ts", + "src/application/policies/setup_pat_creation_url_policy.ts", + "src/infrastructure/composition/setup_credentials_composition_root.ts", + "scripts/validate-npm-package.cjs", + "scripts/render-web-setup-component.cjs" + ], + "tests": [ + "src/__tests__/cli.test.ts", + "src/__tests__/cli_context_root.test.ts", + "src/cli/__tests__/web_setup_bridge.test.ts", + "src/cli/__tests__/web_setup_adapters.test.ts", + "src/cli/__tests__/web_setup_palette.test.ts", + "src/cli/__tests__/web_setup_ui_helpers.test.ts", + "src/cli/__tests__/web_setup_browser_session.test.ts", + "src/cli/__tests__/web_setup_server.test.ts", + "src/cli/__tests__/web_setup_browser_open.test.ts", + "src/cli/__tests__/setup_apply_snapshot.test.ts", + "src/cli/__tests__/setup_session_guard.test.ts", + "src/cli/__tests__/setup_command_options.test.ts", + "src/cli/__tests__/web_setup_components.test.ts", + "src/architecture/__tests__/web_setup_boundaries.test.ts", + "src/application/usecases/setup/__tests__/prepare_setup_pat_intent_use_case.test.ts", + "src/application/usecases/setup/__tests__/audit_configured_setup_pat_use_case.test.ts", + "src/application/usecases/setup/__tests__/verify_web_setup_apply_use_case.test.ts", + "src/application/usecases/setup/__tests__/verify_setup_pat_bootstrap_use_case.test.ts", + "src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts", + "src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts", + "src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts", + "src/application/policies/__tests__/setup_questionnaire_policy.test.ts", + "src/application/policies/__tests__/setup_token_permission_policy.test.ts", + "src/architecture/__tests__/setup_doctor_boundaries.test.ts" + ], + "documentation": [ + "README.md", + "docs/how-to-use.mdx", + "docs/authentication.mdx", + "docs/configuration.mdx", + "docs/configuration-checklist.mdx", + "docs/development/architecture.mdx", + "docs/dependency-rules.md", + "docs/security-operations/operations/troubleshooting.mdx", + "docs/security-operations/operations/provisioning.mdx" + ] + }, { "id": "guided-bot-pat-onboarding", "title": "Guided bot PAT onboarding", diff --git a/specs/guided-bot-pat-onboarding.md b/specs/guided-bot-pat-onboarding.md index e065d450a..ba50c6f45 100644 --- a/specs/guided-bot-pat-onboarding.md +++ b/specs/guided-bot-pat-onboarding.md @@ -581,6 +581,10 @@ PAT, and the bot PAT remains active after setup. - Related specs: [temporary setup operator authorization](./temporary-setup-operator-authorization.md), [setup baseline](./setup-configuration-credentials-and-doctor.md), and [PAT permission guidance](./setup-pat-permission-guidance-and-verification.md). +- Future presentation: [local web setup assistant](./local-web-setup-assistant.md) + must preserve final-plan bot grants, numeric-ID verification, and the + installed Secret's persistent lifecycle. This SDD does not claim a web + implementation today. - Primary sources: [PAT form and URL templates](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens), [browser account switcher](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/switching-between-accounts), [organization PAT endpoints](https://docs.github.com/en/rest/orgs/personal-access-tokens). diff --git a/specs/local-web-setup-assistant.md b/specs/local-web-setup-assistant.md new file mode 100644 index 000000000..098caa5bd --- /dev/null +++ b/specs/local-web-setup-assistant.md @@ -0,0 +1,936 @@ +# Local Web Setup Assistant + +- Status: Implementation in progress — target contract, not yet release acceptance +- Date: 2026-09-28 +- Catalog capability ID: `local-web-setup-assistant` +- Last verified: 2026-09-28 (source/build tests; no live GitHub setup or dogfooding) +- Owners: Copilot maintainers; product, security, and accessibility reviewers +- Scope: optional, local Svelte-based presentation of the existing repository setup journey, sharing its policy, credential, and application engine with the terminal +- Related issues/PRs: [PR #402](https://github.com/vypdev/copilot/pull/402) carries this implementation alongside the earlier guided PAT work; no test issue or Action is created +- Required review gates: product UX, Clean Architecture, browser/loopback security, credential handling, packaging, cross-platform operation, accessibility, testing, documentation +- Open decisions blocking readiness: none at the product-contract level; implementation MUST still pass the security and packaged-install review gates below + +## 1. Executive summary + +The default `copilot setup` remains the terminal wizard. `copilot setup --web` +starts a short-lived web assistant on this machine and opens the default +browser. It presents repository identity, setup choices, the temporary setup +PAT, the reviewed plan, the bot PAT and other credentials, and final application +as one clearly advancing journey. A failed browser launch prints a local URL +and a terminal fallback. It never creates a hosted account, proxies setup +through an external service, or treats the browser as a GitHub login session. + +Svelte + TypeScript + Vite is the presentation/build choice; the packaged +Node CLI serves compiled local assets and owns the setup session. Both UIs +MUST call the **same application decisions and provider ports**. The browser +MUST NOT run GitHub mutations, read repository files directly, or reproduce +the permission/questionnaire policy. Before adding the web adapter, the +orchestration currently embedded in `src/cli/commands/setup.ts` MUST be +extracted into frontend-neutral application contracts. Existing terminal +behavior remains the compatibility baseline. + +```text +copilot setup --web + -> local browser: Repository -> Setup choices -> Setup PAT + -> reviewed plan -> Bot PAT & credentials -> Apply -> Result/cleanup + -> GitHub form in a separate tab for each PAT, when guided creation is chosen +``` + +Text equivalent: one local setup session has two optional presentation +adapters. GitHub still authenticates the operator/bot accounts, performs 2FA, +issues PATs, and lets their owners delete or rotate them. Copilot validates +each supplied token and applies only a newly approved plan. + +## 2. Problem, current behavior, and evidence + +### 2.1 Problem + +The six-stage terminal journey is functional but has many conditional choices, +two account roles, provisional permissions, a plan, and partial outcomes. A +first-time operator can benefit from persistent visual context, progressive +explanations, and a review screen without sacrificing the CLI or creating a +second implementation of setup rules. + +### 2.2 Verified baseline before this implementation + +1. `src/cli/commands/setup.ts` owns command flags, repository resolution, + setup-PAT intent, the initial and final permission audits, wizard + composition, bot-credential collection, and the `runLocalAction` call. + At the start of this work, the web entrypoint did not exist; the current + implementation status and remaining release gates are recorded in §18. +2. `SetupJourneyUseCase`, `SetupQuestionnaireController`, + `SetupWizardUseCase`, `SetupCredentialsUseCase`, the permission policies, + and existing GitHub/workspace adapters already separate parts of the + behavior. They are not yet one frontend-neutral setup-session coordinator. +3. The terminal supports guided and manual PAT entry. GitHub's official + fine-grained PAT form URL pre-fills documented fields but cannot select an + individual repository or authenticate a browser account. The setup PAT is + one-run authority; the bot PAT is a distinct runtime Secret `PAT`. +4. At baseline, `package.json` published `build/cli/index.js` and selected + other bundles but no web asset directory. The new Vite/npm-pack contract + is described in §8.3 and its current evidence in §18. +5. The existing setup contract includes manual/unattended/dry-run paths, + final grant re-audit, storage-shadow checks, backups, partial mutation + reporting, and no claim of remote PAT deletion. + +### 2.3 Evidence and limits + +- Repository sources: [setup baseline](./setup-configuration-credentials-and-doctor.md), + [operator PAT](./temporary-setup-operator-authorization.md), + [bot PAT](./guided-bot-pat-onboarding.md), + [permission evidence](./setup-pat-permission-guidance-and-verification.md), + `package.json`, `src/cli/commands/setup.ts`, and setup use cases/tests. +- [GitHub's PAT form](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#pre-filling-fine-grained-personal-access-token-details-using-url-parameters) + supports documented prefill fields, not issuance, account selection, or + individual-repository selection; [GitHub's account switcher](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/switching-between-accounts) + remains browser-owned. +- [Svelte](https://svelte.dev/docs/svelte/overview) compiles UI components; + [Vite](https://vite.dev/guide/) supports a `svelte-ts` application and + produces static assets. SvelteKit/SSR is unnecessary for this local flow. +- [OWASP CSRF guidance](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html), + [CSP guidance](https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html), + and [browser-storage guidance](https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html) + inform the local-server threat model. These are security controls, not a + claim that loopback or a frontend framework makes secret input risk-free. +- Unknown until implementation: the exact packaged asset manifest, browser + opening support on each OS, and human usability of the final wireframes. + These are measured in the acceptance gates, not assumed from dev mode. + +### 2.4 Retrospective classification + +Not applicable: this is a prospective mode, not an as-built baseline. The +verified CLI behavior above remains the baseline and is not relabeled as +already web-capable. + +## 3. Actors, surfaces, and terminology + +| Actor | Goal | Entry/surface | +|---|---|---| +| Setup operator | Configure one local checkout and understand what changes | shell launch, local browser wizard, final terminal result | +| Operator PAT owner | Authorize setup only | GitHub form/account switcher/2FA, then local masked web input | +| Bot PAT owner | Issue an Action runtime credential | separate GitHub form/account switcher/2FA, then local masked web input | +| Organization admin | Approve access when required by policy | GitHub's own approval UI; not Copilot's local page | +| Maintainer | Diagnose partial setup and renewal | result, `copilot doctor`, GitHub resources, docs | + +**Web session** is one ephemeral CLI-owned run bound to one canonical local +repository. **Plan revision** identifies the exact validated configuration, +repository/workspace facts, remote evidence, grant sets, and file decisions +reviewed by the operator. **Browser controller** is the one tab allowed to +submit actions at a time. **Secret value** is never part of a page view model. +**Installed** means a GitHub Secret write succeeded, not that an Action ran +or that the stored value can be read back. **Discarded locally** does not mean +deleted at GitHub or cryptographically erased from browser/process memory. + +## 4. Goals, non-goals, and fixed invariants + +### 4.1 Goals + +1. `--web` MUST be an optional interactive presentation of the same setup + engine, with feature, grant, plan, and mutation parity with terminal setup. +2. The web journey MUST explain current/completed/next states, show reasons + for conditional choices, retain answers while reviewing, and make every + change to required PAT grants visible before the relevant PAT is used. +3. The web journey MUST guide both PAT roles separately, verify actual + identity/access/grants under the existing policies, and show truthful + cleanup/renewal after success, failure, or cancellation. +4. Application MUST require an explicit, current, single-use final approval; + stale pages, duplicate clicks, and changed repository/remote facts MUST + NOT apply a plan that was not just reviewed. +5. A packaged global npm install MUST launch the same bundled UI without + Vite, source files, dev dependencies, network asset fetches, or a writable + checkout of Copilot. + +### 4.2 Non-goals + +1. Hosted SaaS, remote access, LAN sharing, Docker-facing binding, mobile + access to another computer, or simultaneous multi-user setup. +2. Browser automation of GitHub, scraping private requests/cookies, + capturing 2FA, automatic PAT minting/revocation, or local account profiles. +3. Replacing the bot PAT with a GitHub App, changing Action runtime + authentication, or changing current CLI defaults/flags outside `--web`. +4. Persistent drafts or crash-resume containing credentials. A future + resumable setup needs a separate storage/security design. +5. New GitHub issues, PRs, Actions, or dogfooding as part of this spec task. + +### 4.3 Non-configurable safety invariants + +1. Only `127.0.0.1` on an OS-assigned ephemeral port is bound; no public + `--host`/`--port`, reverse proxy, external asset, CDN, telemetry endpoint, + or permissive CORS is added. The exact origin is checked on requests. +2. Browser-originated events are untrusted. No GET or asset request mutates + repository/GitHub state. API calls are schema-validated, revision-bound, + method-limited, size-limited, and CSRF-protected. +3. PATs, provider keys, session capabilities, cookies, and 2FA codes never + enter URLs, browser history/storage, service workers, config, logs, error + payloads, plans, snapshots, or client-side analytics. No value is returned + to the browser after submission. +4. The setup PAT and bot PAT never exchange roles. The operator token is not + Secret `PAT`; the bot token is not authority for setup writes. GitHub owns + each token's issuance, approval, expiration, and revocation. +5. The existing permission, identity, repository, storage-shadow, + confirmation, backup, and partial-result gates apply equally to both UIs. + A visual indicator or typed assertion is never authorization evidence. +6. Applying starts at most once per approved plan revision. No automatic + destructive rollback of completed local or remote changes is claimed. + +## 5. Current versus proposed journey + +| Stage | Terminal today | Web proposal | Operator effect | +|---|---|---|---| +| Launch/repository | `copilot setup` resolves git remote | `copilot setup --web` shows canonical checkout, owner/repo, branch and scope; asks confirmation if ambiguous | see target before entering a PAT | +| Setup choices | linear conditional questions; explicit review pass possible | grouped cards and explanations, saved answers, source-locked values, live grant implications | understand what a choice enables | +| Setup PAT | terminal permission summary + GitHub form link or manual input | role-labelled summary, remote unknowns, official link, account/repo checklist, masked local paste and audit | no inferred browser identity | +| Plan | terminal file/resource preview and confirmation | inspectable grouped diff/Secret names/scopes/warnings, single approved revision | know local and remote effects | +| Bot PAT & credentials | guided/manual bot link and secret prompts | separate bot identity/grants/renewal step, masked values, no cross-role reuse | know persistent credential owner | +| Apply/result | CLI executes and reports | explicit Apply, live semantic progress, partial facts, recovery and cleanup | no false reset or success claim | + +```mermaid +flowchart LR + CLI[copilot setup --web] --> S[Ephemeral local setup session] + S --> R[Repository and choices] + R --> O[Setup PAT at GitHub + local audit] + O --> P[Final plan and grant audit] + P --> B[Bot PAT at GitHub + local audit] + B --> A[Explicit Apply] + A --> Z[Result, doctor, PAT cleanup/renewal] +``` + +Text equivalent: the CLI owns one session. The browser only collects +operator decisions; GitHub handles each PAT in its own tab. Copilot checks +the supplied PAT and current plan before performing setup, then reports +exactly what completed and what remains. + +## 6. Functional behavior and session state + +### 6.1 Launch and normal path + +1. Validate `--web` combinations and canonicalize the checkout before + starting HTTP. Resolve owner/repository from the existing git policy; + ambiguous/missing remotes block rather than guessing. Acquire a per-checkout + local setup-session guard shared by terminal and web modes so a second + setup process cannot apply to the same checkout concurrently. A lock has + no credentials and is released on normal exit; an orphaned lock requires + verified dead-owner recovery. Web + mode does not require a TTY: the browser is the interactive surface, and + a printed local URL is available if automatic opening is unavailable. +2. Bind `127.0.0.1:0`, record the assigned port, create an unpredictable + one-run capability and first controller lease in process memory, and open + the default browser to `http://127.0.0.1:/`. If opening fails, print + that local URL and instructions; serving continues. If binding or packaged + assets fail, stop without a partial UI and suggest `copilot setup`. +3. Show the six stages already used by terminal setup. Import one immutable + snapshot of defaults, config file, and non-secret flags. Mark supplied + fields with their source; fields fixed by existing flags/config are + read-only in the web questionnaire. A changed config file after launch is + not silently reread; tell the user to restart to adopt it. The browser + sends only bounded answer values; the application owns conditionals. +4. Before the setup PAT, collect permission-affecting local intent and show + the exact required grants plus **May need after GitHub inspection**. A + review/back action may reopen a second pass over saved answers; the UI + labels it as the same run. Opening the guided link is a user gesture to + GitHub; `target_name` is only resource owner. The page explicitly asks the + user to check their GitHub account, switch **All repositories** to + **Only select repositories**, select this repository, and Generate. The + manual path remains available. +5. Paste the setup PAT into a masked field on the local page. Send it once + to the CLI process; clear the input after receipt and never echo it in a + response. The server validates identity, target access, and grants, + displays the actual GitHub account for confirmation, then completes + authenticated inspection, remaining questions, plan, and final grant + audit. Unknown organization approval is not labeled `pending` without + evidence. A new required grant invalidates the prior link and blocks + dependent mutation until corrected/replaced authority passes re-audit. +6. Show the final plan with file actions, backups, GitHub resource names and + scopes, workflow updates, warnings, and credential **status only**. The + operator reviews a plan revision. Then present the *distinct* bot PAT + grants and resolved expected bot user ID. GitHub's form opens under the + bot account; the submitted bot PAT must pass the current guided numeric-ID + and grant checks before Secret `PAT` may be written. Manual and existing + PAT handling retain the baseline's exact claims, not invented identity + assurances. An existing GitHub Secret value cannot be read back: when the + existing policy requires re-audit, ask for a new/re-entered bot PAT and + show preserve-versus-replace consequences before Apply. Other credentials + use masked local inputs. Unlike the terminal composition, the web + composition MUST NOT dispatch or bootstrap the credential-health workflow + during this pre-Apply step. Existing Secret values remain unreadable; the + bot PAT is re-entered for grant/identity audit. Existing optional provider + credentials may be explicitly kept with `unverifiable` status, never + described as healthy. Post-Apply health is checked with `copilot doctor`. +7. Before Apply, recompute a compact impact summary and verify that the + approved plan revision, repository identity, relevant workspace file + digests, remote facts, permission audits, and bot credential are current. + Revisions or stale evidence return to review and require new approval; + missing grants return to the appropriate PAT step. The final Apply button + starts one execution through the existing setup mutation use case. +8. Show event-driven progress, actual completed facts, and a final state. + On success, distinguish local disposal from user-owned GitHub deletion of + the temporary setup PAT, and installed bot Secret from later Action health. + Offer `copilot doctor`/relevant GitHub Settings as inspectable next steps. + Never silently delete the bot PAT, which the Action still needs. + +### 6.2 Alternative and boundary paths + +- `copilot setup` without `--web` is unchanged. `--dry-run --web` MAY display + a local-only plan with explicit **No changes** outcome, no required PAT + creation, and no Apply action; any remote facts unavailable without a PAT + are labeled unknown. It does not become a credential-health proof. +- A detected `PERSONAL_ACCESS_TOKEN` is **not silently consumed** in web + mode. Offer `Use existing environment setup PAT` with no displayed value, + or choose guided/manual web input; the chosen credential follows the same + audit. A supplied value must never be sent to the browser. Exiting Copilot + does not unset the parent shell's environment variable; cleanup copy must + distinguish this source from a one-run pasted PAT and tell the operator + how to remove/revoke it when appropriate. +- Existing non-secret setup flags and `--config` are honored with their + current precedence. `--non-interactive`, `--yes`, `--token`, + `--workflow-pat`, `--secret`, and + `--confirm-unverifiable-write-permissions` combined with `--web` fail early + with a concrete CLI fallback; this prevents a hidden approval or command + history secret path from masquerading as visual review. Unverifiable writes + use the existing explicit, narrowly allowed acknowledgement in the UI; + unverifiable required reads remain blocked. +- The bot may be the same account as the setup operator only under existing + policy. Warn about self-event/guarded-approval consequences. The browser's + active GitHub account is never inferred from Git, `gh`, or the setup PAT. +- An unsupported fine-grained grant or GitHub owner policy disables the + guided link for that role and provides the existing full permission table + and manual compatibility path; never auto-select a broader classic PAT. +- Leaving the page or closing its tab does not prove cancellation or + revocation. A second tab starts read-only and may explicitly take over; + takeover rotates the controller lease, invalidates pending responses from + the old tab, and shows the current server-owned phase. No PAT value is + rehydrated into either tab. Browser Back revisits a *view*; it cannot undo + a committed state or bypass current validation. +- A user may cancel before Apply. The server stops and reports that local + setup mutation did not start; any PAT they already generated on GitHub may + still exist and needs owner cleanup. During Apply, cancellation is + cooperative only at supported safe boundaries; the result is partial or + indeterminate until inspected, never simply `No changes`. + +### 6.3 State machine and event contract + +| State | Meaning / evidence | Allowed next action | Block/expiry behavior | +|---|---|---|---| +| `repository` | canonical checkout/remote shown; no mutation | confirm target | invalid remote blocks startup | +| `choices` | one server-owned intent draft and source locks | answer, review, cancel | invalid/stale answer rejected | +| `setup-pat` | grant preview, unknowns, account checklist | guided/manual/environment, paste, audit | wrong account, missing grant, org approval block | +| `plan` | final normalized plan revision and audit | inspect, revise, approve, dry-run end | drift invalidates revision | +| `credentials` | bot ID/grants and other credentials | paste, audit, revise, cancel | wrong bot/Secret scope blocks | +| `ready-to-apply` | current approved plan and credential facts | one explicit Apply | stale revision returns to review | +| `applying` | mutation has begun; completed facts accumulate | wait; bounded safe cancel | duplicate Apply returns same operation | +| `complete` | every required operation reported success | inspect, stop | bot Secret may remain active | +| `partial` | at least one operation may have committed | inspect, run doctor, deliberate retry | no automatic replay/rollback | +| `blocked` | gate failed before mutation | fix named cause, retry/review, stop | retain non-secret draft only | +| `cancelled` | operator ended pre-Apply | close | cleanup GitHub-created PATs manually | +| `expired` | idle/hard session limit, no Apply running | restart CLI | no credential/draft recovery | + +Every event includes the current session revision, the plan revision when +one exists, and one bounded idempotency key for Apply. The server serializes +decisions for a session; +duplicate answers are harmless, an out-of-order answer returns current state, +and only the active controller can submit. One plan may have at most one +in-flight Apply operation. After a process crash, no session is resumed and +no prior Apply result is assumed; the next run uses existing read-before-write +and doctor policies to reconcile facts, and requires fresh token entry and +approval before any new mutation. + +## 7. User-facing configuration + +| Input | Type / default | Allowed scope and validation | Precedence / persistence | +|---|---|---|---| +| `--web` | boolean / off | interactive local setup only | command invocation; no stored preference | +| `--dry-run --web` | boolean / off | no Apply, credentials optional only where existing plan needs evidence | one run; no mutation | +| Existing non-secret flags and `--config` | existing typed values | same validators, skip/fixed semantics as CLI | flags > config > defaults; snapshot at launch | +| Web answers | existing setup question types | existing bounded enums, names, counts, cross-field rules | editable defaults only; one-run memory | +| Environment setup PAT | optional hidden choice / unused | only after explicit operator selection and audit | process memory; never a web response | +| Bot login | explicit GitHub user / none | existing numeric-ID resolution and guided check | one run; non-secret only | +| Local server | fixed `127.0.0.1:0` | no host/port override | OS-assigned port; never persisted | +| Session lifetime | 30-minute human-idle, 4-hour hard cap | Apply in progress is not interrupted by idle timeout; show countdown/warning and fail closed on hard cap before Apply | monotonic process clock; not configurable | + +Example recommended: `copilot setup --web` in the intended checkout, with +guided setup and bot PAT links. Alternative: `copilot setup` keeps the terminal +wizard; `copilot setup --non-interactive --config setup.yml` remains an +automation path and never starts a browser. `--web --yes` is invalid rather +than silently treating a web Apply button as already clicked. No browser +theme, host, timeout, or credential persistence config is introduced. Unknown +flags/fields fail existing parsing. There is no stored web session or schema +to migrate; a future version cannot reread an old session. + +## 8. Clean Architecture design + +### 8.1 Boundaries and dependency direction + +| Boundary | Owns | Must not own/import | +|---|---|---| +| Domain and pure policies | setup choices, grant plans, PAT roles, identity comparison, config/storage rules, plan revision and drift decisions | Svelte, HTTP, terminal, Octokit, process state | +| Application | frontend-neutral `SetupSession` coordination, stage transitions, immutable semantic commands/views, credential/permission/plan/apply use cases | web routes, DOM, Node HTTP, provider DTOs | +| Semantic ports | repository/workspace facts, GitHub reads/writes, secret input handoff, clock, operation progress, presentation | HTTP request/response or browser objects | +| Adapters/data | existing GitHub/workspace adapters; terminal and web request/view adapters | duplicate question lists, grant matrices, authorization decisions | +| Infrastructure/composition | short-lived Node HTTP server, session lifecycle, asset serving, browser opener, provider wiring | product decisions or alternate setup implementation | +| Browser presentation | Svelte components, accessible copy, conditional view rendering | direct GitHub API calls, filesystem, persisted PATs, authoritative plan state | + +```mermaid +flowchart LR + T[Terminal adapter] --> U[Shared SetupSession use case] + W[Svelte view + local HTTP adapter] --> U + U --> Q[Questionnaire, permission, plan, credential policies] + U --> P[Semantic workspace/GitHub/clock/progress ports] + I[Existing provider adapters] --> P + W -. static same-origin assets .-> H[CLI-owned loopback server] +``` + +Text equivalent: both presentations submit semantic decisions to one +application coordinator, which uses existing policies and provider adapters. +The loopback server adapts HTTP and serves compiled assets; it is not a +second business-logic engine. `src/cli/commands/setup.ts` becomes a thin +entrypoint/composition root. No `application` or `domain` module imports +Svelte, browser, HTTP, terminal rendering, or `runLocalAction` directly. + +The final web Apply authorization is one application use case with injected +repository-facts, selected-file snapshot, remote-facts, permission-audit, +approval, and live-session ports. It must fail closed on missing/drifted facts +or a session that was cancelled/expired while asynchronous reads were running. +The CLI supplies Git/HTTP/provider adapters, but must not reimplement this +decision as an inline sequence. The subsequent mutation boundary remains +single-flight and cannot be entered if the approval use case did not return an +approved result. Deterministic fake-port tests cover every drift category, +cancel/expiry interleavings, and audit outcomes. + +The pre-PAT permission-intent review is likewise an application use case: +it owns questionnaire transitions, owner-kind conflict checks, provisional +grant calculation, review passes, and guided-link eligibility. Terminal and +web adapters supply prompts, status presentation, and the journey view; the +command only wires them and handles the resulting guided/manual outcome. This +keeps the grant decision out of a presentation-specific entrypoint and lets +pure fake-port tests cover repeated review, conflicts, fallback, and invalid +local configuration without creating a GitHub PAT. + +The initial setup-PAT identity/access gate is an application use case shared by +both presentations; it requires explicit acknowledgement for unverifiable +write grants and confirmation of the authenticated operator account before +planning. The configured setup-PAT audit is another application use case. It compares +provisional and final required grants, verifies the authenticated identity and +effective access through the read-only permission port, and returns a blocked +result when owner-kind or grants differ. A pure policy builds corrected official +GitHub form links; presenters own their display and explanatory text. The +command does not decide whether an unverifiable write grant is acceptable. + +The browser is decomposed at its own boundary. `web/src/App.svelte` is only +the page shell and view composition. A single `web/src/session/` client owns +bootstrap, polling, revision-bound answer/cancel/takeover/close commands, and +redacted session state; it never retains a submitted PAT in a store or browser +storage. `web/src/components/` contains cohesive presenters for progress, +header/theme, status, prompt kinds, context, and outcome. Components receive +the redacted view and callbacks; they never call `fetch`, import provider or +policy modules, or infer authority from local UI state. Prompt inputs keep +only transient local values, clear secrets before submission, and remount when +the server prompt revision changes. Small pure helpers may normalize defaults +and allowlisted links. Adding a prompt kind belongs in its presenter rather +than growing the page shell; avoid one-file-per-element indirection with no +reuse. Architecture tests guard dependency direction and bound shell and +presenter sizes, with reviewed exceptions only when cohesion justifies them. + +An empty issue-workflow multi-selection MUST submit an explicit `none` answer, +not an empty answer that reuses defaults or silently selects every workflow. +Both terminal and web routes use the same questionnaire parser for this choice. + +### 8.2 Contracts, ownership, and trust + +- The session command API is semantic (`answer(questionId, value, revision)`, + `review(role)`, `submitCredential(role, value)`, `approvePlan(revision)`, + `apply(revision, idempotencyKey)`, `cancel()`), not a general RPC, shell, + arbitrary path, or raw GitHub endpoint. Exact DTO/schema/size limits are + checked by the HTTP adapter before the application sees them. Provider + URLs and error strings are never trusted as web links or HTML. +- One application-owned session contains repository identity, copied draft, + answered IDs, stage, plan revision, non-secret verification facts, and + short-lived credentials only while needed. Secret-bearing structures never + implement serialization or enter presenter views. The browser receives + only a dedicated redacted view model. Server/process memory disposal is + best-effort, not a remote revocation or guaranteed zeroization. +- Existing questionnaire/default/permission policies are the only source of + question visibility, defaults, validation, and grants. A web control may + describe a rule but cannot loosen it. Once choices or remote facts change, + downstream plan, URL, account confirmation, and approval proofs are + invalidated according to their dependencies; the UI explains why it + returned to an earlier stage. +- The repository path is fixed after launch. Recheck canonical path, git + owner/repo, selected branch/ref, relevant file digests, and remote facts + immediately before Apply. Unexpected drift yields a new plan revision and + requires fresh human review; never apply from a stale browser response. + Repository-relative plan labels such as `workflows/name.yml` and + `ISSUE_TEMPLATE/name.yml` MUST be translated to their actual checkout + destinations under `.github/` for this comparison. Include managed assets + that a changed selection may retire, not only files displayed as selected. +- The shared execution boundary owns idempotency and partial facts. The + browser uses bounded polling or server events for **read-only** progress; + reconnecting to the same live process retrieves redacted current state, + not secret values or an implicit retry. + +### 8.3 Executable architecture and packaging constraints + +1. A dependency test MUST reject `src/domain`/`src/application` imports of + Svelte, Vite, DOM, `node:http`, terminal presenters, Octokit concrete + adapters, and CLI modules; Svelte modules MUST import only public + view/contracts and never provider or mutation modules. +2. Contract tests MUST run the same scenario fixtures through terminal and + web adapters and compare normalized choices, grant sets, plan revisions, + identity gates, and result facts. Question/permission tables cannot be + copied into web source; a structural check enforces one policy owner. +3. Build order MUST produce a Vite static directory plus the `ncc` CLI + bundle. `package.json` allowlist and asset manifest MUST include only + required hashed HTML/JS/CSS/fonts. Paths resolve relative to the installed + package, not `process.cwd()`. Assets are read-only, served with exact MIME + types, and cannot escape their directory through encoded paths or symlinks. +4. `pnpm run validate:build`, `validate:npm-package`, and an isolated + `npm pack`/global-install smoke fixture MUST prove asset presence, + checksums/manifest parity, executable launcher, and no runtime use of + Vite or source/dev files. The Action/API bundles MUST NOT ship Svelte or + acquire a new runtime web-server dependency through shared imports. +5. No issue, PR, check, comment, or label is generated by launching the UI. + Existing setup-induced GitHub resources remain governed by the approved + plan and its existing workflow/permission validators. + +## 9. Web UI/UX and content contract + +### 9.1 Information hierarchy and navigation + +The first viewport of every phase answers: **what is happening**, **what is +complete**, **what is next**, **what the user must do**, and **whether any +change has started**. Render a six-stage text-labelled progress rail, not a +percentage or question count. Use a stable repository badge and PAT role +heading. Show one primary action per screen, with a secondary Review/Back +action only where safe. A stage reopened after revision says why, preserves +answers, and shows `review pass 2` or equivalent, never `Start again` unless +a new CLI process really starts. Permission summary is short by default; +the exact table, reasons, and remote unknowns are one expansion away. + +```text +Copilot setup · Local assistant vypdev/copilot · develop +Repository ✓ Choices ✓ Setup PAT → Plan · Bot PAT · Apply · + +Setup PAT — action required +Known grants: Metadata read · Contents read · Secrets write +May need after GitHub inspection: Actions write (existing managed Secret) +No repository changes have started. + +1. Open GitHub's prepared PAT form as your setup account. +2. Change All repositories to Only select repositories → vypdev/copilot. +3. Generate there, then paste the PAT below. Copilot has not created it. +[Open GitHub form] [View all permissions] [Use existing PAT] +``` + +Text equivalent: the operator is at the third phase, sees known and unknown +grants, must complete GitHub's form under the correct account and select the +specific repository, and knows no setup mutation has begun. The link is an +explicit user action to the fixed official GitHub host; it never contains a +credential. The `Bot PAT` screen repeats the checklist under a visibly +different account/role and states that its token remains needed by Actions. + +| Primary state | Representative visible copy | Primary action | +|---|---|---| +| Pending | `Inspecting existing resources for vypdev/copilot. No setup changes have started.` | Wait; `View details` is secondary | +| Action required | `Open GitHub as the bot account @vypbot, select only vypdev/copilot, then paste its PAT here.` | Open official form | +| Blocked before mutation | `Nothing was changed. The setup PAT lacks repository Variables write. Create a corrected PAT, then return to this step; your answers remain.` | Correct setup PAT | +| Partial after mutation | `8 files were installed and Secret PAT was updated; one workflow update failed. Do not delete the bot PAT. Inspect these results before retrying.` | Inspect result/doctor | +| Complete | `Setup finished. The bot PAT is installed as Secret PAT; its future Action health is not yet proven. Delete the temporary setup PAT in GitHub when no longer needed.` | View result/cleanup | +| Cancelled/expired | `No setup mutation started in this session. Any PAT already generated in GitHub may still exist.` | Open GitHub PAT Settings / restart | + +Errors follow `impact -> cause -> action -> retained state`; partial states +list each committed local file/resource and unresolved step without a raw +provider payload. Plan review shows `create/update/preserve/skip`, target +scope, backups, and warnings, not credentials or their values. Before Apply, +show the exact repository, affected file/resource counts, verified account +labels, unresolved warnings, and an unchecked explicit confirmation. The +button says `Apply to vypdev/copilot`; it is disabled until the current plan +revision is accepted. After click, disable retries until the same operation +returns; never imply progress based on elapsed time alone. + +### 9.2 Browser, responsive, accessibility, and localization + +The visual system is a reusable set of tokens and patterns, not page-specific +colors copied into each screen. `web/src/styles/` separates palette tokens, +foundations, layout, form controls, feedback, and responsive rules, imported +once by `style.css`. Shared visual primitives cover banners, buttons and +card/field patterns where presenters actually reuse them; presenters compose +these for question, choice, credential, plan, context, and result states. +State styling uses semantic tokens (`success`, `warning`, `error`, focus) in +light, dark, and system modes. Components retain native labels, keyboard/focus +behavior and explicit status text; decoration never carries meaning alone. +Visual changes require representative state and interaction tests plus both- +palette contrast checks, not screenshot-only assertions. The contributor +architecture guide documents these boundaries for future steps. + +- The visual system MUST ship complete **light and dark** palettes for page, + surfaces, borders, text, muted text, focus, links, warnings, errors, and + success states. Follow `prefers-color-scheme` by default and offer an + accessible `System / Light / Dark` control. A manual choice lasts for this + browser tab only; it stores no credential or session state and a reload + returns to the system preference. Native controls receive the matching + `color-scheme`. There is no light-only loading flash in a dark system theme. + Status meaning never depends on hue alone. Test text contrast at >= 4.5:1 + (>= 3:1 for large text) and essential UI/focus indicators at >= 3:1 in + both palettes; review hover, disabled, validation, code, and external-link + states as well as the happy-path cards. These thresholds follow + [WCAG contrast guidance](https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html) + and the system-default behavior follows + [`prefers-color-scheme`](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/%40media/prefers-color-scheme). +- The page SHOULD use a coherent editorial dashboard layout: restrained + typography using locally packaged/system fonts, generous spacing, a + high-contrast progress rail, a focused question card, and a persistent + context/impact panel on wide screens. At narrow widths the context moves + below the main action without hiding grant deltas or recovery text. Motion + is purposeful, brief, and removed under `prefers-reduced-motion`. +- The page MUST work at narrow width and 200% zoom, keyboard-only, reduced + motion, and light/dark modes. Semantic headings, labels, descriptions, + field errors, focus restoration, and a polite live region carry status; + color/icons are supplemental. The PAT field is masked, has a visible + show/hide control only if security review approves, and never displays a + pasted value in a toast, debug view, or browser history. +- GitHub links have descriptive text, `rel="noopener noreferrer"`, and a + visible external-destination warning. `Referrer-Policy: no-referrer` protects + the local origin when a GitHub link opens. A copyable link may be shown + when browser pop-ups are blocked; it contains no session or PAT value. +- English is the initial setup locale, matching the terminal; Spanish is + added only with a reviewed complete catalog, not ad hoc strings. Unknown + locale falls back atomically to English. Account/repo names and remote + messages are escaped as text, never injected as HTML or Markdown. +- No issue/PR/check/comment is added by the web surface, so notification + budget is zero. Progress updates in the page are coalesced and do not + repeatedly steal focus or announce the same state. GitHub-side account + switching and 2FA are explained, not reproduced in the local UI. + +## 10. Failure, recovery, and cleanup + +| Condition | Impact and retained facts | Automatic retry | Operator action / cleanup | +|---|---|---|---| +| Browser launch denied or unavailable | local server is ready; no mutation | none | open printed loopback URL or use terminal setup | +| Bind/assets/packaged path failure | web mode never starts | none | run terminal setup; report installation problem | +| Unsupported browser/JS disabled | no secret submitted; no mutation | none | terminal setup; no partial web fallback | +| Second setup process/tab | only one checkout operation/controller; previous state intact | none | stop first process or explicitly take over tab | +| Tab closes, laptop sleeps, session idles | live session may remain until 30-minute idle cap; no implied cancellation | no mutation replay | reopen while live; otherwise restart and clean up GitHub PATs | +| Git remote/ref/file/config changes mid-session | reviewed plan is stale; no Apply | re-read once on request | review new plan or restart to adopt changed config | +| Wrong GitHub account or wrong repository | PAT audit fails; no dependent mutation | no automatic PAT creation | switch account/select repository and generate/correct PAT; delete unused one | +| Org approval pending, unknown, or GitHub rate limit/outage | no dependent mutation; only evidenced pending is named pending | bounded read-only retry with backoff | inspect GitHub/admin status; retry when accessible | +| Grant added/removed after remote inspection | old link/plan no longer exact; no dependent mutation | recompute preview only | correct PAT; disclose excess grant without claiming least privilege | +| Bot ID mismatch or Secret storage shadow | no Secret write; plan/credentials retained without value echo | none | correct account or scope; delete unused PAT if needed | +| Secret write accepted then later operation fails | partial; Secret name/scope may be active, value unreadable | no automatic Apply retry | inspect report/doctor before overwriting or revoking bot PAT | +| Request times out while Apply continues | result unknown to browser; server operation may still run | reconnect to same process, read-only state | inspect progress/result before any retry | +| CLI crash/power loss mid-Apply | durable result unknown; no session recovery | no replay | run doctor/read-before-write reconciliation, enter fresh PATs, approve new plan | +| Local cancel before Apply | no setup mutation; GitHub-created PATs may exist | none | delete unused PATs in GitHub; close page | + +If the local server shuts down, it closes listeners and invalidates session +capabilities. It attempts best-effort cancellation of pre-mutation work and +does not promise to interrupt in-flight GitHub writes. No remote PAT is +deleted by closing the page. The cleanup screen links to GitHub PAT Settings +for the temporary setup PAT and to bot-PAT rotation guidance separately. + +## 11. Security, permissions, and privacy + +1. **Boundary:** the web server is loopback-only, for the launching OS user; + a hostile website and another browser tab are in scope. A malicious + process already running as that same OS user, browser extensions with + page access, or a compromised browser are outside the isolation that a + loopback HTTP server can guarantee. The product must say so honestly. +2. **Request defense:** reject `Host` not exactly `127.0.0.1:`, + proxy/forwarded host headers, unexpected `Origin`/`Referer` on mutations, + cross-site Fetch Metadata, unsupported methods/content types, and requests + over size/time limits. No wildcard CORS or credentials cross-origin. + State-changing requests require a one-run, cryptographically random + capability in a custom header plus a revision check; capability is + delivered only by a same-origin no-store bootstrap response, never a URL, + cookie, localStorage, or sessionStorage. Reject missing/invalid capability + and rotate it on tab takeover. These controls defend cross-site requests + and DNS-rebinding-style host confusion; they are not OS-user isolation. + The local server sets no cookies and ignores, never logs, any Cookie header + another application on the same hostname may have caused the browser to + send. +3. **Browser isolation:** serve packaged scripts/styles only, with a restrictive + CSP (`default-src 'none'`, bundled `script-src/style-src`, `connect-src + 'self'`, `frame-ancestors 'none'`, `form-action 'self'` as applicable), + `X-Content-Type-Options: nosniff`, `Referrer-Policy: no-referrer`, and + `Cache-Control: no-store` for HTML/API/secret responses. No inline/eval + scripts, third-party font/image/script, iframe, service worker, WebSocket + from another origin, or dev-server middleware in the published package. + Escape untrusted content; do not use raw HTML rendering for provider data. +4. **Secrets:** credential submission uses POST over loopback to an exact + role-specific endpoint; both DOM input and any client variable are cleared + after acknowledgement, but browser/process memory cannot be proved erased. + The server never returns a submitted credential, logs raw request bodies, + records it in traces or crash diagnostics, or writes it to a temp file. + It retains the setup token only as long as necessary and the bot token + until its approved Secret write; after failure, give cleanup guidance. + `autocomplete="off"` and related hints are defense in depth, not a + guarantee that browsers/extensions cannot capture a pasted secret. +5. **Provider boundary:** the local UI sends PATs only to this CLI's existing + verified GitHub adapters; the browser never directly calls GitHub APIs + with them. Official GitHub form links use the existing allowlisted builder. + Numeric bot-ID and setup-account checks are preserved; unknown access + does not become success. The final Apply approval cannot be bypassed by + `--yes` or a forged/stale browser event. +6. **Abuse/failure:** bound concurrent clients, read/write time, body and + field sizes, retries, and progress buffer. Avoid exposing arbitrary local + files, project paths, source maps, stack traces, or provider responses. + Expired/invalid capability is a 403-like local error with no secret data; + stale revision is a 409-like refresh instruction. Audit the actual mutation + result without a credential-bearing event log. + +## 12. Observability and operational UX + +The page and terminal share a correlation ID that is random and non-secret. +Report stage, repository, normalized result status, account **login/ID where +already verified**, grant-verification outcomes, plan revision, and completed +resource names/scopes; never PAT text, cookies, browser headers, raw payloads, +or the session capability. Debug mode does not relax redaction. Local access +logs are disabled by default. A page refresh reads the process-owned state +once; status polling is bounded and stops after a terminal state. There is no +cloud telemetry, GitHub comment, or notification from merely using web mode. + +## 13. Compatibility, migration, rollout, and rollback + +Terminal and non-interactive paths remain unchanged. Web mode has no durable +schema or migration; a running terminal setup is never converted into a web +session, and a web session cannot switch presentation mid-run after entering +credentials. New npm packages include static UI assets, but Action/API bundles +and workflow assets retain parity. Roll out behind explicit `--web` only, +first with packaged read-only journey/plan fixtures, then credential handling +and Apply after security review. No hosted service or feature flag is needed. +Rollback removes/hides `--web` and its static assets; it cannot undo PATs +users created in GitHub or resources already applied. A downgraded package +still offers the terminal setup and doctor paths. + +## 14. Testing strategy and numeric budget + +The floor is **102 distinct cases**, derived from shared-engine parity, +six-stage transitions, two PAT roles, local HTTP abuse, packaged installs, +drift, and partial mutation. Each test/parameterized behavior counts once; +existing CLI tests are retained, not re-counted as new web evidence. + +| Area | Minimum cases | Risk covered | +|---|---:|---| +| Pure choices/config/grant/plan projection | 14 | source locks, defaults, conditional questions, two PAT roles, grants, revision invalidation | +| Session/use cases/idempotency/races | 20 | stages, saved-review pass, tab takeover, stale events, single-flight Apply, cancel, idle/crash replay boundaries | +| GitHub/workspace/HTTP adapters | 10 | identity, missing/unknown grants, org approval, Secret scope, bounded errors and provider mapping | +| CLI/packaging/workflow contracts | 10 | flag combinations, browser-open fallback, asset manifest, npm pack/global install, unchanged Action/API bundles | +| UI/accessibility/localization/content | 18 | pending/action/blocked/partial/complete, plan diff, narrow/zoom/keyboard/focus/no-color, both palettes/system toggle and contrast, English fallback, escaping | +| Integration/compatibility/recovery | 12 | terminal-web parity, manual/environment/dry-run, drift, partial write, doctor reconciliation | +| Security/abuse | 18 | Host/Origin/CSRF, CORS, replay, path traversal, XSS/CSP, secret leaks, no GET mutation, body/time/concurrency limits | +| **Total** | **102** | No double counting | + +Within the 18 UI cases, cover at least one render/interaction for each prompt +presenter, one revision-change form reset, secret clearing before dispatch, +read-only disabling, all outcome variants, and both theme palettes. Static +architecture tests additionally reject network/storage/provider calls from +presenters and shell growth; these do not replace behavioral UI evidence. + +Repository-wide Jest/coverage, lint, typecheck, build, documentation, +workflow, catalog, and npm-package gates remain. New pure policies target +100% branch coverage; changed application/server/credential modules target +at least 95% statements/lines and 90% branches/functions, with no regression +to higher existing budgets. Architecture tests parse imports and contract +schemas, not prose. Use deterministic fake clock/IDs, temp repositories, +fake GitHub ports, fake browsers/HTTP clients, and adversarial origins; +never use real PATs, issue/Action test resources, external services, or +blocking sleeps in CI. Golden UI fixtures must have semantic assertions. +Human evidence covers macOS/Linux/Windows launch or documented fallback, +packaged global install, two GitHub browser accounts, 2FA occurring only at +GitHub, wrong-account handling, narrow/200%-zoom keyboard and screen-reader +pass, system/light/dark visual review including contrast/focus/error states, +browser close/reopen, and truthful partial result. Controlled evidence +uses test accounts outside this repository; no dogfooding is required. + +## 15. Documentation and discoverability + +| Audience | Artifact | Required content | Check | +|---|---|---|---| +| New user | `README.md`, `docs/how-to-use.mdx` | default CLI and optional `--web` normal path, stages, screenshots with text alternative | route/link + UX fixture | +| Setup owner | `docs/authentication.mdx`, `docs/configuration.mdx`, `docs/configuration-checklist.mdx` | two PAT roles, account/repo selection, grants, flag conflicts, env-token opt-in, expiry/renewal | permission/CLI contract | +| Operator | `docs/security-operations/operations/troubleshooting.mdx`, `docs/security-operations/operations/provisioning.mdx` | browser/bind/session errors, partial Secret write, recovery/doctor, GitHub cleanup | recovery fixture | +| Contributor | `docs/development/architecture.mdx`, `docs/dependency-rules.md`, this SDD | shared coordinator, trust boundaries, asset pipeline, transport schemas and threat model | architecture + package checks | + +Docs are published with implementation, not ahead of it. Register routes and +verify links/assets. Examples must match executable fixtures. The terminal +help for `--web` explains local-only scope and the `--non-interactive` conflict. + +## 16. Acceptance scenarios + +1. Given an installed npm package and an eligible checkout, `copilot setup + --web` opens a bundled local page showing the exact repository and six + stages; no source checkout or Vite server is needed. +2. Given a failed browser opener, the CLI prints the loopback URL and keeps + serving; given a failed bind or missing assets, it stops without a false + partial setup claim and offers terminal fallback. +3. Given the same bounded configuration through CLI and web, normalized + questions, grant sets, plan and result facts match; fixed config/flags + remain visibly locked and are not silently overridden. +4. Given `--web` with non-interactive/`--yes`/secret-bearing flags, launch + fails before HTTP/credential use. Given an environment PAT, web mode asks + explicitly whether to use it without returning its value to the browser. +5. Given a deliberate second review of choices, the page preserves answers, + labels the review pass, recalculates grants, and returns to Setup PAT + without implying that setup restarted. +6. Given guided setup PAT creation, the page shows the exact local grants and + remote unknowns, opens the official GitHub form only on user action, and + instructs account and single-repository selection. Wrong account or new + required grant blocks setup before mutation. +7. Given finalized runtime requirements, the distinct bot step checks the + PAT's actual numeric account ID and grants before Secret `PAT` write; + manual/legacy paths claim only their existing checks. +8. Given a current approved plan and verified credentials, one click applies + it once. Duplicate click returns the same operation; stale revision, + changed checkout file, changed remote identity, or new permission need + returns to review with no new mutation. +9. Given an invalid/stale tab event or second tab, no action occurs until the + new tab explicitly takes control; the first tab then cannot submit. + Given a concurrent terminal or web setup in the same checkout, the + per-checkout guard blocks its Apply before any mutation. +10. Given cancel/idle expiry before Apply, the process closes without setup + mutation and warns that any PAT already generated at GitHub remains the + user's responsibility. A crash during Apply never auto-replays it. +11. Given a Secret write succeeds and a later setup operation fails, the + result lists the Secret's name/scope as possibly active, never prints its + value, and requires inspection before retry or bot PAT deletion. +12. Given hostile Host/Origin/cross-site requests, missing or replayed session + capability, path traversal, oversized body, or injected account/provider + text, the server rejects/escapes it without mutation or secret disclosure. +13. Given a browser refresh, the same live process restores redacted state + only; PAT values, session capability, and plan approval are never stored + in browser storage or URLs. The final page never calls local disposal + GitHub revocation or Secret installation verified Action health. +14. Given narrow width, 200% zoom, keyboard-only and reduced-motion settings, + every primary state and recovery action remains understandable without + color, sound, hover, or developer tools; English fallback is complete. + System/light/dark selection renders every state legibly, with contrast + checks for text and essential controls in both palettes. +15. Given no `--web`, existing terminal, unattended, and dry-run contracts + remain unchanged. Build/package/architecture checks detect missing UI + assets, policy duplication, and Svelte leaking into Action/API bundles. +16. Given a desktop browser but no terminal TTY, web mode still permits + explicit browser decisions. Given an existing unreadable Secret `PAT`, + the UI does not claim to recover its value and follows the current + re-entry/preservation policy. Given an environment-supplied setup PAT, + exit never claims to have removed it from the parent shell. + +## 17. Requirements traceability + +| Requirement | Owner/boundary | Verification | Documentation | +|---|---|---|---| +| Optional packaged local UI (§4.1, §6.1, §8.3) | CLI composition + asset adapter | scenarios 1–2, 15; npm pack fixture | how-to-use, architecture | +| Shared setup engine/parity (§4.1, §8) | application coordinator + existing policies | scenarios 3, 5, 15; import/schema checks | architecture | +| Bounded config/compatibility (§6.2–7) | CLI parser + config policy | scenarios 3–4, 15–16 | configuration | +| Separate PAT roles/evidence (§4.3, §6) | permission/identity/credential use cases | scenarios 6–7, 11, 13, 16 | authentication, credentials | +| Revision-bound Apply/recovery (§6.3, §10) | session coordinator + execution boundary | scenarios 8–11 | troubleshooting, provisioning | +| Browser security/privacy (§4.3, §11) | loopback HTTP/asset adapters + redacted presenter | scenarios 9, 12–13 | authentication, architecture | +| Accessible truthful UX (§9) | Svelte presenter + message catalog | scenarios 5–7, 10–11, 13–14 | how-to-use, troubleshooting | + +## 18. Implementation sequence and current evidence + +The first implementation slice adds the explicit `--web` route, a Svelte/Vite +static build packaged next to the CLI, a loopback HTTP adapter, an in-memory +semantic prompt bridge, web presentation adapters over existing questionnaire, +wizard, credential, permission, and mutation use cases, and a light/dark/system +responsive UI. It also adds a per-checkout setup guard, redacted views, +controller takeover, origin/Host/capability/revision checks, final browser +Apply approval, and pre-Apply repository/file/remote/permission rechecks. +It composes credential collection without pre-Apply credential-health workflow +dispatch/bootstrap, preserving the terminal's existing composition separately. +The final drift guard resolves package-source labels to checkout destinations, +includes retireable managed assets, and treats cancellation/expiry during +asynchronous final GitHub checks as a hard pre-mutation stop. +Temporary fixture tests and npm-pack checks exercise the built artifacts; +neither this repository nor GitHub is used as a setup test target. + +The browser presentation now uses a small page shell, a single session +transport module, prompt-specific presenters, reusable status/theme/card and +control patterns, pure answer/link helpers, and layered CSS. Tests enforce +the browser dependency boundary, module-size budget, palette contrast, +answer normalization, allowlisted links, and revision/capability transport. +This decomposition is an implementation slice, not evidence of the still-open +application coordinator and full UI/accessibility acceptance gates. + +These facts are **not** release acceptance. The orchestration in +`src/cli/commands/setup.ts` still needs extraction into the prescribed +application-level session coordinator; the 102-case budget, full human +cross-platform/accessibility review, exact per-resource progress/partial +evidence, and adversarial concurrency/idle/crash suite remain open. The +catalog stays `proposed` until the definition of done is evidenced. Existing +terminal policy/use cases remain the authority; the current web path does not +introduce its own permission catalog. + +The latest full local run on 2026-09-28 passed 501 Jest suites / 5,347 tests, +with 95.93% statements, 90.83% branches, 96.51% functions, and 97.24% lines +repository-wide. The new setup-PAT intent, bootstrap audit, configured audit, +remote-fact comparison, and override merge modules each reached 100% in all +four metrics; final web Apply authorization reached 100% lines and 95.83% +branches. The local HTTP server reached 99.33% lines and 92.26% branches. +Focused tests additionally cover the CLI handoff, semantic Svelte/Vite renders, +empty issue-workflow selection, drift, cancellation, and package isolation. +Typecheck, lint, Svelte diagnostics, full build, catalog, documentation, +workflow, npm-package validation, and package smoke checks passed without +real PATs or setup dogfooding. Human browser/accessibility and cross-platform +review, the formal 102-case-by-area acceptance mapping, and the complete +application-level session coordinator remain open release gates. The generated +bundle synchronization check runs after the source/build commit is staged. + +1. Review this threat model and UI prototype with product/security/accessibility; + freeze semantic transport schemas, redacted views, and error taxonomy. +2. Extract the existing CLI orchestration into a frontend-neutral setup + session use case without changing terminal behavior; add parity and + dependency tests first. +3. Build a read-only Svelte/Vite six-stage prototype and packaged asset + pipeline; prove isolated npm install, loopback restrictions, and fallback. +4. Add revision-bound local HTTP commands, session lifetime, tab control, + CSRF/CSP/content limits, and adversarial tests before accepting a PAT. +5. Add role-separated masked PAT collection, identity/permission audits, + final plan and single-flight Apply through existing adapters. +6. Add recovery, partial-result, documentation, accessibility, security, + package, and cross-platform evidence; only then expose `--web` as released. + +## 19. Definition of Done + +- [ ] Product/security/accessibility review accepts the complete local threat + model and every normative requirement has scenario/traceability evidence. +- [ ] Terminal behavior remains compatible; web and terminal use one + application decision engine with enforceable dependency rules. +- [ ] Local HTTP, controller, PAT, plan revision, and Apply defenses pass the + adversarial/security budget with no secret in browser storage or logs. +- [ ] All 102 distinct new web cases by area pass without real PATs or + dogfooding; the repository and changed-module coverage thresholds + already pass for the current implementation slice. +- [ ] Global npm-pack install serves complete local assets; Action/API bundles + and workflow assets remain unchanged except intentional shared policies. +- [ ] Pending, action, blocked, partial, complete, cancelled, and expired + views pass responsive/accessibility/localization/human review. +- [ ] The browser shell, session transport, prompt presenters, shared visual + patterns, and palette/style layers remain separate, with enforced + dependency and module-size checks and contributor guidance. +- [ ] User/setup/operator/contributor docs, flag help, migration/rollback, + recovery, and PAT cleanup/renewal guidance are linked and validated. +- [ ] Build, lint, typecheck, coverage, architecture, workflow, package, + documentation, catalog generation, and `validate:specifications` pass. +- [ ] No readiness-blocking decision remains unresolved; no GitHub issue, + Action run, or test PAT is created while validating this implementation. + +## 20. References and decisions + +- Related specifications: [setup baseline](./setup-configuration-credentials-and-doctor.md), + [operator PAT](./temporary-setup-operator-authorization.md), + [bot PAT](./guided-bot-pat-onboarding.md), + [PAT permission evidence](./setup-pat-permission-guidance-and-verification.md), + [CLI contract](./cli-and-single-action-execution.md). +- Provider/framework sources: [GitHub PAT creation and deletion](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens), + [GitHub account switcher](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/switching-between-accounts), + [Svelte overview](https://svelte.dev/docs/svelte/overview), + [Vite static build](https://vite.dev/guide/build), + [Node HTTP](https://nodejs.org/api/http.html). +- Security sources: [OWASP CSRF](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html), + [CSP](https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html), + [HTML5 storage](https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html). +- Decision: use Svelte + Vite static assets and a CLI-owned Node loopback + server; no SvelteKit, SSR, hosted service, third-party assets, account + manager, or private GitHub website automation in this version. +- Rejected: wrapping the interactive terminal through HTTP, a second web + grant/plan implementation, a browser-only GitHub API client, accepting + `--yes` as web approval, and promising automatic PAT revocation. +- Follow-up outside scope: future GitHub App authentication and optional + durable resume require separate credential/data-safety specifications. diff --git a/specs/setup-configuration-credentials-and-doctor.md b/specs/setup-configuration-credentials-and-doctor.md index 8286e2877..2453eb242 100644 --- a/specs/setup-configuration-credentials-and-doctor.md +++ b/specs/setup-configuration-credentials-and-doctor.md @@ -487,6 +487,10 @@ widths, canceled prompts, secret masking, and GitHub permission variants. - Permission companion: `setup-pat-permission-guidance-and-verification.md` owns the pre-prompt matrices, post-entry evidence states, and read-only probe boundary for setup and workflow PATs. +- Future interface companion: [local web setup assistant](./local-web-setup-assistant.md) + specifies an optional, loopback-only `--web` adapter over the same setup + policies and application gates. This baseline describes the shipped CLI; + the web mode is not implemented by this amendment. - Decision: one configuration policy serves setup, doctor, and workflow inputs. - Rejected: storing credentials in YAML/JSON or silently overwriting managed files. - Follow-up: cross-provider transactional rollback is outside this baseline. diff --git a/specs/temporary-setup-operator-authorization.md b/specs/temporary-setup-operator-authorization.md index ca3c50943..451dc0358 100644 --- a/specs/temporary-setup-operator-authorization.md +++ b/specs/temporary-setup-operator-authorization.md @@ -713,6 +713,10 @@ Secret renewal. - Related: [guided bot PAT onboarding](./guided-bot-pat-onboarding.md), [setup baseline](./setup-configuration-credentials-and-doctor.md), and [PAT permission guidance](./setup-pat-permission-guidance-and-verification.md). +- Future presentation: [local web setup assistant](./local-web-setup-assistant.md) + reuses this role's grant, audit, and cleanup rules; it does not change the + browser-owned PAT issuance or GitHub deletion contract. Its new `--web` + flag is separate from this terminal-focused first release. - Primary sources: [GitHub PAT form and URL parameters](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens), [GitHub browser account switcher](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/switching-between-accounts). - Decision: ship guided PAT creation for both roles; do not present automatic diff --git a/src/__tests__/cli.test.ts b/src/__tests__/cli.test.ts index 5b1bf8e61..912e72be9 100644 --- a/src/__tests__/cli.test.ts +++ b/src/__tests__/cli.test.ts @@ -9,6 +9,13 @@ import { runLocalAction } from '../actions/local_action'; import { ACTIONS } from '../data/model/action_types'; import { INPUT_KEYS } from '../application/contracts/input_keys'; import type { SetupTokenPermissionReport, SetupTokenPermissionRequirement } from '../domain/setup_token_permissions'; +import { WebSetupBridge } from '../cli/web_setup_bridge'; +import { WebSetupQuestionnaireCollector } from '../cli/web_setup_adapters'; +import { startWebSetupServer, openWebSetupBrowser } from '../cli/web_setup_server'; +import { captureSetupApplySnapshot, setupApplySnapshotMatches } from '../cli/setup_apply_snapshot'; +import { createSetupReviewState } from '../application/policies/setup_questionnaire_policy'; +import type { WebSetupPrompt } from '../application/contracts/web_setup_view'; +import { SetupDoctorWorkspaceQueryAdapter } from '../infrastructure/setup_workspace_adapter'; jest.mock('child_process', () => ({ execSync: jest.fn(), @@ -18,6 +25,22 @@ jest.mock('../actions/local_action', () => ({ runLocalAction: jest.fn().mockResolvedValue([]), })); +// Setup serialization is exercised against temporary Git repositories in its +// dedicated adapter tests; CLI command tests mock the filesystem boundary. +jest.mock('../cli/setup_session_guard', () => ({ + acquireSetupSessionGuard: jest.fn(() => jest.fn()), +})); + +jest.mock('../cli/web_setup_server', () => ({ + startWebSetupServer: jest.fn(async () => ({ url: 'http://127.0.0.1:40000/', closed: Promise.resolve(), close: jest.fn() })), + openWebSetupBrowser: jest.fn(), +})); + +jest.mock('../cli/setup_apply_snapshot', () => ({ + captureSetupApplySnapshot: jest.fn(() => ({})), + setupApplySnapshotMatches: jest.fn(() => true), +})); + jest.mock('../utils/logger', () => ({ logError: jest.fn(), logInfo: jest.fn(), @@ -61,7 +84,7 @@ jest.mock('../cli/setup_doctor_presenter', () => ({ }), })); -const mockTokenPermissionInspect = jest.fn(async (request: { role: 'setup' | 'workflow'; requirements: readonly SetupTokenPermissionRequirement[] }): Promise => ({ +const mockTokenPermissionInspect = jest.fn(async (request: { role: 'setup' | 'workflow'; token: string; requirements: readonly SetupTokenPermissionRequirement[] }): Promise => ({ role: request.role, identityStatus: 'valid' as const, identityMessage: 'verified', @@ -94,6 +117,7 @@ jest.mock('../infrastructure/composition/setup_credentials_composition_root', () createSetupCredentialsUseCase: () => ({ collect: mockSetupCredentialsCollect }), createSetupRemoteConfigurationReadPort: () => ({ inspect: mockRemoteConfigurationInspect, + inspectCredentialHealthWorkflow: jest.fn(async () => 'installed'), }), })); @@ -464,6 +488,176 @@ describe('CLI', () => { setupCommand.setOptionValue(option.attributeName(), option.defaultValue); } }); + + describe('local web command handoff', () => { + let ask: jest.SpyInstance; + let collect: jest.SpyInstance; + + const answerWebPrompt = async (prompt: WebSetupPrompt): Promise => { + if (prompt.title === 'Confirm this repository') return 'Yes, this is my repository'; + if (prompt.title === 'How will you provide your setup PAT?') return 'Manual PAT'; + if (prompt.title === 'Temporary setup PAT') return 'github_pat_web_setup_test_token'; + if (prompt.kind === 'plan') return 'approve'; + if (prompt.title === 'Apply this setup now?') return 'Apply setup'; + if (prompt.title === 'Update existing workflows?') return 'Keep existing'; + throw new Error(`Unexpected browser prompt: ${prompt.title}`); + }; + + beforeEach(() => { + (setupApplySnapshotMatches as jest.Mock).mockReturnValue(true); + (execSync as jest.Mock).mockImplementation((command: string) => Buffer.from( + command === 'git rev-parse HEAD' ? 'a'.repeat(40) + : command === 'git rev-parse --show-toplevel' ? process.cwd() + : command === 'git rev-parse --abbrev-ref HEAD' ? 'develop' + : 'https://github.com/test-owner/test-repo.git', + )); + ask = jest.spyOn(WebSetupBridge.prototype, 'ask').mockImplementation(answerWebPrompt); + collect = jest.spyOn(WebSetupQuestionnaireCollector.prototype, 'collect') + .mockImplementation(async initial => createSetupReviewState(initial.draft)); + }); + + afterEach(() => { ask.mockRestore(); collect.mockRestore(); }); + + it('uses one browser session through PAT, plan, revalidation, and Apply', async () => { + await program.parseAsync(['node', 'cli', 'setup', '--web', '--pr-approval-mode', 'off', '--skip-secrets']); + expect(startWebSetupServer).toHaveBeenCalledTimes(1); + expect(openWebSetupBrowser).toHaveBeenCalledWith('http://127.0.0.1:40000/'); + expect(ask.mock.calls.map(call => call[0].title)).toEqual(expect.arrayContaining([ + 'Confirm this repository', 'How will you provide your setup PAT?', 'Temporary setup PAT', + 'Review your setup plan', 'Apply this setup now?', + ])); + expect(mockTokenPermissionInspect).toHaveBeenCalledTimes(3); + expect(captureSetupApplySnapshot).toHaveBeenCalledTimes(1); + expect(setupApplySnapshotMatches).toHaveBeenCalledTimes(1); + expect(runLocalAction).toHaveBeenCalledTimes(1); + expect(process.exitCode).toBeUndefined(); + }); + + it('stops before acquiring a PAT when repository confirmation is declined', async () => { + ask.mockResolvedValueOnce('Stop and choose another checkout'); + await program.parseAsync(['node', 'cli', 'setup', '--web']); + expect(mockTokenPermissionInspect).not.toHaveBeenCalled(); + expect(runLocalAction).not.toHaveBeenCalled(); + }); + + it.each([ + [undefined, 130], + ['decline', undefined], + ] as const)('honors a %s browser plan decision before credentials or Apply', async (answer, exitCode) => { + ask.mockImplementation(async (prompt: WebSetupPrompt) => prompt.kind === 'plan' + ? answer : answerWebPrompt(prompt)); + await program.parseAsync(['node', 'cli', 'setup', '--web', '--pr-approval-mode', 'off', '--skip-secrets']); + expect(runLocalAction).not.toHaveBeenCalled(); + expect(process.exitCode).toBe(exitCode); + expect(ask.mock.calls.map(call => call[0].title)).not.toContain('Apply this setup now?'); + }); + + it('guides setup PAT review and confirms the audited operator account before planning', async () => { + ask.mockImplementation(async (prompt: WebSetupPrompt) => { + if (prompt.title === 'How will you provide your setup PAT?') return 'Guided GitHub link'; + if (prompt.title === 'What kind of GitHub account owns this repository?') return 'Personal account'; + if (prompt.title === 'Review these provisional setup PAT grants') return 'Continue to GitHub'; + if (prompt.title.includes('Is that the intended operator account?')) return 'Yes, continue'; + return answerWebPrompt(prompt); + }); + mockTokenPermissionInspect.mockResolvedValueOnce({ + role: 'setup', account: 'operator', identityStatus: 'valid', identityMessage: 'verified', + ready: true, confirmationRequired: false, checks: [], + }); + await program.parseAsync(['node', 'cli', 'setup', '--web', '--pr-approval-mode', 'off', '--skip-secrets']); + expect(ask.mock.calls.map(call => call[0].title)).toEqual(expect.arrayContaining([ + 'Review these provisional setup PAT grants', + expect.stringContaining('Is that the intended operator account?'), + ])); + expect(runLocalAction).toHaveBeenCalledTimes(1); + }); + + it('keeps web dry-run local and never asks for either PAT or Apply', async () => { + await program.parseAsync(['node', 'cli', 'setup', '--web', '--dry-run', '--pr-approval-mode', 'off']); + expect(ask.mock.calls.map(call => call[0].title)).not.toContain('Temporary setup PAT'); + expect(ask.mock.calls.map(call => call[0].title)).not.toContain('Apply this setup now?'); + expect(mockTokenPermissionInspect).not.toHaveBeenCalled(); + expect(runLocalAction).not.toHaveBeenCalled(); + expect(process.exitCode).toBeUndefined(); + }); + + it('requires explicit selection before using an environment PAT', async () => { + mockGetSetupToken.mockReturnValueOnce('github_pat_from_environment_test'); + ask.mockImplementation(async (prompt: WebSetupPrompt) => prompt.title === 'An environment setup PAT is available' + ? 'Use the environment PAT' : answerWebPrompt(prompt)); + await program.parseAsync(['node', 'cli', 'setup', '--web', '--pr-approval-mode', 'off', '--skip-secrets']); + expect(ask.mock.calls.map(call => call[0].title)).toContain('An environment setup PAT is available'); + expect(ask.mock.calls.map(call => call[0].title)).not.toContain('Temporary setup PAT'); + expect(mockTokenPermissionInspect.mock.calls[0][0].token).toBe('github_pat_from_environment_test'); + expect(runLocalAction).toHaveBeenCalledTimes(1); + }); + + it('does not use an environment PAT when the browser choice is cancelled', async () => { + mockGetSetupToken.mockReturnValueOnce('github_pat_from_environment_test'); + ask.mockImplementation(async (prompt: WebSetupPrompt) => prompt.title === 'An environment setup PAT is available' + ? undefined : answerWebPrompt(prompt)); + await program.parseAsync(['node', 'cli', 'setup', '--web']); + expect(mockTokenPermissionInspect).not.toHaveBeenCalled(); + expect(runLocalAction).not.toHaveBeenCalled(); + expect(process.exitCode).toBe(130); + }); + + it('blocks final Apply if GitHub facts changed after the reviewed plan', async () => { + mockRemoteConfigurationInspect.mockResolvedValueOnce(defaultRemoteConfiguration) + .mockResolvedValueOnce({ ...defaultRemoteConfiguration, repositoryVisibility: 'public' }); + await program.parseAsync(['node', 'cli', 'setup', '--web', '--pr-approval-mode', 'off', '--skip-secrets']); + expect(runLocalAction).not.toHaveBeenCalled(); + expect(process.exitCode).toBe(1); + }); + + it('does not enter the mutation boundary when final Apply is declined', async () => { + ask.mockImplementation(async (prompt: WebSetupPrompt) => prompt.title === 'Apply this setup now?' + ? 'Stop without applying' : answerWebPrompt(prompt)); + await program.parseAsync(['node', 'cli', 'setup', '--web', '--pr-approval-mode', 'off', '--skip-secrets']); + expect(runLocalAction).not.toHaveBeenCalled(); + expect(process.exitCode).toBeUndefined(); + }); + + it('passes only explicitly approved changed workflows to the mutation boundary', async () => { + const comparison = jest.spyOn(SetupDoctorWorkspaceQueryAdapter.prototype, 'compareWorkflows') + .mockReturnValue([ + { file: 'copilot_issue.yml', destination: '.github/workflows/copilot_issue.yml', status: 'changed' }, + { file: 'unmanaged.yml', destination: '.github/workflows/unmanaged.yml', status: 'unmanaged' }, + ]); + ask.mockImplementation(async (prompt: WebSetupPrompt) => prompt.title === 'Update existing workflows?' + ? 'Update setup-managed workflows' : answerWebPrompt(prompt)); + try { + await program.parseAsync(['node', 'cli', 'setup', '--web', '--pr-approval-mode', 'off', '--skip-secrets']); + expect(runLocalAction).toHaveBeenCalledWith(expect.objectContaining({ setupWorkflowUpdates: ['copilot_issue.yml'] })); + } finally { comparison.mockRestore(); } + }); + + it('refuses to launch a browser session without a verified HEAD', async () => { + (execSync as jest.Mock).mockImplementation((command: string) => { + if (command === 'git rev-parse HEAD') throw new Error('missing HEAD'); + return Buffer.from(command === 'git rev-parse --show-toplevel' ? process.cwd() + : 'https://github.com/test-owner/test-repo.git'); + }); + await program.parseAsync(['node', 'cli', 'setup', '--web']); + expect(startWebSetupServer).not.toHaveBeenCalled(); + expect(runLocalAction).not.toHaveBeenCalled(); + expect(process.exitCode).toBe(1); + }); + + it('fails closed when selected files drift after browser plan approval', async () => { + (setupApplySnapshotMatches as jest.Mock).mockReturnValue(false); + await program.parseAsync(['node', 'cli', 'setup', '--web', '--pr-approval-mode', 'off', '--skip-secrets']); + expect(runLocalAction).not.toHaveBeenCalled(); + expect(process.exitCode).toBe(1); + }); + + it('rejects unattended CLI approval flags in web mode', async () => { + await program.parseAsync(['node', 'cli', 'setup', '--web', '--yes']); + expect(startWebSetupServer).not.toHaveBeenCalled(); + expect(runLocalAction).not.toHaveBeenCalled(); + expect(process.exitCode).toBe(1); + }); + }); const guidedTerminal = (answer?: (prompt: string) => string | undefined) => ({ isInteractive: () => true, readText: jest.fn(async (prompt: string) => ({ kind: 'value' as const, value: answer?.(prompt) diff --git a/src/__tests__/cli_context_root.test.ts b/src/__tests__/cli_context_root.test.ts new file mode 100644 index 000000000..181d9639d --- /dev/null +++ b/src/__tests__/cli_context_root.test.ts @@ -0,0 +1,31 @@ +import { execFileSync } from 'node:child_process'; +import { mkdirSync, mkdtempSync, realpathSync, rmSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; +import { getGitRepositoryRoot, isGitRepositoryRoot } from '../cli_context'; + +describe('canonical checkout root for setup plans', () => { + test('repository-relative files resolve from the root even when launched in a child directory', () => { + const checkout = mkdtempSync(join(tmpdir(), 'copilot-setup-root-test-')); + try { + execFileSync('git', ['init', '-q', checkout]); + const child = join(checkout, 'nested'); + mkdirSync(child); + expect(getGitRepositoryRoot(child)).toBe(realpathSync(checkout)); + expect(isGitRepositoryRoot(child)).toBe(false); + expect(isGitRepositoryRoot(checkout)).toBe(true); + } finally { + rmSync(checkout, { recursive: true, force: true }); + } + }); + + test('an unrelated directory has no checkout root', () => { + const outside = mkdtempSync(join(tmpdir(), 'copilot-not-a-checkout-')); + try { + expect(() => getGitRepositoryRoot(outside)).toThrow(); + expect(isGitRepositoryRoot(outside)).toBe(false); + } finally { + rmSync(outside, { recursive: true, force: true }); + } + }); +}); diff --git a/src/application/contracts/web_setup_view.ts b/src/application/contracts/web_setup_view.ts new file mode 100644 index 000000000..fddc06dfb --- /dev/null +++ b/src/application/contracts/web_setup_view.ts @@ -0,0 +1,29 @@ +import type { SetupJourneyView } from '../policies/setup_journey_policy'; +import type { SetupQuestion } from '../../domain/setup_questionnaire'; +import type { SetupTokenPermissionReport, SetupTokenPermissionRequirement, SetupTokenRole } from '../../domain/setup_token_permissions'; + +export type WebSetupPrompt = + | { kind: 'question'; title: string; question: SetupQuestion; phase: string; pass: number } + | { kind: 'choice'; title: string; description?: string; choices: readonly string[]; defaultValue?: string } + | { kind: 'text' | 'secret'; title: string; description?: string; optional?: boolean; link?: string } + | { kind: 'confirm'; title: string; description?: string; choices: readonly string[] } + | { kind: 'plan'; title: string; plan: WebSetupPlan }; + +export interface WebSetupPlan { + readonly files: readonly string[]; + readonly workflows: readonly string[]; + readonly variables: readonly string[]; + readonly secrets: readonly string[]; + readonly warnings: readonly string[]; +} + +export interface WebSetupView { + readonly revision: number; + readonly promptRevision?: number; + readonly repository: string; + readonly journey?: SetupJourneyView; + readonly prompt?: WebSetupPrompt; + readonly message?: { tone: 'info' | 'success' | 'warning' | 'error'; text: string; link?: string }; + readonly permissions?: { role: SetupTokenRole; requirements?: readonly SetupTokenPermissionRequirement[]; report?: SetupTokenPermissionReport }; + readonly outcome?: 'complete' | 'partial' | 'blocked' | 'cancelled' | 'dry-run'; +} diff --git a/src/application/errors/setup_interaction_cancelled_error.ts b/src/application/errors/setup_interaction_cancelled_error.ts new file mode 100644 index 000000000..16e033379 --- /dev/null +++ b/src/application/errors/setup_interaction_cancelled_error.ts @@ -0,0 +1,7 @@ +/** Shared cancellation signal for terminal and browser setup presenters. */ +export class SetupInteractionCancelledError extends Error { + constructor() { + super('Setup input was cancelled.'); + this.name = 'SetupInteractionCancelledError'; + } +} diff --git a/src/application/policies/__tests__/setup_questionnaire_policy.test.ts b/src/application/policies/__tests__/setup_questionnaire_policy.test.ts index 2ab079678..c21e644ff 100644 --- a/src/application/policies/__tests__/setup_questionnaire_policy.test.ts +++ b/src/application/policies/__tests__/setup_questionnaire_policy.test.ts @@ -52,6 +52,12 @@ describe('setup questionnaire policy', () => { expect(state.draft.issueWorkflows.enabled).toEqual([]); }); + it('lets either presentation explicitly clear every issue workflow', () => { + const state = advanceTo(createSetupQuestionnaire(createDefaultSetupConfiguration()), 'issueWorkflows.enabled'); + const cleared = transitionSetupQuestionnaire(state, { kind: 'answer', value: 'none' }); + expect(cleared.draft.issueWorkflows.enabled).toEqual([]); + }); + it('enters review immediately when the permission-intent phase has no open questions', () => { const ids = [ 'features.issues', 'features.pullRequests', 'issueWorkflows.enabled', 'pullRequestApproval.mode', diff --git a/src/application/policies/__tests__/setup_remote_facts_policy.test.ts b/src/application/policies/__tests__/setup_remote_facts_policy.test.ts new file mode 100644 index 000000000..63361abad --- /dev/null +++ b/src/application/policies/__tests__/setup_remote_facts_policy.test.ts @@ -0,0 +1,37 @@ +import type { SetupRemoteConfiguration } from '../../../domain/setup'; +import { sameSetupRemoteFacts } from '../setup_remote_facts_policy'; + +const facts: SetupRemoteConfiguration = { + ownerType: 'Organization', repositoryId: 5, repositoryVisibility: 'private', + repositorySecrets: ['PAT', 'OPENAI_API_KEY'], repositorySecretsAccess: 'available', + organizationSecrets: ['EXISTING'], + repositoryVariables: [{ name: 'MAIN_BRANCH', value: 'main' }, { name: 'AGENT_PROVIDER', value: 'codex' }], + repositoryVariablesAccess: 'available', organizationVariables: [{ name: 'CUSTOM', value: 'one' }], + organizationAccess: 'available', organizationSecretsAccess: 'available', organizationVariablesAccess: 'available', + credentialHealthWorkflow: 'installed', +}; + +describe('setup remote fact equivalence', () => { + test('resource ordering is immaterial', () => { + expect(sameSetupRemoteFacts(facts, { + ...facts, repositorySecrets: [...facts.repositorySecrets].reverse(), + repositoryVariables: [...facts.repositoryVariables].reverse(), + })).toBe(true); + }); + + test.each([ + ['ownerType', 'User'], ['repositoryId', 6], ['repositoryVisibility', 'public'], + ['repositorySecrets', []], ['repositorySecretsAccess', 'unknown'], ['organizationSecrets', []], + ['repositoryVariablesAccess', 'unavailable'], ['organizationVariables', []], + ['organizationAccess', 'unknown'], ['organizationSecretsAccess', 'unknown'], + ['organizationVariablesAccess', 'unknown'], ['credentialHealthWorkflow', 'missing'], + ] as const)('detects a change in %s', (field, value) => { + expect(sameSetupRemoteFacts(facts, { ...facts, [field]: value })).toBe(false); + }); + + test('detects a changed variable value even with the same name', () => { + expect(sameSetupRemoteFacts(facts, { + ...facts, repositoryVariables: [{ name: 'MAIN_BRANCH', value: 'develop' }, facts.repositoryVariables[1]], + })).toBe(false); + }); +}); diff --git a/src/application/policies/__tests__/setup_token_permission_policy.test.ts b/src/application/policies/__tests__/setup_token_permission_policy.test.ts index f5b104f34..b561cccd3 100644 --- a/src/application/policies/__tests__/setup_token_permission_policy.test.ts +++ b/src/application/policies/__tests__/setup_token_permission_policy.test.ts @@ -4,6 +4,7 @@ import { buildSetupPatPermissionRequirements, buildWorkflowPatPermissionRequirements, normalizePermissionRequirements, + requiredSetupPatPermissionDelta, } from '../setup_token_permission_policy'; import type { SetupRemoteConfiguration } from '../../../domain/setup'; import type { SetupTokenPermissionRequirement } from '../../../domain/setup_token_permissions'; @@ -23,6 +24,19 @@ function disabledRuntimeConfiguration() { } describe('setup token permission policy', () => { + it('reports only newly required or upgraded grants after guided review', () => { + const baseline = buildSetupPatPermissionRequirements(); + const metadata = baseline.find(item => item.permission === 'Metadata')!; + const contents = baseline.find(item => item.permission === 'Contents')!; + const secrets = baseline.find(item => item.permission === 'Secrets' && item.scope === 'repository')!; + const final = [metadata, { ...contents, permission: 'contents', level: 'write' as const }, + { ...secrets, applicability: 'required' as const }]; + expect(requiredSetupPatPermissionDelta([metadata, contents, secrets], final)).toEqual([ + 'repository contents write', 'repository Secrets write', + ]); + expect(requiredSetupPatPermissionDelta(final, [metadata, contents, secrets])).toEqual([]); + }); + it('describes the complete setup PAT permission catalog before the prompt', () => { const requirements = buildSetupPatPermissionRequirements(); expect(requirements.map(item => `${item.scope}:${item.permission}:${item.level}`)).toEqual([ diff --git a/src/application/policies/merge_setup_overrides_policy.ts b/src/application/policies/merge_setup_overrides_policy.ts new file mode 100644 index 000000000..73d0f98ff --- /dev/null +++ b/src/application/policies/merge_setup_overrides_policy.ts @@ -0,0 +1,30 @@ +import type { SetupConfigurationOverrides } from './setup_configuration_policy'; + +/** Explicit CLI flags override only their fields; file-only settings remain intact. */ +export function mergeSetupOverrides( + fileOverrides: SetupConfigurationOverrides, + flagOverrides: SetupConfigurationOverrides, +): SetupConfigurationOverrides { + return { + ...fileOverrides, + ...flagOverrides, + features: { ...fileOverrides.features, ...flagOverrides.features }, + agents: { ...fileOverrides.agents, ...flagOverrides.agents }, + repository: { ...fileOverrides.repository, ...flagOverrides.repository }, + ai: { ...fileOverrides.ai, ...flagOverrides.ai }, + pullRequestApproval: { + ...fileOverrides.pullRequestApproval, + ...flagOverrides.pullRequestApproval, + coverage: { ...fileOverrides.pullRequestApproval?.coverage, ...flagOverrides.pullRequestApproval?.coverage }, + } as SetupConfigurationOverrides['pullRequestApproval'], + projects: { ...fileOverrides.projects, ...flagOverrides.projects }, + issueWorkflows: { ...fileOverrides.issueWorkflows, ...flagOverrides.issueWorkflows }, + repositoryAgentGuidance: { ...fileOverrides.repositoryAgentGuidance, ...flagOverrides.repositoryAgentGuidance }, + storage: { + ...fileOverrides.storage, + ...flagOverrides.storage, + secrets: { ...fileOverrides.storage?.secrets, ...flagOverrides.storage?.secrets, overrides: { ...fileOverrides.storage?.secrets?.overrides, ...flagOverrides.storage?.secrets?.overrides } }, + variables: { ...fileOverrides.storage?.variables, ...flagOverrides.storage?.variables, overrides: { ...fileOverrides.storage?.variables?.overrides, ...flagOverrides.storage?.variables?.overrides } }, + }, + }; +} diff --git a/src/application/policies/setup_configuration_plan.ts b/src/application/policies/setup_configuration_plan.ts index 2fb46e435..e97c350fe 100644 --- a/src/application/policies/setup_configuration_plan.ts +++ b/src/application/policies/setup_configuration_plan.ts @@ -13,7 +13,7 @@ import { import { usesOrganizationStorage } from './setup_configuration_storage_policy'; import { buildSetupCredentialRequirements } from './setup_credential_requirement_policy'; import { resolveLocaleProfile } from '../../domain/locale'; -import { issueWorkflowFormFiles, serializeIssueWorkflowProfile } from '../../domain/issue_workflow_profile'; +import { ISSUE_WORKFLOW_CATALOG, ISSUE_WORKFLOW_KINDS, issueWorkflowFormFiles, serializeIssueWorkflowProfile } from '../../domain/issue_workflow_profile'; import { effectiveIssueWorkflowFeatures, effectiveIssueWorkflowProfile } from './setup_issue_workflow_policy'; export { buildSetupCredentialRequirements }; @@ -61,6 +61,28 @@ export function buildSetupPlan( }; } +/** Actual checkout destinations covered by a web Apply drift check. + * The presentation plan uses package-source labels for workflows/forms; + * comparing those labels as checkout paths would silently miss local edits. + */ +export function setupPlanGuardPaths(plan: Readonly): string[] { + const selected = plan.selectedFiles.map(file => { + if (file.startsWith('workflows/')) return `.github/${file}`; + if (file.startsWith('ISSUE_TEMPLATE/')) return `.github/${file}`; + if (file === 'pull_request_template.md') return '.github/pull_request_template.md'; + if (file === 'AGENTS.md (managed pointer only)') return 'AGENTS.md'; + return file; + }); + // Deselected managed assets can be retired to setup-backups during Apply. + const retiredCandidates = [ + ...['config.yml', ...ISSUE_WORKFLOW_KINDS.map(kind => ISSUE_WORKFLOW_CATALOG[kind].formFile)] + .map(file => `.github/ISSUE_TEMPLATE/${file}`), + ...['release_workflow.yml', 'hotfix_workflow.yml', 'copilot_deployment_orchestration.yml'] + .map(file => `.github/workflows/${file}`), + ]; + return [...new Set([...selected, ...retiredCandidates])].sort(); +} + export function buildSetupRepositoryVariables(configuration: SetupConfiguration): SetupVariable[] { const variables: SetupVariable[] = []; const add = (name: string, value: string | number | boolean | undefined) => { diff --git a/src/application/policies/setup_questionnaire_policy.ts b/src/application/policies/setup_questionnaire_policy.ts index 5ce792891..cd5057fc6 100644 --- a/src/application/policies/setup_questionnaire_policy.ts +++ b/src/application/policies/setup_questionnaire_policy.ts @@ -506,6 +506,7 @@ function applyAnswer( function parseWorkflowSelection(raw: string): { value: IssueWorkflowKind[] } | { error: string } { const normalized = raw.trim().toLowerCase(); + if (normalized === 'none') return { value: [] }; if (!normalized || normalized === 'all') return { value: [...ISSUE_WORKFLOW_KINDS] }; const requested = normalized.split(',').map(item => item.trim()).filter(Boolean) .map(item => item.replace(/\s+—.*$/u, '').replace(/^\d+[.)]\s*/u, '')); diff --git a/src/application/policies/setup_remote_facts_policy.ts b/src/application/policies/setup_remote_facts_policy.ts new file mode 100644 index 000000000..a729a3112 --- /dev/null +++ b/src/application/policies/setup_remote_facts_policy.ts @@ -0,0 +1,23 @@ +import type { SetupRemoteConfiguration, SetupVariable } from '../../domain/setup'; + +/** Compare the semantic GitHub facts used by setup, not object/response ordering. */ +export function sameSetupRemoteFacts(left: SetupRemoteConfiguration, right: SetupRemoteConfiguration): boolean { + const variables = (items: readonly SetupVariable[]): readonly string[] => items + .map(item => JSON.stringify([item.name, item.value])).sort(); + const normalize = (facts: SetupRemoteConfiguration) => ({ + ownerType: facts.ownerType, + repositoryId: facts.repositoryId, + repositoryVisibility: facts.repositoryVisibility, + repositorySecrets: [...facts.repositorySecrets].sort(), + repositorySecretsAccess: facts.repositorySecretsAccess, + organizationSecrets: [...facts.organizationSecrets].sort(), + repositoryVariables: variables(facts.repositoryVariables), + repositoryVariablesAccess: facts.repositoryVariablesAccess, + organizationVariables: variables(facts.organizationVariables), + organizationAccess: facts.organizationAccess, + organizationSecretsAccess: facts.organizationSecretsAccess, + organizationVariablesAccess: facts.organizationVariablesAccess, + credentialHealthWorkflow: facts.credentialHealthWorkflow, + }); + return JSON.stringify(normalize(left)) === JSON.stringify(normalize(right)); +} diff --git a/src/application/policies/setup_token_permission_policy.ts b/src/application/policies/setup_token_permission_policy.ts index 4dd2e39c9..3a320fdff 100644 --- a/src/application/policies/setup_token_permission_policy.ts +++ b/src/application/policies/setup_token_permission_policy.ts @@ -91,6 +91,19 @@ export function buildSetupPatIntentUncertainty(configuration: Readonly item.applicability === 'required') + .map(item => [`${item.scope}:${item.permission.toLowerCase()}`, item.level])); + return after.filter(item => item.applicability === 'required' + && (previous.get(`${item.scope}:${item.permission.toLowerCase()}`) === undefined + || (previous.get(`${item.scope}:${item.permission.toLowerCase()}`) === 'read' && item.level === 'write'))) + .map(item => `${item.scope} ${item.permission} ${item.level}`); +} + function buildSetupPatRequirements( configuration: Readonly, organization: boolean, diff --git a/src/application/usecases/setup/__tests__/audit_configured_setup_pat_use_case.test.ts b/src/application/usecases/setup/__tests__/audit_configured_setup_pat_use_case.test.ts new file mode 100644 index 000000000..bd917bff5 --- /dev/null +++ b/src/application/usecases/setup/__tests__/audit_configured_setup_pat_use_case.test.ts @@ -0,0 +1,111 @@ +import { createDefaultSetupConfiguration } from '../../../policies/setup_configuration_defaults'; +import { buildSetupPatPermissionRequirements } from '../../../policies/setup_token_permission_policy'; +import type { SetupRemoteConfiguration } from '../../../../domain/setup'; +import type { SetupTokenPermissionReport } from '../../../../domain/setup_token_permissions'; +import { AuditConfiguredSetupPatUseCase, type AuditConfiguredSetupPatPorts } from '../audit_configured_setup_pat_use_case'; + +const remote: SetupRemoteConfiguration = { + ownerType: 'Organization', repositoryId: 42, repositoryVisibility: 'private', + repositorySecrets: [], repositorySecretsAccess: 'available', organizationSecrets: [], + repositoryVariables: [], repositoryVariablesAccess: 'available', organizationVariables: [], + organizationAccess: 'available', organizationSecretsAccess: 'available', organizationVariablesAccess: 'available', +}; +const report: SetupTokenPermissionReport = { + role: 'setup', account: 'operator', identityStatus: 'valid', identityMessage: 'valid', + checks: [], ready: true, confirmationRequired: false, +}; + +function harness(options: { token?: string; guided?: boolean; assertedOwnerKind?: 'Organization' | 'User' } = {}) { + const provisionalRequirements = buildSetupPatPermissionRequirements(); + const ports: AuditConfiguredSetupPatPorts = { + permissions: { inspect: jest.fn(async () => report) }, + presenter: { showRequirements: jest.fn(), showReport: jest.fn() }, + confirmUnverifiable: jest.fn(async () => true), + showOwnerMismatch: jest.fn(), showExcessGrants: jest.fn(), showUpdatedLink: jest.fn(), + }; + const context = { + owner: 'owner', repository: 'repo', provisionalRequirements, + token: options.token, guided: options.guided ?? false, assertedOwnerKind: options.assertedOwnerKind, + }; + return { context, ports, useCase: new AuditConfiguredSetupPatUseCase(context, ports) }; +} + +describe('AuditConfiguredSetupPatUseCase', () => { + const configuration = createDefaultSetupConfiguration(); + + test('reports final grants and audits the supplied PAT without mutations', async () => { + const { ports, useCase } = harness({ token: 'test-token' }); + expect(await useCase.audit(configuration, remote)).toEqual({ status: 'accepted' }); + expect(ports.permissions.inspect).toHaveBeenCalledWith(expect.objectContaining({ + role: 'setup', owner: 'owner', repository: 'repo', token: 'test-token', requirements: expect.any(Array), + })); + expect(ports.presenter.showRequirements).toHaveBeenCalledWith('setup', expect.any(Array)); + expect(ports.presenter.showReport).toHaveBeenCalledWith(report); + expect(ports.confirmUnverifiable).not.toHaveBeenCalled(); + }); + + test('preview without a PAT shows requirements but never probes permissions', async () => { + const { ports, useCase } = harness(); + expect(await useCase.audit(configuration, remote)).toEqual({ status: 'accepted' }); + expect(ports.permissions.inspect).not.toHaveBeenCalled(); + expect(ports.presenter.showRequirements).toHaveBeenCalled(); + }); + + test('owner mismatch blocks before probing and offers a corrected link', async () => { + const { ports, useCase } = harness({ token: 'test-token', guided: true, assertedOwnerKind: 'User' }); + expect(await useCase.audit(configuration, remote)).toEqual(expect.objectContaining({ status: 'blocked' })); + expect(ports.showOwnerMismatch).toHaveBeenCalledWith('User', 'Organization'); + expect(ports.showUpdatedLink).toHaveBeenCalledWith(expect.stringContaining('target_name=owner'), expect.any(Array)); + expect(ports.permissions.inspect).not.toHaveBeenCalled(); + }); + + test('unknown owner type does not fabricate a mismatch', async () => { + const { ports, useCase } = harness({ assertedOwnerKind: 'User' }); + expect(await useCase.audit(configuration, { ...remote, ownerType: 'Unknown' })).toEqual({ status: 'accepted' }); + expect(ports.showOwnerMismatch).not.toHaveBeenCalled(); + }); + + test('guided review explains grants removed from the provisional link', async () => { + const { context, ports, useCase } = harness({ guided: true }); + const secrets = context.provisionalRequirements.find(item => item.permission === 'Secrets' && item.scope === 'repository')!; + context.provisionalRequirements = [...context.provisionalRequirements, { ...secrets, applicability: 'required' }]; + const minimal = createDefaultSetupConfiguration(); + minimal.manageRepositorySecrets = false; + minimal.manageRepositoryVariables = false; + minimal.issueWorkflows.enabled = []; + minimal.createInitialTag = false; + for (const feature of Object.keys(minimal.features)) minimal.features[feature] = false; + minimal.pullRequestApproval = { ...minimal.pullRequestApproval, mode: 'off' }; + expect(await useCase.audit(minimal, remote)).toEqual({ status: 'accepted' }); + expect(ports.showExcessGrants).toHaveBeenCalledWith(expect.arrayContaining([expect.stringContaining('Secrets')])); + }); + + test('unverifiable writes require explicit confirmation', async () => { + const { ports, useCase } = harness({ token: 'test-token' }); + jest.spyOn(ports.permissions, 'inspect').mockResolvedValue({ ...report, ready: false, confirmationRequired: true }); + expect(await useCase.audit(configuration, remote)).toEqual({ status: 'accepted' }); + expect(ports.confirmUnverifiable).toHaveBeenCalledTimes(1); + }); + + test('declined unverifiable writes block the final plan', async () => { + const { ports, useCase } = harness({ token: 'test-token', guided: true }); + jest.spyOn(ports.permissions, 'inspect').mockResolvedValue({ ...report, ready: false, confirmationRequired: true }); + jest.spyOn(ports, 'confirmUnverifiable').mockResolvedValue(false); + expect(await useCase.audit(configuration, remote)).toEqual(expect.objectContaining({ status: 'blocked' })); + expect(ports.showUpdatedLink).toHaveBeenCalledTimes(1); + }); + + test.each(['invalid', 'unverifiable'] as const)('%s PAT identity blocks even when checks are otherwise ready', async identityStatus => { + const { ports, useCase } = harness({ token: 'test-token', guided: false }); + jest.spyOn(ports.permissions, 'inspect').mockResolvedValue({ ...report, identityStatus }); + expect(await useCase.audit(configuration, remote)).toEqual(expect.objectContaining({ status: 'blocked' })); + expect(ports.showUpdatedLink).not.toHaveBeenCalled(); + }); + + test('missing required access blocks and offers a new guided link', async () => { + const { ports, useCase } = harness({ token: 'test-token', guided: true }); + jest.spyOn(ports.permissions, 'inspect').mockResolvedValue({ ...report, ready: false }); + expect(await useCase.audit(configuration, remote)).toEqual(expect.objectContaining({ status: 'blocked' })); + expect(ports.showUpdatedLink).toHaveBeenCalledWith(expect.stringContaining('https://github.com/settings/personal-access-tokens/new?'), expect.any(Array)); + }); +}); diff --git a/src/application/usecases/setup/__tests__/prepare_setup_pat_intent_use_case.test.ts b/src/application/usecases/setup/__tests__/prepare_setup_pat_intent_use_case.test.ts new file mode 100644 index 000000000..82b7d52c7 --- /dev/null +++ b/src/application/usecases/setup/__tests__/prepare_setup_pat_intent_use_case.test.ts @@ -0,0 +1,128 @@ +import type { SetupQuestionnaireState } from '../../../../domain/setup_questionnaire'; +import { SetupInteractionCancelledError } from '../../../errors/setup_interaction_cancelled_error'; +import { UnsupportedSetupPatLinkError } from '../../../policies/setup_pat_creation_url_policy'; +import * as linkPolicy from '../../../policies/setup_pat_creation_url_policy'; +import { + PrepareSetupPatIntentUseCase, type PrepareSetupPatIntentPorts, type PrepareSetupPatIntentRequest, +} from '../prepare_setup_pat_intent_use_case'; + +const request: PrepareSetupPatIntentRequest = { + owner: 'owner', repository: 'repo', overrides: {}, skipRepositoryVariables: false, skipRepositorySecrets: false, +}; + +function harness() { + const ports: PrepareSetupPatIntentPorts = { + collect: jest.fn(async (initial: SetupQuestionnaireState) => ({ + ...initial, stateId: 'review' as const, question: undefined, + terminal: 'review' as const, answeredQuestionIds: ['features.issues'], + })), + chooseOwnerKind: jest.fn(async () => 'Organization' as const), + review: jest.fn(async () => 'continue' as const), + showPreview: jest.fn(), showDetails: jest.fn(), onManual: jest.fn(), + advanceToSetupPat: jest.fn(), revisitChoices: jest.fn(() => 2), + }; + return { ports, useCase: new PrepareSetupPatIntentUseCase(ports) }; +} + +describe('PrepareSetupPatIntentUseCase', () => { + test('guides a scoped PAT from the reviewed draft and deduplicates answered/fixed questions', async () => { + const { ports, useCase } = harness(); + const result = await useCase.execute({ ...request, overrides: { features: { issues: true } } }); + expect(result.kind).toBe('guided'); + if (result.kind !== 'guided') return; + expect(result.url).toContain('https://github.com/settings/personal-access-tokens/new?'); + expect(result.url).toContain('target_name=owner'); + expect(result.permissionIntent.answeredQuestionIds.filter(id => id === 'features.issues')).toHaveLength(1); + expect(ports.advanceToSetupPat).toHaveBeenCalledTimes(1); + expect(ports.showPreview).toHaveBeenCalledWith(expect.objectContaining({ pass: 1, requirements: result.requirements })); + }); + + test('review details is non-terminal and uses the same provisional grants', async () => { + const { ports, useCase } = harness(); + jest.spyOn(ports, 'review').mockResolvedValueOnce('details').mockResolvedValueOnce('continue'); + const result = await useCase.execute(request); + expect(result.kind).toBe('guided'); + expect(ports.review).toHaveBeenCalledTimes(2); + expect(ports.showDetails).toHaveBeenCalledTimes(1); + expect(ports.showDetails).toHaveBeenCalledWith(expect.arrayContaining([expect.objectContaining({ role: 'setup' })])); + }); + + test('revisiting choices re-collects with the next pass and retains one session', async () => { + const { ports, useCase } = harness(); + jest.spyOn(ports, 'review').mockResolvedValueOnce('revise').mockResolvedValueOnce('continue'); + const result = await useCase.execute(request); + expect(result.kind).toBe('guided'); + expect(ports.collect).toHaveBeenCalledTimes(2); + expect(ports.collect).toHaveBeenNthCalledWith(2, expect.any(Object), expect.any(Object), 2); + expect(ports.revisitChoices).toHaveBeenCalledTimes(1); + expect(ports.advanceToSetupPat).toHaveBeenCalledTimes(2); + }); + + test('manual choice avoids link creation and keeps baseline table available', async () => { + const { ports, useCase } = harness(); + jest.spyOn(ports, 'review').mockResolvedValue('manual'); + expect(await useCase.execute(request)).toEqual({ kind: 'manual' }); + expect(ports.onManual).toHaveBeenCalledWith('chosen'); + }); + + test('unknown owner kind falls back before a misleading organization link', async () => { + const { ports, useCase } = harness(); + jest.spyOn(ports, 'chooseOwnerKind').mockResolvedValue('unknown'); + expect(await useCase.execute(request)).toEqual({ kind: 'manual' }); + expect(ports.onManual).toHaveBeenCalledWith('owner-unknown'); + expect(ports.showPreview).not.toHaveBeenCalled(); + }); + + test('user owner conflicts with organization Projects and cannot continue', async () => { + const { ports, useCase } = harness(); + jest.spyOn(ports, 'chooseOwnerKind').mockResolvedValue('User'); + const collect = jest.spyOn(ports, 'collect'); + collect.mockImplementation(async initial => ({ + ...initial, terminal: 'review', question: undefined, + draft: { ...initial.draft, projects: { ...initial.draft.projects, ids: '42' } }, + })); + await expect(useCase.execute(request)).rejects.toThrow('Correct the reported setup intent'); + expect(ports.showPreview).toHaveBeenCalledWith(expect.objectContaining({ ownerConflict: true })); + }); + + test('invalid reviewed configuration blocks the guided link', async () => { + const { ports, useCase } = harness(); + jest.spyOn(ports, 'collect').mockImplementation(async initial => ({ + ...initial, terminal: 'review', question: undefined, + draft: { ...initial.draft, repository: { ...initial.draft.repository, mainBranch: '' } }, + })); + await expect(useCase.execute(request)).rejects.toThrow('Correct the reported setup intent'); + expect(ports.showPreview).toHaveBeenCalledWith(expect.objectContaining({ errors: expect.arrayContaining([expect.stringContaining('main branch')]) })); + }); + + test('unsupported GitHub form grant falls back to manual without broadening access', async () => { + const { ports, useCase } = harness(); + const link = jest.spyOn(linkPolicy, 'buildSetupPatCreationUrl').mockImplementation(() => { + throw new UnsupportedSetupPatLinkError(['repository Unsupported write']); + }); + try { + expect(await useCase.execute(request)).toEqual({ kind: 'manual' }); + expect(ports.onManual).toHaveBeenCalledWith('unsupported'); + } finally { + link.mockRestore(); + } + }); + + test('unexpected link errors propagate instead of silently using a fallback', async () => { + const { ports, useCase } = harness(); + const link = jest.spyOn(linkPolicy, 'buildSetupPatCreationUrl').mockImplementation(() => { throw new Error('unexpected'); }); + try { + await expect(useCase.execute(request)).rejects.toThrow('unexpected'); + expect(ports.onManual).not.toHaveBeenCalled(); + } finally { + link.mockRestore(); + } + }); + + test('questionnaire cancellation is a shared cancellation outcome', async () => { + const { ports, useCase } = harness(); + jest.spyOn(ports, 'collect').mockImplementation(async initial => ({ ...initial, terminal: 'cancelled' })); + await expect(useCase.execute(request)).rejects.toThrow(SetupInteractionCancelledError); + expect(ports.chooseOwnerKind).not.toHaveBeenCalled(); + }); +}); diff --git a/src/application/usecases/setup/__tests__/verify_setup_pat_bootstrap_use_case.test.ts b/src/application/usecases/setup/__tests__/verify_setup_pat_bootstrap_use_case.test.ts new file mode 100644 index 000000000..aec2397a0 --- /dev/null +++ b/src/application/usecases/setup/__tests__/verify_setup_pat_bootstrap_use_case.test.ts @@ -0,0 +1,72 @@ +import type { SetupTokenPermissionReport } from '../../../../domain/setup_token_permissions'; +import { buildSetupPatPermissionRequirements } from '../../../policies/setup_token_permission_policy'; +import { VerifySetupPatBootstrapUseCase, type VerifySetupPatBootstrapPorts } from '../verify_setup_pat_bootstrap_use_case'; + +const report: SetupTokenPermissionReport = { + role: 'setup', account: 'operator', identityStatus: 'valid', identityMessage: 'valid', checks: [], + ready: true, confirmationRequired: false, +}; +const request = { + owner: 'owner', repository: 'repo', token: 'test-token', requirements: buildSetupPatPermissionRequirements(), guided: true, +}; + +function harness() { + const ports: VerifySetupPatBootstrapPorts = { + permissions: { inspect: jest.fn(async () => report) }, + presenter: { showRequirements: jest.fn(), showReport: jest.fn() }, + confirmUnverifiable: jest.fn(async () => true), + confirmAccount: jest.fn(async () => true), + showCorrectedLink: jest.fn(), + }; + return { ports, useCase: new VerifySetupPatBootstrapUseCase(ports) }; +} + +describe('VerifySetupPatBootstrapUseCase', () => { + test('audits read-only and returns the authenticated operator account', async () => { + const { ports, useCase } = harness(); + expect(await useCase.execute(request)).toBe('operator'); + expect(ports.permissions.inspect).toHaveBeenCalledWith({ + role: 'setup', owner: 'owner', repository: 'repo', token: 'test-token', requirements: request.requirements, + }); + expect(ports.presenter.showReport).toHaveBeenCalledWith(report); + expect(ports.confirmAccount).toHaveBeenCalledWith('operator'); + expect(ports.confirmUnverifiable).not.toHaveBeenCalled(); + }); + + test('requires explicit confirmation for unverifiable write grants', async () => { + const { ports, useCase } = harness(); + jest.spyOn(ports.permissions, 'inspect').mockResolvedValue({ ...report, ready: false, confirmationRequired: true }); + expect(await useCase.execute(request)).toBe('operator'); + expect(ports.confirmUnverifiable).toHaveBeenCalledTimes(1); + }); + + test('declined unverifiable grants block before account confirmation', async () => { + const { ports, useCase } = harness(); + jest.spyOn(ports.permissions, 'inspect').mockResolvedValue({ ...report, ready: false, confirmationRequired: true }); + jest.spyOn(ports, 'confirmUnverifiable').mockResolvedValue(false); + await expect(useCase.execute(request)).rejects.toThrow('missing or unconfirmed required access'); + expect(ports.showCorrectedLink).toHaveBeenCalledWith(expect.stringContaining('target_name=owner')); + expect(ports.confirmAccount).not.toHaveBeenCalled(); + }); + + test.each(['invalid', 'unverifiable'] as const)('%s identity blocks regardless of a ready permission table', async identityStatus => { + const { ports, useCase } = harness(); + jest.spyOn(ports.permissions, 'inspect').mockResolvedValue({ ...report, identityStatus }); + await expect(useCase.execute(request)).rejects.toThrow('missing or unconfirmed required access'); + expect(ports.confirmAccount).not.toHaveBeenCalled(); + }); + + test('manual PAT failure does not imply a guided correction URL', async () => { + const { ports, useCase } = harness(); + jest.spyOn(ports.permissions, 'inspect').mockResolvedValue({ ...report, ready: false }); + await expect(useCase.execute({ ...request, guided: false })).rejects.toThrow('missing or unconfirmed required access'); + expect(ports.showCorrectedLink).not.toHaveBeenCalled(); + }); + + test('operator rejects an authenticated but unintended account', async () => { + const { ports, useCase } = harness(); + jest.spyOn(ports, 'confirmAccount').mockResolvedValue(false); + await expect(useCase.execute(request)).rejects.toThrow('unintended account'); + expect(ports.showCorrectedLink).not.toHaveBeenCalled(); + }); +}); diff --git a/src/application/usecases/setup/__tests__/verify_web_setup_apply_use_case.test.ts b/src/application/usecases/setup/__tests__/verify_web_setup_apply_use_case.test.ts new file mode 100644 index 000000000..444eb188f --- /dev/null +++ b/src/application/usecases/setup/__tests__/verify_web_setup_apply_use_case.test.ts @@ -0,0 +1,147 @@ +import { createDefaultSetupConfiguration } from '../../../policies/setup_configuration_defaults'; +import { SetupInteractionCancelledError } from '../../../errors/setup_interaction_cancelled_error'; +import type { SetupRemoteConfiguration } from '../../../../domain/setup'; +import { + VerifyWebSetupApplyUseCase, type VerifyWebSetupApplyPorts, type VerifyWebSetupApplyRequest, +} from '../verify_web_setup_apply_use_case'; + +const repository = { owner: 'owner', repository: 'repo', checkoutRoot: '/checkout', branch: 'develop', head: 'a'.repeat(40) }; +const remoteBase: SetupRemoteConfiguration = { + ownerType: 'User', repositoryId: 42, repositoryVisibility: 'private', + repositorySecrets: ['PAT', 'OPENAI_API_KEY'], repositorySecretsAccess: 'available', organizationSecrets: [], + repositoryVariables: [{ name: 'AGENT_PROVIDER', value: 'codex' }, { name: 'MAIN_BRANCH', value: 'main' }], + repositoryVariablesAccess: 'available', organizationVariables: [], + organizationAccess: 'not_applicable', organizationSecretsAccess: 'not_applicable', organizationVariablesAccess: 'not_applicable', +}; +const approvedRemote = { ...remoteBase, credentialHealthWorkflow: 'installed' as const }; +const request: VerifyWebSetupApplyRequest = { + repository, selectedFiles: ['.github/workflows/copilot.yml'], fileSnapshot: { '.github/workflows/copilot.yml': 'file:abc' }, + approvedRemote, configuration: createDefaultSetupConfiguration(), setupToken: 'test-token', +}; + +function harness() { + let session: 'active' | 'cancelled' | 'ended' = 'active'; + const ports: VerifyWebSetupApplyPorts = { + confirm: jest.fn(async () => 'apply' as const), + readRepositoryFacts: jest.fn(() => ({ ...repository })), + fileSnapshotMatches: jest.fn(() => true), + remote: { + inspect: jest.fn(async () => ({ ...remoteBase })), + inspectCredentialHealthWorkflow: jest.fn(async () => 'installed' as const), + }, + permissionAudit: { audit: jest.fn(async () => ({ status: 'accepted' as const })) }, + sessionState: () => session, + }; + return { ports, useCase: new VerifyWebSetupApplyUseCase(ports), setSession: (next: typeof session) => { session = next; } }; +} + +describe('VerifyWebSetupApplyUseCase', () => { + test('authorizes only after reconfirming local, remote, workflow and PAT facts', async () => { + const { ports, useCase } = harness(); + expect(await useCase.execute(request)).toBe('approved'); + expect(ports.fileSnapshotMatches).toHaveBeenCalledWith(repository.checkoutRoot, request.selectedFiles, request.fileSnapshot); + expect(ports.remote.inspect).toHaveBeenCalledWith('owner', 'repo', 'test-token'); + expect(ports.remote.inspectCredentialHealthWorkflow).toHaveBeenCalledWith('owner', 'repo', 'test-token', request.configuration.repository.mainBranch); + expect(ports.permissionAudit.audit).toHaveBeenCalledWith(request.configuration, approvedRemote); + }); + + test.each([ + ['stop', 'declined'], + [undefined, 'cancelled'], + ] as const)('does not inspect or mutate after approval response %s', async (answer, expected) => { + const { ports, useCase } = harness(); + jest.spyOn(ports, 'confirm').mockResolvedValue(answer); + expect(await useCase.execute(request)).toBe(expected); + expect(ports.readRepositoryFacts).not.toHaveBeenCalled(); + expect(ports.remote.inspect).not.toHaveBeenCalled(); + }); + + test.each([ + ['owner', 'different'], ['repository', 'different'], ['checkoutRoot', '/elsewhere'], + ['branch', 'main'], ['head', 'b'.repeat(40)], + ] as const)('fails closed when %s changed', async (field, value) => { + const { ports, useCase } = harness(); + jest.spyOn(ports, 'readRepositoryFacts').mockReturnValue({ ...repository, [field]: value }); + await expect(useCase.execute(request)).rejects.toThrow('repository identity changed'); + expect(ports.remote.inspect).not.toHaveBeenCalled(); + }); + + test('fails closed when repository facts disappear', async () => { + const { ports, useCase } = harness(); + jest.spyOn(ports, 'readRepositoryFacts').mockReturnValue(undefined); + await expect(useCase.execute(request)).rejects.toThrow('repository identity changed'); + }); + + test('fails closed on file drift before remote reads', async () => { + const { ports, useCase } = harness(); + jest.spyOn(ports, 'fileSnapshotMatches').mockReturnValue(false); + await expect(useCase.execute(request)).rejects.toThrow('Selected repository files changed'); + expect(ports.remote.inspect).not.toHaveBeenCalled(); + }); + + test('fails closed when GitHub facts changed', async () => { + const { ports, useCase } = harness(); + jest.spyOn(ports.remote, 'inspect').mockResolvedValue({ ...remoteBase, repositoryVisibility: 'public' }); + await expect(useCase.execute(request)).rejects.toThrow('GitHub repository facts changed'); + expect(ports.permissionAudit.audit).not.toHaveBeenCalled(); + }); + + test('ignores response key and resource ordering when GitHub facts are unchanged', async () => { + const { ports, useCase } = harness(); + jest.spyOn(ports.remote, 'inspect').mockResolvedValue({ + repositoryVariables: [...remoteBase.repositoryVariables].reverse(), + repositorySecrets: [...remoteBase.repositorySecrets].reverse(), + ownerType: remoteBase.ownerType, repositoryId: remoteBase.repositoryId, + organizationSecrets: remoteBase.organizationSecrets, organizationVariables: remoteBase.organizationVariables, + repositorySecretsAccess: remoteBase.repositorySecretsAccess, + repositoryVariablesAccess: remoteBase.repositoryVariablesAccess, + organizationAccess: remoteBase.organizationAccess, + organizationSecretsAccess: remoteBase.organizationSecretsAccess, + organizationVariablesAccess: remoteBase.organizationVariablesAccess, + repositoryVisibility: remoteBase.repositoryVisibility, + }); + expect(await useCase.execute(request)).toBe('approved'); + }); + + test('fails closed when a remote variable value changes', async () => { + const { ports, useCase } = harness(); + jest.spyOn(ports.remote, 'inspect').mockResolvedValue({ + ...remoteBase, repositoryVariables: [{ name: 'AGENT_PROVIDER', value: 'cursor' }, ...remoteBase.repositoryVariables.slice(1)], + }); + await expect(useCase.execute(request)).rejects.toThrow('GitHub repository facts changed'); + expect(ports.permissionAudit.audit).not.toHaveBeenCalled(); + }); + + test('unknown selected-ref workflow state cannot inherit earlier installed evidence', async () => { + const { ports, useCase } = harness(); + jest.spyOn(ports.remote, 'inspectCredentialHealthWorkflow').mockRejectedValue(new Error('read failed')); + await expect(useCase.execute(request)).rejects.toThrow('GitHub repository facts changed'); + expect(ports.permissionAudit.audit).not.toHaveBeenCalled(); + }); + + test('blocks when the final PAT audit loses a required grant', async () => { + const { ports, useCase } = harness(); + jest.spyOn(ports.permissionAudit, 'audit').mockResolvedValue({ status: 'blocked', errors: ['missing'] }); + await expect(useCase.execute(request)).rejects.toThrow('Setup PAT access changed'); + }); + + test.each(['cancelled', 'ended'] as const)('stops before facts are read when session is %s', async state => { + const { ports, useCase, setSession } = harness(); + setSession(state); + await expect(useCase.execute(request)).rejects.toThrow(state === 'cancelled' ? SetupInteractionCancelledError : 'session expired'); + expect(ports.readRepositoryFacts).not.toHaveBeenCalled(); + }); + + test('cancellation arriving during remote inspection wins before another read', async () => { + const { ports, useCase, setSession } = harness(); + jest.spyOn(ports.remote, 'inspect').mockImplementation(async () => { setSession('cancelled'); return remoteBase; }); + await expect(useCase.execute(request)).rejects.toThrow(SetupInteractionCancelledError); + expect(ports.remote.inspectCredentialHealthWorkflow).not.toHaveBeenCalled(); + }); + + test('expiry arriving during the final audit prevents approval', async () => { + const { ports, useCase, setSession } = harness(); + jest.spyOn(ports.permissionAudit, 'audit').mockImplementation(async () => { setSession('ended'); return { status: 'accepted' }; }); + await expect(useCase.execute(request)).rejects.toThrow('session expired'); + }); +}); diff --git a/src/application/usecases/setup/audit_configured_setup_pat_use_case.ts b/src/application/usecases/setup/audit_configured_setup_pat_use_case.ts new file mode 100644 index 000000000..e1e06d757 --- /dev/null +++ b/src/application/usecases/setup/audit_configured_setup_pat_use_case.ts @@ -0,0 +1,71 @@ +import type { SetupConfiguration, SetupRemoteConfiguration } from '../../../domain/setup'; +import type { SetupTokenPermissionReport, SetupTokenPermissionRequirement } from '../../../domain/setup_token_permissions'; +import { buildSetupPatCreationUrl } from '../../policies/setup_pat_creation_url_policy'; +import { buildConfiguredSetupPatPermissionRequirements, requiredSetupPatPermissionDelta } from '../../policies/setup_token_permission_policy'; +import type { SetupFinalPermissionAuditPort } from '../../ports/setup_wizard_ports'; +import type { SetupTokenPermissionAuditPort, SetupTokenPermissionPresenterPort } from '../../ports/setup_token_permission_ports'; + +export interface AuditConfiguredSetupPatContext { + readonly owner: string; + readonly repository: string; + readonly token?: string; + readonly provisionalRequirements: readonly SetupTokenPermissionRequirement[]; + readonly assertedOwnerKind?: 'Organization' | 'User'; + readonly guided: boolean; +} + +export interface AuditConfiguredSetupPatPorts { + readonly permissions: SetupTokenPermissionAuditPort; + readonly presenter: SetupTokenPermissionPresenterPort; + confirmUnverifiable(report: SetupTokenPermissionReport): Promise; + showOwnerMismatch(asserted: 'Organization' | 'User', actual: 'Organization' | 'User'): void; + showExcessGrants(grants: readonly string[]): void; + showUpdatedLink(url: string, addedGrants: readonly string[]): void; +} + +/** Rechecks the final plan without granting permission based on the browser preview. */ +export class AuditConfiguredSetupPatUseCase implements SetupFinalPermissionAuditPort { + constructor( + private readonly context: AuditConfiguredSetupPatContext, + private readonly ports: AuditConfiguredSetupPatPorts, + ) {} + + async audit( + configuration: Readonly, + remote?: Readonly, + ): Promise<{ status: 'accepted' } | { status: 'blocked'; errors: readonly string[] }> { + const required = buildConfiguredSetupPatPermissionRequirements(configuration, remote); + this.ports.presenter.showRequirements('setup', required); + if (this.context.assertedOwnerKind && remote && remote.ownerType !== 'Unknown' + && remote.ownerType !== this.context.assertedOwnerKind) { + this.ports.showOwnerMismatch(this.context.assertedOwnerKind, remote.ownerType); + this.showCorrectedLink(required); + return { status: 'blocked', errors: ['Repository owner type differs from the pre-PAT selection. Rerun setup with the correct owner type and PAT.'] }; + } + if (this.context.guided) { + const removed = requiredSetupPatPermissionDelta(required, this.context.provisionalRequirements); + if (removed.length) this.ports.showExcessGrants(removed); + } + if (!this.context.token) return { status: 'accepted' }; + const report = await this.ports.permissions.inspect({ + role: 'setup', owner: this.context.owner, repository: this.context.repository, + token: this.context.token, requirements: required, + }); + this.ports.presenter.showReport(report); + const accepted = report.ready || (report.confirmationRequired && await this.ports.confirmUnverifiable(report)); + if (!accepted || report.identityStatus !== 'valid') { + if (this.context.guided) this.showCorrectedLink(required); + return { status: 'blocked', errors: [ + 'The setup PAT has missing or unconfirmed access required by the approved setup plan. Grant or explicitly confirm the permissions shown above and retry.', + ] }; + } + return { status: 'accepted' }; + } + + private showCorrectedLink(required: readonly SetupTokenPermissionRequirement[]): void { + this.ports.showUpdatedLink(buildSetupPatCreationUrl({ + role: 'setup', owner: this.context.owner, repository: this.context.repository, + expiresIn: 1, requirements: required, + }), requiredSetupPatPermissionDelta(this.context.provisionalRequirements, required)); + } +} diff --git a/src/application/usecases/setup/prepare_setup_pat_intent_use_case.ts b/src/application/usecases/setup/prepare_setup_pat_intent_use_case.ts new file mode 100644 index 000000000..f4dc460ac --- /dev/null +++ b/src/application/usecases/setup/prepare_setup_pat_intent_use_case.ts @@ -0,0 +1,122 @@ +import type { SetupConfiguration } from '../../../domain/setup'; +import type { SetupQuestionnaireContext, SetupQuestionnaireState } from '../../../domain/setup_questionnaire'; +import type { SetupTokenPermissionRequirement } from '../../../domain/setup_token_permissions'; +import { ApplicationError } from '../../errors/application_error'; +import { SetupInteractionCancelledError } from '../../errors/setup_interaction_cancelled_error'; +import type { SetupConfigurationOverrides } from '../../policies/setup_configuration_policy'; +import { validateSetupConfiguration } from '../../policies/setup_configuration_policy'; +import { createSetupPermissionIntentQuestionnaire } from '../../policies/setup_questionnaire_policy'; +import { fixedSetupPatIntentQuestionIds, setupPatIntentNeedsOwnerKind, setupPatIntentOwnerConflict } from '../../policies/setup_pat_intent_policy'; +import { buildSetupPatIntentPermissionRequirements, buildSetupPatIntentUncertainty } from '../../policies/setup_token_permission_policy'; +import { buildSetupPatCreationUrl, UnsupportedSetupPatLinkError } from '../../policies/setup_pat_creation_url_policy'; +import { buildInitialSetupConfiguration } from './setup_wizard_use_case'; + +export interface PrepareSetupPatIntentRequest { + readonly owner: string; + readonly repository: string; + readonly overrides: SetupConfigurationOverrides; + readonly skipRepositoryVariables: boolean; + readonly skipRepositorySecrets: boolean; +} + +export interface SetupPatIntentPreview { + readonly draft: SetupConfiguration; + readonly requirements: readonly SetupTokenPermissionRequirement[]; + readonly uncertain: readonly string[]; + readonly ownerConflict: boolean; + readonly errors: readonly string[]; + readonly pass: number; +} + +export interface PrepareSetupPatIntentPorts { + collect(initial: SetupQuestionnaireState, context: SetupQuestionnaireContext, pass: number): Promise; + chooseOwnerKind(): Promise<'Organization' | 'User' | 'unknown'>; + review(): Promise<'continue' | 'revise' | 'manual' | 'details'>; + showPreview(preview: SetupPatIntentPreview): void; + showDetails(requirements: readonly SetupTokenPermissionRequirement[]): void; + onManual(reason: 'owner-unknown' | 'chosen' | 'unsupported'): void; + advanceToSetupPat(): void; + revisitChoices(): number; +} + +export type PrepareSetupPatIntentResult = + | { readonly kind: 'manual' } + | { + readonly kind: 'guided'; + readonly url: string; + readonly requirements: readonly SetupTokenPermissionRequirement[]; + readonly ownerKind: 'Organization' | 'User'; + readonly permissionIntent: { readonly draft: SetupConfiguration; readonly answeredQuestionIds: readonly string[] }; + }; + +/** Frontend-neutral preparation; GitHub still issues the PAT in its own UI. */ +export class PrepareSetupPatIntentUseCase { + constructor(private readonly ports: PrepareSetupPatIntentPorts) {} + + async execute(request: PrepareSetupPatIntentRequest): Promise { + const fixedQuestionIds = fixedSetupPatIntentQuestionIds( + request.overrides, request.skipRepositoryVariables, request.skipRepositorySecrets, + ); + let draft = buildInitialSetupConfiguration({ + mode: 'interactive', overrides: request.overrides, + skipRepositoryVariables: request.skipRepositoryVariables, + skipRepositorySecrets: request.skipRepositorySecrets, + }); + let pass = 1; + while (true) { + const context = { skipQuestionIds: fixedQuestionIds }; + const intent = await this.ports.collect(createSetupPermissionIntentQuestionnaire(draft, context), context, pass); + if (intent.terminal === 'cancelled') throw new SetupInteractionCancelledError(); + draft = intent.draft; + const ownerKind = setupPatIntentNeedsOwnerKind(draft) ? await this.ports.chooseOwnerKind() : 'User'; + if (ownerKind === 'unknown') { + this.ports.onManual('owner-unknown'); + return { kind: 'manual' }; + } + const ownerConflict = setupPatIntentOwnerConflict(draft, ownerKind); + const errors = validateSetupConfiguration(draft, { allowIncompleteApproval: true }); + const requirements = buildSetupPatIntentPermissionRequirements(draft, ownerKind); + this.ports.advanceToSetupPat(); + this.ports.showPreview({ + draft, requirements, uncertain: buildSetupPatIntentUncertainty(draft, ownerKind), + ownerConflict, errors, pass, + }); + + let decision: Awaited>; + do { + decision = await this.ports.review(); + if (decision === 'details') this.ports.showDetails(requirements); + } while (decision === 'details'); + if (decision === 'manual') { + this.ports.onManual('chosen'); + return { kind: 'manual' }; + } + if (decision === 'revise') { + pass = this.ports.revisitChoices(); + continue; + } + if (ownerConflict || errors.length > 0) { + throw new ApplicationError('configuration.invalid', 'Correct the reported setup intent or local --config/flags, then retry guided setup. No PAT was requested.'); + } + try { + return { + kind: 'guided', + url: buildSetupPatCreationUrl({ + role: 'setup', owner: request.owner, repository: request.repository, expiresIn: 1, + requirements, + }), + requirements, + ownerKind, + permissionIntent: { + draft, + answeredQuestionIds: [...new Set([...fixedQuestionIds, ...(intent.answeredQuestionIds ?? [])])], + }, + }; + } catch (error) { + if (!(error instanceof UnsupportedSetupPatLinkError)) throw error; + this.ports.onManual('unsupported'); + return { kind: 'manual' }; + } + } + } +} diff --git a/src/application/usecases/setup/verify_setup_pat_bootstrap_use_case.ts b/src/application/usecases/setup/verify_setup_pat_bootstrap_use_case.ts new file mode 100644 index 000000000..a2398ac46 --- /dev/null +++ b/src/application/usecases/setup/verify_setup_pat_bootstrap_use_case.ts @@ -0,0 +1,48 @@ +import type { SetupTokenPermissionReport, SetupTokenPermissionRequirement } from '../../../domain/setup_token_permissions'; +import { ApplicationError } from '../../errors/application_error'; +import { buildSetupPatCreationUrl } from '../../policies/setup_pat_creation_url_policy'; +import type { SetupTokenPermissionAuditPort, SetupTokenPermissionPresenterPort } from '../../ports/setup_token_permission_ports'; + +export interface VerifySetupPatBootstrapRequest { + readonly owner: string; + readonly repository: string; + readonly token: string; + readonly requirements: readonly SetupTokenPermissionRequirement[]; + readonly guided: boolean; +} + +export interface VerifySetupPatBootstrapPorts { + readonly permissions: SetupTokenPermissionAuditPort; + readonly presenter: SetupTokenPermissionPresenterPort; + confirmUnverifiable(report: SetupTokenPermissionReport): Promise; + confirmAccount(account?: string): Promise; + showCorrectedLink(url: string): void; +} + +/** Initial read-only gate shared by terminal and browser setup presentations. */ +export class VerifySetupPatBootstrapUseCase { + constructor(private readonly ports: VerifySetupPatBootstrapPorts) {} + + async execute(request: VerifySetupPatBootstrapRequest): Promise { + const report = await this.ports.permissions.inspect({ + role: 'setup', owner: request.owner, repository: request.repository, + token: request.token, requirements: request.requirements, + }); + this.ports.presenter.showReport(report); + const accepted = report.ready + || (report.confirmationRequired && await this.ports.confirmUnverifiable(report)); + if (!accepted || report.identityStatus !== 'valid') { + if (request.guided) this.ports.showCorrectedLink(buildSetupPatCreationUrl({ + role: 'setup', owner: request.owner, repository: request.repository, + expiresIn: 1, requirements: request.requirements, + })); + throw new ApplicationError('authorization.credential-invalid', + 'The setup PAT has missing or unconfirmed required access. Grant or explicitly confirm the permissions shown above and retry.'); + } + if (!await this.ports.confirmAccount(report.account)) { + throw new ApplicationError('authorization.credential-invalid', + 'The setup PAT belongs to an unintended account. Revoke it in GitHub and retry with the correct account.'); + } + return report.account; + } +} diff --git a/src/application/usecases/setup/verify_web_setup_apply_use_case.ts b/src/application/usecases/setup/verify_web_setup_apply_use_case.ts new file mode 100644 index 000000000..fec0ee744 --- /dev/null +++ b/src/application/usecases/setup/verify_web_setup_apply_use_case.ts @@ -0,0 +1,84 @@ +import type { SetupConfiguration, SetupRemoteConfiguration } from '../../../domain/setup'; +import { ApplicationError } from '../../errors/application_error'; +import { SetupInteractionCancelledError } from '../../errors/setup_interaction_cancelled_error'; +import { sameSetupRemoteFacts } from '../../policies/setup_remote_facts_policy'; +import type { SetupFinalPermissionAuditPort, SetupRemoteConfigurationReadPort } from '../../ports/setup_wizard_ports'; + +export interface WebSetupRepositoryFacts { + readonly owner: string; + readonly repository: string; + readonly checkoutRoot: string; + readonly branch: string; + readonly head: string; +} + +export interface VerifyWebSetupApplyRequest { + readonly repository: WebSetupRepositoryFacts; + readonly selectedFiles: readonly string[]; + readonly fileSnapshot: Readonly>; + readonly approvedRemote: Readonly; + readonly configuration: Readonly; + readonly setupToken: string; +} + +export interface VerifyWebSetupApplyPorts { + confirm(): Promise<'apply' | 'stop' | undefined>; + readRepositoryFacts(): WebSetupRepositoryFacts | undefined; + fileSnapshotMatches(root: string, files: readonly string[], snapshot: Readonly>): boolean; + remote: SetupRemoteConfigurationReadPort; + permissionAudit: SetupFinalPermissionAuditPort; + sessionState(): 'active' | 'cancelled' | 'ended'; +} + +/** Authorizes one web Apply against the facts the operator actually reviewed. */ +export class VerifyWebSetupApplyUseCase { + constructor(private readonly ports: VerifyWebSetupApplyPorts) {} + + async execute(request: VerifyWebSetupApplyRequest): Promise<'approved' | 'declined' | 'cancelled'> { + const decision = await this.ports.confirm(); + if (decision === undefined) return 'cancelled'; + if (decision === 'stop') return 'declined'; + this.assertActive(); + + const current = this.ports.readRepositoryFacts(); + const expected = request.repository; + if (!current || current.owner !== expected.owner || current.repository !== expected.repository + || current.checkoutRoot !== expected.checkoutRoot || current.branch !== expected.branch + || current.head !== expected.head) { + throw new ApplicationError('configuration.invalid', 'The repository identity changed during setup. No mutation started; restart and review a new plan.'); + } + if (!this.ports.fileSnapshotMatches(expected.checkoutRoot, request.selectedFiles, request.fileSnapshot)) { + throw new ApplicationError('configuration.invalid', 'Selected repository files changed since plan review. No mutation started; restart and review a new plan.'); + } + + let remote = await this.ports.remote.inspect(expected.owner, expected.repository, request.setupToken); + this.assertActive(); + let credentialHealthWorkflow: 'installed' | 'missing' | 'unavailable' = 'unavailable'; + try { + credentialHealthWorkflow = await this.ports.remote.inspectCredentialHealthWorkflow?.( + expected.owner, expected.repository, request.setupToken, request.configuration.repository.mainBranch, + ) ?? 'unavailable'; + } catch { /* Unknown selected-ref state must not inherit a provisional value. */ } + this.assertActive(); + remote = { ...remote, credentialHealthWorkflow }; + if (!sameSetupRemoteFacts(remote, request.approvedRemote)) { + throw new ApplicationError('configuration.invalid', 'GitHub repository facts changed since plan review. No mutation started; restart and review a new plan.'); + } + const audit = await this.ports.permissionAudit.audit(request.configuration, remote); + this.assertActive(); + if (audit.status === 'blocked') { + throw new ApplicationError('authorization.credential-invalid', 'Setup PAT access changed since plan review. No mutation started; correct the PAT and review a new plan.'); + } + return 'approved'; + } + + private assertActive(): void { + const state = this.ports.sessionState(); + if (state === 'cancelled') { + throw new SetupInteractionCancelledError(); + } + if (state === 'ended') { + throw new ApplicationError('configuration.invalid', 'The local setup session expired during final checks. No mutation started; start a new run and review a fresh plan.'); + } + } +} diff --git a/src/architecture/__tests__/web_setup_boundaries.test.ts b/src/architecture/__tests__/web_setup_boundaries.test.ts new file mode 100644 index 000000000..b1dbd39ce --- /dev/null +++ b/src/architecture/__tests__/web_setup_boundaries.test.ts @@ -0,0 +1,113 @@ +import { existsSync, readFileSync, readdirSync } from 'node:fs'; +import { dirname, join, resolve, sep } from 'node:path'; +import ts from 'typescript'; + +const root = resolve(__dirname, '..', '..', '..'); + +function sources(directory: string): string[] { + return readdirSync(directory, { withFileTypes: true }).flatMap(entry => { + const path = join(directory, entry.name); + return entry.isDirectory() ? sources(path) : entry.name.endsWith('.ts') && !entry.name.endsWith('.test.ts') ? [path] : []; + }); +} + +function browserSources(directory: string): string[] { + return readdirSync(directory, { withFileTypes: true }).flatMap(entry => { + const path = join(directory, entry.name); + return entry.isDirectory() ? browserSources(path) : /\.(svelte|ts)$/.test(entry.name) ? [path] : []; + }); +} + +function moduleImports(path: string): Array<{ specifier: string; typeOnly: boolean }> { + const raw = readFileSync(path, 'utf8'); + const source = path.endsWith('.svelte') + ? [...raw.matchAll(/]*>([\s\S]*?)<\/script>/g)].map(match => match[1]).join('\n') + : raw; + const ast = ts.createSourceFile(path, source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS); + return ast.statements.filter(ts.isImportDeclaration).map(node => ({ + specifier: (node.moduleSpecifier as ts.StringLiteral).text, + typeOnly: node.importClause?.isTypeOnly === true, + })); +} + +function localModule(from: string, specifier: string): string | undefined { + if (!specifier.startsWith('.')) return undefined; + const base = resolve(dirname(from), specifier); + return [base, `${base}.ts`, `${base}.tsx`, `${base}.js`, `${base}.svelte`, join(base, 'index.ts')] + .find(candidate => existsSync(candidate)); +} + +describe('local web setup architecture', () => { + test('browser imports only redacted application contracts, as types, across the actual dependency graph', () => { + const browser = join(root, 'web', 'src'); + const contract = join(root, 'src', 'application', 'contracts', 'web_setup_view'); + for (const path of browserSources(browser)) { + for (const imported of moduleImports(path)) { + const target = imported.specifier.startsWith('.') ? resolve(dirname(path), imported.specifier) : ''; + if (target.startsWith(`${join(root, 'src')}${sep}`)) { + expect(imported.typeOnly).toBe(true); + expect(target).toBe(contract); + } + if (path.includes(`${sep}components${sep}`) || path.includes(`${sep}lib${sep}`)) { + expect(target).not.toContain(`${sep}session${sep}`); + } + } + } + }); + + test('new setup application decisions have no transitive path to CLI, infrastructure, or browser adapters', () => { + const useCases = ['prepare_setup_pat_intent_use_case', 'verify_setup_pat_bootstrap_use_case', + 'audit_configured_setup_pat_use_case', 'verify_web_setup_apply_use_case'] + .map(name => join(root, 'src', 'application', 'usecases', 'setup', `${name}.ts`)); + const visited = new Set(); + const traverse = (path: string): void => { + if (visited.has(path)) return; + visited.add(path); + for (const imported of moduleImports(path)) { + const target = localModule(path, imported.specifier); + if (!target) continue; + for (const forbidden of ['cli', 'infrastructure', 'actions', 'web']) { + expect(target.startsWith(`${join(root, 'src', forbidden)}${sep}`) || target.startsWith(`${join(root, forbidden)}${sep}`)).toBe(false); + } + traverse(target); + } + }; + for (const entry of useCases) traverse(entry); + expect(visited.size).toBeGreaterThan(useCases.length); + }); + test('domain and application never import browser, HTTP server, or terminal adapters', () => { + for (const path of [...sources(join(root, 'src', 'domain')), ...sources(join(root, 'src', 'application'))]) { + const text = readFileSync(path, 'utf8'); + expect(text).not.toMatch(/from ['"](?:svelte|vite|node:http|node:child_process|.*(?:web_setup_server|web_setup_adapters|setup_terminal_driver|setup_question_renderer))['"]/); + } + }); + + test('browser presenters cannot acquire transport, provider, or persistent-secret responsibilities', () => { + const browser = join(root, 'web', 'src'); + for (const path of browserSources(browser)) { + const ui = readFileSync(path, 'utf8'); + expect(ui).not.toMatch(/setup_token_permission_policy|github_identity|runLocalAction|setup_credentials_use_case|localStorage|sessionStorage/); + if (!path.endsWith(join('session', 'setupSession.ts'))) { + expect(ui).not.toMatch(/\bfetch\s*\(|\/api\/|X-Setup-Capability/); + } + if (path.endsWith('.svelte')) { + expect(ui).not.toMatch(/from ['"](?:node:|.*(?:infrastructure|cli\/commands|cli\/web_setup_server))/); + } + } + expect(readFileSync(join(browser, 'App.svelte'), 'utf8')).toContain('createSetupSession'); + expect(readFileSync(join(browser, 'components', 'PromptCard.svelte'), 'utf8')).toContain('CredentialPrompt'); + }); + + test('page shell and presenters remain small and styles have one explicit entrypoint', () => { + const browser = join(root, 'web', 'src'); + expect(readFileSync(join(browser, 'App.svelte'), 'utf8').split('\n').length).toBeLessThanOrEqual(100); + expect(readFileSync(join(browser, 'session', 'setupSession.ts'), 'utf8').split('\n').length).toBeLessThanOrEqual(180); + for (const path of browserSources(join(browser, 'components'))) { + expect(readFileSync(path, 'utf8').split('\n').length).toBeLessThanOrEqual(90); + } + const css = readFileSync(join(browser, 'style.css'), 'utf8'); + for (const layer of ['tokens', 'foundation', 'layout', 'controls', 'feedback', 'responsive']) { + expect(css).toContain(`@import './styles/${layer}.css'`); + } + }); +}); diff --git a/src/cli/__tests__/setup_apply_snapshot.test.ts b/src/cli/__tests__/setup_apply_snapshot.test.ts new file mode 100644 index 000000000..26e815cef --- /dev/null +++ b/src/cli/__tests__/setup_apply_snapshot.test.ts @@ -0,0 +1,68 @@ +import { mkdtempSync, mkdirSync, rmSync, symlinkSync, writeFileSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; +import { captureSetupApplySnapshot, setupApplySnapshotMatches } from '../setup_apply_snapshot'; +import { createDefaultSetupConfiguration } from '../../application/policies/setup_configuration_policy'; +import { buildSetupPlan, setupPlanGuardPaths } from '../../application/policies/setup_configuration_plan'; + +describe('web setup apply snapshot', () => { + let root: string; + beforeEach(() => { root = mkdtempSync(join(tmpdir(), 'copilot-apply-snapshot-')); }); + afterEach(() => rmSync(root, { recursive: true, force: true })); + + test('detects creation and modification of selected files', () => { + const names = ['.github/workflows/copilot.yml']; + const first = captureSetupApplySnapshot(root, names); + expect(first[names[0]]).toBe('missing'); + mkdirSync(join(root, '.github', 'workflows'), { recursive: true }); + writeFileSync(join(root, names[0]), 'original'); + expect(setupApplySnapshotMatches(root, names, first)).toBe(false); + const second = captureSetupApplySnapshot(root, names); + expect(setupApplySnapshotMatches(root, names, second)).toBe(true); + writeFileSync(join(root, names[0]), 'changed'); + expect(setupApplySnapshotMatches(root, names, second)).toBe(false); + }); + + test('rejects traversal and symlinked paths', () => { + expect(() => captureSetupApplySnapshot(root, ['../outside'])).toThrow('outside'); + mkdirSync(join(root, '.github')); + symlinkSync(tmpdir(), join(root, '.github', 'workflows')); + expect(() => captureSetupApplySnapshot(root, ['.github/workflows/copilot.yml'])).toThrow('symbolic link'); + }); + + test('rejects absolute paths and files too large to snapshot safely', () => { + expect(() => captureSetupApplySnapshot(root, [join(root, 'absolute.yml')])).toThrow('outside'); + writeFileSync(join(root, 'large.yml'), 'x'.repeat(5 * 1024 * 1024 + 1)); + expect(() => captureSetupApplySnapshot(root, ['large.yml'])).toThrow('Cannot safely snapshot'); + }); + + test('normalizes duplicate selected paths and remains stable when nothing changed', () => { + writeFileSync(join(root, 'a.yml'), 'stable'); + const snapshot = captureSetupApplySnapshot(root, ['a.yml', 'a.yml']); + expect(Object.keys(snapshot)).toEqual(['a.yml']); + expect(setupApplySnapshotMatches(root, ['a.yml'], snapshot)).toBe(true); + }); + + test('guards checkout destinations, not the package-source labels shown in the plan', () => { + const paths = setupPlanGuardPaths(buildSetupPlan(createDefaultSetupConfiguration())); + expect(paths).toContain('.github/workflows/copilot_issue.yml'); + expect(paths).toContain('.github/ISSUE_TEMPLATE/feature_request.yml'); + expect(paths).toContain('.github/pull_request_template.md'); + expect(paths).toContain('AGENTS.md'); + expect(paths).not.toContain('workflows/copilot_issue.yml'); + expect(paths).not.toContain('AGENTS.md (managed pointer only)'); + const snapshot = captureSetupApplySnapshot(root, paths); + mkdirSync(join(root, '.github', 'workflows'), { recursive: true }); + writeFileSync(join(root, '.github', 'workflows', 'copilot_issue.yml'), 'edited after approval'); + expect(setupApplySnapshotMatches(root, paths, snapshot)).toBe(false); + }); + + test('also guards managed files that a configuration may retire', () => { + const plan = buildSetupPlan(createDefaultSetupConfiguration()); + const paths = setupPlanGuardPaths({ ...plan, selectedFiles: [] }); + expect(paths).toContain('.github/workflows/release_workflow.yml'); + expect(paths).toContain('.github/workflows/hotfix_workflow.yml'); + expect(paths).toContain('.github/ISSUE_TEMPLATE/release.yml'); + expect(paths).toContain('.github/ISSUE_TEMPLATE/config.yml'); + }); +}); diff --git a/src/cli/__tests__/setup_command_options.test.ts b/src/cli/__tests__/setup_command_options.test.ts new file mode 100644 index 000000000..1e078d361 --- /dev/null +++ b/src/cli/__tests__/setup_command_options.test.ts @@ -0,0 +1,98 @@ +import { collectApprovalCheck, collectScope, collectSecret, loadSetupOverrides } from '../setup_command_options'; +import { mergeSetupOverrides } from '../../application/policies/merge_setup_overrides_policy'; +import { ISSUE_WORKFLOW_KINDS } from '../../domain/issue_workflow_profile'; + +describe('setup command option adapter', () => { + test('collects opaque Secrets without logging or altering their values', () => { + expect(collectSecret('PAT=a=b', { EXISTING: 'keep' })).toEqual({ PAT: 'a=b', EXISTING: 'keep' }); + expect(collectApprovalCheck('CI|42|ci.yml', ['other'])).toEqual(['other', 'CI|42|ci.yml']); + }); + + test.each(['PAT', '=value', 'pat=value', 'PAT='])('rejects malformed Secret flag %s', value => { + expect(() => collectSecret(value, {})).toThrow('--secret'); + }); + + test('collects and normalizes per-resource scopes', () => { + expect(collectScope('PAT=ORGANIZATION', { EXISTING: 'repository' })).toEqual({ PAT: 'organization', EXISTING: 'repository' }); + }); + + test.each(['PAT', '=organization', 'pat=repository', 'PAT=elsewhere'])('rejects malformed scope override %s', value => { + expect(() => collectScope(value, {})).toThrow('Scope overrides'); + }); + + test('maps bounded features and issue workflows', () => { + const selected = loadSetupOverrides({ features: 'issues,pullRequests', issueWorkflows: 'feature,bugfix' }); + expect(selected.features).toMatchObject({ issues: true, pullRequests: true, release: false }); + expect(selected.issueWorkflows?.enabled).toEqual(['feature', 'bugfix']); + const all = loadSetupOverrides({ features: 'all', issueWorkflows: 'all' }); + expect(Object.values(all.features ?? {}).every(Boolean)).toBe(true); + expect(all.issueWorkflows?.enabled).toEqual(ISSUE_WORKFLOW_KINDS); + }); + + test.each([ + [{ features: 'unknown' }, 'Unknown setup feature'], + [{ issueWorkflows: 'unknown' }, 'Unknown issue workflow'], + [{ issueWorkflows: 'feature,feature' }, 'cannot contain duplicates'], + [{ agent: 'unknown' }, '--agent'], + [{ agentGuidance: 'unknown' }, '--agent-guidance'], + [{ prApprovalMode: 'unknown' }, '--pr-approval-mode'], + [{ variablesScope: 'unknown' }, '--variables-scope'], + [{ secretsScope: 'unknown' }, '--secrets-scope'], + [{ variablesVisibility: 'unknown' }, '--variables-visibility'], + [{ secretsVisibility: 'unknown' }, '--secrets-visibility'], + ] as const)('rejects invalid option %j', (options, message) => { + expect(() => loadSetupOverrides(options)).toThrow(message); + }); + + test('maps agent, guidance, approval and storage flags into typed overrides', () => { + const result = loadSetupOverrides({ + agent: 'cursor', agentGuidance: 'disabled', prApprovalMode: 'guarded', + prApprovalCheck: ['CI|42|ci.yml'], prApprovalCoverageCheck: 'coverage', prApprovalAttestProducer: true, + variablesScope: 'organization', variablesVisibility: 'private', variableScope: { AGENT_MODEL: 'repository' }, + secretsScope: 'organization', secretsVisibility: 'selected', secretScope: { PAT: 'repository' }, + }); + expect(result.agents?.planner?.provider).toBe('cursor'); + expect(result.repositoryAgentGuidance).toEqual({ agentsPointer: 'disabled', enabled: false }); + expect(result.pullRequestApproval).toMatchObject({ + mode: 'guarded', producerAttested: true, testChecks: [{ name: 'CI', sourceAppId: 42, workflowName: 'ci.yml' }], + coverage: { mode: 'check', checkName: 'coverage' }, + }); + expect(result.storage).toMatchObject({ + variables: { defaultScope: 'organization', organizationVisibility: 'private', overrides: { AGENT_MODEL: 'repository' } }, + secrets: { defaultScope: 'organization', organizationVisibility: 'selected', overrides: { PAT: 'repository' } }, + }); + }); +}); + +describe('setup override merge policy', () => { + test('flag fields win while unrelated file-only fields survive at every nested boundary', () => { + const merged = mergeSetupOverrides({ + features: { issues: true }, agents: { planner: { provider: 'codex' } }, + repository: { mainBranch: 'master' }, ai: { bugbotSeverity: 'info' }, + pullRequestApproval: { mode: 'recommend', coverage: { mode: 'check', checkName: 'base' } }, + projects: { ids: '1' }, issueWorkflows: { enabled: ['feature'] }, + repositoryAgentGuidance: { enabled: true }, + storage: { secrets: { defaultScope: 'organization', overrides: { PAT: 'repository' } }, + variables: { defaultScope: 'repository', overrides: { OLD: 'organization' } } }, + }, { + features: { pullRequests: false }, agents: { fixer: { provider: 'cursor' } }, + repository: { developmentBranch: 'develop' }, ai: { bugbotEffort: 'high' }, + pullRequestApproval: { mode: 'guarded', coverage: { mode: 'check', checkName: 'flag' } }, + projects: { issueCreatedColumn: 'Todo' }, issueWorkflows: { enabled: ['bugfix'] }, + repositoryAgentGuidance: { agentsPointer: 'disabled' }, + storage: { secrets: { overrides: { BOT: 'organization' } }, variables: { overrides: { NEW: 'repository' } } }, + }); + expect(merged.features).toMatchObject({ issues: true, pullRequests: false }); + expect(merged.agents).toMatchObject({ planner: { provider: 'codex' }, fixer: { provider: 'cursor' } }); + expect(merged.repository).toMatchObject({ mainBranch: 'master', developmentBranch: 'develop' }); + expect(merged.ai).toMatchObject({ bugbotSeverity: 'info', bugbotEffort: 'high' }); + expect(merged.pullRequestApproval).toMatchObject({ mode: 'guarded', coverage: { mode: 'check', checkName: 'flag' } }); + expect(merged.projects).toMatchObject({ ids: '1', issueCreatedColumn: 'Todo' }); + expect(merged.issueWorkflows?.enabled).toEqual(['bugfix']); + expect(merged.repositoryAgentGuidance).toMatchObject({ enabled: true, agentsPointer: 'disabled' }); + expect(merged.storage).toMatchObject({ + secrets: { defaultScope: 'organization', overrides: { PAT: 'repository', BOT: 'organization' } }, + variables: { defaultScope: 'repository', overrides: { OLD: 'organization', NEW: 'repository' } }, + }); + }); +}); diff --git a/src/cli/__tests__/setup_session_guard.test.ts b/src/cli/__tests__/setup_session_guard.test.ts new file mode 100644 index 000000000..c7e57d8c6 --- /dev/null +++ b/src/cli/__tests__/setup_session_guard.test.ts @@ -0,0 +1,61 @@ +import { execFileSync } from 'node:child_process'; +import { createHash } from 'node:crypto'; +import { existsSync, mkdtempSync, mkdirSync, readFileSync, realpathSync, rmSync, unlinkSync, writeFileSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; +import { acquireSetupSessionGuard } from '../setup_session_guard'; + +describe('setup session guard', () => { + let root: string; + beforeEach(() => { + root = mkdtempSync(join(tmpdir(), 'copilot-setup-guard-')); + execFileSync('git', ['init', '-q', root]); + mkdirSync(join(root, 'nested')); + }); + const lockPath = () => join(tmpdir(), `copilot-setup-${createHash('sha256').update(realpathSync(root)).digest('hex').slice(0, 32)}.lock`); + afterEach(() => { + const lock = lockPath(); + if (existsSync(lock)) unlinkSync(lock); + rmSync(root, { recursive: true, force: true }); + }); + + test('serializes setup across directories of the same checkout and releases only its own lock', () => { + const release = acquireSetupSessionGuard(root); + expect(() => acquireSetupSessionGuard(join(root, 'nested'))).toThrow('Another setup process'); + release(); + const releaseNext = acquireSetupSessionGuard(join(root, 'nested')); + release(); // An old release callback must not remove a new owner's lock. + expect(() => acquireSetupSessionGuard(root)).toThrow('Another setup process'); + releaseNext(); + const releaseThird = acquireSetupSessionGuard(root); + releaseThird(); + }); + + test('recovers a verified dead owner without reusing its nonce', () => { + writeFileSync(lockPath(), JSON.stringify({ pid: 99999999, nonce: 'old-owner', repository: realpathSync(root) })); + const release = acquireSetupSessionGuard(root); + const current = JSON.parse(readFileSync(lockPath(), 'utf8')) as { pid: number; nonce: string }; + expect(current.pid).toBe(process.pid); + expect(current.nonce).not.toBe('old-owner'); + release(); + expect(existsSync(lockPath())).toBe(false); + }); + + test.each([ + ['not-json'], + [JSON.stringify({ pid: -1, nonce: 'bad', repository: 'wrong' })], + ])('fails closed for an unverifiable lock %s', content => { + writeFileSync(lockPath(), content); + expect(() => acquireSetupSessionGuard(root)).toThrow('lock'); + expect(readFileSync(lockPath(), 'utf8')).toBe(content); + }); + + test('propagates a filesystem error instead of treating it as a competing session', () => { + const open = jest.spyOn(require('node:fs'), 'openSync').mockImplementationOnce(() => { throw Object.assign(new Error('Permission denied'), { code: 'EACCES' }); }); + try { + expect(() => acquireSetupSessionGuard(root)).toThrow('Permission denied'); + } finally { + open.mockRestore(); + } + }); +}); diff --git a/src/cli/__tests__/web_setup_adapters.test.ts b/src/cli/__tests__/web_setup_adapters.test.ts new file mode 100644 index 000000000..223bd06a2 --- /dev/null +++ b/src/cli/__tests__/web_setup_adapters.test.ts @@ -0,0 +1,366 @@ +import { WebSetupBridge } from '../web_setup_bridge'; +import { WebSetupCredentialPrompt, WebSetupJourneyPresenter, WebSetupPermissionPresenter, WebSetupPlanConfirmation, WebSetupPlanPresenter, WebSetupQuestionnaireCollector, WebSetupWorkflowUpdatePrompt } from '../web_setup_adapters'; +import { buildInitialSetupConfiguration } from '../../application/usecases/setup/setup_wizard_use_case'; +import { createSetupPermissionIntentQuestionnaire } from '../../application/policies/setup_questionnaire_policy'; +import type { SetupPlan } from '../../domain/setup'; +import type { SetupTokenPermissionReport } from '../../domain/setup_token_permissions'; +import type { SetupCredentialCheck } from '../../domain/setup'; + +const next = () => new Promise(resolve => setImmediate(resolve)); + +function answer(bridge: WebSetupBridge, value: string): void { + const revision = bridge.snapshot().promptRevision; + expect(revision).toBeDefined(); + expect(bridge.answer(revision!, value)).toBe(true); +} + +const emptyPlan = (): SetupPlan => ({ + configuration: buildInitialSetupConfiguration({ mode: 'interactive' }), + workflowFiles: ['copilot.yml'], issueTemplateFiles: [], selectedFiles: ['.github/workflows/copilot.yml'], + variables: [{ name: 'AGENT_PROVIDER', value: 'codex' }], requiredSecrets: ['PAT'], credentialRequirements: [], + mergeQueueReadiness: [], approvalReadiness: [], warnings: ['Review changes'], +}); + +describe('semantic web setup adapters', () => { + test('collects policy-owned intent questions without terminal prompt parsing', async () => { + const bridge = new WebSetupBridge('owner/repo'); + const collector = new WebSetupQuestionnaireCollector(bridge); + const initial = createSetupPermissionIntentQuestionnaire(buildInitialSetupConfiguration({ mode: 'interactive' })); + const result = collector.collect(initial, {}); + const seen: string[] = []; + for (let index = 0; index < 30; index += 1) { + const prompt = bridge.snapshot().prompt; + if (!prompt) break; + expect(prompt.kind).toBe('question'); + if (prompt.kind === 'question') seen.push(prompt.question.id); + answer(bridge, ''); + await next(); + } + const state = await result; + expect(state.terminal).toBe('review'); + expect(seen).toContain('features.issues'); + expect(state.answeredQuestionIds).toEqual(seen); + expect(JSON.stringify(bridge.snapshot())).not.toContain('github_pat_'); + }); + + test('invalid answer stays on the same policy question and exposes validation', async () => { + const bridge = new WebSetupBridge('owner/repo'); + const collector = new WebSetupQuestionnaireCollector(bridge); + const result = collector.collect(createSetupPermissionIntentQuestionnaire(buildInitialSetupConfiguration({ mode: 'interactive' })), {}); + const firstPrompt = bridge.snapshot().prompt; + const initialId = firstPrompt?.kind === 'question' ? firstPrompt.question.id : ''; + answer(bridge, 'not-yes-or-no'); + await next(); + expect(bridge.snapshot().prompt?.kind).toBe('question'); + const retryPrompt = bridge.snapshot().prompt; + expect(retryPrompt?.kind === 'question' && retryPrompt.question.id).toBe(initialId); + expect(bridge.snapshot().message?.text).toContain('Enter yes or no'); + bridge.cancel(); + expect((await result).terminal).toBe('cancelled'); + }); + + test.each([['approve', 'approved'], ['decline', 'declined']])('plan %s maps to %s', async (reply, expected) => { + const bridge = new WebSetupBridge('owner/repo'); + const confirmation = new WebSetupPlanConfirmation(bridge); + const pending = confirmation.confirm(emptyPlan()); + expect(bridge.snapshot().prompt?.kind).toBe('plan'); + const planPrompt = bridge.snapshot().prompt; + if (planPrompt?.kind === 'plan') expect(planPrompt.plan.secrets).toEqual(['PAT']); + answer(bridge, reply); + expect((await pending).kind).toBe(expected); + }); + + test('guided bot token uses distinct GitHub link, numeric identity, and masked handoff', async () => { + const bridge = new WebSetupBridge('owner/repo'); + const prompt = new WebSetupCredentialPrompt(bridge); + const resolve = jest.fn().mockResolvedValue({ login: 'bot-user', id: 42 }); + prompt.configureWorkflowPatGuide('https://github.com/settings/personal-access-tokens/new?name=bot', resolve); + const pending = prompt.requestWorkflowPat({ name: 'PAT', kind: 'workflowPat', description: 'runtime' }); + answer(bridge, 'Guided GitHub link'); + await next(); + expect(bridge.snapshot().prompt?.kind).toBe('text'); + answer(bridge, 'bot-user'); + await next(); + const secretPrompt = bridge.snapshot().prompt; + expect(secretPrompt?.kind).toBe('secret'); + if (secretPrompt?.kind === 'secret') { + expect(secretPrompt.link).toContain('github.com/settings/personal-access-tokens/new'); + expect(secretPrompt.description).toContain('GitHub ID 42'); + } + answer(bridge, 'github_pat_fake_bot_value'); + expect((await pending)?.value).toBe('github_pat_fake_bot_value'); + expect(prompt.guidedWorkflowBotIdentity?.id).toBe(42); + expect(resolve).toHaveBeenCalledWith('bot-user'); + expect(JSON.stringify(bridge.snapshot())).not.toContain('github_pat_fake_bot_value'); + }); + + test('setup PAT guidance and verified account confirmation remain role-specific', async () => { + const bridge = new WebSetupBridge('owner/repo'); + const prompt = new WebSetupCredentialPrompt(bridge); + const method = prompt.chooseSetupPatMethod(); + answer(bridge, 'Guided GitHub link'); + expect(await method).toBe('guided'); + prompt.configureSetupPatGuide('https://github.com/settings/personal-access-tokens/new?name=setup'); + const token = prompt.requestSetupPat(); + expect(bridge.snapshot().prompt?.kind).toBe('secret'); + answer(bridge, 'github_pat_fake_setup_value'); + expect(await token).toBe('github_pat_fake_setup_value'); + const account = prompt.confirmGuidedSetupAccount('operator'); + expect(bridge.snapshot().prompt?.title).toContain('@operator'); + answer(bridge, 'No, stop'); + expect(await account).toBe(false); + expect(JSON.stringify(bridge.snapshot())).not.toContain('github_pat_fake_setup_value'); + }); + + test('unverifiable required writes need a specific acknowledgement', async () => { + const bridge = new WebSetupBridge('owner/repo'); + const prompt = new WebSetupCredentialPrompt(bridge); + const report: SetupTokenPermissionReport = { + role: 'setup', identityStatus: 'valid', identityMessage: 'checked', ready: false, confirmationRequired: true, + checks: [{ id: 'secrets', role: 'setup', scope: 'repository', permission: 'Secrets', level: 'write', + applicability: 'required', reason: 'Provision', probe: 'secrets', status: 'unverifiable', message: 'No safe write probe' }], + }; + const pending = prompt.confirmUnverifiableTokenPermissions(report); + answer(bridge, 'Yes, I checked them'); + expect(await pending).toBe(true); + const presenter = new WebSetupPermissionPresenter(bridge); + presenter.showReport(report); + expect(bridge.snapshot().permissions?.report?.checks[0].status).toBe('unverifiable'); + }); + + test('workflow update decision is explicit and never inferred from a changed file', async () => { + const bridge = new WebSetupBridge('owner/repo'); + const prompt = new WebSetupWorkflowUpdatePrompt(bridge); + const pending = prompt.confirmWorkflowUpdates([{ file: 'copilot.yml', destination: '.github/workflows/copilot.yml', status: 'changed' }], false); + expect(bridge.snapshot().prompt?.title).toContain('Update existing workflows'); + answer(bridge, 'Keep existing'); + expect(await pending).toBe(false); + }); + + test.each([['Organization', 'Organization'], ['Personal account', 'User'], ['Not sure', 'unknown']])('owner answer %s resolves to %s', async (reply, expected) => { + const bridge = new WebSetupBridge('owner/repo'); + const prompt = new WebSetupCredentialPrompt(bridge); + const pending = prompt.chooseSetupOwnerKind(); + answer(bridge, reply); + expect(await pending).toBe(expected); + }); + + test.each([ + ['Continue to GitHub', 'continue'], ['Review setup choices again', 'revise'], + ['View full permission table', 'details'], ['Enter a PAT manually', 'manual'], + ])('intent review %s resolves to %s', async (reply, expected) => { + const bridge = new WebSetupBridge('owner/repo'); + const prompt = new WebSetupCredentialPrompt(bridge); + const pending = prompt.reviewSetupPatIntent(); + answer(bridge, reply); + expect(await pending).toBe(expected); + }); + + test('manual setup PAT does not claim guided account verification or cleanup', async () => { + const bridge = new WebSetupBridge('owner/repo'); + const prompt = new WebSetupCredentialPrompt(bridge); + const pending = prompt.chooseSetupPatMethod(); + answer(bridge, 'Manual PAT'); + expect(await pending).toBe('manual'); + expect(prompt.usedGuidedSetupPat).toBe(false); + expect(await prompt.confirmGuidedSetupAccount()).toBe(true); + prompt.showSetupPatCleanupReminder(); + expect(bridge.snapshot().message).toBeUndefined(); + }); + + test('guided cleanup and corrected link are visible without token values', async () => { + const bridge = new WebSetupBridge('owner/repo'); + const prompt = new WebSetupCredentialPrompt(bridge); + const pending = prompt.chooseSetupPatMethod(); + answer(bridge, 'Guided GitHub link'); + await pending; + prompt.configureSetupPatGuide('https://github.com/settings/personal-access-tokens/new?name=setup'); + prompt.showUpdatedSetupPatLink('https://github.com/settings/personal-access-tokens/new?name=updated', 'final', ['Secrets write']); + expect(bridge.snapshot().message?.link).toContain('name=updated'); + expect(bridge.snapshot().message?.text).toContain('Secrets write'); + prompt.showSetupPatCleanupReminder(); + expect(bridge.snapshot().message?.link).toBe('https://github.com/settings/personal-access-tokens'); + prompt.useManualSetupPat(); + expect(prompt.usedGuidedSetupPat).toBe(false); + }); + + test.each([['Keep existing', false], ['Update setup-managed workflows', true]])('workflow answer %s maps to %s', async (reply, expected) => { + const bridge = new WebSetupBridge('owner/repo'); + const prompt = new WebSetupWorkflowUpdatePrompt(bridge); + const comparisons = [{ file: 'copilot.yml', destination: '.github/workflows/copilot.yml', status: 'unmanaged' as const }]; + const pending = prompt.confirmWorkflowUpdates(comparisons, false); + answer(bridge, reply); + expect(await pending).toBe(expected); + }); + + test('workflow update leaves unchanged files alone and honors an explicit flag', async () => { + const bridge = new WebSetupBridge('owner/repo'); + const prompt = new WebSetupWorkflowUpdatePrompt(bridge); + const comparison = { file: 'copilot.yml', destination: '.github/workflows/copilot.yml', status: 'changed' as const }; + expect(await prompt.confirmWorkflowUpdates([{ ...comparison, status: 'unchanged' }], false)).toBe(false); + expect(await prompt.confirmWorkflowUpdates([comparison], true)).toBe(true); + expect(bridge.snapshot().prompt).toBeUndefined(); + }); + + test('manual bot PAT falls back to permission table without claiming ID binding', async () => { + const bridge = new WebSetupBridge('owner/repo'); + const prompt = new WebSetupCredentialPrompt(bridge); + prompt.configureWorkflowPatGuide('https://github.com/settings/personal-access-tokens/new?name=bot', async () => ({ login: 'bot', id: 1 }), []); + const pending = prompt.requestWorkflowPat({ name: 'PAT', kind: 'workflowPat', description: 'runtime' }); + answer(bridge, 'Manual PAT'); + await next(); + expect(bridge.snapshot().prompt?.kind).toBe('secret'); + expect(bridge.snapshot().permissions?.role).toBe('workflow'); + answer(bridge, 'manual_fake_pat'); + expect((await pending)?.value).toBe('manual_fake_pat'); + expect(prompt.guidedWorkflowBotIdentity).toBeUndefined(); + }); + + test('API key and existing credential decisions stay in separate secret/choice prompts', async () => { + const bridge = new WebSetupBridge('owner/repo'); + const prompt = new WebSetupCredentialPrompt(bridge); + const requirement = { name: 'OPENAI_API_KEY', kind: 'apiKey' as const, description: 'AI', provider: 'OpenAI', alternativeGroups: ['agent'] }; + const check: SetupCredentialCheck = { name: requirement.name, status: 'unverifiable', message: 'Value cannot be read.' }; + const decision = prompt.chooseExistingCredential(requirement, check); + expect(bridge.snapshot().prompt?.kind).toBe('choice'); + answer(bridge, 'replace'); + expect(await decision).toBe('replace'); + const value = prompt.requestApiKey(requirement, check); + expect(bridge.snapshot().prompt?.kind).toBe('secret'); + if (bridge.snapshot().prompt?.kind === 'secret') expect(bridge.snapshot().prompt).toMatchObject({ optional: true }); + answer(bridge, 'fake_api_key'); + expect((await value)?.value).toBe('fake_api_key'); + expect(JSON.stringify(bridge.snapshot())).not.toContain('fake_api_key'); + }); + + test('presentation adapters publish redacted plan, journey and permission facts', () => { + const bridge = new WebSetupBridge('owner/repo'); + new WebSetupPlanPresenter(bridge).present(emptyPlan()); + expect(bridge.snapshot().message?.text).toContain('1 Secret names'); + new WebSetupPermissionPresenter(bridge).showRequirements('setup', []); + expect(bridge.snapshot().permissions?.role).toBe('setup'); + new WebSetupPermissionPresenter(bridge).showDetailedRequirements('workflow', []); + expect(bridge.snapshot().permissions?.role).toBe('workflow'); + new WebSetupJourneyPresenter(bridge).present({ repository: 'owner/repo', position: 2, total: 6, + current: 'Setup choices', complete: ['Repository'], pending: ['Setup PAT'], mutationStarted: false, choiceReviewPass: 2 }); + expect(bridge.snapshot().journey?.choiceReviewPass).toBe(2); + }); + + test('a cancelled plan and cancelled workflow decision do not approve anything', async () => { + const planBridge = new WebSetupBridge('owner/repo'); + const plan = new WebSetupPlanConfirmation(planBridge).confirm(emptyPlan()); + planBridge.cancel(); + expect((await plan).kind).toBe('cancelled'); + const workflowBridge = new WebSetupBridge('owner/repo'); + const prompt = new WebSetupWorkflowUpdatePrompt(workflowBridge); + const workflow = prompt.confirmWorkflowUpdates([{ file: 'a', destination: 'a', status: 'changed' }], false); + workflowBridge.cancel(); + await expect(workflow).rejects.toThrow('cancelled'); + }); + + test('invalid browser choice cannot consume a prompt and cancellation never supplies a PAT', async () => { + const bridge = new WebSetupBridge('owner/repo'); + const prompt = new WebSetupCredentialPrompt(bridge); + const method = prompt.chooseSetupPatMethod(); + const revision = bridge.snapshot().promptRevision!; + expect(bridge.answer(revision, 'not-an-option')).toBe(false); + expect(bridge.snapshot().promptRevision).toBe(revision); + answer(bridge, 'Manual PAT'); + expect(await method).toBe('manual'); + const token = prompt.requestSetupPat(); + bridge.cancel(); + await expect(token).rejects.toThrow('cancelled'); + }); + + test('guided setup account requires a reported identity and a positive operator decision', async () => { + const bridge = new WebSetupBridge('owner/repo'); + const prompt = new WebSetupCredentialPrompt(bridge); + const method = prompt.chooseSetupPatMethod(); + answer(bridge, 'Guided GitHub link'); + await method; + expect(await prompt.confirmGuidedSetupAccount()).toBe(false); + const confirmed = prompt.confirmGuidedSetupAccount('operator'); + answer(bridge, 'Yes, continue'); + expect(await confirmed).toBe(true); + }); + + test('unverifiable writes without a required confirmation do not prompt', async () => { + const bridge = new WebSetupBridge('owner/repo'); + const prompt = new WebSetupCredentialPrompt(bridge); + const report: SetupTokenPermissionReport = { role: 'setup', identityStatus: 'valid', identityMessage: 'checked', + ready: true, confirmationRequired: false, checks: [] }; + expect(await prompt.confirmUnverifiableTokenPermissions(report)).toBe(false); + expect(bridge.snapshot().prompt).toBeUndefined(); + const noWrite: SetupTokenPermissionReport = { ...report, ready: false, confirmationRequired: true }; + expect(await prompt.confirmUnverifiableTokenPermissions(noWrite)).toBe(false); + }); + + test('invalid guided bot login blocks before a PAT is requested', async () => { + const bridge = new WebSetupBridge('owner/repo'); + const prompt = new WebSetupCredentialPrompt(bridge); + const resolve = jest.fn(); + prompt.configureWorkflowPatGuide('https://github.com/settings/personal-access-tokens/new', resolve); + const pending = prompt.requestWorkflowPat({ name: 'PAT', kind: 'workflowPat', description: 'runtime' }); + answer(bridge, 'Guided GitHub link'); + await next(); + answer(bridge, 'invalid/login'); + await expect(pending).rejects.toThrow('valid GitHub bot login'); + expect(resolve).not.toHaveBeenCalled(); + expect(bridge.snapshot().prompt).toBeUndefined(); + }); + + test('credential explanation and checks distinguish invalid from verified evidence', () => { + const bridge = new WebSetupBridge('owner/repo'); + const prompt = new WebSetupCredentialPrompt(bridge); + prompt.explainCredentialSeparation([{ name: 'PAT', kind: 'workflowPat', description: 'runtime' }]); + expect(bridge.snapshot().message?.text).toContain('separate from your setup PAT'); + prompt.showCredentialChecks([{ name: 'PAT', status: 'invalid', message: 'Wrong account' }]); + expect(bridge.snapshot().message?.tone).toBe('warning'); + prompt.showCredentialChecks([{ name: 'PAT', status: 'valid', message: 'Checked' }]); + expect(bridge.snapshot().message?.tone).toBe('success'); + }); + + test('cancelled bot-login and optional API-key inputs never produce credentials', async () => { + const botBridge = new WebSetupBridge('owner/repo'); + const botPrompt = new WebSetupCredentialPrompt(botBridge); + botPrompt.configureWorkflowPatGuide('https://github.com/settings/personal-access-tokens/new', async () => ({ login: 'bot', id: 1 })); + const bot = botPrompt.requestWorkflowPat({ name: 'PAT', kind: 'workflowPat', description: 'runtime' }); + answer(botBridge, 'Guided GitHub link'); + await next(); + botBridge.cancel(); + await expect(bot).rejects.toThrow('cancelled'); + + const keyBridge = new WebSetupBridge('owner/repo'); + const keyPrompt = new WebSetupCredentialPrompt(keyBridge); + const key = keyPrompt.requestApiKey({ name: 'OPENAI_API_KEY', kind: 'apiKey', description: 'AI', alternativeGroups: ['agent'] }); + answer(keyBridge, ''); + expect(await key).toBeUndefined(); + }); + + test('manual bot entry without a prepared link shows existing status but no numeric identity claim', async () => { + const bridge = new WebSetupBridge('owner/repo'); + const prompt = new WebSetupCredentialPrompt(bridge); + const pending = prompt.requestWorkflowPat({ name: 'PAT', kind: 'workflowPat', description: 'runtime' }, + { name: 'PAT', status: 'unverifiable', message: 'Existing value unreadable' }); + expect(bridge.snapshot().prompt?.kind).toBe('secret'); + const secret = bridge.snapshot().prompt; + if (secret?.kind === 'secret') { + expect(secret.description).toContain('Existing Secret: unverifiable'); + expect(secret.link).toBeUndefined(); + } + answer(bridge, ''); + expect(await pending).toBeUndefined(); + expect(prompt.guidedWorkflowBotIdentity).toBeUndefined(); + }); + + test('a cancelled choice and a corrected bootstrap link do not grant access', async () => { + const bridge = new WebSetupBridge('owner/repo'); + const prompt = new WebSetupCredentialPrompt(bridge); + const method = prompt.chooseSetupPatMethod(); + bridge.cancel(); + await expect(method).rejects.toThrow('cancelled'); + const another = new WebSetupBridge('owner/repo'); + new WebSetupCredentialPrompt(another).showUpdatedSetupPatLink('https://github.com/settings/personal-access-tokens/new', 'bootstrap'); + expect(another.snapshot().message?.text).toContain('access failed'); + }); +}); diff --git a/src/cli/__tests__/web_setup_bridge.test.ts b/src/cli/__tests__/web_setup_bridge.test.ts new file mode 100644 index 000000000..09eeb2c69 --- /dev/null +++ b/src/cli/__tests__/web_setup_bridge.test.ts @@ -0,0 +1,101 @@ +import { WebSetupBridge } from '../web_setup_bridge'; + +describe('WebSetupBridge', () => { + test('publishes semantic prompts with one-use revisions and never echoes an answer', async () => { + const bridge = new WebSetupBridge('owner/repo'); + const pending = bridge.ask({ kind: 'secret', title: 'Setup PAT' }); + const revision = bridge.snapshot().promptRevision!; + expect(bridge.answer(revision + 1, 'secret-value')).toBe(false); + expect(bridge.answer(revision, 'secret-value')).toBe(true); + expect(await pending).toBe('secret-value'); + expect(bridge.answer(revision, 'secret-value')).toBe(false); + expect(bridge.wasAnswered(revision)).toBe(true); + expect(JSON.stringify(bridge.snapshot())).not.toContain('secret-value'); + }); + + test('a second tab is read-only until takeover and old capabilities fail', () => { + const bridge = new WebSetupBridge('owner/repo'); + const first = bridge.bootstrap(); + const second = bridge.bootstrap(); + expect(first.controller).toBe(true); + expect(second.controller).toBe(false); + expect(second.capability).toBeUndefined(); + expect(bridge.takeOver('invalid')).toBeUndefined(); + const replacement = bridge.takeOver(second.takeoverTicket)!; + expect(bridge.isController(first.capability!)).toBe(false); + expect(bridge.isController(replacement)).toBe(true); + expect(bridge.takeOver(second.takeoverTicket)).toBeUndefined(); + }); + + test('cancellation resolves a pending decision and forbids future prompts', async () => { + const bridge = new WebSetupBridge('owner/repo'); + const pending = bridge.ask({ kind: 'text', title: 'Name' }); + bridge.cancel(); + expect(await pending).toBeUndefined(); + expect(bridge.snapshot().outcome).toBe('cancelled'); + await expect(bridge.ask({ kind: 'text', title: 'Again' })).rejects.toThrow('ended'); + }); + + test('late messages and duplicate finishes cannot replace a terminal outcome', () => { + const bridge = new WebSetupBridge('owner/repo'); + bridge.finish('partial', 'Inspect before retry'); + bridge.finish('complete', 'Done'); + expect(bridge.snapshot().outcome).toBe('partial'); + expect(bridge.snapshot().message?.text).toBe('Inspect before retry'); + }); + + test('read-only subscribers receive redacted revisions and can unsubscribe', () => { + const bridge = new WebSetupBridge('old/repo'); + const seen: number[] = []; + const unsubscribe = bridge.subscribe(view => seen.push(view.revision)); + bridge.setRepository('owner/repo'); + bridge.message('Progress', 'info'); + unsubscribe(); + bridge.message('Later'); + expect(seen).toEqual([1, 2]); + expect(bridge.snapshot().repository).toBe('owner/repo'); + }); + + test('only one semantic decision can be pending at a time', async () => { + const bridge = new WebSetupBridge('owner/repo'); + const pending = bridge.ask({ kind: 'choice', title: 'A', choices: ['yes'] }); + await expect(bridge.ask({ kind: 'text', title: 'B' })).rejects.toThrow('already pending'); + bridge.answer(bridge.snapshot().promptRevision!, 'yes'); + expect(await pending).toBe('yes'); + }); + + test('rejects a value outside the visible choice without consuming the prompt', async () => { + const bridge = new WebSetupBridge('owner/repo'); + const pending = bridge.ask({ kind: 'confirm', title: 'Apply?', choices: ['Apply setup', 'Stop'] }); + const revision = bridge.snapshot().promptRevision!; + expect(bridge.answer(revision, 'yes')).toBe(false); + expect(bridge.snapshot().promptRevision).toBe(revision); + expect(bridge.answer(revision, 'Stop')).toBe(true); + expect(await pending).toBe('Stop'); + }); + + test('Apply cannot be cancelled once the mutation boundary started', async () => { + const bridge = new WebSetupBridge('owner/repo'); + bridge.setJourney({ repository: 'owner/repo', position: 6, total: 6, current: 'Apply', complete: [], pending: [], mutationStarted: true, choiceReviewPass: 1 }); + expect(bridge.cancel()).toBe(false); + expect(bridge.snapshot().outcome).toBeUndefined(); + bridge.finish('partial', 'Inspect resources'); + expect(bridge.cancel()).toBe(false); + }); + + test('finishing resolves an unanswered prompt but preserves no secret', async () => { + const bridge = new WebSetupBridge('owner/repo'); + const pending = bridge.ask({ kind: 'secret', title: 'PAT' }); + bridge.finish('blocked', 'Session expired'); + expect(await pending).toBeUndefined(); + expect(bridge.snapshot().prompt).toBeUndefined(); + expect(bridge.snapshot().outcome).toBe('blocked'); + }); + + test('requirement/report projection exposes role and status only', () => { + const bridge = new WebSetupBridge('owner/repo'); + bridge.requirements('setup', []); + bridge.report({ role: 'setup', identityStatus: 'valid', identityMessage: 'checked', checks: [], ready: true, confirmationRequired: false }); + expect(bridge.snapshot().permissions).toMatchObject({ role: 'setup', report: { ready: true }, requirements: [] }); + }); +}); diff --git a/src/cli/__tests__/web_setup_browser_open.test.ts b/src/cli/__tests__/web_setup_browser_open.test.ts new file mode 100644 index 000000000..6a4560625 --- /dev/null +++ b/src/cli/__tests__/web_setup_browser_open.test.ts @@ -0,0 +1,29 @@ +import { EventEmitter } from 'node:events'; +import { openWebSetupBrowser } from '../web_setup_server'; + +const mockSpawn = jest.fn(); +jest.mock('node:child_process', () => ({ spawn: (...args: unknown[]) => mockSpawn(...args) })); + +describe('local browser launch fallback', () => { + test.each([ + ['darwin', 'open', ['http://127.0.0.1:12345/']], + ['linux', 'xdg-open', ['http://127.0.0.1:12345/']], + ['win32', 'cmd', ['/c', 'start', '', 'http://127.0.0.1:12345/']], + ])('uses the %s opener and tolerates failure', (platform, command, args) => { + const original = process.platform; + Object.defineProperty(process, 'platform', { configurable: true, value: platform }); + const child = new EventEmitter() as EventEmitter & { unref: jest.Mock }; + child.unref = jest.fn(); + mockSpawn.mockReturnValueOnce(child); + const url = 'http://127.0.0.1:12345/'; + try { + openWebSetupBrowser(url); + expect(mockSpawn).toHaveBeenCalledWith(command, args, { stdio: 'ignore', detached: true, windowsHide: true }); + expect(child.unref).toHaveBeenCalledTimes(1); + expect(() => child.emit('error', new Error('No desktop opener'))).not.toThrow(); + } finally { + Object.defineProperty(process, 'platform', { configurable: true, value: original }); + mockSpawn.mockClear(); + } + }); +}); diff --git a/src/cli/__tests__/web_setup_browser_session.test.ts b/src/cli/__tests__/web_setup_browser_session.test.ts new file mode 100644 index 000000000..24785bec3 --- /dev/null +++ b/src/cli/__tests__/web_setup_browser_session.test.ts @@ -0,0 +1,72 @@ +import { createSetupSession } from '../../../web/src/session/setupSession'; +import type { WebSetupView } from '../../application/contracts/web_setup_view'; + +jest.mock('svelte/store', () => ({ + writable: (initial: unknown) => { + let value = initial; + const listeners = new Set<(next: unknown) => void>(); + return { + set(next: unknown) { value = next; for (const listener of listeners) listener(value); }, + subscribe(listener: (next: unknown) => void) { listeners.add(listener); listener(value); return () => listeners.delete(listener); }, + }; + }, +})); + +function response(body: unknown, status = 200): Response { + return new Response(JSON.stringify(body), { status, headers: { 'Content-Type': 'application/json' } }); +} + +describe('browser session transport', () => { + const view: WebSetupView = { revision: 3, promptRevision: 7, repository: 'owner/repo', prompt: { kind: 'secret', title: 'Setup PAT' } }; + const originalFetch = globalThis.fetch; + + afterEach(() => { globalThis.fetch = originalFetch; }); + + test('bootstrap and revision-bound submission keep the PAT out of observable state', async () => { + const requests: Array<{ path: string; options?: RequestInit }> = []; + globalThis.fetch = jest.fn(async (path: string, options?: RequestInit) => { + requests.push({ path, options }); + if (path === '/api/bootstrap') return response({ controller: true, capability: 'one-run-capability', takeoverTicket: 'ticket' }); + if (path === '/api/state') return response(view); + if (path === '/api/answer') return response({ ok: true }); + throw new Error('Unexpected route'); + }) as typeof fetch; + + const session = createSetupSession(); + let latest = ''; + session.subscribe(state => { latest = JSON.stringify(state); }); + await session.connect(); + await session.submit(7, 'ghp_example_secret'); + + expect(requests.map(request => request.path)).toEqual(['/api/bootstrap', '/api/state', '/api/answer', '/api/state']); + expect(requests[2].options?.headers).toEqual(expect.objectContaining({ 'X-Setup-Capability': 'one-run-capability' })); + expect(JSON.parse(String(requests[2].options?.body))).toEqual({ revision: 7, value: 'ghp_example_secret' }); + expect(latest).not.toContain('ghp_example_secret'); + await session.submit(6, 'stale'); + expect(requests).toHaveLength(4); + }); + + test('takeover replaces the controller capability and refreshes server-owned state', async () => { + const requests: Array<{ path: string; options?: RequestInit }> = []; + globalThis.fetch = jest.fn(async (path: string, options?: RequestInit) => { + requests.push({ path, options }); + if (path === '/api/bootstrap') return response({ controller: false, takeoverTicket: 'ticket' }); + if (path === '/api/state') return response(view); + if (path === '/api/takeover') return response({ capability: 'new-capability' }); + if (path === '/api/answer') return response({ ok: true }); + throw new Error('Unexpected route'); + }) as typeof fetch; + + const session = createSetupSession(); + let controller = false; + session.subscribe(state => { controller = state.controller; }); + await session.connect(); + await session.submit(7, 'blocked'); + expect(requests.some(request => request.path === '/api/answer')).toBe(false); + await session.takeOver(); + expect(controller).toBe(true); + expect(requests.find(request => request.path === '/api/takeover')?.options?.headers).not.toHaveProperty('X-Setup-Capability'); + await session.submit(7, 'allowed'); + expect(requests.find(request => request.path === '/api/answer')?.options?.headers).toEqual(expect.objectContaining({ 'X-Setup-Capability': 'new-capability' })); + }); +}); diff --git a/src/cli/__tests__/web_setup_components.test.ts b/src/cli/__tests__/web_setup_components.test.ts new file mode 100644 index 000000000..601379bd0 --- /dev/null +++ b/src/cli/__tests__/web_setup_components.test.ts @@ -0,0 +1,137 @@ +import { execFileSync } from 'node:child_process'; +import { resolve } from 'node:path'; + +function markup(name: string, props: Record): string { + return execFileSync(process.execPath, [ + resolve(__dirname, '../../../scripts/render-web-setup-component.cjs'), name, JSON.stringify(props), + ], { encoding: 'utf8' }); +} + +const noOp = async (): Promise => undefined; + +describe('web setup component semantics', () => { + test.each([ + ['complete', 'Your configuration was applied', 'not revoked automatically'], + ['dry-run', 'No changes were made', 'did not begin applying'], + ['cancelled', 'No setup changes started', 'did not begin applying'], + ['blocked', 'No setup changes started', 'did not begin applying'], + ['partial', 'Check partial changes before retrying', 'may have succeeded'], + ])('%s result explains actual mutation state and PAT cleanup', (outcome, heading, explanation) => { + const html = markup('ResultPanel', { outcome, controller: true, onClose: noOp }); + expect(html).toContain(heading); + expect(html).toContain(explanation); + expect(html).toContain('Close local session'); + expect(html).toContain('copilot doctor'); + }); + + test('read-only result cannot show its close control', () => { + expect(markup('ResultPanel', { outcome: 'complete', controller: false, onClose: noOp })).not.toContain('Close local session'); + }); + + test('choice prompt escapes untrusted text and disables a read-only controller', () => { + const html = markup('ChoicePrompt', { + prompt: { kind: 'choice', title: 'Choose', choices: ['', 'Safe'] }, + controller: false, busy: false, onSubmit: noOp, + }); + expect(html).toContain('<script>'); + expect(html).not.toContain('Test setup'); + writeFileSync(join(root, 'assets', 'app.js'), 'const ready = true;'); + writeFileSync(join(root, 'assets', 'app.css'), ':root { color: black; }'); + bridge = new WebSetupBridge('owner/repo'); + server = await startWebSetupServer(bridge, root); + }); + afterEach(async () => { if (server) await server.close(); rmSync(root, { recursive: true, force: true }); }); + + const jsonPost = (url: string, path: string, body: unknown, headers: Record = {}) => fetch(`${url}${path}`, { + method: 'POST', headers: { Origin: url.slice(0, -1), 'Content-Type': 'application/json', ...headers }, body: JSON.stringify(body), + }); + + test('serves only bundled local files with restrictive headers', async () => { + const page = await fetch(server.url); + expect(page.status).toBe(200); + expect(page.headers.get('content-security-policy')).toContain("default-src 'none'"); + expect(page.headers.get('cache-control')).toBe('no-store'); + expect(page.headers.get('access-control-allow-origin')).toBeNull(); + expect((await fetch(`${server.url}assets/app.js`)).status).toBe(200); + writeFileSync(join(root, 'assets', 'unlisted.js'), 'alert(1)'); + expect((await fetch(`${server.url}assets/unlisted.js`)).status).toBe(404); + expect((await fetch(`${server.url}assets/%2e%2e/index.html`)).status).toBe(404); + }); + + test('rejects forged hosts and cross-origin mutation', async () => { + const forgedStatus = await new Promise((resolveStatus, reject) => { + const forged = request(server.url, { headers: { Host: 'evil.example' } }, response => { + response.resume(); resolveStatus(response.statusCode ?? 0); + }); + forged.on('error', reject); forged.end(); + }); + expect(forgedStatus).toBe(403); + const boot = await (await fetch(`${server.url}api/bootstrap`)).json() as { capability: string }; + const pending = bridge.ask({ kind: 'secret', title: 'PAT' }); + const revision = bridge.snapshot().promptRevision!; + expect((await jsonPost(server.url, 'api/answer', { revision, value: 'sensitive' }, { + Origin: 'https://evil.example', 'X-Setup-Capability': boot.capability, + })).status).toBe(403); + expect((await jsonPost(server.url, 'api/answer', { revision, value: 'sensitive' }, { + 'X-Setup-Capability': 'wrong', + })).status).toBe(403); + expect(bridge.answer(revision, 'allowed')).toBe(true); + expect(await pending).toBe('allowed'); + }); + + test('accepts one authorized answer and never returns the submitted PAT', async () => { + const boot = await (await fetch(`${server.url}api/bootstrap`)).json() as { capability: string }; + const pending = bridge.ask({ kind: 'secret', title: 'Setup PAT' }); + const revision = bridge.snapshot().promptRevision!; + const response = await jsonPost(server.url, 'api/answer', { revision, value: 'ghp_private' }, { 'X-Setup-Capability': boot.capability }); + expect(response.status).toBe(200); + expect(await response.text()).not.toContain('ghp_private'); + expect(await pending).toBe('ghp_private'); + const duplicate = await jsonPost(server.url, 'api/answer', { revision, value: 'again' }, { 'X-Setup-Capability': boot.capability }); + expect(duplicate.status).toBe(200); + expect(await duplicate.json()).toMatchObject({ accepted: true, duplicate: true }); + expect((await jsonPost(server.url, 'api/answer', { revision: revision + 1, value: 'again' }, { 'X-Setup-Capability': boot.capability })).status).toBe(409); + expect(await (await fetch(`${server.url}api/state`)).text()).not.toContain('ghp_private'); + }); + + test('rejects oversized answers, wrong method, and unsupported content type', async () => { + const boot = await (await fetch(`${server.url}api/bootstrap`)).json() as { capability: string }; + const pending = bridge.ask({ kind: 'text', title: 'Answer' }); + const revision = bridge.snapshot().promptRevision!; + expect((await jsonPost(server.url, 'api/answer', { revision, value: 'x'.repeat(5000) }, { 'X-Setup-Capability': boot.capability })).status).toBe(400); + expect((await fetch(`${server.url}api/answer`)).status).toBe(404); + expect((await fetch(`${server.url}api/answer`, { method: 'POST', headers: { Origin: server.url.slice(0, -1), 'Content-Type': 'text/plain', 'X-Setup-Capability': boot.capability }, body: '{}' })).status).toBe(415); + bridge.cancel(); + await pending; + }); + + test('a second tab explicitly takes over and invalidates the first tab capability', async () => { + const first = await (await fetch(`${server.url}api/bootstrap`)).json() as { capability: string }; + const second = await (await fetch(`${server.url}api/bootstrap`)).json() as { controller: boolean; takeoverTicket: string }; + expect(second.controller).toBe(false); + expect((await jsonPost(server.url, 'api/takeover', { ticket: 'wrong' })).status).toBe(403); + const takeover = await jsonPost(server.url, 'api/takeover', { ticket: second.takeoverTicket }); + expect(takeover.status).toBe(200); + const { capability } = await takeover.json() as { capability: string }; + const pending = bridge.ask({ kind: 'choice', title: 'Proceed?', choices: ['yes', 'no'] }); + const revision = bridge.snapshot().promptRevision; + expect((await jsonPost(server.url, 'api/answer', { revision, value: 'yes' }, { 'X-Setup-Capability': first.capability })).status).toBe(403); + expect((await jsonPost(server.url, 'api/answer', { revision, value: 'yes' }, { 'X-Setup-Capability': capability })).status).toBe(200); + expect(await pending).toBe('yes'); + expect((await jsonPost(server.url, 'api/takeover', { ticket: second.takeoverTicket })).status).toBe(403); + }); + + test('cancel requires the controller and never claims to cancel in-flight Apply', async () => { + const { capability } = await (await fetch(`${server.url}api/bootstrap`)).json() as { capability: string }; + const pending = bridge.ask({ kind: 'text', title: 'Answer' }); + expect((await jsonPost(server.url, 'api/cancel', {}, { 'X-Setup-Capability': 'wrong' })).status).toBe(403); + expect((await jsonPost(server.url, 'api/cancel', {}, { 'X-Setup-Capability': capability })).status).toBe(200); + expect(await pending).toBeUndefined(); + expect(bridge.snapshot().outcome).toBe('cancelled'); + + const secondBridge = new WebSetupBridge('owner/repo'); + const secondServer = await startWebSetupServer(secondBridge, root); + try { + const nextCapability = (await (await fetch(`${secondServer.url}api/bootstrap`)).json() as { capability: string }).capability; + secondBridge.setJourney({ repository: 'owner/repo', position: 6, total: 6, current: 'Apply', complete: [], pending: [], mutationStarted: true, choiceReviewPass: 1 }); + expect((await jsonPost(secondServer.url, 'api/cancel', {}, { 'X-Setup-Capability': nextCapability })).status).toBe(409); + expect(secondBridge.snapshot().outcome).toBeUndefined(); + } finally { await secondServer.close(); } + }); + + test('close is rejected before a result and succeeds for the finished controller', async () => { + const { capability } = await (await fetch(`${server.url}api/bootstrap`)).json() as { capability: string }; + expect((await jsonPost(server.url, 'api/close', {}, { 'X-Setup-Capability': capability })).status).toBe(403); + bridge.finish('complete', 'done'); + expect((await jsonPost(server.url, 'api/close', {}, { 'X-Setup-Capability': 'wrong' })).status).toBe(403); + expect((await jsonPost(server.url, 'api/close', {}, { 'X-Setup-Capability': capability })).status).toBe(200); + await server.closed; + }); + + test.each([ + [{ 'X-Forwarded-Host': 'evil.example' }, 403], + [{ 'X-Forwarded-Proto': 'https' }, 403], + [{ Forwarded: 'host=evil.example' }, 403], + [{ 'Sec-Fetch-Site': 'cross-site' }, 403], + ])('rejects proxy or cross-site context %j', async (headers, expected) => { + expect((await fetch(server.url, { headers })).status).toBe(expected); + }); + + test('rejects foreign Referer, malformed JSON and wrong API methods', async () => { + const { capability } = await (await fetch(`${server.url}api/bootstrap`)).json() as { capability: string }; + expect((await jsonPost(server.url, 'api/answer', { revision: 1, value: 'x' }, { + Referer: 'https://evil.example/', 'X-Setup-Capability': capability, + })).status).toBe(403); + expect((await fetch(`${server.url}api/state`, { method: 'POST', headers: { Origin: server.url.slice(0, -1), 'Content-Type': 'application/json' }, body: '{}' })).status).toBe(405); + expect((await fetch(`${server.url}api/answer`, { + method: 'POST', headers: { Origin: server.url.slice(0, -1), 'Content-Type': 'application/json', 'X-Setup-Capability': capability }, body: '{invalid', + })).status).toBe(400); + expect((await fetch(`${server.url}api/not-a-route`)).status).toBe(404); + }); + + test('rejects invalid payload types and bodies beyond the byte limit', async () => { + const { capability } = await (await fetch(`${server.url}api/bootstrap`)).json() as { capability: string }; + const pending = bridge.ask({ kind: 'secret', title: 'PAT' }); + const revision = bridge.snapshot().promptRevision; + for (const invalid of [{ revision: '1', value: 'x' }, { revision, value: 7 }, { revision: -1, value: 'x' }]) { + expect((await jsonPost(server.url, 'api/answer', invalid, { 'X-Setup-Capability': capability })).status).toBe(400); + } + expect((await jsonPost(server.url, 'api/answer', [], { 'X-Setup-Capability': capability })).status).toBe(400); + expect((await jsonPost(server.url, 'api/answer', { revision, value: 'x'.repeat(8500) }, { 'X-Setup-Capability': capability })).status).toBe(400); + bridge.cancel(); + await pending; + }); + + test('startup refuses missing or symlinked packaged assets', async () => { + const invalid = mkdtempSync(join(tmpdir(), 'copilot-web-assets-test-')); + try { + writeFileSync(join(invalid, 'index.html'), 'No assets'); + await expect(startWebSetupServer(new WebSetupBridge('owner/repo'), invalid)).rejects.toThrow('incomplete'); + mkdirSync(join(invalid, 'assets')); + writeFileSync(join(invalid, 'index.html'), ''); + writeFileSync(join(invalid, 'assets/app.css'), 'body {}'); + symlinkSync(join(root, 'assets/app.js'), join(invalid, 'assets/app.js')); + await expect(startWebSetupServer(new WebSetupBridge('owner/repo'), invalid)).rejects.toThrow('escapes'); + } finally { rmSync(invalid, { recursive: true, force: true }); } + }); + + test('startup refuses an index symlink outside the asset root', async () => { + const invalid = mkdtempSync(join(tmpdir(), 'copilot-web-index-test-')); + try { + symlinkSync(join(root, 'index.html'), join(invalid, 'index.html')); + await expect(startWebSetupServer(new WebSetupBridge('owner/repo'), invalid)).rejects.toThrow('index must be inside'); + } finally { rmSync(invalid, { recursive: true, force: true }); } + }); + + test('an asset replaced by an escaping symlink is not served', async () => { + unlinkSync(join(root, 'assets', 'app.js')); + const outside = mkdtempSync(join(tmpdir(), 'copilot-asset-escape-test-')); + try { + writeFileSync(join(outside, 'app.js'), 'alert(1)'); + symlinkSync(join(outside, 'app.js'), join(root, 'assets', 'app.js')); + expect((await fetch(`${server.url}assets/app.js`)).status).toBe(404); + } finally { rmSync(outside, { recursive: true, force: true }); } + }); + + test('all mutating endpoints require an exact JSON content type', async () => { + const { capability } = await (await fetch(`${server.url}api/bootstrap`)).json() as { capability: string }; + const wrongType = (path: string) => fetch(`${server.url}${path}`, { + method: 'POST', headers: { Origin: server.url.slice(0, -1), 'Content-Type': 'text/plain', 'X-Setup-Capability': capability }, body: '{}', + }); + expect((await wrongType('api/takeover')).status).toBe(415); + expect((await wrongType('api/cancel')).status).toBe(415); + bridge.finish('complete', 'done'); + expect((await wrongType('api/close')).status).toBe(415); + }); + + test('mutating requests without a controller capability or a takeover ticket do nothing', async () => { + const origin = server.url.slice(0, -1); + const pending = bridge.ask({ kind: 'secret', title: 'PAT' }); + const revision = bridge.snapshot().promptRevision; + expect((await jsonPost(server.url, 'api/answer', { revision, value: 'ignored' })).status).toBe(403); + expect((await jsonPost(server.url, 'api/cancel', {})).status).toBe(403); + expect((await jsonPost(server.url, 'api/takeover', { ticket: 42 })).status).toBe(403); + expect((await fetch(`${server.url}api/answer`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ revision, value: 'ignored' }) })).status).toBe(403); + expect(origin).toContain('127.0.0.1'); + bridge.cancel(); + await pending; + }); + + test.each(['api/answer', 'api/cancel', 'api/close'])('rejects control transferred while reading %s', async path => { + const { capability } = await (await fetch(`${server.url}api/bootstrap`)).json() as { capability: string }; + if (path === 'api/answer') void bridge.ask({ kind: 'secret', title: 'PAT' }); + if (path === 'api/close') bridge.finish('complete', 'done'); + const controller = jest.spyOn(bridge, 'isController').mockReturnValueOnce(true).mockReturnValueOnce(false); + try { + const response = await jsonPost(server.url, path, path === 'api/answer' + ? { revision: bridge.snapshot().promptRevision, value: 'unaccepted' } : {}, { 'X-Setup-Capability': capability }); + expect(response.status).toBe(403); + if (path === 'api/answer') expect(bridge.snapshot().prompt?.kind).toBe('secret'); + } finally { + controller.mockRestore(); + if (path === 'api/answer') bridge.cancel(); + } + }); + + test('an unanswered pre-Apply prompt expires without accepting a late answer', async () => { + await server.close(); + jest.useFakeTimers({ doNotFake: ['nextTick', 'setImmediate'] }); + try { + bridge = new WebSetupBridge('owner/repo'); + server = await startWebSetupServer(bridge, root); + const pending = bridge.ask({ kind: 'secret', title: 'Setup PAT' }); + const revision = bridge.snapshot().promptRevision!; + await jest.advanceTimersByTimeAsync(30 * 60 * 1000); + expect(bridge.snapshot().outcome).toBe('blocked'); + expect(await pending).toBeUndefined(); + expect(bridge.answer(revision, 'late-token')).toBe(false); + expect(JSON.stringify(bridge.snapshot())).not.toContain('late-token'); + } finally { + await server.close(); + jest.useRealTimers(); + } + }); + + test('the idle limit does not interrupt a mutation already in progress', async () => { + await server.close(); + jest.useFakeTimers({ doNotFake: ['nextTick', 'setImmediate'] }); + try { + bridge = new WebSetupBridge('owner/repo'); + server = await startWebSetupServer(bridge, root); + bridge.setJourney({ repository: 'owner/repo', position: 6, total: 6, current: 'Apply', + complete: [], pending: [], mutationStarted: true, choiceReviewPass: 1 }); + await jest.advanceTimersByTimeAsync(4 * 60 * 60 * 1000); + expect(bridge.snapshot().outcome).toBeUndefined(); + } finally { + await server.close(); + jest.useRealTimers(); + } + }); + + test('the absolute lifetime expires a pre-Apply session even after an earlier active interval', async () => { + await server.close(); + jest.useFakeTimers({ doNotFake: ['nextTick', 'setImmediate'] }); + try { + bridge = new WebSetupBridge('owner/repo'); + server = await startWebSetupServer(bridge, root); + const applying = { repository: 'owner/repo', position: 6, total: 6, current: 'Apply', + complete: [] as string[], pending: [] as string[], mutationStarted: true, choiceReviewPass: 1 }; + bridge.setJourney(applying); + await jest.advanceTimersByTimeAsync(30 * 60 * 1000); + bridge.setJourney({ ...applying, mutationStarted: false }); + await jest.advanceTimersByTimeAsync(3.5 * 60 * 60 * 1000); + expect(bridge.snapshot().outcome).toBe('blocked'); + expect(bridge.snapshot().message?.text).toContain('four-hour limit'); + } finally { + await server.close(); + jest.useRealTimers(); + } + }); + + test('a finished session closes itself after its result-reading window', async () => { + await server.close(); + jest.useFakeTimers({ doNotFake: ['nextTick', 'setImmediate'] }); + try { + bridge = new WebSetupBridge('owner/repo'); + server = await startWebSetupServer(bridge, root); + bridge.finish('complete', 'done'); + await jest.advanceTimersByTimeAsync(10 * 60 * 1000); + await expect(server.closed).resolves.toBeUndefined(); + } finally { + await server.close(); + jest.useRealTimers(); + } + }); +}); diff --git a/src/cli/__tests__/web_setup_ui_helpers.test.ts b/src/cli/__tests__/web_setup_ui_helpers.test.ts new file mode 100644 index 000000000..eba77c6ff --- /dev/null +++ b/src/cli/__tests__/web_setup_ui_helpers.test.ts @@ -0,0 +1,49 @@ +import type { WebSetupPrompt } from '../../application/contracts/web_setup_view'; +import { safeGithubLink } from '../../../web/src/lib/githubLink'; +import { initialQuestionAnswer, submittedQuestionAnswer, toggleSelection } from '../../../web/src/lib/questionAnswer'; + +function question(kind: Extract['question']['kind'], defaultValue: string): Extract { + return { kind: 'question', title: 'Choice', phase: 'full', pass: 1, question: { + stateId: 'repository', id: 'test', label: 'A choice', kind, defaultValue, + choices: ['All', 'One — details', 'Two — details'], allowedNames: ['one', 'two'], + } }; +} + +describe('web setup presentation helpers', () => { + test('preselects matching multi-select defaults without selecting All', () => { + expect(initialQuestionAnswer(question('multi-select', 'One,Two'))).toEqual({ + value: 'One,Two', selected: ['One — details', 'Two — details'], + }); + }); + + test('scope overrides preserve explicit names and serialize an empty set as none', () => { + const prompt = question('scope-overrides', 'one,two'); + expect(initialQuestionAnswer(prompt).selected).toEqual(['one', 'two']); + expect(submittedQuestionAnswer(prompt, '', [])).toBe('none'); + expect(submittedQuestionAnswer(prompt, '', ['one'])).toBe('one'); + }); + + test('All is mutually exclusive with individual choices', () => { + expect(toggleSelection(['one'], 'All')).toEqual(['All']); + expect(toggleSelection(['All'], 'one')).toEqual(['one']); + expect(toggleSelection(['one'], 'one')).toEqual([]); + expect(toggleSelection(['All'], 'All')).toEqual([]); + }); + + test('ordinary question answers use the entered value', () => { + expect(submittedQuestionAnswer(question('text', 'old'), 'new', [])).toBe('new'); + expect(submittedQuestionAnswer(question('multi-select', ''), '', ['One — details'])).toBe('One — details'); + expect(submittedQuestionAnswer(question('multi-select', 'One'), '', [])).toBe('none'); + }); + + test.each([ + ['https://github.com/settings/personal-access-tokens/new?name=Setup', true], + ['https://github.com/settings/personal-access-tokens', true], + ['https://evil.example/settings/personal-access-tokens', false], + ['http://github.com/settings/personal-access-tokens', false], + ['https://github.com/settings/keys', false], + ['javascript:alert(1)', false], + ])('allowlisted GitHub link %s: %s', (link, allowed) => { + expect(Boolean(safeGithubLink(link))).toBe(allowed); + }); +}); diff --git a/src/cli/commands/setup.ts b/src/cli/commands/setup.ts index 6077e9c29..9f1e6e14e 100644 --- a/src/cli/commands/setup.ts +++ b/src/cli/commands/setup.ts @@ -3,33 +3,24 @@ import { runLocalAction } from '../../actions/local_action'; import { TITLE } from '../../application/contracts/product_identity'; import { getSetupToken } from '../../utils/setup_files'; import { logError, logInfo } from '../../utils/logger'; -import { getGitInfo, isInsideGitRepo } from '../../cli_context'; +import { getCurrentBranch, getCurrentHeadSha, getGitInfo, getGitRepositoryRoot, isInsideGitRepo } from '../../cli_context'; import { buildSetupParams } from './setup_policy'; -import { loadSetupConfigurationOverrides } from '../setup_config_file'; +import { collectApprovalCheck, collectScope, collectSecret, loadSetupOverrides } from '../setup_command_options'; import { SetupQuestionnaireController, SetupWizardUseCase } from '../../application/usecases/setup'; -import { buildInitialSetupConfiguration } from '../../application/usecases/setup/setup_wizard_use_case'; -import { createSetupPermissionIntentQuestionnaire } from '../../application/policies/setup_questionnaire_policy'; -import { fixedSetupPatIntentQuestionIds, setupPatIntentNeedsOwnerKind, setupPatIntentOwnerConflict } from '../../application/policies/setup_pat_intent_policy'; +import { setupPlanGuardPaths } from '../../application/policies/setup_configuration_plan'; +import { PrepareSetupPatIntentUseCase } from '../../application/usecases/setup/prepare_setup_pat_intent_use_case'; +import { AuditConfiguredSetupPatUseCase } from '../../application/usecases/setup/audit_configured_setup_pat_use_case'; +import { VerifySetupPatBootstrapUseCase } from '../../application/usecases/setup/verify_setup_pat_bootstrap_use_case'; +import { buildSetupCredentialRequirements, effectiveIssueWorkflowFeatures } from '../../application/policies/setup_configuration_policy'; import { - SETUP_FEATURE_DESCRIPTIONS, - buildSetupCredentialRequirements, - effectiveIssueWorkflowFeatures, - validateSetupConfiguration, -} from '../../application/policies/setup_configuration_policy'; -import { - buildConfiguredSetupPatPermissionRequirements, buildSetupPatPermissionRequirements, - buildSetupPatIntentPermissionRequirements, - buildSetupPatIntentUncertainty, buildWorkflowPatPermissionRequirements, } from '../../application/policies/setup_token_permission_policy'; -import type { SetupConfigurationOverrides } from '../../application/policies/setup_configuration_policy'; import { createSetupCredentialsUseCase, createSetupRemoteConfigurationReadPort } from '../../infrastructure/composition/setup_credentials_composition_root'; import { createSetupMergeQueueReadinessUseCase } from '../../infrastructure/composition/setup_doctor_composition_root'; import { SetupDoctorWorkspaceQueryAdapter } from '../../infrastructure/setup_workspace_adapter'; import { GithubSetupApprovalReadinessAdapter } from '../../infrastructure/setup_approval_readiness_adapter'; -import type { SetupConfiguration, SetupRemoteConfiguration, SetupResourceScope } from '../../domain/setup'; -import { ISSUE_WORKFLOW_KINDS, type IssueWorkflowKind } from '../../domain/issue_workflow_profile'; +import type { SetupConfiguration } from '../../domain/setup'; import { ApplicationError, toApplicationError } from '../../application/errors/application_error'; import { createInteractiveTerminalDriver } from '../setup_terminal_driver'; import { ConsoleSetupQuestionRenderer } from '../setup_question_renderer'; @@ -42,9 +33,19 @@ import { createSetupTokenPermissionsUseCase } from '../../infrastructure/composi import { buildSetupPatCreationUrl, UnsupportedSetupPatLinkError } from '../../application/policies/setup_pat_creation_url_policy'; import { SetupGithubIdentityQueryAdapter } from '../../infrastructure/setup_github_identity_query_adapter'; import { VerifyGuidedWorkflowPatIdentityUseCase } from '../../application/usecases/setup/verify_guided_workflow_pat_identity_use_case'; -import type { SetupTokenPermissionRequirement } from '../../domain/setup_token_permissions'; +import { VerifyWebSetupApplyUseCase } from '../../application/usecases/setup/verify_web_setup_apply_use_case'; import { SetupJourneyUseCase } from '../../application/usecases/setup/setup_journey_use_case'; +import { buildSetupJourneyView } from '../../application/policies/setup_journey_policy'; import { ConsoleSetupJourneyPresenter } from '../setup_journey_presenter'; +import { WebSetupBridge } from '../web_setup_bridge'; +import { captureSetupApplySnapshot, setupApplySnapshotMatches } from '../setup_apply_snapshot'; +import { acquireSetupSessionGuard } from '../setup_session_guard'; +import { startWebSetupServer, openWebSetupBrowser, type WebSetupServer } from '../web_setup_server'; +import { + WebSetupCredentialPrompt, WebSetupJourneyPresenter, WebSetupPermissionPresenter, + WebSetupPlanConfirmation, WebSetupPlanPresenter, WebSetupQuestionnaireCollector, + WebSetupWorkflowUpdatePrompt, +} from '../web_setup_adapters'; export function registerSetupCommand(program: Command): void { program @@ -62,6 +63,7 @@ export function registerSetupCommand(program: Command): void { .option('--pr-approval-coverage-check ', 'Exact selected check that enforces the coverage budget') .option('--pr-approval-attest-producer', 'Confirm exact check/App/workflow identity and a coverage-enforcing CI step', false) .option('--non-interactive', 'Use defaults and config-file values without prompting', false) + .option('--web', 'Run the optional local browser setup assistant (127.0.0.1 only)', false) .option('--yes', 'Apply the plan without the final confirmation prompt', false) .option('--confirm-unverifiable-write-permissions', 'Confirm that required PAT write permissions shown as Unverifiable were configured exactly as displayed', false) .option('--dry-run', 'Show the setup plan without changing files or GitHub', false) @@ -77,22 +79,25 @@ export function registerSetupCommand(program: Command): void { .option('--workflow-pat ', 'Workflow PAT for the bot account (prefer the hidden interactive prompt)') .option('--secret ', 'Secret value for non-interactive setup; repeat for each API key', collectSecret, {}) .action(async (options) => { - const terminal = options.nonInteractive ? undefined : createInteractiveTerminalDriver(); - const credentialPrompt = new SetupCredentialPromptAdapter(terminal, { + const terminal = options.nonInteractive || options.web ? undefined : createInteractiveTerminalDriver(); + const webBridge = options.web ? new WebSetupBridge('Resolving repository…') : undefined; + let webServer: WebSetupServer | undefined; + const credentialPrompt = webBridge ? new WebSetupCredentialPrompt(webBridge) : new SetupCredentialPromptAdapter(terminal, { ...(options.workflowPat ? { PAT: options.workflowPat } : {}), ...options.secret, }, Boolean(options.confirmUnverifiableWritePermissions)); - const permissionPresenter = new ConsoleSetupTokenPermissionPresenter(options.nonInteractive ? 'full' : 'summary'); + const permissionPresenter = webBridge ? new WebSetupPermissionPresenter(webBridge) + : new ConsoleSetupTokenPermissionPresenter(options.nonInteractive ? 'full' : 'summary'); const tokenPermissions = createSetupTokenPermissionsUseCase(); - const workflowPrompt = new SetupWorkflowUpdatePromptAdapter(terminal); + const workflowPrompt = webBridge ? new WebSetupWorkflowUpdatePrompt(webBridge) : new SetupWorkflowUpdatePromptAdapter(terminal); const cwd = process.cwd(); let setupMutationStarted = false; + let releaseSetupGuard: (() => void) | undefined; let journey: SetupJourneyUseCase | undefined; try { - if (!options.nonInteractive && !terminal) { - logError('Interactive setup requires a terminal. Use --non-interactive with explicit configuration.'); - process.exitCode = 1; - return; + if (options.web && (options.nonInteractive || options.yes || options.token || options.workflowPat + || Object.keys(options.secret ?? {}).length || options.confirmUnverifiableWritePermissions)) { + throw new ApplicationError('configuration.invalid', '--web cannot be combined with --non-interactive, --yes, --token, --workflow-pat, --secret, or --confirm-unverifiable-write-permissions. Use the browser for these decisions or run copilot setup in the terminal.'); } logInfo('🔍 Checking we are inside a git repository...'); if (!isInsideGitRepo(cwd)) { @@ -109,13 +114,43 @@ export function registerSetupCommand(program: Command): void { return; } logInfo(`📦 Repository: ${gitInfo.owner}/${gitInfo.repo}`); + releaseSetupGuard = acquireSetupSessionGuard(cwd); + const checkoutRoot = webBridge ? getGitRepositoryRoot(cwd) : cwd; + const initialBranch = webBridge ? getCurrentBranch() : undefined; + const initialHead = webBridge ? getCurrentHeadSha() : undefined; + if (webBridge && !initialHead) { + throw new ApplicationError('configuration.invalid', 'The current Git revision could not be verified. No local setup session started.'); + } + if (webBridge) { + webBridge.setRepository(`${gitInfo.owner}/${gitInfo.repo}`); + webBridge.setJourney(buildSetupJourneyView(`${gitInfo.owner}/${gitInfo.repo}`, 'repository', false)); + webServer = await startWebSetupServer(webBridge); + logInfo(`🌐 Local setup assistant: ${webServer.url}`); + logInfo('If the browser does not open, copy this URL into a browser on this computer. The terminal setup remains available with copilot setup.'); + openWebSetupBrowser(webServer.url); + } if (!options.nonInteractive) { - journey = new SetupJourneyUseCase(`${gitInfo.owner}/${gitInfo.repo}`, new ConsoleSetupJourneyPresenter()); + journey = new SetupJourneyUseCase(`${gitInfo.owner}/${gitInfo.repo}`, + webBridge ? new WebSetupJourneyPresenter(webBridge) : new ConsoleSetupJourneyPresenter()); + if (webBridge) { + const target = await webBridge.ask({ kind: 'confirm', title: 'Confirm this repository', + description: `This local checkout resolves to ${gitInfo.owner}/${gitInfo.repo} on branch ${initialBranch}. Confirm the target before configuring PAT access or files.`, + choices: ['Yes, this is my repository', 'Stop and choose another checkout'] }); + if (target === undefined) throw new SetupTerminalCancelledError(); + if (target !== 'Yes, this is my repository') { journey.finish('cancelled'); return; } + } journey.advance('choices'); } const overrides = loadSetupOverrides(options); let setupPatPermissions = buildSetupPatPermissionRequirements(); let token = getSetupToken(cwd, options.token); + if (webBridge && token) { + const choice = await webBridge.ask({ kind: 'choice', title: 'An environment setup PAT is available', + description: 'Its value stays in the CLI process and is never sent to this page. Exiting Copilot cannot unset your parent shell variable.', + choices: ['Use the environment PAT', 'Create or enter a different PAT'] }); + if (choice === undefined) throw new SetupTerminalCancelledError(); + if (choice !== 'Use the environment PAT') token = undefined; + } if (token || options.nonInteractive) permissionPresenter.showDetailedRequirements('setup', setupPatPermissions); else permissionPresenter.showRequirements('setup', setupPatPermissions); let setupPatAccount: string | undefined; @@ -123,83 +158,50 @@ export function registerSetupCommand(program: Command): void { let assertedOwnerKind: 'Organization' | 'User' | undefined; if (!token && !options.nonInteractive && !options.dryRun) { if (await credentialPrompt.chooseSetupPatMethod() === 'guided') { - const fixedQuestionIds = fixedSetupPatIntentQuestionIds(overrides, Boolean(options.skipVariables), Boolean(options.skipSecrets)); - let draft = buildInitialSetupConfiguration({ - mode: 'interactive', overrides, + const prepared = await new PrepareSetupPatIntentUseCase({ + collect: (initial, context, pass) => (webBridge + ? new WebSetupQuestionnaireCollector(webBridge, pass) + : new SetupQuestionnaireController(terminal!, new ConsoleSetupQuestionRenderer('permission-intent', pass))) + .collect(initial, context), + chooseOwnerKind: () => credentialPrompt.chooseSetupOwnerKind(), + review: () => credentialPrompt.reviewSetupPatIntent(), + showPreview: ({ draft, requirements, uncertain, ownerConflict, errors, pass }) => { + if (ownerConflict) logInfo('This plan selects organization storage or Projects, but the owner was declared a personal account. Revise the choices or use the manual PAT path.'); + if (errors.length) logInfo(`The selected local configuration needs correction before a guided link can be generated:\n${errors.map(item => ` - ${item}`).join('\n')}`); + if (pass > 1) logInfo('Choice review complete. Returning to setup PAT permission review.'); + logInfo('Permission intent:'); + logInfo(` Initial tag: ${draft.createInitialTag ? 'yes' : 'no'}; issue workflows: ${draft.features.issues ? draft.issueWorkflows.enabled.join(', ') || 'none' : 'disabled'}; PR approval: ${draft.pullRequestApproval.mode}`); + logInfo(` Secrets: ${draft.manageRepositorySecrets ? draft.storage.secrets.defaultScope : 'off'}; Variables: ${draft.manageRepositoryVariables ? draft.storage.variables.defaultScope : 'off'}; Projects: ${draft.projects.ids.trim() || 'none'}`); + webBridge?.message(`Permission preview: issue workflows ${draft.features.issues ? draft.issueWorkflows.enabled.join(', ') || 'none' : 'disabled'}; PR approval ${draft.pullRequestApproval.mode}; Secrets ${draft.manageRepositorySecrets ? draft.storage.secrets.defaultScope : 'off'}; Variables ${draft.manageRepositoryVariables ? draft.storage.variables.defaultScope : 'off'}; Projects ${draft.projects.ids.trim() || 'none'}.`, 'info'); + permissionPresenter.showRequirements('setup', requirements); + if (uncertain.length) logInfo(`May need after GitHub inspection:\n${uncertain.map(item => ` - ${item}`).join('\n')}`); + }, + showDetails: requirements => permissionPresenter.showDetailedRequirements('setup', requirements), + onManual: reason => { + if (reason === 'owner-unknown') logInfo('Owner type was not confirmed. Use the manual PAT table, or check whether the GitHub owner is an organization before retrying guided setup.'); + if (reason === 'unsupported') logInfo('A guided setup PAT link is unavailable for this owner or permission set. Enter a manually created PAT using the table above.'); + credentialPrompt.useManualSetupPat(); + permissionPresenter.showDetailedRequirements('setup', setupPatPermissions); + }, + advanceToSetupPat: () => { journey?.advance('setup-pat'); }, + revisitChoices: () => journey!.revisitChoices(), + }).execute({ + owner: gitInfo.owner, repository: gitInfo.repo, overrides, skipRepositoryVariables: Boolean(options.skipVariables), skipRepositorySecrets: Boolean(options.skipSecrets), }); - let choiceReviewPass = 1; - while (true) { - const context = { skipQuestionIds: fixedQuestionIds }; - const collector = new SetupQuestionnaireController(terminal!, new ConsoleSetupQuestionRenderer('permission-intent', choiceReviewPass)); - const intent = await collector.collect(createSetupPermissionIntentQuestionnaire(draft, context), context); - if (intent.terminal === 'cancelled') throw new SetupTerminalCancelledError(); - draft = intent.draft; - const ownerKind = setupPatIntentNeedsOwnerKind(draft) - ? await credentialPrompt.chooseSetupOwnerKind() : 'User'; - if (ownerKind === 'unknown') { - logInfo('Owner type was not confirmed. Use the manual PAT table, or check whether the GitHub owner is an organization before retrying guided setup.'); - credentialPrompt.useManualSetupPat(); - permissionPresenter.showDetailedRequirements('setup', setupPatPermissions); - break; - } - if (setupPatIntentOwnerConflict(draft, ownerKind)) { - logInfo('This plan selects organization storage or Projects, but the owner was declared a personal account. Revise the choices or use the manual PAT path.'); - } - const intentErrors = validateSetupConfiguration(draft, { allowIncompleteApproval: true }); - if (intentErrors.length > 0) { - logInfo(`The selected local configuration needs correction before a guided link can be generated:\n${intentErrors.map(item => ` - ${item}`).join('\n')}`); - } - const preview = buildSetupPatIntentPermissionRequirements(draft, ownerKind); - if (choiceReviewPass > 1) logInfo('Choice review complete. Returning to setup PAT permission review.'); - journey?.advance('setup-pat'); - logInfo('Permission intent:'); - logInfo(` Initial tag: ${draft.createInitialTag ? 'yes' : 'no'}; issue workflows: ${draft.features.issues ? draft.issueWorkflows.enabled.join(', ') || 'none' : 'disabled'}; PR approval: ${draft.pullRequestApproval.mode}`); - logInfo(` Secrets: ${draft.manageRepositorySecrets ? draft.storage.secrets.defaultScope : 'off'}; Variables: ${draft.manageRepositoryVariables ? draft.storage.variables.defaultScope : 'off'}; Projects: ${draft.projects.ids.trim() || 'none'}`); - permissionPresenter.showRequirements('setup', preview); - const uncertain = buildSetupPatIntentUncertainty(draft, ownerKind); - if (uncertain.length) logInfo(`May need after GitHub inspection:\n${uncertain.map(item => ` - ${item}`).join('\n')}`); - let decision: Awaited>; - do { - decision = await credentialPrompt.reviewSetupPatIntent(); - if (decision === 'details') permissionPresenter.showDetailedRequirements('setup', preview); - } while (decision === 'details'); - if (decision === 'manual') { - credentialPrompt.useManualSetupPat(); - permissionPresenter.showDetailedRequirements('setup', setupPatPermissions); - break; - } - if (decision === 'revise') { - choiceReviewPass = journey?.revisitChoices() ?? choiceReviewPass + 1; - continue; - } - if (setupPatIntentOwnerConflict(draft, ownerKind) || intentErrors.length > 0) { - throw new ApplicationError('configuration.invalid', 'Correct the reported setup intent or local --config/flags, then retry guided setup. No PAT was requested.'); - } - try { - const url = buildSetupPatCreationUrl({ - role: 'setup', owner: gitInfo.owner, repository: gitInfo.repo, expiresIn: 1, - requirements: preview, - }); - credentialPrompt.configureSetupPatGuide(url); - setupPatPermissions = preview; - assertedOwnerKind = ownerKind; - permissionIntent = { draft, answeredQuestionIds: [...new Set([...fixedQuestionIds, ...intent.answeredQuestionIds!])] }; - } catch (error) { - if (!(error instanceof UnsupportedSetupPatLinkError)) throw error; - logInfo('A guided setup PAT link is unavailable for this owner or permission set. Enter a manually created PAT using the table above.'); - credentialPrompt.useManualSetupPat(); - permissionPresenter.showDetailedRequirements('setup', setupPatPermissions); - } - break; + if (prepared.kind === 'guided') { + credentialPrompt.configureSetupPatGuide(prepared.url); + setupPatPermissions = [...prepared.requirements]; + assertedOwnerKind = prepared.ownerKind; + permissionIntent = prepared.permissionIntent; } } else { journey?.advance('setup-pat'); permissionPresenter.showDetailedRequirements('setup', setupPatPermissions); } } - if (options.dryRun && !token) journey?.advance('plan'); + if (options.dryRun && !token && !webBridge) journey?.advance('plan'); if (!token && !options.dryRun) journey?.advance('setup-pat'); if (!token && !options.nonInteractive && !options.dryRun) token = await credentialPrompt.requestSetupPat(); if (!token && !options.dryRun) { @@ -212,83 +214,41 @@ export function registerSetupCommand(program: Command): void { } if (token) { journey?.advance('setup-pat'); - const permissionReport = await tokenPermissions.inspect({ - role: 'setup', - owner: gitInfo.owner, - repository: gitInfo.repo, - token, - requirements: setupPatPermissions, - }); - permissionPresenter.showReport(permissionReport); - const permissionAccepted = permissionReport.ready - || (permissionReport.confirmationRequired - && await credentialPrompt.confirmUnverifiableTokenPermissions(permissionReport)); - if (!permissionAccepted || permissionReport.identityStatus !== 'valid') { - if (credentialPrompt.usedGuidedSetupPat) credentialPrompt.showUpdatedSetupPatLink(buildSetupPatCreationUrl({ - role: 'setup', owner: gitInfo.owner, repository: gitInfo.repo, expiresIn: 1, - requirements: setupPatPermissions, - }), 'bootstrap'); - throw new ApplicationError( - 'authorization.credential-invalid', - 'The setup PAT has missing or unconfirmed required access. Grant or explicitly confirm the permissions shown above and retry.', - ); - } - if (!await credentialPrompt.confirmGuidedSetupAccount(permissionReport.account)) { - throw new ApplicationError('authorization.credential-invalid', 'The setup PAT belongs to an unintended account. Revoke it in GitHub and retry with the correct account.'); - } - setupPatAccount = permissionReport.account; + setupPatAccount = await new VerifySetupPatBootstrapUseCase({ + permissions: tokenPermissions, + presenter: permissionPresenter, + confirmUnverifiable: report => credentialPrompt.confirmUnverifiableTokenPermissions(report), + confirmAccount: account => credentialPrompt.confirmGuidedSetupAccount(account), + showCorrectedLink: url => credentialPrompt.showUpdatedSetupPatLink(url, 'bootstrap'), + }).execute({ owner: gitInfo.owner, repository: gitInfo.repo, token, + requirements: setupPatPermissions, guided: credentialPrompt.usedGuidedSetupPat }); journey?.advance('plan'); } logInfo(options.dryRun ? '🧭 Building a dry-run setup plan...' : '🧭 Building your setup plan...'); - const auditConfiguredSetupPat = async ( - configuration: Readonly, - remoteConfiguration?: Readonly, - ): Promise<{ status: 'accepted' } | { status: 'blocked'; errors: readonly string[] }> => { - const configuredSetupPatPermissions = buildConfiguredSetupPatPermissionRequirements(configuration, remoteConfiguration); - permissionPresenter.showRequirements('setup', configuredSetupPatPermissions); - if (assertedOwnerKind && remoteConfiguration && remoteConfiguration.ownerType !== 'Unknown' - && remoteConfiguration.ownerType !== assertedOwnerKind) { - logInfo(`The owner was declared ${assertedOwnerKind}, but GitHub reports ${remoteConfiguration.ownerType}. The guided link is no longer valid for this plan.`); - credentialPrompt.showUpdatedSetupPatLink(buildSetupPatCreationUrl({ - role: 'setup', owner: gitInfo.owner, repository: gitInfo.repo, expiresIn: 1, - requirements: configuredSetupPatPermissions, - }), 'final', setupPatPermissionDelta(setupPatPermissions, configuredSetupPatPermissions)); - return { status: 'blocked', errors: ['Repository owner type differs from the pre-PAT selection. Rerun setup with the correct owner type and PAT.'] }; - } - if (credentialPrompt.usedGuidedSetupPat) { - const removed = setupPatPermissionDelta(configuredSetupPatPermissions, setupPatPermissions); - if (removed.length) logInfo(`The final plan no longer requires grants suggested earlier: ${removed.join(', ')}. Your PAT may have excess access; replace it in GitHub if least privilege is required.`); - } - if (!token) return { status: 'accepted' }; - const permissionReport = await tokenPermissions.inspect({ - role: 'setup', owner: gitInfo.owner, repository: gitInfo.repo, token, - requirements: configuredSetupPatPermissions, - }); - permissionPresenter.showReport(permissionReport); - const permissionAccepted = permissionReport.ready - || (permissionReport.confirmationRequired - && await credentialPrompt.confirmUnverifiableTokenPermissions(permissionReport)); - if (!permissionAccepted || permissionReport.identityStatus !== 'valid') { - if (credentialPrompt.usedGuidedSetupPat) credentialPrompt.showUpdatedSetupPatLink(buildSetupPatCreationUrl({ - role: 'setup', owner: gitInfo.owner, repository: gitInfo.repo, expiresIn: 1, - requirements: configuredSetupPatPermissions, - }), 'final', setupPatPermissionDelta(setupPatPermissions, configuredSetupPatPermissions)); - return { status: 'blocked', errors: [ - 'The setup PAT has missing or unconfirmed access required by the approved setup plan. Grant or explicitly confirm the permissions shown above and retry.', - ] }; - } - return { status: 'accepted' }; - }; + const auditConfiguredSetupPat = new AuditConfiguredSetupPatUseCase({ + owner: gitInfo.owner, repository: gitInfo.repo, token, + provisionalRequirements: setupPatPermissions, assertedOwnerKind, + guided: credentialPrompt.usedGuidedSetupPat, + }, { + permissions: tokenPermissions, + presenter: permissionPresenter, + confirmUnverifiable: report => credentialPrompt.confirmUnverifiableTokenPermissions(report), + showOwnerMismatch: (asserted, actual) => logInfo(`The owner was declared ${asserted}, but GitHub reports ${actual}. The guided link is no longer valid for this plan.`), + showExcessGrants: grants => logInfo(`The final plan no longer requires grants suggested earlier: ${grants.join(', ')}. Your PAT may have excess access; replace it in GitHub if least privilege is required.`), + showUpdatedLink: (url, grants) => credentialPrompt.showUpdatedSetupPatLink(url, 'final', grants), + }); const remoteConfigurationReader = createSetupRemoteConfigurationReadPort(); const wizard = new SetupWizardUseCase({ - ...(terminal ? { - collector: new SetupQuestionnaireController(terminal, new ConsoleSetupQuestionRenderer()), + ...(terminal || webBridge ? { + collector: webBridge ? new WebSetupQuestionnaireCollector(webBridge) + : new SetupQuestionnaireController(terminal!, new ConsoleSetupQuestionRenderer()), } : {}), - planPresenter: new ConsoleSetupPlanPresenter(), + planPresenter: webBridge ? new WebSetupPlanPresenter(webBridge) : new ConsoleSetupPlanPresenter(), confirmation: options.dryRun ? new DryRunSetupPlanConfirmation() - : new SetupPlanConfirmationAdapter(terminal, Boolean(options.yes)), - finalPermissionAudit: { audit: auditConfiguredSetupPat }, + : webBridge ? new WebSetupPlanConfirmation(webBridge) + : new SetupPlanConfirmationAdapter(terminal, Boolean(options.yes)), + finalPermissionAudit: auditConfiguredSetupPat, remoteConfiguration: remoteConfigurationReader, mergeQueueReadiness: createSetupMergeQueueReadinessUseCase(), approvalReadiness: new GithubSetupApprovalReadinessAdapter(), @@ -322,6 +282,8 @@ export function registerSetupCommand(program: Command): void { return; } const { configuration, remoteConfiguration } = result; + const guardedFiles = webBridge ? setupPlanGuardPaths(result.plan) : undefined; + const webApplySnapshot = guardedFiles ? captureSetupApplySnapshot(checkoutRoot, guardedFiles) : undefined; const credentialRequirements = buildSetupCredentialRequirements(configuration); const workflowComparisons = new SetupDoctorWorkspaceQueryAdapter().compareWorkflows(effectiveIssueWorkflowFeatures(configuration), configuration); const updateWorkflows = await workflowPrompt.confirmWorkflowUpdates(workflowComparisons, Boolean(options.updateWorkflows)); @@ -329,6 +291,7 @@ export function registerSetupCommand(program: Command): void { ? workflowComparisons.filter(comparison => comparison.status === 'changed').map(comparison => comparison.file) : []; if (options.dryRun) { + if (webBridge) journey?.advance('plan'); journey?.finish('dry-run'); logInfo('✅ Dry run complete. No files or GitHub resources were changed.'); return; @@ -349,7 +312,8 @@ export function registerSetupCommand(program: Command): void { permissionPresenter.showDetailedRequirements('workflow', workflowTokenPermissions); } } - const credentials = await createSetupCredentialsUseCase(credentialPrompt, permissionPresenter).collect({ + const credentials = await createSetupCredentialsUseCase(credentialPrompt, permissionPresenter, + webBridge ? { allowPreApplyHealthWorkflow: false } : undefined).collect({ owner: gitInfo.owner, repository: gitInfo.repo, setupToken: token ?? '', @@ -369,6 +333,38 @@ export function registerSetupCommand(program: Command): void { logInfo('The workflow PAT and setup PAT use the same GitHub account. If this account authors PRs, bot-generated events and guarded self-approval may not behave as intended; use a dedicated bot account where required.'); } } + if (webBridge) { + if (!remoteConfiguration || !guardedFiles || !webApplySnapshot || !initialBranch || !initialHead || !token) { + throw new ApplicationError('configuration.invalid', 'The approved setup evidence is incomplete. No mutation started; restart and review a new plan.'); + } + const authorization = await new VerifyWebSetupApplyUseCase({ + confirm: async () => { + const answer = await webBridge.ask({ kind: 'confirm', title: 'Apply this setup now?', + description: 'This is the final approval. Local files and selected GitHub resources may change. A partial result may require inspection before retrying.', + choices: ['Apply setup', 'Stop without applying'] }); + return answer === undefined ? undefined : answer === 'Apply setup' ? 'apply' : 'stop'; + }, + readRepositoryFacts: () => { + const current = getGitInfo(); + return 'error' in current ? undefined : { + owner: current.owner, repository: current.repo, checkoutRoot: getGitRepositoryRoot(cwd), + branch: getCurrentBranch(), head: getCurrentHeadSha() ?? '', + }; + }, + fileSnapshotMatches: setupApplySnapshotMatches, + remote: remoteConfigurationReader, + permissionAudit: auditConfiguredSetupPat, + sessionState: () => webBridge.snapshot().outcome === 'cancelled' ? 'cancelled' + : webBridge.snapshot().outcome ? 'ended' : 'active', + }).execute({ + repository: { owner: gitInfo.owner, repository: gitInfo.repo, checkoutRoot, + branch: initialBranch, head: initialHead }, + selectedFiles: guardedFiles, fileSnapshot: webApplySnapshot, approvedRemote: remoteConfiguration, + configuration, setupToken: token, + }); + if (authorization === 'cancelled') throw new SetupTerminalCancelledError(); + if (authorization === 'declined') { journey?.finish('cancelled'); return; } + } logInfo('⚙️ Applying the approved setup plan...'); journey?.advance('apply'); const params = buildSetupParams( @@ -411,166 +407,17 @@ export function registerSetupCommand(program: Command): void { } finally { credentialPrompt.showSetupPatCleanupReminder(); terminal?.close(); + if (webBridge && webServer) { + const outcome = webBridge.snapshot().journey?.outcome ?? (process.exitCode ? 'blocked' : 'cancelled'); + webBridge.finish(outcome, outcome === 'complete' + ? 'Setup completed. Delete the temporary setup PAT in GitHub; keep the bot PAT while its Secret is in use.' + : outcome === 'dry-run' ? 'Dry run complete. No files or GitHub resources changed.' + : outcome === 'partial' ? 'Setup may be partial. Inspect GitHub resources and run copilot doctor before retrying.' + : 'No further setup changes will be applied. Any PAT already created in GitHub still exists until you delete it there.'); + logInfo('The local browser page shows the result. Choose “Close local session” there, or stop this command with Ctrl+C.'); + await webServer.closed; + } + releaseSetupGuard?.(); } }); } - -function collectSecret(value: string, previous: Record): Record { - const separator = value.indexOf('='); - if (separator <= 0) throw new Error('--secret must use NAME=VALUE syntax.'); - const name = value.slice(0, separator).trim(); - const secret = value.slice(separator + 1); - if (!/^[A-Z][A-Z0-9_]*$/.test(name) || !secret) throw new Error('--secret must use a non-empty NAME=VALUE with an uppercase secret name.'); - return { ...previous, [name]: secret }; -} - -function collectApprovalCheck(value: string, previous: string[]): string[] { - return [...previous, value]; -} - -function setupPatPermissionDelta( - before: readonly SetupTokenPermissionRequirement[], - after: readonly SetupTokenPermissionRequirement[], -): string[] { - const previous = new Map(before.filter(item => item.applicability === 'required') - .map(item => [`${item.scope}:${item.permission.toLowerCase()}`, item.level])); - return after.filter(item => item.applicability === 'required' - && (previous.get(`${item.scope}:${item.permission.toLowerCase()}`) === undefined - || (previous.get(`${item.scope}:${item.permission.toLowerCase()}`) === 'read' && item.level === 'write'))) - .map(item => `${item.scope} ${item.permission} ${item.level}`); -} - -function loadSetupOverrides(options: { - config?: string; - agent?: string; - features?: string; - issueWorkflows?: string; - agentGuidance?: string; - variablesScope?: string; - secretsScope?: string; - variablesVisibility?: string; - secretsVisibility?: string; - variableScope?: Record; - secretScope?: Record; - prApprovalMode?: string; - prApprovalCheck?: string[]; - prApprovalCoverageCheck?: string; - prApprovalAttestProducer?: boolean; -}): SetupConfigurationOverrides { - const fromFile = options.config ? loadSetupConfigurationOverrides(options.config) : {}; - const fromFlags: SetupConfigurationOverrides = {}; - if (options.prApprovalMode || options.prApprovalCheck?.length || options.prApprovalCoverageCheck || options.prApprovalAttestProducer) { - if (options.prApprovalMode && !['off', 'recommend', 'guarded'].includes(options.prApprovalMode)) { - throw new Error('--pr-approval-mode must be guarded, recommend, or off.'); - } - const checks = options.prApprovalCheck?.map(value => { - const [name, appId, workflowName] = value.split('|').map(item => item.trim()); - return { name, sourceAppId: Number(appId), workflowName }; - }); - fromFlags.pullRequestApproval = { - ...(options.prApprovalMode ? { mode: options.prApprovalMode as 'off' | 'recommend' | 'guarded' } : {}), - ...(checks?.length ? { testChecks: checks } : {}), - ...(options.prApprovalAttestProducer ? { producerAttested: true } : {}), - ...(options.prApprovalCoverageCheck ? { coverage: { mode: 'check', checkName: options.prApprovalCoverageCheck } } : {}), - }; - } - if (options.agent) { - if (!['codex', 'opencode', 'cursor'].includes(options.agent)) { - throw new Error('--agent must be one of: codex, opencode, cursor.'); - } - fromFlags.agents = Object.fromEntries( - ['planner', 'findings', 'reviewer', 'fixer', 'tester'].map(task => [task, { provider: options.agent }]), - ) as SetupConfigurationOverrides['agents']; - } - if (options.features) { - if (options.features.trim().toLowerCase() === 'all') { - fromFlags.features = Object.fromEntries(Object.keys(SETUP_FEATURE_DESCRIPTIONS).map(feature => [feature, true])); - } else { - const requested = options.features.split(',').map(feature => feature.trim()).filter(Boolean); - const unknown = requested.filter(feature => !Object.prototype.hasOwnProperty.call(SETUP_FEATURE_DESCRIPTIONS, feature)); - if (unknown.length > 0) throw new Error(`Unknown setup feature(s): ${unknown.join(', ')}.`); - fromFlags.features = Object.fromEntries(Object.keys(SETUP_FEATURE_DESCRIPTIONS).map(feature => [feature, requested.includes(feature)])); - } - } - if (options.issueWorkflows) { - const raw = options.issueWorkflows.trim().toLowerCase(); - const requested = raw === 'all' ? [...ISSUE_WORKFLOW_KINDS] : raw.split(',').map(item => item.trim()).filter(Boolean); - const unknown = requested.filter(item => !ISSUE_WORKFLOW_KINDS.includes(item as IssueWorkflowKind)); - if (unknown.length > 0) throw new Error(`Unknown issue workflow(s): ${unknown.join(', ')}.`); - if (new Set(requested).size !== requested.length) throw new Error('Issue workflow selection cannot contain duplicates.'); - fromFlags.issueWorkflows = { enabled: requested as IssueWorkflowKind[] }; - } - if (options.agentGuidance) { - const mode = options.agentGuidance.trim().toLowerCase(); - if (!['prompt', 'create-if-missing', 'disabled'].includes(mode)) throw new Error('--agent-guidance must be prompt, create-if-missing, or disabled.'); - fromFlags.repositoryAgentGuidance = { agentsPointer: mode as 'prompt' | 'create-if-missing' | 'disabled', enabled: mode !== 'disabled' }; - } - const storage: NonNullable = {}; - if (options.variablesScope || options.variablesVisibility || Object.keys(options.variableScope ?? {}).length > 0) { - storage.variables = { - ...(options.variablesScope ? { defaultScope: parseScope(options.variablesScope, '--variables-scope') } : {}), - ...(options.variablesVisibility ? { organizationVisibility: parseVisibility(options.variablesVisibility, '--variables-visibility') } : {}), - ...(Object.keys(options.variableScope ?? {}).length > 0 ? { overrides: options.variableScope } : {}), - }; - } - if (options.secretsScope || options.secretsVisibility || Object.keys(options.secretScope ?? {}).length > 0) { - storage.secrets = { - ...(options.secretsScope ? { defaultScope: parseScope(options.secretsScope, '--secrets-scope') } : {}), - ...(options.secretsVisibility ? { organizationVisibility: parseVisibility(options.secretsVisibility, '--secrets-visibility') } : {}), - ...(Object.keys(options.secretScope ?? {}).length > 0 ? { overrides: options.secretScope } : {}), - }; - } - if (Object.keys(storage).length > 0) fromFlags.storage = storage; - return mergeSetupOverrides(fromFile, fromFlags); -} - -function mergeSetupOverrides( - fileOverrides: SetupConfigurationOverrides, - flagOverrides: SetupConfigurationOverrides, -): SetupConfigurationOverrides { - return { - ...fileOverrides, - ...flagOverrides, - features: { ...fileOverrides.features, ...flagOverrides.features }, - agents: { ...fileOverrides.agents, ...flagOverrides.agents }, - repository: { ...fileOverrides.repository, ...flagOverrides.repository }, - ai: { ...fileOverrides.ai, ...flagOverrides.ai }, - pullRequestApproval: { - ...fileOverrides.pullRequestApproval, - ...flagOverrides.pullRequestApproval, - coverage: { ...fileOverrides.pullRequestApproval?.coverage, ...flagOverrides.pullRequestApproval?.coverage }, - } as SetupConfigurationOverrides['pullRequestApproval'], - projects: { ...fileOverrides.projects, ...flagOverrides.projects }, - issueWorkflows: { ...fileOverrides.issueWorkflows, ...flagOverrides.issueWorkflows }, - repositoryAgentGuidance: { ...fileOverrides.repositoryAgentGuidance, ...flagOverrides.repositoryAgentGuidance }, - storage: { - ...fileOverrides.storage, - ...flagOverrides.storage, - secrets: { ...fileOverrides.storage?.secrets, ...flagOverrides.storage?.secrets, overrides: { ...fileOverrides.storage?.secrets?.overrides, ...flagOverrides.storage?.secrets?.overrides } }, - variables: { ...fileOverrides.storage?.variables, ...flagOverrides.storage?.variables, overrides: { ...fileOverrides.storage?.variables?.overrides, ...flagOverrides.storage?.variables?.overrides } }, - }, - }; -} - -function collectScope(value: string, previous: Record): Record { - const separator = value.indexOf('='); - if (separator <= 0) throw new Error('Scope overrides must use NAME=repository or NAME=organization syntax.'); - const name = value.slice(0, separator).trim(); - const scope = value.slice(separator + 1).trim().toLowerCase(); - if (!/^[A-Z][A-Z0-9_]*$/.test(name) || !['repository', 'organization'].includes(scope)) { - throw new Error('Scope overrides must use an uppercase NAME and repository or organization scope.'); - } - return { ...previous, [name]: scope as SetupResourceScope }; -} - -function parseScope(value: string, flag: string): 'repository' | 'organization' { - const normalized = value.trim().toLowerCase(); - if (normalized !== 'repository' && normalized !== 'organization') throw new Error(`${flag} must be repository or organization.`); - return normalized; -} - -function parseVisibility(value: string, flag: string): 'all' | 'private' | 'selected' { - const normalized = value.trim().toLowerCase(); - if (!['all', 'private', 'selected'].includes(normalized)) throw new Error(`${flag} must be selected, private, or all.`); - return normalized as 'all' | 'private' | 'selected'; -} diff --git a/src/cli/setup_apply_snapshot.ts b/src/cli/setup_apply_snapshot.ts new file mode 100644 index 000000000..42c2d69d3 --- /dev/null +++ b/src/cli/setup_apply_snapshot.ts @@ -0,0 +1,42 @@ +import { createHash } from 'node:crypto'; +import { lstatSync, readFileSync } from 'node:fs'; +import { resolve, relative, isAbsolute, sep } from 'node:path'; + +/** Captures only the selected setup paths. Missing files are part of the snapshot. */ +export function captureSetupApplySnapshot(repositoryRoot: string, selectedFiles: readonly string[]): Readonly> { + const root = resolve(repositoryRoot); + const result: Record = {}; + for (const name of [...new Set(selectedFiles)].sort()) { + const path = resolve(root, name); + const inside = relative(root, path); + if (isAbsolute(name) || !inside || inside === '..' || inside.startsWith(`..${sep}`)) { + throw new Error('The setup plan contains a path outside the repository.'); + } + // A lexically in-repository path can still escape through a parent symlink. + let prefix = root; + for (const segment of inside.split(sep)) { + prefix = resolve(prefix, segment); + try { + if (lstatSync(prefix).isSymbolicLink()) throw new Error(`Setup path ${name} traverses a symbolic link.`); + } catch (cause) { + if (cause && typeof cause === 'object' && 'code' in cause && cause.code === 'ENOENT') break; + throw cause; + } + } + try { + const stat = lstatSync(path); + if (stat.isFile() && stat.size <= 5 * 1024 * 1024) { + result[name] = `file:${createHash('sha256').update(readFileSync(path)).digest('hex')}`; + } else throw new Error(`Cannot safely snapshot setup file ${name}.`); + } catch (cause) { + if (cause && typeof cause === 'object' && 'code' in cause && cause.code === 'ENOENT') result[name] = 'missing'; + else throw cause; + } + } + return result; +} + +export function setupApplySnapshotMatches(repositoryRoot: string, selectedFiles: readonly string[], expected: Readonly>): boolean { + const current = captureSetupApplySnapshot(repositoryRoot, selectedFiles); + return JSON.stringify(current) === JSON.stringify(expected); +} diff --git a/src/cli/setup_command_options.ts b/src/cli/setup_command_options.ts new file mode 100644 index 000000000..13a0c8ef5 --- /dev/null +++ b/src/cli/setup_command_options.ts @@ -0,0 +1,127 @@ +import { loadSetupConfigurationOverrides } from './setup_config_file'; +import { SETUP_FEATURE_DESCRIPTIONS, type SetupConfigurationOverrides } from '../application/policies/setup_configuration_policy'; +import { mergeSetupOverrides } from '../application/policies/merge_setup_overrides_policy'; +import type { SetupResourceScope } from '../domain/setup'; +import { ISSUE_WORKFLOW_KINDS, type IssueWorkflowKind } from '../domain/issue_workflow_profile'; + +export function collectSecret(value: string, previous: Record): Record { + const separator = value.indexOf('='); + if (separator <= 0) throw new Error('--secret must use NAME=VALUE syntax.'); + const name = value.slice(0, separator).trim(); + const secret = value.slice(separator + 1); + if (!/^[A-Z][A-Z0-9_]*$/.test(name) || !secret) throw new Error('--secret must use a non-empty NAME=VALUE with an uppercase secret name.'); + return { ...previous, [name]: secret }; +} + +export function collectApprovalCheck(value: string, previous: string[]): string[] { + return [...previous, value]; +} + +export interface SetupCommandOverrideOptions { + config?: string; + agent?: string; + features?: string; + issueWorkflows?: string; + agentGuidance?: string; + variablesScope?: string; + secretsScope?: string; + variablesVisibility?: string; + secretsVisibility?: string; + variableScope?: Record; + secretScope?: Record; + prApprovalMode?: string; + prApprovalCheck?: string[]; + prApprovalCoverageCheck?: string; + prApprovalAttestProducer?: boolean; +} + +export function loadSetupOverrides(options: SetupCommandOverrideOptions): SetupConfigurationOverrides { + const fromFile = options.config ? loadSetupConfigurationOverrides(options.config) : {}; + const fromFlags: SetupConfigurationOverrides = {}; + if (options.prApprovalMode || options.prApprovalCheck?.length || options.prApprovalCoverageCheck || options.prApprovalAttestProducer) { + if (options.prApprovalMode && !['off', 'recommend', 'guarded'].includes(options.prApprovalMode)) { + throw new Error('--pr-approval-mode must be guarded, recommend, or off.'); + } + const checks = options.prApprovalCheck?.map(value => { + const [name, appId, workflowName] = value.split('|').map(item => item.trim()); + return { name, sourceAppId: Number(appId), workflowName }; + }); + fromFlags.pullRequestApproval = { + ...(options.prApprovalMode ? { mode: options.prApprovalMode as 'off' | 'recommend' | 'guarded' } : {}), + ...(checks?.length ? { testChecks: checks } : {}), + ...(options.prApprovalAttestProducer ? { producerAttested: true } : {}), + ...(options.prApprovalCoverageCheck ? { coverage: { mode: 'check', checkName: options.prApprovalCoverageCheck } } : {}), + }; + } + if (options.agent) { + if (!['codex', 'opencode', 'cursor'].includes(options.agent)) { + throw new Error('--agent must be one of: codex, opencode, cursor.'); + } + fromFlags.agents = Object.fromEntries( + ['planner', 'findings', 'reviewer', 'fixer', 'tester'].map(task => [task, { provider: options.agent }]), + ) as SetupConfigurationOverrides['agents']; + } + if (options.features) { + if (options.features.trim().toLowerCase() === 'all') { + fromFlags.features = Object.fromEntries(Object.keys(SETUP_FEATURE_DESCRIPTIONS).map(feature => [feature, true])); + } else { + const requested = options.features.split(',').map(feature => feature.trim()).filter(Boolean); + const unknown = requested.filter(feature => !Object.prototype.hasOwnProperty.call(SETUP_FEATURE_DESCRIPTIONS, feature)); + if (unknown.length > 0) throw new Error(`Unknown setup feature(s): ${unknown.join(', ')}.`); + fromFlags.features = Object.fromEntries(Object.keys(SETUP_FEATURE_DESCRIPTIONS).map(feature => [feature, requested.includes(feature)])); + } + } + if (options.issueWorkflows) { + const raw = options.issueWorkflows.trim().toLowerCase(); + const requested = raw === 'all' ? [...ISSUE_WORKFLOW_KINDS] : raw.split(',').map(item => item.trim()).filter(Boolean); + const unknown = requested.filter(item => !ISSUE_WORKFLOW_KINDS.includes(item as IssueWorkflowKind)); + if (unknown.length > 0) throw new Error(`Unknown issue workflow(s): ${unknown.join(', ')}.`); + if (new Set(requested).size !== requested.length) throw new Error('Issue workflow selection cannot contain duplicates.'); + fromFlags.issueWorkflows = { enabled: requested as IssueWorkflowKind[] }; + } + if (options.agentGuidance) { + const mode = options.agentGuidance.trim().toLowerCase(); + if (!['prompt', 'create-if-missing', 'disabled'].includes(mode)) throw new Error('--agent-guidance must be prompt, create-if-missing, or disabled.'); + fromFlags.repositoryAgentGuidance = { agentsPointer: mode as 'prompt' | 'create-if-missing' | 'disabled', enabled: mode !== 'disabled' }; + } + const storage: NonNullable = {}; + if (options.variablesScope || options.variablesVisibility || Object.keys(options.variableScope ?? {}).length > 0) { + storage.variables = { + ...(options.variablesScope ? { defaultScope: parseScope(options.variablesScope, '--variables-scope') } : {}), + ...(options.variablesVisibility ? { organizationVisibility: parseVisibility(options.variablesVisibility, '--variables-visibility') } : {}), + ...(Object.keys(options.variableScope ?? {}).length > 0 ? { overrides: options.variableScope } : {}), + }; + } + if (options.secretsScope || options.secretsVisibility || Object.keys(options.secretScope ?? {}).length > 0) { + storage.secrets = { + ...(options.secretsScope ? { defaultScope: parseScope(options.secretsScope, '--secrets-scope') } : {}), + ...(options.secretsVisibility ? { organizationVisibility: parseVisibility(options.secretsVisibility, '--secrets-visibility') } : {}), + ...(Object.keys(options.secretScope ?? {}).length > 0 ? { overrides: options.secretScope } : {}), + }; + } + if (Object.keys(storage).length > 0) fromFlags.storage = storage; + return mergeSetupOverrides(fromFile, fromFlags); +} + +export function collectScope(value: string, previous: Record): Record { + const separator = value.indexOf('='); + if (separator <= 0) throw new Error('Scope overrides must use NAME=repository or NAME=organization syntax.'); + const name = value.slice(0, separator).trim(); + const scope = value.slice(separator + 1).trim().toLowerCase(); + if (!/^[A-Z][A-Z0-9_]*$/.test(name) || !['repository', 'organization'].includes(scope)) { + throw new Error('Scope overrides must use an uppercase NAME and repository or organization scope.'); + } + return { ...previous, [name]: scope as SetupResourceScope }; +} + +function parseScope(value: string, flag: string): 'repository' | 'organization' { + const normalized = value.trim().toLowerCase(); + if (normalized !== 'repository' && normalized !== 'organization') throw new Error(`${flag} must be repository or organization.`); + return normalized; +} + +function parseVisibility(value: string, flag: string): 'all' | 'private' | 'selected' { + const normalized = value.trim().toLowerCase(); + if (!['all', 'private', 'selected'].includes(normalized)) throw new Error(`${flag} must be selected, private, or all.`); + return normalized as 'all' | 'private' | 'selected'; +} diff --git a/src/cli/setup_credential_prompt_adapter.ts b/src/cli/setup_credential_prompt_adapter.ts index fe10d47b9..c09c43872 100644 --- a/src/cli/setup_credential_prompt_adapter.ts +++ b/src/cli/setup_credential_prompt_adapter.ts @@ -11,13 +11,10 @@ import type { SetupGithubIdentity } from '../application/ports/setup_pat_identit import { color, renderBox, statusIcon } from './setup_prompt_rendering'; import type { SetupTokenPermissionRequirement } from '../domain/setup_token_permissions'; import { renderSetupTokenPermissionRequirements } from './setup_token_permission_presenter'; +import { SetupInteractionCancelledError } from '../application/errors/setup_interaction_cancelled_error'; -export class SetupTerminalCancelledError extends Error { - constructor() { - super('Setup input was cancelled.'); - this.name = 'SetupTerminalCancelledError'; - } -} +/** @deprecated Use the presentation-neutral cancellation signal in new adapters. */ +export const SetupTerminalCancelledError = SetupInteractionCancelledError; export class SetupCredentialPromptAdapter implements SetupCredentialPromptPort { private setupPatGuide?: string; diff --git a/src/cli/setup_session_guard.ts b/src/cli/setup_session_guard.ts new file mode 100644 index 000000000..501878595 --- /dev/null +++ b/src/cli/setup_session_guard.ts @@ -0,0 +1,49 @@ +import { createHash, randomBytes } from 'node:crypto'; +import { execFileSync } from 'node:child_process'; +import { closeSync, existsSync, openSync, readFileSync, realpathSync, unlinkSync, writeFileSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; + +interface GuardRecord { pid: number; nonce: string; repository: string } + +/** One cooperative setup process per canonical checkout; no credential is stored in the lock. */ +export function acquireSetupSessionGuard(cwd: string): () => void { + const top = execFileSync('git', ['-C', cwd, 'rev-parse', '--show-toplevel'], { encoding: 'utf8' }).trim(); + const repository = realpathSync(top); + const hash = createHash('sha256').update(repository).digest('hex').slice(0, 32); + const lockPath = join(tmpdir(), `copilot-setup-${hash}.lock`); + const record: GuardRecord = { pid: process.pid, nonce: randomBytes(16).toString('hex'), repository }; + for (let attempt = 0; attempt < 2; attempt += 1) { + try { + const fd = openSync(lockPath, 'wx', 0o600); + try { writeFileSync(fd, JSON.stringify(record)); } finally { closeSync(fd); } + return () => { + try { + const current = JSON.parse(readFileSync(lockPath, 'utf8')) as GuardRecord; + if (current.pid === record.pid && current.nonce === record.nonce && current.repository === record.repository) unlinkSync(lockPath); + } catch { /* Missing or replaced lock is not ours to remove. */ } + }; + } catch (cause) { + if (!cause || typeof cause !== 'object' || !('code' in cause) || cause.code !== 'EEXIST') throw cause; + let existing: GuardRecord; + try { existing = JSON.parse(readFileSync(lockPath, 'utf8')) as GuardRecord; } + catch { throw new Error('A setup lock exists but cannot be verified. Inspect it before retrying.'); } + if (!Number.isSafeInteger(existing.pid) || existing.pid <= 0 || existing.repository !== repository || !existing.nonce) { + throw setupLockError('A setup lock has unexpected contents. Inspect it before retrying.', cause); + } + try { + process.kill(existing.pid, 0); + throw setupLockError(`Another setup process (${existing.pid}) is active for this checkout. Finish or stop it before starting a second setup.`, cause); + } catch (checkError) { + if (!checkError || typeof checkError !== 'object' || !('code' in checkError) || checkError.code !== 'ESRCH') throw checkError; + } + // Recover only a verified dead owner and only if the lock has not changed meanwhile. + if (existsSync(lockPath) && readFileSync(lockPath, 'utf8') === JSON.stringify(existing)) unlinkSync(lockPath); + } + } + throw new Error('Could not acquire the local setup lock.'); +} + +function setupLockError(message: string, cause: unknown): Error { + return Object.assign(new Error(message), { cause }); +} diff --git a/src/cli/web_setup_adapters.ts b/src/cli/web_setup_adapters.ts new file mode 100644 index 000000000..3b0645d39 --- /dev/null +++ b/src/cli/web_setup_adapters.ts @@ -0,0 +1,171 @@ +import type { SetupConfigurationCollectorPort, SetupPlanConfirmationPort, SetupPlanPresenterPort } from '../application/ports/setup_terminal_ports'; +import type { SetupCredentialPromptPort, SetupWorkflowUpdatePromptPort } from '../application/ports/setup_wizard_ports'; +import type { SetupTokenPermissionPresenterPort } from '../application/ports/setup_token_permission_ports'; +import type { SetupJourneyPresenterPort } from '../application/usecases/setup/setup_journey_use_case'; +import type { SetupGithubIdentity } from '../application/ports/setup_pat_identity_ports'; +import type { SetupCredentialCheck, SetupCredentialDecision, SetupCredentialRequirement, SetupCredentialValue, SetupPlan, SetupWorkflowComparison } from '../domain/setup'; +import type { SetupTokenPermissionReport, SetupTokenPermissionRequirement, SetupTokenRole } from '../domain/setup_token_permissions'; +import type { SetupQuestionnaireContext, SetupQuestionnaireState } from '../domain/setup_questionnaire'; +import { setupQuestionnaireStateLabel, transitionSetupQuestionnaire } from '../application/policies/setup_questionnaire_policy'; +import { SetupInteractionCancelledError } from '../application/errors/setup_interaction_cancelled_error'; +import { WebSetupBridge, toWebSetupPlan } from './web_setup_bridge'; + +export class WebSetupQuestionnaireCollector implements SetupConfigurationCollectorPort { + constructor(private readonly bridge: WebSetupBridge, private readonly pass = 1) {} + + async collect(initial: SetupQuestionnaireState, context: SetupQuestionnaireContext): Promise { + let state = initial; + while (state.terminal === 'collecting' && state.question) { + if (state.validation) this.bridge.message(state.validation, 'warning'); + const value = await this.bridge.ask({ + kind: 'question', title: setupQuestionnaireStateLabel(state.stateId), + question: state.question, phase: state.phase ?? 'full', pass: this.pass, + }); + state = transitionSetupQuestionnaire(state, value === undefined ? { kind: 'cancel' } : { kind: 'answer', value }, context); + } + return state; + } +} + +export class WebSetupPlanPresenter implements SetupPlanPresenterPort { + constructor(private readonly bridge: WebSetupBridge) {} + present(plan: SetupPlan): void { + this.bridge.message(`Plan ready: ${plan.selectedFiles.length} files, ${plan.variables.length} Variables and ${plan.requiredSecrets.length} Secret names. Review it before continuing.`); + } +} + +export class WebSetupPlanConfirmation implements SetupPlanConfirmationPort { + constructor(private readonly bridge: WebSetupBridge) {} + async confirm(plan: SetupPlan): Promise<{ kind: 'approved' | 'declined' | 'cancelled' }> { + const response = await this.bridge.ask({ kind: 'plan', title: 'Review your setup plan', plan: toWebSetupPlan(plan) }); + return { kind: response === undefined ? 'cancelled' : response === 'approve' ? 'approved' : 'declined' }; + } +} + +export class WebSetupWorkflowUpdatePrompt implements SetupWorkflowUpdatePromptPort { + constructor(private readonly bridge: WebSetupBridge) {} + async confirmWorkflowUpdates(comparisons: readonly SetupWorkflowComparison[], forcedByFlag: boolean): Promise { + const changed = comparisons.filter(item => item.status === 'changed' || item.status === 'unmanaged'); + if (!changed.length) return false; + if (forcedByFlag) return true; + const answer = await this.bridge.ask({ + kind: 'confirm', title: 'Update existing workflows?', + description: changed.map(item => `${item.destination} (${item.status})`).join('\n'), + choices: ['Keep existing', 'Update setup-managed workflows'], + }); + if (answer === undefined) throw new SetupInteractionCancelledError(); + return answer === 'Update setup-managed workflows'; + } +} + +export class WebSetupPermissionPresenter implements SetupTokenPermissionPresenterPort { + constructor(private readonly bridge: WebSetupBridge) {} + showRequirements(role: SetupTokenRole, requirements: readonly SetupTokenPermissionRequirement[]): void { this.bridge.requirements(role, requirements); } + showDetailedRequirements(role: SetupTokenRole, requirements: readonly SetupTokenPermissionRequirement[]): void { this.bridge.requirements(role, requirements); } + showReport(report: SetupTokenPermissionReport): void { this.bridge.report(report); } +} + +export class WebSetupJourneyPresenter implements SetupJourneyPresenterPort { + constructor(private readonly bridge: WebSetupBridge) {} + present(view: Parameters[0]): void { this.bridge.setJourney(view); } +} + +export class WebSetupCredentialPrompt implements SetupCredentialPromptPort { + private setupGuide?: string; + private workflowGuide?: string; + private guidedSetup = false; + private botIdentity?: SetupGithubIdentity; + private resolveBot?: (login: string) => Promise; + private workflowRequirements?: readonly SetupTokenPermissionRequirement[]; + + constructor(private readonly bridge: WebSetupBridge) {} + get usedGuidedSetupPat(): boolean { return this.guidedSetup; } + get guidedWorkflowBotIdentity(): SetupGithubIdentity | undefined { return this.botIdentity; } + configureSetupPatGuide(url: string): void { this.setupGuide = url; } + useManualSetupPat(): void { this.guidedSetup = false; this.setupGuide = undefined; } + async chooseSetupPatMethod(): Promise<'guided' | 'manual'> { + this.guidedSetup = await this.choice('How will you provide your setup PAT?', ['Guided GitHub link', 'Manual PAT']) === 'Guided GitHub link'; + return this.guidedSetup ? 'guided' : 'manual'; + } + async chooseSetupOwnerKind(): Promise<'Organization' | 'User' | 'unknown'> { + const answer = await this.choice('What kind of GitHub account owns this repository?', ['Organization', 'Personal account', 'Not sure']); + return answer === 'Organization' ? 'Organization' : answer === 'Personal account' ? 'User' : 'unknown'; + } + async reviewSetupPatIntent(): Promise<'continue' | 'revise' | 'manual' | 'details'> { + const answer = await this.choice('Review these provisional setup PAT grants', ['Continue to GitHub', 'Review setup choices again', 'View full permission table', 'Enter a PAT manually']); + return answer === 'Review setup choices again' ? 'revise' : answer === 'View full permission table' ? 'details' + : answer === 'Enter a PAT manually' ? 'manual' : 'continue'; + } + async requestSetupPat(): Promise { + return this.secret('Temporary setup PAT', + 'Use the operator account in GitHub. Complete 2FA there, switch to Only select repositories, select this repository, and copy the generated token here. This token is for this run only; delete it in GitHub afterwards.', + this.guidedSetup ? this.setupGuide : undefined); + } + async confirmGuidedSetupAccount(account?: string): Promise { + if (!this.guidedSetup) return true; + if (!account) return false; + return await this.choice(`GitHub authenticated the setup PAT as @${account}. Is that the intended operator account?`, ['Yes, continue', 'No, stop']) === 'Yes, continue'; + } + showUpdatedSetupPatLink(url: string, stage: 'bootstrap' | 'final', delta?: readonly string[]): void { + this.bridge.message(`Setup PAT ${stage === 'final' ? 'permissions changed' : 'access failed'}. No setup mutation started. ${delta?.join(', ') ?? ''} Create a corrected PAT using the updated GitHub link.`, 'warning', url); + } + showSetupPatCleanupReminder(): void { + if (this.guidedSetup) this.bridge.message('Delete the temporary setup PAT in GitHub Settings after this run. Closing Copilot does not revoke it.', 'warning', 'https://github.com/settings/personal-access-tokens'); + } + async confirmUnverifiableTokenPermissions(report: SetupTokenPermissionReport): Promise { + const writes = report.checks.filter(item => item.applicability === 'required' && item.level === 'write' && item.status === 'unverifiable'); + if (!report.confirmationRequired || writes.length === 0) return false; + return await this.choice('GitHub cannot safely prove these write grants without a mutation. Confirm they are configured exactly as shown.', ['No, stop', 'Yes, I checked them']) === 'Yes, I checked them'; + } + configureWorkflowPatGuide(url: string, resolveIdentity: (login: string) => Promise, requirements?: readonly SetupTokenPermissionRequirement[]): void { + this.workflowGuide = url; this.resolveBot = resolveIdentity; this.workflowRequirements = requirements; + } + explainCredentialSeparation(requirements: readonly SetupCredentialRequirement[]): void { + this.bridge.message(`The bot PAT is separate from your setup PAT. Runtime credentials (${requirements.map(item => item.name).join(', ')}) become GitHub Actions Secrets; existing Secret values cannot be read back. This browser flow will not dispatch or install a credential-health workflow before Apply. Re-enter an existing bot PAT so its grants can be audited.`, 'info'); + } + async requestWorkflowPat(requirement: SetupCredentialRequirement, current?: SetupCredentialCheck): Promise { + let guide: string | undefined; + let botInfo = ''; + if (this.workflowGuide) { + const method = await this.choice('How will you provide the bot PAT?', ['Guided GitHub link', 'Manual PAT']); + if (method === 'Guided GitHub link') { + const login = await this.text('Expected GitHub bot login', 'Enter the bot account login, without @. We will verify its numeric account ID against the token.'); + if (!login || !/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/.test(login)) throw new Error('Enter a valid GitHub bot login.'); + this.botIdentity = await this.resolveBot!(login); + guide = this.workflowGuide; + botInfo = `Expected bot account: @${this.botIdentity.login} (GitHub ID ${this.botIdentity.id}). Open GitHub as this account, not the setup operator. `; + } else if (this.workflowRequirements) this.bridge.requirements('workflow', this.workflowRequirements); + } + const value = await this.secret(`${requirement.name} — bot account PAT`, + `${botInfo}Use the bot account, select only the intended repository and review all grants. Suggested expiry is 90 days. ${current ? `Existing Secret: ${current.status}; its value cannot be read back.` : ''}`, + guide); + return value ? { name: requirement.name, value } : undefined; + } + async requestApiKey(requirement: SetupCredentialRequirement, current?: SetupCredentialCheck): Promise { + const value = await this.secret(`${requirement.name} — ${requirement.provider ?? 'provider'} API key`, current?.message, undefined, Boolean(requirement.alternativeGroups?.length)); + return value ? { name: requirement.name, value } : undefined; + } + async chooseExistingCredential(requirement: SetupCredentialRequirement, check: SetupCredentialCheck): Promise { + const answer = await this.choice(`Existing ${requirement.name}: ${check.status}`, ['keep', 'replace', 'skip'], check.message); + return answer as SetupCredentialDecision; + } + showCredentialChecks(checks: readonly SetupCredentialCheck[]): void { + this.bridge.message(checks.map(item => `${item.name}: ${item.status} — ${item.message}`).join('\n'), checks.some(item => item.status === 'invalid') ? 'warning' : 'success'); + } + private async choice(title: string, choices: readonly string[], description?: string): Promise { + const answer = await this.bridge.ask({ kind: 'choice', title, choices, description }); + if (answer === undefined) throw new SetupInteractionCancelledError(); + if (!choices.includes(answer)) throw new Error('Invalid setup choice.'); + return answer; + } + private async text(title: string, description?: string): Promise { + const answer = await this.bridge.ask({ kind: 'text', title, description }); + if (answer === undefined) throw new SetupInteractionCancelledError(); + return answer.trim(); + } + private async secret(title: string, description?: string, link?: string, optional = false): Promise { + const answer = await this.bridge.ask({ kind: 'secret', title, description, optional, link }); + if (answer === undefined) throw new SetupInteractionCancelledError(); + return answer.trim(); + } +} diff --git a/src/cli/web_setup_bridge.ts b/src/cli/web_setup_bridge.ts new file mode 100644 index 000000000..2b19783ae --- /dev/null +++ b/src/cli/web_setup_bridge.ts @@ -0,0 +1,118 @@ +import { randomBytes, timingSafeEqual } from 'node:crypto'; +import type { SetupJourneyView } from '../application/policies/setup_journey_policy'; +import type { SetupPlan } from '../domain/setup'; +import type { SetupTokenPermissionReport, SetupTokenPermissionRequirement, SetupTokenRole } from '../domain/setup_token_permissions'; +import type { WebSetupPlan, WebSetupPrompt, WebSetupView } from '../application/contracts/web_setup_view'; + +/** A one-run, in-memory handoff. Values submitted by the browser are never part of a view. */ +export class WebSetupBridge { + private revision = 0; + private view: WebSetupView; + private pending?: { revision: number; resolve: (value: string | undefined) => void }; + private subscribers = new Set<(view: WebSetupView) => void>(); + private controller?: string; + private lastAnsweredRevision?: number; + private takeoverTicket = randomBytes(32).toString('hex'); + + constructor(repository: string) { + this.view = { revision: 0, repository }; + } + + snapshot(): WebSetupView { return this.view; } + setRepository(repository: string): void { this.publish({ repository }); } + + subscribe(listener: (view: WebSetupView) => void): () => void { + this.subscribers.add(listener); + return () => this.subscribers.delete(listener); + } + + bootstrap(): { controller: boolean; capability?: string; takeoverTicket: string } { + if (!this.controller) this.controller = randomBytes(32).toString('hex'); + // A second tab starts read-only. Its explicit takeover rotates the controller capability. + const first = !this.bootstrapped; + this.bootstrapped = true; + return { controller: first, ...(first ? { capability: this.controller } : {}), takeoverTicket: this.takeoverTicket }; + } + + private bootstrapped = false; + + takeOver(ticket: string): string | undefined { + if (!sameCapability(ticket, this.takeoverTicket)) return undefined; + this.controller = randomBytes(32).toString('hex'); + this.takeoverTicket = randomBytes(32).toString('hex'); + this.publish({ message: { tone: 'info', text: 'Control moved to this tab. The previous tab is now read-only.' } }); + return this.controller; + } + + isController(capability: string): boolean { + return Boolean(this.controller && sameCapability(capability, this.controller)); + } + + async ask(prompt: WebSetupPrompt): Promise { + if (this.pending || this.view.outcome) throw new Error('A setup decision is already pending or the session has ended.'); + const revision = this.revision + 1; + this.publish({ prompt, promptRevision: revision }); + return new Promise(resolve => { this.pending = { revision, resolve }; }); + } + + answer(revision: number, value: string): boolean { + if (!this.pending || this.pending.revision !== revision || this.view.outcome) return false; + const prompt = this.view.prompt; + if (prompt && (prompt.kind === 'choice' || prompt.kind === 'confirm') && !prompt.choices.includes(value)) return false; + const pending = this.pending; + this.pending = undefined; + this.lastAnsweredRevision = revision; + this.publish({ prompt: undefined, promptRevision: undefined }); + pending.resolve(value); + return true; + } + + wasAnswered(revision: number): boolean { return this.lastAnsweredRevision === revision; } + + cancel(): boolean { + if (this.view.journey?.mutationStarted || this.view.outcome) return false; + const pending = this.pending; + this.pending = undefined; + this.publish({ prompt: undefined, promptRevision: undefined, outcome: 'cancelled', message: { tone: 'warning', text: 'Setup cancelled before applying further changes. Any PAT created at GitHub still exists until you delete it there.' } }); + pending?.resolve(undefined); + return true; + } + + setJourney(journey: SetupJourneyView): void { this.publish({ journey }); } + message(text: string, tone: 'info' | 'success' | 'warning' | 'error' = 'info', link?: string): void { + this.publish({ message: { tone, text, ...(link ? { link } : {}) } }); + } + requirements(role: SetupTokenRole, requirements: readonly SetupTokenPermissionRequirement[]): void { + this.publish({ permissions: { role, requirements, report: undefined } }); + } + report(report: SetupTokenPermissionReport): void { + this.publish({ permissions: { role: report.role, requirements: this.view.permissions?.requirements, report } }); + } + finish(outcome: NonNullable, text: string): void { + if (this.view.outcome) return; + this.pending?.resolve(undefined); + this.pending = undefined; + this.publish({ prompt: undefined, promptRevision: undefined, outcome, message: { tone: outcome === 'complete' || outcome === 'dry-run' ? 'success' : 'warning', text } }); + } + + private publish(change: Partial): void { + this.revision += 1; + this.view = { ...this.view, ...change, revision: this.revision }; + for (const listener of this.subscribers) listener(this.view); + } +} + +function sameCapability(provided: string, expected: string): boolean { + if (!/^[a-f0-9]{64}$/.test(provided)) return false; + return timingSafeEqual(Buffer.from(provided, 'hex'), Buffer.from(expected, 'hex')); +} + +export function toWebSetupPlan(plan: SetupPlan): WebSetupPlan { + return { + files: plan.selectedFiles, + workflows: plan.workflowFiles, + variables: plan.variables.map(variable => variable.name), + secrets: plan.requiredSecrets, + warnings: plan.warnings, + }; +} diff --git a/src/cli/web_setup_server.ts b/src/cli/web_setup_server.ts new file mode 100644 index 000000000..1eac16c4a --- /dev/null +++ b/src/cli/web_setup_server.ts @@ -0,0 +1,207 @@ +import { createServer, type IncomingMessage, type ServerResponse } from 'node:http'; +import { readFile, realpath } from 'node:fs/promises'; +import { join, resolve, sep } from 'node:path'; +import { spawn } from 'node:child_process'; +import { WebSetupBridge } from './web_setup_bridge'; + +const MAX_BODY_BYTES = 8192; +const MAX_ANSWER_LENGTH = 4096; +const CSP = "default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' data:; connect-src 'self'; base-uri 'none'; form-action 'self'; frame-ancestors 'none'"; + +export interface WebSetupServer { + readonly url: string; + readonly closed: Promise; + close(): Promise; +} + +/** Transport only: setup policy and credential decisions live behind the bridge. */ +export async function startWebSetupServer(bridge: WebSetupBridge, assets = join(__dirname, '..', 'web')): Promise { + const assetRoot = await realpath(assets); + if (!(await realpath(join(assetRoot, 'index.html'))).startsWith(`${assetRoot}${sep}`)) { + throw new Error('Local setup index must be inside its packaged asset directory.'); + } + const indexHtml = (await readFile(join(assetRoot, 'index.html'))).toString('utf8'); + const allowedAssets = new Set([...indexHtml.matchAll(/(?:\.\/)?(assets\/[A-Za-z0-9._-]+\.(?:js|css))/g)] + .map(match => match[1])); + if (allowedAssets.size < 2) throw new Error('Local setup web assets are incomplete. Reinstall Copilot or use terminal setup.'); + for (const asset of allowedAssets) { + const packagedPath = await realpath(join(assetRoot, asset)); + if (!packagedPath.startsWith(`${assetRoot}${sep}`)) throw new Error('Local setup asset escapes its packaged directory.'); + await readFile(packagedPath); + } + let closeResolver: () => void = () => undefined; + const closed = new Promise(resolveClosed => { closeResolver = resolveClosed; }); + let closing = false; + let idleTimer: NodeJS.Timeout | undefined; + let resultTimer: NodeJS.Timeout | undefined; + const armIdle = (): void => { + if (idleTimer) clearTimeout(idleTimer); + idleTimer = setTimeout(() => { + if (!bridge.snapshot().journey?.mutationStarted && !bridge.snapshot().outcome) { + bridge.finish('blocked', 'This local setup session expired after 30 minutes without a decision. Start a new setup run; GitHub PATs are not revoked automatically.'); + } + }, 30 * 60 * 1000); + }; + const server = createServer(async (request, response) => { + const address = server.address(); + const origin = `http://127.0.0.1:${typeof address === 'object' && address ? address.port : 0}`; + const host = `127.0.0.1:${typeof address === 'object' && address ? address.port : 0}`; + response.setHeader('Content-Security-Policy', CSP); + response.setHeader('X-Content-Type-Options', 'nosniff'); + response.setHeader('Referrer-Policy', 'no-referrer'); + response.setHeader('Cache-Control', 'no-store'); + response.setHeader('Cross-Origin-Resource-Policy', 'same-origin'); + response.setHeader('X-Frame-Options', 'DENY'); + try { + if (request.headers.host !== host || request.headers['x-forwarded-host'] || request.headers.forwarded + || request.headers['x-forwarded-proto'] || request.headers['sec-fetch-site'] === 'cross-site') { + respond(response, 403, { error: 'Invalid local host or request context.' }); + return; + } + if (request.method === 'POST' && (request.headers.origin !== origin + || (request.headers.referer && !request.headers.referer.startsWith(`${origin}/`)))) { + respond(response, 403, { error: 'Invalid request origin.' }); + return; + } + if (request.method === 'GET' && request.url === '/api/bootstrap') { + respond(response, 200, bridge.bootstrap()); + return; + } + if (request.method === 'GET' && request.url === '/api/state') { + respond(response, 200, bridge.snapshot()); + return; + } + if (request.method === 'POST' && request.url === '/api/takeover') { + if (request.headers['content-type'] !== 'application/json') { respond(response, 415, { error: 'JSON required.' }); return; } + const body = await readJson(request); + const ticket = typeof body.ticket === 'string' ? body.ticket : ''; + const capability = bridge.takeOver(ticket); + if (capability) armIdle(); + respond(response, capability ? 200 : 403, capability ? { capability } : { error: 'Invalid takeover ticket.' }); + return; + } + if (request.method === 'POST' && request.url === '/api/answer') { + if (!bridge.isController(String(request.headers['x-setup-capability'] ?? ''))) { respond(response, 403, { error: 'This tab is read-only.' }); return; } + if (request.headers['content-type'] !== 'application/json') { respond(response, 415, { error: 'JSON required.' }); return; } + const body = await readJson(request); + if (!Number.isSafeInteger(body.revision) || (body.revision as number) <= 0 || typeof body.value !== 'string' || body.value.length > MAX_ANSWER_LENGTH) { + respond(response, 400, { error: 'Invalid answer.' }); return; + } + if (!bridge.isController(String(request.headers['x-setup-capability'] ?? ''))) { + respond(response, 403, { error: 'Control moved to another tab.' }); return; + } + const accepted = bridge.answer(body.revision as number, body.value); + const duplicate = !accepted && bridge.wasAnswered(body.revision as number); + if (accepted) armIdle(); + respond(response, accepted || duplicate ? 200 : 409, + accepted || duplicate ? { accepted: true, ...(duplicate ? { duplicate: true } : {}) } + : { error: 'This question changed. Refresh the current state.' }); + return; + } + if (request.method === 'POST' && request.url === '/api/cancel') { + if (!bridge.isController(String(request.headers['x-setup-capability'] ?? ''))) { respond(response, 403, { error: 'This tab is read-only.' }); return; } + if (request.headers['content-type'] !== 'application/json') { respond(response, 415, { error: 'JSON required.' }); return; } + await readJson(request); + if (!bridge.isController(String(request.headers['x-setup-capability'] ?? ''))) { + respond(response, 403, { error: 'Control moved to another tab.' }); return; + } + const cancelled = bridge.cancel(); + respond(response, cancelled ? 200 : 409, cancelled ? { cancelled: true } : { error: 'This setup has already started applying or ended.' }); + return; + } + if (request.method === 'POST' && request.url === '/api/close') { + if (!bridge.isController(String(request.headers['x-setup-capability'] ?? '')) || !bridge.snapshot().outcome) { + respond(response, 403, { error: 'Only the controller can close a finished session.' }); return; + } + if (request.headers['content-type'] !== 'application/json') { respond(response, 415, { error: 'JSON required.' }); return; } + await readJson(request); + if (!bridge.isController(String(request.headers['x-setup-capability'] ?? ''))) { + respond(response, 403, { error: 'Control moved to another tab.' }); return; + } + respond(response, 200, { closed: true }); + setImmediate(() => void close()); + return; + } + if (request.method !== 'GET') { respond(response, 405, { error: 'Method not allowed.' }); return; } + const pathname = request.url ?? ''; + if (pathname !== '/' && !/^\/assets\/[A-Za-z0-9._-]+$/.test(pathname)) { + respond(response, 404, { error: 'Not found.' }); return; + } + const relative = pathname === '/' ? 'index.html' : pathname.slice(1); + if (relative !== 'index.html' && !allowedAssets.has(relative)) { + respond(response, 404, { error: 'Not found.' }); return; + } + const file = resolve(assetRoot, relative); + const realFile = await realpath(file); + if (!realFile.startsWith(`${assetRoot}${sep}`)) { respond(response, 404, { error: 'Not found.' }); return; } + const content = await readFile(realFile); + const contentType = file.endsWith('.js') ? 'text/javascript; charset=utf-8' + : file.endsWith('.css') ? 'text/css; charset=utf-8' + : 'text/html; charset=utf-8'; + response.writeHead(200, { 'Content-Type': contentType }); + response.end(content); + } catch { + if (!response.headersSent) respond(response, 400, { error: 'Invalid local request.' }); + else response.end(); + } + }); + server.requestTimeout = 15_000; + server.headersTimeout = 15_000; + server.maxRequestsPerSocket = 250; + await new Promise((resolveListen, reject) => { + server.once('error', reject); + server.listen(0, '127.0.0.1', () => { server.off('error', reject); resolveListen(); }); + }); + const address = server.address(); + if (!address || typeof address === 'string') throw new Error('Unable to bind local setup server.'); + const url = `http://127.0.0.1:${address.port}/`; + const close = async (): Promise => { + if (closing) return closed; + closing = true; + if (idleTimer) clearTimeout(idleTimer); + if (hardTimer) clearTimeout(hardTimer); + if (resultTimer) clearTimeout(resultTimer); + unsubscribe?.(); + bridge.cancel(); + server.closeAllConnections(); + await new Promise(resolveClose => server.close(() => resolveClose())); + closeResolver(); + }; + armIdle(); + const hardTimer = setTimeout(() => { + if (!bridge.snapshot().journey?.mutationStarted && !bridge.snapshot().outcome) { + bridge.finish('blocked', 'This local setup session reached its four-hour limit. Start a new run; no prior approval can be replayed.'); + } + }, 4 * 60 * 60 * 1000); + const unsubscribe = bridge.subscribe(view => { + if (view.outcome && !resultTimer) resultTimer = setTimeout(() => void close(), 10 * 60 * 1000); + }); + return { url, closed, close }; +} + +function respond(response: ServerResponse, status: number, body: unknown): void { + response.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8' }); + response.end(JSON.stringify(body)); +} + +async function readJson(request: IncomingMessage): Promise> { + let size = 0; + const chunks: Buffer[] = []; + for await (const chunk of request) { + const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk); + size += buffer.length; + if (size > MAX_BODY_BYTES) throw new Error('Body too large.'); + chunks.push(buffer); + } + const parsed: unknown = JSON.parse(Buffer.concat(chunks).toString('utf8')); + if (!parsed || Array.isArray(parsed) || typeof parsed !== 'object') throw new Error('JSON object required.'); + return parsed as Record; +} + +export function openWebSetupBrowser(url: string): void { + const command = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'cmd' : 'xdg-open'; + const args = process.platform === 'win32' ? ['/c', 'start', '', url] : [url]; + const child = spawn(command, args, { stdio: 'ignore', detached: true, windowsHide: true }); + child.on('error', () => { /* The URL was already printed; manual opening remains available. */ }); + child.unref(); +} diff --git a/src/cli_context.ts b/src/cli_context.ts index 5661cba8a..e2418bc54 100644 --- a/src/cli_context.ts +++ b/src/cli_context.ts @@ -48,10 +48,15 @@ export function isInsideGitRepo(cwd: string): boolean { } } +/** Canonical checkout root for plans whose file paths are repository-relative. */ +export function getGitRepositoryRoot(cwd: string): string { + const root = execSync('git rev-parse --show-toplevel', { cwd, stdio: 'pipe' }).toString().trim(); + return realpathSync(root); +} + export function isGitRepositoryRoot(cwd: string): boolean { try { - const root = execSync('git rev-parse --show-toplevel', { cwd, stdio: 'pipe' }).toString().trim(); - return realpathSync(root) === realpathSync(cwd); + return getGitRepositoryRoot(cwd) === realpathSync(cwd); } catch { return false; } diff --git a/src/infrastructure/composition/__tests__/setup_token_permissions_composition_root.test.ts b/src/infrastructure/composition/__tests__/setup_token_permissions_composition_root.test.ts index 85fa527b7..25a409083 100644 --- a/src/infrastructure/composition/__tests__/setup_token_permissions_composition_root.test.ts +++ b/src/infrastructure/composition/__tests__/setup_token_permissions_composition_root.test.ts @@ -20,4 +20,13 @@ describe('setup token permission composition roots', () => { expect(createSetupCredentialsUseCase(prompt, presenter)).toBeInstanceOf(SetupCredentialsUseCase); expect(createSetupRemoteConfigurationReadPort()).toBeDefined(); }); + + it('omits workflow dispatch/bootstrap only in the web pre-Apply composition', () => { + const prompt = {} as SetupCredentialPromptPort; + const remoteHealth = (useCase: SetupCredentialsUseCase) => + (useCase as unknown as { remoteHealth?: unknown }).remoteHealth; + + expect(remoteHealth(createSetupCredentialsUseCase(prompt))).toBeDefined(); + expect(remoteHealth(createSetupCredentialsUseCase(prompt, undefined, { allowPreApplyHealthWorkflow: false }))).toBeUndefined(); + }); }); diff --git a/src/infrastructure/composition/setup_credentials_composition_root.ts b/src/infrastructure/composition/setup_credentials_composition_root.ts index ab5021235..454dd1065 100644 --- a/src/infrastructure/composition/setup_credentials_composition_root.ts +++ b/src/infrastructure/composition/setup_credentials_composition_root.ts @@ -14,13 +14,16 @@ import { createSetupTokenPermissionsUseCase } from './setup_token_permissions_co export function createSetupCredentialsUseCase( prompt: SetupCredentialPromptPort, permissionPresenter?: SetupTokenPermissionPresenterPort, + options: { allowPreApplyHealthWorkflow?: boolean } = {}, ): SetupCredentialsUseCase { const secretNames = new RepositorySecretNamesQueryRepository(createRepositoryVariablesClient()); return new SetupCredentialsUseCase( prompt, new SetupCredentialValidationAdapter(), secretNames, - new SetupRemoteCredentialHealthBootstrapAdapter(new OctokitCredentialHealthClientAdapter()), + options.allowPreApplyHealthWorkflow === false + ? undefined + : new SetupRemoteCredentialHealthBootstrapAdapter(new OctokitCredentialHealthClientAdapter()), createSetupTokenPermissionsUseCase(), permissionPresenter, ); diff --git a/src/infrastructure/specification_catalog_validator.cjs b/src/infrastructure/specification_catalog_validator.cjs index 066b96e7f..fc0a4fecf 100644 --- a/src/infrastructure/specification_catalog_validator.cjs +++ b/src/infrastructure/specification_catalog_validator.cjs @@ -159,8 +159,8 @@ function isInside(root, candidate) { function matchesFieldBoundary(field, relativePath) { if (field === 'specs') return /^specs\/(?!README\.md$|_template\.md$|CATALOG\.md$).+\.md$/.test(relativePath); if (field === 'workflows') return /^(?:\.github|setup)\/workflows\/.+\.ya?ml$/.test(relativePath); - if (field === 'entrypoints') return /^(?:src\/.+|action\.yml|package\.json)$/.test(relativePath); - if (field === 'code') return /^(?:src|scripts)\//.test(relativePath); + if (field === 'entrypoints') return /^(?:src\/.+|web\/src\/main\.ts|action\.yml|package\.json)$/.test(relativePath); + if (field === 'code') return /^(?:(?:src|scripts)\/|web\/src\/.+\.(?:ts|svelte|css)$)/.test(relativePath); if (field === 'tests') return /^src\/.*(?:__tests__\/.*\.test\.ts|\.test\.ts)$/.test(relativePath); if (field === 'documentation') return /^(?:docs\/.*\.(?:md|mdx)|README\.md|CONTRIBUTING\.md)$/.test(relativePath); return false; diff --git a/src/tooling/__tests__/validate_specification_catalog.test.ts b/src/tooling/__tests__/validate_specification_catalog.test.ts index de0964e31..0c2c3b413 100644 --- a/src/tooling/__tests__/validate_specification_catalog.test.ts +++ b/src/tooling/__tests__/validate_specification_catalog.test.ts @@ -72,6 +72,16 @@ describe('specification catalog validator', () => { expect(errors.some(error => error.includes('does not resolve to an existing file'))).toBe(true); }); + it('catalogues shipped browser sources but rejects browser paths outside the source tree', () => { + const catalog = cloneCatalog(); + const capability = catalog.capabilities.find(item => item.id === 'local-web-setup-assistant')!; + expect(capability.entrypoints).toContain('web/src/main.ts'); + expect(capability.code).toContain('web/src/components/PromptCard.svelte'); + capability.code.push('web/vite.config.mts'); + const errors = validator.validateCatalog(root, catalog); + expect(errors.some(error => error.includes('outside the code boundary: web/vite.config.mts'))).toBe(true); + }); + it('rejects an invalid status and verification date', () => { const catalog = cloneCatalog(); catalog.capabilities[0].status = 'done'; diff --git a/web/index.html b/web/index.html new file mode 100644 index 000000000..fbe640e1d --- /dev/null +++ b/web/index.html @@ -0,0 +1,13 @@ + + + + + + + Copilot · Setup studio + + +
          + + + diff --git a/web/src/App.svelte b/web/src/App.svelte new file mode 100644 index 000000000..40c3e7ae0 --- /dev/null +++ b/web/src/App.svelte @@ -0,0 +1,67 @@ + + + + + + +
          + +
          + +
          + + {#if $session.view?.journey?.choiceReviewPass && $session.view.journey.choiceReviewPass > 1 && !$session.view.outcome} +
          Reviewing saved choices — pass {$session.view.journey.choiceReviewPass}. This is the same setup run, not a restart.
          + {/if} + {#if !$session.controller && $session.view} + + {/if} + {#if $session.error}{/if} + {#if $session.view?.message} + + {/if} + + {#if $session.view?.outcome} + + {:else if $session.view?.prompt} +
          + {#key $session.view.promptRevision} + + {/key} + +
          + {#if $session.controller && $session.view.journey?.current !== 'Apply'}{/if} + {:else} + + {/if} +
          LOCALHOST ONLY NO CLOUD SETUP ACCOUNT GITHUB OWNS PAT ISSUANCE
          +
          +
          +
          diff --git a/web/src/components/ActionButton.svelte b/web/src/components/ActionButton.svelte new file mode 100644 index 000000000..b5c767d42 --- /dev/null +++ b/web/src/components/ActionButton.svelte @@ -0,0 +1,11 @@ + + + diff --git a/web/src/components/ChoicePrompt.svelte b/web/src/components/ChoicePrompt.svelte new file mode 100644 index 000000000..8da064b38 --- /dev/null +++ b/web/src/components/ChoicePrompt.svelte @@ -0,0 +1,9 @@ + + +
          {#each prompt.choices as option}{/each}
          diff --git a/web/src/components/ContextPanel.svelte b/web/src/components/ContextPanel.svelte new file mode 100644 index 000000000..8e7c5047e --- /dev/null +++ b/web/src/components/ContextPanel.svelte @@ -0,0 +1,16 @@ + + + diff --git a/web/src/components/CredentialPrompt.svelte b/web/src/components/CredentialPrompt.svelte new file mode 100644 index 000000000..18cddcf68 --- /dev/null +++ b/web/src/components/CredentialPrompt.svelte @@ -0,0 +1,23 @@ + + +{#if githubLink}Open the official GitHub PAT form

          Check the signed-in account, select Only select repositories, then select this repository. GitHub handles 2FA and creates the PAT.

          {/if} + + +{#if prompt.kind === 'secret'}

          Sent only to this local process. It will not be shown again or saved in browser storage.

          {/if} + diff --git a/web/src/components/PlanPrompt.svelte b/web/src/components/PlanPrompt.svelte new file mode 100644 index 000000000..842c9342f --- /dev/null +++ b/web/src/components/PlanPrompt.svelte @@ -0,0 +1,19 @@ + + +

          Review exactly what this run may change. The bot PAT and any additional credentials are collected next.

          +
          {#each sections as section}

          {section.title} {section.items.length}

            {#each section.items as item}
          • {item}
          • {/each}
          {/each}
          +{#if prompt.plan.warnings.length}

          Before you continue

            {#each prompt.plan.warnings as warning}
          • {warning}
          • {/each}
          {/if} +
          onSubmit('decline')} disabled={!controller || busy} /> onSubmit('approve')} disabled={!controller || busy} />
          diff --git a/web/src/components/PromptCard.svelte b/web/src/components/PromptCard.svelte new file mode 100644 index 000000000..79e23a4cd --- /dev/null +++ b/web/src/components/PromptCard.svelte @@ -0,0 +1,26 @@ + + +
          +
          CURRENT DECISIONSESSION {revision}
          + {#if 'description' in prompt && prompt.description}

          {prompt.description}

          {/if} + {#if prompt.kind === 'question'} + + {:else if prompt.kind === 'choice' || prompt.kind === 'confirm'} + + {:else if prompt.kind === 'text' || prompt.kind === 'secret'} + + {:else if prompt.kind === 'plan'} + + {/if} +
          diff --git a/web/src/components/QuestionPrompt.svelte b/web/src/components/QuestionPrompt.svelte new file mode 100644 index 000000000..a5ccfd33d --- /dev/null +++ b/web/src/components/QuestionPrompt.svelte @@ -0,0 +1,24 @@ + + +

          {prompt.question.label.replace(' (Space toggles, Enter confirms)', '')}

          {#if prompt.phase === 'permission-intent'}PERMISSION PREVIEW{/if}
          +{#if prompt.question.kind === 'boolean'} +
          +{:else if prompt.question.kind === 'choice'} + +{:else if prompt.question.kind === 'multi-select' || prompt.question.kind === 'scope-overrides'} +
          {#each (prompt.question.kind === 'multi-select' ? prompt.question.choices ?? [] : prompt.question.allowedNames ?? []) as option}{/each}
          +{:else} + +{/if} +

          Suggested answer: {String(prompt.question.defaultValue) || 'none'} · You can review choices again before creating your setup PAT.

          + onSubmit(submittedQuestionAnswer(prompt, value, selected))} disabled={!controller || busy} /> diff --git a/web/src/components/ResultPanel.svelte b/web/src/components/ResultPanel.svelte new file mode 100644 index 000000000..3bd508762 --- /dev/null +++ b/web/src/components/ResultPanel.svelte @@ -0,0 +1,18 @@ + + +

          {heading}

          {explanation}

          {#if controller}{/if}
          diff --git a/web/src/components/SetupHeader.svelte b/web/src/components/SetupHeader.svelte new file mode 100644 index 000000000..60541a30f --- /dev/null +++ b/web/src/components/SetupHeader.svelte @@ -0,0 +1,9 @@ + + +
          + +
          LOCAL SESSION
          +
          diff --git a/web/src/components/SetupIntro.svelte b/web/src/components/SetupIntro.svelte new file mode 100644 index 000000000..62a0d9706 --- /dev/null +++ b/web/src/components/SetupIntro.svelte @@ -0,0 +1,13 @@ + + +
          {view?.journey?.current?.toUpperCase() ?? 'GETTING READY'} {String(view?.journey?.position ?? 1).padStart(2, '0')} / 06
          +

          {title}

          +

          {view?.outcome ? 'The terminal contains the detailed result. Review the next steps below before closing this session.' : 'One decision at a time. Your choices shape the permissions, plan and credentials needed for this repository.'}

          diff --git a/web/src/components/SetupSidebar.svelte b/web/src/components/SetupSidebar.svelte new file mode 100644 index 000000000..230b0fe99 --- /dev/null +++ b/web/src/components/SetupSidebar.svelte @@ -0,0 +1,19 @@ + + + diff --git a/web/src/components/StatusBanner.svelte b/web/src/components/StatusBanner.svelte new file mode 100644 index 000000000..18222f6fa --- /dev/null +++ b/web/src/components/StatusBanner.svelte @@ -0,0 +1,17 @@ + + + diff --git a/web/src/components/ThemeSwitch.svelte b/web/src/components/ThemeSwitch.svelte new file mode 100644 index 000000000..1eb615f66 --- /dev/null +++ b/web/src/components/ThemeSwitch.svelte @@ -0,0 +1,11 @@ + + +
          + + + +
          diff --git a/web/src/components/WaitingPanel.svelte b/web/src/components/WaitingPanel.svelte new file mode 100644 index 000000000..b88d0c038 --- /dev/null +++ b/web/src/components/WaitingPanel.svelte @@ -0,0 +1 @@ +

          Working on the next step

          The local process is checking your answers and preparing the next decision. Keep this page open.

          diff --git a/web/src/lib/githubLink.ts b/web/src/lib/githubLink.ts new file mode 100644 index 000000000..8845a0366 --- /dev/null +++ b/web/src/lib/githubLink.ts @@ -0,0 +1,10 @@ +export function safeGithubLink(link?: string): string | undefined { + try { + const url = new URL(link ?? ''); + return url.protocol === 'https:' && url.hostname === 'github.com' + && (url.pathname === '/settings/personal-access-tokens' || url.pathname.startsWith('/settings/personal-access-tokens/')) + ? url.toString() : undefined; + } catch { + return undefined; + } +} diff --git a/web/src/lib/questionAnswer.ts b/web/src/lib/questionAnswer.ts new file mode 100644 index 000000000..8c196decf --- /dev/null +++ b/web/src/lib/questionAnswer.ts @@ -0,0 +1,25 @@ +import type { WebSetupPrompt } from '../../../src/application/contracts/web_setup_view'; + +type QuestionPrompt = Extract; + +export function initialQuestionAnswer(prompt: QuestionPrompt): { value: string; selected: string[] } { + const value = String(prompt.question.defaultValue ?? ''); + const defaults = value.split(',').map(item => item.trim()).filter(Boolean); + const selected = prompt.question.kind === 'multi-select' + ? (prompt.question.choices ?? []).filter(item => item !== 'All' && defaults.includes(item.split(' — ')[0])) + : prompt.question.kind === 'scope-overrides' ? defaults : []; + return { value, selected }; +} + +export function toggleSelection(selected: string[], item: string): string[] { + if (item === 'All') return selected.includes('All') ? [] : ['All']; + return selected.includes(item) + ? selected.filter(candidate => candidate !== item) + : [...selected.filter(candidate => candidate !== 'All'), item]; +} + +export function submittedQuestionAnswer(prompt: QuestionPrompt, value: string, selected: string[]): string { + if (prompt.question.kind === 'scope-overrides') return selected.length ? selected.join(',') : 'none'; + if (prompt.question.kind === 'multi-select') return selected.length ? selected.join(',') : 'none'; + return value; +} diff --git a/web/src/main.ts b/web/src/main.ts new file mode 100644 index 000000000..e9bc4ed7d --- /dev/null +++ b/web/src/main.ts @@ -0,0 +1,5 @@ +import App from './App.svelte'; +import './style.css'; +import { mount } from 'svelte'; + +mount(App, { target: document.getElementById('app')! }); diff --git a/web/src/session/setupSession.ts b/web/src/session/setupSession.ts new file mode 100644 index 000000000..9152dc9d0 --- /dev/null +++ b/web/src/session/setupSession.ts @@ -0,0 +1,117 @@ +import { writable } from 'svelte/store'; +import type { WebSetupView } from '../../../src/application/contracts/web_setup_view'; + +interface SessionState { + view?: WebSetupView; + controller: boolean; + busy: boolean; + error: string; +} + +interface Bootstrap { + controller: boolean; + capability?: string; + takeoverTicket: string; +} + +export function createSetupSession() { + const state = writable({ controller: false, busy: false, error: '' }); + let capability: string | undefined; + let takeoverTicket = ''; + let current: SessionState = { controller: false, busy: false, error: '' }; + let loading = false; + + function set(patch: Partial): void { + current = { ...current, ...patch }; + state.set(current); + } + + async function refresh(preserveError = false): Promise { + if (loading) return; + loading = true; + try { + const response = await fetch('/api/state', { cache: 'no-store' } as RequestInit); + if (!response.ok) throw new Error('The local setup session is unavailable.'); + set({ view: await response.json() as WebSetupView, ...(preserveError ? {} : { error: '' }) }); + } catch { + set({ error: 'Connection lost. The CLI may have stopped. Check the terminal before trying again.' }); + } finally { + loading = false; + } + } + + async function connect(): Promise { + try { + const response = await fetch('/api/bootstrap', { cache: 'no-store' } as RequestInit); + if (!response.ok) throw new Error('Could not join this local session.'); + const bootstrap = await response.json() as Bootstrap; + capability = bootstrap.capability; + takeoverTicket = bootstrap.takeoverTicket; + set({ controller: bootstrap.controller, error: '' }); + await refresh(); + } catch { + set({ error: 'Could not connect to the local setup session. Check the terminal.' }); + } + } + + async function post(path: string, body: Record, authorized = true): Promise> { + const response = await fetch(path, { + method: 'POST', cache: 'no-store', + headers: { 'Content-Type': 'application/json', ...(authorized && capability ? { 'X-Setup-Capability': capability } : {}) }, + body: JSON.stringify(body), + } as RequestInit); + const data = await response.json() as Record; + if (!response.ok) throw new Error(String(data.error ?? 'The request was rejected.')); + return data; + } + + async function submit(revision: number, value: string): Promise { + if (current.busy || !current.controller || current.view?.promptRevision !== revision) return; + set({ busy: true, error: '' }); + try { + await post('/api/answer', { revision, value }); + await refresh(); + } catch (cause) { + const message = cause instanceof Error ? cause.message : 'Could not submit this answer.'; + set({ error: message }); + if (/read-only|Control moved/.test(message)) await connect(); + else await refresh(true); + } finally { + set({ busy: false }); + } + } + + async function cancel(): Promise { + if (!current.controller || current.busy) return; + set({ busy: true, error: '' }); + try { + await post('/api/cancel', {}); + await refresh(); + } catch (cause) { + const message = cause instanceof Error ? cause.message : 'Cancellation failed.'; + set({ error: message }); + if (/read-only|Control moved/.test(message)) await connect(); + } finally { + set({ busy: false }); + } + } + + async function takeOver(): Promise { + try { + const result = await post('/api/takeover', { ticket: takeoverTicket }, false); + capability = String(result.capability); + set({ controller: true, error: '' }); + await refresh(); + } catch (cause) { + set({ error: cause instanceof Error ? cause.message : 'Takeover failed.' }); + await connect(); + } + } + + async function close(): Promise { + try { await post('/api/close', {}); } + catch { /* The CLI can also be stopped in the terminal. */ } + } + + return { subscribe: state.subscribe, connect, refresh, submit, cancel, takeOver, close }; +} diff --git a/web/src/style.css b/web/src/style.css new file mode 100644 index 000000000..c5ed5b97e --- /dev/null +++ b/web/src/style.css @@ -0,0 +1,6 @@ +@import './styles/tokens.css'; +@import './styles/foundation.css'; +@import './styles/layout.css'; +@import './styles/controls.css'; +@import './styles/feedback.css'; +@import './styles/responsive.css'; diff --git a/web/src/styles/controls.css b/web/src/styles/controls.css new file mode 100644 index 000000000..37a9e84cc --- /dev/null +++ b/web/src/styles/controls.css @@ -0,0 +1,28 @@ +.decision-card { padding: clamp(25px, 3vw, 40px); min-height: 360px; } +.card-header { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 29px; } +.card-kicker { font-size: 10px; font-weight: 900; color: var(--accent-strong); letter-spacing: .17em; } +.revision { color: var(--muted); font-size: 10px; letter-spacing: .08em; } +.description { white-space: pre-line; color: var(--muted); line-height: 1.65; margin-top: 0; font-size: 13px; } +.question-heading { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 10px; align-items: center; margin-bottom: 15px; } +.question-heading h2, .decision-card > label { display: block; font-weight: 700; font-size: 15px; line-height: 1.4; margin: 0 0 10px; } +.phase-tag { color: var(--accent-strong); background: var(--accent-tint); font-size: 9px; font-weight: 900; padding: 6px 8px; border-radius: 5px; letter-spacing: .07em; } +input[type="text"], input[type="password"], input[type="number"], select { width: 100%; min-height: 46px; border: 1px solid var(--control-line); border-radius: 8px; background: var(--surface-soft); color: var(--text); padding: 10px 13px; } +input[type="checkbox"] { accent-color: var(--accent); width: 17px; height: 17px; } +.field-help { color: var(--muted); font-size: 12px; line-height: 1.6; margin: 14px 0 24px; } +.segmented { display: flex; gap: 9px; } +.segmented button { flex: 1; padding: 13px; border: 1px solid var(--control-line); border-radius: 8px; color: var(--text); background: var(--surface-soft); font-weight: 700; } +.segmented button.selected { border-color: var(--accent); background: var(--accent-tint); color: var(--accent-strong); } +.check-grid { display: grid; gap: 7px; max-height: 280px; overflow-y: auto; } +.check-option { display: flex; align-items: center; gap: 10px; padding: 10px 13px; background: var(--surface-soft); border: 1px solid var(--control-line); border-radius: 7px; font-size: 12px; } +.choice-list { display: grid; gap: 9px; } +.choice-card { width: 100%; min-height: 52px; display: flex; justify-content: space-between; align-items: center; text-align: left; background: var(--surface-soft); color: var(--text); border: 1px solid var(--control-line); border-radius: 8px; padding: 13px 15px; font-size: 13px; font-weight: 650; } +.choice-card:hover:not(:disabled) { border-color: var(--accent); background: var(--accent-tint); } +.github-link { display: block; padding: 14px; background: var(--accent-tint); border-radius: 8px; border: 1px solid var(--accent); font-size: 13px; font-weight: 800; text-decoration: none; margin: 0 0 12px; } +.github-link span { float: right; } +.plan-sections { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; } +.plan-sections > div, .plan-warnings { background: var(--surface-soft); border: 1px solid var(--line); border-radius: 8px; padding: 13px; } +.plan-sections h3, .plan-warnings h3 { font-size: 12px; margin: 0 0 8px; display: flex; justify-content: space-between; } +.plan-sections h3 span { color: var(--accent-strong); } +.plan-sections ul, .plan-warnings ul { margin: 0; padding-left: 18px; max-height: 120px; overflow: auto; font-size: 11px; line-height: 1.7; overflow-wrap: anywhere; } +.plan-warnings { margin-top: 10px; color: var(--warn); background: var(--warn-bg); } +.button-row { display: flex; justify-content: space-between; gap: 10px; margin-top: 18px; } diff --git a/web/src/styles/feedback.css b/web/src/styles/feedback.css new file mode 100644 index 000000000..187a60c37 --- /dev/null +++ b/web/src/styles/feedback.css @@ -0,0 +1,18 @@ +.review-pass, .banner { padding: 14px 18px; margin: 0 0 20px; border-radius: 8px; font-size: 12px; line-height: 1.5; white-space: pre-line; } +.review-pass { color: var(--accent-strong); background: var(--accent-tint); border: 1px solid var(--accent); } +.review-pass span { margin-right: 8px; font-weight: 800; } +.banner { background: var(--surface-soft); border: 1px solid var(--line); } +.banner p { margin: 5px 0 0; } +.banner.warning { color: var(--warn); background: var(--warn-bg); border-color: var(--warn); } +.banner.error { color: var(--error); background: var(--error-bg); border-color: var(--error); } +.banner.success { color: var(--accent-strong); background: var(--accent-tint); border-color: var(--accent); } +.banner button { margin-top: 12px; } +.cancel-link { margin-top: 18px; background: transparent; border: 0; color: var(--muted); text-decoration: underline; font-size: 12px; padding: 5px 0; } +.result-card, .waiting-card { padding: 36px; max-width: 750px; } +.result-icon { display: grid; place-items: center; width: 43px; height: 43px; border-radius: 50%; background: var(--accent-tint); color: var(--accent-strong); font-size: 22px; } +.result-card h2, .waiting-card h2 { font-size: 21px; margin: 18px 0 10px; } +.result-card p, .waiting-card p { line-height: 1.6; color: var(--muted); font-size: 13px; } +.result-links { display: flex; gap: 20px; align-items: center; flex-wrap: wrap; margin: 22px 0; font-size: 12px; } +.result-links code { background: var(--surface-soft); padding: 8px; border-radius: 5px; } +.spinner { width: 25px; height: 25px; border: 3px solid var(--line); border-top-color: var(--accent); border-radius: 50%; animation: spin 1s linear infinite; } +@keyframes spin { to { transform: rotate(360deg); } } diff --git a/web/src/styles/foundation.css b/web/src/styles/foundation.css new file mode 100644 index 000000000..9f5bd2401 --- /dev/null +++ b/web/src/styles/foundation.css @@ -0,0 +1,15 @@ +* { box-sizing: border-box; } +body { margin: 0; background: var(--page); color: var(--text); } +button, input, select { font: inherit; } +button { cursor: pointer; } +button:disabled { cursor: not-allowed; opacity: .5; } +:focus-visible { outline: 3px solid var(--focus); outline-offset: 3px; } +a { color: var(--accent-strong); text-underline-offset: 3px; } +.card, .context-card { background: var(--surface); border: 1px solid var(--line); border-radius: 15px; box-shadow: var(--shadow); } +.primary { border: 1px solid var(--accent-strong); background: var(--accent-strong); color: var(--side); padding: 12px 18px; border-radius: 8px; font-size: 12px; font-weight: 800; min-height: 43px; } +:root[data-theme="light"] .primary, :root:not([data-theme="dark"]) .primary { color: #fff; } +@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .primary { color: #0d2419; } } +.primary span { margin-left: 18px; } +.primary:hover:not(:disabled) { filter: brightness(1.1); } +.secondary { border: 1px solid var(--control-line); color: var(--text); background: var(--surface-soft); border-radius: 8px; padding: 11px 16px; font-size: 12px; font-weight: 700; } +@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; scroll-behavior: auto !important; } } diff --git a/web/src/styles/layout.css b/web/src/styles/layout.css new file mode 100644 index 000000000..30bb9017d --- /dev/null +++ b/web/src/styles/layout.css @@ -0,0 +1,50 @@ +.shell { min-height: 100vh; display: grid; grid-template-columns: minmax(250px, 288px) minmax(0, 1fr); } +.sidebar { background: var(--side); color: var(--side-text); padding: 34px 28px; display: flex; flex-direction: column; position: sticky; top: 0; height: 100vh; } +.brand { display: flex; align-items: center; gap: 13px; letter-spacing: -.035em; } +.brand-mark { width: 37px; height: 37px; display: grid; place-items: center; border-radius: 11px; background: #75d9a0; color: #0c3021; font-size: 26px; line-height: 1; } +.brand strong { display: block; font-size: 23px; line-height: 1; } +.brand small { display: block; font-size: 9px; letter-spacing: .23em; margin-top: 5px; color: #aac8bd; font-weight: 800; } +.rail-caption { color: #9dbab0; font-size: 10px; letter-spacing: .18em; font-weight: 800; margin: 78px 0 22px 7px; } +.steps { padding: 0; margin: 0; list-style: none; position: relative; } +.steps::before { content: ''; position: absolute; top: 22px; bottom: 22px; left: 19px; width: 1px; background: var(--side-line); } +.steps li { position: relative; min-height: 55px; display: flex; align-items: center; gap: 16px; padding: 8px 12px 8px 1px; color: #a6c2b7; font-size: 13px; font-weight: 600; border-radius: 10px; } +.steps li.current { background: #25443b; color: #fff; } +.steps li.completed { color: #dbf2e4; } +.step-index { flex: 0 0 37px; height: 37px; border: 1px solid var(--side-line); border-radius: 50%; display: grid; place-items: center; background: var(--side); font-size: 11px; font-weight: 800; letter-spacing: .04em; } +.steps .current .step-index { background: #80dba8; border-color: #80dba8; color: #102b1d; } +.steps .completed .step-index { background: #204c37; border-color: #45966b; color: #b9f8c9; font-size: 15px; } +.sidebar-note { margin-top: auto; padding: 19px 16px; border: 1px solid var(--side-line); border-radius: 13px; display: flex; gap: 13px; background: rgba(255,255,255,.035); } +.sidebar-note > span { font-size: 20px; color: #8fe1ae; } +.sidebar-note strong { font-size: 12px; } +.sidebar-note p { color: #afcabe; font-size: 11px; line-height: 1.6; margin: 6px 0 0; } +.main { min-width: 0; } +.topbar { height: 80px; border-bottom: 1px solid var(--line); background: var(--surface); display: flex; justify-content: space-between; align-items: center; padding: 0 clamp(24px, 4vw, 70px); gap: 16px; } +.breadcrumb { display: flex; align-items: center; gap: 12px; font-size: 12px; min-width: 0; } +.breadcrumb span:first-child { color: var(--muted); font-size: 10px; font-weight: 800; letter-spacing: .14em; } +.breadcrumb span:nth-child(2) { color: var(--muted); } +.breadcrumb strong { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } +.top-actions { display: flex; align-items: center; gap: 18px; flex-shrink: 0; } +.local-pill { color: var(--accent-strong); background: var(--accent-tint); padding: 8px 11px; border-radius: 6px; font-size: 10px; font-weight: 800; letter-spacing: .08em; white-space: nowrap; } +.pulse-dot { width: 6px; height: 6px; display: inline-block; margin-right: 5px; border-radius: 50%; background: currentColor; } +.theme-switch { display: flex; padding: 3px; border: 1px solid var(--line); border-radius: 8px; background: var(--surface-soft); } +.theme-switch button { background: transparent; color: var(--muted); border: 0; min-width: 31px; height: 27px; border-radius: 5px; font-size: 11px; } +.theme-switch button.active { background: var(--surface); color: var(--text); box-shadow: 0 1px 4px rgba(0,0,0,.12); font-weight: 800; } +.content { max-width: 1320px; padding: 52px clamp(24px, 4vw, 70px) 35px; margin: 0 auto; } +.eyebrow { display: flex; align-items: center; gap: 10px; color: var(--accent-strong); font-size: 10px; letter-spacing: .18em; font-weight: 900; } +.eyebrow-line { width: 21px; height: 2px; background: var(--accent); } +.eyebrow-count { color: var(--muted); margin-left: 8px; letter-spacing: .09em; } +h1 { font-size: clamp(30px, 3vw, 45px); line-height: 1.15; letter-spacing: -.045em; margin: 15px 0 13px; max-width: 860px; } +.lede { color: var(--muted); line-height: 1.65; font-size: 14px; max-width: 700px; margin: 0 0 30px; } +.workspace-grid { display: grid; grid-template-columns: minmax(0, 1.65fr) minmax(230px, .8fr); gap: 19px; align-items: start; } +.context-column { display: grid; gap: 17px; } +.context-card { padding: 25px; box-shadow: none; } +.context-icon { display: grid; place-items: center; width: 32px; height: 32px; border-radius: 8px; background: var(--accent-tint); color: var(--accent-strong); font-size: 19px; } +.context-card h2 { font-size: 14px; margin: 17px 0 7px; letter-spacing: -.015em; } +.context-card p, .context-card > small { display: block; font-size: 12px; line-height: 1.65; color: var(--muted); margin: 0 0 12px; } +.context-card code { display: block; background: var(--surface-soft); padding: 10px; border-radius: 6px; overflow-wrap: anywhere; font-size: 11px; } +.permissions ul { padding: 0; margin: 8px 0 13px; list-style: none; max-height: 270px; overflow: auto; } +.permissions li { display: flex; justify-content: space-between; gap: 10px; padding: 9px 0; border-bottom: 1px solid var(--line); font-size: 11px; } +.permissions li small { color: var(--muted); display: block; margin-top: 3px; } +.permissions li strong { color: var(--accent-strong); text-transform: uppercase; font-size: 9px; } +footer { margin-top: 40px; color: var(--muted); opacity: .85; font-size: 9px; letter-spacing: .11em; font-weight: 700; } +footer span { margin: 0 8px; } diff --git a/web/src/styles/responsive.css b/web/src/styles/responsive.css new file mode 100644 index 000000000..87ed84116 --- /dev/null +++ b/web/src/styles/responsive.css @@ -0,0 +1,3 @@ +@media (max-width: 1100px) { .workspace-grid { grid-template-columns: 1fr; } .context-column { grid-template-columns: repeat(2, minmax(0,1fr)); } } +@media (max-width: 780px) { .shell { display: block; } .sidebar { position: static; height: auto; padding: 16px 20px; } .rail-caption, .sidebar-note { display: none; } .steps { display: flex; overflow-x: auto; margin-top: 18px; gap: 4px; } .steps::before { display: none; } .steps li { flex: 0 0 auto; min-height: 37px; padding: 4px 7px; font-size: 11px; gap: 6px; } .step-index { width: 26px; height: 26px; flex-basis: 26px; } .topbar { height: auto; min-height: 65px; flex-wrap: wrap; padding: 12px 20px; } .content { padding: 28px 20px; } } +@media (max-width: 540px) { .context-column, .plan-sections { grid-template-columns: 1fr; } .top-actions { width: 100%; justify-content: space-between; } .decision-card { padding: 22px; } .breadcrumb { max-width: 100%; } h1 { font-size: 29px; } } diff --git a/web/src/styles/tokens.css b/web/src/styles/tokens.css new file mode 100644 index 000000000..c0be70663 --- /dev/null +++ b/web/src/styles/tokens.css @@ -0,0 +1,27 @@ +:root { + font-family: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; + color-scheme: light dark; + --page: #f6f8f7; --side: #102423; --side-line: #31504b; --side-text: #e8f5f0; + --surface: #fff; --surface-soft: #f3f7f5; --line: #d7e3df; --control-line: #748b82; --text: #18312c; + --muted: #58736b; --accent: #176e5e; --accent-strong: #075743; --accent-tint: #dff4e9; + --focus: #966000; --warn: #76510d; --warn-bg: #fff6df; --error: #a73434; + --error-bg: #fff0ec; --shadow: 0 18px 50px rgba(30, 64, 52, .08); +} +@media (prefers-color-scheme: dark) { + :root:not([data-theme="light"]) { + --page: #0d1716; --side: #10201d; --side-line: #294a41; --side-text: #eaf7f1; + --surface: #182722; --surface-soft: #20342d; --line: #355247; --control-line: #688f7f; --text: #eaf5ee; + --muted: #adccbc; --accent: #7ed6ac; --accent-strong: #a4edc2; --accent-tint: #234b38; + --focus: #ffca6a; --warn: #ffdd8a; --warn-bg: #463b21; --error: #ffc0b7; + --error-bg: #4a2b2a; --shadow: 0 18px 50px rgba(0, 0, 0, .14); + } +} +:root[data-theme="dark"] { + --page: #0d1716; --side: #10201d; --side-line: #294a41; --side-text: #eaf7f1; + --surface: #182722; --surface-soft: #20342d; --line: #355247; --control-line: #688f7f; --text: #eaf5ee; + --muted: #adccbc; --accent: #7ed6ac; --accent-strong: #a4edc2; --accent-tint: #234b38; + --focus: #ffca6a; --warn: #ffdd8a; --warn-bg: #463b21; --error: #ffc0b7; + --error-bg: #4a2b2a; --shadow: 0 18px 50px rgba(0, 0, 0, .14); +} +:root[data-theme="light"] { color-scheme: light; } +:root[data-theme="dark"] { color-scheme: dark; } diff --git a/web/tsconfig.json b/web/tsconfig.json new file mode 100644 index 000000000..36703ec0f --- /dev/null +++ b/web/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "module": "ESNext", + "moduleResolution": "Bundler", + "target": "ES2022", + "lib": ["ES2022", "DOM"], + "types": ["svelte", "vite/client"], + "rootDir": "..", + "allowJs": true, + "checkJs": false, + "noEmit": true + }, + "include": ["src/**/*", "vite.config.mts", "../src/application/contracts/web_setup_view.ts"] +} diff --git a/web/vite.config.mts b/web/vite.config.mts new file mode 100644 index 000000000..c4e549ab9 --- /dev/null +++ b/web/vite.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vite'; +import { svelte } from '@sveltejs/vite-plugin-svelte'; + +export default defineConfig({ + root: 'web', + base: './', + plugins: [svelte()], + build: { outDir: '../build/web', emptyOutDir: true, sourcemap: false }, +}); From 33ac688eb47d342e4933536bcc933361bb00109b Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Mon, 28 Sep 2026 20:19:45 +0200 Subject: [PATCH 15/50] codex-setup-temporary-github-auth: cover web session failures and revoke stale browser control --- .../{index-DBy8dnu-.js => index-BS9cJzpj.js} | 2 +- build/web/index.html | 2 +- jest.config.js | 8 + .../web_setup_browser_session.test.ts | 148 ++++++++++++++++++ .../__tests__/web_setup_ui_helpers.test.ts | 1 + web/src/session/setupSession.ts | 6 +- 6 files changed, 163 insertions(+), 4 deletions(-) rename build/web/assets/{index-DBy8dnu-.js => index-BS9cJzpj.js} (67%) diff --git a/build/web/assets/index-DBy8dnu-.js b/build/web/assets/index-BS9cJzpj.js similarity index 67% rename from build/web/assets/index-DBy8dnu-.js rename to build/web/assets/index-BS9cJzpj.js index f8a1cbfaf..dd55c191d 100644 --- a/build/web/assets/index-DBy8dnu-.js +++ b/build/web/assets/index-BS9cJzpj.js @@ -1,2 +1,2 @@ (function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})(),typeof window<`u`&&((window.__svelte??={}).v??=new Set).add(`5`);var e=!1;function t(){e=!0}t();var n={},r=Symbol(`uninitialized`),i=`http://www.w3.org/1999/xhtml`,a=Array.isArray,o=Array.prototype.indexOf,s=Array.prototype.includes,c=Array.from,l=Object.defineProperty,u=Object.getOwnPropertyDescriptor,d=Object.getOwnPropertyDescriptors,f=Object.prototype,p=Array.prototype,m=Object.getPrototypeOf,h=Object.isExtensible,g=()=>{};function _(e){return e()}function v(e){for(var t=0;t{e=n,t=r}),resolve:e,reject:t}}var b=1024,x=2048,S=4096,ee=8192,te=16384,ne=32768,re=1<<25,ie=65536,ae=1<<18,oe=1<<19,se=1<<20,ce=1<<25,le=1<<21,ue=1<<22,de=1<<23,fe=Symbol(`$state`),pe=Symbol(`component`),me=Symbol(`legacy props`),he=Symbol(``),ge=Symbol(`attributes`),_e=Symbol(`class`),ve=Symbol(`style`),ye=Symbol(`text`),be=Symbol(`form reset`),xe=new class extends Error{name=`StaleReactionError`;message="The reaction that called `getAbortSignal()` was re-run or destroyed"},Se=!!globalThis.document?.contentType&&globalThis.document.contentType.includes(`xml`);function Ce(){console.warn(`https://svelte.dev/e/derived_inert`)}function we(e){console.warn(`https://svelte.dev/e/hydration_mismatch`)}function Te(){console.warn(`https://svelte.dev/e/select_multiple_invalid_value`)}function Ee(){console.warn(`https://svelte.dev/e/svelte_boundary_reset_noop`)}var C=!1;function De(e){C=e}var w;function T(e){if(e===null)throw we(),n;return w=e}function Oe(){return T(on(w))}function E(e){if(C){if(on(w)!==null)throw we(),n;w=e}}function ke(e=1){if(C){for(var t=e,n=w;t--;)n=on(n);w=n}}function Ae(e=!0){for(var t=0,n=w;;){if(n.nodeType===8){var r=n.data;if(r===`]`){if(t===0)return n;--t}else(r===`[`||r===`[!`||r[0]===`[`&&!isNaN(Number(r.slice(1))))&&(t+=1)}var i=on(n);e&&n.remove(),n=i}}function je(e){if(!e||e.nodeType!==8)throw we(),n;return e.data}function Me(e){return e===this.v}function Ne(e,t){return e==e?e!==t||typeof e==`object`&&!!e||typeof e==`function`:t==t}function Pe(e){return!Ne(e,this.v)}function Fe(e){throw Error(`https://svelte.dev/e/lifecycle_outside_component`)}function Ie(){throw Error(`https://svelte.dev/e/async_derived_orphan`)}function Le(e,t,n){throw Error(`https://svelte.dev/e/each_key_duplicate`)}function Re(e){throw Error(`https://svelte.dev/e/effect_in_teardown`)}function ze(){throw Error(`https://svelte.dev/e/effect_in_unowned_derived`)}function Be(e){throw Error(`https://svelte.dev/e/effect_orphan`)}function Ve(){throw Error(`https://svelte.dev/e/effect_update_depth_exceeded`)}function He(e){throw Error(`https://svelte.dev/e/props_invalid_value`)}function Ue(){throw Error(`https://svelte.dev/e/state_descriptors_fixed`)}function We(){throw Error(`https://svelte.dev/e/state_prototype_fixed`)}function Ge(){throw Error(`https://svelte.dev/e/state_unsafe_mutation`)}function Ke(){throw Error(`https://svelte.dev/e/svelte_boundary_reset_onerror`)}var D=null;function qe(e){D=e}function O(t,n=!1,r){D={p:D,i:!1,c:null,e:null,s:t,x:null,r:H,l:e&&!n?{s:null,u:null,$:[]}:null}}function k(e){var t=D,n=t.e;if(n!==null){t.e=null;for(var r of n)bn(r)}return e!==void 0&&(t.x=e),t.i=!0,D=t.p,Je(e)}function Je(e={}){return l(e,pe,{value:!0}),e}function Ye(){return!e||D!==null&&D.l===null}var Xe=[];function Ze(){var e=Xe;Xe=[],v(e)}function Qe(e){if(Xe.length===0&&!wt){var t=Xe;queueMicrotask(()=>{t===Xe&&Ze()})}Xe.push(e)}function $e(){for(;Xe.length>0;)Ze()}var et=~(x|S|b);function A(e,t){e.f=e.f&et|t}function tt(e){e.f&512||e.deps===null?A(e,b):A(e,S)}function nt(e,t,n){e.f&2048?t.add(e):e.f&4096&&n.add(e),A(e,b)}var rt=!1;function it(){rt||(rt=!0,document.addEventListener(`reset`,e=>{Promise.resolve().then(()=>{if(!e.defaultPrevented)for(let t of e.target.elements)t[be]?.()})},{capture:!0}))}function at(e){var t=B,n=H;V(null),Wn(null);try{return e()}finally{V(t),Wn(n)}}function ot(e,t,n,r=n){e.addEventListener(t,()=>at(n));let i=e[be];e[be]=i?()=>{i(),r(!0)}:()=>r(!0),it()}function st(e,t,n,r){let i=Ye()?dt:mt;var a=e.filter(e=>!e.settled),o=t.map(i);if(n.length===0&&a.length===0){r(o);return}var s=H,c=ct(),l=a.length===1?a[0].promise:a.length>1?Promise.all(a.map(e=>e.promise)):null;function u(e){if(!(s.f&16384)){c();try{r([...o,...e])}catch(e){pn(e,s)}lt()}}var d=ut();if(n.length===0){l.then(()=>u([])).finally(d);return}function f(){Promise.all(n.map(e=>pt(e))).then(u).catch(e=>pn(e,s)).finally(d)}l?l.then(()=>{c(),f(),lt()}):f()}function ct(){var e=H,t=B,n=D,r=j;return function(i=!0){Wn(e),V(t),qe(n),i&&!(e.f&16384)&&(r?.activate(),r?.apply())}}function lt(e=!0){Wn(null),V(null),qe(null),e&&j?.deactivate()}function ut(){var e=H,t=e.b,n=j,r=!!t?.is_rendered();return t?.update_pending_count(1,n),n.increment(r,e),()=>{t?.update_pending_count(-1,n),n.decrement(r,e)}}function dt(e){var t=2|x;return H!==null&&(H.f|=oe),{ctx:D,deps:null,effects:null,equals:Me,f:t,fn:e,reactions:null,rv:0,v:r,wv:0,parent:H,ac:null}}var ft=Symbol(`obsolete`);function pt(e,t,n){let i=H;i===null&&Ie();var a=void 0,o=Vt(r),s=!B,c=new Set;return En(()=>{var t=H,n=y();a=n.promise;try{Promise.resolve(e()).then(n.resolve,e=>{e!==xe&&n.reject(e)}).finally(lt)}catch(e){n.reject(e),lt()}var r=j;if(s){if(t.f&32768)var l=ut();if(i.b?.is_rendered())r.async_deriveds.get(t)?.reject(ft);else for(let e of c.values())e.reject(ft);c.add(n),r.async_deriveds.set(t,n)}let u=(e,t=void 0)=>{l?.(),c.delete(n),t!==ft&&(r.activate(),t?(o.f|=de,Gt(o,t)):(o.f&8388608&&(o.f^=de),Gt(o,e)),r.deactivate())};n.promise.then(u,e=>u(null,e||`unknown`))}),vn(()=>{for(let e of c)e.reject(ft)}),new Promise(e=>{function t(n){function r(){n===a?e(o):t(a)}n.then(r,r)}t(a)})}function mt(e){let t=dt(e);return t.equals=Pe,t}function ht(e){var t=e.effects;if(t!==null){e.effects=null;for(var n=0;n{t.ac.abort(xe),t.ac=null}),t.fn!==null&&(t.teardown=g),ar(t,0),An(t))}function yt(e){if(e.effects!==null)for(let t of e.effects)t.teardown&&t.fn!==null&&or(t)}var bt=null,j=null,xt=null,St=null,Ct=null,wt=!1,Tt=!1,Et=null,Dt=null,Ot=0,kt=1,At=class e{id=kt++;#e=!1;linked=!0;#t=null;#n=null;async_deriveds=new Map;current=new Map;previous=new Map;#r=new Set;#i=new Set;#a=0;#o=new Map;#s=null;#c=[];#l=[];#u=new Set;#d=new Set;#f=new Map;#p=new Set;is_fork=!1;#m=!1;constructor(){bt===null?bt=this:(bt.#n=this,this.#t=bt),bt=this}#h(){if(this.is_fork)return!0;for(let n of this.#o.keys()){for(var e=n,t=!1;e.parent!==null;){if(this.#f.has(e)){t=!0;break}e=e.parent}if(!t)return!0}return!1}skip_effect(e){this.#f.has(e)||this.#f.set(e,{d:[],m:[]}),this.#p.delete(e)}unskip_effect(e,t=e=>this.schedule(e)){var n=this.#f.get(e);if(n){this.#f.delete(e);for(var r of n.d)A(r,x),t(r);for(r of n.m)A(r,S),t(r)}this.#p.add(e)}#g(){var e=[];for(let i of this.#c)if(!(i.f&16384||!(i.f&6144))){for(var t=i,n=!1;t.parent!==null;){t=t.parent;var r=t.f;if(r&96){if(!(r&1024)){n=!0;break}t.f^=b}}n||e.push(t)}return this.#c=[],e}#_(){this.#e=!0;for(let e of this.#u)this.#d.delete(e),A(e,x),this.schedule(e);for(let e of this.#d)A(e,S),this.schedule(e);this.apply();for(var t=Et=[],n=[],r=Dt=[];this.#c.length>0;){Ot++>1e3&&(this.#S(),Mt());for(let e of this.#g())try{this.#v(e,t,n)}catch(t){throw Lt(e),this.#h()||this.discard(),t}}if(j=null,r.length>0){var i=e.ensure();for(let e of r)i.schedule(e)}if(Et=null,Dt=null,this.#h()){this.#x(n),this.#x(t);for(let[e,t]of this.#f)It(e,t);r.length>0&&j.#_();return}let a=this.#y();if(a){this.#x(n),this.#x(t),a.#b(this);return}this.#u.clear(),this.#d.clear();for(let e of this.#r)e(this);this.#r.clear(),xt=this,Pt(n),Pt(t),xt=null,this.#s?.resolve();var o=j;if(this.#a===0&&(this.#c.length===0||o!==null)&&this.#S(),this.#c.length>0){if(o!==null){for(let e of this.#c)o.#c.push(e);this.#c=[]}else o=this}o!==null&&(zt.clear(),o.#_())}#v(e,t,n){e.f^=b;for(var r=e.first;r!==null;){var i=r.f,a=!!(i&96);if(!(a&&i&1024||i&8192||this.#f.has(r))&&r.fn!==null){a?r.f^=b:i&4?t.push(r):er(r)&&(i&16&&this.#d.add(r),or(r));var o=r.first;if(o!==null){r=o;continue}}for(;r!==null;){var s=r.next;if(s!==null){r=s;break}r=r.parent}}}#y(){for(var e=this.#t;e!==null;){if(!e.is_fork){for(let[t,[,n]]of this.current)if(e.current.has(t)&&!n)return e}e=e.#t}return null}#b(e){for(let[t,n]of e.current)!this.previous.has(t)&&e.previous.has(t)&&this.previous.set(t,e.previous.get(t)),this.current.set(t,n);for(let[t,n]of e.async_deriveds){let e=this.async_deriveds.get(t);e&&n.promise.then(e.resolve).catch(e.reject)}e.async_deriveds.clear(),this.transfer_effects(e.#u,e.#d);let t=e=>{var n=e.reactions;if(n!==null&&!(e.f&2&&!(e.f&6144)))for(let e of n){var r=e.f;if(r&2)t(e);else{var i=e;r&4194320&&!this.async_deriveds.has(i)&&(this.#d.delete(i),A(i,x),this.schedule(i))}}};for(let e of this.current.keys())t(e);this.oncommit(()=>e.discard()),e.#S(),j=this,this.#_()}#x(e){for(var t=0;t{this.#m=!1,this.linked&&this.flush()}))}transfer_effects(e,t){for(let t of e)this.#u.add(t);for(let e of t)this.#d.add(e);e.clear(),t.clear()}oncommit(e){this.#r.add(e)}ondiscard(e){this.#i.add(e)}settled(){return(this.#s??=y()).promise}static ensure(){if(j===null){let t=j=new e;!Tt&&!wt&&Qe(()=>{t.#e||t.flush()})}return j}apply(){St=null}schedule(e){if(Ct=e,e.b?.is_pending&&e.f&16777228&&!(e.f&32768)){e.b.defer_effect(e);return}this.#c.push(e)}#S(){if(this.linked){var e=this.#t,t=this.#n;e===null||(e.#n=t),t===null?bt=e:t.#t=e,this.linked=!1}}};function jt(e){var t=wt;wt=!0;try{var n;for(e&&(j!==null&&!j.is_fork&&j.flush(),n=e());;){if($e(),j===null)return n;j.flush()}}finally{wt=t}}function Mt(){try{Ve()}catch(e){pn(e,Ct)}}var Nt=null;function Pt(e){var t=e.length;if(t!==0){for(var n=0;n0)){zt.clear();for(let e of Nt){if(e.f&24576)continue;let t=[e],n=e.parent;for(;n!==null;)Nt.has(n)&&(Nt.delete(n),t.push(n)),n=n.parent;for(let e=t.length-1;e>=0;e--){let n=t[e];n.f&24576||or(n)}}Nt.clear()}}Nt=null}}function Ft(e){j.schedule(e)}function It(e,t){if(!(e.f&32&&e.f&1024)){e.f&2048?t.d.push(e):e.f&4096&&t.m.push(e),A(e,b);for(var n=e.first;n!==null;)It(n,t),n=n.next}}function Lt(e){A(e,b);for(var t=e.first;t!==null;)Lt(t),t=t.next}var Rt=new Set,zt=new Map,Bt=!1;function Vt(e,t){return{f:0,v:e,reactions:null,equals:Me,rv:0,wv:0}}function Ht(e,t){let n=Vt(e,t);return Kn(n),n}function M(t,n=!1,r=!0){let i=Vt(t);return n||(i.equals=Pe),e&&r&&D!==null&&D.l!==null&&(D.l.s??=[]).push(i),i}function N(e,t,n=!1){return B!==null&&(!Un||B.f&131072)&&Ye()&&B.f&4325394&&(Gn===null||!Gn.has(e))&&Ge(),Gt(e,n?Yt(t):t,Dt)}var Ut=null,Wt=0;function Gt(e,t,n=null){if(!e.equals(t)){Vn?zt.set(e,t):zt.has(e)||zt.set(e,e.v);var r=At.ensure();if(r.capture(e,t),e.f&2){let t=e;e.f&2048&>(t),St===null&&tt(t)}e.wv=$n(),Ut=null,Wt=0,Jt(e,x,n),Ut=null,Ye()&&H!==null&&H.f&1024&&!(H.f&96)&&(qn===null?Jn([e]):qn.push(e)),!r.is_fork&&Rt.size>0&&!Bt&&Kt()}return t}function Kt(){Bt=!1;for(let e of Rt){e.f&1024&&A(e,S);let t;try{t=er(e)}catch{t=!0}t&&or(e)}Rt.clear()}function qt(e){N(e,e.v+1)}function Jt(e,t,n){var r=e.reactions;if(r!==null){var i=Ye(),a=r.length;if(Wt+=a,Wt>1e5&&Ut===null&&(Ut=new Set),Ut!==null){if(Ut.has(e))return;Ut.add(e)}for(var o=0;o{if(Zn===c)return e();var t=B,n=Zn;V(null),Qn(c);var r=e();return V(t),Qn(n),r};return i&&n.set(`length`,Ht(e.length,s)),new Proxy(e,{defineProperty(e,t,r){(!(`value`in r)||r.configurable===!1||r.enumerable===!1||r.writable===!1)&&Ue();var i=n.get(t);return i===void 0?l(()=>{var e=Ht(r.value,s);return n.set(t,e),e}):N(i,r.value,!0),!0},deleteProperty(e,t){var i=n.get(t);if(i===void 0){if(t in e){let e=l(()=>Ht(r,s));n.set(t,e),qt(o)}}else N(i,r),qt(o);return!0},get(t,i,a){if(i===fe)return e;var o=n.get(i),c=i in t;if(o===void 0&&(!c||u(t,i)?.writable)&&(o=l(()=>Ht(Yt(c?t[i]:r),s)),n.set(i,o)),o!==void 0){var d=G(o);return d===r?void 0:d}return Reflect.get(t,i,a)},getOwnPropertyDescriptor(e,t){this.has?.(e,t);var i=Reflect.getOwnPropertyDescriptor(e,t),a=n.get(t);if(a!==void 0){var o=G(a);if(o===r)return;if(i&&`value`in i)i.value=o;else return{enumerable:!0,configurable:!0,value:o,writable:!0}}return i},has(e,t){if(t===fe)return!0;var i=n.get(t),a=i!==void 0&&i.v!==r||Reflect.has(e,t);return(i!==void 0||H!==null&&(!a||u(e,t)?.writable))&&(i===void 0&&(i=l(()=>Ht(a?Yt(e[t]):r,s)),n.set(t,i)),G(i)===r)?!1:a},set(e,t,a,c){var d=n.get(t),f=t in e;if(i&&t===`length`)for(var p=a;pHt(r,s)),n.set(p+``,m)):N(m,r)}if(d===void 0)(!f||u(e,t)?.writable)&&(d=l(()=>Ht(void 0,s)),N(d,Yt(a)),n.set(t,d));else{f=d.v!==r;var h=l(()=>Yt(a));N(d,h)}var g=Reflect.getOwnPropertyDescriptor(e,t);if(g?.set&&g.set.call(c,a),!f){if(i&&typeof t==`string`){var _=n.get(`length`),v=Number(t);Number.isInteger(v)&&v>=_.v&&N(_,v+1)}qt(o)}return!0},ownKeys(e){G(o);var t=Reflect.ownKeys(e).filter(e=>{var t=n.get(e);return t===void 0||t.v!==r});for(var[i,a]of n)a.v!==r&&!(i in e)&&t.push(i);return t},setPrototypeOf(){We()}})}function Xt(e){try{if(typeof e==`object`&&e&&fe in e)return e[fe]}catch{}return e}function Zt(e,t){return Object.is(Xt(e),Xt(t))}var Qt,$t,en,tn;function nn(){if(Qt===void 0){Qt=window,$t=/Firefox/.test(navigator.userAgent);var e=Element.prototype,t=Node.prototype,n=Text.prototype;en=u(t,`firstChild`).get,tn=u(t,`nextSibling`).get,h(e)&&(e[_e]=void 0,e[ge]=null,e[ve]=void 0,e.__e=void 0),h(n)&&(n[ye]=void 0)}}function rn(e=``){return document.createTextNode(e)}function an(e){return en.call(e)}function on(e){return tn.call(e)}function P(e,t){if(!C)return an(e);var n=an(w);if(n===null)n=w.appendChild(rn());else if(t&&n.nodeType!==3){var r=rn();return n?.before(r),T(r),r}return t&&dn(n),T(n),n}function sn(e,t=!1){if(!C){var n=an(e);return n instanceof Comment&&n.data===``?on(n):n}if(t){if(w?.nodeType!==3){var r=rn();return w?.before(r),T(r),r}dn(w)}return w}function F(e,t=!1){if(!C)return an(e);var n=P(e,t);return E(e),n}function I(e,t=1,n=!1){let r=C?w:e;for(var i;t--;)i=r,r=on(r);if(!C)return r;if(n){if(r?.nodeType!==3){var a=rn();return r===null?i?.after(a):r.before(a),T(a),a}dn(r)}return T(r),r}function cn(e){e.textContent=``}function ln(){return!1}function un(e,t,n){return t==null||t===`http://www.w3.org/1999/xhtml`?n?document.createElement(e,{is:n}):document.createElement(e):n?document.createElementNS(t,e,{is:n}):document.createElementNS(t,e)}function dn(e){if(e.nodeValue.length<65536)return;let t=e.nextSibling;for(;t!==null&&t.nodeType===3;)t.remove(),e.nodeValue+=t.nodeValue,t=e.nextSibling}function fn(e){var t=H;if(t===null)return B.f|=de,e;if(!(t.f&32768)&&!(t.f&4))throw e;pn(e,t)}function pn(e,t){if(!(t!==null&&t.f&16384)){for(;t!==null;){if(t.f&128&&!(t.f&33570816)){if(!(t.f&32768))throw e;try{t.b.error(e);return}catch(t){e=t}}t=t.parent}throw e}}function mn(e){H===null&&(B===null&&Be(e),ze()),Vn&&Re(e)}function hn(e,t){var n=t.last;n===null?t.last=t.first=e:(n.next=e,e.prev=n,t.last=e)}function gn(e,t){var n=H;n!==null&&n.f&8192&&(e|=ee);var r={ctx:D,deps:null,nodes:null,f:e|x|512,first:null,fn:t,last:null,next:null,parent:n,b:n&&n.b,prev:null,teardown:null,wv:0,ac:null};j?.register_created_effect(r);var i=r;if(e&4)Et===null?At.ensure().schedule(r):Et.push(r);else if(t!==null){try{or(r)}catch(e){throw z(r),e}i.deps===null&&i.teardown===null&&i.nodes===null&&i.first===i.last&&!(i.f&524288)&&(i=i.first,e&16&&e&65536&&i!==null&&(i.f|=ie))}if(i!==null&&(i.parent=n,n!==null&&hn(i,n),B!==null&&B.f&2&&!(e&64))){var a=B;(a.effects??=[]).push(i)}return r}function _n(){return B!==null&&!Un}function vn(e){let t=gn(8,null);return A(t,b),t.teardown=e,t}function yn(e){mn(`$effect`);var t=H.f;if(!B&&t&32&&D!==null&&!D.i){var n=D;(n.e??=[]).push(e)}else return bn(e)}function bn(e){return gn(4|se,e)}function xn(e){return mn(`$effect.pre`),gn(8|se,e)}function Sn(e){At.ensure();let t=gn(64|oe,e);return(e={})=>new Promise(n=>{e.outro?Pn(t,()=>{z(t),n(void 0)}):(z(t),n(void 0))})}function Cn(e){return gn(4,e)}function wn(e,t){var n=D,r={effect:null,ran:!1,deps:e};n.l.$.push(r),r.effect=Dn(()=>{if(e(),!r.ran){r.ran=!0;var n=H;try{Wn(n.parent),K(t)}finally{Wn(n)}}})}function Tn(){var e=D;Dn(()=>{for(var t of e.l.$){t.deps();var n=t.effect;n.f&1024&&n.deps!==null&&A(n,S),er(n)&&or(n),t.ran=!1}})}function En(e){return gn(ue|oe,e)}function Dn(e,t=0){return gn(8|t,e)}function L(e,t=[],n=[],r=[]){st(r,t,n,t=>{gn(8,()=>{e(...t.map(G))})})}function On(e,t=0){return gn(16|t,e)}function R(e){return gn(32|oe,e)}function kn(e){var t=e.teardown;if(t!==null){let n=Vn,r=B;Hn(!0),V(null);try{t.call(null)}catch(t){pn(t,e.parent)}finally{Hn(n),V(r)}}}function An(e,t=!1){var n=e.first;for(e.first=e.last=null;n!==null;){let e=n.ac;e!==null&&at(()=>{e.abort(xe)});var r=n.next;n.f&64?n.parent=null:z(n,t),n=r}}function jn(e){for(var t=e.first;t!==null;){var n=t.next;t.f&32||z(t),t=n}}function z(e,t=!0){var n=!1;(t||e.f&262144)&&e.nodes!==null&&e.nodes.end!==null&&(Mn(e.nodes.start,e.nodes.end),n=!0),e.f|=re,An(e,t&&!n),ar(e,0);var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)e.stop();kn(e),e.f^=re,e.f|=te;var i=e.parent;i!==null&&i.first!==null&&Nn(e),e.next=e.prev=e.teardown=e.ctx=e.deps=e.fn=e.nodes=e.ac=e.b=null}function Mn(e,t){for(;e!==null;){var n=e===t?null:on(e);e.remove(),e=n}}function Nn(e){var t=e.parent,n=e.prev,r=e.next;n!==null&&(n.next=r),r!==null&&(r.prev=n),t!==null&&(t.first===e&&(t.first=r),t.last===e&&(t.last=n))}function Pn(e,t,n=!0){var r=[];e.f|=256,Fn(e,r,!0);var i=()=>{n&&z(e),t&&t()},a=r.length;if(a>0){var o=()=>--a||i();for(var s of r)s.out(o)}else i()}function Fn(e,t,n){if(!(e.f&8192)){e.f^=ee;var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)(e.is_global||n)&&t.push(e);for(var i=e.first;i!==null;){var a=i.next;if(!(i.f&64)){var o=!!(i.f&65536)||!!(i.f&32)&&!!(e.f&16);Fn(i,t,o?n:!1)}i=a}}}function In(e){e.f&=-257,Ln(e,!0)}function Ln(e,t){if(!(e.f&256)&&e.f&8192){e.f^=ee,e.f&1024||(A(e,x),At.ensure().schedule(e));for(var n=e.first;n!==null;){var r=n.next,i=!!(n.f&65536)||!!(n.f&32);Ln(n,i?t:!1),n=r}var a=e.nodes&&e.nodes.t;if(a!==null)for(let e of a)(e.is_global||t)&&e.in()}}function Rn(e,t){if(e.nodes)for(var n=e.nodes.start,r=e.nodes.end;n!==null;){var i=n===r?null:on(n);t.append(n),n=i}}var zn=null,Bn=!1,Vn=!1;function Hn(e){Vn=e}var B=null,Un=!1;function V(e){B=e}var H=null;function Wn(e){H=e}var Gn=null;function Kn(e){B!==null&&(B.f&2097152||B.f&2)&&(Gn??=new Set).add(e)}var U=null,W=0,qn=null;function Jn(e){qn=e}var Yn=1,Xn=0,Zn=Xn;function Qn(e){Zn=e}function $n(){return++Yn}function er(e){var t=e.f;if(t&2048)return!0;if(t&4096){for(var n=e.deps,r=n.length,i=0;ie.wv)return!0}t&512&&St===null&&A(e,b)}return!1}function tr(e,t,n=!0){var r=e.reactions;if(r!==null&&!(Gn!==null&&Gn.has(e)))for(var i=0;i{e.ac.abort(xe)}),e.ac=null);try{e.f|=le;var u=e.fn,d=u();e.f|=ne;var f=rr(e);if(Ye()&&qn!==null&&!Un&&f!==null&&!(e.f&6146))for(var p=0;p0)for(t.length=W+U.length,r=0;r{c.ac.abort(xe),c.ac=null,A(c,x)}),vt(c),ar(c,0)}}function ar(e,t){var n=e.deps;if(n!==null)for(var r=t;r{_r=!1,gr=null}));var o=0,s=gr===e&&e[dr];if(s){var c=i.indexOf(s);if(c!==-1&&(t===document||t===window)){e[dr]=t;return}var u=i.indexOf(t);if(u===-1)return;c<=u&&(o=c)}if(a=i[o]||e.target,a!==t){l(e,`currentTarget`,{configurable:!0,get(){return a||n}});var d=B,f=H;V(null),Wn(null);try{for(var p,m=[];a!==null&&a!==t;){try{var h=a[dr]?.[r];h!=null&&(!a.disabled||e.target===a)&&h.call(a,e)}catch(e){p?m.push(e):p=e}if(e.cancelBubble)break;o++,a=o{throw e});throw p}}finally{e[dr]=t,delete e.currentTarget,V(d),Wn(f)}}}var yr=globalThis?.window?.trustedTypes&&globalThis.window.trustedTypes.createPolicy(`svelte-trusted-html`,{createHTML:e=>e});function br(e){return yr?.createHTML(e)??e}function xr(e){var t=un(`template`);return t.innerHTML=br(e.replaceAll(``,``)),t.content}function Sr(e,t){var n=H;n.nodes===null&&(n.nodes={start:e,end:t,a:null,t:null})}function J(e,t){var n=!!(t&1),r=!!(t&2),i,a=!e.startsWith(``);return()=>{if(C)return Sr(w,null),w;i===void 0&&(i=xr(a?e:``+e),n||(i=an(i)));var t=r||$t?document.importNode(i,!0):i.cloneNode(!0);if(n){var o=an(t),s=t.lastChild;Sr(o,s)}else Sr(t,t);return t}}function Y(e,t){if(C){var n=H;(!(n.f&32768)||n.nodes.end===null)&&(n.nodes.end=w),Oe();return}e!==null&&e.before(t)}[...`allowfullscreen.async.autofocus.autoplay.checked.controls.default.disabled.formnovalidate.indeterminate.inert.ismap.loop.multiple.muted.nomodule.novalidate.open.playsinline.readonly.required.reversed.seamless.selected.webkitdirectory.defer.disablepictureinpicture.disableremoteplayback`.split(`.`)];var Cr=[`touchstart`,`touchmove`];function wr(e){return Cr.includes(e)}function Tr(e){let t=0,n=Vt(0),r;return()=>{_n()&&(G(n),Dn(()=>(t===0&&(r=K(()=>e(()=>qt(n)))),t+=1,()=>{Qe(()=>{--t,t===0&&(r?.(),r=void 0,qt(n))})})))}}var Er=ie|oe;function Dr(e,t,n,r){new Or(e,t,n,r)}var Or=class{parent;is_pending=!1;transform_error;#e;#t=C?w:null;#n;#r;#i;#a=null;#o=null;#s=null;#c=null;#l=0;#u=0;#d=!1;#f=new Set;#p=new Set;#m=null;#h=Tr(()=>(this.#m=Vt(this.#l),()=>{this.#m=null}));constructor(e,t,n,r){this.#e=e,this.#n=t,this.#r=e=>{var t=H;t.b=this,t.f|=128,n(e)},this.parent=H.b,this.transform_error=r??this.parent?.transform_error??(e=>e),this.#i=On(()=>{if(C){let e=this.#t;Oe();let t=e.data===`[!`;if(e.data.startsWith(`[?`)){let t=JSON.parse(e.data.slice(2));this.#_(t)}else t?this.#y():this.#g()}else this.#b()},Er),C&&(this.#e=w)}#g(){try{this.#a=R(()=>this.#r(this.#e))}catch(e){this.error(e)}}#_(e){let t=this.#n.failed,{reset:n,invoke_onerror:r}=this.#v(e);Qe(r),t&&(this.#s=R(()=>{t(this.#e,()=>e,()=>n)}))}#v(e){var t=!1,n=!1;let r=()=>{if(t){Ee();return}t=!0,n&&Ke(),this.#s!==null&&Pn(this.#s,()=>{this.#s=null}),this.#S(()=>{this.#b()})};return{reset:r,invoke_onerror:()=>{try{n=!0,this.#n.onerror?.(e,r),n=!1}catch(e){pn(e,this.#i&&this.#i.parent)}}}}#y(){let e=this.#n.pending;e&&(this.is_pending=!0,this.#o=R(()=>e(this.#e)),Qe(()=>{var e=this.#c=document.createDocumentFragment(),t=rn(),n=!1;if(e.append(t),this.#a=this.#S(()=>{try{return R(()=>this.#r(t))}catch(e){try{this.error(e),n=!0}catch(e){pn(e,this.#i.parent)}return null}}),this.#a===null){this.#c=null,n&&this.#x(j);return}this.#u===0&&(this.#e.before(e),this.#c=null,Pn(this.#o,()=>{this.#o=null}),this.#x(j))}))}#b(){try{if(this.is_pending=this.has_pending_snippet(),this.#u=0,this.#l=0,this.#a=R(()=>{this.#r(this.#e)}),this.#u>0){var e=this.#c=document.createDocumentFragment();Rn(this.#a,e);let t=this.#n.pending;this.#o=R(()=>t(this.#e))}else this.#x(j)}catch(e){this.error(e)}}#x(e){this.is_pending=!1,e.transfer_effects(this.#f,this.#p)}defer_effect(e){nt(e,this.#f,this.#p)}is_rendered(){return!this.is_pending&&(!this.parent||this.parent.is_rendered())}has_pending_snippet(){return!!this.#n.pending}#S(e){var t=H,n=B,r=D;Wn(this.#i),V(this.#i),qe(this.#i.ctx);try{return At.ensure(),e()}finally{Wn(t),V(n),qe(r)}}#C(e,t){if(!this.has_pending_snippet()){this.parent&&this.parent.#C(e,t);return}this.#u+=e,this.#u===0&&(this.#x(t),this.#o&&Pn(this.#o,()=>{this.#o=null}),this.#c&&=(this.#e.before(this.#c),null))}update_pending_count(e,t){this.#C(e,t),this.#l+=e,!(!this.#m||this.#d)&&(this.#d=!0,Qe(()=>{this.#d=!1,this.#m&&Gt(this.#m,this.#l)}))}get_effect_pending(){return this.#h(),G(this.#m)}error(e){if(!this.#n.onerror&&!this.#n.failed)throw e;j?.is_fork?(this.#a&&j.skip_effect(this.#a),this.#o&&j.skip_effect(this.#o),this.#s&&j.skip_effect(this.#s),j.oncommit(()=>{this.#w(e)})):this.#w(e)}#w(e){this.#a&&=(z(this.#a),null),this.#o&&=(z(this.#o),null),this.#s&&=(z(this.#s),null),C&&(T(this.#t),ke(),T(Ae()));let t=this.#n.failed,n=e=>{let{reset:n,invoke_onerror:r}=this.#v(e);r(),t&&(this.#s=this.#S(()=>{try{return R(()=>{var r=H;r.b=this,r.f|=128,t(this.#e,()=>e,()=>n)})}catch(e){return pn(e,this.#i.parent),null}}))};Qe(()=>{var t;try{t=this.transform_error(e)}catch(e){pn(e,this.#i&&this.#i.parent);return}typeof t==`object`&&t&&typeof t.then==`function`?t.then(n,e=>pn(e,this.#i&&this.#i.parent)):n(t)})}};function X(e,t){var n=t==null?``:typeof t==`object`?`${t}`:t;n!==(e[ye]??=e.nodeValue)&&(e[ye]=n,e.nodeValue=`${n}`)}function kr(e,t){return jr(e,t)}var Ar=new Map;function jr(e,{target:t,anchor:r,props:i={},events:a,context:o,intro:s=!0,transformError:l}){nn();var u=void 0,d=Sn(()=>{var s=r??t.appendChild(rn());Dr(s,{pending:()=>{}},t=>{O({});var r=D;if(o&&(r.c=o),a&&(i.$$events=a),C&&Sr(t,null),u=e(t,i)||Je(),C&&(H.nodes.end=w,w===null||w.nodeType!==8||w.data!==`]`))throw we(),n;k()},l);var d=new Set,f=e=>{for(var n=0;n{for(var e of d)for(let r of[t,document]){var n=Ar.get(r),i=n.get(e);--i==0?(r.removeEventListener(e,vr),n.delete(e),n.size===0&&Ar.delete(r)):n.set(e,i)}pr.delete(f),s!==r&&s.parentNode?.removeChild(s)}});return Mr.set(u,d),u}var Mr=new WeakMap,Nr=class{anchor;#e=new Map;#t=new Map;#n=new Map;#r=new Set;#i=!0;constructor(e,t=!0){this.anchor=e,this.#i=t}#a=e=>{if(this.#e.has(e)){var t=this.#e.get(e),n=this.#t.get(t);if(n)In(n),this.#r.delete(t);else{var r=this.#n.get(t);r&&(In(r.effect),this.#t.set(t,r.effect),this.#n.delete(t),r.fragment.lastChild.remove(),this.anchor.before(r.fragment),n=r.effect)}for(let[t,n]of this.#e){if(this.#e.delete(t),t===e)break;let r=this.#n.get(n);r&&(z(r.effect),this.#n.delete(n))}for(let[e,r]of this.#t){if(e===t||this.#r.has(e))continue;let i=()=>{if(Array.from(this.#e.values()).includes(e)){var t=document.createDocumentFragment();Rn(r,t),t.append(rn()),this.#n.set(e,{effect:r,fragment:t})}else z(r);this.#r.delete(e),this.#t.delete(e)};this.#i||!n?(this.#r.add(e),Pn(r,i,!1)):i()}}};#o=e=>{this.#e.delete(e);let t=Array.from(this.#e.values());for(let[e,n]of this.#n)t.includes(e)||(z(n.effect),this.#n.delete(e))};ensure(e,t){var n=j,r=ln();if(t&&!this.#t.has(e)&&!this.#n.has(e)){if(r){var i=document.createDocumentFragment(),a=rn();i.append(a),this.#n.set(e,{effect:R(()=>t(a)),fragment:i})}else this.#t.set(e,R(()=>t(this.anchor)))}if(this.#e.set(n,e),r){for(let[t,r]of this.#t)t===e?n.unskip_effect(r):n.skip_effect(r);for(let[t,r]of this.#n)t===e?n.unskip_effect(r.effect):n.skip_effect(r.effect);n.oncommit(this.#a),n.ondiscard(this.#o)}else C&&(this.anchor=w),this.#a(n)}};function Pr(t){D===null&&Fe(`onMount`),e&&D.l!==null?Fr(D).m.push(t):yn(()=>{let e=K(t);if(typeof e==`function`)return e})}function Fr(e){var t=e.l;return t.u??={a:[],b:[],m:[]}}function Z(e,t,n=!1){var r;C&&(r=w,Oe());var i=new Nr(e),a=n?ie:0;function o(e,t){if(C){var n=je(r);if(e!==parseInt(n.substring(1))){var a=Ae();T(a),i.anchor=a,De(!1),i.ensure(e,t),De(!0);return}}i.ensure(e,t)}On(()=>{var e=!1;t((t,n=0)=>{e=!0,o(n,t)}),e||o(-1,null)},a)}var Ir=Symbol(`NaN`);function Lr(e,t,n){C&&Oe();var r=new Nr(e),i=!Ye();On(()=>{var e=t();e!==e&&(e=Ir),i&&typeof e==`object`&&e&&(e={}),r.ensure(e,n)})}function Rr(e,t){return t}function zr(e,t,n){for(var r=[],i=t.length,a,o=t.length,s=0;s{if(a){if(a.pending.delete(n),a.done.add(n),a.pending.size===0){var t=e.outrogroups;Br(e,c(a.done)),t.delete(a),t.size===0&&(e.outrogroups=null)}}else--o},!1)}if(o===0){var l=r.length===0&&n!==null&&e.pending.size===0;if(l){var u=n,d=u.parentNode;cn(d),d.append(u),e.items.clear()}Br(e,t,!l)}else a={pending:new Set(t),done:new Set},(e.outrogroups??=new Set).add(a)}function Br(e,t,n=!0){var r;if(e.pending.size>0){r=new Set;for(let t of e.pending.values())for(let n of t)r.add(e.items.get(n).e)}for(var i=0;i{var e=n();return a(e)?e:e==null?[]:c(e)}),p,m=new Map,h=!0;function g(e){v.effect.f&16384||(v.pending.delete(e),v.fallback=d,Wr(v,p,s,t,r),d!==null&&(p.length===0?d.f&33554432?(d.f^=ce,Kr(d,null,s)):In(d):Pn(d,()=>{d=null})))}function _(e){v.pending.delete(e)}var v={effect:On(()=>{p=G(f);var e=p.length;let a=!1;C&&je(s)===`[!`!=(e===0)&&(s=Ae(),T(s),De(!1),a=!0);for(var c=new Set,u=j,v=ln(),y=0;yo(s)):(d=R(()=>o(Vr??=rn())),d.f|=ce)),e>c.size&&Le(``,``,``),C&&e>0&&T(Ae()),!h){if(m.set(u,c),v){for(let[e,t]of l)c.has(e)||u.skip_effect(t.e);u.oncommit(g),u.ondiscard(_)}else g(u)}a&&De(!0),G(f)}),flags:t,items:l,pending:m,outrogroups:null,fallback:d};h=!1,C&&(s=w)}function Ur(e){for(;e!==null&&!(e.f&32);)e=e.next;return e}function Wr(e,t,n,r,i){var a=!!(r&8),o=t.length,s=e.items,l=Ur(e.effect.first),u,d=null,f,p=[],m=[],h,g,_,v;if(a)for(v=0;v0){var re=r&4&&o===0?n:null;if(a){for(v=0;v{if(f!==void 0)for(_ of f)_.nodes?.a?.apply()})}function Gr(e,t,n,r,i,a,o,s){var c=o&1?o&16?Vt(n):M(n,!1,!1):null,l=o&2?Vt(i):null;return{v:c,i:l,e:R(()=>(a(t,c??n,l??i,s),()=>{e.delete(r)}))}}function Kr(e,t,n){if(e.nodes)for(var r=e.nodes.start,i=e.nodes.end,a=t&&!(t.f&33554432)?t.nodes.start:n;r!==null;){var o=on(r);if(a.before(r),r===i)return;r=o}}function qr(e,t,n){t===null?e.effect.first=n:t.next=n,n===null?e.effect.last=t:n.prev=t}function Jr(e,t){let n=null,r=C;var i;if(C){n=w;for(var a=an(document.head);a!==null&&(a.nodeType!==8||a.data!==e);)a=on(a);if(a===null)De(!1);else{var o=on(a);a.remove(),T(o)}}C||(i=document.head.appendChild(rn()));try{On(()=>{var e=R(()=>t(i));e.f|=ae,C||(e.nodes===null?e.nodes={start:i,end:i,a:null,t:null}:e.nodes.end=i)})}finally{r&&(De(!0),T(n))}}function Yr(e){var t,n,r=``;if(typeof e==`string`||typeof e==`number`)r+=e;else if(typeof e==`object`){if(Array.isArray(e)){var i=e.length;for(t=0;t=0;){var s=o+a;(o===0||Qr.includes(r[o-1]))&&(s===r.length||Qr.includes(r[s]))?r=(o===0?``:r.substring(0,o))+r.substring(s+1):o=s}}return r===``?null:r}function ei(e,t,n,r,i,a){var o=e[_e];if(C||o!==n||o===void 0){var s=$r(n,r,a);(!C||s!==e.getAttribute(`class`))&&(s==null?e.removeAttribute(`class`):t?e.className=s:e.setAttribute(`class`,s)),e[_e]=n}else if(a&&i!==a)for(var c in a){var l=!!a[c];(i==null||l!==!!i[c])&&e.classList.toggle(c,l)}return a}function ti(e,t){t?e.hasAttribute(`selected`)||e.setAttribute(`selected`,``):e.removeAttribute(`selected`)}function ni(e,t){var n=e.__defaultValue,r=e.multiple,i=r?n??[]:null;if(!r||a(i)){var o=e.selectedIndex,s=t&&r?new Set(e.selectedOptions):null;for(var c of e.options){var l=oi(c);ti(c,r?i.includes(l):Zt(l,n))}if(t){if(s!==null)for(c of e.options){var u=s.has(c);c.selected!==u&&(c.selected=u)}else e.selectedIndex!==o&&(e.selectedIndex=o)}}}function ri(e,t,n=!1){if(e.multiple){if(t==null)return;if(!a(t))return Te();for(var r of e.options)r.selected=t.includes(oi(r));return}for(r of e.options)if(Zt(oi(r),t)){r.selected=!0;return}(!n||t!==void 0)&&(e.selectedIndex=-1)}function ii(e){var t=new MutationObserver(t=>{t.every(si)||(`__defaultValue`in e&&ni(e,!1),`__value`in e&&ri(e,e.__value))});t.observe(e,{childList:!0,subtree:!0,attributes:!0,attributeFilter:[`value`]}),vn(()=>{t.disconnect()})}function ai(e,t,n=t){var r=new WeakSet,i=!0;ot(e,`change`,t=>{var i=t?`[selected]`:`:checked`,a;if(e.multiple)a=[].map.call(e.querySelectorAll(i),oi);else{var o=e.querySelector(i)??e.querySelector(`option:not([disabled])`);a=o&&oi(o)}n(a),e.__value=a,j!==null&&r.add(j)}),Cn(()=>{var a=t();if(e===document.activeElement){var o=j;if(r.has(o))return}if(ri(e,a,i),i&&a===void 0){var s=e.querySelector(`:checked`);s!==null&&(a=oi(s),n(a))}e.__value=a,i=!1})}function oi(e){return`__value`in e?e.__value:e.value}function si(e){if(e.target.closest(`selectedcontent`)!==null)return!0;if(e.type===`childList`){var t=[...e.addedNodes,...e.removedNodes];return t.length>0&&t.every(e=>e.nodeName===`SELECTEDCONTENT`)}return!1}var ci=Symbol(`is custom element`),li=Symbol(`is html`),ui=Se?`link`:`LINK`;function di(e){if(C){var t=!1,n=()=>{if(!t){if(t=!0,e.hasAttribute(`value`)){var n=e.value;Q(e,`value`,null),e.value=n}if(e.hasAttribute(`checked`)){var r=e.checked;Q(e,`checked`,null),e.checked=r}}};e[be]=n,Qe(n),it()}}function fi(e,t){var n=pi(e);n.checked!==(n.checked=t??void 0)&&(e.checked=t)}function Q(e,t,n,r){var i=pi(e);C&&(i[t]=e.getAttribute(t),t===`src`||t===`srcset`||t===`href`&&e.nodeName===ui)||i[t]!==(i[t]=n)&&(t===`loading`&&(e[he]=n),n==null?e.removeAttribute(t):typeof n!=`string`&&hi(e).has(t)?e[t]=n:e.setAttribute(t,n))}function pi(e){return e[ge]??={[ci]:e.nodeName.includes(`-`),[li]:e.namespaceURI===i}}var mi=new Map;function hi(e){var t=e.getAttribute(`is`)||e.nodeName,n=mi.get(t);if(n)return n;mi.set(t,n=new Set);for(var r,i=e,a=Element.prototype;a!==i;){for(var o in r=d(i),r)r[o].set&&o!==`innerHTML`&&o!==`textContent`&&o!==`innerText`&&n.add(o);i=m(i)}return n}function gi(e,t,n=t){var r=new WeakSet;ot(e,`input`,async i=>{var a=i?e.defaultValue:e.value;if(a=_i(e)?vi(a):a,n(a),j!==null&&r.add(j),await sr(),a!==(a=t())){var o=e.selectionStart,s=e.selectionEnd,c=e.value.length;if(e.value=a??``,s!==null){var l=e.value.length;o===s&&s===c&&l>c?(e.selectionStart=l,e.selectionEnd=l):(e.selectionStart=o,e.selectionEnd=Math.min(s,l))}}}),(C&&e.defaultValue!==e.value||K(t)==null&&e.value)&&(n(_i(e)?vi(e.value):e.value),j!==null&&r.add(j)),Dn(()=>{var n=t();if(e===document.activeElement){var i=j;if(r.has(i))return}_i(e)&&n===vi(e.value)||(e.type!==`date`||n||e.value)&&n!==e.value&&(e.value=n??``)})}function _i(e){var t=e.type;return t===`number`||t===`range`}function vi(e){return e===``?null:+e}function yi(e=!1){let t=D,n=t.l.u;if(!n)return;let r=()=>q(t.s);if(e){let e=0,n={},i=dt(()=>{let r=!1,i=t.s;for(let e in i)i[e]!==n[e]&&(n[e]=i[e],r=!0);return r&&e++,e});r=()=>G(i)}n.b.length&&xn(()=>{bi(t,r),v(n.b)}),yn(()=>{let e=K(()=>n.m.map(_));return()=>{for(let t of e)typeof t==`function`&&t()}}),n.a.length&&yn(()=>{bi(t,r),v(n.a)})}function bi(e,t){if(e.l.s)for(let t of e.l.s)G(t);t()}function xi(e,t,n){if(e==null)return t(void 0),n&&n(void 0),g;let r=K(()=>e.subscribe(t,n));return r.unsubscribe?()=>r.unsubscribe():r}var Si=[];function Ci(e,t=g){let n=null,r=new Set;function i(t){if(Ne(e,t)&&(e=t,n)){let t=!Si.length;for(let t of r)t[1](),Si.push(t,e);if(t){for(let e=0;e{r.delete(c),r.size===0&&n&&(n(),n=null)}}return{set:i,update:a,subscribe:o}}function wi(e){let t;return xi(e,e=>t=e)(),t}var Ti=!1,Ei=Symbol(`unmounted`);function Di(e,t,n){let r=n[t]??={store:null,source:M(void 0),unsubscribe:g};if(r.store!==e&&!(Ei in n)){if(r.unsubscribe(),r.store=e??null,e==null)r.source.v=void 0,r.unsubscribe=g;else{var i=!0;r.unsubscribe=xi(e,e=>{i?r.source.v=e:N(r.source,e)}),i=!1}}return e&&Ei in n?wi(e):G(r.source)}function Oi(){let e={};function t(){vn(()=>{for(var t in e)e[t].unsubscribe();l(e,Ei,{enumerable:!1,value:!0})})}return[e,t]}function ki(e){var t=Ti;try{return Ti=!1,[e(),Ti]}finally{Ti=t}}function $(t,n,r,i){var a=!e||!!(r&2),o=!!(r&8),s=!!(r&16),c=i,l=!0,d=void 0,f=()=>s&&a?(d??=dt(i),G(d)):(l&&(l=!1,c=s?K(i):i),c);let p;if(o){var m=fe in t||me in t;p=u(t,n)?.set??(m&&n in t?e=>t[n]=e:void 0)}var h,g=!1;o?[h,g]=ki(()=>t[n]):h=t[n],h===void 0&&i!==void 0&&(h=f(),p&&(a&&He(n),p(h)));var _=a?()=>{var e=t[n];return e===void 0?f():(l=!0,e)}:()=>{var e=t[n];return e!==void 0&&(c=void 0),e===void 0?c:e};if(a&&!(r&4))return _;if(p){var v=t.$$legacy;return(function(e,t){return arguments.length>0?((!a||!t||v||g)&&p(t?_():e),e):_()})}var y=!1,b=(r&1?dt:mt)(()=>(y=!1,_()));o&&G(b);var x=H;return(function(e,t){if(arguments.length>0){let n=t?G(b):a&&o?Yt(e):e;return N(b,n),y=!0,c!==void 0&&(c=n),e}return Vn&&y||x.f&16384?b.v:G(b)})}function Ai(){let e=Ci({controller:!1,busy:!1,error:``}),t,n=``,r={controller:!1,busy:!1,error:``},i=!1;function a(t){r={...r,...t},e.set(r)}async function o(e=!1){if(!i){i=!0;try{let t=await fetch(`/api/state`,{cache:`no-store`});if(!t.ok)throw Error(`The local setup session is unavailable.`);a({view:await t.json(),...e?{}:{error:``}})}catch{a({error:`Connection lost. The CLI may have stopped. Check the terminal before trying again.`})}finally{i=!1}}}async function s(){try{let e=await fetch(`/api/bootstrap`,{cache:`no-store`});if(!e.ok)throw Error(`Could not join this local session.`);let r=await e.json();t=r.capability,n=r.takeoverTicket,a({controller:r.controller,error:``}),await o()}catch{a({error:`Could not connect to the local setup session. Check the terminal.`})}}async function c(e,n,r=!0){let i=await fetch(e,{method:`POST`,cache:`no-store`,headers:{"Content-Type":`application/json`,...r&&t?{"X-Setup-Capability":t}:{}},body:JSON.stringify(n)}),a=await i.json();if(!i.ok)throw Error(String(a.error??`The request was rejected.`));return a}async function l(e,t){if(!r.busy&&r.controller&&r.view?.promptRevision===e){a({busy:!0,error:``});try{await c(`/api/answer`,{revision:e,value:t}),await o()}catch(e){let t=e instanceof Error?e.message:`Could not submit this answer.`;a({error:t}),/read-only|Control moved/.test(t)?await s():await o(!0)}finally{a({busy:!1})}}}async function u(){if(r.controller&&!r.busy){a({busy:!0,error:``});try{await c(`/api/cancel`,{}),await o()}catch(e){let t=e instanceof Error?e.message:`Cancellation failed.`;a({error:t}),/read-only|Control moved/.test(t)&&await s()}finally{a({busy:!1})}}}async function d(){try{let e=await c(`/api/takeover`,{ticket:n},!1);t=String(e.capability),a({controller:!0,error:``}),await o()}catch(e){a({error:e instanceof Error?e.message:`Takeover failed.`}),await s()}}async function f(){try{await c(`/api/close`,{})}catch{}}return{subscribe:e.subscribe,connect:s,refresh:o,submit:l,cancel:u,takeOver:d,close:f}}var ji=J(`
        • `),Mi=J(``);function Ni(e,t){O(t,!1);let n=$(t,`journey`,8),r=[`Repository`,`Setup choices`,`Setup PAT`,`Plan`,`Bot PAT & credentials`,`Apply`];yi();var i=Mi(),a=I(P(i),4);Hr(a,5,()=>r,Rr,(e,t,r)=>{var i=ji();let a;var o=P(i),s=F(o,!0),c=F(I(o,2),!0);E(i),L(e=>{Q(i,`aria-current`,(q(n()),K(()=>n()?.position===r+1?`step`:void 0))),a=ei(i,1,``,null,a,{current:n()?.position===r+1,completed:(n()?.position??0)>r+1}),X(s,e),X(c,G(t))},[()=>(q(n()),K(()=>(n()?.position??0)>r+1?`✓`:String(r+1).padStart(2,`0`)))]),Y(e,i)}),E(a),ke(2),E(i),Y(e,i),k()}var Pi=J(`
          `);function Fi(e,t){O(t,!1);let n=M(`system`);wn(()=>G(n),()=>{document.documentElement.dataset.theme=G(n)}),Tn();var r=Pi(),i=P(r);let a;var o=I(i,2);let s;var c=I(o,2);let l;E(r),L(()=>{Q(i,`aria-pressed`,G(n)===`system`),a=ei(i,1,``,null,a,{active:G(n)===`system`}),Q(o,`aria-pressed`,G(n)===`light`),s=ei(o,1,``,null,s,{active:G(n)===`light`}),Q(c,`aria-pressed`,G(n)===`dark`),l=ei(c,1,``,null,l,{active:G(n)===`dark`})}),mr(`click`,i,()=>N(n,`system`)),mr(`click`,o,()=>N(n,`light`)),mr(`click`,c,()=>N(n,`dark`)),Y(e,r),k()}hr([`click`]);var Ii=J(`
          LOCAL SESSION
          `);function Li(e,t){let n=$(t,`repository`,8);var r=Ii(),i=P(r),a=F(I(P(i),2),!0);E(i);var o=I(i,2);Fi(I(P(o)),{}),E(o),E(r),L(()=>X(a,n()??`Connecting…`)),Y(e,r)}var Ri=J(`

          `,1);function zi(e,t){O(t,!1);let n=M(),r=$(t,`view`,8);wn(()=>q(r()),()=>{N(n,r()?.outcome===`complete`?`Setup complete.`:r()?.outcome===`dry-run`?`Preview complete.`:r()?.outcome===`cancelled`?`Setup cancelled.`:r()?.outcome?`Setup needs attention.`:r()?.prompt?.title??`Preparing your setup…`)}),Tn(),yi();var i=Ri(),a=sn(i),o=I(P(a)),s=F(I(o));E(a);var c=I(a,2),l=F(c,!0),u=F(I(c,2),!0);L((e,t)=>{X(o,` ${e??``} `),X(s,`${t??``} / 06`),X(l,G(n)),X(u,(q(r()),K(()=>r()?.outcome?`The terminal contains the detailed result. Review the next steps below before closing this session.`:`One decision at a time. Your choices shape the permissions, plan and credentials needed for this repository.`)))},[()=>(q(r()),K(()=>r()?.journey?.current?.toUpperCase()??`GETTING READY`)),()=>(q(r()),K(()=>String(r()?.journey?.position??1).padStart(2,`0`)))]),Y(e,i),k()}function Bi(e){try{let t=new URL(e??``);return t.protocol===`https:`&&t.hostname===`github.com`&&(t.pathname===`/settings/personal-access-tokens`||t.pathname.startsWith(`/settings/personal-access-tokens/`))?t.toString():void 0}catch{return}}var Vi=J(``),Hi=J(``);function Ui(e,t){let n=$(t,`label`,8),r=$(t,`variant`,8,`primary`),i=$(t,`disabled`,8,!1),a=$(t,`arrow`,8,!1),o=$(t,`onClick`,8);var s=Hi(),c=P(s,!0),l=I(c),u=e=>{Y(e,Vi())};Z(l,e=>{a()&&e(u)}),E(s),L(()=>{ei(s,1,Zr(r())),s.disabled=i(),X(c,n())}),mr(`click`,s,function(...e){o()?.apply(this,e)}),Y(e,s)}hr([`click`]);var Wi=J(`Open GitHub link ↗`),Gi=J(`

          `);function Ki(e,t){O(t,!1);let n=M(),r=$(t,`tone`,8,`info`),i=$(t,`title`,8),a=$(t,`message`,8),o=$(t,`link`,8,void 0),s=$(t,`actionLabel`,8,void 0),c=$(t,`onAction`,8,void 0);wn(()=>q(o()),()=>{N(n,Bi(o()))}),Tn(),yi();var l=Gi(),u=P(l),d=F(u,!0),f=I(u),p=F(f,!0),m=I(f,2),h=e=>{var t=Wi();L(()=>Q(t,`href`,G(n))),Y(e,t)};Z(m,e=>{G(n)&&e(h)});var g=I(m,2),_=e=>{Ui(e,{get label(){return s()},variant:`secondary`,get onClick(){return c()}})};Z(g,e=>{s()&&c()&&e(_)}),E(l),L(()=>{ei(l,1,`banner ${r()??``}`),Q(l,`role`,r()===`error`?`alert`:`status`),X(d,i()),X(p,a())}),Y(e,l),k()}function qi(e){let t=String(e.question.defaultValue??``),n=t.split(`,`).map(e=>e.trim()).filter(Boolean);return{value:t,selected:e.question.kind===`multi-select`?(e.question.choices??[]).filter(e=>e!==`All`&&n.includes(e.split(` — `)[0])):e.question.kind===`scope-overrides`?n:[]}}function Ji(e,t){return t===`All`?e.includes(`All`)?[]:[`All`]:e.includes(t)?e.filter(e=>e!==t):[...e.filter(e=>e!==`All`),t]}function Yi(e,t,n){return e.question.kind===`scope-overrides`||e.question.kind===`multi-select`?n.length?n.join(`,`):`none`:t}var Xi=J(`PERMISSION PREVIEW`),Zi=J(`
          `),Qi=J(``),$i=J(``),ea=J(``),ta=J(`
          `),na=J(``),ra=J(`

          `,1);function ia(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`controller`,8),i=$(t,`busy`,8),a=$(t,`onSubmit`,8),o=qi(n()),s=M(o.value),c=M(o.selected);yi();var l=ra(),u=sn(l),d=P(u),f=F(d,!0),p=I(d),m=e=>{Y(e,Xi())};Z(p,e=>{q(n()),K(()=>n().phase===`permission-intent`)&&e(m)}),E(u);var h=I(u,2),g=e=>{var t=Zi(),i=P(t);let a;var o=I(i);let c;E(t),L(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),Q(i,`aria-pressed`,G(s)===`yes`||G(s)===`true`),i.disabled=!r(),a=ei(i,1,``,null,a,{selected:G(s)===`yes`||G(s)===`true`}),Q(o,`aria-pressed`,G(s)===`no`||G(s)===`false`),o.disabled=!r(),c=ei(o,1,``,null,c,{selected:G(s)===`no`||G(s)===`false`})}),mr(`click`,i,()=>N(s,`yes`)),mr(`click`,o,()=>N(s,`no`)),Y(e,t)},_=e=>{var t=$i();Hr(t,5,()=>(q(n()),K(()=>n().question.choices??[])),Rr,(e,t)=>{var n=Qi(),r=F(n,!0),i={};L(()=>{X(r,G(t)),i!==(i=G(t))&&(n.value=(n.__value=i)??``)}),Y(e,n)}),E(t),ii(t),L(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),t.disabled=!r()}),ai(t,()=>G(s),e=>N(s,e)),Y(e,t)},v=e=>{var t=ta();Hr(t,5,()=>(q(n()),K(()=>n().question.kind===`multi-select`?n().question.choices??[]:n().question.allowedNames??[])),Rr,(e,t)=>{var n=ea(),i=P(n);di(i);var a=F(I(i),!0);E(n),L(e=>{fi(i,e),i.disabled=!r(),X(a,G(t))},[()=>(G(c),G(t),K(()=>G(c).includes(G(t))))]),mr(`change`,i,()=>N(c,Ji(G(c),G(t)))),Y(e,n)}),E(t),L(()=>Q(t,`aria-label`,(q(n()),K(()=>n().question.label)))),Y(e,t)},y=e=>{var t=na();di(t),L(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),Q(t,`type`,(q(n()),K(()=>n().question.kind===`number`?`number`:`text`))),Q(t,`min`,(q(n()),K(()=>n().question.kind===`number`?0:void 0))),t.disabled=!r()}),gi(t,()=>G(s),e=>N(s,e)),Y(e,t)};Z(h,e=>{q(n()),K(()=>n().question.kind===`boolean`)?e(g):(q(n()),K(()=>n().question.kind===`choice`)?e(_,1):(q(n()),K(()=>n().question.kind===`multi-select`||n().question.kind===`scope-overrides`)?e(v,2):e(y,-1)))});var b=I(h,2),x=F(b),S=I(b,2);{let e=mt(()=>!r()||i());Ui(S,{label:`Continue`,arrow:!0,onClick:()=>a()(Yi(n(),G(s),G(c))),get disabled(){return G(e)}})}L((e,t)=>{X(f,e),X(x,`Suggested answer: ${t??``} · You can review choices again before creating your setup PAT.`)},[()=>(q(n()),K(()=>n().question.label.replace(` (Space toggles, Enter confirms)`,``))),()=>(q(n()),K(()=>String(n().question.defaultValue)||`none`))]),Y(e,l),k()}hr([`click`,`change`]);var aa=J(``),oa=J(`
          `);function sa(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`controller`,8),i=$(t,`busy`,8),a=$(t,`onSubmit`,8);yi();var o=oa();Hr(o,5,()=>(q(n()),K(()=>n().choices)),Rr,(e,t)=>{var n=aa(),o=F(P(n),!0);ke(),E(n),L(()=>{n.disabled=!r()||i(),X(o,G(t))}),mr(`click`,n,()=>a()(G(t))),Y(e,n)}),E(o),Y(e,o),k()}hr([`click`]);var ca=J(`Open the official GitHub PAT form

          Check the signed-in account, select Only select repositories, then select this repository. GitHub handles 2FA and creates the PAT.

          `,1),la=J(`

          Sent only to this local process. It will not be shown again or saved in browser storage.

          `),ua=J(` `,1);function da(e,t){O(t,!1);let n=M(),r=$(t,`prompt`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8),s=M(``);function c(){let e=G(s);r().kind===`secret`&&N(s,``),o()(e)}wn(()=>q(r()),()=>{N(n,Bi(r().link))}),Tn(),yi();var l=ua(),u=sn(l),d=e=>{var t=ca(),r=sn(t);ke(),L(()=>Q(r,`href`,G(n))),Y(e,t)};Z(u,e=>{G(n)&&e(d)});var f=I(u,2),p=F(f,!0),m=I(f,2);di(m);var h=I(m,2),g=e=>{Y(e,la())};Z(h,e=>{q(r()),K(()=>r().kind===`secret`)&&e(g)});var _=I(h,2);{let e=mt(()=>(q(i()),q(a()),q(r()),G(s),K(()=>!i()||a()||!r().optional&&!G(s).trim())));Ui(_,{label:`Continue`,arrow:!0,onClick:c,get disabled(){return G(e)}})}L(()=>{X(p,(q(r()),K(()=>r().kind===`secret`?`Paste the value here`:`Your answer`))),Q(m,`type`,(q(r()),K(()=>r().kind===`secret`?`password`:`text`))),m.disabled=!i(),Q(m,`placeholder`,(q(r()),K(()=>r().kind===`secret`?`Hidden after submission`:`Type your answer`)))}),gi(m,()=>G(s),e=>N(s,e)),Y(e,l),k()}var fa=J(`
        • `),pa=J(`

            `),ma=J(`

            Before you continue

              `),ha=J(`

              Review exactly what this run may change. The bot PAT and any additional credentials are collected next.

              `,1);function ga(e,t){O(t,!1);let n=M(),r=$(t,`prompt`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8);wn(()=>q(r()),()=>{N(n,[{title:`Files`,items:r().plan.files},{title:`Workflows`,items:r().plan.workflows},{title:`Variables`,items:r().plan.variables},{title:`Secret names`,items:r().plan.secrets}])}),Tn(),yi();var s=ha(),c=I(sn(s),2);Hr(c,5,()=>G(n),Rr,(e,t)=>{var n=pa(),r=P(n),i=P(r),a=F(I(i),!0);E(r);var o=I(r);Hr(o,5,()=>(G(t),K(()=>G(t).items)),Rr,(e,t)=>{var n=fa(),r=F(n,!0);L(()=>X(r,G(t))),Y(e,n)}),E(o),E(n),L(()=>{X(i,`${G(t),K(()=>G(t).title)??``} `),X(a,(G(t),K(()=>G(t).items.length)))}),Y(e,n)}),E(c);var l=I(c,2),u=e=>{var t=ma(),n=I(P(t));Hr(n,5,()=>(q(r()),K(()=>r().plan.warnings)),Rr,(e,t)=>{var n=fa(),r=F(n,!0);L(()=>X(r,G(t))),Y(e,n)}),E(n),E(t),Y(e,t)};Z(l,e=>{q(r()),K(()=>r().plan.warnings.length)&&e(u)});var d=I(l,2),f=P(d);{let e=mt(()=>!i()||a());Ui(f,{label:`Stop here`,variant:`secondary`,onClick:()=>o()(`decline`),get disabled(){return G(e)}})}var p=I(f);{let e=mt(()=>!i()||a());Ui(p,{label:`Approve this plan`,arrow:!0,onClick:()=>o()(`approve`),get disabled(){return G(e)}})}E(d),Y(e,s),k()}var _a=J(`

              `),va=J(`
              CURRENT DECISION
              `);function ya(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`revision`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8);yi();var s=va(),c=P(s),l=F(I(P(c)));E(c);var u=I(c,2),d=e=>{var t=_a(),r=F(t,!0);L(()=>X(r,(q(n()),K(()=>n().description)))),Y(e,t)};Z(u,e=>{q(n()),K(()=>`description`in n()&&n().description)&&e(d)});var f=I(u,2),p=e=>{ia(e,{get prompt(){return n()},get controller(){return i()},get busy(){return a()},get onSubmit(){return o()}})},m=e=>{sa(e,{get prompt(){return n()},get controller(){return i()},get busy(){return a()},get onSubmit(){return o()}})},h=e=>{da(e,{get prompt(){return n()},get controller(){return i()},get busy(){return a()},get onSubmit(){return o()}})},g=e=>{ga(e,{get prompt(){return n()},get controller(){return i()},get busy(){return a()},get onSubmit(){return o()}})};Z(f,e=>{q(n()),K(()=>n().kind===`question`)?e(p):(q(n()),K(()=>n().kind===`choice`||n().kind===`confirm`)?e(m,1):(q(n()),K(()=>n().kind===`text`||n().kind===`secret`)?e(h,2):(q(n()),K(()=>n().kind===`plan`)&&e(g,3))))}),E(s),L(()=>X(l,`SESSION ${r()??``}`)),Y(e,s),k()}var ba=J(` `),xa=J(`
            • `),Sa=J(`

                Conditional grants depend on the choices and GitHub facts. A final audit runs before setup changes.
                `),Ca=J(`

                Permissions follow your choices

                We’ll show the exact grants before you create each PAT. Nothing is created just by opening this page.

                `),wa=J(``);function Ta(e,t){O(t,!1);let n=$(t,`view`,8);yi();var r=wa(),i=P(r),a=F(I(P(i),3),!0);E(i);var o=I(i,2),s=e=>{var t=Sa(),r=I(P(t)),i=F(r),a=I(r),o=F(a,!0),s=I(a,2);Hr(s,5,()=>(q(n()),K(()=>n().permissions.report?.checks??n().permissions.requirements??[])),Rr,(e,t)=>{var n=xa(),r=P(n),i=P(r),a=I(i),o=F(a),s=F(I(a),!0);E(r);var c=I(r),l=P(c,!0),u=I(l),d=e=>{var n=ba(),r=F(n,!0);L(()=>X(r,(G(t),K(()=>G(t).status)))),Y(e,n)};Z(u,e=>{`status`in G(t)&&e(d)}),E(c),E(n),L(()=>{X(i,`${G(t),K(()=>G(t).permission)??``} `),X(o,`${G(t),K(()=>G(t).scope)??``} · ${G(t),K(()=>G(t).applicability)??``}`),X(s,(G(t),K(()=>G(t).reason))),X(l,(G(t),K(()=>G(t).level)))}),Y(e,n)}),E(s),ke(2),E(t),L(()=>{X(i,`${q(n()),K(()=>n().permissions.role===`setup`?`Setup PAT`:`Bot PAT`)??``} access`),X(o,(q(n()),K(()=>n().permissions.report?`Read-only access check`:`Provisional least-privilege grants`)))}),Y(e,t)},c=e=>{Y(e,Ca())};Z(o,e=>{q(n()),K(()=>n().permissions)?e(s):e(c,-1)}),E(r),L(()=>X(a,(q(n()),K(()=>n().repository)))),Y(e,r),k()}var Ea=J(`

                `);function Da(e,t){O(t,!1);let n=M(),r=M(),i=$(t,`outcome`,8),a=$(t,`controller`,8),o=$(t,`onClose`,8);wn(()=>q(i()),()=>{N(n,i()===`complete`?`Your configuration was applied`:i()===`dry-run`?`No changes were made`:i()===`cancelled`||i()===`blocked`?`No setup changes started`:`Check partial changes before retrying`)}),wn(()=>q(i()),()=>{N(r,i()===`complete`?`The temporary setup PAT is not revoked automatically. Delete it in GitHub after confirming your setup. Keep the bot PAT until you rotate the installed Secret.`:i()===`partial`?`A bot Secret write or another change may have succeeded. Inspect GitHub and run copilot doctor before replacing or deleting its PAT.`:`Copilot did not begin applying setup changes. PATs you created in GitHub still exist until you delete them there.`)}),Tn();var s=Ea(),c=P(s),l=F(c,!0),u=I(c),d=F(u,!0),f=I(u),p=F(f,!0),m=I(f,2),h=e=>{Ui(e,{label:`Close local session`,get onClick(){return o()}})};Z(m,e=>{a()&&e(h)}),E(s),L(()=>{X(l,i()===`complete`?`✓`:`!`),X(d,G(n)),X(p,G(r))}),Y(e,s),k()}var Oa=J(`

                Working on the next step

                The local process is checking your answers and preparing the next decision. Keep this page open.

                `);function ka(e){Y(e,Oa())}var Aa=J(``),ja=J(`
                `),Ma=J(``),Na=J(`
                `,1),Pa=J(`
                LOCALHOST ONLY NO CLOUD SETUP ACCOUNT GITHUB OWNS PAT ISSUANCE
                `);function Fa(e,t){O(t,!1);let n=()=>Di(a,`$session`,r),[r,i]=Oi(),a=Ai();Pr(()=>{a.connect();let e=window.setInterval(()=>{n().view?.outcome||a.refresh()},900);return()=>window.clearInterval(e)});async function o(){window.confirm(`Cancel this local setup session? PATs already created in GitHub will still exist.`)&&await a.cancel()}async function s(e){let t=n().view?.promptRevision;t!==void 0&&await a.submit(t,e)}yi();var c=Pa();Jr(`16t12jp`,e=>{Y(e,Aa())});var l=P(c);{let e=mt(()=>n().view?.journey);Ni(l,{get journey(){return G(e)}})}var u=I(l,2),d=P(u);{let e=mt(()=>n().view?.repository);Li(d,{get repository(){return G(e)}})}var f=I(d,2),p=P(f);zi(p,{get view(){return n().view}});var m=I(p,2),h=e=>{var t=ja(),r=I(P(t));E(t),L(()=>X(r,` Reviewing saved choices — pass ${n().view.journey.choiceReviewPass??``}. This is the same setup run, not a restart.`)),Y(e,t)};Z(m,e=>{n().view?.journey?.choiceReviewPass&&n().view.journey.choiceReviewPass>1&&!n().view.outcome&&e(h)});var g=I(m,2),_=e=>{Ki(e,{tone:`warning`,title:`Read-only tab`,message:`Another tab controls this session. You can review progress here or explicitly take over.`,actionLabel:`Take control in this tab`,get onAction(){return a.takeOver}})};Z(g,e=>{!n().controller&&n().view&&e(_)});var v=I(g,2),y=e=>{Ki(e,{tone:`error`,title:`Needs attention`,get message(){return n().error}})};Z(v,e=>{n().error&&e(y)});var b=I(v,2),x=e=>{{let t=mt(()=>n().view.message.tone===`success`?`Checked`:n().view.message.tone===`warning`?`Please note`:n().view.message.tone===`error`?`Needs attention`:`Progress update`);Ki(e,{get tone(){return n().view.message.tone},get title(){return G(t)},get message(){return n().view.message.text},get link(){return n().view.message.link}})}};Z(b,e=>{n().view?.message&&e(x)});var S=I(b,2),ee=e=>{Da(e,{get outcome(){return n().view.outcome},get controller(){return n().controller},get onClose(){return a.close}})},te=e=>{var t=Na(),r=sn(t),i=P(r);Lr(i,()=>n().view.promptRevision,e=>{ya(e,{get prompt(){return n().view.prompt},get revision(){return n().view.revision},get controller(){return n().controller},get busy(){return n().busy},onSubmit:s})}),Ta(I(i,2),{get view(){return n().view}}),E(r);var a=I(r,2),c=e=>{var t=Ma();L(()=>t.disabled=n().busy),mr(`click`,t,o),Y(e,t)};Z(a,e=>{n().controller&&n().view.journey?.current!==`Apply`&&e(c)}),Y(e,t)},ne=e=>{ka(e,{})};Z(S,e=>{n().view?.outcome?e(ee):n().view?.prompt?e(te,1):e(ne,-1)}),ke(2),E(f),E(u),E(c),Y(e,c),k(),i()}hr([`click`]),kr(Fa,{target:document.getElementById(`app`)}); \ No newline at end of file +\r\f\xA0\v`];function $r(e,t,n){var r=e==null?``:``+e;if(t&&(r=r?r+` `+t:t),n){for(var i of Object.keys(n))if(n[i])r=r?r+` `+i:i;else if(r.length)for(var a=i.length,o=0;(o=r.indexOf(i,o))>=0;){var s=o+a;(o===0||Qr.includes(r[o-1]))&&(s===r.length||Qr.includes(r[s]))?r=(o===0?``:r.substring(0,o))+r.substring(s+1):o=s}}return r===``?null:r}function ei(e,t,n,r,i,a){var o=e[_e];if(C||o!==n||o===void 0){var s=$r(n,r,a);(!C||s!==e.getAttribute(`class`))&&(s==null?e.removeAttribute(`class`):t?e.className=s:e.setAttribute(`class`,s)),e[_e]=n}else if(a&&i!==a)for(var c in a){var l=!!a[c];(i==null||l!==!!i[c])&&e.classList.toggle(c,l)}return a}function ti(e,t){t?e.hasAttribute(`selected`)||e.setAttribute(`selected`,``):e.removeAttribute(`selected`)}function ni(e,t){var n=e.__defaultValue,r=e.multiple,i=r?n??[]:null;if(!r||a(i)){var o=e.selectedIndex,s=t&&r?new Set(e.selectedOptions):null;for(var c of e.options){var l=oi(c);ti(c,r?i.includes(l):Zt(l,n))}if(t){if(s!==null)for(c of e.options){var u=s.has(c);c.selected!==u&&(c.selected=u)}else e.selectedIndex!==o&&(e.selectedIndex=o)}}}function ri(e,t,n=!1){if(e.multiple){if(t==null)return;if(!a(t))return Te();for(var r of e.options)r.selected=t.includes(oi(r));return}for(r of e.options)if(Zt(oi(r),t)){r.selected=!0;return}(!n||t!==void 0)&&(e.selectedIndex=-1)}function ii(e){var t=new MutationObserver(t=>{t.every(si)||(`__defaultValue`in e&&ni(e,!1),`__value`in e&&ri(e,e.__value))});t.observe(e,{childList:!0,subtree:!0,attributes:!0,attributeFilter:[`value`]}),vn(()=>{t.disconnect()})}function ai(e,t,n=t){var r=new WeakSet,i=!0;ot(e,`change`,t=>{var i=t?`[selected]`:`:checked`,a;if(e.multiple)a=[].map.call(e.querySelectorAll(i),oi);else{var o=e.querySelector(i)??e.querySelector(`option:not([disabled])`);a=o&&oi(o)}n(a),e.__value=a,j!==null&&r.add(j)}),Cn(()=>{var a=t();if(e===document.activeElement){var o=j;if(r.has(o))return}if(ri(e,a,i),i&&a===void 0){var s=e.querySelector(`:checked`);s!==null&&(a=oi(s),n(a))}e.__value=a,i=!1})}function oi(e){return`__value`in e?e.__value:e.value}function si(e){if(e.target.closest(`selectedcontent`)!==null)return!0;if(e.type===`childList`){var t=[...e.addedNodes,...e.removedNodes];return t.length>0&&t.every(e=>e.nodeName===`SELECTEDCONTENT`)}return!1}var ci=Symbol(`is custom element`),li=Symbol(`is html`),ui=Se?`link`:`LINK`;function di(e){if(C){var t=!1,n=()=>{if(!t){if(t=!0,e.hasAttribute(`value`)){var n=e.value;Q(e,`value`,null),e.value=n}if(e.hasAttribute(`checked`)){var r=e.checked;Q(e,`checked`,null),e.checked=r}}};e[be]=n,Qe(n),it()}}function fi(e,t){var n=pi(e);n.checked!==(n.checked=t??void 0)&&(e.checked=t)}function Q(e,t,n,r){var i=pi(e);C&&(i[t]=e.getAttribute(t),t===`src`||t===`srcset`||t===`href`&&e.nodeName===ui)||i[t]!==(i[t]=n)&&(t===`loading`&&(e[he]=n),n==null?e.removeAttribute(t):typeof n!=`string`&&hi(e).has(t)?e[t]=n:e.setAttribute(t,n))}function pi(e){return e[ge]??={[ci]:e.nodeName.includes(`-`),[li]:e.namespaceURI===i}}var mi=new Map;function hi(e){var t=e.getAttribute(`is`)||e.nodeName,n=mi.get(t);if(n)return n;mi.set(t,n=new Set);for(var r,i=e,a=Element.prototype;a!==i;){for(var o in r=d(i),r)r[o].set&&o!==`innerHTML`&&o!==`textContent`&&o!==`innerText`&&n.add(o);i=m(i)}return n}function gi(e,t,n=t){var r=new WeakSet;ot(e,`input`,async i=>{var a=i?e.defaultValue:e.value;if(a=_i(e)?vi(a):a,n(a),j!==null&&r.add(j),await sr(),a!==(a=t())){var o=e.selectionStart,s=e.selectionEnd,c=e.value.length;if(e.value=a??``,s!==null){var l=e.value.length;o===s&&s===c&&l>c?(e.selectionStart=l,e.selectionEnd=l):(e.selectionStart=o,e.selectionEnd=Math.min(s,l))}}}),(C&&e.defaultValue!==e.value||K(t)==null&&e.value)&&(n(_i(e)?vi(e.value):e.value),j!==null&&r.add(j)),Dn(()=>{var n=t();if(e===document.activeElement){var i=j;if(r.has(i))return}_i(e)&&n===vi(e.value)||(e.type!==`date`||n||e.value)&&n!==e.value&&(e.value=n??``)})}function _i(e){var t=e.type;return t===`number`||t===`range`}function vi(e){return e===``?null:+e}function yi(e=!1){let t=D,n=t.l.u;if(!n)return;let r=()=>q(t.s);if(e){let e=0,n={},i=dt(()=>{let r=!1,i=t.s;for(let e in i)i[e]!==n[e]&&(n[e]=i[e],r=!0);return r&&e++,e});r=()=>G(i)}n.b.length&&xn(()=>{bi(t,r),v(n.b)}),yn(()=>{let e=K(()=>n.m.map(_));return()=>{for(let t of e)typeof t==`function`&&t()}}),n.a.length&&yn(()=>{bi(t,r),v(n.a)})}function bi(e,t){if(e.l.s)for(let t of e.l.s)G(t);t()}function xi(e,t,n){if(e==null)return t(void 0),n&&n(void 0),g;let r=K(()=>e.subscribe(t,n));return r.unsubscribe?()=>r.unsubscribe():r}var Si=[];function Ci(e,t=g){let n=null,r=new Set;function i(t){if(Ne(e,t)&&(e=t,n)){let t=!Si.length;for(let t of r)t[1](),Si.push(t,e);if(t){for(let e=0;e{r.delete(c),r.size===0&&n&&(n(),n=null)}}return{set:i,update:a,subscribe:o}}function wi(e){let t;return xi(e,e=>t=e)(),t}var Ti=!1,Ei=Symbol(`unmounted`);function Di(e,t,n){let r=n[t]??={store:null,source:M(void 0),unsubscribe:g};if(r.store!==e&&!(Ei in n)){if(r.unsubscribe(),r.store=e??null,e==null)r.source.v=void 0,r.unsubscribe=g;else{var i=!0;r.unsubscribe=xi(e,e=>{i?r.source.v=e:N(r.source,e)}),i=!1}}return e&&Ei in n?wi(e):G(r.source)}function Oi(){let e={};function t(){vn(()=>{for(var t in e)e[t].unsubscribe();l(e,Ei,{enumerable:!1,value:!0})})}return[e,t]}function ki(e){var t=Ti;try{return Ti=!1,[e(),Ti]}finally{Ti=t}}function $(t,n,r,i){var a=!e||!!(r&2),o=!!(r&8),s=!!(r&16),c=i,l=!0,d=void 0,f=()=>s&&a?(d??=dt(i),G(d)):(l&&(l=!1,c=s?K(i):i),c);let p;if(o){var m=fe in t||me in t;p=u(t,n)?.set??(m&&n in t?e=>t[n]=e:void 0)}var h,g=!1;o?[h,g]=ki(()=>t[n]):h=t[n],h===void 0&&i!==void 0&&(h=f(),p&&(a&&He(n),p(h)));var _=a?()=>{var e=t[n];return e===void 0?f():(l=!0,e)}:()=>{var e=t[n];return e!==void 0&&(c=void 0),e===void 0?c:e};if(a&&!(r&4))return _;if(p){var v=t.$$legacy;return(function(e,t){return arguments.length>0?((!a||!t||v||g)&&p(t?_():e),e):_()})}var y=!1,b=(r&1?dt:mt)(()=>(y=!1,_()));o&&G(b);var x=H;return(function(e,t){if(arguments.length>0){let n=t?G(b):a&&o?Yt(e):e;return N(b,n),y=!0,c!==void 0&&(c=n),e}return Vn&&y||x.f&16384?b.v:G(b)})}function Ai(){let e=Ci({controller:!1,busy:!1,error:``}),t,n=``,r={controller:!1,busy:!1,error:``},i=!1;function a(t){r={...r,...t},e.set(r)}async function o(e=!1){if(!i){i=!0;try{let t=await fetch(`/api/state`,{cache:`no-store`});if(!t.ok)throw Error(`The local setup session is unavailable.`);a({view:await t.json(),...e?{}:{error:``}})}catch{a({view:void 0,error:`Connection lost. The CLI may have stopped. Check the terminal before trying again.`})}finally{i=!1}}}async function s(){try{let e=await fetch(`/api/bootstrap`,{cache:`no-store`});if(!e.ok)throw Error(`Could not join this local session.`);let r=await e.json();t=r.capability,n=r.takeoverTicket,a({controller:r.controller,error:``}),await o()}catch{t=void 0,n=``,a({view:void 0,controller:!1,error:`Could not connect to the local setup session. Check the terminal.`})}}async function c(e,n,r=!0){let i=await fetch(e,{method:`POST`,cache:`no-store`,headers:{"Content-Type":`application/json`,...r&&t?{"X-Setup-Capability":t}:{}},body:JSON.stringify(n)}),a=await i.json();if(!i.ok)throw Error(String(a.error??`The request was rejected.`));return a}async function l(e,t){if(!r.busy&&r.controller&&r.view?.promptRevision===e){a({busy:!0,error:``});try{await c(`/api/answer`,{revision:e,value:t}),await o()}catch(e){let t=e instanceof Error?e.message:`Could not submit this answer.`;a({error:t}),/read-only|Control moved/.test(t)?await s():await o(!0)}finally{a({busy:!1})}}}async function u(){if(r.controller&&!r.busy){a({busy:!0,error:``});try{await c(`/api/cancel`,{}),await o()}catch(e){let t=e instanceof Error?e.message:`Cancellation failed.`;a({error:t}),/read-only|Control moved/.test(t)&&await s()}finally{a({busy:!1})}}}async function d(){try{let e=await c(`/api/takeover`,{ticket:n},!1);t=String(e.capability),a({controller:!0,error:``}),await o()}catch(e){a({error:e instanceof Error?e.message:`Takeover failed.`}),await s()}}async function f(){try{await c(`/api/close`,{})}catch{}}return{subscribe:e.subscribe,connect:s,refresh:o,submit:l,cancel:u,takeOver:d,close:f}}var ji=J(`
              • `),Mi=J(``);function Ni(e,t){O(t,!1);let n=$(t,`journey`,8),r=[`Repository`,`Setup choices`,`Setup PAT`,`Plan`,`Bot PAT & credentials`,`Apply`];yi();var i=Mi(),a=I(P(i),4);Hr(a,5,()=>r,Rr,(e,t,r)=>{var i=ji();let a;var o=P(i),s=F(o,!0),c=F(I(o,2),!0);E(i),L(e=>{Q(i,`aria-current`,(q(n()),K(()=>n()?.position===r+1?`step`:void 0))),a=ei(i,1,``,null,a,{current:n()?.position===r+1,completed:(n()?.position??0)>r+1}),X(s,e),X(c,G(t))},[()=>(q(n()),K(()=>(n()?.position??0)>r+1?`✓`:String(r+1).padStart(2,`0`)))]),Y(e,i)}),E(a),ke(2),E(i),Y(e,i),k()}var Pi=J(`
                `);function Fi(e,t){O(t,!1);let n=M(`system`);wn(()=>G(n),()=>{document.documentElement.dataset.theme=G(n)}),Tn();var r=Pi(),i=P(r);let a;var o=I(i,2);let s;var c=I(o,2);let l;E(r),L(()=>{Q(i,`aria-pressed`,G(n)===`system`),a=ei(i,1,``,null,a,{active:G(n)===`system`}),Q(o,`aria-pressed`,G(n)===`light`),s=ei(o,1,``,null,s,{active:G(n)===`light`}),Q(c,`aria-pressed`,G(n)===`dark`),l=ei(c,1,``,null,l,{active:G(n)===`dark`})}),mr(`click`,i,()=>N(n,`system`)),mr(`click`,o,()=>N(n,`light`)),mr(`click`,c,()=>N(n,`dark`)),Y(e,r),k()}hr([`click`]);var Ii=J(`
                LOCAL SESSION
                `);function Li(e,t){let n=$(t,`repository`,8);var r=Ii(),i=P(r),a=F(I(P(i),2),!0);E(i);var o=I(i,2);Fi(I(P(o)),{}),E(o),E(r),L(()=>X(a,n()??`Connecting…`)),Y(e,r)}var Ri=J(`

                `,1);function zi(e,t){O(t,!1);let n=M(),r=$(t,`view`,8);wn(()=>q(r()),()=>{N(n,r()?.outcome===`complete`?`Setup complete.`:r()?.outcome===`dry-run`?`Preview complete.`:r()?.outcome===`cancelled`?`Setup cancelled.`:r()?.outcome?`Setup needs attention.`:r()?.prompt?.title??`Preparing your setup…`)}),Tn(),yi();var i=Ri(),a=sn(i),o=I(P(a)),s=F(I(o));E(a);var c=I(a,2),l=F(c,!0),u=F(I(c,2),!0);L((e,t)=>{X(o,` ${e??``} `),X(s,`${t??``} / 06`),X(l,G(n)),X(u,(q(r()),K(()=>r()?.outcome?`The terminal contains the detailed result. Review the next steps below before closing this session.`:`One decision at a time. Your choices shape the permissions, plan and credentials needed for this repository.`)))},[()=>(q(r()),K(()=>r()?.journey?.current?.toUpperCase()??`GETTING READY`)),()=>(q(r()),K(()=>String(r()?.journey?.position??1).padStart(2,`0`)))]),Y(e,i),k()}function Bi(e){try{let t=new URL(e??``);return t.protocol===`https:`&&t.hostname===`github.com`&&(t.pathname===`/settings/personal-access-tokens`||t.pathname.startsWith(`/settings/personal-access-tokens/`))?t.toString():void 0}catch{return}}var Vi=J(``),Hi=J(``);function Ui(e,t){let n=$(t,`label`,8),r=$(t,`variant`,8,`primary`),i=$(t,`disabled`,8,!1),a=$(t,`arrow`,8,!1),o=$(t,`onClick`,8);var s=Hi(),c=P(s,!0),l=I(c),u=e=>{Y(e,Vi())};Z(l,e=>{a()&&e(u)}),E(s),L(()=>{ei(s,1,Zr(r())),s.disabled=i(),X(c,n())}),mr(`click`,s,function(...e){o()?.apply(this,e)}),Y(e,s)}hr([`click`]);var Wi=J(`Open GitHub link ↗`),Gi=J(`

                `);function Ki(e,t){O(t,!1);let n=M(),r=$(t,`tone`,8,`info`),i=$(t,`title`,8),a=$(t,`message`,8),o=$(t,`link`,8,void 0),s=$(t,`actionLabel`,8,void 0),c=$(t,`onAction`,8,void 0);wn(()=>q(o()),()=>{N(n,Bi(o()))}),Tn(),yi();var l=Gi(),u=P(l),d=F(u,!0),f=I(u),p=F(f,!0),m=I(f,2),h=e=>{var t=Wi();L(()=>Q(t,`href`,G(n))),Y(e,t)};Z(m,e=>{G(n)&&e(h)});var g=I(m,2),_=e=>{Ui(e,{get label(){return s()},variant:`secondary`,get onClick(){return c()}})};Z(g,e=>{s()&&c()&&e(_)}),E(l),L(()=>{ei(l,1,`banner ${r()??``}`),Q(l,`role`,r()===`error`?`alert`:`status`),X(d,i()),X(p,a())}),Y(e,l),k()}function qi(e){let t=String(e.question.defaultValue??``),n=t.split(`,`).map(e=>e.trim()).filter(Boolean);return{value:t,selected:e.question.kind===`multi-select`?(e.question.choices??[]).filter(e=>e!==`All`&&n.includes(e.split(` — `)[0])):e.question.kind===`scope-overrides`?n:[]}}function Ji(e,t){return t===`All`?e.includes(`All`)?[]:[`All`]:e.includes(t)?e.filter(e=>e!==t):[...e.filter(e=>e!==`All`),t]}function Yi(e,t,n){return e.question.kind===`scope-overrides`||e.question.kind===`multi-select`?n.length?n.join(`,`):`none`:t}var Xi=J(`PERMISSION PREVIEW`),Zi=J(`
                `),Qi=J(``),$i=J(``),ea=J(``),ta=J(`
                `),na=J(``),ra=J(`

                `,1);function ia(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`controller`,8),i=$(t,`busy`,8),a=$(t,`onSubmit`,8),o=qi(n()),s=M(o.value),c=M(o.selected);yi();var l=ra(),u=sn(l),d=P(u),f=F(d,!0),p=I(d),m=e=>{Y(e,Xi())};Z(p,e=>{q(n()),K(()=>n().phase===`permission-intent`)&&e(m)}),E(u);var h=I(u,2),g=e=>{var t=Zi(),i=P(t);let a;var o=I(i);let c;E(t),L(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),Q(i,`aria-pressed`,G(s)===`yes`||G(s)===`true`),i.disabled=!r(),a=ei(i,1,``,null,a,{selected:G(s)===`yes`||G(s)===`true`}),Q(o,`aria-pressed`,G(s)===`no`||G(s)===`false`),o.disabled=!r(),c=ei(o,1,``,null,c,{selected:G(s)===`no`||G(s)===`false`})}),mr(`click`,i,()=>N(s,`yes`)),mr(`click`,o,()=>N(s,`no`)),Y(e,t)},_=e=>{var t=$i();Hr(t,5,()=>(q(n()),K(()=>n().question.choices??[])),Rr,(e,t)=>{var n=Qi(),r=F(n,!0),i={};L(()=>{X(r,G(t)),i!==(i=G(t))&&(n.value=(n.__value=i)??``)}),Y(e,n)}),E(t),ii(t),L(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),t.disabled=!r()}),ai(t,()=>G(s),e=>N(s,e)),Y(e,t)},v=e=>{var t=ta();Hr(t,5,()=>(q(n()),K(()=>n().question.kind===`multi-select`?n().question.choices??[]:n().question.allowedNames??[])),Rr,(e,t)=>{var n=ea(),i=P(n);di(i);var a=F(I(i),!0);E(n),L(e=>{fi(i,e),i.disabled=!r(),X(a,G(t))},[()=>(G(c),G(t),K(()=>G(c).includes(G(t))))]),mr(`change`,i,()=>N(c,Ji(G(c),G(t)))),Y(e,n)}),E(t),L(()=>Q(t,`aria-label`,(q(n()),K(()=>n().question.label)))),Y(e,t)},y=e=>{var t=na();di(t),L(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),Q(t,`type`,(q(n()),K(()=>n().question.kind===`number`?`number`:`text`))),Q(t,`min`,(q(n()),K(()=>n().question.kind===`number`?0:void 0))),t.disabled=!r()}),gi(t,()=>G(s),e=>N(s,e)),Y(e,t)};Z(h,e=>{q(n()),K(()=>n().question.kind===`boolean`)?e(g):(q(n()),K(()=>n().question.kind===`choice`)?e(_,1):(q(n()),K(()=>n().question.kind===`multi-select`||n().question.kind===`scope-overrides`)?e(v,2):e(y,-1)))});var b=I(h,2),x=F(b),S=I(b,2);{let e=mt(()=>!r()||i());Ui(S,{label:`Continue`,arrow:!0,onClick:()=>a()(Yi(n(),G(s),G(c))),get disabled(){return G(e)}})}L((e,t)=>{X(f,e),X(x,`Suggested answer: ${t??``} · You can review choices again before creating your setup PAT.`)},[()=>(q(n()),K(()=>n().question.label.replace(` (Space toggles, Enter confirms)`,``))),()=>(q(n()),K(()=>String(n().question.defaultValue)||`none`))]),Y(e,l),k()}hr([`click`,`change`]);var aa=J(``),oa=J(`
                `);function sa(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`controller`,8),i=$(t,`busy`,8),a=$(t,`onSubmit`,8);yi();var o=oa();Hr(o,5,()=>(q(n()),K(()=>n().choices)),Rr,(e,t)=>{var n=aa(),o=F(P(n),!0);ke(),E(n),L(()=>{n.disabled=!r()||i(),X(o,G(t))}),mr(`click`,n,()=>a()(G(t))),Y(e,n)}),E(o),Y(e,o),k()}hr([`click`]);var ca=J(`Open the official GitHub PAT form

                Check the signed-in account, select Only select repositories, then select this repository. GitHub handles 2FA and creates the PAT.

                `,1),la=J(`

                Sent only to this local process. It will not be shown again or saved in browser storage.

                `),ua=J(` `,1);function da(e,t){O(t,!1);let n=M(),r=$(t,`prompt`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8),s=M(``);function c(){let e=G(s);r().kind===`secret`&&N(s,``),o()(e)}wn(()=>q(r()),()=>{N(n,Bi(r().link))}),Tn(),yi();var l=ua(),u=sn(l),d=e=>{var t=ca(),r=sn(t);ke(),L(()=>Q(r,`href`,G(n))),Y(e,t)};Z(u,e=>{G(n)&&e(d)});var f=I(u,2),p=F(f,!0),m=I(f,2);di(m);var h=I(m,2),g=e=>{Y(e,la())};Z(h,e=>{q(r()),K(()=>r().kind===`secret`)&&e(g)});var _=I(h,2);{let e=mt(()=>(q(i()),q(a()),q(r()),G(s),K(()=>!i()||a()||!r().optional&&!G(s).trim())));Ui(_,{label:`Continue`,arrow:!0,onClick:c,get disabled(){return G(e)}})}L(()=>{X(p,(q(r()),K(()=>r().kind===`secret`?`Paste the value here`:`Your answer`))),Q(m,`type`,(q(r()),K(()=>r().kind===`secret`?`password`:`text`))),m.disabled=!i(),Q(m,`placeholder`,(q(r()),K(()=>r().kind===`secret`?`Hidden after submission`:`Type your answer`)))}),gi(m,()=>G(s),e=>N(s,e)),Y(e,l),k()}var fa=J(`
              • `),pa=J(`

                  `),ma=J(`

                  Before you continue

                    `),ha=J(`

                    Review exactly what this run may change. The bot PAT and any additional credentials are collected next.

                    `,1);function ga(e,t){O(t,!1);let n=M(),r=$(t,`prompt`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8);wn(()=>q(r()),()=>{N(n,[{title:`Files`,items:r().plan.files},{title:`Workflows`,items:r().plan.workflows},{title:`Variables`,items:r().plan.variables},{title:`Secret names`,items:r().plan.secrets}])}),Tn(),yi();var s=ha(),c=I(sn(s),2);Hr(c,5,()=>G(n),Rr,(e,t)=>{var n=pa(),r=P(n),i=P(r),a=F(I(i),!0);E(r);var o=I(r);Hr(o,5,()=>(G(t),K(()=>G(t).items)),Rr,(e,t)=>{var n=fa(),r=F(n,!0);L(()=>X(r,G(t))),Y(e,n)}),E(o),E(n),L(()=>{X(i,`${G(t),K(()=>G(t).title)??``} `),X(a,(G(t),K(()=>G(t).items.length)))}),Y(e,n)}),E(c);var l=I(c,2),u=e=>{var t=ma(),n=I(P(t));Hr(n,5,()=>(q(r()),K(()=>r().plan.warnings)),Rr,(e,t)=>{var n=fa(),r=F(n,!0);L(()=>X(r,G(t))),Y(e,n)}),E(n),E(t),Y(e,t)};Z(l,e=>{q(r()),K(()=>r().plan.warnings.length)&&e(u)});var d=I(l,2),f=P(d);{let e=mt(()=>!i()||a());Ui(f,{label:`Stop here`,variant:`secondary`,onClick:()=>o()(`decline`),get disabled(){return G(e)}})}var p=I(f);{let e=mt(()=>!i()||a());Ui(p,{label:`Approve this plan`,arrow:!0,onClick:()=>o()(`approve`),get disabled(){return G(e)}})}E(d),Y(e,s),k()}var _a=J(`

                    `),va=J(`
                    CURRENT DECISION
                    `);function ya(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`revision`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8);yi();var s=va(),c=P(s),l=F(I(P(c)));E(c);var u=I(c,2),d=e=>{var t=_a(),r=F(t,!0);L(()=>X(r,(q(n()),K(()=>n().description)))),Y(e,t)};Z(u,e=>{q(n()),K(()=>`description`in n()&&n().description)&&e(d)});var f=I(u,2),p=e=>{ia(e,{get prompt(){return n()},get controller(){return i()},get busy(){return a()},get onSubmit(){return o()}})},m=e=>{sa(e,{get prompt(){return n()},get controller(){return i()},get busy(){return a()},get onSubmit(){return o()}})},h=e=>{da(e,{get prompt(){return n()},get controller(){return i()},get busy(){return a()},get onSubmit(){return o()}})},g=e=>{ga(e,{get prompt(){return n()},get controller(){return i()},get busy(){return a()},get onSubmit(){return o()}})};Z(f,e=>{q(n()),K(()=>n().kind===`question`)?e(p):(q(n()),K(()=>n().kind===`choice`||n().kind===`confirm`)?e(m,1):(q(n()),K(()=>n().kind===`text`||n().kind===`secret`)?e(h,2):(q(n()),K(()=>n().kind===`plan`)&&e(g,3))))}),E(s),L(()=>X(l,`SESSION ${r()??``}`)),Y(e,s),k()}var ba=J(` `),xa=J(`
                  • `),Sa=J(`

                      Conditional grants depend on the choices and GitHub facts. A final audit runs before setup changes.
                      `),Ca=J(`

                      Permissions follow your choices

                      We’ll show the exact grants before you create each PAT. Nothing is created just by opening this page.

                      `),wa=J(``);function Ta(e,t){O(t,!1);let n=$(t,`view`,8);yi();var r=wa(),i=P(r),a=F(I(P(i),3),!0);E(i);var o=I(i,2),s=e=>{var t=Sa(),r=I(P(t)),i=F(r),a=I(r),o=F(a,!0),s=I(a,2);Hr(s,5,()=>(q(n()),K(()=>n().permissions.report?.checks??n().permissions.requirements??[])),Rr,(e,t)=>{var n=xa(),r=P(n),i=P(r),a=I(i),o=F(a),s=F(I(a),!0);E(r);var c=I(r),l=P(c,!0),u=I(l),d=e=>{var n=ba(),r=F(n,!0);L(()=>X(r,(G(t),K(()=>G(t).status)))),Y(e,n)};Z(u,e=>{`status`in G(t)&&e(d)}),E(c),E(n),L(()=>{X(i,`${G(t),K(()=>G(t).permission)??``} `),X(o,`${G(t),K(()=>G(t).scope)??``} · ${G(t),K(()=>G(t).applicability)??``}`),X(s,(G(t),K(()=>G(t).reason))),X(l,(G(t),K(()=>G(t).level)))}),Y(e,n)}),E(s),ke(2),E(t),L(()=>{X(i,`${q(n()),K(()=>n().permissions.role===`setup`?`Setup PAT`:`Bot PAT`)??``} access`),X(o,(q(n()),K(()=>n().permissions.report?`Read-only access check`:`Provisional least-privilege grants`)))}),Y(e,t)},c=e=>{Y(e,Ca())};Z(o,e=>{q(n()),K(()=>n().permissions)?e(s):e(c,-1)}),E(r),L(()=>X(a,(q(n()),K(()=>n().repository)))),Y(e,r),k()}var Ea=J(`

                      `);function Da(e,t){O(t,!1);let n=M(),r=M(),i=$(t,`outcome`,8),a=$(t,`controller`,8),o=$(t,`onClose`,8);wn(()=>q(i()),()=>{N(n,i()===`complete`?`Your configuration was applied`:i()===`dry-run`?`No changes were made`:i()===`cancelled`||i()===`blocked`?`No setup changes started`:`Check partial changes before retrying`)}),wn(()=>q(i()),()=>{N(r,i()===`complete`?`The temporary setup PAT is not revoked automatically. Delete it in GitHub after confirming your setup. Keep the bot PAT until you rotate the installed Secret.`:i()===`partial`?`A bot Secret write or another change may have succeeded. Inspect GitHub and run copilot doctor before replacing or deleting its PAT.`:`Copilot did not begin applying setup changes. PATs you created in GitHub still exist until you delete them there.`)}),Tn();var s=Ea(),c=P(s),l=F(c,!0),u=I(c),d=F(u,!0),f=I(u),p=F(f,!0),m=I(f,2),h=e=>{Ui(e,{label:`Close local session`,get onClick(){return o()}})};Z(m,e=>{a()&&e(h)}),E(s),L(()=>{X(l,i()===`complete`?`✓`:`!`),X(d,G(n)),X(p,G(r))}),Y(e,s),k()}var Oa=J(`

                      Working on the next step

                      The local process is checking your answers and preparing the next decision. Keep this page open.

                      `);function ka(e){Y(e,Oa())}var Aa=J(``),ja=J(`
                      `),Ma=J(``),Na=J(`
                      `,1),Pa=J(`
                      LOCALHOST ONLY NO CLOUD SETUP ACCOUNT GITHUB OWNS PAT ISSUANCE
                      `);function Fa(e,t){O(t,!1);let n=()=>Di(a,`$session`,r),[r,i]=Oi(),a=Ai();Pr(()=>{a.connect();let e=window.setInterval(()=>{n().view?.outcome||a.refresh()},900);return()=>window.clearInterval(e)});async function o(){window.confirm(`Cancel this local setup session? PATs already created in GitHub will still exist.`)&&await a.cancel()}async function s(e){let t=n().view?.promptRevision;t!==void 0&&await a.submit(t,e)}yi();var c=Pa();Jr(`16t12jp`,e=>{Y(e,Aa())});var l=P(c);{let e=mt(()=>n().view?.journey);Ni(l,{get journey(){return G(e)}})}var u=I(l,2),d=P(u);{let e=mt(()=>n().view?.repository);Li(d,{get repository(){return G(e)}})}var f=I(d,2),p=P(f);zi(p,{get view(){return n().view}});var m=I(p,2),h=e=>{var t=ja(),r=I(P(t));E(t),L(()=>X(r,` Reviewing saved choices — pass ${n().view.journey.choiceReviewPass??``}. This is the same setup run, not a restart.`)),Y(e,t)};Z(m,e=>{n().view?.journey?.choiceReviewPass&&n().view.journey.choiceReviewPass>1&&!n().view.outcome&&e(h)});var g=I(m,2),_=e=>{Ki(e,{tone:`warning`,title:`Read-only tab`,message:`Another tab controls this session. You can review progress here or explicitly take over.`,actionLabel:`Take control in this tab`,get onAction(){return a.takeOver}})};Z(g,e=>{!n().controller&&n().view&&e(_)});var v=I(g,2),y=e=>{Ki(e,{tone:`error`,title:`Needs attention`,get message(){return n().error}})};Z(v,e=>{n().error&&e(y)});var b=I(v,2),x=e=>{{let t=mt(()=>n().view.message.tone===`success`?`Checked`:n().view.message.tone===`warning`?`Please note`:n().view.message.tone===`error`?`Needs attention`:`Progress update`);Ki(e,{get tone(){return n().view.message.tone},get title(){return G(t)},get message(){return n().view.message.text},get link(){return n().view.message.link}})}};Z(b,e=>{n().view?.message&&e(x)});var S=I(b,2),ee=e=>{Da(e,{get outcome(){return n().view.outcome},get controller(){return n().controller},get onClose(){return a.close}})},te=e=>{var t=Na(),r=sn(t),i=P(r);Lr(i,()=>n().view.promptRevision,e=>{ya(e,{get prompt(){return n().view.prompt},get revision(){return n().view.revision},get controller(){return n().controller},get busy(){return n().busy},onSubmit:s})}),Ta(I(i,2),{get view(){return n().view}}),E(r);var a=I(r,2),c=e=>{var t=Ma();L(()=>t.disabled=n().busy),mr(`click`,t,o),Y(e,t)};Z(a,e=>{n().controller&&n().view.journey?.current!==`Apply`&&e(c)}),Y(e,t)},ne=e=>{ka(e,{})};Z(S,e=>{n().view?.outcome?e(ee):n().view?.prompt?e(te,1):e(ne,-1)}),ke(2),E(f),E(u),E(c),Y(e,c),k(),i()}hr([`click`]),kr(Fa,{target:document.getElementById(`app`)}); \ No newline at end of file diff --git a/build/web/index.html b/build/web/index.html index 6159c500e..21b2410cc 100644 --- a/build/web/index.html +++ b/build/web/index.html @@ -5,7 +5,7 @@ Copilot · Setup studio - + diff --git a/jest.config.js b/jest.config.js index 5d58ecb88..893695b04 100644 --- a/jest.config.js +++ b/jest.config.js @@ -6,6 +6,8 @@ module.exports = { passWithNoTests: true, collectCoverageFrom: [ 'src/**/*.ts', + 'web/src/**/*.ts', + '!web/src/main.ts', '!src/**/*.d.ts', '!src/**/__tests__/**', '!src/**/*.test.ts' @@ -16,6 +18,12 @@ module.exports = { statements: 90, functions: 88, branches: 82 + }, + './web/src/': { + lines: 98, + statements: 95, + functions: 100, + branches: 85 } }, coverageDirectory: 'coverage', diff --git a/src/cli/__tests__/web_setup_browser_session.test.ts b/src/cli/__tests__/web_setup_browser_session.test.ts index 24785bec3..1c55eb02e 100644 --- a/src/cli/__tests__/web_setup_browser_session.test.ts +++ b/src/cli/__tests__/web_setup_browser_session.test.ts @@ -69,4 +69,152 @@ describe('browser session transport', () => { await session.submit(7, 'allowed'); expect(requests.find(request => request.path === '/api/answer')?.options?.headers).toEqual(expect.objectContaining({ 'X-Setup-Capability': 'new-capability' })); }); + + test('a rejected answer refreshes the question while keeping the error visible and the PAT private', async () => { + const requests: string[] = []; + globalThis.fetch = jest.fn(async (path: string) => { + requests.push(path); + if (path === '/api/bootstrap') return response({ controller: true, capability: 'controller', takeoverTicket: 'ticket' }); + if (path === '/api/state') return response(view); + if (path === '/api/answer') return response({ error: 'This question changed. Refresh the current state.' }, 409); + throw new Error('Unexpected route'); + }) as typeof fetch; + + const session = createSetupSession(); + let latest = ''; + session.subscribe(state => { latest = JSON.stringify(state); }); + await session.connect(); + await session.submit(7, 'ghp_fake_rejected'); + + expect(requests).toEqual(['/api/bootstrap', '/api/state', '/api/answer', '/api/state']); + expect(latest).toContain('This question changed'); + expect(latest).not.toContain('ghp_fake_rejected'); + expect(JSON.parse(latest).busy).toBe(false); + }); + + test('control transfer reconnects as read-only and does not replay an answer', async () => { + let bootstraps = 0; + const requests: string[] = []; + globalThis.fetch = jest.fn(async (path: string) => { + requests.push(path); + if (path === '/api/bootstrap') { + bootstraps += 1; + return response({ controller: bootstraps === 1, capability: bootstraps === 1 ? 'old' : undefined, takeoverTicket: 'new-ticket' }); + } + if (path === '/api/state') return response(view); + if (path === '/api/answer') return response({ error: 'Control moved to another tab.' }, 403); + throw new Error('Unexpected route'); + }) as typeof fetch; + + const session = createSetupSession(); + let latest: { controller: boolean; busy: boolean } | undefined; + session.subscribe(state => { latest = state; }); + await session.connect(); + await session.submit(7, 'fake-token'); + + expect(requests.filter(path => path === '/api/answer')).toHaveLength(1); + expect(bootstraps).toBe(2); + expect(latest).toMatchObject({ controller: false, busy: false }); + }); + + test('a refused cancellation leaves the server-owned journey intact', async () => { + globalThis.fetch = jest.fn(async (path: string) => { + if (path === '/api/bootstrap') return response({ controller: true, capability: 'controller', takeoverTicket: 'ticket' }); + if (path === '/api/state') return response(view); + if (path === '/api/cancel') return response({ error: 'This setup has already started applying or ended.' }, 409); + throw new Error('Unexpected route'); + }) as typeof fetch; + + const session = createSetupSession(); + let latest = ''; + session.subscribe(state => { latest = JSON.stringify(state); }); + await session.connect(); + await session.cancel(); + + expect(latest).toContain('already started applying'); + expect(JSON.parse(latest)).toMatchObject({ controller: true, busy: false, view }); + }); + + test('a failed takeover re-reads the current controller instead of retaining authority', async () => { + const requests: string[] = []; + globalThis.fetch = jest.fn(async (path: string) => { + requests.push(path); + if (path === '/api/bootstrap') return response({ controller: false, takeoverTicket: 'ticket' }); + if (path === '/api/state') return response(view); + if (path === '/api/takeover') return response({ error: 'Invalid takeover ticket.' }, 403); + throw new Error('Unexpected route'); + }) as typeof fetch; + + const session = createSetupSession(); + let latest: { controller: boolean; busy: boolean } | undefined; + session.subscribe(state => { latest = state; }); + await session.connect(); + await session.takeOver(); + + expect(requests.filter(path => path === '/api/bootstrap')).toHaveLength(2); + expect(latest).toMatchObject({ controller: false, busy: false }); + }); + + test('failed bootstrap and state requests are reported without claiming a live session', async () => { + globalThis.fetch = jest.fn(async (path: string) => path === '/api/bootstrap' + ? response({ error: 'Unavailable' }, 503) : response(view)) as typeof fetch; + const session = createSetupSession(); + let latest = ''; + session.subscribe(state => { latest = JSON.stringify(state); }); + await session.connect(); + expect(latest).toContain('Could not connect'); + expect(JSON.parse(latest).controller).toBe(false); + + globalThis.fetch = jest.fn(async () => response({ error: 'Unavailable' }, 503)) as typeof fetch; + await session.refresh(); + expect(latest).toContain('Connection lost'); + expect(JSON.parse(latest).view).toBeUndefined(); + }); + + test('a failed reconnect drops the old controller capability and cannot replay a PAT', async () => { + let available = true; + const requests: string[] = []; + globalThis.fetch = jest.fn(async (path: string) => { + requests.push(path); + if (path === '/api/bootstrap') return available + ? response({ controller: true, capability: 'old-controller', takeoverTicket: 'ticket' }) + : response({ error: 'Unavailable' }, 503); + if (path === '/api/state') return response(view); + throw new Error('Unexpected route'); + }) as typeof fetch; + + const session = createSetupSession(); + let latest: { controller: boolean; error: string; view?: WebSetupView } | undefined; + session.subscribe(state => { latest = state; }); + await session.connect(); + expect(latest?.controller).toBe(true); + available = false; + await session.connect(); + expect(latest).toMatchObject({ controller: false, error: expect.stringContaining('Could not connect') }); + expect(latest?.view).toBeUndefined(); + await session.submit(7, 'fake-sensitive-pat'); + expect(requests).not.toContain('/api/answer'); + }); + + test('control transfer during cancellation reconnects without reporting success', async () => { + let bootstraps = 0; + globalThis.fetch = jest.fn(async (path: string) => { + if (path === '/api/bootstrap') return response({ controller: ++bootstraps === 1, capability: 'old', takeoverTicket: 'ticket' }); + if (path === '/api/state') return response(view); + if (path === '/api/cancel') return response({ error: 'Control moved to another tab.' }, 403); + throw new Error('Unexpected route'); + }) as typeof fetch; + const session = createSetupSession(); + let latest: { controller: boolean; busy: boolean } | undefined; + session.subscribe(state => { latest = state; }); + await session.connect(); + await session.cancel(); + expect(bootstraps).toBe(2); + expect(latest).toMatchObject({ controller: false, busy: false }); + }); + + test('failure to close the browser session can be handled in the terminal', async () => { + globalThis.fetch = jest.fn(async () => { throw new Error('CLI stopped'); }) as typeof fetch; + await expect(createSetupSession().close()).resolves.toBeUndefined(); + }); }); diff --git a/src/cli/__tests__/web_setup_ui_helpers.test.ts b/src/cli/__tests__/web_setup_ui_helpers.test.ts index eba77c6ff..8b46febfc 100644 --- a/src/cli/__tests__/web_setup_ui_helpers.test.ts +++ b/src/cli/__tests__/web_setup_ui_helpers.test.ts @@ -43,6 +43,7 @@ describe('web setup presentation helpers', () => { ['http://github.com/settings/personal-access-tokens', false], ['https://github.com/settings/keys', false], ['javascript:alert(1)', false], + ['not-a-url', false], ])('allowlisted GitHub link %s: %s', (link, allowed) => { expect(Boolean(safeGithubLink(link))).toBe(allowed); }); diff --git a/web/src/session/setupSession.ts b/web/src/session/setupSession.ts index 9152dc9d0..33b4d0bb9 100644 --- a/web/src/session/setupSession.ts +++ b/web/src/session/setupSession.ts @@ -34,7 +34,7 @@ export function createSetupSession() { if (!response.ok) throw new Error('The local setup session is unavailable.'); set({ view: await response.json() as WebSetupView, ...(preserveError ? {} : { error: '' }) }); } catch { - set({ error: 'Connection lost. The CLI may have stopped. Check the terminal before trying again.' }); + set({ view: undefined, error: 'Connection lost. The CLI may have stopped. Check the terminal before trying again.' }); } finally { loading = false; } @@ -50,7 +50,9 @@ export function createSetupSession() { set({ controller: bootstrap.controller, error: '' }); await refresh(); } catch { - set({ error: 'Could not connect to the local setup session. Check the terminal.' }); + capability = undefined; + takeoverTicket = ''; + set({ view: undefined, controller: false, error: 'Could not connect to the local setup session. Check the terminal.' }); } } From 9f200eef03b8da43cbb6da8e4a089144974c9302 Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Mon, 28 Sep 2026 20:38:43 +0200 Subject: [PATCH 16/50] codex-setup-temporary-github-auth: fail closed on orphaned session locks --- build/cli/index.js | 75 ++++++------ .../{index-BS9cJzpj.js => index-BJEnmD61.js} | 2 +- build/web/index.html | 2 +- .../operations/troubleshooting.mdx | 7 ++ specs/local-web-setup-assistant.md | 10 +- src/cli/__tests__/setup_session_guard.test.ts | 27 ++++- .../web_setup_browser_session.test.ts | 113 ++++++++++++++++++ .../__tests__/web_setup_ui_helpers.test.ts | 10 ++ src/cli/setup_session_guard.ts | 52 ++++---- web/src/lib/questionAnswer.ts | 2 +- 10 files changed, 222 insertions(+), 78 deletions(-) rename build/web/assets/{index-BS9cJzpj.js => index-BJEnmD61.js} (77%) diff --git a/build/cli/index.js b/build/cli/index.js index ddbe2a627..0165533e7 100755 --- a/build/cli/index.js +++ b/build/cli/index.js @@ -67602,51 +67602,48 @@ function acquireSetupSessionGuard(cwd) { const hash = (0, node_crypto_1.createHash)('sha256').update(repository).digest('hex').slice(0, 32); const lockPath = (0, node_path_1.join)((0, node_os_1.tmpdir)(), `copilot-setup-${hash}.lock`); const record = { pid: process.pid, nonce: (0, node_crypto_1.randomBytes)(16).toString('hex'), repository }; - for (let attempt = 0; attempt < 2; attempt += 1) { + try { + const fd = (0, node_fs_1.openSync)(lockPath, 'wx', 0o600); try { - const fd = (0, node_fs_1.openSync)(lockPath, 'wx', 0o600); - try { - (0, node_fs_1.writeFileSync)(fd, JSON.stringify(record)); - } - finally { - (0, node_fs_1.closeSync)(fd); - } - return () => { - try { - const current = JSON.parse((0, node_fs_1.readFileSync)(lockPath, 'utf8')); - if (current.pid === record.pid && current.nonce === record.nonce && current.repository === record.repository) - (0, node_fs_1.unlinkSync)(lockPath); - } - catch { /* Missing or replaced lock is not ours to remove. */ } - }; + (0, node_fs_1.writeFileSync)(fd, JSON.stringify(record)); } - catch (cause) { - if (!cause || typeof cause !== 'object' || !('code' in cause) || cause.code !== 'EEXIST') - throw cause; - let existing; - try { - existing = JSON.parse((0, node_fs_1.readFileSync)(lockPath, 'utf8')); - } - catch { - throw new Error('A setup lock exists but cannot be verified. Inspect it before retrying.'); - } - if (!Number.isSafeInteger(existing.pid) || existing.pid <= 0 || existing.repository !== repository || !existing.nonce) { - throw setupLockError('A setup lock has unexpected contents. Inspect it before retrying.', cause); - } + finally { + (0, node_fs_1.closeSync)(fd); + } + return () => { try { - process.kill(existing.pid, 0); - throw setupLockError(`Another setup process (${existing.pid}) is active for this checkout. Finish or stop it before starting a second setup.`, cause); - } - catch (checkError) { - if (!checkError || typeof checkError !== 'object' || !('code' in checkError) || checkError.code !== 'ESRCH') - throw checkError; + const current = JSON.parse((0, node_fs_1.readFileSync)(lockPath, 'utf8')); + if (current.pid === record.pid && current.nonce === record.nonce && current.repository === record.repository) + (0, node_fs_1.unlinkSync)(lockPath); } - // Recover only a verified dead owner and only if the lock has not changed meanwhile. - if ((0, node_fs_1.existsSync)(lockPath) && (0, node_fs_1.readFileSync)(lockPath, 'utf8') === JSON.stringify(existing)) - (0, node_fs_1.unlinkSync)(lockPath); + catch { /* Missing or replaced lock is not ours to remove. */ } + }; + } + catch (cause) { + if (!cause || typeof cause !== 'object' || !('code' in cause) || cause.code !== 'EEXIST') + throw cause; + let existing; + try { + existing = JSON.parse((0, node_fs_1.readFileSync)(lockPath, 'utf8')); + } + catch { + throw new Error('A setup lock exists but cannot be verified. Inspect it before retrying.'); + } + if (!Number.isSafeInteger(existing.pid) || existing.pid <= 0 || existing.repository !== repository || !existing.nonce) { + throw setupLockError('A setup lock has unexpected contents. Inspect it before retrying.', cause); + } + try { + process.kill(existing.pid, 0); + throw setupLockError(`Another setup process (${existing.pid}) is active for this checkout. Finish or stop it before starting a second setup.`, cause); + } + catch (checkError) { + if (!checkError || typeof checkError !== 'object' || !('code' in checkError) || checkError.code !== 'ESRCH') + throw checkError; } + // Filesystem reads and unlink are not atomic. Never remove a dead owner's lock here: + // another setup process may already have replaced it after our read. + throw setupLockError(`A setup lock for a stopped process (${existing.pid}) remains at ${lockPath}. Verify no setup is running, remove only that file manually, then retry.`, cause); } - throw new Error('Could not acquire the local setup lock.'); } function setupLockError(message, cause) { return Object.assign(new Error(message), { cause }); diff --git a/build/web/assets/index-BS9cJzpj.js b/build/web/assets/index-BJEnmD61.js similarity index 77% rename from build/web/assets/index-BS9cJzpj.js rename to build/web/assets/index-BJEnmD61.js index dd55c191d..7d6d73071 100644 --- a/build/web/assets/index-BS9cJzpj.js +++ b/build/web/assets/index-BJEnmD61.js @@ -1,2 +1,2 @@ (function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})(),typeof window<`u`&&((window.__svelte??={}).v??=new Set).add(`5`);var e=!1;function t(){e=!0}t();var n={},r=Symbol(`uninitialized`),i=`http://www.w3.org/1999/xhtml`,a=Array.isArray,o=Array.prototype.indexOf,s=Array.prototype.includes,c=Array.from,l=Object.defineProperty,u=Object.getOwnPropertyDescriptor,d=Object.getOwnPropertyDescriptors,f=Object.prototype,p=Array.prototype,m=Object.getPrototypeOf,h=Object.isExtensible,g=()=>{};function _(e){return e()}function v(e){for(var t=0;t{e=n,t=r}),resolve:e,reject:t}}var b=1024,x=2048,S=4096,ee=8192,te=16384,ne=32768,re=1<<25,ie=65536,ae=1<<18,oe=1<<19,se=1<<20,ce=1<<25,le=1<<21,ue=1<<22,de=1<<23,fe=Symbol(`$state`),pe=Symbol(`component`),me=Symbol(`legacy props`),he=Symbol(``),ge=Symbol(`attributes`),_e=Symbol(`class`),ve=Symbol(`style`),ye=Symbol(`text`),be=Symbol(`form reset`),xe=new class extends Error{name=`StaleReactionError`;message="The reaction that called `getAbortSignal()` was re-run or destroyed"},Se=!!globalThis.document?.contentType&&globalThis.document.contentType.includes(`xml`);function Ce(){console.warn(`https://svelte.dev/e/derived_inert`)}function we(e){console.warn(`https://svelte.dev/e/hydration_mismatch`)}function Te(){console.warn(`https://svelte.dev/e/select_multiple_invalid_value`)}function Ee(){console.warn(`https://svelte.dev/e/svelte_boundary_reset_noop`)}var C=!1;function De(e){C=e}var w;function T(e){if(e===null)throw we(),n;return w=e}function Oe(){return T(on(w))}function E(e){if(C){if(on(w)!==null)throw we(),n;w=e}}function ke(e=1){if(C){for(var t=e,n=w;t--;)n=on(n);w=n}}function Ae(e=!0){for(var t=0,n=w;;){if(n.nodeType===8){var r=n.data;if(r===`]`){if(t===0)return n;--t}else(r===`[`||r===`[!`||r[0]===`[`&&!isNaN(Number(r.slice(1))))&&(t+=1)}var i=on(n);e&&n.remove(),n=i}}function je(e){if(!e||e.nodeType!==8)throw we(),n;return e.data}function Me(e){return e===this.v}function Ne(e,t){return e==e?e!==t||typeof e==`object`&&!!e||typeof e==`function`:t==t}function Pe(e){return!Ne(e,this.v)}function Fe(e){throw Error(`https://svelte.dev/e/lifecycle_outside_component`)}function Ie(){throw Error(`https://svelte.dev/e/async_derived_orphan`)}function Le(e,t,n){throw Error(`https://svelte.dev/e/each_key_duplicate`)}function Re(e){throw Error(`https://svelte.dev/e/effect_in_teardown`)}function ze(){throw Error(`https://svelte.dev/e/effect_in_unowned_derived`)}function Be(e){throw Error(`https://svelte.dev/e/effect_orphan`)}function Ve(){throw Error(`https://svelte.dev/e/effect_update_depth_exceeded`)}function He(e){throw Error(`https://svelte.dev/e/props_invalid_value`)}function Ue(){throw Error(`https://svelte.dev/e/state_descriptors_fixed`)}function We(){throw Error(`https://svelte.dev/e/state_prototype_fixed`)}function Ge(){throw Error(`https://svelte.dev/e/state_unsafe_mutation`)}function Ke(){throw Error(`https://svelte.dev/e/svelte_boundary_reset_onerror`)}var D=null;function qe(e){D=e}function O(t,n=!1,r){D={p:D,i:!1,c:null,e:null,s:t,x:null,r:H,l:e&&!n?{s:null,u:null,$:[]}:null}}function k(e){var t=D,n=t.e;if(n!==null){t.e=null;for(var r of n)bn(r)}return e!==void 0&&(t.x=e),t.i=!0,D=t.p,Je(e)}function Je(e={}){return l(e,pe,{value:!0}),e}function Ye(){return!e||D!==null&&D.l===null}var Xe=[];function Ze(){var e=Xe;Xe=[],v(e)}function Qe(e){if(Xe.length===0&&!wt){var t=Xe;queueMicrotask(()=>{t===Xe&&Ze()})}Xe.push(e)}function $e(){for(;Xe.length>0;)Ze()}var et=~(x|S|b);function A(e,t){e.f=e.f&et|t}function tt(e){e.f&512||e.deps===null?A(e,b):A(e,S)}function nt(e,t,n){e.f&2048?t.add(e):e.f&4096&&n.add(e),A(e,b)}var rt=!1;function it(){rt||(rt=!0,document.addEventListener(`reset`,e=>{Promise.resolve().then(()=>{if(!e.defaultPrevented)for(let t of e.target.elements)t[be]?.()})},{capture:!0}))}function at(e){var t=B,n=H;V(null),Wn(null);try{return e()}finally{V(t),Wn(n)}}function ot(e,t,n,r=n){e.addEventListener(t,()=>at(n));let i=e[be];e[be]=i?()=>{i(),r(!0)}:()=>r(!0),it()}function st(e,t,n,r){let i=Ye()?dt:mt;var a=e.filter(e=>!e.settled),o=t.map(i);if(n.length===0&&a.length===0){r(o);return}var s=H,c=ct(),l=a.length===1?a[0].promise:a.length>1?Promise.all(a.map(e=>e.promise)):null;function u(e){if(!(s.f&16384)){c();try{r([...o,...e])}catch(e){pn(e,s)}lt()}}var d=ut();if(n.length===0){l.then(()=>u([])).finally(d);return}function f(){Promise.all(n.map(e=>pt(e))).then(u).catch(e=>pn(e,s)).finally(d)}l?l.then(()=>{c(),f(),lt()}):f()}function ct(){var e=H,t=B,n=D,r=j;return function(i=!0){Wn(e),V(t),qe(n),i&&!(e.f&16384)&&(r?.activate(),r?.apply())}}function lt(e=!0){Wn(null),V(null),qe(null),e&&j?.deactivate()}function ut(){var e=H,t=e.b,n=j,r=!!t?.is_rendered();return t?.update_pending_count(1,n),n.increment(r,e),()=>{t?.update_pending_count(-1,n),n.decrement(r,e)}}function dt(e){var t=2|x;return H!==null&&(H.f|=oe),{ctx:D,deps:null,effects:null,equals:Me,f:t,fn:e,reactions:null,rv:0,v:r,wv:0,parent:H,ac:null}}var ft=Symbol(`obsolete`);function pt(e,t,n){let i=H;i===null&&Ie();var a=void 0,o=Vt(r),s=!B,c=new Set;return En(()=>{var t=H,n=y();a=n.promise;try{Promise.resolve(e()).then(n.resolve,e=>{e!==xe&&n.reject(e)}).finally(lt)}catch(e){n.reject(e),lt()}var r=j;if(s){if(t.f&32768)var l=ut();if(i.b?.is_rendered())r.async_deriveds.get(t)?.reject(ft);else for(let e of c.values())e.reject(ft);c.add(n),r.async_deriveds.set(t,n)}let u=(e,t=void 0)=>{l?.(),c.delete(n),t!==ft&&(r.activate(),t?(o.f|=de,Gt(o,t)):(o.f&8388608&&(o.f^=de),Gt(o,e)),r.deactivate())};n.promise.then(u,e=>u(null,e||`unknown`))}),vn(()=>{for(let e of c)e.reject(ft)}),new Promise(e=>{function t(n){function r(){n===a?e(o):t(a)}n.then(r,r)}t(a)})}function mt(e){let t=dt(e);return t.equals=Pe,t}function ht(e){var t=e.effects;if(t!==null){e.effects=null;for(var n=0;n{t.ac.abort(xe),t.ac=null}),t.fn!==null&&(t.teardown=g),ar(t,0),An(t))}function yt(e){if(e.effects!==null)for(let t of e.effects)t.teardown&&t.fn!==null&&or(t)}var bt=null,j=null,xt=null,St=null,Ct=null,wt=!1,Tt=!1,Et=null,Dt=null,Ot=0,kt=1,At=class e{id=kt++;#e=!1;linked=!0;#t=null;#n=null;async_deriveds=new Map;current=new Map;previous=new Map;#r=new Set;#i=new Set;#a=0;#o=new Map;#s=null;#c=[];#l=[];#u=new Set;#d=new Set;#f=new Map;#p=new Set;is_fork=!1;#m=!1;constructor(){bt===null?bt=this:(bt.#n=this,this.#t=bt),bt=this}#h(){if(this.is_fork)return!0;for(let n of this.#o.keys()){for(var e=n,t=!1;e.parent!==null;){if(this.#f.has(e)){t=!0;break}e=e.parent}if(!t)return!0}return!1}skip_effect(e){this.#f.has(e)||this.#f.set(e,{d:[],m:[]}),this.#p.delete(e)}unskip_effect(e,t=e=>this.schedule(e)){var n=this.#f.get(e);if(n){this.#f.delete(e);for(var r of n.d)A(r,x),t(r);for(r of n.m)A(r,S),t(r)}this.#p.add(e)}#g(){var e=[];for(let i of this.#c)if(!(i.f&16384||!(i.f&6144))){for(var t=i,n=!1;t.parent!==null;){t=t.parent;var r=t.f;if(r&96){if(!(r&1024)){n=!0;break}t.f^=b}}n||e.push(t)}return this.#c=[],e}#_(){this.#e=!0;for(let e of this.#u)this.#d.delete(e),A(e,x),this.schedule(e);for(let e of this.#d)A(e,S),this.schedule(e);this.apply();for(var t=Et=[],n=[],r=Dt=[];this.#c.length>0;){Ot++>1e3&&(this.#S(),Mt());for(let e of this.#g())try{this.#v(e,t,n)}catch(t){throw Lt(e),this.#h()||this.discard(),t}}if(j=null,r.length>0){var i=e.ensure();for(let e of r)i.schedule(e)}if(Et=null,Dt=null,this.#h()){this.#x(n),this.#x(t);for(let[e,t]of this.#f)It(e,t);r.length>0&&j.#_();return}let a=this.#y();if(a){this.#x(n),this.#x(t),a.#b(this);return}this.#u.clear(),this.#d.clear();for(let e of this.#r)e(this);this.#r.clear(),xt=this,Pt(n),Pt(t),xt=null,this.#s?.resolve();var o=j;if(this.#a===0&&(this.#c.length===0||o!==null)&&this.#S(),this.#c.length>0){if(o!==null){for(let e of this.#c)o.#c.push(e);this.#c=[]}else o=this}o!==null&&(zt.clear(),o.#_())}#v(e,t,n){e.f^=b;for(var r=e.first;r!==null;){var i=r.f,a=!!(i&96);if(!(a&&i&1024||i&8192||this.#f.has(r))&&r.fn!==null){a?r.f^=b:i&4?t.push(r):er(r)&&(i&16&&this.#d.add(r),or(r));var o=r.first;if(o!==null){r=o;continue}}for(;r!==null;){var s=r.next;if(s!==null){r=s;break}r=r.parent}}}#y(){for(var e=this.#t;e!==null;){if(!e.is_fork){for(let[t,[,n]]of this.current)if(e.current.has(t)&&!n)return e}e=e.#t}return null}#b(e){for(let[t,n]of e.current)!this.previous.has(t)&&e.previous.has(t)&&this.previous.set(t,e.previous.get(t)),this.current.set(t,n);for(let[t,n]of e.async_deriveds){let e=this.async_deriveds.get(t);e&&n.promise.then(e.resolve).catch(e.reject)}e.async_deriveds.clear(),this.transfer_effects(e.#u,e.#d);let t=e=>{var n=e.reactions;if(n!==null&&!(e.f&2&&!(e.f&6144)))for(let e of n){var r=e.f;if(r&2)t(e);else{var i=e;r&4194320&&!this.async_deriveds.has(i)&&(this.#d.delete(i),A(i,x),this.schedule(i))}}};for(let e of this.current.keys())t(e);this.oncommit(()=>e.discard()),e.#S(),j=this,this.#_()}#x(e){for(var t=0;t{this.#m=!1,this.linked&&this.flush()}))}transfer_effects(e,t){for(let t of e)this.#u.add(t);for(let e of t)this.#d.add(e);e.clear(),t.clear()}oncommit(e){this.#r.add(e)}ondiscard(e){this.#i.add(e)}settled(){return(this.#s??=y()).promise}static ensure(){if(j===null){let t=j=new e;!Tt&&!wt&&Qe(()=>{t.#e||t.flush()})}return j}apply(){St=null}schedule(e){if(Ct=e,e.b?.is_pending&&e.f&16777228&&!(e.f&32768)){e.b.defer_effect(e);return}this.#c.push(e)}#S(){if(this.linked){var e=this.#t,t=this.#n;e===null||(e.#n=t),t===null?bt=e:t.#t=e,this.linked=!1}}};function jt(e){var t=wt;wt=!0;try{var n;for(e&&(j!==null&&!j.is_fork&&j.flush(),n=e());;){if($e(),j===null)return n;j.flush()}}finally{wt=t}}function Mt(){try{Ve()}catch(e){pn(e,Ct)}}var Nt=null;function Pt(e){var t=e.length;if(t!==0){for(var n=0;n0)){zt.clear();for(let e of Nt){if(e.f&24576)continue;let t=[e],n=e.parent;for(;n!==null;)Nt.has(n)&&(Nt.delete(n),t.push(n)),n=n.parent;for(let e=t.length-1;e>=0;e--){let n=t[e];n.f&24576||or(n)}}Nt.clear()}}Nt=null}}function Ft(e){j.schedule(e)}function It(e,t){if(!(e.f&32&&e.f&1024)){e.f&2048?t.d.push(e):e.f&4096&&t.m.push(e),A(e,b);for(var n=e.first;n!==null;)It(n,t),n=n.next}}function Lt(e){A(e,b);for(var t=e.first;t!==null;)Lt(t),t=t.next}var Rt=new Set,zt=new Map,Bt=!1;function Vt(e,t){return{f:0,v:e,reactions:null,equals:Me,rv:0,wv:0}}function Ht(e,t){let n=Vt(e,t);return Kn(n),n}function M(t,n=!1,r=!0){let i=Vt(t);return n||(i.equals=Pe),e&&r&&D!==null&&D.l!==null&&(D.l.s??=[]).push(i),i}function N(e,t,n=!1){return B!==null&&(!Un||B.f&131072)&&Ye()&&B.f&4325394&&(Gn===null||!Gn.has(e))&&Ge(),Gt(e,n?Yt(t):t,Dt)}var Ut=null,Wt=0;function Gt(e,t,n=null){if(!e.equals(t)){Vn?zt.set(e,t):zt.has(e)||zt.set(e,e.v);var r=At.ensure();if(r.capture(e,t),e.f&2){let t=e;e.f&2048&>(t),St===null&&tt(t)}e.wv=$n(),Ut=null,Wt=0,Jt(e,x,n),Ut=null,Ye()&&H!==null&&H.f&1024&&!(H.f&96)&&(qn===null?Jn([e]):qn.push(e)),!r.is_fork&&Rt.size>0&&!Bt&&Kt()}return t}function Kt(){Bt=!1;for(let e of Rt){e.f&1024&&A(e,S);let t;try{t=er(e)}catch{t=!0}t&&or(e)}Rt.clear()}function qt(e){N(e,e.v+1)}function Jt(e,t,n){var r=e.reactions;if(r!==null){var i=Ye(),a=r.length;if(Wt+=a,Wt>1e5&&Ut===null&&(Ut=new Set),Ut!==null){if(Ut.has(e))return;Ut.add(e)}for(var o=0;o{if(Zn===c)return e();var t=B,n=Zn;V(null),Qn(c);var r=e();return V(t),Qn(n),r};return i&&n.set(`length`,Ht(e.length,s)),new Proxy(e,{defineProperty(e,t,r){(!(`value`in r)||r.configurable===!1||r.enumerable===!1||r.writable===!1)&&Ue();var i=n.get(t);return i===void 0?l(()=>{var e=Ht(r.value,s);return n.set(t,e),e}):N(i,r.value,!0),!0},deleteProperty(e,t){var i=n.get(t);if(i===void 0){if(t in e){let e=l(()=>Ht(r,s));n.set(t,e),qt(o)}}else N(i,r),qt(o);return!0},get(t,i,a){if(i===fe)return e;var o=n.get(i),c=i in t;if(o===void 0&&(!c||u(t,i)?.writable)&&(o=l(()=>Ht(Yt(c?t[i]:r),s)),n.set(i,o)),o!==void 0){var d=G(o);return d===r?void 0:d}return Reflect.get(t,i,a)},getOwnPropertyDescriptor(e,t){this.has?.(e,t);var i=Reflect.getOwnPropertyDescriptor(e,t),a=n.get(t);if(a!==void 0){var o=G(a);if(o===r)return;if(i&&`value`in i)i.value=o;else return{enumerable:!0,configurable:!0,value:o,writable:!0}}return i},has(e,t){if(t===fe)return!0;var i=n.get(t),a=i!==void 0&&i.v!==r||Reflect.has(e,t);return(i!==void 0||H!==null&&(!a||u(e,t)?.writable))&&(i===void 0&&(i=l(()=>Ht(a?Yt(e[t]):r,s)),n.set(t,i)),G(i)===r)?!1:a},set(e,t,a,c){var d=n.get(t),f=t in e;if(i&&t===`length`)for(var p=a;pHt(r,s)),n.set(p+``,m)):N(m,r)}if(d===void 0)(!f||u(e,t)?.writable)&&(d=l(()=>Ht(void 0,s)),N(d,Yt(a)),n.set(t,d));else{f=d.v!==r;var h=l(()=>Yt(a));N(d,h)}var g=Reflect.getOwnPropertyDescriptor(e,t);if(g?.set&&g.set.call(c,a),!f){if(i&&typeof t==`string`){var _=n.get(`length`),v=Number(t);Number.isInteger(v)&&v>=_.v&&N(_,v+1)}qt(o)}return!0},ownKeys(e){G(o);var t=Reflect.ownKeys(e).filter(e=>{var t=n.get(e);return t===void 0||t.v!==r});for(var[i,a]of n)a.v!==r&&!(i in e)&&t.push(i);return t},setPrototypeOf(){We()}})}function Xt(e){try{if(typeof e==`object`&&e&&fe in e)return e[fe]}catch{}return e}function Zt(e,t){return Object.is(Xt(e),Xt(t))}var Qt,$t,en,tn;function nn(){if(Qt===void 0){Qt=window,$t=/Firefox/.test(navigator.userAgent);var e=Element.prototype,t=Node.prototype,n=Text.prototype;en=u(t,`firstChild`).get,tn=u(t,`nextSibling`).get,h(e)&&(e[_e]=void 0,e[ge]=null,e[ve]=void 0,e.__e=void 0),h(n)&&(n[ye]=void 0)}}function rn(e=``){return document.createTextNode(e)}function an(e){return en.call(e)}function on(e){return tn.call(e)}function P(e,t){if(!C)return an(e);var n=an(w);if(n===null)n=w.appendChild(rn());else if(t&&n.nodeType!==3){var r=rn();return n?.before(r),T(r),r}return t&&dn(n),T(n),n}function sn(e,t=!1){if(!C){var n=an(e);return n instanceof Comment&&n.data===``?on(n):n}if(t){if(w?.nodeType!==3){var r=rn();return w?.before(r),T(r),r}dn(w)}return w}function F(e,t=!1){if(!C)return an(e);var n=P(e,t);return E(e),n}function I(e,t=1,n=!1){let r=C?w:e;for(var i;t--;)i=r,r=on(r);if(!C)return r;if(n){if(r?.nodeType!==3){var a=rn();return r===null?i?.after(a):r.before(a),T(a),a}dn(r)}return T(r),r}function cn(e){e.textContent=``}function ln(){return!1}function un(e,t,n){return t==null||t===`http://www.w3.org/1999/xhtml`?n?document.createElement(e,{is:n}):document.createElement(e):n?document.createElementNS(t,e,{is:n}):document.createElementNS(t,e)}function dn(e){if(e.nodeValue.length<65536)return;let t=e.nextSibling;for(;t!==null&&t.nodeType===3;)t.remove(),e.nodeValue+=t.nodeValue,t=e.nextSibling}function fn(e){var t=H;if(t===null)return B.f|=de,e;if(!(t.f&32768)&&!(t.f&4))throw e;pn(e,t)}function pn(e,t){if(!(t!==null&&t.f&16384)){for(;t!==null;){if(t.f&128&&!(t.f&33570816)){if(!(t.f&32768))throw e;try{t.b.error(e);return}catch(t){e=t}}t=t.parent}throw e}}function mn(e){H===null&&(B===null&&Be(e),ze()),Vn&&Re(e)}function hn(e,t){var n=t.last;n===null?t.last=t.first=e:(n.next=e,e.prev=n,t.last=e)}function gn(e,t){var n=H;n!==null&&n.f&8192&&(e|=ee);var r={ctx:D,deps:null,nodes:null,f:e|x|512,first:null,fn:t,last:null,next:null,parent:n,b:n&&n.b,prev:null,teardown:null,wv:0,ac:null};j?.register_created_effect(r);var i=r;if(e&4)Et===null?At.ensure().schedule(r):Et.push(r);else if(t!==null){try{or(r)}catch(e){throw z(r),e}i.deps===null&&i.teardown===null&&i.nodes===null&&i.first===i.last&&!(i.f&524288)&&(i=i.first,e&16&&e&65536&&i!==null&&(i.f|=ie))}if(i!==null&&(i.parent=n,n!==null&&hn(i,n),B!==null&&B.f&2&&!(e&64))){var a=B;(a.effects??=[]).push(i)}return r}function _n(){return B!==null&&!Un}function vn(e){let t=gn(8,null);return A(t,b),t.teardown=e,t}function yn(e){mn(`$effect`);var t=H.f;if(!B&&t&32&&D!==null&&!D.i){var n=D;(n.e??=[]).push(e)}else return bn(e)}function bn(e){return gn(4|se,e)}function xn(e){return mn(`$effect.pre`),gn(8|se,e)}function Sn(e){At.ensure();let t=gn(64|oe,e);return(e={})=>new Promise(n=>{e.outro?Pn(t,()=>{z(t),n(void 0)}):(z(t),n(void 0))})}function Cn(e){return gn(4,e)}function wn(e,t){var n=D,r={effect:null,ran:!1,deps:e};n.l.$.push(r),r.effect=Dn(()=>{if(e(),!r.ran){r.ran=!0;var n=H;try{Wn(n.parent),K(t)}finally{Wn(n)}}})}function Tn(){var e=D;Dn(()=>{for(var t of e.l.$){t.deps();var n=t.effect;n.f&1024&&n.deps!==null&&A(n,S),er(n)&&or(n),t.ran=!1}})}function En(e){return gn(ue|oe,e)}function Dn(e,t=0){return gn(8|t,e)}function L(e,t=[],n=[],r=[]){st(r,t,n,t=>{gn(8,()=>{e(...t.map(G))})})}function On(e,t=0){return gn(16|t,e)}function R(e){return gn(32|oe,e)}function kn(e){var t=e.teardown;if(t!==null){let n=Vn,r=B;Hn(!0),V(null);try{t.call(null)}catch(t){pn(t,e.parent)}finally{Hn(n),V(r)}}}function An(e,t=!1){var n=e.first;for(e.first=e.last=null;n!==null;){let e=n.ac;e!==null&&at(()=>{e.abort(xe)});var r=n.next;n.f&64?n.parent=null:z(n,t),n=r}}function jn(e){for(var t=e.first;t!==null;){var n=t.next;t.f&32||z(t),t=n}}function z(e,t=!0){var n=!1;(t||e.f&262144)&&e.nodes!==null&&e.nodes.end!==null&&(Mn(e.nodes.start,e.nodes.end),n=!0),e.f|=re,An(e,t&&!n),ar(e,0);var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)e.stop();kn(e),e.f^=re,e.f|=te;var i=e.parent;i!==null&&i.first!==null&&Nn(e),e.next=e.prev=e.teardown=e.ctx=e.deps=e.fn=e.nodes=e.ac=e.b=null}function Mn(e,t){for(;e!==null;){var n=e===t?null:on(e);e.remove(),e=n}}function Nn(e){var t=e.parent,n=e.prev,r=e.next;n!==null&&(n.next=r),r!==null&&(r.prev=n),t!==null&&(t.first===e&&(t.first=r),t.last===e&&(t.last=n))}function Pn(e,t,n=!0){var r=[];e.f|=256,Fn(e,r,!0);var i=()=>{n&&z(e),t&&t()},a=r.length;if(a>0){var o=()=>--a||i();for(var s of r)s.out(o)}else i()}function Fn(e,t,n){if(!(e.f&8192)){e.f^=ee;var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)(e.is_global||n)&&t.push(e);for(var i=e.first;i!==null;){var a=i.next;if(!(i.f&64)){var o=!!(i.f&65536)||!!(i.f&32)&&!!(e.f&16);Fn(i,t,o?n:!1)}i=a}}}function In(e){e.f&=-257,Ln(e,!0)}function Ln(e,t){if(!(e.f&256)&&e.f&8192){e.f^=ee,e.f&1024||(A(e,x),At.ensure().schedule(e));for(var n=e.first;n!==null;){var r=n.next,i=!!(n.f&65536)||!!(n.f&32);Ln(n,i?t:!1),n=r}var a=e.nodes&&e.nodes.t;if(a!==null)for(let e of a)(e.is_global||t)&&e.in()}}function Rn(e,t){if(e.nodes)for(var n=e.nodes.start,r=e.nodes.end;n!==null;){var i=n===r?null:on(n);t.append(n),n=i}}var zn=null,Bn=!1,Vn=!1;function Hn(e){Vn=e}var B=null,Un=!1;function V(e){B=e}var H=null;function Wn(e){H=e}var Gn=null;function Kn(e){B!==null&&(B.f&2097152||B.f&2)&&(Gn??=new Set).add(e)}var U=null,W=0,qn=null;function Jn(e){qn=e}var Yn=1,Xn=0,Zn=Xn;function Qn(e){Zn=e}function $n(){return++Yn}function er(e){var t=e.f;if(t&2048)return!0;if(t&4096){for(var n=e.deps,r=n.length,i=0;ie.wv)return!0}t&512&&St===null&&A(e,b)}return!1}function tr(e,t,n=!0){var r=e.reactions;if(r!==null&&!(Gn!==null&&Gn.has(e)))for(var i=0;i{e.ac.abort(xe)}),e.ac=null);try{e.f|=le;var u=e.fn,d=u();e.f|=ne;var f=rr(e);if(Ye()&&qn!==null&&!Un&&f!==null&&!(e.f&6146))for(var p=0;p0)for(t.length=W+U.length,r=0;r{c.ac.abort(xe),c.ac=null,A(c,x)}),vt(c),ar(c,0)}}function ar(e,t){var n=e.deps;if(n!==null)for(var r=t;r{_r=!1,gr=null}));var o=0,s=gr===e&&e[dr];if(s){var c=i.indexOf(s);if(c!==-1&&(t===document||t===window)){e[dr]=t;return}var u=i.indexOf(t);if(u===-1)return;c<=u&&(o=c)}if(a=i[o]||e.target,a!==t){l(e,`currentTarget`,{configurable:!0,get(){return a||n}});var d=B,f=H;V(null),Wn(null);try{for(var p,m=[];a!==null&&a!==t;){try{var h=a[dr]?.[r];h!=null&&(!a.disabled||e.target===a)&&h.call(a,e)}catch(e){p?m.push(e):p=e}if(e.cancelBubble)break;o++,a=o{throw e});throw p}}finally{e[dr]=t,delete e.currentTarget,V(d),Wn(f)}}}var yr=globalThis?.window?.trustedTypes&&globalThis.window.trustedTypes.createPolicy(`svelte-trusted-html`,{createHTML:e=>e});function br(e){return yr?.createHTML(e)??e}function xr(e){var t=un(`template`);return t.innerHTML=br(e.replaceAll(``,``)),t.content}function Sr(e,t){var n=H;n.nodes===null&&(n.nodes={start:e,end:t,a:null,t:null})}function J(e,t){var n=!!(t&1),r=!!(t&2),i,a=!e.startsWith(``);return()=>{if(C)return Sr(w,null),w;i===void 0&&(i=xr(a?e:``+e),n||(i=an(i)));var t=r||$t?document.importNode(i,!0):i.cloneNode(!0);if(n){var o=an(t),s=t.lastChild;Sr(o,s)}else Sr(t,t);return t}}function Y(e,t){if(C){var n=H;(!(n.f&32768)||n.nodes.end===null)&&(n.nodes.end=w),Oe();return}e!==null&&e.before(t)}[...`allowfullscreen.async.autofocus.autoplay.checked.controls.default.disabled.formnovalidate.indeterminate.inert.ismap.loop.multiple.muted.nomodule.novalidate.open.playsinline.readonly.required.reversed.seamless.selected.webkitdirectory.defer.disablepictureinpicture.disableremoteplayback`.split(`.`)];var Cr=[`touchstart`,`touchmove`];function wr(e){return Cr.includes(e)}function Tr(e){let t=0,n=Vt(0),r;return()=>{_n()&&(G(n),Dn(()=>(t===0&&(r=K(()=>e(()=>qt(n)))),t+=1,()=>{Qe(()=>{--t,t===0&&(r?.(),r=void 0,qt(n))})})))}}var Er=ie|oe;function Dr(e,t,n,r){new Or(e,t,n,r)}var Or=class{parent;is_pending=!1;transform_error;#e;#t=C?w:null;#n;#r;#i;#a=null;#o=null;#s=null;#c=null;#l=0;#u=0;#d=!1;#f=new Set;#p=new Set;#m=null;#h=Tr(()=>(this.#m=Vt(this.#l),()=>{this.#m=null}));constructor(e,t,n,r){this.#e=e,this.#n=t,this.#r=e=>{var t=H;t.b=this,t.f|=128,n(e)},this.parent=H.b,this.transform_error=r??this.parent?.transform_error??(e=>e),this.#i=On(()=>{if(C){let e=this.#t;Oe();let t=e.data===`[!`;if(e.data.startsWith(`[?`)){let t=JSON.parse(e.data.slice(2));this.#_(t)}else t?this.#y():this.#g()}else this.#b()},Er),C&&(this.#e=w)}#g(){try{this.#a=R(()=>this.#r(this.#e))}catch(e){this.error(e)}}#_(e){let t=this.#n.failed,{reset:n,invoke_onerror:r}=this.#v(e);Qe(r),t&&(this.#s=R(()=>{t(this.#e,()=>e,()=>n)}))}#v(e){var t=!1,n=!1;let r=()=>{if(t){Ee();return}t=!0,n&&Ke(),this.#s!==null&&Pn(this.#s,()=>{this.#s=null}),this.#S(()=>{this.#b()})};return{reset:r,invoke_onerror:()=>{try{n=!0,this.#n.onerror?.(e,r),n=!1}catch(e){pn(e,this.#i&&this.#i.parent)}}}}#y(){let e=this.#n.pending;e&&(this.is_pending=!0,this.#o=R(()=>e(this.#e)),Qe(()=>{var e=this.#c=document.createDocumentFragment(),t=rn(),n=!1;if(e.append(t),this.#a=this.#S(()=>{try{return R(()=>this.#r(t))}catch(e){try{this.error(e),n=!0}catch(e){pn(e,this.#i.parent)}return null}}),this.#a===null){this.#c=null,n&&this.#x(j);return}this.#u===0&&(this.#e.before(e),this.#c=null,Pn(this.#o,()=>{this.#o=null}),this.#x(j))}))}#b(){try{if(this.is_pending=this.has_pending_snippet(),this.#u=0,this.#l=0,this.#a=R(()=>{this.#r(this.#e)}),this.#u>0){var e=this.#c=document.createDocumentFragment();Rn(this.#a,e);let t=this.#n.pending;this.#o=R(()=>t(this.#e))}else this.#x(j)}catch(e){this.error(e)}}#x(e){this.is_pending=!1,e.transfer_effects(this.#f,this.#p)}defer_effect(e){nt(e,this.#f,this.#p)}is_rendered(){return!this.is_pending&&(!this.parent||this.parent.is_rendered())}has_pending_snippet(){return!!this.#n.pending}#S(e){var t=H,n=B,r=D;Wn(this.#i),V(this.#i),qe(this.#i.ctx);try{return At.ensure(),e()}finally{Wn(t),V(n),qe(r)}}#C(e,t){if(!this.has_pending_snippet()){this.parent&&this.parent.#C(e,t);return}this.#u+=e,this.#u===0&&(this.#x(t),this.#o&&Pn(this.#o,()=>{this.#o=null}),this.#c&&=(this.#e.before(this.#c),null))}update_pending_count(e,t){this.#C(e,t),this.#l+=e,!(!this.#m||this.#d)&&(this.#d=!0,Qe(()=>{this.#d=!1,this.#m&&Gt(this.#m,this.#l)}))}get_effect_pending(){return this.#h(),G(this.#m)}error(e){if(!this.#n.onerror&&!this.#n.failed)throw e;j?.is_fork?(this.#a&&j.skip_effect(this.#a),this.#o&&j.skip_effect(this.#o),this.#s&&j.skip_effect(this.#s),j.oncommit(()=>{this.#w(e)})):this.#w(e)}#w(e){this.#a&&=(z(this.#a),null),this.#o&&=(z(this.#o),null),this.#s&&=(z(this.#s),null),C&&(T(this.#t),ke(),T(Ae()));let t=this.#n.failed,n=e=>{let{reset:n,invoke_onerror:r}=this.#v(e);r(),t&&(this.#s=this.#S(()=>{try{return R(()=>{var r=H;r.b=this,r.f|=128,t(this.#e,()=>e,()=>n)})}catch(e){return pn(e,this.#i.parent),null}}))};Qe(()=>{var t;try{t=this.transform_error(e)}catch(e){pn(e,this.#i&&this.#i.parent);return}typeof t==`object`&&t&&typeof t.then==`function`?t.then(n,e=>pn(e,this.#i&&this.#i.parent)):n(t)})}};function X(e,t){var n=t==null?``:typeof t==`object`?`${t}`:t;n!==(e[ye]??=e.nodeValue)&&(e[ye]=n,e.nodeValue=`${n}`)}function kr(e,t){return jr(e,t)}var Ar=new Map;function jr(e,{target:t,anchor:r,props:i={},events:a,context:o,intro:s=!0,transformError:l}){nn();var u=void 0,d=Sn(()=>{var s=r??t.appendChild(rn());Dr(s,{pending:()=>{}},t=>{O({});var r=D;if(o&&(r.c=o),a&&(i.$$events=a),C&&Sr(t,null),u=e(t,i)||Je(),C&&(H.nodes.end=w,w===null||w.nodeType!==8||w.data!==`]`))throw we(),n;k()},l);var d=new Set,f=e=>{for(var n=0;n{for(var e of d)for(let r of[t,document]){var n=Ar.get(r),i=n.get(e);--i==0?(r.removeEventListener(e,vr),n.delete(e),n.size===0&&Ar.delete(r)):n.set(e,i)}pr.delete(f),s!==r&&s.parentNode?.removeChild(s)}});return Mr.set(u,d),u}var Mr=new WeakMap,Nr=class{anchor;#e=new Map;#t=new Map;#n=new Map;#r=new Set;#i=!0;constructor(e,t=!0){this.anchor=e,this.#i=t}#a=e=>{if(this.#e.has(e)){var t=this.#e.get(e),n=this.#t.get(t);if(n)In(n),this.#r.delete(t);else{var r=this.#n.get(t);r&&(In(r.effect),this.#t.set(t,r.effect),this.#n.delete(t),r.fragment.lastChild.remove(),this.anchor.before(r.fragment),n=r.effect)}for(let[t,n]of this.#e){if(this.#e.delete(t),t===e)break;let r=this.#n.get(n);r&&(z(r.effect),this.#n.delete(n))}for(let[e,r]of this.#t){if(e===t||this.#r.has(e))continue;let i=()=>{if(Array.from(this.#e.values()).includes(e)){var t=document.createDocumentFragment();Rn(r,t),t.append(rn()),this.#n.set(e,{effect:r,fragment:t})}else z(r);this.#r.delete(e),this.#t.delete(e)};this.#i||!n?(this.#r.add(e),Pn(r,i,!1)):i()}}};#o=e=>{this.#e.delete(e);let t=Array.from(this.#e.values());for(let[e,n]of this.#n)t.includes(e)||(z(n.effect),this.#n.delete(e))};ensure(e,t){var n=j,r=ln();if(t&&!this.#t.has(e)&&!this.#n.has(e)){if(r){var i=document.createDocumentFragment(),a=rn();i.append(a),this.#n.set(e,{effect:R(()=>t(a)),fragment:i})}else this.#t.set(e,R(()=>t(this.anchor)))}if(this.#e.set(n,e),r){for(let[t,r]of this.#t)t===e?n.unskip_effect(r):n.skip_effect(r);for(let[t,r]of this.#n)t===e?n.unskip_effect(r.effect):n.skip_effect(r.effect);n.oncommit(this.#a),n.ondiscard(this.#o)}else C&&(this.anchor=w),this.#a(n)}};function Pr(t){D===null&&Fe(`onMount`),e&&D.l!==null?Fr(D).m.push(t):yn(()=>{let e=K(t);if(typeof e==`function`)return e})}function Fr(e){var t=e.l;return t.u??={a:[],b:[],m:[]}}function Z(e,t,n=!1){var r;C&&(r=w,Oe());var i=new Nr(e),a=n?ie:0;function o(e,t){if(C){var n=je(r);if(e!==parseInt(n.substring(1))){var a=Ae();T(a),i.anchor=a,De(!1),i.ensure(e,t),De(!0);return}}i.ensure(e,t)}On(()=>{var e=!1;t((t,n=0)=>{e=!0,o(n,t)}),e||o(-1,null)},a)}var Ir=Symbol(`NaN`);function Lr(e,t,n){C&&Oe();var r=new Nr(e),i=!Ye();On(()=>{var e=t();e!==e&&(e=Ir),i&&typeof e==`object`&&e&&(e={}),r.ensure(e,n)})}function Rr(e,t){return t}function zr(e,t,n){for(var r=[],i=t.length,a,o=t.length,s=0;s{if(a){if(a.pending.delete(n),a.done.add(n),a.pending.size===0){var t=e.outrogroups;Br(e,c(a.done)),t.delete(a),t.size===0&&(e.outrogroups=null)}}else--o},!1)}if(o===0){var l=r.length===0&&n!==null&&e.pending.size===0;if(l){var u=n,d=u.parentNode;cn(d),d.append(u),e.items.clear()}Br(e,t,!l)}else a={pending:new Set(t),done:new Set},(e.outrogroups??=new Set).add(a)}function Br(e,t,n=!0){var r;if(e.pending.size>0){r=new Set;for(let t of e.pending.values())for(let n of t)r.add(e.items.get(n).e)}for(var i=0;i{var e=n();return a(e)?e:e==null?[]:c(e)}),p,m=new Map,h=!0;function g(e){v.effect.f&16384||(v.pending.delete(e),v.fallback=d,Wr(v,p,s,t,r),d!==null&&(p.length===0?d.f&33554432?(d.f^=ce,Kr(d,null,s)):In(d):Pn(d,()=>{d=null})))}function _(e){v.pending.delete(e)}var v={effect:On(()=>{p=G(f);var e=p.length;let a=!1;C&&je(s)===`[!`!=(e===0)&&(s=Ae(),T(s),De(!1),a=!0);for(var c=new Set,u=j,v=ln(),y=0;yo(s)):(d=R(()=>o(Vr??=rn())),d.f|=ce)),e>c.size&&Le(``,``,``),C&&e>0&&T(Ae()),!h){if(m.set(u,c),v){for(let[e,t]of l)c.has(e)||u.skip_effect(t.e);u.oncommit(g),u.ondiscard(_)}else g(u)}a&&De(!0),G(f)}),flags:t,items:l,pending:m,outrogroups:null,fallback:d};h=!1,C&&(s=w)}function Ur(e){for(;e!==null&&!(e.f&32);)e=e.next;return e}function Wr(e,t,n,r,i){var a=!!(r&8),o=t.length,s=e.items,l=Ur(e.effect.first),u,d=null,f,p=[],m=[],h,g,_,v;if(a)for(v=0;v0){var re=r&4&&o===0?n:null;if(a){for(v=0;v{if(f!==void 0)for(_ of f)_.nodes?.a?.apply()})}function Gr(e,t,n,r,i,a,o,s){var c=o&1?o&16?Vt(n):M(n,!1,!1):null,l=o&2?Vt(i):null;return{v:c,i:l,e:R(()=>(a(t,c??n,l??i,s),()=>{e.delete(r)}))}}function Kr(e,t,n){if(e.nodes)for(var r=e.nodes.start,i=e.nodes.end,a=t&&!(t.f&33554432)?t.nodes.start:n;r!==null;){var o=on(r);if(a.before(r),r===i)return;r=o}}function qr(e,t,n){t===null?e.effect.first=n:t.next=n,n===null?e.effect.last=t:n.prev=t}function Jr(e,t){let n=null,r=C;var i;if(C){n=w;for(var a=an(document.head);a!==null&&(a.nodeType!==8||a.data!==e);)a=on(a);if(a===null)De(!1);else{var o=on(a);a.remove(),T(o)}}C||(i=document.head.appendChild(rn()));try{On(()=>{var e=R(()=>t(i));e.f|=ae,C||(e.nodes===null?e.nodes={start:i,end:i,a:null,t:null}:e.nodes.end=i)})}finally{r&&(De(!0),T(n))}}function Yr(e){var t,n,r=``;if(typeof e==`string`||typeof e==`number`)r+=e;else if(typeof e==`object`){if(Array.isArray(e)){var i=e.length;for(t=0;t=0;){var s=o+a;(o===0||Qr.includes(r[o-1]))&&(s===r.length||Qr.includes(r[s]))?r=(o===0?``:r.substring(0,o))+r.substring(s+1):o=s}}return r===``?null:r}function ei(e,t,n,r,i,a){var o=e[_e];if(C||o!==n||o===void 0){var s=$r(n,r,a);(!C||s!==e.getAttribute(`class`))&&(s==null?e.removeAttribute(`class`):t?e.className=s:e.setAttribute(`class`,s)),e[_e]=n}else if(a&&i!==a)for(var c in a){var l=!!a[c];(i==null||l!==!!i[c])&&e.classList.toggle(c,l)}return a}function ti(e,t){t?e.hasAttribute(`selected`)||e.setAttribute(`selected`,``):e.removeAttribute(`selected`)}function ni(e,t){var n=e.__defaultValue,r=e.multiple,i=r?n??[]:null;if(!r||a(i)){var o=e.selectedIndex,s=t&&r?new Set(e.selectedOptions):null;for(var c of e.options){var l=oi(c);ti(c,r?i.includes(l):Zt(l,n))}if(t){if(s!==null)for(c of e.options){var u=s.has(c);c.selected!==u&&(c.selected=u)}else e.selectedIndex!==o&&(e.selectedIndex=o)}}}function ri(e,t,n=!1){if(e.multiple){if(t==null)return;if(!a(t))return Te();for(var r of e.options)r.selected=t.includes(oi(r));return}for(r of e.options)if(Zt(oi(r),t)){r.selected=!0;return}(!n||t!==void 0)&&(e.selectedIndex=-1)}function ii(e){var t=new MutationObserver(t=>{t.every(si)||(`__defaultValue`in e&&ni(e,!1),`__value`in e&&ri(e,e.__value))});t.observe(e,{childList:!0,subtree:!0,attributes:!0,attributeFilter:[`value`]}),vn(()=>{t.disconnect()})}function ai(e,t,n=t){var r=new WeakSet,i=!0;ot(e,`change`,t=>{var i=t?`[selected]`:`:checked`,a;if(e.multiple)a=[].map.call(e.querySelectorAll(i),oi);else{var o=e.querySelector(i)??e.querySelector(`option:not([disabled])`);a=o&&oi(o)}n(a),e.__value=a,j!==null&&r.add(j)}),Cn(()=>{var a=t();if(e===document.activeElement){var o=j;if(r.has(o))return}if(ri(e,a,i),i&&a===void 0){var s=e.querySelector(`:checked`);s!==null&&(a=oi(s),n(a))}e.__value=a,i=!1})}function oi(e){return`__value`in e?e.__value:e.value}function si(e){if(e.target.closest(`selectedcontent`)!==null)return!0;if(e.type===`childList`){var t=[...e.addedNodes,...e.removedNodes];return t.length>0&&t.every(e=>e.nodeName===`SELECTEDCONTENT`)}return!1}var ci=Symbol(`is custom element`),li=Symbol(`is html`),ui=Se?`link`:`LINK`;function di(e){if(C){var t=!1,n=()=>{if(!t){if(t=!0,e.hasAttribute(`value`)){var n=e.value;Q(e,`value`,null),e.value=n}if(e.hasAttribute(`checked`)){var r=e.checked;Q(e,`checked`,null),e.checked=r}}};e[be]=n,Qe(n),it()}}function fi(e,t){var n=pi(e);n.checked!==(n.checked=t??void 0)&&(e.checked=t)}function Q(e,t,n,r){var i=pi(e);C&&(i[t]=e.getAttribute(t),t===`src`||t===`srcset`||t===`href`&&e.nodeName===ui)||i[t]!==(i[t]=n)&&(t===`loading`&&(e[he]=n),n==null?e.removeAttribute(t):typeof n!=`string`&&hi(e).has(t)?e[t]=n:e.setAttribute(t,n))}function pi(e){return e[ge]??={[ci]:e.nodeName.includes(`-`),[li]:e.namespaceURI===i}}var mi=new Map;function hi(e){var t=e.getAttribute(`is`)||e.nodeName,n=mi.get(t);if(n)return n;mi.set(t,n=new Set);for(var r,i=e,a=Element.prototype;a!==i;){for(var o in r=d(i),r)r[o].set&&o!==`innerHTML`&&o!==`textContent`&&o!==`innerText`&&n.add(o);i=m(i)}return n}function gi(e,t,n=t){var r=new WeakSet;ot(e,`input`,async i=>{var a=i?e.defaultValue:e.value;if(a=_i(e)?vi(a):a,n(a),j!==null&&r.add(j),await sr(),a!==(a=t())){var o=e.selectionStart,s=e.selectionEnd,c=e.value.length;if(e.value=a??``,s!==null){var l=e.value.length;o===s&&s===c&&l>c?(e.selectionStart=l,e.selectionEnd=l):(e.selectionStart=o,e.selectionEnd=Math.min(s,l))}}}),(C&&e.defaultValue!==e.value||K(t)==null&&e.value)&&(n(_i(e)?vi(e.value):e.value),j!==null&&r.add(j)),Dn(()=>{var n=t();if(e===document.activeElement){var i=j;if(r.has(i))return}_i(e)&&n===vi(e.value)||(e.type!==`date`||n||e.value)&&n!==e.value&&(e.value=n??``)})}function _i(e){var t=e.type;return t===`number`||t===`range`}function vi(e){return e===``?null:+e}function yi(e=!1){let t=D,n=t.l.u;if(!n)return;let r=()=>q(t.s);if(e){let e=0,n={},i=dt(()=>{let r=!1,i=t.s;for(let e in i)i[e]!==n[e]&&(n[e]=i[e],r=!0);return r&&e++,e});r=()=>G(i)}n.b.length&&xn(()=>{bi(t,r),v(n.b)}),yn(()=>{let e=K(()=>n.m.map(_));return()=>{for(let t of e)typeof t==`function`&&t()}}),n.a.length&&yn(()=>{bi(t,r),v(n.a)})}function bi(e,t){if(e.l.s)for(let t of e.l.s)G(t);t()}function xi(e,t,n){if(e==null)return t(void 0),n&&n(void 0),g;let r=K(()=>e.subscribe(t,n));return r.unsubscribe?()=>r.unsubscribe():r}var Si=[];function Ci(e,t=g){let n=null,r=new Set;function i(t){if(Ne(e,t)&&(e=t,n)){let t=!Si.length;for(let t of r)t[1](),Si.push(t,e);if(t){for(let e=0;e{r.delete(c),r.size===0&&n&&(n(),n=null)}}return{set:i,update:a,subscribe:o}}function wi(e){let t;return xi(e,e=>t=e)(),t}var Ti=!1,Ei=Symbol(`unmounted`);function Di(e,t,n){let r=n[t]??={store:null,source:M(void 0),unsubscribe:g};if(r.store!==e&&!(Ei in n)){if(r.unsubscribe(),r.store=e??null,e==null)r.source.v=void 0,r.unsubscribe=g;else{var i=!0;r.unsubscribe=xi(e,e=>{i?r.source.v=e:N(r.source,e)}),i=!1}}return e&&Ei in n?wi(e):G(r.source)}function Oi(){let e={};function t(){vn(()=>{for(var t in e)e[t].unsubscribe();l(e,Ei,{enumerable:!1,value:!0})})}return[e,t]}function ki(e){var t=Ti;try{return Ti=!1,[e(),Ti]}finally{Ti=t}}function $(t,n,r,i){var a=!e||!!(r&2),o=!!(r&8),s=!!(r&16),c=i,l=!0,d=void 0,f=()=>s&&a?(d??=dt(i),G(d)):(l&&(l=!1,c=s?K(i):i),c);let p;if(o){var m=fe in t||me in t;p=u(t,n)?.set??(m&&n in t?e=>t[n]=e:void 0)}var h,g=!1;o?[h,g]=ki(()=>t[n]):h=t[n],h===void 0&&i!==void 0&&(h=f(),p&&(a&&He(n),p(h)));var _=a?()=>{var e=t[n];return e===void 0?f():(l=!0,e)}:()=>{var e=t[n];return e!==void 0&&(c=void 0),e===void 0?c:e};if(a&&!(r&4))return _;if(p){var v=t.$$legacy;return(function(e,t){return arguments.length>0?((!a||!t||v||g)&&p(t?_():e),e):_()})}var y=!1,b=(r&1?dt:mt)(()=>(y=!1,_()));o&&G(b);var x=H;return(function(e,t){if(arguments.length>0){let n=t?G(b):a&&o?Yt(e):e;return N(b,n),y=!0,c!==void 0&&(c=n),e}return Vn&&y||x.f&16384?b.v:G(b)})}function Ai(){let e=Ci({controller:!1,busy:!1,error:``}),t,n=``,r={controller:!1,busy:!1,error:``},i=!1;function a(t){r={...r,...t},e.set(r)}async function o(e=!1){if(!i){i=!0;try{let t=await fetch(`/api/state`,{cache:`no-store`});if(!t.ok)throw Error(`The local setup session is unavailable.`);a({view:await t.json(),...e?{}:{error:``}})}catch{a({view:void 0,error:`Connection lost. The CLI may have stopped. Check the terminal before trying again.`})}finally{i=!1}}}async function s(){try{let e=await fetch(`/api/bootstrap`,{cache:`no-store`});if(!e.ok)throw Error(`Could not join this local session.`);let r=await e.json();t=r.capability,n=r.takeoverTicket,a({controller:r.controller,error:``}),await o()}catch{t=void 0,n=``,a({view:void 0,controller:!1,error:`Could not connect to the local setup session. Check the terminal.`})}}async function c(e,n,r=!0){let i=await fetch(e,{method:`POST`,cache:`no-store`,headers:{"Content-Type":`application/json`,...r&&t?{"X-Setup-Capability":t}:{}},body:JSON.stringify(n)}),a=await i.json();if(!i.ok)throw Error(String(a.error??`The request was rejected.`));return a}async function l(e,t){if(!r.busy&&r.controller&&r.view?.promptRevision===e){a({busy:!0,error:``});try{await c(`/api/answer`,{revision:e,value:t}),await o()}catch(e){let t=e instanceof Error?e.message:`Could not submit this answer.`;a({error:t}),/read-only|Control moved/.test(t)?await s():await o(!0)}finally{a({busy:!1})}}}async function u(){if(r.controller&&!r.busy){a({busy:!0,error:``});try{await c(`/api/cancel`,{}),await o()}catch(e){let t=e instanceof Error?e.message:`Cancellation failed.`;a({error:t}),/read-only|Control moved/.test(t)&&await s()}finally{a({busy:!1})}}}async function d(){try{let e=await c(`/api/takeover`,{ticket:n},!1);t=String(e.capability),a({controller:!0,error:``}),await o()}catch(e){a({error:e instanceof Error?e.message:`Takeover failed.`}),await s()}}async function f(){try{await c(`/api/close`,{})}catch{}}return{subscribe:e.subscribe,connect:s,refresh:o,submit:l,cancel:u,takeOver:d,close:f}}var ji=J(`
                    • `),Mi=J(``);function Ni(e,t){O(t,!1);let n=$(t,`journey`,8),r=[`Repository`,`Setup choices`,`Setup PAT`,`Plan`,`Bot PAT & credentials`,`Apply`];yi();var i=Mi(),a=I(P(i),4);Hr(a,5,()=>r,Rr,(e,t,r)=>{var i=ji();let a;var o=P(i),s=F(o,!0),c=F(I(o,2),!0);E(i),L(e=>{Q(i,`aria-current`,(q(n()),K(()=>n()?.position===r+1?`step`:void 0))),a=ei(i,1,``,null,a,{current:n()?.position===r+1,completed:(n()?.position??0)>r+1}),X(s,e),X(c,G(t))},[()=>(q(n()),K(()=>(n()?.position??0)>r+1?`✓`:String(r+1).padStart(2,`0`)))]),Y(e,i)}),E(a),ke(2),E(i),Y(e,i),k()}var Pi=J(`
                      `);function Fi(e,t){O(t,!1);let n=M(`system`);wn(()=>G(n),()=>{document.documentElement.dataset.theme=G(n)}),Tn();var r=Pi(),i=P(r);let a;var o=I(i,2);let s;var c=I(o,2);let l;E(r),L(()=>{Q(i,`aria-pressed`,G(n)===`system`),a=ei(i,1,``,null,a,{active:G(n)===`system`}),Q(o,`aria-pressed`,G(n)===`light`),s=ei(o,1,``,null,s,{active:G(n)===`light`}),Q(c,`aria-pressed`,G(n)===`dark`),l=ei(c,1,``,null,l,{active:G(n)===`dark`})}),mr(`click`,i,()=>N(n,`system`)),mr(`click`,o,()=>N(n,`light`)),mr(`click`,c,()=>N(n,`dark`)),Y(e,r),k()}hr([`click`]);var Ii=J(`
                      LOCAL SESSION
                      `);function Li(e,t){let n=$(t,`repository`,8);var r=Ii(),i=P(r),a=F(I(P(i),2),!0);E(i);var o=I(i,2);Fi(I(P(o)),{}),E(o),E(r),L(()=>X(a,n()??`Connecting…`)),Y(e,r)}var Ri=J(`

                      `,1);function zi(e,t){O(t,!1);let n=M(),r=$(t,`view`,8);wn(()=>q(r()),()=>{N(n,r()?.outcome===`complete`?`Setup complete.`:r()?.outcome===`dry-run`?`Preview complete.`:r()?.outcome===`cancelled`?`Setup cancelled.`:r()?.outcome?`Setup needs attention.`:r()?.prompt?.title??`Preparing your setup…`)}),Tn(),yi();var i=Ri(),a=sn(i),o=I(P(a)),s=F(I(o));E(a);var c=I(a,2),l=F(c,!0),u=F(I(c,2),!0);L((e,t)=>{X(o,` ${e??``} `),X(s,`${t??``} / 06`),X(l,G(n)),X(u,(q(r()),K(()=>r()?.outcome?`The terminal contains the detailed result. Review the next steps below before closing this session.`:`One decision at a time. Your choices shape the permissions, plan and credentials needed for this repository.`)))},[()=>(q(r()),K(()=>r()?.journey?.current?.toUpperCase()??`GETTING READY`)),()=>(q(r()),K(()=>String(r()?.journey?.position??1).padStart(2,`0`)))]),Y(e,i),k()}function Bi(e){try{let t=new URL(e??``);return t.protocol===`https:`&&t.hostname===`github.com`&&(t.pathname===`/settings/personal-access-tokens`||t.pathname.startsWith(`/settings/personal-access-tokens/`))?t.toString():void 0}catch{return}}var Vi=J(``),Hi=J(``);function Ui(e,t){let n=$(t,`label`,8),r=$(t,`variant`,8,`primary`),i=$(t,`disabled`,8,!1),a=$(t,`arrow`,8,!1),o=$(t,`onClick`,8);var s=Hi(),c=P(s,!0),l=I(c),u=e=>{Y(e,Vi())};Z(l,e=>{a()&&e(u)}),E(s),L(()=>{ei(s,1,Zr(r())),s.disabled=i(),X(c,n())}),mr(`click`,s,function(...e){o()?.apply(this,e)}),Y(e,s)}hr([`click`]);var Wi=J(`Open GitHub link ↗`),Gi=J(`

                      `);function Ki(e,t){O(t,!1);let n=M(),r=$(t,`tone`,8,`info`),i=$(t,`title`,8),a=$(t,`message`,8),o=$(t,`link`,8,void 0),s=$(t,`actionLabel`,8,void 0),c=$(t,`onAction`,8,void 0);wn(()=>q(o()),()=>{N(n,Bi(o()))}),Tn(),yi();var l=Gi(),u=P(l),d=F(u,!0),f=I(u),p=F(f,!0),m=I(f,2),h=e=>{var t=Wi();L(()=>Q(t,`href`,G(n))),Y(e,t)};Z(m,e=>{G(n)&&e(h)});var g=I(m,2),_=e=>{Ui(e,{get label(){return s()},variant:`secondary`,get onClick(){return c()}})};Z(g,e=>{s()&&c()&&e(_)}),E(l),L(()=>{ei(l,1,`banner ${r()??``}`),Q(l,`role`,r()===`error`?`alert`:`status`),X(d,i()),X(p,a())}),Y(e,l),k()}function qi(e){let t=String(e.question.defaultValue??``),n=t.split(`,`).map(e=>e.trim()).filter(Boolean);return{value:t,selected:e.question.kind===`multi-select`?(e.question.choices??[]).filter(e=>e!==`All`&&n.includes(e.split(` — `)[0])):e.question.kind===`scope-overrides`?n:[]}}function Ji(e,t){return t===`All`?e.includes(`All`)?[]:[`All`]:e.includes(t)?e.filter(e=>e!==t):[...e.filter(e=>e!==`All`),t]}function Yi(e,t,n){return e.question.kind===`scope-overrides`||e.question.kind===`multi-select`?n.length?n.join(`,`):`none`:t}var Xi=J(`PERMISSION PREVIEW`),Zi=J(`
                      `),Qi=J(``),$i=J(``),ea=J(``),ta=J(`
                      `),na=J(``),ra=J(`

                      `,1);function ia(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`controller`,8),i=$(t,`busy`,8),a=$(t,`onSubmit`,8),o=qi(n()),s=M(o.value),c=M(o.selected);yi();var l=ra(),u=sn(l),d=P(u),f=F(d,!0),p=I(d),m=e=>{Y(e,Xi())};Z(p,e=>{q(n()),K(()=>n().phase===`permission-intent`)&&e(m)}),E(u);var h=I(u,2),g=e=>{var t=Zi(),i=P(t);let a;var o=I(i);let c;E(t),L(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),Q(i,`aria-pressed`,G(s)===`yes`||G(s)===`true`),i.disabled=!r(),a=ei(i,1,``,null,a,{selected:G(s)===`yes`||G(s)===`true`}),Q(o,`aria-pressed`,G(s)===`no`||G(s)===`false`),o.disabled=!r(),c=ei(o,1,``,null,c,{selected:G(s)===`no`||G(s)===`false`})}),mr(`click`,i,()=>N(s,`yes`)),mr(`click`,o,()=>N(s,`no`)),Y(e,t)},_=e=>{var t=$i();Hr(t,5,()=>(q(n()),K(()=>n().question.choices??[])),Rr,(e,t)=>{var n=Qi(),r=F(n,!0),i={};L(()=>{X(r,G(t)),i!==(i=G(t))&&(n.value=(n.__value=i)??``)}),Y(e,n)}),E(t),ii(t),L(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),t.disabled=!r()}),ai(t,()=>G(s),e=>N(s,e)),Y(e,t)},v=e=>{var t=ta();Hr(t,5,()=>(q(n()),K(()=>n().question.kind===`multi-select`?n().question.choices??[]:n().question.allowedNames??[])),Rr,(e,t)=>{var n=ea(),i=P(n);di(i);var a=F(I(i),!0);E(n),L(e=>{fi(i,e),i.disabled=!r(),X(a,G(t))},[()=>(G(c),G(t),K(()=>G(c).includes(G(t))))]),mr(`change`,i,()=>N(c,Ji(G(c),G(t)))),Y(e,n)}),E(t),L(()=>Q(t,`aria-label`,(q(n()),K(()=>n().question.label)))),Y(e,t)},y=e=>{var t=na();di(t),L(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),Q(t,`type`,(q(n()),K(()=>n().question.kind===`number`?`number`:`text`))),Q(t,`min`,(q(n()),K(()=>n().question.kind===`number`?0:void 0))),t.disabled=!r()}),gi(t,()=>G(s),e=>N(s,e)),Y(e,t)};Z(h,e=>{q(n()),K(()=>n().question.kind===`boolean`)?e(g):(q(n()),K(()=>n().question.kind===`choice`)?e(_,1):(q(n()),K(()=>n().question.kind===`multi-select`||n().question.kind===`scope-overrides`)?e(v,2):e(y,-1)))});var b=I(h,2),x=F(b),S=I(b,2);{let e=mt(()=>!r()||i());Ui(S,{label:`Continue`,arrow:!0,onClick:()=>a()(Yi(n(),G(s),G(c))),get disabled(){return G(e)}})}L((e,t)=>{X(f,e),X(x,`Suggested answer: ${t??``} · You can review choices again before creating your setup PAT.`)},[()=>(q(n()),K(()=>n().question.label.replace(` (Space toggles, Enter confirms)`,``))),()=>(q(n()),K(()=>String(n().question.defaultValue)||`none`))]),Y(e,l),k()}hr([`click`,`change`]);var aa=J(``),oa=J(`
                      `);function sa(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`controller`,8),i=$(t,`busy`,8),a=$(t,`onSubmit`,8);yi();var o=oa();Hr(o,5,()=>(q(n()),K(()=>n().choices)),Rr,(e,t)=>{var n=aa(),o=F(P(n),!0);ke(),E(n),L(()=>{n.disabled=!r()||i(),X(o,G(t))}),mr(`click`,n,()=>a()(G(t))),Y(e,n)}),E(o),Y(e,o),k()}hr([`click`]);var ca=J(`Open the official GitHub PAT form

                      Check the signed-in account, select Only select repositories, then select this repository. GitHub handles 2FA and creates the PAT.

                      `,1),la=J(`

                      Sent only to this local process. It will not be shown again or saved in browser storage.

                      `),ua=J(` `,1);function da(e,t){O(t,!1);let n=M(),r=$(t,`prompt`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8),s=M(``);function c(){let e=G(s);r().kind===`secret`&&N(s,``),o()(e)}wn(()=>q(r()),()=>{N(n,Bi(r().link))}),Tn(),yi();var l=ua(),u=sn(l),d=e=>{var t=ca(),r=sn(t);ke(),L(()=>Q(r,`href`,G(n))),Y(e,t)};Z(u,e=>{G(n)&&e(d)});var f=I(u,2),p=F(f,!0),m=I(f,2);di(m);var h=I(m,2),g=e=>{Y(e,la())};Z(h,e=>{q(r()),K(()=>r().kind===`secret`)&&e(g)});var _=I(h,2);{let e=mt(()=>(q(i()),q(a()),q(r()),G(s),K(()=>!i()||a()||!r().optional&&!G(s).trim())));Ui(_,{label:`Continue`,arrow:!0,onClick:c,get disabled(){return G(e)}})}L(()=>{X(p,(q(r()),K(()=>r().kind===`secret`?`Paste the value here`:`Your answer`))),Q(m,`type`,(q(r()),K(()=>r().kind===`secret`?`password`:`text`))),m.disabled=!i(),Q(m,`placeholder`,(q(r()),K(()=>r().kind===`secret`?`Hidden after submission`:`Type your answer`)))}),gi(m,()=>G(s),e=>N(s,e)),Y(e,l),k()}var fa=J(`
                    • `),pa=J(`

                        `),ma=J(`

                        Before you continue

                          `),ha=J(`

                          Review exactly what this run may change. The bot PAT and any additional credentials are collected next.

                          `,1);function ga(e,t){O(t,!1);let n=M(),r=$(t,`prompt`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8);wn(()=>q(r()),()=>{N(n,[{title:`Files`,items:r().plan.files},{title:`Workflows`,items:r().plan.workflows},{title:`Variables`,items:r().plan.variables},{title:`Secret names`,items:r().plan.secrets}])}),Tn(),yi();var s=ha(),c=I(sn(s),2);Hr(c,5,()=>G(n),Rr,(e,t)=>{var n=pa(),r=P(n),i=P(r),a=F(I(i),!0);E(r);var o=I(r);Hr(o,5,()=>(G(t),K(()=>G(t).items)),Rr,(e,t)=>{var n=fa(),r=F(n,!0);L(()=>X(r,G(t))),Y(e,n)}),E(o),E(n),L(()=>{X(i,`${G(t),K(()=>G(t).title)??``} `),X(a,(G(t),K(()=>G(t).items.length)))}),Y(e,n)}),E(c);var l=I(c,2),u=e=>{var t=ma(),n=I(P(t));Hr(n,5,()=>(q(r()),K(()=>r().plan.warnings)),Rr,(e,t)=>{var n=fa(),r=F(n,!0);L(()=>X(r,G(t))),Y(e,n)}),E(n),E(t),Y(e,t)};Z(l,e=>{q(r()),K(()=>r().plan.warnings.length)&&e(u)});var d=I(l,2),f=P(d);{let e=mt(()=>!i()||a());Ui(f,{label:`Stop here`,variant:`secondary`,onClick:()=>o()(`decline`),get disabled(){return G(e)}})}var p=I(f);{let e=mt(()=>!i()||a());Ui(p,{label:`Approve this plan`,arrow:!0,onClick:()=>o()(`approve`),get disabled(){return G(e)}})}E(d),Y(e,s),k()}var _a=J(`

                          `),va=J(`
                          CURRENT DECISION
                          `);function ya(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`revision`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8);yi();var s=va(),c=P(s),l=F(I(P(c)));E(c);var u=I(c,2),d=e=>{var t=_a(),r=F(t,!0);L(()=>X(r,(q(n()),K(()=>n().description)))),Y(e,t)};Z(u,e=>{q(n()),K(()=>`description`in n()&&n().description)&&e(d)});var f=I(u,2),p=e=>{ia(e,{get prompt(){return n()},get controller(){return i()},get busy(){return a()},get onSubmit(){return o()}})},m=e=>{sa(e,{get prompt(){return n()},get controller(){return i()},get busy(){return a()},get onSubmit(){return o()}})},h=e=>{da(e,{get prompt(){return n()},get controller(){return i()},get busy(){return a()},get onSubmit(){return o()}})},g=e=>{ga(e,{get prompt(){return n()},get controller(){return i()},get busy(){return a()},get onSubmit(){return o()}})};Z(f,e=>{q(n()),K(()=>n().kind===`question`)?e(p):(q(n()),K(()=>n().kind===`choice`||n().kind===`confirm`)?e(m,1):(q(n()),K(()=>n().kind===`text`||n().kind===`secret`)?e(h,2):(q(n()),K(()=>n().kind===`plan`)&&e(g,3))))}),E(s),L(()=>X(l,`SESSION ${r()??``}`)),Y(e,s),k()}var ba=J(` `),xa=J(`
                        • `),Sa=J(`

                            Conditional grants depend on the choices and GitHub facts. A final audit runs before setup changes.
                            `),Ca=J(`

                            Permissions follow your choices

                            We’ll show the exact grants before you create each PAT. Nothing is created just by opening this page.

                            `),wa=J(``);function Ta(e,t){O(t,!1);let n=$(t,`view`,8);yi();var r=wa(),i=P(r),a=F(I(P(i),3),!0);E(i);var o=I(i,2),s=e=>{var t=Sa(),r=I(P(t)),i=F(r),a=I(r),o=F(a,!0),s=I(a,2);Hr(s,5,()=>(q(n()),K(()=>n().permissions.report?.checks??n().permissions.requirements??[])),Rr,(e,t)=>{var n=xa(),r=P(n),i=P(r),a=I(i),o=F(a),s=F(I(a),!0);E(r);var c=I(r),l=P(c,!0),u=I(l),d=e=>{var n=ba(),r=F(n,!0);L(()=>X(r,(G(t),K(()=>G(t).status)))),Y(e,n)};Z(u,e=>{`status`in G(t)&&e(d)}),E(c),E(n),L(()=>{X(i,`${G(t),K(()=>G(t).permission)??``} `),X(o,`${G(t),K(()=>G(t).scope)??``} · ${G(t),K(()=>G(t).applicability)??``}`),X(s,(G(t),K(()=>G(t).reason))),X(l,(G(t),K(()=>G(t).level)))}),Y(e,n)}),E(s),ke(2),E(t),L(()=>{X(i,`${q(n()),K(()=>n().permissions.role===`setup`?`Setup PAT`:`Bot PAT`)??``} access`),X(o,(q(n()),K(()=>n().permissions.report?`Read-only access check`:`Provisional least-privilege grants`)))}),Y(e,t)},c=e=>{Y(e,Ca())};Z(o,e=>{q(n()),K(()=>n().permissions)?e(s):e(c,-1)}),E(r),L(()=>X(a,(q(n()),K(()=>n().repository)))),Y(e,r),k()}var Ea=J(`

                            `);function Da(e,t){O(t,!1);let n=M(),r=M(),i=$(t,`outcome`,8),a=$(t,`controller`,8),o=$(t,`onClose`,8);wn(()=>q(i()),()=>{N(n,i()===`complete`?`Your configuration was applied`:i()===`dry-run`?`No changes were made`:i()===`cancelled`||i()===`blocked`?`No setup changes started`:`Check partial changes before retrying`)}),wn(()=>q(i()),()=>{N(r,i()===`complete`?`The temporary setup PAT is not revoked automatically. Delete it in GitHub after confirming your setup. Keep the bot PAT until you rotate the installed Secret.`:i()===`partial`?`A bot Secret write or another change may have succeeded. Inspect GitHub and run copilot doctor before replacing or deleting its PAT.`:`Copilot did not begin applying setup changes. PATs you created in GitHub still exist until you delete them there.`)}),Tn();var s=Ea(),c=P(s),l=F(c,!0),u=I(c),d=F(u,!0),f=I(u),p=F(f,!0),m=I(f,2),h=e=>{Ui(e,{label:`Close local session`,get onClick(){return o()}})};Z(m,e=>{a()&&e(h)}),E(s),L(()=>{X(l,i()===`complete`?`✓`:`!`),X(d,G(n)),X(p,G(r))}),Y(e,s),k()}var Oa=J(`

                            Working on the next step

                            The local process is checking your answers and preparing the next decision. Keep this page open.

                            `);function ka(e){Y(e,Oa())}var Aa=J(``),ja=J(`
                            `),Ma=J(``),Na=J(`
                            `,1),Pa=J(`
                            LOCALHOST ONLY NO CLOUD SETUP ACCOUNT GITHUB OWNS PAT ISSUANCE
                            `);function Fa(e,t){O(t,!1);let n=()=>Di(a,`$session`,r),[r,i]=Oi(),a=Ai();Pr(()=>{a.connect();let e=window.setInterval(()=>{n().view?.outcome||a.refresh()},900);return()=>window.clearInterval(e)});async function o(){window.confirm(`Cancel this local setup session? PATs already created in GitHub will still exist.`)&&await a.cancel()}async function s(e){let t=n().view?.promptRevision;t!==void 0&&await a.submit(t,e)}yi();var c=Pa();Jr(`16t12jp`,e=>{Y(e,Aa())});var l=P(c);{let e=mt(()=>n().view?.journey);Ni(l,{get journey(){return G(e)}})}var u=I(l,2),d=P(u);{let e=mt(()=>n().view?.repository);Li(d,{get repository(){return G(e)}})}var f=I(d,2),p=P(f);zi(p,{get view(){return n().view}});var m=I(p,2),h=e=>{var t=ja(),r=I(P(t));E(t),L(()=>X(r,` Reviewing saved choices — pass ${n().view.journey.choiceReviewPass??``}. This is the same setup run, not a restart.`)),Y(e,t)};Z(m,e=>{n().view?.journey?.choiceReviewPass&&n().view.journey.choiceReviewPass>1&&!n().view.outcome&&e(h)});var g=I(m,2),_=e=>{Ki(e,{tone:`warning`,title:`Read-only tab`,message:`Another tab controls this session. You can review progress here or explicitly take over.`,actionLabel:`Take control in this tab`,get onAction(){return a.takeOver}})};Z(g,e=>{!n().controller&&n().view&&e(_)});var v=I(g,2),y=e=>{Ki(e,{tone:`error`,title:`Needs attention`,get message(){return n().error}})};Z(v,e=>{n().error&&e(y)});var b=I(v,2),x=e=>{{let t=mt(()=>n().view.message.tone===`success`?`Checked`:n().view.message.tone===`warning`?`Please note`:n().view.message.tone===`error`?`Needs attention`:`Progress update`);Ki(e,{get tone(){return n().view.message.tone},get title(){return G(t)},get message(){return n().view.message.text},get link(){return n().view.message.link}})}};Z(b,e=>{n().view?.message&&e(x)});var S=I(b,2),ee=e=>{Da(e,{get outcome(){return n().view.outcome},get controller(){return n().controller},get onClose(){return a.close}})},te=e=>{var t=Na(),r=sn(t),i=P(r);Lr(i,()=>n().view.promptRevision,e=>{ya(e,{get prompt(){return n().view.prompt},get revision(){return n().view.revision},get controller(){return n().controller},get busy(){return n().busy},onSubmit:s})}),Ta(I(i,2),{get view(){return n().view}}),E(r);var a=I(r,2),c=e=>{var t=Ma();L(()=>t.disabled=n().busy),mr(`click`,t,o),Y(e,t)};Z(a,e=>{n().controller&&n().view.journey?.current!==`Apply`&&e(c)}),Y(e,t)},ne=e=>{ka(e,{})};Z(S,e=>{n().view?.outcome?e(ee):n().view?.prompt?e(te,1):e(ne,-1)}),ke(2),E(f),E(u),E(c),Y(e,c),k(),i()}hr([`click`]),kr(Fa,{target:document.getElementById(`app`)}); \ No newline at end of file +\r\f\xA0\v`];function $r(e,t,n){var r=e==null?``:``+e;if(t&&(r=r?r+` `+t:t),n){for(var i of Object.keys(n))if(n[i])r=r?r+` `+i:i;else if(r.length)for(var a=i.length,o=0;(o=r.indexOf(i,o))>=0;){var s=o+a;(o===0||Qr.includes(r[o-1]))&&(s===r.length||Qr.includes(r[s]))?r=(o===0?``:r.substring(0,o))+r.substring(s+1):o=s}}return r===``?null:r}function ei(e,t,n,r,i,a){var o=e[_e];if(C||o!==n||o===void 0){var s=$r(n,r,a);(!C||s!==e.getAttribute(`class`))&&(s==null?e.removeAttribute(`class`):t?e.className=s:e.setAttribute(`class`,s)),e[_e]=n}else if(a&&i!==a)for(var c in a){var l=!!a[c];(i==null||l!==!!i[c])&&e.classList.toggle(c,l)}return a}function ti(e,t){t?e.hasAttribute(`selected`)||e.setAttribute(`selected`,``):e.removeAttribute(`selected`)}function ni(e,t){var n=e.__defaultValue,r=e.multiple,i=r?n??[]:null;if(!r||a(i)){var o=e.selectedIndex,s=t&&r?new Set(e.selectedOptions):null;for(var c of e.options){var l=oi(c);ti(c,r?i.includes(l):Zt(l,n))}if(t){if(s!==null)for(c of e.options){var u=s.has(c);c.selected!==u&&(c.selected=u)}else e.selectedIndex!==o&&(e.selectedIndex=o)}}}function ri(e,t,n=!1){if(e.multiple){if(t==null)return;if(!a(t))return Te();for(var r of e.options)r.selected=t.includes(oi(r));return}for(r of e.options)if(Zt(oi(r),t)){r.selected=!0;return}(!n||t!==void 0)&&(e.selectedIndex=-1)}function ii(e){var t=new MutationObserver(t=>{t.every(si)||(`__defaultValue`in e&&ni(e,!1),`__value`in e&&ri(e,e.__value))});t.observe(e,{childList:!0,subtree:!0,attributes:!0,attributeFilter:[`value`]}),vn(()=>{t.disconnect()})}function ai(e,t,n=t){var r=new WeakSet,i=!0;ot(e,`change`,t=>{var i=t?`[selected]`:`:checked`,a;if(e.multiple)a=[].map.call(e.querySelectorAll(i),oi);else{var o=e.querySelector(i)??e.querySelector(`option:not([disabled])`);a=o&&oi(o)}n(a),e.__value=a,j!==null&&r.add(j)}),Cn(()=>{var a=t();if(e===document.activeElement){var o=j;if(r.has(o))return}if(ri(e,a,i),i&&a===void 0){var s=e.querySelector(`:checked`);s!==null&&(a=oi(s),n(a))}e.__value=a,i=!1})}function oi(e){return`__value`in e?e.__value:e.value}function si(e){if(e.target.closest(`selectedcontent`)!==null)return!0;if(e.type===`childList`){var t=[...e.addedNodes,...e.removedNodes];return t.length>0&&t.every(e=>e.nodeName===`SELECTEDCONTENT`)}return!1}var ci=Symbol(`is custom element`),li=Symbol(`is html`),ui=Se?`link`:`LINK`;function di(e){if(C){var t=!1,n=()=>{if(!t){if(t=!0,e.hasAttribute(`value`)){var n=e.value;Q(e,`value`,null),e.value=n}if(e.hasAttribute(`checked`)){var r=e.checked;Q(e,`checked`,null),e.checked=r}}};e[be]=n,Qe(n),it()}}function fi(e,t){var n=pi(e);n.checked!==(n.checked=t??void 0)&&(e.checked=t)}function Q(e,t,n,r){var i=pi(e);C&&(i[t]=e.getAttribute(t),t===`src`||t===`srcset`||t===`href`&&e.nodeName===ui)||i[t]!==(i[t]=n)&&(t===`loading`&&(e[he]=n),n==null?e.removeAttribute(t):typeof n!=`string`&&hi(e).has(t)?e[t]=n:e.setAttribute(t,n))}function pi(e){return e[ge]??={[ci]:e.nodeName.includes(`-`),[li]:e.namespaceURI===i}}var mi=new Map;function hi(e){var t=e.getAttribute(`is`)||e.nodeName,n=mi.get(t);if(n)return n;mi.set(t,n=new Set);for(var r,i=e,a=Element.prototype;a!==i;){for(var o in r=d(i),r)r[o].set&&o!==`innerHTML`&&o!==`textContent`&&o!==`innerText`&&n.add(o);i=m(i)}return n}function gi(e,t,n=t){var r=new WeakSet;ot(e,`input`,async i=>{var a=i?e.defaultValue:e.value;if(a=_i(e)?vi(a):a,n(a),j!==null&&r.add(j),await sr(),a!==(a=t())){var o=e.selectionStart,s=e.selectionEnd,c=e.value.length;if(e.value=a??``,s!==null){var l=e.value.length;o===s&&s===c&&l>c?(e.selectionStart=l,e.selectionEnd=l):(e.selectionStart=o,e.selectionEnd=Math.min(s,l))}}}),(C&&e.defaultValue!==e.value||K(t)==null&&e.value)&&(n(_i(e)?vi(e.value):e.value),j!==null&&r.add(j)),Dn(()=>{var n=t();if(e===document.activeElement){var i=j;if(r.has(i))return}_i(e)&&n===vi(e.value)||(e.type!==`date`||n||e.value)&&n!==e.value&&(e.value=n??``)})}function _i(e){var t=e.type;return t===`number`||t===`range`}function vi(e){return e===``?null:+e}function yi(e=!1){let t=D,n=t.l.u;if(!n)return;let r=()=>q(t.s);if(e){let e=0,n={},i=dt(()=>{let r=!1,i=t.s;for(let e in i)i[e]!==n[e]&&(n[e]=i[e],r=!0);return r&&e++,e});r=()=>G(i)}n.b.length&&xn(()=>{bi(t,r),v(n.b)}),yn(()=>{let e=K(()=>n.m.map(_));return()=>{for(let t of e)typeof t==`function`&&t()}}),n.a.length&&yn(()=>{bi(t,r),v(n.a)})}function bi(e,t){if(e.l.s)for(let t of e.l.s)G(t);t()}function xi(e,t,n){if(e==null)return t(void 0),n&&n(void 0),g;let r=K(()=>e.subscribe(t,n));return r.unsubscribe?()=>r.unsubscribe():r}var Si=[];function Ci(e,t=g){let n=null,r=new Set;function i(t){if(Ne(e,t)&&(e=t,n)){let t=!Si.length;for(let t of r)t[1](),Si.push(t,e);if(t){for(let e=0;e{r.delete(c),r.size===0&&n&&(n(),n=null)}}return{set:i,update:a,subscribe:o}}function wi(e){let t;return xi(e,e=>t=e)(),t}var Ti=!1,Ei=Symbol(`unmounted`);function Di(e,t,n){let r=n[t]??={store:null,source:M(void 0),unsubscribe:g};if(r.store!==e&&!(Ei in n)){if(r.unsubscribe(),r.store=e??null,e==null)r.source.v=void 0,r.unsubscribe=g;else{var i=!0;r.unsubscribe=xi(e,e=>{i?r.source.v=e:N(r.source,e)}),i=!1}}return e&&Ei in n?wi(e):G(r.source)}function Oi(){let e={};function t(){vn(()=>{for(var t in e)e[t].unsubscribe();l(e,Ei,{enumerable:!1,value:!0})})}return[e,t]}function ki(e){var t=Ti;try{return Ti=!1,[e(),Ti]}finally{Ti=t}}function $(t,n,r,i){var a=!e||!!(r&2),o=!!(r&8),s=!!(r&16),c=i,l=!0,d=void 0,f=()=>s&&a?(d??=dt(i),G(d)):(l&&(l=!1,c=s?K(i):i),c);let p;if(o){var m=fe in t||me in t;p=u(t,n)?.set??(m&&n in t?e=>t[n]=e:void 0)}var h,g=!1;o?[h,g]=ki(()=>t[n]):h=t[n],h===void 0&&i!==void 0&&(h=f(),p&&(a&&He(n),p(h)));var _=a?()=>{var e=t[n];return e===void 0?f():(l=!0,e)}:()=>{var e=t[n];return e!==void 0&&(c=void 0),e===void 0?c:e};if(a&&!(r&4))return _;if(p){var v=t.$$legacy;return(function(e,t){return arguments.length>0?((!a||!t||v||g)&&p(t?_():e),e):_()})}var y=!1,b=(r&1?dt:mt)(()=>(y=!1,_()));o&&G(b);var x=H;return(function(e,t){if(arguments.length>0){let n=t?G(b):a&&o?Yt(e):e;return N(b,n),y=!0,c!==void 0&&(c=n),e}return Vn&&y||x.f&16384?b.v:G(b)})}function Ai(){let e=Ci({controller:!1,busy:!1,error:``}),t,n=``,r={controller:!1,busy:!1,error:``},i=!1;function a(t){r={...r,...t},e.set(r)}async function o(e=!1){if(!i){i=!0;try{let t=await fetch(`/api/state`,{cache:`no-store`});if(!t.ok)throw Error(`The local setup session is unavailable.`);a({view:await t.json(),...e?{}:{error:``}})}catch{a({view:void 0,error:`Connection lost. The CLI may have stopped. Check the terminal before trying again.`})}finally{i=!1}}}async function s(){try{let e=await fetch(`/api/bootstrap`,{cache:`no-store`});if(!e.ok)throw Error(`Could not join this local session.`);let r=await e.json();t=r.capability,n=r.takeoverTicket,a({controller:r.controller,error:``}),await o()}catch{t=void 0,n=``,a({view:void 0,controller:!1,error:`Could not connect to the local setup session. Check the terminal.`})}}async function c(e,n,r=!0){let i=await fetch(e,{method:`POST`,cache:`no-store`,headers:{"Content-Type":`application/json`,...r&&t?{"X-Setup-Capability":t}:{}},body:JSON.stringify(n)}),a=await i.json();if(!i.ok)throw Error(String(a.error??`The request was rejected.`));return a}async function l(e,t){if(!r.busy&&r.controller&&r.view?.promptRevision===e){a({busy:!0,error:``});try{await c(`/api/answer`,{revision:e,value:t}),await o()}catch(e){let t=e instanceof Error?e.message:`Could not submit this answer.`;a({error:t}),/read-only|Control moved/.test(t)?await s():await o(!0)}finally{a({busy:!1})}}}async function u(){if(r.controller&&!r.busy){a({busy:!0,error:``});try{await c(`/api/cancel`,{}),await o()}catch(e){let t=e instanceof Error?e.message:`Cancellation failed.`;a({error:t}),/read-only|Control moved/.test(t)&&await s()}finally{a({busy:!1})}}}async function d(){try{let e=await c(`/api/takeover`,{ticket:n},!1);t=String(e.capability),a({controller:!0,error:``}),await o()}catch(e){a({error:e instanceof Error?e.message:`Takeover failed.`}),await s()}}async function f(){try{await c(`/api/close`,{})}catch{}}return{subscribe:e.subscribe,connect:s,refresh:o,submit:l,cancel:u,takeOver:d,close:f}}var ji=J(`
                          • `),Mi=J(``);function Ni(e,t){O(t,!1);let n=$(t,`journey`,8),r=[`Repository`,`Setup choices`,`Setup PAT`,`Plan`,`Bot PAT & credentials`,`Apply`];yi();var i=Mi(),a=I(P(i),4);Hr(a,5,()=>r,Rr,(e,t,r)=>{var i=ji();let a;var o=P(i),s=F(o,!0),c=F(I(o,2),!0);E(i),L(e=>{Q(i,`aria-current`,(q(n()),K(()=>n()?.position===r+1?`step`:void 0))),a=ei(i,1,``,null,a,{current:n()?.position===r+1,completed:(n()?.position??0)>r+1}),X(s,e),X(c,G(t))},[()=>(q(n()),K(()=>(n()?.position??0)>r+1?`✓`:String(r+1).padStart(2,`0`)))]),Y(e,i)}),E(a),ke(2),E(i),Y(e,i),k()}var Pi=J(`
                            `);function Fi(e,t){O(t,!1);let n=M(`system`);wn(()=>G(n),()=>{document.documentElement.dataset.theme=G(n)}),Tn();var r=Pi(),i=P(r);let a;var o=I(i,2);let s;var c=I(o,2);let l;E(r),L(()=>{Q(i,`aria-pressed`,G(n)===`system`),a=ei(i,1,``,null,a,{active:G(n)===`system`}),Q(o,`aria-pressed`,G(n)===`light`),s=ei(o,1,``,null,s,{active:G(n)===`light`}),Q(c,`aria-pressed`,G(n)===`dark`),l=ei(c,1,``,null,l,{active:G(n)===`dark`})}),mr(`click`,i,()=>N(n,`system`)),mr(`click`,o,()=>N(n,`light`)),mr(`click`,c,()=>N(n,`dark`)),Y(e,r),k()}hr([`click`]);var Ii=J(`
                            LOCAL SESSION
                            `);function Li(e,t){let n=$(t,`repository`,8);var r=Ii(),i=P(r),a=F(I(P(i),2),!0);E(i);var o=I(i,2);Fi(I(P(o)),{}),E(o),E(r),L(()=>X(a,n()??`Connecting…`)),Y(e,r)}var Ri=J(`

                            `,1);function zi(e,t){O(t,!1);let n=M(),r=$(t,`view`,8);wn(()=>q(r()),()=>{N(n,r()?.outcome===`complete`?`Setup complete.`:r()?.outcome===`dry-run`?`Preview complete.`:r()?.outcome===`cancelled`?`Setup cancelled.`:r()?.outcome?`Setup needs attention.`:r()?.prompt?.title??`Preparing your setup…`)}),Tn(),yi();var i=Ri(),a=sn(i),o=I(P(a)),s=F(I(o));E(a);var c=I(a,2),l=F(c,!0),u=F(I(c,2),!0);L((e,t)=>{X(o,` ${e??``} `),X(s,`${t??``} / 06`),X(l,G(n)),X(u,(q(r()),K(()=>r()?.outcome?`The terminal contains the detailed result. Review the next steps below before closing this session.`:`One decision at a time. Your choices shape the permissions, plan and credentials needed for this repository.`)))},[()=>(q(r()),K(()=>r()?.journey?.current?.toUpperCase()??`GETTING READY`)),()=>(q(r()),K(()=>String(r()?.journey?.position??1).padStart(2,`0`)))]),Y(e,i),k()}function Bi(e){try{let t=new URL(e??``);return t.protocol===`https:`&&t.hostname===`github.com`&&(t.pathname===`/settings/personal-access-tokens`||t.pathname.startsWith(`/settings/personal-access-tokens/`))?t.toString():void 0}catch{return}}var Vi=J(``),Hi=J(``);function Ui(e,t){let n=$(t,`label`,8),r=$(t,`variant`,8,`primary`),i=$(t,`disabled`,8,!1),a=$(t,`arrow`,8,!1),o=$(t,`onClick`,8);var s=Hi(),c=P(s,!0),l=I(c),u=e=>{Y(e,Vi())};Z(l,e=>{a()&&e(u)}),E(s),L(()=>{ei(s,1,Zr(r())),s.disabled=i(),X(c,n())}),mr(`click`,s,function(...e){o()?.apply(this,e)}),Y(e,s)}hr([`click`]);var Wi=J(`Open GitHub link ↗`),Gi=J(`

                            `);function Ki(e,t){O(t,!1);let n=M(),r=$(t,`tone`,8,`info`),i=$(t,`title`,8),a=$(t,`message`,8),o=$(t,`link`,8,void 0),s=$(t,`actionLabel`,8,void 0),c=$(t,`onAction`,8,void 0);wn(()=>q(o()),()=>{N(n,Bi(o()))}),Tn(),yi();var l=Gi(),u=P(l),d=F(u,!0),f=I(u),p=F(f,!0),m=I(f,2),h=e=>{var t=Wi();L(()=>Q(t,`href`,G(n))),Y(e,t)};Z(m,e=>{G(n)&&e(h)});var g=I(m,2),_=e=>{Ui(e,{get label(){return s()},variant:`secondary`,get onClick(){return c()}})};Z(g,e=>{s()&&c()&&e(_)}),E(l),L(()=>{ei(l,1,`banner ${r()??``}`),Q(l,`role`,r()===`error`?`alert`:`status`),X(d,i()),X(p,a())}),Y(e,l),k()}function qi(e){let t=String(e.question.defaultValue),n=t.split(`,`).map(e=>e.trim()).filter(Boolean);return{value:t,selected:e.question.kind===`multi-select`?(e.question.choices??[]).filter(e=>e!==`All`&&n.includes(e.split(` — `)[0])):e.question.kind===`scope-overrides`?n:[]}}function Ji(e,t){return t===`All`?e.includes(`All`)?[]:[`All`]:e.includes(t)?e.filter(e=>e!==t):[...e.filter(e=>e!==`All`),t]}function Yi(e,t,n){return e.question.kind===`scope-overrides`||e.question.kind===`multi-select`?n.length?n.join(`,`):`none`:t}var Xi=J(`PERMISSION PREVIEW`),Zi=J(`
                            `),Qi=J(``),$i=J(``),ea=J(``),ta=J(`
                            `),na=J(``),ra=J(`

                            `,1);function ia(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`controller`,8),i=$(t,`busy`,8),a=$(t,`onSubmit`,8),o=qi(n()),s=M(o.value),c=M(o.selected);yi();var l=ra(),u=sn(l),d=P(u),f=F(d,!0),p=I(d),m=e=>{Y(e,Xi())};Z(p,e=>{q(n()),K(()=>n().phase===`permission-intent`)&&e(m)}),E(u);var h=I(u,2),g=e=>{var t=Zi(),i=P(t);let a;var o=I(i);let c;E(t),L(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),Q(i,`aria-pressed`,G(s)===`yes`||G(s)===`true`),i.disabled=!r(),a=ei(i,1,``,null,a,{selected:G(s)===`yes`||G(s)===`true`}),Q(o,`aria-pressed`,G(s)===`no`||G(s)===`false`),o.disabled=!r(),c=ei(o,1,``,null,c,{selected:G(s)===`no`||G(s)===`false`})}),mr(`click`,i,()=>N(s,`yes`)),mr(`click`,o,()=>N(s,`no`)),Y(e,t)},_=e=>{var t=$i();Hr(t,5,()=>(q(n()),K(()=>n().question.choices??[])),Rr,(e,t)=>{var n=Qi(),r=F(n,!0),i={};L(()=>{X(r,G(t)),i!==(i=G(t))&&(n.value=(n.__value=i)??``)}),Y(e,n)}),E(t),ii(t),L(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),t.disabled=!r()}),ai(t,()=>G(s),e=>N(s,e)),Y(e,t)},v=e=>{var t=ta();Hr(t,5,()=>(q(n()),K(()=>n().question.kind===`multi-select`?n().question.choices??[]:n().question.allowedNames??[])),Rr,(e,t)=>{var n=ea(),i=P(n);di(i);var a=F(I(i),!0);E(n),L(e=>{fi(i,e),i.disabled=!r(),X(a,G(t))},[()=>(G(c),G(t),K(()=>G(c).includes(G(t))))]),mr(`change`,i,()=>N(c,Ji(G(c),G(t)))),Y(e,n)}),E(t),L(()=>Q(t,`aria-label`,(q(n()),K(()=>n().question.label)))),Y(e,t)},y=e=>{var t=na();di(t),L(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),Q(t,`type`,(q(n()),K(()=>n().question.kind===`number`?`number`:`text`))),Q(t,`min`,(q(n()),K(()=>n().question.kind===`number`?0:void 0))),t.disabled=!r()}),gi(t,()=>G(s),e=>N(s,e)),Y(e,t)};Z(h,e=>{q(n()),K(()=>n().question.kind===`boolean`)?e(g):(q(n()),K(()=>n().question.kind===`choice`)?e(_,1):(q(n()),K(()=>n().question.kind===`multi-select`||n().question.kind===`scope-overrides`)?e(v,2):e(y,-1)))});var b=I(h,2),x=F(b),S=I(b,2);{let e=mt(()=>!r()||i());Ui(S,{label:`Continue`,arrow:!0,onClick:()=>a()(Yi(n(),G(s),G(c))),get disabled(){return G(e)}})}L((e,t)=>{X(f,e),X(x,`Suggested answer: ${t??``} · You can review choices again before creating your setup PAT.`)},[()=>(q(n()),K(()=>n().question.label.replace(` (Space toggles, Enter confirms)`,``))),()=>(q(n()),K(()=>String(n().question.defaultValue)||`none`))]),Y(e,l),k()}hr([`click`,`change`]);var aa=J(``),oa=J(`
                            `);function sa(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`controller`,8),i=$(t,`busy`,8),a=$(t,`onSubmit`,8);yi();var o=oa();Hr(o,5,()=>(q(n()),K(()=>n().choices)),Rr,(e,t)=>{var n=aa(),o=F(P(n),!0);ke(),E(n),L(()=>{n.disabled=!r()||i(),X(o,G(t))}),mr(`click`,n,()=>a()(G(t))),Y(e,n)}),E(o),Y(e,o),k()}hr([`click`]);var ca=J(`Open the official GitHub PAT form

                            Check the signed-in account, select Only select repositories, then select this repository. GitHub handles 2FA and creates the PAT.

                            `,1),la=J(`

                            Sent only to this local process. It will not be shown again or saved in browser storage.

                            `),ua=J(` `,1);function da(e,t){O(t,!1);let n=M(),r=$(t,`prompt`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8),s=M(``);function c(){let e=G(s);r().kind===`secret`&&N(s,``),o()(e)}wn(()=>q(r()),()=>{N(n,Bi(r().link))}),Tn(),yi();var l=ua(),u=sn(l),d=e=>{var t=ca(),r=sn(t);ke(),L(()=>Q(r,`href`,G(n))),Y(e,t)};Z(u,e=>{G(n)&&e(d)});var f=I(u,2),p=F(f,!0),m=I(f,2);di(m);var h=I(m,2),g=e=>{Y(e,la())};Z(h,e=>{q(r()),K(()=>r().kind===`secret`)&&e(g)});var _=I(h,2);{let e=mt(()=>(q(i()),q(a()),q(r()),G(s),K(()=>!i()||a()||!r().optional&&!G(s).trim())));Ui(_,{label:`Continue`,arrow:!0,onClick:c,get disabled(){return G(e)}})}L(()=>{X(p,(q(r()),K(()=>r().kind===`secret`?`Paste the value here`:`Your answer`))),Q(m,`type`,(q(r()),K(()=>r().kind===`secret`?`password`:`text`))),m.disabled=!i(),Q(m,`placeholder`,(q(r()),K(()=>r().kind===`secret`?`Hidden after submission`:`Type your answer`)))}),gi(m,()=>G(s),e=>N(s,e)),Y(e,l),k()}var fa=J(`
                          • `),pa=J(`

                              `),ma=J(`

                              Before you continue

                                `),ha=J(`

                                Review exactly what this run may change. The bot PAT and any additional credentials are collected next.

                                `,1);function ga(e,t){O(t,!1);let n=M(),r=$(t,`prompt`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8);wn(()=>q(r()),()=>{N(n,[{title:`Files`,items:r().plan.files},{title:`Workflows`,items:r().plan.workflows},{title:`Variables`,items:r().plan.variables},{title:`Secret names`,items:r().plan.secrets}])}),Tn(),yi();var s=ha(),c=I(sn(s),2);Hr(c,5,()=>G(n),Rr,(e,t)=>{var n=pa(),r=P(n),i=P(r),a=F(I(i),!0);E(r);var o=I(r);Hr(o,5,()=>(G(t),K(()=>G(t).items)),Rr,(e,t)=>{var n=fa(),r=F(n,!0);L(()=>X(r,G(t))),Y(e,n)}),E(o),E(n),L(()=>{X(i,`${G(t),K(()=>G(t).title)??``} `),X(a,(G(t),K(()=>G(t).items.length)))}),Y(e,n)}),E(c);var l=I(c,2),u=e=>{var t=ma(),n=I(P(t));Hr(n,5,()=>(q(r()),K(()=>r().plan.warnings)),Rr,(e,t)=>{var n=fa(),r=F(n,!0);L(()=>X(r,G(t))),Y(e,n)}),E(n),E(t),Y(e,t)};Z(l,e=>{q(r()),K(()=>r().plan.warnings.length)&&e(u)});var d=I(l,2),f=P(d);{let e=mt(()=>!i()||a());Ui(f,{label:`Stop here`,variant:`secondary`,onClick:()=>o()(`decline`),get disabled(){return G(e)}})}var p=I(f);{let e=mt(()=>!i()||a());Ui(p,{label:`Approve this plan`,arrow:!0,onClick:()=>o()(`approve`),get disabled(){return G(e)}})}E(d),Y(e,s),k()}var _a=J(`

                                `),va=J(`
                                CURRENT DECISION
                                `);function ya(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`revision`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8);yi();var s=va(),c=P(s),l=F(I(P(c)));E(c);var u=I(c,2),d=e=>{var t=_a(),r=F(t,!0);L(()=>X(r,(q(n()),K(()=>n().description)))),Y(e,t)};Z(u,e=>{q(n()),K(()=>`description`in n()&&n().description)&&e(d)});var f=I(u,2),p=e=>{ia(e,{get prompt(){return n()},get controller(){return i()},get busy(){return a()},get onSubmit(){return o()}})},m=e=>{sa(e,{get prompt(){return n()},get controller(){return i()},get busy(){return a()},get onSubmit(){return o()}})},h=e=>{da(e,{get prompt(){return n()},get controller(){return i()},get busy(){return a()},get onSubmit(){return o()}})},g=e=>{ga(e,{get prompt(){return n()},get controller(){return i()},get busy(){return a()},get onSubmit(){return o()}})};Z(f,e=>{q(n()),K(()=>n().kind===`question`)?e(p):(q(n()),K(()=>n().kind===`choice`||n().kind===`confirm`)?e(m,1):(q(n()),K(()=>n().kind===`text`||n().kind===`secret`)?e(h,2):(q(n()),K(()=>n().kind===`plan`)&&e(g,3))))}),E(s),L(()=>X(l,`SESSION ${r()??``}`)),Y(e,s),k()}var ba=J(` `),xa=J(`
                              • `),Sa=J(`

                                  Conditional grants depend on the choices and GitHub facts. A final audit runs before setup changes.
                                  `),Ca=J(`

                                  Permissions follow your choices

                                  We’ll show the exact grants before you create each PAT. Nothing is created just by opening this page.

                                  `),wa=J(``);function Ta(e,t){O(t,!1);let n=$(t,`view`,8);yi();var r=wa(),i=P(r),a=F(I(P(i),3),!0);E(i);var o=I(i,2),s=e=>{var t=Sa(),r=I(P(t)),i=F(r),a=I(r),o=F(a,!0),s=I(a,2);Hr(s,5,()=>(q(n()),K(()=>n().permissions.report?.checks??n().permissions.requirements??[])),Rr,(e,t)=>{var n=xa(),r=P(n),i=P(r),a=I(i),o=F(a),s=F(I(a),!0);E(r);var c=I(r),l=P(c,!0),u=I(l),d=e=>{var n=ba(),r=F(n,!0);L(()=>X(r,(G(t),K(()=>G(t).status)))),Y(e,n)};Z(u,e=>{`status`in G(t)&&e(d)}),E(c),E(n),L(()=>{X(i,`${G(t),K(()=>G(t).permission)??``} `),X(o,`${G(t),K(()=>G(t).scope)??``} · ${G(t),K(()=>G(t).applicability)??``}`),X(s,(G(t),K(()=>G(t).reason))),X(l,(G(t),K(()=>G(t).level)))}),Y(e,n)}),E(s),ke(2),E(t),L(()=>{X(i,`${q(n()),K(()=>n().permissions.role===`setup`?`Setup PAT`:`Bot PAT`)??``} access`),X(o,(q(n()),K(()=>n().permissions.report?`Read-only access check`:`Provisional least-privilege grants`)))}),Y(e,t)},c=e=>{Y(e,Ca())};Z(o,e=>{q(n()),K(()=>n().permissions)?e(s):e(c,-1)}),E(r),L(()=>X(a,(q(n()),K(()=>n().repository)))),Y(e,r),k()}var Ea=J(`

                                  `);function Da(e,t){O(t,!1);let n=M(),r=M(),i=$(t,`outcome`,8),a=$(t,`controller`,8),o=$(t,`onClose`,8);wn(()=>q(i()),()=>{N(n,i()===`complete`?`Your configuration was applied`:i()===`dry-run`?`No changes were made`:i()===`cancelled`||i()===`blocked`?`No setup changes started`:`Check partial changes before retrying`)}),wn(()=>q(i()),()=>{N(r,i()===`complete`?`The temporary setup PAT is not revoked automatically. Delete it in GitHub after confirming your setup. Keep the bot PAT until you rotate the installed Secret.`:i()===`partial`?`A bot Secret write or another change may have succeeded. Inspect GitHub and run copilot doctor before replacing or deleting its PAT.`:`Copilot did not begin applying setup changes. PATs you created in GitHub still exist until you delete them there.`)}),Tn();var s=Ea(),c=P(s),l=F(c,!0),u=I(c),d=F(u,!0),f=I(u),p=F(f,!0),m=I(f,2),h=e=>{Ui(e,{label:`Close local session`,get onClick(){return o()}})};Z(m,e=>{a()&&e(h)}),E(s),L(()=>{X(l,i()===`complete`?`✓`:`!`),X(d,G(n)),X(p,G(r))}),Y(e,s),k()}var Oa=J(`

                                  Working on the next step

                                  The local process is checking your answers and preparing the next decision. Keep this page open.

                                  `);function ka(e){Y(e,Oa())}var Aa=J(``),ja=J(`
                                  `),Ma=J(``),Na=J(`
                                  `,1),Pa=J(`
                                  LOCALHOST ONLY NO CLOUD SETUP ACCOUNT GITHUB OWNS PAT ISSUANCE
                                  `);function Fa(e,t){O(t,!1);let n=()=>Di(a,`$session`,r),[r,i]=Oi(),a=Ai();Pr(()=>{a.connect();let e=window.setInterval(()=>{n().view?.outcome||a.refresh()},900);return()=>window.clearInterval(e)});async function o(){window.confirm(`Cancel this local setup session? PATs already created in GitHub will still exist.`)&&await a.cancel()}async function s(e){let t=n().view?.promptRevision;t!==void 0&&await a.submit(t,e)}yi();var c=Pa();Jr(`16t12jp`,e=>{Y(e,Aa())});var l=P(c);{let e=mt(()=>n().view?.journey);Ni(l,{get journey(){return G(e)}})}var u=I(l,2),d=P(u);{let e=mt(()=>n().view?.repository);Li(d,{get repository(){return G(e)}})}var f=I(d,2),p=P(f);zi(p,{get view(){return n().view}});var m=I(p,2),h=e=>{var t=ja(),r=I(P(t));E(t),L(()=>X(r,` Reviewing saved choices — pass ${n().view.journey.choiceReviewPass??``}. This is the same setup run, not a restart.`)),Y(e,t)};Z(m,e=>{n().view?.journey?.choiceReviewPass&&n().view.journey.choiceReviewPass>1&&!n().view.outcome&&e(h)});var g=I(m,2),_=e=>{Ki(e,{tone:`warning`,title:`Read-only tab`,message:`Another tab controls this session. You can review progress here or explicitly take over.`,actionLabel:`Take control in this tab`,get onAction(){return a.takeOver}})};Z(g,e=>{!n().controller&&n().view&&e(_)});var v=I(g,2),y=e=>{Ki(e,{tone:`error`,title:`Needs attention`,get message(){return n().error}})};Z(v,e=>{n().error&&e(y)});var b=I(v,2),x=e=>{{let t=mt(()=>n().view.message.tone===`success`?`Checked`:n().view.message.tone===`warning`?`Please note`:n().view.message.tone===`error`?`Needs attention`:`Progress update`);Ki(e,{get tone(){return n().view.message.tone},get title(){return G(t)},get message(){return n().view.message.text},get link(){return n().view.message.link}})}};Z(b,e=>{n().view?.message&&e(x)});var S=I(b,2),ee=e=>{Da(e,{get outcome(){return n().view.outcome},get controller(){return n().controller},get onClose(){return a.close}})},te=e=>{var t=Na(),r=sn(t),i=P(r);Lr(i,()=>n().view.promptRevision,e=>{ya(e,{get prompt(){return n().view.prompt},get revision(){return n().view.revision},get controller(){return n().controller},get busy(){return n().busy},onSubmit:s})}),Ta(I(i,2),{get view(){return n().view}}),E(r);var a=I(r,2),c=e=>{var t=Ma();L(()=>t.disabled=n().busy),mr(`click`,t,o),Y(e,t)};Z(a,e=>{n().controller&&n().view.journey?.current!==`Apply`&&e(c)}),Y(e,t)},ne=e=>{ka(e,{})};Z(S,e=>{n().view?.outcome?e(ee):n().view?.prompt?e(te,1):e(ne,-1)}),ke(2),E(f),E(u),E(c),Y(e,c),k(),i()}hr([`click`]),kr(Fa,{target:document.getElementById(`app`)}); \ No newline at end of file diff --git a/build/web/index.html b/build/web/index.html index 21b2410cc..b88150bc2 100644 --- a/build/web/index.html +++ b/build/web/index.html @@ -5,7 +5,7 @@ Copilot · Setup studio - + diff --git a/docs/security-operations/operations/troubleshooting.mdx b/docs/security-operations/operations/troubleshooting.mdx index 383bcd0bf..a47f4d97e 100644 --- a/docs/security-operations/operations/troubleshooting.mdx +++ b/docs/security-operations/operations/troubleshooting.mdx @@ -33,6 +33,13 @@ If the page closes during Apply, inspect the terminal result and run `copilot doctor` before retrying; do not assume that completed local or remote writes were rolled back. No local session shutdown revokes a PAT in GitHub. +If setup reports a lock belonging to a stopped process, it will not delete +that lock automatically: another setup could have acquired the same path in +the meantime. Verify that no setup process for this checkout is running, +then remove **only the exact lock file path printed in the error** and retry. +Never remove a lock for an active process or clear the entire temporary +directory. The lock contains no PAT or other credential. + **Setup cancellation:** `Ctrl-C` or end-of-input intentionally exits 130 and diff --git a/specs/local-web-setup-assistant.md b/specs/local-web-setup-assistant.md index 098caa5bd..b334c308b 100644 --- a/specs/local-web-setup-assistant.md +++ b/specs/local-web-setup-assistant.md @@ -209,8 +209,11 @@ exactly what completed and what remains. ambiguous/missing remotes block rather than guessing. Acquire a per-checkout local setup-session guard shared by terminal and web modes so a second setup process cannot apply to the same checkout concurrently. A lock has - no credentials and is released on normal exit; an orphaned lock requires - verified dead-owner recovery. Web + no credentials and is released on normal exit. If the recorded owner is + dead, the CLI MUST fail closed, print the exact lock path, and require an + operator to verify no setup process is running before removing that one + file manually. It MUST NOT unlink a stale lock automatically: another + process can replace it between a read and an unlink. Web mode does not require a TTY: the browser is the interactive surface, and a printed local URL is available if automatic opening is unavailable. 2. Bind `127.0.0.1:0`, record the assigned port, create an unpredictable @@ -608,6 +611,7 @@ architecture guide documents these boundaries for future steps. | Bind/assets/packaged path failure | web mode never starts | none | run terminal setup; report installation problem | | Unsupported browser/JS disabled | no secret submitted; no mutation | none | terminal setup; no partial web fallback | | Second setup process/tab | only one checkout operation/controller; previous state intact | none | stop first process or explicitly take over tab | +| Orphaned local setup lock | no automatic unlink, no setup mutation | none | verify no setup process is running; remove only the printed lock path and retry | | Tab closes, laptop sleeps, session idles | live session may remain until 30-minute idle cap; no implied cancellation | no mutation replay | reopen while live; otherwise restart and clean up GitHub PATs | | Git remote/ref/file/config changes mid-session | reviewed plan is stale; no Apply | re-read once on request | review new plan or restart to adopt changed config | | Wrong GitHub account or wrong repository | PAT audit fails; no dependent mutation | no automatic PAT creation | switch account/select repository and generate/correct PAT; delete unused one | @@ -709,7 +713,7 @@ existing CLI tests are retained, not re-counted as new web evidence. | Area | Minimum cases | Risk covered | |---|---:|---| | Pure choices/config/grant/plan projection | 14 | source locks, defaults, conditional questions, two PAT roles, grants, revision invalidation | -| Session/use cases/idempotency/races | 20 | stages, saved-review pass, tab takeover, stale events, single-flight Apply, cancel, idle/crash replay boundaries | +| Session/use cases/idempotency/races | 20 | stages, saved-review pass, tab takeover, stale events, single-flight Apply, cancel, idle/crash replay boundaries, replacement-lock race | | GitHub/workspace/HTTP adapters | 10 | identity, missing/unknown grants, org approval, Secret scope, bounded errors and provider mapping | | CLI/packaging/workflow contracts | 10 | flag combinations, browser-open fallback, asset manifest, npm pack/global install, unchanged Action/API bundles | | UI/accessibility/localization/content | 18 | pending/action/blocked/partial/complete, plan diff, narrow/zoom/keyboard/focus/no-color, both palettes/system toggle and contrast, English fallback, escaping | diff --git a/src/cli/__tests__/setup_session_guard.test.ts b/src/cli/__tests__/setup_session_guard.test.ts index c7e57d8c6..4e9c72e75 100644 --- a/src/cli/__tests__/setup_session_guard.test.ts +++ b/src/cli/__tests__/setup_session_guard.test.ts @@ -31,14 +31,29 @@ describe('setup session guard', () => { releaseThird(); }); - test('recovers a verified dead owner without reusing its nonce', () => { - writeFileSync(lockPath(), JSON.stringify({ pid: 99999999, nonce: 'old-owner', repository: realpathSync(root) })); + test('fails closed on a verified dead owner until the operator removes its exact lock', () => { + const oldRecord = JSON.stringify({ pid: 99999999, nonce: 'old-owner', repository: realpathSync(root) }); + writeFileSync(lockPath(), oldRecord); + expect(() => acquireSetupSessionGuard(root)).toThrow(`remove only that file manually`); + expect(readFileSync(lockPath(), 'utf8')).toBe(oldRecord); + unlinkSync(lockPath()); // Simulates explicit operator recovery after verifying no setup is running. const release = acquireSetupSessionGuard(root); - const current = JSON.parse(readFileSync(lockPath(), 'utf8')) as { pid: number; nonce: string }; - expect(current.pid).toBe(process.pid); - expect(current.nonce).not.toBe('old-owner'); + expect(JSON.parse(readFileSync(lockPath(), 'utf8')).pid).toBe(process.pid); release(); - expect(existsSync(lockPath())).toBe(false); + }); + + test('a replacement lock is never unlinked after a stale-owner probe', () => { + writeFileSync(lockPath(), JSON.stringify({ pid: 99999999, nonce: 'old-owner', repository: realpathSync(root) })); + const newRecord = JSON.stringify({ pid: process.pid, nonce: 'new-owner', repository: realpathSync(root) }); + const probe = jest.spyOn(process, 'kill').mockImplementationOnce(() => { + unlinkSync(lockPath()); + writeFileSync(lockPath(), newRecord); // Another process won the race after our read. + throw Object.assign(new Error('No such process'), { code: 'ESRCH' }); + }); + try { + expect(() => acquireSetupSessionGuard(root)).toThrow('remove only that file manually'); + expect(readFileSync(lockPath(), 'utf8')).toBe(newRecord); + } finally { probe.mockRestore(); } }); test.each([ diff --git a/src/cli/__tests__/web_setup_browser_session.test.ts b/src/cli/__tests__/web_setup_browser_session.test.ts index 1c55eb02e..dbebc9dd0 100644 --- a/src/cli/__tests__/web_setup_browser_session.test.ts +++ b/src/cli/__tests__/web_setup_browser_session.test.ts @@ -135,6 +135,119 @@ describe('browser session transport', () => { expect(JSON.parse(latest)).toMatchObject({ controller: true, busy: false, view }); }); + test('successful cancellation shows the server result and clears the busy indicator', async () => { + const cancelled: WebSetupView = { revision: 4, repository: 'owner/repo', outcome: 'cancelled' }; + let stateReads = 0; + globalThis.fetch = jest.fn(async (path: string) => { + if (path === '/api/bootstrap') return response({ controller: true, capability: 'controller', takeoverTicket: 'ticket' }); + if (path === '/api/state') return response(++stateReads === 1 ? view : cancelled); + if (path === '/api/cancel') return response({ cancelled: true }); + throw new Error('Unexpected route'); + }) as typeof fetch; + + const session = createSetupSession(); + let latest: { busy: boolean; view?: WebSetupView } | undefined; + session.subscribe(state => { latest = state; }); + await session.connect(); + await session.cancel(); + expect(latest).toMatchObject({ busy: false, view: cancelled }); + }); + + test('a server rejection without a message gives a bounded generic error', async () => { + globalThis.fetch = jest.fn(async (path: string) => { + if (path === '/api/bootstrap') return response({ controller: true, capability: 'controller', takeoverTicket: 'ticket' }); + if (path === '/api/state') return response(view); + if (path === '/api/answer') return response({}, 409); + throw new Error('Unexpected route'); + }) as typeof fetch; + + const session = createSetupSession(); + let latest = ''; + session.subscribe(state => { latest = JSON.stringify(state); }); + await session.connect(); + await session.submit(7, 'fake-secret'); + expect(latest).toContain('The request was rejected.'); + expect(latest).not.toContain('fake-secret'); + }); + + test('non-Error transport failures still give safe submission and cancellation messages', async () => { + globalThis.fetch = jest.fn(async (path: string) => { + if (path === '/api/bootstrap') return response({ controller: true, capability: 'controller', takeoverTicket: 'ticket' }); + if (path === '/api/state') return response(view); + if (path === '/api/answer' || path === '/api/cancel') throw 'transport unavailable'; + throw new Error('Unexpected route'); + }) as typeof fetch; + + const session = createSetupSession(); + let latest = ''; + session.subscribe(state => { latest = JSON.stringify(state); }); + await session.connect(); + await session.submit(7, 'fake-secret'); + expect(latest).toContain('Could not submit this answer.'); + expect(latest).not.toContain('fake-secret'); + await session.cancel(); + expect(latest).toContain('Cancellation failed.'); + }); + + test('an unexpected takeover transport failure rechecks who controls the session', async () => { + let bootstrapReads = 0; + globalThis.fetch = jest.fn(async (path: string) => { + if (path === '/api/bootstrap') { bootstrapReads += 1; return response({ controller: false, takeoverTicket: 'ticket' }); } + if (path === '/api/state') return response(view); + if (path === '/api/takeover') throw 'transport unavailable'; + throw new Error('Unexpected route'); + }) as typeof fetch; + + const session = createSetupSession(); + let controller = true; + session.subscribe(state => { controller = state.controller; }); + await session.connect(); + await session.takeOver(); + expect(bootstrapReads).toBe(2); + expect(controller).toBe(false); + }); + + test('a busy submission cannot send a second answer or cancel concurrently', async () => { + let resolveAnswer: ((value: Response) => void) | undefined; + const pendingAnswer = new Promise(resolve => { resolveAnswer = resolve; }); + const requests: string[] = []; + globalThis.fetch = jest.fn(async (path: string) => { + requests.push(path); + if (path === '/api/bootstrap') return response({ controller: true, capability: 'controller', takeoverTicket: 'ticket' }); + if (path === '/api/state') return response(view); + if (path === '/api/answer') return pendingAnswer; + throw new Error('Unexpected route'); + }) as typeof fetch; + + const session = createSetupSession(); + await session.connect(); + const first = session.submit(7, 'first'); + await session.submit(7, 'duplicate'); + await session.cancel(); + expect(requests.filter(path => path === '/api/answer')).toHaveLength(1); + expect(requests).not.toContain('/api/cancel'); + resolveAnswer!(response({ accepted: true })); + await first; + }); + + test('overlapping refresh calls do not race to replace the current view', async () => { + let resolveState: ((value: Response) => void) | undefined; + const pendingState = new Promise(resolve => { resolveState = resolve; }); + const requests: string[] = []; + globalThis.fetch = jest.fn(async (path: string) => { + requests.push(path); + if (path === '/api/state') return pendingState; + throw new Error('Unexpected route'); + }) as typeof fetch; + + const session = createSetupSession(); + const first = session.refresh(); + await session.refresh(); + expect(requests).toEqual(['/api/state']); + resolveState!(response(view)); + await first; + }); + test('a failed takeover re-reads the current controller instead of retaining authority', async () => { const requests: string[] = []; globalThis.fetch = jest.fn(async (path: string) => { diff --git a/src/cli/__tests__/web_setup_ui_helpers.test.ts b/src/cli/__tests__/web_setup_ui_helpers.test.ts index 8b46febfc..f960b6de9 100644 --- a/src/cli/__tests__/web_setup_ui_helpers.test.ts +++ b/src/cli/__tests__/web_setup_ui_helpers.test.ts @@ -16,6 +16,15 @@ describe('web setup presentation helpers', () => { }); }); + test('a multi-select without choices starts empty and never invents an option', () => { + const prompt = question('multi-select', 'one'); + expect(initialQuestionAnswer({ ...prompt, question: { ...prompt.question, choices: undefined } }).selected).toEqual([]); + }); + + test('a plain text question retains its default without a selection', () => { + expect(initialQuestionAnswer(question('text', 'hello'))).toEqual({ value: 'hello', selected: [] }); + }); + test('scope overrides preserve explicit names and serialize an empty set as none', () => { const prompt = question('scope-overrides', 'one,two'); expect(initialQuestionAnswer(prompt).selected).toEqual(['one', 'two']); @@ -37,6 +46,7 @@ describe('web setup presentation helpers', () => { }); test.each([ + [undefined, false], ['https://github.com/settings/personal-access-tokens/new?name=Setup', true], ['https://github.com/settings/personal-access-tokens', true], ['https://evil.example/settings/personal-access-tokens', false], diff --git a/src/cli/setup_session_guard.ts b/src/cli/setup_session_guard.ts index 501878595..6702acb45 100644 --- a/src/cli/setup_session_guard.ts +++ b/src/cli/setup_session_guard.ts @@ -1,6 +1,6 @@ import { createHash, randomBytes } from 'node:crypto'; import { execFileSync } from 'node:child_process'; -import { closeSync, existsSync, openSync, readFileSync, realpathSync, unlinkSync, writeFileSync } from 'node:fs'; +import { closeSync, openSync, readFileSync, realpathSync, unlinkSync, writeFileSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; @@ -13,35 +13,33 @@ export function acquireSetupSessionGuard(cwd: string): () => void { const hash = createHash('sha256').update(repository).digest('hex').slice(0, 32); const lockPath = join(tmpdir(), `copilot-setup-${hash}.lock`); const record: GuardRecord = { pid: process.pid, nonce: randomBytes(16).toString('hex'), repository }; - for (let attempt = 0; attempt < 2; attempt += 1) { - try { - const fd = openSync(lockPath, 'wx', 0o600); - try { writeFileSync(fd, JSON.stringify(record)); } finally { closeSync(fd); } - return () => { - try { - const current = JSON.parse(readFileSync(lockPath, 'utf8')) as GuardRecord; - if (current.pid === record.pid && current.nonce === record.nonce && current.repository === record.repository) unlinkSync(lockPath); - } catch { /* Missing or replaced lock is not ours to remove. */ } - }; - } catch (cause) { - if (!cause || typeof cause !== 'object' || !('code' in cause) || cause.code !== 'EEXIST') throw cause; - let existing: GuardRecord; - try { existing = JSON.parse(readFileSync(lockPath, 'utf8')) as GuardRecord; } - catch { throw new Error('A setup lock exists but cannot be verified. Inspect it before retrying.'); } - if (!Number.isSafeInteger(existing.pid) || existing.pid <= 0 || existing.repository !== repository || !existing.nonce) { - throw setupLockError('A setup lock has unexpected contents. Inspect it before retrying.', cause); - } + try { + const fd = openSync(lockPath, 'wx', 0o600); + try { writeFileSync(fd, JSON.stringify(record)); } finally { closeSync(fd); } + return () => { try { - process.kill(existing.pid, 0); - throw setupLockError(`Another setup process (${existing.pid}) is active for this checkout. Finish or stop it before starting a second setup.`, cause); - } catch (checkError) { - if (!checkError || typeof checkError !== 'object' || !('code' in checkError) || checkError.code !== 'ESRCH') throw checkError; - } - // Recover only a verified dead owner and only if the lock has not changed meanwhile. - if (existsSync(lockPath) && readFileSync(lockPath, 'utf8') === JSON.stringify(existing)) unlinkSync(lockPath); + const current = JSON.parse(readFileSync(lockPath, 'utf8')) as GuardRecord; + if (current.pid === record.pid && current.nonce === record.nonce && current.repository === record.repository) unlinkSync(lockPath); + } catch { /* Missing or replaced lock is not ours to remove. */ } + }; + } catch (cause) { + if (!cause || typeof cause !== 'object' || !('code' in cause) || cause.code !== 'EEXIST') throw cause; + let existing: GuardRecord; + try { existing = JSON.parse(readFileSync(lockPath, 'utf8')) as GuardRecord; } + catch { throw new Error('A setup lock exists but cannot be verified. Inspect it before retrying.'); } + if (!Number.isSafeInteger(existing.pid) || existing.pid <= 0 || existing.repository !== repository || !existing.nonce) { + throw setupLockError('A setup lock has unexpected contents. Inspect it before retrying.', cause); + } + try { + process.kill(existing.pid, 0); + throw setupLockError(`Another setup process (${existing.pid}) is active for this checkout. Finish or stop it before starting a second setup.`, cause); + } catch (checkError) { + if (!checkError || typeof checkError !== 'object' || !('code' in checkError) || checkError.code !== 'ESRCH') throw checkError; } + // Filesystem reads and unlink are not atomic. Never remove a dead owner's lock here: + // another setup process may already have replaced it after our read. + throw setupLockError(`A setup lock for a stopped process (${existing.pid}) remains at ${lockPath}. Verify no setup is running, remove only that file manually, then retry.`, cause); } - throw new Error('Could not acquire the local setup lock.'); } function setupLockError(message: string, cause: unknown): Error { diff --git a/web/src/lib/questionAnswer.ts b/web/src/lib/questionAnswer.ts index 8c196decf..157ed302b 100644 --- a/web/src/lib/questionAnswer.ts +++ b/web/src/lib/questionAnswer.ts @@ -3,7 +3,7 @@ import type { WebSetupPrompt } from '../../../src/application/contracts/web_setu type QuestionPrompt = Extract; export function initialQuestionAnswer(prompt: QuestionPrompt): { value: string; selected: string[] } { - const value = String(prompt.question.defaultValue ?? ''); + const value = String(prompt.question.defaultValue); const defaults = value.split(',').map(item => item.trim()).filter(Boolean); const selected = prompt.question.kind === 'multi-select' ? (prompt.question.choices ?? []).filter(item => item !== 'All' && defaults.includes(item.split(' — ')[0])) From d1d85d8ff72cd2e4a0eb43aa8185008a9ad7f7e3 Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Mon, 28 Sep 2026 21:02:48 +0200 Subject: [PATCH 17/50] codex-setup-temporary-github-auth: protect local session access and atomic lock publication --- build/cli/index.js | 85 +++++++++++++------ build/web/assets/index-BJEnmD61.js | 2 - build/web/assets/index-DzCTZuhQ.js | 2 + build/web/index.html | 2 +- docs/how-to-use.mdx | 4 + .../operations/troubleshooting.mdx | 3 + specs/local-web-setup-assistant.md | 59 ++++++++----- src/__tests__/cli.test.ts | 6 +- src/cli/__tests__/setup_session_guard.test.ts | 17 +++- .../web_setup_browser_session.test.ts | 13 ++- src/cli/__tests__/web_setup_server.test.ts | 76 ++++++++++++++++- src/cli/commands/setup.ts | 4 +- src/cli/setup_session_guard.ts | 50 +++++++---- src/cli/web_setup_server.ts | 16 +++- web/src/session/setupSession.ts | 15 +++- 15 files changed, 276 insertions(+), 78 deletions(-) delete mode 100644 build/web/assets/index-BJEnmD61.js create mode 100644 build/web/assets/index-DzCTZuhQ.js diff --git a/build/cli/index.js b/build/cli/index.js index 0165533e7..cfdb2c7ec 100755 --- a/build/cli/index.js +++ b/build/cli/index.js @@ -65940,9 +65940,9 @@ function registerSetupCommand(program) { webBridge.setRepository(`${gitInfo.owner}/${gitInfo.repo}`); webBridge.setJourney((0, setup_journey_policy_1.buildSetupJourneyView)(`${gitInfo.owner}/${gitInfo.repo}`, 'repository', false)); webServer = await (0, web_setup_server_1.startWebSetupServer)(webBridge); - (0, logger_1.logInfo)(`🌐 Local setup assistant: ${webServer.url}`); + (0, logger_1.logInfo)(`🌐 Private local setup assistant: ${webServer.launchUrl}`, false, undefined, true); (0, logger_1.logInfo)('If the browser does not open, copy this URL into a browser on this computer. The terminal setup remains available with copilot setup.'); - (0, web_setup_server_1.openWebSetupBrowser)(webServer.url); + (0, web_setup_server_1.openWebSetupBrowser)(webServer.launchUrl); } if (!options.nonInteractive) { journey = new setup_journey_use_case_1.SetupJourneyUseCase(`${gitInfo.owner}/${gitInfo.repo}`, webBridge ? new web_setup_adapters_1.WebSetupJourneyPresenter(webBridge) : new setup_journey_presenter_1.ConsoleSetupJourneyPresenter()); @@ -67602,26 +67602,26 @@ function acquireSetupSessionGuard(cwd) { const hash = (0, node_crypto_1.createHash)('sha256').update(repository).digest('hex').slice(0, 32); const lockPath = (0, node_path_1.join)((0, node_os_1.tmpdir)(), `copilot-setup-${hash}.lock`); const record = { pid: process.pid, nonce: (0, node_crypto_1.randomBytes)(16).toString('hex'), repository }; + const stagedPath = `${lockPath}.${record.nonce}.tmp`; + writeStagedLock(stagedPath, record); + let published = false; + let collision; try { - const fd = (0, node_fs_1.openSync)(lockPath, 'wx', 0o600); + (0, node_fs_1.linkSync)(stagedPath, lockPath); // Atomic publication of a fully written record. + published = true; + } + catch (cause) { + collision = cause; + } + finally { try { - (0, node_fs_1.writeFileSync)(fd, JSON.stringify(record)); - } - finally { - (0, node_fs_1.closeSync)(fd); + (0, node_fs_1.unlinkSync)(stagedPath); } - return () => { - try { - const current = JSON.parse((0, node_fs_1.readFileSync)(lockPath, 'utf8')); - if (current.pid === record.pid && current.nonce === record.nonce && current.repository === record.repository) - (0, node_fs_1.unlinkSync)(lockPath); - } - catch { /* Missing or replaced lock is not ours to remove. */ } - }; + catch { /* A staging-file cleanup failure does not invalidate the published lock. */ } } - catch (cause) { - if (!cause || typeof cause !== 'object' || !('code' in cause) || cause.code !== 'EEXIST') - throw cause; + if (!published) { + if (!collision || typeof collision !== 'object' || !('code' in collision) || collision.code !== 'EEXIST') + throw collision; let existing; try { existing = JSON.parse((0, node_fs_1.readFileSync)(lockPath, 'utf8')); @@ -67630,19 +67630,44 @@ function acquireSetupSessionGuard(cwd) { throw new Error('A setup lock exists but cannot be verified. Inspect it before retrying.'); } if (!Number.isSafeInteger(existing.pid) || existing.pid <= 0 || existing.repository !== repository || !existing.nonce) { - throw setupLockError('A setup lock has unexpected contents. Inspect it before retrying.', cause); + throw setupLockError('A setup lock has unexpected contents. Inspect it before retrying.', collision); } try { process.kill(existing.pid, 0); - throw setupLockError(`Another setup process (${existing.pid}) is active for this checkout. Finish or stop it before starting a second setup.`, cause); + throw setupLockError(`Another setup process (${existing.pid}) is active for this checkout. Finish or stop it before starting a second setup.`, collision); } catch (checkError) { if (!checkError || typeof checkError !== 'object' || !('code' in checkError) || checkError.code !== 'ESRCH') throw checkError; } - // Filesystem reads and unlink are not atomic. Never remove a dead owner's lock here: - // another setup process may already have replaced it after our read. - throw setupLockError(`A setup lock for a stopped process (${existing.pid}) remains at ${lockPath}. Verify no setup is running, remove only that file manually, then retry.`, cause); + // Filesystem reads and unlink are not atomic. Never remove a dead owner's lock here. + throw setupLockError(`A setup lock for a stopped process (${existing.pid}) remains at ${lockPath}. Verify no setup is running, remove only that file manually, then retry.`, collision); + } + return () => { + try { + const current = JSON.parse((0, node_fs_1.readFileSync)(lockPath, 'utf8')); + if (current.pid === record.pid && current.nonce === record.nonce && current.repository === record.repository) + (0, node_fs_1.unlinkSync)(lockPath); + } + catch { /* Missing or replaced lock is not ours to remove. */ } + }; +} +function writeStagedLock(path, record) { + const fd = (0, node_fs_1.openSync)(path, 'wx', 0o600); + try { + (0, node_fs_1.writeFileSync)(fd, JSON.stringify(record)); + (0, node_fs_1.closeSync)(fd); + } + catch (cause) { + try { + (0, node_fs_1.closeSync)(fd); + } + catch { /* Already closed or unavailable. */ } + try { + (0, node_fs_1.unlinkSync)(path); + } + catch { /* Preserve the write failure. */ } + throw cause; } } function setupLockError(message, cause) { @@ -68348,11 +68373,13 @@ const node_http_1 = __nccwpck_require__(88849); const promises_1 = __nccwpck_require__(93977); const node_path_1 = __nccwpck_require__(49411); const node_child_process_1 = __nccwpck_require__(17718); +const node_crypto_1 = __nccwpck_require__(6005); const MAX_BODY_BYTES = 8192; const MAX_ANSWER_LENGTH = 4096; const CSP = "default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' data:; connect-src 'self'; base-uri 'none'; form-action 'self'; frame-ancestors 'none'"; /** Transport only: setup policy and credential decisions live behind the bridge. */ async function startWebSetupServer(bridge, assets = (0, node_path_1.join)(__dirname, '..', 'web')) { + const sessionKey = (0, node_crypto_1.randomBytes)(32); const assetRoot = await (0, promises_1.realpath)(assets); if (!(await (0, promises_1.realpath)((0, node_path_1.join)(assetRoot, 'index.html'))).startsWith(`${assetRoot}${node_path_1.sep}`)) { throw new Error('Local setup index must be inside its packaged asset directory.'); @@ -68403,6 +68430,10 @@ async function startWebSetupServer(bridge, assets = (0, node_path_1.join)(__dirn respond(response, 403, { error: 'Invalid request origin.' }); return; } + if (request.url?.startsWith('/api/') && !authorizedSessionKey(request.headers['x-setup-session-key'], sessionKey)) { + respond(response, 403, { error: 'Open the private setup URL printed by the CLI.' }); + return; + } if (request.method === 'GET' && request.url === '/api/bootstrap') { respond(response, 200, bridge.bootstrap()); return; @@ -68523,6 +68554,7 @@ async function startWebSetupServer(bridge, assets = (0, node_path_1.join)(__dirn server.requestTimeout = 15000; server.headersTimeout = 15000; server.maxRequestsPerSocket = 250; + server.maxConnections = 16; await new Promise((resolveListen, reject) => { server.once('error', reject); server.listen(0, '127.0.0.1', () => { server.off('error', reject); resolveListen(); }); @@ -68531,6 +68563,7 @@ async function startWebSetupServer(bridge, assets = (0, node_path_1.join)(__dirn if (!address || typeof address === 'string') throw new Error('Unable to bind local setup server.'); const url = `http://127.0.0.1:${address.port}/`; + const launchUrl = `${url}#setup-key=${sessionKey.toString('hex')}`; const close = async () => { if (closing) return closed; @@ -68557,7 +68590,11 @@ async function startWebSetupServer(bridge, assets = (0, node_path_1.join)(__dirn if (view.outcome && !resultTimer) resultTimer = setTimeout(() => void close(), 10 * 60 * 1000); }); - return { url, closed, close }; + return { url, launchUrl, closed, close }; +} +function authorizedSessionKey(value, expected) { + return typeof value === 'string' && /^[a-f0-9]{64}$/.test(value) + && (0, node_crypto_1.timingSafeEqual)(Buffer.from(value, 'hex'), expected); } function respond(response, status, body) { response.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8' }); diff --git a/build/web/assets/index-BJEnmD61.js b/build/web/assets/index-BJEnmD61.js deleted file mode 100644 index 7d6d73071..000000000 --- a/build/web/assets/index-BJEnmD61.js +++ /dev/null @@ -1,2 +0,0 @@ -(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})(),typeof window<`u`&&((window.__svelte??={}).v??=new Set).add(`5`);var e=!1;function t(){e=!0}t();var n={},r=Symbol(`uninitialized`),i=`http://www.w3.org/1999/xhtml`,a=Array.isArray,o=Array.prototype.indexOf,s=Array.prototype.includes,c=Array.from,l=Object.defineProperty,u=Object.getOwnPropertyDescriptor,d=Object.getOwnPropertyDescriptors,f=Object.prototype,p=Array.prototype,m=Object.getPrototypeOf,h=Object.isExtensible,g=()=>{};function _(e){return e()}function v(e){for(var t=0;t{e=n,t=r}),resolve:e,reject:t}}var b=1024,x=2048,S=4096,ee=8192,te=16384,ne=32768,re=1<<25,ie=65536,ae=1<<18,oe=1<<19,se=1<<20,ce=1<<25,le=1<<21,ue=1<<22,de=1<<23,fe=Symbol(`$state`),pe=Symbol(`component`),me=Symbol(`legacy props`),he=Symbol(``),ge=Symbol(`attributes`),_e=Symbol(`class`),ve=Symbol(`style`),ye=Symbol(`text`),be=Symbol(`form reset`),xe=new class extends Error{name=`StaleReactionError`;message="The reaction that called `getAbortSignal()` was re-run or destroyed"},Se=!!globalThis.document?.contentType&&globalThis.document.contentType.includes(`xml`);function Ce(){console.warn(`https://svelte.dev/e/derived_inert`)}function we(e){console.warn(`https://svelte.dev/e/hydration_mismatch`)}function Te(){console.warn(`https://svelte.dev/e/select_multiple_invalid_value`)}function Ee(){console.warn(`https://svelte.dev/e/svelte_boundary_reset_noop`)}var C=!1;function De(e){C=e}var w;function T(e){if(e===null)throw we(),n;return w=e}function Oe(){return T(on(w))}function E(e){if(C){if(on(w)!==null)throw we(),n;w=e}}function ke(e=1){if(C){for(var t=e,n=w;t--;)n=on(n);w=n}}function Ae(e=!0){for(var t=0,n=w;;){if(n.nodeType===8){var r=n.data;if(r===`]`){if(t===0)return n;--t}else(r===`[`||r===`[!`||r[0]===`[`&&!isNaN(Number(r.slice(1))))&&(t+=1)}var i=on(n);e&&n.remove(),n=i}}function je(e){if(!e||e.nodeType!==8)throw we(),n;return e.data}function Me(e){return e===this.v}function Ne(e,t){return e==e?e!==t||typeof e==`object`&&!!e||typeof e==`function`:t==t}function Pe(e){return!Ne(e,this.v)}function Fe(e){throw Error(`https://svelte.dev/e/lifecycle_outside_component`)}function Ie(){throw Error(`https://svelte.dev/e/async_derived_orphan`)}function Le(e,t,n){throw Error(`https://svelte.dev/e/each_key_duplicate`)}function Re(e){throw Error(`https://svelte.dev/e/effect_in_teardown`)}function ze(){throw Error(`https://svelte.dev/e/effect_in_unowned_derived`)}function Be(e){throw Error(`https://svelte.dev/e/effect_orphan`)}function Ve(){throw Error(`https://svelte.dev/e/effect_update_depth_exceeded`)}function He(e){throw Error(`https://svelte.dev/e/props_invalid_value`)}function Ue(){throw Error(`https://svelte.dev/e/state_descriptors_fixed`)}function We(){throw Error(`https://svelte.dev/e/state_prototype_fixed`)}function Ge(){throw Error(`https://svelte.dev/e/state_unsafe_mutation`)}function Ke(){throw Error(`https://svelte.dev/e/svelte_boundary_reset_onerror`)}var D=null;function qe(e){D=e}function O(t,n=!1,r){D={p:D,i:!1,c:null,e:null,s:t,x:null,r:H,l:e&&!n?{s:null,u:null,$:[]}:null}}function k(e){var t=D,n=t.e;if(n!==null){t.e=null;for(var r of n)bn(r)}return e!==void 0&&(t.x=e),t.i=!0,D=t.p,Je(e)}function Je(e={}){return l(e,pe,{value:!0}),e}function Ye(){return!e||D!==null&&D.l===null}var Xe=[];function Ze(){var e=Xe;Xe=[],v(e)}function Qe(e){if(Xe.length===0&&!wt){var t=Xe;queueMicrotask(()=>{t===Xe&&Ze()})}Xe.push(e)}function $e(){for(;Xe.length>0;)Ze()}var et=~(x|S|b);function A(e,t){e.f=e.f&et|t}function tt(e){e.f&512||e.deps===null?A(e,b):A(e,S)}function nt(e,t,n){e.f&2048?t.add(e):e.f&4096&&n.add(e),A(e,b)}var rt=!1;function it(){rt||(rt=!0,document.addEventListener(`reset`,e=>{Promise.resolve().then(()=>{if(!e.defaultPrevented)for(let t of e.target.elements)t[be]?.()})},{capture:!0}))}function at(e){var t=B,n=H;V(null),Wn(null);try{return e()}finally{V(t),Wn(n)}}function ot(e,t,n,r=n){e.addEventListener(t,()=>at(n));let i=e[be];e[be]=i?()=>{i(),r(!0)}:()=>r(!0),it()}function st(e,t,n,r){let i=Ye()?dt:mt;var a=e.filter(e=>!e.settled),o=t.map(i);if(n.length===0&&a.length===0){r(o);return}var s=H,c=ct(),l=a.length===1?a[0].promise:a.length>1?Promise.all(a.map(e=>e.promise)):null;function u(e){if(!(s.f&16384)){c();try{r([...o,...e])}catch(e){pn(e,s)}lt()}}var d=ut();if(n.length===0){l.then(()=>u([])).finally(d);return}function f(){Promise.all(n.map(e=>pt(e))).then(u).catch(e=>pn(e,s)).finally(d)}l?l.then(()=>{c(),f(),lt()}):f()}function ct(){var e=H,t=B,n=D,r=j;return function(i=!0){Wn(e),V(t),qe(n),i&&!(e.f&16384)&&(r?.activate(),r?.apply())}}function lt(e=!0){Wn(null),V(null),qe(null),e&&j?.deactivate()}function ut(){var e=H,t=e.b,n=j,r=!!t?.is_rendered();return t?.update_pending_count(1,n),n.increment(r,e),()=>{t?.update_pending_count(-1,n),n.decrement(r,e)}}function dt(e){var t=2|x;return H!==null&&(H.f|=oe),{ctx:D,deps:null,effects:null,equals:Me,f:t,fn:e,reactions:null,rv:0,v:r,wv:0,parent:H,ac:null}}var ft=Symbol(`obsolete`);function pt(e,t,n){let i=H;i===null&&Ie();var a=void 0,o=Vt(r),s=!B,c=new Set;return En(()=>{var t=H,n=y();a=n.promise;try{Promise.resolve(e()).then(n.resolve,e=>{e!==xe&&n.reject(e)}).finally(lt)}catch(e){n.reject(e),lt()}var r=j;if(s){if(t.f&32768)var l=ut();if(i.b?.is_rendered())r.async_deriveds.get(t)?.reject(ft);else for(let e of c.values())e.reject(ft);c.add(n),r.async_deriveds.set(t,n)}let u=(e,t=void 0)=>{l?.(),c.delete(n),t!==ft&&(r.activate(),t?(o.f|=de,Gt(o,t)):(o.f&8388608&&(o.f^=de),Gt(o,e)),r.deactivate())};n.promise.then(u,e=>u(null,e||`unknown`))}),vn(()=>{for(let e of c)e.reject(ft)}),new Promise(e=>{function t(n){function r(){n===a?e(o):t(a)}n.then(r,r)}t(a)})}function mt(e){let t=dt(e);return t.equals=Pe,t}function ht(e){var t=e.effects;if(t!==null){e.effects=null;for(var n=0;n{t.ac.abort(xe),t.ac=null}),t.fn!==null&&(t.teardown=g),ar(t,0),An(t))}function yt(e){if(e.effects!==null)for(let t of e.effects)t.teardown&&t.fn!==null&&or(t)}var bt=null,j=null,xt=null,St=null,Ct=null,wt=!1,Tt=!1,Et=null,Dt=null,Ot=0,kt=1,At=class e{id=kt++;#e=!1;linked=!0;#t=null;#n=null;async_deriveds=new Map;current=new Map;previous=new Map;#r=new Set;#i=new Set;#a=0;#o=new Map;#s=null;#c=[];#l=[];#u=new Set;#d=new Set;#f=new Map;#p=new Set;is_fork=!1;#m=!1;constructor(){bt===null?bt=this:(bt.#n=this,this.#t=bt),bt=this}#h(){if(this.is_fork)return!0;for(let n of this.#o.keys()){for(var e=n,t=!1;e.parent!==null;){if(this.#f.has(e)){t=!0;break}e=e.parent}if(!t)return!0}return!1}skip_effect(e){this.#f.has(e)||this.#f.set(e,{d:[],m:[]}),this.#p.delete(e)}unskip_effect(e,t=e=>this.schedule(e)){var n=this.#f.get(e);if(n){this.#f.delete(e);for(var r of n.d)A(r,x),t(r);for(r of n.m)A(r,S),t(r)}this.#p.add(e)}#g(){var e=[];for(let i of this.#c)if(!(i.f&16384||!(i.f&6144))){for(var t=i,n=!1;t.parent!==null;){t=t.parent;var r=t.f;if(r&96){if(!(r&1024)){n=!0;break}t.f^=b}}n||e.push(t)}return this.#c=[],e}#_(){this.#e=!0;for(let e of this.#u)this.#d.delete(e),A(e,x),this.schedule(e);for(let e of this.#d)A(e,S),this.schedule(e);this.apply();for(var t=Et=[],n=[],r=Dt=[];this.#c.length>0;){Ot++>1e3&&(this.#S(),Mt());for(let e of this.#g())try{this.#v(e,t,n)}catch(t){throw Lt(e),this.#h()||this.discard(),t}}if(j=null,r.length>0){var i=e.ensure();for(let e of r)i.schedule(e)}if(Et=null,Dt=null,this.#h()){this.#x(n),this.#x(t);for(let[e,t]of this.#f)It(e,t);r.length>0&&j.#_();return}let a=this.#y();if(a){this.#x(n),this.#x(t),a.#b(this);return}this.#u.clear(),this.#d.clear();for(let e of this.#r)e(this);this.#r.clear(),xt=this,Pt(n),Pt(t),xt=null,this.#s?.resolve();var o=j;if(this.#a===0&&(this.#c.length===0||o!==null)&&this.#S(),this.#c.length>0){if(o!==null){for(let e of this.#c)o.#c.push(e);this.#c=[]}else o=this}o!==null&&(zt.clear(),o.#_())}#v(e,t,n){e.f^=b;for(var r=e.first;r!==null;){var i=r.f,a=!!(i&96);if(!(a&&i&1024||i&8192||this.#f.has(r))&&r.fn!==null){a?r.f^=b:i&4?t.push(r):er(r)&&(i&16&&this.#d.add(r),or(r));var o=r.first;if(o!==null){r=o;continue}}for(;r!==null;){var s=r.next;if(s!==null){r=s;break}r=r.parent}}}#y(){for(var e=this.#t;e!==null;){if(!e.is_fork){for(let[t,[,n]]of this.current)if(e.current.has(t)&&!n)return e}e=e.#t}return null}#b(e){for(let[t,n]of e.current)!this.previous.has(t)&&e.previous.has(t)&&this.previous.set(t,e.previous.get(t)),this.current.set(t,n);for(let[t,n]of e.async_deriveds){let e=this.async_deriveds.get(t);e&&n.promise.then(e.resolve).catch(e.reject)}e.async_deriveds.clear(),this.transfer_effects(e.#u,e.#d);let t=e=>{var n=e.reactions;if(n!==null&&!(e.f&2&&!(e.f&6144)))for(let e of n){var r=e.f;if(r&2)t(e);else{var i=e;r&4194320&&!this.async_deriveds.has(i)&&(this.#d.delete(i),A(i,x),this.schedule(i))}}};for(let e of this.current.keys())t(e);this.oncommit(()=>e.discard()),e.#S(),j=this,this.#_()}#x(e){for(var t=0;t{this.#m=!1,this.linked&&this.flush()}))}transfer_effects(e,t){for(let t of e)this.#u.add(t);for(let e of t)this.#d.add(e);e.clear(),t.clear()}oncommit(e){this.#r.add(e)}ondiscard(e){this.#i.add(e)}settled(){return(this.#s??=y()).promise}static ensure(){if(j===null){let t=j=new e;!Tt&&!wt&&Qe(()=>{t.#e||t.flush()})}return j}apply(){St=null}schedule(e){if(Ct=e,e.b?.is_pending&&e.f&16777228&&!(e.f&32768)){e.b.defer_effect(e);return}this.#c.push(e)}#S(){if(this.linked){var e=this.#t,t=this.#n;e===null||(e.#n=t),t===null?bt=e:t.#t=e,this.linked=!1}}};function jt(e){var t=wt;wt=!0;try{var n;for(e&&(j!==null&&!j.is_fork&&j.flush(),n=e());;){if($e(),j===null)return n;j.flush()}}finally{wt=t}}function Mt(){try{Ve()}catch(e){pn(e,Ct)}}var Nt=null;function Pt(e){var t=e.length;if(t!==0){for(var n=0;n0)){zt.clear();for(let e of Nt){if(e.f&24576)continue;let t=[e],n=e.parent;for(;n!==null;)Nt.has(n)&&(Nt.delete(n),t.push(n)),n=n.parent;for(let e=t.length-1;e>=0;e--){let n=t[e];n.f&24576||or(n)}}Nt.clear()}}Nt=null}}function Ft(e){j.schedule(e)}function It(e,t){if(!(e.f&32&&e.f&1024)){e.f&2048?t.d.push(e):e.f&4096&&t.m.push(e),A(e,b);for(var n=e.first;n!==null;)It(n,t),n=n.next}}function Lt(e){A(e,b);for(var t=e.first;t!==null;)Lt(t),t=t.next}var Rt=new Set,zt=new Map,Bt=!1;function Vt(e,t){return{f:0,v:e,reactions:null,equals:Me,rv:0,wv:0}}function Ht(e,t){let n=Vt(e,t);return Kn(n),n}function M(t,n=!1,r=!0){let i=Vt(t);return n||(i.equals=Pe),e&&r&&D!==null&&D.l!==null&&(D.l.s??=[]).push(i),i}function N(e,t,n=!1){return B!==null&&(!Un||B.f&131072)&&Ye()&&B.f&4325394&&(Gn===null||!Gn.has(e))&&Ge(),Gt(e,n?Yt(t):t,Dt)}var Ut=null,Wt=0;function Gt(e,t,n=null){if(!e.equals(t)){Vn?zt.set(e,t):zt.has(e)||zt.set(e,e.v);var r=At.ensure();if(r.capture(e,t),e.f&2){let t=e;e.f&2048&>(t),St===null&&tt(t)}e.wv=$n(),Ut=null,Wt=0,Jt(e,x,n),Ut=null,Ye()&&H!==null&&H.f&1024&&!(H.f&96)&&(qn===null?Jn([e]):qn.push(e)),!r.is_fork&&Rt.size>0&&!Bt&&Kt()}return t}function Kt(){Bt=!1;for(let e of Rt){e.f&1024&&A(e,S);let t;try{t=er(e)}catch{t=!0}t&&or(e)}Rt.clear()}function qt(e){N(e,e.v+1)}function Jt(e,t,n){var r=e.reactions;if(r!==null){var i=Ye(),a=r.length;if(Wt+=a,Wt>1e5&&Ut===null&&(Ut=new Set),Ut!==null){if(Ut.has(e))return;Ut.add(e)}for(var o=0;o{if(Zn===c)return e();var t=B,n=Zn;V(null),Qn(c);var r=e();return V(t),Qn(n),r};return i&&n.set(`length`,Ht(e.length,s)),new Proxy(e,{defineProperty(e,t,r){(!(`value`in r)||r.configurable===!1||r.enumerable===!1||r.writable===!1)&&Ue();var i=n.get(t);return i===void 0?l(()=>{var e=Ht(r.value,s);return n.set(t,e),e}):N(i,r.value,!0),!0},deleteProperty(e,t){var i=n.get(t);if(i===void 0){if(t in e){let e=l(()=>Ht(r,s));n.set(t,e),qt(o)}}else N(i,r),qt(o);return!0},get(t,i,a){if(i===fe)return e;var o=n.get(i),c=i in t;if(o===void 0&&(!c||u(t,i)?.writable)&&(o=l(()=>Ht(Yt(c?t[i]:r),s)),n.set(i,o)),o!==void 0){var d=G(o);return d===r?void 0:d}return Reflect.get(t,i,a)},getOwnPropertyDescriptor(e,t){this.has?.(e,t);var i=Reflect.getOwnPropertyDescriptor(e,t),a=n.get(t);if(a!==void 0){var o=G(a);if(o===r)return;if(i&&`value`in i)i.value=o;else return{enumerable:!0,configurable:!0,value:o,writable:!0}}return i},has(e,t){if(t===fe)return!0;var i=n.get(t),a=i!==void 0&&i.v!==r||Reflect.has(e,t);return(i!==void 0||H!==null&&(!a||u(e,t)?.writable))&&(i===void 0&&(i=l(()=>Ht(a?Yt(e[t]):r,s)),n.set(t,i)),G(i)===r)?!1:a},set(e,t,a,c){var d=n.get(t),f=t in e;if(i&&t===`length`)for(var p=a;pHt(r,s)),n.set(p+``,m)):N(m,r)}if(d===void 0)(!f||u(e,t)?.writable)&&(d=l(()=>Ht(void 0,s)),N(d,Yt(a)),n.set(t,d));else{f=d.v!==r;var h=l(()=>Yt(a));N(d,h)}var g=Reflect.getOwnPropertyDescriptor(e,t);if(g?.set&&g.set.call(c,a),!f){if(i&&typeof t==`string`){var _=n.get(`length`),v=Number(t);Number.isInteger(v)&&v>=_.v&&N(_,v+1)}qt(o)}return!0},ownKeys(e){G(o);var t=Reflect.ownKeys(e).filter(e=>{var t=n.get(e);return t===void 0||t.v!==r});for(var[i,a]of n)a.v!==r&&!(i in e)&&t.push(i);return t},setPrototypeOf(){We()}})}function Xt(e){try{if(typeof e==`object`&&e&&fe in e)return e[fe]}catch{}return e}function Zt(e,t){return Object.is(Xt(e),Xt(t))}var Qt,$t,en,tn;function nn(){if(Qt===void 0){Qt=window,$t=/Firefox/.test(navigator.userAgent);var e=Element.prototype,t=Node.prototype,n=Text.prototype;en=u(t,`firstChild`).get,tn=u(t,`nextSibling`).get,h(e)&&(e[_e]=void 0,e[ge]=null,e[ve]=void 0,e.__e=void 0),h(n)&&(n[ye]=void 0)}}function rn(e=``){return document.createTextNode(e)}function an(e){return en.call(e)}function on(e){return tn.call(e)}function P(e,t){if(!C)return an(e);var n=an(w);if(n===null)n=w.appendChild(rn());else if(t&&n.nodeType!==3){var r=rn();return n?.before(r),T(r),r}return t&&dn(n),T(n),n}function sn(e,t=!1){if(!C){var n=an(e);return n instanceof Comment&&n.data===``?on(n):n}if(t){if(w?.nodeType!==3){var r=rn();return w?.before(r),T(r),r}dn(w)}return w}function F(e,t=!1){if(!C)return an(e);var n=P(e,t);return E(e),n}function I(e,t=1,n=!1){let r=C?w:e;for(var i;t--;)i=r,r=on(r);if(!C)return r;if(n){if(r?.nodeType!==3){var a=rn();return r===null?i?.after(a):r.before(a),T(a),a}dn(r)}return T(r),r}function cn(e){e.textContent=``}function ln(){return!1}function un(e,t,n){return t==null||t===`http://www.w3.org/1999/xhtml`?n?document.createElement(e,{is:n}):document.createElement(e):n?document.createElementNS(t,e,{is:n}):document.createElementNS(t,e)}function dn(e){if(e.nodeValue.length<65536)return;let t=e.nextSibling;for(;t!==null&&t.nodeType===3;)t.remove(),e.nodeValue+=t.nodeValue,t=e.nextSibling}function fn(e){var t=H;if(t===null)return B.f|=de,e;if(!(t.f&32768)&&!(t.f&4))throw e;pn(e,t)}function pn(e,t){if(!(t!==null&&t.f&16384)){for(;t!==null;){if(t.f&128&&!(t.f&33570816)){if(!(t.f&32768))throw e;try{t.b.error(e);return}catch(t){e=t}}t=t.parent}throw e}}function mn(e){H===null&&(B===null&&Be(e),ze()),Vn&&Re(e)}function hn(e,t){var n=t.last;n===null?t.last=t.first=e:(n.next=e,e.prev=n,t.last=e)}function gn(e,t){var n=H;n!==null&&n.f&8192&&(e|=ee);var r={ctx:D,deps:null,nodes:null,f:e|x|512,first:null,fn:t,last:null,next:null,parent:n,b:n&&n.b,prev:null,teardown:null,wv:0,ac:null};j?.register_created_effect(r);var i=r;if(e&4)Et===null?At.ensure().schedule(r):Et.push(r);else if(t!==null){try{or(r)}catch(e){throw z(r),e}i.deps===null&&i.teardown===null&&i.nodes===null&&i.first===i.last&&!(i.f&524288)&&(i=i.first,e&16&&e&65536&&i!==null&&(i.f|=ie))}if(i!==null&&(i.parent=n,n!==null&&hn(i,n),B!==null&&B.f&2&&!(e&64))){var a=B;(a.effects??=[]).push(i)}return r}function _n(){return B!==null&&!Un}function vn(e){let t=gn(8,null);return A(t,b),t.teardown=e,t}function yn(e){mn(`$effect`);var t=H.f;if(!B&&t&32&&D!==null&&!D.i){var n=D;(n.e??=[]).push(e)}else return bn(e)}function bn(e){return gn(4|se,e)}function xn(e){return mn(`$effect.pre`),gn(8|se,e)}function Sn(e){At.ensure();let t=gn(64|oe,e);return(e={})=>new Promise(n=>{e.outro?Pn(t,()=>{z(t),n(void 0)}):(z(t),n(void 0))})}function Cn(e){return gn(4,e)}function wn(e,t){var n=D,r={effect:null,ran:!1,deps:e};n.l.$.push(r),r.effect=Dn(()=>{if(e(),!r.ran){r.ran=!0;var n=H;try{Wn(n.parent),K(t)}finally{Wn(n)}}})}function Tn(){var e=D;Dn(()=>{for(var t of e.l.$){t.deps();var n=t.effect;n.f&1024&&n.deps!==null&&A(n,S),er(n)&&or(n),t.ran=!1}})}function En(e){return gn(ue|oe,e)}function Dn(e,t=0){return gn(8|t,e)}function L(e,t=[],n=[],r=[]){st(r,t,n,t=>{gn(8,()=>{e(...t.map(G))})})}function On(e,t=0){return gn(16|t,e)}function R(e){return gn(32|oe,e)}function kn(e){var t=e.teardown;if(t!==null){let n=Vn,r=B;Hn(!0),V(null);try{t.call(null)}catch(t){pn(t,e.parent)}finally{Hn(n),V(r)}}}function An(e,t=!1){var n=e.first;for(e.first=e.last=null;n!==null;){let e=n.ac;e!==null&&at(()=>{e.abort(xe)});var r=n.next;n.f&64?n.parent=null:z(n,t),n=r}}function jn(e){for(var t=e.first;t!==null;){var n=t.next;t.f&32||z(t),t=n}}function z(e,t=!0){var n=!1;(t||e.f&262144)&&e.nodes!==null&&e.nodes.end!==null&&(Mn(e.nodes.start,e.nodes.end),n=!0),e.f|=re,An(e,t&&!n),ar(e,0);var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)e.stop();kn(e),e.f^=re,e.f|=te;var i=e.parent;i!==null&&i.first!==null&&Nn(e),e.next=e.prev=e.teardown=e.ctx=e.deps=e.fn=e.nodes=e.ac=e.b=null}function Mn(e,t){for(;e!==null;){var n=e===t?null:on(e);e.remove(),e=n}}function Nn(e){var t=e.parent,n=e.prev,r=e.next;n!==null&&(n.next=r),r!==null&&(r.prev=n),t!==null&&(t.first===e&&(t.first=r),t.last===e&&(t.last=n))}function Pn(e,t,n=!0){var r=[];e.f|=256,Fn(e,r,!0);var i=()=>{n&&z(e),t&&t()},a=r.length;if(a>0){var o=()=>--a||i();for(var s of r)s.out(o)}else i()}function Fn(e,t,n){if(!(e.f&8192)){e.f^=ee;var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)(e.is_global||n)&&t.push(e);for(var i=e.first;i!==null;){var a=i.next;if(!(i.f&64)){var o=!!(i.f&65536)||!!(i.f&32)&&!!(e.f&16);Fn(i,t,o?n:!1)}i=a}}}function In(e){e.f&=-257,Ln(e,!0)}function Ln(e,t){if(!(e.f&256)&&e.f&8192){e.f^=ee,e.f&1024||(A(e,x),At.ensure().schedule(e));for(var n=e.first;n!==null;){var r=n.next,i=!!(n.f&65536)||!!(n.f&32);Ln(n,i?t:!1),n=r}var a=e.nodes&&e.nodes.t;if(a!==null)for(let e of a)(e.is_global||t)&&e.in()}}function Rn(e,t){if(e.nodes)for(var n=e.nodes.start,r=e.nodes.end;n!==null;){var i=n===r?null:on(n);t.append(n),n=i}}var zn=null,Bn=!1,Vn=!1;function Hn(e){Vn=e}var B=null,Un=!1;function V(e){B=e}var H=null;function Wn(e){H=e}var Gn=null;function Kn(e){B!==null&&(B.f&2097152||B.f&2)&&(Gn??=new Set).add(e)}var U=null,W=0,qn=null;function Jn(e){qn=e}var Yn=1,Xn=0,Zn=Xn;function Qn(e){Zn=e}function $n(){return++Yn}function er(e){var t=e.f;if(t&2048)return!0;if(t&4096){for(var n=e.deps,r=n.length,i=0;ie.wv)return!0}t&512&&St===null&&A(e,b)}return!1}function tr(e,t,n=!0){var r=e.reactions;if(r!==null&&!(Gn!==null&&Gn.has(e)))for(var i=0;i{e.ac.abort(xe)}),e.ac=null);try{e.f|=le;var u=e.fn,d=u();e.f|=ne;var f=rr(e);if(Ye()&&qn!==null&&!Un&&f!==null&&!(e.f&6146))for(var p=0;p0)for(t.length=W+U.length,r=0;r{c.ac.abort(xe),c.ac=null,A(c,x)}),vt(c),ar(c,0)}}function ar(e,t){var n=e.deps;if(n!==null)for(var r=t;r{_r=!1,gr=null}));var o=0,s=gr===e&&e[dr];if(s){var c=i.indexOf(s);if(c!==-1&&(t===document||t===window)){e[dr]=t;return}var u=i.indexOf(t);if(u===-1)return;c<=u&&(o=c)}if(a=i[o]||e.target,a!==t){l(e,`currentTarget`,{configurable:!0,get(){return a||n}});var d=B,f=H;V(null),Wn(null);try{for(var p,m=[];a!==null&&a!==t;){try{var h=a[dr]?.[r];h!=null&&(!a.disabled||e.target===a)&&h.call(a,e)}catch(e){p?m.push(e):p=e}if(e.cancelBubble)break;o++,a=o{throw e});throw p}}finally{e[dr]=t,delete e.currentTarget,V(d),Wn(f)}}}var yr=globalThis?.window?.trustedTypes&&globalThis.window.trustedTypes.createPolicy(`svelte-trusted-html`,{createHTML:e=>e});function br(e){return yr?.createHTML(e)??e}function xr(e){var t=un(`template`);return t.innerHTML=br(e.replaceAll(``,``)),t.content}function Sr(e,t){var n=H;n.nodes===null&&(n.nodes={start:e,end:t,a:null,t:null})}function J(e,t){var n=!!(t&1),r=!!(t&2),i,a=!e.startsWith(``);return()=>{if(C)return Sr(w,null),w;i===void 0&&(i=xr(a?e:``+e),n||(i=an(i)));var t=r||$t?document.importNode(i,!0):i.cloneNode(!0);if(n){var o=an(t),s=t.lastChild;Sr(o,s)}else Sr(t,t);return t}}function Y(e,t){if(C){var n=H;(!(n.f&32768)||n.nodes.end===null)&&(n.nodes.end=w),Oe();return}e!==null&&e.before(t)}[...`allowfullscreen.async.autofocus.autoplay.checked.controls.default.disabled.formnovalidate.indeterminate.inert.ismap.loop.multiple.muted.nomodule.novalidate.open.playsinline.readonly.required.reversed.seamless.selected.webkitdirectory.defer.disablepictureinpicture.disableremoteplayback`.split(`.`)];var Cr=[`touchstart`,`touchmove`];function wr(e){return Cr.includes(e)}function Tr(e){let t=0,n=Vt(0),r;return()=>{_n()&&(G(n),Dn(()=>(t===0&&(r=K(()=>e(()=>qt(n)))),t+=1,()=>{Qe(()=>{--t,t===0&&(r?.(),r=void 0,qt(n))})})))}}var Er=ie|oe;function Dr(e,t,n,r){new Or(e,t,n,r)}var Or=class{parent;is_pending=!1;transform_error;#e;#t=C?w:null;#n;#r;#i;#a=null;#o=null;#s=null;#c=null;#l=0;#u=0;#d=!1;#f=new Set;#p=new Set;#m=null;#h=Tr(()=>(this.#m=Vt(this.#l),()=>{this.#m=null}));constructor(e,t,n,r){this.#e=e,this.#n=t,this.#r=e=>{var t=H;t.b=this,t.f|=128,n(e)},this.parent=H.b,this.transform_error=r??this.parent?.transform_error??(e=>e),this.#i=On(()=>{if(C){let e=this.#t;Oe();let t=e.data===`[!`;if(e.data.startsWith(`[?`)){let t=JSON.parse(e.data.slice(2));this.#_(t)}else t?this.#y():this.#g()}else this.#b()},Er),C&&(this.#e=w)}#g(){try{this.#a=R(()=>this.#r(this.#e))}catch(e){this.error(e)}}#_(e){let t=this.#n.failed,{reset:n,invoke_onerror:r}=this.#v(e);Qe(r),t&&(this.#s=R(()=>{t(this.#e,()=>e,()=>n)}))}#v(e){var t=!1,n=!1;let r=()=>{if(t){Ee();return}t=!0,n&&Ke(),this.#s!==null&&Pn(this.#s,()=>{this.#s=null}),this.#S(()=>{this.#b()})};return{reset:r,invoke_onerror:()=>{try{n=!0,this.#n.onerror?.(e,r),n=!1}catch(e){pn(e,this.#i&&this.#i.parent)}}}}#y(){let e=this.#n.pending;e&&(this.is_pending=!0,this.#o=R(()=>e(this.#e)),Qe(()=>{var e=this.#c=document.createDocumentFragment(),t=rn(),n=!1;if(e.append(t),this.#a=this.#S(()=>{try{return R(()=>this.#r(t))}catch(e){try{this.error(e),n=!0}catch(e){pn(e,this.#i.parent)}return null}}),this.#a===null){this.#c=null,n&&this.#x(j);return}this.#u===0&&(this.#e.before(e),this.#c=null,Pn(this.#o,()=>{this.#o=null}),this.#x(j))}))}#b(){try{if(this.is_pending=this.has_pending_snippet(),this.#u=0,this.#l=0,this.#a=R(()=>{this.#r(this.#e)}),this.#u>0){var e=this.#c=document.createDocumentFragment();Rn(this.#a,e);let t=this.#n.pending;this.#o=R(()=>t(this.#e))}else this.#x(j)}catch(e){this.error(e)}}#x(e){this.is_pending=!1,e.transfer_effects(this.#f,this.#p)}defer_effect(e){nt(e,this.#f,this.#p)}is_rendered(){return!this.is_pending&&(!this.parent||this.parent.is_rendered())}has_pending_snippet(){return!!this.#n.pending}#S(e){var t=H,n=B,r=D;Wn(this.#i),V(this.#i),qe(this.#i.ctx);try{return At.ensure(),e()}finally{Wn(t),V(n),qe(r)}}#C(e,t){if(!this.has_pending_snippet()){this.parent&&this.parent.#C(e,t);return}this.#u+=e,this.#u===0&&(this.#x(t),this.#o&&Pn(this.#o,()=>{this.#o=null}),this.#c&&=(this.#e.before(this.#c),null))}update_pending_count(e,t){this.#C(e,t),this.#l+=e,!(!this.#m||this.#d)&&(this.#d=!0,Qe(()=>{this.#d=!1,this.#m&&Gt(this.#m,this.#l)}))}get_effect_pending(){return this.#h(),G(this.#m)}error(e){if(!this.#n.onerror&&!this.#n.failed)throw e;j?.is_fork?(this.#a&&j.skip_effect(this.#a),this.#o&&j.skip_effect(this.#o),this.#s&&j.skip_effect(this.#s),j.oncommit(()=>{this.#w(e)})):this.#w(e)}#w(e){this.#a&&=(z(this.#a),null),this.#o&&=(z(this.#o),null),this.#s&&=(z(this.#s),null),C&&(T(this.#t),ke(),T(Ae()));let t=this.#n.failed,n=e=>{let{reset:n,invoke_onerror:r}=this.#v(e);r(),t&&(this.#s=this.#S(()=>{try{return R(()=>{var r=H;r.b=this,r.f|=128,t(this.#e,()=>e,()=>n)})}catch(e){return pn(e,this.#i.parent),null}}))};Qe(()=>{var t;try{t=this.transform_error(e)}catch(e){pn(e,this.#i&&this.#i.parent);return}typeof t==`object`&&t&&typeof t.then==`function`?t.then(n,e=>pn(e,this.#i&&this.#i.parent)):n(t)})}};function X(e,t){var n=t==null?``:typeof t==`object`?`${t}`:t;n!==(e[ye]??=e.nodeValue)&&(e[ye]=n,e.nodeValue=`${n}`)}function kr(e,t){return jr(e,t)}var Ar=new Map;function jr(e,{target:t,anchor:r,props:i={},events:a,context:o,intro:s=!0,transformError:l}){nn();var u=void 0,d=Sn(()=>{var s=r??t.appendChild(rn());Dr(s,{pending:()=>{}},t=>{O({});var r=D;if(o&&(r.c=o),a&&(i.$$events=a),C&&Sr(t,null),u=e(t,i)||Je(),C&&(H.nodes.end=w,w===null||w.nodeType!==8||w.data!==`]`))throw we(),n;k()},l);var d=new Set,f=e=>{for(var n=0;n{for(var e of d)for(let r of[t,document]){var n=Ar.get(r),i=n.get(e);--i==0?(r.removeEventListener(e,vr),n.delete(e),n.size===0&&Ar.delete(r)):n.set(e,i)}pr.delete(f),s!==r&&s.parentNode?.removeChild(s)}});return Mr.set(u,d),u}var Mr=new WeakMap,Nr=class{anchor;#e=new Map;#t=new Map;#n=new Map;#r=new Set;#i=!0;constructor(e,t=!0){this.anchor=e,this.#i=t}#a=e=>{if(this.#e.has(e)){var t=this.#e.get(e),n=this.#t.get(t);if(n)In(n),this.#r.delete(t);else{var r=this.#n.get(t);r&&(In(r.effect),this.#t.set(t,r.effect),this.#n.delete(t),r.fragment.lastChild.remove(),this.anchor.before(r.fragment),n=r.effect)}for(let[t,n]of this.#e){if(this.#e.delete(t),t===e)break;let r=this.#n.get(n);r&&(z(r.effect),this.#n.delete(n))}for(let[e,r]of this.#t){if(e===t||this.#r.has(e))continue;let i=()=>{if(Array.from(this.#e.values()).includes(e)){var t=document.createDocumentFragment();Rn(r,t),t.append(rn()),this.#n.set(e,{effect:r,fragment:t})}else z(r);this.#r.delete(e),this.#t.delete(e)};this.#i||!n?(this.#r.add(e),Pn(r,i,!1)):i()}}};#o=e=>{this.#e.delete(e);let t=Array.from(this.#e.values());for(let[e,n]of this.#n)t.includes(e)||(z(n.effect),this.#n.delete(e))};ensure(e,t){var n=j,r=ln();if(t&&!this.#t.has(e)&&!this.#n.has(e)){if(r){var i=document.createDocumentFragment(),a=rn();i.append(a),this.#n.set(e,{effect:R(()=>t(a)),fragment:i})}else this.#t.set(e,R(()=>t(this.anchor)))}if(this.#e.set(n,e),r){for(let[t,r]of this.#t)t===e?n.unskip_effect(r):n.skip_effect(r);for(let[t,r]of this.#n)t===e?n.unskip_effect(r.effect):n.skip_effect(r.effect);n.oncommit(this.#a),n.ondiscard(this.#o)}else C&&(this.anchor=w),this.#a(n)}};function Pr(t){D===null&&Fe(`onMount`),e&&D.l!==null?Fr(D).m.push(t):yn(()=>{let e=K(t);if(typeof e==`function`)return e})}function Fr(e){var t=e.l;return t.u??={a:[],b:[],m:[]}}function Z(e,t,n=!1){var r;C&&(r=w,Oe());var i=new Nr(e),a=n?ie:0;function o(e,t){if(C){var n=je(r);if(e!==parseInt(n.substring(1))){var a=Ae();T(a),i.anchor=a,De(!1),i.ensure(e,t),De(!0);return}}i.ensure(e,t)}On(()=>{var e=!1;t((t,n=0)=>{e=!0,o(n,t)}),e||o(-1,null)},a)}var Ir=Symbol(`NaN`);function Lr(e,t,n){C&&Oe();var r=new Nr(e),i=!Ye();On(()=>{var e=t();e!==e&&(e=Ir),i&&typeof e==`object`&&e&&(e={}),r.ensure(e,n)})}function Rr(e,t){return t}function zr(e,t,n){for(var r=[],i=t.length,a,o=t.length,s=0;s{if(a){if(a.pending.delete(n),a.done.add(n),a.pending.size===0){var t=e.outrogroups;Br(e,c(a.done)),t.delete(a),t.size===0&&(e.outrogroups=null)}}else--o},!1)}if(o===0){var l=r.length===0&&n!==null&&e.pending.size===0;if(l){var u=n,d=u.parentNode;cn(d),d.append(u),e.items.clear()}Br(e,t,!l)}else a={pending:new Set(t),done:new Set},(e.outrogroups??=new Set).add(a)}function Br(e,t,n=!0){var r;if(e.pending.size>0){r=new Set;for(let t of e.pending.values())for(let n of t)r.add(e.items.get(n).e)}for(var i=0;i{var e=n();return a(e)?e:e==null?[]:c(e)}),p,m=new Map,h=!0;function g(e){v.effect.f&16384||(v.pending.delete(e),v.fallback=d,Wr(v,p,s,t,r),d!==null&&(p.length===0?d.f&33554432?(d.f^=ce,Kr(d,null,s)):In(d):Pn(d,()=>{d=null})))}function _(e){v.pending.delete(e)}var v={effect:On(()=>{p=G(f);var e=p.length;let a=!1;C&&je(s)===`[!`!=(e===0)&&(s=Ae(),T(s),De(!1),a=!0);for(var c=new Set,u=j,v=ln(),y=0;yo(s)):(d=R(()=>o(Vr??=rn())),d.f|=ce)),e>c.size&&Le(``,``,``),C&&e>0&&T(Ae()),!h){if(m.set(u,c),v){for(let[e,t]of l)c.has(e)||u.skip_effect(t.e);u.oncommit(g),u.ondiscard(_)}else g(u)}a&&De(!0),G(f)}),flags:t,items:l,pending:m,outrogroups:null,fallback:d};h=!1,C&&(s=w)}function Ur(e){for(;e!==null&&!(e.f&32);)e=e.next;return e}function Wr(e,t,n,r,i){var a=!!(r&8),o=t.length,s=e.items,l=Ur(e.effect.first),u,d=null,f,p=[],m=[],h,g,_,v;if(a)for(v=0;v0){var re=r&4&&o===0?n:null;if(a){for(v=0;v{if(f!==void 0)for(_ of f)_.nodes?.a?.apply()})}function Gr(e,t,n,r,i,a,o,s){var c=o&1?o&16?Vt(n):M(n,!1,!1):null,l=o&2?Vt(i):null;return{v:c,i:l,e:R(()=>(a(t,c??n,l??i,s),()=>{e.delete(r)}))}}function Kr(e,t,n){if(e.nodes)for(var r=e.nodes.start,i=e.nodes.end,a=t&&!(t.f&33554432)?t.nodes.start:n;r!==null;){var o=on(r);if(a.before(r),r===i)return;r=o}}function qr(e,t,n){t===null?e.effect.first=n:t.next=n,n===null?e.effect.last=t:n.prev=t}function Jr(e,t){let n=null,r=C;var i;if(C){n=w;for(var a=an(document.head);a!==null&&(a.nodeType!==8||a.data!==e);)a=on(a);if(a===null)De(!1);else{var o=on(a);a.remove(),T(o)}}C||(i=document.head.appendChild(rn()));try{On(()=>{var e=R(()=>t(i));e.f|=ae,C||(e.nodes===null?e.nodes={start:i,end:i,a:null,t:null}:e.nodes.end=i)})}finally{r&&(De(!0),T(n))}}function Yr(e){var t,n,r=``;if(typeof e==`string`||typeof e==`number`)r+=e;else if(typeof e==`object`){if(Array.isArray(e)){var i=e.length;for(t=0;t=0;){var s=o+a;(o===0||Qr.includes(r[o-1]))&&(s===r.length||Qr.includes(r[s]))?r=(o===0?``:r.substring(0,o))+r.substring(s+1):o=s}}return r===``?null:r}function ei(e,t,n,r,i,a){var o=e[_e];if(C||o!==n||o===void 0){var s=$r(n,r,a);(!C||s!==e.getAttribute(`class`))&&(s==null?e.removeAttribute(`class`):t?e.className=s:e.setAttribute(`class`,s)),e[_e]=n}else if(a&&i!==a)for(var c in a){var l=!!a[c];(i==null||l!==!!i[c])&&e.classList.toggle(c,l)}return a}function ti(e,t){t?e.hasAttribute(`selected`)||e.setAttribute(`selected`,``):e.removeAttribute(`selected`)}function ni(e,t){var n=e.__defaultValue,r=e.multiple,i=r?n??[]:null;if(!r||a(i)){var o=e.selectedIndex,s=t&&r?new Set(e.selectedOptions):null;for(var c of e.options){var l=oi(c);ti(c,r?i.includes(l):Zt(l,n))}if(t){if(s!==null)for(c of e.options){var u=s.has(c);c.selected!==u&&(c.selected=u)}else e.selectedIndex!==o&&(e.selectedIndex=o)}}}function ri(e,t,n=!1){if(e.multiple){if(t==null)return;if(!a(t))return Te();for(var r of e.options)r.selected=t.includes(oi(r));return}for(r of e.options)if(Zt(oi(r),t)){r.selected=!0;return}(!n||t!==void 0)&&(e.selectedIndex=-1)}function ii(e){var t=new MutationObserver(t=>{t.every(si)||(`__defaultValue`in e&&ni(e,!1),`__value`in e&&ri(e,e.__value))});t.observe(e,{childList:!0,subtree:!0,attributes:!0,attributeFilter:[`value`]}),vn(()=>{t.disconnect()})}function ai(e,t,n=t){var r=new WeakSet,i=!0;ot(e,`change`,t=>{var i=t?`[selected]`:`:checked`,a;if(e.multiple)a=[].map.call(e.querySelectorAll(i),oi);else{var o=e.querySelector(i)??e.querySelector(`option:not([disabled])`);a=o&&oi(o)}n(a),e.__value=a,j!==null&&r.add(j)}),Cn(()=>{var a=t();if(e===document.activeElement){var o=j;if(r.has(o))return}if(ri(e,a,i),i&&a===void 0){var s=e.querySelector(`:checked`);s!==null&&(a=oi(s),n(a))}e.__value=a,i=!1})}function oi(e){return`__value`in e?e.__value:e.value}function si(e){if(e.target.closest(`selectedcontent`)!==null)return!0;if(e.type===`childList`){var t=[...e.addedNodes,...e.removedNodes];return t.length>0&&t.every(e=>e.nodeName===`SELECTEDCONTENT`)}return!1}var ci=Symbol(`is custom element`),li=Symbol(`is html`),ui=Se?`link`:`LINK`;function di(e){if(C){var t=!1,n=()=>{if(!t){if(t=!0,e.hasAttribute(`value`)){var n=e.value;Q(e,`value`,null),e.value=n}if(e.hasAttribute(`checked`)){var r=e.checked;Q(e,`checked`,null),e.checked=r}}};e[be]=n,Qe(n),it()}}function fi(e,t){var n=pi(e);n.checked!==(n.checked=t??void 0)&&(e.checked=t)}function Q(e,t,n,r){var i=pi(e);C&&(i[t]=e.getAttribute(t),t===`src`||t===`srcset`||t===`href`&&e.nodeName===ui)||i[t]!==(i[t]=n)&&(t===`loading`&&(e[he]=n),n==null?e.removeAttribute(t):typeof n!=`string`&&hi(e).has(t)?e[t]=n:e.setAttribute(t,n))}function pi(e){return e[ge]??={[ci]:e.nodeName.includes(`-`),[li]:e.namespaceURI===i}}var mi=new Map;function hi(e){var t=e.getAttribute(`is`)||e.nodeName,n=mi.get(t);if(n)return n;mi.set(t,n=new Set);for(var r,i=e,a=Element.prototype;a!==i;){for(var o in r=d(i),r)r[o].set&&o!==`innerHTML`&&o!==`textContent`&&o!==`innerText`&&n.add(o);i=m(i)}return n}function gi(e,t,n=t){var r=new WeakSet;ot(e,`input`,async i=>{var a=i?e.defaultValue:e.value;if(a=_i(e)?vi(a):a,n(a),j!==null&&r.add(j),await sr(),a!==(a=t())){var o=e.selectionStart,s=e.selectionEnd,c=e.value.length;if(e.value=a??``,s!==null){var l=e.value.length;o===s&&s===c&&l>c?(e.selectionStart=l,e.selectionEnd=l):(e.selectionStart=o,e.selectionEnd=Math.min(s,l))}}}),(C&&e.defaultValue!==e.value||K(t)==null&&e.value)&&(n(_i(e)?vi(e.value):e.value),j!==null&&r.add(j)),Dn(()=>{var n=t();if(e===document.activeElement){var i=j;if(r.has(i))return}_i(e)&&n===vi(e.value)||(e.type!==`date`||n||e.value)&&n!==e.value&&(e.value=n??``)})}function _i(e){var t=e.type;return t===`number`||t===`range`}function vi(e){return e===``?null:+e}function yi(e=!1){let t=D,n=t.l.u;if(!n)return;let r=()=>q(t.s);if(e){let e=0,n={},i=dt(()=>{let r=!1,i=t.s;for(let e in i)i[e]!==n[e]&&(n[e]=i[e],r=!0);return r&&e++,e});r=()=>G(i)}n.b.length&&xn(()=>{bi(t,r),v(n.b)}),yn(()=>{let e=K(()=>n.m.map(_));return()=>{for(let t of e)typeof t==`function`&&t()}}),n.a.length&&yn(()=>{bi(t,r),v(n.a)})}function bi(e,t){if(e.l.s)for(let t of e.l.s)G(t);t()}function xi(e,t,n){if(e==null)return t(void 0),n&&n(void 0),g;let r=K(()=>e.subscribe(t,n));return r.unsubscribe?()=>r.unsubscribe():r}var Si=[];function Ci(e,t=g){let n=null,r=new Set;function i(t){if(Ne(e,t)&&(e=t,n)){let t=!Si.length;for(let t of r)t[1](),Si.push(t,e);if(t){for(let e=0;e{r.delete(c),r.size===0&&n&&(n(),n=null)}}return{set:i,update:a,subscribe:o}}function wi(e){let t;return xi(e,e=>t=e)(),t}var Ti=!1,Ei=Symbol(`unmounted`);function Di(e,t,n){let r=n[t]??={store:null,source:M(void 0),unsubscribe:g};if(r.store!==e&&!(Ei in n)){if(r.unsubscribe(),r.store=e??null,e==null)r.source.v=void 0,r.unsubscribe=g;else{var i=!0;r.unsubscribe=xi(e,e=>{i?r.source.v=e:N(r.source,e)}),i=!1}}return e&&Ei in n?wi(e):G(r.source)}function Oi(){let e={};function t(){vn(()=>{for(var t in e)e[t].unsubscribe();l(e,Ei,{enumerable:!1,value:!0})})}return[e,t]}function ki(e){var t=Ti;try{return Ti=!1,[e(),Ti]}finally{Ti=t}}function $(t,n,r,i){var a=!e||!!(r&2),o=!!(r&8),s=!!(r&16),c=i,l=!0,d=void 0,f=()=>s&&a?(d??=dt(i),G(d)):(l&&(l=!1,c=s?K(i):i),c);let p;if(o){var m=fe in t||me in t;p=u(t,n)?.set??(m&&n in t?e=>t[n]=e:void 0)}var h,g=!1;o?[h,g]=ki(()=>t[n]):h=t[n],h===void 0&&i!==void 0&&(h=f(),p&&(a&&He(n),p(h)));var _=a?()=>{var e=t[n];return e===void 0?f():(l=!0,e)}:()=>{var e=t[n];return e!==void 0&&(c=void 0),e===void 0?c:e};if(a&&!(r&4))return _;if(p){var v=t.$$legacy;return(function(e,t){return arguments.length>0?((!a||!t||v||g)&&p(t?_():e),e):_()})}var y=!1,b=(r&1?dt:mt)(()=>(y=!1,_()));o&&G(b);var x=H;return(function(e,t){if(arguments.length>0){let n=t?G(b):a&&o?Yt(e):e;return N(b,n),y=!0,c!==void 0&&(c=n),e}return Vn&&y||x.f&16384?b.v:G(b)})}function Ai(){let e=Ci({controller:!1,busy:!1,error:``}),t,n=``,r={controller:!1,busy:!1,error:``},i=!1;function a(t){r={...r,...t},e.set(r)}async function o(e=!1){if(!i){i=!0;try{let t=await fetch(`/api/state`,{cache:`no-store`});if(!t.ok)throw Error(`The local setup session is unavailable.`);a({view:await t.json(),...e?{}:{error:``}})}catch{a({view:void 0,error:`Connection lost. The CLI may have stopped. Check the terminal before trying again.`})}finally{i=!1}}}async function s(){try{let e=await fetch(`/api/bootstrap`,{cache:`no-store`});if(!e.ok)throw Error(`Could not join this local session.`);let r=await e.json();t=r.capability,n=r.takeoverTicket,a({controller:r.controller,error:``}),await o()}catch{t=void 0,n=``,a({view:void 0,controller:!1,error:`Could not connect to the local setup session. Check the terminal.`})}}async function c(e,n,r=!0){let i=await fetch(e,{method:`POST`,cache:`no-store`,headers:{"Content-Type":`application/json`,...r&&t?{"X-Setup-Capability":t}:{}},body:JSON.stringify(n)}),a=await i.json();if(!i.ok)throw Error(String(a.error??`The request was rejected.`));return a}async function l(e,t){if(!r.busy&&r.controller&&r.view?.promptRevision===e){a({busy:!0,error:``});try{await c(`/api/answer`,{revision:e,value:t}),await o()}catch(e){let t=e instanceof Error?e.message:`Could not submit this answer.`;a({error:t}),/read-only|Control moved/.test(t)?await s():await o(!0)}finally{a({busy:!1})}}}async function u(){if(r.controller&&!r.busy){a({busy:!0,error:``});try{await c(`/api/cancel`,{}),await o()}catch(e){let t=e instanceof Error?e.message:`Cancellation failed.`;a({error:t}),/read-only|Control moved/.test(t)&&await s()}finally{a({busy:!1})}}}async function d(){try{let e=await c(`/api/takeover`,{ticket:n},!1);t=String(e.capability),a({controller:!0,error:``}),await o()}catch(e){a({error:e instanceof Error?e.message:`Takeover failed.`}),await s()}}async function f(){try{await c(`/api/close`,{})}catch{}}return{subscribe:e.subscribe,connect:s,refresh:o,submit:l,cancel:u,takeOver:d,close:f}}var ji=J(`
                                • `),Mi=J(``);function Ni(e,t){O(t,!1);let n=$(t,`journey`,8),r=[`Repository`,`Setup choices`,`Setup PAT`,`Plan`,`Bot PAT & credentials`,`Apply`];yi();var i=Mi(),a=I(P(i),4);Hr(a,5,()=>r,Rr,(e,t,r)=>{var i=ji();let a;var o=P(i),s=F(o,!0),c=F(I(o,2),!0);E(i),L(e=>{Q(i,`aria-current`,(q(n()),K(()=>n()?.position===r+1?`step`:void 0))),a=ei(i,1,``,null,a,{current:n()?.position===r+1,completed:(n()?.position??0)>r+1}),X(s,e),X(c,G(t))},[()=>(q(n()),K(()=>(n()?.position??0)>r+1?`✓`:String(r+1).padStart(2,`0`)))]),Y(e,i)}),E(a),ke(2),E(i),Y(e,i),k()}var Pi=J(`
                                  `);function Fi(e,t){O(t,!1);let n=M(`system`);wn(()=>G(n),()=>{document.documentElement.dataset.theme=G(n)}),Tn();var r=Pi(),i=P(r);let a;var o=I(i,2);let s;var c=I(o,2);let l;E(r),L(()=>{Q(i,`aria-pressed`,G(n)===`system`),a=ei(i,1,``,null,a,{active:G(n)===`system`}),Q(o,`aria-pressed`,G(n)===`light`),s=ei(o,1,``,null,s,{active:G(n)===`light`}),Q(c,`aria-pressed`,G(n)===`dark`),l=ei(c,1,``,null,l,{active:G(n)===`dark`})}),mr(`click`,i,()=>N(n,`system`)),mr(`click`,o,()=>N(n,`light`)),mr(`click`,c,()=>N(n,`dark`)),Y(e,r),k()}hr([`click`]);var Ii=J(`
                                  LOCAL SESSION
                                  `);function Li(e,t){let n=$(t,`repository`,8);var r=Ii(),i=P(r),a=F(I(P(i),2),!0);E(i);var o=I(i,2);Fi(I(P(o)),{}),E(o),E(r),L(()=>X(a,n()??`Connecting…`)),Y(e,r)}var Ri=J(`

                                  `,1);function zi(e,t){O(t,!1);let n=M(),r=$(t,`view`,8);wn(()=>q(r()),()=>{N(n,r()?.outcome===`complete`?`Setup complete.`:r()?.outcome===`dry-run`?`Preview complete.`:r()?.outcome===`cancelled`?`Setup cancelled.`:r()?.outcome?`Setup needs attention.`:r()?.prompt?.title??`Preparing your setup…`)}),Tn(),yi();var i=Ri(),a=sn(i),o=I(P(a)),s=F(I(o));E(a);var c=I(a,2),l=F(c,!0),u=F(I(c,2),!0);L((e,t)=>{X(o,` ${e??``} `),X(s,`${t??``} / 06`),X(l,G(n)),X(u,(q(r()),K(()=>r()?.outcome?`The terminal contains the detailed result. Review the next steps below before closing this session.`:`One decision at a time. Your choices shape the permissions, plan and credentials needed for this repository.`)))},[()=>(q(r()),K(()=>r()?.journey?.current?.toUpperCase()??`GETTING READY`)),()=>(q(r()),K(()=>String(r()?.journey?.position??1).padStart(2,`0`)))]),Y(e,i),k()}function Bi(e){try{let t=new URL(e??``);return t.protocol===`https:`&&t.hostname===`github.com`&&(t.pathname===`/settings/personal-access-tokens`||t.pathname.startsWith(`/settings/personal-access-tokens/`))?t.toString():void 0}catch{return}}var Vi=J(``),Hi=J(``);function Ui(e,t){let n=$(t,`label`,8),r=$(t,`variant`,8,`primary`),i=$(t,`disabled`,8,!1),a=$(t,`arrow`,8,!1),o=$(t,`onClick`,8);var s=Hi(),c=P(s,!0),l=I(c),u=e=>{Y(e,Vi())};Z(l,e=>{a()&&e(u)}),E(s),L(()=>{ei(s,1,Zr(r())),s.disabled=i(),X(c,n())}),mr(`click`,s,function(...e){o()?.apply(this,e)}),Y(e,s)}hr([`click`]);var Wi=J(`Open GitHub link ↗`),Gi=J(`

                                  `);function Ki(e,t){O(t,!1);let n=M(),r=$(t,`tone`,8,`info`),i=$(t,`title`,8),a=$(t,`message`,8),o=$(t,`link`,8,void 0),s=$(t,`actionLabel`,8,void 0),c=$(t,`onAction`,8,void 0);wn(()=>q(o()),()=>{N(n,Bi(o()))}),Tn(),yi();var l=Gi(),u=P(l),d=F(u,!0),f=I(u),p=F(f,!0),m=I(f,2),h=e=>{var t=Wi();L(()=>Q(t,`href`,G(n))),Y(e,t)};Z(m,e=>{G(n)&&e(h)});var g=I(m,2),_=e=>{Ui(e,{get label(){return s()},variant:`secondary`,get onClick(){return c()}})};Z(g,e=>{s()&&c()&&e(_)}),E(l),L(()=>{ei(l,1,`banner ${r()??``}`),Q(l,`role`,r()===`error`?`alert`:`status`),X(d,i()),X(p,a())}),Y(e,l),k()}function qi(e){let t=String(e.question.defaultValue),n=t.split(`,`).map(e=>e.trim()).filter(Boolean);return{value:t,selected:e.question.kind===`multi-select`?(e.question.choices??[]).filter(e=>e!==`All`&&n.includes(e.split(` — `)[0])):e.question.kind===`scope-overrides`?n:[]}}function Ji(e,t){return t===`All`?e.includes(`All`)?[]:[`All`]:e.includes(t)?e.filter(e=>e!==t):[...e.filter(e=>e!==`All`),t]}function Yi(e,t,n){return e.question.kind===`scope-overrides`||e.question.kind===`multi-select`?n.length?n.join(`,`):`none`:t}var Xi=J(`PERMISSION PREVIEW`),Zi=J(`
                                  `),Qi=J(``),$i=J(``),ea=J(``),ta=J(`
                                  `),na=J(``),ra=J(`

                                  `,1);function ia(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`controller`,8),i=$(t,`busy`,8),a=$(t,`onSubmit`,8),o=qi(n()),s=M(o.value),c=M(o.selected);yi();var l=ra(),u=sn(l),d=P(u),f=F(d,!0),p=I(d),m=e=>{Y(e,Xi())};Z(p,e=>{q(n()),K(()=>n().phase===`permission-intent`)&&e(m)}),E(u);var h=I(u,2),g=e=>{var t=Zi(),i=P(t);let a;var o=I(i);let c;E(t),L(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),Q(i,`aria-pressed`,G(s)===`yes`||G(s)===`true`),i.disabled=!r(),a=ei(i,1,``,null,a,{selected:G(s)===`yes`||G(s)===`true`}),Q(o,`aria-pressed`,G(s)===`no`||G(s)===`false`),o.disabled=!r(),c=ei(o,1,``,null,c,{selected:G(s)===`no`||G(s)===`false`})}),mr(`click`,i,()=>N(s,`yes`)),mr(`click`,o,()=>N(s,`no`)),Y(e,t)},_=e=>{var t=$i();Hr(t,5,()=>(q(n()),K(()=>n().question.choices??[])),Rr,(e,t)=>{var n=Qi(),r=F(n,!0),i={};L(()=>{X(r,G(t)),i!==(i=G(t))&&(n.value=(n.__value=i)??``)}),Y(e,n)}),E(t),ii(t),L(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),t.disabled=!r()}),ai(t,()=>G(s),e=>N(s,e)),Y(e,t)},v=e=>{var t=ta();Hr(t,5,()=>(q(n()),K(()=>n().question.kind===`multi-select`?n().question.choices??[]:n().question.allowedNames??[])),Rr,(e,t)=>{var n=ea(),i=P(n);di(i);var a=F(I(i),!0);E(n),L(e=>{fi(i,e),i.disabled=!r(),X(a,G(t))},[()=>(G(c),G(t),K(()=>G(c).includes(G(t))))]),mr(`change`,i,()=>N(c,Ji(G(c),G(t)))),Y(e,n)}),E(t),L(()=>Q(t,`aria-label`,(q(n()),K(()=>n().question.label)))),Y(e,t)},y=e=>{var t=na();di(t),L(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),Q(t,`type`,(q(n()),K(()=>n().question.kind===`number`?`number`:`text`))),Q(t,`min`,(q(n()),K(()=>n().question.kind===`number`?0:void 0))),t.disabled=!r()}),gi(t,()=>G(s),e=>N(s,e)),Y(e,t)};Z(h,e=>{q(n()),K(()=>n().question.kind===`boolean`)?e(g):(q(n()),K(()=>n().question.kind===`choice`)?e(_,1):(q(n()),K(()=>n().question.kind===`multi-select`||n().question.kind===`scope-overrides`)?e(v,2):e(y,-1)))});var b=I(h,2),x=F(b),S=I(b,2);{let e=mt(()=>!r()||i());Ui(S,{label:`Continue`,arrow:!0,onClick:()=>a()(Yi(n(),G(s),G(c))),get disabled(){return G(e)}})}L((e,t)=>{X(f,e),X(x,`Suggested answer: ${t??``} · You can review choices again before creating your setup PAT.`)},[()=>(q(n()),K(()=>n().question.label.replace(` (Space toggles, Enter confirms)`,``))),()=>(q(n()),K(()=>String(n().question.defaultValue)||`none`))]),Y(e,l),k()}hr([`click`,`change`]);var aa=J(``),oa=J(`
                                  `);function sa(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`controller`,8),i=$(t,`busy`,8),a=$(t,`onSubmit`,8);yi();var o=oa();Hr(o,5,()=>(q(n()),K(()=>n().choices)),Rr,(e,t)=>{var n=aa(),o=F(P(n),!0);ke(),E(n),L(()=>{n.disabled=!r()||i(),X(o,G(t))}),mr(`click`,n,()=>a()(G(t))),Y(e,n)}),E(o),Y(e,o),k()}hr([`click`]);var ca=J(`Open the official GitHub PAT form

                                  Check the signed-in account, select Only select repositories, then select this repository. GitHub handles 2FA and creates the PAT.

                                  `,1),la=J(`

                                  Sent only to this local process. It will not be shown again or saved in browser storage.

                                  `),ua=J(` `,1);function da(e,t){O(t,!1);let n=M(),r=$(t,`prompt`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8),s=M(``);function c(){let e=G(s);r().kind===`secret`&&N(s,``),o()(e)}wn(()=>q(r()),()=>{N(n,Bi(r().link))}),Tn(),yi();var l=ua(),u=sn(l),d=e=>{var t=ca(),r=sn(t);ke(),L(()=>Q(r,`href`,G(n))),Y(e,t)};Z(u,e=>{G(n)&&e(d)});var f=I(u,2),p=F(f,!0),m=I(f,2);di(m);var h=I(m,2),g=e=>{Y(e,la())};Z(h,e=>{q(r()),K(()=>r().kind===`secret`)&&e(g)});var _=I(h,2);{let e=mt(()=>(q(i()),q(a()),q(r()),G(s),K(()=>!i()||a()||!r().optional&&!G(s).trim())));Ui(_,{label:`Continue`,arrow:!0,onClick:c,get disabled(){return G(e)}})}L(()=>{X(p,(q(r()),K(()=>r().kind===`secret`?`Paste the value here`:`Your answer`))),Q(m,`type`,(q(r()),K(()=>r().kind===`secret`?`password`:`text`))),m.disabled=!i(),Q(m,`placeholder`,(q(r()),K(()=>r().kind===`secret`?`Hidden after submission`:`Type your answer`)))}),gi(m,()=>G(s),e=>N(s,e)),Y(e,l),k()}var fa=J(`
                                • `),pa=J(`

                                    `),ma=J(`

                                    Before you continue

                                      `),ha=J(`

                                      Review exactly what this run may change. The bot PAT and any additional credentials are collected next.

                                      `,1);function ga(e,t){O(t,!1);let n=M(),r=$(t,`prompt`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8);wn(()=>q(r()),()=>{N(n,[{title:`Files`,items:r().plan.files},{title:`Workflows`,items:r().plan.workflows},{title:`Variables`,items:r().plan.variables},{title:`Secret names`,items:r().plan.secrets}])}),Tn(),yi();var s=ha(),c=I(sn(s),2);Hr(c,5,()=>G(n),Rr,(e,t)=>{var n=pa(),r=P(n),i=P(r),a=F(I(i),!0);E(r);var o=I(r);Hr(o,5,()=>(G(t),K(()=>G(t).items)),Rr,(e,t)=>{var n=fa(),r=F(n,!0);L(()=>X(r,G(t))),Y(e,n)}),E(o),E(n),L(()=>{X(i,`${G(t),K(()=>G(t).title)??``} `),X(a,(G(t),K(()=>G(t).items.length)))}),Y(e,n)}),E(c);var l=I(c,2),u=e=>{var t=ma(),n=I(P(t));Hr(n,5,()=>(q(r()),K(()=>r().plan.warnings)),Rr,(e,t)=>{var n=fa(),r=F(n,!0);L(()=>X(r,G(t))),Y(e,n)}),E(n),E(t),Y(e,t)};Z(l,e=>{q(r()),K(()=>r().plan.warnings.length)&&e(u)});var d=I(l,2),f=P(d);{let e=mt(()=>!i()||a());Ui(f,{label:`Stop here`,variant:`secondary`,onClick:()=>o()(`decline`),get disabled(){return G(e)}})}var p=I(f);{let e=mt(()=>!i()||a());Ui(p,{label:`Approve this plan`,arrow:!0,onClick:()=>o()(`approve`),get disabled(){return G(e)}})}E(d),Y(e,s),k()}var _a=J(`

                                      `),va=J(`
                                      CURRENT DECISION
                                      `);function ya(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`revision`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8);yi();var s=va(),c=P(s),l=F(I(P(c)));E(c);var u=I(c,2),d=e=>{var t=_a(),r=F(t,!0);L(()=>X(r,(q(n()),K(()=>n().description)))),Y(e,t)};Z(u,e=>{q(n()),K(()=>`description`in n()&&n().description)&&e(d)});var f=I(u,2),p=e=>{ia(e,{get prompt(){return n()},get controller(){return i()},get busy(){return a()},get onSubmit(){return o()}})},m=e=>{sa(e,{get prompt(){return n()},get controller(){return i()},get busy(){return a()},get onSubmit(){return o()}})},h=e=>{da(e,{get prompt(){return n()},get controller(){return i()},get busy(){return a()},get onSubmit(){return o()}})},g=e=>{ga(e,{get prompt(){return n()},get controller(){return i()},get busy(){return a()},get onSubmit(){return o()}})};Z(f,e=>{q(n()),K(()=>n().kind===`question`)?e(p):(q(n()),K(()=>n().kind===`choice`||n().kind===`confirm`)?e(m,1):(q(n()),K(()=>n().kind===`text`||n().kind===`secret`)?e(h,2):(q(n()),K(()=>n().kind===`plan`)&&e(g,3))))}),E(s),L(()=>X(l,`SESSION ${r()??``}`)),Y(e,s),k()}var ba=J(` `),xa=J(`
                                    • `),Sa=J(`

                                        Conditional grants depend on the choices and GitHub facts. A final audit runs before setup changes.
                                        `),Ca=J(`

                                        Permissions follow your choices

                                        We’ll show the exact grants before you create each PAT. Nothing is created just by opening this page.

                                        `),wa=J(``);function Ta(e,t){O(t,!1);let n=$(t,`view`,8);yi();var r=wa(),i=P(r),a=F(I(P(i),3),!0);E(i);var o=I(i,2),s=e=>{var t=Sa(),r=I(P(t)),i=F(r),a=I(r),o=F(a,!0),s=I(a,2);Hr(s,5,()=>(q(n()),K(()=>n().permissions.report?.checks??n().permissions.requirements??[])),Rr,(e,t)=>{var n=xa(),r=P(n),i=P(r),a=I(i),o=F(a),s=F(I(a),!0);E(r);var c=I(r),l=P(c,!0),u=I(l),d=e=>{var n=ba(),r=F(n,!0);L(()=>X(r,(G(t),K(()=>G(t).status)))),Y(e,n)};Z(u,e=>{`status`in G(t)&&e(d)}),E(c),E(n),L(()=>{X(i,`${G(t),K(()=>G(t).permission)??``} `),X(o,`${G(t),K(()=>G(t).scope)??``} · ${G(t),K(()=>G(t).applicability)??``}`),X(s,(G(t),K(()=>G(t).reason))),X(l,(G(t),K(()=>G(t).level)))}),Y(e,n)}),E(s),ke(2),E(t),L(()=>{X(i,`${q(n()),K(()=>n().permissions.role===`setup`?`Setup PAT`:`Bot PAT`)??``} access`),X(o,(q(n()),K(()=>n().permissions.report?`Read-only access check`:`Provisional least-privilege grants`)))}),Y(e,t)},c=e=>{Y(e,Ca())};Z(o,e=>{q(n()),K(()=>n().permissions)?e(s):e(c,-1)}),E(r),L(()=>X(a,(q(n()),K(()=>n().repository)))),Y(e,r),k()}var Ea=J(`

                                        `);function Da(e,t){O(t,!1);let n=M(),r=M(),i=$(t,`outcome`,8),a=$(t,`controller`,8),o=$(t,`onClose`,8);wn(()=>q(i()),()=>{N(n,i()===`complete`?`Your configuration was applied`:i()===`dry-run`?`No changes were made`:i()===`cancelled`||i()===`blocked`?`No setup changes started`:`Check partial changes before retrying`)}),wn(()=>q(i()),()=>{N(r,i()===`complete`?`The temporary setup PAT is not revoked automatically. Delete it in GitHub after confirming your setup. Keep the bot PAT until you rotate the installed Secret.`:i()===`partial`?`A bot Secret write or another change may have succeeded. Inspect GitHub and run copilot doctor before replacing or deleting its PAT.`:`Copilot did not begin applying setup changes. PATs you created in GitHub still exist until you delete them there.`)}),Tn();var s=Ea(),c=P(s),l=F(c,!0),u=I(c),d=F(u,!0),f=I(u),p=F(f,!0),m=I(f,2),h=e=>{Ui(e,{label:`Close local session`,get onClick(){return o()}})};Z(m,e=>{a()&&e(h)}),E(s),L(()=>{X(l,i()===`complete`?`✓`:`!`),X(d,G(n)),X(p,G(r))}),Y(e,s),k()}var Oa=J(`

                                        Working on the next step

                                        The local process is checking your answers and preparing the next decision. Keep this page open.

                                        `);function ka(e){Y(e,Oa())}var Aa=J(``),ja=J(`
                                        `),Ma=J(``),Na=J(`
                                        `,1),Pa=J(`
                                        LOCALHOST ONLY NO CLOUD SETUP ACCOUNT GITHUB OWNS PAT ISSUANCE
                                        `);function Fa(e,t){O(t,!1);let n=()=>Di(a,`$session`,r),[r,i]=Oi(),a=Ai();Pr(()=>{a.connect();let e=window.setInterval(()=>{n().view?.outcome||a.refresh()},900);return()=>window.clearInterval(e)});async function o(){window.confirm(`Cancel this local setup session? PATs already created in GitHub will still exist.`)&&await a.cancel()}async function s(e){let t=n().view?.promptRevision;t!==void 0&&await a.submit(t,e)}yi();var c=Pa();Jr(`16t12jp`,e=>{Y(e,Aa())});var l=P(c);{let e=mt(()=>n().view?.journey);Ni(l,{get journey(){return G(e)}})}var u=I(l,2),d=P(u);{let e=mt(()=>n().view?.repository);Li(d,{get repository(){return G(e)}})}var f=I(d,2),p=P(f);zi(p,{get view(){return n().view}});var m=I(p,2),h=e=>{var t=ja(),r=I(P(t));E(t),L(()=>X(r,` Reviewing saved choices — pass ${n().view.journey.choiceReviewPass??``}. This is the same setup run, not a restart.`)),Y(e,t)};Z(m,e=>{n().view?.journey?.choiceReviewPass&&n().view.journey.choiceReviewPass>1&&!n().view.outcome&&e(h)});var g=I(m,2),_=e=>{Ki(e,{tone:`warning`,title:`Read-only tab`,message:`Another tab controls this session. You can review progress here or explicitly take over.`,actionLabel:`Take control in this tab`,get onAction(){return a.takeOver}})};Z(g,e=>{!n().controller&&n().view&&e(_)});var v=I(g,2),y=e=>{Ki(e,{tone:`error`,title:`Needs attention`,get message(){return n().error}})};Z(v,e=>{n().error&&e(y)});var b=I(v,2),x=e=>{{let t=mt(()=>n().view.message.tone===`success`?`Checked`:n().view.message.tone===`warning`?`Please note`:n().view.message.tone===`error`?`Needs attention`:`Progress update`);Ki(e,{get tone(){return n().view.message.tone},get title(){return G(t)},get message(){return n().view.message.text},get link(){return n().view.message.link}})}};Z(b,e=>{n().view?.message&&e(x)});var S=I(b,2),ee=e=>{Da(e,{get outcome(){return n().view.outcome},get controller(){return n().controller},get onClose(){return a.close}})},te=e=>{var t=Na(),r=sn(t),i=P(r);Lr(i,()=>n().view.promptRevision,e=>{ya(e,{get prompt(){return n().view.prompt},get revision(){return n().view.revision},get controller(){return n().controller},get busy(){return n().busy},onSubmit:s})}),Ta(I(i,2),{get view(){return n().view}}),E(r);var a=I(r,2),c=e=>{var t=Ma();L(()=>t.disabled=n().busy),mr(`click`,t,o),Y(e,t)};Z(a,e=>{n().controller&&n().view.journey?.current!==`Apply`&&e(c)}),Y(e,t)},ne=e=>{ka(e,{})};Z(S,e=>{n().view?.outcome?e(ee):n().view?.prompt?e(te,1):e(ne,-1)}),ke(2),E(f),E(u),E(c),Y(e,c),k(),i()}hr([`click`]),kr(Fa,{target:document.getElementById(`app`)}); \ No newline at end of file diff --git a/build/web/assets/index-DzCTZuhQ.js b/build/web/assets/index-DzCTZuhQ.js new file mode 100644 index 000000000..62f0f1a1c --- /dev/null +++ b/build/web/assets/index-DzCTZuhQ.js @@ -0,0 +1,2 @@ +(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})(),typeof window<`u`&&((window.__svelte??={}).v??=new Set).add(`5`);var e=!1;function t(){e=!0}t();var n={},r=Symbol(`uninitialized`),i=`http://www.w3.org/1999/xhtml`,a=Array.isArray,o=Array.prototype.indexOf,s=Array.prototype.includes,c=Array.from,l=Object.defineProperty,u=Object.getOwnPropertyDescriptor,d=Object.getOwnPropertyDescriptors,f=Object.prototype,p=Array.prototype,m=Object.getPrototypeOf,h=Object.isExtensible,g=()=>{};function _(e){return e()}function v(e){for(var t=0;t{e=n,t=r}),resolve:e,reject:t}}var b=1024,x=2048,S=4096,ee=8192,te=16384,ne=32768,re=1<<25,ie=65536,ae=1<<18,oe=1<<19,se=1<<20,ce=1<<25,le=1<<21,ue=1<<22,de=1<<23,fe=Symbol(`$state`),pe=Symbol(`component`),me=Symbol(`legacy props`),he=Symbol(``),ge=Symbol(`attributes`),_e=Symbol(`class`),ve=Symbol(`style`),ye=Symbol(`text`),be=Symbol(`form reset`),xe=new class extends Error{name=`StaleReactionError`;message="The reaction that called `getAbortSignal()` was re-run or destroyed"},Se=!!globalThis.document?.contentType&&globalThis.document.contentType.includes(`xml`);function Ce(){console.warn(`https://svelte.dev/e/derived_inert`)}function we(e){console.warn(`https://svelte.dev/e/hydration_mismatch`)}function Te(){console.warn(`https://svelte.dev/e/select_multiple_invalid_value`)}function Ee(){console.warn(`https://svelte.dev/e/svelte_boundary_reset_noop`)}var C=!1;function De(e){C=e}var w;function T(e){if(e===null)throw we(),n;return w=e}function Oe(){return T(an(w))}function E(e){if(C){if(an(w)!==null)throw we(),n;w=e}}function ke(e=1){if(C){for(var t=e,n=w;t--;)n=an(n);w=n}}function Ae(e=!0){for(var t=0,n=w;;){if(n.nodeType===8){var r=n.data;if(r===`]`){if(t===0)return n;--t}else(r===`[`||r===`[!`||r[0]===`[`&&!isNaN(Number(r.slice(1))))&&(t+=1)}var i=an(n);e&&n.remove(),n=i}}function je(e){if(!e||e.nodeType!==8)throw we(),n;return e.data}function Me(e){return e===this.v}function Ne(e,t){return e==e?e!==t||typeof e==`object`&&!!e||typeof e==`function`:t==t}function Pe(e){return!Ne(e,this.v)}function Fe(e){throw Error(`https://svelte.dev/e/lifecycle_outside_component`)}function Ie(){throw Error(`https://svelte.dev/e/async_derived_orphan`)}function Le(e,t,n){throw Error(`https://svelte.dev/e/each_key_duplicate`)}function Re(e){throw Error(`https://svelte.dev/e/effect_in_teardown`)}function ze(){throw Error(`https://svelte.dev/e/effect_in_unowned_derived`)}function Be(e){throw Error(`https://svelte.dev/e/effect_orphan`)}function Ve(){throw Error(`https://svelte.dev/e/effect_update_depth_exceeded`)}function He(e){throw Error(`https://svelte.dev/e/props_invalid_value`)}function Ue(){throw Error(`https://svelte.dev/e/state_descriptors_fixed`)}function We(){throw Error(`https://svelte.dev/e/state_prototype_fixed`)}function Ge(){throw Error(`https://svelte.dev/e/state_unsafe_mutation`)}function Ke(){throw Error(`https://svelte.dev/e/svelte_boundary_reset_onerror`)}var D=null;function qe(e){D=e}function O(t,n=!1,r){D={p:D,i:!1,c:null,e:null,s:t,x:null,r:H,l:e&&!n?{s:null,u:null,$:[]}:null}}function k(e){var t=D,n=t.e;if(n!==null){t.e=null;for(var r of n)yn(r)}return e!==void 0&&(t.x=e),t.i=!0,D=t.p,Je(e)}function Je(e={}){return l(e,pe,{value:!0}),e}function Ye(){return!e||D!==null&&D.l===null}var Xe=[];function Ze(){var e=Xe;Xe=[],v(e)}function Qe(e){if(Xe.length===0&&!wt){var t=Xe;queueMicrotask(()=>{t===Xe&&Ze()})}Xe.push(e)}function $e(){for(;Xe.length>0;)Ze()}var et=~(x|S|b);function A(e,t){e.f=e.f&et|t}function tt(e){e.f&512||e.deps===null?A(e,b):A(e,S)}function nt(e,t,n){e.f&2048?t.add(e):e.f&4096&&n.add(e),A(e,b)}var rt=!1;function it(){rt||(rt=!0,document.addEventListener(`reset`,e=>{Promise.resolve().then(()=>{if(!e.defaultPrevented)for(let t of e.target.elements)t[be]?.()})},{capture:!0}))}function at(e){var t=V,n=H;Un(null),Wn(null);try{return e()}finally{Un(t),Wn(n)}}function ot(e,t,n,r=n){e.addEventListener(t,()=>at(n));let i=e[be];e[be]=i?()=>{i(),r(!0)}:()=>r(!0),it()}function st(e,t,n,r){let i=Ye()?dt:mt;var a=e.filter(e=>!e.settled),o=t.map(i);if(n.length===0&&a.length===0){r(o);return}var s=H,c=ct(),l=a.length===1?a[0].promise:a.length>1?Promise.all(a.map(e=>e.promise)):null;function u(e){if(!(s.f&16384)){c();try{r([...o,...e])}catch(e){fn(e,s)}lt()}}var d=ut();if(n.length===0){l.then(()=>u([])).finally(d);return}function f(){Promise.all(n.map(e=>pt(e))).then(u).catch(e=>fn(e,s)).finally(d)}l?l.then(()=>{c(),f(),lt()}):f()}function ct(){var e=H,t=V,n=D,r=j;return function(i=!0){Wn(e),Un(t),qe(n),i&&!(e.f&16384)&&(r?.activate(),r?.apply())}}function lt(e=!0){Wn(null),Un(null),qe(null),e&&j?.deactivate()}function ut(){var e=H,t=e.b,n=j,r=!!t?.is_rendered();return t?.update_pending_count(1,n),n.increment(r,e),()=>{t?.update_pending_count(-1,n),n.decrement(r,e)}}function dt(e){var t=2|x;return H!==null&&(H.f|=oe),{ctx:D,deps:null,effects:null,equals:Me,f:t,fn:e,reactions:null,rv:0,v:r,wv:0,parent:H,ac:null}}var ft=Symbol(`obsolete`);function pt(e,t,n){let i=H;i===null&&Ie();var a=void 0,o=Vt(r),s=!V,c=new Set;return Tn(()=>{var t=H,n=y();a=n.promise;try{Promise.resolve(e()).then(n.resolve,e=>{e!==xe&&n.reject(e)}).finally(lt)}catch(e){n.reject(e),lt()}var r=j;if(s){if(t.f&32768)var l=ut();if(i.b?.is_rendered())r.async_deriveds.get(t)?.reject(ft);else for(let e of c.values())e.reject(ft);c.add(n),r.async_deriveds.set(t,n)}let u=(e,t=void 0)=>{l?.(),c.delete(n),t!==ft&&(r.activate(),t?(o.f|=de,Gt(o,t)):(o.f&8388608&&(o.f^=de),Gt(o,e)),r.deactivate())};n.promise.then(u,e=>u(null,e||`unknown`))}),_n(()=>{for(let e of c)e.reject(ft)}),new Promise(e=>{function t(n){function r(){n===a?e(o):t(a)}n.then(r,r)}t(a)})}function mt(e){let t=dt(e);return t.equals=Pe,t}function ht(e){var t=e.effects;if(t!==null){e.effects=null;for(var n=0;n{t.ac.abort(xe),t.ac=null}),t.fn!==null&&(t.teardown=g),ar(t,0),kn(t))}function yt(e){if(e.effects!==null)for(let t of e.effects)t.teardown&&t.fn!==null&&or(t)}var bt=null,j=null,xt=null,St=null,Ct=null,wt=!1,Tt=!1,Et=null,Dt=null,Ot=0,kt=1,At=class e{id=kt++;#e=!1;linked=!0;#t=null;#n=null;async_deriveds=new Map;current=new Map;previous=new Map;#r=new Set;#i=new Set;#a=0;#o=new Map;#s=null;#c=[];#l=[];#u=new Set;#d=new Set;#f=new Map;#p=new Set;is_fork=!1;#m=!1;constructor(){bt===null?bt=this:(bt.#n=this,this.#t=bt),bt=this}#h(){if(this.is_fork)return!0;for(let n of this.#o.keys()){for(var e=n,t=!1;e.parent!==null;){if(this.#f.has(e)){t=!0;break}e=e.parent}if(!t)return!0}return!1}skip_effect(e){this.#f.has(e)||this.#f.set(e,{d:[],m:[]}),this.#p.delete(e)}unskip_effect(e,t=e=>this.schedule(e)){var n=this.#f.get(e);if(n){this.#f.delete(e);for(var r of n.d)A(r,x),t(r);for(r of n.m)A(r,S),t(r)}this.#p.add(e)}#g(){var e=[];for(let i of this.#c)if(!(i.f&16384||!(i.f&6144))){for(var t=i,n=!1;t.parent!==null;){t=t.parent;var r=t.f;if(r&96){if(!(r&1024)){n=!0;break}t.f^=b}}n||e.push(t)}return this.#c=[],e}#_(){this.#e=!0;for(let e of this.#u)this.#d.delete(e),A(e,x),this.schedule(e);for(let e of this.#d)A(e,S),this.schedule(e);this.apply();for(var t=Et=[],n=[],r=Dt=[];this.#c.length>0;){Ot++>1e3&&(this.#S(),Mt());for(let e of this.#g())try{this.#v(e,t,n)}catch(t){throw Lt(e),this.#h()||this.discard(),t}}if(j=null,r.length>0){var i=e.ensure();for(let e of r)i.schedule(e)}if(Et=null,Dt=null,this.#h()){this.#x(n),this.#x(t);for(let[e,t]of this.#f)It(e,t);r.length>0&&j.#_();return}let a=this.#y();if(a){this.#x(n),this.#x(t),a.#b(this);return}this.#u.clear(),this.#d.clear();for(let e of this.#r)e(this);this.#r.clear(),xt=this,Pt(n),Pt(t),xt=null,this.#s?.resolve();var o=j;if(this.#a===0&&(this.#c.length===0||o!==null)&&this.#S(),this.#c.length>0){if(o!==null){for(let e of this.#c)o.#c.push(e);this.#c=[]}else o=this}o!==null&&(zt.clear(),o.#_())}#v(e,t,n){e.f^=b;for(var r=e.first;r!==null;){var i=r.f,a=!!(i&96);if(!(a&&i&1024||i&8192||this.#f.has(r))&&r.fn!==null){a?r.f^=b:i&4?t.push(r):er(r)&&(i&16&&this.#d.add(r),or(r));var o=r.first;if(o!==null){r=o;continue}}for(;r!==null;){var s=r.next;if(s!==null){r=s;break}r=r.parent}}}#y(){for(var e=this.#t;e!==null;){if(!e.is_fork){for(let[t,[,n]]of this.current)if(e.current.has(t)&&!n)return e}e=e.#t}return null}#b(e){for(let[t,n]of e.current)!this.previous.has(t)&&e.previous.has(t)&&this.previous.set(t,e.previous.get(t)),this.current.set(t,n);for(let[t,n]of e.async_deriveds){let e=this.async_deriveds.get(t);e&&n.promise.then(e.resolve).catch(e.reject)}e.async_deriveds.clear(),this.transfer_effects(e.#u,e.#d);let t=e=>{var n=e.reactions;if(n!==null&&!(e.f&2&&!(e.f&6144)))for(let e of n){var r=e.f;if(r&2)t(e);else{var i=e;r&4194320&&!this.async_deriveds.has(i)&&(this.#d.delete(i),A(i,x),this.schedule(i))}}};for(let e of this.current.keys())t(e);this.oncommit(()=>e.discard()),e.#S(),j=this,this.#_()}#x(e){for(var t=0;t{this.#m=!1,this.linked&&this.flush()}))}transfer_effects(e,t){for(let t of e)this.#u.add(t);for(let e of t)this.#d.add(e);e.clear(),t.clear()}oncommit(e){this.#r.add(e)}ondiscard(e){this.#i.add(e)}settled(){return(this.#s??=y()).promise}static ensure(){if(j===null){let t=j=new e;!Tt&&!wt&&Qe(()=>{t.#e||t.flush()})}return j}apply(){St=null}schedule(e){if(Ct=e,e.b?.is_pending&&e.f&16777228&&!(e.f&32768)){e.b.defer_effect(e);return}this.#c.push(e)}#S(){if(this.linked){var e=this.#t,t=this.#n;e===null||(e.#n=t),t===null?bt=e:t.#t=e,this.linked=!1}}};function jt(e){var t=wt;wt=!0;try{var n;for(e&&(j!==null&&!j.is_fork&&j.flush(),n=e());;){if($e(),j===null)return n;j.flush()}}finally{wt=t}}function Mt(){try{Ve()}catch(e){fn(e,Ct)}}var Nt=null;function Pt(e){var t=e.length;if(t!==0){for(var n=0;n0)){zt.clear();for(let e of Nt){if(e.f&24576)continue;let t=[e],n=e.parent;for(;n!==null;)Nt.has(n)&&(Nt.delete(n),t.push(n)),n=n.parent;for(let e=t.length-1;e>=0;e--){let n=t[e];n.f&24576||or(n)}}Nt.clear()}}Nt=null}}function Ft(e){j.schedule(e)}function It(e,t){if(!(e.f&32&&e.f&1024)){e.f&2048?t.d.push(e):e.f&4096&&t.m.push(e),A(e,b);for(var n=e.first;n!==null;)It(n,t),n=n.next}}function Lt(e){A(e,b);for(var t=e.first;t!==null;)Lt(t),t=t.next}var Rt=new Set,zt=new Map,Bt=!1;function Vt(e,t){return{f:0,v:e,reactions:null,equals:Me,rv:0,wv:0}}function Ht(e,t){let n=Vt(e,t);return Kn(n),n}function M(t,n=!1,r=!0){let i=Vt(t);return n||(i.equals=Pe),e&&r&&D!==null&&D.l!==null&&(D.l.s??=[]).push(i),i}function N(e,t,n=!1){return V!==null&&(!Hn||V.f&131072)&&Ye()&&V.f&4325394&&(Gn===null||!Gn.has(e))&&Ge(),Gt(e,n?Yt(t):t,Dt)}var Ut=null,Wt=0;function Gt(e,t,n=null){if(!e.equals(t)){Bn?zt.set(e,t):zt.has(e)||zt.set(e,e.v);var r=At.ensure();if(r.capture(e,t),e.f&2){let t=e;e.f&2048&>(t),St===null&&tt(t)}e.wv=$n(),Ut=null,Wt=0,Jt(e,x,n),Ut=null,Ye()&&H!==null&&H.f&1024&&!(H.f&96)&&(qn===null?Jn([e]):qn.push(e)),!r.is_fork&&Rt.size>0&&!Bt&&Kt()}return t}function Kt(){Bt=!1;for(let e of Rt){e.f&1024&&A(e,S);let t;try{t=er(e)}catch{t=!0}t&&or(e)}Rt.clear()}function qt(e){N(e,e.v+1)}function Jt(e,t,n){var r=e.reactions;if(r!==null){var i=Ye(),a=r.length;if(Wt+=a,Wt>1e5&&Ut===null&&(Ut=new Set),Ut!==null){if(Ut.has(e))return;Ut.add(e)}for(var o=0;o{if(Zn===c)return e();var t=V,n=Zn;Un(null),Qn(c);var r=e();return Un(t),Qn(n),r};return i&&n.set(`length`,Ht(e.length,s)),new Proxy(e,{defineProperty(e,t,r){(!(`value`in r)||r.configurable===!1||r.enumerable===!1||r.writable===!1)&&Ue();var i=n.get(t);return i===void 0?l(()=>{var e=Ht(r.value,s);return n.set(t,e),e}):N(i,r.value,!0),!0},deleteProperty(e,t){var i=n.get(t);if(i===void 0){if(t in e){let e=l(()=>Ht(r,s));n.set(t,e),qt(o)}}else N(i,r),qt(o);return!0},get(t,i,a){if(i===fe)return e;var o=n.get(i),c=i in t;if(o===void 0&&(!c||u(t,i)?.writable)&&(o=l(()=>Ht(Yt(c?t[i]:r),s)),n.set(i,o)),o!==void 0){var d=G(o);return d===r?void 0:d}return Reflect.get(t,i,a)},getOwnPropertyDescriptor(e,t){this.has?.(e,t);var i=Reflect.getOwnPropertyDescriptor(e,t),a=n.get(t);if(a!==void 0){var o=G(a);if(o===r)return;if(i&&`value`in i)i.value=o;else return{enumerable:!0,configurable:!0,value:o,writable:!0}}return i},has(e,t){if(t===fe)return!0;var i=n.get(t),a=i!==void 0&&i.v!==r||Reflect.has(e,t);return(i!==void 0||H!==null&&(!a||u(e,t)?.writable))&&(i===void 0&&(i=l(()=>Ht(a?Yt(e[t]):r,s)),n.set(t,i)),G(i)===r)?!1:a},set(e,t,a,c){var d=n.get(t),f=t in e;if(i&&t===`length`)for(var p=a;pHt(r,s)),n.set(p+``,m)):N(m,r)}if(d===void 0)(!f||u(e,t)?.writable)&&(d=l(()=>Ht(void 0,s)),N(d,Yt(a)),n.set(t,d));else{f=d.v!==r;var h=l(()=>Yt(a));N(d,h)}var g=Reflect.getOwnPropertyDescriptor(e,t);if(g?.set&&g.set.call(c,a),!f){if(i&&typeof t==`string`){var _=n.get(`length`),v=Number(t);Number.isInteger(v)&&v>=_.v&&N(_,v+1)}qt(o)}return!0},ownKeys(e){G(o);var t=Reflect.ownKeys(e).filter(e=>{var t=n.get(e);return t===void 0||t.v!==r});for(var[i,a]of n)a.v!==r&&!(i in e)&&t.push(i);return t},setPrototypeOf(){We()}})}function Xt(e){try{if(typeof e==`object`&&e&&fe in e)return e[fe]}catch{}return e}function Zt(e,t){return Object.is(Xt(e),Xt(t))}var Qt,$t,en,tn;function nn(){if(Qt===void 0){Qt=window,$t=/Firefox/.test(navigator.userAgent);var e=Element.prototype,t=Node.prototype,n=Text.prototype;en=u(t,`firstChild`).get,tn=u(t,`nextSibling`).get,h(e)&&(e[_e]=void 0,e[ge]=null,e[ve]=void 0,e.__e=void 0),h(n)&&(n[ye]=void 0)}}function P(e=``){return document.createTextNode(e)}function rn(e){return en.call(e)}function an(e){return tn.call(e)}function F(e,t){if(!C)return rn(e);var n=rn(w);if(n===null)n=w.appendChild(P());else if(t&&n.nodeType!==3){var r=P();return n?.before(r),T(r),r}return t&&un(n),T(n),n}function on(e,t=!1){if(!C){var n=rn(e);return n instanceof Comment&&n.data===``?an(n):n}if(t){if(w?.nodeType!==3){var r=P();return w?.before(r),T(r),r}un(w)}return w}function I(e,t=!1){if(!C)return rn(e);var n=F(e,t);return E(e),n}function L(e,t=1,n=!1){let r=C?w:e;for(var i;t--;)i=r,r=an(r);if(!C)return r;if(n){if(r?.nodeType!==3){var a=P();return r===null?i?.after(a):r.before(a),T(a),a}un(r)}return T(r),r}function sn(e){e.textContent=``}function cn(){return!1}function ln(e,t,n){return t==null||t===`http://www.w3.org/1999/xhtml`?n?document.createElement(e,{is:n}):document.createElement(e):n?document.createElementNS(t,e,{is:n}):document.createElementNS(t,e)}function un(e){if(e.nodeValue.length<65536)return;let t=e.nextSibling;for(;t!==null&&t.nodeType===3;)t.remove(),e.nodeValue+=t.nodeValue,t=e.nextSibling}function dn(e){var t=H;if(t===null)return V.f|=de,e;if(!(t.f&32768)&&!(t.f&4))throw e;fn(e,t)}function fn(e,t){if(!(t!==null&&t.f&16384)){for(;t!==null;){if(t.f&128&&!(t.f&33570816)){if(!(t.f&32768))throw e;try{t.b.error(e);return}catch(t){e=t}}t=t.parent}throw e}}function pn(e){H===null&&(V===null&&Be(e),ze()),Bn&&Re(e)}function mn(e,t){var n=t.last;n===null?t.last=t.first=e:(n.next=e,e.prev=n,t.last=e)}function hn(e,t){var n=H;n!==null&&n.f&8192&&(e|=ee);var r={ctx:D,deps:null,nodes:null,f:e|x|512,first:null,fn:t,last:null,next:null,parent:n,b:n&&n.b,prev:null,teardown:null,wv:0,ac:null};j?.register_created_effect(r);var i=r;if(e&4)Et===null?At.ensure().schedule(r):Et.push(r);else if(t!==null){try{or(r)}catch(e){throw B(r),e}i.deps===null&&i.teardown===null&&i.nodes===null&&i.first===i.last&&!(i.f&524288)&&(i=i.first,e&16&&e&65536&&i!==null&&(i.f|=ie))}if(i!==null&&(i.parent=n,n!==null&&mn(i,n),V!==null&&V.f&2&&!(e&64))){var a=V;(a.effects??=[]).push(i)}return r}function gn(){return V!==null&&!Hn}function _n(e){let t=hn(8,null);return A(t,b),t.teardown=e,t}function vn(e){pn(`$effect`);var t=H.f;if(!V&&t&32&&D!==null&&!D.i){var n=D;(n.e??=[]).push(e)}else return yn(e)}function yn(e){return hn(4|se,e)}function bn(e){return pn(`$effect.pre`),hn(8|se,e)}function xn(e){At.ensure();let t=hn(64|oe,e);return(e={})=>new Promise(n=>{e.outro?Nn(t,()=>{B(t),n(void 0)}):(B(t),n(void 0))})}function Sn(e){return hn(4,e)}function Cn(e,t){var n=D,r={effect:null,ran:!1,deps:e};n.l.$.push(r),r.effect=En(()=>{if(e(),!r.ran){r.ran=!0;var n=H;try{Wn(n.parent),K(t)}finally{Wn(n)}}})}function wn(){var e=D;En(()=>{for(var t of e.l.$){t.deps();var n=t.effect;n.f&1024&&n.deps!==null&&A(n,S),er(n)&&or(n),t.ran=!1}})}function Tn(e){return hn(ue|oe,e)}function En(e,t=0){return hn(8|t,e)}function R(e,t=[],n=[],r=[]){st(r,t,n,t=>{hn(8,()=>{e(...t.map(G))})})}function Dn(e,t=0){return hn(16|t,e)}function z(e){return hn(32|oe,e)}function On(e){var t=e.teardown;if(t!==null){let n=Bn,r=V;Vn(!0),Un(null);try{t.call(null)}catch(t){fn(t,e.parent)}finally{Vn(n),Un(r)}}}function kn(e,t=!1){var n=e.first;for(e.first=e.last=null;n!==null;){let e=n.ac;e!==null&&at(()=>{e.abort(xe)});var r=n.next;n.f&64?n.parent=null:B(n,t),n=r}}function An(e){for(var t=e.first;t!==null;){var n=t.next;t.f&32||B(t),t=n}}function B(e,t=!0){var n=!1;(t||e.f&262144)&&e.nodes!==null&&e.nodes.end!==null&&(jn(e.nodes.start,e.nodes.end),n=!0),e.f|=re,kn(e,t&&!n),ar(e,0);var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)e.stop();On(e),e.f^=re,e.f|=te;var i=e.parent;i!==null&&i.first!==null&&Mn(e),e.next=e.prev=e.teardown=e.ctx=e.deps=e.fn=e.nodes=e.ac=e.b=null}function jn(e,t){for(;e!==null;){var n=e===t?null:an(e);e.remove(),e=n}}function Mn(e){var t=e.parent,n=e.prev,r=e.next;n!==null&&(n.next=r),r!==null&&(r.prev=n),t!==null&&(t.first===e&&(t.first=r),t.last===e&&(t.last=n))}function Nn(e,t,n=!0){var r=[];e.f|=256,Pn(e,r,!0);var i=()=>{n&&B(e),t&&t()},a=r.length;if(a>0){var o=()=>--a||i();for(var s of r)s.out(o)}else i()}function Pn(e,t,n){if(!(e.f&8192)){e.f^=ee;var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)(e.is_global||n)&&t.push(e);for(var i=e.first;i!==null;){var a=i.next;if(!(i.f&64)){var o=!!(i.f&65536)||!!(i.f&32)&&!!(e.f&16);Pn(i,t,o?n:!1)}i=a}}}function Fn(e){e.f&=-257,In(e,!0)}function In(e,t){if(!(e.f&256)&&e.f&8192){e.f^=ee,e.f&1024||(A(e,x),At.ensure().schedule(e));for(var n=e.first;n!==null;){var r=n.next,i=!!(n.f&65536)||!!(n.f&32);In(n,i?t:!1),n=r}var a=e.nodes&&e.nodes.t;if(a!==null)for(let e of a)(e.is_global||t)&&e.in()}}function Ln(e,t){if(e.nodes)for(var n=e.nodes.start,r=e.nodes.end;n!==null;){var i=n===r?null:an(n);t.append(n),n=i}}var Rn=null,zn=!1,Bn=!1;function Vn(e){Bn=e}var V=null,Hn=!1;function Un(e){V=e}var H=null;function Wn(e){H=e}var Gn=null;function Kn(e){V!==null&&(V.f&2097152||V.f&2)&&(Gn??=new Set).add(e)}var U=null,W=0,qn=null;function Jn(e){qn=e}var Yn=1,Xn=0,Zn=Xn;function Qn(e){Zn=e}function $n(){return++Yn}function er(e){var t=e.f;if(t&2048)return!0;if(t&4096){for(var n=e.deps,r=n.length,i=0;ie.wv)return!0}t&512&&St===null&&A(e,b)}return!1}function tr(e,t,n=!0){var r=e.reactions;if(r!==null&&!(Gn!==null&&Gn.has(e)))for(var i=0;i{e.ac.abort(xe)}),e.ac=null);try{e.f|=le;var u=e.fn,d=u();e.f|=ne;var f=rr(e);if(Ye()&&qn!==null&&!Hn&&f!==null&&!(e.f&6146))for(var p=0;p0)for(t.length=W+U.length,r=0;r{c.ac.abort(xe),c.ac=null,A(c,x)}),vt(c),ar(c,0)}}function ar(e,t){var n=e.deps;if(n!==null)for(var r=t;r{_r=!1,gr=null}));var o=0,s=gr===e&&e[dr];if(s){var c=i.indexOf(s);if(c!==-1&&(t===document||t===window)){e[dr]=t;return}var u=i.indexOf(t);if(u===-1)return;c<=u&&(o=c)}if(a=i[o]||e.target,a!==t){l(e,`currentTarget`,{configurable:!0,get(){return a||n}});var d=V,f=H;Un(null),Wn(null);try{for(var p,m=[];a!==null&&a!==t;){try{var h=a[dr]?.[r];h!=null&&(!a.disabled||e.target===a)&&h.call(a,e)}catch(e){p?m.push(e):p=e}if(e.cancelBubble)break;o++,a=o{throw e});throw p}}finally{e[dr]=t,delete e.currentTarget,Un(d),Wn(f)}}}var yr=globalThis?.window?.trustedTypes&&globalThis.window.trustedTypes.createPolicy(`svelte-trusted-html`,{createHTML:e=>e});function br(e){return yr?.createHTML(e)??e}function xr(e){var t=ln(`template`);return t.innerHTML=br(e.replaceAll(``,``)),t.content}function Sr(e,t){var n=H;n.nodes===null&&(n.nodes={start:e,end:t,a:null,t:null})}function J(e,t){var n=!!(t&1),r=!!(t&2),i,a=!e.startsWith(``);return()=>{if(C)return Sr(w,null),w;i===void 0&&(i=xr(a?e:``+e),n||(i=rn(i)));var t=r||$t?document.importNode(i,!0):i.cloneNode(!0);if(n){var o=rn(t),s=t.lastChild;Sr(o,s)}else Sr(t,t);return t}}function Y(e,t){if(C){var n=H;(!(n.f&32768)||n.nodes.end===null)&&(n.nodes.end=w),Oe();return}e!==null&&e.before(t)}[...`allowfullscreen.async.autofocus.autoplay.checked.controls.default.disabled.formnovalidate.indeterminate.inert.ismap.loop.multiple.muted.nomodule.novalidate.open.playsinline.readonly.required.reversed.seamless.selected.webkitdirectory.defer.disablepictureinpicture.disableremoteplayback`.split(`.`)];var Cr=[`touchstart`,`touchmove`];function wr(e){return Cr.includes(e)}function Tr(e){let t=0,n=Vt(0),r;return()=>{gn()&&(G(n),En(()=>(t===0&&(r=K(()=>e(()=>qt(n)))),t+=1,()=>{Qe(()=>{--t,t===0&&(r?.(),r=void 0,qt(n))})})))}}var Er=ie|oe;function Dr(e,t,n,r){new Or(e,t,n,r)}var Or=class{parent;is_pending=!1;transform_error;#e;#t=C?w:null;#n;#r;#i;#a=null;#o=null;#s=null;#c=null;#l=0;#u=0;#d=!1;#f=new Set;#p=new Set;#m=null;#h=Tr(()=>(this.#m=Vt(this.#l),()=>{this.#m=null}));constructor(e,t,n,r){this.#e=e,this.#n=t,this.#r=e=>{var t=H;t.b=this,t.f|=128,n(e)},this.parent=H.b,this.transform_error=r??this.parent?.transform_error??(e=>e),this.#i=Dn(()=>{if(C){let e=this.#t;Oe();let t=e.data===`[!`;if(e.data.startsWith(`[?`)){let t=JSON.parse(e.data.slice(2));this.#_(t)}else t?this.#y():this.#g()}else this.#b()},Er),C&&(this.#e=w)}#g(){try{this.#a=z(()=>this.#r(this.#e))}catch(e){this.error(e)}}#_(e){let t=this.#n.failed,{reset:n,invoke_onerror:r}=this.#v(e);Qe(r),t&&(this.#s=z(()=>{t(this.#e,()=>e,()=>n)}))}#v(e){var t=!1,n=!1;let r=()=>{if(t){Ee();return}t=!0,n&&Ke(),this.#s!==null&&Nn(this.#s,()=>{this.#s=null}),this.#S(()=>{this.#b()})};return{reset:r,invoke_onerror:()=>{try{n=!0,this.#n.onerror?.(e,r),n=!1}catch(e){fn(e,this.#i&&this.#i.parent)}}}}#y(){let e=this.#n.pending;e&&(this.is_pending=!0,this.#o=z(()=>e(this.#e)),Qe(()=>{var e=this.#c=document.createDocumentFragment(),t=P(),n=!1;if(e.append(t),this.#a=this.#S(()=>{try{return z(()=>this.#r(t))}catch(e){try{this.error(e),n=!0}catch(e){fn(e,this.#i.parent)}return null}}),this.#a===null){this.#c=null,n&&this.#x(j);return}this.#u===0&&(this.#e.before(e),this.#c=null,Nn(this.#o,()=>{this.#o=null}),this.#x(j))}))}#b(){try{if(this.is_pending=this.has_pending_snippet(),this.#u=0,this.#l=0,this.#a=z(()=>{this.#r(this.#e)}),this.#u>0){var e=this.#c=document.createDocumentFragment();Ln(this.#a,e);let t=this.#n.pending;this.#o=z(()=>t(this.#e))}else this.#x(j)}catch(e){this.error(e)}}#x(e){this.is_pending=!1,e.transfer_effects(this.#f,this.#p)}defer_effect(e){nt(e,this.#f,this.#p)}is_rendered(){return!this.is_pending&&(!this.parent||this.parent.is_rendered())}has_pending_snippet(){return!!this.#n.pending}#S(e){var t=H,n=V,r=D;Wn(this.#i),Un(this.#i),qe(this.#i.ctx);try{return At.ensure(),e()}finally{Wn(t),Un(n),qe(r)}}#C(e,t){if(!this.has_pending_snippet()){this.parent&&this.parent.#C(e,t);return}this.#u+=e,this.#u===0&&(this.#x(t),this.#o&&Nn(this.#o,()=>{this.#o=null}),this.#c&&=(this.#e.before(this.#c),null))}update_pending_count(e,t){this.#C(e,t),this.#l+=e,!(!this.#m||this.#d)&&(this.#d=!0,Qe(()=>{this.#d=!1,this.#m&&Gt(this.#m,this.#l)}))}get_effect_pending(){return this.#h(),G(this.#m)}error(e){if(!this.#n.onerror&&!this.#n.failed)throw e;j?.is_fork?(this.#a&&j.skip_effect(this.#a),this.#o&&j.skip_effect(this.#o),this.#s&&j.skip_effect(this.#s),j.oncommit(()=>{this.#w(e)})):this.#w(e)}#w(e){this.#a&&=(B(this.#a),null),this.#o&&=(B(this.#o),null),this.#s&&=(B(this.#s),null),C&&(T(this.#t),ke(),T(Ae()));let t=this.#n.failed,n=e=>{let{reset:n,invoke_onerror:r}=this.#v(e);r(),t&&(this.#s=this.#S(()=>{try{return z(()=>{var r=H;r.b=this,r.f|=128,t(this.#e,()=>e,()=>n)})}catch(e){return fn(e,this.#i.parent),null}}))};Qe(()=>{var t;try{t=this.transform_error(e)}catch(e){fn(e,this.#i&&this.#i.parent);return}typeof t==`object`&&t&&typeof t.then==`function`?t.then(n,e=>fn(e,this.#i&&this.#i.parent)):n(t)})}};function X(e,t){var n=t==null?``:typeof t==`object`?`${t}`:t;n!==(e[ye]??=e.nodeValue)&&(e[ye]=n,e.nodeValue=`${n}`)}function kr(e,t){return jr(e,t)}var Ar=new Map;function jr(e,{target:t,anchor:r,props:i={},events:a,context:o,intro:s=!0,transformError:l}){nn();var u=void 0,d=xn(()=>{var s=r??t.appendChild(P());Dr(s,{pending:()=>{}},t=>{O({});var r=D;if(o&&(r.c=o),a&&(i.$$events=a),C&&Sr(t,null),u=e(t,i)||Je(),C&&(H.nodes.end=w,w===null||w.nodeType!==8||w.data!==`]`))throw we(),n;k()},l);var d=new Set,f=e=>{for(var n=0;n{for(var e of d)for(let r of[t,document]){var n=Ar.get(r),i=n.get(e);--i==0?(r.removeEventListener(e,vr),n.delete(e),n.size===0&&Ar.delete(r)):n.set(e,i)}pr.delete(f),s!==r&&s.parentNode?.removeChild(s)}});return Mr.set(u,d),u}var Mr=new WeakMap,Nr=class{anchor;#e=new Map;#t=new Map;#n=new Map;#r=new Set;#i=!0;constructor(e,t=!0){this.anchor=e,this.#i=t}#a=e=>{if(this.#e.has(e)){var t=this.#e.get(e),n=this.#t.get(t);if(n)Fn(n),this.#r.delete(t);else{var r=this.#n.get(t);r&&(Fn(r.effect),this.#t.set(t,r.effect),this.#n.delete(t),r.fragment.lastChild.remove(),this.anchor.before(r.fragment),n=r.effect)}for(let[t,n]of this.#e){if(this.#e.delete(t),t===e)break;let r=this.#n.get(n);r&&(B(r.effect),this.#n.delete(n))}for(let[e,r]of this.#t){if(e===t||this.#r.has(e))continue;let i=()=>{if(Array.from(this.#e.values()).includes(e)){var t=document.createDocumentFragment();Ln(r,t),t.append(P()),this.#n.set(e,{effect:r,fragment:t})}else B(r);this.#r.delete(e),this.#t.delete(e)};this.#i||!n?(this.#r.add(e),Nn(r,i,!1)):i()}}};#o=e=>{this.#e.delete(e);let t=Array.from(this.#e.values());for(let[e,n]of this.#n)t.includes(e)||(B(n.effect),this.#n.delete(e))};ensure(e,t){var n=j,r=cn();if(t&&!this.#t.has(e)&&!this.#n.has(e)){if(r){var i=document.createDocumentFragment(),a=P();i.append(a),this.#n.set(e,{effect:z(()=>t(a)),fragment:i})}else this.#t.set(e,z(()=>t(this.anchor)))}if(this.#e.set(n,e),r){for(let[t,r]of this.#t)t===e?n.unskip_effect(r):n.skip_effect(r);for(let[t,r]of this.#n)t===e?n.unskip_effect(r.effect):n.skip_effect(r.effect);n.oncommit(this.#a),n.ondiscard(this.#o)}else C&&(this.anchor=w),this.#a(n)}};function Pr(t){D===null&&Fe(`onMount`),e&&D.l!==null?Fr(D).m.push(t):vn(()=>{let e=K(t);if(typeof e==`function`)return e})}function Fr(e){var t=e.l;return t.u??={a:[],b:[],m:[]}}function Z(e,t,n=!1){var r;C&&(r=w,Oe());var i=new Nr(e),a=n?ie:0;function o(e,t){if(C){var n=je(r);if(e!==parseInt(n.substring(1))){var a=Ae();T(a),i.anchor=a,De(!1),i.ensure(e,t),De(!0);return}}i.ensure(e,t)}Dn(()=>{var e=!1;t((t,n=0)=>{e=!0,o(n,t)}),e||o(-1,null)},a)}var Ir=Symbol(`NaN`);function Lr(e,t,n){C&&Oe();var r=new Nr(e),i=!Ye();Dn(()=>{var e=t();e!==e&&(e=Ir),i&&typeof e==`object`&&e&&(e={}),r.ensure(e,n)})}function Rr(e,t){return t}function zr(e,t,n){for(var r=[],i=t.length,a,o=t.length,s=0;s{if(a){if(a.pending.delete(n),a.done.add(n),a.pending.size===0){var t=e.outrogroups;Br(e,c(a.done)),t.delete(a),t.size===0&&(e.outrogroups=null)}}else--o},!1)}if(o===0){var l=r.length===0&&n!==null&&e.pending.size===0;if(l){var u=n,d=u.parentNode;sn(d),d.append(u),e.items.clear()}Br(e,t,!l)}else a={pending:new Set(t),done:new Set},(e.outrogroups??=new Set).add(a)}function Br(e,t,n=!0){var r;if(e.pending.size>0){r=new Set;for(let t of e.pending.values())for(let n of t)r.add(e.items.get(n).e)}for(var i=0;i{var e=n();return a(e)?e:e==null?[]:c(e)}),p,m=new Map,h=!0;function g(e){v.effect.f&16384||(v.pending.delete(e),v.fallback=d,Wr(v,p,s,t,r),d!==null&&(p.length===0?d.f&33554432?(d.f^=ce,Kr(d,null,s)):Fn(d):Nn(d,()=>{d=null})))}function _(e){v.pending.delete(e)}var v={effect:Dn(()=>{p=G(f);var e=p.length;let a=!1;C&&je(s)===`[!`!=(e===0)&&(s=Ae(),T(s),De(!1),a=!0);for(var c=new Set,u=j,v=cn(),y=0;yo(s)):(d=z(()=>o(Vr??=P())),d.f|=ce)),e>c.size&&Le(``,``,``),C&&e>0&&T(Ae()),!h){if(m.set(u,c),v){for(let[e,t]of l)c.has(e)||u.skip_effect(t.e);u.oncommit(g),u.ondiscard(_)}else g(u)}a&&De(!0),G(f)}),flags:t,items:l,pending:m,outrogroups:null,fallback:d};h=!1,C&&(s=w)}function Ur(e){for(;e!==null&&!(e.f&32);)e=e.next;return e}function Wr(e,t,n,r,i){var a=!!(r&8),o=t.length,s=e.items,l=Ur(e.effect.first),u,d=null,f,p=[],m=[],h,g,_,v;if(a)for(v=0;v0){var re=r&4&&o===0?n:null;if(a){for(v=0;v{if(f!==void 0)for(_ of f)_.nodes?.a?.apply()})}function Gr(e,t,n,r,i,a,o,s){var c=o&1?o&16?Vt(n):M(n,!1,!1):null,l=o&2?Vt(i):null;return{v:c,i:l,e:z(()=>(a(t,c??n,l??i,s),()=>{e.delete(r)}))}}function Kr(e,t,n){if(e.nodes)for(var r=e.nodes.start,i=e.nodes.end,a=t&&!(t.f&33554432)?t.nodes.start:n;r!==null;){var o=an(r);if(a.before(r),r===i)return;r=o}}function qr(e,t,n){t===null?e.effect.first=n:t.next=n,n===null?e.effect.last=t:n.prev=t}function Jr(e,t){let n=null,r=C;var i;if(C){n=w;for(var a=rn(document.head);a!==null&&(a.nodeType!==8||a.data!==e);)a=an(a);if(a===null)De(!1);else{var o=an(a);a.remove(),T(o)}}C||(i=document.head.appendChild(P()));try{Dn(()=>{var e=z(()=>t(i));e.f|=ae,C||(e.nodes===null?e.nodes={start:i,end:i,a:null,t:null}:e.nodes.end=i)})}finally{r&&(De(!0),T(n))}}function Yr(e){var t,n,r=``;if(typeof e==`string`||typeof e==`number`)r+=e;else if(typeof e==`object`){if(Array.isArray(e)){var i=e.length;for(t=0;t=0;){var s=o+a;(o===0||Qr.includes(r[o-1]))&&(s===r.length||Qr.includes(r[s]))?r=(o===0?``:r.substring(0,o))+r.substring(s+1):o=s}}return r===``?null:r}function ei(e,t,n,r,i,a){var o=e[_e];if(C||o!==n||o===void 0){var s=$r(n,r,a);(!C||s!==e.getAttribute(`class`))&&(s==null?e.removeAttribute(`class`):t?e.className=s:e.setAttribute(`class`,s)),e[_e]=n}else if(a&&i!==a)for(var c in a){var l=!!a[c];(i==null||l!==!!i[c])&&e.classList.toggle(c,l)}return a}function ti(e,t){t?e.hasAttribute(`selected`)||e.setAttribute(`selected`,``):e.removeAttribute(`selected`)}function ni(e,t){var n=e.__defaultValue,r=e.multiple,i=r?n??[]:null;if(!r||a(i)){var o=e.selectedIndex,s=t&&r?new Set(e.selectedOptions):null;for(var c of e.options){var l=oi(c);ti(c,r?i.includes(l):Zt(l,n))}if(t){if(s!==null)for(c of e.options){var u=s.has(c);c.selected!==u&&(c.selected=u)}else e.selectedIndex!==o&&(e.selectedIndex=o)}}}function ri(e,t,n=!1){if(e.multiple){if(t==null)return;if(!a(t))return Te();for(var r of e.options)r.selected=t.includes(oi(r));return}for(r of e.options)if(Zt(oi(r),t)){r.selected=!0;return}(!n||t!==void 0)&&(e.selectedIndex=-1)}function ii(e){var t=new MutationObserver(t=>{t.every(si)||(`__defaultValue`in e&&ni(e,!1),`__value`in e&&ri(e,e.__value))});t.observe(e,{childList:!0,subtree:!0,attributes:!0,attributeFilter:[`value`]}),_n(()=>{t.disconnect()})}function ai(e,t,n=t){var r=new WeakSet,i=!0;ot(e,`change`,t=>{var i=t?`[selected]`:`:checked`,a;if(e.multiple)a=[].map.call(e.querySelectorAll(i),oi);else{var o=e.querySelector(i)??e.querySelector(`option:not([disabled])`);a=o&&oi(o)}n(a),e.__value=a,j!==null&&r.add(j)}),Sn(()=>{var a=t();if(e===document.activeElement){var o=j;if(r.has(o))return}if(ri(e,a,i),i&&a===void 0){var s=e.querySelector(`:checked`);s!==null&&(a=oi(s),n(a))}e.__value=a,i=!1})}function oi(e){return`__value`in e?e.__value:e.value}function si(e){if(e.target.closest(`selectedcontent`)!==null)return!0;if(e.type===`childList`){var t=[...e.addedNodes,...e.removedNodes];return t.length>0&&t.every(e=>e.nodeName===`SELECTEDCONTENT`)}return!1}var ci=Symbol(`is custom element`),li=Symbol(`is html`),ui=Se?`link`:`LINK`;function di(e){if(C){var t=!1,n=()=>{if(!t){if(t=!0,e.hasAttribute(`value`)){var n=e.value;Q(e,`value`,null),e.value=n}if(e.hasAttribute(`checked`)){var r=e.checked;Q(e,`checked`,null),e.checked=r}}};e[be]=n,Qe(n),it()}}function fi(e,t){var n=pi(e);n.checked!==(n.checked=t??void 0)&&(e.checked=t)}function Q(e,t,n,r){var i=pi(e);C&&(i[t]=e.getAttribute(t),t===`src`||t===`srcset`||t===`href`&&e.nodeName===ui)||i[t]!==(i[t]=n)&&(t===`loading`&&(e[he]=n),n==null?e.removeAttribute(t):typeof n!=`string`&&hi(e).has(t)?e[t]=n:e.setAttribute(t,n))}function pi(e){return e[ge]??={[ci]:e.nodeName.includes(`-`),[li]:e.namespaceURI===i}}var mi=new Map;function hi(e){var t=e.getAttribute(`is`)||e.nodeName,n=mi.get(t);if(n)return n;mi.set(t,n=new Set);for(var r,i=e,a=Element.prototype;a!==i;){for(var o in r=d(i),r)r[o].set&&o!==`innerHTML`&&o!==`textContent`&&o!==`innerText`&&n.add(o);i=m(i)}return n}function gi(e,t,n=t){var r=new WeakSet;ot(e,`input`,async i=>{var a=i?e.defaultValue:e.value;if(a=_i(e)?vi(a):a,n(a),j!==null&&r.add(j),await sr(),a!==(a=t())){var o=e.selectionStart,s=e.selectionEnd,c=e.value.length;if(e.value=a??``,s!==null){var l=e.value.length;o===s&&s===c&&l>c?(e.selectionStart=l,e.selectionEnd=l):(e.selectionStart=o,e.selectionEnd=Math.min(s,l))}}}),(C&&e.defaultValue!==e.value||K(t)==null&&e.value)&&(n(_i(e)?vi(e.value):e.value),j!==null&&r.add(j)),En(()=>{var n=t();if(e===document.activeElement){var i=j;if(r.has(i))return}_i(e)&&n===vi(e.value)||(e.type!==`date`||n||e.value)&&n!==e.value&&(e.value=n??``)})}function _i(e){var t=e.type;return t===`number`||t===`range`}function vi(e){return e===``?null:+e}function yi(e=!1){let t=D,n=t.l.u;if(!n)return;let r=()=>q(t.s);if(e){let e=0,n={},i=dt(()=>{let r=!1,i=t.s;for(let e in i)i[e]!==n[e]&&(n[e]=i[e],r=!0);return r&&e++,e});r=()=>G(i)}n.b.length&&bn(()=>{bi(t,r),v(n.b)}),vn(()=>{let e=K(()=>n.m.map(_));return()=>{for(let t of e)typeof t==`function`&&t()}}),n.a.length&&vn(()=>{bi(t,r),v(n.a)})}function bi(e,t){if(e.l.s)for(let t of e.l.s)G(t);t()}function xi(e,t,n){if(e==null)return t(void 0),n&&n(void 0),g;let r=K(()=>e.subscribe(t,n));return r.unsubscribe?()=>r.unsubscribe():r}var Si=[];function Ci(e,t=g){let n=null,r=new Set;function i(t){if(Ne(e,t)&&(e=t,n)){let t=!Si.length;for(let t of r)t[1](),Si.push(t,e);if(t){for(let e=0;e{r.delete(c),r.size===0&&n&&(n(),n=null)}}return{set:i,update:a,subscribe:o}}function wi(e){let t;return xi(e,e=>t=e)(),t}var Ti=!1,Ei=Symbol(`unmounted`);function Di(e,t,n){let r=n[t]??={store:null,source:M(void 0),unsubscribe:g};if(r.store!==e&&!(Ei in n)){if(r.unsubscribe(),r.store=e??null,e==null)r.source.v=void 0,r.unsubscribe=g;else{var i=!0;r.unsubscribe=xi(e,e=>{i?r.source.v=e:N(r.source,e)}),i=!1}}return e&&Ei in n?wi(e):G(r.source)}function Oi(){let e={};function t(){_n(()=>{for(var t in e)e[t].unsubscribe();l(e,Ei,{enumerable:!1,value:!0})})}return[e,t]}function ki(e){var t=Ti;try{return Ti=!1,[e(),Ti]}finally{Ti=t}}function $(t,n,r,i){var a=!e||!!(r&2),o=!!(r&8),s=!!(r&16),c=i,l=!0,d=void 0,f=()=>s&&a?(d??=dt(i),G(d)):(l&&(l=!1,c=s?K(i):i),c);let p;if(o){var m=fe in t||me in t;p=u(t,n)?.set??(m&&n in t?e=>t[n]=e:void 0)}var h,g=!1;o?[h,g]=ki(()=>t[n]):h=t[n],h===void 0&&i!==void 0&&(h=f(),p&&(a&&He(n),p(h)));var _=a?()=>{var e=t[n];return e===void 0?f():(l=!0,e)}:()=>{var e=t[n];return e!==void 0&&(c=void 0),e===void 0?c:e};if(a&&!(r&4))return _;if(p){var v=t.$$legacy;return(function(e,t){return arguments.length>0?((!a||!t||v||g)&&p(t?_():e),e):_()})}var y=!1,b=(r&1?dt:mt)(()=>(y=!1,_()));o&&G(b);var x=H;return(function(e,t){if(arguments.length>0){let n=t?G(b):a&&o?Yt(e):e;return N(b,n),y=!0,c!==void 0&&(c=n),e}return Bn&&y||x.f&16384?b.v:G(b)})}function Ai(e){return new URLSearchParams(e.replace(/^#/,``)).get(`setup-key`)??``}function ji(e=Ai(globalThis.location?.hash??``)){let t=Ci({controller:!1,busy:!1,error:``}),n,r=``,i={controller:!1,busy:!1,error:``},a=!1;function o(e){i={...i,...e},t.set(i)}async function s(t=!1){if(!a){a=!0;try{let n=await fetch(`/api/state`,{cache:`no-store`,headers:{"X-Setup-Session-Key":e}});if(!n.ok)throw Error(`The local setup session is unavailable.`);o({view:await n.json(),...t?{}:{error:``}})}catch{o({view:void 0,error:`Connection lost. The CLI may have stopped. Check the terminal before trying again.`})}finally{a=!1}}}async function c(){try{let t=await fetch(`/api/bootstrap`,{cache:`no-store`,headers:{"X-Setup-Session-Key":e}});if(!t.ok)throw Error(`Could not join this local session.`);let i=await t.json();n=i.capability,r=i.takeoverTicket,o({controller:i.controller,error:``}),await s()}catch{n=void 0,r=``,o({view:void 0,controller:!1,error:`Could not connect to the local setup session. Check the terminal.`})}}async function l(t,r,i=!0){let a=await fetch(t,{method:`POST`,cache:`no-store`,headers:{"Content-Type":`application/json`,"X-Setup-Session-Key":e,...i&&n?{"X-Setup-Capability":n}:{}},body:JSON.stringify(r)}),o=await a.json();if(!a.ok)throw Error(String(o.error??`The request was rejected.`));return o}async function u(e,t){if(!i.busy&&i.controller&&i.view?.promptRevision===e){o({busy:!0,error:``});try{await l(`/api/answer`,{revision:e,value:t}),await s()}catch(e){let t=e instanceof Error?e.message:`Could not submit this answer.`;o({error:t}),/read-only|Control moved/.test(t)?await c():await s(!0)}finally{o({busy:!1})}}}async function d(){if(i.controller&&!i.busy){o({busy:!0,error:``});try{await l(`/api/cancel`,{}),await s()}catch(e){let t=e instanceof Error?e.message:`Cancellation failed.`;o({error:t}),/read-only|Control moved/.test(t)&&await c()}finally{o({busy:!1})}}}async function f(){try{let e=await l(`/api/takeover`,{ticket:r},!1);n=String(e.capability),o({controller:!0,error:``}),await s()}catch(e){o({error:e instanceof Error?e.message:`Takeover failed.`}),await c()}}async function p(){try{await l(`/api/close`,{})}catch{}}return{subscribe:t.subscribe,connect:c,refresh:s,submit:u,cancel:d,takeOver:f,close:p}}var Mi=J(`
                                      • `),Ni=J(``);function Pi(e,t){O(t,!1);let n=$(t,`journey`,8),r=[`Repository`,`Setup choices`,`Setup PAT`,`Plan`,`Bot PAT & credentials`,`Apply`];yi();var i=Ni(),a=L(F(i),4);Hr(a,5,()=>r,Rr,(e,t,r)=>{var i=Mi();let a;var o=F(i),s=I(o,!0),c=I(L(o,2),!0);E(i),R(e=>{Q(i,`aria-current`,(q(n()),K(()=>n()?.position===r+1?`step`:void 0))),a=ei(i,1,``,null,a,{current:n()?.position===r+1,completed:(n()?.position??0)>r+1}),X(s,e),X(c,G(t))},[()=>(q(n()),K(()=>(n()?.position??0)>r+1?`✓`:String(r+1).padStart(2,`0`)))]),Y(e,i)}),E(a),ke(2),E(i),Y(e,i),k()}var Fi=J(`
                                        `);function Ii(e,t){O(t,!1);let n=M(`system`);Cn(()=>G(n),()=>{document.documentElement.dataset.theme=G(n)}),wn();var r=Fi(),i=F(r);let a;var o=L(i,2);let s;var c=L(o,2);let l;E(r),R(()=>{Q(i,`aria-pressed`,G(n)===`system`),a=ei(i,1,``,null,a,{active:G(n)===`system`}),Q(o,`aria-pressed`,G(n)===`light`),s=ei(o,1,``,null,s,{active:G(n)===`light`}),Q(c,`aria-pressed`,G(n)===`dark`),l=ei(c,1,``,null,l,{active:G(n)===`dark`})}),mr(`click`,i,()=>N(n,`system`)),mr(`click`,o,()=>N(n,`light`)),mr(`click`,c,()=>N(n,`dark`)),Y(e,r),k()}hr([`click`]);var Li=J(`
                                        LOCAL SESSION
                                        `);function Ri(e,t){let n=$(t,`repository`,8);var r=Li(),i=F(r),a=I(L(F(i),2),!0);E(i);var o=L(i,2);Ii(L(F(o)),{}),E(o),E(r),R(()=>X(a,n()??`Connecting…`)),Y(e,r)}var zi=J(`

                                        `,1);function Bi(e,t){O(t,!1);let n=M(),r=$(t,`view`,8);Cn(()=>q(r()),()=>{N(n,r()?.outcome===`complete`?`Setup complete.`:r()?.outcome===`dry-run`?`Preview complete.`:r()?.outcome===`cancelled`?`Setup cancelled.`:r()?.outcome?`Setup needs attention.`:r()?.prompt?.title??`Preparing your setup…`)}),wn(),yi();var i=zi(),a=on(i),o=L(F(a)),s=I(L(o));E(a);var c=L(a,2),l=I(c,!0),u=I(L(c,2),!0);R((e,t)=>{X(o,` ${e??``} `),X(s,`${t??``} / 06`),X(l,G(n)),X(u,(q(r()),K(()=>r()?.outcome?`The terminal contains the detailed result. Review the next steps below before closing this session.`:`One decision at a time. Your choices shape the permissions, plan and credentials needed for this repository.`)))},[()=>(q(r()),K(()=>r()?.journey?.current?.toUpperCase()??`GETTING READY`)),()=>(q(r()),K(()=>String(r()?.journey?.position??1).padStart(2,`0`)))]),Y(e,i),k()}function Vi(e){try{let t=new URL(e??``);return t.protocol===`https:`&&t.hostname===`github.com`&&(t.pathname===`/settings/personal-access-tokens`||t.pathname.startsWith(`/settings/personal-access-tokens/`))?t.toString():void 0}catch{return}}var Hi=J(``),Ui=J(``);function Wi(e,t){let n=$(t,`label`,8),r=$(t,`variant`,8,`primary`),i=$(t,`disabled`,8,!1),a=$(t,`arrow`,8,!1),o=$(t,`onClick`,8);var s=Ui(),c=F(s,!0),l=L(c),u=e=>{Y(e,Hi())};Z(l,e=>{a()&&e(u)}),E(s),R(()=>{ei(s,1,Zr(r())),s.disabled=i(),X(c,n())}),mr(`click`,s,function(...e){o()?.apply(this,e)}),Y(e,s)}hr([`click`]);var Gi=J(`Open GitHub link ↗`),Ki=J(`

                                        `);function qi(e,t){O(t,!1);let n=M(),r=$(t,`tone`,8,`info`),i=$(t,`title`,8),a=$(t,`message`,8),o=$(t,`link`,8,void 0),s=$(t,`actionLabel`,8,void 0),c=$(t,`onAction`,8,void 0);Cn(()=>q(o()),()=>{N(n,Vi(o()))}),wn(),yi();var l=Ki(),u=F(l),d=I(u,!0),f=L(u),p=I(f,!0),m=L(f,2),h=e=>{var t=Gi();R(()=>Q(t,`href`,G(n))),Y(e,t)};Z(m,e=>{G(n)&&e(h)});var g=L(m,2),_=e=>{Wi(e,{get label(){return s()},variant:`secondary`,get onClick(){return c()}})};Z(g,e=>{s()&&c()&&e(_)}),E(l),R(()=>{ei(l,1,`banner ${r()??``}`),Q(l,`role`,r()===`error`?`alert`:`status`),X(d,i()),X(p,a())}),Y(e,l),k()}function Ji(e){let t=String(e.question.defaultValue),n=t.split(`,`).map(e=>e.trim()).filter(Boolean);return{value:t,selected:e.question.kind===`multi-select`?(e.question.choices??[]).filter(e=>e!==`All`&&n.includes(e.split(` — `)[0])):e.question.kind===`scope-overrides`?n:[]}}function Yi(e,t){return t===`All`?e.includes(`All`)?[]:[`All`]:e.includes(t)?e.filter(e=>e!==t):[...e.filter(e=>e!==`All`),t]}function Xi(e,t,n){return e.question.kind===`scope-overrides`||e.question.kind===`multi-select`?n.length?n.join(`,`):`none`:t}var Zi=J(`PERMISSION PREVIEW`),Qi=J(`
                                        `),$i=J(``),ea=J(``),ta=J(``),na=J(`
                                        `),ra=J(``),ia=J(`

                                        `,1);function aa(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`controller`,8),i=$(t,`busy`,8),a=$(t,`onSubmit`,8),o=Ji(n()),s=M(o.value),c=M(o.selected);yi();var l=ia(),u=on(l),d=F(u),f=I(d,!0),p=L(d),m=e=>{Y(e,Zi())};Z(p,e=>{q(n()),K(()=>n().phase===`permission-intent`)&&e(m)}),E(u);var h=L(u,2),g=e=>{var t=Qi(),i=F(t);let a;var o=L(i);let c;E(t),R(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),Q(i,`aria-pressed`,G(s)===`yes`||G(s)===`true`),i.disabled=!r(),a=ei(i,1,``,null,a,{selected:G(s)===`yes`||G(s)===`true`}),Q(o,`aria-pressed`,G(s)===`no`||G(s)===`false`),o.disabled=!r(),c=ei(o,1,``,null,c,{selected:G(s)===`no`||G(s)===`false`})}),mr(`click`,i,()=>N(s,`yes`)),mr(`click`,o,()=>N(s,`no`)),Y(e,t)},_=e=>{var t=ea();Hr(t,5,()=>(q(n()),K(()=>n().question.choices??[])),Rr,(e,t)=>{var n=$i(),r=I(n,!0),i={};R(()=>{X(r,G(t)),i!==(i=G(t))&&(n.value=(n.__value=i)??``)}),Y(e,n)}),E(t),ii(t),R(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),t.disabled=!r()}),ai(t,()=>G(s),e=>N(s,e)),Y(e,t)},v=e=>{var t=na();Hr(t,5,()=>(q(n()),K(()=>n().question.kind===`multi-select`?n().question.choices??[]:n().question.allowedNames??[])),Rr,(e,t)=>{var n=ta(),i=F(n);di(i);var a=I(L(i),!0);E(n),R(e=>{fi(i,e),i.disabled=!r(),X(a,G(t))},[()=>(G(c),G(t),K(()=>G(c).includes(G(t))))]),mr(`change`,i,()=>N(c,Yi(G(c),G(t)))),Y(e,n)}),E(t),R(()=>Q(t,`aria-label`,(q(n()),K(()=>n().question.label)))),Y(e,t)},y=e=>{var t=ra();di(t),R(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),Q(t,`type`,(q(n()),K(()=>n().question.kind===`number`?`number`:`text`))),Q(t,`min`,(q(n()),K(()=>n().question.kind===`number`?0:void 0))),t.disabled=!r()}),gi(t,()=>G(s),e=>N(s,e)),Y(e,t)};Z(h,e=>{q(n()),K(()=>n().question.kind===`boolean`)?e(g):(q(n()),K(()=>n().question.kind===`choice`)?e(_,1):(q(n()),K(()=>n().question.kind===`multi-select`||n().question.kind===`scope-overrides`)?e(v,2):e(y,-1)))});var b=L(h,2),x=I(b),S=L(b,2);{let e=mt(()=>!r()||i());Wi(S,{label:`Continue`,arrow:!0,onClick:()=>a()(Xi(n(),G(s),G(c))),get disabled(){return G(e)}})}R((e,t)=>{X(f,e),X(x,`Suggested answer: ${t??``} · You can review choices again before creating your setup PAT.`)},[()=>(q(n()),K(()=>n().question.label.replace(` (Space toggles, Enter confirms)`,``))),()=>(q(n()),K(()=>String(n().question.defaultValue)||`none`))]),Y(e,l),k()}hr([`click`,`change`]);var oa=J(``),sa=J(`
                                        `);function ca(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`controller`,8),i=$(t,`busy`,8),a=$(t,`onSubmit`,8);yi();var o=sa();Hr(o,5,()=>(q(n()),K(()=>n().choices)),Rr,(e,t)=>{var n=oa(),o=I(F(n),!0);ke(),E(n),R(()=>{n.disabled=!r()||i(),X(o,G(t))}),mr(`click`,n,()=>a()(G(t))),Y(e,n)}),E(o),Y(e,o),k()}hr([`click`]);var la=J(`Open the official GitHub PAT form

                                        Check the signed-in account, select Only select repositories, then select this repository. GitHub handles 2FA and creates the PAT.

                                        `,1),ua=J(`

                                        Sent only to this local process. It will not be shown again or saved in browser storage.

                                        `),da=J(` `,1);function fa(e,t){O(t,!1);let n=M(),r=$(t,`prompt`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8),s=M(``);function c(){let e=G(s);r().kind===`secret`&&N(s,``),o()(e)}Cn(()=>q(r()),()=>{N(n,Vi(r().link))}),wn(),yi();var l=da(),u=on(l),d=e=>{var t=la(),r=on(t);ke(),R(()=>Q(r,`href`,G(n))),Y(e,t)};Z(u,e=>{G(n)&&e(d)});var f=L(u,2),p=I(f,!0),m=L(f,2);di(m);var h=L(m,2),g=e=>{Y(e,ua())};Z(h,e=>{q(r()),K(()=>r().kind===`secret`)&&e(g)});var _=L(h,2);{let e=mt(()=>(q(i()),q(a()),q(r()),G(s),K(()=>!i()||a()||!r().optional&&!G(s).trim())));Wi(_,{label:`Continue`,arrow:!0,onClick:c,get disabled(){return G(e)}})}R(()=>{X(p,(q(r()),K(()=>r().kind===`secret`?`Paste the value here`:`Your answer`))),Q(m,`type`,(q(r()),K(()=>r().kind===`secret`?`password`:`text`))),m.disabled=!i(),Q(m,`placeholder`,(q(r()),K(()=>r().kind===`secret`?`Hidden after submission`:`Type your answer`)))}),gi(m,()=>G(s),e=>N(s,e)),Y(e,l),k()}var pa=J(`
                                      • `),ma=J(`

                                          `),ha=J(`

                                          Before you continue

                                            `),ga=J(`

                                            Review exactly what this run may change. The bot PAT and any additional credentials are collected next.

                                            `,1);function _a(e,t){O(t,!1);let n=M(),r=$(t,`prompt`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8);Cn(()=>q(r()),()=>{N(n,[{title:`Files`,items:r().plan.files},{title:`Workflows`,items:r().plan.workflows},{title:`Variables`,items:r().plan.variables},{title:`Secret names`,items:r().plan.secrets}])}),wn(),yi();var s=ga(),c=L(on(s),2);Hr(c,5,()=>G(n),Rr,(e,t)=>{var n=ma(),r=F(n),i=F(r),a=I(L(i),!0);E(r);var o=L(r);Hr(o,5,()=>(G(t),K(()=>G(t).items)),Rr,(e,t)=>{var n=pa(),r=I(n,!0);R(()=>X(r,G(t))),Y(e,n)}),E(o),E(n),R(()=>{X(i,`${G(t),K(()=>G(t).title)??``} `),X(a,(G(t),K(()=>G(t).items.length)))}),Y(e,n)}),E(c);var l=L(c,2),u=e=>{var t=ha(),n=L(F(t));Hr(n,5,()=>(q(r()),K(()=>r().plan.warnings)),Rr,(e,t)=>{var n=pa(),r=I(n,!0);R(()=>X(r,G(t))),Y(e,n)}),E(n),E(t),Y(e,t)};Z(l,e=>{q(r()),K(()=>r().plan.warnings.length)&&e(u)});var d=L(l,2),f=F(d);{let e=mt(()=>!i()||a());Wi(f,{label:`Stop here`,variant:`secondary`,onClick:()=>o()(`decline`),get disabled(){return G(e)}})}var p=L(f);{let e=mt(()=>!i()||a());Wi(p,{label:`Approve this plan`,arrow:!0,onClick:()=>o()(`approve`),get disabled(){return G(e)}})}E(d),Y(e,s),k()}var va=J(`

                                            `),ya=J(`
                                            CURRENT DECISION
                                            `);function ba(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`revision`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8);yi();var s=ya(),c=F(s),l=I(L(F(c)));E(c);var u=L(c,2),d=e=>{var t=va(),r=I(t,!0);R(()=>X(r,(q(n()),K(()=>n().description)))),Y(e,t)};Z(u,e=>{q(n()),K(()=>`description`in n()&&n().description)&&e(d)});var f=L(u,2),p=e=>{aa(e,{get prompt(){return n()},get controller(){return i()},get busy(){return a()},get onSubmit(){return o()}})},m=e=>{ca(e,{get prompt(){return n()},get controller(){return i()},get busy(){return a()},get onSubmit(){return o()}})},h=e=>{fa(e,{get prompt(){return n()},get controller(){return i()},get busy(){return a()},get onSubmit(){return o()}})},g=e=>{_a(e,{get prompt(){return n()},get controller(){return i()},get busy(){return a()},get onSubmit(){return o()}})};Z(f,e=>{q(n()),K(()=>n().kind===`question`)?e(p):(q(n()),K(()=>n().kind===`choice`||n().kind===`confirm`)?e(m,1):(q(n()),K(()=>n().kind===`text`||n().kind===`secret`)?e(h,2):(q(n()),K(()=>n().kind===`plan`)&&e(g,3))))}),E(s),R(()=>X(l,`SESSION ${r()??``}`)),Y(e,s),k()}var xa=J(` `),Sa=J(`
                                          • `),Ca=J(`

                                              Conditional grants depend on the choices and GitHub facts. A final audit runs before setup changes.
                                              `),wa=J(`

                                              Permissions follow your choices

                                              We’ll show the exact grants before you create each PAT. Nothing is created just by opening this page.

                                              `),Ta=J(``);function Ea(e,t){O(t,!1);let n=$(t,`view`,8);yi();var r=Ta(),i=F(r),a=I(L(F(i),3),!0);E(i);var o=L(i,2),s=e=>{var t=Ca(),r=L(F(t)),i=I(r),a=L(r),o=I(a,!0),s=L(a,2);Hr(s,5,()=>(q(n()),K(()=>n().permissions.report?.checks??n().permissions.requirements??[])),Rr,(e,t)=>{var n=Sa(),r=F(n),i=F(r),a=L(i),o=I(a),s=I(L(a),!0);E(r);var c=L(r),l=F(c,!0),u=L(l),d=e=>{var n=xa(),r=I(n,!0);R(()=>X(r,(G(t),K(()=>G(t).status)))),Y(e,n)};Z(u,e=>{`status`in G(t)&&e(d)}),E(c),E(n),R(()=>{X(i,`${G(t),K(()=>G(t).permission)??``} `),X(o,`${G(t),K(()=>G(t).scope)??``} · ${G(t),K(()=>G(t).applicability)??``}`),X(s,(G(t),K(()=>G(t).reason))),X(l,(G(t),K(()=>G(t).level)))}),Y(e,n)}),E(s),ke(2),E(t),R(()=>{X(i,`${q(n()),K(()=>n().permissions.role===`setup`?`Setup PAT`:`Bot PAT`)??``} access`),X(o,(q(n()),K(()=>n().permissions.report?`Read-only access check`:`Provisional least-privilege grants`)))}),Y(e,t)},c=e=>{Y(e,wa())};Z(o,e=>{q(n()),K(()=>n().permissions)?e(s):e(c,-1)}),E(r),R(()=>X(a,(q(n()),K(()=>n().repository)))),Y(e,r),k()}var Da=J(`

                                              `);function Oa(e,t){O(t,!1);let n=M(),r=M(),i=$(t,`outcome`,8),a=$(t,`controller`,8),o=$(t,`onClose`,8);Cn(()=>q(i()),()=>{N(n,i()===`complete`?`Your configuration was applied`:i()===`dry-run`?`No changes were made`:i()===`cancelled`||i()===`blocked`?`No setup changes started`:`Check partial changes before retrying`)}),Cn(()=>q(i()),()=>{N(r,i()===`complete`?`The temporary setup PAT is not revoked automatically. Delete it in GitHub after confirming your setup. Keep the bot PAT until you rotate the installed Secret.`:i()===`partial`?`A bot Secret write or another change may have succeeded. Inspect GitHub and run copilot doctor before replacing or deleting its PAT.`:`Copilot did not begin applying setup changes. PATs you created in GitHub still exist until you delete them there.`)}),wn();var s=Da(),c=F(s),l=I(c,!0),u=L(c),d=I(u,!0),f=L(u),p=I(f,!0),m=L(f,2),h=e=>{Wi(e,{label:`Close local session`,get onClick(){return o()}})};Z(m,e=>{a()&&e(h)}),E(s),R(()=>{X(l,i()===`complete`?`✓`:`!`),X(d,G(n)),X(p,G(r))}),Y(e,s),k()}var ka=J(`

                                              Working on the next step

                                              The local process is checking your answers and preparing the next decision. Keep this page open.

                                              `);function Aa(e){Y(e,ka())}var ja=J(``),Ma=J(`
                                              `),Na=J(``),Pa=J(`
                                              `,1),Fa=J(`
                                              LOCALHOST ONLY NO CLOUD SETUP ACCOUNT GITHUB OWNS PAT ISSUANCE
                                              `);function Ia(e,t){O(t,!1);let n=()=>Di(a,`$session`,r),[r,i]=Oi(),a=ji();Pr(()=>{a.connect();let e=window.setInterval(()=>{n().view?.outcome||a.refresh()},900);return()=>window.clearInterval(e)});async function o(){window.confirm(`Cancel this local setup session? PATs already created in GitHub will still exist.`)&&await a.cancel()}async function s(e){let t=n().view?.promptRevision;t!==void 0&&await a.submit(t,e)}yi();var c=Fa();Jr(`16t12jp`,e=>{Y(e,ja())});var l=F(c);{let e=mt(()=>n().view?.journey);Pi(l,{get journey(){return G(e)}})}var u=L(l,2),d=F(u);{let e=mt(()=>n().view?.repository);Ri(d,{get repository(){return G(e)}})}var f=L(d,2),p=F(f);Bi(p,{get view(){return n().view}});var m=L(p,2),h=e=>{var t=Ma(),r=L(F(t));E(t),R(()=>X(r,` Reviewing saved choices — pass ${n().view.journey.choiceReviewPass??``}. This is the same setup run, not a restart.`)),Y(e,t)};Z(m,e=>{n().view?.journey?.choiceReviewPass&&n().view.journey.choiceReviewPass>1&&!n().view.outcome&&e(h)});var g=L(m,2),_=e=>{qi(e,{tone:`warning`,title:`Read-only tab`,message:`Another tab controls this session. You can review progress here or explicitly take over.`,actionLabel:`Take control in this tab`,get onAction(){return a.takeOver}})};Z(g,e=>{!n().controller&&n().view&&e(_)});var v=L(g,2),y=e=>{qi(e,{tone:`error`,title:`Needs attention`,get message(){return n().error}})};Z(v,e=>{n().error&&e(y)});var b=L(v,2),x=e=>{{let t=mt(()=>n().view.message.tone===`success`?`Checked`:n().view.message.tone===`warning`?`Please note`:n().view.message.tone===`error`?`Needs attention`:`Progress update`);qi(e,{get tone(){return n().view.message.tone},get title(){return G(t)},get message(){return n().view.message.text},get link(){return n().view.message.link}})}};Z(b,e=>{n().view?.message&&e(x)});var S=L(b,2),ee=e=>{Oa(e,{get outcome(){return n().view.outcome},get controller(){return n().controller},get onClose(){return a.close}})},te=e=>{var t=Pa(),r=on(t),i=F(r);Lr(i,()=>n().view.promptRevision,e=>{ba(e,{get prompt(){return n().view.prompt},get revision(){return n().view.revision},get controller(){return n().controller},get busy(){return n().busy},onSubmit:s})}),Ea(L(i,2),{get view(){return n().view}}),E(r);var a=L(r,2),c=e=>{var t=Na();R(()=>t.disabled=n().busy),mr(`click`,t,o),Y(e,t)};Z(a,e=>{n().controller&&n().view.journey?.current!==`Apply`&&e(c)}),Y(e,t)},ne=e=>{Aa(e,{})};Z(S,e=>{n().view?.outcome?e(ee):n().view?.prompt?e(te,1):e(ne,-1)}),ke(2),E(f),E(u),E(c),Y(e,c),k(),i()}hr([`click`]),kr(Ia,{target:document.getElementById(`app`)}); \ No newline at end of file diff --git a/build/web/index.html b/build/web/index.html index b88150bc2..49fd561eb 100644 --- a/build/web/index.html +++ b/build/web/index.html @@ -5,7 +5,7 @@ Copilot · Setup studio - + diff --git a/docs/how-to-use.mdx b/docs/how-to-use.mdx index d0d8ff046..ae6f7a81a 100644 --- a/docs/how-to-use.mdx +++ b/docs/how-to-use.mdx @@ -41,6 +41,10 @@ Once installed, the `copilot` command is available globally. Repository-dependen Prefer a visual walkthrough? Run `copilot setup --web` from the repository root. The CLI starts a short-lived page on `127.0.0.1` and opens your browser; if opening fails, paste the printed local URL into a browser on this computer. +Treat that entire URL, including its `#setup-key` fragment, as private: +loopback by itself does not restrict access to your OS account, and anyone +with the URL on this computer could access the setup session. The fragment +is used for local API authorization and is not sent to GitHub. The page shows the same six setup stages, keeps the review pass visibly part of the current run, presents permissions and the final plan, and asks for a separate final **Apply setup** approval. Use the System/Light/Dark control in diff --git a/docs/security-operations/operations/troubleshooting.mdx b/docs/security-operations/operations/troubleshooting.mdx index a47f4d97e..92ff3ff95 100644 --- a/docs/security-operations/operations/troubleshooting.mdx +++ b/docs/security-operations/operations/troubleshooting.mdx @@ -25,6 +25,9 @@ renew an installed bot PAT before its suggested 90-day expiry. For `copilot setup --web`, an inaccessible page or failed browser launch does not imply a setup failure: open the exact `127.0.0.1` URL printed by the CLI. +Do not share the printed `#setup-key` fragment. TCP loopback can be reached +by other local OS users; the private link, not OS-user identification, grants +access to this setup session. If the local bind or bundled assets fail, stop and use `copilot setup` in the terminal. A second browser tab is read-only until you select **Take control in this tab**; the former tab then cannot submit decisions. A rejected stale diff --git a/specs/local-web-setup-assistant.md b/specs/local-web-setup-assistant.md index b334c308b..f03792cb9 100644 --- a/specs/local-web-setup-assistant.md +++ b/specs/local-web-setup-assistant.md @@ -213,13 +213,17 @@ exactly what completed and what remains. dead, the CLI MUST fail closed, print the exact lock path, and require an operator to verify no setup process is running before removing that one file manually. It MUST NOT unlink a stale lock automatically: another - process can replace it between a read and an unlink. Web + process can replace it between a read and an unlink. Publish a fully + written lock record atomically; failed writes MUST NOT leave a blocking + empty lock. Web mode does not require a TTY: the browser is the interactive surface, and a printed local URL is available if automatic opening is unavailable. 2. Bind `127.0.0.1:0`, record the assigned port, create an unpredictable - one-run capability and first controller lease in process memory, and open - the default browser to `http://127.0.0.1:/`. If opening fails, print - that local URL and instructions; serving continues. If binding or packaged + one-run session key and first controller lease in process memory, and open + the default browser to `http://127.0.0.1:/#setup-key=`. + If opening fails, print that private URL and instructions; serving + continues. The key-bearing URL MUST NOT enter accumulated diagnostic logs. + If binding or packaged assets fail, stop without a partial UI and suggest `copilot setup`. 3. Show the six stages already used by terminal setup. Import one immutable snapshot of defaults, config file, and non-secret flags. Mark supplied @@ -631,21 +635,28 @@ for the temporary setup PAT and to bot-PAT rotation guidance separately. ## 11. Security, permissions, and privacy -1. **Boundary:** the web server is loopback-only, for the launching OS user; - a hostile website and another browser tab are in scope. A malicious - process already running as that same OS user, browser extensions with - page access, or a compromised browser are outside the isolation that a - loopback HTTP server can guarantee. The product must say so honestly. +1. **Boundary:** the web server is loopback-only, but TCP loopback does not + identify or isolate the launching OS user: another local user can connect + to the port. A cryptographically random URL-fragment key is required for + every API read and mutation, including bootstrap and takeover. Possession + of the private URL grants local session access, so it must not be shared. + A malicious process that can read that URL, browser extensions with page + access, or a compromised browser remain outside this boundary. The + product must say so honestly. 2. **Request defense:** reject `Host` not exactly `127.0.0.1:`, proxy/forwarded host headers, unexpected `Origin`/`Referer` on mutations, cross-site Fetch Metadata, unsupported methods/content types, and requests over size/time limits. No wildcard CORS or credentials cross-origin. - State-changing requests require a one-run, cryptographically random - capability in a custom header plus a revision check; capability is - delivered only by a same-origin no-store bootstrap response, never a URL, - cookie, localStorage, or sessionStorage. Reject missing/invalid capability - and rotate it on tab takeover. These controls defend cross-site requests - and DNS-rebinding-style host confusion; they are not OS-user isolation. + The URL-fragment session key is not sent in an HTTP URL, stored in a + cookie/localStorage/sessionStorage, or returned to unauthenticated callers; + the browser sends it in a custom header to every API route. In addition, + state-changing requests require a separate one-run, cryptographically + random controller capability in a custom header plus a revision check; + that capability is delivered only by an authenticated same-origin no-store + bootstrap response, never a URL or browser storage. Reject missing/invalid + keys or capabilities and rotate the controller capability on tab takeover. + These controls defend cross-site requests and host confusion, but do not + prove the identity of a local OS user. The local server sets no cookies and ignores, never logs, any Cookie header another application on the same hostname may have caused the browser to send. @@ -672,7 +683,7 @@ for the temporary setup PAT and to bot-PAT rotation guidance separately. Numeric bot-ID and setup-account checks are preserved; unknown access does not become success. The final Apply approval cannot be bypassed by `--yes` or a forged/stale browser event. -6. **Abuse/failure:** bound concurrent clients, read/write time, body and +6. **Abuse/failure:** cap simultaneous TCP clients at 16 and bound read/write time, body and field sizes, retries, and progress buffer. Avoid exposing arbitrary local files, project paths, source maps, stack traces, or provider responses. Expired/invalid capability is a 403-like local error with no secret data; @@ -718,7 +729,7 @@ existing CLI tests are retained, not re-counted as new web evidence. | CLI/packaging/workflow contracts | 10 | flag combinations, browser-open fallback, asset manifest, npm pack/global install, unchanged Action/API bundles | | UI/accessibility/localization/content | 18 | pending/action/blocked/partial/complete, plan diff, narrow/zoom/keyboard/focus/no-color, both palettes/system toggle and contrast, English fallback, escaping | | Integration/compatibility/recovery | 12 | terminal-web parity, manual/environment/dry-run, drift, partial write, doctor reconciliation | -| Security/abuse | 18 | Host/Origin/CSRF, CORS, replay, path traversal, XSS/CSP, secret leaks, no GET mutation, body/time/concurrency limits | +| Security/abuse | 18 | Host/Origin/CSRF, private launch-key enforcement on every API route, CORS, replay, path traversal, XSS/CSP, secret leaks, no GET mutation, body/time/connection limits, atomic lock publication | | **Total** | **102** | No double counting | Within the 18 UI cases, cover at least one render/interaction for each prompt @@ -794,12 +805,16 @@ help for `--web` explains local-only scope and the `--non-interactive` conflict. 11. Given a Secret write succeeds and a later setup operation fails, the result lists the Secret's name/scope as possibly active, never prints its value, and requires inspection before retry or bot PAT deletion. -12. Given hostile Host/Origin/cross-site requests, missing or replayed session - capability, path traversal, oversized body, or injected account/provider - text, the server rejects/escapes it without mutation or secret disclosure. +12. Given hostile Host/Origin/cross-site requests, a missing/incorrect private + launch key on bootstrap, state, or mutations, a missing/replayed controller + capability, excess simultaneous clients, path traversal, oversized body, + or injected account/provider text, the server rejects/escapes it without + mutation or secret disclosure. A failed lock write leaves no published + lock; an orphaned lock is never removed automatically. 13. Given a browser refresh, the same live process restores redacted state - only; PAT values, session capability, and plan approval are never stored - in browser storage or URLs. The final page never calls local disposal + only; PAT values, controller capability, and plan approval are never stored + in browser storage or URLs. The private launch key remains only in the + URL fragment and browser memory. The final page never calls local disposal GitHub revocation or Secret installation verified Action health. 14. Given narrow width, 200% zoom, keyboard-only and reduced-motion settings, every primary state and recovery action remains understandable without diff --git a/src/__tests__/cli.test.ts b/src/__tests__/cli.test.ts index 912e72be9..b3a3c02b2 100644 --- a/src/__tests__/cli.test.ts +++ b/src/__tests__/cli.test.ts @@ -32,7 +32,7 @@ jest.mock('../cli/setup_session_guard', () => ({ })); jest.mock('../cli/web_setup_server', () => ({ - startWebSetupServer: jest.fn(async () => ({ url: 'http://127.0.0.1:40000/', closed: Promise.resolve(), close: jest.fn() })), + startWebSetupServer: jest.fn(async () => ({ url: 'http://127.0.0.1:40000/', launchUrl: 'http://127.0.0.1:40000/#setup-key=test', closed: Promise.resolve(), close: jest.fn() })), openWebSetupBrowser: jest.fn(), })); @@ -521,7 +521,9 @@ describe('CLI', () => { it('uses one browser session through PAT, plan, revalidation, and Apply', async () => { await program.parseAsync(['node', 'cli', 'setup', '--web', '--pr-approval-mode', 'off', '--skip-secrets']); expect(startWebSetupServer).toHaveBeenCalledTimes(1); - expect(openWebSetupBrowser).toHaveBeenCalledWith('http://127.0.0.1:40000/'); + expect(openWebSetupBrowser).toHaveBeenCalledWith('http://127.0.0.1:40000/#setup-key=test'); + const { logInfo } = require('../utils/logger'); + expect(logInfo).toHaveBeenCalledWith(expect.stringContaining('#setup-key=test'), false, undefined, true); expect(ask.mock.calls.map(call => call[0].title)).toEqual(expect.arrayContaining([ 'Confirm this repository', 'How will you provide your setup PAT?', 'Temporary setup PAT', 'Review your setup plan', 'Apply this setup now?', diff --git a/src/cli/__tests__/setup_session_guard.test.ts b/src/cli/__tests__/setup_session_guard.test.ts index 4e9c72e75..289f094cc 100644 --- a/src/cli/__tests__/setup_session_guard.test.ts +++ b/src/cli/__tests__/setup_session_guard.test.ts @@ -1,8 +1,8 @@ import { execFileSync } from 'node:child_process'; import { createHash } from 'node:crypto'; -import { existsSync, mkdtempSync, mkdirSync, readFileSync, realpathSync, rmSync, unlinkSync, writeFileSync } from 'node:fs'; +import { existsSync, mkdtempSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, unlinkSync, writeFileSync } from 'node:fs'; import { tmpdir } from 'node:os'; -import { join } from 'node:path'; +import { basename, join } from 'node:path'; import { acquireSetupSessionGuard } from '../setup_session_guard'; describe('setup session guard', () => { @@ -73,4 +73,17 @@ describe('setup session guard', () => { open.mockRestore(); } }); + + test('a failed staged write never publishes an empty lock or leaves a staging file', () => { + const write = jest.spyOn(require('node:fs'), 'writeFileSync').mockImplementationOnce(() => { + throw Object.assign(new Error('Disk full'), { code: 'ENOSPC' }); + }); + try { + expect(() => acquireSetupSessionGuard(root)).toThrow('Disk full'); + expect(existsSync(lockPath())).toBe(false); + expect(readdirSync(tmpdir()).filter(name => name.startsWith(`${basename(lockPath())}.`))).toEqual([]); + } finally { write.mockRestore(); } + const release = acquireSetupSessionGuard(root); + release(); + }); }); diff --git a/src/cli/__tests__/web_setup_browser_session.test.ts b/src/cli/__tests__/web_setup_browser_session.test.ts index dbebc9dd0..6f0c3c796 100644 --- a/src/cli/__tests__/web_setup_browser_session.test.ts +++ b/src/cli/__tests__/web_setup_browser_session.test.ts @@ -1,4 +1,4 @@ -import { createSetupSession } from '../../../web/src/session/setupSession'; +import { createSetupSession, sessionKeyFromFragment } from '../../../web/src/session/setupSession'; import type { WebSetupView } from '../../application/contracts/web_setup_view'; jest.mock('svelte/store', () => ({ @@ -22,6 +22,11 @@ describe('browser session transport', () => { afterEach(() => { globalThis.fetch = originalFetch; }); + test('reads the one-run key from the fragment without putting it in observable session state', () => { + expect(sessionKeyFromFragment('#setup-key=private-key')).toBe('private-key'); + expect(sessionKeyFromFragment('#other=value')).toBe(''); + }); + test('bootstrap and revision-bound submission keep the PAT out of observable state', async () => { const requests: Array<{ path: string; options?: RequestInit }> = []; globalThis.fetch = jest.fn(async (path: string, options?: RequestInit) => { @@ -32,16 +37,20 @@ describe('browser session transport', () => { throw new Error('Unexpected route'); }) as typeof fetch; - const session = createSetupSession(); + const session = createSetupSession('private-session-key'); let latest = ''; session.subscribe(state => { latest = JSON.stringify(state); }); await session.connect(); await session.submit(7, 'ghp_example_secret'); expect(requests.map(request => request.path)).toEqual(['/api/bootstrap', '/api/state', '/api/answer', '/api/state']); + for (const request of requests) { + expect(request.options?.headers).toEqual(expect.objectContaining({ 'X-Setup-Session-Key': 'private-session-key' })); + } expect(requests[2].options?.headers).toEqual(expect.objectContaining({ 'X-Setup-Capability': 'one-run-capability' })); expect(JSON.parse(String(requests[2].options?.body))).toEqual({ revision: 7, value: 'ghp_example_secret' }); expect(latest).not.toContain('ghp_example_secret'); + expect(latest).not.toContain('private-session-key'); await session.submit(6, 'stale'); expect(requests).toHaveLength(4); }); diff --git a/src/cli/__tests__/web_setup_server.test.ts b/src/cli/__tests__/web_setup_server.test.ts index c0ed01a3c..fe77ee979 100644 --- a/src/cli/__tests__/web_setup_server.test.ts +++ b/src/cli/__tests__/web_setup_server.test.ts @@ -2,9 +2,25 @@ import { mkdtempSync, mkdirSync, rmSync, symlinkSync, unlinkSync, writeFileSync import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { request } from 'node:http'; +import { connect, type Socket } from 'node:net'; import { WebSetupBridge } from '../web_setup_bridge'; import { startWebSetupServer, type WebSetupServer } from '../web_setup_server'; +const sessionKeys = new Map(); +const registerSession = (session: WebSetupServer): void => { + const launch = new URL(session.launchUrl); + sessionKeys.set(launch.origin, new URLSearchParams(launch.hash.slice(1)).get('setup-key')!); +}; +const fetch: typeof globalThis.fetch = (input, init) => { + const url = new URL(input instanceof Request ? input.url : String(input)); + const headers = new Headers(init?.headers); + if (url.pathname.startsWith('/api/')) { + const key = sessionKeys.get(url.origin); + if (key) headers.set('X-Setup-Session-Key', key); + } + return globalThis.fetch(input, { ...init, headers }); +}; + describe('local web setup server', () => { let root: string; let bridge: WebSetupBridge; @@ -18,8 +34,9 @@ describe('local web setup server', () => { writeFileSync(join(root, 'assets', 'app.css'), ':root { color: black; }'); bridge = new WebSetupBridge('owner/repo'); server = await startWebSetupServer(bridge, root); + registerSession(server); }); - afterEach(async () => { if (server) await server.close(); rmSync(root, { recursive: true, force: true }); }); + afterEach(async () => { if (server) await server.close(); sessionKeys.clear(); rmSync(root, { recursive: true, force: true }); }); const jsonPost = (url: string, path: string, body: unknown, headers: Record = {}) => fetch(`${url}${path}`, { method: 'POST', headers: { Origin: url.slice(0, -1), 'Content-Type': 'application/json', ...headers }, body: JSON.stringify(body), @@ -37,6 +54,58 @@ describe('local web setup server', () => { expect((await fetch(`${server.url}assets/%2e%2e/index.html`)).status).toBe(404); }); + test('a private launch fragment is required before any local API state or controller lease is exposed', async () => { + const launch = new URL(server.launchUrl); + const key = new URLSearchParams(launch.hash.slice(1)).get('setup-key'); + expect(key).toMatch(/^[a-f0-9]{64}$/); + expect(launch.origin).toBe(new URL(server.url).origin); + for (const path of ['api/bootstrap', 'api/state']) { + const missing = await globalThis.fetch(`${server.url}${path}`); + expect(missing.status).toBe(403); + expect(await missing.text()).not.toContain(key!); + expect((await globalThis.fetch(`${server.url}${path}`, { headers: { 'X-Setup-Session-Key': '0'.repeat(64) } })).status).toBe(403); + } + expect((await globalThis.fetch(`${server.url}api/takeover`, { + method: 'POST', headers: { Origin: launch.origin, 'Content-Type': 'application/json' }, body: '{}', + })).status).toBe(403); + expect(bridge.snapshot().prompt).toBeUndefined(); + expect((await fetch(`${server.url}api/bootstrap`)).status).toBe(200); + }); + + test('a key from a different local setup run cannot bootstrap this session', async () => { + const other = await startWebSetupServer(new WebSetupBridge('owner/other'), root); + try { + const firstKey = sessionKeys.get(new URL(server.url).origin)!; + const otherKey = new URLSearchParams(new URL(other.launchUrl).hash.slice(1)).get('setup-key')!; + expect(otherKey).not.toBe(firstKey); + expect((await globalThis.fetch(`${other.url}api/bootstrap`, { + headers: { 'X-Setup-Session-Key': firstKey }, + })).status).toBe(403); + expect((await globalThis.fetch(`${other.url}api/bootstrap`, { + headers: { 'X-Setup-Session-Key': otherKey }, + })).status).toBe(200); + } finally { await other.close(); } + }); + + test('bounds simultaneous loopback connections', async () => { + const { port } = new URL(server.url); + const sockets: Socket[] = []; + const open = () => new Promise((resolveSocket, reject) => { + const socket = connect(Number(port), '127.0.0.1'); + socket.once('connect', () => resolveSocket(socket)); + socket.once('error', reject); + }); + try { + sockets.push(...await Promise.all(Array.from({ length: 16 }, open))); + const overflow = await open(); + sockets.push(overflow); + await expect(new Promise((resolveClose, reject) => { + const timeout = setTimeout(() => reject(new Error('Excess connection was not closed.')), 1000); + overflow.once('close', () => { clearTimeout(timeout); resolveClose(); }); + })).resolves.toBeUndefined(); + } finally { for (const socket of sockets) socket.destroy(); } + }); + test('rejects forged hosts and cross-origin mutation', async () => { const forgedStatus = await new Promise((resolveStatus, reject) => { const forged = request(server.url, { headers: { Host: 'evil.example' } }, response => { @@ -110,6 +179,7 @@ describe('local web setup server', () => { const secondBridge = new WebSetupBridge('owner/repo'); const secondServer = await startWebSetupServer(secondBridge, root); + registerSession(secondServer); try { const nextCapability = (await (await fetch(`${secondServer.url}api/bootstrap`)).json() as { capability: string }).capability; secondBridge.setJourney({ repository: 'owner/repo', position: 6, total: 6, current: 'Apply', complete: [], pending: [], mutationStarted: true, choiceReviewPass: 1 }); @@ -238,6 +308,7 @@ describe('local web setup server', () => { try { bridge = new WebSetupBridge('owner/repo'); server = await startWebSetupServer(bridge, root); + registerSession(server); const pending = bridge.ask({ kind: 'secret', title: 'Setup PAT' }); const revision = bridge.snapshot().promptRevision!; await jest.advanceTimersByTimeAsync(30 * 60 * 1000); @@ -257,6 +328,7 @@ describe('local web setup server', () => { try { bridge = new WebSetupBridge('owner/repo'); server = await startWebSetupServer(bridge, root); + registerSession(server); bridge.setJourney({ repository: 'owner/repo', position: 6, total: 6, current: 'Apply', complete: [], pending: [], mutationStarted: true, choiceReviewPass: 1 }); await jest.advanceTimersByTimeAsync(4 * 60 * 60 * 1000); @@ -273,6 +345,7 @@ describe('local web setup server', () => { try { bridge = new WebSetupBridge('owner/repo'); server = await startWebSetupServer(bridge, root); + registerSession(server); const applying = { repository: 'owner/repo', position: 6, total: 6, current: 'Apply', complete: [] as string[], pending: [] as string[], mutationStarted: true, choiceReviewPass: 1 }; bridge.setJourney(applying); @@ -293,6 +366,7 @@ describe('local web setup server', () => { try { bridge = new WebSetupBridge('owner/repo'); server = await startWebSetupServer(bridge, root); + registerSession(server); bridge.finish('complete', 'done'); await jest.advanceTimersByTimeAsync(10 * 60 * 1000); await expect(server.closed).resolves.toBeUndefined(); diff --git a/src/cli/commands/setup.ts b/src/cli/commands/setup.ts index 9f1e6e14e..cd96e501b 100644 --- a/src/cli/commands/setup.ts +++ b/src/cli/commands/setup.ts @@ -125,9 +125,9 @@ export function registerSetupCommand(program: Command): void { webBridge.setRepository(`${gitInfo.owner}/${gitInfo.repo}`); webBridge.setJourney(buildSetupJourneyView(`${gitInfo.owner}/${gitInfo.repo}`, 'repository', false)); webServer = await startWebSetupServer(webBridge); - logInfo(`🌐 Local setup assistant: ${webServer.url}`); + logInfo(`🌐 Private local setup assistant: ${webServer.launchUrl}`, false, undefined, true); logInfo('If the browser does not open, copy this URL into a browser on this computer. The terminal setup remains available with copilot setup.'); - openWebSetupBrowser(webServer.url); + openWebSetupBrowser(webServer.launchUrl); } if (!options.nonInteractive) { journey = new SetupJourneyUseCase(`${gitInfo.owner}/${gitInfo.repo}`, diff --git a/src/cli/setup_session_guard.ts b/src/cli/setup_session_guard.ts index 6702acb45..ebeb30971 100644 --- a/src/cli/setup_session_guard.ts +++ b/src/cli/setup_session_guard.ts @@ -1,6 +1,6 @@ import { createHash, randomBytes } from 'node:crypto'; import { execFileSync } from 'node:child_process'; -import { closeSync, openSync, readFileSync, realpathSync, unlinkSync, writeFileSync } from 'node:fs'; +import { closeSync, linkSync, openSync, readFileSync, realpathSync, unlinkSync, writeFileSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; @@ -13,32 +13,52 @@ export function acquireSetupSessionGuard(cwd: string): () => void { const hash = createHash('sha256').update(repository).digest('hex').slice(0, 32); const lockPath = join(tmpdir(), `copilot-setup-${hash}.lock`); const record: GuardRecord = { pid: process.pid, nonce: randomBytes(16).toString('hex'), repository }; + const stagedPath = `${lockPath}.${record.nonce}.tmp`; + writeStagedLock(stagedPath, record); + let published = false; + let collision: unknown; try { - const fd = openSync(lockPath, 'wx', 0o600); - try { writeFileSync(fd, JSON.stringify(record)); } finally { closeSync(fd); } - return () => { - try { - const current = JSON.parse(readFileSync(lockPath, 'utf8')) as GuardRecord; - if (current.pid === record.pid && current.nonce === record.nonce && current.repository === record.repository) unlinkSync(lockPath); - } catch { /* Missing or replaced lock is not ours to remove. */ } - }; + linkSync(stagedPath, lockPath); // Atomic publication of a fully written record. + published = true; } catch (cause) { - if (!cause || typeof cause !== 'object' || !('code' in cause) || cause.code !== 'EEXIST') throw cause; + collision = cause; + } finally { + try { unlinkSync(stagedPath); } catch { /* A staging-file cleanup failure does not invalidate the published lock. */ } + } + if (!published) { + if (!collision || typeof collision !== 'object' || !('code' in collision) || collision.code !== 'EEXIST') throw collision; let existing: GuardRecord; try { existing = JSON.parse(readFileSync(lockPath, 'utf8')) as GuardRecord; } catch { throw new Error('A setup lock exists but cannot be verified. Inspect it before retrying.'); } if (!Number.isSafeInteger(existing.pid) || existing.pid <= 0 || existing.repository !== repository || !existing.nonce) { - throw setupLockError('A setup lock has unexpected contents. Inspect it before retrying.', cause); + throw setupLockError('A setup lock has unexpected contents. Inspect it before retrying.', collision); } try { process.kill(existing.pid, 0); - throw setupLockError(`Another setup process (${existing.pid}) is active for this checkout. Finish or stop it before starting a second setup.`, cause); + throw setupLockError(`Another setup process (${existing.pid}) is active for this checkout. Finish or stop it before starting a second setup.`, collision); } catch (checkError) { if (!checkError || typeof checkError !== 'object' || !('code' in checkError) || checkError.code !== 'ESRCH') throw checkError; } - // Filesystem reads and unlink are not atomic. Never remove a dead owner's lock here: - // another setup process may already have replaced it after our read. - throw setupLockError(`A setup lock for a stopped process (${existing.pid}) remains at ${lockPath}. Verify no setup is running, remove only that file manually, then retry.`, cause); + // Filesystem reads and unlink are not atomic. Never remove a dead owner's lock here. + throw setupLockError(`A setup lock for a stopped process (${existing.pid}) remains at ${lockPath}. Verify no setup is running, remove only that file manually, then retry.`, collision); + } + return () => { + try { + const current = JSON.parse(readFileSync(lockPath, 'utf8')) as GuardRecord; + if (current.pid === record.pid && current.nonce === record.nonce && current.repository === record.repository) unlinkSync(lockPath); + } catch { /* Missing or replaced lock is not ours to remove. */ } + }; +} + +function writeStagedLock(path: string, record: GuardRecord): void { + const fd = openSync(path, 'wx', 0o600); + try { + writeFileSync(fd, JSON.stringify(record)); + closeSync(fd); + } catch (cause) { + try { closeSync(fd); } catch { /* Already closed or unavailable. */ } + try { unlinkSync(path); } catch { /* Preserve the write failure. */ } + throw cause; } } diff --git a/src/cli/web_setup_server.ts b/src/cli/web_setup_server.ts index 1eac16c4a..9e4b24296 100644 --- a/src/cli/web_setup_server.ts +++ b/src/cli/web_setup_server.ts @@ -2,6 +2,7 @@ import { createServer, type IncomingMessage, type ServerResponse } from 'node:ht import { readFile, realpath } from 'node:fs/promises'; import { join, resolve, sep } from 'node:path'; import { spawn } from 'node:child_process'; +import { randomBytes, timingSafeEqual } from 'node:crypto'; import { WebSetupBridge } from './web_setup_bridge'; const MAX_BODY_BYTES = 8192; @@ -10,12 +11,14 @@ const CSP = "default-src 'none'; script-src 'self'; style-src 'self'; img-src 's export interface WebSetupServer { readonly url: string; + readonly launchUrl: string; readonly closed: Promise; close(): Promise; } /** Transport only: setup policy and credential decisions live behind the bridge. */ export async function startWebSetupServer(bridge: WebSetupBridge, assets = join(__dirname, '..', 'web')): Promise { + const sessionKey = randomBytes(32); const assetRoot = await realpath(assets); if (!(await realpath(join(assetRoot, 'index.html'))).startsWith(`${assetRoot}${sep}`)) { throw new Error('Local setup index must be inside its packaged asset directory.'); @@ -63,6 +66,10 @@ export async function startWebSetupServer(bridge: WebSetupBridge, assets = join( respond(response, 403, { error: 'Invalid request origin.' }); return; } + if (request.url?.startsWith('/api/') && !authorizedSessionKey(request.headers['x-setup-session-key'], sessionKey)) { + respond(response, 403, { error: 'Open the private setup URL printed by the CLI.' }); + return; + } if (request.method === 'GET' && request.url === '/api/bootstrap') { respond(response, 200, bridge.bootstrap()); return; @@ -148,6 +155,7 @@ export async function startWebSetupServer(bridge: WebSetupBridge, assets = join( server.requestTimeout = 15_000; server.headersTimeout = 15_000; server.maxRequestsPerSocket = 250; + server.maxConnections = 16; await new Promise((resolveListen, reject) => { server.once('error', reject); server.listen(0, '127.0.0.1', () => { server.off('error', reject); resolveListen(); }); @@ -155,6 +163,7 @@ export async function startWebSetupServer(bridge: WebSetupBridge, assets = join( const address = server.address(); if (!address || typeof address === 'string') throw new Error('Unable to bind local setup server.'); const url = `http://127.0.0.1:${address.port}/`; + const launchUrl = `${url}#setup-key=${sessionKey.toString('hex')}`; const close = async (): Promise => { if (closing) return closed; closing = true; @@ -176,7 +185,12 @@ export async function startWebSetupServer(bridge: WebSetupBridge, assets = join( const unsubscribe = bridge.subscribe(view => { if (view.outcome && !resultTimer) resultTimer = setTimeout(() => void close(), 10 * 60 * 1000); }); - return { url, closed, close }; + return { url, launchUrl, closed, close }; +} + +function authorizedSessionKey(value: string | string[] | undefined, expected: Buffer): boolean { + return typeof value === 'string' && /^[a-f0-9]{64}$/.test(value) + && timingSafeEqual(Buffer.from(value, 'hex'), expected); } function respond(response: ServerResponse, status: number, body: unknown): void { diff --git a/web/src/session/setupSession.ts b/web/src/session/setupSession.ts index 33b4d0bb9..574440c6a 100644 --- a/web/src/session/setupSession.ts +++ b/web/src/session/setupSession.ts @@ -14,7 +14,13 @@ interface Bootstrap { takeoverTicket: string; } -export function createSetupSession() { +export function sessionKeyFromFragment(fragment: string): string { + return new URLSearchParams(fragment.replace(/^#/, '')).get('setup-key') ?? ''; +} + +export function createSetupSession(sessionKey = sessionKeyFromFragment( + (globalThis as { location?: { hash?: string } }).location?.hash ?? '', +)) { const state = writable({ controller: false, busy: false, error: '' }); let capability: string | undefined; let takeoverTicket = ''; @@ -30,7 +36,7 @@ export function createSetupSession() { if (loading) return; loading = true; try { - const response = await fetch('/api/state', { cache: 'no-store' } as RequestInit); + const response = await fetch('/api/state', { cache: 'no-store', headers: { 'X-Setup-Session-Key': sessionKey } } as RequestInit); if (!response.ok) throw new Error('The local setup session is unavailable.'); set({ view: await response.json() as WebSetupView, ...(preserveError ? {} : { error: '' }) }); } catch { @@ -42,7 +48,7 @@ export function createSetupSession() { async function connect(): Promise { try { - const response = await fetch('/api/bootstrap', { cache: 'no-store' } as RequestInit); + const response = await fetch('/api/bootstrap', { cache: 'no-store', headers: { 'X-Setup-Session-Key': sessionKey } } as RequestInit); if (!response.ok) throw new Error('Could not join this local session.'); const bootstrap = await response.json() as Bootstrap; capability = bootstrap.capability; @@ -59,7 +65,8 @@ export function createSetupSession() { async function post(path: string, body: Record, authorized = true): Promise> { const response = await fetch(path, { method: 'POST', cache: 'no-store', - headers: { 'Content-Type': 'application/json', ...(authorized && capability ? { 'X-Setup-Capability': capability } : {}) }, + headers: { 'Content-Type': 'application/json', 'X-Setup-Session-Key': sessionKey, + ...(authorized && capability ? { 'X-Setup-Capability': capability } : {}) }, body: JSON.stringify(body), } as RequestInit); const data = await response.json() as Record; From c1b18bba434aedf3e56e077b6825cd3d4d1bf760 Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Mon, 28 Sep 2026 21:27:45 +0200 Subject: [PATCH 18/50] codex-setup-temporary-github-auth: pair local web assistant without URL secrets --- build/cli/index.js | 48 ++++-- build/web/assets/index-D-m5cfVM.js | 2 + build/web/assets/index-DzCTZuhQ.js | 2 - build/web/index.html | 2 +- docs/how-to-use.mdx | 9 +- .../operations/troubleshooting.mdx | 7 +- scripts/render-web-setup-component.cjs | 2 +- specs/CATALOG.md | 4 +- specs/catalog.json | 1 + specs/local-web-setup-assistant.md | 48 +++--- src/__tests__/cli.test.ts | 6 +- src/cli/__tests__/web_setup_bridge.test.ts | 16 ++ .../web_setup_browser_session.test.ts | 145 +++++++++++++++--- .../__tests__/web_setup_components.test.ts | 8 + src/cli/__tests__/web_setup_server.test.ts | 66 ++++++-- src/cli/commands/setup.ts | 7 +- src/cli/web_setup_bridge.ts | 5 +- src/cli/web_setup_server.ts | 27 +++- web/src/App.svelte | 10 +- web/src/components/PairingPanel.svelte | 24 +++ web/src/session/setupSession.ts | 43 ++++-- 21 files changed, 374 insertions(+), 108 deletions(-) create mode 100644 build/web/assets/index-D-m5cfVM.js delete mode 100644 build/web/assets/index-DzCTZuhQ.js create mode 100644 web/src/components/PairingPanel.svelte diff --git a/build/cli/index.js b/build/cli/index.js index cfdb2c7ec..2c1cd44b9 100755 --- a/build/cli/index.js +++ b/build/cli/index.js @@ -65940,9 +65940,10 @@ function registerSetupCommand(program) { webBridge.setRepository(`${gitInfo.owner}/${gitInfo.repo}`); webBridge.setJourney((0, setup_journey_policy_1.buildSetupJourneyView)(`${gitInfo.owner}/${gitInfo.repo}`, 'repository', false)); webServer = await (0, web_setup_server_1.startWebSetupServer)(webBridge); - (0, logger_1.logInfo)(`🌐 Private local setup assistant: ${webServer.launchUrl}`, false, undefined, true); - (0, logger_1.logInfo)('If the browser does not open, copy this URL into a browser on this computer. The terminal setup remains available with copilot setup.'); - (0, web_setup_server_1.openWebSetupBrowser)(webServer.launchUrl); + (0, logger_1.logInfo)(`🌐 Local setup assistant: ${webServer.url}`); + (0, logger_1.logInfo)(`🔑 Browser pairing code: ${webServer.pairingCode}`, false, undefined, true); + (0, logger_1.logInfo)('If the browser does not open, copy this URL into a browser on this computer, then enter the pairing code shown above. The terminal setup remains available with copilot setup.'); + (0, web_setup_server_1.openWebSetupBrowser)(webServer.url); } if (!options.nonInteractive) { journey = new setup_journey_use_case_1.SetupJourneyUseCase(`${gitInfo.owner}/${gitInfo.repo}`, webBridge ? new web_setup_adapters_1.WebSetupJourneyPresenter(webBridge) : new setup_journey_presenter_1.ConsoleSetupJourneyPresenter()); @@ -68338,8 +68339,14 @@ class WebSetupBridge { publish(change) { this.revision += 1; this.view = { ...this.view, ...change, revision: this.revision }; - for (const listener of this.subscribers) - listener(this.view); + for (const listener of this.subscribers) { + try { + listener(this.view); + } + catch { + this.subscribers.delete(listener); /* Observers cannot abort a setup decision. */ + } + } } } exports.WebSetupBridge = WebSetupBridge; @@ -68380,6 +68387,8 @@ const CSP = "default-src 'none'; script-src 'self'; style-src 'self'; img-src 's /** Transport only: setup policy and credential decisions live behind the bridge. */ async function startWebSetupServer(bridge, assets = (0, node_path_1.join)(__dirname, '..', 'web')) { const sessionKey = (0, node_crypto_1.randomBytes)(32); + const pairingCode = (0, node_crypto_1.randomBytes)(8); + let failedPairings = 0; const assetRoot = await (0, promises_1.realpath)(assets); if (!(await (0, promises_1.realpath)((0, node_path_1.join)(assetRoot, 'index.html'))).startsWith(`${assetRoot}${node_path_1.sep}`)) { throw new Error('Local setup index must be inside its packaged asset directory.'); @@ -68430,8 +68439,26 @@ async function startWebSetupServer(bridge, assets = (0, node_path_1.join)(__dirn respond(response, 403, { error: 'Invalid request origin.' }); return; } - if (request.url?.startsWith('/api/') && !authorizedSessionKey(request.headers['x-setup-session-key'], sessionKey)) { - respond(response, 403, { error: 'Open the private setup URL printed by the CLI.' }); + if (request.method === 'POST' && request.url === '/api/pair') { + if (request.headers['content-type'] !== 'application/json') { + respond(response, 415, { error: 'JSON required.' }); + return; + } + if (failedPairings >= 5) { + respond(response, 429, { error: 'Too many pairing attempts. Restart setup.' }); + return; + } + const body = await readJson(request); + if (!matchesHexSecret(body.code, pairingCode)) { + failedPairings += 1; + respond(response, 403, { error: 'Incorrect pairing code. Check the terminal.' }); + return; + } + respond(response, 200, { sessionKey: sessionKey.toString('hex') }); + return; + } + if (request.url?.startsWith('/api/') && !matchesHexSecret(request.headers['x-setup-session-key'], sessionKey)) { + respond(response, 403, { error: 'Pair this browser using the code printed by the CLI.' }); return; } if (request.method === 'GET' && request.url === '/api/bootstrap') { @@ -68563,7 +68590,6 @@ async function startWebSetupServer(bridge, assets = (0, node_path_1.join)(__dirn if (!address || typeof address === 'string') throw new Error('Unable to bind local setup server.'); const url = `http://127.0.0.1:${address.port}/`; - const launchUrl = `${url}#setup-key=${sessionKey.toString('hex')}`; const close = async () => { if (closing) return closed; @@ -68590,10 +68616,10 @@ async function startWebSetupServer(bridge, assets = (0, node_path_1.join)(__dirn if (view.outcome && !resultTimer) resultTimer = setTimeout(() => void close(), 10 * 60 * 1000); }); - return { url, launchUrl, closed, close }; + return { url, pairingCode: pairingCode.toString('hex'), closed, close }; } -function authorizedSessionKey(value, expected) { - return typeof value === 'string' && /^[a-f0-9]{64}$/.test(value) +function matchesHexSecret(value, expected) { + return typeof value === 'string' && value.length === expected.length * 2 && /^[a-f0-9]+$/.test(value) && (0, node_crypto_1.timingSafeEqual)(Buffer.from(value, 'hex'), expected); } function respond(response, status, body) { diff --git a/build/web/assets/index-D-m5cfVM.js b/build/web/assets/index-D-m5cfVM.js new file mode 100644 index 000000000..fcef5dd1c --- /dev/null +++ b/build/web/assets/index-D-m5cfVM.js @@ -0,0 +1,2 @@ +(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})(),typeof window<`u`&&((window.__svelte??={}).v??=new Set).add(`5`);var e=!1;function t(){e=!0}t();var n={},r=Symbol(`uninitialized`),i=`http://www.w3.org/1999/xhtml`,a=Array.isArray,o=Array.prototype.indexOf,s=Array.prototype.includes,c=Array.from,l=Object.defineProperty,u=Object.getOwnPropertyDescriptor,d=Object.getOwnPropertyDescriptors,f=Object.prototype,p=Array.prototype,m=Object.getPrototypeOf,h=Object.isExtensible,g=()=>{};function _(e){return e()}function v(e){for(var t=0;t{e=n,t=r}),resolve:e,reject:t}}var b=1024,x=2048,S=4096,ee=8192,te=16384,ne=32768,re=1<<25,ie=65536,ae=1<<18,oe=1<<19,se=1<<20,ce=1<<25,le=1<<21,ue=1<<22,de=1<<23,fe=Symbol(`$state`),pe=Symbol(`component`),me=Symbol(`legacy props`),he=Symbol(``),ge=Symbol(`attributes`),_e=Symbol(`class`),ve=Symbol(`style`),ye=Symbol(`text`),be=Symbol(`form reset`),xe=new class extends Error{name=`StaleReactionError`;message="The reaction that called `getAbortSignal()` was re-run or destroyed"},Se=!!globalThis.document?.contentType&&globalThis.document.contentType.includes(`xml`);function Ce(){console.warn(`https://svelte.dev/e/derived_inert`)}function we(e){console.warn(`https://svelte.dev/e/hydration_mismatch`)}function Te(){console.warn(`https://svelte.dev/e/select_multiple_invalid_value`)}function Ee(){console.warn(`https://svelte.dev/e/svelte_boundary_reset_noop`)}var C=!1;function De(e){C=e}var w;function T(e){if(e===null)throw we(),n;return w=e}function Oe(){return T(on(w))}function E(e){if(C){if(on(w)!==null)throw we(),n;w=e}}function ke(e=1){if(C){for(var t=e,n=w;t--;)n=on(n);w=n}}function Ae(e=!0){for(var t=0,n=w;;){if(n.nodeType===8){var r=n.data;if(r===`]`){if(t===0)return n;--t}else(r===`[`||r===`[!`||r[0]===`[`&&!isNaN(Number(r.slice(1))))&&(t+=1)}var i=on(n);e&&n.remove(),n=i}}function je(e){if(!e||e.nodeType!==8)throw we(),n;return e.data}function Me(e){return e===this.v}function Ne(e,t){return e==e?e!==t||typeof e==`object`&&!!e||typeof e==`function`:t==t}function Pe(e){return!Ne(e,this.v)}function Fe(e){throw Error(`https://svelte.dev/e/lifecycle_outside_component`)}function Ie(){throw Error(`https://svelte.dev/e/async_derived_orphan`)}function Le(e,t,n){throw Error(`https://svelte.dev/e/each_key_duplicate`)}function Re(e){throw Error(`https://svelte.dev/e/effect_in_teardown`)}function ze(){throw Error(`https://svelte.dev/e/effect_in_unowned_derived`)}function Be(e){throw Error(`https://svelte.dev/e/effect_orphan`)}function Ve(){throw Error(`https://svelte.dev/e/effect_update_depth_exceeded`)}function He(e){throw Error(`https://svelte.dev/e/props_invalid_value`)}function Ue(){throw Error(`https://svelte.dev/e/state_descriptors_fixed`)}function We(){throw Error(`https://svelte.dev/e/state_prototype_fixed`)}function Ge(){throw Error(`https://svelte.dev/e/state_unsafe_mutation`)}function Ke(){throw Error(`https://svelte.dev/e/svelte_boundary_reset_onerror`)}var D=null;function qe(e){D=e}function O(t,n=!1,r){D={p:D,i:!1,c:null,e:null,s:t,x:null,r:H,l:e&&!n?{s:null,u:null,$:[]}:null}}function k(e){var t=D,n=t.e;if(n!==null){t.e=null;for(var r of n)bn(r)}return e!==void 0&&(t.x=e),t.i=!0,D=t.p,Je(e)}function Je(e={}){return l(e,pe,{value:!0}),e}function Ye(){return!e||D!==null&&D.l===null}var Xe=[];function Ze(){var e=Xe;Xe=[],v(e)}function Qe(e){if(Xe.length===0&&!wt){var t=Xe;queueMicrotask(()=>{t===Xe&&Ze()})}Xe.push(e)}function $e(){for(;Xe.length>0;)Ze()}var et=~(x|S|b);function A(e,t){e.f=e.f&et|t}function tt(e){e.f&512||e.deps===null?A(e,b):A(e,S)}function nt(e,t,n){e.f&2048?t.add(e):e.f&4096&&n.add(e),A(e,b)}var rt=!1;function it(){rt||(rt=!0,document.addEventListener(`reset`,e=>{Promise.resolve().then(()=>{if(!e.defaultPrevented)for(let t of e.target.elements)t[be]?.()})},{capture:!0}))}function at(e){var t=B,n=H;V(null),Wn(null);try{return e()}finally{V(t),Wn(n)}}function ot(e,t,n,r=n){e.addEventListener(t,()=>at(n));let i=e[be];e[be]=i?()=>{i(),r(!0)}:()=>r(!0),it()}function st(e,t,n,r){let i=Ye()?dt:mt;var a=e.filter(e=>!e.settled),o=t.map(i);if(n.length===0&&a.length===0){r(o);return}var s=H,c=ct(),l=a.length===1?a[0].promise:a.length>1?Promise.all(a.map(e=>e.promise)):null;function u(e){if(!(s.f&16384)){c();try{r([...o,...e])}catch(e){pn(e,s)}lt()}}var d=ut();if(n.length===0){l.then(()=>u([])).finally(d);return}function f(){Promise.all(n.map(e=>pt(e))).then(u).catch(e=>pn(e,s)).finally(d)}l?l.then(()=>{c(),f(),lt()}):f()}function ct(){var e=H,t=B,n=D,r=j;return function(i=!0){Wn(e),V(t),qe(n),i&&!(e.f&16384)&&(r?.activate(),r?.apply())}}function lt(e=!0){Wn(null),V(null),qe(null),e&&j?.deactivate()}function ut(){var e=H,t=e.b,n=j,r=!!t?.is_rendered();return t?.update_pending_count(1,n),n.increment(r,e),()=>{t?.update_pending_count(-1,n),n.decrement(r,e)}}function dt(e){var t=2|x;return H!==null&&(H.f|=oe),{ctx:D,deps:null,effects:null,equals:Me,f:t,fn:e,reactions:null,rv:0,v:r,wv:0,parent:H,ac:null}}var ft=Symbol(`obsolete`);function pt(e,t,n){let i=H;i===null&&Ie();var a=void 0,o=Vt(r),s=!B,c=new Set;return En(()=>{var t=H,n=y();a=n.promise;try{Promise.resolve(e()).then(n.resolve,e=>{e!==xe&&n.reject(e)}).finally(lt)}catch(e){n.reject(e),lt()}var r=j;if(s){if(t.f&32768)var l=ut();if(i.b?.is_rendered())r.async_deriveds.get(t)?.reject(ft);else for(let e of c.values())e.reject(ft);c.add(n),r.async_deriveds.set(t,n)}let u=(e,t=void 0)=>{l?.(),c.delete(n),t!==ft&&(r.activate(),t?(o.f|=de,Gt(o,t)):(o.f&8388608&&(o.f^=de),Gt(o,e)),r.deactivate())};n.promise.then(u,e=>u(null,e||`unknown`))}),vn(()=>{for(let e of c)e.reject(ft)}),new Promise(e=>{function t(n){function r(){n===a?e(o):t(a)}n.then(r,r)}t(a)})}function mt(e){let t=dt(e);return t.equals=Pe,t}function ht(e){var t=e.effects;if(t!==null){e.effects=null;for(var n=0;n{t.ac.abort(xe),t.ac=null}),t.fn!==null&&(t.teardown=g),ar(t,0),An(t))}function yt(e){if(e.effects!==null)for(let t of e.effects)t.teardown&&t.fn!==null&&or(t)}var bt=null,j=null,xt=null,St=null,Ct=null,wt=!1,Tt=!1,Et=null,Dt=null,Ot=0,kt=1,At=class e{id=kt++;#e=!1;linked=!0;#t=null;#n=null;async_deriveds=new Map;current=new Map;previous=new Map;#r=new Set;#i=new Set;#a=0;#o=new Map;#s=null;#c=[];#l=[];#u=new Set;#d=new Set;#f=new Map;#p=new Set;is_fork=!1;#m=!1;constructor(){bt===null?bt=this:(bt.#n=this,this.#t=bt),bt=this}#h(){if(this.is_fork)return!0;for(let n of this.#o.keys()){for(var e=n,t=!1;e.parent!==null;){if(this.#f.has(e)){t=!0;break}e=e.parent}if(!t)return!0}return!1}skip_effect(e){this.#f.has(e)||this.#f.set(e,{d:[],m:[]}),this.#p.delete(e)}unskip_effect(e,t=e=>this.schedule(e)){var n=this.#f.get(e);if(n){this.#f.delete(e);for(var r of n.d)A(r,x),t(r);for(r of n.m)A(r,S),t(r)}this.#p.add(e)}#g(){var e=[];for(let i of this.#c)if(!(i.f&16384||!(i.f&6144))){for(var t=i,n=!1;t.parent!==null;){t=t.parent;var r=t.f;if(r&96){if(!(r&1024)){n=!0;break}t.f^=b}}n||e.push(t)}return this.#c=[],e}#_(){this.#e=!0;for(let e of this.#u)this.#d.delete(e),A(e,x),this.schedule(e);for(let e of this.#d)A(e,S),this.schedule(e);this.apply();for(var t=Et=[],n=[],r=Dt=[];this.#c.length>0;){Ot++>1e3&&(this.#S(),Mt());for(let e of this.#g())try{this.#v(e,t,n)}catch(t){throw Lt(e),this.#h()||this.discard(),t}}if(j=null,r.length>0){var i=e.ensure();for(let e of r)i.schedule(e)}if(Et=null,Dt=null,this.#h()){this.#x(n),this.#x(t);for(let[e,t]of this.#f)It(e,t);r.length>0&&j.#_();return}let a=this.#y();if(a){this.#x(n),this.#x(t),a.#b(this);return}this.#u.clear(),this.#d.clear();for(let e of this.#r)e(this);this.#r.clear(),xt=this,Pt(n),Pt(t),xt=null,this.#s?.resolve();var o=j;if(this.#a===0&&(this.#c.length===0||o!==null)&&this.#S(),this.#c.length>0){if(o!==null){for(let e of this.#c)o.#c.push(e);this.#c=[]}else o=this}o!==null&&(zt.clear(),o.#_())}#v(e,t,n){e.f^=b;for(var r=e.first;r!==null;){var i=r.f,a=!!(i&96);if(!(a&&i&1024||i&8192||this.#f.has(r))&&r.fn!==null){a?r.f^=b:i&4?t.push(r):er(r)&&(i&16&&this.#d.add(r),or(r));var o=r.first;if(o!==null){r=o;continue}}for(;r!==null;){var s=r.next;if(s!==null){r=s;break}r=r.parent}}}#y(){for(var e=this.#t;e!==null;){if(!e.is_fork){for(let[t,[,n]]of this.current)if(e.current.has(t)&&!n)return e}e=e.#t}return null}#b(e){for(let[t,n]of e.current)!this.previous.has(t)&&e.previous.has(t)&&this.previous.set(t,e.previous.get(t)),this.current.set(t,n);for(let[t,n]of e.async_deriveds){let e=this.async_deriveds.get(t);e&&n.promise.then(e.resolve).catch(e.reject)}e.async_deriveds.clear(),this.transfer_effects(e.#u,e.#d);let t=e=>{var n=e.reactions;if(n!==null&&!(e.f&2&&!(e.f&6144)))for(let e of n){var r=e.f;if(r&2)t(e);else{var i=e;r&4194320&&!this.async_deriveds.has(i)&&(this.#d.delete(i),A(i,x),this.schedule(i))}}};for(let e of this.current.keys())t(e);this.oncommit(()=>e.discard()),e.#S(),j=this,this.#_()}#x(e){for(var t=0;t{this.#m=!1,this.linked&&this.flush()}))}transfer_effects(e,t){for(let t of e)this.#u.add(t);for(let e of t)this.#d.add(e);e.clear(),t.clear()}oncommit(e){this.#r.add(e)}ondiscard(e){this.#i.add(e)}settled(){return(this.#s??=y()).promise}static ensure(){if(j===null){let t=j=new e;!Tt&&!wt&&Qe(()=>{t.#e||t.flush()})}return j}apply(){St=null}schedule(e){if(Ct=e,e.b?.is_pending&&e.f&16777228&&!(e.f&32768)){e.b.defer_effect(e);return}this.#c.push(e)}#S(){if(this.linked){var e=this.#t,t=this.#n;e===null||(e.#n=t),t===null?bt=e:t.#t=e,this.linked=!1}}};function jt(e){var t=wt;wt=!0;try{var n;for(e&&(j!==null&&!j.is_fork&&j.flush(),n=e());;){if($e(),j===null)return n;j.flush()}}finally{wt=t}}function Mt(){try{Ve()}catch(e){pn(e,Ct)}}var Nt=null;function Pt(e){var t=e.length;if(t!==0){for(var n=0;n0)){zt.clear();for(let e of Nt){if(e.f&24576)continue;let t=[e],n=e.parent;for(;n!==null;)Nt.has(n)&&(Nt.delete(n),t.push(n)),n=n.parent;for(let e=t.length-1;e>=0;e--){let n=t[e];n.f&24576||or(n)}}Nt.clear()}}Nt=null}}function Ft(e){j.schedule(e)}function It(e,t){if(!(e.f&32&&e.f&1024)){e.f&2048?t.d.push(e):e.f&4096&&t.m.push(e),A(e,b);for(var n=e.first;n!==null;)It(n,t),n=n.next}}function Lt(e){A(e,b);for(var t=e.first;t!==null;)Lt(t),t=t.next}var Rt=new Set,zt=new Map,Bt=!1;function Vt(e,t){return{f:0,v:e,reactions:null,equals:Me,rv:0,wv:0}}function Ht(e,t){let n=Vt(e,t);return Kn(n),n}function M(t,n=!1,r=!0){let i=Vt(t);return n||(i.equals=Pe),e&&r&&D!==null&&D.l!==null&&(D.l.s??=[]).push(i),i}function N(e,t,n=!1){return B!==null&&(!Un||B.f&131072)&&Ye()&&B.f&4325394&&(Gn===null||!Gn.has(e))&&Ge(),Gt(e,n?Yt(t):t,Dt)}var Ut=null,Wt=0;function Gt(e,t,n=null){if(!e.equals(t)){Vn?zt.set(e,t):zt.has(e)||zt.set(e,e.v);var r=At.ensure();if(r.capture(e,t),e.f&2){let t=e;e.f&2048&>(t),St===null&&tt(t)}e.wv=$n(),Ut=null,Wt=0,Jt(e,x,n),Ut=null,Ye()&&H!==null&&H.f&1024&&!(H.f&96)&&(qn===null?Jn([e]):qn.push(e)),!r.is_fork&&Rt.size>0&&!Bt&&Kt()}return t}function Kt(){Bt=!1;for(let e of Rt){e.f&1024&&A(e,S);let t;try{t=er(e)}catch{t=!0}t&&or(e)}Rt.clear()}function qt(e){N(e,e.v+1)}function Jt(e,t,n){var r=e.reactions;if(r!==null){var i=Ye(),a=r.length;if(Wt+=a,Wt>1e5&&Ut===null&&(Ut=new Set),Ut!==null){if(Ut.has(e))return;Ut.add(e)}for(var o=0;o{if(Zn===c)return e();var t=B,n=Zn;V(null),Qn(c);var r=e();return V(t),Qn(n),r};return i&&n.set(`length`,Ht(e.length,s)),new Proxy(e,{defineProperty(e,t,r){(!(`value`in r)||r.configurable===!1||r.enumerable===!1||r.writable===!1)&&Ue();var i=n.get(t);return i===void 0?l(()=>{var e=Ht(r.value,s);return n.set(t,e),e}):N(i,r.value,!0),!0},deleteProperty(e,t){var i=n.get(t);if(i===void 0){if(t in e){let e=l(()=>Ht(r,s));n.set(t,e),qt(o)}}else N(i,r),qt(o);return!0},get(t,i,a){if(i===fe)return e;var o=n.get(i),c=i in t;if(o===void 0&&(!c||u(t,i)?.writable)&&(o=l(()=>Ht(Yt(c?t[i]:r),s)),n.set(i,o)),o!==void 0){var d=G(o);return d===r?void 0:d}return Reflect.get(t,i,a)},getOwnPropertyDescriptor(e,t){this.has?.(e,t);var i=Reflect.getOwnPropertyDescriptor(e,t),a=n.get(t);if(a!==void 0){var o=G(a);if(o===r)return;if(i&&`value`in i)i.value=o;else return{enumerable:!0,configurable:!0,value:o,writable:!0}}return i},has(e,t){if(t===fe)return!0;var i=n.get(t),a=i!==void 0&&i.v!==r||Reflect.has(e,t);return(i!==void 0||H!==null&&(!a||u(e,t)?.writable))&&(i===void 0&&(i=l(()=>Ht(a?Yt(e[t]):r,s)),n.set(t,i)),G(i)===r)?!1:a},set(e,t,a,c){var d=n.get(t),f=t in e;if(i&&t===`length`)for(var p=a;pHt(r,s)),n.set(p+``,m)):N(m,r)}if(d===void 0)(!f||u(e,t)?.writable)&&(d=l(()=>Ht(void 0,s)),N(d,Yt(a)),n.set(t,d));else{f=d.v!==r;var h=l(()=>Yt(a));N(d,h)}var g=Reflect.getOwnPropertyDescriptor(e,t);if(g?.set&&g.set.call(c,a),!f){if(i&&typeof t==`string`){var _=n.get(`length`),v=Number(t);Number.isInteger(v)&&v>=_.v&&N(_,v+1)}qt(o)}return!0},ownKeys(e){G(o);var t=Reflect.ownKeys(e).filter(e=>{var t=n.get(e);return t===void 0||t.v!==r});for(var[i,a]of n)a.v!==r&&!(i in e)&&t.push(i);return t},setPrototypeOf(){We()}})}function Xt(e){try{if(typeof e==`object`&&e&&fe in e)return e[fe]}catch{}return e}function Zt(e,t){return Object.is(Xt(e),Xt(t))}var Qt,$t,en,tn;function nn(){if(Qt===void 0){Qt=window,$t=/Firefox/.test(navigator.userAgent);var e=Element.prototype,t=Node.prototype,n=Text.prototype;en=u(t,`firstChild`).get,tn=u(t,`nextSibling`).get,h(e)&&(e[_e]=void 0,e[ge]=null,e[ve]=void 0,e.__e=void 0),h(n)&&(n[ye]=void 0)}}function rn(e=``){return document.createTextNode(e)}function an(e){return en.call(e)}function on(e){return tn.call(e)}function P(e,t){if(!C)return an(e);var n=an(w);if(n===null)n=w.appendChild(rn());else if(t&&n.nodeType!==3){var r=rn();return n?.before(r),T(r),r}return t&&dn(n),T(n),n}function sn(e,t=!1){if(!C){var n=an(e);return n instanceof Comment&&n.data===``?on(n):n}if(t){if(w?.nodeType!==3){var r=rn();return w?.before(r),T(r),r}dn(w)}return w}function F(e,t=!1){if(!C)return an(e);var n=P(e,t);return E(e),n}function I(e,t=1,n=!1){let r=C?w:e;for(var i;t--;)i=r,r=on(r);if(!C)return r;if(n){if(r?.nodeType!==3){var a=rn();return r===null?i?.after(a):r.before(a),T(a),a}dn(r)}return T(r),r}function cn(e){e.textContent=``}function ln(){return!1}function un(e,t,n){return t==null||t===`http://www.w3.org/1999/xhtml`?n?document.createElement(e,{is:n}):document.createElement(e):n?document.createElementNS(t,e,{is:n}):document.createElementNS(t,e)}function dn(e){if(e.nodeValue.length<65536)return;let t=e.nextSibling;for(;t!==null&&t.nodeType===3;)t.remove(),e.nodeValue+=t.nodeValue,t=e.nextSibling}function fn(e){var t=H;if(t===null)return B.f|=de,e;if(!(t.f&32768)&&!(t.f&4))throw e;pn(e,t)}function pn(e,t){if(!(t!==null&&t.f&16384)){for(;t!==null;){if(t.f&128&&!(t.f&33570816)){if(!(t.f&32768))throw e;try{t.b.error(e);return}catch(t){e=t}}t=t.parent}throw e}}function mn(e){H===null&&(B===null&&Be(e),ze()),Vn&&Re(e)}function hn(e,t){var n=t.last;n===null?t.last=t.first=e:(n.next=e,e.prev=n,t.last=e)}function gn(e,t){var n=H;n!==null&&n.f&8192&&(e|=ee);var r={ctx:D,deps:null,nodes:null,f:e|x|512,first:null,fn:t,last:null,next:null,parent:n,b:n&&n.b,prev:null,teardown:null,wv:0,ac:null};j?.register_created_effect(r);var i=r;if(e&4)Et===null?At.ensure().schedule(r):Et.push(r);else if(t!==null){try{or(r)}catch(e){throw z(r),e}i.deps===null&&i.teardown===null&&i.nodes===null&&i.first===i.last&&!(i.f&524288)&&(i=i.first,e&16&&e&65536&&i!==null&&(i.f|=ie))}if(i!==null&&(i.parent=n,n!==null&&hn(i,n),B!==null&&B.f&2&&!(e&64))){var a=B;(a.effects??=[]).push(i)}return r}function _n(){return B!==null&&!Un}function vn(e){let t=gn(8,null);return A(t,b),t.teardown=e,t}function yn(e){mn(`$effect`);var t=H.f;if(!B&&t&32&&D!==null&&!D.i){var n=D;(n.e??=[]).push(e)}else return bn(e)}function bn(e){return gn(4|se,e)}function xn(e){return mn(`$effect.pre`),gn(8|se,e)}function Sn(e){At.ensure();let t=gn(64|oe,e);return(e={})=>new Promise(n=>{e.outro?Pn(t,()=>{z(t),n(void 0)}):(z(t),n(void 0))})}function Cn(e){return gn(4,e)}function wn(e,t){var n=D,r={effect:null,ran:!1,deps:e};n.l.$.push(r),r.effect=Dn(()=>{if(e(),!r.ran){r.ran=!0;var n=H;try{Wn(n.parent),K(t)}finally{Wn(n)}}})}function Tn(){var e=D;Dn(()=>{for(var t of e.l.$){t.deps();var n=t.effect;n.f&1024&&n.deps!==null&&A(n,S),er(n)&&or(n),t.ran=!1}})}function En(e){return gn(ue|oe,e)}function Dn(e,t=0){return gn(8|t,e)}function L(e,t=[],n=[],r=[]){st(r,t,n,t=>{gn(8,()=>{e(...t.map(G))})})}function On(e,t=0){return gn(16|t,e)}function R(e){return gn(32|oe,e)}function kn(e){var t=e.teardown;if(t!==null){let n=Vn,r=B;Hn(!0),V(null);try{t.call(null)}catch(t){pn(t,e.parent)}finally{Hn(n),V(r)}}}function An(e,t=!1){var n=e.first;for(e.first=e.last=null;n!==null;){let e=n.ac;e!==null&&at(()=>{e.abort(xe)});var r=n.next;n.f&64?n.parent=null:z(n,t),n=r}}function jn(e){for(var t=e.first;t!==null;){var n=t.next;t.f&32||z(t),t=n}}function z(e,t=!0){var n=!1;(t||e.f&262144)&&e.nodes!==null&&e.nodes.end!==null&&(Mn(e.nodes.start,e.nodes.end),n=!0),e.f|=re,An(e,t&&!n),ar(e,0);var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)e.stop();kn(e),e.f^=re,e.f|=te;var i=e.parent;i!==null&&i.first!==null&&Nn(e),e.next=e.prev=e.teardown=e.ctx=e.deps=e.fn=e.nodes=e.ac=e.b=null}function Mn(e,t){for(;e!==null;){var n=e===t?null:on(e);e.remove(),e=n}}function Nn(e){var t=e.parent,n=e.prev,r=e.next;n!==null&&(n.next=r),r!==null&&(r.prev=n),t!==null&&(t.first===e&&(t.first=r),t.last===e&&(t.last=n))}function Pn(e,t,n=!0){var r=[];e.f|=256,Fn(e,r,!0);var i=()=>{n&&z(e),t&&t()},a=r.length;if(a>0){var o=()=>--a||i();for(var s of r)s.out(o)}else i()}function Fn(e,t,n){if(!(e.f&8192)){e.f^=ee;var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)(e.is_global||n)&&t.push(e);for(var i=e.first;i!==null;){var a=i.next;if(!(i.f&64)){var o=!!(i.f&65536)||!!(i.f&32)&&!!(e.f&16);Fn(i,t,o?n:!1)}i=a}}}function In(e){e.f&=-257,Ln(e,!0)}function Ln(e,t){if(!(e.f&256)&&e.f&8192){e.f^=ee,e.f&1024||(A(e,x),At.ensure().schedule(e));for(var n=e.first;n!==null;){var r=n.next,i=!!(n.f&65536)||!!(n.f&32);Ln(n,i?t:!1),n=r}var a=e.nodes&&e.nodes.t;if(a!==null)for(let e of a)(e.is_global||t)&&e.in()}}function Rn(e,t){if(e.nodes)for(var n=e.nodes.start,r=e.nodes.end;n!==null;){var i=n===r?null:on(n);t.append(n),n=i}}var zn=null,Bn=!1,Vn=!1;function Hn(e){Vn=e}var B=null,Un=!1;function V(e){B=e}var H=null;function Wn(e){H=e}var Gn=null;function Kn(e){B!==null&&(B.f&2097152||B.f&2)&&(Gn??=new Set).add(e)}var U=null,W=0,qn=null;function Jn(e){qn=e}var Yn=1,Xn=0,Zn=Xn;function Qn(e){Zn=e}function $n(){return++Yn}function er(e){var t=e.f;if(t&2048)return!0;if(t&4096){for(var n=e.deps,r=n.length,i=0;ie.wv)return!0}t&512&&St===null&&A(e,b)}return!1}function tr(e,t,n=!0){var r=e.reactions;if(r!==null&&!(Gn!==null&&Gn.has(e)))for(var i=0;i{e.ac.abort(xe)}),e.ac=null);try{e.f|=le;var u=e.fn,d=u();e.f|=ne;var f=rr(e);if(Ye()&&qn!==null&&!Un&&f!==null&&!(e.f&6146))for(var p=0;p0)for(t.length=W+U.length,r=0;r{c.ac.abort(xe),c.ac=null,A(c,x)}),vt(c),ar(c,0)}}function ar(e,t){var n=e.deps;if(n!==null)for(var r=t;rn?.call(this,e))}return e.startsWith(`pointer`)||e.startsWith(`touch`)||e===`wheel`?(i.__removed=!1,Qe(()=>{i.__removed||t.addEventListener(e,i,r)})):t.addEventListener(e,i,r),i}function hr(e,t,n,r,i){var a={capture:r,passive:i},o=mr(e,t,n,a);(t===document.body||t===window||t===document||t instanceof HTMLMediaElement)&&vn(()=>{o.__removed=!0,t.removeEventListener(e,o,a)})}function gr(e,t,n){(t[dr]??={})[e]=n}function _r(e){for(var t=0;t{yr=!1,vr=null}));var o=0,s=vr===e&&e[dr];if(s){var c=i.indexOf(s);if(c!==-1&&(t===document||t===window)){e[dr]=t;return}var u=i.indexOf(t);if(u===-1)return;c<=u&&(o=c)}if(a=i[o]||e.target,a!==t){l(e,`currentTarget`,{configurable:!0,get(){return a||n}});var d=B,f=H;V(null),Wn(null);try{for(var p,m=[];a!==null&&a!==t;){try{var h=a[dr]?.[r];h!=null&&(!a.disabled||e.target===a)&&h.call(a,e)}catch(e){p?m.push(e):p=e}if(e.cancelBubble)break;o++,a=o{throw e});throw p}}finally{e[dr]=t,delete e.currentTarget,V(d),Wn(f)}}}var xr=globalThis?.window?.trustedTypes&&globalThis.window.trustedTypes.createPolicy(`svelte-trusted-html`,{createHTML:e=>e});function Sr(e){return xr?.createHTML(e)??e}function Cr(e){var t=un(`template`);return t.innerHTML=Sr(e.replaceAll(``,``)),t.content}function wr(e,t){var n=H;n.nodes===null&&(n.nodes={start:e,end:t,a:null,t:null})}function J(e,t){var n=!!(t&1),r=!!(t&2),i,a=!e.startsWith(``);return()=>{if(C)return wr(w,null),w;i===void 0&&(i=Cr(a?e:``+e),n||(i=an(i)));var t=r||$t?document.importNode(i,!0):i.cloneNode(!0);if(n){var o=an(t),s=t.lastChild;wr(o,s)}else wr(t,t);return t}}function Y(e,t){if(C){var n=H;(!(n.f&32768)||n.nodes.end===null)&&(n.nodes.end=w),Oe();return}e!==null&&e.before(t)}[...`allowfullscreen.async.autofocus.autoplay.checked.controls.default.disabled.formnovalidate.indeterminate.inert.ismap.loop.multiple.muted.nomodule.novalidate.open.playsinline.readonly.required.reversed.seamless.selected.webkitdirectory.defer.disablepictureinpicture.disableremoteplayback`.split(`.`)];var Tr=[`touchstart`,`touchmove`];function Er(e){return Tr.includes(e)}function Dr(e){let t=0,n=Vt(0),r;return()=>{_n()&&(G(n),Dn(()=>(t===0&&(r=K(()=>e(()=>qt(n)))),t+=1,()=>{Qe(()=>{--t,t===0&&(r?.(),r=void 0,qt(n))})})))}}var Or=ie|oe;function kr(e,t,n,r){new Ar(e,t,n,r)}var Ar=class{parent;is_pending=!1;transform_error;#e;#t=C?w:null;#n;#r;#i;#a=null;#o=null;#s=null;#c=null;#l=0;#u=0;#d=!1;#f=new Set;#p=new Set;#m=null;#h=Dr(()=>(this.#m=Vt(this.#l),()=>{this.#m=null}));constructor(e,t,n,r){this.#e=e,this.#n=t,this.#r=e=>{var t=H;t.b=this,t.f|=128,n(e)},this.parent=H.b,this.transform_error=r??this.parent?.transform_error??(e=>e),this.#i=On(()=>{if(C){let e=this.#t;Oe();let t=e.data===`[!`;if(e.data.startsWith(`[?`)){let t=JSON.parse(e.data.slice(2));this.#_(t)}else t?this.#y():this.#g()}else this.#b()},Or),C&&(this.#e=w)}#g(){try{this.#a=R(()=>this.#r(this.#e))}catch(e){this.error(e)}}#_(e){let t=this.#n.failed,{reset:n,invoke_onerror:r}=this.#v(e);Qe(r),t&&(this.#s=R(()=>{t(this.#e,()=>e,()=>n)}))}#v(e){var t=!1,n=!1;let r=()=>{if(t){Ee();return}t=!0,n&&Ke(),this.#s!==null&&Pn(this.#s,()=>{this.#s=null}),this.#S(()=>{this.#b()})};return{reset:r,invoke_onerror:()=>{try{n=!0,this.#n.onerror?.(e,r),n=!1}catch(e){pn(e,this.#i&&this.#i.parent)}}}}#y(){let e=this.#n.pending;e&&(this.is_pending=!0,this.#o=R(()=>e(this.#e)),Qe(()=>{var e=this.#c=document.createDocumentFragment(),t=rn(),n=!1;if(e.append(t),this.#a=this.#S(()=>{try{return R(()=>this.#r(t))}catch(e){try{this.error(e),n=!0}catch(e){pn(e,this.#i.parent)}return null}}),this.#a===null){this.#c=null,n&&this.#x(j);return}this.#u===0&&(this.#e.before(e),this.#c=null,Pn(this.#o,()=>{this.#o=null}),this.#x(j))}))}#b(){try{if(this.is_pending=this.has_pending_snippet(),this.#u=0,this.#l=0,this.#a=R(()=>{this.#r(this.#e)}),this.#u>0){var e=this.#c=document.createDocumentFragment();Rn(this.#a,e);let t=this.#n.pending;this.#o=R(()=>t(this.#e))}else this.#x(j)}catch(e){this.error(e)}}#x(e){this.is_pending=!1,e.transfer_effects(this.#f,this.#p)}defer_effect(e){nt(e,this.#f,this.#p)}is_rendered(){return!this.is_pending&&(!this.parent||this.parent.is_rendered())}has_pending_snippet(){return!!this.#n.pending}#S(e){var t=H,n=B,r=D;Wn(this.#i),V(this.#i),qe(this.#i.ctx);try{return At.ensure(),e()}finally{Wn(t),V(n),qe(r)}}#C(e,t){if(!this.has_pending_snippet()){this.parent&&this.parent.#C(e,t);return}this.#u+=e,this.#u===0&&(this.#x(t),this.#o&&Pn(this.#o,()=>{this.#o=null}),this.#c&&=(this.#e.before(this.#c),null))}update_pending_count(e,t){this.#C(e,t),this.#l+=e,!(!this.#m||this.#d)&&(this.#d=!0,Qe(()=>{this.#d=!1,this.#m&&Gt(this.#m,this.#l)}))}get_effect_pending(){return this.#h(),G(this.#m)}error(e){if(!this.#n.onerror&&!this.#n.failed)throw e;j?.is_fork?(this.#a&&j.skip_effect(this.#a),this.#o&&j.skip_effect(this.#o),this.#s&&j.skip_effect(this.#s),j.oncommit(()=>{this.#w(e)})):this.#w(e)}#w(e){this.#a&&=(z(this.#a),null),this.#o&&=(z(this.#o),null),this.#s&&=(z(this.#s),null),C&&(T(this.#t),ke(),T(Ae()));let t=this.#n.failed,n=e=>{let{reset:n,invoke_onerror:r}=this.#v(e);r(),t&&(this.#s=this.#S(()=>{try{return R(()=>{var r=H;r.b=this,r.f|=128,t(this.#e,()=>e,()=>n)})}catch(e){return pn(e,this.#i.parent),null}}))};Qe(()=>{var t;try{t=this.transform_error(e)}catch(e){pn(e,this.#i&&this.#i.parent);return}typeof t==`object`&&t&&typeof t.then==`function`?t.then(n,e=>pn(e,this.#i&&this.#i.parent)):n(t)})}};function X(e,t){var n=t==null?``:typeof t==`object`?`${t}`:t;n!==(e[ye]??=e.nodeValue)&&(e[ye]=n,e.nodeValue=`${n}`)}function jr(e,t){return Nr(e,t)}var Mr=new Map;function Nr(e,{target:t,anchor:r,props:i={},events:a,context:o,intro:s=!0,transformError:l}){nn();var u=void 0,d=Sn(()=>{var s=r??t.appendChild(rn());kr(s,{pending:()=>{}},t=>{O({});var r=D;if(o&&(r.c=o),a&&(i.$$events=a),C&&wr(t,null),u=e(t,i)||Je(),C&&(H.nodes.end=w,w===null||w.nodeType!==8||w.data!==`]`))throw we(),n;k()},l);var d=new Set,f=e=>{for(var n=0;n{for(var e of d)for(let r of[t,document]){var n=Mr.get(r),i=n.get(e);--i==0?(r.removeEventListener(e,br),n.delete(e),n.size===0&&Mr.delete(r)):n.set(e,i)}pr.delete(f),s!==r&&s.parentNode?.removeChild(s)}});return Pr.set(u,d),u}var Pr=new WeakMap,Fr=class{anchor;#e=new Map;#t=new Map;#n=new Map;#r=new Set;#i=!0;constructor(e,t=!0){this.anchor=e,this.#i=t}#a=e=>{if(this.#e.has(e)){var t=this.#e.get(e),n=this.#t.get(t);if(n)In(n),this.#r.delete(t);else{var r=this.#n.get(t);r&&(In(r.effect),this.#t.set(t,r.effect),this.#n.delete(t),r.fragment.lastChild.remove(),this.anchor.before(r.fragment),n=r.effect)}for(let[t,n]of this.#e){if(this.#e.delete(t),t===e)break;let r=this.#n.get(n);r&&(z(r.effect),this.#n.delete(n))}for(let[e,r]of this.#t){if(e===t||this.#r.has(e))continue;let i=()=>{if(Array.from(this.#e.values()).includes(e)){var t=document.createDocumentFragment();Rn(r,t),t.append(rn()),this.#n.set(e,{effect:r,fragment:t})}else z(r);this.#r.delete(e),this.#t.delete(e)};this.#i||!n?(this.#r.add(e),Pn(r,i,!1)):i()}}};#o=e=>{this.#e.delete(e);let t=Array.from(this.#e.values());for(let[e,n]of this.#n)t.includes(e)||(z(n.effect),this.#n.delete(e))};ensure(e,t){var n=j,r=ln();if(t&&!this.#t.has(e)&&!this.#n.has(e)){if(r){var i=document.createDocumentFragment(),a=rn();i.append(a),this.#n.set(e,{effect:R(()=>t(a)),fragment:i})}else this.#t.set(e,R(()=>t(this.anchor)))}if(this.#e.set(n,e),r){for(let[t,r]of this.#t)t===e?n.unskip_effect(r):n.skip_effect(r);for(let[t,r]of this.#n)t===e?n.unskip_effect(r.effect):n.skip_effect(r.effect);n.oncommit(this.#a),n.ondiscard(this.#o)}else C&&(this.anchor=w),this.#a(n)}};function Ir(t){D===null&&Fe(`onMount`),e&&D.l!==null?Lr(D).m.push(t):yn(()=>{let e=K(t);if(typeof e==`function`)return e})}function Lr(e){var t=e.l;return t.u??={a:[],b:[],m:[]}}function Z(e,t,n=!1){var r;C&&(r=w,Oe());var i=new Fr(e),a=n?ie:0;function o(e,t){if(C){var n=je(r);if(e!==parseInt(n.substring(1))){var a=Ae();T(a),i.anchor=a,De(!1),i.ensure(e,t),De(!0);return}}i.ensure(e,t)}On(()=>{var e=!1;t((t,n=0)=>{e=!0,o(n,t)}),e||o(-1,null)},a)}var Rr=Symbol(`NaN`);function zr(e,t,n){C&&Oe();var r=new Fr(e),i=!Ye();On(()=>{var e=t();e!==e&&(e=Rr),i&&typeof e==`object`&&e&&(e={}),r.ensure(e,n)})}function Br(e,t){return t}function Vr(e,t,n){for(var r=[],i=t.length,a,o=t.length,s=0;s{if(a){if(a.pending.delete(n),a.done.add(n),a.pending.size===0){var t=e.outrogroups;Hr(e,c(a.done)),t.delete(a),t.size===0&&(e.outrogroups=null)}}else--o},!1)}if(o===0){var l=r.length===0&&n!==null&&e.pending.size===0;if(l){var u=n,d=u.parentNode;cn(d),d.append(u),e.items.clear()}Hr(e,t,!l)}else a={pending:new Set(t),done:new Set},(e.outrogroups??=new Set).add(a)}function Hr(e,t,n=!0){var r;if(e.pending.size>0){r=new Set;for(let t of e.pending.values())for(let n of t)r.add(e.items.get(n).e)}for(var i=0;i{var e=n();return a(e)?e:e==null?[]:c(e)}),p,m=new Map,h=!0;function g(e){v.effect.f&16384||(v.pending.delete(e),v.fallback=d,Kr(v,p,s,t,r),d!==null&&(p.length===0?d.f&33554432?(d.f^=ce,Jr(d,null,s)):In(d):Pn(d,()=>{d=null})))}function _(e){v.pending.delete(e)}var v={effect:On(()=>{p=G(f);var e=p.length;let a=!1;C&&je(s)===`[!`!=(e===0)&&(s=Ae(),T(s),De(!1),a=!0);for(var c=new Set,u=j,v=ln(),y=0;yo(s)):(d=R(()=>o(Ur??=rn())),d.f|=ce)),e>c.size&&Le(``,``,``),C&&e>0&&T(Ae()),!h){if(m.set(u,c),v){for(let[e,t]of l)c.has(e)||u.skip_effect(t.e);u.oncommit(g),u.ondiscard(_)}else g(u)}a&&De(!0),G(f)}),flags:t,items:l,pending:m,outrogroups:null,fallback:d};h=!1,C&&(s=w)}function Gr(e){for(;e!==null&&!(e.f&32);)e=e.next;return e}function Kr(e,t,n,r,i){var a=!!(r&8),o=t.length,s=e.items,l=Gr(e.effect.first),u,d=null,f,p=[],m=[],h,g,_,v;if(a)for(v=0;v0){var re=r&4&&o===0?n:null;if(a){for(v=0;v{if(f!==void 0)for(_ of f)_.nodes?.a?.apply()})}function qr(e,t,n,r,i,a,o,s){var c=o&1?o&16?Vt(n):M(n,!1,!1):null,l=o&2?Vt(i):null;return{v:c,i:l,e:R(()=>(a(t,c??n,l??i,s),()=>{e.delete(r)}))}}function Jr(e,t,n){if(e.nodes)for(var r=e.nodes.start,i=e.nodes.end,a=t&&!(t.f&33554432)?t.nodes.start:n;r!==null;){var o=on(r);if(a.before(r),r===i)return;r=o}}function Yr(e,t,n){t===null?e.effect.first=n:t.next=n,n===null?e.effect.last=t:n.prev=t}function Xr(e,t){let n=null,r=C;var i;if(C){n=w;for(var a=an(document.head);a!==null&&(a.nodeType!==8||a.data!==e);)a=on(a);if(a===null)De(!1);else{var o=on(a);a.remove(),T(o)}}C||(i=document.head.appendChild(rn()));try{On(()=>{var e=R(()=>t(i));e.f|=ae,C||(e.nodes===null?e.nodes={start:i,end:i,a:null,t:null}:e.nodes.end=i)})}finally{r&&(De(!0),T(n))}}function Zr(e){var t,n,r=``;if(typeof e==`string`||typeof e==`number`)r+=e;else if(typeof e==`object`){if(Array.isArray(e)){var i=e.length;for(t=0;t=0;){var s=o+a;(o===0||ei.includes(r[o-1]))&&(s===r.length||ei.includes(r[s]))?r=(o===0?``:r.substring(0,o))+r.substring(s+1):o=s}}return r===``?null:r}function ni(e,t,n,r,i,a){var o=e[_e];if(C||o!==n||o===void 0){var s=ti(n,r,a);(!C||s!==e.getAttribute(`class`))&&(s==null?e.removeAttribute(`class`):t?e.className=s:e.setAttribute(`class`,s)),e[_e]=n}else if(a&&i!==a)for(var c in a){var l=!!a[c];(i==null||l!==!!i[c])&&e.classList.toggle(c,l)}return a}function ri(e,t){t?e.hasAttribute(`selected`)||e.setAttribute(`selected`,``):e.removeAttribute(`selected`)}function ii(e,t){var n=e.__defaultValue,r=e.multiple,i=r?n??[]:null;if(!r||a(i)){var o=e.selectedIndex,s=t&&r?new Set(e.selectedOptions):null;for(var c of e.options){var l=ci(c);ri(c,r?i.includes(l):Zt(l,n))}if(t){if(s!==null)for(c of e.options){var u=s.has(c);c.selected!==u&&(c.selected=u)}else e.selectedIndex!==o&&(e.selectedIndex=o)}}}function ai(e,t,n=!1){if(e.multiple){if(t==null)return;if(!a(t))return Te();for(var r of e.options)r.selected=t.includes(ci(r));return}for(r of e.options)if(Zt(ci(r),t)){r.selected=!0;return}(!n||t!==void 0)&&(e.selectedIndex=-1)}function oi(e){var t=new MutationObserver(t=>{t.every(li)||(`__defaultValue`in e&&ii(e,!1),`__value`in e&&ai(e,e.__value))});t.observe(e,{childList:!0,subtree:!0,attributes:!0,attributeFilter:[`value`]}),vn(()=>{t.disconnect()})}function si(e,t,n=t){var r=new WeakSet,i=!0;ot(e,`change`,t=>{var i=t?`[selected]`:`:checked`,a;if(e.multiple)a=[].map.call(e.querySelectorAll(i),ci);else{var o=e.querySelector(i)??e.querySelector(`option:not([disabled])`);a=o&&ci(o)}n(a),e.__value=a,j!==null&&r.add(j)}),Cn(()=>{var a=t();if(e===document.activeElement){var o=j;if(r.has(o))return}if(ai(e,a,i),i&&a===void 0){var s=e.querySelector(`:checked`);s!==null&&(a=ci(s),n(a))}e.__value=a,i=!1})}function ci(e){return`__value`in e?e.__value:e.value}function li(e){if(e.target.closest(`selectedcontent`)!==null)return!0;if(e.type===`childList`){var t=[...e.addedNodes,...e.removedNodes];return t.length>0&&t.every(e=>e.nodeName===`SELECTEDCONTENT`)}return!1}var ui=Symbol(`is custom element`),di=Symbol(`is html`),fi=Se?`link`:`LINK`;function pi(e){if(C){var t=!1,n=()=>{if(!t){if(t=!0,e.hasAttribute(`value`)){var n=e.value;Q(e,`value`,null),e.value=n}if(e.hasAttribute(`checked`)){var r=e.checked;Q(e,`checked`,null),e.checked=r}}};e[be]=n,Qe(n),it()}}function mi(e,t){var n=hi(e);n.checked!==(n.checked=t??void 0)&&(e.checked=t)}function Q(e,t,n,r){var i=hi(e);C&&(i[t]=e.getAttribute(t),t===`src`||t===`srcset`||t===`href`&&e.nodeName===fi)||i[t]!==(i[t]=n)&&(t===`loading`&&(e[he]=n),n==null?e.removeAttribute(t):typeof n!=`string`&&_i(e).has(t)?e[t]=n:e.setAttribute(t,n))}function hi(e){return e[ge]??={[ui]:e.nodeName.includes(`-`),[di]:e.namespaceURI===i}}var gi=new Map;function _i(e){var t=e.getAttribute(`is`)||e.nodeName,n=gi.get(t);if(n)return n;gi.set(t,n=new Set);for(var r,i=e,a=Element.prototype;a!==i;){for(var o in r=d(i),r)r[o].set&&o!==`innerHTML`&&o!==`textContent`&&o!==`innerText`&&n.add(o);i=m(i)}return n}function vi(e,t,n=t){var r=new WeakSet;ot(e,`input`,async i=>{var a=i?e.defaultValue:e.value;if(a=yi(e)?bi(a):a,n(a),j!==null&&r.add(j),await sr(),a!==(a=t())){var o=e.selectionStart,s=e.selectionEnd,c=e.value.length;if(e.value=a??``,s!==null){var l=e.value.length;o===s&&s===c&&l>c?(e.selectionStart=l,e.selectionEnd=l):(e.selectionStart=o,e.selectionEnd=Math.min(s,l))}}}),(C&&e.defaultValue!==e.value||K(t)==null&&e.value)&&(n(yi(e)?bi(e.value):e.value),j!==null&&r.add(j)),Dn(()=>{var n=t();if(e===document.activeElement){var i=j;if(r.has(i))return}yi(e)&&n===bi(e.value)||(e.type!==`date`||n||e.value)&&n!==e.value&&(e.value=n??``)})}function yi(e){var t=e.type;return t===`number`||t===`range`}function bi(e){return e===``?null:+e}function xi(e=!1){let t=D,n=t.l.u;if(!n)return;let r=()=>q(t.s);if(e){let e=0,n={},i=dt(()=>{let r=!1,i=t.s;for(let e in i)i[e]!==n[e]&&(n[e]=i[e],r=!0);return r&&e++,e});r=()=>G(i)}n.b.length&&xn(()=>{Si(t,r),v(n.b)}),yn(()=>{let e=K(()=>n.m.map(_));return()=>{for(let t of e)typeof t==`function`&&t()}}),n.a.length&&yn(()=>{Si(t,r),v(n.a)})}function Si(e,t){if(e.l.s)for(let t of e.l.s)G(t);t()}function Ci(e,t,n){if(e==null)return t(void 0),n&&n(void 0),g;let r=K(()=>e.subscribe(t,n));return r.unsubscribe?()=>r.unsubscribe():r}var wi=[];function Ti(e,t=g){let n=null,r=new Set;function i(t){if(Ne(e,t)&&(e=t,n)){let t=!wi.length;for(let t of r)t[1](),wi.push(t,e);if(t){for(let e=0;e{r.delete(c),r.size===0&&n&&(n(),n=null)}}return{set:i,update:a,subscribe:o}}function Ei(e){let t;return Ci(e,e=>t=e)(),t}var Di=!1,Oi=Symbol(`unmounted`);function ki(e,t,n){let r=n[t]??={store:null,source:M(void 0),unsubscribe:g};if(r.store!==e&&!(Oi in n)){if(r.unsubscribe(),r.store=e??null,e==null)r.source.v=void 0,r.unsubscribe=g;else{var i=!0;r.unsubscribe=Ci(e,e=>{i?r.source.v=e:N(r.source,e)}),i=!1}}return e&&Oi in n?Ei(e):G(r.source)}function Ai(){let e={};function t(){vn(()=>{for(var t in e)e[t].unsubscribe();l(e,Oi,{enumerable:!1,value:!0})})}return[e,t]}function ji(e){var t=Di;try{return Di=!1,[e(),Di]}finally{Di=t}}function $(t,n,r,i){var a=!e||!!(r&2),o=!!(r&8),s=!!(r&16),c=i,l=!0,d=void 0,f=()=>s&&a?(d??=dt(i),G(d)):(l&&(l=!1,c=s?K(i):i),c);let p;if(o){var m=fe in t||me in t;p=u(t,n)?.set??(m&&n in t?e=>t[n]=e:void 0)}var h,g=!1;o?[h,g]=ji(()=>t[n]):h=t[n],h===void 0&&i!==void 0&&(h=f(),p&&(a&&He(n),p(h)));var _=a?()=>{var e=t[n];return e===void 0?f():(l=!0,e)}:()=>{var e=t[n];return e!==void 0&&(c=void 0),e===void 0?c:e};if(a&&!(r&4))return _;if(p){var v=t.$$legacy;return(function(e,t){return arguments.length>0?((!a||!t||v||g)&&p(t?_():e),e):_()})}var y=!1,b=(r&1?dt:mt)(()=>(y=!1,_()));o&&G(b);var x=H;return(function(e,t){if(arguments.length>0){let n=t?G(b):a&&o?Yt(e):e;return N(b,n),y=!0,c!==void 0&&(c=n),e}return Vn&&y||x.f&16384?b.v:G(b)})}function Mi(e){let t=Ti({paired:!!e,controller:!1,busy:!1,error:``}),n=e,r,i=``,a={paired:!!e,controller:!1,busy:!1,error:``},o=!1;function s(e){a={...a,...e},t.set(a)}async function c(e=!1){if(!o&&n){o=!0;try{let t=await fetch(`/api/state`,{cache:`no-store`,headers:{"X-Setup-Session-Key":n}});if(!t.ok)throw Error(`The local setup session is unavailable.`);s({view:await t.json(),...e?{}:{error:``}})}catch{s({view:void 0,error:`Connection lost. The CLI may have stopped. Check the terminal before trying again.`})}finally{o=!1}}}async function l(){if(n)try{let e=await fetch(`/api/bootstrap`,{cache:`no-store`,headers:{"X-Setup-Session-Key":n}});if(!e.ok)throw Error(`Could not join this local session.`);let t=await e.json();r=t.capability,i=t.takeoverTicket,s({controller:t.controller,error:``}),await c()}catch{n=void 0,r=void 0,i=``,s({view:void 0,paired:!1,controller:!1,error:`Could not connect to the local setup session. Check the terminal and pair again.`})}}async function u(e){if(!(a.busy||a.paired)){s({busy:!0,error:``});try{let t=await fetch(`/api/pair`,{method:`POST`,cache:`no-store`,headers:{"Content-Type":`application/json`},body:JSON.stringify({code:e.trim().toLowerCase()})}),r=await t.json();if(!t.ok)throw Error(String(r.error??`Pairing was rejected.`));if(typeof r.sessionKey!=`string`||!/^[a-f0-9]{64}$/.test(r.sessionKey))throw Error(`Invalid local pairing response.`);n=r.sessionKey,s({paired:!0,error:``}),await l()}catch(e){s({error:e instanceof Error?e.message:`Could not pair this browser.`})}finally{s({busy:!1})}}}async function d(e,t,i=!0){let a=await fetch(e,{method:`POST`,cache:`no-store`,headers:{"Content-Type":`application/json`,"X-Setup-Session-Key":n,...i&&r?{"X-Setup-Capability":r}:{}},body:JSON.stringify(t)}),o=await a.json();if(!a.ok)throw Error(String(o.error??`The request was rejected.`));return o}async function f(e,t){if(!a.busy&&a.controller&&a.view?.promptRevision===e){s({busy:!0,error:``});try{await d(`/api/answer`,{revision:e,value:t}),await c()}catch(e){let t=e instanceof Error?e.message:`Could not submit this answer.`;s({error:t}),/read-only|Control moved/.test(t)?await l():await c(!0)}finally{s({busy:!1})}}}async function p(){if(a.controller&&!a.busy){s({busy:!0,error:``});try{await d(`/api/cancel`,{}),await c()}catch(e){let t=e instanceof Error?e.message:`Cancellation failed.`;s({error:t}),/read-only|Control moved/.test(t)&&await l()}finally{s({busy:!1})}}}async function m(){try{let e=await d(`/api/takeover`,{ticket:i},!1);r=String(e.capability),s({controller:!0,error:``}),await c()}catch(e){s({error:e instanceof Error?e.message:`Takeover failed.`}),await l()}}async function h(){try{await d(`/api/close`,{})}catch{}}return{subscribe:t.subscribe,pair:u,connect:l,refresh:c,submit:f,cancel:p,takeOver:m,close:h}}var Ni=J(`
                                            • `),Pi=J(``);function Fi(e,t){O(t,!1);let n=$(t,`journey`,8),r=[`Repository`,`Setup choices`,`Setup PAT`,`Plan`,`Bot PAT & credentials`,`Apply`];xi();var i=Pi(),a=I(P(i),4);Wr(a,5,()=>r,Br,(e,t,r)=>{var i=Ni();let a;var o=P(i),s=F(o,!0),c=F(I(o,2),!0);E(i),L(e=>{Q(i,`aria-current`,(q(n()),K(()=>n()?.position===r+1?`step`:void 0))),a=ni(i,1,``,null,a,{current:n()?.position===r+1,completed:(n()?.position??0)>r+1}),X(s,e),X(c,G(t))},[()=>(q(n()),K(()=>(n()?.position??0)>r+1?`✓`:String(r+1).padStart(2,`0`)))]),Y(e,i)}),E(a),ke(2),E(i),Y(e,i),k()}var Ii=J(`
                                              `);function Li(e,t){O(t,!1);let n=M(`system`);wn(()=>G(n),()=>{document.documentElement.dataset.theme=G(n)}),Tn();var r=Ii(),i=P(r);let a;var o=I(i,2);let s;var c=I(o,2);let l;E(r),L(()=>{Q(i,`aria-pressed`,G(n)===`system`),a=ni(i,1,``,null,a,{active:G(n)===`system`}),Q(o,`aria-pressed`,G(n)===`light`),s=ni(o,1,``,null,s,{active:G(n)===`light`}),Q(c,`aria-pressed`,G(n)===`dark`),l=ni(c,1,``,null,l,{active:G(n)===`dark`})}),gr(`click`,i,()=>N(n,`system`)),gr(`click`,o,()=>N(n,`light`)),gr(`click`,c,()=>N(n,`dark`)),Y(e,r),k()}_r([`click`]);var Ri=J(`
                                              LOCAL SESSION
                                              `);function zi(e,t){let n=$(t,`repository`,8);var r=Ri(),i=P(r),a=F(I(P(i),2),!0);E(i);var o=I(i,2);Li(I(P(o)),{}),E(o),E(r),L(()=>X(a,n()??`Connecting…`)),Y(e,r)}var Bi=J(`

                                              `,1);function Vi(e,t){O(t,!1);let n=M(),r=$(t,`view`,8);wn(()=>q(r()),()=>{N(n,r()?.outcome===`complete`?`Setup complete.`:r()?.outcome===`dry-run`?`Preview complete.`:r()?.outcome===`cancelled`?`Setup cancelled.`:r()?.outcome?`Setup needs attention.`:r()?.prompt?.title??`Preparing your setup…`)}),Tn(),xi();var i=Bi(),a=sn(i),o=I(P(a)),s=F(I(o));E(a);var c=I(a,2),l=F(c,!0),u=F(I(c,2),!0);L((e,t)=>{X(o,` ${e??``} `),X(s,`${t??``} / 06`),X(l,G(n)),X(u,(q(r()),K(()=>r()?.outcome?`The terminal contains the detailed result. Review the next steps below before closing this session.`:`One decision at a time. Your choices shape the permissions, plan and credentials needed for this repository.`)))},[()=>(q(r()),K(()=>r()?.journey?.current?.toUpperCase()??`GETTING READY`)),()=>(q(r()),K(()=>String(r()?.journey?.position??1).padStart(2,`0`)))]),Y(e,i),k()}function Hi(e){try{let t=new URL(e??``);return t.protocol===`https:`&&t.hostname===`github.com`&&(t.pathname===`/settings/personal-access-tokens`||t.pathname.startsWith(`/settings/personal-access-tokens/`))?t.toString():void 0}catch{return}}var Ui=J(``),Wi=J(``);function Gi(e,t){let n=$(t,`label`,8),r=$(t,`variant`,8,`primary`),i=$(t,`disabled`,8,!1),a=$(t,`arrow`,8,!1),o=$(t,`onClick`,8);var s=Wi(),c=P(s,!0),l=I(c),u=e=>{Y(e,Ui())};Z(l,e=>{a()&&e(u)}),E(s),L(()=>{ni(s,1,$r(r())),s.disabled=i(),X(c,n())}),gr(`click`,s,function(...e){o()?.apply(this,e)}),Y(e,s)}_r([`click`]);var Ki=J(`Open GitHub link ↗`),qi=J(`

                                              `);function Ji(e,t){O(t,!1);let n=M(),r=$(t,`tone`,8,`info`),i=$(t,`title`,8),a=$(t,`message`,8),o=$(t,`link`,8,void 0),s=$(t,`actionLabel`,8,void 0),c=$(t,`onAction`,8,void 0);wn(()=>q(o()),()=>{N(n,Hi(o()))}),Tn(),xi();var l=qi(),u=P(l),d=F(u,!0),f=I(u),p=F(f,!0),m=I(f,2),h=e=>{var t=Ki();L(()=>Q(t,`href`,G(n))),Y(e,t)};Z(m,e=>{G(n)&&e(h)});var g=I(m,2),_=e=>{Gi(e,{get label(){return s()},variant:`secondary`,get onClick(){return c()}})};Z(g,e=>{s()&&c()&&e(_)}),E(l),L(()=>{ni(l,1,`banner ${r()??``}`),Q(l,`role`,r()===`error`?`alert`:`status`),X(d,i()),X(p,a())}),Y(e,l),k()}function Yi(e){let t=String(e.question.defaultValue),n=t.split(`,`).map(e=>e.trim()).filter(Boolean);return{value:t,selected:e.question.kind===`multi-select`?(e.question.choices??[]).filter(e=>e!==`All`&&n.includes(e.split(` — `)[0])):e.question.kind===`scope-overrides`?n:[]}}function Xi(e,t){return t===`All`?e.includes(`All`)?[]:[`All`]:e.includes(t)?e.filter(e=>e!==t):[...e.filter(e=>e!==`All`),t]}function Zi(e,t,n){return e.question.kind===`scope-overrides`||e.question.kind===`multi-select`?n.length?n.join(`,`):`none`:t}var Qi=J(`PERMISSION PREVIEW`),$i=J(`
                                              `),ea=J(``),ta=J(``),na=J(``),ra=J(`
                                              `),ia=J(``),aa=J(`

                                              `,1);function oa(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`controller`,8),i=$(t,`busy`,8),a=$(t,`onSubmit`,8),o=Yi(n()),s=M(o.value),c=M(o.selected);xi();var l=aa(),u=sn(l),d=P(u),f=F(d,!0),p=I(d),m=e=>{Y(e,Qi())};Z(p,e=>{q(n()),K(()=>n().phase===`permission-intent`)&&e(m)}),E(u);var h=I(u,2),g=e=>{var t=$i(),i=P(t);let a;var o=I(i);let c;E(t),L(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),Q(i,`aria-pressed`,G(s)===`yes`||G(s)===`true`),i.disabled=!r(),a=ni(i,1,``,null,a,{selected:G(s)===`yes`||G(s)===`true`}),Q(o,`aria-pressed`,G(s)===`no`||G(s)===`false`),o.disabled=!r(),c=ni(o,1,``,null,c,{selected:G(s)===`no`||G(s)===`false`})}),gr(`click`,i,()=>N(s,`yes`)),gr(`click`,o,()=>N(s,`no`)),Y(e,t)},_=e=>{var t=ta();Wr(t,5,()=>(q(n()),K(()=>n().question.choices??[])),Br,(e,t)=>{var n=ea(),r=F(n,!0),i={};L(()=>{X(r,G(t)),i!==(i=G(t))&&(n.value=(n.__value=i)??``)}),Y(e,n)}),E(t),oi(t),L(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),t.disabled=!r()}),si(t,()=>G(s),e=>N(s,e)),Y(e,t)},v=e=>{var t=ra();Wr(t,5,()=>(q(n()),K(()=>n().question.kind===`multi-select`?n().question.choices??[]:n().question.allowedNames??[])),Br,(e,t)=>{var n=na(),i=P(n);pi(i);var a=F(I(i),!0);E(n),L(e=>{mi(i,e),i.disabled=!r(),X(a,G(t))},[()=>(G(c),G(t),K(()=>G(c).includes(G(t))))]),gr(`change`,i,()=>N(c,Xi(G(c),G(t)))),Y(e,n)}),E(t),L(()=>Q(t,`aria-label`,(q(n()),K(()=>n().question.label)))),Y(e,t)},y=e=>{var t=ia();pi(t),L(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),Q(t,`type`,(q(n()),K(()=>n().question.kind===`number`?`number`:`text`))),Q(t,`min`,(q(n()),K(()=>n().question.kind===`number`?0:void 0))),t.disabled=!r()}),vi(t,()=>G(s),e=>N(s,e)),Y(e,t)};Z(h,e=>{q(n()),K(()=>n().question.kind===`boolean`)?e(g):(q(n()),K(()=>n().question.kind===`choice`)?e(_,1):(q(n()),K(()=>n().question.kind===`multi-select`||n().question.kind===`scope-overrides`)?e(v,2):e(y,-1)))});var b=I(h,2),x=F(b),S=I(b,2);{let e=mt(()=>!r()||i());Gi(S,{label:`Continue`,arrow:!0,onClick:()=>a()(Zi(n(),G(s),G(c))),get disabled(){return G(e)}})}L((e,t)=>{X(f,e),X(x,`Suggested answer: ${t??``} · You can review choices again before creating your setup PAT.`)},[()=>(q(n()),K(()=>n().question.label.replace(` (Space toggles, Enter confirms)`,``))),()=>(q(n()),K(()=>String(n().question.defaultValue)||`none`))]),Y(e,l),k()}_r([`click`,`change`]);var sa=J(``),ca=J(`
                                              `);function la(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`controller`,8),i=$(t,`busy`,8),a=$(t,`onSubmit`,8);xi();var o=ca();Wr(o,5,()=>(q(n()),K(()=>n().choices)),Br,(e,t)=>{var n=sa(),o=F(P(n),!0);ke(),E(n),L(()=>{n.disabled=!r()||i(),X(o,G(t))}),gr(`click`,n,()=>a()(G(t))),Y(e,n)}),E(o),Y(e,o),k()}_r([`click`]);var ua=J(`Open the official GitHub PAT form

                                              Check the signed-in account, select Only select repositories, then select this repository. GitHub handles 2FA and creates the PAT.

                                              `,1),da=J(`

                                              Sent only to this local process. It will not be shown again or saved in browser storage.

                                              `),fa=J(` `,1);function pa(e,t){O(t,!1);let n=M(),r=$(t,`prompt`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8),s=M(``);function c(){let e=G(s);r().kind===`secret`&&N(s,``),o()(e)}wn(()=>q(r()),()=>{N(n,Hi(r().link))}),Tn(),xi();var l=fa(),u=sn(l),d=e=>{var t=ua(),r=sn(t);ke(),L(()=>Q(r,`href`,G(n))),Y(e,t)};Z(u,e=>{G(n)&&e(d)});var f=I(u,2),p=F(f,!0),m=I(f,2);pi(m);var h=I(m,2),g=e=>{Y(e,da())};Z(h,e=>{q(r()),K(()=>r().kind===`secret`)&&e(g)});var _=I(h,2);{let e=mt(()=>(q(i()),q(a()),q(r()),G(s),K(()=>!i()||a()||!r().optional&&!G(s).trim())));Gi(_,{label:`Continue`,arrow:!0,onClick:c,get disabled(){return G(e)}})}L(()=>{X(p,(q(r()),K(()=>r().kind===`secret`?`Paste the value here`:`Your answer`))),Q(m,`type`,(q(r()),K(()=>r().kind===`secret`?`password`:`text`))),m.disabled=!i(),Q(m,`placeholder`,(q(r()),K(()=>r().kind===`secret`?`Hidden after submission`:`Type your answer`)))}),vi(m,()=>G(s),e=>N(s,e)),Y(e,l),k()}var ma=J(`
                                            • `),ha=J(`

                                                `),ga=J(`

                                                Before you continue

                                                  `),_a=J(`

                                                  Review exactly what this run may change. The bot PAT and any additional credentials are collected next.

                                                  `,1);function va(e,t){O(t,!1);let n=M(),r=$(t,`prompt`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8);wn(()=>q(r()),()=>{N(n,[{title:`Files`,items:r().plan.files},{title:`Workflows`,items:r().plan.workflows},{title:`Variables`,items:r().plan.variables},{title:`Secret names`,items:r().plan.secrets}])}),Tn(),xi();var s=_a(),c=I(sn(s),2);Wr(c,5,()=>G(n),Br,(e,t)=>{var n=ha(),r=P(n),i=P(r),a=F(I(i),!0);E(r);var o=I(r);Wr(o,5,()=>(G(t),K(()=>G(t).items)),Br,(e,t)=>{var n=ma(),r=F(n,!0);L(()=>X(r,G(t))),Y(e,n)}),E(o),E(n),L(()=>{X(i,`${G(t),K(()=>G(t).title)??``} `),X(a,(G(t),K(()=>G(t).items.length)))}),Y(e,n)}),E(c);var l=I(c,2),u=e=>{var t=ga(),n=I(P(t));Wr(n,5,()=>(q(r()),K(()=>r().plan.warnings)),Br,(e,t)=>{var n=ma(),r=F(n,!0);L(()=>X(r,G(t))),Y(e,n)}),E(n),E(t),Y(e,t)};Z(l,e=>{q(r()),K(()=>r().plan.warnings.length)&&e(u)});var d=I(l,2),f=P(d);{let e=mt(()=>!i()||a());Gi(f,{label:`Stop here`,variant:`secondary`,onClick:()=>o()(`decline`),get disabled(){return G(e)}})}var p=I(f);{let e=mt(()=>!i()||a());Gi(p,{label:`Approve this plan`,arrow:!0,onClick:()=>o()(`approve`),get disabled(){return G(e)}})}E(d),Y(e,s),k()}var ya=J(`

                                                  `),ba=J(`
                                                  CURRENT DECISION
                                                  `);function xa(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`revision`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8);xi();var s=ba(),c=P(s),l=F(I(P(c)));E(c);var u=I(c,2),d=e=>{var t=ya(),r=F(t,!0);L(()=>X(r,(q(n()),K(()=>n().description)))),Y(e,t)};Z(u,e=>{q(n()),K(()=>`description`in n()&&n().description)&&e(d)});var f=I(u,2),p=e=>{oa(e,{get prompt(){return n()},get controller(){return i()},get busy(){return a()},get onSubmit(){return o()}})},m=e=>{la(e,{get prompt(){return n()},get controller(){return i()},get busy(){return a()},get onSubmit(){return o()}})},h=e=>{pa(e,{get prompt(){return n()},get controller(){return i()},get busy(){return a()},get onSubmit(){return o()}})},g=e=>{va(e,{get prompt(){return n()},get controller(){return i()},get busy(){return a()},get onSubmit(){return o()}})};Z(f,e=>{q(n()),K(()=>n().kind===`question`)?e(p):(q(n()),K(()=>n().kind===`choice`||n().kind===`confirm`)?e(m,1):(q(n()),K(()=>n().kind===`text`||n().kind===`secret`)?e(h,2):(q(n()),K(()=>n().kind===`plan`)&&e(g,3))))}),E(s),L(()=>X(l,`SESSION ${r()??``}`)),Y(e,s),k()}var Sa=J(` `),Ca=J(`
                                                • `),wa=J(`

                                                    Conditional grants depend on the choices and GitHub facts. A final audit runs before setup changes.
                                                    `),Ta=J(`

                                                    Permissions follow your choices

                                                    We’ll show the exact grants before you create each PAT. Nothing is created just by opening this page.

                                                    `),Ea=J(``);function Da(e,t){O(t,!1);let n=$(t,`view`,8);xi();var r=Ea(),i=P(r),a=F(I(P(i),3),!0);E(i);var o=I(i,2),s=e=>{var t=wa(),r=I(P(t)),i=F(r),a=I(r),o=F(a,!0),s=I(a,2);Wr(s,5,()=>(q(n()),K(()=>n().permissions.report?.checks??n().permissions.requirements??[])),Br,(e,t)=>{var n=Ca(),r=P(n),i=P(r),a=I(i),o=F(a),s=F(I(a),!0);E(r);var c=I(r),l=P(c,!0),u=I(l),d=e=>{var n=Sa(),r=F(n,!0);L(()=>X(r,(G(t),K(()=>G(t).status)))),Y(e,n)};Z(u,e=>{`status`in G(t)&&e(d)}),E(c),E(n),L(()=>{X(i,`${G(t),K(()=>G(t).permission)??``} `),X(o,`${G(t),K(()=>G(t).scope)??``} · ${G(t),K(()=>G(t).applicability)??``}`),X(s,(G(t),K(()=>G(t).reason))),X(l,(G(t),K(()=>G(t).level)))}),Y(e,n)}),E(s),ke(2),E(t),L(()=>{X(i,`${q(n()),K(()=>n().permissions.role===`setup`?`Setup PAT`:`Bot PAT`)??``} access`),X(o,(q(n()),K(()=>n().permissions.report?`Read-only access check`:`Provisional least-privilege grants`)))}),Y(e,t)},c=e=>{Y(e,Ta())};Z(o,e=>{q(n()),K(()=>n().permissions)?e(s):e(c,-1)}),E(r),L(()=>X(a,(q(n()),K(()=>n().repository)))),Y(e,r),k()}var Oa=J(`

                                                    `);function ka(e,t){O(t,!1);let n=M(),r=M(),i=$(t,`outcome`,8),a=$(t,`controller`,8),o=$(t,`onClose`,8);wn(()=>q(i()),()=>{N(n,i()===`complete`?`Your configuration was applied`:i()===`dry-run`?`No changes were made`:i()===`cancelled`||i()===`blocked`?`No setup changes started`:`Check partial changes before retrying`)}),wn(()=>q(i()),()=>{N(r,i()===`complete`?`The temporary setup PAT is not revoked automatically. Delete it in GitHub after confirming your setup. Keep the bot PAT until you rotate the installed Secret.`:i()===`partial`?`A bot Secret write or another change may have succeeded. Inspect GitHub and run copilot doctor before replacing or deleting its PAT.`:`Copilot did not begin applying setup changes. PATs you created in GitHub still exist until you delete them there.`)}),Tn();var s=Oa(),c=P(s),l=F(c,!0),u=I(c),d=F(u,!0),f=I(u),p=F(f,!0),m=I(f,2),h=e=>{Gi(e,{label:`Close local session`,get onClick(){return o()}})};Z(m,e=>{a()&&e(h)}),E(s),L(()=>{X(l,i()===`complete`?`✓`:`!`),X(d,G(n)),X(p,G(r))}),Y(e,s),k()}var Aa=J(`

                                                    Working on the next step

                                                    The local process is checking your answers and preparing the next decision. Keep this page open.

                                                    `);function ja(e){Y(e,Aa())}var Ma=J(`
                                                    PRIVATE LOCAL SESSION

                                                    Pair this browser

                                                    Find the 16-character pairing code in the terminal that started copilot setup --web. Enter it here to view or control this setup. The code is never placed in the browser URL or stored after this page closes.

                                                    Keep the code private. After refreshing this page, enter it again to reconnect.

                                                    `);function Na(e,t){O(t,!1);let n=$(t,`busy`,8),r=$(t,`onPair`,8),i=M(``);function a(){let e=G(i);N(i,``),r()(e)}xi();var o=Ma(),s=I(P(o),6),c=I(P(s),2);pi(c);var l=I(c,4);{let e=mt(()=>(q(n()),G(i),K(()=>n()||G(i).trim().length!==16)));Gi(l,{label:`Connect to local setup`,arrow:!0,onClick:a,get disabled(){return G(e)}})}E(s),E(o),L(()=>c.disabled=n()),hr(`submit`,s,e=>{e.preventDefault(),a()}),vi(c,()=>G(i),e=>N(i,e)),Y(e,o),k()}var Pa=J(``),Fa=J(`
                                                    `),Ia=J(``),La=J(`
                                                    `,1),Ra=J(`
                                                    LOCALHOST ONLY NO CLOUD SETUP ACCOUNT GITHUB OWNS PAT ISSUANCE
                                                    `);function za(e,t){O(t,!1);let n=()=>ki(a,`$session`,r),[r,i]=Ai(),a=Mi();Ir(()=>{let e=window.setInterval(()=>{n().paired&&!n().view?.outcome&&a.refresh()},900);return()=>window.clearInterval(e)});async function o(){window.confirm(`Cancel this local setup session? PATs already created in GitHub will still exist.`)&&await a.cancel()}async function s(e){let t=n().view?.promptRevision;t!==void 0&&await a.submit(t,e)}xi();var c=Ra();Xr(`16t12jp`,e=>{Y(e,Pa())});var l=P(c);{let e=mt(()=>n().view?.journey);Fi(l,{get journey(){return G(e)}})}var u=I(l,2),d=P(u);{let e=mt(()=>n().view?.repository);zi(d,{get repository(){return G(e)}})}var f=I(d,2),p=P(f),m=e=>{Vi(e,{get view(){return n().view}})};Z(p,e=>{n().paired&&e(m)});var h=I(p,2),g=e=>{var t=Fa(),r=I(P(t));E(t),L(()=>X(r,` Reviewing saved choices — pass ${n().view.journey.choiceReviewPass??``}. This is the same setup run, not a restart.`)),Y(e,t)};Z(h,e=>{n().view?.journey?.choiceReviewPass&&n().view.journey.choiceReviewPass>1&&!n().view.outcome&&e(g)});var _=I(h,2),v=e=>{Ji(e,{tone:`warning`,title:`Read-only tab`,message:`Another tab controls this session. You can review progress here or explicitly take over.`,actionLabel:`Take control in this tab`,get onAction(){return a.takeOver}})};Z(_,e=>{!n().controller&&n().view&&e(v)});var y=I(_,2),b=e=>{Ji(e,{tone:`error`,title:`Needs attention`,get message(){return n().error}})};Z(y,e=>{n().error&&e(b)});var x=I(y,2),S=e=>{{let t=mt(()=>n().view.message.tone===`success`?`Checked`:n().view.message.tone===`warning`?`Please note`:n().view.message.tone===`error`?`Needs attention`:`Progress update`);Ji(e,{get tone(){return n().view.message.tone},get title(){return G(t)},get message(){return n().view.message.text},get link(){return n().view.message.link}})}};Z(x,e=>{n().view?.message&&e(S)});var ee=I(x,2),te=e=>{Na(e,{get busy(){return n().busy},get onPair(){return a.pair}})},ne=e=>{ka(e,{get outcome(){return n().view.outcome},get controller(){return n().controller},get onClose(){return a.close}})},re=e=>{var t=La(),r=sn(t),i=P(r);zr(i,()=>n().view.promptRevision,e=>{xa(e,{get prompt(){return n().view.prompt},get revision(){return n().view.revision},get controller(){return n().controller},get busy(){return n().busy},onSubmit:s})}),Da(I(i,2),{get view(){return n().view}}),E(r);var a=I(r,2),c=e=>{var t=Ia();L(()=>t.disabled=n().busy),gr(`click`,t,o),Y(e,t)};Z(a,e=>{n().controller&&n().view.journey?.current!==`Apply`&&e(c)}),Y(e,t)},ie=e=>{ja(e,{})};Z(ee,e=>{n().paired?n().view?.outcome?e(ne,1):n().view?.prompt?e(re,2):e(ie,-1):e(te)}),ke(2),E(f),E(u),E(c),Y(e,c),k(),i()}_r([`click`]),jr(za,{target:document.getElementById(`app`)}); \ No newline at end of file diff --git a/build/web/assets/index-DzCTZuhQ.js b/build/web/assets/index-DzCTZuhQ.js deleted file mode 100644 index 62f0f1a1c..000000000 --- a/build/web/assets/index-DzCTZuhQ.js +++ /dev/null @@ -1,2 +0,0 @@ -(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})(),typeof window<`u`&&((window.__svelte??={}).v??=new Set).add(`5`);var e=!1;function t(){e=!0}t();var n={},r=Symbol(`uninitialized`),i=`http://www.w3.org/1999/xhtml`,a=Array.isArray,o=Array.prototype.indexOf,s=Array.prototype.includes,c=Array.from,l=Object.defineProperty,u=Object.getOwnPropertyDescriptor,d=Object.getOwnPropertyDescriptors,f=Object.prototype,p=Array.prototype,m=Object.getPrototypeOf,h=Object.isExtensible,g=()=>{};function _(e){return e()}function v(e){for(var t=0;t{e=n,t=r}),resolve:e,reject:t}}var b=1024,x=2048,S=4096,ee=8192,te=16384,ne=32768,re=1<<25,ie=65536,ae=1<<18,oe=1<<19,se=1<<20,ce=1<<25,le=1<<21,ue=1<<22,de=1<<23,fe=Symbol(`$state`),pe=Symbol(`component`),me=Symbol(`legacy props`),he=Symbol(``),ge=Symbol(`attributes`),_e=Symbol(`class`),ve=Symbol(`style`),ye=Symbol(`text`),be=Symbol(`form reset`),xe=new class extends Error{name=`StaleReactionError`;message="The reaction that called `getAbortSignal()` was re-run or destroyed"},Se=!!globalThis.document?.contentType&&globalThis.document.contentType.includes(`xml`);function Ce(){console.warn(`https://svelte.dev/e/derived_inert`)}function we(e){console.warn(`https://svelte.dev/e/hydration_mismatch`)}function Te(){console.warn(`https://svelte.dev/e/select_multiple_invalid_value`)}function Ee(){console.warn(`https://svelte.dev/e/svelte_boundary_reset_noop`)}var C=!1;function De(e){C=e}var w;function T(e){if(e===null)throw we(),n;return w=e}function Oe(){return T(an(w))}function E(e){if(C){if(an(w)!==null)throw we(),n;w=e}}function ke(e=1){if(C){for(var t=e,n=w;t--;)n=an(n);w=n}}function Ae(e=!0){for(var t=0,n=w;;){if(n.nodeType===8){var r=n.data;if(r===`]`){if(t===0)return n;--t}else(r===`[`||r===`[!`||r[0]===`[`&&!isNaN(Number(r.slice(1))))&&(t+=1)}var i=an(n);e&&n.remove(),n=i}}function je(e){if(!e||e.nodeType!==8)throw we(),n;return e.data}function Me(e){return e===this.v}function Ne(e,t){return e==e?e!==t||typeof e==`object`&&!!e||typeof e==`function`:t==t}function Pe(e){return!Ne(e,this.v)}function Fe(e){throw Error(`https://svelte.dev/e/lifecycle_outside_component`)}function Ie(){throw Error(`https://svelte.dev/e/async_derived_orphan`)}function Le(e,t,n){throw Error(`https://svelte.dev/e/each_key_duplicate`)}function Re(e){throw Error(`https://svelte.dev/e/effect_in_teardown`)}function ze(){throw Error(`https://svelte.dev/e/effect_in_unowned_derived`)}function Be(e){throw Error(`https://svelte.dev/e/effect_orphan`)}function Ve(){throw Error(`https://svelte.dev/e/effect_update_depth_exceeded`)}function He(e){throw Error(`https://svelte.dev/e/props_invalid_value`)}function Ue(){throw Error(`https://svelte.dev/e/state_descriptors_fixed`)}function We(){throw Error(`https://svelte.dev/e/state_prototype_fixed`)}function Ge(){throw Error(`https://svelte.dev/e/state_unsafe_mutation`)}function Ke(){throw Error(`https://svelte.dev/e/svelte_boundary_reset_onerror`)}var D=null;function qe(e){D=e}function O(t,n=!1,r){D={p:D,i:!1,c:null,e:null,s:t,x:null,r:H,l:e&&!n?{s:null,u:null,$:[]}:null}}function k(e){var t=D,n=t.e;if(n!==null){t.e=null;for(var r of n)yn(r)}return e!==void 0&&(t.x=e),t.i=!0,D=t.p,Je(e)}function Je(e={}){return l(e,pe,{value:!0}),e}function Ye(){return!e||D!==null&&D.l===null}var Xe=[];function Ze(){var e=Xe;Xe=[],v(e)}function Qe(e){if(Xe.length===0&&!wt){var t=Xe;queueMicrotask(()=>{t===Xe&&Ze()})}Xe.push(e)}function $e(){for(;Xe.length>0;)Ze()}var et=~(x|S|b);function A(e,t){e.f=e.f&et|t}function tt(e){e.f&512||e.deps===null?A(e,b):A(e,S)}function nt(e,t,n){e.f&2048?t.add(e):e.f&4096&&n.add(e),A(e,b)}var rt=!1;function it(){rt||(rt=!0,document.addEventListener(`reset`,e=>{Promise.resolve().then(()=>{if(!e.defaultPrevented)for(let t of e.target.elements)t[be]?.()})},{capture:!0}))}function at(e){var t=V,n=H;Un(null),Wn(null);try{return e()}finally{Un(t),Wn(n)}}function ot(e,t,n,r=n){e.addEventListener(t,()=>at(n));let i=e[be];e[be]=i?()=>{i(),r(!0)}:()=>r(!0),it()}function st(e,t,n,r){let i=Ye()?dt:mt;var a=e.filter(e=>!e.settled),o=t.map(i);if(n.length===0&&a.length===0){r(o);return}var s=H,c=ct(),l=a.length===1?a[0].promise:a.length>1?Promise.all(a.map(e=>e.promise)):null;function u(e){if(!(s.f&16384)){c();try{r([...o,...e])}catch(e){fn(e,s)}lt()}}var d=ut();if(n.length===0){l.then(()=>u([])).finally(d);return}function f(){Promise.all(n.map(e=>pt(e))).then(u).catch(e=>fn(e,s)).finally(d)}l?l.then(()=>{c(),f(),lt()}):f()}function ct(){var e=H,t=V,n=D,r=j;return function(i=!0){Wn(e),Un(t),qe(n),i&&!(e.f&16384)&&(r?.activate(),r?.apply())}}function lt(e=!0){Wn(null),Un(null),qe(null),e&&j?.deactivate()}function ut(){var e=H,t=e.b,n=j,r=!!t?.is_rendered();return t?.update_pending_count(1,n),n.increment(r,e),()=>{t?.update_pending_count(-1,n),n.decrement(r,e)}}function dt(e){var t=2|x;return H!==null&&(H.f|=oe),{ctx:D,deps:null,effects:null,equals:Me,f:t,fn:e,reactions:null,rv:0,v:r,wv:0,parent:H,ac:null}}var ft=Symbol(`obsolete`);function pt(e,t,n){let i=H;i===null&&Ie();var a=void 0,o=Vt(r),s=!V,c=new Set;return Tn(()=>{var t=H,n=y();a=n.promise;try{Promise.resolve(e()).then(n.resolve,e=>{e!==xe&&n.reject(e)}).finally(lt)}catch(e){n.reject(e),lt()}var r=j;if(s){if(t.f&32768)var l=ut();if(i.b?.is_rendered())r.async_deriveds.get(t)?.reject(ft);else for(let e of c.values())e.reject(ft);c.add(n),r.async_deriveds.set(t,n)}let u=(e,t=void 0)=>{l?.(),c.delete(n),t!==ft&&(r.activate(),t?(o.f|=de,Gt(o,t)):(o.f&8388608&&(o.f^=de),Gt(o,e)),r.deactivate())};n.promise.then(u,e=>u(null,e||`unknown`))}),_n(()=>{for(let e of c)e.reject(ft)}),new Promise(e=>{function t(n){function r(){n===a?e(o):t(a)}n.then(r,r)}t(a)})}function mt(e){let t=dt(e);return t.equals=Pe,t}function ht(e){var t=e.effects;if(t!==null){e.effects=null;for(var n=0;n{t.ac.abort(xe),t.ac=null}),t.fn!==null&&(t.teardown=g),ar(t,0),kn(t))}function yt(e){if(e.effects!==null)for(let t of e.effects)t.teardown&&t.fn!==null&&or(t)}var bt=null,j=null,xt=null,St=null,Ct=null,wt=!1,Tt=!1,Et=null,Dt=null,Ot=0,kt=1,At=class e{id=kt++;#e=!1;linked=!0;#t=null;#n=null;async_deriveds=new Map;current=new Map;previous=new Map;#r=new Set;#i=new Set;#a=0;#o=new Map;#s=null;#c=[];#l=[];#u=new Set;#d=new Set;#f=new Map;#p=new Set;is_fork=!1;#m=!1;constructor(){bt===null?bt=this:(bt.#n=this,this.#t=bt),bt=this}#h(){if(this.is_fork)return!0;for(let n of this.#o.keys()){for(var e=n,t=!1;e.parent!==null;){if(this.#f.has(e)){t=!0;break}e=e.parent}if(!t)return!0}return!1}skip_effect(e){this.#f.has(e)||this.#f.set(e,{d:[],m:[]}),this.#p.delete(e)}unskip_effect(e,t=e=>this.schedule(e)){var n=this.#f.get(e);if(n){this.#f.delete(e);for(var r of n.d)A(r,x),t(r);for(r of n.m)A(r,S),t(r)}this.#p.add(e)}#g(){var e=[];for(let i of this.#c)if(!(i.f&16384||!(i.f&6144))){for(var t=i,n=!1;t.parent!==null;){t=t.parent;var r=t.f;if(r&96){if(!(r&1024)){n=!0;break}t.f^=b}}n||e.push(t)}return this.#c=[],e}#_(){this.#e=!0;for(let e of this.#u)this.#d.delete(e),A(e,x),this.schedule(e);for(let e of this.#d)A(e,S),this.schedule(e);this.apply();for(var t=Et=[],n=[],r=Dt=[];this.#c.length>0;){Ot++>1e3&&(this.#S(),Mt());for(let e of this.#g())try{this.#v(e,t,n)}catch(t){throw Lt(e),this.#h()||this.discard(),t}}if(j=null,r.length>0){var i=e.ensure();for(let e of r)i.schedule(e)}if(Et=null,Dt=null,this.#h()){this.#x(n),this.#x(t);for(let[e,t]of this.#f)It(e,t);r.length>0&&j.#_();return}let a=this.#y();if(a){this.#x(n),this.#x(t),a.#b(this);return}this.#u.clear(),this.#d.clear();for(let e of this.#r)e(this);this.#r.clear(),xt=this,Pt(n),Pt(t),xt=null,this.#s?.resolve();var o=j;if(this.#a===0&&(this.#c.length===0||o!==null)&&this.#S(),this.#c.length>0){if(o!==null){for(let e of this.#c)o.#c.push(e);this.#c=[]}else o=this}o!==null&&(zt.clear(),o.#_())}#v(e,t,n){e.f^=b;for(var r=e.first;r!==null;){var i=r.f,a=!!(i&96);if(!(a&&i&1024||i&8192||this.#f.has(r))&&r.fn!==null){a?r.f^=b:i&4?t.push(r):er(r)&&(i&16&&this.#d.add(r),or(r));var o=r.first;if(o!==null){r=o;continue}}for(;r!==null;){var s=r.next;if(s!==null){r=s;break}r=r.parent}}}#y(){for(var e=this.#t;e!==null;){if(!e.is_fork){for(let[t,[,n]]of this.current)if(e.current.has(t)&&!n)return e}e=e.#t}return null}#b(e){for(let[t,n]of e.current)!this.previous.has(t)&&e.previous.has(t)&&this.previous.set(t,e.previous.get(t)),this.current.set(t,n);for(let[t,n]of e.async_deriveds){let e=this.async_deriveds.get(t);e&&n.promise.then(e.resolve).catch(e.reject)}e.async_deriveds.clear(),this.transfer_effects(e.#u,e.#d);let t=e=>{var n=e.reactions;if(n!==null&&!(e.f&2&&!(e.f&6144)))for(let e of n){var r=e.f;if(r&2)t(e);else{var i=e;r&4194320&&!this.async_deriveds.has(i)&&(this.#d.delete(i),A(i,x),this.schedule(i))}}};for(let e of this.current.keys())t(e);this.oncommit(()=>e.discard()),e.#S(),j=this,this.#_()}#x(e){for(var t=0;t{this.#m=!1,this.linked&&this.flush()}))}transfer_effects(e,t){for(let t of e)this.#u.add(t);for(let e of t)this.#d.add(e);e.clear(),t.clear()}oncommit(e){this.#r.add(e)}ondiscard(e){this.#i.add(e)}settled(){return(this.#s??=y()).promise}static ensure(){if(j===null){let t=j=new e;!Tt&&!wt&&Qe(()=>{t.#e||t.flush()})}return j}apply(){St=null}schedule(e){if(Ct=e,e.b?.is_pending&&e.f&16777228&&!(e.f&32768)){e.b.defer_effect(e);return}this.#c.push(e)}#S(){if(this.linked){var e=this.#t,t=this.#n;e===null||(e.#n=t),t===null?bt=e:t.#t=e,this.linked=!1}}};function jt(e){var t=wt;wt=!0;try{var n;for(e&&(j!==null&&!j.is_fork&&j.flush(),n=e());;){if($e(),j===null)return n;j.flush()}}finally{wt=t}}function Mt(){try{Ve()}catch(e){fn(e,Ct)}}var Nt=null;function Pt(e){var t=e.length;if(t!==0){for(var n=0;n0)){zt.clear();for(let e of Nt){if(e.f&24576)continue;let t=[e],n=e.parent;for(;n!==null;)Nt.has(n)&&(Nt.delete(n),t.push(n)),n=n.parent;for(let e=t.length-1;e>=0;e--){let n=t[e];n.f&24576||or(n)}}Nt.clear()}}Nt=null}}function Ft(e){j.schedule(e)}function It(e,t){if(!(e.f&32&&e.f&1024)){e.f&2048?t.d.push(e):e.f&4096&&t.m.push(e),A(e,b);for(var n=e.first;n!==null;)It(n,t),n=n.next}}function Lt(e){A(e,b);for(var t=e.first;t!==null;)Lt(t),t=t.next}var Rt=new Set,zt=new Map,Bt=!1;function Vt(e,t){return{f:0,v:e,reactions:null,equals:Me,rv:0,wv:0}}function Ht(e,t){let n=Vt(e,t);return Kn(n),n}function M(t,n=!1,r=!0){let i=Vt(t);return n||(i.equals=Pe),e&&r&&D!==null&&D.l!==null&&(D.l.s??=[]).push(i),i}function N(e,t,n=!1){return V!==null&&(!Hn||V.f&131072)&&Ye()&&V.f&4325394&&(Gn===null||!Gn.has(e))&&Ge(),Gt(e,n?Yt(t):t,Dt)}var Ut=null,Wt=0;function Gt(e,t,n=null){if(!e.equals(t)){Bn?zt.set(e,t):zt.has(e)||zt.set(e,e.v);var r=At.ensure();if(r.capture(e,t),e.f&2){let t=e;e.f&2048&>(t),St===null&&tt(t)}e.wv=$n(),Ut=null,Wt=0,Jt(e,x,n),Ut=null,Ye()&&H!==null&&H.f&1024&&!(H.f&96)&&(qn===null?Jn([e]):qn.push(e)),!r.is_fork&&Rt.size>0&&!Bt&&Kt()}return t}function Kt(){Bt=!1;for(let e of Rt){e.f&1024&&A(e,S);let t;try{t=er(e)}catch{t=!0}t&&or(e)}Rt.clear()}function qt(e){N(e,e.v+1)}function Jt(e,t,n){var r=e.reactions;if(r!==null){var i=Ye(),a=r.length;if(Wt+=a,Wt>1e5&&Ut===null&&(Ut=new Set),Ut!==null){if(Ut.has(e))return;Ut.add(e)}for(var o=0;o{if(Zn===c)return e();var t=V,n=Zn;Un(null),Qn(c);var r=e();return Un(t),Qn(n),r};return i&&n.set(`length`,Ht(e.length,s)),new Proxy(e,{defineProperty(e,t,r){(!(`value`in r)||r.configurable===!1||r.enumerable===!1||r.writable===!1)&&Ue();var i=n.get(t);return i===void 0?l(()=>{var e=Ht(r.value,s);return n.set(t,e),e}):N(i,r.value,!0),!0},deleteProperty(e,t){var i=n.get(t);if(i===void 0){if(t in e){let e=l(()=>Ht(r,s));n.set(t,e),qt(o)}}else N(i,r),qt(o);return!0},get(t,i,a){if(i===fe)return e;var o=n.get(i),c=i in t;if(o===void 0&&(!c||u(t,i)?.writable)&&(o=l(()=>Ht(Yt(c?t[i]:r),s)),n.set(i,o)),o!==void 0){var d=G(o);return d===r?void 0:d}return Reflect.get(t,i,a)},getOwnPropertyDescriptor(e,t){this.has?.(e,t);var i=Reflect.getOwnPropertyDescriptor(e,t),a=n.get(t);if(a!==void 0){var o=G(a);if(o===r)return;if(i&&`value`in i)i.value=o;else return{enumerable:!0,configurable:!0,value:o,writable:!0}}return i},has(e,t){if(t===fe)return!0;var i=n.get(t),a=i!==void 0&&i.v!==r||Reflect.has(e,t);return(i!==void 0||H!==null&&(!a||u(e,t)?.writable))&&(i===void 0&&(i=l(()=>Ht(a?Yt(e[t]):r,s)),n.set(t,i)),G(i)===r)?!1:a},set(e,t,a,c){var d=n.get(t),f=t in e;if(i&&t===`length`)for(var p=a;pHt(r,s)),n.set(p+``,m)):N(m,r)}if(d===void 0)(!f||u(e,t)?.writable)&&(d=l(()=>Ht(void 0,s)),N(d,Yt(a)),n.set(t,d));else{f=d.v!==r;var h=l(()=>Yt(a));N(d,h)}var g=Reflect.getOwnPropertyDescriptor(e,t);if(g?.set&&g.set.call(c,a),!f){if(i&&typeof t==`string`){var _=n.get(`length`),v=Number(t);Number.isInteger(v)&&v>=_.v&&N(_,v+1)}qt(o)}return!0},ownKeys(e){G(o);var t=Reflect.ownKeys(e).filter(e=>{var t=n.get(e);return t===void 0||t.v!==r});for(var[i,a]of n)a.v!==r&&!(i in e)&&t.push(i);return t},setPrototypeOf(){We()}})}function Xt(e){try{if(typeof e==`object`&&e&&fe in e)return e[fe]}catch{}return e}function Zt(e,t){return Object.is(Xt(e),Xt(t))}var Qt,$t,en,tn;function nn(){if(Qt===void 0){Qt=window,$t=/Firefox/.test(navigator.userAgent);var e=Element.prototype,t=Node.prototype,n=Text.prototype;en=u(t,`firstChild`).get,tn=u(t,`nextSibling`).get,h(e)&&(e[_e]=void 0,e[ge]=null,e[ve]=void 0,e.__e=void 0),h(n)&&(n[ye]=void 0)}}function P(e=``){return document.createTextNode(e)}function rn(e){return en.call(e)}function an(e){return tn.call(e)}function F(e,t){if(!C)return rn(e);var n=rn(w);if(n===null)n=w.appendChild(P());else if(t&&n.nodeType!==3){var r=P();return n?.before(r),T(r),r}return t&&un(n),T(n),n}function on(e,t=!1){if(!C){var n=rn(e);return n instanceof Comment&&n.data===``?an(n):n}if(t){if(w?.nodeType!==3){var r=P();return w?.before(r),T(r),r}un(w)}return w}function I(e,t=!1){if(!C)return rn(e);var n=F(e,t);return E(e),n}function L(e,t=1,n=!1){let r=C?w:e;for(var i;t--;)i=r,r=an(r);if(!C)return r;if(n){if(r?.nodeType!==3){var a=P();return r===null?i?.after(a):r.before(a),T(a),a}un(r)}return T(r),r}function sn(e){e.textContent=``}function cn(){return!1}function ln(e,t,n){return t==null||t===`http://www.w3.org/1999/xhtml`?n?document.createElement(e,{is:n}):document.createElement(e):n?document.createElementNS(t,e,{is:n}):document.createElementNS(t,e)}function un(e){if(e.nodeValue.length<65536)return;let t=e.nextSibling;for(;t!==null&&t.nodeType===3;)t.remove(),e.nodeValue+=t.nodeValue,t=e.nextSibling}function dn(e){var t=H;if(t===null)return V.f|=de,e;if(!(t.f&32768)&&!(t.f&4))throw e;fn(e,t)}function fn(e,t){if(!(t!==null&&t.f&16384)){for(;t!==null;){if(t.f&128&&!(t.f&33570816)){if(!(t.f&32768))throw e;try{t.b.error(e);return}catch(t){e=t}}t=t.parent}throw e}}function pn(e){H===null&&(V===null&&Be(e),ze()),Bn&&Re(e)}function mn(e,t){var n=t.last;n===null?t.last=t.first=e:(n.next=e,e.prev=n,t.last=e)}function hn(e,t){var n=H;n!==null&&n.f&8192&&(e|=ee);var r={ctx:D,deps:null,nodes:null,f:e|x|512,first:null,fn:t,last:null,next:null,parent:n,b:n&&n.b,prev:null,teardown:null,wv:0,ac:null};j?.register_created_effect(r);var i=r;if(e&4)Et===null?At.ensure().schedule(r):Et.push(r);else if(t!==null){try{or(r)}catch(e){throw B(r),e}i.deps===null&&i.teardown===null&&i.nodes===null&&i.first===i.last&&!(i.f&524288)&&(i=i.first,e&16&&e&65536&&i!==null&&(i.f|=ie))}if(i!==null&&(i.parent=n,n!==null&&mn(i,n),V!==null&&V.f&2&&!(e&64))){var a=V;(a.effects??=[]).push(i)}return r}function gn(){return V!==null&&!Hn}function _n(e){let t=hn(8,null);return A(t,b),t.teardown=e,t}function vn(e){pn(`$effect`);var t=H.f;if(!V&&t&32&&D!==null&&!D.i){var n=D;(n.e??=[]).push(e)}else return yn(e)}function yn(e){return hn(4|se,e)}function bn(e){return pn(`$effect.pre`),hn(8|se,e)}function xn(e){At.ensure();let t=hn(64|oe,e);return(e={})=>new Promise(n=>{e.outro?Nn(t,()=>{B(t),n(void 0)}):(B(t),n(void 0))})}function Sn(e){return hn(4,e)}function Cn(e,t){var n=D,r={effect:null,ran:!1,deps:e};n.l.$.push(r),r.effect=En(()=>{if(e(),!r.ran){r.ran=!0;var n=H;try{Wn(n.parent),K(t)}finally{Wn(n)}}})}function wn(){var e=D;En(()=>{for(var t of e.l.$){t.deps();var n=t.effect;n.f&1024&&n.deps!==null&&A(n,S),er(n)&&or(n),t.ran=!1}})}function Tn(e){return hn(ue|oe,e)}function En(e,t=0){return hn(8|t,e)}function R(e,t=[],n=[],r=[]){st(r,t,n,t=>{hn(8,()=>{e(...t.map(G))})})}function Dn(e,t=0){return hn(16|t,e)}function z(e){return hn(32|oe,e)}function On(e){var t=e.teardown;if(t!==null){let n=Bn,r=V;Vn(!0),Un(null);try{t.call(null)}catch(t){fn(t,e.parent)}finally{Vn(n),Un(r)}}}function kn(e,t=!1){var n=e.first;for(e.first=e.last=null;n!==null;){let e=n.ac;e!==null&&at(()=>{e.abort(xe)});var r=n.next;n.f&64?n.parent=null:B(n,t),n=r}}function An(e){for(var t=e.first;t!==null;){var n=t.next;t.f&32||B(t),t=n}}function B(e,t=!0){var n=!1;(t||e.f&262144)&&e.nodes!==null&&e.nodes.end!==null&&(jn(e.nodes.start,e.nodes.end),n=!0),e.f|=re,kn(e,t&&!n),ar(e,0);var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)e.stop();On(e),e.f^=re,e.f|=te;var i=e.parent;i!==null&&i.first!==null&&Mn(e),e.next=e.prev=e.teardown=e.ctx=e.deps=e.fn=e.nodes=e.ac=e.b=null}function jn(e,t){for(;e!==null;){var n=e===t?null:an(e);e.remove(),e=n}}function Mn(e){var t=e.parent,n=e.prev,r=e.next;n!==null&&(n.next=r),r!==null&&(r.prev=n),t!==null&&(t.first===e&&(t.first=r),t.last===e&&(t.last=n))}function Nn(e,t,n=!0){var r=[];e.f|=256,Pn(e,r,!0);var i=()=>{n&&B(e),t&&t()},a=r.length;if(a>0){var o=()=>--a||i();for(var s of r)s.out(o)}else i()}function Pn(e,t,n){if(!(e.f&8192)){e.f^=ee;var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)(e.is_global||n)&&t.push(e);for(var i=e.first;i!==null;){var a=i.next;if(!(i.f&64)){var o=!!(i.f&65536)||!!(i.f&32)&&!!(e.f&16);Pn(i,t,o?n:!1)}i=a}}}function Fn(e){e.f&=-257,In(e,!0)}function In(e,t){if(!(e.f&256)&&e.f&8192){e.f^=ee,e.f&1024||(A(e,x),At.ensure().schedule(e));for(var n=e.first;n!==null;){var r=n.next,i=!!(n.f&65536)||!!(n.f&32);In(n,i?t:!1),n=r}var a=e.nodes&&e.nodes.t;if(a!==null)for(let e of a)(e.is_global||t)&&e.in()}}function Ln(e,t){if(e.nodes)for(var n=e.nodes.start,r=e.nodes.end;n!==null;){var i=n===r?null:an(n);t.append(n),n=i}}var Rn=null,zn=!1,Bn=!1;function Vn(e){Bn=e}var V=null,Hn=!1;function Un(e){V=e}var H=null;function Wn(e){H=e}var Gn=null;function Kn(e){V!==null&&(V.f&2097152||V.f&2)&&(Gn??=new Set).add(e)}var U=null,W=0,qn=null;function Jn(e){qn=e}var Yn=1,Xn=0,Zn=Xn;function Qn(e){Zn=e}function $n(){return++Yn}function er(e){var t=e.f;if(t&2048)return!0;if(t&4096){for(var n=e.deps,r=n.length,i=0;ie.wv)return!0}t&512&&St===null&&A(e,b)}return!1}function tr(e,t,n=!0){var r=e.reactions;if(r!==null&&!(Gn!==null&&Gn.has(e)))for(var i=0;i{e.ac.abort(xe)}),e.ac=null);try{e.f|=le;var u=e.fn,d=u();e.f|=ne;var f=rr(e);if(Ye()&&qn!==null&&!Hn&&f!==null&&!(e.f&6146))for(var p=0;p0)for(t.length=W+U.length,r=0;r{c.ac.abort(xe),c.ac=null,A(c,x)}),vt(c),ar(c,0)}}function ar(e,t){var n=e.deps;if(n!==null)for(var r=t;r{_r=!1,gr=null}));var o=0,s=gr===e&&e[dr];if(s){var c=i.indexOf(s);if(c!==-1&&(t===document||t===window)){e[dr]=t;return}var u=i.indexOf(t);if(u===-1)return;c<=u&&(o=c)}if(a=i[o]||e.target,a!==t){l(e,`currentTarget`,{configurable:!0,get(){return a||n}});var d=V,f=H;Un(null),Wn(null);try{for(var p,m=[];a!==null&&a!==t;){try{var h=a[dr]?.[r];h!=null&&(!a.disabled||e.target===a)&&h.call(a,e)}catch(e){p?m.push(e):p=e}if(e.cancelBubble)break;o++,a=o{throw e});throw p}}finally{e[dr]=t,delete e.currentTarget,Un(d),Wn(f)}}}var yr=globalThis?.window?.trustedTypes&&globalThis.window.trustedTypes.createPolicy(`svelte-trusted-html`,{createHTML:e=>e});function br(e){return yr?.createHTML(e)??e}function xr(e){var t=ln(`template`);return t.innerHTML=br(e.replaceAll(``,``)),t.content}function Sr(e,t){var n=H;n.nodes===null&&(n.nodes={start:e,end:t,a:null,t:null})}function J(e,t){var n=!!(t&1),r=!!(t&2),i,a=!e.startsWith(``);return()=>{if(C)return Sr(w,null),w;i===void 0&&(i=xr(a?e:``+e),n||(i=rn(i)));var t=r||$t?document.importNode(i,!0):i.cloneNode(!0);if(n){var o=rn(t),s=t.lastChild;Sr(o,s)}else Sr(t,t);return t}}function Y(e,t){if(C){var n=H;(!(n.f&32768)||n.nodes.end===null)&&(n.nodes.end=w),Oe();return}e!==null&&e.before(t)}[...`allowfullscreen.async.autofocus.autoplay.checked.controls.default.disabled.formnovalidate.indeterminate.inert.ismap.loop.multiple.muted.nomodule.novalidate.open.playsinline.readonly.required.reversed.seamless.selected.webkitdirectory.defer.disablepictureinpicture.disableremoteplayback`.split(`.`)];var Cr=[`touchstart`,`touchmove`];function wr(e){return Cr.includes(e)}function Tr(e){let t=0,n=Vt(0),r;return()=>{gn()&&(G(n),En(()=>(t===0&&(r=K(()=>e(()=>qt(n)))),t+=1,()=>{Qe(()=>{--t,t===0&&(r?.(),r=void 0,qt(n))})})))}}var Er=ie|oe;function Dr(e,t,n,r){new Or(e,t,n,r)}var Or=class{parent;is_pending=!1;transform_error;#e;#t=C?w:null;#n;#r;#i;#a=null;#o=null;#s=null;#c=null;#l=0;#u=0;#d=!1;#f=new Set;#p=new Set;#m=null;#h=Tr(()=>(this.#m=Vt(this.#l),()=>{this.#m=null}));constructor(e,t,n,r){this.#e=e,this.#n=t,this.#r=e=>{var t=H;t.b=this,t.f|=128,n(e)},this.parent=H.b,this.transform_error=r??this.parent?.transform_error??(e=>e),this.#i=Dn(()=>{if(C){let e=this.#t;Oe();let t=e.data===`[!`;if(e.data.startsWith(`[?`)){let t=JSON.parse(e.data.slice(2));this.#_(t)}else t?this.#y():this.#g()}else this.#b()},Er),C&&(this.#e=w)}#g(){try{this.#a=z(()=>this.#r(this.#e))}catch(e){this.error(e)}}#_(e){let t=this.#n.failed,{reset:n,invoke_onerror:r}=this.#v(e);Qe(r),t&&(this.#s=z(()=>{t(this.#e,()=>e,()=>n)}))}#v(e){var t=!1,n=!1;let r=()=>{if(t){Ee();return}t=!0,n&&Ke(),this.#s!==null&&Nn(this.#s,()=>{this.#s=null}),this.#S(()=>{this.#b()})};return{reset:r,invoke_onerror:()=>{try{n=!0,this.#n.onerror?.(e,r),n=!1}catch(e){fn(e,this.#i&&this.#i.parent)}}}}#y(){let e=this.#n.pending;e&&(this.is_pending=!0,this.#o=z(()=>e(this.#e)),Qe(()=>{var e=this.#c=document.createDocumentFragment(),t=P(),n=!1;if(e.append(t),this.#a=this.#S(()=>{try{return z(()=>this.#r(t))}catch(e){try{this.error(e),n=!0}catch(e){fn(e,this.#i.parent)}return null}}),this.#a===null){this.#c=null,n&&this.#x(j);return}this.#u===0&&(this.#e.before(e),this.#c=null,Nn(this.#o,()=>{this.#o=null}),this.#x(j))}))}#b(){try{if(this.is_pending=this.has_pending_snippet(),this.#u=0,this.#l=0,this.#a=z(()=>{this.#r(this.#e)}),this.#u>0){var e=this.#c=document.createDocumentFragment();Ln(this.#a,e);let t=this.#n.pending;this.#o=z(()=>t(this.#e))}else this.#x(j)}catch(e){this.error(e)}}#x(e){this.is_pending=!1,e.transfer_effects(this.#f,this.#p)}defer_effect(e){nt(e,this.#f,this.#p)}is_rendered(){return!this.is_pending&&(!this.parent||this.parent.is_rendered())}has_pending_snippet(){return!!this.#n.pending}#S(e){var t=H,n=V,r=D;Wn(this.#i),Un(this.#i),qe(this.#i.ctx);try{return At.ensure(),e()}finally{Wn(t),Un(n),qe(r)}}#C(e,t){if(!this.has_pending_snippet()){this.parent&&this.parent.#C(e,t);return}this.#u+=e,this.#u===0&&(this.#x(t),this.#o&&Nn(this.#o,()=>{this.#o=null}),this.#c&&=(this.#e.before(this.#c),null))}update_pending_count(e,t){this.#C(e,t),this.#l+=e,!(!this.#m||this.#d)&&(this.#d=!0,Qe(()=>{this.#d=!1,this.#m&&Gt(this.#m,this.#l)}))}get_effect_pending(){return this.#h(),G(this.#m)}error(e){if(!this.#n.onerror&&!this.#n.failed)throw e;j?.is_fork?(this.#a&&j.skip_effect(this.#a),this.#o&&j.skip_effect(this.#o),this.#s&&j.skip_effect(this.#s),j.oncommit(()=>{this.#w(e)})):this.#w(e)}#w(e){this.#a&&=(B(this.#a),null),this.#o&&=(B(this.#o),null),this.#s&&=(B(this.#s),null),C&&(T(this.#t),ke(),T(Ae()));let t=this.#n.failed,n=e=>{let{reset:n,invoke_onerror:r}=this.#v(e);r(),t&&(this.#s=this.#S(()=>{try{return z(()=>{var r=H;r.b=this,r.f|=128,t(this.#e,()=>e,()=>n)})}catch(e){return fn(e,this.#i.parent),null}}))};Qe(()=>{var t;try{t=this.transform_error(e)}catch(e){fn(e,this.#i&&this.#i.parent);return}typeof t==`object`&&t&&typeof t.then==`function`?t.then(n,e=>fn(e,this.#i&&this.#i.parent)):n(t)})}};function X(e,t){var n=t==null?``:typeof t==`object`?`${t}`:t;n!==(e[ye]??=e.nodeValue)&&(e[ye]=n,e.nodeValue=`${n}`)}function kr(e,t){return jr(e,t)}var Ar=new Map;function jr(e,{target:t,anchor:r,props:i={},events:a,context:o,intro:s=!0,transformError:l}){nn();var u=void 0,d=xn(()=>{var s=r??t.appendChild(P());Dr(s,{pending:()=>{}},t=>{O({});var r=D;if(o&&(r.c=o),a&&(i.$$events=a),C&&Sr(t,null),u=e(t,i)||Je(),C&&(H.nodes.end=w,w===null||w.nodeType!==8||w.data!==`]`))throw we(),n;k()},l);var d=new Set,f=e=>{for(var n=0;n{for(var e of d)for(let r of[t,document]){var n=Ar.get(r),i=n.get(e);--i==0?(r.removeEventListener(e,vr),n.delete(e),n.size===0&&Ar.delete(r)):n.set(e,i)}pr.delete(f),s!==r&&s.parentNode?.removeChild(s)}});return Mr.set(u,d),u}var Mr=new WeakMap,Nr=class{anchor;#e=new Map;#t=new Map;#n=new Map;#r=new Set;#i=!0;constructor(e,t=!0){this.anchor=e,this.#i=t}#a=e=>{if(this.#e.has(e)){var t=this.#e.get(e),n=this.#t.get(t);if(n)Fn(n),this.#r.delete(t);else{var r=this.#n.get(t);r&&(Fn(r.effect),this.#t.set(t,r.effect),this.#n.delete(t),r.fragment.lastChild.remove(),this.anchor.before(r.fragment),n=r.effect)}for(let[t,n]of this.#e){if(this.#e.delete(t),t===e)break;let r=this.#n.get(n);r&&(B(r.effect),this.#n.delete(n))}for(let[e,r]of this.#t){if(e===t||this.#r.has(e))continue;let i=()=>{if(Array.from(this.#e.values()).includes(e)){var t=document.createDocumentFragment();Ln(r,t),t.append(P()),this.#n.set(e,{effect:r,fragment:t})}else B(r);this.#r.delete(e),this.#t.delete(e)};this.#i||!n?(this.#r.add(e),Nn(r,i,!1)):i()}}};#o=e=>{this.#e.delete(e);let t=Array.from(this.#e.values());for(let[e,n]of this.#n)t.includes(e)||(B(n.effect),this.#n.delete(e))};ensure(e,t){var n=j,r=cn();if(t&&!this.#t.has(e)&&!this.#n.has(e)){if(r){var i=document.createDocumentFragment(),a=P();i.append(a),this.#n.set(e,{effect:z(()=>t(a)),fragment:i})}else this.#t.set(e,z(()=>t(this.anchor)))}if(this.#e.set(n,e),r){for(let[t,r]of this.#t)t===e?n.unskip_effect(r):n.skip_effect(r);for(let[t,r]of this.#n)t===e?n.unskip_effect(r.effect):n.skip_effect(r.effect);n.oncommit(this.#a),n.ondiscard(this.#o)}else C&&(this.anchor=w),this.#a(n)}};function Pr(t){D===null&&Fe(`onMount`),e&&D.l!==null?Fr(D).m.push(t):vn(()=>{let e=K(t);if(typeof e==`function`)return e})}function Fr(e){var t=e.l;return t.u??={a:[],b:[],m:[]}}function Z(e,t,n=!1){var r;C&&(r=w,Oe());var i=new Nr(e),a=n?ie:0;function o(e,t){if(C){var n=je(r);if(e!==parseInt(n.substring(1))){var a=Ae();T(a),i.anchor=a,De(!1),i.ensure(e,t),De(!0);return}}i.ensure(e,t)}Dn(()=>{var e=!1;t((t,n=0)=>{e=!0,o(n,t)}),e||o(-1,null)},a)}var Ir=Symbol(`NaN`);function Lr(e,t,n){C&&Oe();var r=new Nr(e),i=!Ye();Dn(()=>{var e=t();e!==e&&(e=Ir),i&&typeof e==`object`&&e&&(e={}),r.ensure(e,n)})}function Rr(e,t){return t}function zr(e,t,n){for(var r=[],i=t.length,a,o=t.length,s=0;s{if(a){if(a.pending.delete(n),a.done.add(n),a.pending.size===0){var t=e.outrogroups;Br(e,c(a.done)),t.delete(a),t.size===0&&(e.outrogroups=null)}}else--o},!1)}if(o===0){var l=r.length===0&&n!==null&&e.pending.size===0;if(l){var u=n,d=u.parentNode;sn(d),d.append(u),e.items.clear()}Br(e,t,!l)}else a={pending:new Set(t),done:new Set},(e.outrogroups??=new Set).add(a)}function Br(e,t,n=!0){var r;if(e.pending.size>0){r=new Set;for(let t of e.pending.values())for(let n of t)r.add(e.items.get(n).e)}for(var i=0;i{var e=n();return a(e)?e:e==null?[]:c(e)}),p,m=new Map,h=!0;function g(e){v.effect.f&16384||(v.pending.delete(e),v.fallback=d,Wr(v,p,s,t,r),d!==null&&(p.length===0?d.f&33554432?(d.f^=ce,Kr(d,null,s)):Fn(d):Nn(d,()=>{d=null})))}function _(e){v.pending.delete(e)}var v={effect:Dn(()=>{p=G(f);var e=p.length;let a=!1;C&&je(s)===`[!`!=(e===0)&&(s=Ae(),T(s),De(!1),a=!0);for(var c=new Set,u=j,v=cn(),y=0;yo(s)):(d=z(()=>o(Vr??=P())),d.f|=ce)),e>c.size&&Le(``,``,``),C&&e>0&&T(Ae()),!h){if(m.set(u,c),v){for(let[e,t]of l)c.has(e)||u.skip_effect(t.e);u.oncommit(g),u.ondiscard(_)}else g(u)}a&&De(!0),G(f)}),flags:t,items:l,pending:m,outrogroups:null,fallback:d};h=!1,C&&(s=w)}function Ur(e){for(;e!==null&&!(e.f&32);)e=e.next;return e}function Wr(e,t,n,r,i){var a=!!(r&8),o=t.length,s=e.items,l=Ur(e.effect.first),u,d=null,f,p=[],m=[],h,g,_,v;if(a)for(v=0;v0){var re=r&4&&o===0?n:null;if(a){for(v=0;v{if(f!==void 0)for(_ of f)_.nodes?.a?.apply()})}function Gr(e,t,n,r,i,a,o,s){var c=o&1?o&16?Vt(n):M(n,!1,!1):null,l=o&2?Vt(i):null;return{v:c,i:l,e:z(()=>(a(t,c??n,l??i,s),()=>{e.delete(r)}))}}function Kr(e,t,n){if(e.nodes)for(var r=e.nodes.start,i=e.nodes.end,a=t&&!(t.f&33554432)?t.nodes.start:n;r!==null;){var o=an(r);if(a.before(r),r===i)return;r=o}}function qr(e,t,n){t===null?e.effect.first=n:t.next=n,n===null?e.effect.last=t:n.prev=t}function Jr(e,t){let n=null,r=C;var i;if(C){n=w;for(var a=rn(document.head);a!==null&&(a.nodeType!==8||a.data!==e);)a=an(a);if(a===null)De(!1);else{var o=an(a);a.remove(),T(o)}}C||(i=document.head.appendChild(P()));try{Dn(()=>{var e=z(()=>t(i));e.f|=ae,C||(e.nodes===null?e.nodes={start:i,end:i,a:null,t:null}:e.nodes.end=i)})}finally{r&&(De(!0),T(n))}}function Yr(e){var t,n,r=``;if(typeof e==`string`||typeof e==`number`)r+=e;else if(typeof e==`object`){if(Array.isArray(e)){var i=e.length;for(t=0;t=0;){var s=o+a;(o===0||Qr.includes(r[o-1]))&&(s===r.length||Qr.includes(r[s]))?r=(o===0?``:r.substring(0,o))+r.substring(s+1):o=s}}return r===``?null:r}function ei(e,t,n,r,i,a){var o=e[_e];if(C||o!==n||o===void 0){var s=$r(n,r,a);(!C||s!==e.getAttribute(`class`))&&(s==null?e.removeAttribute(`class`):t?e.className=s:e.setAttribute(`class`,s)),e[_e]=n}else if(a&&i!==a)for(var c in a){var l=!!a[c];(i==null||l!==!!i[c])&&e.classList.toggle(c,l)}return a}function ti(e,t){t?e.hasAttribute(`selected`)||e.setAttribute(`selected`,``):e.removeAttribute(`selected`)}function ni(e,t){var n=e.__defaultValue,r=e.multiple,i=r?n??[]:null;if(!r||a(i)){var o=e.selectedIndex,s=t&&r?new Set(e.selectedOptions):null;for(var c of e.options){var l=oi(c);ti(c,r?i.includes(l):Zt(l,n))}if(t){if(s!==null)for(c of e.options){var u=s.has(c);c.selected!==u&&(c.selected=u)}else e.selectedIndex!==o&&(e.selectedIndex=o)}}}function ri(e,t,n=!1){if(e.multiple){if(t==null)return;if(!a(t))return Te();for(var r of e.options)r.selected=t.includes(oi(r));return}for(r of e.options)if(Zt(oi(r),t)){r.selected=!0;return}(!n||t!==void 0)&&(e.selectedIndex=-1)}function ii(e){var t=new MutationObserver(t=>{t.every(si)||(`__defaultValue`in e&&ni(e,!1),`__value`in e&&ri(e,e.__value))});t.observe(e,{childList:!0,subtree:!0,attributes:!0,attributeFilter:[`value`]}),_n(()=>{t.disconnect()})}function ai(e,t,n=t){var r=new WeakSet,i=!0;ot(e,`change`,t=>{var i=t?`[selected]`:`:checked`,a;if(e.multiple)a=[].map.call(e.querySelectorAll(i),oi);else{var o=e.querySelector(i)??e.querySelector(`option:not([disabled])`);a=o&&oi(o)}n(a),e.__value=a,j!==null&&r.add(j)}),Sn(()=>{var a=t();if(e===document.activeElement){var o=j;if(r.has(o))return}if(ri(e,a,i),i&&a===void 0){var s=e.querySelector(`:checked`);s!==null&&(a=oi(s),n(a))}e.__value=a,i=!1})}function oi(e){return`__value`in e?e.__value:e.value}function si(e){if(e.target.closest(`selectedcontent`)!==null)return!0;if(e.type===`childList`){var t=[...e.addedNodes,...e.removedNodes];return t.length>0&&t.every(e=>e.nodeName===`SELECTEDCONTENT`)}return!1}var ci=Symbol(`is custom element`),li=Symbol(`is html`),ui=Se?`link`:`LINK`;function di(e){if(C){var t=!1,n=()=>{if(!t){if(t=!0,e.hasAttribute(`value`)){var n=e.value;Q(e,`value`,null),e.value=n}if(e.hasAttribute(`checked`)){var r=e.checked;Q(e,`checked`,null),e.checked=r}}};e[be]=n,Qe(n),it()}}function fi(e,t){var n=pi(e);n.checked!==(n.checked=t??void 0)&&(e.checked=t)}function Q(e,t,n,r){var i=pi(e);C&&(i[t]=e.getAttribute(t),t===`src`||t===`srcset`||t===`href`&&e.nodeName===ui)||i[t]!==(i[t]=n)&&(t===`loading`&&(e[he]=n),n==null?e.removeAttribute(t):typeof n!=`string`&&hi(e).has(t)?e[t]=n:e.setAttribute(t,n))}function pi(e){return e[ge]??={[ci]:e.nodeName.includes(`-`),[li]:e.namespaceURI===i}}var mi=new Map;function hi(e){var t=e.getAttribute(`is`)||e.nodeName,n=mi.get(t);if(n)return n;mi.set(t,n=new Set);for(var r,i=e,a=Element.prototype;a!==i;){for(var o in r=d(i),r)r[o].set&&o!==`innerHTML`&&o!==`textContent`&&o!==`innerText`&&n.add(o);i=m(i)}return n}function gi(e,t,n=t){var r=new WeakSet;ot(e,`input`,async i=>{var a=i?e.defaultValue:e.value;if(a=_i(e)?vi(a):a,n(a),j!==null&&r.add(j),await sr(),a!==(a=t())){var o=e.selectionStart,s=e.selectionEnd,c=e.value.length;if(e.value=a??``,s!==null){var l=e.value.length;o===s&&s===c&&l>c?(e.selectionStart=l,e.selectionEnd=l):(e.selectionStart=o,e.selectionEnd=Math.min(s,l))}}}),(C&&e.defaultValue!==e.value||K(t)==null&&e.value)&&(n(_i(e)?vi(e.value):e.value),j!==null&&r.add(j)),En(()=>{var n=t();if(e===document.activeElement){var i=j;if(r.has(i))return}_i(e)&&n===vi(e.value)||(e.type!==`date`||n||e.value)&&n!==e.value&&(e.value=n??``)})}function _i(e){var t=e.type;return t===`number`||t===`range`}function vi(e){return e===``?null:+e}function yi(e=!1){let t=D,n=t.l.u;if(!n)return;let r=()=>q(t.s);if(e){let e=0,n={},i=dt(()=>{let r=!1,i=t.s;for(let e in i)i[e]!==n[e]&&(n[e]=i[e],r=!0);return r&&e++,e});r=()=>G(i)}n.b.length&&bn(()=>{bi(t,r),v(n.b)}),vn(()=>{let e=K(()=>n.m.map(_));return()=>{for(let t of e)typeof t==`function`&&t()}}),n.a.length&&vn(()=>{bi(t,r),v(n.a)})}function bi(e,t){if(e.l.s)for(let t of e.l.s)G(t);t()}function xi(e,t,n){if(e==null)return t(void 0),n&&n(void 0),g;let r=K(()=>e.subscribe(t,n));return r.unsubscribe?()=>r.unsubscribe():r}var Si=[];function Ci(e,t=g){let n=null,r=new Set;function i(t){if(Ne(e,t)&&(e=t,n)){let t=!Si.length;for(let t of r)t[1](),Si.push(t,e);if(t){for(let e=0;e{r.delete(c),r.size===0&&n&&(n(),n=null)}}return{set:i,update:a,subscribe:o}}function wi(e){let t;return xi(e,e=>t=e)(),t}var Ti=!1,Ei=Symbol(`unmounted`);function Di(e,t,n){let r=n[t]??={store:null,source:M(void 0),unsubscribe:g};if(r.store!==e&&!(Ei in n)){if(r.unsubscribe(),r.store=e??null,e==null)r.source.v=void 0,r.unsubscribe=g;else{var i=!0;r.unsubscribe=xi(e,e=>{i?r.source.v=e:N(r.source,e)}),i=!1}}return e&&Ei in n?wi(e):G(r.source)}function Oi(){let e={};function t(){_n(()=>{for(var t in e)e[t].unsubscribe();l(e,Ei,{enumerable:!1,value:!0})})}return[e,t]}function ki(e){var t=Ti;try{return Ti=!1,[e(),Ti]}finally{Ti=t}}function $(t,n,r,i){var a=!e||!!(r&2),o=!!(r&8),s=!!(r&16),c=i,l=!0,d=void 0,f=()=>s&&a?(d??=dt(i),G(d)):(l&&(l=!1,c=s?K(i):i),c);let p;if(o){var m=fe in t||me in t;p=u(t,n)?.set??(m&&n in t?e=>t[n]=e:void 0)}var h,g=!1;o?[h,g]=ki(()=>t[n]):h=t[n],h===void 0&&i!==void 0&&(h=f(),p&&(a&&He(n),p(h)));var _=a?()=>{var e=t[n];return e===void 0?f():(l=!0,e)}:()=>{var e=t[n];return e!==void 0&&(c=void 0),e===void 0?c:e};if(a&&!(r&4))return _;if(p){var v=t.$$legacy;return(function(e,t){return arguments.length>0?((!a||!t||v||g)&&p(t?_():e),e):_()})}var y=!1,b=(r&1?dt:mt)(()=>(y=!1,_()));o&&G(b);var x=H;return(function(e,t){if(arguments.length>0){let n=t?G(b):a&&o?Yt(e):e;return N(b,n),y=!0,c!==void 0&&(c=n),e}return Bn&&y||x.f&16384?b.v:G(b)})}function Ai(e){return new URLSearchParams(e.replace(/^#/,``)).get(`setup-key`)??``}function ji(e=Ai(globalThis.location?.hash??``)){let t=Ci({controller:!1,busy:!1,error:``}),n,r=``,i={controller:!1,busy:!1,error:``},a=!1;function o(e){i={...i,...e},t.set(i)}async function s(t=!1){if(!a){a=!0;try{let n=await fetch(`/api/state`,{cache:`no-store`,headers:{"X-Setup-Session-Key":e}});if(!n.ok)throw Error(`The local setup session is unavailable.`);o({view:await n.json(),...t?{}:{error:``}})}catch{o({view:void 0,error:`Connection lost. The CLI may have stopped. Check the terminal before trying again.`})}finally{a=!1}}}async function c(){try{let t=await fetch(`/api/bootstrap`,{cache:`no-store`,headers:{"X-Setup-Session-Key":e}});if(!t.ok)throw Error(`Could not join this local session.`);let i=await t.json();n=i.capability,r=i.takeoverTicket,o({controller:i.controller,error:``}),await s()}catch{n=void 0,r=``,o({view:void 0,controller:!1,error:`Could not connect to the local setup session. Check the terminal.`})}}async function l(t,r,i=!0){let a=await fetch(t,{method:`POST`,cache:`no-store`,headers:{"Content-Type":`application/json`,"X-Setup-Session-Key":e,...i&&n?{"X-Setup-Capability":n}:{}},body:JSON.stringify(r)}),o=await a.json();if(!a.ok)throw Error(String(o.error??`The request was rejected.`));return o}async function u(e,t){if(!i.busy&&i.controller&&i.view?.promptRevision===e){o({busy:!0,error:``});try{await l(`/api/answer`,{revision:e,value:t}),await s()}catch(e){let t=e instanceof Error?e.message:`Could not submit this answer.`;o({error:t}),/read-only|Control moved/.test(t)?await c():await s(!0)}finally{o({busy:!1})}}}async function d(){if(i.controller&&!i.busy){o({busy:!0,error:``});try{await l(`/api/cancel`,{}),await s()}catch(e){let t=e instanceof Error?e.message:`Cancellation failed.`;o({error:t}),/read-only|Control moved/.test(t)&&await c()}finally{o({busy:!1})}}}async function f(){try{let e=await l(`/api/takeover`,{ticket:r},!1);n=String(e.capability),o({controller:!0,error:``}),await s()}catch(e){o({error:e instanceof Error?e.message:`Takeover failed.`}),await c()}}async function p(){try{await l(`/api/close`,{})}catch{}}return{subscribe:t.subscribe,connect:c,refresh:s,submit:u,cancel:d,takeOver:f,close:p}}var Mi=J(`
                                                  • `),Ni=J(``);function Pi(e,t){O(t,!1);let n=$(t,`journey`,8),r=[`Repository`,`Setup choices`,`Setup PAT`,`Plan`,`Bot PAT & credentials`,`Apply`];yi();var i=Ni(),a=L(F(i),4);Hr(a,5,()=>r,Rr,(e,t,r)=>{var i=Mi();let a;var o=F(i),s=I(o,!0),c=I(L(o,2),!0);E(i),R(e=>{Q(i,`aria-current`,(q(n()),K(()=>n()?.position===r+1?`step`:void 0))),a=ei(i,1,``,null,a,{current:n()?.position===r+1,completed:(n()?.position??0)>r+1}),X(s,e),X(c,G(t))},[()=>(q(n()),K(()=>(n()?.position??0)>r+1?`✓`:String(r+1).padStart(2,`0`)))]),Y(e,i)}),E(a),ke(2),E(i),Y(e,i),k()}var Fi=J(`
                                                    `);function Ii(e,t){O(t,!1);let n=M(`system`);Cn(()=>G(n),()=>{document.documentElement.dataset.theme=G(n)}),wn();var r=Fi(),i=F(r);let a;var o=L(i,2);let s;var c=L(o,2);let l;E(r),R(()=>{Q(i,`aria-pressed`,G(n)===`system`),a=ei(i,1,``,null,a,{active:G(n)===`system`}),Q(o,`aria-pressed`,G(n)===`light`),s=ei(o,1,``,null,s,{active:G(n)===`light`}),Q(c,`aria-pressed`,G(n)===`dark`),l=ei(c,1,``,null,l,{active:G(n)===`dark`})}),mr(`click`,i,()=>N(n,`system`)),mr(`click`,o,()=>N(n,`light`)),mr(`click`,c,()=>N(n,`dark`)),Y(e,r),k()}hr([`click`]);var Li=J(`
                                                    LOCAL SESSION
                                                    `);function Ri(e,t){let n=$(t,`repository`,8);var r=Li(),i=F(r),a=I(L(F(i),2),!0);E(i);var o=L(i,2);Ii(L(F(o)),{}),E(o),E(r),R(()=>X(a,n()??`Connecting…`)),Y(e,r)}var zi=J(`

                                                    `,1);function Bi(e,t){O(t,!1);let n=M(),r=$(t,`view`,8);Cn(()=>q(r()),()=>{N(n,r()?.outcome===`complete`?`Setup complete.`:r()?.outcome===`dry-run`?`Preview complete.`:r()?.outcome===`cancelled`?`Setup cancelled.`:r()?.outcome?`Setup needs attention.`:r()?.prompt?.title??`Preparing your setup…`)}),wn(),yi();var i=zi(),a=on(i),o=L(F(a)),s=I(L(o));E(a);var c=L(a,2),l=I(c,!0),u=I(L(c,2),!0);R((e,t)=>{X(o,` ${e??``} `),X(s,`${t??``} / 06`),X(l,G(n)),X(u,(q(r()),K(()=>r()?.outcome?`The terminal contains the detailed result. Review the next steps below before closing this session.`:`One decision at a time. Your choices shape the permissions, plan and credentials needed for this repository.`)))},[()=>(q(r()),K(()=>r()?.journey?.current?.toUpperCase()??`GETTING READY`)),()=>(q(r()),K(()=>String(r()?.journey?.position??1).padStart(2,`0`)))]),Y(e,i),k()}function Vi(e){try{let t=new URL(e??``);return t.protocol===`https:`&&t.hostname===`github.com`&&(t.pathname===`/settings/personal-access-tokens`||t.pathname.startsWith(`/settings/personal-access-tokens/`))?t.toString():void 0}catch{return}}var Hi=J(``),Ui=J(``);function Wi(e,t){let n=$(t,`label`,8),r=$(t,`variant`,8,`primary`),i=$(t,`disabled`,8,!1),a=$(t,`arrow`,8,!1),o=$(t,`onClick`,8);var s=Ui(),c=F(s,!0),l=L(c),u=e=>{Y(e,Hi())};Z(l,e=>{a()&&e(u)}),E(s),R(()=>{ei(s,1,Zr(r())),s.disabled=i(),X(c,n())}),mr(`click`,s,function(...e){o()?.apply(this,e)}),Y(e,s)}hr([`click`]);var Gi=J(`Open GitHub link ↗`),Ki=J(`

                                                    `);function qi(e,t){O(t,!1);let n=M(),r=$(t,`tone`,8,`info`),i=$(t,`title`,8),a=$(t,`message`,8),o=$(t,`link`,8,void 0),s=$(t,`actionLabel`,8,void 0),c=$(t,`onAction`,8,void 0);Cn(()=>q(o()),()=>{N(n,Vi(o()))}),wn(),yi();var l=Ki(),u=F(l),d=I(u,!0),f=L(u),p=I(f,!0),m=L(f,2),h=e=>{var t=Gi();R(()=>Q(t,`href`,G(n))),Y(e,t)};Z(m,e=>{G(n)&&e(h)});var g=L(m,2),_=e=>{Wi(e,{get label(){return s()},variant:`secondary`,get onClick(){return c()}})};Z(g,e=>{s()&&c()&&e(_)}),E(l),R(()=>{ei(l,1,`banner ${r()??``}`),Q(l,`role`,r()===`error`?`alert`:`status`),X(d,i()),X(p,a())}),Y(e,l),k()}function Ji(e){let t=String(e.question.defaultValue),n=t.split(`,`).map(e=>e.trim()).filter(Boolean);return{value:t,selected:e.question.kind===`multi-select`?(e.question.choices??[]).filter(e=>e!==`All`&&n.includes(e.split(` — `)[0])):e.question.kind===`scope-overrides`?n:[]}}function Yi(e,t){return t===`All`?e.includes(`All`)?[]:[`All`]:e.includes(t)?e.filter(e=>e!==t):[...e.filter(e=>e!==`All`),t]}function Xi(e,t,n){return e.question.kind===`scope-overrides`||e.question.kind===`multi-select`?n.length?n.join(`,`):`none`:t}var Zi=J(`PERMISSION PREVIEW`),Qi=J(`
                                                    `),$i=J(``),ea=J(``),ta=J(``),na=J(`
                                                    `),ra=J(``),ia=J(`

                                                    `,1);function aa(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`controller`,8),i=$(t,`busy`,8),a=$(t,`onSubmit`,8),o=Ji(n()),s=M(o.value),c=M(o.selected);yi();var l=ia(),u=on(l),d=F(u),f=I(d,!0),p=L(d),m=e=>{Y(e,Zi())};Z(p,e=>{q(n()),K(()=>n().phase===`permission-intent`)&&e(m)}),E(u);var h=L(u,2),g=e=>{var t=Qi(),i=F(t);let a;var o=L(i);let c;E(t),R(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),Q(i,`aria-pressed`,G(s)===`yes`||G(s)===`true`),i.disabled=!r(),a=ei(i,1,``,null,a,{selected:G(s)===`yes`||G(s)===`true`}),Q(o,`aria-pressed`,G(s)===`no`||G(s)===`false`),o.disabled=!r(),c=ei(o,1,``,null,c,{selected:G(s)===`no`||G(s)===`false`})}),mr(`click`,i,()=>N(s,`yes`)),mr(`click`,o,()=>N(s,`no`)),Y(e,t)},_=e=>{var t=ea();Hr(t,5,()=>(q(n()),K(()=>n().question.choices??[])),Rr,(e,t)=>{var n=$i(),r=I(n,!0),i={};R(()=>{X(r,G(t)),i!==(i=G(t))&&(n.value=(n.__value=i)??``)}),Y(e,n)}),E(t),ii(t),R(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),t.disabled=!r()}),ai(t,()=>G(s),e=>N(s,e)),Y(e,t)},v=e=>{var t=na();Hr(t,5,()=>(q(n()),K(()=>n().question.kind===`multi-select`?n().question.choices??[]:n().question.allowedNames??[])),Rr,(e,t)=>{var n=ta(),i=F(n);di(i);var a=I(L(i),!0);E(n),R(e=>{fi(i,e),i.disabled=!r(),X(a,G(t))},[()=>(G(c),G(t),K(()=>G(c).includes(G(t))))]),mr(`change`,i,()=>N(c,Yi(G(c),G(t)))),Y(e,n)}),E(t),R(()=>Q(t,`aria-label`,(q(n()),K(()=>n().question.label)))),Y(e,t)},y=e=>{var t=ra();di(t),R(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),Q(t,`type`,(q(n()),K(()=>n().question.kind===`number`?`number`:`text`))),Q(t,`min`,(q(n()),K(()=>n().question.kind===`number`?0:void 0))),t.disabled=!r()}),gi(t,()=>G(s),e=>N(s,e)),Y(e,t)};Z(h,e=>{q(n()),K(()=>n().question.kind===`boolean`)?e(g):(q(n()),K(()=>n().question.kind===`choice`)?e(_,1):(q(n()),K(()=>n().question.kind===`multi-select`||n().question.kind===`scope-overrides`)?e(v,2):e(y,-1)))});var b=L(h,2),x=I(b),S=L(b,2);{let e=mt(()=>!r()||i());Wi(S,{label:`Continue`,arrow:!0,onClick:()=>a()(Xi(n(),G(s),G(c))),get disabled(){return G(e)}})}R((e,t)=>{X(f,e),X(x,`Suggested answer: ${t??``} · You can review choices again before creating your setup PAT.`)},[()=>(q(n()),K(()=>n().question.label.replace(` (Space toggles, Enter confirms)`,``))),()=>(q(n()),K(()=>String(n().question.defaultValue)||`none`))]),Y(e,l),k()}hr([`click`,`change`]);var oa=J(``),sa=J(`
                                                    `);function ca(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`controller`,8),i=$(t,`busy`,8),a=$(t,`onSubmit`,8);yi();var o=sa();Hr(o,5,()=>(q(n()),K(()=>n().choices)),Rr,(e,t)=>{var n=oa(),o=I(F(n),!0);ke(),E(n),R(()=>{n.disabled=!r()||i(),X(o,G(t))}),mr(`click`,n,()=>a()(G(t))),Y(e,n)}),E(o),Y(e,o),k()}hr([`click`]);var la=J(`Open the official GitHub PAT form

                                                    Check the signed-in account, select Only select repositories, then select this repository. GitHub handles 2FA and creates the PAT.

                                                    `,1),ua=J(`

                                                    Sent only to this local process. It will not be shown again or saved in browser storage.

                                                    `),da=J(` `,1);function fa(e,t){O(t,!1);let n=M(),r=$(t,`prompt`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8),s=M(``);function c(){let e=G(s);r().kind===`secret`&&N(s,``),o()(e)}Cn(()=>q(r()),()=>{N(n,Vi(r().link))}),wn(),yi();var l=da(),u=on(l),d=e=>{var t=la(),r=on(t);ke(),R(()=>Q(r,`href`,G(n))),Y(e,t)};Z(u,e=>{G(n)&&e(d)});var f=L(u,2),p=I(f,!0),m=L(f,2);di(m);var h=L(m,2),g=e=>{Y(e,ua())};Z(h,e=>{q(r()),K(()=>r().kind===`secret`)&&e(g)});var _=L(h,2);{let e=mt(()=>(q(i()),q(a()),q(r()),G(s),K(()=>!i()||a()||!r().optional&&!G(s).trim())));Wi(_,{label:`Continue`,arrow:!0,onClick:c,get disabled(){return G(e)}})}R(()=>{X(p,(q(r()),K(()=>r().kind===`secret`?`Paste the value here`:`Your answer`))),Q(m,`type`,(q(r()),K(()=>r().kind===`secret`?`password`:`text`))),m.disabled=!i(),Q(m,`placeholder`,(q(r()),K(()=>r().kind===`secret`?`Hidden after submission`:`Type your answer`)))}),gi(m,()=>G(s),e=>N(s,e)),Y(e,l),k()}var pa=J(`
                                                  • `),ma=J(`

                                                      `),ha=J(`

                                                      Before you continue

                                                        `),ga=J(`

                                                        Review exactly what this run may change. The bot PAT and any additional credentials are collected next.

                                                        `,1);function _a(e,t){O(t,!1);let n=M(),r=$(t,`prompt`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8);Cn(()=>q(r()),()=>{N(n,[{title:`Files`,items:r().plan.files},{title:`Workflows`,items:r().plan.workflows},{title:`Variables`,items:r().plan.variables},{title:`Secret names`,items:r().plan.secrets}])}),wn(),yi();var s=ga(),c=L(on(s),2);Hr(c,5,()=>G(n),Rr,(e,t)=>{var n=ma(),r=F(n),i=F(r),a=I(L(i),!0);E(r);var o=L(r);Hr(o,5,()=>(G(t),K(()=>G(t).items)),Rr,(e,t)=>{var n=pa(),r=I(n,!0);R(()=>X(r,G(t))),Y(e,n)}),E(o),E(n),R(()=>{X(i,`${G(t),K(()=>G(t).title)??``} `),X(a,(G(t),K(()=>G(t).items.length)))}),Y(e,n)}),E(c);var l=L(c,2),u=e=>{var t=ha(),n=L(F(t));Hr(n,5,()=>(q(r()),K(()=>r().plan.warnings)),Rr,(e,t)=>{var n=pa(),r=I(n,!0);R(()=>X(r,G(t))),Y(e,n)}),E(n),E(t),Y(e,t)};Z(l,e=>{q(r()),K(()=>r().plan.warnings.length)&&e(u)});var d=L(l,2),f=F(d);{let e=mt(()=>!i()||a());Wi(f,{label:`Stop here`,variant:`secondary`,onClick:()=>o()(`decline`),get disabled(){return G(e)}})}var p=L(f);{let e=mt(()=>!i()||a());Wi(p,{label:`Approve this plan`,arrow:!0,onClick:()=>o()(`approve`),get disabled(){return G(e)}})}E(d),Y(e,s),k()}var va=J(`

                                                        `),ya=J(`
                                                        CURRENT DECISION
                                                        `);function ba(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`revision`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8);yi();var s=ya(),c=F(s),l=I(L(F(c)));E(c);var u=L(c,2),d=e=>{var t=va(),r=I(t,!0);R(()=>X(r,(q(n()),K(()=>n().description)))),Y(e,t)};Z(u,e=>{q(n()),K(()=>`description`in n()&&n().description)&&e(d)});var f=L(u,2),p=e=>{aa(e,{get prompt(){return n()},get controller(){return i()},get busy(){return a()},get onSubmit(){return o()}})},m=e=>{ca(e,{get prompt(){return n()},get controller(){return i()},get busy(){return a()},get onSubmit(){return o()}})},h=e=>{fa(e,{get prompt(){return n()},get controller(){return i()},get busy(){return a()},get onSubmit(){return o()}})},g=e=>{_a(e,{get prompt(){return n()},get controller(){return i()},get busy(){return a()},get onSubmit(){return o()}})};Z(f,e=>{q(n()),K(()=>n().kind===`question`)?e(p):(q(n()),K(()=>n().kind===`choice`||n().kind===`confirm`)?e(m,1):(q(n()),K(()=>n().kind===`text`||n().kind===`secret`)?e(h,2):(q(n()),K(()=>n().kind===`plan`)&&e(g,3))))}),E(s),R(()=>X(l,`SESSION ${r()??``}`)),Y(e,s),k()}var xa=J(` `),Sa=J(`
                                                      • `),Ca=J(`

                                                          Conditional grants depend on the choices and GitHub facts. A final audit runs before setup changes.
                                                          `),wa=J(`

                                                          Permissions follow your choices

                                                          We’ll show the exact grants before you create each PAT. Nothing is created just by opening this page.

                                                          `),Ta=J(``);function Ea(e,t){O(t,!1);let n=$(t,`view`,8);yi();var r=Ta(),i=F(r),a=I(L(F(i),3),!0);E(i);var o=L(i,2),s=e=>{var t=Ca(),r=L(F(t)),i=I(r),a=L(r),o=I(a,!0),s=L(a,2);Hr(s,5,()=>(q(n()),K(()=>n().permissions.report?.checks??n().permissions.requirements??[])),Rr,(e,t)=>{var n=Sa(),r=F(n),i=F(r),a=L(i),o=I(a),s=I(L(a),!0);E(r);var c=L(r),l=F(c,!0),u=L(l),d=e=>{var n=xa(),r=I(n,!0);R(()=>X(r,(G(t),K(()=>G(t).status)))),Y(e,n)};Z(u,e=>{`status`in G(t)&&e(d)}),E(c),E(n),R(()=>{X(i,`${G(t),K(()=>G(t).permission)??``} `),X(o,`${G(t),K(()=>G(t).scope)??``} · ${G(t),K(()=>G(t).applicability)??``}`),X(s,(G(t),K(()=>G(t).reason))),X(l,(G(t),K(()=>G(t).level)))}),Y(e,n)}),E(s),ke(2),E(t),R(()=>{X(i,`${q(n()),K(()=>n().permissions.role===`setup`?`Setup PAT`:`Bot PAT`)??``} access`),X(o,(q(n()),K(()=>n().permissions.report?`Read-only access check`:`Provisional least-privilege grants`)))}),Y(e,t)},c=e=>{Y(e,wa())};Z(o,e=>{q(n()),K(()=>n().permissions)?e(s):e(c,-1)}),E(r),R(()=>X(a,(q(n()),K(()=>n().repository)))),Y(e,r),k()}var Da=J(`

                                                          `);function Oa(e,t){O(t,!1);let n=M(),r=M(),i=$(t,`outcome`,8),a=$(t,`controller`,8),o=$(t,`onClose`,8);Cn(()=>q(i()),()=>{N(n,i()===`complete`?`Your configuration was applied`:i()===`dry-run`?`No changes were made`:i()===`cancelled`||i()===`blocked`?`No setup changes started`:`Check partial changes before retrying`)}),Cn(()=>q(i()),()=>{N(r,i()===`complete`?`The temporary setup PAT is not revoked automatically. Delete it in GitHub after confirming your setup. Keep the bot PAT until you rotate the installed Secret.`:i()===`partial`?`A bot Secret write or another change may have succeeded. Inspect GitHub and run copilot doctor before replacing or deleting its PAT.`:`Copilot did not begin applying setup changes. PATs you created in GitHub still exist until you delete them there.`)}),wn();var s=Da(),c=F(s),l=I(c,!0),u=L(c),d=I(u,!0),f=L(u),p=I(f,!0),m=L(f,2),h=e=>{Wi(e,{label:`Close local session`,get onClick(){return o()}})};Z(m,e=>{a()&&e(h)}),E(s),R(()=>{X(l,i()===`complete`?`✓`:`!`),X(d,G(n)),X(p,G(r))}),Y(e,s),k()}var ka=J(`

                                                          Working on the next step

                                                          The local process is checking your answers and preparing the next decision. Keep this page open.

                                                          `);function Aa(e){Y(e,ka())}var ja=J(``),Ma=J(`
                                                          `),Na=J(``),Pa=J(`
                                                          `,1),Fa=J(`
                                                          LOCALHOST ONLY NO CLOUD SETUP ACCOUNT GITHUB OWNS PAT ISSUANCE
                                                          `);function Ia(e,t){O(t,!1);let n=()=>Di(a,`$session`,r),[r,i]=Oi(),a=ji();Pr(()=>{a.connect();let e=window.setInterval(()=>{n().view?.outcome||a.refresh()},900);return()=>window.clearInterval(e)});async function o(){window.confirm(`Cancel this local setup session? PATs already created in GitHub will still exist.`)&&await a.cancel()}async function s(e){let t=n().view?.promptRevision;t!==void 0&&await a.submit(t,e)}yi();var c=Fa();Jr(`16t12jp`,e=>{Y(e,ja())});var l=F(c);{let e=mt(()=>n().view?.journey);Pi(l,{get journey(){return G(e)}})}var u=L(l,2),d=F(u);{let e=mt(()=>n().view?.repository);Ri(d,{get repository(){return G(e)}})}var f=L(d,2),p=F(f);Bi(p,{get view(){return n().view}});var m=L(p,2),h=e=>{var t=Ma(),r=L(F(t));E(t),R(()=>X(r,` Reviewing saved choices — pass ${n().view.journey.choiceReviewPass??``}. This is the same setup run, not a restart.`)),Y(e,t)};Z(m,e=>{n().view?.journey?.choiceReviewPass&&n().view.journey.choiceReviewPass>1&&!n().view.outcome&&e(h)});var g=L(m,2),_=e=>{qi(e,{tone:`warning`,title:`Read-only tab`,message:`Another tab controls this session. You can review progress here or explicitly take over.`,actionLabel:`Take control in this tab`,get onAction(){return a.takeOver}})};Z(g,e=>{!n().controller&&n().view&&e(_)});var v=L(g,2),y=e=>{qi(e,{tone:`error`,title:`Needs attention`,get message(){return n().error}})};Z(v,e=>{n().error&&e(y)});var b=L(v,2),x=e=>{{let t=mt(()=>n().view.message.tone===`success`?`Checked`:n().view.message.tone===`warning`?`Please note`:n().view.message.tone===`error`?`Needs attention`:`Progress update`);qi(e,{get tone(){return n().view.message.tone},get title(){return G(t)},get message(){return n().view.message.text},get link(){return n().view.message.link}})}};Z(b,e=>{n().view?.message&&e(x)});var S=L(b,2),ee=e=>{Oa(e,{get outcome(){return n().view.outcome},get controller(){return n().controller},get onClose(){return a.close}})},te=e=>{var t=Pa(),r=on(t),i=F(r);Lr(i,()=>n().view.promptRevision,e=>{ba(e,{get prompt(){return n().view.prompt},get revision(){return n().view.revision},get controller(){return n().controller},get busy(){return n().busy},onSubmit:s})}),Ea(L(i,2),{get view(){return n().view}}),E(r);var a=L(r,2),c=e=>{var t=Na();R(()=>t.disabled=n().busy),mr(`click`,t,o),Y(e,t)};Z(a,e=>{n().controller&&n().view.journey?.current!==`Apply`&&e(c)}),Y(e,t)},ne=e=>{Aa(e,{})};Z(S,e=>{n().view?.outcome?e(ee):n().view?.prompt?e(te,1):e(ne,-1)}),ke(2),E(f),E(u),E(c),Y(e,c),k(),i()}hr([`click`]),kr(Ia,{target:document.getElementById(`app`)}); \ No newline at end of file diff --git a/build/web/index.html b/build/web/index.html index 49fd561eb..2fe905c02 100644 --- a/build/web/index.html +++ b/build/web/index.html @@ -5,7 +5,7 @@ Copilot · Setup studio - + diff --git a/docs/how-to-use.mdx b/docs/how-to-use.mdx index ae6f7a81a..5784b315c 100644 --- a/docs/how-to-use.mdx +++ b/docs/how-to-use.mdx @@ -41,10 +41,11 @@ Once installed, the `copilot` command is available globally. Repository-dependen Prefer a visual walkthrough? Run `copilot setup --web` from the repository root. The CLI starts a short-lived page on `127.0.0.1` and opens your browser; if opening fails, paste the printed local URL into a browser on this computer. -Treat that entire URL, including its `#setup-key` fragment, as private: -loopback by itself does not restrict access to your OS account, and anyone -with the URL on this computer could access the setup session. The fragment -is used for local API authorization and is not sent to GitHub. +Enter the 16-character pairing code shown in the terminal before setup state +appears. The URL contains no secret; keep the pairing code private. Loopback +by itself does not restrict access to your OS account, and anyone on this +computer with the code could join the session. After a page refresh, enter +the code again. Neither the code nor the session key is sent to GitHub. The page shows the same six setup stages, keeps the review pass visibly part of the current run, presents permissions and the final plan, and asks for a separate final **Apply setup** approval. Use the System/Light/Dark control in diff --git a/docs/security-operations/operations/troubleshooting.mdx b/docs/security-operations/operations/troubleshooting.mdx index 92ff3ff95..daec1dc39 100644 --- a/docs/security-operations/operations/troubleshooting.mdx +++ b/docs/security-operations/operations/troubleshooting.mdx @@ -25,9 +25,10 @@ renew an installed bot PAT before its suggested 90-day expiry. For `copilot setup --web`, an inaccessible page or failed browser launch does not imply a setup failure: open the exact `127.0.0.1` URL printed by the CLI. -Do not share the printed `#setup-key` fragment. TCP loopback can be reached -by other local OS users; the private link, not OS-user identification, grants -access to this setup session. +Enter the terminal's 16-character pairing code in the page; after a refresh, +enter it again. Do not share the code. TCP loopback can be reached by other +local OS users; the code, not the public URL or OS-user identification, grants +access to this setup session. After five wrong codes, restart setup. If the local bind or bundled assets fail, stop and use `copilot setup` in the terminal. A second browser tab is read-only until you select **Take control in this tab**; the former tab then cannot submit decisions. A rejected stale diff --git a/scripts/render-web-setup-component.cjs b/scripts/render-web-setup-component.cjs index 6f4629f6d..20f6da1e2 100644 --- a/scripts/render-web-setup-component.cjs +++ b/scripts/render-web-setup-component.cjs @@ -16,7 +16,7 @@ async function main() { // Use the same Svelte SSR runtime as the Vite-transformed component. const { render } = await server.ssrLoadModule('svelte/server'); const props = JSON.parse(encodedProps); - for (const key of ['onSubmit', 'onClose', 'onAction']) props[key] = async () => undefined; + for (const key of ['onSubmit', 'onClose', 'onAction', 'onPair']) props[key] = async () => undefined; process.stdout.write(render(component.default, { props }).body); } finally { await server.close(); diff --git a/specs/CATALOG.md b/specs/CATALOG.md index c68f35ddc..34539a75b 100644 --- a/specs/CATALOG.md +++ b/specs/CATALOG.md @@ -17,7 +17,7 @@ debt or convert unknown historic intent into a design decision. | `execution-lifecycle` | Implemented | Shared GitHub Action lifecycle from event admission through durable user-facing results | [Execution admission, queueing, routing, and result publication](./execution-admission-queue-and-publication.md) + 3 companion | 84 paths · 2026-09-16 | | `architecture-quality-hardening` | Implemented | Close verified concurrency, error-contract, context-coupling, fan-out, setup/doctor, and provider-policy risks in dependency order | [Architecture quality and scalability hardening](./architecture-quality-and-scalability-hardening.md) + 1 companion | 72 paths · 2026-09-16 | | `setup-and-doctor` | Implemented | Plan, validate, provision, and audit a repository installation without exposing credentials | [Setup, configuration, credentials, and doctor](./setup-configuration-credentials-and-doctor.md) + 2 companion | 90 paths · 2026-09-28 | -| `local-web-setup-assistant` | Proposed | Offer a packaged, loopback-only Svelte setup interface over the existing setup engine with role-separated PAT guidance and revision-bound application | [Local web setup assistant](./local-web-setup-assistant.md) | 71 paths · 2026-09-28 | +| `local-web-setup-assistant` | Proposed | Offer a packaged, loopback-only Svelte setup interface over the existing setup engine with role-separated PAT guidance and revision-bound application | [Local web setup assistant](./local-web-setup-assistant.md) | 72 paths · 2026-09-28 | | `guided-bot-pat-onboarding` | Proposed | Guide creation of the persistent workflow PAT using GitHub's official form, verify bot identity and grants, and install the approved Actions Secret | [Guided bot PAT onboarding](./guided-bot-pat-onboarding.md) | 25 paths · 2026-09-25 | | `temporary-setup-operator-authorization` | Proposed | Guide creation of the one-run operator PAT through GitHub's official form, verify final setup access, and report user-owned deletion accurately | [Assisted setup PAT creation](./temporary-setup-operator-authorization.md) | 28 paths · 2026-09-25 | | `issue-start-and-sdd-readiness` | Implemented | Start every admitted issue with one explicit signal and publish a validated SDD before eligible Action-managed branch work | [Uniform issue start and pre-branch SDD readiness](./issue-start-and-branch-readiness.md) + 1 companion | 51 paths · 2026-09-17 | @@ -118,7 +118,7 @@ debt or convert unknown historic intent into a design decision. - Specifications: [`specs/local-web-setup-assistant.md`](./local-web-setup-assistant.md) - Workflows: Not applicable for this capability. - Entrypoints: [`src/cli/commands/setup.ts`](../src/cli/commands/setup.ts) · [`package.json`](../package.json) · [`web/src/main.ts`](../web/src/main.ts) -- Core code: [`web/src/App.svelte`](../web/src/App.svelte) · [`web/src/session/setupSession.ts`](../web/src/session/setupSession.ts) · [`web/src/components/ActionButton.svelte`](../web/src/components/ActionButton.svelte) · [`web/src/components/ContextPanel.svelte`](../web/src/components/ContextPanel.svelte) · [`web/src/components/PromptCard.svelte`](../web/src/components/PromptCard.svelte) · [`web/src/components/QuestionPrompt.svelte`](../web/src/components/QuestionPrompt.svelte) · [`web/src/components/StatusBanner.svelte`](../web/src/components/StatusBanner.svelte) · [`web/src/lib/questionAnswer.ts`](../web/src/lib/questionAnswer.ts) · [`web/src/styles/tokens.css`](../web/src/styles/tokens.css) · [`web/src/style.css`](../web/src/style.css) · [`src/application/contracts/web_setup_view.ts`](../src/application/contracts/web_setup_view.ts) · [`src/application/errors/setup_interaction_cancelled_error.ts`](../src/application/errors/setup_interaction_cancelled_error.ts) · [`src/application/policies/merge_setup_overrides_policy.ts`](../src/application/policies/merge_setup_overrides_policy.ts) · [`src/application/policies/setup_remote_facts_policy.ts`](../src/application/policies/setup_remote_facts_policy.ts) · [`src/application/usecases/setup/prepare_setup_pat_intent_use_case.ts`](../src/application/usecases/setup/prepare_setup_pat_intent_use_case.ts) · [`src/application/usecases/setup/audit_configured_setup_pat_use_case.ts`](../src/application/usecases/setup/audit_configured_setup_pat_use_case.ts) · [`src/application/usecases/setup/verify_web_setup_apply_use_case.ts`](../src/application/usecases/setup/verify_web_setup_apply_use_case.ts) · [`src/application/usecases/setup/verify_setup_pat_bootstrap_use_case.ts`](../src/application/usecases/setup/verify_setup_pat_bootstrap_use_case.ts) · [`src/cli_context.ts`](../src/cli_context.ts) · [`src/cli/setup_command_options.ts`](../src/cli/setup_command_options.ts) · [`src/cli/web_setup_bridge.ts`](../src/cli/web_setup_bridge.ts) · [`src/cli/web_setup_adapters.ts`](../src/cli/web_setup_adapters.ts) · [`src/cli/web_setup_server.ts`](../src/cli/web_setup_server.ts) · [`src/cli/setup_apply_snapshot.ts`](../src/cli/setup_apply_snapshot.ts) · [`src/cli/setup_session_guard.ts`](../src/cli/setup_session_guard.ts) · [`src/application/usecases/setup/setup_wizard_use_case.ts`](../src/application/usecases/setup/setup_wizard_use_case.ts) · [`src/application/usecases/setup/setup_journey_use_case.ts`](../src/application/usecases/setup/setup_journey_use_case.ts) · [`src/application/usecases/setup/setup_credentials_use_case.ts`](../src/application/usecases/setup/setup_credentials_use_case.ts) · [`src/application/policies/setup_questionnaire_policy.ts`](../src/application/policies/setup_questionnaire_policy.ts) · [`src/application/policies/setup_token_permission_policy.ts`](../src/application/policies/setup_token_permission_policy.ts) · [`src/application/policies/setup_configuration_plan.ts`](../src/application/policies/setup_configuration_plan.ts) · [`src/application/policies/setup_pat_creation_url_policy.ts`](../src/application/policies/setup_pat_creation_url_policy.ts) · [`src/infrastructure/composition/setup_credentials_composition_root.ts`](../src/infrastructure/composition/setup_credentials_composition_root.ts) · [`scripts/validate-npm-package.cjs`](../scripts/validate-npm-package.cjs) · [`scripts/render-web-setup-component.cjs`](../scripts/render-web-setup-component.cjs) +- Core code: [`web/src/App.svelte`](../web/src/App.svelte) · [`web/src/session/setupSession.ts`](../web/src/session/setupSession.ts) · [`web/src/components/ActionButton.svelte`](../web/src/components/ActionButton.svelte) · [`web/src/components/PairingPanel.svelte`](../web/src/components/PairingPanel.svelte) · [`web/src/components/ContextPanel.svelte`](../web/src/components/ContextPanel.svelte) · [`web/src/components/PromptCard.svelte`](../web/src/components/PromptCard.svelte) · [`web/src/components/QuestionPrompt.svelte`](../web/src/components/QuestionPrompt.svelte) · [`web/src/components/StatusBanner.svelte`](../web/src/components/StatusBanner.svelte) · [`web/src/lib/questionAnswer.ts`](../web/src/lib/questionAnswer.ts) · [`web/src/styles/tokens.css`](../web/src/styles/tokens.css) · [`web/src/style.css`](../web/src/style.css) · [`src/application/contracts/web_setup_view.ts`](../src/application/contracts/web_setup_view.ts) · [`src/application/errors/setup_interaction_cancelled_error.ts`](../src/application/errors/setup_interaction_cancelled_error.ts) · [`src/application/policies/merge_setup_overrides_policy.ts`](../src/application/policies/merge_setup_overrides_policy.ts) · [`src/application/policies/setup_remote_facts_policy.ts`](../src/application/policies/setup_remote_facts_policy.ts) · [`src/application/usecases/setup/prepare_setup_pat_intent_use_case.ts`](../src/application/usecases/setup/prepare_setup_pat_intent_use_case.ts) · [`src/application/usecases/setup/audit_configured_setup_pat_use_case.ts`](../src/application/usecases/setup/audit_configured_setup_pat_use_case.ts) · [`src/application/usecases/setup/verify_web_setup_apply_use_case.ts`](../src/application/usecases/setup/verify_web_setup_apply_use_case.ts) · [`src/application/usecases/setup/verify_setup_pat_bootstrap_use_case.ts`](../src/application/usecases/setup/verify_setup_pat_bootstrap_use_case.ts) · [`src/cli_context.ts`](../src/cli_context.ts) · [`src/cli/setup_command_options.ts`](../src/cli/setup_command_options.ts) · [`src/cli/web_setup_bridge.ts`](../src/cli/web_setup_bridge.ts) · [`src/cli/web_setup_adapters.ts`](../src/cli/web_setup_adapters.ts) · [`src/cli/web_setup_server.ts`](../src/cli/web_setup_server.ts) · [`src/cli/setup_apply_snapshot.ts`](../src/cli/setup_apply_snapshot.ts) · [`src/cli/setup_session_guard.ts`](../src/cli/setup_session_guard.ts) · [`src/application/usecases/setup/setup_wizard_use_case.ts`](../src/application/usecases/setup/setup_wizard_use_case.ts) · [`src/application/usecases/setup/setup_journey_use_case.ts`](../src/application/usecases/setup/setup_journey_use_case.ts) · [`src/application/usecases/setup/setup_credentials_use_case.ts`](../src/application/usecases/setup/setup_credentials_use_case.ts) · [`src/application/policies/setup_questionnaire_policy.ts`](../src/application/policies/setup_questionnaire_policy.ts) · [`src/application/policies/setup_token_permission_policy.ts`](../src/application/policies/setup_token_permission_policy.ts) · [`src/application/policies/setup_configuration_plan.ts`](../src/application/policies/setup_configuration_plan.ts) · [`src/application/policies/setup_pat_creation_url_policy.ts`](../src/application/policies/setup_pat_creation_url_policy.ts) · [`src/infrastructure/composition/setup_credentials_composition_root.ts`](../src/infrastructure/composition/setup_credentials_composition_root.ts) · [`scripts/validate-npm-package.cjs`](../scripts/validate-npm-package.cjs) · [`scripts/render-web-setup-component.cjs`](../scripts/render-web-setup-component.cjs) - Tests: [`src/__tests__/cli.test.ts`](../src/__tests__/cli.test.ts) · [`src/__tests__/cli_context_root.test.ts`](../src/__tests__/cli_context_root.test.ts) · [`src/cli/__tests__/web_setup_bridge.test.ts`](../src/cli/__tests__/web_setup_bridge.test.ts) · [`src/cli/__tests__/web_setup_adapters.test.ts`](../src/cli/__tests__/web_setup_adapters.test.ts) · [`src/cli/__tests__/web_setup_palette.test.ts`](../src/cli/__tests__/web_setup_palette.test.ts) · [`src/cli/__tests__/web_setup_ui_helpers.test.ts`](../src/cli/__tests__/web_setup_ui_helpers.test.ts) · [`src/cli/__tests__/web_setup_browser_session.test.ts`](../src/cli/__tests__/web_setup_browser_session.test.ts) · [`src/cli/__tests__/web_setup_server.test.ts`](../src/cli/__tests__/web_setup_server.test.ts) · [`src/cli/__tests__/web_setup_browser_open.test.ts`](../src/cli/__tests__/web_setup_browser_open.test.ts) · [`src/cli/__tests__/setup_apply_snapshot.test.ts`](../src/cli/__tests__/setup_apply_snapshot.test.ts) · [`src/cli/__tests__/setup_session_guard.test.ts`](../src/cli/__tests__/setup_session_guard.test.ts) · [`src/cli/__tests__/setup_command_options.test.ts`](../src/cli/__tests__/setup_command_options.test.ts) · [`src/cli/__tests__/web_setup_components.test.ts`](../src/cli/__tests__/web_setup_components.test.ts) · [`src/architecture/__tests__/web_setup_boundaries.test.ts`](../src/architecture/__tests__/web_setup_boundaries.test.ts) · [`src/application/usecases/setup/__tests__/prepare_setup_pat_intent_use_case.test.ts`](../src/application/usecases/setup/__tests__/prepare_setup_pat_intent_use_case.test.ts) · [`src/application/usecases/setup/__tests__/audit_configured_setup_pat_use_case.test.ts`](../src/application/usecases/setup/__tests__/audit_configured_setup_pat_use_case.test.ts) · [`src/application/usecases/setup/__tests__/verify_web_setup_apply_use_case.test.ts`](../src/application/usecases/setup/__tests__/verify_web_setup_apply_use_case.test.ts) · [`src/application/usecases/setup/__tests__/verify_setup_pat_bootstrap_use_case.test.ts`](../src/application/usecases/setup/__tests__/verify_setup_pat_bootstrap_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts) · [`src/application/policies/__tests__/setup_questionnaire_policy.test.ts`](../src/application/policies/__tests__/setup_questionnaire_policy.test.ts) · [`src/application/policies/__tests__/setup_token_permission_policy.test.ts`](../src/application/policies/__tests__/setup_token_permission_policy.test.ts) · [`src/architecture/__tests__/setup_doctor_boundaries.test.ts`](../src/architecture/__tests__/setup_doctor_boundaries.test.ts) - User documentation: [`README.md`](../README.md) · [`docs/how-to-use.mdx`](../docs/how-to-use.mdx) · [`docs/authentication.mdx`](../docs/authentication.mdx) · [`docs/configuration.mdx`](../docs/configuration.mdx) · [`docs/configuration-checklist.mdx`](../docs/configuration-checklist.mdx) · [`docs/development/architecture.mdx`](../docs/development/architecture.mdx) · [`docs/dependency-rules.md`](../docs/dependency-rules.md) · [`docs/security-operations/operations/troubleshooting.mdx`](../docs/security-operations/operations/troubleshooting.mdx) · [`docs/security-operations/operations/provisioning.mdx`](../docs/security-operations/operations/provisioning.mdx) diff --git a/specs/catalog.json b/specs/catalog.json index 07ce45b3c..b87a344d8 100644 --- a/specs/catalog.json +++ b/specs/catalog.json @@ -771,6 +771,7 @@ "web/src/App.svelte", "web/src/session/setupSession.ts", "web/src/components/ActionButton.svelte", + "web/src/components/PairingPanel.svelte", "web/src/components/ContextPanel.svelte", "web/src/components/PromptCard.svelte", "web/src/components/QuestionPrompt.svelte", diff --git a/specs/local-web-setup-assistant.md b/specs/local-web-setup-assistant.md index f03792cb9..3548d80f1 100644 --- a/specs/local-web-setup-assistant.md +++ b/specs/local-web-setup-assistant.md @@ -219,10 +219,15 @@ exactly what completed and what remains. mode does not require a TTY: the browser is the interactive surface, and a printed local URL is available if automatic opening is unavailable. 2. Bind `127.0.0.1:0`, record the assigned port, create an unpredictable - one-run session key and first controller lease in process memory, and open - the default browser to `http://127.0.0.1:/#setup-key=`. - If opening fails, print that private URL and instructions; serving - continues. The key-bearing URL MUST NOT enter accumulated diagnostic logs. + one-run session key, a separate 16-hex-character pairing code, and first + controller lease in process memory. Print the pairing code only in the + terminal, without adding it to accumulated diagnostics, then open the + default browser to the public `http://127.0.0.1:/` URL. The initial + page asks for the code before any setup state is shown. A same-origin POST + exchanges it for the session key held only in browser memory; five invalid + attempts lock pairing until a new setup run. If opening fails, print the + public URL and instructions; serving continues. Neither code nor key may + appear in URL, history, cookies, browser storage, or accumulated logs. If binding or packaged assets fail, stop without a partial UI and suggest `copilot setup`. 3. Show the six stages already used by terminal setup. Import one immutable @@ -637,19 +642,22 @@ for the temporary setup PAT and to bot-PAT rotation guidance separately. 1. **Boundary:** the web server is loopback-only, but TCP loopback does not identify or isolate the launching OS user: another local user can connect - to the port. A cryptographically random URL-fragment key is required for - every API read and mutation, including bootstrap and takeover. Possession - of the private URL grants local session access, so it must not be shared. - A malicious process that can read that URL, browser extensions with page - access, or a compromised browser remain outside this boundary. The - product must say so honestly. + to the port. The browser must enter a cryptographically random code shown + only in the launching terminal; the server exchanges it for a one-run + session key. That key is required for every API read and mutation, including + bootstrap and takeover. Possession of the pairing code grants local session + access, so it must not be shared. A malicious process that can read the + terminal, browser extensions with page access, or a compromised browser + remain outside this boundary. The product must say so honestly. 2. **Request defense:** reject `Host` not exactly `127.0.0.1:`, proxy/forwarded host headers, unexpected `Origin`/`Referer` on mutations, cross-site Fetch Metadata, unsupported methods/content types, and requests over size/time limits. No wildcard CORS or credentials cross-origin. - The URL-fragment session key is not sent in an HTTP URL, stored in a - cookie/localStorage/sessionStorage, or returned to unauthenticated callers; - the browser sends it in a custom header to every API route. In addition, + The pairing endpoint accepts only same-origin JSON POST, bounds wrong-code + attempts, and returns the session key only for the correct code. The code + and key are not sent in an HTTP URL or stored in a cookie/localStorage/ + sessionStorage; the browser sends the key in a custom header to every + subsequent API route. In addition, state-changing requests require a separate one-run, cryptographically random controller capability in a custom header plus a revision check; that capability is delivered only by an authenticated same-origin no-store @@ -729,7 +737,7 @@ existing CLI tests are retained, not re-counted as new web evidence. | CLI/packaging/workflow contracts | 10 | flag combinations, browser-open fallback, asset manifest, npm pack/global install, unchanged Action/API bundles | | UI/accessibility/localization/content | 18 | pending/action/blocked/partial/complete, plan diff, narrow/zoom/keyboard/focus/no-color, both palettes/system toggle and contrast, English fallback, escaping | | Integration/compatibility/recovery | 12 | terminal-web parity, manual/environment/dry-run, drift, partial write, doctor reconciliation | -| Security/abuse | 18 | Host/Origin/CSRF, private launch-key enforcement on every API route, CORS, replay, path traversal, XSS/CSP, secret leaks, no GET mutation, body/time/connection limits, atomic lock publication | +| Security/abuse | 18 | Host/Origin/CSRF, terminal pairing and attempt cap, session-key enforcement on every other API route, CORS, replay, path traversal, XSS/CSP, secret leaks, no GET mutation, body/time/connection limits, atomic lock publication | | **Total** | **102** | No double counting | Within the 18 UI cases, cover at least one render/interaction for each prompt @@ -805,16 +813,16 @@ help for `--web` explains local-only scope and the `--non-interactive` conflict. 11. Given a Secret write succeeds and a later setup operation fails, the result lists the Secret's name/scope as possibly active, never prints its value, and requires inspection before retry or bot PAT deletion. -12. Given hostile Host/Origin/cross-site requests, a missing/incorrect private - launch key on bootstrap, state, or mutations, a missing/replayed controller +12. Given hostile Host/Origin/cross-site requests, missing/incorrect pairing + codes or a missing session key on bootstrap, state, or mutations, a missing/replayed controller capability, excess simultaneous clients, path traversal, oversized body, or injected account/provider text, the server rejects/escapes it without mutation or secret disclosure. A failed lock write leaves no published lock; an orphaned lock is never removed automatically. -13. Given a browser refresh, the same live process restores redacted state - only; PAT values, controller capability, and plan approval are never stored - in browser storage or URLs. The private launch key remains only in the - URL fragment and browser memory. The final page never calls local disposal +13. Given a browser refresh, the user re-enters the terminal pairing code and + the same live process restores redacted state only; PAT values, controller + capability, and plan approval are never stored in browser storage or URLs. + Neither the code nor the session key appears in browser history. The final page never calls local disposal GitHub revocation or Secret installation verified Action health. 14. Given narrow width, 200% zoom, keyboard-only and reduced-motion settings, every primary state and recovery action remains understandable without diff --git a/src/__tests__/cli.test.ts b/src/__tests__/cli.test.ts index b3a3c02b2..1240c2812 100644 --- a/src/__tests__/cli.test.ts +++ b/src/__tests__/cli.test.ts @@ -32,7 +32,7 @@ jest.mock('../cli/setup_session_guard', () => ({ })); jest.mock('../cli/web_setup_server', () => ({ - startWebSetupServer: jest.fn(async () => ({ url: 'http://127.0.0.1:40000/', launchUrl: 'http://127.0.0.1:40000/#setup-key=test', closed: Promise.resolve(), close: jest.fn() })), + startWebSetupServer: jest.fn(async () => ({ url: 'http://127.0.0.1:40000/', pairingCode: '0123456789abcdef', closed: Promise.resolve(), close: jest.fn() })), openWebSetupBrowser: jest.fn(), })); @@ -521,9 +521,9 @@ describe('CLI', () => { it('uses one browser session through PAT, plan, revalidation, and Apply', async () => { await program.parseAsync(['node', 'cli', 'setup', '--web', '--pr-approval-mode', 'off', '--skip-secrets']); expect(startWebSetupServer).toHaveBeenCalledTimes(1); - expect(openWebSetupBrowser).toHaveBeenCalledWith('http://127.0.0.1:40000/#setup-key=test'); + expect(openWebSetupBrowser).toHaveBeenCalledWith('http://127.0.0.1:40000/'); const { logInfo } = require('../utils/logger'); - expect(logInfo).toHaveBeenCalledWith(expect.stringContaining('#setup-key=test'), false, undefined, true); + expect(logInfo).toHaveBeenCalledWith(expect.stringContaining('0123456789abcdef'), false, undefined, true); expect(ask.mock.calls.map(call => call[0].title)).toEqual(expect.arrayContaining([ 'Confirm this repository', 'How will you provide your setup PAT?', 'Temporary setup PAT', 'Review your setup plan', 'Apply this setup now?', diff --git a/src/cli/__tests__/web_setup_bridge.test.ts b/src/cli/__tests__/web_setup_bridge.test.ts index 09eeb2c69..d5f31f452 100644 --- a/src/cli/__tests__/web_setup_bridge.test.ts +++ b/src/cli/__tests__/web_setup_bridge.test.ts @@ -56,6 +56,22 @@ describe('WebSetupBridge', () => { expect(bridge.snapshot().repository).toBe('owner/repo'); }); + test('a failing subscriber is detached without losing prompts or blocking healthy observers', async () => { + const bridge = new WebSetupBridge('owner/repo'); + const failed = jest.fn(() => { throw new Error('Observer failed'); }); + const seen: number[] = []; + bridge.subscribe(failed); + bridge.subscribe(view => seen.push(view.revision)); + const pending = bridge.ask({ kind: 'text', title: 'Continue setup' }); + const revision = bridge.snapshot().promptRevision!; + expect(seen).toEqual([revision]); + expect(bridge.answer(revision, 'yes')).toBe(true); + expect(await pending).toBe('yes'); + bridge.message('Next step'); + expect(failed).toHaveBeenCalledTimes(1); + expect(seen).toEqual([revision, revision + 1, revision + 2]); + }); + test('only one semantic decision can be pending at a time', async () => { const bridge = new WebSetupBridge('owner/repo'); const pending = bridge.ask({ kind: 'choice', title: 'A', choices: ['yes'] }); diff --git a/src/cli/__tests__/web_setup_browser_session.test.ts b/src/cli/__tests__/web_setup_browser_session.test.ts index 6f0c3c796..f98fd5d9f 100644 --- a/src/cli/__tests__/web_setup_browser_session.test.ts +++ b/src/cli/__tests__/web_setup_browser_session.test.ts @@ -1,4 +1,4 @@ -import { createSetupSession, sessionKeyFromFragment } from '../../../web/src/session/setupSession'; +import { createSetupSession } from '../../../web/src/session/setupSession'; import type { WebSetupView } from '../../application/contracts/web_setup_view'; jest.mock('svelte/store', () => ({ @@ -17,14 +17,117 @@ function response(body: unknown, status = 200): Response { } describe('browser session transport', () => { + const TEST_SESSION_KEY = 'a'.repeat(64); const view: WebSetupView = { revision: 3, promptRevision: 7, repository: 'owner/repo', prompt: { kind: 'secret', title: 'Setup PAT' } }; const originalFetch = globalThis.fetch; afterEach(() => { globalThis.fetch = originalFetch; }); - test('reads the one-run key from the fragment without putting it in observable session state', () => { - expect(sessionKeyFromFragment('#setup-key=private-key')).toBe('private-key'); - expect(sessionKeyFromFragment('#other=value')).toBe(''); + test('starts unpaired, and does not contact the API until terminal pairing', async () => { + globalThis.fetch = jest.fn() as typeof fetch; + const session = createSetupSession(); + let state = {} as { paired: boolean }; + session.subscribe(next => { state = next; }); + await session.connect(); + await session.refresh(); + expect(state.paired).toBe(false); + expect(globalThis.fetch).not.toHaveBeenCalled(); + }); + + test('pairs through same-origin POST and keeps code and key out of observable state', async () => { + const requests: Array<{ path: string; options?: RequestInit }> = []; + globalThis.fetch = jest.fn(async (path: string, options?: RequestInit) => { + requests.push({ path, options }); + if (path === '/api/pair') return response({ sessionKey: TEST_SESSION_KEY }); + if (path === '/api/bootstrap') return response({ controller: true, capability: 'controller', takeoverTicket: 'ticket' }); + if (path === '/api/state') return response(view); + throw new Error('Unexpected route'); + }) as typeof fetch; + const session = createSetupSession(); + let latest = ''; + session.subscribe(next => { latest = JSON.stringify(next); }); + await session.pair(' 0123456789ABCDEF '); + expect(requests.map(request => request.path)).toEqual(['/api/pair', '/api/bootstrap', '/api/state']); + expect(JSON.parse(String(requests[0].options?.body))).toEqual({ code: '0123456789abcdef' }); + expect(requests[1].options?.headers).toEqual(expect.objectContaining({ 'X-Setup-Session-Key': TEST_SESSION_KEY })); + expect(latest).toContain('"paired":true'); + expect(latest).not.toContain('0123456789abcdef'); + expect(latest).not.toContain(TEST_SESSION_KEY); + }); + + test('invalid pairing response never enables the setup UI', async () => { + globalThis.fetch = jest.fn(async () => response({ sessionKey: 'not-a-key' })) as typeof fetch; + const session = createSetupSession(); + let latest = ''; + session.subscribe(next => { latest = JSON.stringify(next); }); + await session.pair('0123456789abcdef'); + expect(latest).toContain('Invalid local pairing response'); + expect(latest).toContain('"paired":false'); + expect(globalThis.fetch).toHaveBeenCalledTimes(1); + }); + + test('failed bootstrap after pairing returns to unpaired state', async () => { + globalThis.fetch = jest.fn(async (path: string) => path === '/api/pair' + ? response({ sessionKey: TEST_SESSION_KEY }) : response({ error: 'No session' }, 403)) as typeof fetch; + const session = createSetupSession(); + let latest = ''; + session.subscribe(next => { latest = JSON.stringify(next); }); + await session.pair('0123456789abcdef'); + expect(latest).toContain('"paired":false'); + expect(latest).toContain('pair again'); + }); + + test('incorrect code is shown as an error without disclosing the code', async () => { + globalThis.fetch = jest.fn(async () => response({ error: 'Incorrect pairing code.' }, 403)) as typeof fetch; + const session = createSetupSession(); + let latest = ''; + session.subscribe(next => { latest = JSON.stringify(next); }); + await session.pair('0123456789abcdef'); + expect(latest).toContain('Incorrect pairing code.'); + expect(latest).toContain('"paired":false'); + expect(latest).not.toContain('0123456789abcdef'); + }); + + test('pairing does not run concurrently or repeat after success', async () => { + let resolvePair!: (value: Response) => void; + const pendingPair = new Promise(resolve => { resolvePair = resolve; }); + const requests: string[] = []; + globalThis.fetch = jest.fn(async (path: string) => { + requests.push(path); + if (path === '/api/pair') return pendingPair; + if (path === '/api/bootstrap') return response({ controller: true, capability: 'controller', takeoverTicket: 'ticket' }); + if (path === '/api/state') return response(view); + throw new Error('Unexpected route'); + }) as typeof fetch; + const session = createSetupSession(); + const first = session.pair('0123456789abcdef'); + await session.pair('0123456789abcdef'); + expect(requests).toEqual(['/api/pair']); + resolvePair(response({ sessionKey: TEST_SESSION_KEY })); + await first; + await session.pair('0123456789abcdef'); + expect(requests).toEqual(['/api/pair', '/api/bootstrap', '/api/state']); + }); + + test('pairing failures use a bounded generic message when the server omits one', async () => { + globalThis.fetch = jest.fn(async () => response({}, 403)) as typeof fetch; + const session = createSetupSession(); + let latest = ''; + session.subscribe(next => { latest = JSON.stringify(next); }); + await session.pair('0123456789abcdef'); + expect(latest).toContain('Pairing was rejected.'); + expect(latest).toContain('"paired":false'); + }); + + test('a non-Error pairing failure stays generic and keeps the page unpaired', async () => { + globalThis.fetch = jest.fn(async () => { throw 'network unavailable'; }) as typeof fetch; + const session = createSetupSession(); + let latest = ''; + session.subscribe(next => { latest = JSON.stringify(next); }); + await session.pair('0123456789abcdef'); + expect(latest).toContain('Could not pair this browser.'); + expect(latest).not.toContain('network unavailable'); + expect(latest).toContain('"paired":false'); }); test('bootstrap and revision-bound submission keep the PAT out of observable state', async () => { @@ -66,7 +169,7 @@ describe('browser session transport', () => { throw new Error('Unexpected route'); }) as typeof fetch; - const session = createSetupSession(); + const session = createSetupSession(TEST_SESSION_KEY); let controller = false; session.subscribe(state => { controller = state.controller; }); await session.connect(); @@ -89,7 +192,7 @@ describe('browser session transport', () => { throw new Error('Unexpected route'); }) as typeof fetch; - const session = createSetupSession(); + const session = createSetupSession(TEST_SESSION_KEY); let latest = ''; session.subscribe(state => { latest = JSON.stringify(state); }); await session.connect(); @@ -115,7 +218,7 @@ describe('browser session transport', () => { throw new Error('Unexpected route'); }) as typeof fetch; - const session = createSetupSession(); + const session = createSetupSession(TEST_SESSION_KEY); let latest: { controller: boolean; busy: boolean } | undefined; session.subscribe(state => { latest = state; }); await session.connect(); @@ -134,7 +237,7 @@ describe('browser session transport', () => { throw new Error('Unexpected route'); }) as typeof fetch; - const session = createSetupSession(); + const session = createSetupSession(TEST_SESSION_KEY); let latest = ''; session.subscribe(state => { latest = JSON.stringify(state); }); await session.connect(); @@ -154,7 +257,7 @@ describe('browser session transport', () => { throw new Error('Unexpected route'); }) as typeof fetch; - const session = createSetupSession(); + const session = createSetupSession(TEST_SESSION_KEY); let latest: { busy: boolean; view?: WebSetupView } | undefined; session.subscribe(state => { latest = state; }); await session.connect(); @@ -170,7 +273,7 @@ describe('browser session transport', () => { throw new Error('Unexpected route'); }) as typeof fetch; - const session = createSetupSession(); + const session = createSetupSession(TEST_SESSION_KEY); let latest = ''; session.subscribe(state => { latest = JSON.stringify(state); }); await session.connect(); @@ -187,7 +290,7 @@ describe('browser session transport', () => { throw new Error('Unexpected route'); }) as typeof fetch; - const session = createSetupSession(); + const session = createSetupSession(TEST_SESSION_KEY); let latest = ''; session.subscribe(state => { latest = JSON.stringify(state); }); await session.connect(); @@ -207,7 +310,7 @@ describe('browser session transport', () => { throw new Error('Unexpected route'); }) as typeof fetch; - const session = createSetupSession(); + const session = createSetupSession(TEST_SESSION_KEY); let controller = true; session.subscribe(state => { controller = state.controller; }); await session.connect(); @@ -228,7 +331,7 @@ describe('browser session transport', () => { throw new Error('Unexpected route'); }) as typeof fetch; - const session = createSetupSession(); + const session = createSetupSession(TEST_SESSION_KEY); await session.connect(); const first = session.submit(7, 'first'); await session.submit(7, 'duplicate'); @@ -249,7 +352,7 @@ describe('browser session transport', () => { throw new Error('Unexpected route'); }) as typeof fetch; - const session = createSetupSession(); + const session = createSetupSession(TEST_SESSION_KEY); const first = session.refresh(); await session.refresh(); expect(requests).toEqual(['/api/state']); @@ -267,7 +370,7 @@ describe('browser session transport', () => { throw new Error('Unexpected route'); }) as typeof fetch; - const session = createSetupSession(); + const session = createSetupSession(TEST_SESSION_KEY); let latest: { controller: boolean; busy: boolean } | undefined; session.subscribe(state => { latest = state; }); await session.connect(); @@ -280,7 +383,7 @@ describe('browser session transport', () => { test('failed bootstrap and state requests are reported without claiming a live session', async () => { globalThis.fetch = jest.fn(async (path: string) => path === '/api/bootstrap' ? response({ error: 'Unavailable' }, 503) : response(view)) as typeof fetch; - const session = createSetupSession(); + const session = createSetupSession(TEST_SESSION_KEY); let latest = ''; session.subscribe(state => { latest = JSON.stringify(state); }); await session.connect(); @@ -288,7 +391,9 @@ describe('browser session transport', () => { expect(JSON.parse(latest).controller).toBe(false); globalThis.fetch = jest.fn(async () => response({ error: 'Unavailable' }, 503)) as typeof fetch; - await session.refresh(); + const stillPaired = createSetupSession(TEST_SESSION_KEY); + stillPaired.subscribe(state => { latest = JSON.stringify(state); }); + await stillPaired.refresh(); expect(latest).toContain('Connection lost'); expect(JSON.parse(latest).view).toBeUndefined(); }); @@ -305,7 +410,7 @@ describe('browser session transport', () => { throw new Error('Unexpected route'); }) as typeof fetch; - const session = createSetupSession(); + const session = createSetupSession(TEST_SESSION_KEY); let latest: { controller: boolean; error: string; view?: WebSetupView } | undefined; session.subscribe(state => { latest = state; }); await session.connect(); @@ -326,7 +431,7 @@ describe('browser session transport', () => { if (path === '/api/cancel') return response({ error: 'Control moved to another tab.' }, 403); throw new Error('Unexpected route'); }) as typeof fetch; - const session = createSetupSession(); + const session = createSetupSession(TEST_SESSION_KEY); let latest: { controller: boolean; busy: boolean } | undefined; session.subscribe(state => { latest = state; }); await session.connect(); @@ -337,6 +442,6 @@ describe('browser session transport', () => { test('failure to close the browser session can be handled in the terminal', async () => { globalThis.fetch = jest.fn(async () => { throw new Error('CLI stopped'); }) as typeof fetch; - await expect(createSetupSession().close()).resolves.toBeUndefined(); + await expect(createSetupSession(TEST_SESSION_KEY).close()).resolves.toBeUndefined(); }); }); diff --git a/src/cli/__tests__/web_setup_components.test.ts b/src/cli/__tests__/web_setup_components.test.ts index 601379bd0..1fa141dbb 100644 --- a/src/cli/__tests__/web_setup_components.test.ts +++ b/src/cli/__tests__/web_setup_components.test.ts @@ -10,6 +10,14 @@ function markup(name: string, props: Record): string { const noOp = async (): Promise => undefined; describe('web setup component semantics', () => { + test('pairing screen explains terminal code without exposing a key in the URL', () => { + const html = markup('PairingPanel', { busy: false }); + expect(html).toContain('Pair this browser'); + expect(html).toContain('Pairing code from terminal'); + expect(html).toContain('16-character pairing code'); + expect(html).toContain('After refreshing'); + expect(html).not.toContain('setup-key'); + }); test.each([ ['complete', 'Your configuration was applied', 'not revoked automatically'], ['dry-run', 'No changes were made', 'did not begin applying'], diff --git a/src/cli/__tests__/web_setup_server.test.ts b/src/cli/__tests__/web_setup_server.test.ts index fe77ee979..39c6b0a0e 100644 --- a/src/cli/__tests__/web_setup_server.test.ts +++ b/src/cli/__tests__/web_setup_server.test.ts @@ -7,9 +7,15 @@ import { WebSetupBridge } from '../web_setup_bridge'; import { startWebSetupServer, type WebSetupServer } from '../web_setup_server'; const sessionKeys = new Map(); -const registerSession = (session: WebSetupServer): void => { - const launch = new URL(session.launchUrl); - sessionKeys.set(launch.origin, new URLSearchParams(launch.hash.slice(1)).get('setup-key')!); +const registerSession = async (session: WebSetupServer): Promise => { + const origin = new URL(session.url).origin; + const paired = await globalThis.fetch(`${session.url}api/pair`, { + method: 'POST', headers: { Origin: origin, 'Content-Type': 'application/json' }, + body: JSON.stringify({ code: session.pairingCode }), + }); + expect(paired.status).toBe(200); + const { sessionKey } = await paired.json() as { sessionKey: string }; + sessionKeys.set(origin, sessionKey); }; const fetch: typeof globalThis.fetch = (input, init) => { const url = new URL(input instanceof Request ? input.url : String(input)); @@ -34,7 +40,7 @@ describe('local web setup server', () => { writeFileSync(join(root, 'assets', 'app.css'), ':root { color: black; }'); bridge = new WebSetupBridge('owner/repo'); server = await startWebSetupServer(bridge, root); - registerSession(server); + await registerSession(server); }); afterEach(async () => { if (server) await server.close(); sessionKeys.clear(); rmSync(root, { recursive: true, force: true }); }); @@ -54,11 +60,13 @@ describe('local web setup server', () => { expect((await fetch(`${server.url}assets/%2e%2e/index.html`)).status).toBe(404); }); - test('a private launch fragment is required before any local API state or controller lease is exposed', async () => { - const launch = new URL(server.launchUrl); - const key = new URLSearchParams(launch.hash.slice(1)).get('setup-key'); + test('the public loopback URL contains no secret and API access requires terminal pairing', async () => { + const launch = new URL(server.url); + const key = sessionKeys.get(launch.origin)!; + expect(launch.hash).toBe(''); + expect(launch.search).toBe(''); + expect(server.pairingCode).toMatch(/^[a-f0-9]{16}$/); expect(key).toMatch(/^[a-f0-9]{64}$/); - expect(launch.origin).toBe(new URL(server.url).origin); for (const path of ['api/bootstrap', 'api/state']) { const missing = await globalThis.fetch(`${server.url}${path}`); expect(missing.status).toBe(403); @@ -76,7 +84,8 @@ describe('local web setup server', () => { const other = await startWebSetupServer(new WebSetupBridge('owner/other'), root); try { const firstKey = sessionKeys.get(new URL(server.url).origin)!; - const otherKey = new URLSearchParams(new URL(other.launchUrl).hash.slice(1)).get('setup-key')!; + await registerSession(other); + const otherKey = sessionKeys.get(new URL(other.url).origin)!; expect(otherKey).not.toBe(firstKey); expect((await globalThis.fetch(`${other.url}api/bootstrap`, { headers: { 'X-Setup-Session-Key': firstKey }, @@ -87,6 +96,35 @@ describe('local web setup server', () => { } finally { await other.close(); } }); + test('pairing rejects missing, cross-origin, and incorrect codes without exposing the session key', async () => { + const endpoint = `${server.url}api/pair`; + const origin = new URL(server.url).origin; + const post = (code: unknown, requestOrigin = origin) => globalThis.fetch(endpoint, { + method: 'POST', headers: { Origin: requestOrigin, 'Content-Type': 'application/json' }, body: JSON.stringify({ code }), + }); + expect((await post(server.pairingCode, 'https://evil.example')).status).toBe(403); + expect((await post(undefined)).status).toBe(403); + const wrong = await post('0'.repeat(16)); + expect(wrong.status).toBe(403); + expect(await wrong.text()).not.toContain(sessionKeys.get(origin)!); + expect((await post(server.pairingCode)).status).toBe(200); + }); + + test('five incorrect pairing attempts lock out even the valid code for this run', async () => { + const endpoint = `${server.url}api/pair`; + const origin = new URL(server.url).origin; + const post = (code: string) => globalThis.fetch(endpoint, { + method: 'POST', headers: { Origin: origin, 'Content-Type': 'application/json' }, body: JSON.stringify({ code }), + }); + for (let attempt = 0; attempt < 5; attempt += 1) { + expect((await post('0'.repeat(16))).status).toBe(403); + } + const locked = await post(server.pairingCode); + expect(locked.status).toBe(429); + expect(await locked.text()).not.toContain(sessionKeys.get(origin)!); + expect((await fetch(`${server.url}api/bootstrap`)).status).toBe(200); + }); + test('bounds simultaneous loopback connections', async () => { const { port } = new URL(server.url); const sockets: Socket[] = []; @@ -179,7 +217,7 @@ describe('local web setup server', () => { const secondBridge = new WebSetupBridge('owner/repo'); const secondServer = await startWebSetupServer(secondBridge, root); - registerSession(secondServer); + await registerSession(secondServer); try { const nextCapability = (await (await fetch(`${secondServer.url}api/bootstrap`)).json() as { capability: string }).capability; secondBridge.setJourney({ repository: 'owner/repo', position: 6, total: 6, current: 'Apply', complete: [], pending: [], mutationStarted: true, choiceReviewPass: 1 }); @@ -308,7 +346,7 @@ describe('local web setup server', () => { try { bridge = new WebSetupBridge('owner/repo'); server = await startWebSetupServer(bridge, root); - registerSession(server); + await registerSession(server); const pending = bridge.ask({ kind: 'secret', title: 'Setup PAT' }); const revision = bridge.snapshot().promptRevision!; await jest.advanceTimersByTimeAsync(30 * 60 * 1000); @@ -328,7 +366,7 @@ describe('local web setup server', () => { try { bridge = new WebSetupBridge('owner/repo'); server = await startWebSetupServer(bridge, root); - registerSession(server); + await registerSession(server); bridge.setJourney({ repository: 'owner/repo', position: 6, total: 6, current: 'Apply', complete: [], pending: [], mutationStarted: true, choiceReviewPass: 1 }); await jest.advanceTimersByTimeAsync(4 * 60 * 60 * 1000); @@ -345,7 +383,7 @@ describe('local web setup server', () => { try { bridge = new WebSetupBridge('owner/repo'); server = await startWebSetupServer(bridge, root); - registerSession(server); + await registerSession(server); const applying = { repository: 'owner/repo', position: 6, total: 6, current: 'Apply', complete: [] as string[], pending: [] as string[], mutationStarted: true, choiceReviewPass: 1 }; bridge.setJourney(applying); @@ -366,7 +404,7 @@ describe('local web setup server', () => { try { bridge = new WebSetupBridge('owner/repo'); server = await startWebSetupServer(bridge, root); - registerSession(server); + await registerSession(server); bridge.finish('complete', 'done'); await jest.advanceTimersByTimeAsync(10 * 60 * 1000); await expect(server.closed).resolves.toBeUndefined(); diff --git a/src/cli/commands/setup.ts b/src/cli/commands/setup.ts index cd96e501b..7ffa99115 100644 --- a/src/cli/commands/setup.ts +++ b/src/cli/commands/setup.ts @@ -125,9 +125,10 @@ export function registerSetupCommand(program: Command): void { webBridge.setRepository(`${gitInfo.owner}/${gitInfo.repo}`); webBridge.setJourney(buildSetupJourneyView(`${gitInfo.owner}/${gitInfo.repo}`, 'repository', false)); webServer = await startWebSetupServer(webBridge); - logInfo(`🌐 Private local setup assistant: ${webServer.launchUrl}`, false, undefined, true); - logInfo('If the browser does not open, copy this URL into a browser on this computer. The terminal setup remains available with copilot setup.'); - openWebSetupBrowser(webServer.launchUrl); + logInfo(`🌐 Local setup assistant: ${webServer.url}`); + logInfo(`🔑 Browser pairing code: ${webServer.pairingCode}`, false, undefined, true); + logInfo('If the browser does not open, copy this URL into a browser on this computer, then enter the pairing code shown above. The terminal setup remains available with copilot setup.'); + openWebSetupBrowser(webServer.url); } if (!options.nonInteractive) { journey = new SetupJourneyUseCase(`${gitInfo.owner}/${gitInfo.repo}`, diff --git a/src/cli/web_setup_bridge.ts b/src/cli/web_setup_bridge.ts index 2b19783ae..0df008e2e 100644 --- a/src/cli/web_setup_bridge.ts +++ b/src/cli/web_setup_bridge.ts @@ -98,7 +98,10 @@ export class WebSetupBridge { private publish(change: Partial): void { this.revision += 1; this.view = { ...this.view, ...change, revision: this.revision }; - for (const listener of this.subscribers) listener(this.view); + for (const listener of this.subscribers) { + try { listener(this.view); } + catch { this.subscribers.delete(listener); /* Observers cannot abort a setup decision. */ } + } } } diff --git a/src/cli/web_setup_server.ts b/src/cli/web_setup_server.ts index 9e4b24296..cd9457639 100644 --- a/src/cli/web_setup_server.ts +++ b/src/cli/web_setup_server.ts @@ -11,7 +11,7 @@ const CSP = "default-src 'none'; script-src 'self'; style-src 'self'; img-src 's export interface WebSetupServer { readonly url: string; - readonly launchUrl: string; + readonly pairingCode: string; readonly closed: Promise; close(): Promise; } @@ -19,6 +19,8 @@ export interface WebSetupServer { /** Transport only: setup policy and credential decisions live behind the bridge. */ export async function startWebSetupServer(bridge: WebSetupBridge, assets = join(__dirname, '..', 'web')): Promise { const sessionKey = randomBytes(32); + const pairingCode = randomBytes(8); + let failedPairings = 0; const assetRoot = await realpath(assets); if (!(await realpath(join(assetRoot, 'index.html'))).startsWith(`${assetRoot}${sep}`)) { throw new Error('Local setup index must be inside its packaged asset directory.'); @@ -66,8 +68,20 @@ export async function startWebSetupServer(bridge: WebSetupBridge, assets = join( respond(response, 403, { error: 'Invalid request origin.' }); return; } - if (request.url?.startsWith('/api/') && !authorizedSessionKey(request.headers['x-setup-session-key'], sessionKey)) { - respond(response, 403, { error: 'Open the private setup URL printed by the CLI.' }); + if (request.method === 'POST' && request.url === '/api/pair') { + if (request.headers['content-type'] !== 'application/json') { respond(response, 415, { error: 'JSON required.' }); return; } + if (failedPairings >= 5) { respond(response, 429, { error: 'Too many pairing attempts. Restart setup.' }); return; } + const body = await readJson(request); + if (!matchesHexSecret(body.code, pairingCode)) { + failedPairings += 1; + respond(response, 403, { error: 'Incorrect pairing code. Check the terminal.' }); + return; + } + respond(response, 200, { sessionKey: sessionKey.toString('hex') }); + return; + } + if (request.url?.startsWith('/api/') && !matchesHexSecret(request.headers['x-setup-session-key'], sessionKey)) { + respond(response, 403, { error: 'Pair this browser using the code printed by the CLI.' }); return; } if (request.method === 'GET' && request.url === '/api/bootstrap') { @@ -163,7 +177,6 @@ export async function startWebSetupServer(bridge: WebSetupBridge, assets = join( const address = server.address(); if (!address || typeof address === 'string') throw new Error('Unable to bind local setup server.'); const url = `http://127.0.0.1:${address.port}/`; - const launchUrl = `${url}#setup-key=${sessionKey.toString('hex')}`; const close = async (): Promise => { if (closing) return closed; closing = true; @@ -185,11 +198,11 @@ export async function startWebSetupServer(bridge: WebSetupBridge, assets = join( const unsubscribe = bridge.subscribe(view => { if (view.outcome && !resultTimer) resultTimer = setTimeout(() => void close(), 10 * 60 * 1000); }); - return { url, launchUrl, closed, close }; + return { url, pairingCode: pairingCode.toString('hex'), closed, close }; } -function authorizedSessionKey(value: string | string[] | undefined, expected: Buffer): boolean { - return typeof value === 'string' && /^[a-f0-9]{64}$/.test(value) +function matchesHexSecret(value: unknown, expected: Buffer): boolean { + return typeof value === 'string' && value.length === expected.length * 2 && /^[a-f0-9]+$/.test(value) && timingSafeEqual(Buffer.from(value, 'hex'), expected); } diff --git a/web/src/App.svelte b/web/src/App.svelte index 40c3e7ae0..ca10f374c 100644 --- a/web/src/App.svelte +++ b/web/src/App.svelte @@ -9,11 +9,11 @@ import ContextPanel from './components/ContextPanel.svelte'; import ResultPanel from './components/ResultPanel.svelte'; import WaitingPanel from './components/WaitingPanel.svelte'; + import PairingPanel from './components/PairingPanel.svelte'; const session = createSetupSession(); onMount(() => { - void session.connect(); - const interval = window.setInterval(() => { if (!$session.view?.outcome) void session.refresh(); }, 900); + const interval = window.setInterval(() => { if ($session.paired && !$session.view?.outcome) void session.refresh(); }, 900); return () => window.clearInterval(interval); }); @@ -36,7 +36,7 @@
                                                          - + {#if $session.paired}{/if} {#if $session.view?.journey?.choiceReviewPass && $session.view.journey.choiceReviewPass > 1 && !$session.view.outcome}
                                                          Reviewing saved choices — pass {$session.view.journey.choiceReviewPass}. This is the same setup run, not a restart.
                                                          {/if} @@ -48,7 +48,9 @@ {/if} - {#if $session.view?.outcome} + {#if !$session.paired} + + {:else if $session.view?.outcome} {:else if $session.view?.prompt}
                                                          diff --git a/web/src/components/PairingPanel.svelte b/web/src/components/PairingPanel.svelte new file mode 100644 index 000000000..8e9f0c91a --- /dev/null +++ b/web/src/components/PairingPanel.svelte @@ -0,0 +1,24 @@ + + +
                                                          +
                                                          PRIVATE LOCAL SESSION
                                                          +

                                                          Pair this browser

                                                          +

                                                          Find the 16-character pairing code in the terminal that started copilot setup --web. Enter it here to view or control this setup. The code is never placed in the browser URL or stored after this page closes.

                                                          +
                                                          { event.preventDefault(); submit(); }}> + + +

                                                          Keep the code private. After refreshing this page, enter it again to reconnect.

                                                          + + +
                                                          diff --git a/web/src/session/setupSession.ts b/web/src/session/setupSession.ts index 574440c6a..00a4012d3 100644 --- a/web/src/session/setupSession.ts +++ b/web/src/session/setupSession.ts @@ -3,6 +3,7 @@ import type { WebSetupView } from '../../../src/application/contracts/web_setup_ interface SessionState { view?: WebSetupView; + paired: boolean; controller: boolean; busy: boolean; error: string; @@ -14,17 +15,12 @@ interface Bootstrap { takeoverTicket: string; } -export function sessionKeyFromFragment(fragment: string): string { - return new URLSearchParams(fragment.replace(/^#/, '')).get('setup-key') ?? ''; -} - -export function createSetupSession(sessionKey = sessionKeyFromFragment( - (globalThis as { location?: { hash?: string } }).location?.hash ?? '', -)) { - const state = writable({ controller: false, busy: false, error: '' }); +export function createSetupSession(initialSessionKey?: string) { + const state = writable({ paired: Boolean(initialSessionKey), controller: false, busy: false, error: '' }); + let sessionKey = initialSessionKey; let capability: string | undefined; let takeoverTicket = ''; - let current: SessionState = { controller: false, busy: false, error: '' }; + let current: SessionState = { paired: Boolean(initialSessionKey), controller: false, busy: false, error: '' }; let loading = false; function set(patch: Partial): void { @@ -33,7 +29,7 @@ export function createSetupSession(sessionKey = sessionKeyFromFragment( } async function refresh(preserveError = false): Promise { - if (loading) return; + if (loading || !sessionKey) return; loading = true; try { const response = await fetch('/api/state', { cache: 'no-store', headers: { 'X-Setup-Session-Key': sessionKey } } as RequestInit); @@ -47,6 +43,7 @@ export function createSetupSession(sessionKey = sessionKeyFromFragment( } async function connect(): Promise { + if (!sessionKey) return; try { const response = await fetch('/api/bootstrap', { cache: 'no-store', headers: { 'X-Setup-Session-Key': sessionKey } } as RequestInit); if (!response.ok) throw new Error('Could not join this local session.'); @@ -56,9 +53,31 @@ export function createSetupSession(sessionKey = sessionKeyFromFragment( set({ controller: bootstrap.controller, error: '' }); await refresh(); } catch { + sessionKey = undefined; capability = undefined; takeoverTicket = ''; - set({ view: undefined, controller: false, error: 'Could not connect to the local setup session. Check the terminal.' }); + set({ view: undefined, paired: false, controller: false, error: 'Could not connect to the local setup session. Check the terminal and pair again.' }); + } + } + + async function pair(code: string): Promise { + if (current.busy || current.paired) return; + set({ busy: true, error: '' }); + try { + const response = await fetch('/api/pair', { + method: 'POST', cache: 'no-store', headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ code: code.trim().toLowerCase() }), + } as RequestInit); + const data = await response.json() as Record; + if (!response.ok) throw new Error(String(data.error ?? 'Pairing was rejected.')); + if (typeof data.sessionKey !== 'string' || !/^[a-f0-9]{64}$/.test(data.sessionKey)) throw new Error('Invalid local pairing response.'); + sessionKey = data.sessionKey; + set({ paired: true, error: '' }); + await connect(); + } catch (cause) { + set({ error: cause instanceof Error ? cause.message : 'Could not pair this browser.' }); + } finally { + set({ busy: false }); } } @@ -122,5 +141,5 @@ export function createSetupSession(sessionKey = sessionKeyFromFragment( catch { /* The CLI can also be stopped in the terminal. */ } } - return { subscribe: state.subscribe, connect, refresh, submit, cancel, takeOver, close }; + return { subscribe: state.subscribe, pair, connect, refresh, submit, cancel, takeOver, close }; } From 42572428e9c3e7e4568069b2c7d5f668295be40d Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Mon, 28 Sep 2026 21:50:30 +0200 Subject: [PATCH 19/50] codex-setup-temporary-github-auth: reject detached web checkout before credentials --- build/cli/index.js | 19 +++++++++++++++---- build/github_action/index.js | 11 +++++++++++ docs/how-to-use.mdx | 4 +++- specs/CATALOG.md | 4 ++-- specs/catalog.json | 1 + specs/local-web-setup-assistant.md | 17 ++++++++++++----- src/__tests__/cli.test.ts | 20 +++++++++++++++++++- src/__tests__/cli_context_branch.test.ts | 24 ++++++++++++++++++++++++ src/cli/commands/setup.ts | 10 +++++----- src/cli_context.ts | 10 ++++++++++ 10 files changed, 102 insertions(+), 18 deletions(-) create mode 100644 src/__tests__/cli_context_branch.test.ts diff --git a/build/cli/index.js b/build/cli/index.js index 2c1cd44b9..d3a48c897 100755 --- a/build/cli/index.js +++ b/build/cli/index.js @@ -65931,10 +65931,10 @@ function registerSetupCommand(program) { (0, logger_1.logInfo)(`📦 Repository: ${gitInfo.owner}/${gitInfo.repo}`); releaseSetupGuard = (0, setup_session_guard_1.acquireSetupSessionGuard)(cwd); const checkoutRoot = webBridge ? (0, cli_context_1.getGitRepositoryRoot)(cwd) : cwd; - const initialBranch = webBridge ? (0, cli_context_1.getCurrentBranch)() : undefined; + const initialBranch = webBridge ? (0, cli_context_1.getCurrentAttachedBranch)(cwd) : undefined; const initialHead = webBridge ? (0, cli_context_1.getCurrentHeadSha)() : undefined; - if (webBridge && !initialHead) { - throw new application_error_1.ApplicationError('configuration.invalid', 'The current Git revision could not be verified. No local setup session started.'); + if (webBridge && (!initialBranch || !initialHead)) { + throw new application_error_1.ApplicationError('configuration.invalid', 'An attached Git branch and revision are required for web setup. Check out a branch before creating PATs. No local setup session started.'); } if (webBridge) { webBridge.setRepository(`${gitInfo.owner}/${gitInfo.repo}`); @@ -66181,7 +66181,7 @@ function registerSetupCommand(program) { const current = (0, cli_context_1.getGitInfo)(); return 'error' in current ? undefined : { owner: current.owner, repository: current.repo, checkoutRoot: (0, cli_context_1.getGitRepositoryRoot)(cwd), - branch: (0, cli_context_1.getCurrentBranch)(), head: (0, cli_context_1.getCurrentHeadSha)() ?? '', + branch: (0, cli_context_1.getCurrentAttachedBranch)(cwd) ?? '', head: (0, cli_context_1.getCurrentHeadSha)() ?? '', }; }, fileSnapshotMatches: setup_apply_snapshot_1.setupApplySnapshotMatches, @@ -68661,6 +68661,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.cleanCliArg = cleanCliArg; exports.getGitInfo = getGitInfo; exports.getCurrentBranch = getCurrentBranch; +exports.getCurrentAttachedBranch = getCurrentAttachedBranch; exports.getCurrentHeadSha = getCurrentHeadSha; exports.isInsideGitRepo = isInsideGitRepo; exports.getGitRepositoryRoot = getGitRepositoryRoot; @@ -68695,6 +68696,16 @@ function getCurrentBranch() { return 'main'; } } +/** A verified branch name for web setup; detached HEAD and failed git reads are not guessed. */ +function getCurrentAttachedBranch(cwd) { + try { + const branch = (0, child_process_1.execSync)('git symbolic-ref --quiet --short HEAD', { cwd }).toString().trim(); + return branch && branch !== 'HEAD' ? branch : undefined; + } + catch { + return undefined; + } +} /** Returns the canonical object ID for the workspace revision being analyzed. */ function getCurrentHeadSha() { try { diff --git a/build/github_action/index.js b/build/github_action/index.js index 94a7c5ecd..1a2d4a819 100644 --- a/build/github_action/index.js +++ b/build/github_action/index.js @@ -65235,6 +65235,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.cleanCliArg = cleanCliArg; exports.getGitInfo = getGitInfo; exports.getCurrentBranch = getCurrentBranch; +exports.getCurrentAttachedBranch = getCurrentAttachedBranch; exports.getCurrentHeadSha = getCurrentHeadSha; exports.isInsideGitRepo = isInsideGitRepo; exports.getGitRepositoryRoot = getGitRepositoryRoot; @@ -65269,6 +65270,16 @@ function getCurrentBranch() { return 'main'; } } +/** A verified branch name for web setup; detached HEAD and failed git reads are not guessed. */ +function getCurrentAttachedBranch(cwd) { + try { + const branch = (0, child_process_1.execSync)('git symbolic-ref --quiet --short HEAD', { cwd }).toString().trim(); + return branch && branch !== 'HEAD' ? branch : undefined; + } + catch { + return undefined; + } +} /** Returns the canonical object ID for the workspace revision being analyzed. */ function getCurrentHeadSha() { try { diff --git a/docs/how-to-use.mdx b/docs/how-to-use.mdx index 5784b315c..1162c9f26 100644 --- a/docs/how-to-use.mdx +++ b/docs/how-to-use.mdx @@ -39,7 +39,9 @@ If the checkout does not include the compiled `build/` folder (e.g. it is gitign Once installed, the `copilot` command is available globally. Repository-dependent commands such as `copilot setup`, `copilot doctor`, `copilot check-progress`, `copilot think`, and `copilot do` must be run **from the root of the target repository**. The `copilot upgrade`, `copilot --version`, and help flows can run from any directory. Commands that access GitHub accept `--token` or `PERSONAL_ACCESS_TOKEN` from the environment. `copilot setup` and `copilot doctor` securely prompt for the setup PAT when run interactively; no `.env` file is read or created. `copilot setup --dry-run` is the only setup mode that can run without a token. See [CLI commands](/single-actions/workflow-and-cli). Prefer a visual walkthrough? Run `copilot setup --web` from the repository -root. The CLI starts a short-lived page on `127.0.0.1` and opens your browser; +root on an attached Git branch. A detached HEAD is rejected before credentials +are requested; check out a branch first. The CLI starts a short-lived page on +`127.0.0.1` and opens your browser; if opening fails, paste the printed local URL into a browser on this computer. Enter the 16-character pairing code shown in the terminal before setup state appears. The URL contains no secret; keep the pairing code private. Loopback diff --git a/specs/CATALOG.md b/specs/CATALOG.md index 34539a75b..27b72d123 100644 --- a/specs/CATALOG.md +++ b/specs/CATALOG.md @@ -17,7 +17,7 @@ debt or convert unknown historic intent into a design decision. | `execution-lifecycle` | Implemented | Shared GitHub Action lifecycle from event admission through durable user-facing results | [Execution admission, queueing, routing, and result publication](./execution-admission-queue-and-publication.md) + 3 companion | 84 paths · 2026-09-16 | | `architecture-quality-hardening` | Implemented | Close verified concurrency, error-contract, context-coupling, fan-out, setup/doctor, and provider-policy risks in dependency order | [Architecture quality and scalability hardening](./architecture-quality-and-scalability-hardening.md) + 1 companion | 72 paths · 2026-09-16 | | `setup-and-doctor` | Implemented | Plan, validate, provision, and audit a repository installation without exposing credentials | [Setup, configuration, credentials, and doctor](./setup-configuration-credentials-and-doctor.md) + 2 companion | 90 paths · 2026-09-28 | -| `local-web-setup-assistant` | Proposed | Offer a packaged, loopback-only Svelte setup interface over the existing setup engine with role-separated PAT guidance and revision-bound application | [Local web setup assistant](./local-web-setup-assistant.md) | 72 paths · 2026-09-28 | +| `local-web-setup-assistant` | Proposed | Offer a packaged, loopback-only Svelte setup interface over the existing setup engine with role-separated PAT guidance and revision-bound application | [Local web setup assistant](./local-web-setup-assistant.md) | 73 paths · 2026-09-28 | | `guided-bot-pat-onboarding` | Proposed | Guide creation of the persistent workflow PAT using GitHub's official form, verify bot identity and grants, and install the approved Actions Secret | [Guided bot PAT onboarding](./guided-bot-pat-onboarding.md) | 25 paths · 2026-09-25 | | `temporary-setup-operator-authorization` | Proposed | Guide creation of the one-run operator PAT through GitHub's official form, verify final setup access, and report user-owned deletion accurately | [Assisted setup PAT creation](./temporary-setup-operator-authorization.md) | 28 paths · 2026-09-25 | | `issue-start-and-sdd-readiness` | Implemented | Start every admitted issue with one explicit signal and publish a validated SDD before eligible Action-managed branch work | [Uniform issue start and pre-branch SDD readiness](./issue-start-and-branch-readiness.md) + 1 companion | 51 paths · 2026-09-17 | @@ -119,7 +119,7 @@ debt or convert unknown historic intent into a design decision. - Workflows: Not applicable for this capability. - Entrypoints: [`src/cli/commands/setup.ts`](../src/cli/commands/setup.ts) · [`package.json`](../package.json) · [`web/src/main.ts`](../web/src/main.ts) - Core code: [`web/src/App.svelte`](../web/src/App.svelte) · [`web/src/session/setupSession.ts`](../web/src/session/setupSession.ts) · [`web/src/components/ActionButton.svelte`](../web/src/components/ActionButton.svelte) · [`web/src/components/PairingPanel.svelte`](../web/src/components/PairingPanel.svelte) · [`web/src/components/ContextPanel.svelte`](../web/src/components/ContextPanel.svelte) · [`web/src/components/PromptCard.svelte`](../web/src/components/PromptCard.svelte) · [`web/src/components/QuestionPrompt.svelte`](../web/src/components/QuestionPrompt.svelte) · [`web/src/components/StatusBanner.svelte`](../web/src/components/StatusBanner.svelte) · [`web/src/lib/questionAnswer.ts`](../web/src/lib/questionAnswer.ts) · [`web/src/styles/tokens.css`](../web/src/styles/tokens.css) · [`web/src/style.css`](../web/src/style.css) · [`src/application/contracts/web_setup_view.ts`](../src/application/contracts/web_setup_view.ts) · [`src/application/errors/setup_interaction_cancelled_error.ts`](../src/application/errors/setup_interaction_cancelled_error.ts) · [`src/application/policies/merge_setup_overrides_policy.ts`](../src/application/policies/merge_setup_overrides_policy.ts) · [`src/application/policies/setup_remote_facts_policy.ts`](../src/application/policies/setup_remote_facts_policy.ts) · [`src/application/usecases/setup/prepare_setup_pat_intent_use_case.ts`](../src/application/usecases/setup/prepare_setup_pat_intent_use_case.ts) · [`src/application/usecases/setup/audit_configured_setup_pat_use_case.ts`](../src/application/usecases/setup/audit_configured_setup_pat_use_case.ts) · [`src/application/usecases/setup/verify_web_setup_apply_use_case.ts`](../src/application/usecases/setup/verify_web_setup_apply_use_case.ts) · [`src/application/usecases/setup/verify_setup_pat_bootstrap_use_case.ts`](../src/application/usecases/setup/verify_setup_pat_bootstrap_use_case.ts) · [`src/cli_context.ts`](../src/cli_context.ts) · [`src/cli/setup_command_options.ts`](../src/cli/setup_command_options.ts) · [`src/cli/web_setup_bridge.ts`](../src/cli/web_setup_bridge.ts) · [`src/cli/web_setup_adapters.ts`](../src/cli/web_setup_adapters.ts) · [`src/cli/web_setup_server.ts`](../src/cli/web_setup_server.ts) · [`src/cli/setup_apply_snapshot.ts`](../src/cli/setup_apply_snapshot.ts) · [`src/cli/setup_session_guard.ts`](../src/cli/setup_session_guard.ts) · [`src/application/usecases/setup/setup_wizard_use_case.ts`](../src/application/usecases/setup/setup_wizard_use_case.ts) · [`src/application/usecases/setup/setup_journey_use_case.ts`](../src/application/usecases/setup/setup_journey_use_case.ts) · [`src/application/usecases/setup/setup_credentials_use_case.ts`](../src/application/usecases/setup/setup_credentials_use_case.ts) · [`src/application/policies/setup_questionnaire_policy.ts`](../src/application/policies/setup_questionnaire_policy.ts) · [`src/application/policies/setup_token_permission_policy.ts`](../src/application/policies/setup_token_permission_policy.ts) · [`src/application/policies/setup_configuration_plan.ts`](../src/application/policies/setup_configuration_plan.ts) · [`src/application/policies/setup_pat_creation_url_policy.ts`](../src/application/policies/setup_pat_creation_url_policy.ts) · [`src/infrastructure/composition/setup_credentials_composition_root.ts`](../src/infrastructure/composition/setup_credentials_composition_root.ts) · [`scripts/validate-npm-package.cjs`](../scripts/validate-npm-package.cjs) · [`scripts/render-web-setup-component.cjs`](../scripts/render-web-setup-component.cjs) -- Tests: [`src/__tests__/cli.test.ts`](../src/__tests__/cli.test.ts) · [`src/__tests__/cli_context_root.test.ts`](../src/__tests__/cli_context_root.test.ts) · [`src/cli/__tests__/web_setup_bridge.test.ts`](../src/cli/__tests__/web_setup_bridge.test.ts) · [`src/cli/__tests__/web_setup_adapters.test.ts`](../src/cli/__tests__/web_setup_adapters.test.ts) · [`src/cli/__tests__/web_setup_palette.test.ts`](../src/cli/__tests__/web_setup_palette.test.ts) · [`src/cli/__tests__/web_setup_ui_helpers.test.ts`](../src/cli/__tests__/web_setup_ui_helpers.test.ts) · [`src/cli/__tests__/web_setup_browser_session.test.ts`](../src/cli/__tests__/web_setup_browser_session.test.ts) · [`src/cli/__tests__/web_setup_server.test.ts`](../src/cli/__tests__/web_setup_server.test.ts) · [`src/cli/__tests__/web_setup_browser_open.test.ts`](../src/cli/__tests__/web_setup_browser_open.test.ts) · [`src/cli/__tests__/setup_apply_snapshot.test.ts`](../src/cli/__tests__/setup_apply_snapshot.test.ts) · [`src/cli/__tests__/setup_session_guard.test.ts`](../src/cli/__tests__/setup_session_guard.test.ts) · [`src/cli/__tests__/setup_command_options.test.ts`](../src/cli/__tests__/setup_command_options.test.ts) · [`src/cli/__tests__/web_setup_components.test.ts`](../src/cli/__tests__/web_setup_components.test.ts) · [`src/architecture/__tests__/web_setup_boundaries.test.ts`](../src/architecture/__tests__/web_setup_boundaries.test.ts) · [`src/application/usecases/setup/__tests__/prepare_setup_pat_intent_use_case.test.ts`](../src/application/usecases/setup/__tests__/prepare_setup_pat_intent_use_case.test.ts) · [`src/application/usecases/setup/__tests__/audit_configured_setup_pat_use_case.test.ts`](../src/application/usecases/setup/__tests__/audit_configured_setup_pat_use_case.test.ts) · [`src/application/usecases/setup/__tests__/verify_web_setup_apply_use_case.test.ts`](../src/application/usecases/setup/__tests__/verify_web_setup_apply_use_case.test.ts) · [`src/application/usecases/setup/__tests__/verify_setup_pat_bootstrap_use_case.test.ts`](../src/application/usecases/setup/__tests__/verify_setup_pat_bootstrap_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts) · [`src/application/policies/__tests__/setup_questionnaire_policy.test.ts`](../src/application/policies/__tests__/setup_questionnaire_policy.test.ts) · [`src/application/policies/__tests__/setup_token_permission_policy.test.ts`](../src/application/policies/__tests__/setup_token_permission_policy.test.ts) · [`src/architecture/__tests__/setup_doctor_boundaries.test.ts`](../src/architecture/__tests__/setup_doctor_boundaries.test.ts) +- Tests: [`src/__tests__/cli.test.ts`](../src/__tests__/cli.test.ts) · [`src/__tests__/cli_context_branch.test.ts`](../src/__tests__/cli_context_branch.test.ts) · [`src/__tests__/cli_context_root.test.ts`](../src/__tests__/cli_context_root.test.ts) · [`src/cli/__tests__/web_setup_bridge.test.ts`](../src/cli/__tests__/web_setup_bridge.test.ts) · [`src/cli/__tests__/web_setup_adapters.test.ts`](../src/cli/__tests__/web_setup_adapters.test.ts) · [`src/cli/__tests__/web_setup_palette.test.ts`](../src/cli/__tests__/web_setup_palette.test.ts) · [`src/cli/__tests__/web_setup_ui_helpers.test.ts`](../src/cli/__tests__/web_setup_ui_helpers.test.ts) · [`src/cli/__tests__/web_setup_browser_session.test.ts`](../src/cli/__tests__/web_setup_browser_session.test.ts) · [`src/cli/__tests__/web_setup_server.test.ts`](../src/cli/__tests__/web_setup_server.test.ts) · [`src/cli/__tests__/web_setup_browser_open.test.ts`](../src/cli/__tests__/web_setup_browser_open.test.ts) · [`src/cli/__tests__/setup_apply_snapshot.test.ts`](../src/cli/__tests__/setup_apply_snapshot.test.ts) · [`src/cli/__tests__/setup_session_guard.test.ts`](../src/cli/__tests__/setup_session_guard.test.ts) · [`src/cli/__tests__/setup_command_options.test.ts`](../src/cli/__tests__/setup_command_options.test.ts) · [`src/cli/__tests__/web_setup_components.test.ts`](../src/cli/__tests__/web_setup_components.test.ts) · [`src/architecture/__tests__/web_setup_boundaries.test.ts`](../src/architecture/__tests__/web_setup_boundaries.test.ts) · [`src/application/usecases/setup/__tests__/prepare_setup_pat_intent_use_case.test.ts`](../src/application/usecases/setup/__tests__/prepare_setup_pat_intent_use_case.test.ts) · [`src/application/usecases/setup/__tests__/audit_configured_setup_pat_use_case.test.ts`](../src/application/usecases/setup/__tests__/audit_configured_setup_pat_use_case.test.ts) · [`src/application/usecases/setup/__tests__/verify_web_setup_apply_use_case.test.ts`](../src/application/usecases/setup/__tests__/verify_web_setup_apply_use_case.test.ts) · [`src/application/usecases/setup/__tests__/verify_setup_pat_bootstrap_use_case.test.ts`](../src/application/usecases/setup/__tests__/verify_setup_pat_bootstrap_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts) · [`src/application/policies/__tests__/setup_questionnaire_policy.test.ts`](../src/application/policies/__tests__/setup_questionnaire_policy.test.ts) · [`src/application/policies/__tests__/setup_token_permission_policy.test.ts`](../src/application/policies/__tests__/setup_token_permission_policy.test.ts) · [`src/architecture/__tests__/setup_doctor_boundaries.test.ts`](../src/architecture/__tests__/setup_doctor_boundaries.test.ts) - User documentation: [`README.md`](../README.md) · [`docs/how-to-use.mdx`](../docs/how-to-use.mdx) · [`docs/authentication.mdx`](../docs/authentication.mdx) · [`docs/configuration.mdx`](../docs/configuration.mdx) · [`docs/configuration-checklist.mdx`](../docs/configuration-checklist.mdx) · [`docs/development/architecture.mdx`](../docs/development/architecture.mdx) · [`docs/dependency-rules.md`](../docs/dependency-rules.md) · [`docs/security-operations/operations/troubleshooting.mdx`](../docs/security-operations/operations/troubleshooting.mdx) · [`docs/security-operations/operations/provisioning.mdx`](../docs/security-operations/operations/provisioning.mdx) ### `guided-bot-pat-onboarding` — Guided bot PAT onboarding diff --git a/specs/catalog.json b/specs/catalog.json index b87a344d8..4a2da6346 100644 --- a/specs/catalog.json +++ b/specs/catalog.json @@ -807,6 +807,7 @@ ], "tests": [ "src/__tests__/cli.test.ts", + "src/__tests__/cli_context_branch.test.ts", "src/__tests__/cli_context_root.test.ts", "src/cli/__tests__/web_setup_bridge.test.ts", "src/cli/__tests__/web_setup_adapters.test.ts", diff --git a/specs/local-web-setup-assistant.md b/specs/local-web-setup-assistant.md index 3548d80f1..5884edc2f 100644 --- a/specs/local-web-setup-assistant.md +++ b/specs/local-web-setup-assistant.md @@ -218,6 +218,10 @@ exactly what completed and what remains. empty lock. Web mode does not require a TTY: the browser is the interactive surface, and a printed local URL is available if automatic opening is unavailable. + Web setup MUST verify an attached Git branch and canonical HEAD before + opening the browser or collecting credentials. Detached HEAD or an unreadable + branch fails immediately with checkout guidance; no fallback branch name + may be inferred for this guarded session. 2. Bind `127.0.0.1:0`, record the assigned port, create an unpredictable one-run session key, a separate 16-hex-character pairing code, and first controller lease in process memory. Print the pairing code only in the @@ -777,9 +781,11 @@ help for `--web` explains local-only scope and the `--non-interactive` conflict. ## 16. Acceptance scenarios -1. Given an installed npm package and an eligible checkout, `copilot setup +1. Given an installed npm package and an eligible checkout on an attached + branch, `copilot setup --web` opens a bundled local page showing the exact repository and six - stages; no source checkout or Vite server is needed. + stages; no source checkout or Vite server is needed. A detached HEAD is + rejected before a browser opens or any PAT is requested. 2. Given a failed browser opener, the CLI prints the loopback URL and keeps serving; given a failed bind or missing assets, it stops without a false partial setup claim and offers terminal fallback. @@ -884,12 +890,13 @@ catalog stays `proposed` until the definition of done is evidenced. Existing terminal policy/use cases remain the authority; the current web path does not introduce its own permission catalog. -The latest full local run on 2026-09-28 passed 501 Jest suites / 5,347 tests, -with 95.93% statements, 90.83% branches, 96.51% functions, and 97.24% lines +The latest full local run on 2026-09-28 passed 502 Jest suites / 5,388 tests, +with 95.95% statements, 90.88% branches, 96.53% functions, and 97.26% lines repository-wide. The new setup-PAT intent, bootstrap audit, configured audit, remote-fact comparison, and override merge modules each reached 100% in all four metrics; final web Apply authorization reached 100% lines and 95.83% -branches. The local HTTP server reached 99.33% lines and 92.26% branches. +branches. The browser session transport reached 100% in all four metrics. +The local HTTP server reached 99.41% lines and 92.46% branches. Focused tests additionally cover the CLI handoff, semantic Svelte/Vite renders, empty issue-workflow selection, drift, cancellation, and package isolation. Typecheck, lint, Svelte diagnostics, full build, catalog, documentation, diff --git a/src/__tests__/cli.test.ts b/src/__tests__/cli.test.ts index 1240c2812..b483e4a87 100644 --- a/src/__tests__/cli.test.ts +++ b/src/__tests__/cli.test.ts @@ -508,7 +508,7 @@ describe('CLI', () => { (execSync as jest.Mock).mockImplementation((command: string) => Buffer.from( command === 'git rev-parse HEAD' ? 'a'.repeat(40) : command === 'git rev-parse --show-toplevel' ? process.cwd() - : command === 'git rev-parse --abbrev-ref HEAD' ? 'develop' + : command === 'git rev-parse --abbrev-ref HEAD' || command === 'git symbolic-ref --quiet --short HEAD' ? 'develop' : 'https://github.com/test-owner/test-repo.git', )); ask = jest.spyOn(WebSetupBridge.prototype, 'ask').mockImplementation(answerWebPrompt); @@ -542,6 +542,24 @@ describe('CLI', () => { expect(runLocalAction).not.toHaveBeenCalled(); }); + it('rejects detached HEAD before opening the browser or collecting a PAT', async () => { + (execSync as jest.Mock).mockImplementation((command: string) => { + if (command === 'git symbolic-ref --quiet --short HEAD') throw new Error('detached HEAD'); + return Buffer.from(command === 'git rev-parse HEAD' ? 'a'.repeat(40) + : command === 'git rev-parse --show-toplevel' ? process.cwd() + : 'https://github.com/test-owner/test-repo.git'); + }); + await program.parseAsync(['node', 'cli', 'setup', '--web']); + expect(startWebSetupServer).not.toHaveBeenCalled(); + expect(openWebSetupBrowser).not.toHaveBeenCalled(); + expect(ask).not.toHaveBeenCalled(); + expect(mockTokenPermissionInspect).not.toHaveBeenCalled(); + expect(runLocalAction).not.toHaveBeenCalled(); + expect(process.exitCode).toBe(1); + const { logError } = require('../utils/logger'); + expect(logError).toHaveBeenCalledWith(expect.objectContaining({ message: expect.stringContaining('Check out a branch') })); + }); + it.each([ [undefined, 130], ['decline', undefined], diff --git a/src/__tests__/cli_context_branch.test.ts b/src/__tests__/cli_context_branch.test.ts new file mode 100644 index 000000000..0b7991741 --- /dev/null +++ b/src/__tests__/cli_context_branch.test.ts @@ -0,0 +1,24 @@ +import { execSync } from 'child_process'; +import { getCurrentAttachedBranch } from '../cli_context'; + +jest.mock('child_process', () => ({ execSync: jest.fn() })); + +describe('verified branch for web setup', () => { + afterEach(() => jest.clearAllMocks()); + + test('returns the attached branch from the requested checkout', () => { + (execSync as jest.Mock).mockReturnValue(Buffer.from('develop\n')); + expect(getCurrentAttachedBranch('/a/checkout')).toBe('develop'); + expect(execSync).toHaveBeenCalledWith('git symbolic-ref --quiet --short HEAD', { cwd: '/a/checkout' }); + }); + + test.each([['', undefined], ['HEAD', undefined]])('rejects an unusable branch value %j', (output, expected) => { + (execSync as jest.Mock).mockReturnValue(Buffer.from(output)); + expect(getCurrentAttachedBranch('/a/checkout')).toBe(expected); + }); + + test('returns no branch for a detached HEAD or failed git read', () => { + (execSync as jest.Mock).mockImplementation(() => { throw new Error('detached'); }); + expect(getCurrentAttachedBranch('/a/checkout')).toBeUndefined(); + }); +}); diff --git a/src/cli/commands/setup.ts b/src/cli/commands/setup.ts index 7ffa99115..c14312b79 100644 --- a/src/cli/commands/setup.ts +++ b/src/cli/commands/setup.ts @@ -3,7 +3,7 @@ import { runLocalAction } from '../../actions/local_action'; import { TITLE } from '../../application/contracts/product_identity'; import { getSetupToken } from '../../utils/setup_files'; import { logError, logInfo } from '../../utils/logger'; -import { getCurrentBranch, getCurrentHeadSha, getGitInfo, getGitRepositoryRoot, isInsideGitRepo } from '../../cli_context'; +import { getCurrentAttachedBranch, getCurrentHeadSha, getGitInfo, getGitRepositoryRoot, isInsideGitRepo } from '../../cli_context'; import { buildSetupParams } from './setup_policy'; import { collectApprovalCheck, collectScope, collectSecret, loadSetupOverrides } from '../setup_command_options'; import { SetupQuestionnaireController, SetupWizardUseCase } from '../../application/usecases/setup'; @@ -116,10 +116,10 @@ export function registerSetupCommand(program: Command): void { logInfo(`📦 Repository: ${gitInfo.owner}/${gitInfo.repo}`); releaseSetupGuard = acquireSetupSessionGuard(cwd); const checkoutRoot = webBridge ? getGitRepositoryRoot(cwd) : cwd; - const initialBranch = webBridge ? getCurrentBranch() : undefined; + const initialBranch = webBridge ? getCurrentAttachedBranch(cwd) : undefined; const initialHead = webBridge ? getCurrentHeadSha() : undefined; - if (webBridge && !initialHead) { - throw new ApplicationError('configuration.invalid', 'The current Git revision could not be verified. No local setup session started.'); + if (webBridge && (!initialBranch || !initialHead)) { + throw new ApplicationError('configuration.invalid', 'An attached Git branch and revision are required for web setup. Check out a branch before creating PATs. No local setup session started.'); } if (webBridge) { webBridge.setRepository(`${gitInfo.owner}/${gitInfo.repo}`); @@ -349,7 +349,7 @@ export function registerSetupCommand(program: Command): void { const current = getGitInfo(); return 'error' in current ? undefined : { owner: current.owner, repository: current.repo, checkoutRoot: getGitRepositoryRoot(cwd), - branch: getCurrentBranch(), head: getCurrentHeadSha() ?? '', + branch: getCurrentAttachedBranch(cwd) ?? '', head: getCurrentHeadSha() ?? '', }; }, fileSnapshotMatches: setupApplySnapshotMatches, diff --git a/src/cli_context.ts b/src/cli_context.ts index e2418bc54..a4132e7bf 100644 --- a/src/cli_context.ts +++ b/src/cli_context.ts @@ -30,6 +30,16 @@ export function getCurrentBranch(): string { } } +/** A verified branch name for web setup; detached HEAD and failed git reads are not guessed. */ +export function getCurrentAttachedBranch(cwd: string): string | undefined { + try { + const branch = execSync('git symbolic-ref --quiet --short HEAD', { cwd }).toString().trim(); + return branch && branch !== 'HEAD' ? branch : undefined; + } catch { + return undefined; + } +} + /** Returns the canonical object ID for the workspace revision being analyzed. */ export function getCurrentHeadSha(): string | undefined { try { From ad68d4283bed857b246a007b52c1d2e3cb0f52e2 Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Mon, 28 Sep 2026 22:07:26 +0200 Subject: [PATCH 20/50] codex-setup-temporary-github-auth: enforce architecture across re-exports --- docs/dependency-rules.md | 5 +- specs/local-web-setup-assistant.md | 8 ++- .../__tests__/web_setup_boundaries.test.ts | 58 +++++++++++++++++-- 3 files changed, 61 insertions(+), 10 deletions(-) diff --git a/docs/dependency-rules.md b/docs/dependency-rules.md index 731bd9fa0..f4f67e62b 100644 --- a/docs/dependency-rules.md +++ b/docs/dependency-rules.md @@ -25,7 +25,10 @@ but cannot import provider adapters, mutation use cases, Node HTTP, or PAT permission tables. `src/cli/web_setup_server.ts` owns loopback transport and static assets; `src/cli/web_setup_adapters.ts` maps semantic browser decisions to the existing application ports. Domain/application policy modules must not -import the browser, Vite, Node HTTP, or terminal renderers. +import or re-export the browser, Vite, Node HTTP, or terminal renderers. The +web setup boundary test follows imports, re-exports, literal `require()` and +dynamic `import()` calls, including type-only forms, so a barrel or lazy load +cannot hide an outer-layer dependency. ## Current physical layers diff --git a/specs/local-web-setup-assistant.md b/specs/local-web-setup-assistant.md index 5884edc2f..357c82473 100644 --- a/specs/local-web-setup-assistant.md +++ b/specs/local-web-setup-assistant.md @@ -485,7 +485,8 @@ Both terminal and web routes use the same questionnaire parser for this choice. ### 8.3 Executable architecture and packaging constraints -1. A dependency test MUST reject `src/domain`/`src/application` imports of +1. A dependency test MUST reject `src/domain`/`src/application` imports, + re-exports, and literal lazy/CommonJS dependencies on Svelte, Vite, DOM, `node:http`, terminal presenters, Octokit concrete adapters, and CLI modules; Svelte modules MUST import only public view/contracts and never provider or mutation modules. @@ -754,7 +755,8 @@ Repository-wide Jest/coverage, lint, typecheck, build, documentation, workflow, catalog, and npm-package gates remain. New pure policies target 100% branch coverage; changed application/server/credential modules target at least 95% statements/lines and 90% branches/functions, with no regression -to higher existing budgets. Architecture tests parse imports and contract +to higher existing budgets. Architecture tests parse imports, re-exports, +literal lazy/CommonJS dependencies, and contract schemas, not prose. Use deterministic fake clock/IDs, temp repositories, fake GitHub ports, fake browsers/HTTP clients, and adversarial origins; never use real PATs, issue/Action test resources, external services, or @@ -890,7 +892,7 @@ catalog stays `proposed` until the definition of done is evidenced. Existing terminal policy/use cases remain the authority; the current web path does not introduce its own permission catalog. -The latest full local run on 2026-09-28 passed 502 Jest suites / 5,388 tests, +The latest full local run on 2026-09-28 passed 502 Jest suites / 5,389 tests, with 95.95% statements, 90.88% branches, 96.53% functions, and 97.26% lines repository-wide. The new setup-PAT intent, bootstrap audit, configured audit, remote-fact comparison, and override merge modules each reached 100% in all diff --git a/src/architecture/__tests__/web_setup_boundaries.test.ts b/src/architecture/__tests__/web_setup_boundaries.test.ts index b1dbd39ce..3c5fe0eaf 100644 --- a/src/architecture/__tests__/web_setup_boundaries.test.ts +++ b/src/architecture/__tests__/web_setup_boundaries.test.ts @@ -18,16 +18,41 @@ function browserSources(directory: string): string[] { }); } -function moduleImports(path: string): Array<{ specifier: string; typeOnly: boolean }> { - const raw = readFileSync(path, 'utf8'); +function moduleImports(path: string, text = readFileSync(path, 'utf8')): Array<{ specifier: string; typeOnly: boolean }> { + const raw = text; const source = path.endsWith('.svelte') ? [...raw.matchAll(/]*>([\s\S]*?)<\/script>/g)].map(match => match[1]).join('\n') : raw; const ast = ts.createSourceFile(path, source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS); - return ast.statements.filter(ts.isImportDeclaration).map(node => ({ - specifier: (node.moduleSpecifier as ts.StringLiteral).text, - typeOnly: node.importClause?.isTypeOnly === true, - })); + const dependencies: Array<{ specifier: string; typeOnly: boolean }> = []; + const visit = (node: ts.Node): void => { + if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) { + const named = node.importClause?.namedBindings; + dependencies.push({ specifier: node.moduleSpecifier.text, + typeOnly: node.importClause?.isTypeOnly === true || (named && ts.isNamedImports(named) + && named.elements.length > 0 && named.elements.every(element => element.isTypeOnly)) === true }); + return; + } + if (ts.isExportDeclaration(node) && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) { + const named = node.exportClause; + dependencies.push({ specifier: node.moduleSpecifier.text, + typeOnly: node.isTypeOnly || (named && ts.isNamedExports(named) + && named.elements.length > 0 && named.elements.every(element => element.isTypeOnly)) === true }); + return; + } + if (ts.isCallExpression(node) && node.arguments.length === 1 && ts.isStringLiteral(node.arguments[0]) + && ((ts.isIdentifier(node.expression) && node.expression.text === 'require') + || node.expression.kind === ts.SyntaxKind.ImportKeyword)) { + dependencies.push({ specifier: node.arguments[0].text, typeOnly: false }); + } + if (ts.isImportTypeNode(node) && ts.isLiteralTypeNode(node.argument) + && ts.isStringLiteral(node.argument.literal)) { + dependencies.push({ specifier: node.argument.literal.text, typeOnly: true }); + } + ts.forEachChild(node, visit); + }; + visit(ast); + return dependencies; } function localModule(from: string, specifier: string): string | undefined { @@ -38,6 +63,27 @@ function localModule(from: string, specifier: string): string | undefined { } describe('local web setup architecture', () => { + test('dependency reader includes re-exports, require, dynamic import and type-only forms', () => { + expect(moduleImports('fixture.ts', ` + import { type Input } from './types'; + export * from './runtime'; + export { Adapter } from './adapter'; + export type { Contract } from './contract'; + export { type View } from './view'; + const runtime = require('./commonjs'); + const later = import('./lazy'); + type LazyType = import('./type-import').Shape; + `)).toEqual([ + { specifier: './types', typeOnly: true }, + { specifier: './runtime', typeOnly: false }, + { specifier: './adapter', typeOnly: false }, + { specifier: './contract', typeOnly: true }, + { specifier: './view', typeOnly: true }, + { specifier: './commonjs', typeOnly: false }, + { specifier: './lazy', typeOnly: false }, + { specifier: './type-import', typeOnly: true }, + ]); + }); test('browser imports only redacted application contracts, as types, across the actual dependency graph', () => { const browser = join(root, 'web', 'src'); const contract = join(root, 'src', 'application', 'contracts', 'web_setup_view'); From 74ab6c5da708a71c18eb5165f7e61c0c712c96cd Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Mon, 28 Sep 2026 22:23:40 +0200 Subject: [PATCH 21/50] codex-setup-temporary-github-auth: reset prompt inputs per decision revision --- build/web/assets/index-D-m5cfVM.js | 2 -- build/web/assets/index-D3fDoDei.js | 2 ++ build/web/index.html | 2 +- docs/how-to-use.mdx | 4 +++- specs/local-web-setup-assistant.md | 8 ++++--- .../__tests__/web_setup_boundaries.test.ts | 2 ++ .../__tests__/web_setup_components.test.ts | 14 +++++++++++++ web/src/App.svelte | 4 +--- web/src/components/PromptCard.svelte | 21 +++++++++++-------- 9 files changed, 40 insertions(+), 19 deletions(-) delete mode 100644 build/web/assets/index-D-m5cfVM.js create mode 100644 build/web/assets/index-D3fDoDei.js diff --git a/build/web/assets/index-D-m5cfVM.js b/build/web/assets/index-D-m5cfVM.js deleted file mode 100644 index fcef5dd1c..000000000 --- a/build/web/assets/index-D-m5cfVM.js +++ /dev/null @@ -1,2 +0,0 @@ -(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})(),typeof window<`u`&&((window.__svelte??={}).v??=new Set).add(`5`);var e=!1;function t(){e=!0}t();var n={},r=Symbol(`uninitialized`),i=`http://www.w3.org/1999/xhtml`,a=Array.isArray,o=Array.prototype.indexOf,s=Array.prototype.includes,c=Array.from,l=Object.defineProperty,u=Object.getOwnPropertyDescriptor,d=Object.getOwnPropertyDescriptors,f=Object.prototype,p=Array.prototype,m=Object.getPrototypeOf,h=Object.isExtensible,g=()=>{};function _(e){return e()}function v(e){for(var t=0;t{e=n,t=r}),resolve:e,reject:t}}var b=1024,x=2048,S=4096,ee=8192,te=16384,ne=32768,re=1<<25,ie=65536,ae=1<<18,oe=1<<19,se=1<<20,ce=1<<25,le=1<<21,ue=1<<22,de=1<<23,fe=Symbol(`$state`),pe=Symbol(`component`),me=Symbol(`legacy props`),he=Symbol(``),ge=Symbol(`attributes`),_e=Symbol(`class`),ve=Symbol(`style`),ye=Symbol(`text`),be=Symbol(`form reset`),xe=new class extends Error{name=`StaleReactionError`;message="The reaction that called `getAbortSignal()` was re-run or destroyed"},Se=!!globalThis.document?.contentType&&globalThis.document.contentType.includes(`xml`);function Ce(){console.warn(`https://svelte.dev/e/derived_inert`)}function we(e){console.warn(`https://svelte.dev/e/hydration_mismatch`)}function Te(){console.warn(`https://svelte.dev/e/select_multiple_invalid_value`)}function Ee(){console.warn(`https://svelte.dev/e/svelte_boundary_reset_noop`)}var C=!1;function De(e){C=e}var w;function T(e){if(e===null)throw we(),n;return w=e}function Oe(){return T(on(w))}function E(e){if(C){if(on(w)!==null)throw we(),n;w=e}}function ke(e=1){if(C){for(var t=e,n=w;t--;)n=on(n);w=n}}function Ae(e=!0){for(var t=0,n=w;;){if(n.nodeType===8){var r=n.data;if(r===`]`){if(t===0)return n;--t}else(r===`[`||r===`[!`||r[0]===`[`&&!isNaN(Number(r.slice(1))))&&(t+=1)}var i=on(n);e&&n.remove(),n=i}}function je(e){if(!e||e.nodeType!==8)throw we(),n;return e.data}function Me(e){return e===this.v}function Ne(e,t){return e==e?e!==t||typeof e==`object`&&!!e||typeof e==`function`:t==t}function Pe(e){return!Ne(e,this.v)}function Fe(e){throw Error(`https://svelte.dev/e/lifecycle_outside_component`)}function Ie(){throw Error(`https://svelte.dev/e/async_derived_orphan`)}function Le(e,t,n){throw Error(`https://svelte.dev/e/each_key_duplicate`)}function Re(e){throw Error(`https://svelte.dev/e/effect_in_teardown`)}function ze(){throw Error(`https://svelte.dev/e/effect_in_unowned_derived`)}function Be(e){throw Error(`https://svelte.dev/e/effect_orphan`)}function Ve(){throw Error(`https://svelte.dev/e/effect_update_depth_exceeded`)}function He(e){throw Error(`https://svelte.dev/e/props_invalid_value`)}function Ue(){throw Error(`https://svelte.dev/e/state_descriptors_fixed`)}function We(){throw Error(`https://svelte.dev/e/state_prototype_fixed`)}function Ge(){throw Error(`https://svelte.dev/e/state_unsafe_mutation`)}function Ke(){throw Error(`https://svelte.dev/e/svelte_boundary_reset_onerror`)}var D=null;function qe(e){D=e}function O(t,n=!1,r){D={p:D,i:!1,c:null,e:null,s:t,x:null,r:H,l:e&&!n?{s:null,u:null,$:[]}:null}}function k(e){var t=D,n=t.e;if(n!==null){t.e=null;for(var r of n)bn(r)}return e!==void 0&&(t.x=e),t.i=!0,D=t.p,Je(e)}function Je(e={}){return l(e,pe,{value:!0}),e}function Ye(){return!e||D!==null&&D.l===null}var Xe=[];function Ze(){var e=Xe;Xe=[],v(e)}function Qe(e){if(Xe.length===0&&!wt){var t=Xe;queueMicrotask(()=>{t===Xe&&Ze()})}Xe.push(e)}function $e(){for(;Xe.length>0;)Ze()}var et=~(x|S|b);function A(e,t){e.f=e.f&et|t}function tt(e){e.f&512||e.deps===null?A(e,b):A(e,S)}function nt(e,t,n){e.f&2048?t.add(e):e.f&4096&&n.add(e),A(e,b)}var rt=!1;function it(){rt||(rt=!0,document.addEventListener(`reset`,e=>{Promise.resolve().then(()=>{if(!e.defaultPrevented)for(let t of e.target.elements)t[be]?.()})},{capture:!0}))}function at(e){var t=B,n=H;V(null),Wn(null);try{return e()}finally{V(t),Wn(n)}}function ot(e,t,n,r=n){e.addEventListener(t,()=>at(n));let i=e[be];e[be]=i?()=>{i(),r(!0)}:()=>r(!0),it()}function st(e,t,n,r){let i=Ye()?dt:mt;var a=e.filter(e=>!e.settled),o=t.map(i);if(n.length===0&&a.length===0){r(o);return}var s=H,c=ct(),l=a.length===1?a[0].promise:a.length>1?Promise.all(a.map(e=>e.promise)):null;function u(e){if(!(s.f&16384)){c();try{r([...o,...e])}catch(e){pn(e,s)}lt()}}var d=ut();if(n.length===0){l.then(()=>u([])).finally(d);return}function f(){Promise.all(n.map(e=>pt(e))).then(u).catch(e=>pn(e,s)).finally(d)}l?l.then(()=>{c(),f(),lt()}):f()}function ct(){var e=H,t=B,n=D,r=j;return function(i=!0){Wn(e),V(t),qe(n),i&&!(e.f&16384)&&(r?.activate(),r?.apply())}}function lt(e=!0){Wn(null),V(null),qe(null),e&&j?.deactivate()}function ut(){var e=H,t=e.b,n=j,r=!!t?.is_rendered();return t?.update_pending_count(1,n),n.increment(r,e),()=>{t?.update_pending_count(-1,n),n.decrement(r,e)}}function dt(e){var t=2|x;return H!==null&&(H.f|=oe),{ctx:D,deps:null,effects:null,equals:Me,f:t,fn:e,reactions:null,rv:0,v:r,wv:0,parent:H,ac:null}}var ft=Symbol(`obsolete`);function pt(e,t,n){let i=H;i===null&&Ie();var a=void 0,o=Vt(r),s=!B,c=new Set;return En(()=>{var t=H,n=y();a=n.promise;try{Promise.resolve(e()).then(n.resolve,e=>{e!==xe&&n.reject(e)}).finally(lt)}catch(e){n.reject(e),lt()}var r=j;if(s){if(t.f&32768)var l=ut();if(i.b?.is_rendered())r.async_deriveds.get(t)?.reject(ft);else for(let e of c.values())e.reject(ft);c.add(n),r.async_deriveds.set(t,n)}let u=(e,t=void 0)=>{l?.(),c.delete(n),t!==ft&&(r.activate(),t?(o.f|=de,Gt(o,t)):(o.f&8388608&&(o.f^=de),Gt(o,e)),r.deactivate())};n.promise.then(u,e=>u(null,e||`unknown`))}),vn(()=>{for(let e of c)e.reject(ft)}),new Promise(e=>{function t(n){function r(){n===a?e(o):t(a)}n.then(r,r)}t(a)})}function mt(e){let t=dt(e);return t.equals=Pe,t}function ht(e){var t=e.effects;if(t!==null){e.effects=null;for(var n=0;n{t.ac.abort(xe),t.ac=null}),t.fn!==null&&(t.teardown=g),ar(t,0),An(t))}function yt(e){if(e.effects!==null)for(let t of e.effects)t.teardown&&t.fn!==null&&or(t)}var bt=null,j=null,xt=null,St=null,Ct=null,wt=!1,Tt=!1,Et=null,Dt=null,Ot=0,kt=1,At=class e{id=kt++;#e=!1;linked=!0;#t=null;#n=null;async_deriveds=new Map;current=new Map;previous=new Map;#r=new Set;#i=new Set;#a=0;#o=new Map;#s=null;#c=[];#l=[];#u=new Set;#d=new Set;#f=new Map;#p=new Set;is_fork=!1;#m=!1;constructor(){bt===null?bt=this:(bt.#n=this,this.#t=bt),bt=this}#h(){if(this.is_fork)return!0;for(let n of this.#o.keys()){for(var e=n,t=!1;e.parent!==null;){if(this.#f.has(e)){t=!0;break}e=e.parent}if(!t)return!0}return!1}skip_effect(e){this.#f.has(e)||this.#f.set(e,{d:[],m:[]}),this.#p.delete(e)}unskip_effect(e,t=e=>this.schedule(e)){var n=this.#f.get(e);if(n){this.#f.delete(e);for(var r of n.d)A(r,x),t(r);for(r of n.m)A(r,S),t(r)}this.#p.add(e)}#g(){var e=[];for(let i of this.#c)if(!(i.f&16384||!(i.f&6144))){for(var t=i,n=!1;t.parent!==null;){t=t.parent;var r=t.f;if(r&96){if(!(r&1024)){n=!0;break}t.f^=b}}n||e.push(t)}return this.#c=[],e}#_(){this.#e=!0;for(let e of this.#u)this.#d.delete(e),A(e,x),this.schedule(e);for(let e of this.#d)A(e,S),this.schedule(e);this.apply();for(var t=Et=[],n=[],r=Dt=[];this.#c.length>0;){Ot++>1e3&&(this.#S(),Mt());for(let e of this.#g())try{this.#v(e,t,n)}catch(t){throw Lt(e),this.#h()||this.discard(),t}}if(j=null,r.length>0){var i=e.ensure();for(let e of r)i.schedule(e)}if(Et=null,Dt=null,this.#h()){this.#x(n),this.#x(t);for(let[e,t]of this.#f)It(e,t);r.length>0&&j.#_();return}let a=this.#y();if(a){this.#x(n),this.#x(t),a.#b(this);return}this.#u.clear(),this.#d.clear();for(let e of this.#r)e(this);this.#r.clear(),xt=this,Pt(n),Pt(t),xt=null,this.#s?.resolve();var o=j;if(this.#a===0&&(this.#c.length===0||o!==null)&&this.#S(),this.#c.length>0){if(o!==null){for(let e of this.#c)o.#c.push(e);this.#c=[]}else o=this}o!==null&&(zt.clear(),o.#_())}#v(e,t,n){e.f^=b;for(var r=e.first;r!==null;){var i=r.f,a=!!(i&96);if(!(a&&i&1024||i&8192||this.#f.has(r))&&r.fn!==null){a?r.f^=b:i&4?t.push(r):er(r)&&(i&16&&this.#d.add(r),or(r));var o=r.first;if(o!==null){r=o;continue}}for(;r!==null;){var s=r.next;if(s!==null){r=s;break}r=r.parent}}}#y(){for(var e=this.#t;e!==null;){if(!e.is_fork){for(let[t,[,n]]of this.current)if(e.current.has(t)&&!n)return e}e=e.#t}return null}#b(e){for(let[t,n]of e.current)!this.previous.has(t)&&e.previous.has(t)&&this.previous.set(t,e.previous.get(t)),this.current.set(t,n);for(let[t,n]of e.async_deriveds){let e=this.async_deriveds.get(t);e&&n.promise.then(e.resolve).catch(e.reject)}e.async_deriveds.clear(),this.transfer_effects(e.#u,e.#d);let t=e=>{var n=e.reactions;if(n!==null&&!(e.f&2&&!(e.f&6144)))for(let e of n){var r=e.f;if(r&2)t(e);else{var i=e;r&4194320&&!this.async_deriveds.has(i)&&(this.#d.delete(i),A(i,x),this.schedule(i))}}};for(let e of this.current.keys())t(e);this.oncommit(()=>e.discard()),e.#S(),j=this,this.#_()}#x(e){for(var t=0;t{this.#m=!1,this.linked&&this.flush()}))}transfer_effects(e,t){for(let t of e)this.#u.add(t);for(let e of t)this.#d.add(e);e.clear(),t.clear()}oncommit(e){this.#r.add(e)}ondiscard(e){this.#i.add(e)}settled(){return(this.#s??=y()).promise}static ensure(){if(j===null){let t=j=new e;!Tt&&!wt&&Qe(()=>{t.#e||t.flush()})}return j}apply(){St=null}schedule(e){if(Ct=e,e.b?.is_pending&&e.f&16777228&&!(e.f&32768)){e.b.defer_effect(e);return}this.#c.push(e)}#S(){if(this.linked){var e=this.#t,t=this.#n;e===null||(e.#n=t),t===null?bt=e:t.#t=e,this.linked=!1}}};function jt(e){var t=wt;wt=!0;try{var n;for(e&&(j!==null&&!j.is_fork&&j.flush(),n=e());;){if($e(),j===null)return n;j.flush()}}finally{wt=t}}function Mt(){try{Ve()}catch(e){pn(e,Ct)}}var Nt=null;function Pt(e){var t=e.length;if(t!==0){for(var n=0;n0)){zt.clear();for(let e of Nt){if(e.f&24576)continue;let t=[e],n=e.parent;for(;n!==null;)Nt.has(n)&&(Nt.delete(n),t.push(n)),n=n.parent;for(let e=t.length-1;e>=0;e--){let n=t[e];n.f&24576||or(n)}}Nt.clear()}}Nt=null}}function Ft(e){j.schedule(e)}function It(e,t){if(!(e.f&32&&e.f&1024)){e.f&2048?t.d.push(e):e.f&4096&&t.m.push(e),A(e,b);for(var n=e.first;n!==null;)It(n,t),n=n.next}}function Lt(e){A(e,b);for(var t=e.first;t!==null;)Lt(t),t=t.next}var Rt=new Set,zt=new Map,Bt=!1;function Vt(e,t){return{f:0,v:e,reactions:null,equals:Me,rv:0,wv:0}}function Ht(e,t){let n=Vt(e,t);return Kn(n),n}function M(t,n=!1,r=!0){let i=Vt(t);return n||(i.equals=Pe),e&&r&&D!==null&&D.l!==null&&(D.l.s??=[]).push(i),i}function N(e,t,n=!1){return B!==null&&(!Un||B.f&131072)&&Ye()&&B.f&4325394&&(Gn===null||!Gn.has(e))&&Ge(),Gt(e,n?Yt(t):t,Dt)}var Ut=null,Wt=0;function Gt(e,t,n=null){if(!e.equals(t)){Vn?zt.set(e,t):zt.has(e)||zt.set(e,e.v);var r=At.ensure();if(r.capture(e,t),e.f&2){let t=e;e.f&2048&>(t),St===null&&tt(t)}e.wv=$n(),Ut=null,Wt=0,Jt(e,x,n),Ut=null,Ye()&&H!==null&&H.f&1024&&!(H.f&96)&&(qn===null?Jn([e]):qn.push(e)),!r.is_fork&&Rt.size>0&&!Bt&&Kt()}return t}function Kt(){Bt=!1;for(let e of Rt){e.f&1024&&A(e,S);let t;try{t=er(e)}catch{t=!0}t&&or(e)}Rt.clear()}function qt(e){N(e,e.v+1)}function Jt(e,t,n){var r=e.reactions;if(r!==null){var i=Ye(),a=r.length;if(Wt+=a,Wt>1e5&&Ut===null&&(Ut=new Set),Ut!==null){if(Ut.has(e))return;Ut.add(e)}for(var o=0;o{if(Zn===c)return e();var t=B,n=Zn;V(null),Qn(c);var r=e();return V(t),Qn(n),r};return i&&n.set(`length`,Ht(e.length,s)),new Proxy(e,{defineProperty(e,t,r){(!(`value`in r)||r.configurable===!1||r.enumerable===!1||r.writable===!1)&&Ue();var i=n.get(t);return i===void 0?l(()=>{var e=Ht(r.value,s);return n.set(t,e),e}):N(i,r.value,!0),!0},deleteProperty(e,t){var i=n.get(t);if(i===void 0){if(t in e){let e=l(()=>Ht(r,s));n.set(t,e),qt(o)}}else N(i,r),qt(o);return!0},get(t,i,a){if(i===fe)return e;var o=n.get(i),c=i in t;if(o===void 0&&(!c||u(t,i)?.writable)&&(o=l(()=>Ht(Yt(c?t[i]:r),s)),n.set(i,o)),o!==void 0){var d=G(o);return d===r?void 0:d}return Reflect.get(t,i,a)},getOwnPropertyDescriptor(e,t){this.has?.(e,t);var i=Reflect.getOwnPropertyDescriptor(e,t),a=n.get(t);if(a!==void 0){var o=G(a);if(o===r)return;if(i&&`value`in i)i.value=o;else return{enumerable:!0,configurable:!0,value:o,writable:!0}}return i},has(e,t){if(t===fe)return!0;var i=n.get(t),a=i!==void 0&&i.v!==r||Reflect.has(e,t);return(i!==void 0||H!==null&&(!a||u(e,t)?.writable))&&(i===void 0&&(i=l(()=>Ht(a?Yt(e[t]):r,s)),n.set(t,i)),G(i)===r)?!1:a},set(e,t,a,c){var d=n.get(t),f=t in e;if(i&&t===`length`)for(var p=a;pHt(r,s)),n.set(p+``,m)):N(m,r)}if(d===void 0)(!f||u(e,t)?.writable)&&(d=l(()=>Ht(void 0,s)),N(d,Yt(a)),n.set(t,d));else{f=d.v!==r;var h=l(()=>Yt(a));N(d,h)}var g=Reflect.getOwnPropertyDescriptor(e,t);if(g?.set&&g.set.call(c,a),!f){if(i&&typeof t==`string`){var _=n.get(`length`),v=Number(t);Number.isInteger(v)&&v>=_.v&&N(_,v+1)}qt(o)}return!0},ownKeys(e){G(o);var t=Reflect.ownKeys(e).filter(e=>{var t=n.get(e);return t===void 0||t.v!==r});for(var[i,a]of n)a.v!==r&&!(i in e)&&t.push(i);return t},setPrototypeOf(){We()}})}function Xt(e){try{if(typeof e==`object`&&e&&fe in e)return e[fe]}catch{}return e}function Zt(e,t){return Object.is(Xt(e),Xt(t))}var Qt,$t,en,tn;function nn(){if(Qt===void 0){Qt=window,$t=/Firefox/.test(navigator.userAgent);var e=Element.prototype,t=Node.prototype,n=Text.prototype;en=u(t,`firstChild`).get,tn=u(t,`nextSibling`).get,h(e)&&(e[_e]=void 0,e[ge]=null,e[ve]=void 0,e.__e=void 0),h(n)&&(n[ye]=void 0)}}function rn(e=``){return document.createTextNode(e)}function an(e){return en.call(e)}function on(e){return tn.call(e)}function P(e,t){if(!C)return an(e);var n=an(w);if(n===null)n=w.appendChild(rn());else if(t&&n.nodeType!==3){var r=rn();return n?.before(r),T(r),r}return t&&dn(n),T(n),n}function sn(e,t=!1){if(!C){var n=an(e);return n instanceof Comment&&n.data===``?on(n):n}if(t){if(w?.nodeType!==3){var r=rn();return w?.before(r),T(r),r}dn(w)}return w}function F(e,t=!1){if(!C)return an(e);var n=P(e,t);return E(e),n}function I(e,t=1,n=!1){let r=C?w:e;for(var i;t--;)i=r,r=on(r);if(!C)return r;if(n){if(r?.nodeType!==3){var a=rn();return r===null?i?.after(a):r.before(a),T(a),a}dn(r)}return T(r),r}function cn(e){e.textContent=``}function ln(){return!1}function un(e,t,n){return t==null||t===`http://www.w3.org/1999/xhtml`?n?document.createElement(e,{is:n}):document.createElement(e):n?document.createElementNS(t,e,{is:n}):document.createElementNS(t,e)}function dn(e){if(e.nodeValue.length<65536)return;let t=e.nextSibling;for(;t!==null&&t.nodeType===3;)t.remove(),e.nodeValue+=t.nodeValue,t=e.nextSibling}function fn(e){var t=H;if(t===null)return B.f|=de,e;if(!(t.f&32768)&&!(t.f&4))throw e;pn(e,t)}function pn(e,t){if(!(t!==null&&t.f&16384)){for(;t!==null;){if(t.f&128&&!(t.f&33570816)){if(!(t.f&32768))throw e;try{t.b.error(e);return}catch(t){e=t}}t=t.parent}throw e}}function mn(e){H===null&&(B===null&&Be(e),ze()),Vn&&Re(e)}function hn(e,t){var n=t.last;n===null?t.last=t.first=e:(n.next=e,e.prev=n,t.last=e)}function gn(e,t){var n=H;n!==null&&n.f&8192&&(e|=ee);var r={ctx:D,deps:null,nodes:null,f:e|x|512,first:null,fn:t,last:null,next:null,parent:n,b:n&&n.b,prev:null,teardown:null,wv:0,ac:null};j?.register_created_effect(r);var i=r;if(e&4)Et===null?At.ensure().schedule(r):Et.push(r);else if(t!==null){try{or(r)}catch(e){throw z(r),e}i.deps===null&&i.teardown===null&&i.nodes===null&&i.first===i.last&&!(i.f&524288)&&(i=i.first,e&16&&e&65536&&i!==null&&(i.f|=ie))}if(i!==null&&(i.parent=n,n!==null&&hn(i,n),B!==null&&B.f&2&&!(e&64))){var a=B;(a.effects??=[]).push(i)}return r}function _n(){return B!==null&&!Un}function vn(e){let t=gn(8,null);return A(t,b),t.teardown=e,t}function yn(e){mn(`$effect`);var t=H.f;if(!B&&t&32&&D!==null&&!D.i){var n=D;(n.e??=[]).push(e)}else return bn(e)}function bn(e){return gn(4|se,e)}function xn(e){return mn(`$effect.pre`),gn(8|se,e)}function Sn(e){At.ensure();let t=gn(64|oe,e);return(e={})=>new Promise(n=>{e.outro?Pn(t,()=>{z(t),n(void 0)}):(z(t),n(void 0))})}function Cn(e){return gn(4,e)}function wn(e,t){var n=D,r={effect:null,ran:!1,deps:e};n.l.$.push(r),r.effect=Dn(()=>{if(e(),!r.ran){r.ran=!0;var n=H;try{Wn(n.parent),K(t)}finally{Wn(n)}}})}function Tn(){var e=D;Dn(()=>{for(var t of e.l.$){t.deps();var n=t.effect;n.f&1024&&n.deps!==null&&A(n,S),er(n)&&or(n),t.ran=!1}})}function En(e){return gn(ue|oe,e)}function Dn(e,t=0){return gn(8|t,e)}function L(e,t=[],n=[],r=[]){st(r,t,n,t=>{gn(8,()=>{e(...t.map(G))})})}function On(e,t=0){return gn(16|t,e)}function R(e){return gn(32|oe,e)}function kn(e){var t=e.teardown;if(t!==null){let n=Vn,r=B;Hn(!0),V(null);try{t.call(null)}catch(t){pn(t,e.parent)}finally{Hn(n),V(r)}}}function An(e,t=!1){var n=e.first;for(e.first=e.last=null;n!==null;){let e=n.ac;e!==null&&at(()=>{e.abort(xe)});var r=n.next;n.f&64?n.parent=null:z(n,t),n=r}}function jn(e){for(var t=e.first;t!==null;){var n=t.next;t.f&32||z(t),t=n}}function z(e,t=!0){var n=!1;(t||e.f&262144)&&e.nodes!==null&&e.nodes.end!==null&&(Mn(e.nodes.start,e.nodes.end),n=!0),e.f|=re,An(e,t&&!n),ar(e,0);var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)e.stop();kn(e),e.f^=re,e.f|=te;var i=e.parent;i!==null&&i.first!==null&&Nn(e),e.next=e.prev=e.teardown=e.ctx=e.deps=e.fn=e.nodes=e.ac=e.b=null}function Mn(e,t){for(;e!==null;){var n=e===t?null:on(e);e.remove(),e=n}}function Nn(e){var t=e.parent,n=e.prev,r=e.next;n!==null&&(n.next=r),r!==null&&(r.prev=n),t!==null&&(t.first===e&&(t.first=r),t.last===e&&(t.last=n))}function Pn(e,t,n=!0){var r=[];e.f|=256,Fn(e,r,!0);var i=()=>{n&&z(e),t&&t()},a=r.length;if(a>0){var o=()=>--a||i();for(var s of r)s.out(o)}else i()}function Fn(e,t,n){if(!(e.f&8192)){e.f^=ee;var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)(e.is_global||n)&&t.push(e);for(var i=e.first;i!==null;){var a=i.next;if(!(i.f&64)){var o=!!(i.f&65536)||!!(i.f&32)&&!!(e.f&16);Fn(i,t,o?n:!1)}i=a}}}function In(e){e.f&=-257,Ln(e,!0)}function Ln(e,t){if(!(e.f&256)&&e.f&8192){e.f^=ee,e.f&1024||(A(e,x),At.ensure().schedule(e));for(var n=e.first;n!==null;){var r=n.next,i=!!(n.f&65536)||!!(n.f&32);Ln(n,i?t:!1),n=r}var a=e.nodes&&e.nodes.t;if(a!==null)for(let e of a)(e.is_global||t)&&e.in()}}function Rn(e,t){if(e.nodes)for(var n=e.nodes.start,r=e.nodes.end;n!==null;){var i=n===r?null:on(n);t.append(n),n=i}}var zn=null,Bn=!1,Vn=!1;function Hn(e){Vn=e}var B=null,Un=!1;function V(e){B=e}var H=null;function Wn(e){H=e}var Gn=null;function Kn(e){B!==null&&(B.f&2097152||B.f&2)&&(Gn??=new Set).add(e)}var U=null,W=0,qn=null;function Jn(e){qn=e}var Yn=1,Xn=0,Zn=Xn;function Qn(e){Zn=e}function $n(){return++Yn}function er(e){var t=e.f;if(t&2048)return!0;if(t&4096){for(var n=e.deps,r=n.length,i=0;ie.wv)return!0}t&512&&St===null&&A(e,b)}return!1}function tr(e,t,n=!0){var r=e.reactions;if(r!==null&&!(Gn!==null&&Gn.has(e)))for(var i=0;i{e.ac.abort(xe)}),e.ac=null);try{e.f|=le;var u=e.fn,d=u();e.f|=ne;var f=rr(e);if(Ye()&&qn!==null&&!Un&&f!==null&&!(e.f&6146))for(var p=0;p0)for(t.length=W+U.length,r=0;r{c.ac.abort(xe),c.ac=null,A(c,x)}),vt(c),ar(c,0)}}function ar(e,t){var n=e.deps;if(n!==null)for(var r=t;rn?.call(this,e))}return e.startsWith(`pointer`)||e.startsWith(`touch`)||e===`wheel`?(i.__removed=!1,Qe(()=>{i.__removed||t.addEventListener(e,i,r)})):t.addEventListener(e,i,r),i}function hr(e,t,n,r,i){var a={capture:r,passive:i},o=mr(e,t,n,a);(t===document.body||t===window||t===document||t instanceof HTMLMediaElement)&&vn(()=>{o.__removed=!0,t.removeEventListener(e,o,a)})}function gr(e,t,n){(t[dr]??={})[e]=n}function _r(e){for(var t=0;t{yr=!1,vr=null}));var o=0,s=vr===e&&e[dr];if(s){var c=i.indexOf(s);if(c!==-1&&(t===document||t===window)){e[dr]=t;return}var u=i.indexOf(t);if(u===-1)return;c<=u&&(o=c)}if(a=i[o]||e.target,a!==t){l(e,`currentTarget`,{configurable:!0,get(){return a||n}});var d=B,f=H;V(null),Wn(null);try{for(var p,m=[];a!==null&&a!==t;){try{var h=a[dr]?.[r];h!=null&&(!a.disabled||e.target===a)&&h.call(a,e)}catch(e){p?m.push(e):p=e}if(e.cancelBubble)break;o++,a=o{throw e});throw p}}finally{e[dr]=t,delete e.currentTarget,V(d),Wn(f)}}}var xr=globalThis?.window?.trustedTypes&&globalThis.window.trustedTypes.createPolicy(`svelte-trusted-html`,{createHTML:e=>e});function Sr(e){return xr?.createHTML(e)??e}function Cr(e){var t=un(`template`);return t.innerHTML=Sr(e.replaceAll(``,``)),t.content}function wr(e,t){var n=H;n.nodes===null&&(n.nodes={start:e,end:t,a:null,t:null})}function J(e,t){var n=!!(t&1),r=!!(t&2),i,a=!e.startsWith(``);return()=>{if(C)return wr(w,null),w;i===void 0&&(i=Cr(a?e:``+e),n||(i=an(i)));var t=r||$t?document.importNode(i,!0):i.cloneNode(!0);if(n){var o=an(t),s=t.lastChild;wr(o,s)}else wr(t,t);return t}}function Y(e,t){if(C){var n=H;(!(n.f&32768)||n.nodes.end===null)&&(n.nodes.end=w),Oe();return}e!==null&&e.before(t)}[...`allowfullscreen.async.autofocus.autoplay.checked.controls.default.disabled.formnovalidate.indeterminate.inert.ismap.loop.multiple.muted.nomodule.novalidate.open.playsinline.readonly.required.reversed.seamless.selected.webkitdirectory.defer.disablepictureinpicture.disableremoteplayback`.split(`.`)];var Tr=[`touchstart`,`touchmove`];function Er(e){return Tr.includes(e)}function Dr(e){let t=0,n=Vt(0),r;return()=>{_n()&&(G(n),Dn(()=>(t===0&&(r=K(()=>e(()=>qt(n)))),t+=1,()=>{Qe(()=>{--t,t===0&&(r?.(),r=void 0,qt(n))})})))}}var Or=ie|oe;function kr(e,t,n,r){new Ar(e,t,n,r)}var Ar=class{parent;is_pending=!1;transform_error;#e;#t=C?w:null;#n;#r;#i;#a=null;#o=null;#s=null;#c=null;#l=0;#u=0;#d=!1;#f=new Set;#p=new Set;#m=null;#h=Dr(()=>(this.#m=Vt(this.#l),()=>{this.#m=null}));constructor(e,t,n,r){this.#e=e,this.#n=t,this.#r=e=>{var t=H;t.b=this,t.f|=128,n(e)},this.parent=H.b,this.transform_error=r??this.parent?.transform_error??(e=>e),this.#i=On(()=>{if(C){let e=this.#t;Oe();let t=e.data===`[!`;if(e.data.startsWith(`[?`)){let t=JSON.parse(e.data.slice(2));this.#_(t)}else t?this.#y():this.#g()}else this.#b()},Or),C&&(this.#e=w)}#g(){try{this.#a=R(()=>this.#r(this.#e))}catch(e){this.error(e)}}#_(e){let t=this.#n.failed,{reset:n,invoke_onerror:r}=this.#v(e);Qe(r),t&&(this.#s=R(()=>{t(this.#e,()=>e,()=>n)}))}#v(e){var t=!1,n=!1;let r=()=>{if(t){Ee();return}t=!0,n&&Ke(),this.#s!==null&&Pn(this.#s,()=>{this.#s=null}),this.#S(()=>{this.#b()})};return{reset:r,invoke_onerror:()=>{try{n=!0,this.#n.onerror?.(e,r),n=!1}catch(e){pn(e,this.#i&&this.#i.parent)}}}}#y(){let e=this.#n.pending;e&&(this.is_pending=!0,this.#o=R(()=>e(this.#e)),Qe(()=>{var e=this.#c=document.createDocumentFragment(),t=rn(),n=!1;if(e.append(t),this.#a=this.#S(()=>{try{return R(()=>this.#r(t))}catch(e){try{this.error(e),n=!0}catch(e){pn(e,this.#i.parent)}return null}}),this.#a===null){this.#c=null,n&&this.#x(j);return}this.#u===0&&(this.#e.before(e),this.#c=null,Pn(this.#o,()=>{this.#o=null}),this.#x(j))}))}#b(){try{if(this.is_pending=this.has_pending_snippet(),this.#u=0,this.#l=0,this.#a=R(()=>{this.#r(this.#e)}),this.#u>0){var e=this.#c=document.createDocumentFragment();Rn(this.#a,e);let t=this.#n.pending;this.#o=R(()=>t(this.#e))}else this.#x(j)}catch(e){this.error(e)}}#x(e){this.is_pending=!1,e.transfer_effects(this.#f,this.#p)}defer_effect(e){nt(e,this.#f,this.#p)}is_rendered(){return!this.is_pending&&(!this.parent||this.parent.is_rendered())}has_pending_snippet(){return!!this.#n.pending}#S(e){var t=H,n=B,r=D;Wn(this.#i),V(this.#i),qe(this.#i.ctx);try{return At.ensure(),e()}finally{Wn(t),V(n),qe(r)}}#C(e,t){if(!this.has_pending_snippet()){this.parent&&this.parent.#C(e,t);return}this.#u+=e,this.#u===0&&(this.#x(t),this.#o&&Pn(this.#o,()=>{this.#o=null}),this.#c&&=(this.#e.before(this.#c),null))}update_pending_count(e,t){this.#C(e,t),this.#l+=e,!(!this.#m||this.#d)&&(this.#d=!0,Qe(()=>{this.#d=!1,this.#m&&Gt(this.#m,this.#l)}))}get_effect_pending(){return this.#h(),G(this.#m)}error(e){if(!this.#n.onerror&&!this.#n.failed)throw e;j?.is_fork?(this.#a&&j.skip_effect(this.#a),this.#o&&j.skip_effect(this.#o),this.#s&&j.skip_effect(this.#s),j.oncommit(()=>{this.#w(e)})):this.#w(e)}#w(e){this.#a&&=(z(this.#a),null),this.#o&&=(z(this.#o),null),this.#s&&=(z(this.#s),null),C&&(T(this.#t),ke(),T(Ae()));let t=this.#n.failed,n=e=>{let{reset:n,invoke_onerror:r}=this.#v(e);r(),t&&(this.#s=this.#S(()=>{try{return R(()=>{var r=H;r.b=this,r.f|=128,t(this.#e,()=>e,()=>n)})}catch(e){return pn(e,this.#i.parent),null}}))};Qe(()=>{var t;try{t=this.transform_error(e)}catch(e){pn(e,this.#i&&this.#i.parent);return}typeof t==`object`&&t&&typeof t.then==`function`?t.then(n,e=>pn(e,this.#i&&this.#i.parent)):n(t)})}};function X(e,t){var n=t==null?``:typeof t==`object`?`${t}`:t;n!==(e[ye]??=e.nodeValue)&&(e[ye]=n,e.nodeValue=`${n}`)}function jr(e,t){return Nr(e,t)}var Mr=new Map;function Nr(e,{target:t,anchor:r,props:i={},events:a,context:o,intro:s=!0,transformError:l}){nn();var u=void 0,d=Sn(()=>{var s=r??t.appendChild(rn());kr(s,{pending:()=>{}},t=>{O({});var r=D;if(o&&(r.c=o),a&&(i.$$events=a),C&&wr(t,null),u=e(t,i)||Je(),C&&(H.nodes.end=w,w===null||w.nodeType!==8||w.data!==`]`))throw we(),n;k()},l);var d=new Set,f=e=>{for(var n=0;n{for(var e of d)for(let r of[t,document]){var n=Mr.get(r),i=n.get(e);--i==0?(r.removeEventListener(e,br),n.delete(e),n.size===0&&Mr.delete(r)):n.set(e,i)}pr.delete(f),s!==r&&s.parentNode?.removeChild(s)}});return Pr.set(u,d),u}var Pr=new WeakMap,Fr=class{anchor;#e=new Map;#t=new Map;#n=new Map;#r=new Set;#i=!0;constructor(e,t=!0){this.anchor=e,this.#i=t}#a=e=>{if(this.#e.has(e)){var t=this.#e.get(e),n=this.#t.get(t);if(n)In(n),this.#r.delete(t);else{var r=this.#n.get(t);r&&(In(r.effect),this.#t.set(t,r.effect),this.#n.delete(t),r.fragment.lastChild.remove(),this.anchor.before(r.fragment),n=r.effect)}for(let[t,n]of this.#e){if(this.#e.delete(t),t===e)break;let r=this.#n.get(n);r&&(z(r.effect),this.#n.delete(n))}for(let[e,r]of this.#t){if(e===t||this.#r.has(e))continue;let i=()=>{if(Array.from(this.#e.values()).includes(e)){var t=document.createDocumentFragment();Rn(r,t),t.append(rn()),this.#n.set(e,{effect:r,fragment:t})}else z(r);this.#r.delete(e),this.#t.delete(e)};this.#i||!n?(this.#r.add(e),Pn(r,i,!1)):i()}}};#o=e=>{this.#e.delete(e);let t=Array.from(this.#e.values());for(let[e,n]of this.#n)t.includes(e)||(z(n.effect),this.#n.delete(e))};ensure(e,t){var n=j,r=ln();if(t&&!this.#t.has(e)&&!this.#n.has(e)){if(r){var i=document.createDocumentFragment(),a=rn();i.append(a),this.#n.set(e,{effect:R(()=>t(a)),fragment:i})}else this.#t.set(e,R(()=>t(this.anchor)))}if(this.#e.set(n,e),r){for(let[t,r]of this.#t)t===e?n.unskip_effect(r):n.skip_effect(r);for(let[t,r]of this.#n)t===e?n.unskip_effect(r.effect):n.skip_effect(r.effect);n.oncommit(this.#a),n.ondiscard(this.#o)}else C&&(this.anchor=w),this.#a(n)}};function Ir(t){D===null&&Fe(`onMount`),e&&D.l!==null?Lr(D).m.push(t):yn(()=>{let e=K(t);if(typeof e==`function`)return e})}function Lr(e){var t=e.l;return t.u??={a:[],b:[],m:[]}}function Z(e,t,n=!1){var r;C&&(r=w,Oe());var i=new Fr(e),a=n?ie:0;function o(e,t){if(C){var n=je(r);if(e!==parseInt(n.substring(1))){var a=Ae();T(a),i.anchor=a,De(!1),i.ensure(e,t),De(!0);return}}i.ensure(e,t)}On(()=>{var e=!1;t((t,n=0)=>{e=!0,o(n,t)}),e||o(-1,null)},a)}var Rr=Symbol(`NaN`);function zr(e,t,n){C&&Oe();var r=new Fr(e),i=!Ye();On(()=>{var e=t();e!==e&&(e=Rr),i&&typeof e==`object`&&e&&(e={}),r.ensure(e,n)})}function Br(e,t){return t}function Vr(e,t,n){for(var r=[],i=t.length,a,o=t.length,s=0;s{if(a){if(a.pending.delete(n),a.done.add(n),a.pending.size===0){var t=e.outrogroups;Hr(e,c(a.done)),t.delete(a),t.size===0&&(e.outrogroups=null)}}else--o},!1)}if(o===0){var l=r.length===0&&n!==null&&e.pending.size===0;if(l){var u=n,d=u.parentNode;cn(d),d.append(u),e.items.clear()}Hr(e,t,!l)}else a={pending:new Set(t),done:new Set},(e.outrogroups??=new Set).add(a)}function Hr(e,t,n=!0){var r;if(e.pending.size>0){r=new Set;for(let t of e.pending.values())for(let n of t)r.add(e.items.get(n).e)}for(var i=0;i{var e=n();return a(e)?e:e==null?[]:c(e)}),p,m=new Map,h=!0;function g(e){v.effect.f&16384||(v.pending.delete(e),v.fallback=d,Kr(v,p,s,t,r),d!==null&&(p.length===0?d.f&33554432?(d.f^=ce,Jr(d,null,s)):In(d):Pn(d,()=>{d=null})))}function _(e){v.pending.delete(e)}var v={effect:On(()=>{p=G(f);var e=p.length;let a=!1;C&&je(s)===`[!`!=(e===0)&&(s=Ae(),T(s),De(!1),a=!0);for(var c=new Set,u=j,v=ln(),y=0;yo(s)):(d=R(()=>o(Ur??=rn())),d.f|=ce)),e>c.size&&Le(``,``,``),C&&e>0&&T(Ae()),!h){if(m.set(u,c),v){for(let[e,t]of l)c.has(e)||u.skip_effect(t.e);u.oncommit(g),u.ondiscard(_)}else g(u)}a&&De(!0),G(f)}),flags:t,items:l,pending:m,outrogroups:null,fallback:d};h=!1,C&&(s=w)}function Gr(e){for(;e!==null&&!(e.f&32);)e=e.next;return e}function Kr(e,t,n,r,i){var a=!!(r&8),o=t.length,s=e.items,l=Gr(e.effect.first),u,d=null,f,p=[],m=[],h,g,_,v;if(a)for(v=0;v0){var re=r&4&&o===0?n:null;if(a){for(v=0;v{if(f!==void 0)for(_ of f)_.nodes?.a?.apply()})}function qr(e,t,n,r,i,a,o,s){var c=o&1?o&16?Vt(n):M(n,!1,!1):null,l=o&2?Vt(i):null;return{v:c,i:l,e:R(()=>(a(t,c??n,l??i,s),()=>{e.delete(r)}))}}function Jr(e,t,n){if(e.nodes)for(var r=e.nodes.start,i=e.nodes.end,a=t&&!(t.f&33554432)?t.nodes.start:n;r!==null;){var o=on(r);if(a.before(r),r===i)return;r=o}}function Yr(e,t,n){t===null?e.effect.first=n:t.next=n,n===null?e.effect.last=t:n.prev=t}function Xr(e,t){let n=null,r=C;var i;if(C){n=w;for(var a=an(document.head);a!==null&&(a.nodeType!==8||a.data!==e);)a=on(a);if(a===null)De(!1);else{var o=on(a);a.remove(),T(o)}}C||(i=document.head.appendChild(rn()));try{On(()=>{var e=R(()=>t(i));e.f|=ae,C||(e.nodes===null?e.nodes={start:i,end:i,a:null,t:null}:e.nodes.end=i)})}finally{r&&(De(!0),T(n))}}function Zr(e){var t,n,r=``;if(typeof e==`string`||typeof e==`number`)r+=e;else if(typeof e==`object`){if(Array.isArray(e)){var i=e.length;for(t=0;t=0;){var s=o+a;(o===0||ei.includes(r[o-1]))&&(s===r.length||ei.includes(r[s]))?r=(o===0?``:r.substring(0,o))+r.substring(s+1):o=s}}return r===``?null:r}function ni(e,t,n,r,i,a){var o=e[_e];if(C||o!==n||o===void 0){var s=ti(n,r,a);(!C||s!==e.getAttribute(`class`))&&(s==null?e.removeAttribute(`class`):t?e.className=s:e.setAttribute(`class`,s)),e[_e]=n}else if(a&&i!==a)for(var c in a){var l=!!a[c];(i==null||l!==!!i[c])&&e.classList.toggle(c,l)}return a}function ri(e,t){t?e.hasAttribute(`selected`)||e.setAttribute(`selected`,``):e.removeAttribute(`selected`)}function ii(e,t){var n=e.__defaultValue,r=e.multiple,i=r?n??[]:null;if(!r||a(i)){var o=e.selectedIndex,s=t&&r?new Set(e.selectedOptions):null;for(var c of e.options){var l=ci(c);ri(c,r?i.includes(l):Zt(l,n))}if(t){if(s!==null)for(c of e.options){var u=s.has(c);c.selected!==u&&(c.selected=u)}else e.selectedIndex!==o&&(e.selectedIndex=o)}}}function ai(e,t,n=!1){if(e.multiple){if(t==null)return;if(!a(t))return Te();for(var r of e.options)r.selected=t.includes(ci(r));return}for(r of e.options)if(Zt(ci(r),t)){r.selected=!0;return}(!n||t!==void 0)&&(e.selectedIndex=-1)}function oi(e){var t=new MutationObserver(t=>{t.every(li)||(`__defaultValue`in e&&ii(e,!1),`__value`in e&&ai(e,e.__value))});t.observe(e,{childList:!0,subtree:!0,attributes:!0,attributeFilter:[`value`]}),vn(()=>{t.disconnect()})}function si(e,t,n=t){var r=new WeakSet,i=!0;ot(e,`change`,t=>{var i=t?`[selected]`:`:checked`,a;if(e.multiple)a=[].map.call(e.querySelectorAll(i),ci);else{var o=e.querySelector(i)??e.querySelector(`option:not([disabled])`);a=o&&ci(o)}n(a),e.__value=a,j!==null&&r.add(j)}),Cn(()=>{var a=t();if(e===document.activeElement){var o=j;if(r.has(o))return}if(ai(e,a,i),i&&a===void 0){var s=e.querySelector(`:checked`);s!==null&&(a=ci(s),n(a))}e.__value=a,i=!1})}function ci(e){return`__value`in e?e.__value:e.value}function li(e){if(e.target.closest(`selectedcontent`)!==null)return!0;if(e.type===`childList`){var t=[...e.addedNodes,...e.removedNodes];return t.length>0&&t.every(e=>e.nodeName===`SELECTEDCONTENT`)}return!1}var ui=Symbol(`is custom element`),di=Symbol(`is html`),fi=Se?`link`:`LINK`;function pi(e){if(C){var t=!1,n=()=>{if(!t){if(t=!0,e.hasAttribute(`value`)){var n=e.value;Q(e,`value`,null),e.value=n}if(e.hasAttribute(`checked`)){var r=e.checked;Q(e,`checked`,null),e.checked=r}}};e[be]=n,Qe(n),it()}}function mi(e,t){var n=hi(e);n.checked!==(n.checked=t??void 0)&&(e.checked=t)}function Q(e,t,n,r){var i=hi(e);C&&(i[t]=e.getAttribute(t),t===`src`||t===`srcset`||t===`href`&&e.nodeName===fi)||i[t]!==(i[t]=n)&&(t===`loading`&&(e[he]=n),n==null?e.removeAttribute(t):typeof n!=`string`&&_i(e).has(t)?e[t]=n:e.setAttribute(t,n))}function hi(e){return e[ge]??={[ui]:e.nodeName.includes(`-`),[di]:e.namespaceURI===i}}var gi=new Map;function _i(e){var t=e.getAttribute(`is`)||e.nodeName,n=gi.get(t);if(n)return n;gi.set(t,n=new Set);for(var r,i=e,a=Element.prototype;a!==i;){for(var o in r=d(i),r)r[o].set&&o!==`innerHTML`&&o!==`textContent`&&o!==`innerText`&&n.add(o);i=m(i)}return n}function vi(e,t,n=t){var r=new WeakSet;ot(e,`input`,async i=>{var a=i?e.defaultValue:e.value;if(a=yi(e)?bi(a):a,n(a),j!==null&&r.add(j),await sr(),a!==(a=t())){var o=e.selectionStart,s=e.selectionEnd,c=e.value.length;if(e.value=a??``,s!==null){var l=e.value.length;o===s&&s===c&&l>c?(e.selectionStart=l,e.selectionEnd=l):(e.selectionStart=o,e.selectionEnd=Math.min(s,l))}}}),(C&&e.defaultValue!==e.value||K(t)==null&&e.value)&&(n(yi(e)?bi(e.value):e.value),j!==null&&r.add(j)),Dn(()=>{var n=t();if(e===document.activeElement){var i=j;if(r.has(i))return}yi(e)&&n===bi(e.value)||(e.type!==`date`||n||e.value)&&n!==e.value&&(e.value=n??``)})}function yi(e){var t=e.type;return t===`number`||t===`range`}function bi(e){return e===``?null:+e}function xi(e=!1){let t=D,n=t.l.u;if(!n)return;let r=()=>q(t.s);if(e){let e=0,n={},i=dt(()=>{let r=!1,i=t.s;for(let e in i)i[e]!==n[e]&&(n[e]=i[e],r=!0);return r&&e++,e});r=()=>G(i)}n.b.length&&xn(()=>{Si(t,r),v(n.b)}),yn(()=>{let e=K(()=>n.m.map(_));return()=>{for(let t of e)typeof t==`function`&&t()}}),n.a.length&&yn(()=>{Si(t,r),v(n.a)})}function Si(e,t){if(e.l.s)for(let t of e.l.s)G(t);t()}function Ci(e,t,n){if(e==null)return t(void 0),n&&n(void 0),g;let r=K(()=>e.subscribe(t,n));return r.unsubscribe?()=>r.unsubscribe():r}var wi=[];function Ti(e,t=g){let n=null,r=new Set;function i(t){if(Ne(e,t)&&(e=t,n)){let t=!wi.length;for(let t of r)t[1](),wi.push(t,e);if(t){for(let e=0;e{r.delete(c),r.size===0&&n&&(n(),n=null)}}return{set:i,update:a,subscribe:o}}function Ei(e){let t;return Ci(e,e=>t=e)(),t}var Di=!1,Oi=Symbol(`unmounted`);function ki(e,t,n){let r=n[t]??={store:null,source:M(void 0),unsubscribe:g};if(r.store!==e&&!(Oi in n)){if(r.unsubscribe(),r.store=e??null,e==null)r.source.v=void 0,r.unsubscribe=g;else{var i=!0;r.unsubscribe=Ci(e,e=>{i?r.source.v=e:N(r.source,e)}),i=!1}}return e&&Oi in n?Ei(e):G(r.source)}function Ai(){let e={};function t(){vn(()=>{for(var t in e)e[t].unsubscribe();l(e,Oi,{enumerable:!1,value:!0})})}return[e,t]}function ji(e){var t=Di;try{return Di=!1,[e(),Di]}finally{Di=t}}function $(t,n,r,i){var a=!e||!!(r&2),o=!!(r&8),s=!!(r&16),c=i,l=!0,d=void 0,f=()=>s&&a?(d??=dt(i),G(d)):(l&&(l=!1,c=s?K(i):i),c);let p;if(o){var m=fe in t||me in t;p=u(t,n)?.set??(m&&n in t?e=>t[n]=e:void 0)}var h,g=!1;o?[h,g]=ji(()=>t[n]):h=t[n],h===void 0&&i!==void 0&&(h=f(),p&&(a&&He(n),p(h)));var _=a?()=>{var e=t[n];return e===void 0?f():(l=!0,e)}:()=>{var e=t[n];return e!==void 0&&(c=void 0),e===void 0?c:e};if(a&&!(r&4))return _;if(p){var v=t.$$legacy;return(function(e,t){return arguments.length>0?((!a||!t||v||g)&&p(t?_():e),e):_()})}var y=!1,b=(r&1?dt:mt)(()=>(y=!1,_()));o&&G(b);var x=H;return(function(e,t){if(arguments.length>0){let n=t?G(b):a&&o?Yt(e):e;return N(b,n),y=!0,c!==void 0&&(c=n),e}return Vn&&y||x.f&16384?b.v:G(b)})}function Mi(e){let t=Ti({paired:!!e,controller:!1,busy:!1,error:``}),n=e,r,i=``,a={paired:!!e,controller:!1,busy:!1,error:``},o=!1;function s(e){a={...a,...e},t.set(a)}async function c(e=!1){if(!o&&n){o=!0;try{let t=await fetch(`/api/state`,{cache:`no-store`,headers:{"X-Setup-Session-Key":n}});if(!t.ok)throw Error(`The local setup session is unavailable.`);s({view:await t.json(),...e?{}:{error:``}})}catch{s({view:void 0,error:`Connection lost. The CLI may have stopped. Check the terminal before trying again.`})}finally{o=!1}}}async function l(){if(n)try{let e=await fetch(`/api/bootstrap`,{cache:`no-store`,headers:{"X-Setup-Session-Key":n}});if(!e.ok)throw Error(`Could not join this local session.`);let t=await e.json();r=t.capability,i=t.takeoverTicket,s({controller:t.controller,error:``}),await c()}catch{n=void 0,r=void 0,i=``,s({view:void 0,paired:!1,controller:!1,error:`Could not connect to the local setup session. Check the terminal and pair again.`})}}async function u(e){if(!(a.busy||a.paired)){s({busy:!0,error:``});try{let t=await fetch(`/api/pair`,{method:`POST`,cache:`no-store`,headers:{"Content-Type":`application/json`},body:JSON.stringify({code:e.trim().toLowerCase()})}),r=await t.json();if(!t.ok)throw Error(String(r.error??`Pairing was rejected.`));if(typeof r.sessionKey!=`string`||!/^[a-f0-9]{64}$/.test(r.sessionKey))throw Error(`Invalid local pairing response.`);n=r.sessionKey,s({paired:!0,error:``}),await l()}catch(e){s({error:e instanceof Error?e.message:`Could not pair this browser.`})}finally{s({busy:!1})}}}async function d(e,t,i=!0){let a=await fetch(e,{method:`POST`,cache:`no-store`,headers:{"Content-Type":`application/json`,"X-Setup-Session-Key":n,...i&&r?{"X-Setup-Capability":r}:{}},body:JSON.stringify(t)}),o=await a.json();if(!a.ok)throw Error(String(o.error??`The request was rejected.`));return o}async function f(e,t){if(!a.busy&&a.controller&&a.view?.promptRevision===e){s({busy:!0,error:``});try{await d(`/api/answer`,{revision:e,value:t}),await c()}catch(e){let t=e instanceof Error?e.message:`Could not submit this answer.`;s({error:t}),/read-only|Control moved/.test(t)?await l():await c(!0)}finally{s({busy:!1})}}}async function p(){if(a.controller&&!a.busy){s({busy:!0,error:``});try{await d(`/api/cancel`,{}),await c()}catch(e){let t=e instanceof Error?e.message:`Cancellation failed.`;s({error:t}),/read-only|Control moved/.test(t)&&await l()}finally{s({busy:!1})}}}async function m(){try{let e=await d(`/api/takeover`,{ticket:i},!1);r=String(e.capability),s({controller:!0,error:``}),await c()}catch(e){s({error:e instanceof Error?e.message:`Takeover failed.`}),await l()}}async function h(){try{await d(`/api/close`,{})}catch{}}return{subscribe:t.subscribe,pair:u,connect:l,refresh:c,submit:f,cancel:p,takeOver:m,close:h}}var Ni=J(`
                                                        • `),Pi=J(``);function Fi(e,t){O(t,!1);let n=$(t,`journey`,8),r=[`Repository`,`Setup choices`,`Setup PAT`,`Plan`,`Bot PAT & credentials`,`Apply`];xi();var i=Pi(),a=I(P(i),4);Wr(a,5,()=>r,Br,(e,t,r)=>{var i=Ni();let a;var o=P(i),s=F(o,!0),c=F(I(o,2),!0);E(i),L(e=>{Q(i,`aria-current`,(q(n()),K(()=>n()?.position===r+1?`step`:void 0))),a=ni(i,1,``,null,a,{current:n()?.position===r+1,completed:(n()?.position??0)>r+1}),X(s,e),X(c,G(t))},[()=>(q(n()),K(()=>(n()?.position??0)>r+1?`✓`:String(r+1).padStart(2,`0`)))]),Y(e,i)}),E(a),ke(2),E(i),Y(e,i),k()}var Ii=J(`
                                                          `);function Li(e,t){O(t,!1);let n=M(`system`);wn(()=>G(n),()=>{document.documentElement.dataset.theme=G(n)}),Tn();var r=Ii(),i=P(r);let a;var o=I(i,2);let s;var c=I(o,2);let l;E(r),L(()=>{Q(i,`aria-pressed`,G(n)===`system`),a=ni(i,1,``,null,a,{active:G(n)===`system`}),Q(o,`aria-pressed`,G(n)===`light`),s=ni(o,1,``,null,s,{active:G(n)===`light`}),Q(c,`aria-pressed`,G(n)===`dark`),l=ni(c,1,``,null,l,{active:G(n)===`dark`})}),gr(`click`,i,()=>N(n,`system`)),gr(`click`,o,()=>N(n,`light`)),gr(`click`,c,()=>N(n,`dark`)),Y(e,r),k()}_r([`click`]);var Ri=J(`
                                                          LOCAL SESSION
                                                          `);function zi(e,t){let n=$(t,`repository`,8);var r=Ri(),i=P(r),a=F(I(P(i),2),!0);E(i);var o=I(i,2);Li(I(P(o)),{}),E(o),E(r),L(()=>X(a,n()??`Connecting…`)),Y(e,r)}var Bi=J(`

                                                          `,1);function Vi(e,t){O(t,!1);let n=M(),r=$(t,`view`,8);wn(()=>q(r()),()=>{N(n,r()?.outcome===`complete`?`Setup complete.`:r()?.outcome===`dry-run`?`Preview complete.`:r()?.outcome===`cancelled`?`Setup cancelled.`:r()?.outcome?`Setup needs attention.`:r()?.prompt?.title??`Preparing your setup…`)}),Tn(),xi();var i=Bi(),a=sn(i),o=I(P(a)),s=F(I(o));E(a);var c=I(a,2),l=F(c,!0),u=F(I(c,2),!0);L((e,t)=>{X(o,` ${e??``} `),X(s,`${t??``} / 06`),X(l,G(n)),X(u,(q(r()),K(()=>r()?.outcome?`The terminal contains the detailed result. Review the next steps below before closing this session.`:`One decision at a time. Your choices shape the permissions, plan and credentials needed for this repository.`)))},[()=>(q(r()),K(()=>r()?.journey?.current?.toUpperCase()??`GETTING READY`)),()=>(q(r()),K(()=>String(r()?.journey?.position??1).padStart(2,`0`)))]),Y(e,i),k()}function Hi(e){try{let t=new URL(e??``);return t.protocol===`https:`&&t.hostname===`github.com`&&(t.pathname===`/settings/personal-access-tokens`||t.pathname.startsWith(`/settings/personal-access-tokens/`))?t.toString():void 0}catch{return}}var Ui=J(``),Wi=J(``);function Gi(e,t){let n=$(t,`label`,8),r=$(t,`variant`,8,`primary`),i=$(t,`disabled`,8,!1),a=$(t,`arrow`,8,!1),o=$(t,`onClick`,8);var s=Wi(),c=P(s,!0),l=I(c),u=e=>{Y(e,Ui())};Z(l,e=>{a()&&e(u)}),E(s),L(()=>{ni(s,1,$r(r())),s.disabled=i(),X(c,n())}),gr(`click`,s,function(...e){o()?.apply(this,e)}),Y(e,s)}_r([`click`]);var Ki=J(`Open GitHub link ↗`),qi=J(`

                                                          `);function Ji(e,t){O(t,!1);let n=M(),r=$(t,`tone`,8,`info`),i=$(t,`title`,8),a=$(t,`message`,8),o=$(t,`link`,8,void 0),s=$(t,`actionLabel`,8,void 0),c=$(t,`onAction`,8,void 0);wn(()=>q(o()),()=>{N(n,Hi(o()))}),Tn(),xi();var l=qi(),u=P(l),d=F(u,!0),f=I(u),p=F(f,!0),m=I(f,2),h=e=>{var t=Ki();L(()=>Q(t,`href`,G(n))),Y(e,t)};Z(m,e=>{G(n)&&e(h)});var g=I(m,2),_=e=>{Gi(e,{get label(){return s()},variant:`secondary`,get onClick(){return c()}})};Z(g,e=>{s()&&c()&&e(_)}),E(l),L(()=>{ni(l,1,`banner ${r()??``}`),Q(l,`role`,r()===`error`?`alert`:`status`),X(d,i()),X(p,a())}),Y(e,l),k()}function Yi(e){let t=String(e.question.defaultValue),n=t.split(`,`).map(e=>e.trim()).filter(Boolean);return{value:t,selected:e.question.kind===`multi-select`?(e.question.choices??[]).filter(e=>e!==`All`&&n.includes(e.split(` — `)[0])):e.question.kind===`scope-overrides`?n:[]}}function Xi(e,t){return t===`All`?e.includes(`All`)?[]:[`All`]:e.includes(t)?e.filter(e=>e!==t):[...e.filter(e=>e!==`All`),t]}function Zi(e,t,n){return e.question.kind===`scope-overrides`||e.question.kind===`multi-select`?n.length?n.join(`,`):`none`:t}var Qi=J(`PERMISSION PREVIEW`),$i=J(`
                                                          `),ea=J(``),ta=J(``),na=J(``),ra=J(`
                                                          `),ia=J(``),aa=J(`

                                                          `,1);function oa(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`controller`,8),i=$(t,`busy`,8),a=$(t,`onSubmit`,8),o=Yi(n()),s=M(o.value),c=M(o.selected);xi();var l=aa(),u=sn(l),d=P(u),f=F(d,!0),p=I(d),m=e=>{Y(e,Qi())};Z(p,e=>{q(n()),K(()=>n().phase===`permission-intent`)&&e(m)}),E(u);var h=I(u,2),g=e=>{var t=$i(),i=P(t);let a;var o=I(i);let c;E(t),L(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),Q(i,`aria-pressed`,G(s)===`yes`||G(s)===`true`),i.disabled=!r(),a=ni(i,1,``,null,a,{selected:G(s)===`yes`||G(s)===`true`}),Q(o,`aria-pressed`,G(s)===`no`||G(s)===`false`),o.disabled=!r(),c=ni(o,1,``,null,c,{selected:G(s)===`no`||G(s)===`false`})}),gr(`click`,i,()=>N(s,`yes`)),gr(`click`,o,()=>N(s,`no`)),Y(e,t)},_=e=>{var t=ta();Wr(t,5,()=>(q(n()),K(()=>n().question.choices??[])),Br,(e,t)=>{var n=ea(),r=F(n,!0),i={};L(()=>{X(r,G(t)),i!==(i=G(t))&&(n.value=(n.__value=i)??``)}),Y(e,n)}),E(t),oi(t),L(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),t.disabled=!r()}),si(t,()=>G(s),e=>N(s,e)),Y(e,t)},v=e=>{var t=ra();Wr(t,5,()=>(q(n()),K(()=>n().question.kind===`multi-select`?n().question.choices??[]:n().question.allowedNames??[])),Br,(e,t)=>{var n=na(),i=P(n);pi(i);var a=F(I(i),!0);E(n),L(e=>{mi(i,e),i.disabled=!r(),X(a,G(t))},[()=>(G(c),G(t),K(()=>G(c).includes(G(t))))]),gr(`change`,i,()=>N(c,Xi(G(c),G(t)))),Y(e,n)}),E(t),L(()=>Q(t,`aria-label`,(q(n()),K(()=>n().question.label)))),Y(e,t)},y=e=>{var t=ia();pi(t),L(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),Q(t,`type`,(q(n()),K(()=>n().question.kind===`number`?`number`:`text`))),Q(t,`min`,(q(n()),K(()=>n().question.kind===`number`?0:void 0))),t.disabled=!r()}),vi(t,()=>G(s),e=>N(s,e)),Y(e,t)};Z(h,e=>{q(n()),K(()=>n().question.kind===`boolean`)?e(g):(q(n()),K(()=>n().question.kind===`choice`)?e(_,1):(q(n()),K(()=>n().question.kind===`multi-select`||n().question.kind===`scope-overrides`)?e(v,2):e(y,-1)))});var b=I(h,2),x=F(b),S=I(b,2);{let e=mt(()=>!r()||i());Gi(S,{label:`Continue`,arrow:!0,onClick:()=>a()(Zi(n(),G(s),G(c))),get disabled(){return G(e)}})}L((e,t)=>{X(f,e),X(x,`Suggested answer: ${t??``} · You can review choices again before creating your setup PAT.`)},[()=>(q(n()),K(()=>n().question.label.replace(` (Space toggles, Enter confirms)`,``))),()=>(q(n()),K(()=>String(n().question.defaultValue)||`none`))]),Y(e,l),k()}_r([`click`,`change`]);var sa=J(``),ca=J(`
                                                          `);function la(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`controller`,8),i=$(t,`busy`,8),a=$(t,`onSubmit`,8);xi();var o=ca();Wr(o,5,()=>(q(n()),K(()=>n().choices)),Br,(e,t)=>{var n=sa(),o=F(P(n),!0);ke(),E(n),L(()=>{n.disabled=!r()||i(),X(o,G(t))}),gr(`click`,n,()=>a()(G(t))),Y(e,n)}),E(o),Y(e,o),k()}_r([`click`]);var ua=J(`Open the official GitHub PAT form

                                                          Check the signed-in account, select Only select repositories, then select this repository. GitHub handles 2FA and creates the PAT.

                                                          `,1),da=J(`

                                                          Sent only to this local process. It will not be shown again or saved in browser storage.

                                                          `),fa=J(` `,1);function pa(e,t){O(t,!1);let n=M(),r=$(t,`prompt`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8),s=M(``);function c(){let e=G(s);r().kind===`secret`&&N(s,``),o()(e)}wn(()=>q(r()),()=>{N(n,Hi(r().link))}),Tn(),xi();var l=fa(),u=sn(l),d=e=>{var t=ua(),r=sn(t);ke(),L(()=>Q(r,`href`,G(n))),Y(e,t)};Z(u,e=>{G(n)&&e(d)});var f=I(u,2),p=F(f,!0),m=I(f,2);pi(m);var h=I(m,2),g=e=>{Y(e,da())};Z(h,e=>{q(r()),K(()=>r().kind===`secret`)&&e(g)});var _=I(h,2);{let e=mt(()=>(q(i()),q(a()),q(r()),G(s),K(()=>!i()||a()||!r().optional&&!G(s).trim())));Gi(_,{label:`Continue`,arrow:!0,onClick:c,get disabled(){return G(e)}})}L(()=>{X(p,(q(r()),K(()=>r().kind===`secret`?`Paste the value here`:`Your answer`))),Q(m,`type`,(q(r()),K(()=>r().kind===`secret`?`password`:`text`))),m.disabled=!i(),Q(m,`placeholder`,(q(r()),K(()=>r().kind===`secret`?`Hidden after submission`:`Type your answer`)))}),vi(m,()=>G(s),e=>N(s,e)),Y(e,l),k()}var ma=J(`
                                                        • `),ha=J(`

                                                            `),ga=J(`

                                                            Before you continue

                                                              `),_a=J(`

                                                              Review exactly what this run may change. The bot PAT and any additional credentials are collected next.

                                                              `,1);function va(e,t){O(t,!1);let n=M(),r=$(t,`prompt`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8);wn(()=>q(r()),()=>{N(n,[{title:`Files`,items:r().plan.files},{title:`Workflows`,items:r().plan.workflows},{title:`Variables`,items:r().plan.variables},{title:`Secret names`,items:r().plan.secrets}])}),Tn(),xi();var s=_a(),c=I(sn(s),2);Wr(c,5,()=>G(n),Br,(e,t)=>{var n=ha(),r=P(n),i=P(r),a=F(I(i),!0);E(r);var o=I(r);Wr(o,5,()=>(G(t),K(()=>G(t).items)),Br,(e,t)=>{var n=ma(),r=F(n,!0);L(()=>X(r,G(t))),Y(e,n)}),E(o),E(n),L(()=>{X(i,`${G(t),K(()=>G(t).title)??``} `),X(a,(G(t),K(()=>G(t).items.length)))}),Y(e,n)}),E(c);var l=I(c,2),u=e=>{var t=ga(),n=I(P(t));Wr(n,5,()=>(q(r()),K(()=>r().plan.warnings)),Br,(e,t)=>{var n=ma(),r=F(n,!0);L(()=>X(r,G(t))),Y(e,n)}),E(n),E(t),Y(e,t)};Z(l,e=>{q(r()),K(()=>r().plan.warnings.length)&&e(u)});var d=I(l,2),f=P(d);{let e=mt(()=>!i()||a());Gi(f,{label:`Stop here`,variant:`secondary`,onClick:()=>o()(`decline`),get disabled(){return G(e)}})}var p=I(f);{let e=mt(()=>!i()||a());Gi(p,{label:`Approve this plan`,arrow:!0,onClick:()=>o()(`approve`),get disabled(){return G(e)}})}E(d),Y(e,s),k()}var ya=J(`

                                                              `),ba=J(`
                                                              CURRENT DECISION
                                                              `);function xa(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`revision`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8);xi();var s=ba(),c=P(s),l=F(I(P(c)));E(c);var u=I(c,2),d=e=>{var t=ya(),r=F(t,!0);L(()=>X(r,(q(n()),K(()=>n().description)))),Y(e,t)};Z(u,e=>{q(n()),K(()=>`description`in n()&&n().description)&&e(d)});var f=I(u,2),p=e=>{oa(e,{get prompt(){return n()},get controller(){return i()},get busy(){return a()},get onSubmit(){return o()}})},m=e=>{la(e,{get prompt(){return n()},get controller(){return i()},get busy(){return a()},get onSubmit(){return o()}})},h=e=>{pa(e,{get prompt(){return n()},get controller(){return i()},get busy(){return a()},get onSubmit(){return o()}})},g=e=>{va(e,{get prompt(){return n()},get controller(){return i()},get busy(){return a()},get onSubmit(){return o()}})};Z(f,e=>{q(n()),K(()=>n().kind===`question`)?e(p):(q(n()),K(()=>n().kind===`choice`||n().kind===`confirm`)?e(m,1):(q(n()),K(()=>n().kind===`text`||n().kind===`secret`)?e(h,2):(q(n()),K(()=>n().kind===`plan`)&&e(g,3))))}),E(s),L(()=>X(l,`SESSION ${r()??``}`)),Y(e,s),k()}var Sa=J(` `),Ca=J(`
                                                            • `),wa=J(`

                                                                Conditional grants depend on the choices and GitHub facts. A final audit runs before setup changes.
                                                                `),Ta=J(`

                                                                Permissions follow your choices

                                                                We’ll show the exact grants before you create each PAT. Nothing is created just by opening this page.

                                                                `),Ea=J(``);function Da(e,t){O(t,!1);let n=$(t,`view`,8);xi();var r=Ea(),i=P(r),a=F(I(P(i),3),!0);E(i);var o=I(i,2),s=e=>{var t=wa(),r=I(P(t)),i=F(r),a=I(r),o=F(a,!0),s=I(a,2);Wr(s,5,()=>(q(n()),K(()=>n().permissions.report?.checks??n().permissions.requirements??[])),Br,(e,t)=>{var n=Ca(),r=P(n),i=P(r),a=I(i),o=F(a),s=F(I(a),!0);E(r);var c=I(r),l=P(c,!0),u=I(l),d=e=>{var n=Sa(),r=F(n,!0);L(()=>X(r,(G(t),K(()=>G(t).status)))),Y(e,n)};Z(u,e=>{`status`in G(t)&&e(d)}),E(c),E(n),L(()=>{X(i,`${G(t),K(()=>G(t).permission)??``} `),X(o,`${G(t),K(()=>G(t).scope)??``} · ${G(t),K(()=>G(t).applicability)??``}`),X(s,(G(t),K(()=>G(t).reason))),X(l,(G(t),K(()=>G(t).level)))}),Y(e,n)}),E(s),ke(2),E(t),L(()=>{X(i,`${q(n()),K(()=>n().permissions.role===`setup`?`Setup PAT`:`Bot PAT`)??``} access`),X(o,(q(n()),K(()=>n().permissions.report?`Read-only access check`:`Provisional least-privilege grants`)))}),Y(e,t)},c=e=>{Y(e,Ta())};Z(o,e=>{q(n()),K(()=>n().permissions)?e(s):e(c,-1)}),E(r),L(()=>X(a,(q(n()),K(()=>n().repository)))),Y(e,r),k()}var Oa=J(`

                                                                `);function ka(e,t){O(t,!1);let n=M(),r=M(),i=$(t,`outcome`,8),a=$(t,`controller`,8),o=$(t,`onClose`,8);wn(()=>q(i()),()=>{N(n,i()===`complete`?`Your configuration was applied`:i()===`dry-run`?`No changes were made`:i()===`cancelled`||i()===`blocked`?`No setup changes started`:`Check partial changes before retrying`)}),wn(()=>q(i()),()=>{N(r,i()===`complete`?`The temporary setup PAT is not revoked automatically. Delete it in GitHub after confirming your setup. Keep the bot PAT until you rotate the installed Secret.`:i()===`partial`?`A bot Secret write or another change may have succeeded. Inspect GitHub and run copilot doctor before replacing or deleting its PAT.`:`Copilot did not begin applying setup changes. PATs you created in GitHub still exist until you delete them there.`)}),Tn();var s=Oa(),c=P(s),l=F(c,!0),u=I(c),d=F(u,!0),f=I(u),p=F(f,!0),m=I(f,2),h=e=>{Gi(e,{label:`Close local session`,get onClick(){return o()}})};Z(m,e=>{a()&&e(h)}),E(s),L(()=>{X(l,i()===`complete`?`✓`:`!`),X(d,G(n)),X(p,G(r))}),Y(e,s),k()}var Aa=J(`

                                                                Working on the next step

                                                                The local process is checking your answers and preparing the next decision. Keep this page open.

                                                                `);function ja(e){Y(e,Aa())}var Ma=J(`
                                                                PRIVATE LOCAL SESSION

                                                                Pair this browser

                                                                Find the 16-character pairing code in the terminal that started copilot setup --web. Enter it here to view or control this setup. The code is never placed in the browser URL or stored after this page closes.

                                                                Keep the code private. After refreshing this page, enter it again to reconnect.

                                                                `);function Na(e,t){O(t,!1);let n=$(t,`busy`,8),r=$(t,`onPair`,8),i=M(``);function a(){let e=G(i);N(i,``),r()(e)}xi();var o=Ma(),s=I(P(o),6),c=I(P(s),2);pi(c);var l=I(c,4);{let e=mt(()=>(q(n()),G(i),K(()=>n()||G(i).trim().length!==16)));Gi(l,{label:`Connect to local setup`,arrow:!0,onClick:a,get disabled(){return G(e)}})}E(s),E(o),L(()=>c.disabled=n()),hr(`submit`,s,e=>{e.preventDefault(),a()}),vi(c,()=>G(i),e=>N(i,e)),Y(e,o),k()}var Pa=J(``),Fa=J(`
                                                                `),Ia=J(``),La=J(`
                                                                `,1),Ra=J(`
                                                                LOCALHOST ONLY NO CLOUD SETUP ACCOUNT GITHUB OWNS PAT ISSUANCE
                                                                `);function za(e,t){O(t,!1);let n=()=>ki(a,`$session`,r),[r,i]=Ai(),a=Mi();Ir(()=>{let e=window.setInterval(()=>{n().paired&&!n().view?.outcome&&a.refresh()},900);return()=>window.clearInterval(e)});async function o(){window.confirm(`Cancel this local setup session? PATs already created in GitHub will still exist.`)&&await a.cancel()}async function s(e){let t=n().view?.promptRevision;t!==void 0&&await a.submit(t,e)}xi();var c=Ra();Xr(`16t12jp`,e=>{Y(e,Pa())});var l=P(c);{let e=mt(()=>n().view?.journey);Fi(l,{get journey(){return G(e)}})}var u=I(l,2),d=P(u);{let e=mt(()=>n().view?.repository);zi(d,{get repository(){return G(e)}})}var f=I(d,2),p=P(f),m=e=>{Vi(e,{get view(){return n().view}})};Z(p,e=>{n().paired&&e(m)});var h=I(p,2),g=e=>{var t=Fa(),r=I(P(t));E(t),L(()=>X(r,` Reviewing saved choices — pass ${n().view.journey.choiceReviewPass??``}. This is the same setup run, not a restart.`)),Y(e,t)};Z(h,e=>{n().view?.journey?.choiceReviewPass&&n().view.journey.choiceReviewPass>1&&!n().view.outcome&&e(g)});var _=I(h,2),v=e=>{Ji(e,{tone:`warning`,title:`Read-only tab`,message:`Another tab controls this session. You can review progress here or explicitly take over.`,actionLabel:`Take control in this tab`,get onAction(){return a.takeOver}})};Z(_,e=>{!n().controller&&n().view&&e(v)});var y=I(_,2),b=e=>{Ji(e,{tone:`error`,title:`Needs attention`,get message(){return n().error}})};Z(y,e=>{n().error&&e(b)});var x=I(y,2),S=e=>{{let t=mt(()=>n().view.message.tone===`success`?`Checked`:n().view.message.tone===`warning`?`Please note`:n().view.message.tone===`error`?`Needs attention`:`Progress update`);Ji(e,{get tone(){return n().view.message.tone},get title(){return G(t)},get message(){return n().view.message.text},get link(){return n().view.message.link}})}};Z(x,e=>{n().view?.message&&e(S)});var ee=I(x,2),te=e=>{Na(e,{get busy(){return n().busy},get onPair(){return a.pair}})},ne=e=>{ka(e,{get outcome(){return n().view.outcome},get controller(){return n().controller},get onClose(){return a.close}})},re=e=>{var t=La(),r=sn(t),i=P(r);zr(i,()=>n().view.promptRevision,e=>{xa(e,{get prompt(){return n().view.prompt},get revision(){return n().view.revision},get controller(){return n().controller},get busy(){return n().busy},onSubmit:s})}),Da(I(i,2),{get view(){return n().view}}),E(r);var a=I(r,2),c=e=>{var t=Ia();L(()=>t.disabled=n().busy),gr(`click`,t,o),Y(e,t)};Z(a,e=>{n().controller&&n().view.journey?.current!==`Apply`&&e(c)}),Y(e,t)},ie=e=>{ja(e,{})};Z(ee,e=>{n().paired?n().view?.outcome?e(ne,1):n().view?.prompt?e(re,2):e(ie,-1):e(te)}),ke(2),E(f),E(u),E(c),Y(e,c),k(),i()}_r([`click`]),jr(za,{target:document.getElementById(`app`)}); \ No newline at end of file diff --git a/build/web/assets/index-D3fDoDei.js b/build/web/assets/index-D3fDoDei.js new file mode 100644 index 000000000..4fd66ad47 --- /dev/null +++ b/build/web/assets/index-D3fDoDei.js @@ -0,0 +1,2 @@ +(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})(),typeof window<`u`&&((window.__svelte??={}).v??=new Set).add(`5`);var e=!1;function t(){e=!0}t();var n={},r=Symbol(`uninitialized`),i=`http://www.w3.org/1999/xhtml`,a=Array.isArray,o=Array.prototype.indexOf,s=Array.prototype.includes,c=Array.from,l=Object.defineProperty,u=Object.getOwnPropertyDescriptor,d=Object.getOwnPropertyDescriptors,f=Object.prototype,p=Array.prototype,m=Object.getPrototypeOf,h=Object.isExtensible,g=()=>{};function _(e){return e()}function v(e){for(var t=0;t{e=n,t=r}),resolve:e,reject:t}}var b=1024,x=2048,S=4096,ee=8192,te=16384,ne=32768,re=1<<25,ie=65536,ae=1<<18,oe=1<<19,se=1<<20,ce=1<<25,le=1<<21,ue=1<<22,de=1<<23,fe=Symbol(`$state`),pe=Symbol(`component`),me=Symbol(`legacy props`),he=Symbol(``),ge=Symbol(`attributes`),_e=Symbol(`class`),ve=Symbol(`style`),ye=Symbol(`text`),be=Symbol(`form reset`),xe=new class extends Error{name=`StaleReactionError`;message="The reaction that called `getAbortSignal()` was re-run or destroyed"},Se=!!globalThis.document?.contentType&&globalThis.document.contentType.includes(`xml`);function Ce(){console.warn(`https://svelte.dev/e/derived_inert`)}function we(e){console.warn(`https://svelte.dev/e/hydration_mismatch`)}function Te(){console.warn(`https://svelte.dev/e/select_multiple_invalid_value`)}function Ee(){console.warn(`https://svelte.dev/e/svelte_boundary_reset_noop`)}var C=!1;function De(e){C=e}var w;function T(e){if(e===null)throw we(),n;return w=e}function Oe(){return T(an(w))}function E(e){if(C){if(an(w)!==null)throw we(),n;w=e}}function ke(e=1){if(C){for(var t=e,n=w;t--;)n=an(n);w=n}}function Ae(e=!0){for(var t=0,n=w;;){if(n.nodeType===8){var r=n.data;if(r===`]`){if(t===0)return n;--t}else(r===`[`||r===`[!`||r[0]===`[`&&!isNaN(Number(r.slice(1))))&&(t+=1)}var i=an(n);e&&n.remove(),n=i}}function je(e){if(!e||e.nodeType!==8)throw we(),n;return e.data}function Me(e){return e===this.v}function Ne(e,t){return e==e?e!==t||typeof e==`object`&&!!e||typeof e==`function`:t==t}function Pe(e){return!Ne(e,this.v)}function Fe(e){throw Error(`https://svelte.dev/e/lifecycle_outside_component`)}function Ie(){throw Error(`https://svelte.dev/e/async_derived_orphan`)}function Le(e,t,n){throw Error(`https://svelte.dev/e/each_key_duplicate`)}function Re(e){throw Error(`https://svelte.dev/e/effect_in_teardown`)}function ze(){throw Error(`https://svelte.dev/e/effect_in_unowned_derived`)}function Be(e){throw Error(`https://svelte.dev/e/effect_orphan`)}function Ve(){throw Error(`https://svelte.dev/e/effect_update_depth_exceeded`)}function He(e){throw Error(`https://svelte.dev/e/props_invalid_value`)}function Ue(){throw Error(`https://svelte.dev/e/state_descriptors_fixed`)}function We(){throw Error(`https://svelte.dev/e/state_prototype_fixed`)}function Ge(){throw Error(`https://svelte.dev/e/state_unsafe_mutation`)}function Ke(){throw Error(`https://svelte.dev/e/svelte_boundary_reset_onerror`)}var D=null;function qe(e){D=e}function O(t,n=!1,r){D={p:D,i:!1,c:null,e:null,s:t,x:null,r:H,l:e&&!n?{s:null,u:null,$:[]}:null}}function k(e){var t=D,n=t.e;if(n!==null){t.e=null;for(var r of n)yn(r)}return e!==void 0&&(t.x=e),t.i=!0,D=t.p,Je(e)}function Je(e={}){return l(e,pe,{value:!0}),e}function Ye(){return!e||D!==null&&D.l===null}var Xe=[];function Ze(){var e=Xe;Xe=[],v(e)}function Qe(e){if(Xe.length===0&&!wt){var t=Xe;queueMicrotask(()=>{t===Xe&&Ze()})}Xe.push(e)}function $e(){for(;Xe.length>0;)Ze()}var et=~(x|S|b);function A(e,t){e.f=e.f&et|t}function tt(e){e.f&512||e.deps===null?A(e,b):A(e,S)}function nt(e,t,n){e.f&2048?t.add(e):e.f&4096&&n.add(e),A(e,b)}var rt=!1;function it(){rt||(rt=!0,document.addEventListener(`reset`,e=>{Promise.resolve().then(()=>{if(!e.defaultPrevented)for(let t of e.target.elements)t[be]?.()})},{capture:!0}))}function at(e){var t=V,n=H;Un(null),Wn(null);try{return e()}finally{Un(t),Wn(n)}}function ot(e,t,n,r=n){e.addEventListener(t,()=>at(n));let i=e[be];e[be]=i?()=>{i(),r(!0)}:()=>r(!0),it()}function st(e,t,n,r){let i=Ye()?dt:mt;var a=e.filter(e=>!e.settled),o=t.map(i);if(n.length===0&&a.length===0){r(o);return}var s=H,c=ct(),l=a.length===1?a[0].promise:a.length>1?Promise.all(a.map(e=>e.promise)):null;function u(e){if(!(s.f&16384)){c();try{r([...o,...e])}catch(e){fn(e,s)}lt()}}var d=ut();if(n.length===0){l.then(()=>u([])).finally(d);return}function f(){Promise.all(n.map(e=>pt(e))).then(u).catch(e=>fn(e,s)).finally(d)}l?l.then(()=>{c(),f(),lt()}):f()}function ct(){var e=H,t=V,n=D,r=j;return function(i=!0){Wn(e),Un(t),qe(n),i&&!(e.f&16384)&&(r?.activate(),r?.apply())}}function lt(e=!0){Wn(null),Un(null),qe(null),e&&j?.deactivate()}function ut(){var e=H,t=e.b,n=j,r=!!t?.is_rendered();return t?.update_pending_count(1,n),n.increment(r,e),()=>{t?.update_pending_count(-1,n),n.decrement(r,e)}}function dt(e){var t=2|x;return H!==null&&(H.f|=oe),{ctx:D,deps:null,effects:null,equals:Me,f:t,fn:e,reactions:null,rv:0,v:r,wv:0,parent:H,ac:null}}var ft=Symbol(`obsolete`);function pt(e,t,n){let i=H;i===null&&Ie();var a=void 0,o=Vt(r),s=!V,c=new Set;return Tn(()=>{var t=H,n=y();a=n.promise;try{Promise.resolve(e()).then(n.resolve,e=>{e!==xe&&n.reject(e)}).finally(lt)}catch(e){n.reject(e),lt()}var r=j;if(s){if(t.f&32768)var l=ut();if(i.b?.is_rendered())r.async_deriveds.get(t)?.reject(ft);else for(let e of c.values())e.reject(ft);c.add(n),r.async_deriveds.set(t,n)}let u=(e,t=void 0)=>{l?.(),c.delete(n),t!==ft&&(r.activate(),t?(o.f|=de,Gt(o,t)):(o.f&8388608&&(o.f^=de),Gt(o,e)),r.deactivate())};n.promise.then(u,e=>u(null,e||`unknown`))}),_n(()=>{for(let e of c)e.reject(ft)}),new Promise(e=>{function t(n){function r(){n===a?e(o):t(a)}n.then(r,r)}t(a)})}function mt(e){let t=dt(e);return t.equals=Pe,t}function ht(e){var t=e.effects;if(t!==null){e.effects=null;for(var n=0;n{t.ac.abort(xe),t.ac=null}),t.fn!==null&&(t.teardown=g),ar(t,0),kn(t))}function yt(e){if(e.effects!==null)for(let t of e.effects)t.teardown&&t.fn!==null&&or(t)}var bt=null,j=null,xt=null,St=null,Ct=null,wt=!1,Tt=!1,Et=null,Dt=null,Ot=0,kt=1,At=class e{id=kt++;#e=!1;linked=!0;#t=null;#n=null;async_deriveds=new Map;current=new Map;previous=new Map;#r=new Set;#i=new Set;#a=0;#o=new Map;#s=null;#c=[];#l=[];#u=new Set;#d=new Set;#f=new Map;#p=new Set;is_fork=!1;#m=!1;constructor(){bt===null?bt=this:(bt.#n=this,this.#t=bt),bt=this}#h(){if(this.is_fork)return!0;for(let n of this.#o.keys()){for(var e=n,t=!1;e.parent!==null;){if(this.#f.has(e)){t=!0;break}e=e.parent}if(!t)return!0}return!1}skip_effect(e){this.#f.has(e)||this.#f.set(e,{d:[],m:[]}),this.#p.delete(e)}unskip_effect(e,t=e=>this.schedule(e)){var n=this.#f.get(e);if(n){this.#f.delete(e);for(var r of n.d)A(r,x),t(r);for(r of n.m)A(r,S),t(r)}this.#p.add(e)}#g(){var e=[];for(let i of this.#c)if(!(i.f&16384||!(i.f&6144))){for(var t=i,n=!1;t.parent!==null;){t=t.parent;var r=t.f;if(r&96){if(!(r&1024)){n=!0;break}t.f^=b}}n||e.push(t)}return this.#c=[],e}#_(){this.#e=!0;for(let e of this.#u)this.#d.delete(e),A(e,x),this.schedule(e);for(let e of this.#d)A(e,S),this.schedule(e);this.apply();for(var t=Et=[],n=[],r=Dt=[];this.#c.length>0;){Ot++>1e3&&(this.#S(),Mt());for(let e of this.#g())try{this.#v(e,t,n)}catch(t){throw Lt(e),this.#h()||this.discard(),t}}if(j=null,r.length>0){var i=e.ensure();for(let e of r)i.schedule(e)}if(Et=null,Dt=null,this.#h()){this.#x(n),this.#x(t);for(let[e,t]of this.#f)It(e,t);r.length>0&&j.#_();return}let a=this.#y();if(a){this.#x(n),this.#x(t),a.#b(this);return}this.#u.clear(),this.#d.clear();for(let e of this.#r)e(this);this.#r.clear(),xt=this,Pt(n),Pt(t),xt=null,this.#s?.resolve();var o=j;if(this.#a===0&&(this.#c.length===0||o!==null)&&this.#S(),this.#c.length>0){if(o!==null){for(let e of this.#c)o.#c.push(e);this.#c=[]}else o=this}o!==null&&(zt.clear(),o.#_())}#v(e,t,n){e.f^=b;for(var r=e.first;r!==null;){var i=r.f,a=!!(i&96);if(!(a&&i&1024||i&8192||this.#f.has(r))&&r.fn!==null){a?r.f^=b:i&4?t.push(r):er(r)&&(i&16&&this.#d.add(r),or(r));var o=r.first;if(o!==null){r=o;continue}}for(;r!==null;){var s=r.next;if(s!==null){r=s;break}r=r.parent}}}#y(){for(var e=this.#t;e!==null;){if(!e.is_fork){for(let[t,[,n]]of this.current)if(e.current.has(t)&&!n)return e}e=e.#t}return null}#b(e){for(let[t,n]of e.current)!this.previous.has(t)&&e.previous.has(t)&&this.previous.set(t,e.previous.get(t)),this.current.set(t,n);for(let[t,n]of e.async_deriveds){let e=this.async_deriveds.get(t);e&&n.promise.then(e.resolve).catch(e.reject)}e.async_deriveds.clear(),this.transfer_effects(e.#u,e.#d);let t=e=>{var n=e.reactions;if(n!==null&&!(e.f&2&&!(e.f&6144)))for(let e of n){var r=e.f;if(r&2)t(e);else{var i=e;r&4194320&&!this.async_deriveds.has(i)&&(this.#d.delete(i),A(i,x),this.schedule(i))}}};for(let e of this.current.keys())t(e);this.oncommit(()=>e.discard()),e.#S(),j=this,this.#_()}#x(e){for(var t=0;t{this.#m=!1,this.linked&&this.flush()}))}transfer_effects(e,t){for(let t of e)this.#u.add(t);for(let e of t)this.#d.add(e);e.clear(),t.clear()}oncommit(e){this.#r.add(e)}ondiscard(e){this.#i.add(e)}settled(){return(this.#s??=y()).promise}static ensure(){if(j===null){let t=j=new e;!Tt&&!wt&&Qe(()=>{t.#e||t.flush()})}return j}apply(){St=null}schedule(e){if(Ct=e,e.b?.is_pending&&e.f&16777228&&!(e.f&32768)){e.b.defer_effect(e);return}this.#c.push(e)}#S(){if(this.linked){var e=this.#t,t=this.#n;e===null||(e.#n=t),t===null?bt=e:t.#t=e,this.linked=!1}}};function jt(e){var t=wt;wt=!0;try{var n;for(e&&(j!==null&&!j.is_fork&&j.flush(),n=e());;){if($e(),j===null)return n;j.flush()}}finally{wt=t}}function Mt(){try{Ve()}catch(e){fn(e,Ct)}}var Nt=null;function Pt(e){var t=e.length;if(t!==0){for(var n=0;n0)){zt.clear();for(let e of Nt){if(e.f&24576)continue;let t=[e],n=e.parent;for(;n!==null;)Nt.has(n)&&(Nt.delete(n),t.push(n)),n=n.parent;for(let e=t.length-1;e>=0;e--){let n=t[e];n.f&24576||or(n)}}Nt.clear()}}Nt=null}}function Ft(e){j.schedule(e)}function It(e,t){if(!(e.f&32&&e.f&1024)){e.f&2048?t.d.push(e):e.f&4096&&t.m.push(e),A(e,b);for(var n=e.first;n!==null;)It(n,t),n=n.next}}function Lt(e){A(e,b);for(var t=e.first;t!==null;)Lt(t),t=t.next}var Rt=new Set,zt=new Map,Bt=!1;function Vt(e,t){return{f:0,v:e,reactions:null,equals:Me,rv:0,wv:0}}function Ht(e,t){let n=Vt(e,t);return Kn(n),n}function M(t,n=!1,r=!0){let i=Vt(t);return n||(i.equals=Pe),e&&r&&D!==null&&D.l!==null&&(D.l.s??=[]).push(i),i}function N(e,t,n=!1){return V!==null&&(!Hn||V.f&131072)&&Ye()&&V.f&4325394&&(Gn===null||!Gn.has(e))&&Ge(),Gt(e,n?Yt(t):t,Dt)}var Ut=null,Wt=0;function Gt(e,t,n=null){if(!e.equals(t)){Bn?zt.set(e,t):zt.has(e)||zt.set(e,e.v);var r=At.ensure();if(r.capture(e,t),e.f&2){let t=e;e.f&2048&>(t),St===null&&tt(t)}e.wv=$n(),Ut=null,Wt=0,Jt(e,x,n),Ut=null,Ye()&&H!==null&&H.f&1024&&!(H.f&96)&&(qn===null?Jn([e]):qn.push(e)),!r.is_fork&&Rt.size>0&&!Bt&&Kt()}return t}function Kt(){Bt=!1;for(let e of Rt){e.f&1024&&A(e,S);let t;try{t=er(e)}catch{t=!0}t&&or(e)}Rt.clear()}function qt(e){N(e,e.v+1)}function Jt(e,t,n){var r=e.reactions;if(r!==null){var i=Ye(),a=r.length;if(Wt+=a,Wt>1e5&&Ut===null&&(Ut=new Set),Ut!==null){if(Ut.has(e))return;Ut.add(e)}for(var o=0;o{if(Zn===c)return e();var t=V,n=Zn;Un(null),Qn(c);var r=e();return Un(t),Qn(n),r};return i&&n.set(`length`,Ht(e.length,s)),new Proxy(e,{defineProperty(e,t,r){(!(`value`in r)||r.configurable===!1||r.enumerable===!1||r.writable===!1)&&Ue();var i=n.get(t);return i===void 0?l(()=>{var e=Ht(r.value,s);return n.set(t,e),e}):N(i,r.value,!0),!0},deleteProperty(e,t){var i=n.get(t);if(i===void 0){if(t in e){let e=l(()=>Ht(r,s));n.set(t,e),qt(o)}}else N(i,r),qt(o);return!0},get(t,i,a){if(i===fe)return e;var o=n.get(i),c=i in t;if(o===void 0&&(!c||u(t,i)?.writable)&&(o=l(()=>Ht(Yt(c?t[i]:r),s)),n.set(i,o)),o!==void 0){var d=G(o);return d===r?void 0:d}return Reflect.get(t,i,a)},getOwnPropertyDescriptor(e,t){this.has?.(e,t);var i=Reflect.getOwnPropertyDescriptor(e,t),a=n.get(t);if(a!==void 0){var o=G(a);if(o===r)return;if(i&&`value`in i)i.value=o;else return{enumerable:!0,configurable:!0,value:o,writable:!0}}return i},has(e,t){if(t===fe)return!0;var i=n.get(t),a=i!==void 0&&i.v!==r||Reflect.has(e,t);return(i!==void 0||H!==null&&(!a||u(e,t)?.writable))&&(i===void 0&&(i=l(()=>Ht(a?Yt(e[t]):r,s)),n.set(t,i)),G(i)===r)?!1:a},set(e,t,a,c){var d=n.get(t),f=t in e;if(i&&t===`length`)for(var p=a;pHt(r,s)),n.set(p+``,m)):N(m,r)}if(d===void 0)(!f||u(e,t)?.writable)&&(d=l(()=>Ht(void 0,s)),N(d,Yt(a)),n.set(t,d));else{f=d.v!==r;var h=l(()=>Yt(a));N(d,h)}var g=Reflect.getOwnPropertyDescriptor(e,t);if(g?.set&&g.set.call(c,a),!f){if(i&&typeof t==`string`){var _=n.get(`length`),v=Number(t);Number.isInteger(v)&&v>=_.v&&N(_,v+1)}qt(o)}return!0},ownKeys(e){G(o);var t=Reflect.ownKeys(e).filter(e=>{var t=n.get(e);return t===void 0||t.v!==r});for(var[i,a]of n)a.v!==r&&!(i in e)&&t.push(i);return t},setPrototypeOf(){We()}})}function Xt(e){try{if(typeof e==`object`&&e&&fe in e)return e[fe]}catch{}return e}function Zt(e,t){return Object.is(Xt(e),Xt(t))}var Qt,$t,en,tn;function nn(){if(Qt===void 0){Qt=window,$t=/Firefox/.test(navigator.userAgent);var e=Element.prototype,t=Node.prototype,n=Text.prototype;en=u(t,`firstChild`).get,tn=u(t,`nextSibling`).get,h(e)&&(e[_e]=void 0,e[ge]=null,e[ve]=void 0,e.__e=void 0),h(n)&&(n[ye]=void 0)}}function P(e=``){return document.createTextNode(e)}function rn(e){return en.call(e)}function an(e){return tn.call(e)}function F(e,t){if(!C)return rn(e);var n=rn(w);if(n===null)n=w.appendChild(P());else if(t&&n.nodeType!==3){var r=P();return n?.before(r),T(r),r}return t&&un(n),T(n),n}function on(e,t=!1){if(!C){var n=rn(e);return n instanceof Comment&&n.data===``?an(n):n}if(t){if(w?.nodeType!==3){var r=P();return w?.before(r),T(r),r}un(w)}return w}function I(e,t=!1){if(!C)return rn(e);var n=F(e,t);return E(e),n}function L(e,t=1,n=!1){let r=C?w:e;for(var i;t--;)i=r,r=an(r);if(!C)return r;if(n){if(r?.nodeType!==3){var a=P();return r===null?i?.after(a):r.before(a),T(a),a}un(r)}return T(r),r}function sn(e){e.textContent=``}function cn(){return!1}function ln(e,t,n){return t==null||t===`http://www.w3.org/1999/xhtml`?n?document.createElement(e,{is:n}):document.createElement(e):n?document.createElementNS(t,e,{is:n}):document.createElementNS(t,e)}function un(e){if(e.nodeValue.length<65536)return;let t=e.nextSibling;for(;t!==null&&t.nodeType===3;)t.remove(),e.nodeValue+=t.nodeValue,t=e.nextSibling}function dn(e){var t=H;if(t===null)return V.f|=de,e;if(!(t.f&32768)&&!(t.f&4))throw e;fn(e,t)}function fn(e,t){if(!(t!==null&&t.f&16384)){for(;t!==null;){if(t.f&128&&!(t.f&33570816)){if(!(t.f&32768))throw e;try{t.b.error(e);return}catch(t){e=t}}t=t.parent}throw e}}function pn(e){H===null&&(V===null&&Be(e),ze()),Bn&&Re(e)}function mn(e,t){var n=t.last;n===null?t.last=t.first=e:(n.next=e,e.prev=n,t.last=e)}function hn(e,t){var n=H;n!==null&&n.f&8192&&(e|=ee);var r={ctx:D,deps:null,nodes:null,f:e|x|512,first:null,fn:t,last:null,next:null,parent:n,b:n&&n.b,prev:null,teardown:null,wv:0,ac:null};j?.register_created_effect(r);var i=r;if(e&4)Et===null?At.ensure().schedule(r):Et.push(r);else if(t!==null){try{or(r)}catch(e){throw B(r),e}i.deps===null&&i.teardown===null&&i.nodes===null&&i.first===i.last&&!(i.f&524288)&&(i=i.first,e&16&&e&65536&&i!==null&&(i.f|=ie))}if(i!==null&&(i.parent=n,n!==null&&mn(i,n),V!==null&&V.f&2&&!(e&64))){var a=V;(a.effects??=[]).push(i)}return r}function gn(){return V!==null&&!Hn}function _n(e){let t=hn(8,null);return A(t,b),t.teardown=e,t}function vn(e){pn(`$effect`);var t=H.f;if(!V&&t&32&&D!==null&&!D.i){var n=D;(n.e??=[]).push(e)}else return yn(e)}function yn(e){return hn(4|se,e)}function bn(e){return pn(`$effect.pre`),hn(8|se,e)}function xn(e){At.ensure();let t=hn(64|oe,e);return(e={})=>new Promise(n=>{e.outro?Nn(t,()=>{B(t),n(void 0)}):(B(t),n(void 0))})}function Sn(e){return hn(4,e)}function Cn(e,t){var n=D,r={effect:null,ran:!1,deps:e};n.l.$.push(r),r.effect=En(()=>{if(e(),!r.ran){r.ran=!0;var n=H;try{Wn(n.parent),K(t)}finally{Wn(n)}}})}function wn(){var e=D;En(()=>{for(var t of e.l.$){t.deps();var n=t.effect;n.f&1024&&n.deps!==null&&A(n,S),er(n)&&or(n),t.ran=!1}})}function Tn(e){return hn(ue|oe,e)}function En(e,t=0){return hn(8|t,e)}function R(e,t=[],n=[],r=[]){st(r,t,n,t=>{hn(8,()=>{e(...t.map(G))})})}function Dn(e,t=0){return hn(16|t,e)}function z(e){return hn(32|oe,e)}function On(e){var t=e.teardown;if(t!==null){let n=Bn,r=V;Vn(!0),Un(null);try{t.call(null)}catch(t){fn(t,e.parent)}finally{Vn(n),Un(r)}}}function kn(e,t=!1){var n=e.first;for(e.first=e.last=null;n!==null;){let e=n.ac;e!==null&&at(()=>{e.abort(xe)});var r=n.next;n.f&64?n.parent=null:B(n,t),n=r}}function An(e){for(var t=e.first;t!==null;){var n=t.next;t.f&32||B(t),t=n}}function B(e,t=!0){var n=!1;(t||e.f&262144)&&e.nodes!==null&&e.nodes.end!==null&&(jn(e.nodes.start,e.nodes.end),n=!0),e.f|=re,kn(e,t&&!n),ar(e,0);var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)e.stop();On(e),e.f^=re,e.f|=te;var i=e.parent;i!==null&&i.first!==null&&Mn(e),e.next=e.prev=e.teardown=e.ctx=e.deps=e.fn=e.nodes=e.ac=e.b=null}function jn(e,t){for(;e!==null;){var n=e===t?null:an(e);e.remove(),e=n}}function Mn(e){var t=e.parent,n=e.prev,r=e.next;n!==null&&(n.next=r),r!==null&&(r.prev=n),t!==null&&(t.first===e&&(t.first=r),t.last===e&&(t.last=n))}function Nn(e,t,n=!0){var r=[];e.f|=256,Pn(e,r,!0);var i=()=>{n&&B(e),t&&t()},a=r.length;if(a>0){var o=()=>--a||i();for(var s of r)s.out(o)}else i()}function Pn(e,t,n){if(!(e.f&8192)){e.f^=ee;var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)(e.is_global||n)&&t.push(e);for(var i=e.first;i!==null;){var a=i.next;if(!(i.f&64)){var o=!!(i.f&65536)||!!(i.f&32)&&!!(e.f&16);Pn(i,t,o?n:!1)}i=a}}}function Fn(e){e.f&=-257,In(e,!0)}function In(e,t){if(!(e.f&256)&&e.f&8192){e.f^=ee,e.f&1024||(A(e,x),At.ensure().schedule(e));for(var n=e.first;n!==null;){var r=n.next,i=!!(n.f&65536)||!!(n.f&32);In(n,i?t:!1),n=r}var a=e.nodes&&e.nodes.t;if(a!==null)for(let e of a)(e.is_global||t)&&e.in()}}function Ln(e,t){if(e.nodes)for(var n=e.nodes.start,r=e.nodes.end;n!==null;){var i=n===r?null:an(n);t.append(n),n=i}}var Rn=null,zn=!1,Bn=!1;function Vn(e){Bn=e}var V=null,Hn=!1;function Un(e){V=e}var H=null;function Wn(e){H=e}var Gn=null;function Kn(e){V!==null&&(V.f&2097152||V.f&2)&&(Gn??=new Set).add(e)}var U=null,W=0,qn=null;function Jn(e){qn=e}var Yn=1,Xn=0,Zn=Xn;function Qn(e){Zn=e}function $n(){return++Yn}function er(e){var t=e.f;if(t&2048)return!0;if(t&4096){for(var n=e.deps,r=n.length,i=0;ie.wv)return!0}t&512&&St===null&&A(e,b)}return!1}function tr(e,t,n=!0){var r=e.reactions;if(r!==null&&!(Gn!==null&&Gn.has(e)))for(var i=0;i{e.ac.abort(xe)}),e.ac=null);try{e.f|=le;var u=e.fn,d=u();e.f|=ne;var f=rr(e);if(Ye()&&qn!==null&&!Hn&&f!==null&&!(e.f&6146))for(var p=0;p0)for(t.length=W+U.length,r=0;r{c.ac.abort(xe),c.ac=null,A(c,x)}),vt(c),ar(c,0)}}function ar(e,t){var n=e.deps;if(n!==null)for(var r=t;rn?.call(this,e))}return e.startsWith(`pointer`)||e.startsWith(`touch`)||e===`wheel`?(i.__removed=!1,Qe(()=>{i.__removed||t.addEventListener(e,i,r)})):t.addEventListener(e,i,r),i}function hr(e,t,n,r,i){var a={capture:r,passive:i},o=mr(e,t,n,a);(t===document.body||t===window||t===document||t instanceof HTMLMediaElement)&&_n(()=>{o.__removed=!0,t.removeEventListener(e,o,a)})}function gr(e,t,n){(t[dr]??={})[e]=n}function _r(e){for(var t=0;t{yr=!1,vr=null}));var o=0,s=vr===e&&e[dr];if(s){var c=i.indexOf(s);if(c!==-1&&(t===document||t===window)){e[dr]=t;return}var u=i.indexOf(t);if(u===-1)return;c<=u&&(o=c)}if(a=i[o]||e.target,a!==t){l(e,`currentTarget`,{configurable:!0,get(){return a||n}});var d=V,f=H;Un(null),Wn(null);try{for(var p,m=[];a!==null&&a!==t;){try{var h=a[dr]?.[r];h!=null&&(!a.disabled||e.target===a)&&h.call(a,e)}catch(e){p?m.push(e):p=e}if(e.cancelBubble)break;o++,a=o{throw e});throw p}}finally{e[dr]=t,delete e.currentTarget,Un(d),Wn(f)}}}var xr=globalThis?.window?.trustedTypes&&globalThis.window.trustedTypes.createPolicy(`svelte-trusted-html`,{createHTML:e=>e});function Sr(e){return xr?.createHTML(e)??e}function Cr(e){var t=ln(`template`);return t.innerHTML=Sr(e.replaceAll(``,``)),t.content}function wr(e,t){var n=H;n.nodes===null&&(n.nodes={start:e,end:t,a:null,t:null})}function J(e,t){var n=!!(t&1),r=!!(t&2),i,a=!e.startsWith(``);return()=>{if(C)return wr(w,null),w;i===void 0&&(i=Cr(a?e:``+e),n||(i=rn(i)));var t=r||$t?document.importNode(i,!0):i.cloneNode(!0);if(n){var o=rn(t),s=t.lastChild;wr(o,s)}else wr(t,t);return t}}function Tr(){if(C)return wr(w,null),w;var e=document.createDocumentFragment(),t=document.createComment(``),n=P();return e.append(t,n),wr(t,n),e}function Y(e,t){if(C){var n=H;(!(n.f&32768)||n.nodes.end===null)&&(n.nodes.end=w),Oe();return}e!==null&&e.before(t)}[...`allowfullscreen.async.autofocus.autoplay.checked.controls.default.disabled.formnovalidate.indeterminate.inert.ismap.loop.multiple.muted.nomodule.novalidate.open.playsinline.readonly.required.reversed.seamless.selected.webkitdirectory.defer.disablepictureinpicture.disableremoteplayback`.split(`.`)];var Er=[`touchstart`,`touchmove`];function Dr(e){return Er.includes(e)}function Or(e){let t=0,n=Vt(0),r;return()=>{gn()&&(G(n),En(()=>(t===0&&(r=K(()=>e(()=>qt(n)))),t+=1,()=>{Qe(()=>{--t,t===0&&(r?.(),r=void 0,qt(n))})})))}}var kr=ie|oe;function Ar(e,t,n,r){new jr(e,t,n,r)}var jr=class{parent;is_pending=!1;transform_error;#e;#t=C?w:null;#n;#r;#i;#a=null;#o=null;#s=null;#c=null;#l=0;#u=0;#d=!1;#f=new Set;#p=new Set;#m=null;#h=Or(()=>(this.#m=Vt(this.#l),()=>{this.#m=null}));constructor(e,t,n,r){this.#e=e,this.#n=t,this.#r=e=>{var t=H;t.b=this,t.f|=128,n(e)},this.parent=H.b,this.transform_error=r??this.parent?.transform_error??(e=>e),this.#i=Dn(()=>{if(C){let e=this.#t;Oe();let t=e.data===`[!`;if(e.data.startsWith(`[?`)){let t=JSON.parse(e.data.slice(2));this.#_(t)}else t?this.#y():this.#g()}else this.#b()},kr),C&&(this.#e=w)}#g(){try{this.#a=z(()=>this.#r(this.#e))}catch(e){this.error(e)}}#_(e){let t=this.#n.failed,{reset:n,invoke_onerror:r}=this.#v(e);Qe(r),t&&(this.#s=z(()=>{t(this.#e,()=>e,()=>n)}))}#v(e){var t=!1,n=!1;let r=()=>{if(t){Ee();return}t=!0,n&&Ke(),this.#s!==null&&Nn(this.#s,()=>{this.#s=null}),this.#S(()=>{this.#b()})};return{reset:r,invoke_onerror:()=>{try{n=!0,this.#n.onerror?.(e,r),n=!1}catch(e){fn(e,this.#i&&this.#i.parent)}}}}#y(){let e=this.#n.pending;e&&(this.is_pending=!0,this.#o=z(()=>e(this.#e)),Qe(()=>{var e=this.#c=document.createDocumentFragment(),t=P(),n=!1;if(e.append(t),this.#a=this.#S(()=>{try{return z(()=>this.#r(t))}catch(e){try{this.error(e),n=!0}catch(e){fn(e,this.#i.parent)}return null}}),this.#a===null){this.#c=null,n&&this.#x(j);return}this.#u===0&&(this.#e.before(e),this.#c=null,Nn(this.#o,()=>{this.#o=null}),this.#x(j))}))}#b(){try{if(this.is_pending=this.has_pending_snippet(),this.#u=0,this.#l=0,this.#a=z(()=>{this.#r(this.#e)}),this.#u>0){var e=this.#c=document.createDocumentFragment();Ln(this.#a,e);let t=this.#n.pending;this.#o=z(()=>t(this.#e))}else this.#x(j)}catch(e){this.error(e)}}#x(e){this.is_pending=!1,e.transfer_effects(this.#f,this.#p)}defer_effect(e){nt(e,this.#f,this.#p)}is_rendered(){return!this.is_pending&&(!this.parent||this.parent.is_rendered())}has_pending_snippet(){return!!this.#n.pending}#S(e){var t=H,n=V,r=D;Wn(this.#i),Un(this.#i),qe(this.#i.ctx);try{return At.ensure(),e()}finally{Wn(t),Un(n),qe(r)}}#C(e,t){if(!this.has_pending_snippet()){this.parent&&this.parent.#C(e,t);return}this.#u+=e,this.#u===0&&(this.#x(t),this.#o&&Nn(this.#o,()=>{this.#o=null}),this.#c&&=(this.#e.before(this.#c),null))}update_pending_count(e,t){this.#C(e,t),this.#l+=e,!(!this.#m||this.#d)&&(this.#d=!0,Qe(()=>{this.#d=!1,this.#m&&Gt(this.#m,this.#l)}))}get_effect_pending(){return this.#h(),G(this.#m)}error(e){if(!this.#n.onerror&&!this.#n.failed)throw e;j?.is_fork?(this.#a&&j.skip_effect(this.#a),this.#o&&j.skip_effect(this.#o),this.#s&&j.skip_effect(this.#s),j.oncommit(()=>{this.#w(e)})):this.#w(e)}#w(e){this.#a&&=(B(this.#a),null),this.#o&&=(B(this.#o),null),this.#s&&=(B(this.#s),null),C&&(T(this.#t),ke(),T(Ae()));let t=this.#n.failed,n=e=>{let{reset:n,invoke_onerror:r}=this.#v(e);r(),t&&(this.#s=this.#S(()=>{try{return z(()=>{var r=H;r.b=this,r.f|=128,t(this.#e,()=>e,()=>n)})}catch(e){return fn(e,this.#i.parent),null}}))};Qe(()=>{var t;try{t=this.transform_error(e)}catch(e){fn(e,this.#i&&this.#i.parent);return}typeof t==`object`&&t&&typeof t.then==`function`?t.then(n,e=>fn(e,this.#i&&this.#i.parent)):n(t)})}};function X(e,t){var n=t==null?``:typeof t==`object`?`${t}`:t;n!==(e[ye]??=e.nodeValue)&&(e[ye]=n,e.nodeValue=`${n}`)}function Mr(e,t){return Pr(e,t)}var Nr=new Map;function Pr(e,{target:t,anchor:r,props:i={},events:a,context:o,intro:s=!0,transformError:l}){nn();var u=void 0,d=xn(()=>{var s=r??t.appendChild(P());Ar(s,{pending:()=>{}},t=>{O({});var r=D;if(o&&(r.c=o),a&&(i.$$events=a),C&&wr(t,null),u=e(t,i)||Je(),C&&(H.nodes.end=w,w===null||w.nodeType!==8||w.data!==`]`))throw we(),n;k()},l);var d=new Set,f=e=>{for(var n=0;n{for(var e of d)for(let r of[t,document]){var n=Nr.get(r),i=n.get(e);--i==0?(r.removeEventListener(e,br),n.delete(e),n.size===0&&Nr.delete(r)):n.set(e,i)}pr.delete(f),s!==r&&s.parentNode?.removeChild(s)}});return Fr.set(u,d),u}var Fr=new WeakMap,Ir=class{anchor;#e=new Map;#t=new Map;#n=new Map;#r=new Set;#i=!0;constructor(e,t=!0){this.anchor=e,this.#i=t}#a=e=>{if(this.#e.has(e)){var t=this.#e.get(e),n=this.#t.get(t);if(n)Fn(n),this.#r.delete(t);else{var r=this.#n.get(t);r&&(Fn(r.effect),this.#t.set(t,r.effect),this.#n.delete(t),r.fragment.lastChild.remove(),this.anchor.before(r.fragment),n=r.effect)}for(let[t,n]of this.#e){if(this.#e.delete(t),t===e)break;let r=this.#n.get(n);r&&(B(r.effect),this.#n.delete(n))}for(let[e,r]of this.#t){if(e===t||this.#r.has(e))continue;let i=()=>{if(Array.from(this.#e.values()).includes(e)){var t=document.createDocumentFragment();Ln(r,t),t.append(P()),this.#n.set(e,{effect:r,fragment:t})}else B(r);this.#r.delete(e),this.#t.delete(e)};this.#i||!n?(this.#r.add(e),Nn(r,i,!1)):i()}}};#o=e=>{this.#e.delete(e);let t=Array.from(this.#e.values());for(let[e,n]of this.#n)t.includes(e)||(B(n.effect),this.#n.delete(e))};ensure(e,t){var n=j,r=cn();if(t&&!this.#t.has(e)&&!this.#n.has(e)){if(r){var i=document.createDocumentFragment(),a=P();i.append(a),this.#n.set(e,{effect:z(()=>t(a)),fragment:i})}else this.#t.set(e,z(()=>t(this.anchor)))}if(this.#e.set(n,e),r){for(let[t,r]of this.#t)t===e?n.unskip_effect(r):n.skip_effect(r);for(let[t,r]of this.#n)t===e?n.unskip_effect(r.effect):n.skip_effect(r.effect);n.oncommit(this.#a),n.ondiscard(this.#o)}else C&&(this.anchor=w),this.#a(n)}};function Lr(t){D===null&&Fe(`onMount`),e&&D.l!==null?Rr(D).m.push(t):vn(()=>{let e=K(t);if(typeof e==`function`)return e})}function Rr(e){var t=e.l;return t.u??={a:[],b:[],m:[]}}function Z(e,t,n=!1){var r;C&&(r=w,Oe());var i=new Ir(e),a=n?ie:0;function o(e,t){if(C){var n=je(r);if(e!==parseInt(n.substring(1))){var a=Ae();T(a),i.anchor=a,De(!1),i.ensure(e,t),De(!0);return}}i.ensure(e,t)}Dn(()=>{var e=!1;t((t,n=0)=>{e=!0,o(n,t)}),e||o(-1,null)},a)}var zr=Symbol(`NaN`);function Br(e,t,n){C&&Oe();var r=new Ir(e),i=!Ye();Dn(()=>{var e=t();e!==e&&(e=zr),i&&typeof e==`object`&&e&&(e={}),r.ensure(e,n)})}function Vr(e,t){return t}function Hr(e,t,n){for(var r=[],i=t.length,a,o=t.length,s=0;s{if(a){if(a.pending.delete(n),a.done.add(n),a.pending.size===0){var t=e.outrogroups;Ur(e,c(a.done)),t.delete(a),t.size===0&&(e.outrogroups=null)}}else--o},!1)}if(o===0){var l=r.length===0&&n!==null&&e.pending.size===0;if(l){var u=n,d=u.parentNode;sn(d),d.append(u),e.items.clear()}Ur(e,t,!l)}else a={pending:new Set(t),done:new Set},(e.outrogroups??=new Set).add(a)}function Ur(e,t,n=!0){var r;if(e.pending.size>0){r=new Set;for(let t of e.pending.values())for(let n of t)r.add(e.items.get(n).e)}for(var i=0;i{var e=n();return a(e)?e:e==null?[]:c(e)}),p,m=new Map,h=!0;function g(e){v.effect.f&16384||(v.pending.delete(e),v.fallback=d,qr(v,p,s,t,r),d!==null&&(p.length===0?d.f&33554432?(d.f^=ce,Yr(d,null,s)):Fn(d):Nn(d,()=>{d=null})))}function _(e){v.pending.delete(e)}var v={effect:Dn(()=>{p=G(f);var e=p.length;let a=!1;C&&je(s)===`[!`!=(e===0)&&(s=Ae(),T(s),De(!1),a=!0);for(var c=new Set,u=j,v=cn(),y=0;yo(s)):(d=z(()=>o(Wr??=P())),d.f|=ce)),e>c.size&&Le(``,``,``),C&&e>0&&T(Ae()),!h){if(m.set(u,c),v){for(let[e,t]of l)c.has(e)||u.skip_effect(t.e);u.oncommit(g),u.ondiscard(_)}else g(u)}a&&De(!0),G(f)}),flags:t,items:l,pending:m,outrogroups:null,fallback:d};h=!1,C&&(s=w)}function Kr(e){for(;e!==null&&!(e.f&32);)e=e.next;return e}function qr(e,t,n,r,i){var a=!!(r&8),o=t.length,s=e.items,l=Kr(e.effect.first),u,d=null,f,p=[],m=[],h,g,_,v;if(a)for(v=0;v0){var re=r&4&&o===0?n:null;if(a){for(v=0;v{if(f!==void 0)for(_ of f)_.nodes?.a?.apply()})}function Jr(e,t,n,r,i,a,o,s){var c=o&1?o&16?Vt(n):M(n,!1,!1):null,l=o&2?Vt(i):null;return{v:c,i:l,e:z(()=>(a(t,c??n,l??i,s),()=>{e.delete(r)}))}}function Yr(e,t,n){if(e.nodes)for(var r=e.nodes.start,i=e.nodes.end,a=t&&!(t.f&33554432)?t.nodes.start:n;r!==null;){var o=an(r);if(a.before(r),r===i)return;r=o}}function Xr(e,t,n){t===null?e.effect.first=n:t.next=n,n===null?e.effect.last=t:n.prev=t}function Zr(e,t){let n=null,r=C;var i;if(C){n=w;for(var a=rn(document.head);a!==null&&(a.nodeType!==8||a.data!==e);)a=an(a);if(a===null)De(!1);else{var o=an(a);a.remove(),T(o)}}C||(i=document.head.appendChild(P()));try{Dn(()=>{var e=z(()=>t(i));e.f|=ae,C||(e.nodes===null?e.nodes={start:i,end:i,a:null,t:null}:e.nodes.end=i)})}finally{r&&(De(!0),T(n))}}function Qr(e){var t,n,r=``;if(typeof e==`string`||typeof e==`number`)r+=e;else if(typeof e==`object`){if(Array.isArray(e)){var i=e.length;for(t=0;t=0;){var s=o+a;(o===0||ti.includes(r[o-1]))&&(s===r.length||ti.includes(r[s]))?r=(o===0?``:r.substring(0,o))+r.substring(s+1):o=s}}return r===``?null:r}function ri(e,t,n,r,i,a){var o=e[_e];if(C||o!==n||o===void 0){var s=ni(n,r,a);(!C||s!==e.getAttribute(`class`))&&(s==null?e.removeAttribute(`class`):t?e.className=s:e.setAttribute(`class`,s)),e[_e]=n}else if(a&&i!==a)for(var c in a){var l=!!a[c];(i==null||l!==!!i[c])&&e.classList.toggle(c,l)}return a}function ii(e,t){t?e.hasAttribute(`selected`)||e.setAttribute(`selected`,``):e.removeAttribute(`selected`)}function ai(e,t){var n=e.__defaultValue,r=e.multiple,i=r?n??[]:null;if(!r||a(i)){var o=e.selectedIndex,s=t&&r?new Set(e.selectedOptions):null;for(var c of e.options){var l=li(c);ii(c,r?i.includes(l):Zt(l,n))}if(t){if(s!==null)for(c of e.options){var u=s.has(c);c.selected!==u&&(c.selected=u)}else e.selectedIndex!==o&&(e.selectedIndex=o)}}}function oi(e,t,n=!1){if(e.multiple){if(t==null)return;if(!a(t))return Te();for(var r of e.options)r.selected=t.includes(li(r));return}for(r of e.options)if(Zt(li(r),t)){r.selected=!0;return}(!n||t!==void 0)&&(e.selectedIndex=-1)}function si(e){var t=new MutationObserver(t=>{t.every(ui)||(`__defaultValue`in e&&ai(e,!1),`__value`in e&&oi(e,e.__value))});t.observe(e,{childList:!0,subtree:!0,attributes:!0,attributeFilter:[`value`]}),_n(()=>{t.disconnect()})}function ci(e,t,n=t){var r=new WeakSet,i=!0;ot(e,`change`,t=>{var i=t?`[selected]`:`:checked`,a;if(e.multiple)a=[].map.call(e.querySelectorAll(i),li);else{var o=e.querySelector(i)??e.querySelector(`option:not([disabled])`);a=o&&li(o)}n(a),e.__value=a,j!==null&&r.add(j)}),Sn(()=>{var a=t();if(e===document.activeElement){var o=j;if(r.has(o))return}if(oi(e,a,i),i&&a===void 0){var s=e.querySelector(`:checked`);s!==null&&(a=li(s),n(a))}e.__value=a,i=!1})}function li(e){return`__value`in e?e.__value:e.value}function ui(e){if(e.target.closest(`selectedcontent`)!==null)return!0;if(e.type===`childList`){var t=[...e.addedNodes,...e.removedNodes];return t.length>0&&t.every(e=>e.nodeName===`SELECTEDCONTENT`)}return!1}var di=Symbol(`is custom element`),fi=Symbol(`is html`),pi=Se?`link`:`LINK`;function mi(e){if(C){var t=!1,n=()=>{if(!t){if(t=!0,e.hasAttribute(`value`)){var n=e.value;Q(e,`value`,null),e.value=n}if(e.hasAttribute(`checked`)){var r=e.checked;Q(e,`checked`,null),e.checked=r}}};e[be]=n,Qe(n),it()}}function hi(e,t){var n=gi(e);n.checked!==(n.checked=t??void 0)&&(e.checked=t)}function Q(e,t,n,r){var i=gi(e);C&&(i[t]=e.getAttribute(t),t===`src`||t===`srcset`||t===`href`&&e.nodeName===pi)||i[t]!==(i[t]=n)&&(t===`loading`&&(e[he]=n),n==null?e.removeAttribute(t):typeof n!=`string`&&vi(e).has(t)?e[t]=n:e.setAttribute(t,n))}function gi(e){return e[ge]??={[di]:e.nodeName.includes(`-`),[fi]:e.namespaceURI===i}}var _i=new Map;function vi(e){var t=e.getAttribute(`is`)||e.nodeName,n=_i.get(t);if(n)return n;_i.set(t,n=new Set);for(var r,i=e,a=Element.prototype;a!==i;){for(var o in r=d(i),r)r[o].set&&o!==`innerHTML`&&o!==`textContent`&&o!==`innerText`&&n.add(o);i=m(i)}return n}function yi(e,t,n=t){var r=new WeakSet;ot(e,`input`,async i=>{var a=i?e.defaultValue:e.value;if(a=bi(e)?xi(a):a,n(a),j!==null&&r.add(j),await sr(),a!==(a=t())){var o=e.selectionStart,s=e.selectionEnd,c=e.value.length;if(e.value=a??``,s!==null){var l=e.value.length;o===s&&s===c&&l>c?(e.selectionStart=l,e.selectionEnd=l):(e.selectionStart=o,e.selectionEnd=Math.min(s,l))}}}),(C&&e.defaultValue!==e.value||K(t)==null&&e.value)&&(n(bi(e)?xi(e.value):e.value),j!==null&&r.add(j)),En(()=>{var n=t();if(e===document.activeElement){var i=j;if(r.has(i))return}bi(e)&&n===xi(e.value)||(e.type!==`date`||n||e.value)&&n!==e.value&&(e.value=n??``)})}function bi(e){var t=e.type;return t===`number`||t===`range`}function xi(e){return e===``?null:+e}function Si(e=!1){let t=D,n=t.l.u;if(!n)return;let r=()=>q(t.s);if(e){let e=0,n={},i=dt(()=>{let r=!1,i=t.s;for(let e in i)i[e]!==n[e]&&(n[e]=i[e],r=!0);return r&&e++,e});r=()=>G(i)}n.b.length&&bn(()=>{Ci(t,r),v(n.b)}),vn(()=>{let e=K(()=>n.m.map(_));return()=>{for(let t of e)typeof t==`function`&&t()}}),n.a.length&&vn(()=>{Ci(t,r),v(n.a)})}function Ci(e,t){if(e.l.s)for(let t of e.l.s)G(t);t()}function wi(e,t,n){if(e==null)return t(void 0),n&&n(void 0),g;let r=K(()=>e.subscribe(t,n));return r.unsubscribe?()=>r.unsubscribe():r}var Ti=[];function Ei(e,t=g){let n=null,r=new Set;function i(t){if(Ne(e,t)&&(e=t,n)){let t=!Ti.length;for(let t of r)t[1](),Ti.push(t,e);if(t){for(let e=0;e{r.delete(c),r.size===0&&n&&(n(),n=null)}}return{set:i,update:a,subscribe:o}}function Di(e){let t;return wi(e,e=>t=e)(),t}var Oi=!1,ki=Symbol(`unmounted`);function Ai(e,t,n){let r=n[t]??={store:null,source:M(void 0),unsubscribe:g};if(r.store!==e&&!(ki in n)){if(r.unsubscribe(),r.store=e??null,e==null)r.source.v=void 0,r.unsubscribe=g;else{var i=!0;r.unsubscribe=wi(e,e=>{i?r.source.v=e:N(r.source,e)}),i=!1}}return e&&ki in n?Di(e):G(r.source)}function ji(){let e={};function t(){_n(()=>{for(var t in e)e[t].unsubscribe();l(e,ki,{enumerable:!1,value:!0})})}return[e,t]}function Mi(e){var t=Oi;try{return Oi=!1,[e(),Oi]}finally{Oi=t}}function $(t,n,r,i){var a=!e||!!(r&2),o=!!(r&8),s=!!(r&16),c=i,l=!0,d=void 0,f=()=>s&&a?(d??=dt(i),G(d)):(l&&(l=!1,c=s?K(i):i),c);let p;if(o){var m=fe in t||me in t;p=u(t,n)?.set??(m&&n in t?e=>t[n]=e:void 0)}var h,g=!1;o?[h,g]=Mi(()=>t[n]):h=t[n],h===void 0&&i!==void 0&&(h=f(),p&&(a&&He(n),p(h)));var _=a?()=>{var e=t[n];return e===void 0?f():(l=!0,e)}:()=>{var e=t[n];return e!==void 0&&(c=void 0),e===void 0?c:e};if(a&&!(r&4))return _;if(p){var v=t.$$legacy;return(function(e,t){return arguments.length>0?((!a||!t||v||g)&&p(t?_():e),e):_()})}var y=!1,b=(r&1?dt:mt)(()=>(y=!1,_()));o&&G(b);var x=H;return(function(e,t){if(arguments.length>0){let n=t?G(b):a&&o?Yt(e):e;return N(b,n),y=!0,c!==void 0&&(c=n),e}return Bn&&y||x.f&16384?b.v:G(b)})}function Ni(e){let t=Ei({paired:!!e,controller:!1,busy:!1,error:``}),n=e,r,i=``,a={paired:!!e,controller:!1,busy:!1,error:``},o=!1;function s(e){a={...a,...e},t.set(a)}async function c(e=!1){if(!o&&n){o=!0;try{let t=await fetch(`/api/state`,{cache:`no-store`,headers:{"X-Setup-Session-Key":n}});if(!t.ok)throw Error(`The local setup session is unavailable.`);s({view:await t.json(),...e?{}:{error:``}})}catch{s({view:void 0,error:`Connection lost. The CLI may have stopped. Check the terminal before trying again.`})}finally{o=!1}}}async function l(){if(n)try{let e=await fetch(`/api/bootstrap`,{cache:`no-store`,headers:{"X-Setup-Session-Key":n}});if(!e.ok)throw Error(`Could not join this local session.`);let t=await e.json();r=t.capability,i=t.takeoverTicket,s({controller:t.controller,error:``}),await c()}catch{n=void 0,r=void 0,i=``,s({view:void 0,paired:!1,controller:!1,error:`Could not connect to the local setup session. Check the terminal and pair again.`})}}async function u(e){if(!(a.busy||a.paired)){s({busy:!0,error:``});try{let t=await fetch(`/api/pair`,{method:`POST`,cache:`no-store`,headers:{"Content-Type":`application/json`},body:JSON.stringify({code:e.trim().toLowerCase()})}),r=await t.json();if(!t.ok)throw Error(String(r.error??`Pairing was rejected.`));if(typeof r.sessionKey!=`string`||!/^[a-f0-9]{64}$/.test(r.sessionKey))throw Error(`Invalid local pairing response.`);n=r.sessionKey,s({paired:!0,error:``}),await l()}catch(e){s({error:e instanceof Error?e.message:`Could not pair this browser.`})}finally{s({busy:!1})}}}async function d(e,t,i=!0){let a=await fetch(e,{method:`POST`,cache:`no-store`,headers:{"Content-Type":`application/json`,"X-Setup-Session-Key":n,...i&&r?{"X-Setup-Capability":r}:{}},body:JSON.stringify(t)}),o=await a.json();if(!a.ok)throw Error(String(o.error??`The request was rejected.`));return o}async function f(e,t){if(!a.busy&&a.controller&&a.view?.promptRevision===e){s({busy:!0,error:``});try{await d(`/api/answer`,{revision:e,value:t}),await c()}catch(e){let t=e instanceof Error?e.message:`Could not submit this answer.`;s({error:t}),/read-only|Control moved/.test(t)?await l():await c(!0)}finally{s({busy:!1})}}}async function p(){if(a.controller&&!a.busy){s({busy:!0,error:``});try{await d(`/api/cancel`,{}),await c()}catch(e){let t=e instanceof Error?e.message:`Cancellation failed.`;s({error:t}),/read-only|Control moved/.test(t)&&await l()}finally{s({busy:!1})}}}async function m(){try{let e=await d(`/api/takeover`,{ticket:i},!1);r=String(e.capability),s({controller:!0,error:``}),await c()}catch(e){s({error:e instanceof Error?e.message:`Takeover failed.`}),await l()}}async function h(){try{await d(`/api/close`,{})}catch{}}return{subscribe:t.subscribe,pair:u,connect:l,refresh:c,submit:f,cancel:p,takeOver:m,close:h}}var Pi=J(`
                                                              • `),Fi=J(``);function Ii(e,t){O(t,!1);let n=$(t,`journey`,8),r=[`Repository`,`Setup choices`,`Setup PAT`,`Plan`,`Bot PAT & credentials`,`Apply`];Si();var i=Fi(),a=L(F(i),4);Gr(a,5,()=>r,Vr,(e,t,r)=>{var i=Pi();let a;var o=F(i),s=I(o,!0),c=I(L(o,2),!0);E(i),R(e=>{Q(i,`aria-current`,(q(n()),K(()=>n()?.position===r+1?`step`:void 0))),a=ri(i,1,``,null,a,{current:n()?.position===r+1,completed:(n()?.position??0)>r+1}),X(s,e),X(c,G(t))},[()=>(q(n()),K(()=>(n()?.position??0)>r+1?`✓`:String(r+1).padStart(2,`0`)))]),Y(e,i)}),E(a),ke(2),E(i),Y(e,i),k()}var Li=J(`
                                                                `);function Ri(e,t){O(t,!1);let n=M(`system`);Cn(()=>G(n),()=>{document.documentElement.dataset.theme=G(n)}),wn();var r=Li(),i=F(r);let a;var o=L(i,2);let s;var c=L(o,2);let l;E(r),R(()=>{Q(i,`aria-pressed`,G(n)===`system`),a=ri(i,1,``,null,a,{active:G(n)===`system`}),Q(o,`aria-pressed`,G(n)===`light`),s=ri(o,1,``,null,s,{active:G(n)===`light`}),Q(c,`aria-pressed`,G(n)===`dark`),l=ri(c,1,``,null,l,{active:G(n)===`dark`})}),gr(`click`,i,()=>N(n,`system`)),gr(`click`,o,()=>N(n,`light`)),gr(`click`,c,()=>N(n,`dark`)),Y(e,r),k()}_r([`click`]);var zi=J(`
                                                                LOCAL SESSION
                                                                `);function Bi(e,t){let n=$(t,`repository`,8);var r=zi(),i=F(r),a=I(L(F(i),2),!0);E(i);var o=L(i,2);Ri(L(F(o)),{}),E(o),E(r),R(()=>X(a,n()??`Connecting…`)),Y(e,r)}var Vi=J(`

                                                                `,1);function Hi(e,t){O(t,!1);let n=M(),r=$(t,`view`,8);Cn(()=>q(r()),()=>{N(n,r()?.outcome===`complete`?`Setup complete.`:r()?.outcome===`dry-run`?`Preview complete.`:r()?.outcome===`cancelled`?`Setup cancelled.`:r()?.outcome?`Setup needs attention.`:r()?.prompt?.title??`Preparing your setup…`)}),wn(),Si();var i=Vi(),a=on(i),o=L(F(a)),s=I(L(o));E(a);var c=L(a,2),l=I(c,!0),u=I(L(c,2),!0);R((e,t)=>{X(o,` ${e??``} `),X(s,`${t??``} / 06`),X(l,G(n)),X(u,(q(r()),K(()=>r()?.outcome?`The terminal contains the detailed result. Review the next steps below before closing this session.`:`One decision at a time. Your choices shape the permissions, plan and credentials needed for this repository.`)))},[()=>(q(r()),K(()=>r()?.journey?.current?.toUpperCase()??`GETTING READY`)),()=>(q(r()),K(()=>String(r()?.journey?.position??1).padStart(2,`0`)))]),Y(e,i),k()}function Ui(e){try{let t=new URL(e??``);return t.protocol===`https:`&&t.hostname===`github.com`&&(t.pathname===`/settings/personal-access-tokens`||t.pathname.startsWith(`/settings/personal-access-tokens/`))?t.toString():void 0}catch{return}}var Wi=J(``),Gi=J(``);function Ki(e,t){let n=$(t,`label`,8),r=$(t,`variant`,8,`primary`),i=$(t,`disabled`,8,!1),a=$(t,`arrow`,8,!1),o=$(t,`onClick`,8);var s=Gi(),c=F(s,!0),l=L(c),u=e=>{Y(e,Wi())};Z(l,e=>{a()&&e(u)}),E(s),R(()=>{ri(s,1,ei(r())),s.disabled=i(),X(c,n())}),gr(`click`,s,function(...e){o()?.apply(this,e)}),Y(e,s)}_r([`click`]);var qi=J(`Open GitHub link ↗`),Ji=J(`

                                                                `);function Yi(e,t){O(t,!1);let n=M(),r=$(t,`tone`,8,`info`),i=$(t,`title`,8),a=$(t,`message`,8),o=$(t,`link`,8,void 0),s=$(t,`actionLabel`,8,void 0),c=$(t,`onAction`,8,void 0);Cn(()=>q(o()),()=>{N(n,Ui(o()))}),wn(),Si();var l=Ji(),u=F(l),d=I(u,!0),f=L(u),p=I(f,!0),m=L(f,2),h=e=>{var t=qi();R(()=>Q(t,`href`,G(n))),Y(e,t)};Z(m,e=>{G(n)&&e(h)});var g=L(m,2),_=e=>{Ki(e,{get label(){return s()},variant:`secondary`,get onClick(){return c()}})};Z(g,e=>{s()&&c()&&e(_)}),E(l),R(()=>{ri(l,1,`banner ${r()??``}`),Q(l,`role`,r()===`error`?`alert`:`status`),X(d,i()),X(p,a())}),Y(e,l),k()}function Xi(e){let t=String(e.question.defaultValue),n=t.split(`,`).map(e=>e.trim()).filter(Boolean);return{value:t,selected:e.question.kind===`multi-select`?(e.question.choices??[]).filter(e=>e!==`All`&&n.includes(e.split(` — `)[0])):e.question.kind===`scope-overrides`?n:[]}}function Zi(e,t){return t===`All`?e.includes(`All`)?[]:[`All`]:e.includes(t)?e.filter(e=>e!==t):[...e.filter(e=>e!==`All`),t]}function Qi(e,t,n){return e.question.kind===`scope-overrides`||e.question.kind===`multi-select`?n.length?n.join(`,`):`none`:t}var $i=J(`PERMISSION PREVIEW`),ea=J(`
                                                                `),ta=J(``),na=J(``),ra=J(``),ia=J(`
                                                                `),aa=J(``),oa=J(`

                                                                `,1);function sa(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`controller`,8),i=$(t,`busy`,8),a=$(t,`onSubmit`,8),o=Xi(n()),s=M(o.value),c=M(o.selected);Si();var l=oa(),u=on(l),d=F(u),f=I(d,!0),p=L(d),m=e=>{Y(e,$i())};Z(p,e=>{q(n()),K(()=>n().phase===`permission-intent`)&&e(m)}),E(u);var h=L(u,2),g=e=>{var t=ea(),i=F(t);let a;var o=L(i);let c;E(t),R(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),Q(i,`aria-pressed`,G(s)===`yes`||G(s)===`true`),i.disabled=!r(),a=ri(i,1,``,null,a,{selected:G(s)===`yes`||G(s)===`true`}),Q(o,`aria-pressed`,G(s)===`no`||G(s)===`false`),o.disabled=!r(),c=ri(o,1,``,null,c,{selected:G(s)===`no`||G(s)===`false`})}),gr(`click`,i,()=>N(s,`yes`)),gr(`click`,o,()=>N(s,`no`)),Y(e,t)},_=e=>{var t=na();Gr(t,5,()=>(q(n()),K(()=>n().question.choices??[])),Vr,(e,t)=>{var n=ta(),r=I(n,!0),i={};R(()=>{X(r,G(t)),i!==(i=G(t))&&(n.value=(n.__value=i)??``)}),Y(e,n)}),E(t),si(t),R(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),t.disabled=!r()}),ci(t,()=>G(s),e=>N(s,e)),Y(e,t)},v=e=>{var t=ia();Gr(t,5,()=>(q(n()),K(()=>n().question.kind===`multi-select`?n().question.choices??[]:n().question.allowedNames??[])),Vr,(e,t)=>{var n=ra(),i=F(n);mi(i);var a=I(L(i),!0);E(n),R(e=>{hi(i,e),i.disabled=!r(),X(a,G(t))},[()=>(G(c),G(t),K(()=>G(c).includes(G(t))))]),gr(`change`,i,()=>N(c,Zi(G(c),G(t)))),Y(e,n)}),E(t),R(()=>Q(t,`aria-label`,(q(n()),K(()=>n().question.label)))),Y(e,t)},y=e=>{var t=aa();mi(t),R(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),Q(t,`type`,(q(n()),K(()=>n().question.kind===`number`?`number`:`text`))),Q(t,`min`,(q(n()),K(()=>n().question.kind===`number`?0:void 0))),t.disabled=!r()}),yi(t,()=>G(s),e=>N(s,e)),Y(e,t)};Z(h,e=>{q(n()),K(()=>n().question.kind===`boolean`)?e(g):(q(n()),K(()=>n().question.kind===`choice`)?e(_,1):(q(n()),K(()=>n().question.kind===`multi-select`||n().question.kind===`scope-overrides`)?e(v,2):e(y,-1)))});var b=L(h,2),x=I(b),S=L(b,2);{let e=mt(()=>!r()||i());Ki(S,{label:`Continue`,arrow:!0,onClick:()=>a()(Qi(n(),G(s),G(c))),get disabled(){return G(e)}})}R((e,t)=>{X(f,e),X(x,`Suggested answer: ${t??``} · You can review choices again before creating your setup PAT.`)},[()=>(q(n()),K(()=>n().question.label.replace(` (Space toggles, Enter confirms)`,``))),()=>(q(n()),K(()=>String(n().question.defaultValue)||`none`))]),Y(e,l),k()}_r([`click`,`change`]);var ca=J(``),la=J(`
                                                                `);function ua(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`controller`,8),i=$(t,`busy`,8),a=$(t,`onSubmit`,8);Si();var o=la();Gr(o,5,()=>(q(n()),K(()=>n().choices)),Vr,(e,t)=>{var n=ca(),o=I(F(n),!0);ke(),E(n),R(()=>{n.disabled=!r()||i(),X(o,G(t))}),gr(`click`,n,()=>a()(G(t))),Y(e,n)}),E(o),Y(e,o),k()}_r([`click`]);var da=J(`Open the official GitHub PAT form

                                                                Check the signed-in account, select Only select repositories, then select this repository. GitHub handles 2FA and creates the PAT.

                                                                `,1),fa=J(`

                                                                Sent only to this local process. It will not be shown again or saved in browser storage.

                                                                `),pa=J(` `,1);function ma(e,t){O(t,!1);let n=M(),r=$(t,`prompt`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8),s=M(``);function c(){let e=G(s);r().kind===`secret`&&N(s,``),o()(e)}Cn(()=>q(r()),()=>{N(n,Ui(r().link))}),wn(),Si();var l=pa(),u=on(l),d=e=>{var t=da(),r=on(t);ke(),R(()=>Q(r,`href`,G(n))),Y(e,t)};Z(u,e=>{G(n)&&e(d)});var f=L(u,2),p=I(f,!0),m=L(f,2);mi(m);var h=L(m,2),g=e=>{Y(e,fa())};Z(h,e=>{q(r()),K(()=>r().kind===`secret`)&&e(g)});var _=L(h,2);{let e=mt(()=>(q(i()),q(a()),q(r()),G(s),K(()=>!i()||a()||!r().optional&&!G(s).trim())));Ki(_,{label:`Continue`,arrow:!0,onClick:c,get disabled(){return G(e)}})}R(()=>{X(p,(q(r()),K(()=>r().kind===`secret`?`Paste the value here`:`Your answer`))),Q(m,`type`,(q(r()),K(()=>r().kind===`secret`?`password`:`text`))),m.disabled=!i(),Q(m,`placeholder`,(q(r()),K(()=>r().kind===`secret`?`Hidden after submission`:`Type your answer`)))}),yi(m,()=>G(s),e=>N(s,e)),Y(e,l),k()}var ha=J(`
                                                              • `),ga=J(`

                                                                  `),_a=J(`

                                                                  Before you continue

                                                                    `),va=J(`

                                                                    Review exactly what this run may change. The bot PAT and any additional credentials are collected next.

                                                                    `,1);function ya(e,t){O(t,!1);let n=M(),r=$(t,`prompt`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8);Cn(()=>q(r()),()=>{N(n,[{title:`Files`,items:r().plan.files},{title:`Workflows`,items:r().plan.workflows},{title:`Variables`,items:r().plan.variables},{title:`Secret names`,items:r().plan.secrets}])}),wn(),Si();var s=va(),c=L(on(s),2);Gr(c,5,()=>G(n),Vr,(e,t)=>{var n=ga(),r=F(n),i=F(r),a=I(L(i),!0);E(r);var o=L(r);Gr(o,5,()=>(G(t),K(()=>G(t).items)),Vr,(e,t)=>{var n=ha(),r=I(n,!0);R(()=>X(r,G(t))),Y(e,n)}),E(o),E(n),R(()=>{X(i,`${G(t),K(()=>G(t).title)??``} `),X(a,(G(t),K(()=>G(t).items.length)))}),Y(e,n)}),E(c);var l=L(c,2),u=e=>{var t=_a(),n=L(F(t));Gr(n,5,()=>(q(r()),K(()=>r().plan.warnings)),Vr,(e,t)=>{var n=ha(),r=I(n,!0);R(()=>X(r,G(t))),Y(e,n)}),E(n),E(t),Y(e,t)};Z(l,e=>{q(r()),K(()=>r().plan.warnings.length)&&e(u)});var d=L(l,2),f=F(d);{let e=mt(()=>!i()||a());Ki(f,{label:`Stop here`,variant:`secondary`,onClick:()=>o()(`decline`),get disabled(){return G(e)}})}var p=L(f);{let e=mt(()=>!i()||a());Ki(p,{label:`Approve this plan`,arrow:!0,onClick:()=>o()(`approve`),get disabled(){return G(e)}})}E(d),Y(e,s),k()}var ba=J(`

                                                                    `),xa=J(`
                                                                    CURRENT DECISION
                                                                    `);function Sa(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`revision`,8),i=$(t,`promptRevision`,8),a=$(t,`controller`,8),o=$(t,`busy`,8),s=$(t,`onSubmit`,8);Si();var c=xa(),l=F(c),u=I(L(F(l)));E(l);var d=L(l,2),f=e=>{var t=ba(),r=I(t,!0);R(()=>X(r,(q(n()),K(()=>n().description)))),Y(e,t)};Z(d,e=>{q(n()),K(()=>`description`in n()&&n().description)&&e(f)}),Br(L(d,2),i,e=>{var t=Tr(),r=on(t),i=e=>{sa(e,{get prompt(){return n()},get controller(){return a()},get busy(){return o()},get onSubmit(){return s()}})},c=e=>{ua(e,{get prompt(){return n()},get controller(){return a()},get busy(){return o()},get onSubmit(){return s()}})},l=e=>{ma(e,{get prompt(){return n()},get controller(){return a()},get busy(){return o()},get onSubmit(){return s()}})},u=e=>{ya(e,{get prompt(){return n()},get controller(){return a()},get busy(){return o()},get onSubmit(){return s()}})};Z(r,e=>{q(n()),K(()=>n().kind===`question`)?e(i):(q(n()),K(()=>n().kind===`choice`||n().kind===`confirm`)?e(c,1):(q(n()),K(()=>n().kind===`text`||n().kind===`secret`)?e(l,2):(q(n()),K(()=>n().kind===`plan`)&&e(u,3))))}),Y(e,t)}),E(c),R(()=>X(u,`SESSION ${r()??``}`)),Y(e,c),k()}var Ca=J(` `),wa=J(`
                                                                  • `),Ta=J(`

                                                                      Conditional grants depend on the choices and GitHub facts. A final audit runs before setup changes.
                                                                      `),Ea=J(`

                                                                      Permissions follow your choices

                                                                      We’ll show the exact grants before you create each PAT. Nothing is created just by opening this page.

                                                                      `),Da=J(``);function Oa(e,t){O(t,!1);let n=$(t,`view`,8);Si();var r=Da(),i=F(r),a=I(L(F(i),3),!0);E(i);var o=L(i,2),s=e=>{var t=Ta(),r=L(F(t)),i=I(r),a=L(r),o=I(a,!0),s=L(a,2);Gr(s,5,()=>(q(n()),K(()=>n().permissions.report?.checks??n().permissions.requirements??[])),Vr,(e,t)=>{var n=wa(),r=F(n),i=F(r),a=L(i),o=I(a),s=I(L(a),!0);E(r);var c=L(r),l=F(c,!0),u=L(l),d=e=>{var n=Ca(),r=I(n,!0);R(()=>X(r,(G(t),K(()=>G(t).status)))),Y(e,n)};Z(u,e=>{`status`in G(t)&&e(d)}),E(c),E(n),R(()=>{X(i,`${G(t),K(()=>G(t).permission)??``} `),X(o,`${G(t),K(()=>G(t).scope)??``} · ${G(t),K(()=>G(t).applicability)??``}`),X(s,(G(t),K(()=>G(t).reason))),X(l,(G(t),K(()=>G(t).level)))}),Y(e,n)}),E(s),ke(2),E(t),R(()=>{X(i,`${q(n()),K(()=>n().permissions.role===`setup`?`Setup PAT`:`Bot PAT`)??``} access`),X(o,(q(n()),K(()=>n().permissions.report?`Read-only access check`:`Provisional least-privilege grants`)))}),Y(e,t)},c=e=>{Y(e,Ea())};Z(o,e=>{q(n()),K(()=>n().permissions)?e(s):e(c,-1)}),E(r),R(()=>X(a,(q(n()),K(()=>n().repository)))),Y(e,r),k()}var ka=J(`

                                                                      `);function Aa(e,t){O(t,!1);let n=M(),r=M(),i=$(t,`outcome`,8),a=$(t,`controller`,8),o=$(t,`onClose`,8);Cn(()=>q(i()),()=>{N(n,i()===`complete`?`Your configuration was applied`:i()===`dry-run`?`No changes were made`:i()===`cancelled`||i()===`blocked`?`No setup changes started`:`Check partial changes before retrying`)}),Cn(()=>q(i()),()=>{N(r,i()===`complete`?`The temporary setup PAT is not revoked automatically. Delete it in GitHub after confirming your setup. Keep the bot PAT until you rotate the installed Secret.`:i()===`partial`?`A bot Secret write or another change may have succeeded. Inspect GitHub and run copilot doctor before replacing or deleting its PAT.`:`Copilot did not begin applying setup changes. PATs you created in GitHub still exist until you delete them there.`)}),wn();var s=ka(),c=F(s),l=I(c,!0),u=L(c),d=I(u,!0),f=L(u),p=I(f,!0),m=L(f,2),h=e=>{Ki(e,{label:`Close local session`,get onClick(){return o()}})};Z(m,e=>{a()&&e(h)}),E(s),R(()=>{X(l,i()===`complete`?`✓`:`!`),X(d,G(n)),X(p,G(r))}),Y(e,s),k()}var ja=J(`

                                                                      Working on the next step

                                                                      The local process is checking your answers and preparing the next decision. Keep this page open.

                                                                      `);function Ma(e){Y(e,ja())}var Na=J(`
                                                                      PRIVATE LOCAL SESSION

                                                                      Pair this browser

                                                                      Find the 16-character pairing code in the terminal that started copilot setup --web. Enter it here to view or control this setup. The code is never placed in the browser URL or stored after this page closes.

                                                                      Keep the code private. After refreshing this page, enter it again to reconnect.

                                                                      `);function Pa(e,t){O(t,!1);let n=$(t,`busy`,8),r=$(t,`onPair`,8),i=M(``);function a(){let e=G(i);N(i,``),r()(e)}Si();var o=Na(),s=L(F(o),6),c=L(F(s),2);mi(c);var l=L(c,4);{let e=mt(()=>(q(n()),G(i),K(()=>n()||G(i).trim().length!==16)));Ki(l,{label:`Connect to local setup`,arrow:!0,onClick:a,get disabled(){return G(e)}})}E(s),E(o),R(()=>c.disabled=n()),hr(`submit`,s,e=>{e.preventDefault(),a()}),yi(c,()=>G(i),e=>N(i,e)),Y(e,o),k()}var Fa=J(``),Ia=J(`
                                                                      `),La=J(``),Ra=J(`
                                                                      `,1),za=J(`
                                                                      LOCALHOST ONLY NO CLOUD SETUP ACCOUNT GITHUB OWNS PAT ISSUANCE
                                                                      `);function Ba(e,t){O(t,!1);let n=()=>Ai(a,`$session`,r),[r,i]=ji(),a=Ni();Lr(()=>{let e=window.setInterval(()=>{n().paired&&!n().view?.outcome&&a.refresh()},900);return()=>window.clearInterval(e)});async function o(){window.confirm(`Cancel this local setup session? PATs already created in GitHub will still exist.`)&&await a.cancel()}async function s(e){let t=n().view?.promptRevision;t!==void 0&&await a.submit(t,e)}Si();var c=za();Zr(`16t12jp`,e=>{Y(e,Fa())});var l=F(c);{let e=mt(()=>n().view?.journey);Ii(l,{get journey(){return G(e)}})}var u=L(l,2),d=F(u);{let e=mt(()=>n().view?.repository);Bi(d,{get repository(){return G(e)}})}var f=L(d,2),p=F(f),m=e=>{Hi(e,{get view(){return n().view}})};Z(p,e=>{n().paired&&e(m)});var h=L(p,2),g=e=>{var t=Ia(),r=L(F(t));E(t),R(()=>X(r,` Reviewing saved choices — pass ${n().view.journey.choiceReviewPass??``}. This is the same setup run, not a restart.`)),Y(e,t)};Z(h,e=>{n().view?.journey?.choiceReviewPass&&n().view.journey.choiceReviewPass>1&&!n().view.outcome&&e(g)});var _=L(h,2),v=e=>{Yi(e,{tone:`warning`,title:`Read-only tab`,message:`Another tab controls this session. You can review progress here or explicitly take over.`,actionLabel:`Take control in this tab`,get onAction(){return a.takeOver}})};Z(_,e=>{!n().controller&&n().view&&e(v)});var y=L(_,2),b=e=>{Yi(e,{tone:`error`,title:`Needs attention`,get message(){return n().error}})};Z(y,e=>{n().error&&e(b)});var x=L(y,2),S=e=>{{let t=mt(()=>n().view.message.tone===`success`?`Checked`:n().view.message.tone===`warning`?`Please note`:n().view.message.tone===`error`?`Needs attention`:`Progress update`);Yi(e,{get tone(){return n().view.message.tone},get title(){return G(t)},get message(){return n().view.message.text},get link(){return n().view.message.link}})}};Z(x,e=>{n().view?.message&&e(S)});var ee=L(x,2),te=e=>{Pa(e,{get busy(){return n().busy},get onPair(){return a.pair}})},ne=e=>{Aa(e,{get outcome(){return n().view.outcome},get controller(){return n().controller},get onClose(){return a.close}})},re=e=>{var t=Ra(),r=on(t),i=F(r);Sa(i,{get prompt(){return n().view.prompt},get revision(){return n().view.revision},get promptRevision(){return n().view.promptRevision},get controller(){return n().controller},get busy(){return n().busy},onSubmit:s}),Oa(L(i,2),{get view(){return n().view}}),E(r);var a=L(r,2),c=e=>{var t=La();R(()=>t.disabled=n().busy),gr(`click`,t,o),Y(e,t)};Z(a,e=>{n().controller&&n().view.journey?.current!==`Apply`&&e(c)}),Y(e,t)},ie=e=>{Ma(e,{})};Z(ee,e=>{n().paired?n().view?.outcome?e(ne,1):n().view?.prompt?e(re,2):e(ie,-1):e(te)}),ke(2),E(f),E(u),E(c),Y(e,c),k(),i()}_r([`click`]),Mr(Ba,{target:document.getElementById(`app`)}); \ No newline at end of file diff --git a/build/web/index.html b/build/web/index.html index 2fe905c02..b9b0a01c8 100644 --- a/build/web/index.html +++ b/build/web/index.html @@ -5,7 +5,7 @@ Copilot · Setup studio - + diff --git a/docs/how-to-use.mdx b/docs/how-to-use.mdx index 1162c9f26..3b85d5f53 100644 --- a/docs/how-to-use.mdx +++ b/docs/how-to-use.mdx @@ -53,7 +53,9 @@ of the current run, presents permissions and the final plan, and asks for a separate final **Apply setup** approval. Use the System/Light/Dark control in the top bar to choose a readable palette; the choice lasts only in that tab. PAT values are submitted to the local process through masked fields and are -not restored after refreshing the page. A second tab is read-only until you +not restored after refreshing the page. A new setup question starts with its +own suggested answer; status updates do not erase an answer you are typing. +A second tab is read-only until you explicitly take control there. The terminal remains the default with `copilot setup`. diff --git a/specs/local-web-setup-assistant.md b/specs/local-web-setup-assistant.md index 357c82473..ef06de31a 100644 --- a/specs/local-web-setup-assistant.md +++ b/specs/local-web-setup-assistant.md @@ -439,8 +439,10 @@ storage. `web/src/components/` contains cohesive presenters for progress, header/theme, status, prompt kinds, context, and outcome. Components receive the redacted view and callbacks; they never call `fetch`, import provider or policy modules, or infer authority from local UI state. Prompt inputs keep -only transient local values, clear secrets before submission, and remount when -the server prompt revision changes. Small pure helpers may normalize defaults +only transient local values, clear secrets before submission, and the prompt +presenter is keyed by the server prompt revision inside `PromptCard`. A new +question remounts with its own defaults; ordinary polling or session-message +revisions do not erase an answer in progress. Small pure helpers may normalize defaults and allowlisted links. Adding a prompt kind belongs in its presenter rather than growing the page shell; avoid one-file-per-element indirection with no reuse. Architecture tests guard dependency direction and bound shell and @@ -892,7 +894,7 @@ catalog stays `proposed` until the definition of done is evidenced. Existing terminal policy/use cases remain the authority; the current web path does not introduce its own permission catalog. -The latest full local run on 2026-09-28 passed 502 Jest suites / 5,389 tests, +The latest full local run on 2026-09-28 passed 502 Jest suites / 5,390 tests, with 95.95% statements, 90.88% branches, 96.53% functions, and 97.26% lines repository-wide. The new setup-PAT intent, bootstrap audit, configured audit, remote-fact comparison, and override merge modules each reached 100% in all diff --git a/src/architecture/__tests__/web_setup_boundaries.test.ts b/src/architecture/__tests__/web_setup_boundaries.test.ts index 3c5fe0eaf..4a8a7d428 100644 --- a/src/architecture/__tests__/web_setup_boundaries.test.ts +++ b/src/architecture/__tests__/web_setup_boundaries.test.ts @@ -141,7 +141,9 @@ describe('local web setup architecture', () => { } } expect(readFileSync(join(browser, 'App.svelte'), 'utf8')).toContain('createSetupSession'); + expect(readFileSync(join(browser, 'App.svelte'), 'utf8')).toContain('promptRevision={$session.view.promptRevision!}'); expect(readFileSync(join(browser, 'components', 'PromptCard.svelte'), 'utf8')).toContain('CredentialPrompt'); + expect(readFileSync(join(browser, 'components', 'PromptCard.svelte'), 'utf8')).toContain('{#key promptRevision}'); }); test('page shell and presenters remain small and styles have one explicit entrypoint', () => { diff --git a/src/cli/__tests__/web_setup_components.test.ts b/src/cli/__tests__/web_setup_components.test.ts index 1fa141dbb..ceea53876 100644 --- a/src/cli/__tests__/web_setup_components.test.ts +++ b/src/cli/__tests__/web_setup_components.test.ts @@ -138,6 +138,20 @@ describe('web setup component semantics', () => { expect(html).not.toMatch(/]*disabled[^>]*>Continue/); }); + test('successive question revisions render their own default values', () => { + const question = (id: string, defaultValue: string) => ({ + kind: 'question', title: 'Question', phase: 'full', pass: 1, + question: { stateId: 'repository', id, label: id, kind: 'text', defaultValue }, + }); + const first = markup('PromptCard', { promptRevision: 7, revision: 7, + prompt: question('first', 'alpha'), controller: true, busy: false }); + const second = markup('PromptCard', { promptRevision: 9, revision: 9, + prompt: question('second', 'beta'), controller: true, busy: false }); + expect(first).toContain('value="alpha"'); + expect(second).toContain('value="beta"'); + expect(second).not.toContain('value="alpha"'); + }); + test('waiting state never implies that setup has completed', () => { const html = markup('WaitingPanel', {}); expect(html).not.toContain('Setup completed'); diff --git a/web/src/App.svelte b/web/src/App.svelte index ca10f374c..f5069966a 100644 --- a/web/src/App.svelte +++ b/web/src/App.svelte @@ -54,9 +54,7 @@ {:else if $session.view?.prompt}
                                                                      - {#key $session.view.promptRevision} - - {/key} +
                                                                      {#if $session.controller && $session.view.journey?.current !== 'Apply'}{/if} diff --git a/web/src/components/PromptCard.svelte b/web/src/components/PromptCard.svelte index 79e23a4cd..70ed4c1cc 100644 --- a/web/src/components/PromptCard.svelte +++ b/web/src/components/PromptCard.svelte @@ -6,6 +6,7 @@ import PlanPrompt from './PlanPrompt.svelte'; export let prompt: WebSetupPrompt; export let revision: number; + export let promptRevision: number; export let controller: boolean; export let busy: boolean; export let onSubmit: (value: string) => Promise; @@ -14,13 +15,15 @@
                                                                      CURRENT DECISIONSESSION {revision}
                                                                      {#if 'description' in prompt && prompt.description}

                                                                      {prompt.description}

                                                                      {/if} - {#if prompt.kind === 'question'} - - {:else if prompt.kind === 'choice' || prompt.kind === 'confirm'} - - {:else if prompt.kind === 'text' || prompt.kind === 'secret'} - - {:else if prompt.kind === 'plan'} - - {/if} + {#key promptRevision} + {#if prompt.kind === 'question'} + + {:else if prompt.kind === 'choice' || prompt.kind === 'confirm'} + + {:else if prompt.kind === 'text' || prompt.kind === 'secret'} + + {:else if prompt.kind === 'plan'} + + {/if} + {/key}
                                                                      From d02b5f34750d1ae030bad2f9c00a1d0193e79297 Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Mon, 28 Sep 2026 22:42:52 +0200 Subject: [PATCH 22/50] codex-setup-temporary-github-auth: recheck local facts after async Apply audit --- build/cli/index.js | 29 ++++++++++------- specs/local-web-setup-assistant.md | 12 ++++--- src/__tests__/cli.test.ts | 2 +- .../verify_web_setup_apply_use_case.test.ts | 22 +++++++++++++ .../setup/verify_web_setup_apply_use_case.ts | 31 +++++++++++-------- 5 files changed, 66 insertions(+), 30 deletions(-) diff --git a/build/cli/index.js b/build/cli/index.js index d3a48c897..8f890d9af 100755 --- a/build/cli/index.js +++ b/build/cli/index.js @@ -56394,21 +56394,12 @@ class VerifyWebSetupApplyUseCase { if (decision === 'stop') return 'declined'; this.assertActive(); - const current = this.ports.readRepositoryFacts(); - const expected = request.repository; - if (!current || current.owner !== expected.owner || current.repository !== expected.repository - || current.checkoutRoot !== expected.checkoutRoot || current.branch !== expected.branch - || current.head !== expected.head) { - throw new application_error_1.ApplicationError('configuration.invalid', 'The repository identity changed during setup. No mutation started; restart and review a new plan.'); - } - if (!this.ports.fileSnapshotMatches(expected.checkoutRoot, request.selectedFiles, request.fileSnapshot)) { - throw new application_error_1.ApplicationError('configuration.invalid', 'Selected repository files changed since plan review. No mutation started; restart and review a new plan.'); - } - let remote = await this.ports.remote.inspect(expected.owner, expected.repository, request.setupToken); + this.assertLocalSnapshot(request); + let remote = await this.ports.remote.inspect(request.repository.owner, request.repository.repository, request.setupToken); this.assertActive(); let credentialHealthWorkflow = 'unavailable'; try { - credentialHealthWorkflow = await this.ports.remote.inspectCredentialHealthWorkflow?.(expected.owner, expected.repository, request.setupToken, request.configuration.repository.mainBranch) ?? 'unavailable'; + credentialHealthWorkflow = await this.ports.remote.inspectCredentialHealthWorkflow?.(request.repository.owner, request.repository.repository, request.setupToken, request.configuration.repository.mainBranch) ?? 'unavailable'; } catch { /* Unknown selected-ref state must not inherit a provisional value. */ } this.assertActive(); @@ -56421,8 +56412,22 @@ class VerifyWebSetupApplyUseCase { if (audit.status === 'blocked') { throw new application_error_1.ApplicationError('authorization.credential-invalid', 'Setup PAT access changed since plan review. No mutation started; correct the PAT and review a new plan.'); } + // The remote reads above can take time. Close that window before the caller applies. + this.assertLocalSnapshot(request); return 'approved'; } + assertLocalSnapshot(request) { + const current = this.ports.readRepositoryFacts(); + const expected = request.repository; + if (!current || current.owner !== expected.owner || current.repository !== expected.repository + || current.checkoutRoot !== expected.checkoutRoot || current.branch !== expected.branch + || current.head !== expected.head) { + throw new application_error_1.ApplicationError('configuration.invalid', 'The repository identity changed during setup. No mutation started; restart and review a new plan.'); + } + if (!this.ports.fileSnapshotMatches(expected.checkoutRoot, request.selectedFiles, request.fileSnapshot)) { + throw new application_error_1.ApplicationError('configuration.invalid', 'Selected repository files changed since plan review. No mutation started; restart and review a new plan.'); + } + } assertActive() { const state = this.ports.sessionState(); if (state === 'cancelled') { diff --git a/specs/local-web-setup-assistant.md b/specs/local-web-setup-assistant.md index ef06de31a..8fe9c0f1c 100644 --- a/specs/local-web-setup-assistant.md +++ b/specs/local-web-setup-assistant.md @@ -406,6 +406,9 @@ The final web Apply authorization is one application use case with injected repository-facts, selected-file snapshot, remote-facts, permission-audit, approval, and live-session ports. It must fail closed on missing/drifted facts or a session that was cancelled/expired while asynchronous reads were running. +It checks repository identity and selected-file digests before remote reads +and again after the final asynchronous permission audit, immediately before +returning approval; drift during those awaits cannot inherit earlier proof. The CLI supplies Git/HTTP/provider adapters, but must not reimplement this decision as an inline sequence. The subsequent mutation boundary remains single-flight and cannot be entered if the approval use case did not return an @@ -473,8 +476,9 @@ Both terminal and web routes use the same questionnaire parser for this choice. invalidated according to their dependencies; the UI explains why it returned to an earlier stage. - The repository path is fixed after launch. Recheck canonical path, git - owner/repo, selected branch/ref, relevant file digests, and remote facts - immediately before Apply. Unexpected drift yields a new plan revision and + owner/repo, selected branch/ref, and relevant file digests both before and + after asynchronous remote/PAT checks; recheck remote facts during those + checks, immediately before Apply. Unexpected drift yields a new plan revision and requires fresh human review; never apply from a stale browser response. Repository-relative plan labels such as `workflows/name.yml` and `ISSUE_TEMPLATE/name.yml` MUST be translated to their actual checkout @@ -894,8 +898,8 @@ catalog stays `proposed` until the definition of done is evidenced. Existing terminal policy/use cases remain the authority; the current web path does not introduce its own permission catalog. -The latest full local run on 2026-09-28 passed 502 Jest suites / 5,390 tests, -with 95.95% statements, 90.88% branches, 96.53% functions, and 97.26% lines +The latest full local run on 2026-09-28 passed 502 Jest suites / 5,392 tests, +with 95.95% statements, 90.88% branches, 96.53% functions, and 97.27% lines repository-wide. The new setup-PAT intent, bootstrap audit, configured audit, remote-fact comparison, and override merge modules each reached 100% in all four metrics; final web Apply authorization reached 100% lines and 95.83% diff --git a/src/__tests__/cli.test.ts b/src/__tests__/cli.test.ts index b483e4a87..0bbf851ea 100644 --- a/src/__tests__/cli.test.ts +++ b/src/__tests__/cli.test.ts @@ -530,7 +530,7 @@ describe('CLI', () => { ])); expect(mockTokenPermissionInspect).toHaveBeenCalledTimes(3); expect(captureSetupApplySnapshot).toHaveBeenCalledTimes(1); - expect(setupApplySnapshotMatches).toHaveBeenCalledTimes(1); + expect(setupApplySnapshotMatches).toHaveBeenCalledTimes(2); expect(runLocalAction).toHaveBeenCalledTimes(1); expect(process.exitCode).toBeUndefined(); }); diff --git a/src/application/usecases/setup/__tests__/verify_web_setup_apply_use_case.test.ts b/src/application/usecases/setup/__tests__/verify_web_setup_apply_use_case.test.ts index 444eb188f..e62af0185 100644 --- a/src/application/usecases/setup/__tests__/verify_web_setup_apply_use_case.test.ts +++ b/src/application/usecases/setup/__tests__/verify_web_setup_apply_use_case.test.ts @@ -40,6 +40,8 @@ describe('VerifyWebSetupApplyUseCase', () => { const { ports, useCase } = harness(); expect(await useCase.execute(request)).toBe('approved'); expect(ports.fileSnapshotMatches).toHaveBeenCalledWith(repository.checkoutRoot, request.selectedFiles, request.fileSnapshot); + expect(ports.readRepositoryFacts).toHaveBeenCalledTimes(2); + expect(ports.fileSnapshotMatches).toHaveBeenCalledTimes(2); expect(ports.remote.inspect).toHaveBeenCalledWith('owner', 'repo', 'test-token'); expect(ports.remote.inspectCredentialHealthWorkflow).toHaveBeenCalledWith('owner', 'repo', 'test-token', request.configuration.repository.mainBranch); expect(ports.permissionAudit.audit).toHaveBeenCalledWith(request.configuration, approvedRemote); @@ -79,6 +81,26 @@ describe('VerifyWebSetupApplyUseCase', () => { expect(ports.remote.inspect).not.toHaveBeenCalled(); }); + test('rejects a changed HEAD after asynchronous GitHub and permission checks', async () => { + const { ports, useCase } = harness(); + const read = jest.spyOn(ports, 'readRepositoryFacts') + .mockReturnValueOnce({ ...repository }) + .mockReturnValue({ ...repository, head: 'b'.repeat(40) }); + await expect(useCase.execute(request)).rejects.toThrow('repository identity changed'); + expect(read).toHaveBeenCalledTimes(2); + expect(ports.permissionAudit.audit).toHaveBeenCalledTimes(1); + }); + + test('rejects a selected-file change after asynchronous final checks', async () => { + const { ports, useCase } = harness(); + const matches = jest.spyOn(ports, 'fileSnapshotMatches') + .mockReturnValueOnce(true) + .mockReturnValue(false); + await expect(useCase.execute(request)).rejects.toThrow('Selected repository files changed'); + expect(matches).toHaveBeenCalledTimes(2); + expect(ports.permissionAudit.audit).toHaveBeenCalledTimes(1); + }); + test('fails closed when GitHub facts changed', async () => { const { ports, useCase } = harness(); jest.spyOn(ports.remote, 'inspect').mockResolvedValue({ ...remoteBase, repositoryVisibility: 'public' }); diff --git a/src/application/usecases/setup/verify_web_setup_apply_use_case.ts b/src/application/usecases/setup/verify_web_setup_apply_use_case.ts index fec0ee744..c11fde3e9 100644 --- a/src/application/usecases/setup/verify_web_setup_apply_use_case.ts +++ b/src/application/usecases/setup/verify_web_setup_apply_use_case.ts @@ -39,24 +39,14 @@ export class VerifyWebSetupApplyUseCase { if (decision === undefined) return 'cancelled'; if (decision === 'stop') return 'declined'; this.assertActive(); + this.assertLocalSnapshot(request); - const current = this.ports.readRepositoryFacts(); - const expected = request.repository; - if (!current || current.owner !== expected.owner || current.repository !== expected.repository - || current.checkoutRoot !== expected.checkoutRoot || current.branch !== expected.branch - || current.head !== expected.head) { - throw new ApplicationError('configuration.invalid', 'The repository identity changed during setup. No mutation started; restart and review a new plan.'); - } - if (!this.ports.fileSnapshotMatches(expected.checkoutRoot, request.selectedFiles, request.fileSnapshot)) { - throw new ApplicationError('configuration.invalid', 'Selected repository files changed since plan review. No mutation started; restart and review a new plan.'); - } - - let remote = await this.ports.remote.inspect(expected.owner, expected.repository, request.setupToken); + let remote = await this.ports.remote.inspect(request.repository.owner, request.repository.repository, request.setupToken); this.assertActive(); let credentialHealthWorkflow: 'installed' | 'missing' | 'unavailable' = 'unavailable'; try { credentialHealthWorkflow = await this.ports.remote.inspectCredentialHealthWorkflow?.( - expected.owner, expected.repository, request.setupToken, request.configuration.repository.mainBranch, + request.repository.owner, request.repository.repository, request.setupToken, request.configuration.repository.mainBranch, ) ?? 'unavailable'; } catch { /* Unknown selected-ref state must not inherit a provisional value. */ } this.assertActive(); @@ -69,9 +59,24 @@ export class VerifyWebSetupApplyUseCase { if (audit.status === 'blocked') { throw new ApplicationError('authorization.credential-invalid', 'Setup PAT access changed since plan review. No mutation started; correct the PAT and review a new plan.'); } + // The remote reads above can take time. Close that window before the caller applies. + this.assertLocalSnapshot(request); return 'approved'; } + private assertLocalSnapshot(request: VerifyWebSetupApplyRequest): void { + const current = this.ports.readRepositoryFacts(); + const expected = request.repository; + if (!current || current.owner !== expected.owner || current.repository !== expected.repository + || current.checkoutRoot !== expected.checkoutRoot || current.branch !== expected.branch + || current.head !== expected.head) { + throw new ApplicationError('configuration.invalid', 'The repository identity changed during setup. No mutation started; restart and review a new plan.'); + } + if (!this.ports.fileSnapshotMatches(expected.checkoutRoot, request.selectedFiles, request.fileSnapshot)) { + throw new ApplicationError('configuration.invalid', 'Selected repository files changed since plan review. No mutation started; restart and review a new plan.'); + } + } + private assertActive(): void { const state = this.ports.sessionState(); if (state === 'cancelled') { From b5e4373b531af811f569b2e6ff4796c18c9cae69 Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Mon, 28 Sep 2026 23:00:58 +0200 Subject: [PATCH 23/50] codex-setup-temporary-github-auth: preserve All default and disclose pre-Apply workflow mutations --- build/cli/index.js | 39 ++++++++--- .../{index-D3fDoDei.js => index-CWAStamn.js} | 2 +- build/web/index.html | 2 +- docs/authentication.mdx | 8 +++ docs/how-to-use.mdx | 10 ++- .../operations/troubleshooting.mdx | 5 ++ specs/local-web-setup-assistant.md | 5 ++ ...up-configuration-credentials-and-doctor.md | 18 ++++- src/__tests__/cli.test.ts | 65 ++++++++++++++++++- .../__tests__/setup_journey_use_case.test.ts | 14 +++- .../usecases/setup/setup_journey_use_case.ts | 5 +- .../__tests__/setup_journey_presenter.test.ts | 11 +++- .../__tests__/web_setup_ui_helpers.test.ts | 14 ++++ src/cli/commands/setup.ts | 20 ++++-- src/cli/setup_journey_presenter.ts | 6 +- ...p_remote_credential_health_adapter.test.ts | 39 +++++++++++ .../setup_credentials_composition_root.ts | 6 +- .../setup_remote_credential_health_adapter.ts | 5 ++ web/src/lib/questionAnswer.ts | 3 +- 19 files changed, 249 insertions(+), 28 deletions(-) rename build/web/assets/{index-D3fDoDei.js => index-CWAStamn.js} (75%) diff --git a/build/cli/index.js b/build/cli/index.js index 8f890d9af..e76952861 100755 --- a/build/cli/index.js +++ b/build/cli/index.js @@ -55946,8 +55946,11 @@ class SetupJourneyUseCase { return this.choiceReviewPass; } markMutationStarted() { - if (this.stage !== 'apply' || this.outcome) - throw new Error('Setup mutation must start in the apply stage.'); + if ((this.stage !== 'credentials' && this.stage !== 'apply') || this.outcome) { + throw new Error('Setup mutation can start only during credential validation or apply.'); + } + if (this.mutationStarted) + return; this.mutationStarted = true; this.present(); } @@ -65912,6 +65915,7 @@ function registerSetupCommand(program) { const workflowPrompt = webBridge ? new web_setup_adapters_1.WebSetupWorkflowUpdatePrompt(webBridge) : new setup_workflow_update_prompt_adapter_1.SetupWorkflowUpdatePromptAdapter(terminal); const cwd = process.cwd(); let setupMutationStarted = false; + let setupApplyStarted = false; let releaseSetupGuard; let journey; try { @@ -66151,7 +66155,12 @@ function registerSetupCommand(program) { permissionPresenter.showDetailedRequirements('workflow', workflowTokenPermissions); } } - const credentials = await (0, setup_credentials_composition_root_1.createSetupCredentialsUseCase)(credentialPrompt, permissionPresenter, webBridge ? { allowPreApplyHealthWorkflow: false } : undefined).collect({ + const credentials = await (0, setup_credentials_composition_root_1.createSetupCredentialsUseCase)(credentialPrompt, permissionPresenter, webBridge ? { allowPreApplyHealthWorkflow: false } : { + onTemporaryWorkflowMutationAttempt: () => { + setupMutationStarted = true; + journey?.markMutationStarted(); + }, + }).collect({ owner: gitInfo.owner, repository: gitInfo.repo, setupToken: token ?? '', @@ -66216,6 +66225,7 @@ function registerSetupCommand(program) { } setupMutationStarted = true; journey?.markMutationStarted(); + setupApplyStarted = true; const actionResults = await (0, local_action_1.runLocalAction)(params); if (actionResults.some(actionResult => !actionResult.success || actionResult.errors.length > 0)) { journey?.finish('partial'); @@ -66228,13 +66238,18 @@ function registerSetupCommand(program) { } catch (error) { journey?.finish(setupMutationStarted ? 'partial' : error instanceof setup_credential_prompt_adapter_1.SetupTerminalCancelledError ? 'cancelled' : 'blocked'); + if (setupMutationStarted && !setupApplyStarted) { + (0, logger_1.logInfo)('A temporary credential-health workflow create was attempted before Apply. Inspect the selected branch and GitHub workflow history before retrying; a failed request may still have reached GitHub.'); + } if (credentialPrompt.guidedWorkflowBotIdentity) { - (0, logger_1.logInfo)(setupMutationStarted + (0, logger_1.logInfo)(setupApplyStarted ? 'Setup may be partially applied. Inspect the GitHub Secret before deleting or replacing the bot PAT.' - : 'No setup mutation started. If you generated an unused bot PAT in GitHub, delete it there; Copilot cannot revoke it.'); + : 'No bot Secret write started. If you generated an unused bot PAT in GitHub, delete it there; Copilot cannot revoke it.'); } if (error instanceof setup_credential_prompt_adapter_1.SetupTerminalCancelledError) { - (0, logger_1.logInfo)('Setup cancelled. No changes were applied.'); + (0, logger_1.logInfo)(setupMutationStarted + ? 'Setup stopped after a possible credential-health workflow change. Inspect the selected branch and GitHub workflow history before retrying.' + : 'Setup cancelled. No changes were applied.'); process.exitCode = 130; return; } @@ -67277,10 +67292,12 @@ function renderSetupJourney(view, maximumWidth) { const revisitingChoices = view.current === 'Setup choices' && view.choiceReviewPass > 1; const state = view.outcome === 'complete' ? 'Complete: setup applied successfully.' : view.outcome === 'dry-run' ? 'Complete: dry run only; no changes were applied.' - : view.outcome === 'partial' ? 'Partial: application started; inspect the result before retrying.' + : view.outcome === 'partial' ? 'Partial: changes may exist; inspect the branch and GitHub resources before retrying.' : view.outcome === 'blocked' ? 'Blocked: setup cannot continue.' : view.outcome === 'cancelled' ? 'Cancelled: setup stopped.' - : view.mutationStarted ? 'Applying the approved plan; changes may already exist.' + : view.mutationStarted ? view.current === 'Bot PAT & credentials' + ? 'Checking credentials; a temporary GitHub workflow change may exist.' + : 'Applying the approved plan; changes may already exist.' : 'No changes have been applied.'; return (0, setup_prompt_rendering_1.renderBox)([ `Repository: ${view.repository}`, @@ -82933,7 +82950,9 @@ function createSetupCredentialsUseCase(prompt, permissionPresenter, options = {} const secretNames = new repository_variables_repository_1.RepositorySecretNamesQueryRepository((0, github_identity_client_factory_1.createRepositoryVariablesClient)()); return new setup_credentials_use_case_1.SetupCredentialsUseCase(prompt, new setup_credential_validation_adapter_1.SetupCredentialValidationAdapter(), secretNames, options.allowPreApplyHealthWorkflow === false ? undefined - : new setup_remote_credential_health_adapter_1.SetupRemoteCredentialHealthBootstrapAdapter(new octokit_credential_health_adapter_1.OctokitCredentialHealthClientAdapter()), (0, setup_token_permissions_composition_root_1.createSetupTokenPermissionsUseCase)(), permissionPresenter); + : new setup_remote_credential_health_adapter_1.SetupRemoteCredentialHealthBootstrapAdapter(new octokit_credential_health_adapter_1.OctokitCredentialHealthClientAdapter(), { + onTemporaryWorkflowMutationAttempt: options.onTemporaryWorkflowMutationAttempt, + }), (0, setup_token_permissions_composition_root_1.createSetupTokenPermissionsUseCase)(), permissionPresenter); } function createSetupRemoteConfigurationReadPort() { return new repository_variables_repository_1.SetupRemoteConfigurationQueryRepository((0, github_identity_client_factory_1.createRepositoryVariablesClient)()); @@ -84600,6 +84619,7 @@ class SetupRemoteCredentialHealthBootstrapAdapter { this.githubClient = githubClient; this.options = resolveOptions(options); this.workflowContent = options.workflowContent ?? readHealthWorkflow(); + this.onTemporaryWorkflowMutationAttempt = options.onTemporaryWorkflowMutationAttempt; } async validateExisting(owner, repository, token, ref, requirements) { const client = this.githubClient.getClient(token); @@ -84625,6 +84645,7 @@ class SetupRemoteCredentialHealthBootstrapAdapter { if (!this.workflowContent) throw new Error('Credential health workflow template is unavailable.'); let created; + this.onTemporaryWorkflowMutationAttempt?.(); try { created = await client.repos.createOrUpdateFileContents({ owner, diff --git a/build/web/assets/index-D3fDoDei.js b/build/web/assets/index-CWAStamn.js similarity index 75% rename from build/web/assets/index-D3fDoDei.js rename to build/web/assets/index-CWAStamn.js index 4fd66ad47..a1821bd02 100644 --- a/build/web/assets/index-D3fDoDei.js +++ b/build/web/assets/index-CWAStamn.js @@ -1,2 +1,2 @@ (function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})(),typeof window<`u`&&((window.__svelte??={}).v??=new Set).add(`5`);var e=!1;function t(){e=!0}t();var n={},r=Symbol(`uninitialized`),i=`http://www.w3.org/1999/xhtml`,a=Array.isArray,o=Array.prototype.indexOf,s=Array.prototype.includes,c=Array.from,l=Object.defineProperty,u=Object.getOwnPropertyDescriptor,d=Object.getOwnPropertyDescriptors,f=Object.prototype,p=Array.prototype,m=Object.getPrototypeOf,h=Object.isExtensible,g=()=>{};function _(e){return e()}function v(e){for(var t=0;t{e=n,t=r}),resolve:e,reject:t}}var b=1024,x=2048,S=4096,ee=8192,te=16384,ne=32768,re=1<<25,ie=65536,ae=1<<18,oe=1<<19,se=1<<20,ce=1<<25,le=1<<21,ue=1<<22,de=1<<23,fe=Symbol(`$state`),pe=Symbol(`component`),me=Symbol(`legacy props`),he=Symbol(``),ge=Symbol(`attributes`),_e=Symbol(`class`),ve=Symbol(`style`),ye=Symbol(`text`),be=Symbol(`form reset`),xe=new class extends Error{name=`StaleReactionError`;message="The reaction that called `getAbortSignal()` was re-run or destroyed"},Se=!!globalThis.document?.contentType&&globalThis.document.contentType.includes(`xml`);function Ce(){console.warn(`https://svelte.dev/e/derived_inert`)}function we(e){console.warn(`https://svelte.dev/e/hydration_mismatch`)}function Te(){console.warn(`https://svelte.dev/e/select_multiple_invalid_value`)}function Ee(){console.warn(`https://svelte.dev/e/svelte_boundary_reset_noop`)}var C=!1;function De(e){C=e}var w;function T(e){if(e===null)throw we(),n;return w=e}function Oe(){return T(an(w))}function E(e){if(C){if(an(w)!==null)throw we(),n;w=e}}function ke(e=1){if(C){for(var t=e,n=w;t--;)n=an(n);w=n}}function Ae(e=!0){for(var t=0,n=w;;){if(n.nodeType===8){var r=n.data;if(r===`]`){if(t===0)return n;--t}else(r===`[`||r===`[!`||r[0]===`[`&&!isNaN(Number(r.slice(1))))&&(t+=1)}var i=an(n);e&&n.remove(),n=i}}function je(e){if(!e||e.nodeType!==8)throw we(),n;return e.data}function Me(e){return e===this.v}function Ne(e,t){return e==e?e!==t||typeof e==`object`&&!!e||typeof e==`function`:t==t}function Pe(e){return!Ne(e,this.v)}function Fe(e){throw Error(`https://svelte.dev/e/lifecycle_outside_component`)}function Ie(){throw Error(`https://svelte.dev/e/async_derived_orphan`)}function Le(e,t,n){throw Error(`https://svelte.dev/e/each_key_duplicate`)}function Re(e){throw Error(`https://svelte.dev/e/effect_in_teardown`)}function ze(){throw Error(`https://svelte.dev/e/effect_in_unowned_derived`)}function Be(e){throw Error(`https://svelte.dev/e/effect_orphan`)}function Ve(){throw Error(`https://svelte.dev/e/effect_update_depth_exceeded`)}function He(e){throw Error(`https://svelte.dev/e/props_invalid_value`)}function Ue(){throw Error(`https://svelte.dev/e/state_descriptors_fixed`)}function We(){throw Error(`https://svelte.dev/e/state_prototype_fixed`)}function Ge(){throw Error(`https://svelte.dev/e/state_unsafe_mutation`)}function Ke(){throw Error(`https://svelte.dev/e/svelte_boundary_reset_onerror`)}var D=null;function qe(e){D=e}function O(t,n=!1,r){D={p:D,i:!1,c:null,e:null,s:t,x:null,r:H,l:e&&!n?{s:null,u:null,$:[]}:null}}function k(e){var t=D,n=t.e;if(n!==null){t.e=null;for(var r of n)yn(r)}return e!==void 0&&(t.x=e),t.i=!0,D=t.p,Je(e)}function Je(e={}){return l(e,pe,{value:!0}),e}function Ye(){return!e||D!==null&&D.l===null}var Xe=[];function Ze(){var e=Xe;Xe=[],v(e)}function Qe(e){if(Xe.length===0&&!wt){var t=Xe;queueMicrotask(()=>{t===Xe&&Ze()})}Xe.push(e)}function $e(){for(;Xe.length>0;)Ze()}var et=~(x|S|b);function A(e,t){e.f=e.f&et|t}function tt(e){e.f&512||e.deps===null?A(e,b):A(e,S)}function nt(e,t,n){e.f&2048?t.add(e):e.f&4096&&n.add(e),A(e,b)}var rt=!1;function it(){rt||(rt=!0,document.addEventListener(`reset`,e=>{Promise.resolve().then(()=>{if(!e.defaultPrevented)for(let t of e.target.elements)t[be]?.()})},{capture:!0}))}function at(e){var t=V,n=H;Un(null),Wn(null);try{return e()}finally{Un(t),Wn(n)}}function ot(e,t,n,r=n){e.addEventListener(t,()=>at(n));let i=e[be];e[be]=i?()=>{i(),r(!0)}:()=>r(!0),it()}function st(e,t,n,r){let i=Ye()?dt:mt;var a=e.filter(e=>!e.settled),o=t.map(i);if(n.length===0&&a.length===0){r(o);return}var s=H,c=ct(),l=a.length===1?a[0].promise:a.length>1?Promise.all(a.map(e=>e.promise)):null;function u(e){if(!(s.f&16384)){c();try{r([...o,...e])}catch(e){fn(e,s)}lt()}}var d=ut();if(n.length===0){l.then(()=>u([])).finally(d);return}function f(){Promise.all(n.map(e=>pt(e))).then(u).catch(e=>fn(e,s)).finally(d)}l?l.then(()=>{c(),f(),lt()}):f()}function ct(){var e=H,t=V,n=D,r=j;return function(i=!0){Wn(e),Un(t),qe(n),i&&!(e.f&16384)&&(r?.activate(),r?.apply())}}function lt(e=!0){Wn(null),Un(null),qe(null),e&&j?.deactivate()}function ut(){var e=H,t=e.b,n=j,r=!!t?.is_rendered();return t?.update_pending_count(1,n),n.increment(r,e),()=>{t?.update_pending_count(-1,n),n.decrement(r,e)}}function dt(e){var t=2|x;return H!==null&&(H.f|=oe),{ctx:D,deps:null,effects:null,equals:Me,f:t,fn:e,reactions:null,rv:0,v:r,wv:0,parent:H,ac:null}}var ft=Symbol(`obsolete`);function pt(e,t,n){let i=H;i===null&&Ie();var a=void 0,o=Vt(r),s=!V,c=new Set;return Tn(()=>{var t=H,n=y();a=n.promise;try{Promise.resolve(e()).then(n.resolve,e=>{e!==xe&&n.reject(e)}).finally(lt)}catch(e){n.reject(e),lt()}var r=j;if(s){if(t.f&32768)var l=ut();if(i.b?.is_rendered())r.async_deriveds.get(t)?.reject(ft);else for(let e of c.values())e.reject(ft);c.add(n),r.async_deriveds.set(t,n)}let u=(e,t=void 0)=>{l?.(),c.delete(n),t!==ft&&(r.activate(),t?(o.f|=de,Gt(o,t)):(o.f&8388608&&(o.f^=de),Gt(o,e)),r.deactivate())};n.promise.then(u,e=>u(null,e||`unknown`))}),_n(()=>{for(let e of c)e.reject(ft)}),new Promise(e=>{function t(n){function r(){n===a?e(o):t(a)}n.then(r,r)}t(a)})}function mt(e){let t=dt(e);return t.equals=Pe,t}function ht(e){var t=e.effects;if(t!==null){e.effects=null;for(var n=0;n{t.ac.abort(xe),t.ac=null}),t.fn!==null&&(t.teardown=g),ar(t,0),kn(t))}function yt(e){if(e.effects!==null)for(let t of e.effects)t.teardown&&t.fn!==null&&or(t)}var bt=null,j=null,xt=null,St=null,Ct=null,wt=!1,Tt=!1,Et=null,Dt=null,Ot=0,kt=1,At=class e{id=kt++;#e=!1;linked=!0;#t=null;#n=null;async_deriveds=new Map;current=new Map;previous=new Map;#r=new Set;#i=new Set;#a=0;#o=new Map;#s=null;#c=[];#l=[];#u=new Set;#d=new Set;#f=new Map;#p=new Set;is_fork=!1;#m=!1;constructor(){bt===null?bt=this:(bt.#n=this,this.#t=bt),bt=this}#h(){if(this.is_fork)return!0;for(let n of this.#o.keys()){for(var e=n,t=!1;e.parent!==null;){if(this.#f.has(e)){t=!0;break}e=e.parent}if(!t)return!0}return!1}skip_effect(e){this.#f.has(e)||this.#f.set(e,{d:[],m:[]}),this.#p.delete(e)}unskip_effect(e,t=e=>this.schedule(e)){var n=this.#f.get(e);if(n){this.#f.delete(e);for(var r of n.d)A(r,x),t(r);for(r of n.m)A(r,S),t(r)}this.#p.add(e)}#g(){var e=[];for(let i of this.#c)if(!(i.f&16384||!(i.f&6144))){for(var t=i,n=!1;t.parent!==null;){t=t.parent;var r=t.f;if(r&96){if(!(r&1024)){n=!0;break}t.f^=b}}n||e.push(t)}return this.#c=[],e}#_(){this.#e=!0;for(let e of this.#u)this.#d.delete(e),A(e,x),this.schedule(e);for(let e of this.#d)A(e,S),this.schedule(e);this.apply();for(var t=Et=[],n=[],r=Dt=[];this.#c.length>0;){Ot++>1e3&&(this.#S(),Mt());for(let e of this.#g())try{this.#v(e,t,n)}catch(t){throw Lt(e),this.#h()||this.discard(),t}}if(j=null,r.length>0){var i=e.ensure();for(let e of r)i.schedule(e)}if(Et=null,Dt=null,this.#h()){this.#x(n),this.#x(t);for(let[e,t]of this.#f)It(e,t);r.length>0&&j.#_();return}let a=this.#y();if(a){this.#x(n),this.#x(t),a.#b(this);return}this.#u.clear(),this.#d.clear();for(let e of this.#r)e(this);this.#r.clear(),xt=this,Pt(n),Pt(t),xt=null,this.#s?.resolve();var o=j;if(this.#a===0&&(this.#c.length===0||o!==null)&&this.#S(),this.#c.length>0){if(o!==null){for(let e of this.#c)o.#c.push(e);this.#c=[]}else o=this}o!==null&&(zt.clear(),o.#_())}#v(e,t,n){e.f^=b;for(var r=e.first;r!==null;){var i=r.f,a=!!(i&96);if(!(a&&i&1024||i&8192||this.#f.has(r))&&r.fn!==null){a?r.f^=b:i&4?t.push(r):er(r)&&(i&16&&this.#d.add(r),or(r));var o=r.first;if(o!==null){r=o;continue}}for(;r!==null;){var s=r.next;if(s!==null){r=s;break}r=r.parent}}}#y(){for(var e=this.#t;e!==null;){if(!e.is_fork){for(let[t,[,n]]of this.current)if(e.current.has(t)&&!n)return e}e=e.#t}return null}#b(e){for(let[t,n]of e.current)!this.previous.has(t)&&e.previous.has(t)&&this.previous.set(t,e.previous.get(t)),this.current.set(t,n);for(let[t,n]of e.async_deriveds){let e=this.async_deriveds.get(t);e&&n.promise.then(e.resolve).catch(e.reject)}e.async_deriveds.clear(),this.transfer_effects(e.#u,e.#d);let t=e=>{var n=e.reactions;if(n!==null&&!(e.f&2&&!(e.f&6144)))for(let e of n){var r=e.f;if(r&2)t(e);else{var i=e;r&4194320&&!this.async_deriveds.has(i)&&(this.#d.delete(i),A(i,x),this.schedule(i))}}};for(let e of this.current.keys())t(e);this.oncommit(()=>e.discard()),e.#S(),j=this,this.#_()}#x(e){for(var t=0;t{this.#m=!1,this.linked&&this.flush()}))}transfer_effects(e,t){for(let t of e)this.#u.add(t);for(let e of t)this.#d.add(e);e.clear(),t.clear()}oncommit(e){this.#r.add(e)}ondiscard(e){this.#i.add(e)}settled(){return(this.#s??=y()).promise}static ensure(){if(j===null){let t=j=new e;!Tt&&!wt&&Qe(()=>{t.#e||t.flush()})}return j}apply(){St=null}schedule(e){if(Ct=e,e.b?.is_pending&&e.f&16777228&&!(e.f&32768)){e.b.defer_effect(e);return}this.#c.push(e)}#S(){if(this.linked){var e=this.#t,t=this.#n;e===null||(e.#n=t),t===null?bt=e:t.#t=e,this.linked=!1}}};function jt(e){var t=wt;wt=!0;try{var n;for(e&&(j!==null&&!j.is_fork&&j.flush(),n=e());;){if($e(),j===null)return n;j.flush()}}finally{wt=t}}function Mt(){try{Ve()}catch(e){fn(e,Ct)}}var Nt=null;function Pt(e){var t=e.length;if(t!==0){for(var n=0;n0)){zt.clear();for(let e of Nt){if(e.f&24576)continue;let t=[e],n=e.parent;for(;n!==null;)Nt.has(n)&&(Nt.delete(n),t.push(n)),n=n.parent;for(let e=t.length-1;e>=0;e--){let n=t[e];n.f&24576||or(n)}}Nt.clear()}}Nt=null}}function Ft(e){j.schedule(e)}function It(e,t){if(!(e.f&32&&e.f&1024)){e.f&2048?t.d.push(e):e.f&4096&&t.m.push(e),A(e,b);for(var n=e.first;n!==null;)It(n,t),n=n.next}}function Lt(e){A(e,b);for(var t=e.first;t!==null;)Lt(t),t=t.next}var Rt=new Set,zt=new Map,Bt=!1;function Vt(e,t){return{f:0,v:e,reactions:null,equals:Me,rv:0,wv:0}}function Ht(e,t){let n=Vt(e,t);return Kn(n),n}function M(t,n=!1,r=!0){let i=Vt(t);return n||(i.equals=Pe),e&&r&&D!==null&&D.l!==null&&(D.l.s??=[]).push(i),i}function N(e,t,n=!1){return V!==null&&(!Hn||V.f&131072)&&Ye()&&V.f&4325394&&(Gn===null||!Gn.has(e))&&Ge(),Gt(e,n?Yt(t):t,Dt)}var Ut=null,Wt=0;function Gt(e,t,n=null){if(!e.equals(t)){Bn?zt.set(e,t):zt.has(e)||zt.set(e,e.v);var r=At.ensure();if(r.capture(e,t),e.f&2){let t=e;e.f&2048&>(t),St===null&&tt(t)}e.wv=$n(),Ut=null,Wt=0,Jt(e,x,n),Ut=null,Ye()&&H!==null&&H.f&1024&&!(H.f&96)&&(qn===null?Jn([e]):qn.push(e)),!r.is_fork&&Rt.size>0&&!Bt&&Kt()}return t}function Kt(){Bt=!1;for(let e of Rt){e.f&1024&&A(e,S);let t;try{t=er(e)}catch{t=!0}t&&or(e)}Rt.clear()}function qt(e){N(e,e.v+1)}function Jt(e,t,n){var r=e.reactions;if(r!==null){var i=Ye(),a=r.length;if(Wt+=a,Wt>1e5&&Ut===null&&(Ut=new Set),Ut!==null){if(Ut.has(e))return;Ut.add(e)}for(var o=0;o{if(Zn===c)return e();var t=V,n=Zn;Un(null),Qn(c);var r=e();return Un(t),Qn(n),r};return i&&n.set(`length`,Ht(e.length,s)),new Proxy(e,{defineProperty(e,t,r){(!(`value`in r)||r.configurable===!1||r.enumerable===!1||r.writable===!1)&&Ue();var i=n.get(t);return i===void 0?l(()=>{var e=Ht(r.value,s);return n.set(t,e),e}):N(i,r.value,!0),!0},deleteProperty(e,t){var i=n.get(t);if(i===void 0){if(t in e){let e=l(()=>Ht(r,s));n.set(t,e),qt(o)}}else N(i,r),qt(o);return!0},get(t,i,a){if(i===fe)return e;var o=n.get(i),c=i in t;if(o===void 0&&(!c||u(t,i)?.writable)&&(o=l(()=>Ht(Yt(c?t[i]:r),s)),n.set(i,o)),o!==void 0){var d=G(o);return d===r?void 0:d}return Reflect.get(t,i,a)},getOwnPropertyDescriptor(e,t){this.has?.(e,t);var i=Reflect.getOwnPropertyDescriptor(e,t),a=n.get(t);if(a!==void 0){var o=G(a);if(o===r)return;if(i&&`value`in i)i.value=o;else return{enumerable:!0,configurable:!0,value:o,writable:!0}}return i},has(e,t){if(t===fe)return!0;var i=n.get(t),a=i!==void 0&&i.v!==r||Reflect.has(e,t);return(i!==void 0||H!==null&&(!a||u(e,t)?.writable))&&(i===void 0&&(i=l(()=>Ht(a?Yt(e[t]):r,s)),n.set(t,i)),G(i)===r)?!1:a},set(e,t,a,c){var d=n.get(t),f=t in e;if(i&&t===`length`)for(var p=a;pHt(r,s)),n.set(p+``,m)):N(m,r)}if(d===void 0)(!f||u(e,t)?.writable)&&(d=l(()=>Ht(void 0,s)),N(d,Yt(a)),n.set(t,d));else{f=d.v!==r;var h=l(()=>Yt(a));N(d,h)}var g=Reflect.getOwnPropertyDescriptor(e,t);if(g?.set&&g.set.call(c,a),!f){if(i&&typeof t==`string`){var _=n.get(`length`),v=Number(t);Number.isInteger(v)&&v>=_.v&&N(_,v+1)}qt(o)}return!0},ownKeys(e){G(o);var t=Reflect.ownKeys(e).filter(e=>{var t=n.get(e);return t===void 0||t.v!==r});for(var[i,a]of n)a.v!==r&&!(i in e)&&t.push(i);return t},setPrototypeOf(){We()}})}function Xt(e){try{if(typeof e==`object`&&e&&fe in e)return e[fe]}catch{}return e}function Zt(e,t){return Object.is(Xt(e),Xt(t))}var Qt,$t,en,tn;function nn(){if(Qt===void 0){Qt=window,$t=/Firefox/.test(navigator.userAgent);var e=Element.prototype,t=Node.prototype,n=Text.prototype;en=u(t,`firstChild`).get,tn=u(t,`nextSibling`).get,h(e)&&(e[_e]=void 0,e[ge]=null,e[ve]=void 0,e.__e=void 0),h(n)&&(n[ye]=void 0)}}function P(e=``){return document.createTextNode(e)}function rn(e){return en.call(e)}function an(e){return tn.call(e)}function F(e,t){if(!C)return rn(e);var n=rn(w);if(n===null)n=w.appendChild(P());else if(t&&n.nodeType!==3){var r=P();return n?.before(r),T(r),r}return t&&un(n),T(n),n}function on(e,t=!1){if(!C){var n=rn(e);return n instanceof Comment&&n.data===``?an(n):n}if(t){if(w?.nodeType!==3){var r=P();return w?.before(r),T(r),r}un(w)}return w}function I(e,t=!1){if(!C)return rn(e);var n=F(e,t);return E(e),n}function L(e,t=1,n=!1){let r=C?w:e;for(var i;t--;)i=r,r=an(r);if(!C)return r;if(n){if(r?.nodeType!==3){var a=P();return r===null?i?.after(a):r.before(a),T(a),a}un(r)}return T(r),r}function sn(e){e.textContent=``}function cn(){return!1}function ln(e,t,n){return t==null||t===`http://www.w3.org/1999/xhtml`?n?document.createElement(e,{is:n}):document.createElement(e):n?document.createElementNS(t,e,{is:n}):document.createElementNS(t,e)}function un(e){if(e.nodeValue.length<65536)return;let t=e.nextSibling;for(;t!==null&&t.nodeType===3;)t.remove(),e.nodeValue+=t.nodeValue,t=e.nextSibling}function dn(e){var t=H;if(t===null)return V.f|=de,e;if(!(t.f&32768)&&!(t.f&4))throw e;fn(e,t)}function fn(e,t){if(!(t!==null&&t.f&16384)){for(;t!==null;){if(t.f&128&&!(t.f&33570816)){if(!(t.f&32768))throw e;try{t.b.error(e);return}catch(t){e=t}}t=t.parent}throw e}}function pn(e){H===null&&(V===null&&Be(e),ze()),Bn&&Re(e)}function mn(e,t){var n=t.last;n===null?t.last=t.first=e:(n.next=e,e.prev=n,t.last=e)}function hn(e,t){var n=H;n!==null&&n.f&8192&&(e|=ee);var r={ctx:D,deps:null,nodes:null,f:e|x|512,first:null,fn:t,last:null,next:null,parent:n,b:n&&n.b,prev:null,teardown:null,wv:0,ac:null};j?.register_created_effect(r);var i=r;if(e&4)Et===null?At.ensure().schedule(r):Et.push(r);else if(t!==null){try{or(r)}catch(e){throw B(r),e}i.deps===null&&i.teardown===null&&i.nodes===null&&i.first===i.last&&!(i.f&524288)&&(i=i.first,e&16&&e&65536&&i!==null&&(i.f|=ie))}if(i!==null&&(i.parent=n,n!==null&&mn(i,n),V!==null&&V.f&2&&!(e&64))){var a=V;(a.effects??=[]).push(i)}return r}function gn(){return V!==null&&!Hn}function _n(e){let t=hn(8,null);return A(t,b),t.teardown=e,t}function vn(e){pn(`$effect`);var t=H.f;if(!V&&t&32&&D!==null&&!D.i){var n=D;(n.e??=[]).push(e)}else return yn(e)}function yn(e){return hn(4|se,e)}function bn(e){return pn(`$effect.pre`),hn(8|se,e)}function xn(e){At.ensure();let t=hn(64|oe,e);return(e={})=>new Promise(n=>{e.outro?Nn(t,()=>{B(t),n(void 0)}):(B(t),n(void 0))})}function Sn(e){return hn(4,e)}function Cn(e,t){var n=D,r={effect:null,ran:!1,deps:e};n.l.$.push(r),r.effect=En(()=>{if(e(),!r.ran){r.ran=!0;var n=H;try{Wn(n.parent),K(t)}finally{Wn(n)}}})}function wn(){var e=D;En(()=>{for(var t of e.l.$){t.deps();var n=t.effect;n.f&1024&&n.deps!==null&&A(n,S),er(n)&&or(n),t.ran=!1}})}function Tn(e){return hn(ue|oe,e)}function En(e,t=0){return hn(8|t,e)}function R(e,t=[],n=[],r=[]){st(r,t,n,t=>{hn(8,()=>{e(...t.map(G))})})}function Dn(e,t=0){return hn(16|t,e)}function z(e){return hn(32|oe,e)}function On(e){var t=e.teardown;if(t!==null){let n=Bn,r=V;Vn(!0),Un(null);try{t.call(null)}catch(t){fn(t,e.parent)}finally{Vn(n),Un(r)}}}function kn(e,t=!1){var n=e.first;for(e.first=e.last=null;n!==null;){let e=n.ac;e!==null&&at(()=>{e.abort(xe)});var r=n.next;n.f&64?n.parent=null:B(n,t),n=r}}function An(e){for(var t=e.first;t!==null;){var n=t.next;t.f&32||B(t),t=n}}function B(e,t=!0){var n=!1;(t||e.f&262144)&&e.nodes!==null&&e.nodes.end!==null&&(jn(e.nodes.start,e.nodes.end),n=!0),e.f|=re,kn(e,t&&!n),ar(e,0);var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)e.stop();On(e),e.f^=re,e.f|=te;var i=e.parent;i!==null&&i.first!==null&&Mn(e),e.next=e.prev=e.teardown=e.ctx=e.deps=e.fn=e.nodes=e.ac=e.b=null}function jn(e,t){for(;e!==null;){var n=e===t?null:an(e);e.remove(),e=n}}function Mn(e){var t=e.parent,n=e.prev,r=e.next;n!==null&&(n.next=r),r!==null&&(r.prev=n),t!==null&&(t.first===e&&(t.first=r),t.last===e&&(t.last=n))}function Nn(e,t,n=!0){var r=[];e.f|=256,Pn(e,r,!0);var i=()=>{n&&B(e),t&&t()},a=r.length;if(a>0){var o=()=>--a||i();for(var s of r)s.out(o)}else i()}function Pn(e,t,n){if(!(e.f&8192)){e.f^=ee;var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)(e.is_global||n)&&t.push(e);for(var i=e.first;i!==null;){var a=i.next;if(!(i.f&64)){var o=!!(i.f&65536)||!!(i.f&32)&&!!(e.f&16);Pn(i,t,o?n:!1)}i=a}}}function Fn(e){e.f&=-257,In(e,!0)}function In(e,t){if(!(e.f&256)&&e.f&8192){e.f^=ee,e.f&1024||(A(e,x),At.ensure().schedule(e));for(var n=e.first;n!==null;){var r=n.next,i=!!(n.f&65536)||!!(n.f&32);In(n,i?t:!1),n=r}var a=e.nodes&&e.nodes.t;if(a!==null)for(let e of a)(e.is_global||t)&&e.in()}}function Ln(e,t){if(e.nodes)for(var n=e.nodes.start,r=e.nodes.end;n!==null;){var i=n===r?null:an(n);t.append(n),n=i}}var Rn=null,zn=!1,Bn=!1;function Vn(e){Bn=e}var V=null,Hn=!1;function Un(e){V=e}var H=null;function Wn(e){H=e}var Gn=null;function Kn(e){V!==null&&(V.f&2097152||V.f&2)&&(Gn??=new Set).add(e)}var U=null,W=0,qn=null;function Jn(e){qn=e}var Yn=1,Xn=0,Zn=Xn;function Qn(e){Zn=e}function $n(){return++Yn}function er(e){var t=e.f;if(t&2048)return!0;if(t&4096){for(var n=e.deps,r=n.length,i=0;ie.wv)return!0}t&512&&St===null&&A(e,b)}return!1}function tr(e,t,n=!0){var r=e.reactions;if(r!==null&&!(Gn!==null&&Gn.has(e)))for(var i=0;i{e.ac.abort(xe)}),e.ac=null);try{e.f|=le;var u=e.fn,d=u();e.f|=ne;var f=rr(e);if(Ye()&&qn!==null&&!Hn&&f!==null&&!(e.f&6146))for(var p=0;p0)for(t.length=W+U.length,r=0;r{c.ac.abort(xe),c.ac=null,A(c,x)}),vt(c),ar(c,0)}}function ar(e,t){var n=e.deps;if(n!==null)for(var r=t;rn?.call(this,e))}return e.startsWith(`pointer`)||e.startsWith(`touch`)||e===`wheel`?(i.__removed=!1,Qe(()=>{i.__removed||t.addEventListener(e,i,r)})):t.addEventListener(e,i,r),i}function hr(e,t,n,r,i){var a={capture:r,passive:i},o=mr(e,t,n,a);(t===document.body||t===window||t===document||t instanceof HTMLMediaElement)&&_n(()=>{o.__removed=!0,t.removeEventListener(e,o,a)})}function gr(e,t,n){(t[dr]??={})[e]=n}function _r(e){for(var t=0;t{yr=!1,vr=null}));var o=0,s=vr===e&&e[dr];if(s){var c=i.indexOf(s);if(c!==-1&&(t===document||t===window)){e[dr]=t;return}var u=i.indexOf(t);if(u===-1)return;c<=u&&(o=c)}if(a=i[o]||e.target,a!==t){l(e,`currentTarget`,{configurable:!0,get(){return a||n}});var d=V,f=H;Un(null),Wn(null);try{for(var p,m=[];a!==null&&a!==t;){try{var h=a[dr]?.[r];h!=null&&(!a.disabled||e.target===a)&&h.call(a,e)}catch(e){p?m.push(e):p=e}if(e.cancelBubble)break;o++,a=o{throw e});throw p}}finally{e[dr]=t,delete e.currentTarget,Un(d),Wn(f)}}}var xr=globalThis?.window?.trustedTypes&&globalThis.window.trustedTypes.createPolicy(`svelte-trusted-html`,{createHTML:e=>e});function Sr(e){return xr?.createHTML(e)??e}function Cr(e){var t=ln(`template`);return t.innerHTML=Sr(e.replaceAll(``,``)),t.content}function wr(e,t){var n=H;n.nodes===null&&(n.nodes={start:e,end:t,a:null,t:null})}function J(e,t){var n=!!(t&1),r=!!(t&2),i,a=!e.startsWith(``);return()=>{if(C)return wr(w,null),w;i===void 0&&(i=Cr(a?e:``+e),n||(i=rn(i)));var t=r||$t?document.importNode(i,!0):i.cloneNode(!0);if(n){var o=rn(t),s=t.lastChild;wr(o,s)}else wr(t,t);return t}}function Tr(){if(C)return wr(w,null),w;var e=document.createDocumentFragment(),t=document.createComment(``),n=P();return e.append(t,n),wr(t,n),e}function Y(e,t){if(C){var n=H;(!(n.f&32768)||n.nodes.end===null)&&(n.nodes.end=w),Oe();return}e!==null&&e.before(t)}[...`allowfullscreen.async.autofocus.autoplay.checked.controls.default.disabled.formnovalidate.indeterminate.inert.ismap.loop.multiple.muted.nomodule.novalidate.open.playsinline.readonly.required.reversed.seamless.selected.webkitdirectory.defer.disablepictureinpicture.disableremoteplayback`.split(`.`)];var Er=[`touchstart`,`touchmove`];function Dr(e){return Er.includes(e)}function Or(e){let t=0,n=Vt(0),r;return()=>{gn()&&(G(n),En(()=>(t===0&&(r=K(()=>e(()=>qt(n)))),t+=1,()=>{Qe(()=>{--t,t===0&&(r?.(),r=void 0,qt(n))})})))}}var kr=ie|oe;function Ar(e,t,n,r){new jr(e,t,n,r)}var jr=class{parent;is_pending=!1;transform_error;#e;#t=C?w:null;#n;#r;#i;#a=null;#o=null;#s=null;#c=null;#l=0;#u=0;#d=!1;#f=new Set;#p=new Set;#m=null;#h=Or(()=>(this.#m=Vt(this.#l),()=>{this.#m=null}));constructor(e,t,n,r){this.#e=e,this.#n=t,this.#r=e=>{var t=H;t.b=this,t.f|=128,n(e)},this.parent=H.b,this.transform_error=r??this.parent?.transform_error??(e=>e),this.#i=Dn(()=>{if(C){let e=this.#t;Oe();let t=e.data===`[!`;if(e.data.startsWith(`[?`)){let t=JSON.parse(e.data.slice(2));this.#_(t)}else t?this.#y():this.#g()}else this.#b()},kr),C&&(this.#e=w)}#g(){try{this.#a=z(()=>this.#r(this.#e))}catch(e){this.error(e)}}#_(e){let t=this.#n.failed,{reset:n,invoke_onerror:r}=this.#v(e);Qe(r),t&&(this.#s=z(()=>{t(this.#e,()=>e,()=>n)}))}#v(e){var t=!1,n=!1;let r=()=>{if(t){Ee();return}t=!0,n&&Ke(),this.#s!==null&&Nn(this.#s,()=>{this.#s=null}),this.#S(()=>{this.#b()})};return{reset:r,invoke_onerror:()=>{try{n=!0,this.#n.onerror?.(e,r),n=!1}catch(e){fn(e,this.#i&&this.#i.parent)}}}}#y(){let e=this.#n.pending;e&&(this.is_pending=!0,this.#o=z(()=>e(this.#e)),Qe(()=>{var e=this.#c=document.createDocumentFragment(),t=P(),n=!1;if(e.append(t),this.#a=this.#S(()=>{try{return z(()=>this.#r(t))}catch(e){try{this.error(e),n=!0}catch(e){fn(e,this.#i.parent)}return null}}),this.#a===null){this.#c=null,n&&this.#x(j);return}this.#u===0&&(this.#e.before(e),this.#c=null,Nn(this.#o,()=>{this.#o=null}),this.#x(j))}))}#b(){try{if(this.is_pending=this.has_pending_snippet(),this.#u=0,this.#l=0,this.#a=z(()=>{this.#r(this.#e)}),this.#u>0){var e=this.#c=document.createDocumentFragment();Ln(this.#a,e);let t=this.#n.pending;this.#o=z(()=>t(this.#e))}else this.#x(j)}catch(e){this.error(e)}}#x(e){this.is_pending=!1,e.transfer_effects(this.#f,this.#p)}defer_effect(e){nt(e,this.#f,this.#p)}is_rendered(){return!this.is_pending&&(!this.parent||this.parent.is_rendered())}has_pending_snippet(){return!!this.#n.pending}#S(e){var t=H,n=V,r=D;Wn(this.#i),Un(this.#i),qe(this.#i.ctx);try{return At.ensure(),e()}finally{Wn(t),Un(n),qe(r)}}#C(e,t){if(!this.has_pending_snippet()){this.parent&&this.parent.#C(e,t);return}this.#u+=e,this.#u===0&&(this.#x(t),this.#o&&Nn(this.#o,()=>{this.#o=null}),this.#c&&=(this.#e.before(this.#c),null))}update_pending_count(e,t){this.#C(e,t),this.#l+=e,!(!this.#m||this.#d)&&(this.#d=!0,Qe(()=>{this.#d=!1,this.#m&&Gt(this.#m,this.#l)}))}get_effect_pending(){return this.#h(),G(this.#m)}error(e){if(!this.#n.onerror&&!this.#n.failed)throw e;j?.is_fork?(this.#a&&j.skip_effect(this.#a),this.#o&&j.skip_effect(this.#o),this.#s&&j.skip_effect(this.#s),j.oncommit(()=>{this.#w(e)})):this.#w(e)}#w(e){this.#a&&=(B(this.#a),null),this.#o&&=(B(this.#o),null),this.#s&&=(B(this.#s),null),C&&(T(this.#t),ke(),T(Ae()));let t=this.#n.failed,n=e=>{let{reset:n,invoke_onerror:r}=this.#v(e);r(),t&&(this.#s=this.#S(()=>{try{return z(()=>{var r=H;r.b=this,r.f|=128,t(this.#e,()=>e,()=>n)})}catch(e){return fn(e,this.#i.parent),null}}))};Qe(()=>{var t;try{t=this.transform_error(e)}catch(e){fn(e,this.#i&&this.#i.parent);return}typeof t==`object`&&t&&typeof t.then==`function`?t.then(n,e=>fn(e,this.#i&&this.#i.parent)):n(t)})}};function X(e,t){var n=t==null?``:typeof t==`object`?`${t}`:t;n!==(e[ye]??=e.nodeValue)&&(e[ye]=n,e.nodeValue=`${n}`)}function Mr(e,t){return Pr(e,t)}var Nr=new Map;function Pr(e,{target:t,anchor:r,props:i={},events:a,context:o,intro:s=!0,transformError:l}){nn();var u=void 0,d=xn(()=>{var s=r??t.appendChild(P());Ar(s,{pending:()=>{}},t=>{O({});var r=D;if(o&&(r.c=o),a&&(i.$$events=a),C&&wr(t,null),u=e(t,i)||Je(),C&&(H.nodes.end=w,w===null||w.nodeType!==8||w.data!==`]`))throw we(),n;k()},l);var d=new Set,f=e=>{for(var n=0;n{for(var e of d)for(let r of[t,document]){var n=Nr.get(r),i=n.get(e);--i==0?(r.removeEventListener(e,br),n.delete(e),n.size===0&&Nr.delete(r)):n.set(e,i)}pr.delete(f),s!==r&&s.parentNode?.removeChild(s)}});return Fr.set(u,d),u}var Fr=new WeakMap,Ir=class{anchor;#e=new Map;#t=new Map;#n=new Map;#r=new Set;#i=!0;constructor(e,t=!0){this.anchor=e,this.#i=t}#a=e=>{if(this.#e.has(e)){var t=this.#e.get(e),n=this.#t.get(t);if(n)Fn(n),this.#r.delete(t);else{var r=this.#n.get(t);r&&(Fn(r.effect),this.#t.set(t,r.effect),this.#n.delete(t),r.fragment.lastChild.remove(),this.anchor.before(r.fragment),n=r.effect)}for(let[t,n]of this.#e){if(this.#e.delete(t),t===e)break;let r=this.#n.get(n);r&&(B(r.effect),this.#n.delete(n))}for(let[e,r]of this.#t){if(e===t||this.#r.has(e))continue;let i=()=>{if(Array.from(this.#e.values()).includes(e)){var t=document.createDocumentFragment();Ln(r,t),t.append(P()),this.#n.set(e,{effect:r,fragment:t})}else B(r);this.#r.delete(e),this.#t.delete(e)};this.#i||!n?(this.#r.add(e),Nn(r,i,!1)):i()}}};#o=e=>{this.#e.delete(e);let t=Array.from(this.#e.values());for(let[e,n]of this.#n)t.includes(e)||(B(n.effect),this.#n.delete(e))};ensure(e,t){var n=j,r=cn();if(t&&!this.#t.has(e)&&!this.#n.has(e)){if(r){var i=document.createDocumentFragment(),a=P();i.append(a),this.#n.set(e,{effect:z(()=>t(a)),fragment:i})}else this.#t.set(e,z(()=>t(this.anchor)))}if(this.#e.set(n,e),r){for(let[t,r]of this.#t)t===e?n.unskip_effect(r):n.skip_effect(r);for(let[t,r]of this.#n)t===e?n.unskip_effect(r.effect):n.skip_effect(r.effect);n.oncommit(this.#a),n.ondiscard(this.#o)}else C&&(this.anchor=w),this.#a(n)}};function Lr(t){D===null&&Fe(`onMount`),e&&D.l!==null?Rr(D).m.push(t):vn(()=>{let e=K(t);if(typeof e==`function`)return e})}function Rr(e){var t=e.l;return t.u??={a:[],b:[],m:[]}}function Z(e,t,n=!1){var r;C&&(r=w,Oe());var i=new Ir(e),a=n?ie:0;function o(e,t){if(C){var n=je(r);if(e!==parseInt(n.substring(1))){var a=Ae();T(a),i.anchor=a,De(!1),i.ensure(e,t),De(!0);return}}i.ensure(e,t)}Dn(()=>{var e=!1;t((t,n=0)=>{e=!0,o(n,t)}),e||o(-1,null)},a)}var zr=Symbol(`NaN`);function Br(e,t,n){C&&Oe();var r=new Ir(e),i=!Ye();Dn(()=>{var e=t();e!==e&&(e=zr),i&&typeof e==`object`&&e&&(e={}),r.ensure(e,n)})}function Vr(e,t){return t}function Hr(e,t,n){for(var r=[],i=t.length,a,o=t.length,s=0;s{if(a){if(a.pending.delete(n),a.done.add(n),a.pending.size===0){var t=e.outrogroups;Ur(e,c(a.done)),t.delete(a),t.size===0&&(e.outrogroups=null)}}else--o},!1)}if(o===0){var l=r.length===0&&n!==null&&e.pending.size===0;if(l){var u=n,d=u.parentNode;sn(d),d.append(u),e.items.clear()}Ur(e,t,!l)}else a={pending:new Set(t),done:new Set},(e.outrogroups??=new Set).add(a)}function Ur(e,t,n=!0){var r;if(e.pending.size>0){r=new Set;for(let t of e.pending.values())for(let n of t)r.add(e.items.get(n).e)}for(var i=0;i{var e=n();return a(e)?e:e==null?[]:c(e)}),p,m=new Map,h=!0;function g(e){v.effect.f&16384||(v.pending.delete(e),v.fallback=d,qr(v,p,s,t,r),d!==null&&(p.length===0?d.f&33554432?(d.f^=ce,Yr(d,null,s)):Fn(d):Nn(d,()=>{d=null})))}function _(e){v.pending.delete(e)}var v={effect:Dn(()=>{p=G(f);var e=p.length;let a=!1;C&&je(s)===`[!`!=(e===0)&&(s=Ae(),T(s),De(!1),a=!0);for(var c=new Set,u=j,v=cn(),y=0;yo(s)):(d=z(()=>o(Wr??=P())),d.f|=ce)),e>c.size&&Le(``,``,``),C&&e>0&&T(Ae()),!h){if(m.set(u,c),v){for(let[e,t]of l)c.has(e)||u.skip_effect(t.e);u.oncommit(g),u.ondiscard(_)}else g(u)}a&&De(!0),G(f)}),flags:t,items:l,pending:m,outrogroups:null,fallback:d};h=!1,C&&(s=w)}function Kr(e){for(;e!==null&&!(e.f&32);)e=e.next;return e}function qr(e,t,n,r,i){var a=!!(r&8),o=t.length,s=e.items,l=Kr(e.effect.first),u,d=null,f,p=[],m=[],h,g,_,v;if(a)for(v=0;v0){var re=r&4&&o===0?n:null;if(a){for(v=0;v{if(f!==void 0)for(_ of f)_.nodes?.a?.apply()})}function Jr(e,t,n,r,i,a,o,s){var c=o&1?o&16?Vt(n):M(n,!1,!1):null,l=o&2?Vt(i):null;return{v:c,i:l,e:z(()=>(a(t,c??n,l??i,s),()=>{e.delete(r)}))}}function Yr(e,t,n){if(e.nodes)for(var r=e.nodes.start,i=e.nodes.end,a=t&&!(t.f&33554432)?t.nodes.start:n;r!==null;){var o=an(r);if(a.before(r),r===i)return;r=o}}function Xr(e,t,n){t===null?e.effect.first=n:t.next=n,n===null?e.effect.last=t:n.prev=t}function Zr(e,t){let n=null,r=C;var i;if(C){n=w;for(var a=rn(document.head);a!==null&&(a.nodeType!==8||a.data!==e);)a=an(a);if(a===null)De(!1);else{var o=an(a);a.remove(),T(o)}}C||(i=document.head.appendChild(P()));try{Dn(()=>{var e=z(()=>t(i));e.f|=ae,C||(e.nodes===null?e.nodes={start:i,end:i,a:null,t:null}:e.nodes.end=i)})}finally{r&&(De(!0),T(n))}}function Qr(e){var t,n,r=``;if(typeof e==`string`||typeof e==`number`)r+=e;else if(typeof e==`object`){if(Array.isArray(e)){var i=e.length;for(t=0;t=0;){var s=o+a;(o===0||ti.includes(r[o-1]))&&(s===r.length||ti.includes(r[s]))?r=(o===0?``:r.substring(0,o))+r.substring(s+1):o=s}}return r===``?null:r}function ri(e,t,n,r,i,a){var o=e[_e];if(C||o!==n||o===void 0){var s=ni(n,r,a);(!C||s!==e.getAttribute(`class`))&&(s==null?e.removeAttribute(`class`):t?e.className=s:e.setAttribute(`class`,s)),e[_e]=n}else if(a&&i!==a)for(var c in a){var l=!!a[c];(i==null||l!==!!i[c])&&e.classList.toggle(c,l)}return a}function ii(e,t){t?e.hasAttribute(`selected`)||e.setAttribute(`selected`,``):e.removeAttribute(`selected`)}function ai(e,t){var n=e.__defaultValue,r=e.multiple,i=r?n??[]:null;if(!r||a(i)){var o=e.selectedIndex,s=t&&r?new Set(e.selectedOptions):null;for(var c of e.options){var l=li(c);ii(c,r?i.includes(l):Zt(l,n))}if(t){if(s!==null)for(c of e.options){var u=s.has(c);c.selected!==u&&(c.selected=u)}else e.selectedIndex!==o&&(e.selectedIndex=o)}}}function oi(e,t,n=!1){if(e.multiple){if(t==null)return;if(!a(t))return Te();for(var r of e.options)r.selected=t.includes(li(r));return}for(r of e.options)if(Zt(li(r),t)){r.selected=!0;return}(!n||t!==void 0)&&(e.selectedIndex=-1)}function si(e){var t=new MutationObserver(t=>{t.every(ui)||(`__defaultValue`in e&&ai(e,!1),`__value`in e&&oi(e,e.__value))});t.observe(e,{childList:!0,subtree:!0,attributes:!0,attributeFilter:[`value`]}),_n(()=>{t.disconnect()})}function ci(e,t,n=t){var r=new WeakSet,i=!0;ot(e,`change`,t=>{var i=t?`[selected]`:`:checked`,a;if(e.multiple)a=[].map.call(e.querySelectorAll(i),li);else{var o=e.querySelector(i)??e.querySelector(`option:not([disabled])`);a=o&&li(o)}n(a),e.__value=a,j!==null&&r.add(j)}),Sn(()=>{var a=t();if(e===document.activeElement){var o=j;if(r.has(o))return}if(oi(e,a,i),i&&a===void 0){var s=e.querySelector(`:checked`);s!==null&&(a=li(s),n(a))}e.__value=a,i=!1})}function li(e){return`__value`in e?e.__value:e.value}function ui(e){if(e.target.closest(`selectedcontent`)!==null)return!0;if(e.type===`childList`){var t=[...e.addedNodes,...e.removedNodes];return t.length>0&&t.every(e=>e.nodeName===`SELECTEDCONTENT`)}return!1}var di=Symbol(`is custom element`),fi=Symbol(`is html`),pi=Se?`link`:`LINK`;function mi(e){if(C){var t=!1,n=()=>{if(!t){if(t=!0,e.hasAttribute(`value`)){var n=e.value;Q(e,`value`,null),e.value=n}if(e.hasAttribute(`checked`)){var r=e.checked;Q(e,`checked`,null),e.checked=r}}};e[be]=n,Qe(n),it()}}function hi(e,t){var n=gi(e);n.checked!==(n.checked=t??void 0)&&(e.checked=t)}function Q(e,t,n,r){var i=gi(e);C&&(i[t]=e.getAttribute(t),t===`src`||t===`srcset`||t===`href`&&e.nodeName===pi)||i[t]!==(i[t]=n)&&(t===`loading`&&(e[he]=n),n==null?e.removeAttribute(t):typeof n!=`string`&&vi(e).has(t)?e[t]=n:e.setAttribute(t,n))}function gi(e){return e[ge]??={[di]:e.nodeName.includes(`-`),[fi]:e.namespaceURI===i}}var _i=new Map;function vi(e){var t=e.getAttribute(`is`)||e.nodeName,n=_i.get(t);if(n)return n;_i.set(t,n=new Set);for(var r,i=e,a=Element.prototype;a!==i;){for(var o in r=d(i),r)r[o].set&&o!==`innerHTML`&&o!==`textContent`&&o!==`innerText`&&n.add(o);i=m(i)}return n}function yi(e,t,n=t){var r=new WeakSet;ot(e,`input`,async i=>{var a=i?e.defaultValue:e.value;if(a=bi(e)?xi(a):a,n(a),j!==null&&r.add(j),await sr(),a!==(a=t())){var o=e.selectionStart,s=e.selectionEnd,c=e.value.length;if(e.value=a??``,s!==null){var l=e.value.length;o===s&&s===c&&l>c?(e.selectionStart=l,e.selectionEnd=l):(e.selectionStart=o,e.selectionEnd=Math.min(s,l))}}}),(C&&e.defaultValue!==e.value||K(t)==null&&e.value)&&(n(bi(e)?xi(e.value):e.value),j!==null&&r.add(j)),En(()=>{var n=t();if(e===document.activeElement){var i=j;if(r.has(i))return}bi(e)&&n===xi(e.value)||(e.type!==`date`||n||e.value)&&n!==e.value&&(e.value=n??``)})}function bi(e){var t=e.type;return t===`number`||t===`range`}function xi(e){return e===``?null:+e}function Si(e=!1){let t=D,n=t.l.u;if(!n)return;let r=()=>q(t.s);if(e){let e=0,n={},i=dt(()=>{let r=!1,i=t.s;for(let e in i)i[e]!==n[e]&&(n[e]=i[e],r=!0);return r&&e++,e});r=()=>G(i)}n.b.length&&bn(()=>{Ci(t,r),v(n.b)}),vn(()=>{let e=K(()=>n.m.map(_));return()=>{for(let t of e)typeof t==`function`&&t()}}),n.a.length&&vn(()=>{Ci(t,r),v(n.a)})}function Ci(e,t){if(e.l.s)for(let t of e.l.s)G(t);t()}function wi(e,t,n){if(e==null)return t(void 0),n&&n(void 0),g;let r=K(()=>e.subscribe(t,n));return r.unsubscribe?()=>r.unsubscribe():r}var Ti=[];function Ei(e,t=g){let n=null,r=new Set;function i(t){if(Ne(e,t)&&(e=t,n)){let t=!Ti.length;for(let t of r)t[1](),Ti.push(t,e);if(t){for(let e=0;e{r.delete(c),r.size===0&&n&&(n(),n=null)}}return{set:i,update:a,subscribe:o}}function Di(e){let t;return wi(e,e=>t=e)(),t}var Oi=!1,ki=Symbol(`unmounted`);function Ai(e,t,n){let r=n[t]??={store:null,source:M(void 0),unsubscribe:g};if(r.store!==e&&!(ki in n)){if(r.unsubscribe(),r.store=e??null,e==null)r.source.v=void 0,r.unsubscribe=g;else{var i=!0;r.unsubscribe=wi(e,e=>{i?r.source.v=e:N(r.source,e)}),i=!1}}return e&&ki in n?Di(e):G(r.source)}function ji(){let e={};function t(){_n(()=>{for(var t in e)e[t].unsubscribe();l(e,ki,{enumerable:!1,value:!0})})}return[e,t]}function Mi(e){var t=Oi;try{return Oi=!1,[e(),Oi]}finally{Oi=t}}function $(t,n,r,i){var a=!e||!!(r&2),o=!!(r&8),s=!!(r&16),c=i,l=!0,d=void 0,f=()=>s&&a?(d??=dt(i),G(d)):(l&&(l=!1,c=s?K(i):i),c);let p;if(o){var m=fe in t||me in t;p=u(t,n)?.set??(m&&n in t?e=>t[n]=e:void 0)}var h,g=!1;o?[h,g]=Mi(()=>t[n]):h=t[n],h===void 0&&i!==void 0&&(h=f(),p&&(a&&He(n),p(h)));var _=a?()=>{var e=t[n];return e===void 0?f():(l=!0,e)}:()=>{var e=t[n];return e!==void 0&&(c=void 0),e===void 0?c:e};if(a&&!(r&4))return _;if(p){var v=t.$$legacy;return(function(e,t){return arguments.length>0?((!a||!t||v||g)&&p(t?_():e),e):_()})}var y=!1,b=(r&1?dt:mt)(()=>(y=!1,_()));o&&G(b);var x=H;return(function(e,t){if(arguments.length>0){let n=t?G(b):a&&o?Yt(e):e;return N(b,n),y=!0,c!==void 0&&(c=n),e}return Bn&&y||x.f&16384?b.v:G(b)})}function Ni(e){let t=Ei({paired:!!e,controller:!1,busy:!1,error:``}),n=e,r,i=``,a={paired:!!e,controller:!1,busy:!1,error:``},o=!1;function s(e){a={...a,...e},t.set(a)}async function c(e=!1){if(!o&&n){o=!0;try{let t=await fetch(`/api/state`,{cache:`no-store`,headers:{"X-Setup-Session-Key":n}});if(!t.ok)throw Error(`The local setup session is unavailable.`);s({view:await t.json(),...e?{}:{error:``}})}catch{s({view:void 0,error:`Connection lost. The CLI may have stopped. Check the terminal before trying again.`})}finally{o=!1}}}async function l(){if(n)try{let e=await fetch(`/api/bootstrap`,{cache:`no-store`,headers:{"X-Setup-Session-Key":n}});if(!e.ok)throw Error(`Could not join this local session.`);let t=await e.json();r=t.capability,i=t.takeoverTicket,s({controller:t.controller,error:``}),await c()}catch{n=void 0,r=void 0,i=``,s({view:void 0,paired:!1,controller:!1,error:`Could not connect to the local setup session. Check the terminal and pair again.`})}}async function u(e){if(!(a.busy||a.paired)){s({busy:!0,error:``});try{let t=await fetch(`/api/pair`,{method:`POST`,cache:`no-store`,headers:{"Content-Type":`application/json`},body:JSON.stringify({code:e.trim().toLowerCase()})}),r=await t.json();if(!t.ok)throw Error(String(r.error??`Pairing was rejected.`));if(typeof r.sessionKey!=`string`||!/^[a-f0-9]{64}$/.test(r.sessionKey))throw Error(`Invalid local pairing response.`);n=r.sessionKey,s({paired:!0,error:``}),await l()}catch(e){s({error:e instanceof Error?e.message:`Could not pair this browser.`})}finally{s({busy:!1})}}}async function d(e,t,i=!0){let a=await fetch(e,{method:`POST`,cache:`no-store`,headers:{"Content-Type":`application/json`,"X-Setup-Session-Key":n,...i&&r?{"X-Setup-Capability":r}:{}},body:JSON.stringify(t)}),o=await a.json();if(!a.ok)throw Error(String(o.error??`The request was rejected.`));return o}async function f(e,t){if(!a.busy&&a.controller&&a.view?.promptRevision===e){s({busy:!0,error:``});try{await d(`/api/answer`,{revision:e,value:t}),await c()}catch(e){let t=e instanceof Error?e.message:`Could not submit this answer.`;s({error:t}),/read-only|Control moved/.test(t)?await l():await c(!0)}finally{s({busy:!1})}}}async function p(){if(a.controller&&!a.busy){s({busy:!0,error:``});try{await d(`/api/cancel`,{}),await c()}catch(e){let t=e instanceof Error?e.message:`Cancellation failed.`;s({error:t}),/read-only|Control moved/.test(t)&&await l()}finally{s({busy:!1})}}}async function m(){try{let e=await d(`/api/takeover`,{ticket:i},!1);r=String(e.capability),s({controller:!0,error:``}),await c()}catch(e){s({error:e instanceof Error?e.message:`Takeover failed.`}),await l()}}async function h(){try{await d(`/api/close`,{})}catch{}}return{subscribe:t.subscribe,pair:u,connect:l,refresh:c,submit:f,cancel:p,takeOver:m,close:h}}var Pi=J(`
                                                                    • `),Fi=J(``);function Ii(e,t){O(t,!1);let n=$(t,`journey`,8),r=[`Repository`,`Setup choices`,`Setup PAT`,`Plan`,`Bot PAT & credentials`,`Apply`];Si();var i=Fi(),a=L(F(i),4);Gr(a,5,()=>r,Vr,(e,t,r)=>{var i=Pi();let a;var o=F(i),s=I(o,!0),c=I(L(o,2),!0);E(i),R(e=>{Q(i,`aria-current`,(q(n()),K(()=>n()?.position===r+1?`step`:void 0))),a=ri(i,1,``,null,a,{current:n()?.position===r+1,completed:(n()?.position??0)>r+1}),X(s,e),X(c,G(t))},[()=>(q(n()),K(()=>(n()?.position??0)>r+1?`✓`:String(r+1).padStart(2,`0`)))]),Y(e,i)}),E(a),ke(2),E(i),Y(e,i),k()}var Li=J(`
                                                                      `);function Ri(e,t){O(t,!1);let n=M(`system`);Cn(()=>G(n),()=>{document.documentElement.dataset.theme=G(n)}),wn();var r=Li(),i=F(r);let a;var o=L(i,2);let s;var c=L(o,2);let l;E(r),R(()=>{Q(i,`aria-pressed`,G(n)===`system`),a=ri(i,1,``,null,a,{active:G(n)===`system`}),Q(o,`aria-pressed`,G(n)===`light`),s=ri(o,1,``,null,s,{active:G(n)===`light`}),Q(c,`aria-pressed`,G(n)===`dark`),l=ri(c,1,``,null,l,{active:G(n)===`dark`})}),gr(`click`,i,()=>N(n,`system`)),gr(`click`,o,()=>N(n,`light`)),gr(`click`,c,()=>N(n,`dark`)),Y(e,r),k()}_r([`click`]);var zi=J(`
                                                                      LOCAL SESSION
                                                                      `);function Bi(e,t){let n=$(t,`repository`,8);var r=zi(),i=F(r),a=I(L(F(i),2),!0);E(i);var o=L(i,2);Ri(L(F(o)),{}),E(o),E(r),R(()=>X(a,n()??`Connecting…`)),Y(e,r)}var Vi=J(`

                                                                      `,1);function Hi(e,t){O(t,!1);let n=M(),r=$(t,`view`,8);Cn(()=>q(r()),()=>{N(n,r()?.outcome===`complete`?`Setup complete.`:r()?.outcome===`dry-run`?`Preview complete.`:r()?.outcome===`cancelled`?`Setup cancelled.`:r()?.outcome?`Setup needs attention.`:r()?.prompt?.title??`Preparing your setup…`)}),wn(),Si();var i=Vi(),a=on(i),o=L(F(a)),s=I(L(o));E(a);var c=L(a,2),l=I(c,!0),u=I(L(c,2),!0);R((e,t)=>{X(o,` ${e??``} `),X(s,`${t??``} / 06`),X(l,G(n)),X(u,(q(r()),K(()=>r()?.outcome?`The terminal contains the detailed result. Review the next steps below before closing this session.`:`One decision at a time. Your choices shape the permissions, plan and credentials needed for this repository.`)))},[()=>(q(r()),K(()=>r()?.journey?.current?.toUpperCase()??`GETTING READY`)),()=>(q(r()),K(()=>String(r()?.journey?.position??1).padStart(2,`0`)))]),Y(e,i),k()}function Ui(e){try{let t=new URL(e??``);return t.protocol===`https:`&&t.hostname===`github.com`&&(t.pathname===`/settings/personal-access-tokens`||t.pathname.startsWith(`/settings/personal-access-tokens/`))?t.toString():void 0}catch{return}}var Wi=J(``),Gi=J(``);function Ki(e,t){let n=$(t,`label`,8),r=$(t,`variant`,8,`primary`),i=$(t,`disabled`,8,!1),a=$(t,`arrow`,8,!1),o=$(t,`onClick`,8);var s=Gi(),c=F(s,!0),l=L(c),u=e=>{Y(e,Wi())};Z(l,e=>{a()&&e(u)}),E(s),R(()=>{ri(s,1,ei(r())),s.disabled=i(),X(c,n())}),gr(`click`,s,function(...e){o()?.apply(this,e)}),Y(e,s)}_r([`click`]);var qi=J(`Open GitHub link ↗`),Ji=J(`

                                                                      `);function Yi(e,t){O(t,!1);let n=M(),r=$(t,`tone`,8,`info`),i=$(t,`title`,8),a=$(t,`message`,8),o=$(t,`link`,8,void 0),s=$(t,`actionLabel`,8,void 0),c=$(t,`onAction`,8,void 0);Cn(()=>q(o()),()=>{N(n,Ui(o()))}),wn(),Si();var l=Ji(),u=F(l),d=I(u,!0),f=L(u),p=I(f,!0),m=L(f,2),h=e=>{var t=qi();R(()=>Q(t,`href`,G(n))),Y(e,t)};Z(m,e=>{G(n)&&e(h)});var g=L(m,2),_=e=>{Ki(e,{get label(){return s()},variant:`secondary`,get onClick(){return c()}})};Z(g,e=>{s()&&c()&&e(_)}),E(l),R(()=>{ri(l,1,`banner ${r()??``}`),Q(l,`role`,r()===`error`?`alert`:`status`),X(d,i()),X(p,a())}),Y(e,l),k()}function Xi(e){let t=String(e.question.defaultValue),n=t.split(`,`).map(e=>e.trim()).filter(Boolean);return{value:t,selected:e.question.kind===`multi-select`?(e.question.choices??[]).filter(e=>e!==`All`&&n.includes(e.split(` — `)[0])):e.question.kind===`scope-overrides`?n:[]}}function Zi(e,t){return t===`All`?e.includes(`All`)?[]:[`All`]:e.includes(t)?e.filter(e=>e!==t):[...e.filter(e=>e!==`All`),t]}function Qi(e,t,n){return e.question.kind===`scope-overrides`||e.question.kind===`multi-select`?n.length?n.join(`,`):`none`:t}var $i=J(`PERMISSION PREVIEW`),ea=J(`
                                                                      `),ta=J(``),na=J(``),ra=J(``),ia=J(`
                                                                      `),aa=J(``),oa=J(`

                                                                      `,1);function sa(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`controller`,8),i=$(t,`busy`,8),a=$(t,`onSubmit`,8),o=Xi(n()),s=M(o.value),c=M(o.selected);Si();var l=oa(),u=on(l),d=F(u),f=I(d,!0),p=L(d),m=e=>{Y(e,$i())};Z(p,e=>{q(n()),K(()=>n().phase===`permission-intent`)&&e(m)}),E(u);var h=L(u,2),g=e=>{var t=ea(),i=F(t);let a;var o=L(i);let c;E(t),R(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),Q(i,`aria-pressed`,G(s)===`yes`||G(s)===`true`),i.disabled=!r(),a=ri(i,1,``,null,a,{selected:G(s)===`yes`||G(s)===`true`}),Q(o,`aria-pressed`,G(s)===`no`||G(s)===`false`),o.disabled=!r(),c=ri(o,1,``,null,c,{selected:G(s)===`no`||G(s)===`false`})}),gr(`click`,i,()=>N(s,`yes`)),gr(`click`,o,()=>N(s,`no`)),Y(e,t)},_=e=>{var t=na();Gr(t,5,()=>(q(n()),K(()=>n().question.choices??[])),Vr,(e,t)=>{var n=ta(),r=I(n,!0),i={};R(()=>{X(r,G(t)),i!==(i=G(t))&&(n.value=(n.__value=i)??``)}),Y(e,n)}),E(t),si(t),R(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),t.disabled=!r()}),ci(t,()=>G(s),e=>N(s,e)),Y(e,t)},v=e=>{var t=ia();Gr(t,5,()=>(q(n()),K(()=>n().question.kind===`multi-select`?n().question.choices??[]:n().question.allowedNames??[])),Vr,(e,t)=>{var n=ra(),i=F(n);mi(i);var a=I(L(i),!0);E(n),R(e=>{hi(i,e),i.disabled=!r(),X(a,G(t))},[()=>(G(c),G(t),K(()=>G(c).includes(G(t))))]),gr(`change`,i,()=>N(c,Zi(G(c),G(t)))),Y(e,n)}),E(t),R(()=>Q(t,`aria-label`,(q(n()),K(()=>n().question.label)))),Y(e,t)},y=e=>{var t=aa();mi(t),R(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),Q(t,`type`,(q(n()),K(()=>n().question.kind===`number`?`number`:`text`))),Q(t,`min`,(q(n()),K(()=>n().question.kind===`number`?0:void 0))),t.disabled=!r()}),yi(t,()=>G(s),e=>N(s,e)),Y(e,t)};Z(h,e=>{q(n()),K(()=>n().question.kind===`boolean`)?e(g):(q(n()),K(()=>n().question.kind===`choice`)?e(_,1):(q(n()),K(()=>n().question.kind===`multi-select`||n().question.kind===`scope-overrides`)?e(v,2):e(y,-1)))});var b=L(h,2),x=I(b),S=L(b,2);{let e=mt(()=>!r()||i());Ki(S,{label:`Continue`,arrow:!0,onClick:()=>a()(Qi(n(),G(s),G(c))),get disabled(){return G(e)}})}R((e,t)=>{X(f,e),X(x,`Suggested answer: ${t??``} · You can review choices again before creating your setup PAT.`)},[()=>(q(n()),K(()=>n().question.label.replace(` (Space toggles, Enter confirms)`,``))),()=>(q(n()),K(()=>String(n().question.defaultValue)||`none`))]),Y(e,l),k()}_r([`click`,`change`]);var ca=J(``),la=J(`
                                                                      `);function ua(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`controller`,8),i=$(t,`busy`,8),a=$(t,`onSubmit`,8);Si();var o=la();Gr(o,5,()=>(q(n()),K(()=>n().choices)),Vr,(e,t)=>{var n=ca(),o=I(F(n),!0);ke(),E(n),R(()=>{n.disabled=!r()||i(),X(o,G(t))}),gr(`click`,n,()=>a()(G(t))),Y(e,n)}),E(o),Y(e,o),k()}_r([`click`]);var da=J(`Open the official GitHub PAT form

                                                                      Check the signed-in account, select Only select repositories, then select this repository. GitHub handles 2FA and creates the PAT.

                                                                      `,1),fa=J(`

                                                                      Sent only to this local process. It will not be shown again or saved in browser storage.

                                                                      `),pa=J(` `,1);function ma(e,t){O(t,!1);let n=M(),r=$(t,`prompt`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8),s=M(``);function c(){let e=G(s);r().kind===`secret`&&N(s,``),o()(e)}Cn(()=>q(r()),()=>{N(n,Ui(r().link))}),wn(),Si();var l=pa(),u=on(l),d=e=>{var t=da(),r=on(t);ke(),R(()=>Q(r,`href`,G(n))),Y(e,t)};Z(u,e=>{G(n)&&e(d)});var f=L(u,2),p=I(f,!0),m=L(f,2);mi(m);var h=L(m,2),g=e=>{Y(e,fa())};Z(h,e=>{q(r()),K(()=>r().kind===`secret`)&&e(g)});var _=L(h,2);{let e=mt(()=>(q(i()),q(a()),q(r()),G(s),K(()=>!i()||a()||!r().optional&&!G(s).trim())));Ki(_,{label:`Continue`,arrow:!0,onClick:c,get disabled(){return G(e)}})}R(()=>{X(p,(q(r()),K(()=>r().kind===`secret`?`Paste the value here`:`Your answer`))),Q(m,`type`,(q(r()),K(()=>r().kind===`secret`?`password`:`text`))),m.disabled=!i(),Q(m,`placeholder`,(q(r()),K(()=>r().kind===`secret`?`Hidden after submission`:`Type your answer`)))}),yi(m,()=>G(s),e=>N(s,e)),Y(e,l),k()}var ha=J(`
                                                                    • `),ga=J(`

                                                                        `),_a=J(`

                                                                        Before you continue

                                                                          `),va=J(`

                                                                          Review exactly what this run may change. The bot PAT and any additional credentials are collected next.

                                                                          `,1);function ya(e,t){O(t,!1);let n=M(),r=$(t,`prompt`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8);Cn(()=>q(r()),()=>{N(n,[{title:`Files`,items:r().plan.files},{title:`Workflows`,items:r().plan.workflows},{title:`Variables`,items:r().plan.variables},{title:`Secret names`,items:r().plan.secrets}])}),wn(),Si();var s=va(),c=L(on(s),2);Gr(c,5,()=>G(n),Vr,(e,t)=>{var n=ga(),r=F(n),i=F(r),a=I(L(i),!0);E(r);var o=L(r);Gr(o,5,()=>(G(t),K(()=>G(t).items)),Vr,(e,t)=>{var n=ha(),r=I(n,!0);R(()=>X(r,G(t))),Y(e,n)}),E(o),E(n),R(()=>{X(i,`${G(t),K(()=>G(t).title)??``} `),X(a,(G(t),K(()=>G(t).items.length)))}),Y(e,n)}),E(c);var l=L(c,2),u=e=>{var t=_a(),n=L(F(t));Gr(n,5,()=>(q(r()),K(()=>r().plan.warnings)),Vr,(e,t)=>{var n=ha(),r=I(n,!0);R(()=>X(r,G(t))),Y(e,n)}),E(n),E(t),Y(e,t)};Z(l,e=>{q(r()),K(()=>r().plan.warnings.length)&&e(u)});var d=L(l,2),f=F(d);{let e=mt(()=>!i()||a());Ki(f,{label:`Stop here`,variant:`secondary`,onClick:()=>o()(`decline`),get disabled(){return G(e)}})}var p=L(f);{let e=mt(()=>!i()||a());Ki(p,{label:`Approve this plan`,arrow:!0,onClick:()=>o()(`approve`),get disabled(){return G(e)}})}E(d),Y(e,s),k()}var ba=J(`

                                                                          `),xa=J(`
                                                                          CURRENT DECISION
                                                                          `);function Sa(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`revision`,8),i=$(t,`promptRevision`,8),a=$(t,`controller`,8),o=$(t,`busy`,8),s=$(t,`onSubmit`,8);Si();var c=xa(),l=F(c),u=I(L(F(l)));E(l);var d=L(l,2),f=e=>{var t=ba(),r=I(t,!0);R(()=>X(r,(q(n()),K(()=>n().description)))),Y(e,t)};Z(d,e=>{q(n()),K(()=>`description`in n()&&n().description)&&e(f)}),Br(L(d,2),i,e=>{var t=Tr(),r=on(t),i=e=>{sa(e,{get prompt(){return n()},get controller(){return a()},get busy(){return o()},get onSubmit(){return s()}})},c=e=>{ua(e,{get prompt(){return n()},get controller(){return a()},get busy(){return o()},get onSubmit(){return s()}})},l=e=>{ma(e,{get prompt(){return n()},get controller(){return a()},get busy(){return o()},get onSubmit(){return s()}})},u=e=>{ya(e,{get prompt(){return n()},get controller(){return a()},get busy(){return o()},get onSubmit(){return s()}})};Z(r,e=>{q(n()),K(()=>n().kind===`question`)?e(i):(q(n()),K(()=>n().kind===`choice`||n().kind===`confirm`)?e(c,1):(q(n()),K(()=>n().kind===`text`||n().kind===`secret`)?e(l,2):(q(n()),K(()=>n().kind===`plan`)&&e(u,3))))}),Y(e,t)}),E(c),R(()=>X(u,`SESSION ${r()??``}`)),Y(e,c),k()}var Ca=J(` `),wa=J(`
                                                                        • `),Ta=J(`

                                                                            Conditional grants depend on the choices and GitHub facts. A final audit runs before setup changes.
                                                                            `),Ea=J(`

                                                                            Permissions follow your choices

                                                                            We’ll show the exact grants before you create each PAT. Nothing is created just by opening this page.

                                                                            `),Da=J(``);function Oa(e,t){O(t,!1);let n=$(t,`view`,8);Si();var r=Da(),i=F(r),a=I(L(F(i),3),!0);E(i);var o=L(i,2),s=e=>{var t=Ta(),r=L(F(t)),i=I(r),a=L(r),o=I(a,!0),s=L(a,2);Gr(s,5,()=>(q(n()),K(()=>n().permissions.report?.checks??n().permissions.requirements??[])),Vr,(e,t)=>{var n=wa(),r=F(n),i=F(r),a=L(i),o=I(a),s=I(L(a),!0);E(r);var c=L(r),l=F(c,!0),u=L(l),d=e=>{var n=Ca(),r=I(n,!0);R(()=>X(r,(G(t),K(()=>G(t).status)))),Y(e,n)};Z(u,e=>{`status`in G(t)&&e(d)}),E(c),E(n),R(()=>{X(i,`${G(t),K(()=>G(t).permission)??``} `),X(o,`${G(t),K(()=>G(t).scope)??``} · ${G(t),K(()=>G(t).applicability)??``}`),X(s,(G(t),K(()=>G(t).reason))),X(l,(G(t),K(()=>G(t).level)))}),Y(e,n)}),E(s),ke(2),E(t),R(()=>{X(i,`${q(n()),K(()=>n().permissions.role===`setup`?`Setup PAT`:`Bot PAT`)??``} access`),X(o,(q(n()),K(()=>n().permissions.report?`Read-only access check`:`Provisional least-privilege grants`)))}),Y(e,t)},c=e=>{Y(e,Ea())};Z(o,e=>{q(n()),K(()=>n().permissions)?e(s):e(c,-1)}),E(r),R(()=>X(a,(q(n()),K(()=>n().repository)))),Y(e,r),k()}var ka=J(`

                                                                            `);function Aa(e,t){O(t,!1);let n=M(),r=M(),i=$(t,`outcome`,8),a=$(t,`controller`,8),o=$(t,`onClose`,8);Cn(()=>q(i()),()=>{N(n,i()===`complete`?`Your configuration was applied`:i()===`dry-run`?`No changes were made`:i()===`cancelled`||i()===`blocked`?`No setup changes started`:`Check partial changes before retrying`)}),Cn(()=>q(i()),()=>{N(r,i()===`complete`?`The temporary setup PAT is not revoked automatically. Delete it in GitHub after confirming your setup. Keep the bot PAT until you rotate the installed Secret.`:i()===`partial`?`A bot Secret write or another change may have succeeded. Inspect GitHub and run copilot doctor before replacing or deleting its PAT.`:`Copilot did not begin applying setup changes. PATs you created in GitHub still exist until you delete them there.`)}),wn();var s=ka(),c=F(s),l=I(c,!0),u=L(c),d=I(u,!0),f=L(u),p=I(f,!0),m=L(f,2),h=e=>{Ki(e,{label:`Close local session`,get onClick(){return o()}})};Z(m,e=>{a()&&e(h)}),E(s),R(()=>{X(l,i()===`complete`?`✓`:`!`),X(d,G(n)),X(p,G(r))}),Y(e,s),k()}var ja=J(`

                                                                            Working on the next step

                                                                            The local process is checking your answers and preparing the next decision. Keep this page open.

                                                                            `);function Ma(e){Y(e,ja())}var Na=J(`
                                                                            PRIVATE LOCAL SESSION

                                                                            Pair this browser

                                                                            Find the 16-character pairing code in the terminal that started copilot setup --web. Enter it here to view or control this setup. The code is never placed in the browser URL or stored after this page closes.

                                                                            Keep the code private. After refreshing this page, enter it again to reconnect.

                                                                            `);function Pa(e,t){O(t,!1);let n=$(t,`busy`,8),r=$(t,`onPair`,8),i=M(``);function a(){let e=G(i);N(i,``),r()(e)}Si();var o=Na(),s=L(F(o),6),c=L(F(s),2);mi(c);var l=L(c,4);{let e=mt(()=>(q(n()),G(i),K(()=>n()||G(i).trim().length!==16)));Ki(l,{label:`Connect to local setup`,arrow:!0,onClick:a,get disabled(){return G(e)}})}E(s),E(o),R(()=>c.disabled=n()),hr(`submit`,s,e=>{e.preventDefault(),a()}),yi(c,()=>G(i),e=>N(i,e)),Y(e,o),k()}var Fa=J(``),Ia=J(`
                                                                            `),La=J(``),Ra=J(`
                                                                            `,1),za=J(`
                                                                            LOCALHOST ONLY NO CLOUD SETUP ACCOUNT GITHUB OWNS PAT ISSUANCE
                                                                            `);function Ba(e,t){O(t,!1);let n=()=>Ai(a,`$session`,r),[r,i]=ji(),a=Ni();Lr(()=>{let e=window.setInterval(()=>{n().paired&&!n().view?.outcome&&a.refresh()},900);return()=>window.clearInterval(e)});async function o(){window.confirm(`Cancel this local setup session? PATs already created in GitHub will still exist.`)&&await a.cancel()}async function s(e){let t=n().view?.promptRevision;t!==void 0&&await a.submit(t,e)}Si();var c=za();Zr(`16t12jp`,e=>{Y(e,Fa())});var l=F(c);{let e=mt(()=>n().view?.journey);Ii(l,{get journey(){return G(e)}})}var u=L(l,2),d=F(u);{let e=mt(()=>n().view?.repository);Bi(d,{get repository(){return G(e)}})}var f=L(d,2),p=F(f),m=e=>{Hi(e,{get view(){return n().view}})};Z(p,e=>{n().paired&&e(m)});var h=L(p,2),g=e=>{var t=Ia(),r=L(F(t));E(t),R(()=>X(r,` Reviewing saved choices — pass ${n().view.journey.choiceReviewPass??``}. This is the same setup run, not a restart.`)),Y(e,t)};Z(h,e=>{n().view?.journey?.choiceReviewPass&&n().view.journey.choiceReviewPass>1&&!n().view.outcome&&e(g)});var _=L(h,2),v=e=>{Yi(e,{tone:`warning`,title:`Read-only tab`,message:`Another tab controls this session. You can review progress here or explicitly take over.`,actionLabel:`Take control in this tab`,get onAction(){return a.takeOver}})};Z(_,e=>{!n().controller&&n().view&&e(v)});var y=L(_,2),b=e=>{Yi(e,{tone:`error`,title:`Needs attention`,get message(){return n().error}})};Z(y,e=>{n().error&&e(b)});var x=L(y,2),S=e=>{{let t=mt(()=>n().view.message.tone===`success`?`Checked`:n().view.message.tone===`warning`?`Please note`:n().view.message.tone===`error`?`Needs attention`:`Progress update`);Yi(e,{get tone(){return n().view.message.tone},get title(){return G(t)},get message(){return n().view.message.text},get link(){return n().view.message.link}})}};Z(x,e=>{n().view?.message&&e(S)});var ee=L(x,2),te=e=>{Pa(e,{get busy(){return n().busy},get onPair(){return a.pair}})},ne=e=>{Aa(e,{get outcome(){return n().view.outcome},get controller(){return n().controller},get onClose(){return a.close}})},re=e=>{var t=Ra(),r=on(t),i=F(r);Sa(i,{get prompt(){return n().view.prompt},get revision(){return n().view.revision},get promptRevision(){return n().view.promptRevision},get controller(){return n().controller},get busy(){return n().busy},onSubmit:s}),Oa(L(i,2),{get view(){return n().view}}),E(r);var a=L(r,2),c=e=>{var t=La();R(()=>t.disabled=n().busy),gr(`click`,t,o),Y(e,t)};Z(a,e=>{n().controller&&n().view.journey?.current!==`Apply`&&e(c)}),Y(e,t)},ie=e=>{Ma(e,{})};Z(ee,e=>{n().paired?n().view?.outcome?e(ne,1):n().view?.prompt?e(re,2):e(ie,-1):e(te)}),ke(2),E(f),E(u),E(c),Y(e,c),k(),i()}_r([`click`]),Mr(Ba,{target:document.getElementById(`app`)}); \ No newline at end of file +\r\f\xA0\v`];function ni(e,t,n){var r=e==null?``:``+e;if(t&&(r=r?r+` `+t:t),n){for(var i of Object.keys(n))if(n[i])r=r?r+` `+i:i;else if(r.length)for(var a=i.length,o=0;(o=r.indexOf(i,o))>=0;){var s=o+a;(o===0||ti.includes(r[o-1]))&&(s===r.length||ti.includes(r[s]))?r=(o===0?``:r.substring(0,o))+r.substring(s+1):o=s}}return r===``?null:r}function ri(e,t,n,r,i,a){var o=e[_e];if(C||o!==n||o===void 0){var s=ni(n,r,a);(!C||s!==e.getAttribute(`class`))&&(s==null?e.removeAttribute(`class`):t?e.className=s:e.setAttribute(`class`,s)),e[_e]=n}else if(a&&i!==a)for(var c in a){var l=!!a[c];(i==null||l!==!!i[c])&&e.classList.toggle(c,l)}return a}function ii(e,t){t?e.hasAttribute(`selected`)||e.setAttribute(`selected`,``):e.removeAttribute(`selected`)}function ai(e,t){var n=e.__defaultValue,r=e.multiple,i=r?n??[]:null;if(!r||a(i)){var o=e.selectedIndex,s=t&&r?new Set(e.selectedOptions):null;for(var c of e.options){var l=li(c);ii(c,r?i.includes(l):Zt(l,n))}if(t){if(s!==null)for(c of e.options){var u=s.has(c);c.selected!==u&&(c.selected=u)}else e.selectedIndex!==o&&(e.selectedIndex=o)}}}function oi(e,t,n=!1){if(e.multiple){if(t==null)return;if(!a(t))return Te();for(var r of e.options)r.selected=t.includes(li(r));return}for(r of e.options)if(Zt(li(r),t)){r.selected=!0;return}(!n||t!==void 0)&&(e.selectedIndex=-1)}function si(e){var t=new MutationObserver(t=>{t.every(ui)||(`__defaultValue`in e&&ai(e,!1),`__value`in e&&oi(e,e.__value))});t.observe(e,{childList:!0,subtree:!0,attributes:!0,attributeFilter:[`value`]}),_n(()=>{t.disconnect()})}function ci(e,t,n=t){var r=new WeakSet,i=!0;ot(e,`change`,t=>{var i=t?`[selected]`:`:checked`,a;if(e.multiple)a=[].map.call(e.querySelectorAll(i),li);else{var o=e.querySelector(i)??e.querySelector(`option:not([disabled])`);a=o&&li(o)}n(a),e.__value=a,j!==null&&r.add(j)}),Sn(()=>{var a=t();if(e===document.activeElement){var o=j;if(r.has(o))return}if(oi(e,a,i),i&&a===void 0){var s=e.querySelector(`:checked`);s!==null&&(a=li(s),n(a))}e.__value=a,i=!1})}function li(e){return`__value`in e?e.__value:e.value}function ui(e){if(e.target.closest(`selectedcontent`)!==null)return!0;if(e.type===`childList`){var t=[...e.addedNodes,...e.removedNodes];return t.length>0&&t.every(e=>e.nodeName===`SELECTEDCONTENT`)}return!1}var di=Symbol(`is custom element`),fi=Symbol(`is html`),pi=Se?`link`:`LINK`;function mi(e){if(C){var t=!1,n=()=>{if(!t){if(t=!0,e.hasAttribute(`value`)){var n=e.value;Q(e,`value`,null),e.value=n}if(e.hasAttribute(`checked`)){var r=e.checked;Q(e,`checked`,null),e.checked=r}}};e[be]=n,Qe(n),it()}}function hi(e,t){var n=gi(e);n.checked!==(n.checked=t??void 0)&&(e.checked=t)}function Q(e,t,n,r){var i=gi(e);C&&(i[t]=e.getAttribute(t),t===`src`||t===`srcset`||t===`href`&&e.nodeName===pi)||i[t]!==(i[t]=n)&&(t===`loading`&&(e[he]=n),n==null?e.removeAttribute(t):typeof n!=`string`&&vi(e).has(t)?e[t]=n:e.setAttribute(t,n))}function gi(e){return e[ge]??={[di]:e.nodeName.includes(`-`),[fi]:e.namespaceURI===i}}var _i=new Map;function vi(e){var t=e.getAttribute(`is`)||e.nodeName,n=_i.get(t);if(n)return n;_i.set(t,n=new Set);for(var r,i=e,a=Element.prototype;a!==i;){for(var o in r=d(i),r)r[o].set&&o!==`innerHTML`&&o!==`textContent`&&o!==`innerText`&&n.add(o);i=m(i)}return n}function yi(e,t,n=t){var r=new WeakSet;ot(e,`input`,async i=>{var a=i?e.defaultValue:e.value;if(a=bi(e)?xi(a):a,n(a),j!==null&&r.add(j),await sr(),a!==(a=t())){var o=e.selectionStart,s=e.selectionEnd,c=e.value.length;if(e.value=a??``,s!==null){var l=e.value.length;o===s&&s===c&&l>c?(e.selectionStart=l,e.selectionEnd=l):(e.selectionStart=o,e.selectionEnd=Math.min(s,l))}}}),(C&&e.defaultValue!==e.value||K(t)==null&&e.value)&&(n(bi(e)?xi(e.value):e.value),j!==null&&r.add(j)),En(()=>{var n=t();if(e===document.activeElement){var i=j;if(r.has(i))return}bi(e)&&n===xi(e.value)||(e.type!==`date`||n||e.value)&&n!==e.value&&(e.value=n??``)})}function bi(e){var t=e.type;return t===`number`||t===`range`}function xi(e){return e===``?null:+e}function Si(e=!1){let t=D,n=t.l.u;if(!n)return;let r=()=>q(t.s);if(e){let e=0,n={},i=dt(()=>{let r=!1,i=t.s;for(let e in i)i[e]!==n[e]&&(n[e]=i[e],r=!0);return r&&e++,e});r=()=>G(i)}n.b.length&&bn(()=>{Ci(t,r),v(n.b)}),vn(()=>{let e=K(()=>n.m.map(_));return()=>{for(let t of e)typeof t==`function`&&t()}}),n.a.length&&vn(()=>{Ci(t,r),v(n.a)})}function Ci(e,t){if(e.l.s)for(let t of e.l.s)G(t);t()}function wi(e,t,n){if(e==null)return t(void 0),n&&n(void 0),g;let r=K(()=>e.subscribe(t,n));return r.unsubscribe?()=>r.unsubscribe():r}var Ti=[];function Ei(e,t=g){let n=null,r=new Set;function i(t){if(Ne(e,t)&&(e=t,n)){let t=!Ti.length;for(let t of r)t[1](),Ti.push(t,e);if(t){for(let e=0;e{r.delete(c),r.size===0&&n&&(n(),n=null)}}return{set:i,update:a,subscribe:o}}function Di(e){let t;return wi(e,e=>t=e)(),t}var Oi=!1,ki=Symbol(`unmounted`);function Ai(e,t,n){let r=n[t]??={store:null,source:M(void 0),unsubscribe:g};if(r.store!==e&&!(ki in n)){if(r.unsubscribe(),r.store=e??null,e==null)r.source.v=void 0,r.unsubscribe=g;else{var i=!0;r.unsubscribe=wi(e,e=>{i?r.source.v=e:N(r.source,e)}),i=!1}}return e&&ki in n?Di(e):G(r.source)}function ji(){let e={};function t(){_n(()=>{for(var t in e)e[t].unsubscribe();l(e,ki,{enumerable:!1,value:!0})})}return[e,t]}function Mi(e){var t=Oi;try{return Oi=!1,[e(),Oi]}finally{Oi=t}}function $(t,n,r,i){var a=!e||!!(r&2),o=!!(r&8),s=!!(r&16),c=i,l=!0,d=void 0,f=()=>s&&a?(d??=dt(i),G(d)):(l&&(l=!1,c=s?K(i):i),c);let p;if(o){var m=fe in t||me in t;p=u(t,n)?.set??(m&&n in t?e=>t[n]=e:void 0)}var h,g=!1;o?[h,g]=Mi(()=>t[n]):h=t[n],h===void 0&&i!==void 0&&(h=f(),p&&(a&&He(n),p(h)));var _=a?()=>{var e=t[n];return e===void 0?f():(l=!0,e)}:()=>{var e=t[n];return e!==void 0&&(c=void 0),e===void 0?c:e};if(a&&!(r&4))return _;if(p){var v=t.$$legacy;return(function(e,t){return arguments.length>0?((!a||!t||v||g)&&p(t?_():e),e):_()})}var y=!1,b=(r&1?dt:mt)(()=>(y=!1,_()));o&&G(b);var x=H;return(function(e,t){if(arguments.length>0){let n=t?G(b):a&&o?Yt(e):e;return N(b,n),y=!0,c!==void 0&&(c=n),e}return Bn&&y||x.f&16384?b.v:G(b)})}function Ni(e){let t=Ei({paired:!!e,controller:!1,busy:!1,error:``}),n=e,r,i=``,a={paired:!!e,controller:!1,busy:!1,error:``},o=!1;function s(e){a={...a,...e},t.set(a)}async function c(e=!1){if(!o&&n){o=!0;try{let t=await fetch(`/api/state`,{cache:`no-store`,headers:{"X-Setup-Session-Key":n}});if(!t.ok)throw Error(`The local setup session is unavailable.`);s({view:await t.json(),...e?{}:{error:``}})}catch{s({view:void 0,error:`Connection lost. The CLI may have stopped. Check the terminal before trying again.`})}finally{o=!1}}}async function l(){if(n)try{let e=await fetch(`/api/bootstrap`,{cache:`no-store`,headers:{"X-Setup-Session-Key":n}});if(!e.ok)throw Error(`Could not join this local session.`);let t=await e.json();r=t.capability,i=t.takeoverTicket,s({controller:t.controller,error:``}),await c()}catch{n=void 0,r=void 0,i=``,s({view:void 0,paired:!1,controller:!1,error:`Could not connect to the local setup session. Check the terminal and pair again.`})}}async function u(e){if(!(a.busy||a.paired)){s({busy:!0,error:``});try{let t=await fetch(`/api/pair`,{method:`POST`,cache:`no-store`,headers:{"Content-Type":`application/json`},body:JSON.stringify({code:e.trim().toLowerCase()})}),r=await t.json();if(!t.ok)throw Error(String(r.error??`Pairing was rejected.`));if(typeof r.sessionKey!=`string`||!/^[a-f0-9]{64}$/.test(r.sessionKey))throw Error(`Invalid local pairing response.`);n=r.sessionKey,s({paired:!0,error:``}),await l()}catch(e){s({error:e instanceof Error?e.message:`Could not pair this browser.`})}finally{s({busy:!1})}}}async function d(e,t,i=!0){let a=await fetch(e,{method:`POST`,cache:`no-store`,headers:{"Content-Type":`application/json`,"X-Setup-Session-Key":n,...i&&r?{"X-Setup-Capability":r}:{}},body:JSON.stringify(t)}),o=await a.json();if(!a.ok)throw Error(String(o.error??`The request was rejected.`));return o}async function f(e,t){if(!a.busy&&a.controller&&a.view?.promptRevision===e){s({busy:!0,error:``});try{await d(`/api/answer`,{revision:e,value:t}),await c()}catch(e){let t=e instanceof Error?e.message:`Could not submit this answer.`;s({error:t}),/read-only|Control moved/.test(t)?await l():await c(!0)}finally{s({busy:!1})}}}async function p(){if(a.controller&&!a.busy){s({busy:!0,error:``});try{await d(`/api/cancel`,{}),await c()}catch(e){let t=e instanceof Error?e.message:`Cancellation failed.`;s({error:t}),/read-only|Control moved/.test(t)&&await l()}finally{s({busy:!1})}}}async function m(){try{let e=await d(`/api/takeover`,{ticket:i},!1);r=String(e.capability),s({controller:!0,error:``}),await c()}catch(e){s({error:e instanceof Error?e.message:`Takeover failed.`}),await l()}}async function h(){try{await d(`/api/close`,{})}catch{}}return{subscribe:t.subscribe,pair:u,connect:l,refresh:c,submit:f,cancel:p,takeOver:m,close:h}}var Pi=J(`
                                                                          • `),Fi=J(``);function Ii(e,t){O(t,!1);let n=$(t,`journey`,8),r=[`Repository`,`Setup choices`,`Setup PAT`,`Plan`,`Bot PAT & credentials`,`Apply`];Si();var i=Fi(),a=L(F(i),4);Gr(a,5,()=>r,Vr,(e,t,r)=>{var i=Pi();let a;var o=F(i),s=I(o,!0),c=I(L(o,2),!0);E(i),R(e=>{Q(i,`aria-current`,(q(n()),K(()=>n()?.position===r+1?`step`:void 0))),a=ri(i,1,``,null,a,{current:n()?.position===r+1,completed:(n()?.position??0)>r+1}),X(s,e),X(c,G(t))},[()=>(q(n()),K(()=>(n()?.position??0)>r+1?`✓`:String(r+1).padStart(2,`0`)))]),Y(e,i)}),E(a),ke(2),E(i),Y(e,i),k()}var Li=J(`
                                                                            `);function Ri(e,t){O(t,!1);let n=M(`system`);Cn(()=>G(n),()=>{document.documentElement.dataset.theme=G(n)}),wn();var r=Li(),i=F(r);let a;var o=L(i,2);let s;var c=L(o,2);let l;E(r),R(()=>{Q(i,`aria-pressed`,G(n)===`system`),a=ri(i,1,``,null,a,{active:G(n)===`system`}),Q(o,`aria-pressed`,G(n)===`light`),s=ri(o,1,``,null,s,{active:G(n)===`light`}),Q(c,`aria-pressed`,G(n)===`dark`),l=ri(c,1,``,null,l,{active:G(n)===`dark`})}),gr(`click`,i,()=>N(n,`system`)),gr(`click`,o,()=>N(n,`light`)),gr(`click`,c,()=>N(n,`dark`)),Y(e,r),k()}_r([`click`]);var zi=J(`
                                                                            LOCAL SESSION
                                                                            `);function Bi(e,t){let n=$(t,`repository`,8);var r=zi(),i=F(r),a=I(L(F(i),2),!0);E(i);var o=L(i,2);Ri(L(F(o)),{}),E(o),E(r),R(()=>X(a,n()??`Connecting…`)),Y(e,r)}var Vi=J(`

                                                                            `,1);function Hi(e,t){O(t,!1);let n=M(),r=$(t,`view`,8);Cn(()=>q(r()),()=>{N(n,r()?.outcome===`complete`?`Setup complete.`:r()?.outcome===`dry-run`?`Preview complete.`:r()?.outcome===`cancelled`?`Setup cancelled.`:r()?.outcome?`Setup needs attention.`:r()?.prompt?.title??`Preparing your setup…`)}),wn(),Si();var i=Vi(),a=on(i),o=L(F(a)),s=I(L(o));E(a);var c=L(a,2),l=I(c,!0),u=I(L(c,2),!0);R((e,t)=>{X(o,` ${e??``} `),X(s,`${t??``} / 06`),X(l,G(n)),X(u,(q(r()),K(()=>r()?.outcome?`The terminal contains the detailed result. Review the next steps below before closing this session.`:`One decision at a time. Your choices shape the permissions, plan and credentials needed for this repository.`)))},[()=>(q(r()),K(()=>r()?.journey?.current?.toUpperCase()??`GETTING READY`)),()=>(q(r()),K(()=>String(r()?.journey?.position??1).padStart(2,`0`)))]),Y(e,i),k()}function Ui(e){try{let t=new URL(e??``);return t.protocol===`https:`&&t.hostname===`github.com`&&(t.pathname===`/settings/personal-access-tokens`||t.pathname.startsWith(`/settings/personal-access-tokens/`))?t.toString():void 0}catch{return}}var Wi=J(``),Gi=J(``);function Ki(e,t){let n=$(t,`label`,8),r=$(t,`variant`,8,`primary`),i=$(t,`disabled`,8,!1),a=$(t,`arrow`,8,!1),o=$(t,`onClick`,8);var s=Gi(),c=F(s,!0),l=L(c),u=e=>{Y(e,Wi())};Z(l,e=>{a()&&e(u)}),E(s),R(()=>{ri(s,1,ei(r())),s.disabled=i(),X(c,n())}),gr(`click`,s,function(...e){o()?.apply(this,e)}),Y(e,s)}_r([`click`]);var qi=J(`Open GitHub link ↗`),Ji=J(`

                                                                            `);function Yi(e,t){O(t,!1);let n=M(),r=$(t,`tone`,8,`info`),i=$(t,`title`,8),a=$(t,`message`,8),o=$(t,`link`,8,void 0),s=$(t,`actionLabel`,8,void 0),c=$(t,`onAction`,8,void 0);Cn(()=>q(o()),()=>{N(n,Ui(o()))}),wn(),Si();var l=Ji(),u=F(l),d=I(u,!0),f=L(u),p=I(f,!0),m=L(f,2),h=e=>{var t=qi();R(()=>Q(t,`href`,G(n))),Y(e,t)};Z(m,e=>{G(n)&&e(h)});var g=L(m,2),_=e=>{Ki(e,{get label(){return s()},variant:`secondary`,get onClick(){return c()}})};Z(g,e=>{s()&&c()&&e(_)}),E(l),R(()=>{ri(l,1,`banner ${r()??``}`),Q(l,`role`,r()===`error`?`alert`:`status`),X(d,i()),X(p,a())}),Y(e,l),k()}function Xi(e){let t=String(e.question.defaultValue),n=t.split(`,`).map(e=>e.trim()).filter(Boolean);return{value:t,selected:e.question.kind===`multi-select`?n.includes(`All`)&&e.question.choices?.includes(`All`)?[`All`]:(e.question.choices??[]).filter(e=>e!==`All`&&n.includes(e.split(` — `)[0])):e.question.kind===`scope-overrides`?n:[]}}function Zi(e,t){return t===`All`?e.includes(`All`)?[]:[`All`]:e.includes(t)?e.filter(e=>e!==t):[...e.filter(e=>e!==`All`),t]}function Qi(e,t,n){return e.question.kind===`scope-overrides`||e.question.kind===`multi-select`?n.length?n.join(`,`):`none`:t}var $i=J(`PERMISSION PREVIEW`),ea=J(`
                                                                            `),ta=J(``),na=J(``),ra=J(``),ia=J(`
                                                                            `),aa=J(``),oa=J(`

                                                                            `,1);function sa(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`controller`,8),i=$(t,`busy`,8),a=$(t,`onSubmit`,8),o=Xi(n()),s=M(o.value),c=M(o.selected);Si();var l=oa(),u=on(l),d=F(u),f=I(d,!0),p=L(d),m=e=>{Y(e,$i())};Z(p,e=>{q(n()),K(()=>n().phase===`permission-intent`)&&e(m)}),E(u);var h=L(u,2),g=e=>{var t=ea(),i=F(t);let a;var o=L(i);let c;E(t),R(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),Q(i,`aria-pressed`,G(s)===`yes`||G(s)===`true`),i.disabled=!r(),a=ri(i,1,``,null,a,{selected:G(s)===`yes`||G(s)===`true`}),Q(o,`aria-pressed`,G(s)===`no`||G(s)===`false`),o.disabled=!r(),c=ri(o,1,``,null,c,{selected:G(s)===`no`||G(s)===`false`})}),gr(`click`,i,()=>N(s,`yes`)),gr(`click`,o,()=>N(s,`no`)),Y(e,t)},_=e=>{var t=na();Gr(t,5,()=>(q(n()),K(()=>n().question.choices??[])),Vr,(e,t)=>{var n=ta(),r=I(n,!0),i={};R(()=>{X(r,G(t)),i!==(i=G(t))&&(n.value=(n.__value=i)??``)}),Y(e,n)}),E(t),si(t),R(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),t.disabled=!r()}),ci(t,()=>G(s),e=>N(s,e)),Y(e,t)},v=e=>{var t=ia();Gr(t,5,()=>(q(n()),K(()=>n().question.kind===`multi-select`?n().question.choices??[]:n().question.allowedNames??[])),Vr,(e,t)=>{var n=ra(),i=F(n);mi(i);var a=I(L(i),!0);E(n),R(e=>{hi(i,e),i.disabled=!r(),X(a,G(t))},[()=>(G(c),G(t),K(()=>G(c).includes(G(t))))]),gr(`change`,i,()=>N(c,Zi(G(c),G(t)))),Y(e,n)}),E(t),R(()=>Q(t,`aria-label`,(q(n()),K(()=>n().question.label)))),Y(e,t)},y=e=>{var t=aa();mi(t),R(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),Q(t,`type`,(q(n()),K(()=>n().question.kind===`number`?`number`:`text`))),Q(t,`min`,(q(n()),K(()=>n().question.kind===`number`?0:void 0))),t.disabled=!r()}),yi(t,()=>G(s),e=>N(s,e)),Y(e,t)};Z(h,e=>{q(n()),K(()=>n().question.kind===`boolean`)?e(g):(q(n()),K(()=>n().question.kind===`choice`)?e(_,1):(q(n()),K(()=>n().question.kind===`multi-select`||n().question.kind===`scope-overrides`)?e(v,2):e(y,-1)))});var b=L(h,2),x=I(b),S=L(b,2);{let e=mt(()=>!r()||i());Ki(S,{label:`Continue`,arrow:!0,onClick:()=>a()(Qi(n(),G(s),G(c))),get disabled(){return G(e)}})}R((e,t)=>{X(f,e),X(x,`Suggested answer: ${t??``} · You can review choices again before creating your setup PAT.`)},[()=>(q(n()),K(()=>n().question.label.replace(` (Space toggles, Enter confirms)`,``))),()=>(q(n()),K(()=>String(n().question.defaultValue)||`none`))]),Y(e,l),k()}_r([`click`,`change`]);var ca=J(``),la=J(`
                                                                            `);function ua(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`controller`,8),i=$(t,`busy`,8),a=$(t,`onSubmit`,8);Si();var o=la();Gr(o,5,()=>(q(n()),K(()=>n().choices)),Vr,(e,t)=>{var n=ca(),o=I(F(n),!0);ke(),E(n),R(()=>{n.disabled=!r()||i(),X(o,G(t))}),gr(`click`,n,()=>a()(G(t))),Y(e,n)}),E(o),Y(e,o),k()}_r([`click`]);var da=J(`Open the official GitHub PAT form

                                                                            Check the signed-in account, select Only select repositories, then select this repository. GitHub handles 2FA and creates the PAT.

                                                                            `,1),fa=J(`

                                                                            Sent only to this local process. It will not be shown again or saved in browser storage.

                                                                            `),pa=J(` `,1);function ma(e,t){O(t,!1);let n=M(),r=$(t,`prompt`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8),s=M(``);function c(){let e=G(s);r().kind===`secret`&&N(s,``),o()(e)}Cn(()=>q(r()),()=>{N(n,Ui(r().link))}),wn(),Si();var l=pa(),u=on(l),d=e=>{var t=da(),r=on(t);ke(),R(()=>Q(r,`href`,G(n))),Y(e,t)};Z(u,e=>{G(n)&&e(d)});var f=L(u,2),p=I(f,!0),m=L(f,2);mi(m);var h=L(m,2),g=e=>{Y(e,fa())};Z(h,e=>{q(r()),K(()=>r().kind===`secret`)&&e(g)});var _=L(h,2);{let e=mt(()=>(q(i()),q(a()),q(r()),G(s),K(()=>!i()||a()||!r().optional&&!G(s).trim())));Ki(_,{label:`Continue`,arrow:!0,onClick:c,get disabled(){return G(e)}})}R(()=>{X(p,(q(r()),K(()=>r().kind===`secret`?`Paste the value here`:`Your answer`))),Q(m,`type`,(q(r()),K(()=>r().kind===`secret`?`password`:`text`))),m.disabled=!i(),Q(m,`placeholder`,(q(r()),K(()=>r().kind===`secret`?`Hidden after submission`:`Type your answer`)))}),yi(m,()=>G(s),e=>N(s,e)),Y(e,l),k()}var ha=J(`
                                                                          • `),ga=J(`

                                                                              `),_a=J(`

                                                                              Before you continue

                                                                                `),va=J(`

                                                                                Review exactly what this run may change. The bot PAT and any additional credentials are collected next.

                                                                                `,1);function ya(e,t){O(t,!1);let n=M(),r=$(t,`prompt`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8);Cn(()=>q(r()),()=>{N(n,[{title:`Files`,items:r().plan.files},{title:`Workflows`,items:r().plan.workflows},{title:`Variables`,items:r().plan.variables},{title:`Secret names`,items:r().plan.secrets}])}),wn(),Si();var s=va(),c=L(on(s),2);Gr(c,5,()=>G(n),Vr,(e,t)=>{var n=ga(),r=F(n),i=F(r),a=I(L(i),!0);E(r);var o=L(r);Gr(o,5,()=>(G(t),K(()=>G(t).items)),Vr,(e,t)=>{var n=ha(),r=I(n,!0);R(()=>X(r,G(t))),Y(e,n)}),E(o),E(n),R(()=>{X(i,`${G(t),K(()=>G(t).title)??``} `),X(a,(G(t),K(()=>G(t).items.length)))}),Y(e,n)}),E(c);var l=L(c,2),u=e=>{var t=_a(),n=L(F(t));Gr(n,5,()=>(q(r()),K(()=>r().plan.warnings)),Vr,(e,t)=>{var n=ha(),r=I(n,!0);R(()=>X(r,G(t))),Y(e,n)}),E(n),E(t),Y(e,t)};Z(l,e=>{q(r()),K(()=>r().plan.warnings.length)&&e(u)});var d=L(l,2),f=F(d);{let e=mt(()=>!i()||a());Ki(f,{label:`Stop here`,variant:`secondary`,onClick:()=>o()(`decline`),get disabled(){return G(e)}})}var p=L(f);{let e=mt(()=>!i()||a());Ki(p,{label:`Approve this plan`,arrow:!0,onClick:()=>o()(`approve`),get disabled(){return G(e)}})}E(d),Y(e,s),k()}var ba=J(`

                                                                                `),xa=J(`
                                                                                CURRENT DECISION
                                                                                `);function Sa(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`revision`,8),i=$(t,`promptRevision`,8),a=$(t,`controller`,8),o=$(t,`busy`,8),s=$(t,`onSubmit`,8);Si();var c=xa(),l=F(c),u=I(L(F(l)));E(l);var d=L(l,2),f=e=>{var t=ba(),r=I(t,!0);R(()=>X(r,(q(n()),K(()=>n().description)))),Y(e,t)};Z(d,e=>{q(n()),K(()=>`description`in n()&&n().description)&&e(f)}),Br(L(d,2),i,e=>{var t=Tr(),r=on(t),i=e=>{sa(e,{get prompt(){return n()},get controller(){return a()},get busy(){return o()},get onSubmit(){return s()}})},c=e=>{ua(e,{get prompt(){return n()},get controller(){return a()},get busy(){return o()},get onSubmit(){return s()}})},l=e=>{ma(e,{get prompt(){return n()},get controller(){return a()},get busy(){return o()},get onSubmit(){return s()}})},u=e=>{ya(e,{get prompt(){return n()},get controller(){return a()},get busy(){return o()},get onSubmit(){return s()}})};Z(r,e=>{q(n()),K(()=>n().kind===`question`)?e(i):(q(n()),K(()=>n().kind===`choice`||n().kind===`confirm`)?e(c,1):(q(n()),K(()=>n().kind===`text`||n().kind===`secret`)?e(l,2):(q(n()),K(()=>n().kind===`plan`)&&e(u,3))))}),Y(e,t)}),E(c),R(()=>X(u,`SESSION ${r()??``}`)),Y(e,c),k()}var Ca=J(` `),wa=J(`
                                                                              • `),Ta=J(`

                                                                                  Conditional grants depend on the choices and GitHub facts. A final audit runs before setup changes.
                                                                                  `),Ea=J(`

                                                                                  Permissions follow your choices

                                                                                  We’ll show the exact grants before you create each PAT. Nothing is created just by opening this page.

                                                                                  `),Da=J(``);function Oa(e,t){O(t,!1);let n=$(t,`view`,8);Si();var r=Da(),i=F(r),a=I(L(F(i),3),!0);E(i);var o=L(i,2),s=e=>{var t=Ta(),r=L(F(t)),i=I(r),a=L(r),o=I(a,!0),s=L(a,2);Gr(s,5,()=>(q(n()),K(()=>n().permissions.report?.checks??n().permissions.requirements??[])),Vr,(e,t)=>{var n=wa(),r=F(n),i=F(r),a=L(i),o=I(a),s=I(L(a),!0);E(r);var c=L(r),l=F(c,!0),u=L(l),d=e=>{var n=Ca(),r=I(n,!0);R(()=>X(r,(G(t),K(()=>G(t).status)))),Y(e,n)};Z(u,e=>{`status`in G(t)&&e(d)}),E(c),E(n),R(()=>{X(i,`${G(t),K(()=>G(t).permission)??``} `),X(o,`${G(t),K(()=>G(t).scope)??``} · ${G(t),K(()=>G(t).applicability)??``}`),X(s,(G(t),K(()=>G(t).reason))),X(l,(G(t),K(()=>G(t).level)))}),Y(e,n)}),E(s),ke(2),E(t),R(()=>{X(i,`${q(n()),K(()=>n().permissions.role===`setup`?`Setup PAT`:`Bot PAT`)??``} access`),X(o,(q(n()),K(()=>n().permissions.report?`Read-only access check`:`Provisional least-privilege grants`)))}),Y(e,t)},c=e=>{Y(e,Ea())};Z(o,e=>{q(n()),K(()=>n().permissions)?e(s):e(c,-1)}),E(r),R(()=>X(a,(q(n()),K(()=>n().repository)))),Y(e,r),k()}var ka=J(`

                                                                                  `);function Aa(e,t){O(t,!1);let n=M(),r=M(),i=$(t,`outcome`,8),a=$(t,`controller`,8),o=$(t,`onClose`,8);Cn(()=>q(i()),()=>{N(n,i()===`complete`?`Your configuration was applied`:i()===`dry-run`?`No changes were made`:i()===`cancelled`||i()===`blocked`?`No setup changes started`:`Check partial changes before retrying`)}),Cn(()=>q(i()),()=>{N(r,i()===`complete`?`The temporary setup PAT is not revoked automatically. Delete it in GitHub after confirming your setup. Keep the bot PAT until you rotate the installed Secret.`:i()===`partial`?`A bot Secret write or another change may have succeeded. Inspect GitHub and run copilot doctor before replacing or deleting its PAT.`:`Copilot did not begin applying setup changes. PATs you created in GitHub still exist until you delete them there.`)}),wn();var s=ka(),c=F(s),l=I(c,!0),u=L(c),d=I(u,!0),f=L(u),p=I(f,!0),m=L(f,2),h=e=>{Ki(e,{label:`Close local session`,get onClick(){return o()}})};Z(m,e=>{a()&&e(h)}),E(s),R(()=>{X(l,i()===`complete`?`✓`:`!`),X(d,G(n)),X(p,G(r))}),Y(e,s),k()}var ja=J(`

                                                                                  Working on the next step

                                                                                  The local process is checking your answers and preparing the next decision. Keep this page open.

                                                                                  `);function Ma(e){Y(e,ja())}var Na=J(`
                                                                                  PRIVATE LOCAL SESSION

                                                                                  Pair this browser

                                                                                  Find the 16-character pairing code in the terminal that started copilot setup --web. Enter it here to view or control this setup. The code is never placed in the browser URL or stored after this page closes.

                                                                                  Keep the code private. After refreshing this page, enter it again to reconnect.

                                                                                  `);function Pa(e,t){O(t,!1);let n=$(t,`busy`,8),r=$(t,`onPair`,8),i=M(``);function a(){let e=G(i);N(i,``),r()(e)}Si();var o=Na(),s=L(F(o),6),c=L(F(s),2);mi(c);var l=L(c,4);{let e=mt(()=>(q(n()),G(i),K(()=>n()||G(i).trim().length!==16)));Ki(l,{label:`Connect to local setup`,arrow:!0,onClick:a,get disabled(){return G(e)}})}E(s),E(o),R(()=>c.disabled=n()),hr(`submit`,s,e=>{e.preventDefault(),a()}),yi(c,()=>G(i),e=>N(i,e)),Y(e,o),k()}var Fa=J(``),Ia=J(`
                                                                                  `),La=J(``),Ra=J(`
                                                                                  `,1),za=J(`
                                                                                  LOCALHOST ONLY NO CLOUD SETUP ACCOUNT GITHUB OWNS PAT ISSUANCE
                                                                                  `);function Ba(e,t){O(t,!1);let n=()=>Ai(a,`$session`,r),[r,i]=ji(),a=Ni();Lr(()=>{let e=window.setInterval(()=>{n().paired&&!n().view?.outcome&&a.refresh()},900);return()=>window.clearInterval(e)});async function o(){window.confirm(`Cancel this local setup session? PATs already created in GitHub will still exist.`)&&await a.cancel()}async function s(e){let t=n().view?.promptRevision;t!==void 0&&await a.submit(t,e)}Si();var c=za();Zr(`16t12jp`,e=>{Y(e,Fa())});var l=F(c);{let e=mt(()=>n().view?.journey);Ii(l,{get journey(){return G(e)}})}var u=L(l,2),d=F(u);{let e=mt(()=>n().view?.repository);Bi(d,{get repository(){return G(e)}})}var f=L(d,2),p=F(f),m=e=>{Hi(e,{get view(){return n().view}})};Z(p,e=>{n().paired&&e(m)});var h=L(p,2),g=e=>{var t=Ia(),r=L(F(t));E(t),R(()=>X(r,` Reviewing saved choices — pass ${n().view.journey.choiceReviewPass??``}. This is the same setup run, not a restart.`)),Y(e,t)};Z(h,e=>{n().view?.journey?.choiceReviewPass&&n().view.journey.choiceReviewPass>1&&!n().view.outcome&&e(g)});var _=L(h,2),v=e=>{Yi(e,{tone:`warning`,title:`Read-only tab`,message:`Another tab controls this session. You can review progress here or explicitly take over.`,actionLabel:`Take control in this tab`,get onAction(){return a.takeOver}})};Z(_,e=>{!n().controller&&n().view&&e(v)});var y=L(_,2),b=e=>{Yi(e,{tone:`error`,title:`Needs attention`,get message(){return n().error}})};Z(y,e=>{n().error&&e(b)});var x=L(y,2),S=e=>{{let t=mt(()=>n().view.message.tone===`success`?`Checked`:n().view.message.tone===`warning`?`Please note`:n().view.message.tone===`error`?`Needs attention`:`Progress update`);Yi(e,{get tone(){return n().view.message.tone},get title(){return G(t)},get message(){return n().view.message.text},get link(){return n().view.message.link}})}};Z(x,e=>{n().view?.message&&e(S)});var ee=L(x,2),te=e=>{Pa(e,{get busy(){return n().busy},get onPair(){return a.pair}})},ne=e=>{Aa(e,{get outcome(){return n().view.outcome},get controller(){return n().controller},get onClose(){return a.close}})},re=e=>{var t=Ra(),r=on(t),i=F(r);Sa(i,{get prompt(){return n().view.prompt},get revision(){return n().view.revision},get promptRevision(){return n().view.promptRevision},get controller(){return n().controller},get busy(){return n().busy},onSubmit:s}),Oa(L(i,2),{get view(){return n().view}}),E(r);var a=L(r,2),c=e=>{var t=La();R(()=>t.disabled=n().busy),gr(`click`,t,o),Y(e,t)};Z(a,e=>{n().controller&&n().view.journey?.current!==`Apply`&&e(c)}),Y(e,t)},ie=e=>{Ma(e,{})};Z(ee,e=>{n().paired?n().view?.outcome?e(ne,1):n().view?.prompt?e(re,2):e(ie,-1):e(te)}),ke(2),E(f),E(u),E(c),Y(e,c),k(),i()}_r([`click`]),Mr(Ba,{target:document.getElementById(`app`)}); \ No newline at end of file diff --git a/build/web/index.html b/build/web/index.html index b9b0a01c8..efbc470c8 100644 --- a/build/web/index.html +++ b/build/web/index.html @@ -5,7 +5,7 @@ Copilot · Setup studio - + diff --git a/docs/authentication.mdx b/docs/authentication.mdx index 0fbc48bd1..c97cb568e 100644 --- a/docs/authentication.mdx +++ b/docs/authentication.mdx @@ -194,6 +194,14 @@ with bounded recovery guidance, including for a repository-scope default. GitHub does not reveal Secret values through its API. Copilot validates new credentials with provider metadata requests and validates existing remote Secrets through the read-only `copilot_credential_health.yml` workflow. Setup proves the exact file on the selected main ref and dispatches that path only when the workflow is also registered or installed on GitHub's default branch; doctor remains query-only and expects the normally indexed installed workflow. The health workflow reports each requested credential independently, but that bounded reachability result is not a permission audit. If `PAT` already exists, interactive setup asks you to re-enter it and runs the complete workflow-PAT permission matrix before provisioning; unattended setup must supply `PAT` again or stops before mutation. Temporary workflow bootstrap is available only during setup. A preauthenticated Codex session is runner state, not a Secret: it is accepted only when the runtime preflight can execute `codex login status` successfully. +Terminal setup may attempt a temporary credential-health workflow creation before +final Apply when checking existing Secrets. That request can create Git commits +even if the workflow is later removed. If setup stops after this attempt, it +reports a partial outcome: inspect the selected branch and GitHub workflow +history before retrying. An ambiguous create failure is treated conservatively +as a possible remote change. The browser setup path does not perform this +pre-Apply bootstrap. + For other existing credentials, choosing `keep` works only when the selected storage policy preserves the Secret in its current repository or organization scope. With `preserveExisting: false`, or an override that moves the Secret, diff --git a/docs/how-to-use.mdx b/docs/how-to-use.mdx index 3b85d5f53..f627f14f4 100644 --- a/docs/how-to-use.mdx +++ b/docs/how-to-use.mdx @@ -75,6 +75,9 @@ inspection and PAT checks; it does not dispatch or temporarily install a credential-health workflow. Re-enter an existing bot PAT for its grant audit. An optional existing provider Secret that you choose to keep may remain `unverifiable`; check runtime health with `copilot doctor` after installation. +In the browser questionnaire, an issue-workflow choice defaulted to **All** +stays selected if you continue unchanged; clearing that selection submits +**none** explicitly. Interactive `copilot setup` offers a guided GitHub link or manual entry for each PAT. The first link prepares a short-lived **setup PAT** for the person @@ -86,8 +89,11 @@ those answers. The second link, after the setup plan, prepares the The interactive terminal also shows your current phase: Repository → Setup choices → Setup PAT → Plan → Bot PAT & credentials → Apply. These are milestones, -not a percentage or a fixed number of questions. Before Apply, it says that no -changes have been made; after Apply starts, a failed run is marked partial so +not a percentage or a fixed number of questions. Before a remote mutation +attempt, it says that no changes have been made. Checking existing Secrets may +attempt a temporary credential-health workflow before final Apply; from that +point a failed run is marked partial, with branch-inspection guidance. After +Apply starts, a failed run is likewise marked partial so you can inspect what was installed. The initial and guided PAT views show a compact list of required grants. Choose **view full permission table** during review to see reasons and conditional grants without restarting the questions; diff --git a/docs/security-operations/operations/troubleshooting.mdx b/docs/security-operations/operations/troubleshooting.mdx index daec1dc39..5fc212e24 100644 --- a/docs/security-operations/operations/troubleshooting.mdx +++ b/docs/security-operations/operations/troubleshooting.mdx @@ -159,6 +159,11 @@ directory. The lock contains no PAT or other credential. If another actor creates or changes the temporary workflow, setup does not overwrite or remove that actor's file; inspect the selected branch before retrying a reported cleanup failure. + Terminal setup marks the run partial as soon as it attempts a temporary + credential-health workflow create, even if the request fails ambiguously or + the file is later removed. Inspect the selected branch and GitHub workflow + history before retrying: the create/delete commits remain in history. + Browser setup never performs this bootstrap before final Apply. Expected missing or unconfirmed final PAT permissions return a blocked result with the chosen configuration and stop before storage validation or any mutation. diff --git a/specs/local-web-setup-assistant.md b/specs/local-web-setup-assistant.md index 8fe9c0f1c..c137728da 100644 --- a/specs/local-web-setup-assistant.md +++ b/specs/local-web-setup-assistant.md @@ -453,6 +453,11 @@ presenter sizes, with reviewed exceptions only when cohesion justifies them. An empty issue-workflow multi-selection MUST submit an explicit `none` answer, not an empty answer that reuses defaults or silently selects every workflow. +When the questionnaire's multi-select default is `All`, the browser MUST +visibly preselect `All` and submit `All` if the operator continues unchanged; +explicitly deselecting it MUST still submit `none`. `All` remains mutually +exclusive with individual workflow choices. This is a pure presentation +initialization/serialization rule, not a second questionnaire parser. Both terminal and web routes use the same questionnaire parser for this choice. ### 8.2 Contracts, ownership, and trust diff --git a/specs/setup-configuration-credentials-and-doctor.md b/specs/setup-configuration-credentials-and-doctor.md index 2453eb242..b37ebbb31 100644 --- a/specs/setup-configuration-credentials-and-doctor.md +++ b/specs/setup-configuration-credentials-and-doctor.md @@ -160,6 +160,15 @@ cancellation, skipped diagnosis, ordering, and read-only authority explicit. complete permission audit before provisioning; credential health and storage preservation do not authorize an unaudited keep path. - Invalid required credentials must be replaced. +- Terminal credential-health validation MAY temporarily create and remove the + selected-branch health workflow before final Apply. As soon as the create + request is attempted, the journey MUST disclose a possible remote mutation; + a subsequent failure or cancellation MUST report a partial outcome and + direct the operator to inspect the selected branch, even if cleanup appeared + successful, because the create/delete commits remain in history. A failed + create request is conservatively classified as possible mutation when its + remote outcome is uncertain. The browser path does not bootstrap a workflow + before Apply and retains its no-mutation pre-approval guarantee. - A missing remote resource snapshot is never an empty inventory. Selected Secret/Variable management MUST stop before all remote resource, label, issue-type, and tag calls when inspection fails, its port is absent, or a @@ -417,7 +426,9 @@ widths, canceled prompts, secret masking, and GitHub permission variants. 2. Given non-interactive missing required input, setup fails without prompting or writes. 3. Given valid organization Secret and preserve-existing, no repository shadow is created. 4. Given invalid required existing credential, setup requires replacement. -5. Given canceled confirmation, local and GitHub state are unchanged. +5. Given canceled confirmation without a prior credential-health bootstrap + attempt, local and GitHub state are unchanged. With such an attempt, setup + reports a partial result and requires branch/history inspection. 6. Given a changed managed file, setup backs up before approved replacement. 7. Given doctor, no mutation port is called and unhealthy state returns non-zero. 8. Given merge-queue without proven support, setup/doctor reports fail closed. @@ -443,6 +454,11 @@ widths, canceled prompts, secret masking, and GitHub permission variants. Secret/Variable/label/issue-type/tag mutation; absence cannot be interpreted as an empty repository inventory. Pre-plan failures still reach the final audit as bounded unavailable access facts. +18. Given terminal credential-health bootstrap was attempted and cleanup + fails, setup reports `partial` with branch-inspection guidance rather + than `blocked` or "no changes applied"; the same conservative outcome + applies when creation times out ambiguously. Without a bootstrap attempt, + pre-Apply cancellation remains `cancelled`. 18. Given an organization Secret or Variable target, repository inventory is available and confirms that no same-name repository resource exists; otherwise setup blocks before credential collection or mutation, even with diff --git a/src/__tests__/cli.test.ts b/src/__tests__/cli.test.ts index 0bbf851ea..7e3718b53 100644 --- a/src/__tests__/cli.test.ts +++ b/src/__tests__/cli.test.ts @@ -113,8 +113,12 @@ const mockRemoteConfigurationInspect = jest.fn().mockResolvedValue(defaultRemote const mockSetupCredentialsCollect = jest.fn().mockResolvedValue({ collection: { apiKeys: [] }, checks: [], existingSecretNames: [], }); +const mockSetupCredentialsOptions = jest.fn(); jest.mock('../infrastructure/composition/setup_credentials_composition_root', () => ({ - createSetupCredentialsUseCase: () => ({ collect: mockSetupCredentialsCollect }), + createSetupCredentialsUseCase: (_prompt: unknown, _presenter: unknown, options: unknown) => { + mockSetupCredentialsOptions(options); + return { collect: mockSetupCredentialsCollect }; + }, createSetupRemoteConfigurationReadPort: () => ({ inspect: mockRemoteConfigurationInspect, inspectCredentialHealthWorkflow: jest.fn(async () => 'installed'), @@ -1149,7 +1153,7 @@ describe('CLI', () => { try { await program.parseAsync(['node', 'cli', 'setup', '--yes', '--pr-approval-mode', 'off', '--skip-secrets']); const { logInfo } = require('../utils/logger'); - expect(logInfo).toHaveBeenCalledWith(expect.stringContaining('No setup mutation started')); + expect(logInfo).toHaveBeenCalledWith(expect.stringContaining('No bot Secret write started')); expect(runLocalAction).not.toHaveBeenCalled(); expect(process.exitCode).toBe(1); } finally { identify.mockRestore(); botIdentity.mockRestore(); createTerminal.mockRestore(); } @@ -1344,6 +1348,63 @@ describe('CLI', () => { expect(process.exitCode).toBe(1); }); + it('reports a possible pre-Apply GitHub mutation if temporary health workflow creation was attempted', async () => { + mockSetupCredentialsCollect.mockImplementationOnce(async () => { + mockSetupCredentialsOptions.mock.lastCall?.[0].onTemporaryWorkflowMutationAttempt(); + throw new Error('Could not safely remove the temporary credential health workflow'); + }); + await program.parseAsync([ + 'node', 'cli', 'setup', '--token', 'ghp_abcdefghijklmnopqrstuvwxyz12', + '--skip-secrets', '--non-interactive', '--pr-approval-mode', 'off', '--yes', + ]); + const { logInfo } = require('../utils/logger'); + expect(logInfo.mock.calls.flat().join('\n')).toContain('temporary credential-health workflow create was attempted before Apply'); + expect(logInfo.mock.calls.flat().join('\n')).not.toContain('No changes were applied'); + expect(runLocalAction).not.toHaveBeenCalled(); + expect(process.exitCode).toBe(1); + }); + + it('shows a partial journey when terminal credential validation may have mutated GitHub', async () => { + const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); + const input = guidedTerminal(); + const createTerminal = jest.spyOn(terminalDriver, 'createInteractiveTerminalDriver') + .mockReturnValue(input as unknown as ReturnType); + mockTokenPermissionInspect.mockResolvedValueOnce(acceptedSetupPatReport()); + mockSetupCredentialsCollect.mockImplementationOnce(async () => { + mockSetupCredentialsOptions.mock.lastCall?.[0].onTemporaryWorkflowMutationAttempt(); + throw new Error('Could not safely remove the temporary credential health workflow'); + }); + try { + await program.parseAsync(['node', 'cli', 'setup', '--yes', '--pr-approval-mode', 'off', '--skip-secrets']); + const output = consoleLogSpy.mock.calls.flat().join('\n'); + expect(output).toContain('Partial: changes may exist'); + expect(output).not.toContain('Partial: application started'); + expect(runLocalAction).not.toHaveBeenCalled(); + expect(process.exitCode).toBe(1); + } finally { createTerminal.mockRestore(); } + }); + + it('does not claim a clean cancellation after temporary workflow creation was attempted', async () => { + const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); + const { SetupTerminalCancelledError } = require('../cli/setup_credential_prompt_adapter'); + const input = guidedTerminal(); + const createTerminal = jest.spyOn(terminalDriver, 'createInteractiveTerminalDriver') + .mockReturnValue(input as unknown as ReturnType); + mockTokenPermissionInspect.mockResolvedValueOnce(acceptedSetupPatReport()); + mockSetupCredentialsCollect.mockImplementationOnce(async () => { + mockSetupCredentialsOptions.mock.lastCall?.[0].onTemporaryWorkflowMutationAttempt(); + throw new SetupTerminalCancelledError(); + }); + try { + await program.parseAsync(['node', 'cli', 'setup', '--yes', '--pr-approval-mode', 'off', '--skip-secrets']); + const { logInfo } = require('../utils/logger'); + expect(logInfo.mock.calls.flat().join('\n')).toContain('Setup stopped after a possible credential-health workflow change'); + expect(logInfo.mock.calls.flat().join('\n')).not.toContain('Setup cancelled. No changes were applied.'); + expect(consoleLogSpy.mock.calls.flat().join('\n')).toContain('Partial: changes may exist'); + expect(process.exitCode).toBe(130); + } finally { createTerminal.mockRestore(); } + }); + it.each([ { ready: false, identityStatus: 'valid' as const }, { ready: true, identityStatus: 'invalid' as const }, diff --git a/src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts b/src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts index 2bd141239..acc8d5cdc 100644 --- a/src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts +++ b/src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts @@ -20,7 +20,7 @@ describe('setup journey', () => { expect(() => journey.advance('choices')).toThrow('backwards'); expect(() => journey.finish('complete')).toThrow('before applying'); expect(() => journey.finish('partial')).toThrow('before mutation'); - expect(() => journey.markMutationStarted()).toThrow('apply stage'); + expect(() => journey.markMutationStarted()).toThrow('credential validation or apply'); }); it('distinguishes dry-run, blocked, cancelled, and post-mutation partial state', () => { @@ -40,6 +40,18 @@ describe('setup journey', () => { expect(present.mock.lastCall?.[0]).toMatchObject({ outcome: 'partial', mutationStarted: true }); }); + it('records a possible temporary workflow mutation during credential validation and allows a partial result', () => { + const present = jest.fn(); + const journey = new SetupJourneyUseCase('owner/repo', { present }); + journey.advance('credentials'); + journey.markMutationStarted(); + journey.markMutationStarted(); + expect(present.mock.lastCall?.[0]).toMatchObject({ current: 'Bot PAT & credentials', mutationStarted: true }); + journey.finish('partial'); + expect(present.mock.lastCall?.[0]).toMatchObject({ outcome: 'partial', mutationStarted: true }); + expect(() => journey.advance('apply')).toThrow('finished'); + }); + it('reports completion only after mutation starts and ignores duplicate finish', () => { const present = jest.fn(); const journey = new SetupJourneyUseCase('owner/repo', { present }); diff --git a/src/application/usecases/setup/setup_journey_use_case.ts b/src/application/usecases/setup/setup_journey_use_case.ts index 3ea1e28a4..106ceb776 100644 --- a/src/application/usecases/setup/setup_journey_use_case.ts +++ b/src/application/usecases/setup/setup_journey_use_case.ts @@ -35,7 +35,10 @@ export class SetupJourneyUseCase { } markMutationStarted(): void { - if (this.stage !== 'apply' || this.outcome) throw new Error('Setup mutation must start in the apply stage.'); + if ((this.stage !== 'credentials' && this.stage !== 'apply') || this.outcome) { + throw new Error('Setup mutation can start only during credential validation or apply.'); + } + if (this.mutationStarted) return; this.mutationStarted = true; this.present(); } diff --git a/src/cli/__tests__/setup_journey_presenter.test.ts b/src/cli/__tests__/setup_journey_presenter.test.ts index 3fa384af0..b010c2c8c 100644 --- a/src/cli/__tests__/setup_journey_presenter.test.ts +++ b/src/cli/__tests__/setup_journey_presenter.test.ts @@ -11,7 +11,7 @@ describe('setup journey presenter', () => { it('keeps narrow output readable and distinguishes partial from complete', () => { const partial = renderSetupJourney(buildSetupJourneyView('owner/repo', 'apply', true, 'partial'), 40); - expect(partial).toContain('Partial: application started'); + expect(partial).toContain('Partial: changes may exist'); expect(partial).not.toContain('No changes have been applied.'); expect(partial.split('\n').every(line => line.length <= 42)).toBe(true); const complete = renderSetupJourney(buildSetupJourneyView('owner/repo', 'apply', true, 'complete'), 80); @@ -40,6 +40,15 @@ describe('setup journey presenter', () => { expect(starting).toContain('Next: Setup choices'); }); + it('explains a pre-Apply credential-health mutation without claiming final Apply began', () => { + const validating = renderSetupJourney(buildSetupJourneyView('owner/repo', 'credentials', true), 95); + expect(validating).toContain('temporary GitHub workflow change may exist'); + expect(validating).not.toContain('Applying the approved plan'); + const partial = renderSetupJourney(buildSetupJourneyView('owner/repo', 'credentials', true, 'partial'), 95); + expect(partial).toContain('Partial: changes may exist'); + expect(partial).not.toContain('No changes have been applied'); + }); + it('does not echo terminal control characters from a repository label', () => { const output = renderSetupJourney(buildSetupJourneyView('owner/\u001b[31mrepo', 'choices', false), 80); expect(output).not.toContain('\u001b[31m'); diff --git a/src/cli/__tests__/web_setup_ui_helpers.test.ts b/src/cli/__tests__/web_setup_ui_helpers.test.ts index f960b6de9..d455981b1 100644 --- a/src/cli/__tests__/web_setup_ui_helpers.test.ts +++ b/src/cli/__tests__/web_setup_ui_helpers.test.ts @@ -16,6 +16,20 @@ describe('web setup presentation helpers', () => { }); }); + test('preserves the documented All default until explicitly deselected', () => { + const prompt = question('multi-select', 'All'); + const initial = initialQuestionAnswer(prompt); + expect(initial).toEqual({ value: 'All', selected: ['All'] }); + expect(submittedQuestionAnswer(prompt, initial.value, initial.selected)).toBe('All'); + expect(submittedQuestionAnswer(prompt, initial.value, toggleSelection(initial.selected, 'All'))).toBe('none'); + expect(submittedQuestionAnswer(prompt, initial.value, toggleSelection(initial.selected, 'One — details'))).toBe('One — details'); + }); + + test('never invents an All selection when the choices do not offer it', () => { + const prompt = question('multi-select', 'All'); + expect(initialQuestionAnswer({ ...prompt, question: { ...prompt.question, choices: ['One — details'] } }).selected).toEqual([]); + }); + test('a multi-select without choices starts empty and never invents an option', () => { const prompt = question('multi-select', 'one'); expect(initialQuestionAnswer({ ...prompt, question: { ...prompt.question, choices: undefined } }).selected).toEqual([]); diff --git a/src/cli/commands/setup.ts b/src/cli/commands/setup.ts index c14312b79..b3b102d08 100644 --- a/src/cli/commands/setup.ts +++ b/src/cli/commands/setup.ts @@ -92,6 +92,7 @@ export function registerSetupCommand(program: Command): void { const workflowPrompt = webBridge ? new WebSetupWorkflowUpdatePrompt(webBridge) : new SetupWorkflowUpdatePromptAdapter(terminal); const cwd = process.cwd(); let setupMutationStarted = false; + let setupApplyStarted = false; let releaseSetupGuard: (() => void) | undefined; let journey: SetupJourneyUseCase | undefined; try { @@ -314,7 +315,12 @@ export function registerSetupCommand(program: Command): void { } } const credentials = await createSetupCredentialsUseCase(credentialPrompt, permissionPresenter, - webBridge ? { allowPreApplyHealthWorkflow: false } : undefined).collect({ + webBridge ? { allowPreApplyHealthWorkflow: false } : { + onTemporaryWorkflowMutationAttempt: () => { + setupMutationStarted = true; + journey?.markMutationStarted(); + }, + }).collect({ owner: gitInfo.owner, repository: gitInfo.repo, setupToken: token ?? '', @@ -383,6 +389,7 @@ export function registerSetupCommand(program: Command): void { } setupMutationStarted = true; journey?.markMutationStarted(); + setupApplyStarted = true; const actionResults = await runLocalAction(params); if (actionResults.some(actionResult => !actionResult.success || actionResult.errors.length > 0)) { journey?.finish('partial'); @@ -393,13 +400,18 @@ export function registerSetupCommand(program: Command): void { } } catch (error) { journey?.finish(setupMutationStarted ? 'partial' : error instanceof SetupTerminalCancelledError ? 'cancelled' : 'blocked'); + if (setupMutationStarted && !setupApplyStarted) { + logInfo('A temporary credential-health workflow create was attempted before Apply. Inspect the selected branch and GitHub workflow history before retrying; a failed request may still have reached GitHub.'); + } if (credentialPrompt.guidedWorkflowBotIdentity) { - logInfo(setupMutationStarted + logInfo(setupApplyStarted ? 'Setup may be partially applied. Inspect the GitHub Secret before deleting or replacing the bot PAT.' - : 'No setup mutation started. If you generated an unused bot PAT in GitHub, delete it there; Copilot cannot revoke it.'); + : 'No bot Secret write started. If you generated an unused bot PAT in GitHub, delete it there; Copilot cannot revoke it.'); } if (error instanceof SetupTerminalCancelledError) { - logInfo('Setup cancelled. No changes were applied.'); + logInfo(setupMutationStarted + ? 'Setup stopped after a possible credential-health workflow change. Inspect the selected branch and GitHub workflow history before retrying.' + : 'Setup cancelled. No changes were applied.'); process.exitCode = 130; return; } diff --git a/src/cli/setup_journey_presenter.ts b/src/cli/setup_journey_presenter.ts index c4d3605c8..83e09476f 100644 --- a/src/cli/setup_journey_presenter.ts +++ b/src/cli/setup_journey_presenter.ts @@ -12,10 +12,12 @@ export function renderSetupJourney(view: SetupJourneyView, maximumWidth?: number const revisitingChoices = view.current === 'Setup choices' && view.choiceReviewPass > 1; const state = view.outcome === 'complete' ? 'Complete: setup applied successfully.' : view.outcome === 'dry-run' ? 'Complete: dry run only; no changes were applied.' - : view.outcome === 'partial' ? 'Partial: application started; inspect the result before retrying.' + : view.outcome === 'partial' ? 'Partial: changes may exist; inspect the branch and GitHub resources before retrying.' : view.outcome === 'blocked' ? 'Blocked: setup cannot continue.' : view.outcome === 'cancelled' ? 'Cancelled: setup stopped.' - : view.mutationStarted ? 'Applying the approved plan; changes may already exist.' + : view.mutationStarted ? view.current === 'Bot PAT & credentials' + ? 'Checking credentials; a temporary GitHub workflow change may exist.' + : 'Applying the approved plan; changes may already exist.' : 'No changes have been applied.'; return renderBox([ `Repository: ${view.repository}`, diff --git a/src/infrastructure/__tests__/setup_remote_credential_health_adapter.test.ts b/src/infrastructure/__tests__/setup_remote_credential_health_adapter.test.ts index ee8dd87ac..5fc14f368 100644 --- a/src/infrastructure/__tests__/setup_remote_credential_health_adapter.test.ts +++ b/src/infrastructure/__tests__/setup_remote_credential_health_adapter.test.ts @@ -137,6 +137,45 @@ describe('setup remote credential health adapters', () => { expect(github.repos.deleteFile).toHaveBeenCalledWith(expect.objectContaining({ sha: 'created-sha', branch: 'main' })); }); + it('reports the remote mutation risk before sending the temporary workflow create request', async () => { + const github = client(); + github.repos.getContent.mockResolvedValueOnce({ data: [] }) + .mockRejectedValueOnce({ status: 404 }) + .mockResolvedValueOnce({ data: { sha: 'created-sha' } }); + const onTemporaryWorkflowMutationAttempt = jest.fn(() => { + expect(github.repos.createOrUpdateFileContents).not.toHaveBeenCalled(); + }); + await new SetupRemoteCredentialHealthBootstrapAdapter({ getClient: jest.fn(() => github) }, { + workflowContent: 'name: health', waitMs: 0, pollMs: 0, onTemporaryWorkflowMutationAttempt, + }).validateExisting('owner', 'repo', 'token', 'main', requirements); + expect(onTemporaryWorkflowMutationAttempt).toHaveBeenCalledTimes(1); + }); + + it('reports possible mutation when creation has an ambiguous transport failure', async () => { + const github = client(); + github.repos.getContent.mockResolvedValueOnce({ data: [] }).mockRejectedValueOnce({ status: 404 }); + github.repos.createOrUpdateFileContents.mockRejectedValueOnce(new Error('connection lost after request')); + const onTemporaryWorkflowMutationAttempt = jest.fn(); + await expect(new SetupRemoteCredentialHealthBootstrapAdapter({ getClient: jest.fn(() => github) }, { + workflowContent: 'name: health', onTemporaryWorkflowMutationAttempt, + }).validateExisting('owner', 'repo', 'token', 'main', requirements)) + .rejects.toThrow('Could not create the temporary credential health workflow safely'); + expect(onTemporaryWorkflowMutationAttempt).toHaveBeenCalledTimes(1); + expect(github.repos.deleteFile).not.toHaveBeenCalled(); + }); + + it('does not mark a remote workflow mutation when only an installed workflow is dispatched', async () => { + const github = client(); + github.repos.getContent.mockResolvedValueOnce({ data: [] }) + .mockResolvedValueOnce({ data: { sha: 'installed-sha' } }); + const onTemporaryWorkflowMutationAttempt = jest.fn(); + await new SetupRemoteCredentialHealthBootstrapAdapter({ getClient: jest.fn(() => github) }, { + waitMs: 0, pollMs: 0, onTemporaryWorkflowMutationAttempt, + }).validateExisting('owner', 'repo', 'token', 'main', requirements); + expect(onTemporaryWorkflowMutationAttempt).not.toHaveBeenCalled(); + expect(github.repos.createOrUpdateFileContents).not.toHaveBeenCalled(); + }); + it('bootstraps a missing selected ref even when Actions finds the workflow on the default branch', async () => { const github = client(); github.repos.getContent.mockResolvedValueOnce({ data: [] }) diff --git a/src/infrastructure/composition/setup_credentials_composition_root.ts b/src/infrastructure/composition/setup_credentials_composition_root.ts index 454dd1065..7f6a3994a 100644 --- a/src/infrastructure/composition/setup_credentials_composition_root.ts +++ b/src/infrastructure/composition/setup_credentials_composition_root.ts @@ -14,7 +14,7 @@ import { createSetupTokenPermissionsUseCase } from './setup_token_permissions_co export function createSetupCredentialsUseCase( prompt: SetupCredentialPromptPort, permissionPresenter?: SetupTokenPermissionPresenterPort, - options: { allowPreApplyHealthWorkflow?: boolean } = {}, + options: { allowPreApplyHealthWorkflow?: boolean; onTemporaryWorkflowMutationAttempt?: () => void } = {}, ): SetupCredentialsUseCase { const secretNames = new RepositorySecretNamesQueryRepository(createRepositoryVariablesClient()); return new SetupCredentialsUseCase( @@ -23,7 +23,9 @@ export function createSetupCredentialsUseCase( secretNames, options.allowPreApplyHealthWorkflow === false ? undefined - : new SetupRemoteCredentialHealthBootstrapAdapter(new OctokitCredentialHealthClientAdapter()), + : new SetupRemoteCredentialHealthBootstrapAdapter(new OctokitCredentialHealthClientAdapter(), { + onTemporaryWorkflowMutationAttempt: options.onTemporaryWorkflowMutationAttempt, + }), createSetupTokenPermissionsUseCase(), permissionPresenter, ); diff --git a/src/infrastructure/setup_remote_credential_health_adapter.ts b/src/infrastructure/setup_remote_credential_health_adapter.ts index 590aca5a3..cd1199f6a 100644 --- a/src/infrastructure/setup_remote_credential_health_adapter.ts +++ b/src/infrastructure/setup_remote_credential_health_adapter.ts @@ -45,6 +45,8 @@ export interface CredentialHealthQueryOptions { export interface CredentialHealthBootstrapOptions extends CredentialHealthQueryOptions { workflowContent?: string; + /** Invoked before the create request: an ambiguous transport failure may still have committed the file. */ + onTemporaryWorkflowMutationAttempt?: () => void; } /** Doctor boundary: dispatches and reads an existing health workflow; it has no repository mutation client. */ @@ -80,6 +82,7 @@ export class SetupRemoteCredentialHealthQueryAdapter implements SetupRemoteCrede export class SetupRemoteCredentialHealthBootstrapAdapter implements SetupRemoteCredentialHealthPort { private readonly options: Required; private readonly workflowContent: string; + private readonly onTemporaryWorkflowMutationAttempt?: () => void; constructor( private readonly githubClient: GithubClientPort, @@ -87,6 +90,7 @@ export class SetupRemoteCredentialHealthBootstrapAdapter implements SetupRemoteC ) { this.options = resolveOptions(options); this.workflowContent = options.workflowContent ?? readHealthWorkflow(); + this.onTemporaryWorkflowMutationAttempt = options.onTemporaryWorkflowMutationAttempt; } async validateExisting( @@ -123,6 +127,7 @@ export class SetupRemoteCredentialHealthBootstrapAdapter implements SetupRemoteC ): Promise { if (!this.workflowContent) throw new Error('Credential health workflow template is unavailable.'); let created: Awaited>; + this.onTemporaryWorkflowMutationAttempt?.(); try { created = await client.repos.createOrUpdateFileContents({ owner, diff --git a/web/src/lib/questionAnswer.ts b/web/src/lib/questionAnswer.ts index 157ed302b..c4af02e36 100644 --- a/web/src/lib/questionAnswer.ts +++ b/web/src/lib/questionAnswer.ts @@ -6,7 +6,8 @@ export function initialQuestionAnswer(prompt: QuestionPrompt): { value: string; const value = String(prompt.question.defaultValue); const defaults = value.split(',').map(item => item.trim()).filter(Boolean); const selected = prompt.question.kind === 'multi-select' - ? (prompt.question.choices ?? []).filter(item => item !== 'All' && defaults.includes(item.split(' — ')[0])) + ? defaults.includes('All') && prompt.question.choices?.includes('All') ? ['All'] + : (prompt.question.choices ?? []).filter(item => item !== 'All' && defaults.includes(item.split(' — ')[0])) : prompt.question.kind === 'scope-overrides' ? defaults : []; return { value, selected }; } From 516dafd933fa1b78fd429ceeee4da7d2fbcb30a3 Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Mon, 28 Sep 2026 23:20:04 +0200 Subject: [PATCH 24/50] codex-setup-temporary-github-auth: fail closed when repository owner type is unknown --- build/cli/index.js | 9 +++++++- docs/authentication.mdx | 6 +++++ .../operations/troubleshooting.mdx | 5 ++++ ...at-permission-guidance-and-verification.md | 11 +++++++++ .../temporary-setup-operator-authorization.md | 12 +++++++++- src/__tests__/cli.test.ts | 10 +++++--- .../setup_token_permission_policy.test.ts | 16 +++++++++++++ .../policies/setup_token_permission_policy.ts | 5 +++- ...udit_configured_setup_pat_use_case.test.ts | 23 ++++++++++++++++++- .../audit_configured_setup_pat_use_case.ts | 5 ++++ 10 files changed, 95 insertions(+), 7 deletions(-) diff --git a/build/cli/index.js b/build/cli/index.js index e76952861..0228015c3 100755 --- a/build/cli/index.js +++ b/build/cli/index.js @@ -48649,7 +48649,9 @@ function buildSetupPatPermissionRequirements() { * selected setup operation or its read-only preflight. */ function buildConfiguredSetupPatPermissionRequirements(configuration, remote) { - return buildSetupPatRequirements(configuration, remote?.ownerType === 'Organization', remote); + // Unknown is not evidence of a personal owner: keep possible organization + // grants visible until the final audit can verify the actual owner type. + return buildSetupPatRequirements(configuration, remote?.ownerType === 'Organization' || remote?.ownerType === 'Unknown', remote); } /** Grants justified by local choices alone; remote-only conditions stay unresolved. */ function buildSetupPatIntentPermissionRequirements(configuration, ownerKind) { @@ -54949,6 +54951,11 @@ class AuditConfiguredSetupPatUseCase { async audit(configuration, remote) { const required = (0, setup_token_permission_policy_1.buildConfiguredSetupPatPermissionRequirements)(configuration, remote); this.ports.presenter.showRequirements('setup', required); + if (this.context.token && (!remote || remote.ownerType === 'Unknown')) { + return { status: 'blocked', errors: [ + 'GitHub could not verify whether this repository is owned by an organization or a user. Retry remote inspection before applying setup; the pre-PAT owner selection is not authorization evidence.', + ] }; + } if (this.context.assertedOwnerKind && remote && remote.ownerType !== 'Unknown' && remote.ownerType !== this.context.assertedOwnerKind) { this.ports.showOwnerMismatch(this.context.assertedOwnerKind, remote.ownerType); diff --git a/docs/authentication.mdx b/docs/authentication.mdx index c97cb568e..8fbfdfc11 100644 --- a/docs/authentication.mdx +++ b/docs/authentication.mdx @@ -202,6 +202,12 @@ history before retrying. An ambiguous create failure is treated conservatively as a possible remote change. The browser setup path does not perform this pre-Apply bootstrap. +The final setup-PAT audit requires GitHub to verify whether the repository +owner is a person or an organization. If that remote owner type is unknown or +unavailable, setup stops before provisioning and asks you to retry inspection; +the owner type you selected earlier for a guided link is only provisional. +Potential organization grants remain visible in the permission preview. + For other existing credentials, choosing `keep` works only when the selected storage policy preserves the Secret in its current repository or organization scope. With `preserveExisting: false`, or an override that moves the Secret, diff --git a/docs/security-operations/operations/troubleshooting.mdx b/docs/security-operations/operations/troubleshooting.mdx index 5fc212e24..ec39ba44a 100644 --- a/docs/security-operations/operations/troubleshooting.mdx +++ b/docs/security-operations/operations/troubleshooting.mdx @@ -164,6 +164,11 @@ directory. The lock contains no PAT or other credential. the file is later removed. Inspect the selected branch and GitHub workflow history before retrying: the create/delete commits remain in history. Browser setup never performs this bootstrap before final Apply. + + If the final PAT audit says GitHub could not verify the repository owner + type, do not treat the earlier guided owner selection as proof. Confirm the + repository owner in GitHub, restore API access, and retry setup. No local + provisioning or bot Secret write should begin from that blocked audit. Expected missing or unconfirmed final PAT permissions return a blocked result with the chosen configuration and stop before storage validation or any mutation. diff --git a/specs/setup-pat-permission-guidance-and-verification.md b/specs/setup-pat-permission-guidance-and-verification.md index a5ff0a8e0..8a78044bd 100644 --- a/specs/setup-pat-permission-guidance-and-verification.md +++ b/specs/setup-pat-permission-guidance-and-verification.md @@ -478,6 +478,11 @@ derived from the existing immutable configuration, repository owner type, storage targets, and selected features. The permission catalog, status semantics, maximum probe concurrency, and prohibition on write probes are not configurable. +If authenticated repository inspection cannot establish whether the owner is +an organization or a user, the final token-backed setup audit MUST stop before +all provisioning, with a retry/inspection action. The preview may display +potential organization grants, but neither a guided owner assertion nor an +accepted PAT probe may convert unknown ownership into verified scope. Recommended interactive use remains `copilot setup`. Non-interactive setup prints permission results for supplied PATs but never prompts. `--dry-run` @@ -946,6 +951,12 @@ at widths 40/80/120 and `NO_COLOR`. still can. An unclosed quoted fence cannot hide a later shell block after the blockquote level ends, and an exceptional quoted shell fence remains inspectable if its container ends without a closing marker. +51. Given authenticated remote owner type is `Unknown`, the setup permission + preview retains potential organization grants for selected issue workflows, + Projects, and organization storage, but a token-backed final audit blocks + before permission probes or provisioning. A guided owner assertion cannot + bypass this; once GitHub verifies `User` or `Organization`, the requirements + are recomputed for that actual type. ## 17. Requirements traceability diff --git a/specs/temporary-setup-operator-authorization.md b/specs/temporary-setup-operator-authorization.md index 451dc0358..c896c581d 100644 --- a/specs/temporary-setup-operator-authorization.md +++ b/specs/temporary-setup-operator-authorization.md @@ -169,6 +169,11 @@ means authenticated remote facts may require a corrected grant after entry. 6. Preflight answers are operator intent, not GitHub facts or authorization. Unknown owner type, remote inventory, approval, and workflow status MUST never be fabricated from defaults or treated as proven by a user answer. + An unknown or unavailable authenticated owner type MUST block a token-backed + final setup-PAT audit before provisioning. The operator's preflight owner + assertion may shape a provisional link but cannot replace verified GitHub + owner evidence. The final requirement preview MUST retain potential + organization grants rather than silently treating `Unknown` as `User`. ## 5. Current versus proposed product journey @@ -667,6 +672,11 @@ Secret renewal. that GitHub form choice. 14. All primary states remain readable without color at narrow width and the full URL is copyable. +15. Given authenticated remote owner type is `Unknown` (or unavailable), a + token-backed final audit blocks before provisioning even if the operator + asserted `Organization` or `User` earlier. The preview keeps possible + organization grants visible and asks for a fresh GitHub inspection; it + never presents the asserted kind as verified evidence. ## 17. Requirements traceability @@ -675,7 +685,7 @@ Secret renewal. | Guided/manual choice (§4.1) | setup CLI + presenter | scenarios 1–2, 8 | how-to-use | | Intent collection/reuse (§4.1, §6.1) | questionnaire + pure projection | scenarios 3–4, 9 | how-to-use/configuration | | Exact/provisional grants (§4.1–4.3) | permission policy + URL builder | scenarios 4–5, 7, 12–13 | authentication/configuration | -| Actual token audit (§4.1) | existing permission use case | scenarios 6–7 | troubleshooting | +| Actual token audit (§4.1) | existing permission use case | scenarios 6–7, 15 | troubleshooting | | Cleanup truth (§4.1–4.3) | setup result presenter | scenarios 9–11 | authentication/troubleshooting | | Accessible UI (§9) | terminal renderer | scenario 14 | how-to-use | diff --git a/src/__tests__/cli.test.ts b/src/__tests__/cli.test.ts index 7e3718b53..c6109282c 100644 --- a/src/__tests__/cli.test.ts +++ b/src/__tests__/cli.test.ts @@ -1093,7 +1093,7 @@ describe('CLI', () => { } finally { createTerminal.mockRestore(); } }); - it('warns about excess organization grants if remote owner type cannot be resolved', async () => { + it('blocks before mutation if GitHub cannot verify the owner type despite guided organization intent', async () => { const terminalDriver = require('../cli/setup_terminal_driver') as typeof import('../cli/setup_terminal_driver'); const input = guidedTerminal(prompt => prompt.includes('repository owner an organization') ? '1' : undefined); const createTerminal = jest.spyOn(terminalDriver, 'createInteractiveTerminalDriver') @@ -1102,8 +1102,12 @@ describe('CLI', () => { mockTokenPermissionInspect.mockResolvedValueOnce(acceptedSetupPatReport()); try { await program.parseAsync(['node', 'cli', 'setup', '--yes', '--pr-approval-mode', 'off', '--skip-secrets']); - const { logInfo } = require('../utils/logger'); - expect(logInfo).toHaveBeenCalledWith(expect.stringContaining('PAT may have excess access')); + const { logError } = require('../utils/logger'); + expect(logError).toHaveBeenCalledWith(expect.objectContaining({ + message: expect.stringContaining('could not verify whether this repository is owned'), + })); + expect(runLocalAction).not.toHaveBeenCalled(); + expect(process.exitCode).toBe(1); } finally { createTerminal.mockRestore(); } }); diff --git a/src/application/policies/__tests__/setup_token_permission_policy.test.ts b/src/application/policies/__tests__/setup_token_permission_policy.test.ts index b561cccd3..c3cf93cce 100644 --- a/src/application/policies/__tests__/setup_token_permission_policy.test.ts +++ b/src/application/policies/__tests__/setup_token_permission_policy.test.ts @@ -79,6 +79,22 @@ describe('setup token permission policy', () => { ]); }); + it('keeps possible organization grants visible when remote owner type is unknown', () => { + const configuration = createDefaultSetupConfiguration(); + configuration.projects.ids = 'PVT_example'; + configuration.storage.secrets.defaultScope = 'organization'; + const unknown = buildConfiguredSetupPatPermissionRequirements(configuration, { + ...organization, ownerType: 'Unknown', + }).map(item => `${item.scope}:${item.permission}:${item.level}`); + expect(unknown).toEqual(expect.arrayContaining([ + 'organization:Secrets:write', 'organization:Issue Types:write', 'organization:Projects:write', + ])); + const personal = buildConfiguredSetupPatPermissionRequirements(configuration, { + ...organization, ownerType: 'User', + }); + expect(personal.some(item => item.scope === 'organization')).toBe(false); + }); + it('omits stale disabled issue workflows from the configured setup PAT plan', () => { const configuration = createDefaultSetupConfiguration(); configuration.manageRepositorySecrets = false; diff --git a/src/application/policies/setup_token_permission_policy.ts b/src/application/policies/setup_token_permission_policy.ts index 3a320fdff..5d083d61c 100644 --- a/src/application/policies/setup_token_permission_policy.ts +++ b/src/application/policies/setup_token_permission_policy.ts @@ -64,7 +64,10 @@ export function buildConfiguredSetupPatPermissionRequirements( configuration: Readonly, remote?: Readonly, ): SetupTokenPermissionRequirement[] { - return buildSetupPatRequirements(configuration, remote?.ownerType === 'Organization', remote); + // Unknown is not evidence of a personal owner: keep possible organization + // grants visible until the final audit can verify the actual owner type. + return buildSetupPatRequirements(configuration, + remote?.ownerType === 'Organization' || remote?.ownerType === 'Unknown', remote); } /** Grants justified by local choices alone; remote-only conditions stay unresolved. */ diff --git a/src/application/usecases/setup/__tests__/audit_configured_setup_pat_use_case.test.ts b/src/application/usecases/setup/__tests__/audit_configured_setup_pat_use_case.test.ts index bd917bff5..3ea6c3d4f 100644 --- a/src/application/usecases/setup/__tests__/audit_configured_setup_pat_use_case.test.ts +++ b/src/application/usecases/setup/__tests__/audit_configured_setup_pat_use_case.test.ts @@ -59,9 +59,30 @@ describe('AuditConfiguredSetupPatUseCase', () => { expect(ports.permissions.inspect).not.toHaveBeenCalled(); }); - test('unknown owner type does not fabricate a mismatch', async () => { + test.each(['Organization', 'User'] as const)('unknown owner type blocks token-backed audit despite %s assertion', async assertedOwnerKind => { + const { ports, useCase } = harness({ token: 'test-token', guided: true, assertedOwnerKind }); + const result = await useCase.audit(configuration, { ...remote, ownerType: 'Unknown' }); + expect(result).toEqual({ status: 'blocked', errors: [expect.stringContaining('could not verify')] }); + expect(ports.showOwnerMismatch).not.toHaveBeenCalled(); + expect(ports.showUpdatedLink).not.toHaveBeenCalled(); + expect(ports.permissions.inspect).not.toHaveBeenCalled(); + expect(ports.presenter.showRequirements).toHaveBeenCalledWith('setup', expect.arrayContaining([ + expect.objectContaining({ scope: 'organization', permission: 'Issue Types' }), + ])); + }); + + test('unavailable owner inspection blocks a token-backed audit before permission probes', async () => { + const { ports, useCase } = harness({ token: 'test-token' }); + expect(await useCase.audit(configuration)).toEqual({ + status: 'blocked', errors: [expect.stringContaining('could not verify')], + }); + expect(ports.permissions.inspect).not.toHaveBeenCalled(); + }); + + test('dry-run preview without a token may show unknown owner grants without authorizing mutations', async () => { const { ports, useCase } = harness({ assertedOwnerKind: 'User' }); expect(await useCase.audit(configuration, { ...remote, ownerType: 'Unknown' })).toEqual({ status: 'accepted' }); + expect(ports.permissions.inspect).not.toHaveBeenCalled(); expect(ports.showOwnerMismatch).not.toHaveBeenCalled(); }); diff --git a/src/application/usecases/setup/audit_configured_setup_pat_use_case.ts b/src/application/usecases/setup/audit_configured_setup_pat_use_case.ts index e1e06d757..2850602a6 100644 --- a/src/application/usecases/setup/audit_configured_setup_pat_use_case.ts +++ b/src/application/usecases/setup/audit_configured_setup_pat_use_case.ts @@ -36,6 +36,11 @@ export class AuditConfiguredSetupPatUseCase implements SetupFinalPermissionAudit ): Promise<{ status: 'accepted' } | { status: 'blocked'; errors: readonly string[] }> { const required = buildConfiguredSetupPatPermissionRequirements(configuration, remote); this.ports.presenter.showRequirements('setup', required); + if (this.context.token && (!remote || remote.ownerType === 'Unknown')) { + return { status: 'blocked', errors: [ + 'GitHub could not verify whether this repository is owned by an organization or a user. Retry remote inspection before applying setup; the pre-PAT owner selection is not authorization evidence.', + ] }; + } if (this.context.assertedOwnerKind && remote && remote.ownerType !== 'Unknown' && remote.ownerType !== this.context.assertedOwnerKind) { this.ports.showOwnerMismatch(this.context.assertedOwnerKind, remote.ownerType); From c8715816fc125e0212b5bcaccbb4bb5eab96c745 Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Tue, 29 Sep 2026 09:44:44 +0200 Subject: [PATCH 25/50] codex-setup-temporary-github-auth: cover web setup edge paths --- src/__tests__/cli.test.ts | 35 +++++++++++++++++++ .../verify_web_setup_apply_use_case.test.ts | 7 ++++ src/cli/__tests__/setup_presenters.test.ts | 6 +++- src/cli/__tests__/setup_session_guard.test.ts | 11 ++++++ .../setup_token_permission_presenter.test.ts | 12 +++++++ src/cli/__tests__/web_setup_adapters.test.ts | 6 ++++ src/cli/__tests__/web_setup_server.test.ts | 13 +++++++ .../commands/__tests__/setup_policy.test.ts | 4 --- src/cli/commands/setup.ts | 4 --- src/cli/commands/setup_policy.ts | 5 ++- 10 files changed, 91 insertions(+), 12 deletions(-) diff --git a/src/__tests__/cli.test.ts b/src/__tests__/cli.test.ts index c6109282c..cd4611101 100644 --- a/src/__tests__/cli.test.ts +++ b/src/__tests__/cli.test.ts @@ -546,6 +546,14 @@ describe('CLI', () => { expect(runLocalAction).not.toHaveBeenCalled(); }); + it('treats a dismissed repository confirmation as cancellation', async () => { + ask.mockResolvedValueOnce(undefined); + await program.parseAsync(['node', 'cli', 'setup', '--web']); + expect(mockTokenPermissionInspect).not.toHaveBeenCalled(); + expect(runLocalAction).not.toHaveBeenCalled(); + expect(process.exitCode).toBe(130); + }); + it('rejects detached HEAD before opening the browser or collecting a PAT', async () => { (execSync as jest.Mock).mockImplementation((command: string) => { if (command === 'git symbolic-ref --quiet --short HEAD') throw new Error('detached HEAD'); @@ -616,6 +624,16 @@ describe('CLI', () => { expect(runLocalAction).toHaveBeenCalledTimes(1); }); + it('discards an environment PAT when the operator chooses a different one', async () => { + mockGetSetupToken.mockReturnValueOnce('github_pat_from_environment_test'); + ask.mockImplementation(async (prompt: WebSetupPrompt) => prompt.title === 'An environment setup PAT is available' + ? 'Create or enter a different PAT' : answerWebPrompt(prompt)); + await program.parseAsync(['node', 'cli', 'setup', '--web', '--pr-approval-mode', 'off', '--skip-secrets']); + expect(ask.mock.calls.map(call => call[0].title)).toContain('Temporary setup PAT'); + expect(mockTokenPermissionInspect.mock.calls[0][0].token).toBe('github_pat_web_setup_test_token'); + expect(runLocalAction).toHaveBeenCalledTimes(1); + }); + it('does not use an environment PAT when the browser choice is cancelled', async () => { mockGetSetupToken.mockReturnValueOnce('github_pat_from_environment_test'); ask.mockImplementation(async (prompt: WebSetupPrompt) => prompt.title === 'An environment setup PAT is available' @@ -642,6 +660,23 @@ describe('CLI', () => { expect(process.exitCode).toBeUndefined(); }); + it('treats a dismissed final Apply prompt as cancellation', async () => { + ask.mockImplementation(async (prompt: WebSetupPrompt) => prompt.title === 'Apply this setup now?' + ? undefined : answerWebPrompt(prompt)); + await program.parseAsync(['node', 'cli', 'setup', '--web', '--pr-approval-mode', 'off', '--skip-secrets']); + expect(runLocalAction).not.toHaveBeenCalled(); + expect(process.exitCode).toBe(130); + }); + + it('reports partial completion when an approved action fails', async () => { + (runLocalAction as jest.Mock).mockResolvedValueOnce([{ success: false, errors: ['provider failed'] }]); + await program.parseAsync(['node', 'cli', 'setup', '--web', '--pr-approval-mode', 'off', '--skip-secrets']); + expect(runLocalAction).toHaveBeenCalledTimes(1); + expect(process.exitCode).toBe(1); + const { logInfo } = require('../utils/logger'); + expect(logInfo).toHaveBeenCalledWith(expect.stringContaining('partial completion')); + }); + it('passes only explicitly approved changed workflows to the mutation boundary', async () => { const comparison = jest.spyOn(SetupDoctorWorkspaceQueryAdapter.prototype, 'compareWorkflows') .mockReturnValue([ diff --git a/src/application/usecases/setup/__tests__/verify_web_setup_apply_use_case.test.ts b/src/application/usecases/setup/__tests__/verify_web_setup_apply_use_case.test.ts index e62af0185..8dc0c6813 100644 --- a/src/application/usecases/setup/__tests__/verify_web_setup_apply_use_case.test.ts +++ b/src/application/usecases/setup/__tests__/verify_web_setup_apply_use_case.test.ts @@ -141,6 +141,13 @@ describe('VerifyWebSetupApplyUseCase', () => { expect(ports.permissionAudit.audit).not.toHaveBeenCalled(); }); + test('treats an unavailable workflow inspection port as unknown rather than reusing stale evidence', async () => { + const { ports, useCase } = harness(); + delete (ports.remote as { inspectCredentialHealthWorkflow?: unknown }).inspectCredentialHealthWorkflow; + await expect(useCase.execute(request)).rejects.toThrow('GitHub repository facts changed'); + expect(ports.permissionAudit.audit).not.toHaveBeenCalled(); + }); + test('blocks when the final PAT audit loses a required grant', async () => { const { ports, useCase } = harness(); jest.spyOn(ports.permissionAudit, 'audit').mockResolvedValue({ status: 'blocked', errors: ['missing'] }); diff --git a/src/cli/__tests__/setup_presenters.test.ts b/src/cli/__tests__/setup_presenters.test.ts index 72d046d11..bfc5a2d07 100644 --- a/src/cli/__tests__/setup_presenters.test.ts +++ b/src/cli/__tests__/setup_presenters.test.ts @@ -462,11 +462,15 @@ describe('setup presenters and prompt-specific adapters', () => { { kind: 'value', value: '2' }, { kind: 'value', value: 'manual-bot-token' }, ]), {}); const resolve = jest.fn(); - adapter.configureWorkflowPatGuide('https://github.com/settings/personal-access-tokens/new', resolve); + adapter.configureWorkflowPatGuide('https://github.com/settings/personal-access-tokens/new', resolve, [{ + id: 'workflow.repository.contents', role: 'workflow', scope: 'repository', permission: 'Contents', + level: 'write', applicability: 'required', reason: 'Manage branches.', probe: 'contents', + }]); await expect(adapter.requestWorkflowPat({ name: 'PAT', kind: 'workflowPat', description: 'Runtime token' })) .resolves.toEqual({ name: 'PAT', value: 'manual-bot-token' }); expect(resolve).not.toHaveBeenCalled(); expect(adapter.guidedWorkflowBotIdentity).toBeUndefined(); + expect(log.mock.calls.flat().join('\n')).toContain('Workflow PAT permissions required'); } finally { log.mockRestore(); } }); diff --git a/src/cli/__tests__/setup_session_guard.test.ts b/src/cli/__tests__/setup_session_guard.test.ts index 289f094cc..04784eb2e 100644 --- a/src/cli/__tests__/setup_session_guard.test.ts +++ b/src/cli/__tests__/setup_session_guard.test.ts @@ -74,6 +74,17 @@ describe('setup session guard', () => { } }); + test('does not mistake a failed atomic link for an existing setup session', () => { + const link = jest.spyOn(require('node:fs'), 'linkSync').mockImplementationOnce(() => { + throw Object.assign(new Error('Filesystem is read-only'), { code: 'EROFS' }); + }); + try { + expect(() => acquireSetupSessionGuard(root)).toThrow('Filesystem is read-only'); + expect(existsSync(lockPath())).toBe(false); + expect(readdirSync(tmpdir()).filter(name => name.startsWith(`${basename(lockPath())}.`))).toEqual([]); + } finally { link.mockRestore(); } + }); + test('a failed staged write never publishes an empty lock or leaves a staging file', () => { const write = jest.spyOn(require('node:fs'), 'writeFileSync').mockImplementationOnce(() => { throw Object.assign(new Error('Disk full'), { code: 'ENOSPC' }); diff --git a/src/cli/__tests__/setup_token_permission_presenter.test.ts b/src/cli/__tests__/setup_token_permission_presenter.test.ts index c941b546a..fbbdc858d 100644 --- a/src/cli/__tests__/setup_token_permission_presenter.test.ts +++ b/src/cli/__tests__/setup_token_permission_presenter.test.ts @@ -1,4 +1,5 @@ import { + ConsoleSetupTokenPermissionPresenter, renderSetupTokenPermissionReport, renderSetupTokenPermissionRequirements, renderSetupTokenPermissionSummary, @@ -16,6 +17,17 @@ const secrets: SetupTokenPermissionRequirement = { }; describe('setup token permission presenter', () => { + it('uses the full requirement table by default and summary only when requested', () => { + const log = jest.spyOn(console, 'log').mockImplementation(); + try { + new ConsoleSetupTokenPermissionPresenter().showRequirements('setup', [metadata, secrets]); + expect(log.mock.calls.flat().join('\n')).toContain('Provision Actions Secrets.'); + log.mockClear(); + new ConsoleSetupTokenPermissionPresenter('summary').showRequirements('setup', [metadata, secrets]); + expect(log.mock.calls.flat().join('\n')).toContain('Conditional permissions: 1'); + expect(log.mock.calls.flat().join('\n')).not.toContain('Provision Actions Secrets.'); + } finally { log.mockRestore(); } + }); it('summarizes only required URL grants and counts conditional rows without changing policy', () => { const output = renderSetupTokenPermissionSummary('setup', [metadata, secrets], 80); expect(output).toContain('Required now: Metadata read (repository)'); diff --git a/src/cli/__tests__/web_setup_adapters.test.ts b/src/cli/__tests__/web_setup_adapters.test.ts index 223bd06a2..54ecc4c62 100644 --- a/src/cli/__tests__/web_setup_adapters.test.ts +++ b/src/cli/__tests__/web_setup_adapters.test.ts @@ -272,6 +272,12 @@ describe('semantic web setup adapters', () => { await expect(token).rejects.toThrow('cancelled'); }); + test('rejects an out-of-contract choice even if the bridge supplies one', async () => { + const bridge = new WebSetupBridge('owner/repo'); + jest.spyOn(bridge, 'ask').mockResolvedValueOnce('unlisted choice'); + await expect(new WebSetupCredentialPrompt(bridge).chooseSetupPatMethod()).rejects.toThrow('Invalid setup choice'); + }); + test('guided setup account requires a reported identity and a positive operator decision', async () => { const bridge = new WebSetupBridge('owner/repo'); const prompt = new WebSetupCredentialPrompt(bridge); diff --git a/src/cli/__tests__/web_setup_server.test.ts b/src/cli/__tests__/web_setup_server.test.ts index 39c6b0a0e..4419ccc1c 100644 --- a/src/cli/__tests__/web_setup_server.test.ts +++ b/src/cli/__tests__/web_setup_server.test.ts @@ -55,6 +55,9 @@ describe('local web setup server', () => { expect(page.headers.get('cache-control')).toBe('no-store'); expect(page.headers.get('access-control-allow-origin')).toBeNull(); expect((await fetch(`${server.url}assets/app.js`)).status).toBe(200); + const css = await fetch(`${server.url}assets/app.css`); + expect(css.status).toBe(200); + expect(css.headers.get('content-type')).toBe('text/css; charset=utf-8'); writeFileSync(join(root, 'assets', 'unlisted.js'), 'alert(1)'); expect((await fetch(`${server.url}assets/unlisted.js`)).status).toBe(404); expect((await fetch(`${server.url}assets/%2e%2e/index.html`)).status).toBe(404); @@ -110,6 +113,16 @@ describe('local web setup server', () => { expect((await post(server.pairingCode)).status).toBe(200); }); + test('pairing requires JSON even for a valid code', async () => { + const response = await globalThis.fetch(`${server.url}api/pair`, { + method: 'POST', + headers: { Origin: new URL(server.url).origin, 'Content-Type': 'text/plain' }, + body: JSON.stringify({ code: server.pairingCode }), + }); + expect(response.status).toBe(415); + expect(await response.text()).not.toContain(sessionKeys.get(new URL(server.url).origin)!); + }); + test('five incorrect pairing attempts lock out even the valid code for this run', async () => { const endpoint = `${server.url}api/pair`; const origin = new URL(server.url).origin; diff --git a/src/cli/commands/__tests__/setup_policy.test.ts b/src/cli/commands/__tests__/setup_policy.test.ts index 6ab4285ef..7384d6d16 100644 --- a/src/cli/commands/__tests__/setup_policy.test.ts +++ b/src/cli/commands/__tests__/setup_policy.test.ts @@ -7,7 +7,6 @@ const gitInfo = { owner: 'owner', repo: 'repo' } as const; describe('setup command policy', () => { it('builds the initial setup action with repository and token context', () => { const params = buildSetupParams({ debug: true }, gitInfo, 'token'); - if (!params) throw new Error('Expected valid setup parameters.'); expect(params).toMatchObject({ [INPUT_KEYS.DEBUG]: 'true', [INPUT_KEYS.SINGLE_ACTION]: ACTIONS.INITIAL_SETUP, @@ -18,7 +17,4 @@ describe('setup command policy', () => { expect(params[INPUT_KEYS.WELCOME_MESSAGES]).toHaveLength(2); }); - it('does not build params for an invalid git context', () => { - expect(buildSetupParams({}, { error: 'missing' }, 'token')).toBeUndefined(); - }); }); diff --git a/src/cli/commands/setup.ts b/src/cli/commands/setup.ts index b3b102d08..e64c05a5a 100644 --- a/src/cli/commands/setup.ts +++ b/src/cli/commands/setup.ts @@ -383,10 +383,6 @@ export function registerSetupCommand(program: Command): void { approvedWorkflowFiles, remoteConfiguration, ); - if (!params) { - journey?.finish('blocked'); - return; - } setupMutationStarted = true; journey?.markMutationStarted(); setupApplyStarted = true; diff --git a/src/cli/commands/setup_policy.ts b/src/cli/commands/setup_policy.ts index 11ff37247..16fc8fcfa 100644 --- a/src/cli/commands/setup_policy.ts +++ b/src/cli/commands/setup_policy.ts @@ -10,14 +10,13 @@ export interface SetupCommandOptions { export function buildSetupParams( options: SetupCommandOptions, - gitInfo: GitInfo, + gitInfo: Extract, token: string, configuration?: SetupConfiguration, credentials?: SetupCredentialCollection, approvedWorkflowFiles: readonly string[] = [], remoteConfiguration?: SetupRemoteConfiguration, -): Record | undefined { - if ('error' in gitInfo) return undefined; +): Record { return { ...(configuration ? buildSetupActionInputs(configuration) : {}), [INPUT_KEYS.DEBUG]: options.debug?.toString() ?? 'false', From d6b5c816b46a3bf693324a8c646fdfd7f613725b Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Tue, 29 Sep 2026 09:51:06 +0200 Subject: [PATCH 26/50] codex-setup-temporary-github-auth: sync CLI bundle with coverage cleanup --- build/cli/index.js | 5942 ++++++++++++++++++++++---------------------- 1 file changed, 2968 insertions(+), 2974 deletions(-) diff --git a/build/cli/index.js b/build/cli/index.js index 0228015c3..b9fd33c45 100755 --- a/build/cli/index.js +++ b/build/cli/index.js @@ -2,7 +2,7 @@ /******/ (() => { // webpackBootstrap /******/ var __webpack_modules__ = ({ -/***/ 18538: +/***/ 36086: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -53,7 +53,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.exec = exec; exports.getExecOutput = getExecOutput; const string_decoder_1 = __nccwpck_require__(71576); -const tr = __importStar(__nccwpck_require__(4094)); +const tr = __importStar(__nccwpck_require__(55908)); /** * Exec a command. * Output will be streamed to the live console. @@ -125,7 +125,7 @@ function getExecOutput(commandLine, args, options) { /***/ }), -/***/ 4094: +/***/ 55908: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -179,8 +179,8 @@ const os = __importStar(__nccwpck_require__(22037)); const events = __importStar(__nccwpck_require__(82361)); const child = __importStar(__nccwpck_require__(32081)); const path = __importStar(__nccwpck_require__(71017)); -const io = __importStar(__nccwpck_require__(34166)); -const ioUtil = __importStar(__nccwpck_require__(4813)); +const io = __importStar(__nccwpck_require__(15476)); +const ioUtil = __importStar(__nccwpck_require__(90188)); const timers_1 = __nccwpck_require__(39512); /* eslint-disable @typescript-eslint/unbound-method */ const IS_WINDOWS = process.platform === 'win32'; @@ -757,7 +757,7 @@ class ExecState extends events.EventEmitter { /***/ }), -/***/ 26402: +/***/ 81103: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -820,7 +820,7 @@ exports.Context = Context; /***/ }), -/***/ 78227: +/***/ 87211: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -861,8 +861,8 @@ var __importStar = (this && this.__importStar) || (function () { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.context = void 0; exports.getOctokit = getOctokit; -const Context = __importStar(__nccwpck_require__(26402)); -const utils_1 = __nccwpck_require__(33536); +const Context = __importStar(__nccwpck_require__(81103)); +const utils_1 = __nccwpck_require__(76954); exports.context = new Context.Context(); /** * Returns a hydrated octokit ready to use for GitHub Actions @@ -878,7 +878,7 @@ function getOctokit(token, options, ...additionalPlugins) { /***/ }), -/***/ 92746: +/***/ 8423: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -931,8 +931,8 @@ exports.getProxyAgent = getProxyAgent; exports.getProxyAgentDispatcher = getProxyAgentDispatcher; exports.getProxyFetch = getProxyFetch; exports.getApiBaseUrl = getApiBaseUrl; -const httpClient = __importStar(__nccwpck_require__(75784)); -const undici_1 = __nccwpck_require__(18381); +const httpClient = __importStar(__nccwpck_require__(33843)); +const undici_1 = __nccwpck_require__(79868); function getAuthString(token, options) { if (!token && !options.auth) { throw new Error('Parameter token or opts.auth is required'); @@ -964,7 +964,7 @@ function getApiBaseUrl() { /***/ }), -/***/ 33536: +/***/ 76954: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -1005,12 +1005,12 @@ var __importStar = (this && this.__importStar) || (function () { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.GitHub = exports.defaults = exports.context = void 0; exports.getOctokitOptions = getOctokitOptions; -const Context = __importStar(__nccwpck_require__(26402)); -const Utils = __importStar(__nccwpck_require__(92746)); +const Context = __importStar(__nccwpck_require__(81103)); +const Utils = __importStar(__nccwpck_require__(8423)); // octokit + plugins -const core_1 = __nccwpck_require__(922); -const plugin_rest_endpoint_methods_1 = __nccwpck_require__(50305); -const plugin_paginate_rest_1 = __nccwpck_require__(36738); +const core_1 = __nccwpck_require__(47216); +const plugin_rest_endpoint_methods_1 = __nccwpck_require__(57496); +const plugin_paginate_rest_1 = __nccwpck_require__(55347); exports.context = new Context.Context(); const baseUrl = Utils.getApiBaseUrl(); exports.defaults = { @@ -1040,7 +1040,7 @@ function getOctokitOptions(token, options) { /***/ }), -/***/ 75784: +/***/ 33843: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -1094,9 +1094,9 @@ exports.getProxyUrl = getProxyUrl; exports.isHttps = isHttps; const http = __importStar(__nccwpck_require__(13685)); const https = __importStar(__nccwpck_require__(95687)); -const pm = __importStar(__nccwpck_require__(34583)); -const tunnel = __importStar(__nccwpck_require__(64249)); -const undici_1 = __nccwpck_require__(18381); +const pm = __importStar(__nccwpck_require__(67906)); +const tunnel = __importStar(__nccwpck_require__(98787)); +const undici_1 = __nccwpck_require__(79868); var HttpCodes; (function (HttpCodes) { HttpCodes[HttpCodes["OK"] = 200] = "OK"; @@ -1784,7 +1784,7 @@ const lowercaseKeys = (obj) => Object.keys(obj).reduce((c, k) => ((c[k.toLowerCa /***/ }), -/***/ 34583: +/***/ 67906: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -1885,7 +1885,7 @@ class DecodedURL extends URL { /***/ }), -/***/ 4813: +/***/ 90188: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -2116,7 +2116,7 @@ function getCmdPath() { /***/ }), -/***/ 34166: +/***/ 15476: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -2172,7 +2172,7 @@ exports.which = which; exports.findInPath = findInPath; const assert_1 = __nccwpck_require__(39491); const path = __importStar(__nccwpck_require__(71017)); -const ioUtil = __importStar(__nccwpck_require__(4813)); +const ioUtil = __importStar(__nccwpck_require__(90188)); /** * Copies a file or folder. * Based off of shelljs - https://github.com/shelljs/shelljs/blob/9237f66c52e5daa40458f94f9565e18e8132f5a6/src/cp.js @@ -2435,13 +2435,13 @@ function copyFile(srcFile, destFile, force) { /***/ }), -/***/ 61570: +/***/ 6465: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const stringWidth = __nccwpck_require__(77486) +const stringWidth = __nccwpck_require__(78963) function ansiAlign (text, opts) { if (!text) return text @@ -2504,7 +2504,7 @@ function fullDiff (maxWidth, curWidth) { /***/ }), -/***/ 75207: +/***/ 16083: /***/ ((module) => { "use strict"; @@ -2522,12 +2522,12 @@ module.exports = ({onlyFirst = false} = {}) => { /***/ }), -/***/ 77755: +/***/ 76291: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const cliBoxes = __nccwpck_require__(57227); +const cliBoxes = __nccwpck_require__(70106); module.exports = cliBoxes; // TODO: Remove this for the next major release @@ -2536,7 +2536,7 @@ module.exports["default"] = cliBoxes; /***/ }), -/***/ 33104: +/***/ 25863: /***/ ((module) => { module.exports = () => { @@ -2547,7 +2547,7 @@ module.exports = () => { /***/ }), -/***/ 29311: +/***/ 44393: /***/ ((module) => { "use strict"; @@ -2561,7 +2561,7 @@ module.exports = function () { /***/ }), -/***/ 24063: +/***/ 22439: /***/ ((module) => { "use strict"; @@ -2619,7 +2619,7 @@ module.exports["default"] = isFullwidthCodePoint; /***/ }), -/***/ 783: +/***/ 87969: /***/ ((__unused_webpack_module, exports) => { /*! js-yaml 5.4.1 https://github.com/nodeca/js-yaml @license MIT */ @@ -6350,19 +6350,19 @@ exports.visit = visit; /***/ }), -/***/ 75430: +/***/ 18342: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; -exports.quote = __nccwpck_require__(91017); -exports.parse = __nccwpck_require__(79131); +exports.quote = __nccwpck_require__(17833); +exports.parse = __nccwpck_require__(71663); /***/ }), -/***/ 79131: +/***/ 71663: /***/ ((module) => { "use strict"; @@ -6700,7 +6700,7 @@ module.exports = function parse(s, env, opts) { /***/ }), -/***/ 91017: +/***/ 17833: /***/ ((module) => { "use strict"; @@ -6773,14 +6773,14 @@ module.exports = function quote(xs) { /***/ }), -/***/ 77486: +/***/ 78963: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const stripAnsi = __nccwpck_require__(10828); -const isFullwidthCodePoint = __nccwpck_require__(24063); -const emojiRegex = __nccwpck_require__(29311); +const stripAnsi = __nccwpck_require__(83941); +const isFullwidthCodePoint = __nccwpck_require__(22439); +const emojiRegex = __nccwpck_require__(44393); const stringWidth = string => { if (typeof string !== 'string' || string.length === 0) { @@ -6828,27 +6828,27 @@ module.exports["default"] = stringWidth; /***/ }), -/***/ 10828: +/***/ 83941: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const ansiRegex = __nccwpck_require__(75207); +const ansiRegex = __nccwpck_require__(16083); module.exports = string => typeof string === 'string' ? string.replace(ansiRegex(), '') : string; /***/ }), -/***/ 64249: +/***/ 98787: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -module.exports = __nccwpck_require__(30709); +module.exports = __nccwpck_require__(27222); /***/ }), -/***/ 30709: +/***/ 27222: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -7120,7 +7120,7 @@ exports.debug = debug; // for test /***/ }), -/***/ 24258: +/***/ 37124: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { (function(nacl) { @@ -9518,34 +9518,34 @@ nacl.setPRNG = function(fn) { /***/ }), -/***/ 18381: +/***/ 79868: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const Client = __nccwpck_require__(85849) -const Dispatcher = __nccwpck_require__(66071) -const Pool = __nccwpck_require__(80229) -const BalancedPool = __nccwpck_require__(68255) -const Agent = __nccwpck_require__(73274) -const ProxyAgent = __nccwpck_require__(87187) -const EnvHttpProxyAgent = __nccwpck_require__(29941) -const RetryAgent = __nccwpck_require__(55184) -const errors = __nccwpck_require__(35990) -const util = __nccwpck_require__(50011) +const Client = __nccwpck_require__(41234) +const Dispatcher = __nccwpck_require__(11588) +const Pool = __nccwpck_require__(3639) +const BalancedPool = __nccwpck_require__(58591) +const Agent = __nccwpck_require__(78590) +const ProxyAgent = __nccwpck_require__(76930) +const EnvHttpProxyAgent = __nccwpck_require__(52673) +const RetryAgent = __nccwpck_require__(4420) +const errors = __nccwpck_require__(5425) +const util = __nccwpck_require__(39141) const { InvalidArgumentError } = errors -const api = __nccwpck_require__(20617) -const buildConnector = __nccwpck_require__(41429) -const MockClient = __nccwpck_require__(4227) -const MockAgent = __nccwpck_require__(46432) -const MockPool = __nccwpck_require__(36575) -const mockErrors = __nccwpck_require__(19329) -const RetryHandler = __nccwpck_require__(64524) -const { getGlobalDispatcher, setGlobalDispatcher } = __nccwpck_require__(19405) -const DecoratorHandler = __nccwpck_require__(41738) -const RedirectHandler = __nccwpck_require__(64014) -const createRedirectInterceptor = __nccwpck_require__(40928) +const api = __nccwpck_require__(56796) +const buildConnector = __nccwpck_require__(28786) +const MockClient = __nccwpck_require__(56607) +const MockAgent = __nccwpck_require__(36294) +const MockPool = __nccwpck_require__(60834) +const mockErrors = __nccwpck_require__(63739) +const RetryHandler = __nccwpck_require__(30160) +const { getGlobalDispatcher, setGlobalDispatcher } = __nccwpck_require__(12621) +const DecoratorHandler = __nccwpck_require__(63859) +const RedirectHandler = __nccwpck_require__(71779) +const createRedirectInterceptor = __nccwpck_require__(96329) Object.assign(Dispatcher.prototype, api) @@ -9563,10 +9563,10 @@ module.exports.DecoratorHandler = DecoratorHandler module.exports.RedirectHandler = RedirectHandler module.exports.createRedirectInterceptor = createRedirectInterceptor module.exports.interceptors = { - redirect: __nccwpck_require__(74872), - retry: __nccwpck_require__(79637), - dump: __nccwpck_require__(12493), - dns: __nccwpck_require__(20346) + redirect: __nccwpck_require__(37849), + retry: __nccwpck_require__(19182), + dump: __nccwpck_require__(13413), + dns: __nccwpck_require__(58033) } module.exports.buildConnector = buildConnector @@ -9628,7 +9628,7 @@ function makeDispatcher (fn) { module.exports.setGlobalDispatcher = setGlobalDispatcher module.exports.getGlobalDispatcher = getGlobalDispatcher -const fetchImpl = (__nccwpck_require__(78329).fetch) +const fetchImpl = (__nccwpck_require__(9526).fetch) module.exports.fetch = async function fetch (init, options = undefined) { try { return await fetchImpl(init, options) @@ -9640,39 +9640,39 @@ module.exports.fetch = async function fetch (init, options = undefined) { throw err } } -module.exports.Headers = __nccwpck_require__(10561).Headers -module.exports.Response = __nccwpck_require__(51132).Response -module.exports.Request = __nccwpck_require__(83211).Request -module.exports.FormData = __nccwpck_require__(62598).FormData +module.exports.Headers = __nccwpck_require__(66089).Headers +module.exports.Response = __nccwpck_require__(98579).Response +module.exports.Request = __nccwpck_require__(3891).Request +module.exports.FormData = __nccwpck_require__(26697).FormData module.exports.File = globalThis.File ?? (__nccwpck_require__(72254).File) -module.exports.FileReader = __nccwpck_require__(65153).FileReader +module.exports.FileReader = __nccwpck_require__(73002).FileReader -const { setGlobalOrigin, getGlobalOrigin } = __nccwpck_require__(13924) +const { setGlobalOrigin, getGlobalOrigin } = __nccwpck_require__(82470) module.exports.setGlobalOrigin = setGlobalOrigin module.exports.getGlobalOrigin = getGlobalOrigin -const { CacheStorage } = __nccwpck_require__(11069) -const { kConstruct } = __nccwpck_require__(50591) +const { CacheStorage } = __nccwpck_require__(10471) +const { kConstruct } = __nccwpck_require__(93639) // Cache & CacheStorage are tightly coupled with fetch. Even if it may run // in an older version of Node, it doesn't have any use without fetch. module.exports.caches = new CacheStorage(kConstruct) -const { deleteCookie, getCookies, getSetCookies, setCookie } = __nccwpck_require__(15855) +const { deleteCookie, getCookies, getSetCookies, setCookie } = __nccwpck_require__(30035) module.exports.deleteCookie = deleteCookie module.exports.getCookies = getCookies module.exports.getSetCookies = getSetCookies module.exports.setCookie = setCookie -const { parseMIMEType, serializeAMimeType } = __nccwpck_require__(96730) +const { parseMIMEType, serializeAMimeType } = __nccwpck_require__(29192) module.exports.parseMIMEType = parseMIMEType module.exports.serializeAMimeType = serializeAMimeType -const { CloseEvent, ErrorEvent, MessageEvent } = __nccwpck_require__(69459) -module.exports.WebSocket = __nccwpck_require__(16416).WebSocket +const { CloseEvent, ErrorEvent, MessageEvent } = __nccwpck_require__(46055) +module.exports.WebSocket = __nccwpck_require__(1468).WebSocket module.exports.CloseEvent = CloseEvent module.exports.ErrorEvent = ErrorEvent module.exports.MessageEvent = MessageEvent @@ -9688,18 +9688,18 @@ module.exports.MockPool = MockPool module.exports.MockAgent = MockAgent module.exports.mockErrors = mockErrors -const { EventSource } = __nccwpck_require__(6731) +const { EventSource } = __nccwpck_require__(90109) module.exports.EventSource = EventSource /***/ }), -/***/ 97433: +/***/ 52872: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -const { addAbortListener } = __nccwpck_require__(50011) -const { RequestAbortedError } = __nccwpck_require__(35990) +const { addAbortListener } = __nccwpck_require__(39141) +const { RequestAbortedError } = __nccwpck_require__(5425) const kListener = Symbol('kListener') const kSignal = Symbol('kSignal') @@ -9759,7 +9759,7 @@ module.exports = { /***/ }), -/***/ 93671: +/***/ 15921: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -9767,9 +9767,9 @@ module.exports = { const assert = __nccwpck_require__(98061) const { AsyncResource } = __nccwpck_require__(92761) -const { InvalidArgumentError, SocketError } = __nccwpck_require__(35990) -const util = __nccwpck_require__(50011) -const { addSignal, removeSignal } = __nccwpck_require__(97433) +const { InvalidArgumentError, SocketError } = __nccwpck_require__(5425) +const util = __nccwpck_require__(39141) +const { addSignal, removeSignal } = __nccwpck_require__(52872) class ConnectHandler extends AsyncResource { constructor (opts, callback) { @@ -9875,7 +9875,7 @@ module.exports = connect /***/ }), -/***/ 281: +/***/ 69785: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -9890,10 +9890,10 @@ const { InvalidArgumentError, InvalidReturnValueError, RequestAbortedError -} = __nccwpck_require__(35990) -const util = __nccwpck_require__(50011) +} = __nccwpck_require__(5425) +const util = __nccwpck_require__(39141) const { AsyncResource } = __nccwpck_require__(92761) -const { addSignal, removeSignal } = __nccwpck_require__(97433) +const { addSignal, removeSignal } = __nccwpck_require__(52872) const assert = __nccwpck_require__(98061) const kResume = Symbol('resume') @@ -10134,17 +10134,17 @@ module.exports = pipeline /***/ }), -/***/ 26562: +/***/ 67177: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; const assert = __nccwpck_require__(98061) -const { Readable } = __nccwpck_require__(93401) -const { InvalidArgumentError, RequestAbortedError } = __nccwpck_require__(35990) -const util = __nccwpck_require__(50011) -const { getResolveErrorBodyCallback } = __nccwpck_require__(80710) +const { Readable } = __nccwpck_require__(12217) +const { InvalidArgumentError, RequestAbortedError } = __nccwpck_require__(5425) +const util = __nccwpck_require__(39141) +const { getResolveErrorBodyCallback } = __nccwpck_require__(23292) const { AsyncResource } = __nccwpck_require__(92761) class RequestHandler extends AsyncResource { @@ -10356,7 +10356,7 @@ module.exports.RequestHandler = RequestHandler /***/ }), -/***/ 75059: +/***/ 39777: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -10364,11 +10364,11 @@ module.exports.RequestHandler = RequestHandler const assert = __nccwpck_require__(98061) const { finished, PassThrough } = __nccwpck_require__(84492) -const { InvalidArgumentError, InvalidReturnValueError } = __nccwpck_require__(35990) -const util = __nccwpck_require__(50011) -const { getResolveErrorBodyCallback } = __nccwpck_require__(80710) +const { InvalidArgumentError, InvalidReturnValueError } = __nccwpck_require__(5425) +const util = __nccwpck_require__(39141) +const { getResolveErrorBodyCallback } = __nccwpck_require__(23292) const { AsyncResource } = __nccwpck_require__(92761) -const { addSignal, removeSignal } = __nccwpck_require__(97433) +const { addSignal, removeSignal } = __nccwpck_require__(52872) class StreamHandler extends AsyncResource { constructor (opts, factory, callback) { @@ -10584,16 +10584,16 @@ module.exports = stream /***/ }), -/***/ 23792: +/***/ 7981: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { InvalidArgumentError, SocketError } = __nccwpck_require__(35990) +const { InvalidArgumentError, SocketError } = __nccwpck_require__(5425) const { AsyncResource } = __nccwpck_require__(92761) -const util = __nccwpck_require__(50011) -const { addSignal, removeSignal } = __nccwpck_require__(97433) +const util = __nccwpck_require__(39141) +const { addSignal, removeSignal } = __nccwpck_require__(52872) const assert = __nccwpck_require__(98061) class UpgradeHandler extends AsyncResource { @@ -10700,22 +10700,22 @@ module.exports = upgrade /***/ }), -/***/ 20617: +/***/ 56796: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -module.exports.request = __nccwpck_require__(26562) -module.exports.stream = __nccwpck_require__(75059) -module.exports.pipeline = __nccwpck_require__(281) -module.exports.upgrade = __nccwpck_require__(23792) -module.exports.connect = __nccwpck_require__(93671) +module.exports.request = __nccwpck_require__(67177) +module.exports.stream = __nccwpck_require__(39777) +module.exports.pipeline = __nccwpck_require__(69785) +module.exports.upgrade = __nccwpck_require__(7981) +module.exports.connect = __nccwpck_require__(15921) /***/ }), -/***/ 93401: +/***/ 12217: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -10725,9 +10725,9 @@ module.exports.connect = __nccwpck_require__(93671) const assert = __nccwpck_require__(98061) const { Readable } = __nccwpck_require__(84492) -const { RequestAbortedError, NotSupportedError, InvalidArgumentError, AbortError } = __nccwpck_require__(35990) -const util = __nccwpck_require__(50011) -const { ReadableStreamFrom } = __nccwpck_require__(50011) +const { RequestAbortedError, NotSupportedError, InvalidArgumentError, AbortError } = __nccwpck_require__(5425) +const util = __nccwpck_require__(39141) +const { ReadableStreamFrom } = __nccwpck_require__(39141) const kConsume = Symbol('kConsume') const kReading = Symbol('kReading') @@ -11108,15 +11108,15 @@ module.exports = { Readable: BodyReadable, chunksDecode } /***/ }), -/***/ 80710: +/***/ 23292: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { const assert = __nccwpck_require__(98061) const { ResponseStatusCodeError -} = __nccwpck_require__(35990) +} = __nccwpck_require__(5425) -const { chunksDecode } = __nccwpck_require__(93401) +const { chunksDecode } = __nccwpck_require__(12217) const CHUNK_LIMIT = 128 * 1024 async function getResolveErrorBodyCallback ({ callback, body, contentType, statusCode, statusMessage, headers }) { @@ -11208,7 +11208,7 @@ module.exports = { /***/ }), -/***/ 41429: +/***/ 28786: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -11216,9 +11216,9 @@ module.exports = { const net = __nccwpck_require__(87503) const assert = __nccwpck_require__(98061) -const util = __nccwpck_require__(50011) -const { InvalidArgumentError, ConnectTimeoutError } = __nccwpck_require__(35990) -const timers = __nccwpck_require__(77512) +const util = __nccwpck_require__(39141) +const { InvalidArgumentError, ConnectTimeoutError } = __nccwpck_require__(5425) +const timers = __nccwpck_require__(52372) function noop () {} @@ -11456,7 +11456,7 @@ module.exports = buildConnector /***/ }), -/***/ 53451: +/***/ 41233: /***/ ((module) => { "use strict"; @@ -11582,7 +11582,7 @@ module.exports = { /***/ }), -/***/ 65543: +/***/ 59241: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -11792,7 +11792,7 @@ module.exports = { /***/ }), -/***/ 35990: +/***/ 5425: /***/ ((module) => { "use strict"; @@ -12225,7 +12225,7 @@ module.exports = { /***/ }), -/***/ 13484: +/***/ 48356: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -12234,7 +12234,7 @@ module.exports = { const { InvalidArgumentError, NotSupportedError -} = __nccwpck_require__(35990) +} = __nccwpck_require__(5425) const assert = __nccwpck_require__(98061) const { isValidHTTPToken, @@ -12249,9 +12249,9 @@ const { validateHandler, getServerName, normalizedMethodRecords -} = __nccwpck_require__(50011) -const { channels } = __nccwpck_require__(65543) -const { headerNameLowerCasedRecord } = __nccwpck_require__(53451) +} = __nccwpck_require__(39141) +const { channels } = __nccwpck_require__(59241) +const { headerNameLowerCasedRecord } = __nccwpck_require__(41233) // Verifies that a given path is valid does not contain control chars \x00 to \x20 const invalidPathRegex = /[^\u0021-\u00ff]/ @@ -12649,7 +12649,7 @@ module.exports = Request /***/ }), -/***/ 13638: +/***/ 53606: /***/ ((module) => { module.exports = { @@ -12723,7 +12723,7 @@ module.exports = { /***/ }), -/***/ 30723: +/***/ 71228: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -12732,7 +12732,7 @@ module.exports = { const { wellknownHeaderNames, headerNameLowerCasedRecord -} = __nccwpck_require__(53451) +} = __nccwpck_require__(41233) class TstNode { /** @type {any} */ @@ -12883,14 +12883,14 @@ module.exports = { /***/ }), -/***/ 50011: +/***/ 39141: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; const assert = __nccwpck_require__(98061) -const { kDestroyed, kBodyUsed, kListeners, kBody } = __nccwpck_require__(13638) +const { kDestroyed, kBodyUsed, kListeners, kBody } = __nccwpck_require__(53606) const { IncomingMessage } = __nccwpck_require__(88849) const stream = __nccwpck_require__(84492) const net = __nccwpck_require__(87503) @@ -12898,9 +12898,9 @@ const { Blob } = __nccwpck_require__(72254) const nodeUtil = __nccwpck_require__(47261) const { stringify } = __nccwpck_require__(39630) const { EventEmitter: EE } = __nccwpck_require__(15673) -const { InvalidArgumentError } = __nccwpck_require__(35990) -const { headerNameLowerCasedRecord } = __nccwpck_require__(53451) -const { tree } = __nccwpck_require__(30723) +const { InvalidArgumentError } = __nccwpck_require__(5425) +const { headerNameLowerCasedRecord } = __nccwpck_require__(41233) +const { tree } = __nccwpck_require__(71228) const [nodeMajor, nodeMinor] = process.versions.node.split('.').map(v => Number(v)) @@ -13610,19 +13610,19 @@ module.exports = { /***/ }), -/***/ 73274: +/***/ 78590: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { InvalidArgumentError } = __nccwpck_require__(35990) -const { kClients, kRunning, kClose, kDestroy, kDispatch, kInterceptors } = __nccwpck_require__(13638) -const DispatcherBase = __nccwpck_require__(39504) -const Pool = __nccwpck_require__(80229) -const Client = __nccwpck_require__(85849) -const util = __nccwpck_require__(50011) -const createRedirectInterceptor = __nccwpck_require__(40928) +const { InvalidArgumentError } = __nccwpck_require__(5425) +const { kClients, kRunning, kClose, kDestroy, kDispatch, kInterceptors } = __nccwpck_require__(53606) +const DispatcherBase = __nccwpck_require__(92548) +const Pool = __nccwpck_require__(3639) +const Client = __nccwpck_require__(41234) +const util = __nccwpck_require__(39141) +const createRedirectInterceptor = __nccwpck_require__(96329) const kOnConnect = Symbol('onConnect') const kOnDisconnect = Symbol('onDisconnect') @@ -13747,7 +13747,7 @@ module.exports = Agent /***/ }), -/***/ 68255: +/***/ 58591: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -13756,7 +13756,7 @@ module.exports = Agent const { BalancedPoolMissingUpstreamError, InvalidArgumentError -} = __nccwpck_require__(35990) +} = __nccwpck_require__(5425) const { PoolBase, kClients, @@ -13764,10 +13764,10 @@ const { kAddClient, kRemoveClient, kGetDispatcher -} = __nccwpck_require__(1467) -const Pool = __nccwpck_require__(80229) -const { kUrl, kInterceptors } = __nccwpck_require__(13638) -const { parseOrigin } = __nccwpck_require__(50011) +} = __nccwpck_require__(24458) +const Pool = __nccwpck_require__(3639) +const { kUrl, kInterceptors } = __nccwpck_require__(53606) +const { parseOrigin } = __nccwpck_require__(39141) const kFactory = Symbol('factory') const kOptions = Symbol('options') @@ -13964,7 +13964,7 @@ module.exports = BalancedPool /***/ }), -/***/ 14429: +/***/ 9581: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -13973,9 +13973,9 @@ module.exports = BalancedPool /* global WebAssembly */ const assert = __nccwpck_require__(98061) -const util = __nccwpck_require__(50011) -const { channels } = __nccwpck_require__(65543) -const timers = __nccwpck_require__(77512) +const util = __nccwpck_require__(39141) +const { channels } = __nccwpck_require__(59241) +const timers = __nccwpck_require__(52372) const { RequestContentLengthMismatchError, ResponseContentLengthMismatchError, @@ -13988,7 +13988,7 @@ const { BodyTimeoutError, HTTPParserError, ResponseExceededMaxSizeError -} = __nccwpck_require__(35990) +} = __nccwpck_require__(5425) const { kUrl, kReset, @@ -14021,9 +14021,9 @@ const { kOnError, kResume, kHTTPContext -} = __nccwpck_require__(13638) +} = __nccwpck_require__(53606) -const constants = __nccwpck_require__(41721) +const constants = __nccwpck_require__(41227) const EMPTY_BUF = Buffer.alloc(0) const FastBuffer = Buffer[Symbol.species] const addListener = util.addListener @@ -14035,11 +14035,11 @@ const kSocketUsed = Symbol('kSocketUsed') let extractBody async function lazyllhttp () { - const llhttpWasmData = process.env.JEST_WORKER_ID ? __nccwpck_require__(46081) : undefined + const llhttpWasmData = process.env.JEST_WORKER_ID ? __nccwpck_require__(72134) : undefined let mod try { - mod = await WebAssembly.compile(__nccwpck_require__(97877)) + mod = await WebAssembly.compile(__nccwpck_require__(81820)) } catch (e) { /* istanbul ignore next */ @@ -14047,7 +14047,7 @@ async function lazyllhttp () { // being enabled, but the occurring of this other error // * https://github.com/emscripten-core/emscripten/issues/11495 // got me to remove that check to avoid breaking Node 12. - mod = await WebAssembly.compile(llhttpWasmData || __nccwpck_require__(46081)) + mod = await WebAssembly.compile(llhttpWasmData || __nccwpck_require__(72134)) } return await WebAssembly.instantiate(mod, { @@ -14963,7 +14963,7 @@ function writeH1 (client, request) { if (util.isFormDataLike(body)) { if (!extractBody) { - extractBody = (__nccwpck_require__(12749).extractBody) + extractBody = (__nccwpck_require__(27134).extractBody) } const [bodyStream, contentType] = extractBody(body) @@ -15476,7 +15476,7 @@ module.exports = connectH1 /***/ }), -/***/ 34879: +/***/ 2003: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -15484,13 +15484,13 @@ module.exports = connectH1 const assert = __nccwpck_require__(98061) const { pipeline } = __nccwpck_require__(84492) -const util = __nccwpck_require__(50011) +const util = __nccwpck_require__(39141) const { RequestContentLengthMismatchError, RequestAbortedError, SocketError, InformationalError -} = __nccwpck_require__(35990) +} = __nccwpck_require__(5425) const { kUrl, kReset, @@ -15509,7 +15509,7 @@ const { kResume, kSize, kHTTPContext -} = __nccwpck_require__(13638) +} = __nccwpck_require__(53606) const kOpenStreams = Symbol('open streams') @@ -15868,7 +15868,7 @@ function writeH2 (client, request) { let contentLength = util.bodyLength(body) if (util.isFormDataLike(body)) { - extractBody ??= (__nccwpck_require__(12749).extractBody) + extractBody ??= (__nccwpck_require__(27134).extractBody) const [bodyStream, contentType] = extractBody(body) headers['content-type'] = contentType @@ -16228,7 +16228,7 @@ module.exports = connectH2 /***/ }), -/***/ 85849: +/***/ 41234: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -16239,16 +16239,16 @@ module.exports = connectH2 const assert = __nccwpck_require__(98061) const net = __nccwpck_require__(87503) const http = __nccwpck_require__(88849) -const util = __nccwpck_require__(50011) -const { channels } = __nccwpck_require__(65543) -const Request = __nccwpck_require__(13484) -const DispatcherBase = __nccwpck_require__(39504) +const util = __nccwpck_require__(39141) +const { channels } = __nccwpck_require__(59241) +const Request = __nccwpck_require__(48356) +const DispatcherBase = __nccwpck_require__(92548) const { InvalidArgumentError, InformationalError, ClientDestroyedError -} = __nccwpck_require__(35990) -const buildConnector = __nccwpck_require__(41429) +} = __nccwpck_require__(5425) +const buildConnector = __nccwpck_require__(28786) const { kUrl, kServerName, @@ -16290,9 +16290,9 @@ const { kHTTPContext, kMaxConcurrentStreams, kResume -} = __nccwpck_require__(13638) -const connectH1 = __nccwpck_require__(14429) -const connectH2 = __nccwpck_require__(34879) +} = __nccwpck_require__(53606) +const connectH1 = __nccwpck_require__(9581) +const connectH2 = __nccwpck_require__(2003) let deprecatedInterceptorWarned = false const kClosedResolve = Symbol('kClosedResolve') @@ -16599,7 +16599,7 @@ class Client extends DispatcherBase { } } -const createRedirectInterceptor = __nccwpck_require__(40928) +const createRedirectInterceptor = __nccwpck_require__(96329) function onError (client, err) { if ( @@ -16859,19 +16859,19 @@ module.exports = Client /***/ }), -/***/ 39504: +/***/ 92548: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const Dispatcher = __nccwpck_require__(66071) +const Dispatcher = __nccwpck_require__(11588) const { ClientDestroyedError, ClientClosedError, InvalidArgumentError -} = __nccwpck_require__(35990) -const { kDestroy, kClose, kClosed, kDestroyed, kDispatch, kInterceptors } = __nccwpck_require__(13638) +} = __nccwpck_require__(5425) +const { kDestroy, kClose, kClosed, kDestroyed, kDispatch, kInterceptors } = __nccwpck_require__(53606) const kOnDestroyed = Symbol('onDestroyed') const kOnClosed = Symbol('onClosed') @@ -17066,7 +17066,7 @@ module.exports = DispatcherBase /***/ }), -/***/ 66071: +/***/ 11588: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -17139,16 +17139,16 @@ module.exports = Dispatcher /***/ }), -/***/ 29941: +/***/ 52673: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const DispatcherBase = __nccwpck_require__(39504) -const { kClose, kDestroy, kClosed, kDestroyed, kDispatch, kNoProxyAgent, kHttpProxyAgent, kHttpsProxyAgent } = __nccwpck_require__(13638) -const ProxyAgent = __nccwpck_require__(87187) -const Agent = __nccwpck_require__(73274) +const DispatcherBase = __nccwpck_require__(92548) +const { kClose, kDestroy, kClosed, kDestroyed, kDispatch, kNoProxyAgent, kHttpProxyAgent, kHttpsProxyAgent } = __nccwpck_require__(53606) +const ProxyAgent = __nccwpck_require__(76930) +const Agent = __nccwpck_require__(78590) const DEFAULT_PORTS = { 'http:': 80, @@ -17307,7 +17307,7 @@ module.exports = EnvHttpProxyAgent /***/ }), -/***/ 27092: +/***/ 79468: /***/ ((module) => { "use strict"; @@ -17432,16 +17432,16 @@ module.exports = class FixedQueue { /***/ }), -/***/ 1467: +/***/ 24458: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const DispatcherBase = __nccwpck_require__(39504) -const FixedQueue = __nccwpck_require__(27092) -const { kConnected, kSize, kRunning, kPending, kQueued, kBusy, kFree, kUrl, kClose, kDestroy, kDispatch } = __nccwpck_require__(13638) -const PoolStats = __nccwpck_require__(48309) +const DispatcherBase = __nccwpck_require__(92548) +const FixedQueue = __nccwpck_require__(79468) +const { kConnected, kSize, kRunning, kPending, kQueued, kBusy, kFree, kUrl, kClose, kDestroy, kDispatch } = __nccwpck_require__(53606) +const PoolStats = __nccwpck_require__(41872) const kClients = Symbol('clients') const kNeedDrain = Symbol('needDrain') @@ -17634,10 +17634,10 @@ module.exports = { /***/ }), -/***/ 48309: +/***/ 41872: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -const { kFree, kConnected, kPending, kQueued, kRunning, kSize } = __nccwpck_require__(13638) +const { kFree, kConnected, kPending, kQueued, kRunning, kSize } = __nccwpck_require__(53606) const kPool = Symbol('pool') class PoolStats { @@ -17675,7 +17675,7 @@ module.exports = PoolStats /***/ }), -/***/ 80229: +/***/ 3639: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -17687,14 +17687,14 @@ const { kNeedDrain, kAddClient, kGetDispatcher -} = __nccwpck_require__(1467) -const Client = __nccwpck_require__(85849) +} = __nccwpck_require__(24458) +const Client = __nccwpck_require__(41234) const { InvalidArgumentError -} = __nccwpck_require__(35990) -const util = __nccwpck_require__(50011) -const { kUrl, kInterceptors } = __nccwpck_require__(13638) -const buildConnector = __nccwpck_require__(41429) +} = __nccwpck_require__(5425) +const util = __nccwpck_require__(39141) +const { kUrl, kInterceptors } = __nccwpck_require__(53606) +const buildConnector = __nccwpck_require__(28786) const kOptions = Symbol('options') const kConnections = Symbol('connections') @@ -17790,20 +17790,20 @@ module.exports = Pool /***/ }), -/***/ 87187: +/***/ 76930: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { kProxy, kClose, kDestroy, kDispatch, kInterceptors } = __nccwpck_require__(13638) +const { kProxy, kClose, kDestroy, kDispatch, kInterceptors } = __nccwpck_require__(53606) const { URL } = __nccwpck_require__(41041) -const Agent = __nccwpck_require__(73274) -const Pool = __nccwpck_require__(80229) -const DispatcherBase = __nccwpck_require__(39504) -const { InvalidArgumentError, RequestAbortedError, SecureProxyConnectionError } = __nccwpck_require__(35990) -const buildConnector = __nccwpck_require__(41429) -const Client = __nccwpck_require__(85849) +const Agent = __nccwpck_require__(78590) +const Pool = __nccwpck_require__(3639) +const DispatcherBase = __nccwpck_require__(92548) +const { InvalidArgumentError, RequestAbortedError, SecureProxyConnectionError } = __nccwpck_require__(5425) +const buildConnector = __nccwpck_require__(28786) +const Client = __nccwpck_require__(41234) const kAgent = Symbol('proxy agent') const kClient = Symbol('proxy client') @@ -18072,14 +18072,14 @@ module.exports = ProxyAgent /***/ }), -/***/ 55184: +/***/ 4420: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const Dispatcher = __nccwpck_require__(66071) -const RetryHandler = __nccwpck_require__(64524) +const Dispatcher = __nccwpck_require__(11588) +const RetryHandler = __nccwpck_require__(30160) class RetryAgent extends Dispatcher { #agent = null @@ -18115,7 +18115,7 @@ module.exports = RetryAgent /***/ }), -/***/ 19405: +/***/ 12621: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -18124,8 +18124,8 @@ module.exports = RetryAgent // We include a version number for the Dispatcher API. In case of breaking changes, // this version number must be increased to avoid conflicts. const globalDispatcher = Symbol.for('undici.globalDispatcher.1') -const { InvalidArgumentError } = __nccwpck_require__(35990) -const Agent = __nccwpck_require__(73274) +const { InvalidArgumentError } = __nccwpck_require__(5425) +const Agent = __nccwpck_require__(78590) if (getGlobalDispatcher() === undefined) { setGlobalDispatcher(new Agent()) @@ -18155,7 +18155,7 @@ module.exports = { /***/ }), -/***/ 41738: +/***/ 63859: /***/ ((module) => { "use strict"; @@ -18207,16 +18207,16 @@ module.exports = class DecoratorHandler { /***/ }), -/***/ 64014: +/***/ 71779: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const util = __nccwpck_require__(50011) -const { kBodyUsed } = __nccwpck_require__(13638) +const util = __nccwpck_require__(39141) +const { kBodyUsed } = __nccwpck_require__(53606) const assert = __nccwpck_require__(98061) -const { InvalidArgumentError } = __nccwpck_require__(35990) +const { InvalidArgumentError } = __nccwpck_require__(5425) const EE = __nccwpck_require__(15673) const redirectableStatusCodes = [300, 301, 302, 303, 307, 308] @@ -18447,21 +18447,21 @@ module.exports = RedirectHandler /***/ }), -/***/ 64524: +/***/ 30160: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; const assert = __nccwpck_require__(98061) -const { kRetryHandlerDefaultRetry } = __nccwpck_require__(13638) -const { RequestRetryError } = __nccwpck_require__(35990) +const { kRetryHandlerDefaultRetry } = __nccwpck_require__(53606) +const { RequestRetryError } = __nccwpck_require__(5425) const { isDisturbed, parseHeaders, parseRangeHeader, wrapRequestBody -} = __nccwpck_require__(50011) +} = __nccwpck_require__(39141) function calculateRetryAfterHeader (retryAfter) { const current = Date.now() @@ -18889,15 +18889,15 @@ module.exports = RetryHandler /***/ }), -/***/ 20346: +/***/ 58033: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; const { isIP } = __nccwpck_require__(87503) const { lookup } = __nccwpck_require__(30604) -const DecoratorHandler = __nccwpck_require__(41738) -const { InvalidArgumentError, InformationalError } = __nccwpck_require__(35990) +const DecoratorHandler = __nccwpck_require__(63859) +const { InvalidArgumentError, InformationalError } = __nccwpck_require__(5425) const maxInt = Math.pow(2, 31) - 1 class DNSInstance { @@ -19272,15 +19272,15 @@ module.exports = interceptorOpts => { /***/ }), -/***/ 12493: +/***/ 13413: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const util = __nccwpck_require__(50011) -const { InvalidArgumentError, RequestAbortedError } = __nccwpck_require__(35990) -const DecoratorHandler = __nccwpck_require__(41738) +const util = __nccwpck_require__(39141) +const { InvalidArgumentError, RequestAbortedError } = __nccwpck_require__(5425) +const DecoratorHandler = __nccwpck_require__(63859) class DumpHandler extends DecoratorHandler { #maxSize = 1024 * 1024 @@ -19403,13 +19403,13 @@ module.exports = createDumpInterceptor /***/ }), -/***/ 40928: +/***/ 96329: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const RedirectHandler = __nccwpck_require__(64014) +const RedirectHandler = __nccwpck_require__(71779) function createRedirectInterceptor ({ maxRedirections: defaultMaxRedirections }) { return (dispatch) => { @@ -19432,12 +19432,12 @@ module.exports = createRedirectInterceptor /***/ }), -/***/ 74872: +/***/ 37849: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const RedirectHandler = __nccwpck_require__(64014) +const RedirectHandler = __nccwpck_require__(71779) module.exports = opts => { const globalMaxRedirections = opts?.maxRedirections @@ -19464,12 +19464,12 @@ module.exports = opts => { /***/ }), -/***/ 79637: +/***/ 19182: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const RetryHandler = __nccwpck_require__(64524) +const RetryHandler = __nccwpck_require__(30160) module.exports = globalOpts => { return dispatch => { @@ -19491,14 +19491,14 @@ module.exports = globalOpts => { /***/ }), -/***/ 41721: +/***/ 41227: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SPECIAL_HEADERS = exports.HEADER_STATE = exports.MINOR = exports.MAJOR = exports.CONNECTION_TOKEN_CHARS = exports.HEADER_CHARS = exports.TOKEN = exports.STRICT_TOKEN = exports.HEX = exports.URL_CHAR = exports.STRICT_URL_CHAR = exports.USERINFO_CHARS = exports.MARK = exports.ALPHANUM = exports.NUM = exports.HEX_MAP = exports.NUM_MAP = exports.ALPHA = exports.FINISH = exports.H_METHOD_MAP = exports.METHOD_MAP = exports.METHODS_RTSP = exports.METHODS_ICE = exports.METHODS_HTTP = exports.METHODS = exports.LENIENT_FLAGS = exports.FLAGS = exports.TYPE = exports.ERROR = void 0; -const utils_1 = __nccwpck_require__(69573); +const utils_1 = __nccwpck_require__(8318); // C headers var ERROR; (function (ERROR) { @@ -19776,7 +19776,7 @@ exports.SPECIAL_HEADERS = { /***/ }), -/***/ 46081: +/***/ 72134: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -19789,7 +19789,7 @@ module.exports = Buffer.from('AGFzbQEAAAABJwdgAX8Bf2ADf39/AX9gAX8AYAJ/fwBgBH9/f3 /***/ }), -/***/ 97877: +/***/ 81820: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -19802,7 +19802,7 @@ module.exports = Buffer.from('AGFzbQEAAAABJwdgAX8Bf2ADf39/AX9gAX8AYAJ/fwBgBH9/f3 /***/ }), -/***/ 69573: +/***/ 8318: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -19824,14 +19824,14 @@ exports.enumToMap = enumToMap; /***/ }), -/***/ 46432: +/***/ 36294: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { kClients } = __nccwpck_require__(13638) -const Agent = __nccwpck_require__(73274) +const { kClients } = __nccwpck_require__(53606) +const Agent = __nccwpck_require__(78590) const { kAgent, kMockAgentSet, @@ -19842,14 +19842,14 @@ const { kGetNetConnect, kOptions, kFactory -} = __nccwpck_require__(63822) -const MockClient = __nccwpck_require__(4227) -const MockPool = __nccwpck_require__(36575) -const { matchValue, buildMockOptions } = __nccwpck_require__(38053) -const { InvalidArgumentError, UndiciError } = __nccwpck_require__(35990) -const Dispatcher = __nccwpck_require__(66071) -const Pluralizer = __nccwpck_require__(97472) -const PendingInterceptorsFormatter = __nccwpck_require__(56155) +} = __nccwpck_require__(53353) +const MockClient = __nccwpck_require__(56607) +const MockPool = __nccwpck_require__(60834) +const { matchValue, buildMockOptions } = __nccwpck_require__(58405) +const { InvalidArgumentError, UndiciError } = __nccwpck_require__(5425) +const Dispatcher = __nccwpck_require__(11588) +const Pluralizer = __nccwpck_require__(90583) +const PendingInterceptorsFormatter = __nccwpck_require__(63535) class MockAgent extends Dispatcher { constructor (opts) { @@ -19992,15 +19992,15 @@ module.exports = MockAgent /***/ }), -/***/ 4227: +/***/ 56607: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; const { promisify } = __nccwpck_require__(47261) -const Client = __nccwpck_require__(85849) -const { buildMockDispatch } = __nccwpck_require__(38053) +const Client = __nccwpck_require__(41234) +const { buildMockDispatch } = __nccwpck_require__(58405) const { kDispatches, kMockAgent, @@ -20009,10 +20009,10 @@ const { kOrigin, kOriginalDispatch, kConnected -} = __nccwpck_require__(63822) -const { MockInterceptor } = __nccwpck_require__(9238) -const Symbols = __nccwpck_require__(13638) -const { InvalidArgumentError } = __nccwpck_require__(35990) +} = __nccwpck_require__(53353) +const { MockInterceptor } = __nccwpck_require__(14791) +const Symbols = __nccwpck_require__(53606) +const { InvalidArgumentError } = __nccwpck_require__(5425) /** * MockClient provides an API that extends the Client to influence the mockDispatches. @@ -20059,13 +20059,13 @@ module.exports = MockClient /***/ }), -/***/ 19329: +/***/ 63739: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { UndiciError } = __nccwpck_require__(35990) +const { UndiciError } = __nccwpck_require__(5425) const kMockNotMatchedError = Symbol.for('undici.error.UND_MOCK_ERR_MOCK_NOT_MATCHED') @@ -20095,13 +20095,13 @@ module.exports = { /***/ }), -/***/ 9238: +/***/ 14791: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { getResponseData, buildKey, addMockDispatch } = __nccwpck_require__(38053) +const { getResponseData, buildKey, addMockDispatch } = __nccwpck_require__(58405) const { kDispatches, kDispatchKey, @@ -20109,9 +20109,9 @@ const { kDefaultTrailers, kContentLength, kMockDispatch -} = __nccwpck_require__(63822) -const { InvalidArgumentError } = __nccwpck_require__(35990) -const { buildURL } = __nccwpck_require__(50011) +} = __nccwpck_require__(53353) +const { InvalidArgumentError } = __nccwpck_require__(5425) +const { buildURL } = __nccwpck_require__(39141) /** * Defines the scope API for an interceptor reply @@ -20310,15 +20310,15 @@ module.exports.MockScope = MockScope /***/ }), -/***/ 36575: +/***/ 60834: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; const { promisify } = __nccwpck_require__(47261) -const Pool = __nccwpck_require__(80229) -const { buildMockDispatch } = __nccwpck_require__(38053) +const Pool = __nccwpck_require__(3639) +const { buildMockDispatch } = __nccwpck_require__(58405) const { kDispatches, kMockAgent, @@ -20327,10 +20327,10 @@ const { kOrigin, kOriginalDispatch, kConnected -} = __nccwpck_require__(63822) -const { MockInterceptor } = __nccwpck_require__(9238) -const Symbols = __nccwpck_require__(13638) -const { InvalidArgumentError } = __nccwpck_require__(35990) +} = __nccwpck_require__(53353) +const { MockInterceptor } = __nccwpck_require__(14791) +const Symbols = __nccwpck_require__(53606) +const { InvalidArgumentError } = __nccwpck_require__(5425) /** * MockPool provides an API that extends the Pool to influence the mockDispatches. @@ -20377,7 +20377,7 @@ module.exports = MockPool /***/ }), -/***/ 63822: +/***/ 53353: /***/ ((module) => { "use strict"; @@ -20408,21 +20408,21 @@ module.exports = { /***/ }), -/***/ 38053: +/***/ 58405: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { MockNotMatchedError } = __nccwpck_require__(19329) +const { MockNotMatchedError } = __nccwpck_require__(63739) const { kDispatches, kMockAgent, kOriginalDispatch, kOrigin, kGetNetConnect -} = __nccwpck_require__(63822) -const { buildURL } = __nccwpck_require__(50011) +} = __nccwpck_require__(53353) +const { buildURL } = __nccwpck_require__(39141) const { STATUS_CODES } = __nccwpck_require__(88849) const { types: { @@ -20783,7 +20783,7 @@ module.exports = { /***/ }), -/***/ 56155: +/***/ 63535: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -20834,7 +20834,7 @@ module.exports = class PendingInterceptorsFormatter { /***/ }), -/***/ 97472: +/***/ 90583: /***/ ((module) => { "use strict"; @@ -20871,7 +20871,7 @@ module.exports = class Pluralizer { /***/ }), -/***/ 77512: +/***/ 52372: /***/ ((module) => { "use strict"; @@ -21302,21 +21302,21 @@ module.exports = { /***/ }), -/***/ 12714: +/***/ 34713: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { kConstruct } = __nccwpck_require__(50591) -const { urlEquals, getFieldValues } = __nccwpck_require__(99205) -const { kEnumerableProperty, isDisturbed } = __nccwpck_require__(50011) -const { webidl } = __nccwpck_require__(2227) -const { Response, cloneResponse, fromInnerResponse } = __nccwpck_require__(51132) -const { Request, fromInnerRequest } = __nccwpck_require__(83211) -const { kState } = __nccwpck_require__(14935) -const { fetching } = __nccwpck_require__(78329) -const { urlIsHttpHttpsScheme, createDeferredPromise, readAllBytes } = __nccwpck_require__(98730) +const { kConstruct } = __nccwpck_require__(93639) +const { urlEquals, getFieldValues } = __nccwpck_require__(52789) +const { kEnumerableProperty, isDisturbed } = __nccwpck_require__(39141) +const { webidl } = __nccwpck_require__(69293) +const { Response, cloneResponse, fromInnerResponse } = __nccwpck_require__(98579) +const { Request, fromInnerRequest } = __nccwpck_require__(3891) +const { kState } = __nccwpck_require__(15575) +const { fetching } = __nccwpck_require__(9526) +const { urlIsHttpHttpsScheme, createDeferredPromise, readAllBytes } = __nccwpck_require__(37458) const assert = __nccwpck_require__(98061) /** @@ -22169,16 +22169,16 @@ module.exports = { /***/ }), -/***/ 11069: +/***/ 10471: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { kConstruct } = __nccwpck_require__(50591) -const { Cache } = __nccwpck_require__(12714) -const { webidl } = __nccwpck_require__(2227) -const { kEnumerableProperty } = __nccwpck_require__(50011) +const { kConstruct } = __nccwpck_require__(93639) +const { Cache } = __nccwpck_require__(34713) +const { webidl } = __nccwpck_require__(69293) +const { kEnumerableProperty } = __nccwpck_require__(39141) class CacheStorage { /** @@ -22329,28 +22329,28 @@ module.exports = { /***/ }), -/***/ 50591: +/***/ 93639: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; module.exports = { - kConstruct: (__nccwpck_require__(13638).kConstruct) + kConstruct: (__nccwpck_require__(53606).kConstruct) } /***/ }), -/***/ 99205: +/***/ 52789: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; const assert = __nccwpck_require__(98061) -const { URLSerializer } = __nccwpck_require__(96730) -const { isValidHeaderName } = __nccwpck_require__(98730) +const { URLSerializer } = __nccwpck_require__(29192) +const { isValidHeaderName } = __nccwpck_require__(37458) /** * @see https://url.spec.whatwg.org/#concept-url-equals @@ -22395,7 +22395,7 @@ module.exports = { /***/ }), -/***/ 16155: +/***/ 40384: /***/ ((module) => { "use strict"; @@ -22415,16 +22415,16 @@ module.exports = { /***/ }), -/***/ 15855: +/***/ 30035: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { parseSetCookie } = __nccwpck_require__(80742) -const { stringify } = __nccwpck_require__(93989) -const { webidl } = __nccwpck_require__(2227) -const { Headers } = __nccwpck_require__(10561) +const { parseSetCookie } = __nccwpck_require__(94017) +const { stringify } = __nccwpck_require__(82559) +const { webidl } = __nccwpck_require__(69293) +const { Headers } = __nccwpck_require__(66089) /** * @typedef {Object} Cookie @@ -22607,15 +22607,15 @@ module.exports = { /***/ }), -/***/ 80742: +/***/ 94017: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { maxNameValuePairSize, maxAttributeValueSize } = __nccwpck_require__(16155) -const { isCTLExcludingHtab } = __nccwpck_require__(93989) -const { collectASequenceOfCodePointsFast } = __nccwpck_require__(96730) +const { maxNameValuePairSize, maxAttributeValueSize } = __nccwpck_require__(40384) +const { isCTLExcludingHtab } = __nccwpck_require__(82559) +const { collectASequenceOfCodePointsFast } = __nccwpck_require__(29192) const assert = __nccwpck_require__(98061) /** @@ -22925,7 +22925,7 @@ module.exports = { /***/ }), -/***/ 93989: +/***/ 82559: /***/ ((module) => { "use strict"; @@ -23285,13 +23285,13 @@ module.exports = { /***/ }), -/***/ 41408: +/***/ 65199: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; const { Transform } = __nccwpck_require__(84492) -const { isASCIINumber, isValidLastEventId } = __nccwpck_require__(19079) +const { isASCIINumber, isValidLastEventId } = __nccwpck_require__(44665) /** * @type {number[]} BOM @@ -23786,23 +23786,23 @@ module.exports = { /***/ }), -/***/ 6731: +/***/ 90109: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; const { pipeline } = __nccwpck_require__(84492) -const { fetching } = __nccwpck_require__(78329) -const { makeRequest } = __nccwpck_require__(83211) -const { webidl } = __nccwpck_require__(2227) -const { EventSourceStream } = __nccwpck_require__(41408) -const { parseMIMEType } = __nccwpck_require__(96730) -const { createFastMessageEvent } = __nccwpck_require__(69459) -const { isNetworkError } = __nccwpck_require__(51132) -const { delay } = __nccwpck_require__(19079) -const { kEnumerableProperty } = __nccwpck_require__(50011) -const { environmentSettingsObject } = __nccwpck_require__(98730) +const { fetching } = __nccwpck_require__(9526) +const { makeRequest } = __nccwpck_require__(3891) +const { webidl } = __nccwpck_require__(69293) +const { EventSourceStream } = __nccwpck_require__(65199) +const { parseMIMEType } = __nccwpck_require__(29192) +const { createFastMessageEvent } = __nccwpck_require__(46055) +const { isNetworkError } = __nccwpck_require__(98579) +const { delay } = __nccwpck_require__(44665) +const { kEnumerableProperty } = __nccwpck_require__(39141) +const { environmentSettingsObject } = __nccwpck_require__(37458) let experimentalWarned = false @@ -24274,7 +24274,7 @@ module.exports = { /***/ }), -/***/ 19079: +/***/ 44665: /***/ ((module) => { "use strict"; @@ -24319,13 +24319,13 @@ module.exports = { /***/ }), -/***/ 12749: +/***/ 27134: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const util = __nccwpck_require__(50011) +const util = __nccwpck_require__(39141) const { ReadableStreamFrom, isBlobLike, @@ -24335,16 +24335,16 @@ const { fullyReadBody, extractMimeType, utf8DecodeBytes -} = __nccwpck_require__(98730) -const { FormData } = __nccwpck_require__(62598) -const { kState } = __nccwpck_require__(14935) -const { webidl } = __nccwpck_require__(2227) +} = __nccwpck_require__(37458) +const { FormData } = __nccwpck_require__(26697) +const { kState } = __nccwpck_require__(15575) +const { webidl } = __nccwpck_require__(69293) const { Blob } = __nccwpck_require__(72254) const assert = __nccwpck_require__(98061) const { isErrored, isDisturbed } = __nccwpck_require__(84492) const { isArrayBuffer } = __nccwpck_require__(93746) -const { serializeAMimeType } = __nccwpck_require__(96730) -const { multipartFormDataParser } = __nccwpck_require__(25152) +const { serializeAMimeType } = __nccwpck_require__(29192) +const { multipartFormDataParser } = __nccwpck_require__(48294) let random try { @@ -24856,7 +24856,7 @@ module.exports = { /***/ }), -/***/ 54823: +/***/ 83686: /***/ ((module) => { "use strict"; @@ -24988,7 +24988,7 @@ module.exports = { /***/ }), -/***/ 96730: +/***/ 29192: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -25740,13 +25740,13 @@ module.exports = { /***/ }), -/***/ 61451: +/***/ 58330: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { kConnected, kSize } = __nccwpck_require__(13638) +const { kConnected, kSize } = __nccwpck_require__(53606) class CompatWeakRef { constructor (value) { @@ -25794,15 +25794,15 @@ module.exports = function () { /***/ }), -/***/ 60027: +/***/ 37170: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; const { Blob, File } = __nccwpck_require__(72254) -const { kState } = __nccwpck_require__(14935) -const { webidl } = __nccwpck_require__(2227) +const { kState } = __nccwpck_require__(15575) +const { webidl } = __nccwpck_require__(69293) // TODO(@KhafraDev): remove class FileLike { @@ -25928,17 +25928,17 @@ module.exports = { FileLike, isFileLike } /***/ }), -/***/ 25152: +/***/ 48294: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { isUSVString, bufferToLowerCasedHeaderName } = __nccwpck_require__(50011) -const { utf8DecodeBytes } = __nccwpck_require__(98730) -const { HTTP_TOKEN_CODEPOINTS, isomorphicDecode } = __nccwpck_require__(96730) -const { isFileLike } = __nccwpck_require__(60027) -const { makeEntry } = __nccwpck_require__(62598) +const { isUSVString, bufferToLowerCasedHeaderName } = __nccwpck_require__(39141) +const { utf8DecodeBytes } = __nccwpck_require__(37458) +const { HTTP_TOKEN_CODEPOINTS, isomorphicDecode } = __nccwpck_require__(29192) +const { isFileLike } = __nccwpck_require__(37170) +const { makeEntry } = __nccwpck_require__(26697) const assert = __nccwpck_require__(98061) const { File: NodeFile } = __nccwpck_require__(72254) @@ -26410,17 +26410,17 @@ module.exports = { /***/ }), -/***/ 62598: +/***/ 26697: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { isBlobLike, iteratorMixin } = __nccwpck_require__(98730) -const { kState } = __nccwpck_require__(14935) -const { kEnumerableProperty } = __nccwpck_require__(50011) -const { FileLike, isFileLike } = __nccwpck_require__(60027) -const { webidl } = __nccwpck_require__(2227) +const { isBlobLike, iteratorMixin } = __nccwpck_require__(37458) +const { kState } = __nccwpck_require__(15575) +const { kEnumerableProperty } = __nccwpck_require__(39141) +const { FileLike, isFileLike } = __nccwpck_require__(37170) +const { webidl } = __nccwpck_require__(69293) const { File: NativeFile } = __nccwpck_require__(72254) const nodeUtil = __nccwpck_require__(47261) @@ -26670,7 +26670,7 @@ module.exports = { FormData, makeEntry } /***/ }), -/***/ 13924: +/***/ 82470: /***/ ((module) => { "use strict"; @@ -26718,7 +26718,7 @@ module.exports = { /***/ }), -/***/ 10561: +/***/ 66089: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -26726,14 +26726,14 @@ module.exports = { -const { kConstruct } = __nccwpck_require__(13638) -const { kEnumerableProperty } = __nccwpck_require__(50011) +const { kConstruct } = __nccwpck_require__(53606) +const { kEnumerableProperty } = __nccwpck_require__(39141) const { iteratorMixin, isValidHeaderName, isValidHeaderValue -} = __nccwpck_require__(98730) -const { webidl } = __nccwpck_require__(2227) +} = __nccwpck_require__(37458) +const { webidl } = __nccwpck_require__(69293) const assert = __nccwpck_require__(98061) const util = __nccwpck_require__(47261) @@ -27413,7 +27413,7 @@ module.exports = { /***/ }), -/***/ 78329: +/***/ 9526: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -27427,9 +27427,9 @@ const { filterResponse, makeResponse, fromInnerResponse -} = __nccwpck_require__(51132) -const { HeadersList } = __nccwpck_require__(10561) -const { Request, cloneRequest } = __nccwpck_require__(83211) +} = __nccwpck_require__(98579) +const { HeadersList } = __nccwpck_require__(66089) +const { Request, cloneRequest } = __nccwpck_require__(3891) const zlib = __nccwpck_require__(65628) const { bytesMatch, @@ -27465,23 +27465,23 @@ const { buildContentRange, createInflate, extractMimeType -} = __nccwpck_require__(98730) -const { kState, kDispatcher } = __nccwpck_require__(14935) +} = __nccwpck_require__(37458) +const { kState, kDispatcher } = __nccwpck_require__(15575) const assert = __nccwpck_require__(98061) -const { safelyExtractBody, extractBody } = __nccwpck_require__(12749) +const { safelyExtractBody, extractBody } = __nccwpck_require__(27134) const { redirectStatusSet, nullBodyStatus, safeMethodsSet, requestBodyHeader, subresourceSet -} = __nccwpck_require__(54823) +} = __nccwpck_require__(83686) const EE = __nccwpck_require__(15673) const { Readable, pipeline, finished } = __nccwpck_require__(84492) -const { addAbortListener, isErrored, isReadable, bufferToLowerCasedHeaderName } = __nccwpck_require__(50011) -const { dataURLProcessor, serializeAMimeType, minimizeSupportedMimeType } = __nccwpck_require__(96730) -const { getGlobalDispatcher } = __nccwpck_require__(19405) -const { webidl } = __nccwpck_require__(2227) +const { addAbortListener, isErrored, isReadable, bufferToLowerCasedHeaderName } = __nccwpck_require__(39141) +const { dataURLProcessor, serializeAMimeType, minimizeSupportedMimeType } = __nccwpck_require__(29192) +const { getGlobalDispatcher } = __nccwpck_require__(12621) +const { webidl } = __nccwpck_require__(69293) const { STATUS_CODES } = __nccwpck_require__(88849) const GET_OR_HEAD = ['GET', 'HEAD'] @@ -29693,7 +29693,7 @@ module.exports = { /***/ }), -/***/ 83211: +/***/ 3891: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -29701,16 +29701,16 @@ module.exports = { -const { extractBody, mixinBody, cloneBody, bodyUnusable } = __nccwpck_require__(12749) -const { Headers, fill: fillHeaders, HeadersList, setHeadersGuard, getHeadersGuard, setHeadersList, getHeadersList } = __nccwpck_require__(10561) -const { FinalizationRegistry } = __nccwpck_require__(61451)() -const util = __nccwpck_require__(50011) +const { extractBody, mixinBody, cloneBody, bodyUnusable } = __nccwpck_require__(27134) +const { Headers, fill: fillHeaders, HeadersList, setHeadersGuard, getHeadersGuard, setHeadersList, getHeadersList } = __nccwpck_require__(66089) +const { FinalizationRegistry } = __nccwpck_require__(58330)() +const util = __nccwpck_require__(39141) const nodeUtil = __nccwpck_require__(47261) const { isValidHTTPToken, sameOrigin, environmentSettingsObject -} = __nccwpck_require__(98730) +} = __nccwpck_require__(37458) const { forbiddenMethodsSet, corsSafeListedMethodsSet, @@ -29720,12 +29720,12 @@ const { requestCredentials, requestCache, requestDuplex -} = __nccwpck_require__(54823) +} = __nccwpck_require__(83686) const { kEnumerableProperty, normalizedMethodRecordsBase, normalizedMethodRecords } = util -const { kHeaders, kSignal, kState, kDispatcher } = __nccwpck_require__(14935) -const { webidl } = __nccwpck_require__(2227) -const { URLSerializer } = __nccwpck_require__(96730) -const { kConstruct } = __nccwpck_require__(13638) +const { kHeaders, kSignal, kState, kDispatcher } = __nccwpck_require__(15575) +const { webidl } = __nccwpck_require__(69293) +const { URLSerializer } = __nccwpck_require__(29192) +const { kConstruct } = __nccwpck_require__(53606) const assert = __nccwpck_require__(98061) const { getMaxListeners, setMaxListeners, getEventListeners, defaultMaxListeners } = __nccwpck_require__(15673) @@ -30738,15 +30738,15 @@ module.exports = { Request, makeRequest, fromInnerRequest, cloneRequest } /***/ }), -/***/ 51132: +/***/ 98579: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { Headers, HeadersList, fill, getHeadersGuard, setHeadersGuard, setHeadersList } = __nccwpck_require__(10561) -const { extractBody, cloneBody, mixinBody, hasFinalizationRegistry, streamRegistry, bodyUnusable } = __nccwpck_require__(12749) -const util = __nccwpck_require__(50011) +const { Headers, HeadersList, fill, getHeadersGuard, setHeadersGuard, setHeadersList } = __nccwpck_require__(66089) +const { extractBody, cloneBody, mixinBody, hasFinalizationRegistry, streamRegistry, bodyUnusable } = __nccwpck_require__(27134) +const util = __nccwpck_require__(39141) const nodeUtil = __nccwpck_require__(47261) const { kEnumerableProperty } = util const { @@ -30758,16 +30758,16 @@ const { isErrorLike, isomorphicEncode, environmentSettingsObject: relevantRealm -} = __nccwpck_require__(98730) +} = __nccwpck_require__(37458) const { redirectStatusSet, nullBodyStatus -} = __nccwpck_require__(54823) -const { kState, kHeaders } = __nccwpck_require__(14935) -const { webidl } = __nccwpck_require__(2227) -const { FormData } = __nccwpck_require__(62598) -const { URLSerializer } = __nccwpck_require__(96730) -const { kConstruct } = __nccwpck_require__(13638) +} = __nccwpck_require__(83686) +const { kState, kHeaders } = __nccwpck_require__(15575) +const { webidl } = __nccwpck_require__(69293) +const { FormData } = __nccwpck_require__(26697) +const { URLSerializer } = __nccwpck_require__(29192) +const { kConstruct } = __nccwpck_require__(53606) const assert = __nccwpck_require__(98061) const { types } = __nccwpck_require__(47261) @@ -31356,7 +31356,7 @@ module.exports = { /***/ }), -/***/ 14935: +/***/ 15575: /***/ ((module) => { "use strict"; @@ -31373,7 +31373,7 @@ module.exports = { /***/ }), -/***/ 98730: +/***/ 37458: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -31381,14 +31381,14 @@ module.exports = { const { Transform } = __nccwpck_require__(84492) const zlib = __nccwpck_require__(65628) -const { redirectStatusSet, referrerPolicySet: referrerPolicyTokens, badPortsSet } = __nccwpck_require__(54823) -const { getGlobalOrigin } = __nccwpck_require__(13924) -const { collectASequenceOfCodePoints, collectAnHTTPQuotedString, removeChars, parseMIMEType } = __nccwpck_require__(96730) +const { redirectStatusSet, referrerPolicySet: referrerPolicyTokens, badPortsSet } = __nccwpck_require__(83686) +const { getGlobalOrigin } = __nccwpck_require__(82470) +const { collectASequenceOfCodePoints, collectAnHTTPQuotedString, removeChars, parseMIMEType } = __nccwpck_require__(29192) const { performance } = __nccwpck_require__(38846) -const { isBlobLike, ReadableStreamFrom, isValidHTTPToken, normalizedMethodRecordsBase } = __nccwpck_require__(50011) +const { isBlobLike, ReadableStreamFrom, isValidHTTPToken, normalizedMethodRecordsBase } = __nccwpck_require__(39141) const assert = __nccwpck_require__(98061) const { isUint8Array } = __nccwpck_require__(93746) -const { webidl } = __nccwpck_require__(2227) +const { webidl } = __nccwpck_require__(69293) let supportedHashes = [] @@ -33013,7 +33013,7 @@ module.exports = { /***/ }), -/***/ 2227: +/***/ 69293: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -33021,7 +33021,7 @@ module.exports = { const { types, inspect } = __nccwpck_require__(47261) const { markAsUncloneable } = __nccwpck_require__(24086) -const { toUSVString } = __nccwpck_require__(50011) +const { toUSVString } = __nccwpck_require__(39141) /** @type {import('../../../types/webidl').Webidl} */ const webidl = {} @@ -33716,7 +33716,7 @@ module.exports = { /***/ }), -/***/ 74973: +/***/ 24696: /***/ ((module) => { "use strict"; @@ -34014,7 +34014,7 @@ module.exports = { /***/ }), -/***/ 65153: +/***/ 73002: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -34024,16 +34024,16 @@ const { staticPropertyDescriptors, readOperation, fireAProgressEvent -} = __nccwpck_require__(24277) +} = __nccwpck_require__(76274) const { kState, kError, kResult, kEvents, kAborted -} = __nccwpck_require__(30010) -const { webidl } = __nccwpck_require__(2227) -const { kEnumerableProperty } = __nccwpck_require__(50011) +} = __nccwpck_require__(1673) +const { webidl } = __nccwpck_require__(69293) +const { kEnumerableProperty } = __nccwpck_require__(39141) class FileReader extends EventTarget { constructor () { @@ -34366,13 +34366,13 @@ module.exports = { /***/ }), -/***/ 53788: +/***/ 37663: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { webidl } = __nccwpck_require__(2227) +const { webidl } = __nccwpck_require__(69293) const kState = Symbol('ProgressEvent state') @@ -34452,7 +34452,7 @@ module.exports = { /***/ }), -/***/ 30010: +/***/ 1673: /***/ ((module) => { "use strict"; @@ -34470,7 +34470,7 @@ module.exports = { /***/ }), -/***/ 24277: +/***/ 76274: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -34482,10 +34482,10 @@ const { kResult, kAborted, kLastProgressEventFired -} = __nccwpck_require__(30010) -const { ProgressEvent } = __nccwpck_require__(53788) -const { getEncoding } = __nccwpck_require__(74973) -const { serializeAMimeType, parseMIMEType } = __nccwpck_require__(96730) +} = __nccwpck_require__(1673) +const { ProgressEvent } = __nccwpck_require__(37663) +const { getEncoding } = __nccwpck_require__(24696) +const { serializeAMimeType, parseMIMEType } = __nccwpck_require__(29192) const { types } = __nccwpck_require__(47261) const { StringDecoder } = __nccwpck_require__(71576) const { btoa } = __nccwpck_require__(72254) @@ -34869,28 +34869,28 @@ module.exports = { /***/ }), -/***/ 17299: +/***/ 72007: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { uid, states, sentCloseFrameState, emptyBuffer, opcodes } = __nccwpck_require__(44285) +const { uid, states, sentCloseFrameState, emptyBuffer, opcodes } = __nccwpck_require__(26566) const { kReadyState, kSentClose, kByteParser, kReceivedClose, kResponse -} = __nccwpck_require__(34939) -const { fireEvent, failWebsocketConnection, isClosing, isClosed, isEstablished, parseExtensions } = __nccwpck_require__(93194) -const { channels } = __nccwpck_require__(65543) -const { CloseEvent } = __nccwpck_require__(69459) -const { makeRequest } = __nccwpck_require__(83211) -const { fetching } = __nccwpck_require__(78329) -const { Headers, getHeadersList } = __nccwpck_require__(10561) -const { getDecodeSplit } = __nccwpck_require__(98730) -const { WebsocketFrameSend } = __nccwpck_require__(84618) +} = __nccwpck_require__(6608) +const { fireEvent, failWebsocketConnection, isClosing, isClosed, isEstablished, parseExtensions } = __nccwpck_require__(22887) +const { channels } = __nccwpck_require__(59241) +const { CloseEvent } = __nccwpck_require__(46055) +const { makeRequest } = __nccwpck_require__(3891) +const { fetching } = __nccwpck_require__(9526) +const { Headers, getHeadersList } = __nccwpck_require__(66089) +const { getDecodeSplit } = __nccwpck_require__(37458) +const { WebsocketFrameSend } = __nccwpck_require__(56248) /** @type {import('crypto')} */ let crypto @@ -35248,7 +35248,7 @@ module.exports = { /***/ }), -/***/ 44285: +/***/ 26566: /***/ ((module) => { "use strict"; @@ -35322,15 +35322,15 @@ module.exports = { /***/ }), -/***/ 69459: +/***/ 46055: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { webidl } = __nccwpck_require__(2227) -const { kEnumerableProperty } = __nccwpck_require__(50011) -const { kConstruct } = __nccwpck_require__(13638) +const { webidl } = __nccwpck_require__(69293) +const { kEnumerableProperty } = __nccwpck_require__(39141) +const { kConstruct } = __nccwpck_require__(53606) const { MessagePort } = __nccwpck_require__(24086) /** @@ -35659,13 +35659,13 @@ module.exports = { /***/ }), -/***/ 84618: +/***/ 56248: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { maxUnsigned16Bit } = __nccwpck_require__(44285) +const { maxUnsigned16Bit } = __nccwpck_require__(26566) const BUFFER_SIZE = 16386 @@ -35763,15 +35763,15 @@ module.exports = { /***/ }), -/***/ 7133: +/***/ 77885: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; const { createInflateRaw, Z_DEFAULT_WINDOWBITS } = __nccwpck_require__(65628) -const { isValidClientWindowBits } = __nccwpck_require__(93194) -const { MessageSizeExceededError } = __nccwpck_require__(35990) +const { isValidClientWindowBits } = __nccwpck_require__(22887) +const { MessageSizeExceededError } = __nccwpck_require__(5425) const tail = Buffer.from([0x00, 0x00, 0xff, 0xff]) const kBuffer = Symbol('kBuffer') @@ -35876,7 +35876,7 @@ module.exports = { PerMessageDeflate } /***/ }), -/***/ 46080: +/***/ 48756: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -35884,9 +35884,9 @@ module.exports = { PerMessageDeflate } const { Writable } = __nccwpck_require__(84492) const assert = __nccwpck_require__(98061) -const { parserStates, opcodes, states, emptyBuffer, sentCloseFrameState } = __nccwpck_require__(44285) -const { kReadyState, kSentClose, kResponse, kReceivedClose } = __nccwpck_require__(34939) -const { channels } = __nccwpck_require__(65543) +const { parserStates, opcodes, states, emptyBuffer, sentCloseFrameState } = __nccwpck_require__(26566) +const { kReadyState, kSentClose, kResponse, kReceivedClose } = __nccwpck_require__(6608) +const { channels } = __nccwpck_require__(59241) const { isValidStatusCode, isValidOpcode, @@ -35896,11 +35896,11 @@ const { isControlFrame, isTextBinaryFrame, isContinuationFrame -} = __nccwpck_require__(93194) -const { WebsocketFrameSend } = __nccwpck_require__(84618) -const { closeWebSocketConnection } = __nccwpck_require__(17299) -const { PerMessageDeflate } = __nccwpck_require__(7133) -const { MessageSizeExceededError } = __nccwpck_require__(35990) +} = __nccwpck_require__(22887) +const { WebsocketFrameSend } = __nccwpck_require__(56248) +const { closeWebSocketConnection } = __nccwpck_require__(72007) +const { PerMessageDeflate } = __nccwpck_require__(77885) +const { MessageSizeExceededError } = __nccwpck_require__(5425) function failWebsocketConnectionWithCode (ws, code, reason) { closeWebSocketConnection(ws, code, reason, Buffer.byteLength(reason)) @@ -36397,15 +36397,15 @@ module.exports = { /***/ }), -/***/ 26515: +/***/ 10774: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { WebsocketFrameSend } = __nccwpck_require__(84618) -const { opcodes, sendHints } = __nccwpck_require__(44285) -const FixedQueue = __nccwpck_require__(27092) +const { WebsocketFrameSend } = __nccwpck_require__(56248) +const { opcodes, sendHints } = __nccwpck_require__(26566) +const FixedQueue = __nccwpck_require__(79468) /** @type {typeof Uint8Array} */ const FastBuffer = Buffer[Symbol.species] @@ -36509,7 +36509,7 @@ module.exports = { SendQueue } /***/ }), -/***/ 34939: +/***/ 6608: /***/ ((module) => { "use strict"; @@ -36529,17 +36529,17 @@ module.exports = { /***/ }), -/***/ 93194: +/***/ 22887: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { kReadyState, kController, kResponse, kBinaryType, kWebSocketURL } = __nccwpck_require__(34939) -const { states, opcodes } = __nccwpck_require__(44285) -const { ErrorEvent, createFastMessageEvent } = __nccwpck_require__(69459) +const { kReadyState, kController, kResponse, kBinaryType, kWebSocketURL } = __nccwpck_require__(6608) +const { states, opcodes } = __nccwpck_require__(26566) +const { ErrorEvent, createFastMessageEvent } = __nccwpck_require__(46055) const { isUtf8 } = __nccwpck_require__(72254) -const { collectASequenceOfCodePointsFast, removeHTTPWhitespace } = __nccwpck_require__(96730) +const { collectASequenceOfCodePointsFast, removeHTTPWhitespace } = __nccwpck_require__(29192) /* globals Blob */ @@ -36859,16 +36859,16 @@ module.exports = { /***/ }), -/***/ 16416: +/***/ 1468: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { webidl } = __nccwpck_require__(2227) -const { URLSerializer } = __nccwpck_require__(96730) -const { environmentSettingsObject } = __nccwpck_require__(98730) -const { staticPropertyDescriptors, states, sentCloseFrameState, sendHints } = __nccwpck_require__(44285) +const { webidl } = __nccwpck_require__(69293) +const { URLSerializer } = __nccwpck_require__(29192) +const { environmentSettingsObject } = __nccwpck_require__(37458) +const { staticPropertyDescriptors, states, sentCloseFrameState, sendHints } = __nccwpck_require__(26566) const { kWebSocketURL, kReadyState, @@ -36877,21 +36877,21 @@ const { kResponse, kSentClose, kByteParser -} = __nccwpck_require__(34939) +} = __nccwpck_require__(6608) const { isConnecting, isEstablished, isClosing, isValidSubprotocol, fireEvent -} = __nccwpck_require__(93194) -const { establishWebSocketConnection, closeWebSocketConnection } = __nccwpck_require__(17299) -const { ByteParser } = __nccwpck_require__(46080) -const { kEnumerableProperty, isBlobLike } = __nccwpck_require__(50011) -const { getGlobalDispatcher } = __nccwpck_require__(19405) +} = __nccwpck_require__(22887) +const { establishWebSocketConnection, closeWebSocketConnection } = __nccwpck_require__(72007) +const { ByteParser } = __nccwpck_require__(48756) +const { kEnumerableProperty, isBlobLike } = __nccwpck_require__(39141) +const { getGlobalDispatcher } = __nccwpck_require__(12621) const { types } = __nccwpck_require__(47261) -const { ErrorEvent, CloseEvent } = __nccwpck_require__(69459) -const { SendQueue } = __nccwpck_require__(26515) +const { ErrorEvent, CloseEvent } = __nccwpck_require__(46055) +const { SendQueue } = __nccwpck_require__(10774) // https://websockets.spec.whatwg.org/#interface-definition class WebSocket extends EventTarget { @@ -37462,7 +37462,7 @@ module.exports = { /***/ }), -/***/ 98143: +/***/ 58486: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -37489,14 +37489,14 @@ function resolveActionInput(additionalParams, actionInputs, key) { /***/ }), -/***/ 81248: +/***/ 43128: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildAgentTasks = buildAgentTasks; -const agent_configuration_input_policy_1 = __nccwpck_require__(7699); +const agent_configuration_input_policy_1 = __nccwpck_require__(60436); /** Builds the validated findings/fixer pair used by both action lifecycles. */ function buildAgentTasks(values, environment = process.env) { return (0, agent_configuration_input_policy_1.buildAgentTaskConfiguration)(values, environment); @@ -37505,7 +37505,7 @@ function buildAgentTasks(values, environment = process.env) { /***/ }), -/***/ 71404: +/***/ 65289: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -37513,9 +37513,9 @@ function buildAgentTasks(values, environment = process.env) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildAgentTasksFromInputs = buildAgentTasksFromInputs; exports.buildAgentTasksFromValues = buildAgentTasksFromValues; -const input_keys_1 = __nccwpck_require__(88539); -const agent_configuration_builder_1 = __nccwpck_require__(81248); -const agent_1 = __nccwpck_require__(89040); +const input_keys_1 = __nccwpck_require__(83725); +const agent_configuration_builder_1 = __nccwpck_require__(43128); +const agent_1 = __nccwpck_require__(95407); function buildAgentTasksFromInputs(read) { const provider = read(input_keys_1.INPUT_KEYS.AGENT_PROVIDER)?.trim() || agent_1.DEFAULT_AGENT_PROVIDER; const modelProvider = read(input_keys_1.INPUT_KEYS.AGENT_MODEL_PROVIDER)?.trim() @@ -37565,14 +37565,14 @@ function buildAgentTasksFromValues(values) { /***/ }), -/***/ 30085: +/***/ 45364: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildBranches = buildBranches; -const branches_1 = __nccwpck_require__(29506); +const branches_1 = __nccwpck_require__(94871); function buildBranches(values) { return new branches_1.Branches(values.main, values.defaultBranch, values.development, values.featureTree, values.bugfixTree, values.hotfixTree, values.releaseTree, values.docsTree, values.choreTree); } @@ -37580,29 +37580,29 @@ function buildBranches(values) { /***/ }), -/***/ 42238: +/***/ 23134: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.mainRun = mainRun; -const result_1 = __nccwpck_require__(73817); -const logger_1 = __nccwpck_require__(91151); -const main_run_route_1 = __nccwpck_require__(8466); -const execution_setup_composition_root_1 = __nccwpck_require__(83965); -const setup_execution_boundary_1 = __nccwpck_require__(45805); -const main_run_route_composition_root_1 = __nccwpck_require__(4706); -const repository_context_1 = __nccwpck_require__(78958); -const logging_ports_1 = __nccwpck_require__(6152); -const logger_adapter_1 = __nccwpck_require__(72762); -const lifecycle_synchronization_context_1 = __nccwpck_require__(28121); -const agent_activity_policy_1 = __nccwpck_require__(15375); -const push_single_action_contexts_1 = __nccwpck_require__(47841); -const main_run_lifecycle_1 = __nccwpck_require__(916); -const issue_workflow_runtime_policy_1 = __nccwpck_require__(77734); -const application_error_1 = __nccwpck_require__(75999); -const issue_start_policy_1 = __nccwpck_require__(90332); +const result_1 = __nccwpck_require__(61444); +const logger_1 = __nccwpck_require__(50135); +const main_run_route_1 = __nccwpck_require__(78693); +const execution_setup_composition_root_1 = __nccwpck_require__(71774); +const setup_execution_boundary_1 = __nccwpck_require__(56062); +const main_run_route_composition_root_1 = __nccwpck_require__(7473); +const repository_context_1 = __nccwpck_require__(57421); +const logging_ports_1 = __nccwpck_require__(73001); +const logger_adapter_1 = __nccwpck_require__(56932); +const lifecycle_synchronization_context_1 = __nccwpck_require__(724); +const agent_activity_policy_1 = __nccwpck_require__(69527); +const push_single_action_contexts_1 = __nccwpck_require__(87805); +const main_run_lifecycle_1 = __nccwpck_require__(70730); +const issue_workflow_runtime_policy_1 = __nccwpck_require__(84598); +const application_error_1 = __nccwpck_require__(2965); +const issue_start_policy_1 = __nccwpck_require__(20953); async function mainRun(execution, projectBoardCommandPort, latestTagQueryPort, compositionSurface, lifecycleStateUseCase, agentActivityUseCase, prepareRuntime) { (0, logging_ports_1.configureApplicationLogger)((0, logger_adapter_1.createLoggerAdapter)()); (0, logging_ports_1.setGlobalLoggerDebug)(execution.debug, execution.inputs === undefined); @@ -37754,7 +37754,7 @@ function applyAgentActivityOutcome(execution, outcome) { /***/ }), -/***/ 19094: +/***/ 28645: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -37769,15 +37769,15 @@ exports.buildEmoji = buildEmoji; exports.buildTokens = buildTokens; exports.buildLabels = buildLabels; exports.buildIssueTypes = buildIssueTypes; -const emoji_1 = __nccwpck_require__(24146); -const issue_1 = __nccwpck_require__(46760); -const issue_types_1 = __nccwpck_require__(27357); -const labels_1 = __nccwpck_require__(79463); -const locale_1 = __nccwpck_require__(9832); -const pull_request_1 = __nccwpck_require__(55713); -const projects_1 = __nccwpck_require__(13231); -const tokens_1 = __nccwpck_require__(44153); -const workflows_1 = __nccwpck_require__(45790); +const emoji_1 = __nccwpck_require__(24750); +const issue_1 = __nccwpck_require__(2224); +const issue_types_1 = __nccwpck_require__(67189); +const labels_1 = __nccwpck_require__(54505); +const locale_1 = __nccwpck_require__(31159); +const pull_request_1 = __nccwpck_require__(28347); +const projects_1 = __nccwpck_require__(95230); +const tokens_1 = __nccwpck_require__(47356); +const workflows_1 = __nccwpck_require__(70513); function buildProjects(values) { return new projects_1.Projects(values.projects, values.issueCreated, values.pullRequestCreated, values.issueInProgress, values.pullRequestInProgress); } @@ -37809,17 +37809,17 @@ function buildIssueTypes(values) { /***/ }), -/***/ 30098: +/***/ 15672: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.readDeploymentConfiguration = readDeploymentConfiguration; -const application_error_1 = __nccwpck_require__(75999); -const deployment_configuration_1 = __nccwpck_require__(22495); -const input_keys_1 = __nccwpck_require__(88539); -const merge_queue_readiness_1 = __nccwpck_require__(12515); +const application_error_1 = __nccwpck_require__(2965); +const deployment_configuration_1 = __nccwpck_require__(5664); +const input_keys_1 = __nccwpck_require__(83725); +const merge_queue_readiness_1 = __nccwpck_require__(36637); function readDeploymentConfiguration(getInput, branches) { const errors = []; const readEnum = (key, allowed, fallback) => { @@ -37872,14 +37872,14 @@ function readBoolean(value, fallback, name, errors) { /***/ }), -/***/ 20236: +/***/ 98884: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildExecution = buildExecution; -const execution_1 = __nccwpck_require__(31546); +const execution_1 = __nccwpck_require__(99925); function buildExecution(components) { return new execution_1.Execution(components); } @@ -37887,7 +37887,7 @@ function buildExecution(components) { /***/ }), -/***/ 18330: +/***/ 89280: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -37912,7 +37912,7 @@ function parseIssueWorkflowBoolean(value, inputName, defaultValue) { /***/ }), -/***/ 47165: +/***/ 85160: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -37950,7 +37950,7 @@ function parseStrictInteger(value) { /***/ }), -/***/ 68841: +/***/ 39364: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -37967,24 +37967,24 @@ function parseDelimitedValues(value) { /***/ }), -/***/ 76102: +/***/ 70609: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runLocalAction = runLocalAction; -const local_action_composition_root_1 = __nccwpck_require__(34760); -const common_action_1 = __nccwpck_require__(42238); -const local_action_output_1 = __nccwpck_require__(94290); -const local_action_configuration_1 = __nccwpck_require__(66645); -const local_action_execution_1 = __nccwpck_require__(47047); -const repository_context_1 = __nccwpck_require__(78958); -const agent_activity_composition_root_1 = __nccwpck_require__(94253); -const application_error_context_1 = __nccwpck_require__(4034); -const input_keys_1 = __nccwpck_require__(88539); -const local_single_action_policy_1 = __nccwpck_require__(99190); -const publication_message_catalog_1 = __nccwpck_require__(34223); +const local_action_composition_root_1 = __nccwpck_require__(36426); +const common_action_1 = __nccwpck_require__(23134); +const local_action_output_1 = __nccwpck_require__(91598); +const local_action_configuration_1 = __nccwpck_require__(89324); +const local_action_execution_1 = __nccwpck_require__(13196); +const repository_context_1 = __nccwpck_require__(57421); +const agent_activity_composition_root_1 = __nccwpck_require__(11013); +const application_error_context_1 = __nccwpck_require__(15491); +const input_keys_1 = __nccwpck_require__(83725); +const local_single_action_policy_1 = __nccwpck_require__(87929); +const publication_message_catalog_1 = __nccwpck_require__(46042); async function runLocalAction(additionalParams, options = {}) { return (0, application_error_context_1.runAtApplicationErrorBoundary)(async () => { const requestedAction = additionalParams[input_keys_1.INPUT_KEYS.SINGLE_ACTION]; @@ -38010,15 +38010,15 @@ async function runLocalAction(additionalParams, options = {}) { /***/ }), -/***/ 66645: +/***/ 89324: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildLocalActionConfiguration = buildLocalActionConfiguration; -const yml_utils_1 = __nccwpck_require__(61788); -const local_action_configuration_sections_1 = __nccwpck_require__(27946); +const yml_utils_1 = __nccwpck_require__(61047); +const local_action_configuration_sections_1 = __nccwpck_require__(52511); async function buildLocalActionConfiguration(additionalParams, projectRepository) { const actionInputs = (0, yml_utils_1.getActionInputsWithDefaults)(); const core = (0, local_action_configuration_sections_1.readLocalCoreConfiguration)(additionalParams, actionInputs); @@ -38039,7 +38039,7 @@ async function buildLocalActionConfiguration(additionalParams, projectRepository /***/ }), -/***/ 27946: +/***/ 52511: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -38050,19 +38050,19 @@ exports.readLocalAgentConfiguration = readLocalAgentConfiguration; exports.readLocalProjectConfiguration = readLocalProjectConfiguration; exports.readLocalLabelsAndIssueTypes = readLocalLabelsAndIssueTypes; exports.readLocalWorkflowConfiguration = readLocalWorkflowConfiguration; -const locale_1 = __nccwpck_require__(9832); -const bugbot_constants_1 = __nccwpck_require__(51389); -const input_keys_1 = __nccwpck_require__(88539); -const input_boolean_policy_1 = __nccwpck_require__(18330); -const action_input_source_1 = __nccwpck_require__(98143); -const project_details_loader_1 = __nccwpck_require__(73448); -const input_number_policy_1 = __nccwpck_require__(47165); -const input_values_policy_1 = __nccwpck_require__(68841); -const agent_input_builder_1 = __nccwpck_require__(71404); -const pull_request_description_1 = __nccwpck_require__(45315); -const issue_inactivity_1 = __nccwpck_require__(38572); -const review_configuration_1 = __nccwpck_require__(3994); -const deployment_configuration_builder_1 = __nccwpck_require__(30098); +const locale_1 = __nccwpck_require__(31159); +const bugbot_constants_1 = __nccwpck_require__(16868); +const input_keys_1 = __nccwpck_require__(83725); +const input_boolean_policy_1 = __nccwpck_require__(89280); +const action_input_source_1 = __nccwpck_require__(58486); +const project_details_loader_1 = __nccwpck_require__(60181); +const input_number_policy_1 = __nccwpck_require__(85160); +const input_values_policy_1 = __nccwpck_require__(39364); +const agent_input_builder_1 = __nccwpck_require__(65289); +const pull_request_description_1 = __nccwpck_require__(25623); +const issue_inactivity_1 = __nccwpck_require__(7703); +const review_configuration_1 = __nccwpck_require__(19249); +const deployment_configuration_builder_1 = __nccwpck_require__(15672); function input(additionalParams, actionInputs, key) { return (0, action_input_source_1.resolveActionInput)(additionalParams, actionInputs, key); } @@ -38302,22 +38302,22 @@ function readLocalWorkflowConfiguration(additionalParams, actionInputs) { /***/ }), -/***/ 47047: +/***/ 13196: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildLocalActionExecution = buildLocalActionExecution; -const ai_1 = __nccwpck_require__(37478); -const hotfix_1 = __nccwpck_require__(18537); -const release_1 = __nccwpck_require__(74715); -const single_action_1 = __nccwpck_require__(45898); -const welcome_1 = __nccwpck_require__(49834); -const execution_builder_1 = __nccwpck_require__(20236); -const configuration_builders_1 = __nccwpck_require__(19094); -const branches_builder_1 = __nccwpck_require__(30085); -const size_threshold_builder_1 = __nccwpck_require__(39757); +const ai_1 = __nccwpck_require__(1465); +const hotfix_1 = __nccwpck_require__(26127); +const release_1 = __nccwpck_require__(40810); +const single_action_1 = __nccwpck_require__(11457); +const welcome_1 = __nccwpck_require__(42046); +const execution_builder_1 = __nccwpck_require__(98884); +const configuration_builders_1 = __nccwpck_require__(28645); +const branches_builder_1 = __nccwpck_require__(45364); +const size_threshold_builder_1 = __nccwpck_require__(5916); function buildLocalActionExecution(configuration, additionalParams) { const { debug, singleAction, singleActionIssue, singleActionVersion, singleActionTitle, singleActionChangelog, singleActionMessage, singleActionCommentId, singleActionCommentMode, singleActionOperationId, inactivityThresholdHours, commitPrefixBuilder, issueManagedBranches, preBranchSdd, reopenIssueOnPush, issueDesiredAssigneesCount, pullRequestDesiredAssigneesCount, pullRequestDesiredReviewersCount, titleEmoji, branchManagementEmoji, token, agentModel, aiPullRequestDescriptionMode, aiMembersOnly, aiIgnoreFiles, aiIncludeReasoning, bugbotSeverity, bugbotCommentLimit, bugbotFixVerifyCommands, bugbotReviewConfiguration, agentTasks, bugLabel, bugfixLabel, hotfixLabel, enhancementLabel, featureLabel, releaseLabel, questionLabel, helpLabel, deployLabel, deployedLabel, docsLabel, documentationLabel, choreLabel, maintenanceLabel, priorityHighLabel, priorityMediumLabel, priorityLowLabel, priorityNoneLabel, sizeXxlLabel, sizeXlLabel, sizeLLabel, sizeMLabel, sizeSLabel, sizeXsLabel, lifecycle, issueTypeTask, issueTypeTaskDescription, issueTypeTaskColor, issueTypeBug, issueTypeBugDescription, issueTypeBugColor, issueTypeFeature, issueTypeFeatureDescription, issueTypeFeatureColor, issueTypeDocumentation, issueTypeDocumentationDescription, issueTypeDocumentationColor, issueTypeMaintenance, issueTypeMaintenanceDescription, issueTypeMaintenanceColor, issueTypeHotfix, issueTypeHotfixDescription, issueTypeHotfixColor, issueTypeRelease, issueTypeReleaseDescription, issueTypeReleaseColor, issueTypeQuestion, issueTypeQuestionDescription, issueTypeQuestionColor, issueTypeHelp, issueTypeHelpDescription, issueTypeHelpColor, repositoryLocale, issueLocale, pullRequestLocale, sizeXxlThresholdLines, sizeXxlThresholdFiles, sizeXxlThresholdCommits, sizeXlThresholdLines, sizeXlThresholdFiles, sizeXlThresholdCommits, sizeLThresholdLines, sizeLThresholdFiles, sizeLThresholdCommits, sizeMThresholdLines, sizeMThresholdFiles, sizeMThresholdCommits, sizeSThresholdLines, sizeSThresholdFiles, sizeSThresholdCommits, sizeXsThresholdLines, sizeXsThresholdFiles, sizeXsThresholdCommits, mainBranch, developmentBranch, featureTree, bugfixTree, hotfixTree, releaseTree, docsTree, choreTree, releaseWorkflow, hotfixWorkflow, projects, projectColumnIssueCreated, projectColumnPullRequestCreated, projectColumnIssueInProgress, projectColumnPullRequestInProgress, welcomeTitle, welcomeMessages, deployment, } = configuration; return (0, execution_builder_1.buildExecution)({ @@ -38387,7 +38387,7 @@ function buildLocalActionExecution(configuration, additionalParams) { /***/ }), -/***/ 94290: +/***/ 91598: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -38397,14 +38397,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) { }; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.renderLocalActionResults = renderLocalActionResults; -const chalk_1 = __importDefault(__nccwpck_require__(8578)); -const boxen_1 = __importDefault(__nccwpck_require__(11652)); -const product_identity_1 = __nccwpck_require__(18739); -const application_error_presentation_policy_1 = __nccwpck_require__(95067); -const result_1 = __nccwpck_require__(73817); -const untrusted_content_1 = __nccwpck_require__(67057); -const publication_message_catalog_1 = __nccwpck_require__(34223); -const logger_1 = __nccwpck_require__(91151); +const chalk_1 = __importDefault(__nccwpck_require__(43920)); +const boxen_1 = __importDefault(__nccwpck_require__(32634)); +const product_identity_1 = __nccwpck_require__(44908); +const application_error_presentation_policy_1 = __nccwpck_require__(47255); +const result_1 = __nccwpck_require__(61444); +const untrusted_content_1 = __nccwpck_require__(12334); +const publication_message_catalog_1 = __nccwpck_require__(46042); +const logger_1 = __nccwpck_require__(50135); function renderLocalActionResults(results, catalog = publication_message_catalog_1.ENGLISH_PUBLICATION_CATALOG) { let content = ''; const failed = results.filter(result => result.errors.length > 0 || !result.success).length; @@ -38468,14 +38468,14 @@ function directAnswer(payload) { /***/ }), -/***/ 28586: +/***/ 60059: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.dispatchMainRunRoute = dispatchMainRunRoute; -const logger_1 = __nccwpck_require__(91151); +const logger_1 = __nccwpck_require__(50135); async function dispatchMainRunRoute(route, execution, handlers) { switch (route) { case 'single-action': @@ -38504,7 +38504,7 @@ async function dispatchMainRunRoute(route, execution, handlers) { /***/ }), -/***/ 916: +/***/ 70730: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -38520,15 +38520,15 @@ exports.logWelcomeMessage = logWelcomeMessage; exports.runTokenExecution = runTokenExecution; exports.runNoIssueExecution = runNoIssueExecution; exports.runMainRoute = runMainRoute; -const chalk_1 = __importDefault(__nccwpck_require__(8578)); -const boxen_1 = __importDefault(__nccwpck_require__(11652)); -const result_1 = __nccwpck_require__(73817); -const product_identity_1 = __nccwpck_require__(18739); -const logger_1 = __nccwpck_require__(91151); -const main_run_dispatcher_1 = __nccwpck_require__(28586); -const workflow_context_1 = __nccwpck_require__(55224); -const workflow_queue_composition_root_1 = __nccwpck_require__(21598); -const application_error_1 = __nccwpck_require__(75999); +const chalk_1 = __importDefault(__nccwpck_require__(43920)); +const boxen_1 = __importDefault(__nccwpck_require__(32634)); +const result_1 = __nccwpck_require__(61444); +const product_identity_1 = __nccwpck_require__(44908); +const logger_1 = __nccwpck_require__(50135); +const main_run_dispatcher_1 = __nccwpck_require__(60059); +const workflow_context_1 = __nccwpck_require__(13835); +const workflow_queue_composition_root_1 = __nccwpck_require__(57010); +const application_error_1 = __nccwpck_require__(2965); exports.WORKFLOW_QUEUE_FAILURE_MESSAGE = 'Workflow queue check failed; sequential execution was not bypassed.'; /** * Keeps provider diagnostics out of the action's externally visible failure @@ -38634,7 +38634,7 @@ async function runMainRoute(execution, route, routeHandlers) { /***/ }), -/***/ 8466: +/***/ 78693: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -38659,7 +38659,7 @@ function resolveMainRunRoute(input) { /***/ }), -/***/ 73448: +/***/ 60181: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -38684,7 +38684,7 @@ async function loadProjectDetails(projectRepository, projectIds, owner, token) { /***/ }), -/***/ 78958: +/***/ 57421: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -38714,7 +38714,7 @@ function requireRepositoryCoordinates(value) { /***/ }), -/***/ 45805: +/***/ 56062: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -38722,7 +38722,7 @@ function requireRepositoryCoordinates(value) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.projectSetupExecutionContext = projectSetupExecutionContext; exports.applySetupExecutionResult = applySetupExecutionResult; -const input_keys_1 = __nccwpck_require__(88539); +const input_keys_1 = __nccwpck_require__(83725); function projectSetupExecutionContext(source) { const configuredIssue = readConfiguredIssue(source.inputs); return Object.freeze({ @@ -38842,15 +38842,15 @@ function readConfiguredIssue(inputs) { /***/ }), -/***/ 39757: +/***/ 5916: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildSizeThresholds = buildSizeThresholds; -const size_threshold_1 = __nccwpck_require__(6362); -const size_thresholds_1 = __nccwpck_require__(54820); +const size_threshold_1 = __nccwpck_require__(7267); +const size_thresholds_1 = __nccwpck_require__(97155); function buildSizeThresholds(values) { return new size_thresholds_1.SizeThresholds(new size_threshold_1.SizeThreshold(values.xxl.lines, values.xxl.files, values.xxl.commits), new size_threshold_1.SizeThreshold(values.xl.lines, values.xl.files, values.xl.commits), new size_threshold_1.SizeThreshold(values.l.lines, values.l.files, values.l.commits), new size_threshold_1.SizeThreshold(values.m.lines, values.m.files, values.m.commits), new size_threshold_1.SizeThreshold(values.s.lines, values.s.files, values.s.commits), new size_threshold_1.SizeThreshold(values.xs.lines, values.xs.files, values.xs.commits)); } @@ -38858,7 +38858,7 @@ function buildSizeThresholds(values) { /***/ }), -/***/ 55224: +/***/ 13835: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -38887,7 +38887,7 @@ function resolveWorkflowIdentifier(workflowRef) { /***/ }), -/***/ 88539: +/***/ 83725: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -39098,7 +39098,7 @@ exports.INPUT_KEYS = { /***/ }), -/***/ 18739: +/***/ 44908: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -39110,7 +39110,7 @@ exports.TITLE = 'Copilot'; /***/ }), -/***/ 75999: +/***/ 2965: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -39118,9 +39118,9 @@ exports.TITLE = 'Copilot'; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.ApplicationError = exports.APPLICATION_ERROR_RECOVERY_IDS = exports.APPLICATION_ERROR_METADATA = void 0; exports.toApplicationError = toApplicationError; -const application_error_1 = __nccwpck_require__(97790); -const application_error_context_1 = __nccwpck_require__(4034); -var application_error_2 = __nccwpck_require__(97790); +const application_error_1 = __nccwpck_require__(28206); +const application_error_context_1 = __nccwpck_require__(15491); +var application_error_2 = __nccwpck_require__(28206); Object.defineProperty(exports, "APPLICATION_ERROR_METADATA", ({ enumerable: true, get: function () { return application_error_2.APPLICATION_ERROR_METADATA; } })); Object.defineProperty(exports, "APPLICATION_ERROR_RECOVERY_IDS", ({ enumerable: true, get: function () { return application_error_2.APPLICATION_ERROR_RECOVERY_IDS; } })); /** Creates a semantic error and owns correlation identity outside the pure model. */ @@ -39144,7 +39144,7 @@ function toApplicationError(error, code, message, options = {}) { /***/ }), -/***/ 4034: +/***/ 15491: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -39156,7 +39156,7 @@ exports.runWithApplicationErrorCorrelation = runWithApplicationErrorCorrelation; exports.runAtApplicationErrorBoundary = runAtApplicationErrorBoundary; const node_async_hooks_1 = __nccwpck_require__(92761); const node_crypto_1 = __nccwpck_require__(6005); -const application_error_1 = __nccwpck_require__(97790); +const application_error_1 = __nccwpck_require__(28206); const applicationErrorCorrelation = new node_async_hooks_1.AsyncLocalStorage(); function getApplicationErrorCorrelationId() { return applicationErrorCorrelation.getStore(); @@ -39180,7 +39180,7 @@ function runAtApplicationErrorBoundary(operation) { /***/ }), -/***/ 38313: +/***/ 93638: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -39199,7 +39199,7 @@ exports.SetupInteractionCancelledError = SetupInteractionCancelledError; /***/ }), -/***/ 79966: +/***/ 24342: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -39218,14 +39218,14 @@ function replaceAgentActivityLabel(currentLabels, activityLabel, active) { /***/ }), -/***/ 15375: +/***/ 69527: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.shouldTrackAgentActivity = shouldTrackAgentActivity; -const agent_1 = __nccwpck_require__(89040); +const agent_1 = __nccwpck_require__(95407); /** Decides whether a route can invoke an agent for its current event. */ function shouldTrackAgentActivity(execution, route) { if (!hasTarget(execution)) @@ -39278,7 +39278,7 @@ function hasTarget(execution) { /***/ }), -/***/ 7699: +/***/ 60436: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -39287,8 +39287,8 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildAgentConfiguration = buildAgentConfiguration; exports.mergeAgentTaskValues = mergeAgentTaskValues; exports.buildAgentTaskConfiguration = buildAgentTaskConfiguration; -const agent_configuration_validation_policy_1 = __nccwpck_require__(60596); -const agent_executable_policy_1 = __nccwpck_require__(12570); +const agent_configuration_validation_policy_1 = __nccwpck_require__(4345); +const agent_executable_policy_1 = __nccwpck_require__(53773); function buildAgentConfiguration(values, environment) { const provider = (0, agent_configuration_validation_policy_1.resolveAgentProvider)(values.provider.trim().toLowerCase()); const modelProvider = (0, agent_configuration_validation_policy_1.resolveModelProvider)(values.modelProvider, environment, provider); @@ -39336,7 +39336,7 @@ function hasTaskOverride(value) { /***/ }), -/***/ 60596: +/***/ 4345: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -39349,7 +39349,7 @@ exports.assertProviderModelCompatibility = assertProviderModelCompatibility; exports.resolveModel = resolveModel; exports.resolveEffort = resolveEffort; exports.assertModelAllowlisted = assertModelAllowlisted; -const application_error_1 = __nccwpck_require__(75999); +const application_error_1 = __nccwpck_require__(2965); exports.SUPPORTED_AGENT_PROVIDERS = ['opencode', 'cursor', 'codex']; function resolveAgentProvider(value) { if (exports.SUPPORTED_AGENT_PROVIDERS.includes(value)) @@ -39410,15 +39410,15 @@ function assertIdentifier(value, message, pattern = /^[a-z0-9][a-z0-9_-]*$/i) { /***/ }), -/***/ 12570: +/***/ 53773: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.validateAgentExecutableSelection = validateAgentExecutableSelection; -const agent_1 = __nccwpck_require__(89040); -const application_error_1 = __nccwpck_require__(75999); +const agent_1 = __nccwpck_require__(95407); +const application_error_1 = __nccwpck_require__(2965); /** Accepts only the provider basename or one absolute path to that binary. */ function validateAgentExecutableSelection(configuration) { const selected = configuration.executable?.trim(); @@ -39437,17 +39437,17 @@ function validateAgentExecutableSelection(configuration) { /***/ }), -/***/ 25690: +/***/ 3341: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildProviderExecutionPolicy = buildProviderExecutionPolicy; -const codex_execution_plan_policy_1 = __nccwpck_require__(87204); -const cursor_execution_plan_policy_1 = __nccwpck_require__(93955); -const opencode_execution_plan_policy_1 = __nccwpck_require__(99100); -const provider_execution_policy_1 = __nccwpck_require__(50480); +const codex_execution_plan_policy_1 = __nccwpck_require__(30680); +const cursor_execution_plan_policy_1 = __nccwpck_require__(91102); +const opencode_execution_plan_policy_1 = __nccwpck_require__(14679); +const provider_execution_policy_1 = __nccwpck_require__(81815); /** Static exhaustive dispatch: providers cannot register or bypass policy at runtime. */ function buildProviderExecutionPolicy(input) { const provider = input.configuration.provider; @@ -39462,16 +39462,16 @@ function buildProviderExecutionPolicy(input) { /***/ }), -/***/ 87204: +/***/ 30680: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildCodexExecutionPolicy = buildCodexExecutionPolicy; -const agent_execution_plan_1 = __nccwpck_require__(12253); -const provider_execution_policy_1 = __nccwpck_require__(50480); -const strict_output_schema_policy_1 = __nccwpck_require__(56743); +const agent_execution_plan_1 = __nccwpck_require__(25901); +const provider_execution_policy_1 = __nccwpck_require__(81815); +const strict_output_schema_policy_1 = __nccwpck_require__(85197); function buildCodexExecutionPolicy(input) { const { configuration } = input; if (configuration.provider !== 'codex') @@ -39532,15 +39532,15 @@ function tomlString(value) { /***/ }), -/***/ 93955: +/***/ 91102: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildCursorExecutionPolicy = buildCursorExecutionPolicy; -const agent_execution_plan_1 = __nccwpck_require__(12253); -const provider_execution_policy_1 = __nccwpck_require__(50480); +const agent_execution_plan_1 = __nccwpck_require__(25901); +const provider_execution_policy_1 = __nccwpck_require__(81815); function buildCursorExecutionPolicy(input) { const { configuration } = input; if (configuration.provider !== 'cursor') @@ -39607,15 +39607,15 @@ function buildCursorExecutionPolicy(input) { /***/ }), -/***/ 99100: +/***/ 14679: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildOpenCodeExecutionPolicy = buildOpenCodeExecutionPolicy; -const agent_execution_plan_1 = __nccwpck_require__(12253); -const provider_execution_policy_1 = __nccwpck_require__(50480); +const agent_execution_plan_1 = __nccwpck_require__(25901); +const provider_execution_policy_1 = __nccwpck_require__(81815); const READONLY_AGENT = 'copilot-controlled-readonly'; const FIXER_AGENT = 'copilot-controlled-fixer'; function buildOpenCodeExecutionPolicy(input) { @@ -39705,7 +39705,7 @@ function buildOpenCodeExecutionPolicy(input) { /***/ }), -/***/ 50480: +/***/ 81815: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -39724,7 +39724,7 @@ function managedArtifactPath(runtimeDirectory, relativePath) { /***/ }), -/***/ 56743: +/***/ 85197: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -39791,7 +39791,7 @@ function isRecord(value) { /***/ }), -/***/ 30601: +/***/ 2584: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -39801,7 +39801,7 @@ exports.AGENT_OUTPUT_LOCALE_SCHEMA_PROPERTY = exports.PRODUCT_FACING_AGENT_TASKS exports.productFacingAgentQueryOptions = productFacingAgentQueryOptions; exports.validateAgentOutputLocale = validateAgentOutputLocale; exports.agentOutputLocaleFailureMessage = agentOutputLocaleFailureMessage; -const locale_1 = __nccwpck_require__(15386); +const locale_1 = __nccwpck_require__(64552); exports.PRODUCT_FACING_AGENT_TASKS = [ 'think', 'answer-issue-help', @@ -39877,7 +39877,7 @@ function agentOutputLocaleFailureMessage(validation) { /***/ }), -/***/ 25603: +/***/ 63523: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -39885,7 +39885,7 @@ function agentOutputLocaleFailureMessage(validation) { /** Shared structured-response contracts used by agent-backed application flows. */ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.PULL_REQUEST_DESCRIPTION_RESPONSE_SCHEMA = exports.RECOMMEND_STEPS_RESPONSE_SCHEMA = exports.THINK_RESPONSE_SCHEMA = exports.LANGUAGE_ADAPTATION_RESPONSE_SCHEMA = void 0; -const agent_output_locale_policy_1 = __nccwpck_require__(30601); +const agent_output_locale_policy_1 = __nccwpck_require__(2584); exports.LANGUAGE_ADAPTATION_RESPONSE_SCHEMA = { type: 'object', properties: { @@ -40025,7 +40025,7 @@ exports.PULL_REQUEST_DESCRIPTION_RESPONSE_SCHEMA = { /***/ }), -/***/ 85712: +/***/ 2601: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -40057,7 +40057,7 @@ function resolveThinkAgentTask(commandName, destinationType) { /***/ }), -/***/ 64809: +/***/ 67880: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -40066,9 +40066,9 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.readEnglishApplicationErrorMessage = exports.APPLICATION_ERROR_CATALOG_DEFINITIONS = exports.SPANISH_APPLICATION_ERROR_DEFINITION = exports.ENGLISH_APPLICATION_ERROR_DEFINITION = exports.SPANISH_APPLICATION_ERROR_MESSAGES = exports.ENGLISH_APPLICATION_ERROR_MESSAGES = exports.APPLICATION_ERROR_MESSAGE_IDS = exports.APPLICATION_ERROR_CODES = void 0; exports.resolveStaticApplicationErrorCatalog = resolveStaticApplicationErrorCatalog; exports.resolveApplicationErrorCatalog = resolveApplicationErrorCatalog; -const application_error_1 = __nccwpck_require__(97790); -const message_catalog_1 = __nccwpck_require__(27097); -const resolved_message_catalog_policy_1 = __nccwpck_require__(55069); +const application_error_1 = __nccwpck_require__(28206); +const message_catalog_1 = __nccwpck_require__(5313); +const resolved_message_catalog_policy_1 = __nccwpck_require__(98248); const LABEL_KEYS = Object.freeze([ 'impact', 'errorCode', 'action', 'retainedState', 'retryable', 'yes', 'no', 'reference', ]); @@ -40331,7 +40331,7 @@ exports.readEnglishApplicationErrorMessage = readEnglishApplicationErrorMessage; /***/ }), -/***/ 95067: +/***/ 47255: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -40340,7 +40340,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildApplicationErrorPresentation = buildApplicationErrorPresentation; exports.renderApplicationErrorText = renderApplicationErrorText; exports.renderApplicationErrorMarkdown = renderApplicationErrorMarkdown; -const application_error_message_catalog_1 = __nccwpck_require__(64809); +const application_error_message_catalog_1 = __nccwpck_require__(67880); /** Shared semantic view model for terminal, GitHub, and API presentation. */ function buildApplicationErrorPresentation(error, message = application_error_message_catalog_1.readEnglishApplicationErrorMessage) { const descriptor = error.recovery @@ -40388,7 +40388,7 @@ function renderApplicationErrorMarkdown(error, message = application_error_messa /***/ }), -/***/ 36904: +/***/ 24591: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -40495,7 +40495,7 @@ exports.SPANISH_APPROVAL_DOCTOR_MESSAGES = Object.freeze({ /***/ }), -/***/ 85918: +/***/ 2426: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -40532,7 +40532,7 @@ function selectConfirmedAssignees(requestedMembers, assignedMembers) { /***/ }), -/***/ 35596: +/***/ 50189: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -40574,7 +40574,7 @@ async function runWithConcurrencyLimit(tasks, limit) { /***/ }), -/***/ 97307: +/***/ 59489: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -40613,7 +40613,7 @@ function findPreviousIssueBranch(branches, issueNumber, branchTypes) { /***/ }), -/***/ 89245: +/***/ 71641: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -40622,8 +40622,8 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.BRANCH_SYNC_CATALOG_DEFINITIONS = exports.SPANISH_BRANCH_SYNC_DEFINITION = exports.ENGLISH_BRANCH_SYNC_DEFINITION = exports.BRANCH_SYNC_MESSAGE_IDS = void 0; exports.resolveStaticBranchSyncCatalog = resolveStaticBranchSyncCatalog; exports.resolveBranchSyncCatalog = resolveBranchSyncCatalog; -const message_catalog_1 = __nccwpck_require__(27097); -const resolved_message_catalog_policy_1 = __nccwpck_require__(55069); +const message_catalog_1 = __nccwpck_require__(5313); +const resolved_message_catalog_policy_1 = __nccwpck_require__(98248); exports.BRANCH_SYNC_MESSAGE_IDS = Object.freeze([ 'branchSync.stale.heading', 'branchSync.stale.behind', @@ -40706,7 +40706,7 @@ async function resolveBranchSyncCatalog(locale, configuration, resolver) { /***/ }), -/***/ 79895: +/***/ 90741: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -40722,10 +40722,10 @@ exports.buildBranchSyncStatusCommentUrl = buildBranchSyncStatusCommentUrl; exports.isStaleBranchSyncComment = isStaleBranchSyncComment; exports.buildStaleBranchSyncComment = buildStaleBranchSyncComment; exports.buildAlignedBranchSyncComment = buildAlignedBranchSyncComment; -const github_user_policy_1 = __nccwpck_require__(84403); -const git_object_id_1 = __nccwpck_require__(88623); -const publication_identity_policy_1 = __nccwpck_require__(45403); -const github_comment_publication_policy_1 = __nccwpck_require__(72712); +const github_user_policy_1 = __nccwpck_require__(19596); +const git_object_id_1 = __nccwpck_require__(36924); +const publication_identity_policy_1 = __nccwpck_require__(12590); +const github_comment_publication_policy_1 = __nccwpck_require__(22913); function selectBranchDependenciesForPush(dependencies, pushedBranch) { const selected = dependencies.filter((dependency) => dependency.parentBranch === pushedBranch || dependency.workingBranch === pushedBranch); @@ -40859,7 +40859,7 @@ function safeLinkLabel(value) { /***/ }), -/***/ 51389: +/***/ 16868: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -40876,7 +40876,7 @@ exports.BUGBOT_MIN_SEVERITY = 'low'; /***/ }), -/***/ 31601: +/***/ 51471: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -40886,9 +40886,9 @@ exports.BugbotDiffPlanLimitError = exports.MAX_REVIEW_DIFF_NORMALIZED_INPUT_LENG exports.buildReviewDiffPlan = buildReviewDiffPlan; exports.splitReviewDiffPatch = splitReviewDiffPatch; const node_crypto_1 = __nccwpck_require__(6005); -const untrusted_content_1 = __nccwpck_require__(67057); -const git_object_id_1 = __nccwpck_require__(88623); -const file_ignore_policy_1 = __nccwpck_require__(20542); +const untrusted_content_1 = __nccwpck_require__(12334); +const git_object_id_1 = __nccwpck_require__(36924); +const file_ignore_policy_1 = __nccwpck_require__(56498); exports.MAX_REVIEW_DIFF_PARTITION_LENGTH = 64000; exports.MAX_REVIEW_DIFF_FRAGMENT_LENGTH = 12000; exports.MAX_REVIEW_DIFF_PARTITIONS = 64; @@ -41097,7 +41097,7 @@ function stableDiffPartitionDigest(value) { /***/ }), -/***/ 52771: +/***/ 10580: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -41119,7 +41119,7 @@ function selectPullRequestOwnerForPushReview(context) { /***/ }), -/***/ 98024: +/***/ 80639: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -41141,10 +41141,10 @@ exports.replaceMarkerInBody = replaceMarkerInBody; exports.extractTitleFromBody = extractTitleFromBody; exports.buildCommentBody = buildCommentBody; exports.buildResolvedFindingNote = buildResolvedFindingNote; -const bugbot_constants_1 = __nccwpck_require__(51389); -const application_error_1 = __nccwpck_require__(75999); -const github_comment_publication_policy_1 = __nccwpck_require__(72712); -const bugbot_message_catalog_1 = __nccwpck_require__(7406); +const bugbot_constants_1 = __nccwpck_require__(16868); +const application_error_1 = __nccwpck_require__(2965); +const github_comment_publication_policy_1 = __nccwpck_require__(22913); +const bugbot_message_catalog_1 = __nccwpck_require__(84479); /** Maximum lossless finding identity accepted by the marker contract. */ exports.MAX_FINDING_ID_LENGTH = 200; /** Safe character set for finding IDs in regex (alphanumeric, path/segment chars). */ @@ -41294,14 +41294,14 @@ function buildResolvedFindingNote(resolution, catalog = (0, bugbot_message_catal /***/ }), -/***/ 53822: +/***/ 9298: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.projectBugbotFindingStatuses = projectBugbotFindingStatuses; -const review_state_1 = __nccwpck_require__(79200); +const review_state_1 = __nccwpck_require__(17271); /** Projects durable comment markers and the current analysis into a stable finding state. */ function projectBugbotFindingStatuses(existingByFindingId, activeFindings, resolvedFindingIds = new Set(), resolvedFindingResolutions = new Map()) { const ids = new Set([ @@ -41347,7 +41347,7 @@ function countStatuses(statuses) { /***/ }), -/***/ 7406: +/***/ 84479: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -41358,8 +41358,8 @@ exports.resolveStaticBugbotCatalog = resolveStaticBugbotCatalog; exports.resolveBugbotCatalog = resolveBugbotCatalog; exports.renderBugbotDiagnostic = renderBugbotDiagnostic; exports.bugbotDiagnosticOperatorMessage = bugbotDiagnosticOperatorMessage; -const message_catalog_1 = __nccwpck_require__(27097); -const resolved_message_catalog_policy_1 = __nccwpck_require__(55069); +const message_catalog_1 = __nccwpck_require__(5313); +const resolved_message_catalog_policy_1 = __nccwpck_require__(98248); exports.BUGBOT_MESSAGE_IDS = Object.freeze([ 'bugbot.status.heading.partial', 'bugbot.status.heading.verification', @@ -41677,7 +41677,7 @@ function bugbotDiagnosticOperatorMessage(diagnostic) { /***/ }), -/***/ 57555: +/***/ 83782: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -41708,17 +41708,17 @@ function formatBugbotPartitionCompletion(input) { /***/ }), -/***/ 85821: +/***/ 95220: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.projectBugbotProviderEvidence = projectBugbotProviderEvidence; -const bugbot_finding_marker_policy_1 = __nccwpck_require__(98024); -const bugbot_constants_1 = __nccwpck_require__(51389); -const github_user_policy_1 = __nccwpck_require__(84403); -const review_state_1 = __nccwpck_require__(79200); +const bugbot_finding_marker_policy_1 = __nccwpck_require__(80639); +const bugbot_constants_1 = __nccwpck_require__(16868); +const github_user_policy_1 = __nccwpck_require__(19596); +const review_state_1 = __nccwpck_require__(17271); /** * Converts a provider snapshot into semantic finding evidence. Issue and PR * destinations are projected independently and then folded conservatively, so @@ -41906,7 +41906,7 @@ function containsBugbotFindingMarkerSyntax(body) { /***/ }), -/***/ 78128: +/***/ 54271: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -42031,7 +42031,7 @@ function hasMissingNonCleanDurableDestination(findingId, finding, observed) { /***/ }), -/***/ 89189: +/***/ 15950: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -42055,16 +42055,16 @@ function filterEligibleBugbotResolutionIds(claimedIds, eligibleIds, existingByFi /***/ }), -/***/ 98117: +/***/ 22443: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.projectBugbotResultFindingStates = projectBugbotResultFindingStates; -const result_1 = __nccwpck_require__(73817); -const review_state_1 = __nccwpck_require__(79200); -const bugbot_telemetry_projection_policy_1 = __nccwpck_require__(43244); +const result_1 = __nccwpck_require__(61444); +const review_state_1 = __nccwpck_require__(17271); +const bugbot_telemetry_projection_policy_1 = __nccwpck_require__(59783); const BUGBOT_FINDING_STATE_SET = new Set(review_state_1.BUGBOT_FINDING_STATES); const OUTCOMES_REQUIRING_FINDING_STATES = new Set([ 'completed', @@ -42127,7 +42127,7 @@ function isNonNegativeSafeInteger(value) { /***/ }), -/***/ 83288: +/***/ 11719: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -42135,8 +42135,8 @@ function isNonNegativeSafeInteger(value) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.selectOwnedBugbotReviews = selectOwnedBugbotReviews; exports.isTrustedBugbotAuthor = isTrustedBugbotAuthor; -const github_user_policy_1 = __nccwpck_require__(84403); -const bugbot_finding_marker_policy_1 = __nccwpck_require__(98024); +const github_user_policy_1 = __nccwpck_require__(19596); +const bugbot_finding_marker_policy_1 = __nccwpck_require__(80639); /** Associates trusted review summaries with every child finding they own. */ function selectOwnedBugbotReviews(input) { const findingById = new Map(input.findings.map((finding) => [finding.id, finding])); @@ -42184,7 +42184,7 @@ function addFinding(findingsByReview, reviewIdentity, findingId) { /***/ }), -/***/ 43799: +/***/ 77193: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -42198,10 +42198,10 @@ exports.isBugbotStatusComment = isBugbotStatusComment; exports.renderBugbotStatusCard = renderBugbotStatusCard; exports.renderBugbotReviewSnapshot = renderBugbotReviewSnapshot; exports.buildNewBugbotReviewSnapshotHeader = buildNewBugbotReviewSnapshotHeader; -const review_state_1 = __nccwpck_require__(79200); -const github_comment_publication_policy_1 = __nccwpck_require__(72712); -const publication_identity_policy_1 = __nccwpck_require__(45403); -const bugbot_message_catalog_1 = __nccwpck_require__(7406); +const review_state_1 = __nccwpck_require__(17271); +const github_comment_publication_policy_1 = __nccwpck_require__(22913); +const publication_identity_policy_1 = __nccwpck_require__(12590); +const bugbot_message_catalog_1 = __nccwpck_require__(84479); exports.BUGBOT_STATUS_MARKER_PREFIX = 'copilot-bugbot-status'; exports.BUGBOT_REVIEW_MARKER_PREFIX = 'copilot-bugbot-review'; exports.BUGBOT_REVIEW_STATUS_START = '`; @@ -43671,7 +43671,7 @@ function shortSha(value) { /***/ }), -/***/ 20542: +/***/ 56498: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -43733,7 +43733,7 @@ function fileMatchesIgnorePatterns(filePath, ignorePatterns) { /***/ }), -/***/ 72712: +/***/ 22913: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -43742,8 +43742,8 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.sanitizeAgentMarkdown = sanitizeAgentMarkdown; exports.sanitizePublishedError = sanitizePublishedError; exports.escapeHtml = escapeHtml; -const untrusted_content_1 = __nccwpck_require__(67057); -const secret_redaction_1 = __nccwpck_require__(254); +const untrusted_content_1 = __nccwpck_require__(12334); +const secret_redaction_1 = __nccwpck_require__(93523); /** * Model output is untrusted too. Keep useful Markdown, but neutralize the * GitHub automation surfaces that could create side effects when published. @@ -43786,7 +43786,7 @@ function neutralizeGithubControls(value) { /***/ }), -/***/ 74902: +/***/ 78447: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -43795,8 +43795,8 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.INACTIVITY_CATALOG_DEFINITIONS = exports.SPANISH_INACTIVITY_DEFINITION = exports.ENGLISH_INACTIVITY_DEFINITION = exports.INACTIVITY_MESSAGE_IDS = void 0; exports.resolveStaticInactivityCatalog = resolveStaticInactivityCatalog; exports.resolveInactivityCatalog = resolveInactivityCatalog; -const message_catalog_1 = __nccwpck_require__(27097); -const resolved_message_catalog_policy_1 = __nccwpck_require__(55069); +const message_catalog_1 = __nccwpck_require__(5313); +const resolved_message_catalog_policy_1 = __nccwpck_require__(98248); exports.INACTIVITY_MESSAGE_IDS = Object.freeze([ 'inactivity.closure.heading', 'inactivity.closure.reason', @@ -43890,7 +43890,7 @@ async function resolveInactivityCatalog(locale, configuration, resolver) { /***/ }), -/***/ 1572: +/***/ 62453: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -43898,7 +43898,7 @@ async function resolveInactivityCatalog(locale, configuration, resolver) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildInactivityClosureComment = buildInactivityClosureComment; exports.buildInactivitySummarySteps = buildInactivitySummarySteps; -const publication_identity_policy_1 = __nccwpck_require__(45403); +const publication_identity_policy_1 = __nccwpck_require__(12590); function buildInactivityClosureComment(input) { const digest = (0, publication_identity_policy_1.createSemanticDigest)({ updatedAt: input.candidate.updatedAt, @@ -43939,16 +43939,16 @@ function buildInactivitySummarySteps(input) { /***/ }), -/***/ 73160: +/***/ 36432: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildInitialLabelProvisioningPlan = buildInitialLabelProvisioningPlan; -const progress_labels_1 = __nccwpck_require__(97890); -const copilot_lifecycle_1 = __nccwpck_require__(72418); -const issue_start_policy_1 = __nccwpck_require__(90332); +const progress_labels_1 = __nccwpck_require__(71285); +const copilot_lifecycle_1 = __nccwpck_require__(4227); +const issue_start_policy_1 = __nccwpck_require__(20953); const normalizeLabelName = (name) => name.trim().toLowerCase(); function configuredLabelDefinitions(labels) { const metadata = [ @@ -44032,15 +44032,15 @@ function buildInitialLabelProvisioningPlan(labels, existingLabelNames) { /***/ }), -/***/ 61899: +/***/ 28956: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.resolveIssueCommentPublicationRequest = resolveIssueCommentPublicationRequest; -const comment_content_policy_1 = __nccwpck_require__(77454); -const input_keys_1 = __nccwpck_require__(88539); +const comment_content_policy_1 = __nccwpck_require__(2324); +const input_keys_1 = __nccwpck_require__(83725); function resolveIssueCommentPublicationRequest(input) { if (!(0, comment_content_policy_1.hasVisibleCommentContent)(input.message)) { return new Error(`${input_keys_1.INPUT_KEYS.SINGLE_ACTION_MESSAGE} must contain a visible message.`); @@ -44072,15 +44072,15 @@ function resolveMode(mode, commentId) { /***/ }), -/***/ 99190: +/***/ 87929: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.assertLocalSingleActionAllowed = assertLocalSingleActionAllowed; -const action_types_1 = __nccwpck_require__(19625); -const application_error_1 = __nccwpck_require__(75999); +const action_types_1 = __nccwpck_require__(8960); +const application_error_1 = __nccwpck_require__(2965); const GITHUB_WORKFLOW_ONLY_ACTIONS = [ action_types_1.ACTIONS.CREATE_TAG, action_types_1.ACTIONS.CREATE_RELEASE, @@ -44099,7 +44099,7 @@ function assertLocalSingleActionAllowed(requestedAction) { /***/ }), -/***/ 55078: +/***/ 32742: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -44154,7 +44154,7 @@ function buildManagedBranchPresentation(input) { /***/ }), -/***/ 56033: +/***/ 22459: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -44164,7 +44164,7 @@ exports.SPANISH_MERGE_QUEUE_MESSAGES = exports.ENGLISH_MERGE_QUEUE_MESSAGES = ex exports.renderMergeQueueReadinessFailure = renderMergeQueueReadinessFailure; exports.producerStateMessageId = producerStateMessageId; exports.boundedMergeQueueDiagnostic = boundedMergeQueueDiagnostic; -const sensitive_text_1 = __nccwpck_require__(47122); +const sensitive_text_1 = __nccwpck_require__(98209); exports.MERGE_QUEUE_MESSAGE_IDS = Object.freeze([ 'mergeQueue.readiness.failure', 'mergeQueue.readiness.incompleteEvidence', @@ -44287,7 +44287,7 @@ function boundedMergeQueueDiagnostic(value) { /***/ }), -/***/ 39267: +/***/ 39631: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -44323,7 +44323,7 @@ function mergeSetupOverrides(fileOverrides, flagOverrides) { /***/ }), -/***/ 97890: +/***/ 71285: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -44357,7 +44357,7 @@ function progressPercentToColor(percent) { /***/ }), -/***/ 45403: +/***/ 12590: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -44374,7 +44374,7 @@ exports.buildPublicationTransitionMarker = buildPublicationTransitionMarker; exports.parsePublicationTransitionMarker = parsePublicationTransitionMarker; exports.buildDuplicateMarker = buildDuplicateMarker; const node_crypto_1 = __nccwpck_require__(6005); -const github_publication_1 = __nccwpck_require__(35793); +const github_publication_1 = __nccwpck_require__(75905); exports.PUBLICATION_SCHEMA = '1'; exports.PUBLICATION_MARKER_PREFIX = 'copilot:publication'; exports.PUBLICATION_DUPLICATE_MARKER_PREFIX = 'copilot:publication-duplicate'; @@ -44505,7 +44505,7 @@ function stableSerialize(value) { /***/ }), -/***/ 34223: +/***/ 46042: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -44516,9 +44516,9 @@ exports.publicationLocaleNeedsDynamicCatalog = publicationLocaleNeedsDynamicCata exports.resolveStaticPublicationCatalog = resolveStaticPublicationCatalog; exports.resolvePublicationCatalog = resolvePublicationCatalog; exports.toPublicationCatalog = toPublicationCatalog; -const message_catalog_1 = __nccwpck_require__(27097); -const locale_1 = __nccwpck_require__(15386); -const application_error_message_catalog_1 = __nccwpck_require__(64809); +const message_catalog_1 = __nccwpck_require__(5313); +const locale_1 = __nccwpck_require__(64552); +const application_error_message_catalog_1 = __nccwpck_require__(67880); const PUBLICATION_SURFACE_MESSAGE_IDS = Object.freeze([ 'publication.implementationPlan', 'publication.planReady', @@ -44917,7 +44917,7 @@ exports.SPANISH_PUBLICATION_CATALOG = toPublicationCatalog(Object.freeze({ /***/ }), -/***/ 79719: +/***/ 11035: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -44929,8 +44929,8 @@ exports.buildDuplicateCompactionPublicationPayload = buildDuplicateCompactionPub exports.duplicateCompactionPublicationOutcomes = duplicateCompactionPublicationOutcomes; exports.buildTransitionPublicationPayload = buildTransitionPublicationPayload; exports.transitionPublicationOutcomes = transitionPublicationOutcomes; -const result_1 = __nccwpck_require__(73817); -const github_publication_1 = __nccwpck_require__(35793); +const result_1 = __nccwpck_require__(61444); +const github_publication_1 = __nccwpck_require__(75905); const MAX_REPORTED_COMMENT_IDS = 20; const MAX_REPORTED_TRANSITIONS = 20; /** Builds bounded evidence for a commit-derived result that was intentionally suppressed. */ @@ -45042,7 +45042,7 @@ function areOrderedUniquePositiveIntegers(values) { /***/ }), -/***/ 43268: +/***/ 87855: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -45050,7 +45050,7 @@ function areOrderedUniquePositiveIntegers(values) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.MAX_PULL_REQUEST_DESCRIPTION_LENGTH = void 0; exports.renderPullRequestDescriptionContent = renderPullRequestDescriptionContent; -const github_comment_publication_policy_1 = __nccwpck_require__(72712); +const github_comment_publication_policy_1 = __nccwpck_require__(22913); exports.MAX_PULL_REQUEST_DESCRIPTION_LENGTH = 12000; const CONTENT_KEYS = Object.freeze([ 'outputLocale', @@ -45242,7 +45242,7 @@ function renderList(values) { /***/ }), -/***/ 39410: +/***/ 83857: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -45282,7 +45282,7 @@ function createSha256(value) { /***/ }), -/***/ 67402: +/***/ 47992: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -45294,9 +45294,9 @@ exports.renderRepositoryAgentArtifacts = renderRepositoryAgentArtifacts; exports.renderRepositoryAgentGuide = renderRepositoryAgentGuide; exports.renderRepositoryAgentSkill = renderRepositoryAgentSkill; exports.renderRepositoryAgentPointerBlock = renderRepositoryAgentPointerBlock; -const issue_workflow_profile_1 = __nccwpck_require__(26744); -const setup_issue_workflow_policy_1 = __nccwpck_require__(81182); -const issue_start_policy_1 = __nccwpck_require__(90332); +const issue_workflow_profile_1 = __nccwpck_require__(62721); +const setup_issue_workflow_policy_1 = __nccwpck_require__(47280); +const issue_start_policy_1 = __nccwpck_require__(20953); exports.REPOSITORY_AGENT_PROFILE_PATH = '.copilot/repository-profile.json'; exports.REPOSITORY_AGENT_GUIDE_PATH = '.copilot/AGENT_GUIDE.md'; exports.REPOSITORY_AGENT_SKILL_PATH = '.agents/skills/copilot-repository-workflow/SKILL.md'; @@ -45452,7 +45452,7 @@ function renderRepositoryAgentPointerBlock() { /***/ }), -/***/ 55069: +/***/ 98248: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -45461,8 +45461,8 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.toResolvedMessageCatalogView = toResolvedMessageCatalogView; exports.resolveStaticMessageCatalogView = resolveStaticMessageCatalogView; exports.resolveMessageCatalogView = resolveMessageCatalogView; -const message_catalog_1 = __nccwpck_require__(27097); -const locale_1 = __nccwpck_require__(15386); +const message_catalog_1 = __nccwpck_require__(5313); +const locale_1 = __nccwpck_require__(64552); function toResolvedMessageCatalogView(resolved) { return Object.freeze({ locale: resolved.resolvedLocale, @@ -45528,7 +45528,7 @@ async function resolveMessageCatalogView(locale, ids, sourceCatalog, bundledCata /***/ }), -/***/ 88350: +/***/ 49532: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -45575,7 +45575,7 @@ function calculateReviewersStillNeeded(desiredCount, currentCount, confirmedCoun /***/ }), -/***/ 81985: +/***/ 55150: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -45587,18 +45587,18 @@ exports.hasOwnedPrimaryIssuePublication = hasOwnedPrimaryIssuePublication; exports.selectSemanticReplyIntents = selectSemanticReplyIntents; exports.renderSemanticReply = renderSemanticReply; exports.renderSemanticStatus = renderSemanticStatus; -const github_publication_1 = __nccwpck_require__(35793); -const github_user_policy_1 = __nccwpck_require__(84403); -const result_1 = __nccwpck_require__(73817); -const github_comment_publication_policy_1 = __nccwpck_require__(72712); -const publication_identity_policy_1 = __nccwpck_require__(45403); -const publication_message_catalog_1 = __nccwpck_require__(34223); -const copilot_interaction_policy_1 = __nccwpck_require__(90108); -const status_command_policy_1 = __nccwpck_require__(3449); -const comment_translation_policy_1 = __nccwpck_require__(27150); -const application_error_presentation_policy_1 = __nccwpck_require__(95067); -const git_object_id_1 = __nccwpck_require__(88623); -const implementation_plan_1 = __nccwpck_require__(77001); +const github_publication_1 = __nccwpck_require__(75905); +const github_user_policy_1 = __nccwpck_require__(19596); +const result_1 = __nccwpck_require__(61444); +const github_comment_publication_policy_1 = __nccwpck_require__(22913); +const publication_identity_policy_1 = __nccwpck_require__(12590); +const publication_message_catalog_1 = __nccwpck_require__(46042); +const copilot_interaction_policy_1 = __nccwpck_require__(8964); +const status_command_policy_1 = __nccwpck_require__(62186); +const comment_translation_policy_1 = __nccwpck_require__(22406); +const application_error_presentation_policy_1 = __nccwpck_require__(47255); +const git_object_id_1 = __nccwpck_require__(36924); +const implementation_plan_1 = __nccwpck_require__(52620); function selectSemanticStatusIntents(context) { return Object.freeze(context.results.flatMap(result => { if (!result.executed || !result.success) @@ -46001,15 +46001,15 @@ function safeDigest(value) { /***/ }), -/***/ 53296: +/***/ 8794: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildApprovalDoctorChecks = buildApprovalDoctorChecks; -const pull_request_approval_policy_1 = __nccwpck_require__(98820); -const setup_doctor_message_catalog_1 = __nccwpck_require__(80226); +const pull_request_approval_policy_1 = __nccwpck_require__(53553); +const setup_doctor_message_catalog_1 = __nccwpck_require__(67183); /** Stable ordered checks; unknown prerequisites never project native-approval readiness. */ function buildApprovalDoctorChecks(input) { const checks = []; @@ -46092,14 +46092,14 @@ function check(id, status, summary, action, blockedBy = []) { /***/ }), -/***/ 85881: +/***/ 6802: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.cloneSetupConfiguration = cloneSetupConfiguration; -const setup_configuration_defaults_1 = __nccwpck_require__(23381); +const setup_configuration_defaults_1 = __nccwpck_require__(31713); /** Returns a reference-isolated configuration snapshot without serializing it. */ function cloneSetupConfiguration(configuration) { const agents = Object.fromEntries(setup_configuration_defaults_1.SETUP_AGENT_TASKS.map((task) => [ @@ -46146,7 +46146,7 @@ function cloneSetupConfiguration(configuration) { /***/ }), -/***/ 23381: +/***/ 31713: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -46158,12 +46158,12 @@ exports.createDefaultSetupStorageConfiguration = createDefaultSetupStorageConfig exports.createDefaultSetupConfiguration = createDefaultSetupConfiguration; exports.mergeSetupConfiguration = mergeSetupConfiguration; exports.normalizeSetupConfigurationLocales = normalizeSetupConfigurationLocales; -const agent_1 = __nccwpck_require__(89040); -const issue_inactivity_1 = __nccwpck_require__(38572); -const deployment_configuration_1 = __nccwpck_require__(22495); -const locale_1 = __nccwpck_require__(15386); -const issue_workflow_profile_1 = __nccwpck_require__(26744); -const pull_request_approval_policy_1 = __nccwpck_require__(98820); +const agent_1 = __nccwpck_require__(95407); +const issue_inactivity_1 = __nccwpck_require__(7703); +const deployment_configuration_1 = __nccwpck_require__(5664); +const locale_1 = __nccwpck_require__(64552); +const issue_workflow_profile_1 = __nccwpck_require__(62721); +const pull_request_approval_policy_1 = __nccwpck_require__(53553); exports.SETUP_AGENT_TASKS = [ 'planner', 'findings', @@ -46368,7 +46368,7 @@ function normalizeSetupConfigurationLocales(configuration) { /***/ }), -/***/ 87770: +/***/ 44018: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -46379,15 +46379,15 @@ exports.buildSetupPlan = buildSetupPlan; exports.setupPlanGuardPaths = setupPlanGuardPaths; exports.buildSetupRepositoryVariables = buildSetupRepositoryVariables; exports.buildSetupActionInputs = buildSetupActionInputs; -const pull_request_description_1 = __nccwpck_require__(45315); -const setup_workflow_catalog_1 = __nccwpck_require__(24596); -const setup_configuration_defaults_1 = __nccwpck_require__(23381); -const setup_configuration_storage_policy_1 = __nccwpck_require__(2554); -const setup_credential_requirement_policy_1 = __nccwpck_require__(43562); +const pull_request_description_1 = __nccwpck_require__(25623); +const setup_workflow_catalog_1 = __nccwpck_require__(37008); +const setup_configuration_defaults_1 = __nccwpck_require__(31713); +const setup_configuration_storage_policy_1 = __nccwpck_require__(60368); +const setup_credential_requirement_policy_1 = __nccwpck_require__(61975); Object.defineProperty(exports, "buildSetupCredentialRequirements", ({ enumerable: true, get: function () { return setup_credential_requirement_policy_1.buildSetupCredentialRequirements; } })); -const locale_1 = __nccwpck_require__(15386); -const issue_workflow_profile_1 = __nccwpck_require__(26744); -const setup_issue_workflow_policy_1 = __nccwpck_require__(81182); +const locale_1 = __nccwpck_require__(64552); +const issue_workflow_profile_1 = __nccwpck_require__(62721); +const setup_issue_workflow_policy_1 = __nccwpck_require__(47280); function buildSetupPlan(configuration, mergeQueueReadiness = [], approvalReadiness = []) { const workflowFiles = (0, setup_workflow_catalog_1.enabledSetupWorkflowFiles)((0, setup_issue_workflow_policy_1.effectiveIssueWorkflowFeatures)(configuration)) .filter(file => file !== 'copilot_pull_request_approval.yml' || configuration.pullRequestApproval.mode !== 'off'); @@ -46664,7 +46664,7 @@ function unique(values) { /***/ }), -/***/ 56637: +/***/ 93015: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -46685,16 +46685,16 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) { }; Object.defineProperty(exports, "__esModule", ({ value: true })); /** Public setup-policy boundary. Each concern is implemented in a focused policy module. */ -__exportStar(__nccwpck_require__(23381), exports); -__exportStar(__nccwpck_require__(87770), exports); -__exportStar(__nccwpck_require__(2554), exports); -__exportStar(__nccwpck_require__(13339), exports); -__exportStar(__nccwpck_require__(81182), exports); +__exportStar(__nccwpck_require__(31713), exports); +__exportStar(__nccwpck_require__(44018), exports); +__exportStar(__nccwpck_require__(60368), exports); +__exportStar(__nccwpck_require__(31156), exports); +__exportStar(__nccwpck_require__(47280), exports); /***/ }), -/***/ 2554: +/***/ 60368: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -46714,7 +46714,7 @@ exports.validateSetupStorageAgainstRemote = validateSetupStorageAgainstRemote; exports.validateSetupManagedResourceInventory = validateSetupManagedResourceInventory; exports.usesOrganizationStorage = usesOrganizationStorage; exports.validateStorageConfiguration = validateStorageConfiguration; -const setup_configuration_defaults_1 = __nccwpck_require__(23381); +const setup_configuration_defaults_1 = __nccwpck_require__(31713); function resolveSetupResourceScope(policy, name) { return policy.overrides[name] ?? policy.defaultScope; } @@ -46925,22 +46925,22 @@ function mergeStoragePolicy(base, override) { /***/ }), -/***/ 13339: +/***/ 31156: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.validateSetupConfiguration = validateSetupConfiguration; -const setup_configuration_defaults_1 = __nccwpck_require__(23381); -const agent_configuration_validation_policy_1 = __nccwpck_require__(60596); -const setup_configuration_storage_policy_1 = __nccwpck_require__(2554); -const issue_inactivity_1 = __nccwpck_require__(38572); -const deployment_configuration_1 = __nccwpck_require__(22495); -const locale_1 = __nccwpck_require__(15386); -const issue_workflow_profile_1 = __nccwpck_require__(26744); -const setup_issue_workflow_policy_1 = __nccwpck_require__(81182); -const pull_request_approval_policy_1 = __nccwpck_require__(98820); +const setup_configuration_defaults_1 = __nccwpck_require__(31713); +const agent_configuration_validation_policy_1 = __nccwpck_require__(4345); +const setup_configuration_storage_policy_1 = __nccwpck_require__(60368); +const issue_inactivity_1 = __nccwpck_require__(7703); +const deployment_configuration_1 = __nccwpck_require__(5664); +const locale_1 = __nccwpck_require__(64552); +const issue_workflow_profile_1 = __nccwpck_require__(62721); +const setup_issue_workflow_policy_1 = __nccwpck_require__(47280); +const pull_request_approval_policy_1 = __nccwpck_require__(53553); function validateSetupConfiguration(configuration, options = {}) { const errors = []; errors.push(...(0, pull_request_approval_policy_1.validatePullRequestApprovalPolicy)(configuration.pullRequestApproval, options.allowIncompleteApproval === true)); @@ -47098,14 +47098,14 @@ function validateLocale(errors, label, value, optional) { /***/ }), -/***/ 43562: +/***/ 61975: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildSetupCredentialRequirements = buildSetupCredentialRequirements; -const setup_configuration_defaults_1 = __nccwpck_require__(23381); +const setup_configuration_defaults_1 = __nccwpck_require__(31713); const SECRET_BY_MODEL_PROVIDER = { openai: 'OPENAI_API_KEY', anthropic: 'ANTHROPIC_API_KEY', @@ -47213,7 +47213,7 @@ function uniqueDefined(current, next) { /***/ }), -/***/ 80226: +/***/ 67183: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -47222,10 +47222,10 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SETUP_DOCTOR_CATALOG_DEFINITIONS = exports.SPANISH_SETUP_DOCTOR_DEFINITION = exports.ENGLISH_SETUP_DOCTOR_DEFINITION = exports.SETUP_DOCTOR_MESSAGE_IDS = void 0; exports.resolveStaticSetupDoctorCatalog = resolveStaticSetupDoctorCatalog; exports.resolveSetupDoctorCatalog = resolveSetupDoctorCatalog; -const message_catalog_1 = __nccwpck_require__(27097); -const resolved_message_catalog_policy_1 = __nccwpck_require__(55069); -const merge_queue_message_catalog_1 = __nccwpck_require__(56033); -const approval_doctor_message_catalog_1 = __nccwpck_require__(36904); +const message_catalog_1 = __nccwpck_require__(5313); +const resolved_message_catalog_policy_1 = __nccwpck_require__(98248); +const merge_queue_message_catalog_1 = __nccwpck_require__(22459); +const approval_doctor_message_catalog_1 = __nccwpck_require__(24591); const DOCTOR_ONLY_MESSAGE_IDS = Object.freeze([ 'doctor.title', 'doctor.title.partial', @@ -47551,7 +47551,7 @@ function resolveSetupDoctorCatalog(locale, configuration, resolver) { /***/ }), -/***/ 67615: +/***/ 81332: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -47562,10 +47562,10 @@ exports.doctorCheck = doctorCheck; exports.skippedDoctorCheck = skippedDoctorCheck; exports.normalizedDoctorPathId = normalizedDoctorPathId; exports.buildLocaleDoctorChecks = buildLocaleDoctorChecks; -const locale_1 = __nccwpck_require__(15386); -const message_catalog_1 = __nccwpck_require__(27097); -const agent_1 = __nccwpck_require__(89040); -const setup_doctor_message_catalog_1 = __nccwpck_require__(80226); +const locale_1 = __nccwpck_require__(64552); +const message_catalog_1 = __nccwpck_require__(5313); +const agent_1 = __nccwpck_require__(95407); +const setup_doctor_message_catalog_1 = __nccwpck_require__(67183); const EMPTY_TOTALS = { pass: 0, warn: 0, @@ -47656,7 +47656,7 @@ function localeDoctorCheck(scope, configured, effective, inheritedWhenEmpty, dyn /***/ }), -/***/ 67323: +/***/ 36206: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -47664,7 +47664,7 @@ function localeDoctorCheck(scope, configured, effective, inheritedWhenEmpty, dyn Object.defineProperty(exports, "__esModule", ({ value: true })); exports.selectedInitialLabels = selectedInitialLabels; exports.selectedInitialIssueTypes = selectedInitialIssueTypes; -const setup_issue_workflow_policy_1 = __nccwpck_require__(81182); +const setup_issue_workflow_policy_1 = __nccwpck_require__(47280); /** Removes workflow-specific resources that are not part of the effective profile. */ function selectedInitialLabels(labels, configuration) { if (!configuration) @@ -47723,7 +47723,7 @@ function selectedInitialIssueTypes(issueTypes, configuration) { /***/ }), -/***/ 81182: +/***/ 47280: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -47733,7 +47733,7 @@ exports.effectiveIssueWorkflowProfile = effectiveIssueWorkflowProfile; exports.effectiveIssueWorkflowFeatures = effectiveIssueWorkflowFeatures; exports.effectiveIssueWorkflowLabels = effectiveIssueWorkflowLabels; exports.effectiveIssueFormLabels = effectiveIssueFormLabels; -const issue_workflow_profile_1 = __nccwpck_require__(26744); +const issue_workflow_profile_1 = __nccwpck_require__(62721); /** Applies feature switches to the explicit issue workflow selection. */ function effectiveIssueWorkflowProfile(configuration) { const configured = configuration.issueWorkflows?.enabled ?? issue_workflow_profile_1.ISSUE_WORKFLOW_KINDS; @@ -47786,7 +47786,7 @@ function effectiveIssueFormLabels(configuration) { /***/ }), -/***/ 53289: +/***/ 55254: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -47826,7 +47826,7 @@ function buildSetupJourneyView(repository, stage, mutationStarted, outcome, choi /***/ }), -/***/ 54718: +/***/ 96850: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -47906,7 +47906,7 @@ function buildSetupPatCreationUrl(input) { /***/ }), -/***/ 30748: +/***/ 66964: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -47915,7 +47915,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.fixedSetupPatIntentQuestionIds = fixedSetupPatIntentQuestionIds; exports.setupPatIntentNeedsOwnerKind = setupPatIntentNeedsOwnerKind; exports.setupPatIntentOwnerConflict = setupPatIntentOwnerConflict; -const setup_token_permission_policy_1 = __nccwpck_require__(99590); +const setup_token_permission_policy_1 = __nccwpck_require__(10947); /** Local inputs with explicit precedence are decisions, not questions. */ function fixedSetupPatIntentQuestionIds(overrides, skipVariables, skipSecrets) { const fixed = []; @@ -47960,7 +47960,7 @@ function setupPatIntentOwnerConflict(configuration, ownerKind) { /***/ }), -/***/ 10267: +/***/ 48581: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -47979,7 +47979,7 @@ function summarizeSetupPermissions(requirements) { /***/ }), -/***/ 6009: +/***/ 65207: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -47992,9 +47992,9 @@ exports.transitionSetupQuestionnaire = transitionSetupQuestionnaire; exports.enterSetupConfirmation = enterSetupConfirmation; exports.finishSetupQuestionnaire = finishSetupQuestionnaire; exports.setupQuestionnaireStateLabel = setupQuestionnaireStateLabel; -const setup_configuration_clone_policy_1 = __nccwpck_require__(85881); -const setup_configuration_defaults_1 = __nccwpck_require__(23381); -const issue_workflow_profile_1 = __nccwpck_require__(26744); +const setup_configuration_clone_policy_1 = __nccwpck_require__(6802); +const setup_configuration_defaults_1 = __nccwpck_require__(31713); +const issue_workflow_profile_1 = __nccwpck_require__(62721); const AGENT_PROVIDERS = ['codex', 'opencode', 'cursor']; const MODEL_PROVIDERS = ['openai', 'anthropic', 'google', 'openrouter', 'opencode', 'local']; const PERMISSION_INTENT_QUESTION_IDS = new Set([ @@ -48492,7 +48492,7 @@ function projectLabel(field) { /***/ }), -/***/ 92567: +/***/ 41599: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -48524,7 +48524,7 @@ function sameSetupRemoteFacts(left, right) { /***/ }), -/***/ 65640: +/***/ 19750: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -48601,7 +48601,7 @@ function unverifiable(requirement, message) { /***/ }), -/***/ 99590: +/***/ 10947: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -48614,10 +48614,10 @@ exports.buildSetupPatIntentUncertainty = buildSetupPatIntentUncertainty; exports.requiredSetupPatPermissionDelta = requiredSetupPatPermissionDelta; exports.buildWorkflowPatPermissionRequirements = buildWorkflowPatPermissionRequirements; exports.normalizePermissionRequirements = normalizePermissionRequirements; -const setup_configuration_plan_1 = __nccwpck_require__(87770); -const setup_credential_requirement_policy_1 = __nccwpck_require__(43562); -const setup_issue_workflow_policy_1 = __nccwpck_require__(81182); -const setup_configuration_storage_policy_1 = __nccwpck_require__(2554); +const setup_configuration_plan_1 = __nccwpck_require__(44018); +const setup_credential_requirement_policy_1 = __nccwpck_require__(61975); +const setup_issue_workflow_policy_1 = __nccwpck_require__(47280); +const setup_configuration_storage_policy_1 = __nccwpck_require__(60368); const requirement = (input) => ({ id: `${input.role}.${input.scope}.${input.permission.toLowerCase().replace(/[^a-z0-9]+/gu, '-')}`, applicability: 'required', @@ -48847,7 +48847,7 @@ function levelRank(level) { /***/ }), -/***/ 3449: +/***/ 62186: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -48856,9 +48856,9 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildCopilotStatusSnapshot = buildCopilotStatusSnapshot; exports.buildCopilotStatusResult = buildCopilotStatusResult; exports.formatCopilotStatus = formatCopilotStatus; -const result_1 = __nccwpck_require__(73817); -const bugbot_result_finding_state_projection_policy_1 = __nccwpck_require__(98117); -const publication_message_catalog_1 = __nccwpck_require__(34223); +const result_1 = __nccwpck_require__(61444); +const bugbot_result_finding_state_projection_policy_1 = __nccwpck_require__(22443); +const publication_message_catalog_1 = __nccwpck_require__(46042); /** Builds a read-only status snapshot from the facts already loaded by setup. */ function buildCopilotStatusSnapshot(execution) { const issueLabels = [...(execution.labels?.currentIssueLabels ?? [])]; @@ -48947,7 +48947,7 @@ function formatCopilotStatus(snapshot, locale = 'en-US', catalog = (0, publicati /***/ }), -/***/ 43193: +/***/ 97549: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -48976,7 +48976,7 @@ function calculateJitteredWorkflowDelay(baseDelayMilliseconds, randomValue, poli /***/ }), -/***/ 6152: +/***/ 73001: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -49039,7 +49039,7 @@ function setGlobalLoggerDebug(debug, isRemote = false) { /***/ }), -/***/ 46445: +/***/ 81504: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -49090,7 +49090,7 @@ function toPullRequestReviewOperationError(error, operation, context) { /***/ }), -/***/ 77658: +/***/ 55294: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -49105,13 +49105,13 @@ exports.deploymentSuccess = deploymentSuccess; exports.blockedDeploymentResult = blockedDeploymentResult; exports.shouldRecordUnexpectedFailure = shouldRecordUnexpectedFailure; exports.semanticCleanupError = semanticCleanupError; -const application_error_1 = __nccwpck_require__(75999); -const deployment_plan_policy_1 = __nccwpck_require__(8352); -const deployment_presentation_policy_1 = __nccwpck_require__(83221); -const deployment_message_catalog_1 = __nccwpck_require__(79364); -const deployment_operation_1 = __nccwpck_require__(92730); -const merge_queue_readiness_1 = __nccwpck_require__(12515); -const result_1 = __nccwpck_require__(73817); +const application_error_1 = __nccwpck_require__(2965); +const deployment_plan_policy_1 = __nccwpck_require__(86485); +const deployment_presentation_policy_1 = __nccwpck_require__(85458); +const deployment_message_catalog_1 = __nccwpck_require__(71375); +const deployment_operation_1 = __nccwpck_require__(17176); +const merge_queue_readiness_1 = __nccwpck_require__(36637); +const result_1 = __nccwpck_require__(61444); exports.DEPLOYMENT_ORCHESTRATION_TASK_ID = "DeploymentOrchestrationUseCase"; class DeploymentOrchestrationRuntime { constructor(dependencies, stateBoundary) { @@ -49395,16 +49395,16 @@ function failureCategory(operation) { /***/ }), -/***/ 27827: +/***/ 55649: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SupersededDeploymentInvocationError = exports.DeploymentStateBoundary = void 0; -const deployment_state_fence_1 = __nccwpck_require__(72369); -const deployment_lifecycle_policy_1 = __nccwpck_require__(54037); -const application_error_1 = __nccwpck_require__(75999); +const deployment_state_fence_1 = __nccwpck_require__(32481); +const deployment_lifecycle_policy_1 = __nccwpck_require__(83013); +const application_error_1 = __nccwpck_require__(2965); class DeploymentStateBoundary { constructor(state, labels) { this.state = state; @@ -49484,14 +49484,14 @@ exports.SupersededDeploymentInvocationError = SupersededDeploymentInvocationErro /***/ }), -/***/ 41601: +/***/ 21113: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.CheckProgressUseCase = void 0; -const check_progress_workflow_1 = __nccwpck_require__(94343); +const check_progress_workflow_1 = __nccwpck_require__(54747); /** Application boundary for assessing and publishing issue progress. */ class CheckProgressUseCase { constructor(issueDescriptionQueryPort, issueLabelsPort, issueProgressPort, branchRepository, pullRequestRepository, aiRepository, publicationSourceQuery) { @@ -49521,21 +49521,21 @@ exports.CheckProgressUseCase = CheckProgressUseCase; /***/ }), -/***/ 94343: +/***/ 54747: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runCheckProgressWorkflow = runCheckProgressWorkflow; -const result_1 = __nccwpck_require__(73817); -const logging_ports_1 = __nccwpck_require__(6152); -const task_emoji_1 = __nccwpck_require__(46103); -const sync_progress_labels_to_open_pull_requests_1 = __nccwpck_require__(18277); -const progress_summary_builder_1 = __nccwpck_require__(62721); -const progress_analysis_workflow_1 = __nccwpck_require__(88729); -const application_error_1 = __nccwpck_require__(75999); -const publication_outcome_policy_1 = __nccwpck_require__(79719); +const result_1 = __nccwpck_require__(61444); +const logging_ports_1 = __nccwpck_require__(73001); +const task_emoji_1 = __nccwpck_require__(83142); +const sync_progress_labels_to_open_pull_requests_1 = __nccwpck_require__(24603); +const progress_summary_builder_1 = __nccwpck_require__(96279); +const progress_analysis_workflow_1 = __nccwpck_require__(67608); +const application_error_1 = __nccwpck_require__(2965); +const publication_outcome_policy_1 = __nccwpck_require__(11035); /** Publishes a completed progress assessment after the analysis workflow succeeds. */ async function runCheckProgressWorkflow(param, taskId, dependencies) { (0, logging_ports_1.logInfo)(`${(0, task_emoji_1.getTaskEmoji)(taskId)} Executing ${taskId}.`); @@ -49642,14 +49642,14 @@ function logProgressAssessment(progress, summary, reasoning, remaining) { /***/ }), -/***/ 84579: +/***/ 56690: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.CloseInactiveIssuesUseCase = void 0; -const close_inactive_issues_workflow_1 = __nccwpck_require__(86288); +const close_inactive_issues_workflow_1 = __nccwpck_require__(12632); /** Application boundary for the scheduled inactivity-maintenance action. */ class CloseInactiveIssuesUseCase { constructor(issueQueryPort, issueClosurePort, clock, catalogResolver) { @@ -49673,19 +49673,19 @@ exports.CloseInactiveIssuesUseCase = CloseInactiveIssuesUseCase; /***/ }), -/***/ 86288: +/***/ 12632: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runCloseInactiveIssuesWorkflow = runCloseInactiveIssuesWorkflow; -const result_1 = __nccwpck_require__(73817); -const issue_inactivity_1 = __nccwpck_require__(38572); -const logging_ports_1 = __nccwpck_require__(6152); -const application_error_1 = __nccwpck_require__(75999); -const inactivity_message_catalog_1 = __nccwpck_require__(74902); -const inactivity_notification_policy_1 = __nccwpck_require__(1572); +const result_1 = __nccwpck_require__(61444); +const issue_inactivity_1 = __nccwpck_require__(7703); +const logging_ports_1 = __nccwpck_require__(73001); +const application_error_1 = __nccwpck_require__(2965); +const inactivity_message_catalog_1 = __nccwpck_require__(78447); +const inactivity_notification_policy_1 = __nccwpck_require__(62453); const TASK_ID = 'CloseInactiveIssuesUseCase'; /** Scans waiting issues and closes only candidates that remain inactive. */ async function runCloseInactiveIssuesWorkflow(param, dependencies) { @@ -49839,7 +49839,7 @@ function unique(values) { /***/ }), -/***/ 76549: +/***/ 59528: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -49848,8 +49848,8 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.validateReleaseInput = validateReleaseInput; exports.normalizeVersion = normalizeVersion; exports.versionForRelease = versionForRelease; -const input_keys_1 = __nccwpck_require__(88539); -const application_error_1 = __nccwpck_require__(75999); +const input_keys_1 = __nccwpck_require__(83725); +const application_error_1 = __nccwpck_require__(2965); const SEMVER_PATTERN = /^\d+(\.\d+){0,2}$/; function validateReleaseInput(input) { if (!input.version.length) @@ -49877,16 +49877,16 @@ function versionForRelease(version) { /***/ }), -/***/ 25258: +/***/ 68781: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.CreateReleaseUseCase = void 0; -const logging_ports_1 = __nccwpck_require__(6152); -const task_emoji_1 = __nccwpck_require__(46103); -const create_release_workflow_1 = __nccwpck_require__(75138); +const logging_ports_1 = __nccwpck_require__(73001); +const task_emoji_1 = __nccwpck_require__(83142); +const create_release_workflow_1 = __nccwpck_require__(96528); class CreateReleaseUseCase { constructor(repositoryReleasePort) { this.repositoryReleasePort = repositoryReleasePort; @@ -49902,18 +49902,18 @@ exports.CreateReleaseUseCase = CreateReleaseUseCase; /***/ }), -/***/ 75138: +/***/ 96528: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runCreateRelease = runCreateRelease; -const result_1 = __nccwpck_require__(73817); -const logging_ports_1 = __nccwpck_require__(6152); -const create_release_policy_1 = __nccwpck_require__(76549); -const deployment_continuation_guard_1 = __nccwpck_require__(1779); -const application_error_1 = __nccwpck_require__(75999); +const result_1 = __nccwpck_require__(61444); +const logging_ports_1 = __nccwpck_require__(73001); +const create_release_policy_1 = __nccwpck_require__(59528); +const deployment_continuation_guard_1 = __nccwpck_require__(47063); +const application_error_1 = __nccwpck_require__(2965); async function runCreateRelease(param, taskId, repositoryReleasePort) { const operation = param.operation; const continuationError = (0, deployment_continuation_guard_1.validateDeploymentContinuation)(operation, param.requestedOperationId, ["publishing"], param.requestedVersion); @@ -49964,16 +49964,16 @@ function failureResult(taskId, message, code) { /***/ }), -/***/ 22120: +/***/ 27977: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.CreateTagUseCase = void 0; -const logging_ports_1 = __nccwpck_require__(6152); -const task_emoji_1 = __nccwpck_require__(46103); -const create_tag_workflow_1 = __nccwpck_require__(23539); +const logging_ports_1 = __nccwpck_require__(73001); +const task_emoji_1 = __nccwpck_require__(83142); +const create_tag_workflow_1 = __nccwpck_require__(69061); class CreateTagUseCase { constructor(repositoryReleasePort) { this.repositoryReleasePort = repositoryReleasePort; @@ -49989,17 +49989,17 @@ exports.CreateTagUseCase = CreateTagUseCase; /***/ }), -/***/ 23539: +/***/ 69061: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runCreateTag = runCreateTag; -const result_1 = __nccwpck_require__(73817); -const logging_ports_1 = __nccwpck_require__(6152); -const deployment_continuation_guard_1 = __nccwpck_require__(1779); -const application_error_1 = __nccwpck_require__(75999); +const result_1 = __nccwpck_require__(61444); +const logging_ports_1 = __nccwpck_require__(73001); +const deployment_continuation_guard_1 = __nccwpck_require__(47063); +const application_error_1 = __nccwpck_require__(2965); async function runCreateTag(param, taskId, repositoryTagPort) { const validationFailure = validateTagInput(param, taskId); if (validationFailure) @@ -50039,14 +50039,14 @@ function noTagResult(taskId, tagName) { /***/ }), -/***/ 28399: +/***/ 57402: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.AcceptPromotionHandler = void 0; -const deployment_orchestration_runtime_1 = __nccwpck_require__(77658); +const deployment_orchestration_runtime_1 = __nccwpck_require__(55294); class AcceptPromotionHandler { constructor(runtime) { this.runtime = runtime; @@ -50101,17 +50101,17 @@ function matchesPromotion(operation, pullRequest) { /***/ }), -/***/ 46361: +/***/ 92224: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.ConfirmPublicationHandler = void 0; -const application_error_1 = __nccwpck_require__(75999); -const deployment_plan_policy_1 = __nccwpck_require__(8352); -const deployment_operation_1 = __nccwpck_require__(92730); -const deployment_orchestration_runtime_1 = __nccwpck_require__(77658); +const application_error_1 = __nccwpck_require__(2965); +const deployment_plan_policy_1 = __nccwpck_require__(86485); +const deployment_operation_1 = __nccwpck_require__(17176); +const deployment_orchestration_runtime_1 = __nccwpck_require__(55294); class ConfirmPublicationHandler { constructor(runtime, reconciliation) { this.runtime = runtime; @@ -50202,17 +50202,17 @@ function requireProductionSha(operation) { /***/ }), -/***/ 85138: +/***/ 58024: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.ContinueDeploymentHandler = void 0; -const application_error_1 = __nccwpck_require__(75999); -const deployment_operation_1 = __nccwpck_require__(92730); -const managed_pull_request_1 = __nccwpck_require__(95914); -const deployment_orchestration_runtime_1 = __nccwpck_require__(77658); +const application_error_1 = __nccwpck_require__(2965); +const deployment_operation_1 = __nccwpck_require__(17176); +const managed_pull_request_1 = __nccwpck_require__(7975); +const deployment_orchestration_runtime_1 = __nccwpck_require__(55294); class ContinueDeploymentHandler { constructor(runtime, acceptPromotion, reconciliation) { this.runtime = runtime; @@ -50288,17 +50288,17 @@ function assertSameRepository(context, pullRequest) { /***/ }), -/***/ 43877: +/***/ 13502: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.PreparePromotionHandler = void 0; -const application_error_1 = __nccwpck_require__(75999); -const deployment_plan_policy_1 = __nccwpck_require__(8352); -const deployment_operation_1 = __nccwpck_require__(92730); -const deployment_orchestration_runtime_1 = __nccwpck_require__(77658); +const application_error_1 = __nccwpck_require__(2965); +const deployment_plan_policy_1 = __nccwpck_require__(86485); +const deployment_operation_1 = __nccwpck_require__(17176); +const deployment_orchestration_runtime_1 = __nccwpck_require__(55294); class PreparePromotionHandler { constructor(runtime, acceptPromotion) { this.runtime = runtime; @@ -50455,16 +50455,16 @@ function pendingPromotion(operation, pullRequest) { /***/ }), -/***/ 3344: +/***/ 36195: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.ReconciliationHandler = void 0; -const deployment_plan_policy_1 = __nccwpck_require__(8352); -const deployment_operation_1 = __nccwpck_require__(92730); -const deployment_orchestration_runtime_1 = __nccwpck_require__(77658); +const deployment_plan_policy_1 = __nccwpck_require__(86485); +const deployment_operation_1 = __nccwpck_require__(17176); +const deployment_orchestration_runtime_1 = __nccwpck_require__(55294); class ReconciliationHandler { constructor(runtime) { this.runtime = runtime; @@ -50604,17 +50604,17 @@ exports.ReconciliationHandler = ReconciliationHandler; /***/ }), -/***/ 66571: +/***/ 93088: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.RecordFailureHandler = void 0; -const application_error_1 = __nccwpck_require__(75999); -const result_1 = __nccwpck_require__(73817); -const deployment_operation_1 = __nccwpck_require__(92730); -const deployment_orchestration_runtime_1 = __nccwpck_require__(77658); +const application_error_1 = __nccwpck_require__(2965); +const result_1 = __nccwpck_require__(61444); +const deployment_operation_1 = __nccwpck_require__(17176); +const deployment_orchestration_runtime_1 = __nccwpck_require__(55294); class RecordFailureHandler { constructor(runtime) { this.runtime = runtime; @@ -50653,23 +50653,23 @@ function failureCategory(phase) { /***/ }), -/***/ 36850: +/***/ 67132: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.DeploymentOrchestrationUseCase = void 0; -const application_error_1 = __nccwpck_require__(75999); -const deployment_orchestration_runtime_1 = __nccwpck_require__(77658); -const deployment_state_boundary_1 = __nccwpck_require__(27827); -const result_1 = __nccwpck_require__(73817); -const accept_promotion_handler_1 = __nccwpck_require__(28399); -const confirm_publication_handler_1 = __nccwpck_require__(46361); -const continue_deployment_handler_1 = __nccwpck_require__(85138); -const prepare_promotion_handler_1 = __nccwpck_require__(43877); -const reconciliation_handler_1 = __nccwpck_require__(3344); -const record_failure_handler_1 = __nccwpck_require__(66571); +const application_error_1 = __nccwpck_require__(2965); +const deployment_orchestration_runtime_1 = __nccwpck_require__(55294); +const deployment_state_boundary_1 = __nccwpck_require__(55649); +const result_1 = __nccwpck_require__(61444); +const accept_promotion_handler_1 = __nccwpck_require__(57402); +const confirm_publication_handler_1 = __nccwpck_require__(92224); +const continue_deployment_handler_1 = __nccwpck_require__(58024); +const prepare_promotion_handler_1 = __nccwpck_require__(13502); +const reconciliation_handler_1 = __nccwpck_require__(36195); +const record_failure_handler_1 = __nccwpck_require__(93088); class DeploymentOrchestrationUseCase { constructor(dependencies) { this.taskId = deployment_orchestration_runtime_1.DEPLOYMENT_ORCHESTRATION_TASK_ID; @@ -50726,14 +50726,14 @@ exports.DeploymentOrchestrationUseCase = DeploymentOrchestrationUseCase; /***/ }), -/***/ 38575: +/***/ 72387: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.findIssueBranch = findIssueBranch; -const logging_ports_1 = __nccwpck_require__(6152); +const logging_ports_1 = __nccwpck_require__(73001); async function findIssueBranch(param, repository) { if (param.pushedBranch) return param.pushedBranch; @@ -50751,14 +50751,14 @@ async function findIssueBranch(param, repository) { /***/ }), -/***/ 84837: +/***/ 50658: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.InitialSetupUseCase = void 0; -const initial_setup_workflow_1 = __nccwpck_require__(18079); +const initial_setup_workflow_1 = __nccwpck_require__(32599); /** Application boundary for provisioning a repository for Copilot automation. */ class InitialSetupUseCase { constructor(authenticatedUserPort, initialLabelProvisioningPort, issueTypeProvisioningPort, latestTagQueryPort, repositoryDefaultBranchPort, repositoryTagPort, setupWorkspacePort, setupRepositoryVariablesPort, setupRepositorySecretsPort, setupRemoteConfigurationReadPort) { @@ -50794,21 +50794,21 @@ exports.InitialSetupUseCase = InitialSetupUseCase; /***/ }), -/***/ 18079: +/***/ 32599: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runInitialSetupWorkflow = runInitialSetupWorkflow; -const result_1 = __nccwpck_require__(73817); -const version_policy_1 = __nccwpck_require__(8381); -const logging_ports_1 = __nccwpck_require__(6152); -const task_emoji_1 = __nccwpck_require__(46103); -const setup_resource_provisioning_1 = __nccwpck_require__(94894); -const application_error_1 = __nccwpck_require__(75999); -const setup_issue_resource_policy_1 = __nccwpck_require__(67323); -const setup_configuration_policy_1 = __nccwpck_require__(56637); +const result_1 = __nccwpck_require__(61444); +const version_policy_1 = __nccwpck_require__(36707); +const logging_ports_1 = __nccwpck_require__(73001); +const task_emoji_1 = __nccwpck_require__(83142); +const setup_resource_provisioning_1 = __nccwpck_require__(64709); +const application_error_1 = __nccwpck_require__(2965); +const setup_issue_resource_policy_1 = __nccwpck_require__(36206); +const setup_configuration_policy_1 = __nccwpck_require__(93015); const TASK_ID = 'InitialSetupUseCase'; /** Runs repository setup as an ordered application workflow with explicit port dependencies. */ async function runInitialSetupWorkflow(request, dependencies) { @@ -50994,7 +50994,7 @@ function fromMessages(messages, code) { /***/ }), -/***/ 28121: +/***/ 724: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -51084,20 +51084,20 @@ function projectEvidence(source) { /***/ }), -/***/ 84542: +/***/ 34342: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.ObserveBranchSyncUseCase = void 0; -const result_1 = __nccwpck_require__(73817); -const branch_sync_notification_policy_1 = __nccwpck_require__(79895); -const logging_ports_1 = __nccwpck_require__(6152); -const application_error_1 = __nccwpck_require__(75999); -const branch_sync_message_catalog_1 = __nccwpck_require__(89245); -const transition_notification_workflow_1 = __nccwpck_require__(1725); -const publication_outcome_policy_1 = __nccwpck_require__(79719); +const result_1 = __nccwpck_require__(61444); +const branch_sync_notification_policy_1 = __nccwpck_require__(90741); +const logging_ports_1 = __nccwpck_require__(73001); +const application_error_1 = __nccwpck_require__(2965); +const branch_sync_message_catalog_1 = __nccwpck_require__(71641); +const transition_notification_workflow_1 = __nccwpck_require__(93066); +const publication_outcome_policy_1 = __nccwpck_require__(11035); const TASK_ID = "ObserveBranchSyncUseCase"; /** * Cheap push-time observer. It only queries branch relationships/comparisons @@ -51218,24 +51218,24 @@ function failure(message, cause, payload) { /***/ }), -/***/ 88729: +/***/ 67608: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.analyzeProgress = analyzeProgress; -const agent_1 = __nccwpck_require__(79937); -const result_1 = __nccwpck_require__(73817); -const agent_task_policy_1 = __nccwpck_require__(85712); -const prompts_1 = __nccwpck_require__(69518); -const logging_ports_1 = __nccwpck_require__(6152); -const project_context_instruction_1 = __nccwpck_require__(63907); -const find_issue_branch_1 = __nccwpck_require__(38575); -const progress_prerequisite_policy_1 = __nccwpck_require__(31001); -const progress_response_1 = __nccwpck_require__(64264); -const application_error_1 = __nccwpck_require__(75999); -const agent_output_locale_policy_1 = __nccwpck_require__(30601); +const agent_1 = __nccwpck_require__(71889); +const result_1 = __nccwpck_require__(61444); +const agent_task_policy_1 = __nccwpck_require__(2601); +const prompts_1 = __nccwpck_require__(71854); +const logging_ports_1 = __nccwpck_require__(73001); +const project_context_instruction_1 = __nccwpck_require__(36158); +const find_issue_branch_1 = __nccwpck_require__(72387); +const progress_prerequisite_policy_1 = __nccwpck_require__(96802); +const progress_response_1 = __nccwpck_require__(68049); +const application_error_1 = __nccwpck_require__(2965); +const agent_output_locale_policy_1 = __nccwpck_require__(2584); /** Loads progress context and asks the configured agent for an assessment. */ async function analyzeProgress(param, taskId, dependencies) { const issueNumber = param.issueNumber; @@ -51320,7 +51320,7 @@ function failure(taskId, message, code) { /***/ }), -/***/ 31001: +/***/ 96802: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -51346,7 +51346,7 @@ function validateProgressPrerequisites(input) { /***/ }), -/***/ 64264: +/***/ 68049: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -51354,8 +51354,8 @@ function validateProgressPrerequisites(input) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.PROGRESS_RESPONSE_SCHEMA = void 0; exports.parseProgressResponse = parseProgressResponse; -const agent_output_locale_policy_1 = __nccwpck_require__(30601); -const application_error_1 = __nccwpck_require__(75999); +const agent_output_locale_policy_1 = __nccwpck_require__(2584); +const application_error_1 = __nccwpck_require__(2965); exports.PROGRESS_RESPONSE_SCHEMA = { type: 'object', properties: { @@ -51385,7 +51385,7 @@ function parseProgressResponse(response, targetLocale) { /***/ }), -/***/ 62721: +/***/ 96279: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -51417,16 +51417,16 @@ function buildProgressSummaryMessage({ summary, progress, remaining, reasoning } /***/ }), -/***/ 68891: +/***/ 31153: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.PublishGithubActionUseCase = void 0; -const logging_ports_1 = __nccwpck_require__(6152); -const task_emoji_1 = __nccwpck_require__(46103); -const publish_github_action_workflow_1 = __nccwpck_require__(63037); +const logging_ports_1 = __nccwpck_require__(73001); +const task_emoji_1 = __nccwpck_require__(83142); +const publish_github_action_workflow_1 = __nccwpck_require__(77361); class PublishGithubActionUseCase { constructor(repositoryTagPort, repositoryReleasePort) { this.repositoryTagPort = repositoryTagPort; @@ -51443,18 +51443,18 @@ exports.PublishGithubActionUseCase = PublishGithubActionUseCase; /***/ }), -/***/ 63037: +/***/ 77361: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runPublishGithubAction = runPublishGithubAction; -const result_1 = __nccwpck_require__(73817); -const input_keys_1 = __nccwpck_require__(88539); -const logging_ports_1 = __nccwpck_require__(6152); -const deployment_continuation_guard_1 = __nccwpck_require__(1779); -const application_error_1 = __nccwpck_require__(75999); +const result_1 = __nccwpck_require__(61444); +const input_keys_1 = __nccwpck_require__(83725); +const logging_ports_1 = __nccwpck_require__(73001); +const deployment_continuation_guard_1 = __nccwpck_require__(47063); +const application_error_1 = __nccwpck_require__(2965); async function runPublishGithubAction(param, taskId, repositoryTagPort, repositoryReleasePort) { const validationFailure = validateVersion(param, taskId); if (validationFailure) @@ -51499,16 +51499,16 @@ function failureResult(taskId, sourceTag, targetTag) { /***/ }), -/***/ 61313: +/***/ 23758: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.PublishIssueCommentUseCase = void 0; -const logging_ports_1 = __nccwpck_require__(6152); -const task_emoji_1 = __nccwpck_require__(46103); -const publish_issue_comment_workflow_1 = __nccwpck_require__(30626); +const logging_ports_1 = __nccwpck_require__(73001); +const task_emoji_1 = __nccwpck_require__(83142); +const publish_issue_comment_workflow_1 = __nccwpck_require__(95981); /** Application boundary for creating or updating a specific issue comment. */ class PublishIssueCommentUseCase { constructor(issueCommentPort) { @@ -51525,16 +51525,16 @@ exports.PublishIssueCommentUseCase = PublishIssueCommentUseCase; /***/ }), -/***/ 30626: +/***/ 95981: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runPublishIssueComment = runPublishIssueComment; -const result_1 = __nccwpck_require__(73817); -const logging_ports_1 = __nccwpck_require__(6152); -const application_error_1 = __nccwpck_require__(75999); +const result_1 = __nccwpck_require__(61444); +const logging_ports_1 = __nccwpck_require__(73001); +const application_error_1 = __nccwpck_require__(2965); async function runPublishIssueComment(param, taskId, issueCommentPort) { if (param.kind === 'invalid') { return [new result_1.Result({ @@ -51583,19 +51583,19 @@ function appendCommentContent(previous, addition) { /***/ }), -/***/ 65928: +/***/ 78348: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildRecommendationResult = buildRecommendationResult; -const result_1 = __nccwpck_require__(73817); -const recommendation_policy_1 = __nccwpck_require__(39410); -const logging_ports_1 = __nccwpck_require__(6152); -const application_error_1 = __nccwpck_require__(75999); -const agent_output_locale_policy_1 = __nccwpck_require__(30601); -const implementation_plan_1 = __nccwpck_require__(77001); +const result_1 = __nccwpck_require__(61444); +const recommendation_policy_1 = __nccwpck_require__(83857); +const logging_ports_1 = __nccwpck_require__(73001); +const application_error_1 = __nccwpck_require__(2965); +const agent_output_locale_policy_1 = __nccwpck_require__(2584); +const implementation_plan_1 = __nccwpck_require__(52620); function buildRecommendationResult(param, taskId, response, issueDescriptionFingerprint, previousRecommendation, issueNumber) { const extracted = extractImplementationPlan(response, param.targetLocale); if (!extracted) { @@ -51682,14 +51682,14 @@ function hasOnlyResponseKeys(payload) { /***/ }), -/***/ 73746: +/***/ 33526: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.RecommendStepsUseCase = void 0; -const recommend_steps_workflow_1 = __nccwpck_require__(77522); +const recommend_steps_workflow_1 = __nccwpck_require__(17415); /** Application boundary for generating non-duplicated implementation guidance. */ class RecommendStepsUseCase { constructor(issueDescriptionQueryPort, aiRepository) { @@ -51709,26 +51709,26 @@ exports.RecommendStepsUseCase = RecommendStepsUseCase; /***/ }), -/***/ 77522: +/***/ 17415: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runRecommendStepsWorkflow = runRecommendStepsWorkflow; -const agent_1 = __nccwpck_require__(79937); -const result_1 = __nccwpck_require__(73817); -const agent_task_policy_1 = __nccwpck_require__(85712); -const recommendation_policy_1 = __nccwpck_require__(39410); -const prompts_1 = __nccwpck_require__(69518); -const logging_ports_1 = __nccwpck_require__(6152); -const project_context_instruction_1 = __nccwpck_require__(63907); -const task_emoji_1 = __nccwpck_require__(46103); -const recommend_steps_result_policy_1 = __nccwpck_require__(65928); -const application_error_1 = __nccwpck_require__(75999); -const agent_response_schemas_1 = __nccwpck_require__(25603); -const agent_output_locale_policy_1 = __nccwpck_require__(30601); -const implementation_plan_1 = __nccwpck_require__(77001); +const agent_1 = __nccwpck_require__(71889); +const result_1 = __nccwpck_require__(61444); +const agent_task_policy_1 = __nccwpck_require__(2601); +const recommendation_policy_1 = __nccwpck_require__(83857); +const prompts_1 = __nccwpck_require__(71854); +const logging_ports_1 = __nccwpck_require__(73001); +const project_context_instruction_1 = __nccwpck_require__(36158); +const task_emoji_1 = __nccwpck_require__(83142); +const recommend_steps_result_policy_1 = __nccwpck_require__(78348); +const application_error_1 = __nccwpck_require__(2965); +const agent_response_schemas_1 = __nccwpck_require__(63523); +const agent_output_locale_policy_1 = __nccwpck_require__(2584); +const implementation_plan_1 = __nccwpck_require__(52620); /** Runs the recommendation policy and agent interaction for an issue. */ async function runRecommendStepsWorkflow(param, taskId, dependencies) { (0, logging_ports_1.logInfo)(`${(0, task_emoji_1.getTaskEmoji)(taskId)} Executing ${taskId}.`); @@ -51825,7 +51825,7 @@ function failure(taskId, message, code) { /***/ }), -/***/ 94894: +/***/ 64709: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -51835,9 +51835,9 @@ exports.ensureRepositoryVariables = ensureRepositoryVariables; exports.ensureRepositorySecrets = ensureRepositorySecrets; exports.resolveRemoteConfiguration = resolveRemoteConfiguration; exports.groupSetupResources = groupSetupResources; -const setup_configuration_policy_1 = __nccwpck_require__(56637); -const logging_ports_1 = __nccwpck_require__(6152); -const application_error_1 = __nccwpck_require__(75999); +const setup_configuration_policy_1 = __nccwpck_require__(93015); +const logging_ports_1 = __nccwpck_require__(73001); +const application_error_1 = __nccwpck_require__(2965); async function ensureRepositoryVariables(context, dependencies, setupConfiguration, remoteConfiguration) { if (!setupConfiguration?.manageRepositoryVariables || !dependencies.setupRepositoryVariablesPort) { return { errors: [] }; @@ -51991,15 +51991,15 @@ async function upsertSecretGroups(context, port, groups) { /***/ }), -/***/ 18277: +/***/ 24603: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.syncProgressLabelsToOpenPullRequests = syncProgressLabelsToOpenPullRequests; -const progress_labels_1 = __nccwpck_require__(97890); -const logging_ports_1 = __nccwpck_require__(6152); +const progress_labels_1 = __nccwpck_require__(71285); +const logging_ports_1 = __nccwpck_require__(73001); async function syncProgressLabelsToOpenPullRequests(branch, progress, issueRepository, pullRequestRepository) { const roundedProgress = Math.min(100, Math.max(0, Math.round(progress / 5) * 5)); const newProgressLabel = `${roundedProgress}%`; @@ -52018,16 +52018,16 @@ async function syncProgressLabelsToOpenPullRequests(branch, progress, issueRepos /***/ }), -/***/ 44880: +/***/ 83269: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SynchronizeAgentActivityUseCase = void 0; -const agent_activity_label_policy_1 = __nccwpck_require__(79966); -const logging_ports_1 = __nccwpck_require__(6152); -const application_error_1 = __nccwpck_require__(75999); +const agent_activity_label_policy_1 = __nccwpck_require__(24342); +const logging_ports_1 = __nccwpck_require__(73001); +const application_error_1 = __nccwpck_require__(2965); /** * Maintains the temporary agent-activity label around a complete route. * Cleanup is deliberately best-effort so a label outage never hides the @@ -52082,16 +52082,16 @@ function sameLabels(left, right) { /***/ }), -/***/ 4643: +/***/ 70937: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runBranchSyncCommand = runBranchSyncCommand; -const result_1 = __nccwpck_require__(73817); -const branch_sync_command_1 = __nccwpck_require__(51114); -const application_error_1 = __nccwpck_require__(75999); +const result_1 = __nccwpck_require__(61444); +const branch_sync_command_1 = __nccwpck_require__(7465); +const application_error_1 = __nccwpck_require__(2965); /** Authorizes and runs an explicit or natural-language branch synchronization request. */ async function runBranchSyncCommand(context, options, args, authorization) { const parsed = (0, branch_sync_command_1.parseBranchSyncCommandArguments)(args); @@ -52127,7 +52127,7 @@ function unauthorized(taskId) { /***/ }), -/***/ 82113: +/***/ 52921: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -52138,10 +52138,10 @@ exports.branchSyncConflictEligibilityError = branchSyncConflictEligibilityError; exports.completedBranchSyncResult = completedBranchSyncResult; exports.unavailableBranchSyncResult = unavailableBranchSyncResult; exports.failedBranchSyncResult = failedBranchSyncResult; -const agent_1 = __nccwpck_require__(79937); -const result_1 = __nccwpck_require__(73817); -const workspace_changes_1 = __nccwpck_require__(93370); -const application_error_1 = __nccwpck_require__(75999); +const agent_1 = __nccwpck_require__(71889); +const result_1 = __nccwpck_require__(61444); +const workspace_changes_1 = __nccwpck_require__(51578); +const application_error_1 = __nccwpck_require__(2965); exports.BRANCH_SYNC_TASK_ID = "SyncBranchUseCase"; const MAX_AGENT_CONFLICT_PATHS = 20; function branchSyncConflictEligibilityError(preparation, useAgent, agentConfiguration) { @@ -52203,18 +52203,18 @@ function failedBranchSyncResult(reason, cause) { /***/ }), -/***/ 392: +/***/ 43725: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SyncBranchUseCase = void 0; -const branch_sync_conflicts_1 = __nccwpck_require__(84434); -const logging_ports_1 = __nccwpck_require__(6152); -const verify_command_policy_1 = __nccwpck_require__(96031); -const verify_command_runner_1 = __nccwpck_require__(57742); -const branch_sync_execution_policy_1 = __nccwpck_require__(82113); +const branch_sync_conflicts_1 = __nccwpck_require__(93843); +const logging_ports_1 = __nccwpck_require__(73001); +const verify_command_policy_1 = __nccwpck_require__(32739); +const verify_command_runner_1 = __nccwpck_require__(71843); +const branch_sync_execution_policy_1 = __nccwpck_require__(52921); /** Performs a race-safe parent-to-child merge and invokes the fixer only for eligible conflicts. */ class SyncBranchUseCase { constructor(dependencies, workspace, fixer, authenticatedUser, git) { @@ -52338,14 +52338,14 @@ exports.SyncBranchUseCase = SyncBranchUseCase; /***/ }), -/***/ 55721: +/***/ 11677: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.CheckCliUpdateUseCase = void 0; -const cli_version_1 = __nccwpck_require__(27089); +const cli_version_1 = __nccwpck_require__(30717); /** Checks for a newer published CLI version without coupling the application to npm. */ class CheckCliUpdateUseCase { constructor(cliUpdateCheckPort) { @@ -52363,18 +52363,18 @@ exports.CheckCliUpdateUseCase = CheckCliUpdateUseCase; /***/ }), -/***/ 42442: +/***/ 97791: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runCommentAutomationAction = runCommentAutomationAction; -const result_1 = __nccwpck_require__(73817); -const commit_autofix_and_resolve_workflow_1 = __nccwpck_require__(93455); -const commit_user_request_workflow_1 = __nccwpck_require__(43393); -const logging_ports_1 = __nccwpck_require__(6152); -const application_error_1 = __nccwpck_require__(75999); +const result_1 = __nccwpck_require__(61444); +const commit_autofix_and_resolve_workflow_1 = __nccwpck_require__(75112); +const commit_user_request_workflow_1 = __nccwpck_require__(17359); +const logging_ports_1 = __nccwpck_require__(73001); +const application_error_1 = __nccwpck_require__(2965); /** Runs the selected mutating action and returns any result records it produces. */ async function runCommentAutomationAction(param, options, route, intentPayload) { if (route === "review") @@ -52451,7 +52451,7 @@ async function runDoUserRequestAction(param, options, intentPayload) { /***/ }), -/***/ 63134: +/***/ 99496: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -52459,15 +52459,15 @@ async function runDoUserRequestAction(param, options, intentPayload) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runExplicitCommentCommand = runExplicitCommentCommand; exports.invalidCommentCommandResult = invalidCommentCommandResult; -const result_1 = __nccwpck_require__(73817); -const status_command_policy_1 = __nccwpck_require__(3449); -const copilot_interaction_policy_1 = __nccwpck_require__(90108); -const review_command_1 = __nccwpck_require__(1811); -const commit_user_request_workflow_1 = __nccwpck_require__(43393); -const workspace_mutation_guard_1 = __nccwpck_require__(24243); -const branch_sync_comment_command_1 = __nccwpck_require__(4643); -const application_error_1 = __nccwpck_require__(75999); -const bugbot_review_operation_context_1 = __nccwpck_require__(16660); +const result_1 = __nccwpck_require__(61444); +const status_command_policy_1 = __nccwpck_require__(62186); +const copilot_interaction_policy_1 = __nccwpck_require__(8964); +const review_command_1 = __nccwpck_require__(22551); +const commit_user_request_workflow_1 = __nccwpck_require__(17359); +const workspace_mutation_guard_1 = __nccwpck_require__(12627); +const branch_sync_comment_command_1 = __nccwpck_require__(70937); +const application_error_1 = __nccwpck_require__(2965); +const bugbot_review_operation_context_1 = __nccwpck_require__(50616); const LEARNED_BUGBOT_RULE_PATH = '.copilot/BUGBOT.learned.md'; /** Executes deterministic /copilot commands without routing them through intent detection. */ async function runExplicitCommentCommand(param, options, command, actorAuthorizationPort) { @@ -52629,16 +52629,16 @@ function invalidCommentCommandResult(taskId, reason) { /***/ }), -/***/ 46187: +/***/ 74745: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.completeCommentAutomation = completeCommentAutomation; -const bugbot_fix_intent_payload_1 = __nccwpck_require__(25734); -const logging_ports_1 = __nccwpck_require__(6152); -const comment_automation_action_workflow_1 = __nccwpck_require__(42442); +const bugbot_fix_intent_payload_1 = __nccwpck_require__(56352); +const logging_ports_1 = __nccwpck_require__(73001); +const comment_automation_action_workflow_1 = __nccwpck_require__(97791); async function completeCommentAutomation(param, options, decision) { logUnauthorizedActionSkip(decision); if (decision.route === 'think') { @@ -52658,7 +52658,7 @@ function logUnauthorizedActionSkip(decision) { /***/ }), -/***/ 37055: +/***/ 70719: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -52666,14 +52666,14 @@ function logUnauthorizedActionSkip(decision) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.projectCommentAutomationContext = projectCommentAutomationContext; exports.withCommentLanguageAdaptation = withCommentLanguageAdaptation; -const status_command_policy_1 = __nccwpck_require__(3449); -const comment_language_translation_workflow_1 = __nccwpck_require__(72770); -const think_workflow_1 = __nccwpck_require__(36450); -const bugbot_review_operation_context_1 = __nccwpck_require__(16660); -const push_single_action_contexts_1 = __nccwpck_require__(47841); -const think_request_policy_1 = __nccwpck_require__(23995); -const think_input_policy_1 = __nccwpck_require__(59687); -const copilot_command_1 = __nccwpck_require__(11771); +const status_command_policy_1 = __nccwpck_require__(62186); +const comment_language_translation_workflow_1 = __nccwpck_require__(78212); +const think_workflow_1 = __nccwpck_require__(14720); +const bugbot_review_operation_context_1 = __nccwpck_require__(50616); +const push_single_action_contexts_1 = __nccwpck_require__(87805); +const think_request_policy_1 = __nccwpck_require__(98559); +const think_input_policy_1 = __nccwpck_require__(12636); +const copilot_command_1 = __nccwpck_require__(87134); function projectCommentAutomationContext(source, language, userComment) { const review = (0, bugbot_review_operation_context_1.projectBugbotReviewOperationContext)(source); return Object.freeze({ @@ -52743,18 +52743,18 @@ function withCommentLanguageAdaptation(context, adaptation) { /***/ }), -/***/ 46175: +/***/ 55136: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.resolveCommentAutomationDecision = resolveCommentAutomationDecision; -const logging_ports_1 = __nccwpck_require__(6152); -const bugbot_fix_intent_payload_1 = __nccwpck_require__(25734); -const comment_automation_route_policy_1 = __nccwpck_require__(47058); -const copilot_comment_request_1 = __nccwpck_require__(86819); -const copilot_command_1 = __nccwpck_require__(11771); +const logging_ports_1 = __nccwpck_require__(73001); +const bugbot_fix_intent_payload_1 = __nccwpck_require__(56352); +const comment_automation_route_policy_1 = __nccwpck_require__(34038); +const copilot_comment_request_1 = __nccwpck_require__(81916); +const copilot_command_1 = __nccwpck_require__(87134); async function resolveCommentAutomationDecision(param, options, actorAuthorizationPort) { (0, logging_ports_1.logInfo)("Running bugbot fix intent detection (before Think)."); const intentResults = await options.intentUseCase.invoke(param.bugbot.fixIntent); @@ -52778,15 +52778,15 @@ function logIntent(intentPayload) { /***/ }), -/***/ 10554: +/***/ 33657: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runNaturalLanguageCommentAutomation = runNaturalLanguageCommentAutomation; -const comment_automation_decision_workflow_1 = __nccwpck_require__(46175); -const comment_automation_completion_workflow_1 = __nccwpck_require__(46187); +const comment_automation_decision_workflow_1 = __nccwpck_require__(55136); +const comment_automation_completion_workflow_1 = __nccwpck_require__(74745); /** Runs the natural-language comment pipeline after deterministic commands are excluded. */ async function runNaturalLanguageCommentAutomation(param, options, actorAuthorizationPort, languageResults) { const decision = await (0, comment_automation_decision_workflow_1.resolveCommentAutomationDecision)(param, options, actorAuthorizationPort); @@ -52800,14 +52800,14 @@ async function runNaturalLanguageCommentAutomation(param, options, actorAuthoriz /***/ }), -/***/ 47058: +/***/ 34038: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.resolveCommentAutomationRoute = resolveCommentAutomationRoute; -const bugbot_fix_intent_payload_1 = __nccwpck_require__(25734); +const bugbot_fix_intent_payload_1 = __nccwpck_require__(56352); function resolveCommentAutomationRoute(payload, allowedToModifyFiles, botMentioned = false, explicitMutationCommand = false) { if (!botMentioned && !explicitMutationCommand) return 'think'; @@ -52825,24 +52825,24 @@ function resolveCommentAutomationRoute(payload, allowedToModifyFiles, botMention /***/ }), -/***/ 9661: +/***/ 91490: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runCommentAutomation = runCommentAutomation; -const result_1 = __nccwpck_require__(73817); -const logging_ports_1 = __nccwpck_require__(6152); -const copilot_comment_request_1 = __nccwpck_require__(86819); -const copilot_command_1 = __nccwpck_require__(11771); -const comment_automation_command_workflow_1 = __nccwpck_require__(63134); -const comment_automation_natural_language_workflow_1 = __nccwpck_require__(10554); -const application_error_1 = __nccwpck_require__(75999); -const branch_sync_command_1 = __nccwpck_require__(51114); -const branch_sync_comment_command_1 = __nccwpck_require__(4643); -const comment_automation_context_1 = __nccwpck_require__(37055); -const comment_language_translation_workflow_1 = __nccwpck_require__(72770); +const result_1 = __nccwpck_require__(61444); +const logging_ports_1 = __nccwpck_require__(73001); +const copilot_comment_request_1 = __nccwpck_require__(81916); +const copilot_command_1 = __nccwpck_require__(87134); +const comment_automation_command_workflow_1 = __nccwpck_require__(99496); +const comment_automation_natural_language_workflow_1 = __nccwpck_require__(33657); +const application_error_1 = __nccwpck_require__(2965); +const branch_sync_command_1 = __nccwpck_require__(7465); +const branch_sync_comment_command_1 = __nccwpck_require__(70937); +const comment_automation_context_1 = __nccwpck_require__(70719); +const comment_language_translation_workflow_1 = __nccwpck_require__(78212); async function runCommentAutomation(initialParam, options, actorAuthorizationPort) { (0, logging_ports_1.logInfo)(`${options.taskId} started.`); let languageResults = []; @@ -52904,19 +52904,19 @@ async function runCommentAutomation(initialParam, options, actorAuthorizationPor /***/ }), -/***/ 28001: +/***/ 84014: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.CommitUseCase = void 0; -const result_1 = __nccwpck_require__(73817); -const logging_ports_1 = __nccwpck_require__(6152); -const task_emoji_1 = __nccwpck_require__(46103); -const application_error_1 = __nccwpck_require__(75999); -const bugbot_review_operation_context_1 = __nccwpck_require__(16660); -const push_single_action_contexts_1 = __nccwpck_require__(47841); +const result_1 = __nccwpck_require__(61444); +const logging_ports_1 = __nccwpck_require__(73001); +const task_emoji_1 = __nccwpck_require__(83142); +const application_error_1 = __nccwpck_require__(2965); +const bugbot_review_operation_context_1 = __nccwpck_require__(50616); +const push_single_action_contexts_1 = __nccwpck_require__(87805); class CommitUseCase { constructor(notifyNewCommitUseCase, checkChangesIssueSizeUseCase, detectPotentialProblemsUseCase, checkProgressUseCase, actorAuthorizationPort) { this.notifyNewCommitUseCase = notifyNewCommitUseCase; @@ -52970,18 +52970,18 @@ exports.CommitUseCase = CommitUseCase; /***/ }), -/***/ 71813: +/***/ 83966: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.ExecutionBranchVersionResolver = void 0; -const result_1 = __nccwpck_require__(73817); -const version_resolution_application_policy_1 = __nccwpck_require__(40231); -const version_resolution_outcome_policy_1 = __nccwpck_require__(43496); -const version_resolution_result_policy_1 = __nccwpck_require__(11730); -const version_resolution_policy_1 = __nccwpck_require__(92373); +const result_1 = __nccwpck_require__(61444); +const version_resolution_application_policy_1 = __nccwpck_require__(28787); +const version_resolution_outcome_policy_1 = __nccwpck_require__(23448); +const version_resolution_result_policy_1 = __nccwpck_require__(86641); +const version_resolution_policy_1 = __nccwpck_require__(20610); class ExecutionBranchVersionResolver { constructor(latestTagQueryPort, getReleaseVersion, getReleaseType, getHotfixVersion) { this.latestTagQueryPort = latestTagQueryPort; @@ -53079,7 +53079,7 @@ function unchanged(context) { /***/ }), -/***/ 63436: +/***/ 66185: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -53087,8 +53087,8 @@ function unchanged(context) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.resolveEventIssueNumber = resolveEventIssueNumber; exports.resolveSingleActionIssueNumber = resolveSingleActionIssueNumber; -const positive_integer_policy_1 = __nccwpck_require__(19879); -const title_utils_1 = __nccwpck_require__(46267); +const positive_integer_policy_1 = __nccwpck_require__(45613); +const title_utils_1 = __nccwpck_require__(58747); function resolveEventIssueNumber(context) { let issueNumber; if (context.isIssue) @@ -53182,14 +53182,14 @@ function currentSingleAction(context) { /***/ }), -/***/ 90972: +/***/ 78531: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.resolveExecutionIssueNumber = resolveExecutionIssueNumber; -const execution_issue_number_policy_1 = __nccwpck_require__(63436); +const execution_issue_number_policy_1 = __nccwpck_require__(66185); async function resolveExecutionIssueNumber(context, issueRepository) { return context.isSingleAction ? (0, execution_issue_number_policy_1.resolveSingleActionIssueNumber)(context, issueRepository) @@ -53199,14 +53199,14 @@ async function resolveExecutionIssueNumber(context, issueRepository) { /***/ }), -/***/ 88512: +/***/ 51778: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SetupExecutionUseCase = void 0; -const setup_execution_workflow_1 = __nccwpck_require__(42285); +const setup_execution_workflow_1 = __nccwpck_require__(70395); class SetupExecutionUseCase { constructor(issueSetupPort, organizationSetupPort, configurationPort, branchVersionResolver) { this.issueSetupPort = issueSetupPort; @@ -53229,19 +53229,19 @@ exports.SetupExecutionUseCase = SetupExecutionUseCase; /***/ }), -/***/ 42285: +/***/ 70395: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runSetupExecution = runSetupExecution; -const application_error_1 = __nccwpck_require__(75999); -const initial_labels_policy_1 = __nccwpck_require__(50293); -const previous_branch_state_policy_1 = __nccwpck_require__(43630); -const issue_workflow_profile_1 = __nccwpck_require__(26744); -const logging_ports_1 = __nccwpck_require__(6152); -const resolve_execution_issue_number_1 = __nccwpck_require__(90972); +const application_error_1 = __nccwpck_require__(2965); +const initial_labels_policy_1 = __nccwpck_require__(21435); +const previous_branch_state_policy_1 = __nccwpck_require__(25491); +const issue_workflow_profile_1 = __nccwpck_require__(62721); +const logging_ports_1 = __nccwpck_require__(73001); +const resolve_execution_issue_number_1 = __nccwpck_require__(78531); async function runSetupExecution(context, dependencies) { (0, logging_ports_1.setGlobalLoggerDebug)(context.debug, context.local); const tokenUser = await loadTokenUser(context, dependencies.organizationSetupPort); @@ -53414,18 +53414,18 @@ function positiveIssueNumberOrUndefined(value) { /***/ }), -/***/ 72042: +/***/ 14502: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.IssueCommentUseCase = void 0; -const result_1 = __nccwpck_require__(73817); -const comment_automation_use_case_1 = __nccwpck_require__(9661); -const check_issue_comment_language_use_case_1 = __nccwpck_require__(93152); -const comment_automation_context_1 = __nccwpck_require__(37055); -const pull_request_workflow_context_1 = __nccwpck_require__(73447); +const result_1 = __nccwpck_require__(61444); +const comment_automation_use_case_1 = __nccwpck_require__(91490); +const check_issue_comment_language_use_case_1 = __nccwpck_require__(34670); +const comment_automation_context_1 = __nccwpck_require__(70719); +const pull_request_workflow_context_1 = __nccwpck_require__(90587); class IssueCommentUseCase { constructor(languageUseCase, intentUseCase, thinkUseCase, autofixUseCase, doUserRequestUseCase, actorAuthorizationPort, bugbotGitMutationPort, dismissBugbotFindingsUseCase, reviewPotentialProblemsUseCase, updatePullRequestDescriptionUseCase, rememberBugbotRuleUseCase, syncBranchUseCase, preBranchSddContinuation) { this.languageUseCase = languageUseCase; @@ -53490,24 +53490,24 @@ exports.IssueCommentUseCase = IssueCommentUseCase; /***/ }), -/***/ 65281: +/***/ 43712: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.IssueUseCase = void 0; -const result_1 = __nccwpck_require__(73817); -const application_error_1 = __nccwpck_require__(75999); -const logging_ports_1 = __nccwpck_require__(6152); -const task_emoji_1 = __nccwpck_require__(46103); -const issue_workflow_1 = __nccwpck_require__(661); -const check_permissions_workflow_1 = __nccwpck_require__(17102); -const update_title_workflow_1 = __nccwpck_require__(50029); -const project_content_link_workflow_1 = __nccwpck_require__(89064); -const issue_workflow_context_1 = __nccwpck_require__(98005); -const push_single_action_contexts_1 = __nccwpck_require__(47841); -const issue_start_policy_1 = __nccwpck_require__(90332); +const result_1 = __nccwpck_require__(61444); +const application_error_1 = __nccwpck_require__(2965); +const logging_ports_1 = __nccwpck_require__(73001); +const task_emoji_1 = __nccwpck_require__(83142); +const issue_workflow_1 = __nccwpck_require__(95967); +const check_permissions_workflow_1 = __nccwpck_require__(98658); +const update_title_workflow_1 = __nccwpck_require__(89641); +const project_content_link_workflow_1 = __nccwpck_require__(72383); +const issue_workflow_context_1 = __nccwpck_require__(13765); +const push_single_action_contexts_1 = __nccwpck_require__(87805); +const issue_start_policy_1 = __nccwpck_require__(20953); class IssueUseCase { constructor(recommendStepsUseCase, answerIssueHelpUseCase, workflowSteps, issueCommentQueryPort, actorAuthorizationPort, preBranchSddGate) { this.recommendStepsUseCase = recommendStepsUseCase; @@ -53649,18 +53649,18 @@ function applyBranchConfigurationPatch(param, patch) { /***/ }), -/***/ 661: +/***/ 95967: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runIssueWorkflow = runIssueWorkflow; -const result_1 = __nccwpck_require__(73817); -const logging_ports_1 = __nccwpck_require__(6152); -const copilot_interaction_policy_1 = __nccwpck_require__(90108); -const semantic_result_publication_policy_1 = __nccwpck_require__(81985); -const application_error_1 = __nccwpck_require__(75999); +const result_1 = __nccwpck_require__(61444); +const logging_ports_1 = __nccwpck_require__(73001); +const copilot_interaction_policy_1 = __nccwpck_require__(8964); +const semantic_result_publication_policy_1 = __nccwpck_require__(55150); +const application_error_1 = __nccwpck_require__(2965); /** Coordinates issue lifecycle steps in their required sequential order. */ async function runIssueWorkflow(context, taskId, ports) { const results = []; @@ -53811,7 +53811,7 @@ function issueWorkflowOutcome(results, branchConfigurationPatch, recommendationS /***/ }), -/***/ 98005: +/***/ 13765: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -53821,7 +53821,7 @@ exports.branchPreparationOutcome = branchPreparationOutcome; exports.projectIssueWorkflowStepContexts = projectIssueWorkflowStepContexts; exports.projectAssignmentContext = projectAssignmentContext; exports.copyProjects = copyProjects; -const issue_start_policy_1 = __nccwpck_require__(90332); +const issue_start_policy_1 = __nccwpck_require__(20953); function branchPreparationOutcome(results, configurationPatch = {}) { return Object.freeze({ results: Object.freeze([...results]), @@ -53973,7 +53973,7 @@ function selectIssueType(source) { /***/ }), -/***/ 99961: +/***/ 53803: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -53981,11 +53981,11 @@ function selectIssueType(source) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.ResolveMessageCatalogUseCase = void 0; exports.buildCatalogResponseSchema = buildCatalogResponseSchema; -const agent_task_policy_1 = __nccwpck_require__(85712); -const message_catalog_1 = __nccwpck_require__(27097); -const locale_1 = __nccwpck_require__(15386); -const localize_message_catalog_1 = __nccwpck_require__(64005); -const logging_ports_1 = __nccwpck_require__(6152); +const agent_task_policy_1 = __nccwpck_require__(2601); +const message_catalog_1 = __nccwpck_require__(5313); +const locale_1 = __nccwpck_require__(64552); +const localize_message_catalog_1 = __nccwpck_require__(22907); +const logging_ports_1 = __nccwpck_require__(73001); class ResolveMessageCatalogUseCase { constructor(language) { this.language = language; @@ -54119,17 +54119,17 @@ function buildCatalogResponseSchema(source, ids, targetLocale = 'en-US') { /***/ }), -/***/ 29415: +/***/ 94097: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.PullRequestReviewCommentUseCase = void 0; -const comment_automation_use_case_1 = __nccwpck_require__(9661); -const check_pull_request_comment_language_use_case_1 = __nccwpck_require__(21729); -const comment_automation_context_1 = __nccwpck_require__(37055); -const pull_request_workflow_context_1 = __nccwpck_require__(73447); +const comment_automation_use_case_1 = __nccwpck_require__(91490); +const check_pull_request_comment_language_use_case_1 = __nccwpck_require__(5988); +const comment_automation_context_1 = __nccwpck_require__(70719); +const pull_request_workflow_context_1 = __nccwpck_require__(90587); class PullRequestReviewCommentUseCase { constructor(languageUseCase, intentUseCase, thinkUseCase, autofixUseCase, doUserRequestUseCase, actorAuthorizationPort, bugbotGitMutationPort, dismissBugbotFindingsUseCase, reviewPotentialProblemsUseCase, updatePullRequestDescriptionUseCase, rememberBugbotRuleUseCase, syncBranchUseCase) { this.languageUseCase = languageUseCase; @@ -54181,20 +54181,20 @@ exports.PullRequestReviewCommentUseCase = PullRequestReviewCommentUseCase; /***/ }), -/***/ 27259: +/***/ 93567: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.PullRequestUseCase = void 0; -const logging_ports_1 = __nccwpck_require__(6152); -const task_emoji_1 = __nccwpck_require__(46103); -const pull_request_workflow_1 = __nccwpck_require__(95238); -const update_title_workflow_1 = __nccwpck_require__(50029); -const project_content_link_workflow_1 = __nccwpck_require__(89064); -const pull_request_workflow_context_1 = __nccwpck_require__(73447); -const bugbot_review_operation_context_1 = __nccwpck_require__(16660); +const logging_ports_1 = __nccwpck_require__(73001); +const task_emoji_1 = __nccwpck_require__(83142); +const pull_request_workflow_1 = __nccwpck_require__(14671); +const update_title_workflow_1 = __nccwpck_require__(89641); +const project_content_link_workflow_1 = __nccwpck_require__(72383); +const pull_request_workflow_context_1 = __nccwpck_require__(90587); +const bugbot_review_operation_context_1 = __nccwpck_require__(50616); class PullRequestUseCase { constructor(updatePullRequestDescriptionUseCase, workflowSteps, reviewPotentialProblemsUseCase, actorAuthorizationPort) { this.updatePullRequestDescriptionUseCase = updatePullRequestDescriptionUseCase; @@ -54249,16 +54249,16 @@ function projectPullRequestWorkflowRouteContext(param) { /***/ }), -/***/ 95238: +/***/ 14671: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runPullRequestWorkflow = runPullRequestWorkflow; -const result_1 = __nccwpck_require__(73817); -const logging_ports_1 = __nccwpck_require__(6152); -const application_error_1 = __nccwpck_require__(75999); +const result_1 = __nccwpck_require__(61444); +const logging_ports_1 = __nccwpck_require__(73001); +const application_error_1 = __nccwpck_require__(2965); /** Coordinates pull-request lifecycle actions while preserving their sequential order. */ async function runPullRequestWorkflow(context, taskId, ports) { try { @@ -54337,7 +54337,7 @@ function logPullRequestState(context) { /***/ }), -/***/ 73447: +/***/ 90587: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -54345,7 +54345,7 @@ function logPullRequestState(context) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.projectPullRequestWorkflowStepContexts = projectPullRequestWorkflowStepContexts; exports.projectPullRequestDescriptionContext = projectPullRequestDescriptionContext; -const issue_workflow_context_1 = __nccwpck_require__(98005); +const issue_workflow_context_1 = __nccwpck_require__(13765); function projectPullRequestWorkflowStepContexts(source) { const projects = (0, issue_workflow_context_1.copyProjects)(source.project.getProjects()); return Object.freeze({ @@ -54405,7 +54405,7 @@ function projectPullRequestDescriptionContext(source) { /***/ }), -/***/ 47841: +/***/ 87805: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -54424,9 +54424,9 @@ exports.projectChangeSizeContext = projectChangeSizeContext; exports.projectInitialSetupContext = projectInitialSetupContext; exports.projectIssueCommentActionContext = projectIssueCommentActionContext; exports.projectAgentActivityContext = projectAgentActivityContext; -const recommendation_state_1 = __nccwpck_require__(68514); -const issue_comment_publication_policy_1 = __nccwpck_require__(61899); -const git_object_id_1 = __nccwpck_require__(88623); +const recommendation_state_1 = __nccwpck_require__(21602); +const issue_comment_publication_policy_1 = __nccwpck_require__(28956); +const git_object_id_1 = __nccwpck_require__(36924); function projectDeploymentPublicationContext(source) { return Object.freeze({ requestedOperationId: source.singleAction.operationId, @@ -54642,7 +54642,7 @@ function deepFreezeCopy(value) { /***/ }), -/***/ 29475: +/***/ 89463: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -54650,10 +54650,10 @@ function deepFreezeCopy(value) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.PreBranchSddGateUseCase = void 0; const node_crypto_1 = __nccwpck_require__(6005); -const result_1 = __nccwpck_require__(73817); -const issue_start_policy_1 = __nccwpck_require__(90332); -const pre_branch_sdd_1 = __nccwpck_require__(34730); -const application_error_1 = __nccwpck_require__(75999); +const result_1 = __nccwpck_require__(61444); +const issue_start_policy_1 = __nccwpck_require__(20953); +const pre_branch_sdd_1 = __nccwpck_require__(54078); +const application_error_1 = __nccwpck_require__(2965); const ANALYSIS_SCHEMA = { type: 'object', properties: { @@ -54933,15 +54933,15 @@ function buildDraftPrompt(context, snapshot, plan, answers, currentSdd) { /***/ }), -/***/ 60830: +/***/ 39967: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.AuditConfiguredSetupPatUseCase = void 0; -const setup_pat_creation_url_policy_1 = __nccwpck_require__(54718); -const setup_token_permission_policy_1 = __nccwpck_require__(99590); +const setup_pat_creation_url_policy_1 = __nccwpck_require__(96850); +const setup_token_permission_policy_1 = __nccwpck_require__(10947); /** Rechecks the final plan without granting permission based on the browser preview. */ class AuditConfiguredSetupPatUseCase { constructor(context, ports) { @@ -54996,20 +54996,20 @@ exports.AuditConfiguredSetupPatUseCase = AuditConfiguredSetupPatUseCase; /***/ }), -/***/ 87328: +/***/ 39: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SetupDoctorUseCase = void 0; -const locale_1 = __nccwpck_require__(15386); -const setup_configuration_policy_1 = __nccwpck_require__(56637); -const setup_doctor_report_policy_1 = __nccwpck_require__(67615); -const bounded_concurrency_policy_1 = __nccwpck_require__(35596); -const setup_doctor_message_catalog_1 = __nccwpck_require__(80226); -const setup_approval_doctor_policy_1 = __nccwpck_require__(53296); -const pull_request_approval_policy_1 = __nccwpck_require__(98820); +const locale_1 = __nccwpck_require__(64552); +const setup_configuration_policy_1 = __nccwpck_require__(93015); +const setup_doctor_report_policy_1 = __nccwpck_require__(81332); +const bounded_concurrency_policy_1 = __nccwpck_require__(50189); +const setup_doctor_message_catalog_1 = __nccwpck_require__(67183); +const setup_approval_doctor_policy_1 = __nccwpck_require__(8794); +const pull_request_approval_policy_1 = __nccwpck_require__(53553); class SetupDoctorUseCase { constructor(dependencies) { this.dependencies = dependencies; @@ -55414,35 +55414,35 @@ function doctorCatalogLocale(configuration) { /***/ }), -/***/ 36888: +/***/ 24711: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SetupCredentialsUseCase = exports.SetupQuestionnaireController = exports.SetupWizardUseCase = void 0; -var setup_wizard_use_case_1 = __nccwpck_require__(43433); +var setup_wizard_use_case_1 = __nccwpck_require__(27649); Object.defineProperty(exports, "SetupWizardUseCase", ({ enumerable: true, get: function () { return setup_wizard_use_case_1.SetupWizardUseCase; } })); -var setup_questionnaire_controller_1 = __nccwpck_require__(41644); +var setup_questionnaire_controller_1 = __nccwpck_require__(20526); Object.defineProperty(exports, "SetupQuestionnaireController", ({ enumerable: true, get: function () { return setup_questionnaire_controller_1.SetupQuestionnaireController; } })); -var setup_credentials_use_case_1 = __nccwpck_require__(67438); +var setup_credentials_use_case_1 = __nccwpck_require__(82634); Object.defineProperty(exports, "SetupCredentialsUseCase", ({ enumerable: true, get: function () { return setup_credentials_use_case_1.SetupCredentialsUseCase; } })); /***/ }), -/***/ 9890: +/***/ 14236: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SetupMergeQueueReadinessUseCase = void 0; -const deployment_plan_policy_1 = __nccwpck_require__(8352); -const merge_queue_readiness_1 = __nccwpck_require__(12515); -const setup_doctor_report_policy_1 = __nccwpck_require__(67615); -const setup_doctor_message_catalog_1 = __nccwpck_require__(80226); -const merge_queue_message_catalog_1 = __nccwpck_require__(56033); +const deployment_plan_policy_1 = __nccwpck_require__(86485); +const merge_queue_readiness_1 = __nccwpck_require__(36637); +const setup_doctor_report_policy_1 = __nccwpck_require__(81332); +const setup_doctor_message_catalog_1 = __nccwpck_require__(67183); +const merge_queue_message_catalog_1 = __nccwpck_require__(22459); class SetupMergeQueueReadinessUseCase { constructor(targets, catalogResolver) { this.targets = targets; @@ -55590,21 +55590,21 @@ function uniqueTargets(targets) { /***/ }), -/***/ 69277: +/***/ 99264: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.PrepareSetupPatIntentUseCase = void 0; -const application_error_1 = __nccwpck_require__(75999); -const setup_interaction_cancelled_error_1 = __nccwpck_require__(38313); -const setup_configuration_policy_1 = __nccwpck_require__(56637); -const setup_questionnaire_policy_1 = __nccwpck_require__(6009); -const setup_pat_intent_policy_1 = __nccwpck_require__(30748); -const setup_token_permission_policy_1 = __nccwpck_require__(99590); -const setup_pat_creation_url_policy_1 = __nccwpck_require__(54718); -const setup_wizard_use_case_1 = __nccwpck_require__(43433); +const application_error_1 = __nccwpck_require__(2965); +const setup_interaction_cancelled_error_1 = __nccwpck_require__(93638); +const setup_configuration_policy_1 = __nccwpck_require__(93015); +const setup_questionnaire_policy_1 = __nccwpck_require__(65207); +const setup_pat_intent_policy_1 = __nccwpck_require__(66964); +const setup_token_permission_policy_1 = __nccwpck_require__(10947); +const setup_pat_creation_url_policy_1 = __nccwpck_require__(96850); +const setup_wizard_use_case_1 = __nccwpck_require__(27649); /** Frontend-neutral preparation; GitHub still issues the PAT in its own UI. */ class PrepareSetupPatIntentUseCase { constructor(ports) { @@ -55683,15 +55683,15 @@ exports.PrepareSetupPatIntentUseCase = PrepareSetupPatIntentUseCase; /***/ }), -/***/ 67438: +/***/ 82634: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SetupCredentialsUseCase = void 0; -const application_error_1 = __nccwpck_require__(75999); -const setup_configuration_storage_policy_1 = __nccwpck_require__(2554); +const application_error_1 = __nccwpck_require__(2965); +const setup_configuration_storage_policy_1 = __nccwpck_require__(60368); /** Coordinates secret collection and validation without placing secret values in config files. */ class SetupCredentialsUseCase { constructor(prompt, validation, secrets, remoteHealth, tokenPermissions, permissionPresenter) { @@ -55914,14 +55914,14 @@ function isAcceptedCredentialCheck(requirement, check) { /***/ }), -/***/ 8419: +/***/ 32060: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SetupJourneyUseCase = void 0; -const setup_journey_policy_1 = __nccwpck_require__(53289); +const setup_journey_policy_1 = __nccwpck_require__(55254); /** Tracks semantic milestones, independently of the CLI's rendering. */ class SetupJourneyUseCase { constructor(repository, presenter) { @@ -55982,15 +55982,15 @@ exports.SetupJourneyUseCase = SetupJourneyUseCase; /***/ }), -/***/ 41644: +/***/ 20526: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SetupQuestionnaireController = void 0; -const setup_questionnaire_policy_1 = __nccwpck_require__(6009); -const application_error_1 = __nccwpck_require__(75999); +const setup_questionnaire_policy_1 = __nccwpck_require__(65207); +const application_error_1 = __nccwpck_require__(2965); class SetupQuestionnaireController { constructor(terminal, renderer) { this.terminal = terminal; @@ -56031,14 +56031,14 @@ function toEvent(input) { /***/ }), -/***/ 11797: +/***/ 64888: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SetupTokenPermissionsUseCase = void 0; -const setup_token_permission_evidence_policy_1 = __nccwpck_require__(65640); +const setup_token_permission_evidence_policy_1 = __nccwpck_require__(19750); /** Validates PAT identity first, then runs only read-only permission probes. */ class SetupTokenPermissionsUseCase { constructor(credentials, permissions) { @@ -56095,7 +56095,7 @@ exports.SetupTokenPermissionsUseCase = SetupTokenPermissionsUseCase; /***/ }), -/***/ 43433: +/***/ 27649: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -56103,12 +56103,12 @@ exports.SetupTokenPermissionsUseCase = SetupTokenPermissionsUseCase; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SetupWizardUseCase = void 0; exports.buildInitialSetupConfiguration = buildInitialSetupConfiguration; -const application_error_1 = __nccwpck_require__(75999); -const setup_configuration_policy_1 = __nccwpck_require__(56637); -const setup_questionnaire_policy_1 = __nccwpck_require__(6009); -const setup_configuration_clone_policy_1 = __nccwpck_require__(85881); -const setup_doctor_message_catalog_1 = __nccwpck_require__(80226); -const pull_request_approval_policy_1 = __nccwpck_require__(98820); +const application_error_1 = __nccwpck_require__(2965); +const setup_configuration_policy_1 = __nccwpck_require__(93015); +const setup_questionnaire_policy_1 = __nccwpck_require__(65207); +const setup_configuration_clone_policy_1 = __nccwpck_require__(6802); +const setup_doctor_message_catalog_1 = __nccwpck_require__(67183); +const pull_request_approval_policy_1 = __nccwpck_require__(53553); class SetupWizardUseCase { constructor(dependencies) { this.dependencies = dependencies; @@ -56315,14 +56315,14 @@ function unavailableRemoteConfiguration() { /***/ }), -/***/ 35697: +/***/ 36411: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.VerifyGuidedWorkflowPatIdentityUseCase = void 0; -const application_error_1 = __nccwpck_require__(75999); +const application_error_1 = __nccwpck_require__(2965); /** Binds a guided runtime PAT to the bot account chosen before token entry. */ class VerifyGuidedWorkflowPatIdentityUseCase { constructor(identities) { @@ -56341,15 +56341,15 @@ exports.VerifyGuidedWorkflowPatIdentityUseCase = VerifyGuidedWorkflowPatIdentity /***/ }), -/***/ 23388: +/***/ 47936: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.VerifySetupPatBootstrapUseCase = void 0; -const application_error_1 = __nccwpck_require__(75999); -const setup_pat_creation_url_policy_1 = __nccwpck_require__(54718); +const application_error_1 = __nccwpck_require__(2965); +const setup_pat_creation_url_policy_1 = __nccwpck_require__(96850); /** Initial read-only gate shared by terminal and browser setup presentations. */ class VerifySetupPatBootstrapUseCase { constructor(ports) { @@ -56382,16 +56382,16 @@ exports.VerifySetupPatBootstrapUseCase = VerifySetupPatBootstrapUseCase; /***/ }), -/***/ 5303: +/***/ 75598: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.VerifyWebSetupApplyUseCase = void 0; -const application_error_1 = __nccwpck_require__(75999); -const setup_interaction_cancelled_error_1 = __nccwpck_require__(38313); -const setup_remote_facts_policy_1 = __nccwpck_require__(92567); +const application_error_1 = __nccwpck_require__(2965); +const setup_interaction_cancelled_error_1 = __nccwpck_require__(93638); +const setup_remote_facts_policy_1 = __nccwpck_require__(41599); /** Authorizes one web Apply against the facts the operator actually reviewed. */ class VerifyWebSetupApplyUseCase { constructor(ports) { @@ -56453,19 +56453,19 @@ exports.VerifyWebSetupApplyUseCase = VerifyWebSetupApplyUseCase; /***/ }), -/***/ 73572: +/***/ 73840: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SingleActionUseCase = void 0; -const logging_ports_1 = __nccwpck_require__(6152); -const task_emoji_1 = __nccwpck_require__(46103); -const single_action_workflow_1 = __nccwpck_require__(6130); -const think_workflow_1 = __nccwpck_require__(36450); -const bugbot_review_operation_context_1 = __nccwpck_require__(16660); -const push_single_action_contexts_1 = __nccwpck_require__(47841); +const logging_ports_1 = __nccwpck_require__(73001); +const task_emoji_1 = __nccwpck_require__(83142); +const single_action_workflow_1 = __nccwpck_require__(86940); +const think_workflow_1 = __nccwpck_require__(14720); +const bugbot_review_operation_context_1 = __nccwpck_require__(50616); +const push_single_action_contexts_1 = __nccwpck_require__(87805); class SingleActionUseCase { constructor(publishGithubActionUseCase, createReleaseUseCase, createTagUseCase, thinkUseCase, initialSetupUseCase, checkProgressUseCase, detectPotentialProblemsUseCase, recommendStepsUseCase, closeInactiveIssuesUseCase, actorAuthorizationPort, publishIssueCommentUseCase, observeBranchSyncUseCase, deploymentOrchestrationUseCase) { this.publishGithubActionUseCase = publishGithubActionUseCase; @@ -56558,16 +56558,16 @@ function isAgentBackedSingleAction(param) { /***/ }), -/***/ 6130: +/***/ 86940: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runSingleActionWorkflow = runSingleActionWorkflow; -const result_1 = __nccwpck_require__(73817); -const logging_ports_1 = __nccwpck_require__(6152); -const application_error_1 = __nccwpck_require__(75999); +const result_1 = __nccwpck_require__(61444); +const logging_ports_1 = __nccwpck_require__(73001); +const application_error_1 = __nccwpck_require__(2965); async function runSingleActionWorkflow(dispatch, taskId, ports) { if (dispatch.kind === 'invalid') { (0, logging_ports_1.logDebugInfo)(`Single action is not valid: ${dispatch.action}. Skipping.`); @@ -56621,24 +56621,24 @@ function singleActionFailure(action, taskId, error) { /***/ }), -/***/ 4658: +/***/ 92650: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.analyzeBugbotRevision = analyzeBugbotRevision; -const bugbot_reconciliation_policy_1 = __nccwpck_require__(78128); -const logging_ports_1 = __nccwpck_require__(6152); -const limit_comments_1 = __nccwpck_require__(31643); -const finding_1 = __nccwpck_require__(31011); -const build_bugbot_prompt_1 = __nccwpck_require__(52483); -const prepare_bugbot_findings_1 = __nccwpck_require__(85016); -const query_bugbot_findings_1 = __nccwpck_require__(13059); -const bugbot_resolution_eligibility_policy_1 = __nccwpck_require__(89189); -const bounded_concurrency_policy_1 = __nccwpck_require__(35596); -const bugbot_partition_aggregation_1 = __nccwpck_require__(84575); -const application_error_1 = __nccwpck_require__(75999); +const bugbot_reconciliation_policy_1 = __nccwpck_require__(54271); +const logging_ports_1 = __nccwpck_require__(73001); +const limit_comments_1 = __nccwpck_require__(9306); +const finding_1 = __nccwpck_require__(82048); +const build_bugbot_prompt_1 = __nccwpck_require__(88971); +const prepare_bugbot_findings_1 = __nccwpck_require__(13325); +const query_bugbot_findings_1 = __nccwpck_require__(41038); +const bugbot_resolution_eligibility_policy_1 = __nccwpck_require__(15950); +const bounded_concurrency_policy_1 = __nccwpck_require__(50189); +const bugbot_partition_aggregation_1 = __nccwpck_require__(49942); +const application_error_1 = __nccwpck_require__(2965); /** Pure analysis phase: query, validate, normalize, deduplicate and reconcile; never mutates the SCM. */ async function analyzeBugbotRevision(execution, context, dependencies) { dependencies.telemetry.observeContext(context); @@ -56720,16 +56720,16 @@ function suppressDismissedFindings(execution, context, prepared) { /***/ }), -/***/ 20793: +/***/ 60017: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.applyDetectedFindings = applyDetectedFindings; -const mark_findings_resolved_use_case_1 = __nccwpck_require__(96963); -const publish_findings_use_case_1 = __nccwpck_require__(88442); -const pull_request_review_errors_1 = __nccwpck_require__(46445); +const mark_findings_resolved_use_case_1 = __nccwpck_require__(74985); +const publish_findings_use_case_1 = __nccwpck_require__(60836); +const pull_request_review_errors_1 = __nccwpck_require__(81504); async function applyDetectedFindings(operation, context, prepared, publicationPorts, resolutionPorts, catalog) { try { await (0, publish_findings_use_case_1.publishFindings)({ @@ -56763,7 +56763,7 @@ async function applyDetectedFindings(operation, context, prepared, publicationPo /***/ }), -/***/ 98158: +/***/ 35010: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -56771,8 +56771,8 @@ async function applyDetectedFindings(operation, context, prepared, publicationPo Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runBugbotAutofixCommitAndPush = runBugbotAutofixCommitAndPush; exports.runUserRequestCommitAndPush = runUserRequestCommitAndPush; -const commit_message_policy_1 = __nccwpck_require__(85518); -const commit_and_push_workflow_1 = __nccwpck_require__(53708); +const commit_message_policy_1 = __nccwpck_require__(37803); +const commit_and_push_workflow_1 = __nccwpck_require__(30978); async function runBugbotAutofixCommitAndPush(context, options, gitCommitPort) { const branch = options?.branchOverride ?? context.branch; return (0, commit_and_push_workflow_1.runCommitAndPushWorkflow)(context, { @@ -56797,17 +56797,17 @@ async function runUserRequestCommitAndPush(context, options, gitCommitPort) { /***/ }), -/***/ 79698: +/***/ 99776: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.finalizeBugbotAutofix = finalizeBugbotAutofix; -const result_1 = __nccwpck_require__(73817); -const logging_ports_1 = __nccwpck_require__(6152); -const workspace_mutation_guard_1 = __nccwpck_require__(24243); -const application_error_1 = __nccwpck_require__(75999); +const result_1 = __nccwpck_require__(61444); +const logging_ports_1 = __nccwpck_require__(73001); +const workspace_mutation_guard_1 = __nccwpck_require__(12627); +const application_error_1 = __nccwpck_require__(2965); async function finalizeBugbotAutofix(context, idsToFix, workspacePathsBefore, branchCheckedOut, responseText, gitCommitPort) { if (!responseText) { (0, logging_ports_1.logError)('Bugbot autofix: no response from configured build agent.'); @@ -56838,21 +56838,21 @@ function failure(semanticError) { /***/ }), -/***/ 67170: +/***/ 30398: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.prepareBugbotAutofix = prepareBugbotAutofix; -const result_1 = __nccwpck_require__(73817); -const finding_1 = __nccwpck_require__(31011); -const build_bugbot_fix_prompt_1 = __nccwpck_require__(89819); -const load_bugbot_context_use_case_1 = __nccwpck_require__(4050); -const bugbot_context_request_1 = __nccwpck_require__(98299); -const logging_ports_1 = __nccwpck_require__(6152); -const workspace_mutation_guard_1 = __nccwpck_require__(24243); -const application_error_1 = __nccwpck_require__(75999); +const result_1 = __nccwpck_require__(61444); +const finding_1 = __nccwpck_require__(82048); +const build_bugbot_fix_prompt_1 = __nccwpck_require__(48942); +const load_bugbot_context_use_case_1 = __nccwpck_require__(56378); +const bugbot_context_request_1 = __nccwpck_require__(72881); +const logging_ports_1 = __nccwpck_require__(73001); +const workspace_mutation_guard_1 = __nccwpck_require__(12627); +const application_error_1 = __nccwpck_require__(2965); async function prepareBugbotAutofix(operation, targetFindingIds, userComment, providedContext, branchOverride, contextPorts, gitCommitPort) { const canonicalHint = providedContext?.canonicalPullRequest; const targetBranch = branchOverride?.trim() || canonicalHint?.headRef; @@ -56915,14 +56915,14 @@ function failure(semanticError) { /***/ }), -/***/ 45446: +/***/ 92886: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.BugbotAutofixUseCase = void 0; -const bugbot_autofix_workflow_1 = __nccwpck_require__(69600); +const bugbot_autofix_workflow_1 = __nccwpck_require__(60366); /** Application boundary for safe, agent-driven remediation of Bugbot findings. */ class BugbotAutofixUseCase { constructor(aiRepository, contextPorts, gitCommitPort) { @@ -56944,20 +56944,20 @@ exports.BugbotAutofixUseCase = BugbotAutofixUseCase; /***/ }), -/***/ 69600: +/***/ 60366: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runBugbotAutofixWorkflow = runBugbotAutofixWorkflow; -const agent_1 = __nccwpck_require__(79937); -const result_1 = __nccwpck_require__(73817); -const logging_ports_1 = __nccwpck_require__(6152); -const task_emoji_1 = __nccwpck_require__(46103); -const bugbot_autofix_postflight_1 = __nccwpck_require__(79698); -const bugbot_autofix_preflight_1 = __nccwpck_require__(67170); -const application_error_1 = __nccwpck_require__(75999); +const agent_1 = __nccwpck_require__(71889); +const result_1 = __nccwpck_require__(61444); +const logging_ports_1 = __nccwpck_require__(73001); +const task_emoji_1 = __nccwpck_require__(83142); +const bugbot_autofix_postflight_1 = __nccwpck_require__(99776); +const bugbot_autofix_preflight_1 = __nccwpck_require__(30398); +const application_error_1 = __nccwpck_require__(2965); const TASK_ID = 'BugbotAutofixUseCase'; /** Coordinates preflight, agent execution and postflight workspace safety. */ async function runBugbotAutofixWorkflow(param, dependencies) { @@ -57000,14 +57000,14 @@ function newResultFailure(semanticError) { /***/ }), -/***/ 98299: +/***/ 72881: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.projectBugbotContextRequest = projectBugbotContextRequest; -const positive_integer_policy_1 = __nccwpck_require__(19879); +const positive_integer_policy_1 = __nccwpck_require__(45613); function projectBugbotContextRequest(context, options) { const issueNumber = (0, positive_integer_policy_1.parsePositiveSafeInteger)(options?.issueNumberOverride ?? context.target.issueNumber); const pullRequestNumber = (0, positive_integer_policy_1.parsePositiveSafeInteger)(options?.pullRequestNumberOverride @@ -57041,7 +57041,7 @@ function projectBugbotContextRequest(context, options) { /***/ }), -/***/ 62946: +/***/ 7993: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -57049,11 +57049,11 @@ function projectBugbotContextRequest(context, options) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.parseBugbotFindingComments = parseBugbotFindingComments; exports.collectPreviousBugbotFindings = collectPreviousBugbotFindings; -const build_bugbot_fix_prompt_1 = __nccwpck_require__(89819); -const bugbot_finding_marker_policy_1 = __nccwpck_require__(98024); -const finding_1 = __nccwpck_require__(31011); -const github_user_policy_1 = __nccwpck_require__(84403); -const review_state_1 = __nccwpck_require__(79200); +const build_bugbot_fix_prompt_1 = __nccwpck_require__(48942); +const bugbot_finding_marker_policy_1 = __nccwpck_require__(80639); +const finding_1 = __nccwpck_require__(82048); +const github_user_policy_1 = __nccwpck_require__(19596); +const review_state_1 = __nccwpck_require__(17271); function parseBugbotFindingComments(issueComments, pullRequestCommentsByNumber, trustedAuthorLogin, reviewThreadStatesByPullRequest = new Map()) { const existingByFindingId = parseIssueFindingMarkers(issueComments, trustedAuthorLogin); const pullRequestFindings = parsePullRequestFindingMarkers(pullRequestCommentsByNumber, trustedAuthorLogin, reviewThreadStatesByPullRequest); @@ -57174,7 +57174,7 @@ function collectPreviousBugbotFindings(issueComments, existingByFindingId, prFin /***/ }), -/***/ 25734: +/***/ 56352: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -57213,7 +57213,7 @@ function canRunDoUserRequest(payload) { /***/ }), -/***/ 84575: +/***/ 49942: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -57221,7 +57221,7 @@ function canRunDoUserRequest(payload) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.MAX_AGGREGATE_PARTITION_FINDINGS = void 0; exports.aggregateBugbotPartitionResponses = aggregateBugbotPartitionResponses; -const application_error_1 = __nccwpck_require__(75999); +const application_error_1 = __nccwpck_require__(2965); const MAX_PARTITION_FINDINGS_PER_RESPONSE = 200; exports.MAX_AGGREGATE_PARTITION_FINDINGS = 2000; const MAX_OWNER_RESOLUTIONS = 500; @@ -57274,7 +57274,7 @@ function invalidAggregate(message) { /***/ }), -/***/ 3346: +/***/ 9819: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -57282,8 +57282,8 @@ function invalidAggregate(message) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.MAX_PREVIOUS_FINDINGS_BLOCK_LENGTH = exports.MAX_PREVIOUS_FINDINGS = void 0; exports.buildPreviousFindingsContext = buildPreviousFindingsContext; -const untrusted_content_1 = __nccwpck_require__(67057); -const build_bugbot_fix_prompt_1 = __nccwpck_require__(89819); +const untrusted_content_1 = __nccwpck_require__(12334); +const build_bugbot_fix_prompt_1 = __nccwpck_require__(48942); exports.MAX_PREVIOUS_FINDINGS = 100; exports.MAX_PREVIOUS_FINDINGS_BLOCK_LENGTH = 48000; function buildPreviousFindingsContext(previousFindings) { @@ -57344,7 +57344,7 @@ function timestamp(value) { /***/ }), -/***/ 50536: +/***/ 36905: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -57354,9 +57354,9 @@ exports.buildReviewDiffBlock = buildReviewDiffBlock; exports.buildReviewDiffContext = buildReviewDiffContext; exports.buildReviewConversationBlock = buildReviewConversationBlock; exports.buildReviewConversationContext = buildReviewConversationContext; -const github_user_policy_1 = __nccwpck_require__(84403); -const untrusted_content_1 = __nccwpck_require__(67057); -const bugbot_diff_partition_policy_1 = __nccwpck_require__(31601); +const github_user_policy_1 = __nccwpck_require__(19596); +const untrusted_content_1 = __nccwpck_require__(12334); +const bugbot_diff_partition_policy_1 = __nccwpck_require__(51471); const MAX_CONVERSATION_LENGTH = 24000; const MAX_CONVERSATION_ITEMS = 50; const MAX_CONVERSATION_ITEM_LENGTH = 2000; @@ -57439,7 +57439,7 @@ function isBot(author, botLogin) { /***/ }), -/***/ 14307: +/***/ 26699: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -57462,7 +57462,7 @@ async function hasNewerBugbotRevision(context, ports) { /***/ }), -/***/ 16660: +/***/ 50616: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -57474,7 +57474,7 @@ exports.withBugbotReviewOverrides = withBugbotReviewOverrides; exports.projectBugbotFixIntentContext = projectBugbotFixIntentContext; exports.projectBugbotAutofixOperationContext = projectBugbotAutofixOperationContext; exports.projectBugbotCommitContext = projectBugbotCommitContext; -const positive_integer_policy_1 = __nccwpck_require__(19879); +const positive_integer_policy_1 = __nccwpck_require__(45613); /** Copies only the non-secret facts required to select canonical Bugbot context. */ function projectBugbotContextSelectionContext(source) { const reviewConfiguration = source.ai.getBugbotReviewConfiguration(); @@ -57620,7 +57620,7 @@ function normalizeExpectedHeadSha(eventName, inputs) { /***/ }), -/***/ 25011: +/***/ 45270: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -57628,7 +57628,7 @@ function normalizeExpectedHeadSha(eventName, inputs) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.MAX_BUGBOT_RULES_LENGTH = exports.MAX_BUGBOT_RULE_LENGTH = void 0; exports.buildBugbotReviewRuleSet = buildBugbotReviewRuleSet; -const untrusted_content_1 = __nccwpck_require__(67057); +const untrusted_content_1 = __nccwpck_require__(12334); exports.MAX_BUGBOT_RULE_LENGTH = 30000; exports.MAX_BUGBOT_RULES_LENGTH = 100000; function buildBugbotReviewRuleSet(organizationRules, repositoryRules) { @@ -57681,14 +57681,14 @@ function deduplicateRules(rules) { /***/ }), -/***/ 46790: +/***/ 14285: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.BugbotReviewTelemetry = void 0; -const bugbot_finding_status_policy_1 = __nccwpck_require__(53822); +const bugbot_finding_status_policy_1 = __nccwpck_require__(9298); const systemClock = { now: () => Date.now(), isoNow: () => new Date().toISOString(), @@ -57890,7 +57890,7 @@ function sanitizeMetricName(value) { /***/ }), -/***/ 18799: +/***/ 41672: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -57901,9 +57901,9 @@ function sanitizeMetricName(value) { */ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildBugbotFixIntentPrompt = buildBugbotFixIntentPrompt; -const prompts_1 = __nccwpck_require__(69518); -const project_context_instruction_1 = __nccwpck_require__(63907); -const sanitize_user_comment_for_prompt_1 = __nccwpck_require__(59828); +const prompts_1 = __nccwpck_require__(71854); +const project_context_instruction_1 = __nccwpck_require__(36158); +const sanitize_user_comment_for_prompt_1 = __nccwpck_require__(28636); const MAX_TITLE_LENGTH = 200; const MAX_FILE_LENGTH = 256; function safeForPrompt(s, maxLen) { @@ -57953,7 +57953,7 @@ function buildParentBlock(parentCommentBody) { /***/ }), -/***/ 89819: +/***/ 48942: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -57962,10 +57962,10 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.MAX_FINDING_BODY_LENGTH = void 0; exports.truncateFindingBody = truncateFindingBody; exports.buildBugbotFixPrompt = buildBugbotFixPrompt; -const prompts_1 = __nccwpck_require__(69518); -const project_context_instruction_1 = __nccwpck_require__(63907); -const sanitize_user_comment_for_prompt_1 = __nccwpck_require__(59828); -const untrusted_content_1 = __nccwpck_require__(67057); +const prompts_1 = __nccwpck_require__(71854); +const project_context_instruction_1 = __nccwpck_require__(36158); +const sanitize_user_comment_for_prompt_1 = __nccwpck_require__(28636); +const untrusted_content_1 = __nccwpck_require__(12334); /** Maximum characters for a single finding's full comment body to avoid prompt bloat and token limits. */ exports.MAX_FINDING_BODY_LENGTH = 12000; const TRUNCATION_SUFFIX = "\n\n[... truncated for length ...]"; @@ -58022,7 +58022,7 @@ function buildBugbotFixPrompt(param, context, targetFindingIds, userComment, ver /***/ }), -/***/ 52483: +/***/ 88971: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -58036,10 +58036,10 @@ function buildBugbotFixPrompt(param, context, targetFindingIds, userComment, ver */ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildBugbotPrompt = buildBugbotPrompt; -const prompts_1 = __nccwpck_require__(69518); -const project_context_instruction_1 = __nccwpck_require__(63907); -const review_configuration_1 = __nccwpck_require__(3994); -const file_ignore_policy_1 = __nccwpck_require__(20542); +const prompts_1 = __nccwpck_require__(71854); +const project_context_instruction_1 = __nccwpck_require__(36158); +const review_configuration_1 = __nccwpck_require__(19249); +const file_ignore_policy_1 = __nccwpck_require__(56498); const MAX_IGNORE_BLOCK_LENGTH = 2000; const GIT_OBJECT_ID = /^[0-9a-f]{7,64}$/i; function buildBugbotPrompt(param, context, assignment) { @@ -58161,18 +58161,18 @@ function normalizedObjectId(value) { /***/ }), -/***/ 49629: +/***/ 66117: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runCommitAndPushPreflight = runCommitAndPushPreflight; -const logging_ports_1 = __nccwpck_require__(6152); -const git_branch_checkout_1 = __nccwpck_require__(68549); -const verify_command_policy_1 = __nccwpck_require__(96031); -const verify_command_runner_1 = __nccwpck_require__(57742); -const workspace_changes_1 = __nccwpck_require__(93370); +const logging_ports_1 = __nccwpck_require__(73001); +const git_branch_checkout_1 = __nccwpck_require__(96838); +const verify_command_policy_1 = __nccwpck_require__(32739); +const verify_command_runner_1 = __nccwpck_require__(71843); +const workspace_changes_1 = __nccwpck_require__(51578); async function runCommitAndPushPreflight(context, options, gitCommitPort) { if (!options.branch?.trim()) { return { status: "failure", error: "No branch to commit to." }; @@ -58209,16 +58209,16 @@ async function runVerification(context, gitCommitPort) { /***/ }), -/***/ 53708: +/***/ 30978: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runCommitAndPushWorkflow = runCommitAndPushWorkflow; -const logging_ports_1 = __nccwpck_require__(6152); -const commit_and_push_preflight_1 = __nccwpck_require__(49629); -const application_error_1 = __nccwpck_require__(75999); +const logging_ports_1 = __nccwpck_require__(73001); +const commit_and_push_preflight_1 = __nccwpck_require__(66117); +const application_error_1 = __nccwpck_require__(2965); async function runCommitAndPushWorkflow(context, options, gitCommitPort) { const preflight = await (0, commit_and_push_preflight_1.runCommitAndPushPreflight)(context, options, gitCommitPort); if (preflight.status === 'failure') { @@ -58253,16 +58253,16 @@ async function runCommitAndPushWorkflow(context, options, gitCommitPort) { /***/ }), -/***/ 93455: +/***/ 75112: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.commitAutofixAndResolveFindings = commitAutofixAndResolveFindings; -const logging_ports_1 = __nccwpck_require__(6152); -const bugbot_autofix_commit_1 = __nccwpck_require__(98158); -const github_comment_publication_policy_1 = __nccwpck_require__(72712); +const logging_ports_1 = __nccwpck_require__(73001); +const bugbot_autofix_commit_1 = __nccwpck_require__(35010); +const github_comment_publication_policy_1 = __nccwpck_require__(22913); async function commitAutofixAndResolveFindings(context, payload, autofixResults, gitCommitPort) { const lastAutofix = autofixResults.at(-1); if (!lastAutofix?.success) { @@ -58296,7 +58296,7 @@ async function commitAutofixAndResolveFindings(context, payload, autofixResults, /***/ }), -/***/ 85518: +/***/ 37803: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -58350,18 +58350,18 @@ function buildUserRequestCommitMessage(issueNumber) { /***/ }), -/***/ 43393: +/***/ 17359: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.commitUserRequestIfSuccessful = commitUserRequestIfSuccessful; -const logging_ports_1 = __nccwpck_require__(6152); -const bugbot_autofix_commit_1 = __nccwpck_require__(98158); -const result_1 = __nccwpck_require__(73817); -const github_comment_publication_policy_1 = __nccwpck_require__(72712); -const application_error_1 = __nccwpck_require__(75999); +const logging_ports_1 = __nccwpck_require__(73001); +const bugbot_autofix_commit_1 = __nccwpck_require__(35010); +const result_1 = __nccwpck_require__(61444); +const github_comment_publication_policy_1 = __nccwpck_require__(22913); +const application_error_1 = __nccwpck_require__(2965); async function commitUserRequestIfSuccessful(context, branchOverride, results, gitCommitPort) { if (!results.at(-1)?.success) { (0, logging_ports_1.logInfo)('Do user request did not succeed; skipping commit.'); @@ -58394,7 +58394,7 @@ async function commitUserRequestIfSuccessful(context, branchOverride, results, g /***/ }), -/***/ 62908: +/***/ 71392: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -58427,7 +58427,7 @@ function deduplicateFindings(findings) { /***/ }), -/***/ 14796: +/***/ 58778: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -58436,7 +58436,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.selectBugbotCommentBody = selectBugbotCommentBody; exports.buildUnresolvedFindingSummaries = buildUnresolvedFindingSummaries; exports.parseBugbotFixIntentResponse = parseBugbotFixIntentResponse; -const bugbot_finding_marker_policy_1 = __nccwpck_require__(98024); +const bugbot_finding_marker_policy_1 = __nccwpck_require__(80639); /** Selects the user-authored comment that can trigger intent detection. */ function selectBugbotCommentBody(sources) { if (sources.issue.isIssueComment) @@ -58481,16 +58481,16 @@ function unique(values) { /***/ }), -/***/ 76234: +/***/ 50385: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.DetectBugbotFixIntentUseCase = void 0; -const logging_ports_1 = __nccwpck_require__(6152); -const task_emoji_1 = __nccwpck_require__(46103); -const detect_bugbot_fix_intent_workflow_1 = __nccwpck_require__(88390); +const logging_ports_1 = __nccwpck_require__(73001); +const task_emoji_1 = __nccwpck_require__(83142); +const detect_bugbot_fix_intent_workflow_1 = __nccwpck_require__(59265); const TASK_ID = "DetectBugbotFixIntentUseCase"; /** Application boundary for detecting Bugbot fix intent in user comments. */ class DetectBugbotFixIntentUseCase { @@ -58512,23 +58512,23 @@ exports.DetectBugbotFixIntentUseCase = DetectBugbotFixIntentUseCase; /***/ }), -/***/ 88390: +/***/ 59265: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runDetectBugbotFixIntentWorkflow = runDetectBugbotFixIntentWorkflow; -const agent_1 = __nccwpck_require__(79937); -const agent_task_policy_1 = __nccwpck_require__(85712); -const logging_ports_1 = __nccwpck_require__(6152); -const result_1 = __nccwpck_require__(73817); -const copilot_command_1 = __nccwpck_require__(11771); -const build_bugbot_fix_intent_prompt_1 = __nccwpck_require__(18799); -const load_bugbot_context_use_case_1 = __nccwpck_require__(4050); -const bugbot_context_request_1 = __nccwpck_require__(98299); -const schema_1 = __nccwpck_require__(16808); -const detect_bugbot_fix_intent_policy_1 = __nccwpck_require__(14796); +const agent_1 = __nccwpck_require__(71889); +const agent_task_policy_1 = __nccwpck_require__(2601); +const logging_ports_1 = __nccwpck_require__(73001); +const result_1 = __nccwpck_require__(61444); +const copilot_command_1 = __nccwpck_require__(87134); +const build_bugbot_fix_intent_prompt_1 = __nccwpck_require__(41672); +const load_bugbot_context_use_case_1 = __nccwpck_require__(56378); +const bugbot_context_request_1 = __nccwpck_require__(72881); +const schema_1 = __nccwpck_require__(98135); +const detect_bugbot_fix_intent_policy_1 = __nccwpck_require__(58778); const TASK_ID = "DetectBugbotFixIntentUseCase"; /** Detects whether a comment requests a finding fix, repository change, or read-only review. */ async function runDetectBugbotFixIntentWorkflow(param, ports) { @@ -58665,21 +58665,21 @@ async function resolveParentCommentBody(param, contextPorts) { /***/ }), -/***/ 37685: +/***/ 8677: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.DismissBugbotFindingsUseCase = void 0; -const result_1 = __nccwpck_require__(73817); -const load_bugbot_context_use_case_1 = __nccwpck_require__(4050); -const bugbot_context_request_1 = __nccwpck_require__(98299); -const mark_findings_resolved_workflow_1 = __nccwpck_require__(65916); -const bugbot_finding_marker_policy_1 = __nccwpck_require__(98024); -const logging_ports_1 = __nccwpck_require__(6152); -const application_error_1 = __nccwpck_require__(75999); -const bugbot_message_catalog_1 = __nccwpck_require__(7406); +const result_1 = __nccwpck_require__(61444); +const load_bugbot_context_use_case_1 = __nccwpck_require__(56378); +const bugbot_context_request_1 = __nccwpck_require__(72881); +const mark_findings_resolved_workflow_1 = __nccwpck_require__(29578); +const bugbot_finding_marker_policy_1 = __nccwpck_require__(80639); +const logging_ports_1 = __nccwpck_require__(73001); +const application_error_1 = __nccwpck_require__(2965); +const bugbot_message_catalog_1 = __nccwpck_require__(84479); /** Dismisses only findings present in the current persisted Bugbot context. */ class DismissBugbotFindingsUseCase { constructor(dependencies) { @@ -58750,14 +58750,14 @@ async function loadDismissContext(operation, ports) { /***/ }), -/***/ 31643: +/***/ 9306: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.applyCommentLimit = applyCommentLimit; -const bugbot_constants_1 = __nccwpck_require__(51389); +const bugbot_constants_1 = __nccwpck_require__(16868); /** * Applies the max-comments limit: returns the first N findings to publish individually, * and overflow count + titles for a single "revisar en local" summary comment. @@ -58778,7 +58778,7 @@ function applyCommentLimit(findings, maxComments = bugbot_constants_1.BUGBOT_MAX /***/ }), -/***/ 4050: +/***/ 56378: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -58786,16 +58786,16 @@ function applyCommentLimit(findings, maxComments = bugbot_constants_1.BUGBOT_MAX Object.defineProperty(exports, "__esModule", ({ value: true })); exports.preflightBugbotContext = preflightBugbotContext; exports.loadBugbotContext = loadBugbotContext; -const application_error_1 = __nccwpck_require__(75999); -const bounded_concurrency_policy_1 = __nccwpck_require__(35596); -const context_1 = __nccwpck_require__(14712); -const logging_ports_1 = __nccwpck_require__(6152); -const bugbot_finding_context_1 = __nccwpck_require__(62946); -const bugbot_previous_findings_context_1 = __nccwpck_require__(3346); -const bugbot_diff_partition_policy_1 = __nccwpck_require__(31601); -const bugbot_review_context_1 = __nccwpck_require__(50536); -const file_ignore_policy_1 = __nccwpck_require__(20542); -const bugbot_review_rules_1 = __nccwpck_require__(25011); +const application_error_1 = __nccwpck_require__(2965); +const bounded_concurrency_policy_1 = __nccwpck_require__(50189); +const context_1 = __nccwpck_require__(32721); +const logging_ports_1 = __nccwpck_require__(73001); +const bugbot_finding_context_1 = __nccwpck_require__(7993); +const bugbot_previous_findings_context_1 = __nccwpck_require__(9819); +const bugbot_diff_partition_policy_1 = __nccwpck_require__(51471); +const bugbot_review_context_1 = __nccwpck_require__(36905); +const file_ignore_policy_1 = __nccwpck_require__(56498); +const bugbot_review_rules_1 = __nccwpck_require__(45270); /** Resolves and validates the provider-owned PR identity without loading review context. */ async function preflightBugbotContext(request, ports) { const selection = await selectCanonicalPullRequest(request, ports); @@ -58963,14 +58963,14 @@ function toPrContext(identity, snapshot) { /***/ }), -/***/ 44861: +/***/ 50980: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.loadBugbotReconciliationSnapshot = loadBugbotReconciliationSnapshot; -const pull_request_review_errors_1 = __nccwpck_require__(46445); +const pull_request_review_errors_1 = __nccwpck_require__(81504); /** * Acquires one coherent final snapshot around two head guards. Surface reads * run concurrently, while the second guard rejects data collected across a @@ -59061,32 +59061,32 @@ function stateOf(result) { /***/ }), -/***/ 96963: +/***/ 74985: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.markFindingsResolved = void 0; -var mark_findings_resolved_workflow_1 = __nccwpck_require__(65916); +var mark_findings_resolved_workflow_1 = __nccwpck_require__(29578); Object.defineProperty(exports, "markFindingsResolved", ({ enumerable: true, get: function () { return mark_findings_resolved_workflow_1.markFindingsResolved; } })); /***/ }), -/***/ 65916: +/***/ 29578: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.markFindingsResolved = markFindingsResolved; -const pull_request_review_errors_1 = __nccwpck_require__(46445); -const logging_ports_1 = __nccwpck_require__(6152); -const resolve_issue_finding_1 = __nccwpck_require__(35300); -const resolve_pull_request_finding_1 = __nccwpck_require__(64567); -const review_state_1 = __nccwpck_require__(79200); -const application_error_1 = __nccwpck_require__(75999); +const pull_request_review_errors_1 = __nccwpck_require__(81504); +const logging_ports_1 = __nccwpck_require__(73001); +const resolve_issue_finding_1 = __nccwpck_require__(101); +const resolve_pull_request_finding_1 = __nccwpck_require__(72038); +const review_state_1 = __nccwpck_require__(17271); +const application_error_1 = __nccwpck_require__(2965); async function markFindingsResolved(param) { const errors = []; for (const [findingId, existing] of Object.entries(param.context.existingByFindingId)) { @@ -59170,7 +59170,7 @@ function addResolutionError(errors, destination) { /***/ }), -/***/ 70124: +/***/ 33308: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -59233,14 +59233,14 @@ function resolveFindingPathForPr(findingFile, prFiles) { /***/ }), -/***/ 85016: +/***/ 13325: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.prepareBugbotFindings = prepareBugbotFindings; -const prepare_bugbot_findings_policy_1 = __nccwpck_require__(3496); +const prepare_bugbot_findings_policy_1 = __nccwpck_require__(73654); function prepareBugbotFindings(response, ignorePatterns, minSeverityValue, maxComments, maxAgentFindings) { const normalized = (0, prepare_bugbot_findings_policy_1.normalizeBugbotResponse)(response, maxAgentFindings); return normalized === undefined @@ -59255,7 +59255,7 @@ function prepareBugbotFindings(response, ignorePatterns, minSeverityValue, maxCo /***/ }), -/***/ 3496: +/***/ 73654: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -59264,14 +59264,14 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.MIN_AGENT_FINDING_CONFIDENCE = exports.MAX_AGENT_RESOLVED_FINDINGS = exports.MAX_AGENT_FINDINGS = void 0; exports.normalizeBugbotResponse = normalizeBugbotResponse; exports.prepareFindings = prepareFindings; -const deduplicate_findings_1 = __nccwpck_require__(62908); -const file_ignore_policy_1 = __nccwpck_require__(20542); -const limit_comments_1 = __nccwpck_require__(31643); -const bugbot_finding_marker_policy_1 = __nccwpck_require__(98024); -const path_validation_1 = __nccwpck_require__(70124); -const severity_1 = __nccwpck_require__(14626); -const finding_identity_1 = __nccwpck_require__(91853); -const sensitive_text_1 = __nccwpck_require__(47122); +const deduplicate_findings_1 = __nccwpck_require__(71392); +const file_ignore_policy_1 = __nccwpck_require__(56498); +const limit_comments_1 = __nccwpck_require__(9306); +const bugbot_finding_marker_policy_1 = __nccwpck_require__(80639); +const path_validation_1 = __nccwpck_require__(33308); +const severity_1 = __nccwpck_require__(2624); +const finding_identity_1 = __nccwpck_require__(657); +const sensitive_text_1 = __nccwpck_require__(98209); /** Hard cap for model-controlled arrays before any filtering or publication. */ exports.MAX_AGENT_FINDINGS = 500; exports.MAX_AGENT_RESOLVED_FINDINGS = 500; @@ -59404,7 +59404,7 @@ function isRecord(value) { /***/ }), -/***/ 88442: +/***/ 60836: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -59415,10 +59415,10 @@ function isRecord(value) { */ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.publishFindings = publishFindings; -const finding_1 = __nccwpck_require__(31011); -const publish_issue_finding_comment_1 = __nccwpck_require__(84950); -const publish_pr_review_comments_1 = __nccwpck_require__(50352); -const publish_overflow_comment_1 = __nccwpck_require__(10974); +const finding_1 = __nccwpck_require__(82048); +const publish_issue_finding_comment_1 = __nccwpck_require__(62743); +const publish_pr_review_comments_1 = __nccwpck_require__(34439); +const publish_overflow_comment_1 = __nccwpck_require__(2810); async function publishFindings(param) { const { operation, context, findings, commitSha, overflowCount = 0, overflowTitles = [], ports, catalog } = param; const { existingByFindingId, canonicalPullRequest, prContext } = context; @@ -59450,15 +59450,15 @@ async function publishFindings(param) { /***/ }), -/***/ 84950: +/***/ 62743: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.publishIssueFindingComment = publishIssueFindingComment; -const bugbot_finding_marker_policy_1 = __nccwpck_require__(98024); -const logging_ports_1 = __nccwpck_require__(6152); +const bugbot_finding_marker_policy_1 = __nccwpck_require__(80639); +const logging_ports_1 = __nccwpck_require__(73001); async function publishIssueFindingComment(repository, issueNumber, finding, existing, commitSha, catalog) { const body = (0, bugbot_finding_marker_policy_1.buildCommentBody)(finding, false, undefined, { catalog }); const options = commitSha ? { commitSha } : undefined; @@ -59474,16 +59474,16 @@ async function publishIssueFindingComment(repository, issueNumber, finding, exis /***/ }), -/***/ 10974: +/***/ 2810: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.publishOverflowComment = publishOverflowComment; -const logging_ports_1 = __nccwpck_require__(6152); -const github_comment_publication_policy_1 = __nccwpck_require__(72712); -const bugbot_message_catalog_1 = __nccwpck_require__(7406); +const logging_ports_1 = __nccwpck_require__(73001); +const github_comment_publication_policy_1 = __nccwpck_require__(22913); +const bugbot_message_catalog_1 = __nccwpck_require__(84479); async function publishOverflowComment(repository, issueNumber, overflowCount, overflowTitles, commitSha, catalog = (0, bugbot_message_catalog_1.resolveStaticBugbotCatalog)('en-US')) { if (overflowCount <= 0) return; @@ -59503,19 +59503,19 @@ ${catalog.message('bugbot.overflow.body', { count: `**${overflowCount}**` }, ove /***/ }), -/***/ 50352: +/***/ 34439: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.PullRequestReviewCommentPublisher = void 0; -const bugbot_finding_marker_policy_1 = __nccwpck_require__(98024); -const path_validation_1 = __nccwpck_require__(70124); -const logging_ports_1 = __nccwpck_require__(6152); -const github_comment_publication_policy_1 = __nccwpck_require__(72712); -const bugbot_review_presentation_policy_1 = __nccwpck_require__(43799); -const bugbot_message_catalog_1 = __nccwpck_require__(7406); +const bugbot_finding_marker_policy_1 = __nccwpck_require__(80639); +const path_validation_1 = __nccwpck_require__(33308); +const logging_ports_1 = __nccwpck_require__(73001); +const github_comment_publication_policy_1 = __nccwpck_require__(22913); +const bugbot_review_presentation_policy_1 = __nccwpck_require__(77193); +const bugbot_message_catalog_1 = __nccwpck_require__(84479); class PullRequestReviewCommentPublisher { constructor(options) { this.options = options; @@ -59669,7 +59669,7 @@ function sanitizeSummaryText(value, maximum) { /***/ }), -/***/ 13059: +/***/ 41038: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -59677,11 +59677,11 @@ function sanitizeSummaryText(value, maximum) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.queryBugbotFindings = queryBugbotFindings; exports.queryBugbotPartitionFindings = queryBugbotPartitionFindings; -const agent_task_policy_1 = __nccwpck_require__(85712); -const schema_1 = __nccwpck_require__(16808); -const agent_output_locale_policy_1 = __nccwpck_require__(30601); -const application_error_1 = __nccwpck_require__(75999); -const logging_ports_1 = __nccwpck_require__(6152); +const agent_task_policy_1 = __nccwpck_require__(2601); +const schema_1 = __nccwpck_require__(98135); +const agent_output_locale_policy_1 = __nccwpck_require__(2584); +const application_error_1 = __nccwpck_require__(2965); +const logging_ports_1 = __nccwpck_require__(73001); const MAX_PARTITION_QUERY_ATTEMPTS = 3; function bugbotQueryOptions(schema) { return (0, agent_output_locale_policy_1.productFacingAgentQueryOptions)('bugbot-review', schema); @@ -59734,19 +59734,19 @@ async function queryBugbotPartitionFindings(repository, configuration, prompt, t /***/ }), -/***/ 57515: +/***/ 39174: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.reconcileBugbotReviewState = reconcileBugbotReviewState; -const review_projection_1 = __nccwpck_require__(80859); -const bugbot_reconciliation_policy_1 = __nccwpck_require__(78128); -const bugbot_provider_projection_policy_1 = __nccwpck_require__(85821); -const bugbot_finding_marker_policy_1 = __nccwpck_require__(98024); -const load_bugbot_reconciliation_snapshot_use_case_1 = __nccwpck_require__(44861); -const synchronize_bugbot_review_presentation_use_case_1 = __nccwpck_require__(44491); +const review_projection_1 = __nccwpck_require__(23272); +const bugbot_reconciliation_policy_1 = __nccwpck_require__(54271); +const bugbot_provider_projection_policy_1 = __nccwpck_require__(95220); +const bugbot_finding_marker_policy_1 = __nccwpck_require__(80639); +const load_bugbot_reconciliation_snapshot_use_case_1 = __nccwpck_require__(50980); +const synchronize_bugbot_review_presentation_use_case_1 = __nccwpck_require__(861); /** * Orchestrates final Bugbot reconciliation. Provider acquisition, pure state * planning, and presentation mutations are deliberately owned by dedicated @@ -59814,15 +59814,15 @@ function toSafeOperationMessage(error) { /***/ }), -/***/ 17437: +/***/ 83711: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.RememberBugbotRuleUseCase = void 0; -const result_1 = __nccwpck_require__(73817); -const application_error_1 = __nccwpck_require__(75999); +const result_1 = __nccwpck_require__(61444); +const application_error_1 = __nccwpck_require__(2965); /** Stores an explicitly approved, repository-versioned Bugbot rule. */ class RememberBugbotRuleUseCase { constructor(rules) { @@ -59857,14 +59857,14 @@ exports.RememberBugbotRuleUseCase = RememberBugbotRuleUseCase; /***/ }), -/***/ 35300: +/***/ 101: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.resolveIssueFinding = resolveIssueFinding; -const bugbot_finding_marker_policy_1 = __nccwpck_require__(98024); +const bugbot_finding_marker_policy_1 = __nccwpck_require__(80639); async function resolveIssueFinding(repository, resolution, catalog) { const body = resolution.comment.body; const marker = (0, bugbot_finding_marker_policy_1.parseMarker)(body).find((candidate) => candidate.findingId === resolution.findingId); @@ -59881,15 +59881,15 @@ async function resolveIssueFinding(repository, resolution, catalog) { /***/ }), -/***/ 64567: +/***/ 72038: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.resolvePullRequestFinding = resolvePullRequestFinding; -const pull_request_review_errors_1 = __nccwpck_require__(46445); -const bugbot_finding_marker_policy_1 = __nccwpck_require__(98024); +const pull_request_review_errors_1 = __nccwpck_require__(81504); +const bugbot_finding_marker_policy_1 = __nccwpck_require__(80639); async function resolvePullRequestFinding(repository, resolution, catalog) { const comments = await repository.listPullRequestReviewComments(resolution.pullRequestNumber); const comment = comments.find((candidate) => candidate.identity === resolution.commentIdentity); @@ -59918,7 +59918,7 @@ async function resolvePullRequestFinding(repository, resolution, catalog) { /***/ }), -/***/ 59828: +/***/ 28636: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -59964,7 +59964,7 @@ function sanitizeUserCommentForPrompt(raw) { /***/ }), -/***/ 16808: +/***/ 98135: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -59976,8 +59976,8 @@ function sanitizeUserCommentForPrompt(raw) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.BUGBOT_FIX_INTENT_RESPONSE_SCHEMA = exports.BUGBOT_PARTITION_RESPONSE_SCHEMA = exports.BUGBOT_RESPONSE_SCHEMA = void 0; exports.buildBugbotPartitionResponseSchema = buildBugbotPartitionResponseSchema; -const bugbot_finding_marker_policy_1 = __nccwpck_require__(98024); -const agent_output_locale_policy_1 = __nccwpck_require__(30601); +const bugbot_finding_marker_policy_1 = __nccwpck_require__(80639); +const agent_output_locale_policy_1 = __nccwpck_require__(2584); /** Detection returns findings and explicit lifecycle changes for prior finding IDs. */ exports.BUGBOT_RESPONSE_SCHEMA = { type: 'object', @@ -60114,7 +60114,7 @@ exports.BUGBOT_FIX_INTENT_RESPONSE_SCHEMA = { /***/ }), -/***/ 14626: +/***/ 2624: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -60151,19 +60151,19 @@ function meetsMinSeverity(findingSeverity, minSeverity) { /***/ }), -/***/ 44491: +/***/ 861: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.synchronizeBugbotReviewPresentation = synchronizeBugbotReviewPresentation; -const application_error_1 = __nccwpck_require__(75999); -const bugbot_review_presentation_policy_1 = __nccwpck_require__(43799); -const bugbot_review_ownership_policy_1 = __nccwpck_require__(83288); -const review_projection_1 = __nccwpck_require__(80859); -const publication_identity_policy_1 = __nccwpck_require__(45403); -const bugbot_message_catalog_1 = __nccwpck_require__(7406); +const application_error_1 = __nccwpck_require__(2965); +const bugbot_review_presentation_policy_1 = __nccwpck_require__(77193); +const bugbot_review_ownership_policy_1 = __nccwpck_require__(11719); +const review_projection_1 = __nccwpck_require__(23272); +const publication_identity_policy_1 = __nccwpck_require__(12590); +const bugbot_message_catalog_1 = __nccwpck_require__(84479); const REVIEW_UPDATE_BATCH_SIZE = 20; const MAX_REVIEW_UPDATES_PER_RUN = 100; const REVIEW_UPDATE_CONCURRENCY = 4; @@ -60341,7 +60341,7 @@ async function mapWithConcurrency(values, concurrency, operation) { /***/ }), -/***/ 96031: +/***/ 32739: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -60383,7 +60383,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.MAX_VERIFY_COMMANDS = void 0; exports.parseVerifyCommand = parseVerifyCommand; exports.limitVerifyCommands = limitVerifyCommands; -const shellQuote = __importStar(__nccwpck_require__(75430)); +const shellQuote = __importStar(__nccwpck_require__(18342)); exports.MAX_VERIFY_COMMANDS = 20; function parseVerifyCommand(cmd) { const trimmed = cmd.trim(); @@ -60409,15 +60409,15 @@ function limitVerifyCommands(commands) { /***/ }), -/***/ 57742: +/***/ 71843: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runVerifyCommands = runVerifyCommands; -const logging_ports_1 = __nccwpck_require__(6152); -const verify_command_policy_1 = __nccwpck_require__(96031); +const logging_ports_1 = __nccwpck_require__(73001); +const verify_command_policy_1 = __nccwpck_require__(32739); async function runVerifyCommands(commands, execute) { for (const command of commands) { const result = await executeVerifyCommand(command, execute); @@ -60468,7 +60468,7 @@ function isSensitiveArgumentName(argument) { /***/ }), -/***/ 93370: +/***/ 51578: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -60547,16 +60547,16 @@ async function hasWorkspaceChanges(gitCommitPort) { /***/ }), -/***/ 28356: +/***/ 75384: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.CheckChangesIssueSizeUseCase = void 0; -const logging_ports_1 = __nccwpck_require__(6152); -const task_emoji_1 = __nccwpck_require__(46103); -const check_changes_issue_size_workflow_1 = __nccwpck_require__(43250); +const logging_ports_1 = __nccwpck_require__(73001); +const task_emoji_1 = __nccwpck_require__(83142); +const check_changes_issue_size_workflow_1 = __nccwpck_require__(51875); class CheckChangesIssueSizeUseCase { constructor(projectBoardCommandPort, issueRepository, pullRequestRepository, branchChangeSizePort) { this.projectBoardCommandPort = projectBoardCommandPort; @@ -60580,17 +60580,17 @@ exports.CheckChangesIssueSizeUseCase = CheckChangesIssueSizeUseCase; /***/ }), -/***/ 43250: +/***/ 51875: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runCheckChangesIssueSize = runCheckChangesIssueSize; -const result_1 = __nccwpck_require__(73817); -const logging_ports_1 = __nccwpck_require__(6152); -const update_change_size_labels_1 = __nccwpck_require__(51200); -const application_error_1 = __nccwpck_require__(75999); +const result_1 = __nccwpck_require__(61444); +const logging_ports_1 = __nccwpck_require__(73001); +const update_change_size_labels_1 = __nccwpck_require__(65146); +const application_error_1 = __nccwpck_require__(2965); async function runCheckChangesIssueSize(param, taskId, dependencies) { try { const baseBranch = param.baseBranch; @@ -60649,14 +60649,14 @@ function logSize(size, githubSize, reason, currentLabel) { /***/ }), -/***/ 6287: +/***/ 65545: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.DetectPotentialProblemsUseCase = void 0; -const detect_potential_problems_workflow_1 = __nccwpck_require__(37033); +const detect_potential_problems_workflow_1 = __nccwpck_require__(40555); /** Application boundary for detecting, publishing and resolving Bugbot findings. */ class DetectPotentialProblemsUseCase { constructor(aiRepository, scm, telemetryPort, catalogResolver) { @@ -60680,30 +60680,30 @@ exports.DetectPotentialProblemsUseCase = DetectPotentialProblemsUseCase; /***/ }), -/***/ 37033: +/***/ 40555: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runDetectPotentialProblemsWorkflow = runDetectPotentialProblemsWorkflow; -const agent_1 = __nccwpck_require__(79937); -const result_1 = __nccwpck_require__(73817); -const task_emoji_1 = __nccwpck_require__(46103); -const logging_ports_1 = __nccwpck_require__(6152); -const pull_request_review_errors_1 = __nccwpck_require__(46445); -const load_bugbot_context_use_case_1 = __nccwpck_require__(4050); -const bugbot_context_request_1 = __nccwpck_require__(98299); -const apply_detected_findings_1 = __nccwpck_require__(20793); -const bugbot_finding_status_policy_1 = __nccwpck_require__(53822); -const bugbot_review_telemetry_1 = __nccwpck_require__(46790); -const analyze_bugbot_revision_use_case_1 = __nccwpck_require__(4658); -const bugbot_review_freshness_1 = __nccwpck_require__(14307); -const reconcile_bugbot_review_state_use_case_1 = __nccwpck_require__(57515); -const application_error_1 = __nccwpck_require__(75999); -const bugbot_event_ownership_policy_1 = __nccwpck_require__(52771); -const bugbot_message_catalog_1 = __nccwpck_require__(7406); -const bugbot_partition_completion_policy_1 = __nccwpck_require__(57555); +const agent_1 = __nccwpck_require__(71889); +const result_1 = __nccwpck_require__(61444); +const task_emoji_1 = __nccwpck_require__(83142); +const logging_ports_1 = __nccwpck_require__(73001); +const pull_request_review_errors_1 = __nccwpck_require__(81504); +const load_bugbot_context_use_case_1 = __nccwpck_require__(56378); +const bugbot_context_request_1 = __nccwpck_require__(72881); +const apply_detected_findings_1 = __nccwpck_require__(60017); +const bugbot_finding_status_policy_1 = __nccwpck_require__(9298); +const bugbot_review_telemetry_1 = __nccwpck_require__(14285); +const analyze_bugbot_revision_use_case_1 = __nccwpck_require__(92650); +const bugbot_review_freshness_1 = __nccwpck_require__(26699); +const reconcile_bugbot_review_state_use_case_1 = __nccwpck_require__(39174); +const application_error_1 = __nccwpck_require__(2965); +const bugbot_event_ownership_policy_1 = __nccwpck_require__(10580); +const bugbot_message_catalog_1 = __nccwpck_require__(84479); +const bugbot_partition_completion_policy_1 = __nccwpck_require__(83782); const TASK_ID = 'DetectPotentialProblemsUseCase'; /** Coordinates Bugbot context, analysis and finding publication behind application ports. */ async function runDetectPotentialProblemsWorkflow(reviewContext, dependencies) { @@ -61035,15 +61035,15 @@ function resolvePublicationCatalog(operation, dependencies, publishesToPullReque /***/ }), -/***/ 68549: +/***/ 96838: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.checkoutBranch = checkoutBranch; -const application_error_1 = __nccwpck_require__(75999); -const logging_ports_1 = __nccwpck_require__(6152); +const application_error_1 = __nccwpck_require__(2965); +const logging_ports_1 = __nccwpck_require__(73001); const STASH_MESSAGE = 'bugbot-autofix-before-checkout'; async function hasUncommittedChanges(port) { let output = ''; @@ -61096,16 +61096,16 @@ async function restoreStashedChanges(port) { /***/ }), -/***/ 33276: +/***/ 77749: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.NotifyNewCommitOnIssueUseCase = void 0; -const logging_ports_1 = __nccwpck_require__(6152); -const task_emoji_1 = __nccwpck_require__(46103); -const notify_new_commit_on_issue_workflow_1 = __nccwpck_require__(46101); +const logging_ports_1 = __nccwpck_require__(73001); +const task_emoji_1 = __nccwpck_require__(83142); +const notify_new_commit_on_issue_workflow_1 = __nccwpck_require__(22712); class NotifyNewCommitOnIssueUseCase { constructor(issueRepository) { this.issueRepository = issueRepository; @@ -61121,16 +61121,16 @@ exports.NotifyNewCommitOnIssueUseCase = NotifyNewCommitOnIssueUseCase; /***/ }), -/***/ 46101: +/***/ 22712: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runNotifyNewCommitOnIssueWorkflow = runNotifyNewCommitOnIssueWorkflow; -const result_1 = __nccwpck_require__(73817); -const logging_ports_1 = __nccwpck_require__(6152); -const application_error_1 = __nccwpck_require__(75999); +const result_1 = __nccwpck_require__(61444); +const logging_ports_1 = __nccwpck_require__(73001); +const application_error_1 = __nccwpck_require__(2965); async function runNotifyNewCommitOnIssueWorkflow(param, taskId, issueRepository) { const result = []; try { @@ -61158,7 +61158,7 @@ async function runNotifyNewCommitOnIssueWorkflow(param, taskId, issueRepository) /***/ }), -/***/ 51200: +/***/ 65146: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -61194,7 +61194,7 @@ async function updateIssueAndRelatedPullRequests(request, ports) { /***/ }), -/***/ 19004: +/***/ 39633: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -61206,15 +61206,15 @@ async function updateIssueAndRelatedPullRequests(request, ports) { */ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.DoUserRequestUseCase = void 0; -const agent_1 = __nccwpck_require__(79937); -const prompts_1 = __nccwpck_require__(69518); -const logging_ports_1 = __nccwpck_require__(6152); -const task_emoji_1 = __nccwpck_require__(46103); -const result_1 = __nccwpck_require__(73817); -const project_context_instruction_1 = __nccwpck_require__(63907); -const sanitize_user_comment_for_prompt_1 = __nccwpck_require__(59828); -const workspace_mutation_guard_1 = __nccwpck_require__(24243); -const application_error_1 = __nccwpck_require__(75999); +const agent_1 = __nccwpck_require__(71889); +const prompts_1 = __nccwpck_require__(71854); +const logging_ports_1 = __nccwpck_require__(73001); +const task_emoji_1 = __nccwpck_require__(83142); +const result_1 = __nccwpck_require__(61444); +const project_context_instruction_1 = __nccwpck_require__(36158); +const sanitize_user_comment_for_prompt_1 = __nccwpck_require__(28636); +const workspace_mutation_guard_1 = __nccwpck_require__(12627); +const application_error_1 = __nccwpck_require__(2965); const TASK_ID = "DoUserRequestUseCase"; class DoUserRequestUseCase { constructor(aiRepository, gitCommitPort) { @@ -61308,7 +61308,7 @@ function failure(error) { /***/ }), -/***/ 24243: +/***/ 12627: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -61317,9 +61317,9 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.MAX_AUTOMATED_CHANGED_PATHS = void 0; exports.prepareWorkspaceMutation = prepareWorkspaceMutation; exports.finalizeWorkspaceMutation = finalizeWorkspaceMutation; -const application_error_1 = __nccwpck_require__(75999); -const git_branch_checkout_1 = __nccwpck_require__(68549); -const workspace_changes_1 = __nccwpck_require__(93370); +const application_error_1 = __nccwpck_require__(2965); +const git_branch_checkout_1 = __nccwpck_require__(96838); +const workspace_changes_1 = __nccwpck_require__(51578); exports.MAX_AUTOMATED_CHANGED_PATHS = 100; /** Establishes a clean and deterministic repository boundary before an agent may mutate files. */ async function prepareWorkspaceMutation(gitCommitPort, options) { @@ -61373,15 +61373,15 @@ async function inspectWorkspace(gitCommitPort, phase) { /***/ }), -/***/ 72063: +/***/ 3389: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.extractStructuredAnswer = extractStructuredAnswer; -const agent_output_locale_policy_1 = __nccwpck_require__(30601); -const application_error_1 = __nccwpck_require__(75999); +const agent_output_locale_policy_1 = __nccwpck_require__(2584); +const application_error_1 = __nccwpck_require__(2965); function extractStructuredAnswer(response, targetLocale) { if (response == null) return ''; @@ -61396,16 +61396,16 @@ function extractStructuredAnswer(response, targetLocale) { /***/ }), -/***/ 18846: +/***/ 88960: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.CheckPermissionsUseCase = void 0; -const logging_ports_1 = __nccwpck_require__(6152); -const task_emoji_1 = __nccwpck_require__(46103); -const check_permissions_workflow_1 = __nccwpck_require__(17102); +const logging_ports_1 = __nccwpck_require__(73001); +const task_emoji_1 = __nccwpck_require__(83142); +const check_permissions_workflow_1 = __nccwpck_require__(98658); class CheckPermissionsUseCase { constructor(organizationMembersPort) { this.organizationMembersPort = organizationMembersPort; @@ -61423,7 +61423,7 @@ exports.CheckPermissionsUseCase = CheckPermissionsUseCase; /***/ }), -/***/ 17102: +/***/ 98658: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -61431,9 +61431,9 @@ exports.CheckPermissionsUseCase = CheckPermissionsUseCase; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.projectCheckPermissionsContext = projectCheckPermissionsContext; exports.runCheckPermissionsWorkflow = runCheckPermissionsWorkflow; -const result_1 = __nccwpck_require__(73817); -const logging_ports_1 = __nccwpck_require__(6152); -const application_error_1 = __nccwpck_require__(75999); +const result_1 = __nccwpck_require__(61444); +const logging_ports_1 = __nccwpck_require__(73001); +const application_error_1 = __nccwpck_require__(2965); function projectCheckPermissionsContext(source) { const issueTarget = source.isIssue; return Object.freeze({ @@ -61497,7 +61497,7 @@ function buildInactiveResult(param, taskId) { /***/ }), -/***/ 72770: +/***/ 78212: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -61506,16 +61506,16 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.CommentLanguageTranslationWorkflow = exports.TRANSLATED_COMMENT_MARKER = void 0; exports.projectCommentLanguageRequest = projectCommentLanguageRequest; exports.getCommentLanguageAdaptationPayload = getCommentLanguageAdaptationPayload; -const result_1 = __nccwpck_require__(73817); -const agent_task_policy_1 = __nccwpck_require__(85712); -const agent_response_schemas_1 = __nccwpck_require__(25603); -const prompts_1 = __nccwpck_require__(69518); -const logging_ports_1 = __nccwpck_require__(6152); -const task_emoji_1 = __nccwpck_require__(46103); -const comment_translation_policy_1 = __nccwpck_require__(27150); -const locale_1 = __nccwpck_require__(15386); -const application_error_1 = __nccwpck_require__(75999); -var comment_translation_policy_2 = __nccwpck_require__(27150); +const result_1 = __nccwpck_require__(61444); +const agent_task_policy_1 = __nccwpck_require__(2601); +const agent_response_schemas_1 = __nccwpck_require__(63523); +const prompts_1 = __nccwpck_require__(71854); +const logging_ports_1 = __nccwpck_require__(73001); +const task_emoji_1 = __nccwpck_require__(83142); +const comment_translation_policy_1 = __nccwpck_require__(22406); +const locale_1 = __nccwpck_require__(64552); +const application_error_1 = __nccwpck_require__(2965); +var comment_translation_policy_2 = __nccwpck_require__(22406); Object.defineProperty(exports, "TRANSLATED_COMMENT_MARKER", ({ enumerable: true, get: function () { return comment_translation_policy_2.TRANSLATED_COMMENT_MARKER; } })); function projectCommentLanguageRequest(source) { return Object.freeze({ @@ -61656,7 +61656,7 @@ function languageAdaptationPayload(status, targetLocale, interpretedComment, sou /***/ }), -/***/ 56334: +/***/ 26147: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -61700,7 +61700,7 @@ function toCamelCase(input) { /***/ }), -/***/ 79544: +/***/ 93499: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -61723,14 +61723,14 @@ async function cleanupDuplicateComment(context, comments, sourceIsCurrent) { /***/ }), -/***/ 65440: +/***/ 54191: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildCommitPrefix = buildCommitPrefix; -const commit_prefix_transform_policy_1 = __nccwpck_require__(56334); +const commit_prefix_transform_policy_1 = __nccwpck_require__(26147); function buildCommitPrefix(branchName, transforms, onUnknownTransform) { return transforms .split(',') @@ -61741,18 +61741,18 @@ function buildCommitPrefix(branchName, transforms, onUnknownTransform) { /***/ }), -/***/ 59946: +/***/ 85276: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.GetHotfixVersionUseCase = void 0; -const result_1 = __nccwpck_require__(73817); -const content_utils_1 = __nccwpck_require__(92816); -const logging_ports_1 = __nccwpck_require__(6152); -const task_emoji_1 = __nccwpck_require__(46103); -const application_error_1 = __nccwpck_require__(75999); +const result_1 = __nccwpck_require__(61444); +const content_utils_1 = __nccwpck_require__(61146); +const logging_ports_1 = __nccwpck_require__(73001); +const task_emoji_1 = __nccwpck_require__(83142); +const application_error_1 = __nccwpck_require__(2965); class GetHotfixVersionUseCase { constructor(issueRepository) { this.issueRepository = issueRepository; @@ -61833,18 +61833,18 @@ function isPositiveIssueNumber(value) { /***/ }), -/***/ 64410: +/***/ 65633: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.GetReleaseTypeUseCase = void 0; -const result_1 = __nccwpck_require__(73817); -const content_utils_1 = __nccwpck_require__(92816); -const logging_ports_1 = __nccwpck_require__(6152); -const task_emoji_1 = __nccwpck_require__(46103); -const application_error_1 = __nccwpck_require__(75999); +const result_1 = __nccwpck_require__(61444); +const content_utils_1 = __nccwpck_require__(61146); +const logging_ports_1 = __nccwpck_require__(73001); +const task_emoji_1 = __nccwpck_require__(83142); +const application_error_1 = __nccwpck_require__(2965); class GetReleaseTypeUseCase { constructor(issueRepository) { this.issueRepository = issueRepository; @@ -61914,18 +61914,18 @@ function isPositiveIssueNumber(value) { /***/ }), -/***/ 70587: +/***/ 8709: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.GetReleaseVersionUseCase = void 0; -const result_1 = __nccwpck_require__(73817); -const content_utils_1 = __nccwpck_require__(92816); -const logging_ports_1 = __nccwpck_require__(6152); -const task_emoji_1 = __nccwpck_require__(46103); -const application_error_1 = __nccwpck_require__(75999); +const result_1 = __nccwpck_require__(61444); +const content_utils_1 = __nccwpck_require__(61146); +const logging_ports_1 = __nccwpck_require__(73001); +const task_emoji_1 = __nccwpck_require__(83142); +const application_error_1 = __nccwpck_require__(2965); class GetReleaseVersionUseCase { constructor(issueRepository) { this.issueRepository = issueRepository; @@ -61996,7 +61996,7 @@ function isPositiveIssueNumber(value) { /***/ }), -/***/ 89064: +/***/ 72383: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -62005,10 +62005,10 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.projectIssueContentLinkContext = projectIssueContentLinkContext; exports.projectPullRequestContentLinkContext = projectPullRequestContentLinkContext; exports.runProjectContentLinkWorkflow = runProjectContentLinkWorkflow; -const result_1 = __nccwpck_require__(73817); -const logging_ports_1 = __nccwpck_require__(6152); -const task_emoji_1 = __nccwpck_require__(46103); -const application_error_1 = __nccwpck_require__(75999); +const result_1 = __nccwpck_require__(61444); +const logging_ports_1 = __nccwpck_require__(73001); +const task_emoji_1 = __nccwpck_require__(83142); +const application_error_1 = __nccwpck_require__(2965); function projectIssueContentLinkContext(source) { return projectContext(source, 'issue', source.issue.number, source.project.getProjectColumnIssueCreated()); } @@ -62088,23 +62088,23 @@ function capitalize(value) { /***/ }), -/***/ 40558: +/***/ 81581: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runThinkAnswerWorkflow = runThinkAnswerWorkflow; -const result_1 = __nccwpck_require__(73817); -const agent_task_policy_1 = __nccwpck_require__(85712); -const agent_response_schemas_1 = __nccwpck_require__(25603); -const prompts_1 = __nccwpck_require__(69518); -const logging_ports_1 = __nccwpck_require__(6152); -const project_context_instruction_1 = __nccwpck_require__(63907); -const agent_answer_policy_1 = __nccwpck_require__(72063); -const github_comment_publication_policy_1 = __nccwpck_require__(72712); -const application_error_1 = __nccwpck_require__(75999); -const agent_output_locale_policy_1 = __nccwpck_require__(30601); +const result_1 = __nccwpck_require__(61444); +const agent_task_policy_1 = __nccwpck_require__(2601); +const agent_response_schemas_1 = __nccwpck_require__(63523); +const prompts_1 = __nccwpck_require__(71854); +const logging_ports_1 = __nccwpck_require__(73001); +const project_context_instruction_1 = __nccwpck_require__(36158); +const agent_answer_policy_1 = __nccwpck_require__(3389); +const github_comment_publication_policy_1 = __nccwpck_require__(22913); +const application_error_1 = __nccwpck_require__(2965); +const agent_output_locale_policy_1 = __nccwpck_require__(2584); async function runThinkAnswerWorkflow(param, taskId, request, dependencies) { const issueDescription = await loadIssueDescription(request.issueNumberForContext, dependencies.issueDescriptionQueryPort); const contextBlock = issueDescription @@ -62180,7 +62180,7 @@ async function queryThinkAnswer(param, prompt, repository, targetLocale) { /***/ }), -/***/ 59687: +/***/ 12636: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -62203,7 +62203,7 @@ function extractMentionQuestion(commentBody, tokenUser) { /***/ }), -/***/ 23995: +/***/ 98559: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -62211,10 +62211,10 @@ function extractMentionQuestion(commentBody, tokenUser) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.resolveThinkRequest = resolveThinkRequest; exports.buildExplicitCommandQuestion = buildExplicitCommandQuestion; -const copilot_command_1 = __nccwpck_require__(11771); -const copilot_comment_request_1 = __nccwpck_require__(86819); -const think_input_policy_1 = __nccwpck_require__(59687); -const sanitize_user_comment_for_prompt_1 = __nccwpck_require__(59828); +const copilot_command_1 = __nccwpck_require__(87134); +const copilot_comment_request_1 = __nccwpck_require__(81916); +const think_input_policy_1 = __nccwpck_require__(12636); +const sanitize_user_comment_for_prompt_1 = __nccwpck_require__(28636); /** Resolves the comment input and destination without performing I/O. */ function resolveThinkRequest(param) { const commentBody = (0, think_input_policy_1.getThinkCommentBody)({ @@ -62277,14 +62277,14 @@ function buildExplicitCommandQuestion(command) { /***/ }), -/***/ 89255: +/***/ 25099: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.ThinkUseCase = void 0; -const think_workflow_1 = __nccwpck_require__(36450); +const think_workflow_1 = __nccwpck_require__(14720); class ThinkUseCase { constructor(issueDescriptionQueryPort, aiRepository) { this.issueDescriptionQueryPort = issueDescriptionQueryPort; @@ -62303,7 +62303,7 @@ exports.ThinkUseCase = ThinkUseCase; /***/ }), -/***/ 36450: +/***/ 14720: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -62311,14 +62311,14 @@ exports.ThinkUseCase = ThinkUseCase; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.projectThinkContext = projectThinkContext; exports.runThinkWorkflow = runThinkWorkflow; -const agent_1 = __nccwpck_require__(79937); -const result_1 = __nccwpck_require__(73817); -const logging_ports_1 = __nccwpck_require__(6152); -const think_request_policy_1 = __nccwpck_require__(23995); -const think_answer_workflow_1 = __nccwpck_require__(40558); -const agent_task_policy_1 = __nccwpck_require__(85712); -const application_error_1 = __nccwpck_require__(75999); -const locale_1 = __nccwpck_require__(15386); +const agent_1 = __nccwpck_require__(71889); +const result_1 = __nccwpck_require__(61444); +const logging_ports_1 = __nccwpck_require__(73001); +const think_request_policy_1 = __nccwpck_require__(98559); +const think_answer_workflow_1 = __nccwpck_require__(81581); +const agent_task_policy_1 = __nccwpck_require__(2601); +const application_error_1 = __nccwpck_require__(2965); +const locale_1 = __nccwpck_require__(64552); function projectThinkContext(source) { const request = (0, think_request_policy_1.resolveThinkRequest)(source); const tokenUser = source.tokenUser?.trim(); @@ -62402,7 +62402,7 @@ function logSkipReason(reason, tokenUser) { /***/ }), -/***/ 1725: +/***/ 93066: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -62411,10 +62411,10 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.MAX_TRANSITION_NOTIFICATION_LINKS = exports.MAX_TRANSITION_NOTIFICATION_CHARACTERS = void 0; exports.reconcileTransitionNotification = reconcileTransitionNotification; exports.renderTransitionNotification = renderTransitionNotification; -const github_publication_1 = __nccwpck_require__(35793); -const github_user_policy_1 = __nccwpck_require__(84403); -const publication_identity_policy_1 = __nccwpck_require__(45403); -const duplicate_comment_cleanup_workflow_1 = __nccwpck_require__(79544); +const github_publication_1 = __nccwpck_require__(75905); +const github_user_policy_1 = __nccwpck_require__(19596); +const publication_identity_policy_1 = __nccwpck_require__(12590); +const duplicate_comment_cleanup_workflow_1 = __nccwpck_require__(93499); exports.MAX_TRANSITION_NOTIFICATION_CHARACTERS = 400; exports.MAX_TRANSITION_NOTIFICATION_LINKS = 2; /** Creates one immutable action notification per exact transition fingerprint. */ @@ -62501,16 +62501,16 @@ function outcome(effect, canonicalCommentId, duplicatesRemoved = 0, duplicatesCo /***/ }), -/***/ 20556: +/***/ 21376: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.UpdateTitleUseCase = void 0; -const logging_ports_1 = __nccwpck_require__(6152); -const task_emoji_1 = __nccwpck_require__(46103); -const update_title_workflow_1 = __nccwpck_require__(50029); +const logging_ports_1 = __nccwpck_require__(73001); +const task_emoji_1 = __nccwpck_require__(83142); +const update_title_workflow_1 = __nccwpck_require__(89641); class UpdateTitleUseCase { constructor(issueRepository) { this.issueRepository = issueRepository; @@ -62535,7 +62535,7 @@ exports.UpdateTitleUseCase = UpdateTitleUseCase; /***/ }), -/***/ 50029: +/***/ 89641: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -62545,9 +62545,9 @@ exports.projectUpdateTitleContext = projectUpdateTitleContext; exports.runIssueTitleUpdate = runIssueTitleUpdate; exports.runPullRequestTitleUpdate = runPullRequestTitleUpdate; exports.titleUpdateFailure = titleUpdateFailure; -const result_1 = __nccwpck_require__(73817); -const application_error_1 = __nccwpck_require__(75999); -const positive_integer_policy_1 = __nccwpck_require__(19879); +const result_1 = __nccwpck_require__(61444); +const application_error_1 = __nccwpck_require__(2965); +const positive_integer_policy_1 = __nccwpck_require__(45613); function projectUpdateTitleContext(source) { if (source.isIssue) { return Object.freeze({ @@ -62649,14 +62649,14 @@ function skippedResult(taskId) { /***/ }), -/***/ 10706: +/***/ 41713: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.AnswerIssueHelpUseCase = void 0; -const answer_issue_help_workflow_1 = __nccwpck_require__(86428); +const answer_issue_help_workflow_1 = __nccwpck_require__(75001); /** Application boundary for the initial response to question/help issues. */ class AnswerIssueHelpUseCase { constructor(aiRepository) { @@ -62674,25 +62674,25 @@ exports.AnswerIssueHelpUseCase = AnswerIssueHelpUseCase; /***/ }), -/***/ 86428: +/***/ 75001: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runAnswerIssueHelpWorkflow = runAnswerIssueHelpWorkflow; -const agent_1 = __nccwpck_require__(79937); -const result_1 = __nccwpck_require__(73817); -const agent_task_policy_1 = __nccwpck_require__(85712); -const agent_response_schemas_1 = __nccwpck_require__(25603); -const prompts_1 = __nccwpck_require__(69518); -const logging_ports_1 = __nccwpck_require__(6152); -const project_context_instruction_1 = __nccwpck_require__(63907); -const task_emoji_1 = __nccwpck_require__(46103); -const agent_answer_policy_1 = __nccwpck_require__(72063); -const github_comment_publication_policy_1 = __nccwpck_require__(72712); -const application_error_1 = __nccwpck_require__(75999); -const agent_output_locale_policy_1 = __nccwpck_require__(30601); +const agent_1 = __nccwpck_require__(71889); +const result_1 = __nccwpck_require__(61444); +const agent_task_policy_1 = __nccwpck_require__(2601); +const agent_response_schemas_1 = __nccwpck_require__(63523); +const prompts_1 = __nccwpck_require__(71854); +const logging_ports_1 = __nccwpck_require__(73001); +const project_context_instruction_1 = __nccwpck_require__(36158); +const task_emoji_1 = __nccwpck_require__(83142); +const agent_answer_policy_1 = __nccwpck_require__(3389); +const github_comment_publication_policy_1 = __nccwpck_require__(22913); +const application_error_1 = __nccwpck_require__(2965); +const agent_output_locale_policy_1 = __nccwpck_require__(2584); const TASK_ID = 'AnswerIssueHelpUseCase'; /** Posts one contextual answer for a newly opened question/help issue. */ async function runAnswerIssueHelpWorkflow(param, dependencies) { @@ -62777,14 +62777,14 @@ function skipped() { /***/ }), -/***/ 55523: +/***/ 18189: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.AssignMemberToIssueUseCase = void 0; -const assign_members_workflow_1 = __nccwpck_require__(42343); +const assign_members_workflow_1 = __nccwpck_require__(78123); /** Application boundary for assigning issue or pull-request members. */ class AssignMemberToIssueUseCase { constructor(issueRepository, projectRepository) { @@ -62804,18 +62804,18 @@ exports.AssignMemberToIssueUseCase = AssignMemberToIssueUseCase; /***/ }), -/***/ 42343: +/***/ 78123: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runAssignMembersWorkflow = runAssignMembersWorkflow; -const result_1 = __nccwpck_require__(73817); -const logging_ports_1 = __nccwpck_require__(6152); -const task_emoji_1 = __nccwpck_require__(46103); -const assignee_assignment_policy_1 = __nccwpck_require__(85918); -const application_error_1 = __nccwpck_require__(75999); +const result_1 = __nccwpck_require__(61444); +const logging_ports_1 = __nccwpck_require__(73001); +const task_emoji_1 = __nccwpck_require__(83142); +const assignee_assignment_policy_1 = __nccwpck_require__(2426); +const application_error_1 = __nccwpck_require__(2965); const TASK_ID = 'AssignMemberToIssueUseCase'; /** Assigns the creator and remaining project members according to the pure assignment policy. */ async function runAssignMembersWorkflow(param, dependencies) { @@ -62878,14 +62878,14 @@ function assignmentResult(success, step) { /***/ }), -/***/ 80174: +/***/ 1093: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.AssignReviewersToIssueUseCase = void 0; -const assign_reviewers_workflow_1 = __nccwpck_require__(97260); +const assign_reviewers_workflow_1 = __nccwpck_require__(35521); /** Application boundary for requesting the configured number of reviewers. */ class AssignReviewersToIssueUseCase { constructor(issueRepository, pullRequestRepository, projectRepository) { @@ -62907,19 +62907,19 @@ exports.AssignReviewersToIssueUseCase = AssignReviewersToIssueUseCase; /***/ }), -/***/ 97260: +/***/ 35521: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runAssignReviewersWorkflow = runAssignReviewersWorkflow; -const result_1 = __nccwpck_require__(73817); -const pull_request_review_errors_1 = __nccwpck_require__(46445); -const logging_ports_1 = __nccwpck_require__(6152); -const task_emoji_1 = __nccwpck_require__(46103); -const reviewer_assignment_policy_1 = __nccwpck_require__(88350); -const application_error_1 = __nccwpck_require__(75999); +const result_1 = __nccwpck_require__(61444); +const pull_request_review_errors_1 = __nccwpck_require__(81504); +const logging_ports_1 = __nccwpck_require__(73001); +const task_emoji_1 = __nccwpck_require__(83142); +const reviewer_assignment_policy_1 = __nccwpck_require__(49532); +const application_error_1 = __nccwpck_require__(2965); const TASK_ID = 'AssignReviewersToIssueUseCase'; /** Selects and requests reviewers without coupling the use-case boundary to GitHub. */ async function runAssignReviewersWorkflow(param, dependencies) { @@ -62999,7 +62999,7 @@ function failureResult(step) { /***/ }), -/***/ 29988: +/***/ 50133: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -63021,16 +63021,16 @@ function selectBranchPreparationStrategy(flags) { /***/ }), -/***/ 19511: +/***/ 46243: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.CheckPriorityIssueSizeUseCase = void 0; -const logging_ports_1 = __nccwpck_require__(6152); -const task_emoji_1 = __nccwpck_require__(46103); -const priority_size_check_use_case_1 = __nccwpck_require__(98060); +const logging_ports_1 = __nccwpck_require__(73001); +const task_emoji_1 = __nccwpck_require__(83142); +const priority_size_check_use_case_1 = __nccwpck_require__(81753); class CheckPriorityIssueSizeUseCase { constructor(projectBoardPriorityPort) { this.projectBoardPriorityPort = projectBoardPriorityPort; @@ -63046,18 +63046,18 @@ exports.CheckPriorityIssueSizeUseCase = CheckPriorityIssueSizeUseCase; /***/ }), -/***/ 46753: +/***/ 88705: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.CloseIssueAfterMergingUseCase = void 0; -const result_1 = __nccwpck_require__(73817); -const logging_ports_1 = __nccwpck_require__(6152); -const task_emoji_1 = __nccwpck_require__(46103); -const application_error_1 = __nccwpck_require__(75999); -const positive_integer_policy_1 = __nccwpck_require__(19879); +const result_1 = __nccwpck_require__(61444); +const logging_ports_1 = __nccwpck_require__(73001); +const task_emoji_1 = __nccwpck_require__(83142); +const application_error_1 = __nccwpck_require__(2965); +const positive_integer_policy_1 = __nccwpck_require__(45613); class CloseIssueAfterMergingUseCase { constructor(issueRepository) { this.issueRepository = issueRepository; @@ -63119,17 +63119,17 @@ exports.CloseIssueAfterMergingUseCase = CloseIssueAfterMergingUseCase; /***/ }), -/***/ 86675: +/***/ 71594: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.CloseNotAllowedIssueUseCase = void 0; -const result_1 = __nccwpck_require__(73817); -const logging_ports_1 = __nccwpck_require__(6152); -const task_emoji_1 = __nccwpck_require__(46103); -const application_error_1 = __nccwpck_require__(75999); +const result_1 = __nccwpck_require__(61444); +const logging_ports_1 = __nccwpck_require__(73001); +const task_emoji_1 = __nccwpck_require__(83142); +const application_error_1 = __nccwpck_require__(2965); class CloseNotAllowedIssueUseCase { constructor(issueRepository) { this.issueRepository = issueRepository; @@ -63180,18 +63180,18 @@ exports.CloseNotAllowedIssueUseCase = CloseNotAllowedIssueUseCase; /***/ }), -/***/ 33445: +/***/ 2608: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runDeployAddedWorkflow = runDeployAddedWorkflow; -const result_1 = __nccwpck_require__(73817); -const content_utils_1 = __nccwpck_require__(92816); -const logging_ports_1 = __nccwpck_require__(6152); -const deploy_workflow_policy_1 = __nccwpck_require__(8428); -const application_error_1 = __nccwpck_require__(75999); +const result_1 = __nccwpck_require__(61444); +const content_utils_1 = __nccwpck_require__(61146); +const logging_ports_1 = __nccwpck_require__(73001); +const deploy_workflow_policy_1 = __nccwpck_require__(61524); +const application_error_1 = __nccwpck_require__(2965); async function runDeployAddedWorkflow(param, taskId, branchWorkflowPort, moveIssueToInProgressUseCase) { const plan = (0, deploy_workflow_policy_1.resolveDeployWorkflowPlan)(param); if (!plan) @@ -63234,16 +63234,16 @@ async function runDeployAddedWorkflow(param, taskId, branchWorkflowPort, moveIss /***/ }), -/***/ 27708: +/***/ 56723: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.DeployAddedUseCase = void 0; -const logging_ports_1 = __nccwpck_require__(6152); -const task_emoji_1 = __nccwpck_require__(46103); -const deploy_added_workflow_1 = __nccwpck_require__(33445); +const logging_ports_1 = __nccwpck_require__(73001); +const task_emoji_1 = __nccwpck_require__(83142); +const deploy_added_workflow_1 = __nccwpck_require__(2608); class DeployAddedUseCase { constructor(branchWorkflowPort, moveIssueToInProgressUseCase) { this.branchWorkflowPort = branchWorkflowPort; @@ -63260,14 +63260,14 @@ exports.DeployAddedUseCase = DeployAddedUseCase; /***/ }), -/***/ 34100: +/***/ 34835: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.LinkIssueProjectUseCase = void 0; -const project_content_link_workflow_1 = __nccwpck_require__(89064); +const project_content_link_workflow_1 = __nccwpck_require__(72383); /** Application boundary for linking issues to configured ProjectV2 boards. */ class LinkIssueProjectUseCase { constructor(projectContentPort) { @@ -63283,17 +63283,17 @@ exports.LinkIssueProjectUseCase = LinkIssueProjectUseCase; /***/ }), -/***/ 52309: +/***/ 79453: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.MoveIssueToInProgressUseCase = void 0; -const result_1 = __nccwpck_require__(73817); -const logging_ports_1 = __nccwpck_require__(6152); -const task_emoji_1 = __nccwpck_require__(46103); -const application_error_1 = __nccwpck_require__(75999); +const result_1 = __nccwpck_require__(61444); +const logging_ports_1 = __nccwpck_require__(73001); +const task_emoji_1 = __nccwpck_require__(83142); +const application_error_1 = __nccwpck_require__(2965); class MoveIssueToInProgressUseCase { constructor(projectRepository) { this.projectRepository = projectRepository; @@ -63339,22 +63339,22 @@ exports.MoveIssueToInProgressUseCase = MoveIssueToInProgressUseCase; /***/ }), -/***/ 67546: +/***/ 35833: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.PrepareBranchesUseCase = void 0; -const result_1 = __nccwpck_require__(73817); -const logging_ports_1 = __nccwpck_require__(6152); -const task_emoji_1 = __nccwpck_require__(46103); -const branch_preparation_strategy_1 = __nccwpck_require__(29988); -const prepare_managed_branch_1 = __nccwpck_require__(29928); -const prepare_hotfix_branch_1 = __nccwpck_require__(96318); -const prepare_release_branch_1 = __nccwpck_require__(83059); -const application_error_1 = __nccwpck_require__(75999); -const issue_workflow_context_1 = __nccwpck_require__(98005); +const result_1 = __nccwpck_require__(61444); +const logging_ports_1 = __nccwpck_require__(73001); +const task_emoji_1 = __nccwpck_require__(83142); +const branch_preparation_strategy_1 = __nccwpck_require__(50133); +const prepare_managed_branch_1 = __nccwpck_require__(28983); +const prepare_hotfix_branch_1 = __nccwpck_require__(88395); +const prepare_release_branch_1 = __nccwpck_require__(16712); +const application_error_1 = __nccwpck_require__(2965); +const issue_workflow_context_1 = __nccwpck_require__(13765); class PrepareBranchesUseCase { constructor(branchListQueryPort, branchNamePort, remoteBranchSyncPort, commitTagQueryPort, linkedBranchCommandPort, branchPropagationDelayPort, moveIssueToInProgressUseCase) { this.branchListQueryPort = branchListQueryPort; @@ -63432,16 +63432,16 @@ exports.PrepareBranchesUseCase = PrepareBranchesUseCase; /***/ }), -/***/ 96318: +/***/ 88395: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.prepareHotfixBranch = prepareHotfixBranch; -const result_1 = __nccwpck_require__(73817); -const logging_ports_1 = __nccwpck_require__(6152); -const issue_workflow_context_1 = __nccwpck_require__(98005); +const result_1 = __nccwpck_require__(61444); +const logging_ports_1 = __nccwpck_require__(73001); +const issue_workflow_context_1 = __nccwpck_require__(13765); async function prepareHotfixBranch(param, commitTagQuery, linkedBranchCommand, branches, taskId) { const { hotfix } = param; if (hotfix.baseVersion === undefined || @@ -63501,20 +63501,20 @@ async function prepareHotfixBranch(param, commitTagQuery, linkedBranchCommand, b /***/ }), -/***/ 29928: +/***/ 28983: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.prepareManagedBranch = prepareManagedBranch; -const result_1 = __nccwpck_require__(73817); -const branch_preparation_policy_1 = __nccwpck_require__(97307); -const managed_branch_result_policy_1 = __nccwpck_require__(55078); -const logging_ports_1 = __nccwpck_require__(6152); -const application_error_1 = __nccwpck_require__(75999); -const execute_script_use_case_1 = __nccwpck_require__(65440); -const issue_workflow_context_1 = __nccwpck_require__(98005); +const result_1 = __nccwpck_require__(61444); +const branch_preparation_policy_1 = __nccwpck_require__(59489); +const managed_branch_result_policy_1 = __nccwpck_require__(32742); +const logging_ports_1 = __nccwpck_require__(73001); +const application_error_1 = __nccwpck_require__(2965); +const execute_script_use_case_1 = __nccwpck_require__(54191); +const issue_workflow_context_1 = __nccwpck_require__(13765); async function prepareManagedBranch(param, issueTitle, branches, taskId, dependencies) { (0, logging_ports_1.logDebugInfo)(`Branch type: ${param.managementBranch}`); const decision = (0, branch_preparation_policy_1.decideManagedBranchPreparation)({ @@ -63598,17 +63598,17 @@ async function buildConfiguredCommitPrefix(param, branchName) { /***/ }), -/***/ 83059: +/***/ 16712: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.prepareReleaseBranch = prepareReleaseBranch; -const result_1 = __nccwpck_require__(73817); -const execute_script_use_case_1 = __nccwpck_require__(65440); -const logging_ports_1 = __nccwpck_require__(6152); -const issue_workflow_context_1 = __nccwpck_require__(98005); +const result_1 = __nccwpck_require__(61444); +const execute_script_use_case_1 = __nccwpck_require__(54191); +const logging_ports_1 = __nccwpck_require__(73001); +const issue_workflow_context_1 = __nccwpck_require__(13765); async function prepareReleaseBranch(param, linkedBranchCommand, branches, taskId) { const { release } = param; if (release.version === undefined || release.branch === undefined) { @@ -63698,7 +63698,7 @@ function buildReleaseReminder(param, releaseUrl, developmentUrl, mainUrl) { /***/ }), -/***/ 16530: +/***/ 46093: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -63717,17 +63717,17 @@ function resolveGithubPriorityLabel(priority, labels) { /***/ }), -/***/ 98060: +/***/ 81753: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runPrioritySizeCheck = runPrioritySizeCheck; -const result_1 = __nccwpck_require__(73817); -const logging_ports_1 = __nccwpck_require__(6152); -const priority_label_policy_1 = __nccwpck_require__(16530); -const application_error_1 = __nccwpck_require__(75999); +const result_1 = __nccwpck_require__(61444); +const logging_ports_1 = __nccwpck_require__(73001); +const priority_label_policy_1 = __nccwpck_require__(46093); +const application_error_1 = __nccwpck_require__(2965); async function runPrioritySizeCheck(param, taskId, projectRepository) { try { return await applyPriorityToProjects(param, taskId, projectRepository); @@ -63773,16 +63773,16 @@ async function applyPriorityToProjects(param, taskId, projectRepository) { /***/ }), -/***/ 71836: +/***/ 10027: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.ReconcileBranchReadinessUseCase = void 0; -const issue_start_policy_1 = __nccwpck_require__(90332); -const result_1 = __nccwpck_require__(73817); -const application_error_1 = __nccwpck_require__(75999); +const issue_start_policy_1 = __nccwpck_require__(20953); +const result_1 = __nccwpck_require__(61444); +const application_error_1 = __nccwpck_require__(2965); /** Projects verified remote facts into the managed `branched` output label. */ class ReconcileBranchReadinessUseCase { constructor(linkedBranch, labels) { @@ -63856,7 +63856,7 @@ exports.ReconcileBranchReadinessUseCase = ReconcileBranchReadinessUseCase; /***/ }), -/***/ 57836: +/***/ 11944: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -63874,18 +63874,18 @@ function selectIssueBranchesToRemove(branches, issueNumber, branchTypes) { /***/ }), -/***/ 15608: +/***/ 92405: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.RemoveIssueBranchesUseCase = void 0; -const result_1 = __nccwpck_require__(73817); -const logging_ports_1 = __nccwpck_require__(6152); -const task_emoji_1 = __nccwpck_require__(46103); -const remove_issue_branches_policy_1 = __nccwpck_require__(57836); -const application_error_1 = __nccwpck_require__(75999); +const result_1 = __nccwpck_require__(61444); +const logging_ports_1 = __nccwpck_require__(73001); +const task_emoji_1 = __nccwpck_require__(83142); +const remove_issue_branches_policy_1 = __nccwpck_require__(11944); +const application_error_1 = __nccwpck_require__(2965); /** * Remove any branch created for this issue */ @@ -63949,17 +63949,17 @@ async function removeIssueBranch(param, taskId, branchName, branchLifecyclePort) /***/ }), -/***/ 67129: +/***/ 26142: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.RemoveNotNeededBranchesUseCase = void 0; -const result_1 = __nccwpck_require__(73817); -const logging_ports_1 = __nccwpck_require__(6152); -const task_emoji_1 = __nccwpck_require__(46103); -const application_error_1 = __nccwpck_require__(75999); +const result_1 = __nccwpck_require__(61444); +const logging_ports_1 = __nccwpck_require__(73001); +const task_emoji_1 = __nccwpck_require__(83142); +const application_error_1 = __nccwpck_require__(2965); class RemoveNotNeededBranchesUseCase { constructor(branchLifecyclePort, branchNamePort) { this.branchLifecyclePort = branchLifecyclePort; @@ -64043,17 +64043,17 @@ exports.RemoveNotNeededBranchesUseCase = RemoveNotNeededBranchesUseCase; /***/ }), -/***/ 38222: +/***/ 25514: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.UpdateIssueTypeUseCase = void 0; -const result_1 = __nccwpck_require__(73817); -const logging_ports_1 = __nccwpck_require__(6152); -const task_emoji_1 = __nccwpck_require__(46103); -const application_error_1 = __nccwpck_require__(75999); +const result_1 = __nccwpck_require__(61444); +const logging_ports_1 = __nccwpck_require__(73001); +const task_emoji_1 = __nccwpck_require__(83142); +const application_error_1 = __nccwpck_require__(2965); class UpdateIssueTypeUseCase { constructor(issueRepository) { this.issueRepository = issueRepository; @@ -64086,7 +64086,7 @@ exports.UpdateIssueTypeUseCase = UpdateIssueTypeUseCase; /***/ }), -/***/ 93152: +/***/ 34670: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -64094,7 +64094,7 @@ exports.UpdateIssueTypeUseCase = UpdateIssueTypeUseCase; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.CheckIssueCommentLanguageUseCase = void 0; exports.projectIssueCommentLanguageRequest = projectIssueCommentLanguageRequest; -const comment_language_translation_workflow_1 = __nccwpck_require__(72770); +const comment_language_translation_workflow_1 = __nccwpck_require__(78212); function projectIssueCommentLanguageRequest(source) { return (0, comment_language_translation_workflow_1.projectCommentLanguageRequest)({ commentBody: source.issue.commentBody, @@ -64122,16 +64122,16 @@ exports.CheckIssueCommentLanguageUseCase = CheckIssueCommentLanguageUseCase; /***/ }), -/***/ 12738: +/***/ 61696: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.CheckPriorityPullRequestSizeUseCase = void 0; -const logging_ports_1 = __nccwpck_require__(6152); -const task_emoji_1 = __nccwpck_require__(46103); -const priority_size_check_use_case_1 = __nccwpck_require__(98060); +const logging_ports_1 = __nccwpck_require__(73001); +const task_emoji_1 = __nccwpck_require__(83142); +const priority_size_check_use_case_1 = __nccwpck_require__(81753); class CheckPriorityPullRequestSizeUseCase { constructor(projectBoardPriorityPort) { this.projectBoardPriorityPort = projectBoardPriorityPort; @@ -64147,18 +64147,18 @@ exports.CheckPriorityPullRequestSizeUseCase = CheckPriorityPullRequestSizeUseCas /***/ }), -/***/ 38259: +/***/ 64280: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.LinkPullRequestIssueUseCase = void 0; -const result_1 = __nccwpck_require__(73817); -const logging_ports_1 = __nccwpck_require__(6152); -const task_emoji_1 = __nccwpck_require__(46103); -const link_pull_request_issue_workflow_1 = __nccwpck_require__(19033); -const application_error_1 = __nccwpck_require__(75999); +const result_1 = __nccwpck_require__(61444); +const logging_ports_1 = __nccwpck_require__(73001); +const task_emoji_1 = __nccwpck_require__(83142); +const link_pull_request_issue_workflow_1 = __nccwpck_require__(90858); +const application_error_1 = __nccwpck_require__(2965); class LinkPullRequestIssueUseCase { constructor(pullRequestIssueLinkPort, eventualConsistencyDelayPort) { this.pullRequestIssueLinkPort = pullRequestIssueLinkPort; @@ -64215,7 +64215,7 @@ function describeRecovery(error) { /***/ }), -/***/ 19033: +/***/ 90858: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -64223,9 +64223,9 @@ function describeRecovery(error) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.PullRequestIssueLinkOperationError = void 0; exports.runLinkPullRequestIssue = runLinkPullRequestIssue; -const result_1 = __nccwpck_require__(73817); -const positive_integer_policy_1 = __nccwpck_require__(19879); -const deployment_configuration_1 = __nccwpck_require__(22495); +const result_1 = __nccwpck_require__(61444); +const positive_integer_policy_1 = __nccwpck_require__(45613); +const deployment_configuration_1 = __nccwpck_require__(5664); const LINK_MARKER_PREFIX = '/iu; function resolveOpenBranchDependencies(issues, pullRequests) { const candidates = []; @@ -72414,14 +72408,14 @@ function uniqueValidDependencies(candidates) { /***/ }), -/***/ 9627: +/***/ 78769: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.BranchDependencyRepository = void 0; -const branch_dependency_policy_1 = __nccwpck_require__(54874); +const branch_dependency_policy_1 = __nccwpck_require__(64454); const OPEN_DEPENDENCIES_QUERY = ` query BranchSyncDependencies($owner: String!, $repo: String!, $issuesCursor: String, $pullsCursor: String) { repository(owner: $owner, name: $repo) { @@ -72540,7 +72534,7 @@ function withCause(message, cause) { /***/ }), -/***/ 77509: +/***/ 26781: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -72565,7 +72559,7 @@ exports.DeploymentContinuationRepository = DeploymentContinuationRepository; /***/ }), -/***/ 91985: +/***/ 86534: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -72602,17 +72596,17 @@ exports.DeploymentPresentationRepository = DeploymentPresentationRepository; /***/ }), -/***/ 3182: +/***/ 59536: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.DeploymentStateRepositoryFactory = void 0; -const deployment_state_fence_1 = __nccwpck_require__(72369); -const config_1 = __nccwpck_require__(90450); -const configuration_handler_1 = __nccwpck_require__(40188); -const configuration_payload_policy_1 = __nccwpck_require__(58043); +const deployment_state_fence_1 = __nccwpck_require__(32481); +const config_1 = __nccwpck_require__(98013); +const configuration_handler_1 = __nccwpck_require__(51068); +const configuration_payload_policy_1 = __nccwpck_require__(23509); class DeploymentStateRepositoryFactory { constructor(issues) { this.issues = issues; @@ -72681,14 +72675,14 @@ function isRecord(value) { /***/ }), -/***/ 85886: +/***/ 35315: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.GithubDeploymentGitRepository = void 0; -const application_error_1 = __nccwpck_require__(75999); +const application_error_1 = __nccwpck_require__(2965); class GithubDeploymentGitRepository { constructor(clientProvider) { this.clientProvider = clientProvider; @@ -72801,14 +72795,14 @@ function isNotFound(error) { /***/ }), -/***/ 96483: +/***/ 57536: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.GithubManagedPullRequestRepository = void 0; -const managed_pull_request_1 = __nccwpck_require__(95914); +const managed_pull_request_1 = __nccwpck_require__(7975); class GithubManagedPullRequestRepository { constructor(clientProvider) { this.clientProvider = clientProvider; @@ -72912,7 +72906,7 @@ function mapPullRequest(value, owner, repository) { /***/ }), -/***/ 55527: +/***/ 46950: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -72952,7 +72946,7 @@ var __importStar = (this && this.__importStar) || (function () { })(); Object.defineProperty(exports, "__esModule", ({ value: true })); exports.GithubTargetMergeCapabilitiesInspector = void 0; -const yaml = __importStar(__nccwpck_require__(783)); +const yaml = __importStar(__nccwpck_require__(87969)); class GithubTargetMergeCapabilitiesInspector { constructor(clientProvider) { this.clientProvider = clientProvider; @@ -73532,7 +73526,7 @@ function isNotFound(error) { /***/ }), -/***/ 26331: +/***/ 72119: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -73572,11 +73566,11 @@ var __importStar = (this && this.__importStar) || (function () { })(); Object.defineProperty(exports, "__esModule", ({ value: true })); exports.GitCliRepository = void 0; -const exec = __importStar(__nccwpck_require__(18538)); -const logger_1 = __nccwpck_require__(91151); -const version_policy_1 = __nccwpck_require__(8381); -const git_authentication_environment_1 = __nccwpck_require__(16535); -const application_error_1 = __nccwpck_require__(75999); +const exec = __importStar(__nccwpck_require__(36086)); +const logger_1 = __nccwpck_require__(50135); +const version_policy_1 = __nccwpck_require__(36707); +const git_authentication_environment_1 = __nccwpck_require__(1906); +const application_error_1 = __nccwpck_require__(2965); /** * Repository for Git operations executed via CLI (exec). * Isolated to allow unit tests with mocked @actions/exec. @@ -73675,7 +73669,7 @@ exports.GitCliRepository = GitCliRepository; /***/ }), -/***/ 57628: +/***/ 85071: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -73683,8 +73677,8 @@ exports.GitCliRepository = GitCliRepository; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.inspectMissingCredentialHealthWorkflow = inspectMissingCredentialHealthWorkflow; exports.inspectCredentialHealthWorkflowAtRef = inspectCredentialHealthWorkflowAtRef; -const setup_workflow_catalog_1 = __nccwpck_require__(24596); -const github_error_policy_1 = __nccwpck_require__(58791); +const setup_workflow_catalog_1 = __nccwpck_require__(37008); +const github_error_policy_1 = __nccwpck_require__(26189); /** A workflow API 404 is confirmed absence only after two independent Contents reads. */ async function inspectMissingCredentialHealthWorkflow(getContent, owner, repository, ref) { const state = await inspectCredentialHealthWorkflowAtRef(getContent, owner, repository, ref); @@ -73722,7 +73716,7 @@ async function inspectCredentialHealthWorkflowAtRef(getContent, owner, repositor /***/ }), -/***/ 58791: +/***/ 26189: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -73797,14 +73791,14 @@ function readHeader(headers, expected) { /***/ }), -/***/ 2761: +/***/ 64064: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.paginateCursor = paginateCursor; -const logger_1 = __nccwpck_require__(91151); +const logger_1 = __nccwpck_require__(50135); /** * Iterates cursor-based API pages while enforcing a finite boundary and a * valid cursor transition. Consumers can `break` early when they find the @@ -73835,7 +73829,7 @@ async function* paginateCursor(fetchPage, options = {}) { /***/ }), -/***/ 44812: +/***/ 24347: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -73865,15 +73859,15 @@ function requireObject(data, operation) { /***/ }), -/***/ 52644: +/***/ 97590: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.GithubPublicationSourceRepository = void 0; -const application_error_1 = __nccwpck_require__(75999); -const git_object_id_1 = __nccwpck_require__(88623); +const application_error_1 = __nccwpck_require__(2965); +const git_object_id_1 = __nccwpck_require__(36924); /** Reads the authoritative branch head without exposing Octokit to application code. */ class GithubPublicationSourceRepository { constructor(clientProvider) { @@ -73897,14 +73891,14 @@ exports.GithubPublicationSourceRepository = GithubPublicationSourceRepository; /***/ }), -/***/ 88593: +/***/ 85949: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.BugbotIssueCommentQueryRepository = void 0; -const application_error_1 = __nccwpck_require__(75999); +const application_error_1 = __nccwpck_require__(2965); /** Reads the newest Bugbot issue/PR conversation comments with a fixed two-page budget. */ class BugbotIssueCommentQueryRepository { constructor(githubClient) { @@ -73988,7 +73982,7 @@ exports.BugbotIssueCommentQueryRepository = BugbotIssueCommentQueryRepository; /***/ }), -/***/ 82726: +/***/ 15589: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -74008,7 +74002,7 @@ exports.BugbotIssueRepository = BugbotIssueRepository; /***/ }), -/***/ 91153: +/***/ 32004: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -74034,15 +74028,15 @@ exports.ExecutionIssueSetupRepository = ExecutionIssueSetupRepository; /***/ }), -/***/ 75023: +/***/ 7223: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.IssueAssignmentRepository = void 0; -const logger_1 = __nccwpck_require__(91151); -const application_error_1 = __nccwpck_require__(75999); +const logger_1 = __nccwpck_require__(50135); +const application_error_1 = __nccwpck_require__(2965); class IssueAssignmentRepository { constructor(githubClient) { this.githubClient = githubClient; @@ -74081,7 +74075,7 @@ exports.IssueAssignmentRepository = IssueAssignmentRepository; /***/ }), -/***/ 23231: +/***/ 80674: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -74101,18 +74095,18 @@ exports.IssueClosureRepository = IssueClosureRepository; /***/ }), -/***/ 2313: +/***/ 43338: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.IssueContentRepository = void 0; -const comment_content_policy_1 = __nccwpck_require__(77454); -const logger_1 = __nccwpck_require__(91151); -const github_pagination_policy_1 = __nccwpck_require__(44812); -const application_error_1 = __nccwpck_require__(75999); -const github_error_policy_1 = __nccwpck_require__(58791); +const comment_content_policy_1 = __nccwpck_require__(2324); +const logger_1 = __nccwpck_require__(50135); +const github_pagination_policy_1 = __nccwpck_require__(24347); +const application_error_1 = __nccwpck_require__(2965); +const github_error_policy_1 = __nccwpck_require__(26189); class IssueContentRepository { constructor(githubClient) { this.githubClient = githubClient; @@ -74240,14 +74234,14 @@ exports.IssueContentRepository = IssueContentRepository; /***/ }), -/***/ 28868: +/***/ 5533: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.IssueInactivityRepository = void 0; -const github_pagination_policy_1 = __nccwpck_require__(44812); +const github_pagination_policy_1 = __nccwpck_require__(24347); /** Reads the provider's issue activity timestamp and waiting-state labels. */ class IssueInactivityRepository { constructor(githubClient) { @@ -74301,18 +74295,18 @@ function toSnapshot(issue) { /***/ }), -/***/ 59699: +/***/ 4532: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.IssueLabelProvisioningRepository = void 0; -const initial_label_provisioning_policy_1 = __nccwpck_require__(73160); -const logger_1 = __nccwpck_require__(91151); -const github_error_policy_1 = __nccwpck_require__(58791); -const github_pagination_policy_1 = __nccwpck_require__(44812); -const application_error_1 = __nccwpck_require__(75999); +const initial_label_provisioning_policy_1 = __nccwpck_require__(36432); +const logger_1 = __nccwpck_require__(50135); +const github_error_policy_1 = __nccwpck_require__(26189); +const github_pagination_policy_1 = __nccwpck_require__(24347); +const application_error_1 = __nccwpck_require__(2965); class IssueLabelProvisioningRepository { constructor(githubClient) { this.githubClient = githubClient; @@ -74378,16 +74372,16 @@ function mapLabelMutationError(name, error) { /***/ }), -/***/ 45725: +/***/ 27825: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.IssueLabelRepository = void 0; -const logger_1 = __nccwpck_require__(91151); -const github_pagination_policy_1 = __nccwpck_require__(44812); -const application_error_1 = __nccwpck_require__(75999); +const logger_1 = __nccwpck_require__(50135); +const github_pagination_policy_1 = __nccwpck_require__(24347); +const application_error_1 = __nccwpck_require__(2965); class IssueLabelRepository { constructor(githubClient) { this.githubClient = githubClient; @@ -74429,14 +74423,14 @@ exports.IssueLabelRepository = IssueLabelRepository; /***/ }), -/***/ 8346: +/***/ 44023: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.IssueLifecycleRepository = void 0; -const logger_1 = __nccwpck_require__(91151); +const logger_1 = __nccwpck_require__(50135); class IssueLifecycleRepository { constructor(githubClient) { this.githubClient = githubClient; @@ -74461,16 +74455,16 @@ exports.IssueLifecycleRepository = IssueLifecycleRepository; /***/ }), -/***/ 11333: +/***/ 37674: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.IssueMetadataRepository = void 0; -const logger_1 = __nccwpck_require__(91151); -const milestone_1 = __nccwpck_require__(2016); -const application_error_1 = __nccwpck_require__(75999); +const logger_1 = __nccwpck_require__(50135); +const milestone_1 = __nccwpck_require__(22410); +const application_error_1 = __nccwpck_require__(2965); class IssueMetadataRepository { constructor(metadataClient, graphqlClient) { this.metadataClient = metadataClient; @@ -74548,7 +74542,7 @@ exports.IssueMetadataRepository = IssueMetadataRepository; /***/ }), -/***/ 907: +/***/ 24682: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -74568,15 +74562,15 @@ exports.IssueNotificationRepository = IssueNotificationRepository; /***/ }), -/***/ 66610: +/***/ 58763: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.IssueProgressLabelRepository = void 0; -const logger_1 = __nccwpck_require__(91151); -const progress_labels_1 = __nccwpck_require__(97890); +const logger_1 = __nccwpck_require__(50135); +const progress_labels_1 = __nccwpck_require__(71285); class IssueProgressLabelRepository { constructor(issueLabelRepository) { this.issueLabelRepository = issueLabelRepository; @@ -74598,7 +74592,7 @@ exports.IssueProgressLabelRepository = IssueProgressLabelRepository; /***/ }), -/***/ 26674: +/***/ 64844: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -74621,16 +74615,16 @@ exports.IssueProgressTrackingRepository = IssueProgressTrackingRepository; /***/ }), -/***/ 10121: +/***/ 89116: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.IssueTitleRepository = void 0; -const issue_emoji_policy_1 = __nccwpck_require__(81201); -const issue_title_policy_1 = __nccwpck_require__(83179); -const issue_title_update_1 = __nccwpck_require__(9229); +const issue_emoji_policy_1 = __nccwpck_require__(70469); +const issue_title_policy_1 = __nccwpck_require__(16208); +const issue_title_update_1 = __nccwpck_require__(60886); class IssueTitleRepository { constructor(issueTitleClient, issueMetadataRepository) { this.issueTitleClient = issueTitleClient; @@ -74666,7 +74660,7 @@ exports.IssueTitleRepository = IssueTitleRepository; /***/ }), -/***/ 9229: +/***/ 60886: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -74674,8 +74668,8 @@ exports.IssueTitleRepository = IssueTitleRepository; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.updateIssueTitle = updateIssueTitle; exports.withTitleUpdateLogging = withTitleUpdateLogging; -const logger_1 = __nccwpck_require__(91151); -const application_error_1 = __nccwpck_require__(75999); +const logger_1 = __nccwpck_require__(50135); +const application_error_1 = __nccwpck_require__(2965); async function updateIssueTitle(client, owner, repository, currentTitle, nextTitle, issueNumber, token) { if (nextTitle === currentTitle) return undefined; @@ -74696,16 +74690,16 @@ async function withTitleUpdateLogging(update) { /***/ }), -/***/ 19118: +/***/ 55913: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.IssueTypeAssignmentRepository = void 0; -const logger_1 = __nccwpck_require__(91151); -const issue_type_assignment_workflow_1 = __nccwpck_require__(40102); -const application_error_1 = __nccwpck_require__(75999); +const logger_1 = __nccwpck_require__(50135); +const issue_type_assignment_workflow_1 = __nccwpck_require__(82994); +const application_error_1 = __nccwpck_require__(2965); class IssueTypeAssignmentRepository { constructor(getIssueId, graphqlClient) { this.getIssueId = getIssueId; @@ -74727,7 +74721,7 @@ exports.IssueTypeAssignmentRepository = IssueTypeAssignmentRepository; /***/ }), -/***/ 40102: +/***/ 82994: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -74735,8 +74729,8 @@ exports.IssueTypeAssignmentRepository = IssueTypeAssignmentRepository; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.IssueTypeCreationSkippedError = void 0; exports.assignIssueType = assignIssueType; -const logger_1 = __nccwpck_require__(91151); -const application_error_1 = __nccwpck_require__(75999); +const logger_1 = __nccwpck_require__(50135); +const application_error_1 = __nccwpck_require__(2965); async function assignIssueType(getIssueId, client, owner, repository, issueNumber, selected, token) { (0, logger_1.logDebugInfo)(`Setting issue type for issue ${issueNumber} to ${selected.name}`); const issueId = await getIssueId(owner, repository, issueNumber, token); @@ -74807,7 +74801,7 @@ exports.IssueTypeCreationSkippedError = IssueTypeCreationSkippedError; /***/ }), -/***/ 62726: +/***/ 39335: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -74832,7 +74826,7 @@ function configuredIssueTypes(issueTypes) { /***/ }), -/***/ 89634: +/***/ 98235: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -74840,10 +74834,10 @@ function configuredIssueTypes(issueTypes) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.ensureIssueType = ensureIssueType; exports.ensureIssueTypes = ensureIssueTypes; -const logger_1 = __nccwpck_require__(91151); -const issue_type_configuration_1 = __nccwpck_require__(62726); -const issue_type_queries_1 = __nccwpck_require__(73192); -const application_error_1 = __nccwpck_require__(75999); +const logger_1 = __nccwpck_require__(50135); +const issue_type_configuration_1 = __nccwpck_require__(39335); +const issue_type_queries_1 = __nccwpck_require__(49841); +const application_error_1 = __nccwpck_require__(2965); async function ensureIssueType(client, owner, name, description, color) { try { const existingTypes = await (0, issue_type_queries_1.listIssueTypes)(client, owner); @@ -74891,7 +74885,7 @@ function ensureConfiguredIssueType(client, owner, configured) { /***/ }), -/***/ 73192: +/***/ 49841: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -74955,15 +74949,15 @@ async function createIssueType(client, owner, name, description, color) { /***/ }), -/***/ 4858: +/***/ 87600: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.IssueTypeRepository = void 0; -const issue_type_queries_1 = __nccwpck_require__(73192); -const issue_type_ensure_workflow_1 = __nccwpck_require__(89634); +const issue_type_queries_1 = __nccwpck_require__(49841); +const issue_type_ensure_workflow_1 = __nccwpck_require__(98235); class IssueTypeRepository { constructor(graphqlClient) { this.graphqlClient = graphqlClient; @@ -74978,7 +74972,7 @@ exports.IssueTypeRepository = IssueTypeRepository; /***/ }), -/***/ 81201: +/***/ 70469: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -75018,7 +75012,7 @@ function firstMatchingEmoji(rules, labels) { /***/ }), -/***/ 83179: +/***/ 16208: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -75069,16 +75063,16 @@ function normalizePullRequestSourceTitle(title, issueNumber) { /***/ }), -/***/ 96711: +/***/ 54771: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.ActorAuthorizationRepository = void 0; -const logger_1 = __nccwpck_require__(91151); -const actor_modification_policy_1 = __nccwpck_require__(34737); -const application_error_1 = __nccwpck_require__(75999); +const logger_1 = __nccwpck_require__(50135); +const actor_modification_policy_1 = __nccwpck_require__(74888); +const application_error_1 = __nccwpck_require__(2965); class ActorAuthorizationRepository { constructor(githubClient) { this.githubClient = githubClient; @@ -75149,7 +75143,7 @@ function logUnlessNotFound(error, operation) { /***/ }), -/***/ 11454: +/***/ 85397: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -75180,7 +75174,7 @@ exports.AuthenticatedUserRepository = AuthenticatedUserRepository; /***/ }), -/***/ 84916: +/***/ 59939: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -75188,7 +75182,7 @@ exports.AuthenticatedUserRepository = AuthenticatedUserRepository; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.listOrganizationTeams = listOrganizationTeams; exports.listOrganizationTeamMembers = listOrganizationTeamMembers; -const github_pagination_policy_1 = __nccwpck_require__(44812); +const github_pagination_policy_1 = __nccwpck_require__(24347); async function listOrganizationTeams(client, organization) { const teams = []; for await (const response of client.paginate.iterator(client.rest.teams.list, { @@ -75225,17 +75219,17 @@ function isRecord(value) { /***/ }), -/***/ 845: +/***/ 14553: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.OrganizationMembersRepository = void 0; -const logger_1 = __nccwpck_require__(91151); -const project_members_policy_1 = __nccwpck_require__(41370); -const organization_members_query_1 = __nccwpck_require__(84916); -const application_error_1 = __nccwpck_require__(75999); +const logger_1 = __nccwpck_require__(50135); +const project_members_policy_1 = __nccwpck_require__(52983); +const organization_members_query_1 = __nccwpck_require__(59939); +const application_error_1 = __nccwpck_require__(2965); class OrganizationMembersRepository { constructor(githubClient) { this.githubClient = githubClient; @@ -75283,14 +75277,14 @@ exports.OrganizationMembersRepository = OrganizationMembersRepository; /***/ }), -/***/ 98952: +/***/ 76220: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.ProjectBoardCommandRepository = void 0; -const project_board_field_update_1 = __nccwpck_require__(31603); +const project_board_field_update_1 = __nccwpck_require__(40805); /** GitHub GraphQL adapter for ProjectV2 field mutations. */ class ProjectBoardCommandRepository { constructor(projectBoardContentQueryPort, graphqlClient) { @@ -75313,16 +75307,16 @@ exports.ProjectBoardCommandRepository = ProjectBoardCommandRepository; /***/ }), -/***/ 73579: +/***/ 10442: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.getProjectBoardDetail = getProjectBoardDetail; -const logger_1 = __nccwpck_require__(91151); -const project_detail_1 = __nccwpck_require__(33428); -const application_error_1 = __nccwpck_require__(75999); +const logger_1 = __nccwpck_require__(50135); +const project_detail_1 = __nccwpck_require__(74263); +const application_error_1 = __nccwpck_require__(2965); /** Reads a ProjectV2 without leaking GitHub's owner-specific GraphQL shape. */ async function getProjectBoardDetail(ownerTypeClient, graphqlClient, projectId, owner, token) { try { @@ -75386,7 +75380,7 @@ function validateProjectId(projectId) { /***/ }), -/***/ 31603: +/***/ 40805: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -75394,9 +75388,9 @@ function validateProjectId(projectId) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.setProjectBoardSingleSelectField = setProjectBoardSingleSelectField; exports.setProjectBoardSingleSelectFieldByItemId = setProjectBoardSingleSelectFieldByItemId; -const project_board_provider_limits_1 = __nccwpck_require__(96997); -const logger_1 = __nccwpck_require__(91151); -const github_pagination_adapter_1 = __nccwpck_require__(2761); +const project_board_provider_limits_1 = __nccwpck_require__(79506); +const logger_1 = __nccwpck_require__(50135); +const github_pagination_adapter_1 = __nccwpck_require__(64064); const FIELD_QUERY = ` query($projectId: ID!, $after: String) { node(id: $projectId) { @@ -75536,7 +75530,7 @@ async function findProjectItem(client, project, itemId, fieldName) { /***/ }), -/***/ 63552: +/***/ 59112: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -75544,9 +75538,9 @@ async function findProjectItem(client, project, itemId, fieldName) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.getProjectItemId = getProjectItemId; exports.getProjectItemIdByContentId = getProjectItemIdByContentId; -const project_board_provider_limits_1 = __nccwpck_require__(96997); -const logger_1 = __nccwpck_require__(91151); -const github_pagination_adapter_1 = __nccwpck_require__(2761); +const project_board_provider_limits_1 = __nccwpck_require__(79506); +const logger_1 = __nccwpck_require__(50135); +const github_pagination_adapter_1 = __nccwpck_require__(64064); const CONTENT_QUERY = ` query($owner: String!, $repo: String!, $number: Int!) { repository(owner: $owner, name: $repo) { @@ -75616,14 +75610,14 @@ async function findProjectItemId(client, project, contentId) { /***/ }), -/***/ 79285: +/***/ 5367: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.ProjectBoardLinkRepository = void 0; -const logger_1 = __nccwpck_require__(91151); +const logger_1 = __nccwpck_require__(50135); class ProjectBoardLinkRepository { constructor(projectBoardQueryPort, graphqlClient) { this.projectBoardQueryPort = projectBoardQueryPort; @@ -75650,15 +75644,15 @@ exports.ProjectBoardLinkRepository = ProjectBoardLinkRepository; /***/ }), -/***/ 97301: +/***/ 17726: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.ProjectBoardQueryRepository = void 0; -const project_board_detail_query_1 = __nccwpck_require__(73579); -const project_board_item_query_1 = __nccwpck_require__(63552); +const project_board_detail_query_1 = __nccwpck_require__(10442); +const project_board_item_query_1 = __nccwpck_require__(59112); class ProjectBoardQueryRepository { constructor(ownerTypeClient, graphqlClient) { this.ownerTypeClient = ownerTypeClient; @@ -75673,7 +75667,7 @@ exports.ProjectBoardQueryRepository = ProjectBoardQueryRepository; /***/ }), -/***/ 41370: +/***/ 52983: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -75712,7 +75706,7 @@ function selectAvailableMembers(members, currentMembers, requested) { /***/ }), -/***/ 55165: +/***/ 66202: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -75743,17 +75737,17 @@ exports.BugbotPullRequestRepository = BugbotPullRequestRepository; /***/ }), -/***/ 71564: +/***/ 65797: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.PullRequestChangesRepository = void 0; -const logger_1 = __nccwpck_require__(91151); -const pull_request_review_errors_1 = __nccwpck_require__(46445); -const github_pagination_policy_1 = __nccwpck_require__(44812); -const application_error_1 = __nccwpck_require__(75999); +const logger_1 = __nccwpck_require__(50135); +const pull_request_review_errors_1 = __nccwpck_require__(81504); +const github_pagination_policy_1 = __nccwpck_require__(24347); +const application_error_1 = __nccwpck_require__(2965); class PullRequestChangesRepository { constructor(githubClient) { this.githubClient = githubClient; @@ -75923,15 +75917,15 @@ exports.PullRequestChangesRepository = PullRequestChangesRepository; /***/ }), -/***/ 24189: +/***/ 57177: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.PullRequestLifecycleRepository = void 0; -const logger_1 = __nccwpck_require__(91151); -const application_error_1 = __nccwpck_require__(75999); +const logger_1 = __nccwpck_require__(50135); +const application_error_1 = __nccwpck_require__(2965); class PullRequestLifecycleRepository { constructor(githubClient) { this.githubClient = githubClient; @@ -76115,15 +76109,15 @@ function toBugbotPullRequestIdentity(value) { /***/ }), -/***/ 17120: +/***/ 48987: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.PullRequestReviewCommentCommandRepository = void 0; -const pull_request_review_errors_1 = __nccwpck_require__(46445); -const github_pagination_policy_1 = __nccwpck_require__(44812); +const pull_request_review_errors_1 = __nccwpck_require__(81504); +const github_pagination_policy_1 = __nccwpck_require__(24347); class PullRequestReviewCommentCommandRepository { constructor(createClient, graphqlClient, queryClient) { this.createClient = createClient; @@ -76239,15 +76233,15 @@ exports.PullRequestReviewCommentCommandRepository = PullRequestReviewCommentComm /***/ }), -/***/ 44085: +/***/ 31371: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.PullRequestReviewCommentQueryRepository = void 0; -const pull_request_review_errors_1 = __nccwpck_require__(46445); -const github_pagination_policy_1 = __nccwpck_require__(44812); +const pull_request_review_errors_1 = __nccwpck_require__(81504); +const github_pagination_policy_1 = __nccwpck_require__(24347); function toReviewComment(comment) { if (typeof comment.node_id !== "string" || comment.node_id.length === 0) { throw new Error("Review comment identity is unavailable."); @@ -76382,14 +76376,14 @@ exports.PullRequestReviewCommentQueryRepository = PullRequestReviewCommentQueryR /***/ }), -/***/ 2307: +/***/ 20491: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.findPullRequestReviewThread = findPullRequestReviewThread; -const pull_request_review_errors_1 = __nccwpck_require__(46445); +const pull_request_review_errors_1 = __nccwpck_require__(81504); const THREADS_QUERY = ` query ($owner: String!, $repo: String!, $prNumber: Int!, $threadsAfter: String) { repository(owner: $owner, name: $repo) { @@ -76483,16 +76477,16 @@ function nextConnectionCursor(pageInfo, seenCursors) { /***/ }), -/***/ 23314: +/***/ 64125: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.PullRequestReviewThreadRepository = void 0; -const logger_1 = __nccwpck_require__(91151); -const pull_request_review_errors_1 = __nccwpck_require__(46445); -const pull_request_review_thread_locator_1 = __nccwpck_require__(2307); +const logger_1 = __nccwpck_require__(50135); +const pull_request_review_errors_1 = __nccwpck_require__(81504); +const pull_request_review_thread_locator_1 = __nccwpck_require__(20491); /** GitHub GraphQL adapter for locating and resolving a pull-request review thread. */ class PullRequestReviewThreadRepository { constructor(githubClient) { @@ -76664,15 +76658,15 @@ exports.PullRequestReviewThreadRepository = PullRequestReviewThreadRepository; /***/ }), -/***/ 13779: +/***/ 45908: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.PullRequestReviewerRepository = void 0; -const pull_request_review_errors_1 = __nccwpck_require__(46445); -const github_pagination_policy_1 = __nccwpck_require__(44812); +const pull_request_review_errors_1 = __nccwpck_require__(81504); +const github_pagination_policy_1 = __nccwpck_require__(24347); const COMPLETED_REVIEW_STATES = new Set([ "APPROVED", "CHANGES_REQUESTED", @@ -76758,15 +76752,15 @@ exports.PullRequestReviewerRepository = PullRequestReviewerRepository; /***/ }), -/***/ 96578: +/***/ 38818: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.RepositoryDefaultBranchRepository = void 0; -const logger_1 = __nccwpck_require__(91151); -const application_error_1 = __nccwpck_require__(75999); +const logger_1 = __nccwpck_require__(50135); +const application_error_1 = __nccwpck_require__(2965); class RepositoryDefaultBranchRepository { constructor(githubClient) { this.githubClient = githubClient; @@ -76789,22 +76783,22 @@ exports.RepositoryDefaultBranchRepository = RepositoryDefaultBranchRepository; /***/ }), -/***/ 42075: +/***/ 9204: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.RepositoryReleasePublicationRepository = void 0; -const logger_1 = __nccwpck_require__(91151); -const release_content_policy_1 = __nccwpck_require__(56818); -const release_transition_policy_1 = __nccwpck_require__(27673); -const release_tag_policy_1 = __nccwpck_require__(62748); -const repository_release_query_1 = __nccwpck_require__(10766); -const application_error_1 = __nccwpck_require__(75999); -const github_error_policy_1 = __nccwpck_require__(58791); -const repository_tag_query_1 = __nccwpck_require__(46772); -const deployment_publication_1 = __nccwpck_require__(6912); +const logger_1 = __nccwpck_require__(50135); +const release_content_policy_1 = __nccwpck_require__(87013); +const release_transition_policy_1 = __nccwpck_require__(85551); +const release_tag_policy_1 = __nccwpck_require__(61708); +const repository_release_query_1 = __nccwpck_require__(37052); +const application_error_1 = __nccwpck_require__(2965); +const github_error_policy_1 = __nccwpck_require__(26189); +const repository_tag_query_1 = __nccwpck_require__(71677); +const deployment_publication_1 = __nccwpck_require__(32982); class RepositoryReleasePublicationRepository { constructor(githubClient) { this.githubClient = githubClient; @@ -76965,7 +76959,7 @@ function verifiedReleaseUrl(release, tag, expectedName, expectedBody, operationI /***/ }), -/***/ 10766: +/***/ 37052: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -76987,7 +76981,7 @@ async function listRepositoryReleases(client, owner, repository) { /***/ }), -/***/ 46772: +/***/ 71677: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -76995,8 +76989,8 @@ async function listRepositoryReleases(client, owner, repository) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.findRepositoryTag = findRepositoryTag; exports.getRepositoryTagSha = getRepositoryTagSha; -const github_error_policy_1 = __nccwpck_require__(58791); -const release_tag_policy_1 = __nccwpck_require__(62748); +const github_error_policy_1 = __nccwpck_require__(26189); +const release_tag_policy_1 = __nccwpck_require__(61708); async function findRepositoryTag(client, owner, repository, tag) { try { const { data } = await client.rest.git.getRef({ owner, repo: repository, ref: (0, release_tag_policy_1.tagReference)(tag) }); @@ -77025,17 +77019,17 @@ async function getRepositoryTagSha(client, owner, repository, tag) { /***/ }), -/***/ 58717: +/***/ 83842: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.RepositoryTagRepository = void 0; -const logger_1 = __nccwpck_require__(91151); -const release_tag_policy_1 = __nccwpck_require__(62748); -const repository_tag_query_1 = __nccwpck_require__(46772); -const application_error_1 = __nccwpck_require__(75999); +const logger_1 = __nccwpck_require__(50135); +const release_tag_policy_1 = __nccwpck_require__(61708); +const repository_tag_query_1 = __nccwpck_require__(71677); +const application_error_1 = __nccwpck_require__(2965); class RepositoryTagRepository { constructor(githubClient) { this.githubClient = githubClient; @@ -77135,7 +77129,7 @@ exports.RepositoryTagRepository = RepositoryTagRepository; /***/ }), -/***/ 56818: +/***/ 87013: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -77159,7 +77153,7 @@ function hasReleaseContent(release) { /***/ }), -/***/ 62748: +/***/ 61708: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -77181,7 +77175,7 @@ function releaseName(version, title) { /***/ }), -/***/ 27673: +/***/ 85551: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -77199,7 +77193,7 @@ function releaseIdAsString(id) { /***/ }), -/***/ 28493: +/***/ 73307: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -77210,10 +77204,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.RepositorySecretsCommandRepository = exports.RepositoryVariablesCommandRepository = exports.SetupRemoteConfigurationQueryRepository = exports.RepositoryVariablesQueryRepository = exports.RepositorySecretNamesQueryRepository = void 0; exports.encryptSecret = encryptSecret; -const setup_workflow_catalog_1 = __nccwpck_require__(24596); -const github_error_policy_1 = __nccwpck_require__(58791); -const credential_health_workflow_visibility_1 = __nccwpck_require__(57628); -const tweetnacl_1 = __importDefault(__nccwpck_require__(24258)); +const setup_workflow_catalog_1 = __nccwpck_require__(37008); +const github_error_policy_1 = __nccwpck_require__(26189); +const credential_health_workflow_visibility_1 = __nccwpck_require__(85071); +const tweetnacl_1 = __importDefault(__nccwpck_require__(37124)); const node_crypto_1 = __nccwpck_require__(6005); class GithubActionsResourceTransport { constructor(githubClient) { @@ -77578,15 +77572,15 @@ function encryptSecret(value, base64PublicKey) { /***/ }), -/***/ 40941: +/***/ 42351: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.ActivePreviousWorkflowRunsRepository = void 0; -const workflow_status_1 = __nccwpck_require__(1462); -const workflow_runs_retry_1 = __nccwpck_require__(86434); +const workflow_status_1 = __nccwpck_require__(37003); +const workflow_runs_retry_1 = __nccwpck_require__(11946); const NO_OP_DELAY_PORT = { wait: async () => undefined }; const SYSTEM_CLOCK = { nowMilliseconds: () => Date.now() }; const SYSTEM_RANDOM = { next: () => Math.random() }; @@ -77663,7 +77657,7 @@ function isActivePreviousRun(run, query) { /***/ }), -/***/ 29509: +/***/ 70051: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -77690,7 +77684,7 @@ exports.WorkflowDispatchRepository = WorkflowDispatchRepository; /***/ }), -/***/ 86434: +/***/ 11946: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -77698,7 +77692,7 @@ exports.WorkflowDispatchRepository = WorkflowDispatchRepository; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.WorkflowQueueDeadlineError = exports.WORKFLOW_RUNS_RETRY_POLICY = void 0; exports.withWorkflowRunsRetry = withWorkflowRunsRetry; -const workflow_queue_policy_1 = __nccwpck_require__(43193); +const workflow_queue_policy_1 = __nccwpck_require__(97549); exports.WORKFLOW_RUNS_RETRY_POLICY = { maximumAttempts: 5, rateLimitMaximumAttempts: 5, @@ -77854,7 +77848,7 @@ function firstNumericValue(...values) { /***/ }), -/***/ 1462: +/***/ 37003: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -77884,7 +77878,7 @@ exports.WORKFLOW_ACTIVE_STATUSES = [ /***/ }), -/***/ 89040: +/***/ 95407: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -77907,7 +77901,7 @@ function isAgentConfigurationReady(configuration) { /***/ }), -/***/ 12253: +/***/ 25901: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -77925,7 +77919,7 @@ function workspaceModeForCapability(capability) { /***/ }), -/***/ 51114: +/***/ 7465: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -77982,7 +77976,7 @@ function escapeRegExp(value) { /***/ }), -/***/ 14712: +/***/ 32721: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -77991,7 +77985,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.selectCanonicalBugbotPullRequest = selectCanonicalBugbotPullRequest; exports.summarizeBugbotCoverage = summarizeBugbotCoverage; exports.completeBugbotSourceCoverage = completeBugbotSourceCoverage; -const git_object_id_1 = __nccwpck_require__(88623); +const git_object_id_1 = __nccwpck_require__(36924); function selectCanonicalBugbotPullRequest(target, candidates, source) { if (source === "exact-head" && candidates.length === 0) return { kind: "none" }; @@ -78075,7 +78069,7 @@ function matchesConstrainedHead(target, candidate) { /***/ }), -/***/ 31011: +/***/ 82048: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -78131,7 +78125,7 @@ function identitiesAreCompatible(existing, finding) { /***/ }), -/***/ 91853: +/***/ 657: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -78206,7 +78200,7 @@ function fnv1a(value) { /***/ }), -/***/ 1811: +/***/ 22551: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -78257,7 +78251,7 @@ function invalid(reason) { /***/ }), -/***/ 3994: +/***/ 19249: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -78326,14 +78320,14 @@ function resolveBugbotReviewEffort(configured, complexity) { /***/ }), -/***/ 80859: +/***/ 23272: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildBugbotReviewProjection = buildBugbotReviewProjection; -const review_state_1 = __nccwpck_require__(79200); +const review_state_1 = __nccwpck_require__(17271); function buildBugbotReviewProjection(input) { const findings = [...input.findings].sort((left, right) => left.id.localeCompare(right.id)); const counts = (0, review_state_1.countBugbotFindingStates)(findings.map((finding) => finding.state)); @@ -78388,7 +78382,7 @@ function stableDigest(value) { /***/ }), -/***/ 79200: +/***/ 17271: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -78462,7 +78456,7 @@ function countActionableBugbotFindings(counts) { /***/ }), -/***/ 27089: +/***/ 30717: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -78530,7 +78524,7 @@ function isNewerCliVersion(installedVersion, publishedVersion) { /***/ }), -/***/ 77454: +/***/ 2324: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -78553,7 +78547,7 @@ function hasVisibleCommentContent(value) { /***/ }), -/***/ 11771: +/***/ 87134: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -78634,7 +78628,7 @@ function parseCopilotCommand(raw) { /***/ }), -/***/ 86819: +/***/ 81916: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -78642,7 +78636,7 @@ function parseCopilotCommand(raw) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.containsBotMention = containsBotMention; exports.isCopilotCommentRequest = isCopilotCommentRequest; -const copilot_command_1 = __nccwpck_require__(11771); +const copilot_command_1 = __nccwpck_require__(87134); /** Matches GitHub usernames case-insensitively without matching a larger username. */ function containsBotMention(commentBody, tokenUser) { const normalizedUser = tokenUser.trim().replace(/^@/u, ''); @@ -78663,7 +78657,7 @@ function isCopilotCommentRequest(commentBody, botLogin) { /***/ }), -/***/ 72418: +/***/ 4227: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -78788,7 +78782,7 @@ function lifecycleStateFromLabels(currentLabels, labels = exports.DEFAULT_COPILO /***/ }), -/***/ 22495: +/***/ 5664: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -78905,12 +78899,12 @@ function parseDeploymentEnum(value, allowed, fallback) { ? { value: normalized, valid: true } : { value: fallback, valid: false }; } -const merge_queue_readiness_1 = __nccwpck_require__(12515); +const merge_queue_readiness_1 = __nccwpck_require__(36637); /***/ }), -/***/ 92730: +/***/ 17176: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -78924,8 +78918,8 @@ exports.completeReconciliationTarget = completeReconciliationTarget; exports.sanitizeDeploymentMessage = sanitizeDeploymentMessage; exports.isDeploymentOperationSnapshot = isDeploymentOperationSnapshot; exports.requiredDeploymentFailure = requiredDeploymentFailure; -const deployment_configuration_1 = __nccwpck_require__(22495); -const locale_1 = __nccwpck_require__(15386); +const deployment_configuration_1 = __nccwpck_require__(5664); +const locale_1 = __nccwpck_require__(64552); exports.DEPLOYMENT_PHASES = [ "preparing", "promotion_pr_pending", @@ -79133,7 +79127,7 @@ function hasOnlyKeys(value, allowed) { /***/ }), -/***/ 6912: +/***/ 32982: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -79166,7 +79160,7 @@ function parseDeploymentPublicationMarker(body) { /***/ }), -/***/ 72369: +/***/ 32481: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -79176,7 +79170,7 @@ exports.readDeploymentOperationState = readDeploymentOperationState; exports.deploymentStateFence = deploymentStateFence; exports.nextDeploymentRevision = nextDeploymentRevision; exports.decideDeploymentStateSave = decideDeploymentStateSave; -const deployment_operation_1 = __nccwpck_require__(92730); +const deployment_operation_1 = __nccwpck_require__(17176); function readDeploymentOperationState(value) { if (value === undefined || value === null) return { kind: "absent" }; @@ -79258,7 +79252,7 @@ function isRecord(value) { /***/ }), -/***/ 88623: +/***/ 36924: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -79279,7 +79273,7 @@ function canonicalGitObjectId(value) { /***/ }), -/***/ 21486: +/***/ 77025: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -79297,7 +79291,7 @@ function isPullRequestConversationComment(input) { /***/ }), -/***/ 35793: +/***/ 75905: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -79334,7 +79328,7 @@ function publicationTargetToken(target) { /***/ }), -/***/ 84403: +/***/ 19596: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -79350,7 +79344,7 @@ function githubUsersMatch(left, right) { /***/ }), -/***/ 77001: +/***/ 52620: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -79421,7 +79415,7 @@ function hasOnlyKeys(value, allowed) { /***/ }), -/***/ 38572: +/***/ 7703: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -79479,7 +79473,7 @@ function normalize(value) { /***/ }), -/***/ 90332: +/***/ 20953: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -79520,7 +79514,7 @@ function branchIsReady(input) { /***/ }), -/***/ 26744: +/***/ 62721: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -79705,7 +79699,7 @@ function isAutomaticOrVersion(value) { /***/ }), -/***/ 77734: +/***/ 84598: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -79780,7 +79774,7 @@ function decideIssueWorkflowRuntime(context) { /***/ }), -/***/ 15386: +/***/ 64552: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -79884,7 +79878,7 @@ function optionalLocale(value) { /***/ }), -/***/ 95914: +/***/ 7975: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -79916,7 +79910,7 @@ function isSafeOperationId(value) { /***/ }), -/***/ 12515: +/***/ 36637: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -80053,7 +80047,7 @@ function hasUnsafeControlCharacter(value) { /***/ }), -/***/ 27097: +/***/ 5313: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -80066,7 +80060,7 @@ exports.validateDynamicCatalogMessages = validateDynamicCatalogMessages; exports.renderCatalogMessage = renderCatalogMessage; exports.catalogPlaceholders = catalogPlaceholders; exports.catalogPluralCategories = catalogPluralCategories; -const locale_1 = __nccwpck_require__(15386); +const locale_1 = __nccwpck_require__(64552); exports.MESSAGE_CATALOG_VERSION = '3'; exports.CATALOG_PLURAL_CATEGORIES = Object.freeze([ 'zero', @@ -80223,7 +80217,7 @@ function placeholdersForTemplate(value) { /***/ }), -/***/ 19879: +/***/ 45613: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -80253,7 +80247,7 @@ function parsePositiveSafeInteger(value) { /***/ }), -/***/ 34730: +/***/ 54078: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -80445,7 +80439,7 @@ function isRecord(value) { /***/ }), -/***/ 98820: +/***/ 53553: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -80625,7 +80619,7 @@ function enumArray(value, allowed, label, max, errors) { /***/ }), -/***/ 45315: +/***/ 25623: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -80684,7 +80678,7 @@ function shouldAutomaticallyUpdatePullRequestDescription(mode) { /***/ }), -/***/ 47122: +/***/ 98209: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -80707,7 +80701,7 @@ function redactSensitiveText(value) { /***/ }), -/***/ 67057: +/***/ 12334: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -80818,7 +80812,7 @@ function normalizeOrigin(origin) { /***/ }), -/***/ 9512: +/***/ 56189: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -80840,7 +80834,7 @@ function renderApprovalObserverWorkflow(template, policy) { /***/ }), -/***/ 24596: +/***/ 37008: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -80886,7 +80880,7 @@ function featureEnabled(feature, features) { /***/ }), -/***/ 11800: +/***/ 73447: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -80898,12 +80892,12 @@ const node_child_process_1 = __nccwpck_require__(17718); const node_fs_1 = __nccwpck_require__(87561); const node_os_1 = __nccwpck_require__(70612); const node_path_1 = __nccwpck_require__(49411); -const agent_execution_policy_dispatcher_1 = __nccwpck_require__(25690); -const agent_execution_plan_1 = __nccwpck_require__(12253); -const agent_cli_contracts_1 = __nccwpck_require__(48254); -const agent_executable_policy_1 = __nccwpck_require__(12570); -const agent_authentication_1 = __nccwpck_require__(51371); -const agent_runtime_manifest_1 = __nccwpck_require__(57104); +const agent_execution_policy_dispatcher_1 = __nccwpck_require__(3341); +const agent_execution_plan_1 = __nccwpck_require__(25901); +const agent_cli_contracts_1 = __nccwpck_require__(99483); +const agent_executable_policy_1 = __nccwpck_require__(53773); +const agent_authentication_1 = __nccwpck_require__(67354); +const agent_runtime_manifest_1 = __nccwpck_require__(18890); const DEFAULT_SYSTEM = { resolveExecutable: resolveExecutablePath, readVersion(executable, environment) { @@ -81092,7 +81086,7 @@ function definedEnvironment(environment) { /***/ }), -/***/ 57104: +/***/ 18890: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -81136,7 +81130,7 @@ function assertInstalledAgentRuntimeVersion(provider, output) { /***/ }), -/***/ 51520: +/***/ 85401: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -81180,14 +81174,14 @@ exports.BoundBugbotGitMutationAdapter = BoundBugbotGitMutationAdapter; /***/ }), -/***/ 81849: +/***/ 51697: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.BranchSyncWorkspaceAdapter = void 0; -const workspace_changes_1 = __nccwpck_require__(93370); +const workspace_changes_1 = __nccwpck_require__(51578); /** Owns Git's merge state while keeping credentials confined to fetch/push subprocesses. */ class BranchSyncWorkspaceAdapter { constructor(git) { @@ -81341,7 +81335,7 @@ function invalid(reason) { /***/ }), -/***/ 76182: +/***/ 83605: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -81353,7 +81347,7 @@ exports.COPILOT_PACKAGE_NAME = '@vypdev/copilot'; /***/ }), -/***/ 62007: +/***/ 21570: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -81364,7 +81358,7 @@ exports.resolveUpdateCheckCachePath = resolveUpdateCheckCachePath; const node_fs_1 = __nccwpck_require__(87561); const node_os_1 = __nccwpck_require__(70612); const node_path_1 = __nccwpck_require__(49411); -const copilot_package_1 = __nccwpck_require__(76182); +const copilot_package_1 = __nccwpck_require__(83605); exports.NPM_REGISTRY_URL = `https://registry.npmjs.org/${encodeURIComponent(copilot_package_1.COPILOT_PACKAGE_NAME)}`; exports.UPDATE_CHECK_CACHE_TTL_MS = 24 * 60 * 60 * 1000; exports.UPDATE_CHECK_TIMEOUT_MS = 1500; @@ -81467,7 +81461,7 @@ exports.NpmCliUpdateCheckAdapter = NpmCliUpdateCheckAdapter; /***/ }), -/***/ 64975: +/***/ 28021: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -81476,7 +81470,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.PnpmCliUpgradeAdapter = void 0; exports.resolvePnpmExecutable = resolvePnpmExecutable; const node_child_process_1 = __nccwpck_require__(17718); -const copilot_package_1 = __nccwpck_require__(76182); +const copilot_package_1 = __nccwpck_require__(83605); function resolvePnpmExecutable(platform = process.platform) { return platform === 'win32' ? 'pnpm.cmd' : 'pnpm'; } @@ -81519,15 +81513,15 @@ exports.PnpmCliUpgradeAdapter = PnpmCliUpgradeAdapter; /***/ }), -/***/ 233: +/***/ 43758: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createActorAuthorizationRepository = createActorAuthorizationRepository; -const github_identity_client_factory_1 = __nccwpck_require__(93081); -const actor_authorization_repository_1 = __nccwpck_require__(96711); +const github_identity_client_factory_1 = __nccwpck_require__(17930); +const actor_authorization_repository_1 = __nccwpck_require__(54771); function createActorAuthorizationRepository() { return new actor_authorization_repository_1.ActorAuthorizationRepository((0, github_identity_client_factory_1.createActorAuthorizationClient)()); } @@ -81535,16 +81529,16 @@ function createActorAuthorizationRepository() { /***/ }), -/***/ 94253: +/***/ 11013: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createSynchronizeAgentActivityUseCase = createSynchronizeAgentActivityUseCase; -const synchronize_agent_activity_use_case_1 = __nccwpck_require__(44880); -const issue_labels_composition_root_1 = __nccwpck_require__(34780); -const lifecycle_capability_port_binding_1 = __nccwpck_require__(85785); +const synchronize_agent_activity_use_case_1 = __nccwpck_require__(83269); +const issue_labels_composition_root_1 = __nccwpck_require__(94185); +const lifecycle_capability_port_binding_1 = __nccwpck_require__(28822); function createSynchronizeAgentActivityUseCase(binding) { return new synchronize_agent_activity_use_case_1.SynchronizeAgentActivityUseCase((0, lifecycle_capability_port_binding_1.bindIssueLabels)((0, issue_labels_composition_root_1.createIssueLabelRepository)(), binding)); } @@ -81552,7 +81546,7 @@ function createSynchronizeAgentActivityUseCase(binding) { /***/ }), -/***/ 85079: +/***/ 7753: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -81561,12 +81555,12 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createFindingsQueryPort = createFindingsQueryPort; exports.createFixerQueryPort = createFixerQueryPort; exports.createLanguageQueryPort = createLanguageQueryPort; -const agent_cli_client_1 = __nccwpck_require__(68570); -const agent_execution_planner_1 = __nccwpck_require__(11800); -const logger_agent_execution_observer_adapter_1 = __nccwpck_require__(59844); -const findings_agent_adapter_1 = __nccwpck_require__(27725); -const fixer_agent_adapter_1 = __nccwpck_require__(62259); -const language_agent_adapter_1 = __nccwpck_require__(10573); +const agent_cli_client_1 = __nccwpck_require__(64596); +const agent_execution_planner_1 = __nccwpck_require__(73447); +const logger_agent_execution_observer_adapter_1 = __nccwpck_require__(36337); +const findings_agent_adapter_1 = __nccwpck_require__(94109); +const fixer_agent_adapter_1 = __nccwpck_require__(99972); +const language_agent_adapter_1 = __nccwpck_require__(76206); function defaultInfrastructure() { return { cli: new agent_cli_client_1.AgentCliClient(new agent_execution_planner_1.AgentExecutionPlanner(), new logger_agent_execution_observer_adapter_1.LoggerAgentExecutionObserverAdapter()), @@ -81585,15 +81579,15 @@ function createLanguageQueryPort(infrastructure = defaultInfrastructure()) { /***/ }), -/***/ 33885: +/***/ 55549: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createAuthenticatedUserCompositionRoot = createAuthenticatedUserCompositionRoot; -const github_identity_client_factory_1 = __nccwpck_require__(93081); -const authenticated_user_repository_1 = __nccwpck_require__(11454); +const github_identity_client_factory_1 = __nccwpck_require__(17930); +const authenticated_user_repository_1 = __nccwpck_require__(85397); function createAuthenticatedUserCompositionRoot() { return new authenticated_user_repository_1.AuthenticatedUserRepository((0, github_identity_client_factory_1.createAuthenticatedUserClient)()); } @@ -81601,29 +81595,29 @@ function createAuthenticatedUserCompositionRoot() { /***/ }), -/***/ 67395: +/***/ 40733: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createBugbotCompositionRoot = createBugbotCompositionRoot; -const github_issue_client_factory_1 = __nccwpck_require__(95883); -const github_project_client_factory_1 = __nccwpck_require__(23691); -const github_pull_request_client_factory_1 = __nccwpck_require__(9068); -const bugbot_issue_repository_1 = __nccwpck_require__(82726); -const issue_content_repository_1 = __nccwpck_require__(2313); -const bugbot_issue_comment_query_repository_1 = __nccwpck_require__(88593); -const bugbot_pull_request_repository_1 = __nccwpck_require__(55165); -const pull_request_changes_repository_1 = __nccwpck_require__(71564); -const pull_request_lifecycle_repository_1 = __nccwpck_require__(24189); -const pull_request_review_comment_command_repository_1 = __nccwpck_require__(17120); -const pull_request_review_comment_query_repository_1 = __nccwpck_require__(44085); -const pull_request_review_thread_repository_1 = __nccwpck_require__(23314); -const workspace_bugbot_rules_repository_1 = __nccwpck_require__(50183); -const logger_bugbot_telemetry_adapter_1 = __nccwpck_require__(34685); -const github_bugbot_review_navigation_adapter_1 = __nccwpck_require__(19008); -const bugbot_scm_port_factory_1 = __nccwpck_require__(19937); +const github_issue_client_factory_1 = __nccwpck_require__(15161); +const github_project_client_factory_1 = __nccwpck_require__(63165); +const github_pull_request_client_factory_1 = __nccwpck_require__(46236); +const bugbot_issue_repository_1 = __nccwpck_require__(15589); +const issue_content_repository_1 = __nccwpck_require__(43338); +const bugbot_issue_comment_query_repository_1 = __nccwpck_require__(85949); +const bugbot_pull_request_repository_1 = __nccwpck_require__(66202); +const pull_request_changes_repository_1 = __nccwpck_require__(65797); +const pull_request_lifecycle_repository_1 = __nccwpck_require__(57177); +const pull_request_review_comment_command_repository_1 = __nccwpck_require__(48987); +const pull_request_review_comment_query_repository_1 = __nccwpck_require__(31371); +const pull_request_review_thread_repository_1 = __nccwpck_require__(64125); +const workspace_bugbot_rules_repository_1 = __nccwpck_require__(83645); +const logger_bugbot_telemetry_adapter_1 = __nccwpck_require__(55375); +const github_bugbot_review_navigation_adapter_1 = __nccwpck_require__(33451); +const bugbot_scm_port_factory_1 = __nccwpck_require__(29985); function createBugbotCompositionRoot(binding) { const issueContent = new issue_content_repository_1.IssueContentRepository((0, github_issue_client_factory_1.createIssueContentClient)()); const issue = new bugbot_issue_repository_1.BugbotIssueRepository(issueContent); @@ -81650,7 +81644,7 @@ function createBugbotCompositionRoot(binding) { /***/ }), -/***/ 19937: +/***/ 29985: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -81732,28 +81726,28 @@ exports.BugbotScmPortFactory = BugbotScmPortFactory; /***/ }), -/***/ 21531: +/***/ 25949: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createCheckProgressCompositionRoot = createCheckProgressCompositionRoot; -const github_branch_client_factory_1 = __nccwpck_require__(30144); -const github_issue_client_factory_1 = __nccwpck_require__(95883); -const github_pull_request_client_factory_1 = __nccwpck_require__(9068); -const check_progress_use_case_1 = __nccwpck_require__(41601); -const agent_capability_composition_root_1 = __nccwpck_require__(85079); -const issue_content_repository_1 = __nccwpck_require__(2313); -const issue_label_repository_1 = __nccwpck_require__(45725); -const issue_progress_label_repository_1 = __nccwpck_require__(66610); -const issue_progress_tracking_repository_1 = __nccwpck_require__(26674); -const branch_lifecycle_repository_1 = __nccwpck_require__(19504); -const pull_request_lifecycle_repository_1 = __nccwpck_require__(24189); -const github_publication_source_repository_1 = __nccwpck_require__(52644); -const shared_capability_port_binding_1 = __nccwpck_require__(47399); -const lifecycle_capability_port_binding_1 = __nccwpck_require__(85785); -const push_single_action_capability_port_binding_1 = __nccwpck_require__(49417); +const github_branch_client_factory_1 = __nccwpck_require__(32112); +const github_issue_client_factory_1 = __nccwpck_require__(15161); +const github_pull_request_client_factory_1 = __nccwpck_require__(46236); +const check_progress_use_case_1 = __nccwpck_require__(21113); +const agent_capability_composition_root_1 = __nccwpck_require__(7753); +const issue_content_repository_1 = __nccwpck_require__(43338); +const issue_label_repository_1 = __nccwpck_require__(27825); +const issue_progress_label_repository_1 = __nccwpck_require__(58763); +const issue_progress_tracking_repository_1 = __nccwpck_require__(64844); +const branch_lifecycle_repository_1 = __nccwpck_require__(88216); +const pull_request_lifecycle_repository_1 = __nccwpck_require__(57177); +const github_publication_source_repository_1 = __nccwpck_require__(97590); +const shared_capability_port_binding_1 = __nccwpck_require__(20918); +const lifecycle_capability_port_binding_1 = __nccwpck_require__(28822); +const push_single_action_capability_port_binding_1 = __nccwpck_require__(54110); function createCheckProgressCompositionRoot(binding) { const labels = new issue_label_repository_1.IssueLabelRepository((0, github_issue_client_factory_1.createIssueLabelsClient)()); const content = new issue_content_repository_1.IssueContentRepository((0, github_issue_client_factory_1.createIssueContentClient)()); @@ -81763,15 +81757,15 @@ function createCheckProgressCompositionRoot(binding) { /***/ }), -/***/ 78998: +/***/ 89267: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createCliUpdateCheckUseCase = createCliUpdateCheckUseCase; -const check_cli_update_use_case_1 = __nccwpck_require__(55721); -const npm_cli_update_check_adapter_1 = __nccwpck_require__(62007); +const check_cli_update_use_case_1 = __nccwpck_require__(11677); +const npm_cli_update_check_adapter_1 = __nccwpck_require__(21570); function createCliUpdateCheckUseCase() { return new check_cli_update_use_case_1.CheckCliUpdateUseCase(new npm_cli_update_check_adapter_1.NpmCliUpdateCheckAdapter()); } @@ -81779,15 +81773,15 @@ function createCliUpdateCheckUseCase() { /***/ }), -/***/ 74142: +/***/ 91561: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createUpgradeCliUseCase = createUpgradeCliUseCase; -const upgrade_cli_use_case_1 = __nccwpck_require__(45762); -const pnpm_cli_upgrade_adapter_1 = __nccwpck_require__(64975); +const upgrade_cli_use_case_1 = __nccwpck_require__(45773); +const pnpm_cli_upgrade_adapter_1 = __nccwpck_require__(28021); function createUpgradeCliUseCase(cliUpgradePort = new pnpm_cli_upgrade_adapter_1.PnpmCliUpgradeAdapter()) { return new upgrade_cli_use_case_1.UpgradeCliUseCase(cliUpgradePort); } @@ -81795,19 +81789,19 @@ function createUpgradeCliUseCase(cliUpgradePort = new pnpm_cli_upgrade_adapter_1 /***/ }), -/***/ 98313: +/***/ 84724: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createExecutionIssueSetupCompositionRoot = createExecutionIssueSetupCompositionRoot; -const github_issue_client_factory_1 = __nccwpck_require__(95883); -const github_project_client_factory_1 = __nccwpck_require__(23691); -const execution_issue_setup_repository_1 = __nccwpck_require__(91153); -const issue_content_repository_1 = __nccwpck_require__(2313); -const issue_label_repository_1 = __nccwpck_require__(45725); -const issue_metadata_repository_1 = __nccwpck_require__(11333); +const github_issue_client_factory_1 = __nccwpck_require__(15161); +const github_project_client_factory_1 = __nccwpck_require__(63165); +const execution_issue_setup_repository_1 = __nccwpck_require__(32004); +const issue_content_repository_1 = __nccwpck_require__(43338); +const issue_label_repository_1 = __nccwpck_require__(27825); +const issue_metadata_repository_1 = __nccwpck_require__(37674); function createExecutionIssueSetupCompositionRoot() { return new execution_issue_setup_repository_1.ExecutionIssueSetupRepository(new issue_metadata_repository_1.IssueMetadataRepository((0, github_issue_client_factory_1.createIssueMetadataClient)(), (0, github_project_client_factory_1.createGraphqlTransportClient)()), new issue_content_repository_1.IssueContentRepository((0, github_issue_client_factory_1.createIssueContentClient)()), new issue_label_repository_1.IssueLabelRepository((0, github_issue_client_factory_1.createIssueLabelsClient)())); } @@ -81815,21 +81809,21 @@ function createExecutionIssueSetupCompositionRoot() { /***/ }), -/***/ 83965: +/***/ 71774: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createSetupExecutionUseCase = createSetupExecutionUseCase; -const execution_branch_version_resolver_1 = __nccwpck_require__(71813); -const setup_execution_use_case_1 = __nccwpck_require__(88512); -const get_hotfix_version_use_case_1 = __nccwpck_require__(59946); -const get_release_type_use_case_1 = __nccwpck_require__(64410); -const get_release_version_use_case_1 = __nccwpck_require__(70587); -const configuration_handler_1 = __nccwpck_require__(40188); -const authenticated_user_composition_root_1 = __nccwpck_require__(33885); -const execution_issue_setup_composition_root_1 = __nccwpck_require__(98313); +const execution_branch_version_resolver_1 = __nccwpck_require__(83966); +const setup_execution_use_case_1 = __nccwpck_require__(51778); +const get_hotfix_version_use_case_1 = __nccwpck_require__(85276); +const get_release_type_use_case_1 = __nccwpck_require__(65633); +const get_release_version_use_case_1 = __nccwpck_require__(8709); +const configuration_handler_1 = __nccwpck_require__(51068); +const authenticated_user_composition_root_1 = __nccwpck_require__(55549); +const execution_issue_setup_composition_root_1 = __nccwpck_require__(84724); function createSetupExecutionUseCase(latestTagQueryPort, credentials) { const rawIssueSetupPort = (0, execution_issue_setup_composition_root_1.createExecutionIssueSetupCompositionRoot)(); const rawOrganizationSetupPort = (0, authenticated_user_composition_root_1.createAuthenticatedUserCompositionRoot)(); @@ -81861,14 +81855,14 @@ function createSetupExecutionUseCase(latestTagQueryPort, credentials) { /***/ }), -/***/ 30144: +/***/ 32112: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createBranchComparisonClient = exports.createBranchClient = void 0; -const octokit_branch_adapters_1 = __nccwpck_require__(77889); +const octokit_branch_adapters_1 = __nccwpck_require__(95215); const createBranchClient = () => new octokit_branch_adapters_1.OctokitBranchClientAdapter(); exports.createBranchClient = createBranchClient; const createBranchComparisonClient = () => new octokit_branch_adapters_1.OctokitBranchComparisonClientAdapter(); @@ -81877,15 +81871,15 @@ exports.createBranchComparisonClient = createBranchComparisonClient; /***/ }), -/***/ 93081: +/***/ 17930: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createRepositoryVariablesClient = exports.createOrganizationMembersClient = exports.createActorAuthorizationClient = exports.createAuthenticatedUserClient = void 0; -const octokit_identity_adapters_1 = __nccwpck_require__(29996); -const octokit_repository_variables_adapter_1 = __nccwpck_require__(81329); +const octokit_identity_adapters_1 = __nccwpck_require__(39787); +const octokit_repository_variables_adapter_1 = __nccwpck_require__(93883); const createAuthenticatedUserClient = () => new octokit_identity_adapters_1.OctokitAuthenticatedUserClientAdapter(); exports.createAuthenticatedUserClient = createAuthenticatedUserClient; const createActorAuthorizationClient = () => new octokit_identity_adapters_1.OctokitActorAuthorizationClientAdapter(); @@ -81898,14 +81892,14 @@ exports.createRepositoryVariablesClient = createRepositoryVariablesClient; /***/ }), -/***/ 95883: +/***/ 15161: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createIssueTitleClient = exports.createIssueMetadataClient = exports.createIssueInactivityClient = exports.createIssueLifecycleClient = exports.createIssueLabelsClient = exports.createIssueLabelProvisioningClient = exports.createIssueContentClient = exports.createIssueAssignmentClient = void 0; -const octokit_issue_adapters_1 = __nccwpck_require__(77179); +const octokit_issue_adapters_1 = __nccwpck_require__(73387); const createIssueAssignmentClient = () => new octokit_issue_adapters_1.OctokitIssueAssignmentClientAdapter(); exports.createIssueAssignmentClient = createIssueAssignmentClient; const createIssueContentClient = () => new octokit_issue_adapters_1.OctokitIssueContentClientAdapter(); @@ -81926,15 +81920,15 @@ exports.createIssueTitleClient = createIssueTitleClient; /***/ }), -/***/ 23691: +/***/ 63165: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createOwnerTypeClient = exports.createGraphqlTransportClient = void 0; -const octokit_project_adapters_1 = __nccwpck_require__(68505); -const octokit_identity_adapters_1 = __nccwpck_require__(29996); +const octokit_project_adapters_1 = __nccwpck_require__(39191); +const octokit_identity_adapters_1 = __nccwpck_require__(39787); const createGraphqlTransportClient = () => new octokit_project_adapters_1.OctokitGraphqlTransportClientAdapter(); exports.createGraphqlTransportClient = createGraphqlTransportClient; const createOwnerTypeClient = () => new octokit_identity_adapters_1.OctokitOwnerTypeClientAdapter(); @@ -81943,14 +81937,14 @@ exports.createOwnerTypeClient = createOwnerTypeClient; /***/ }), -/***/ 9068: +/***/ 46236: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createPullRequestReviewCommentClient = exports.createPullRequestReviewerClient = exports.createPullRequestLifecycleClient = exports.createPullRequestChangesClient = void 0; -const octokit_pull_request_adapters_1 = __nccwpck_require__(1397); +const octokit_pull_request_adapters_1 = __nccwpck_require__(38632); const createPullRequestChangesClient = () => new octokit_pull_request_adapters_1.OctokitPullRequestChangesClientAdapter(); exports.createPullRequestChangesClient = createPullRequestChangesClient; const createPullRequestLifecycleClient = () => new octokit_pull_request_adapters_1.OctokitPullRequestLifecycleClientAdapter(); @@ -81963,28 +81957,28 @@ exports.createPullRequestReviewCommentClient = createPullRequestReviewCommentCli /***/ }), -/***/ 76706: +/***/ 66236: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createReleaseClient = void 0; -const octokit_release_adapters_1 = __nccwpck_require__(5334); +const octokit_release_adapters_1 = __nccwpck_require__(51480); const createReleaseClient = () => new octokit_release_adapters_1.OctokitReleaseClientAdapter(); exports.createReleaseClient = createReleaseClient; /***/ }), -/***/ 29839: +/***/ 7654: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createWorkflowDispatchClient = exports.createWorkflowRunsClient = void 0; -const octokit_workflow_adapters_1 = __nccwpck_require__(86719); +const octokit_workflow_adapters_1 = __nccwpck_require__(36430); const createWorkflowRunsClient = () => new octokit_workflow_adapters_1.OctokitWorkflowRunsClientAdapter(); exports.createWorkflowRunsClient = createWorkflowRunsClient; const createWorkflowDispatchClient = () => new octokit_workflow_adapters_1.OctokitWorkflowDispatchClientAdapter(); @@ -81993,28 +81987,28 @@ exports.createWorkflowDispatchClient = createWorkflowDispatchClient; /***/ }), -/***/ 84138: +/***/ 84423: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createInitialSetupCompositionRoot = createInitialSetupCompositionRoot; -const github_identity_client_factory_1 = __nccwpck_require__(93081); -const github_issue_client_factory_1 = __nccwpck_require__(95883); -const github_project_client_factory_1 = __nccwpck_require__(23691); -const github_release_client_factory_1 = __nccwpck_require__(76706); -const issue_label_provisioning_repository_1 = __nccwpck_require__(59699); -const issue_type_repository_1 = __nccwpck_require__(4858); -const authenticated_user_repository_1 = __nccwpck_require__(11454); -const repository_default_branch_repository_1 = __nccwpck_require__(96578); -const repository_tag_repository_1 = __nccwpck_require__(58717); -const git_cli_repository_1 = __nccwpck_require__(26331); -const initial_setup_use_case_composition_1 = __nccwpck_require__(93141); -const setup_workspace_adapter_1 = __nccwpck_require__(5729); -const repository_variables_repository_1 = __nccwpck_require__(28493); -const github_identity_client_factory_2 = __nccwpck_require__(93081); -const push_single_action_capability_port_binding_1 = __nccwpck_require__(49417); +const github_identity_client_factory_1 = __nccwpck_require__(17930); +const github_issue_client_factory_1 = __nccwpck_require__(15161); +const github_project_client_factory_1 = __nccwpck_require__(63165); +const github_release_client_factory_1 = __nccwpck_require__(66236); +const issue_label_provisioning_repository_1 = __nccwpck_require__(4532); +const issue_type_repository_1 = __nccwpck_require__(87600); +const authenticated_user_repository_1 = __nccwpck_require__(85397); +const repository_default_branch_repository_1 = __nccwpck_require__(38818); +const repository_tag_repository_1 = __nccwpck_require__(83842); +const git_cli_repository_1 = __nccwpck_require__(72119); +const initial_setup_use_case_composition_1 = __nccwpck_require__(97792); +const setup_workspace_adapter_1 = __nccwpck_require__(23376); +const repository_variables_repository_1 = __nccwpck_require__(73307); +const github_identity_client_factory_2 = __nccwpck_require__(17930); +const push_single_action_capability_port_binding_1 = __nccwpck_require__(54110); function createInitialSetupCompositionRoot(binding) { const labelProvisioning = new issue_label_provisioning_repository_1.IssueLabelProvisioningRepository((0, github_issue_client_factory_1.createIssueLabelProvisioningClient)()); const githubResourceClient = (0, github_identity_client_factory_2.createRepositoryVariablesClient)(); @@ -82024,14 +82018,14 @@ function createInitialSetupCompositionRoot(binding) { /***/ }), -/***/ 93141: +/***/ 97792: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.composeInitialSetupUseCase = composeInitialSetupUseCase; -const initial_setup_use_case_1 = __nccwpck_require__(84837); +const initial_setup_use_case_1 = __nccwpck_require__(50658); function composeInitialSetupUseCase(...dependencies) { return new initial_setup_use_case_1.InitialSetupUseCase(...dependencies); } @@ -82039,15 +82033,15 @@ function composeInitialSetupUseCase(...dependencies) { /***/ }), -/***/ 62255: +/***/ 82808: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createIssueContentCompositionRoot = createIssueContentCompositionRoot; -const github_issue_client_factory_1 = __nccwpck_require__(95883); -const issue_content_repository_1 = __nccwpck_require__(2313); +const github_issue_client_factory_1 = __nccwpck_require__(15161); +const issue_content_repository_1 = __nccwpck_require__(43338); function createIssueContentCompositionRoot() { return new issue_content_repository_1.IssueContentRepository((0, github_issue_client_factory_1.createIssueContentClient)()); } @@ -82055,20 +82049,20 @@ function createIssueContentCompositionRoot() { /***/ }), -/***/ 74914: +/***/ 36937: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createCloseInactiveIssuesUseCase = createCloseInactiveIssuesUseCase; -const close_inactive_issues_use_case_1 = __nccwpck_require__(84579); -const issue_inactivity_repository_1 = __nccwpck_require__(28868); -const system_issue_inactivity_clock_adapter_1 = __nccwpck_require__(86457); -const github_issue_client_factory_1 = __nccwpck_require__(95883); -const issue_interaction_composition_root_1 = __nccwpck_require__(92503); -const lifecycle_capability_port_binding_1 = __nccwpck_require__(85785); -const push_single_action_capability_port_binding_1 = __nccwpck_require__(49417); +const close_inactive_issues_use_case_1 = __nccwpck_require__(56690); +const issue_inactivity_repository_1 = __nccwpck_require__(5533); +const system_issue_inactivity_clock_adapter_1 = __nccwpck_require__(62557); +const github_issue_client_factory_1 = __nccwpck_require__(15161); +const issue_interaction_composition_root_1 = __nccwpck_require__(33777); +const lifecycle_capability_port_binding_1 = __nccwpck_require__(28822); +const push_single_action_capability_port_binding_1 = __nccwpck_require__(54110); function createCloseInactiveIssuesUseCase(binding, catalogResolver) { return new close_inactive_issues_use_case_1.CloseInactiveIssuesUseCase((0, push_single_action_capability_port_binding_1.bindIssueInactivityQuery)(new issue_inactivity_repository_1.IssueInactivityRepository((0, github_issue_client_factory_1.createIssueInactivityClient)()), binding), (0, lifecycle_capability_port_binding_1.bindIssueClosure)((0, issue_interaction_composition_root_1.createIssueClosureRepository)(), binding), new system_issue_inactivity_clock_adapter_1.SystemIssueInactivityClockAdapter(), catalogResolver); } @@ -82076,7 +82070,7 @@ function createCloseInactiveIssuesUseCase(binding, catalogResolver) { /***/ }), -/***/ 92503: +/***/ 33777: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -82084,11 +82078,11 @@ function createCloseInactiveIssuesUseCase(binding, catalogResolver) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createIssueClosureRepository = createIssueClosureRepository; exports.createIssueNotificationRepository = createIssueNotificationRepository; -const github_issue_client_factory_1 = __nccwpck_require__(95883); -const issue_content_repository_1 = __nccwpck_require__(2313); -const issue_lifecycle_repository_1 = __nccwpck_require__(8346); -const issue_closure_repository_1 = __nccwpck_require__(23231); -const issue_notification_repository_1 = __nccwpck_require__(907); +const github_issue_client_factory_1 = __nccwpck_require__(15161); +const issue_content_repository_1 = __nccwpck_require__(43338); +const issue_lifecycle_repository_1 = __nccwpck_require__(44023); +const issue_closure_repository_1 = __nccwpck_require__(80674); +const issue_notification_repository_1 = __nccwpck_require__(24682); function createIssueClosureRepository() { return new issue_closure_repository_1.IssueClosureRepository(new issue_lifecycle_repository_1.IssueLifecycleRepository((0, github_issue_client_factory_1.createIssueLifecycleClient)()), new issue_content_repository_1.IssueContentRepository((0, github_issue_client_factory_1.createIssueContentClient)())); } @@ -82099,15 +82093,15 @@ function createIssueNotificationRepository() { /***/ }), -/***/ 34780: +/***/ 94185: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createIssueLabelRepository = createIssueLabelRepository; -const github_issue_client_factory_1 = __nccwpck_require__(95883); -const issue_label_repository_1 = __nccwpck_require__(45725); +const github_issue_client_factory_1 = __nccwpck_require__(15161); +const issue_label_repository_1 = __nccwpck_require__(27825); function createIssueLabelRepository() { return new issue_label_repository_1.IssueLabelRepository((0, github_issue_client_factory_1.createIssueLabelsClient)()); } @@ -82115,14 +82109,14 @@ function createIssueLabelRepository() { /***/ }), -/***/ 21239: +/***/ 91667: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.composeIssueUseCase = composeIssueUseCase; -const issue_use_case_1 = __nccwpck_require__(65281); +const issue_use_case_1 = __nccwpck_require__(43712); function composeIssueUseCase(...dependencies) { return new issue_use_case_1.IssueUseCase(...dependencies); } @@ -82130,57 +82124,57 @@ function composeIssueUseCase(...dependencies) { /***/ }), -/***/ 43022: +/***/ 97844: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createIssueUseCaseCompositionRoot = createIssueUseCaseCompositionRoot; -const github_branch_client_factory_1 = __nccwpck_require__(30144); -const github_issue_client_factory_1 = __nccwpck_require__(95883); -const github_project_client_factory_1 = __nccwpck_require__(23691); -const github_workflow_client_factory_1 = __nccwpck_require__(29839); -const recommend_steps_use_case_1 = __nccwpck_require__(73746); -const check_permissions_use_case_1 = __nccwpck_require__(18846); -const update_title_use_case_1 = __nccwpck_require__(20556); -const assign_members_to_issue_use_case_1 = __nccwpck_require__(55523); -const check_priority_issue_size_use_case_1 = __nccwpck_require__(19511); -const close_not_allowed_issue_use_case_1 = __nccwpck_require__(86675); -const label_deploy_added_use_case_1 = __nccwpck_require__(27708); -const link_issue_project_use_case_1 = __nccwpck_require__(34100); -const move_issue_to_in_progress_1 = __nccwpck_require__(52309); -const prepare_branches_use_case_1 = __nccwpck_require__(67546); -const remove_issue_branches_use_case_1 = __nccwpck_require__(15608); -const remove_not_needed_branches_use_case_1 = __nccwpck_require__(67129); -const update_issue_type_use_case_1 = __nccwpck_require__(38222); -const answer_issue_help_use_case_1 = __nccwpck_require__(10706); -const branch_lifecycle_repository_1 = __nccwpck_require__(19504); -const branch_name_repository_1 = __nccwpck_require__(61887); -const linked_branch_repository_1 = __nccwpck_require__(78009); -const linked_branch_readiness_repository_1 = __nccwpck_require__(79421); -const reconcile_branch_readiness_use_case_1 = __nccwpck_require__(71836); -const pre_branch_sdd_gate_use_case_1 = __nccwpck_require__(29475); -const pre_branch_sdd_workspace_adapter_1 = __nccwpck_require__(35849); -const push_single_action_capability_port_binding_1 = __nccwpck_require__(49417); -const issue_labels_composition_root_1 = __nccwpck_require__(34780); -const git_cli_repository_1 = __nccwpck_require__(26331); -const issue_assignment_repository_1 = __nccwpck_require__(75023); -const issue_closure_repository_1 = __nccwpck_require__(23231); -const issue_content_repository_1 = __nccwpck_require__(2313); -const issue_lifecycle_repository_1 = __nccwpck_require__(8346); -const issue_metadata_repository_1 = __nccwpck_require__(11333); -const issue_title_repository_1 = __nccwpck_require__(10121); -const issue_type_assignment_repository_1 = __nccwpck_require__(19118); -const workflow_dispatch_repository_1 = __nccwpck_require__(29509); -const timer_branch_propagation_delay_adapter_1 = __nccwpck_require__(20846); -const agent_capability_composition_root_1 = __nccwpck_require__(85079); -const issue_use_case_composition_1 = __nccwpck_require__(21239); -const organization_members_composition_root_1 = __nccwpck_require__(50603); -const project_board_composition_root_1 = __nccwpck_require__(37194); -const actor_authorization_composition_root_1 = __nccwpck_require__(233); -const shared_capability_port_binding_1 = __nccwpck_require__(47399); -const lifecycle_capability_port_binding_1 = __nccwpck_require__(85785); +const github_branch_client_factory_1 = __nccwpck_require__(32112); +const github_issue_client_factory_1 = __nccwpck_require__(15161); +const github_project_client_factory_1 = __nccwpck_require__(63165); +const github_workflow_client_factory_1 = __nccwpck_require__(7654); +const recommend_steps_use_case_1 = __nccwpck_require__(33526); +const check_permissions_use_case_1 = __nccwpck_require__(88960); +const update_title_use_case_1 = __nccwpck_require__(21376); +const assign_members_to_issue_use_case_1 = __nccwpck_require__(18189); +const check_priority_issue_size_use_case_1 = __nccwpck_require__(46243); +const close_not_allowed_issue_use_case_1 = __nccwpck_require__(71594); +const label_deploy_added_use_case_1 = __nccwpck_require__(56723); +const link_issue_project_use_case_1 = __nccwpck_require__(34835); +const move_issue_to_in_progress_1 = __nccwpck_require__(79453); +const prepare_branches_use_case_1 = __nccwpck_require__(35833); +const remove_issue_branches_use_case_1 = __nccwpck_require__(92405); +const remove_not_needed_branches_use_case_1 = __nccwpck_require__(26142); +const update_issue_type_use_case_1 = __nccwpck_require__(25514); +const answer_issue_help_use_case_1 = __nccwpck_require__(41713); +const branch_lifecycle_repository_1 = __nccwpck_require__(88216); +const branch_name_repository_1 = __nccwpck_require__(94776); +const linked_branch_repository_1 = __nccwpck_require__(23822); +const linked_branch_readiness_repository_1 = __nccwpck_require__(69983); +const reconcile_branch_readiness_use_case_1 = __nccwpck_require__(10027); +const pre_branch_sdd_gate_use_case_1 = __nccwpck_require__(89463); +const pre_branch_sdd_workspace_adapter_1 = __nccwpck_require__(71976); +const push_single_action_capability_port_binding_1 = __nccwpck_require__(54110); +const issue_labels_composition_root_1 = __nccwpck_require__(94185); +const git_cli_repository_1 = __nccwpck_require__(72119); +const issue_assignment_repository_1 = __nccwpck_require__(7223); +const issue_closure_repository_1 = __nccwpck_require__(80674); +const issue_content_repository_1 = __nccwpck_require__(43338); +const issue_lifecycle_repository_1 = __nccwpck_require__(44023); +const issue_metadata_repository_1 = __nccwpck_require__(37674); +const issue_title_repository_1 = __nccwpck_require__(89116); +const issue_type_assignment_repository_1 = __nccwpck_require__(55913); +const workflow_dispatch_repository_1 = __nccwpck_require__(70051); +const timer_branch_propagation_delay_adapter_1 = __nccwpck_require__(20015); +const agent_capability_composition_root_1 = __nccwpck_require__(7753); +const issue_use_case_composition_1 = __nccwpck_require__(91667); +const organization_members_composition_root_1 = __nccwpck_require__(17705); +const project_board_composition_root_1 = __nccwpck_require__(17798); +const actor_authorization_composition_root_1 = __nccwpck_require__(43758); +const shared_capability_port_binding_1 = __nccwpck_require__(20918); +const lifecycle_capability_port_binding_1 = __nccwpck_require__(28822); function createIssueUseCaseCompositionRoot(binding) { const issueMetadata = new issue_metadata_repository_1.IssueMetadataRepository((0, github_issue_client_factory_1.createIssueMetadataClient)(), (0, github_project_client_factory_1.createGraphqlTransportClient)()); const issueContent = new issue_content_repository_1.IssueContentRepository((0, github_issue_client_factory_1.createIssueContentClient)()); @@ -82227,7 +82221,7 @@ function createIssueUseCaseCompositionRoot(binding) { /***/ }), -/***/ 85785: +/***/ 28822: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -82248,7 +82242,7 @@ exports.bindPullRequestIssueLink = bindPullRequestIssueLink; exports.bindIssueLabels = bindIssueLabels; exports.bindPullRequestHeadSha = bindPullRequestHeadSha; exports.bindPullRequestDescription = bindPullRequestDescription; -const project_detail_1 = __nccwpck_require__(33428); +const project_detail_1 = __nccwpck_require__(74263); function bindActorAuthorization(port, binding) { return Object.freeze({ isActorAllowedToModifyFiles: (actor) => port.isActorAllowedToModifyFiles(binding.owner, binding.repository, actor, binding.token), @@ -82350,17 +82344,17 @@ function toProjectDetail(project) { /***/ }), -/***/ 34760: +/***/ 36426: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createLocalActionCompositionRoot = createLocalActionCompositionRoot; -const resolve_message_catalog_use_case_1 = __nccwpck_require__(99961); -const git_cli_repository_1 = __nccwpck_require__(26331); -const project_board_composition_root_1 = __nccwpck_require__(37194); -const agent_capability_composition_root_1 = __nccwpck_require__(85079); +const resolve_message_catalog_use_case_1 = __nccwpck_require__(53803); +const git_cli_repository_1 = __nccwpck_require__(72119); +const project_board_composition_root_1 = __nccwpck_require__(17798); +const agent_capability_composition_root_1 = __nccwpck_require__(7753); /** * Owns the concrete dependencies shared by the local action lifecycle. * Keeping them in one root preserves the project-board query/command scope and @@ -82378,7 +82372,7 @@ function createLocalActionCompositionRoot() { /***/ }), -/***/ 4706: +/***/ 7473: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -82389,71 +82383,71 @@ exports.createIssueCommentUseCaseCompositionRoot = createIssueCommentUseCaseComp exports.createPullRequestReviewCommentUseCaseCompositionRoot = createPullRequestReviewCommentUseCaseCompositionRoot; exports.createCommitUseCaseCompositionRoot = createCommitUseCaseCompositionRoot; exports.createMainRunRouteCompositionRoot = createMainRunRouteCompositionRoot; -const commit_use_case_1 = __nccwpck_require__(28001); -const issue_comment_use_case_1 = __nccwpck_require__(72042); -const pull_request_review_comment_use_case_1 = __nccwpck_require__(29415); -const single_action_use_case_1 = __nccwpck_require__(73572); -const create_release_use_case_1 = __nccwpck_require__(25258); -const create_tag_use_case_1 = __nccwpck_require__(22120); -const publish_github_action_use_case_1 = __nccwpck_require__(68891); -const publish_issue_comment_use_case_1 = __nccwpck_require__(61313); -const recommend_steps_use_case_1 = __nccwpck_require__(73746); -const check_changes_issue_size_use_case_1 = __nccwpck_require__(28356); -const bugbot_autofix_use_case_1 = __nccwpck_require__(45446); -const detect_bugbot_fix_intent_use_case_1 = __nccwpck_require__(76234); -const dismiss_bugbot_findings_use_case_1 = __nccwpck_require__(37685); -const remember_bugbot_rule_use_case_1 = __nccwpck_require__(17437); -const detect_potential_problems_use_case_1 = __nccwpck_require__(6287); -const notify_new_commit_on_issue_use_case_1 = __nccwpck_require__(33276); -const user_request_use_case_1 = __nccwpck_require__(19004); -const think_use_case_1 = __nccwpck_require__(89255); -const check_issue_comment_language_use_case_1 = __nccwpck_require__(93152); -const check_pull_request_comment_language_use_case_1 = __nccwpck_require__(21729); -const comment_language_translation_workflow_1 = __nccwpck_require__(72770); -const branch_compare_repository_1 = __nccwpck_require__(95859); -const repository_release_publication_repository_1 = __nccwpck_require__(42075); -const repository_tag_repository_1 = __nccwpck_require__(58717); -const git_commit_adapter_1 = __nccwpck_require__(18606); -const bound_bugbot_git_mutation_adapter_1 = __nccwpck_require__(51520); -const actor_authorization_composition_root_1 = __nccwpck_require__(233); -const agent_capability_composition_root_1 = __nccwpck_require__(85079); -const authenticated_user_composition_root_1 = __nccwpck_require__(33885); -const bugbot_composition_root_1 = __nccwpck_require__(67395); -const check_progress_composition_root_1 = __nccwpck_require__(21531); -const github_branch_client_factory_1 = __nccwpck_require__(30144); -const github_pull_request_client_factory_1 = __nccwpck_require__(9068); -const github_release_client_factory_1 = __nccwpck_require__(76706); -const initial_setup_composition_root_1 = __nccwpck_require__(84138); -const issue_content_composition_root_1 = __nccwpck_require__(62255); -const issue_interaction_composition_root_1 = __nccwpck_require__(92503); -const issue_labels_composition_root_1 = __nccwpck_require__(34780); -const issue_use_case_composition_root_1 = __nccwpck_require__(43022); -const pull_request_use_case_composition_root_1 = __nccwpck_require__(70636); -const organization_members_composition_root_1 = __nccwpck_require__(50603); -const update_pull_request_description_use_case_1 = __nccwpck_require__(75089); -const pull_request_lifecycle_repository_1 = __nccwpck_require__(24189); -const issue_inactivity_composition_root_1 = __nccwpck_require__(74914); -const github_project_client_factory_1 = __nccwpck_require__(23691); -const branch_dependency_repository_1 = __nccwpck_require__(9627); -const branch_sync_workspace_adapter_1 = __nccwpck_require__(81849); -const observe_branch_sync_use_case_1 = __nccwpck_require__(84542); -const sync_branch_use_case_1 = __nccwpck_require__(392); -const deployment_orchestration_use_case_1 = __nccwpck_require__(36850); -const github_deployment_git_repository_1 = __nccwpck_require__(85886); -const github_managed_pull_request_repository_1 = __nccwpck_require__(96483); -const github_target_merge_capabilities_inspector_1 = __nccwpck_require__(55527); -const deployment_continuation_repository_1 = __nccwpck_require__(77509); -const deployment_presentation_repository_1 = __nccwpck_require__(91985); -const deployment_state_repository_1 = __nccwpck_require__(3182); -const octokit_deployment_adapter_1 = __nccwpck_require__(46819); -const workflow_dispatch_repository_1 = __nccwpck_require__(29509); -const github_workflow_client_factory_1 = __nccwpck_require__(29839); +const commit_use_case_1 = __nccwpck_require__(84014); +const issue_comment_use_case_1 = __nccwpck_require__(14502); +const pull_request_review_comment_use_case_1 = __nccwpck_require__(94097); +const single_action_use_case_1 = __nccwpck_require__(73840); +const create_release_use_case_1 = __nccwpck_require__(68781); +const create_tag_use_case_1 = __nccwpck_require__(27977); +const publish_github_action_use_case_1 = __nccwpck_require__(31153); +const publish_issue_comment_use_case_1 = __nccwpck_require__(23758); +const recommend_steps_use_case_1 = __nccwpck_require__(33526); +const check_changes_issue_size_use_case_1 = __nccwpck_require__(75384); +const bugbot_autofix_use_case_1 = __nccwpck_require__(92886); +const detect_bugbot_fix_intent_use_case_1 = __nccwpck_require__(50385); +const dismiss_bugbot_findings_use_case_1 = __nccwpck_require__(8677); +const remember_bugbot_rule_use_case_1 = __nccwpck_require__(83711); +const detect_potential_problems_use_case_1 = __nccwpck_require__(65545); +const notify_new_commit_on_issue_use_case_1 = __nccwpck_require__(77749); +const user_request_use_case_1 = __nccwpck_require__(39633); +const think_use_case_1 = __nccwpck_require__(25099); +const check_issue_comment_language_use_case_1 = __nccwpck_require__(34670); +const check_pull_request_comment_language_use_case_1 = __nccwpck_require__(5988); +const comment_language_translation_workflow_1 = __nccwpck_require__(78212); +const branch_compare_repository_1 = __nccwpck_require__(81122); +const repository_release_publication_repository_1 = __nccwpck_require__(9204); +const repository_tag_repository_1 = __nccwpck_require__(83842); +const git_commit_adapter_1 = __nccwpck_require__(89443); +const bound_bugbot_git_mutation_adapter_1 = __nccwpck_require__(85401); +const actor_authorization_composition_root_1 = __nccwpck_require__(43758); +const agent_capability_composition_root_1 = __nccwpck_require__(7753); +const authenticated_user_composition_root_1 = __nccwpck_require__(55549); +const bugbot_composition_root_1 = __nccwpck_require__(40733); +const check_progress_composition_root_1 = __nccwpck_require__(25949); +const github_branch_client_factory_1 = __nccwpck_require__(32112); +const github_pull_request_client_factory_1 = __nccwpck_require__(46236); +const github_release_client_factory_1 = __nccwpck_require__(66236); +const initial_setup_composition_root_1 = __nccwpck_require__(84423); +const issue_content_composition_root_1 = __nccwpck_require__(82808); +const issue_interaction_composition_root_1 = __nccwpck_require__(33777); +const issue_labels_composition_root_1 = __nccwpck_require__(94185); +const issue_use_case_composition_root_1 = __nccwpck_require__(97844); +const pull_request_use_case_composition_root_1 = __nccwpck_require__(75176); +const organization_members_composition_root_1 = __nccwpck_require__(17705); +const update_pull_request_description_use_case_1 = __nccwpck_require__(10016); +const pull_request_lifecycle_repository_1 = __nccwpck_require__(57177); +const issue_inactivity_composition_root_1 = __nccwpck_require__(36937); +const github_project_client_factory_1 = __nccwpck_require__(63165); +const branch_dependency_repository_1 = __nccwpck_require__(78769); +const branch_sync_workspace_adapter_1 = __nccwpck_require__(51697); +const observe_branch_sync_use_case_1 = __nccwpck_require__(34342); +const sync_branch_use_case_1 = __nccwpck_require__(43725); +const deployment_orchestration_use_case_1 = __nccwpck_require__(67132); +const github_deployment_git_repository_1 = __nccwpck_require__(35315); +const github_managed_pull_request_repository_1 = __nccwpck_require__(57536); +const github_target_merge_capabilities_inspector_1 = __nccwpck_require__(46950); +const deployment_continuation_repository_1 = __nccwpck_require__(26781); +const deployment_presentation_repository_1 = __nccwpck_require__(86534); +const deployment_state_repository_1 = __nccwpck_require__(59536); +const octokit_deployment_adapter_1 = __nccwpck_require__(15246); +const workflow_dispatch_repository_1 = __nccwpck_require__(70051); +const github_workflow_client_factory_1 = __nccwpck_require__(7654); const node_crypto_1 = __nccwpck_require__(6005); -const resolve_message_catalog_use_case_1 = __nccwpck_require__(99961); -const shared_capability_port_binding_1 = __nccwpck_require__(47399); -const lifecycle_capability_port_binding_1 = __nccwpck_require__(85785); -const lifecycle_capability_port_binding_2 = __nccwpck_require__(85785); -const push_single_action_capability_port_binding_1 = __nccwpck_require__(49417); +const resolve_message_catalog_use_case_1 = __nccwpck_require__(53803); +const shared_capability_port_binding_1 = __nccwpck_require__(20918); +const lifecycle_capability_port_binding_1 = __nccwpck_require__(28822); +const lifecycle_capability_port_binding_2 = __nccwpck_require__(28822); +const push_single_action_capability_port_binding_1 = __nccwpck_require__(54110); function createDetectPotentialProblemsUseCase(binding) { const bugbot = (0, bugbot_composition_root_1.createBugbotCompositionRoot)(binding); return new detect_potential_problems_use_case_1.DetectPotentialProblemsUseCase((0, agent_capability_composition_root_1.createFindingsQueryPort)(), bugbot.scm, bugbot.telemetry, new resolve_message_catalog_use_case_1.ResolveMessageCatalogUseCase((0, agent_capability_composition_root_1.createLanguageQueryPort)())); @@ -82570,15 +82564,15 @@ function bugbotBinding(execution) { /***/ }), -/***/ 50603: +/***/ 17705: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createOrganizationMembersCompositionRoot = createOrganizationMembersCompositionRoot; -const github_identity_client_factory_1 = __nccwpck_require__(93081); -const organization_members_repository_1 = __nccwpck_require__(845); +const github_identity_client_factory_1 = __nccwpck_require__(17930); +const organization_members_repository_1 = __nccwpck_require__(14553); function createOrganizationMembersCompositionRoot() { return new organization_members_repository_1.OrganizationMembersRepository((0, github_identity_client_factory_1.createOrganizationMembersClient)()); } @@ -82586,17 +82580,17 @@ function createOrganizationMembersCompositionRoot() { /***/ }), -/***/ 37194: +/***/ 17798: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createProjectBoardCompositionRoot = createProjectBoardCompositionRoot; -const github_project_client_factory_1 = __nccwpck_require__(23691); -const project_board_command_repository_1 = __nccwpck_require__(98952); -const project_board_link_repository_1 = __nccwpck_require__(79285); -const project_board_query_repository_1 = __nccwpck_require__(97301); +const github_project_client_factory_1 = __nccwpck_require__(63165); +const project_board_command_repository_1 = __nccwpck_require__(76220); +const project_board_link_repository_1 = __nccwpck_require__(5367); +const project_board_query_repository_1 = __nccwpck_require__(17726); function createProjectBoardCompositionRoot() { const query = new project_board_query_repository_1.ProjectBoardQueryRepository((0, github_project_client_factory_1.createOwnerTypeClient)(), (0, github_project_client_factory_1.createGraphqlTransportClient)()); return { @@ -82609,15 +82603,15 @@ function createProjectBoardCompositionRoot() { /***/ }), -/***/ 72651: +/***/ 39876: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createPullRequestReviewerCompositionRoot = createPullRequestReviewerCompositionRoot; -const pull_request_reviewer_repository_1 = __nccwpck_require__(13779); -const github_pull_request_client_factory_1 = __nccwpck_require__(9068); +const pull_request_reviewer_repository_1 = __nccwpck_require__(45908); +const github_pull_request_client_factory_1 = __nccwpck_require__(46236); function createPullRequestReviewerCompositionRoot() { return new pull_request_reviewer_repository_1.PullRequestReviewerRepository((0, github_pull_request_client_factory_1.createPullRequestReviewerClient)()); } @@ -82625,14 +82619,14 @@ function createPullRequestReviewerCompositionRoot() { /***/ }), -/***/ 24: +/***/ 23969: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.composePullRequestUseCase = composePullRequestUseCase; -const pull_request_use_case_1 = __nccwpck_require__(27259); +const pull_request_use_case_1 = __nccwpck_require__(93567); function composePullRequestUseCase(...dependencies) { return new pull_request_use_case_1.PullRequestUseCase(...dependencies); } @@ -82640,45 +82634,45 @@ function composePullRequestUseCase(...dependencies) { /***/ }), -/***/ 70636: +/***/ 75176: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createPullRequestUseCaseCompositionRoot = createPullRequestUseCaseCompositionRoot; -const github_issue_client_factory_1 = __nccwpck_require__(95883); -const github_project_client_factory_1 = __nccwpck_require__(23691); -const github_pull_request_client_factory_1 = __nccwpck_require__(9068); -const update_pull_request_description_use_case_1 = __nccwpck_require__(75089); -const update_title_use_case_1 = __nccwpck_require__(20556); -const assign_members_to_issue_use_case_1 = __nccwpck_require__(55523); -const assign_reviewers_to_issue_use_case_1 = __nccwpck_require__(80174); -const close_issue_after_merging_use_case_1 = __nccwpck_require__(46753); -const check_priority_pull_request_size_use_case_1 = __nccwpck_require__(12738); -const link_pull_request_issue_use_case_1 = __nccwpck_require__(38259); -const link_pull_request_project_use_case_1 = __nccwpck_require__(57169); -const sync_size_and_progress_labels_from_issue_to_pr_use_case_1 = __nccwpck_require__(89085); -const agent_capability_composition_root_1 = __nccwpck_require__(85079); -const issue_assignment_repository_1 = __nccwpck_require__(75023); -const issue_closure_repository_1 = __nccwpck_require__(23231); -const issue_content_repository_1 = __nccwpck_require__(2313); -const issue_label_repository_1 = __nccwpck_require__(45725); -const issue_lifecycle_repository_1 = __nccwpck_require__(8346); -const issue_metadata_repository_1 = __nccwpck_require__(11333); -const issue_title_repository_1 = __nccwpck_require__(10121); -const pull_request_lifecycle_repository_1 = __nccwpck_require__(24189); -const pull_request_use_case_composition_1 = __nccwpck_require__(24); -const pull_request_reviewer_composition_root_1 = __nccwpck_require__(72651); -const organization_members_composition_root_1 = __nccwpck_require__(50603); -const project_board_composition_root_1 = __nccwpck_require__(37194); -const timer_delay_adapter_1 = __nccwpck_require__(71942); -const detect_potential_problems_use_case_1 = __nccwpck_require__(6287); -const resolve_message_catalog_use_case_1 = __nccwpck_require__(99961); -const bugbot_composition_root_1 = __nccwpck_require__(67395); -const actor_authorization_composition_root_1 = __nccwpck_require__(233); -const shared_capability_port_binding_1 = __nccwpck_require__(47399); -const lifecycle_capability_port_binding_1 = __nccwpck_require__(85785); +const github_issue_client_factory_1 = __nccwpck_require__(15161); +const github_project_client_factory_1 = __nccwpck_require__(63165); +const github_pull_request_client_factory_1 = __nccwpck_require__(46236); +const update_pull_request_description_use_case_1 = __nccwpck_require__(10016); +const update_title_use_case_1 = __nccwpck_require__(21376); +const assign_members_to_issue_use_case_1 = __nccwpck_require__(18189); +const assign_reviewers_to_issue_use_case_1 = __nccwpck_require__(1093); +const close_issue_after_merging_use_case_1 = __nccwpck_require__(88705); +const check_priority_pull_request_size_use_case_1 = __nccwpck_require__(61696); +const link_pull_request_issue_use_case_1 = __nccwpck_require__(64280); +const link_pull_request_project_use_case_1 = __nccwpck_require__(42469); +const sync_size_and_progress_labels_from_issue_to_pr_use_case_1 = __nccwpck_require__(37698); +const agent_capability_composition_root_1 = __nccwpck_require__(7753); +const issue_assignment_repository_1 = __nccwpck_require__(7223); +const issue_closure_repository_1 = __nccwpck_require__(80674); +const issue_content_repository_1 = __nccwpck_require__(43338); +const issue_label_repository_1 = __nccwpck_require__(27825); +const issue_lifecycle_repository_1 = __nccwpck_require__(44023); +const issue_metadata_repository_1 = __nccwpck_require__(37674); +const issue_title_repository_1 = __nccwpck_require__(89116); +const pull_request_lifecycle_repository_1 = __nccwpck_require__(57177); +const pull_request_use_case_composition_1 = __nccwpck_require__(23969); +const pull_request_reviewer_composition_root_1 = __nccwpck_require__(39876); +const organization_members_composition_root_1 = __nccwpck_require__(17705); +const project_board_composition_root_1 = __nccwpck_require__(17798); +const timer_delay_adapter_1 = __nccwpck_require__(25288); +const detect_potential_problems_use_case_1 = __nccwpck_require__(65545); +const resolve_message_catalog_use_case_1 = __nccwpck_require__(53803); +const bugbot_composition_root_1 = __nccwpck_require__(40733); +const actor_authorization_composition_root_1 = __nccwpck_require__(43758); +const shared_capability_port_binding_1 = __nccwpck_require__(20918); +const lifecycle_capability_port_binding_1 = __nccwpck_require__(28822); function createPullRequestUseCaseCompositionRoot(binding) { const issueLifecycle = new issue_lifecycle_repository_1.IssueLifecycleRepository((0, github_issue_client_factory_1.createIssueLifecycleClient)()); const issueContent = new issue_content_repository_1.IssueContentRepository((0, github_issue_client_factory_1.createIssueContentClient)()); @@ -82714,7 +82708,7 @@ function createPullRequestUseCaseCompositionRoot(binding) { /***/ }), -/***/ 49417: +/***/ 54110: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -82938,7 +82932,7 @@ function bindSetupRemoteConfiguration(port, binding) { /***/ }), -/***/ 69084: +/***/ 45203: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -82946,13 +82940,13 @@ function bindSetupRemoteConfiguration(port, binding) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createSetupCredentialsUseCase = createSetupCredentialsUseCase; exports.createSetupRemoteConfigurationReadPort = createSetupRemoteConfigurationReadPort; -const setup_credentials_use_case_1 = __nccwpck_require__(67438); -const setup_credential_validation_adapter_1 = __nccwpck_require__(47020); -const repository_variables_repository_1 = __nccwpck_require__(28493); -const github_identity_client_factory_1 = __nccwpck_require__(93081); -const setup_remote_credential_health_adapter_1 = __nccwpck_require__(1489); -const octokit_credential_health_adapter_1 = __nccwpck_require__(41760); -const setup_token_permissions_composition_root_1 = __nccwpck_require__(64132); +const setup_credentials_use_case_1 = __nccwpck_require__(82634); +const setup_credential_validation_adapter_1 = __nccwpck_require__(23702); +const repository_variables_repository_1 = __nccwpck_require__(73307); +const github_identity_client_factory_1 = __nccwpck_require__(17930); +const setup_remote_credential_health_adapter_1 = __nccwpck_require__(37570); +const octokit_credential_health_adapter_1 = __nccwpck_require__(62696); +const setup_token_permissions_composition_root_1 = __nccwpck_require__(43759); function createSetupCredentialsUseCase(prompt, permissionPresenter, options = {}) { const secretNames = new repository_variables_repository_1.RepositorySecretNamesQueryRepository((0, github_identity_client_factory_1.createRepositoryVariablesClient)()); return new setup_credentials_use_case_1.SetupCredentialsUseCase(prompt, new setup_credential_validation_adapter_1.SetupCredentialValidationAdapter(), secretNames, options.allowPreApplyHealthWorkflow === false @@ -82968,7 +82962,7 @@ function createSetupRemoteConfigurationReadPort() { /***/ }), -/***/ 56360: +/***/ 90620: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -82976,19 +82970,19 @@ function createSetupRemoteConfigurationReadPort() { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createSetupMergeQueueReadinessUseCase = createSetupMergeQueueReadinessUseCase; exports.createSetupDoctorUseCase = createSetupDoctorUseCase; -const doctor_use_case_1 = __nccwpck_require__(87328); -const setup_credential_validation_adapter_1 = __nccwpck_require__(47020); -const repository_variables_repository_1 = __nccwpck_require__(28493); -const github_identity_client_factory_1 = __nccwpck_require__(93081); -const setup_workspace_adapter_1 = __nccwpck_require__(5729); -const setup_remote_credential_health_adapter_1 = __nccwpck_require__(1489); -const octokit_credential_health_adapter_1 = __nccwpck_require__(41760); -const github_target_merge_capabilities_inspector_1 = __nccwpck_require__(55527); -const octokit_deployment_adapter_1 = __nccwpck_require__(46819); -const merge_queue_readiness_use_case_1 = __nccwpck_require__(9890); -const resolve_message_catalog_use_case_1 = __nccwpck_require__(99961); -const agent_capability_composition_root_1 = __nccwpck_require__(85079); -const setup_approval_readiness_adapter_1 = __nccwpck_require__(78572); +const doctor_use_case_1 = __nccwpck_require__(39); +const setup_credential_validation_adapter_1 = __nccwpck_require__(23702); +const repository_variables_repository_1 = __nccwpck_require__(73307); +const github_identity_client_factory_1 = __nccwpck_require__(17930); +const setup_workspace_adapter_1 = __nccwpck_require__(23376); +const setup_remote_credential_health_adapter_1 = __nccwpck_require__(37570); +const octokit_credential_health_adapter_1 = __nccwpck_require__(62696); +const github_target_merge_capabilities_inspector_1 = __nccwpck_require__(46950); +const octokit_deployment_adapter_1 = __nccwpck_require__(15246); +const merge_queue_readiness_use_case_1 = __nccwpck_require__(14236); +const resolve_message_catalog_use_case_1 = __nccwpck_require__(53803); +const agent_capability_composition_root_1 = __nccwpck_require__(7753); +const setup_approval_readiness_adapter_1 = __nccwpck_require__(93230); function createSetupMergeQueueReadinessUseCase(catalogResolver = new resolve_message_catalog_use_case_1.ResolveMessageCatalogUseCase((0, agent_capability_composition_root_1.createLanguageQueryPort)())) { return new merge_queue_readiness_use_case_1.SetupMergeQueueReadinessUseCase(new github_target_merge_capabilities_inspector_1.GithubTargetMergeCapabilitiesInspector(new octokit_deployment_adapter_1.OctokitDeploymentClientAdapter()), catalogResolver); } @@ -83009,16 +83003,16 @@ function createSetupDoctorUseCase() { /***/ }), -/***/ 64132: +/***/ 43759: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createSetupTokenPermissionsUseCase = createSetupTokenPermissionsUseCase; -const setup_token_permissions_use_case_1 = __nccwpck_require__(11797); -const setup_credential_validation_adapter_1 = __nccwpck_require__(47020); -const setup_token_permission_query_adapter_1 = __nccwpck_require__(67758); +const setup_token_permissions_use_case_1 = __nccwpck_require__(64888); +const setup_credential_validation_adapter_1 = __nccwpck_require__(23702); +const setup_token_permission_query_adapter_1 = __nccwpck_require__(90378); function createSetupTokenPermissionsUseCase() { return new setup_token_permissions_use_case_1.SetupTokenPermissionsUseCase(new setup_credential_validation_adapter_1.SetupCredentialValidationAdapter(), new setup_token_permission_query_adapter_1.SetupTokenPermissionQueryAdapter()); } @@ -83026,7 +83020,7 @@ function createSetupTokenPermissionsUseCase() { /***/ }), -/***/ 47399: +/***/ 20918: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -83040,7 +83034,7 @@ exports.bindIssueCommentQuery = bindIssueCommentQuery; exports.bindIssueCommentUpdate = bindIssueCommentUpdate; exports.bindIssueTitle = bindIssueTitle; exports.bindProjectContent = bindProjectContent; -const project_detail_1 = __nccwpck_require__(33428); +const project_detail_1 = __nccwpck_require__(74263); function bindPublicationSourceQuery(port, binding) { return Object.freeze({ getBranchHeadSha: (branch) => port.getBranchHeadSha(binding.owner, binding.repository, branch, binding.token), @@ -83092,20 +83086,20 @@ function toProjectDetail(project) { /***/ }), -/***/ 21598: +/***/ 57010: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createWaitForPreviousWorkflowRunsUseCase = createWaitForPreviousWorkflowRunsUseCase; -const wait_for_previous_workflow_runs_use_case_1 = __nccwpck_require__(38301); -const active_previous_workflow_runs_repository_1 = __nccwpck_require__(40941); -const timer_workflow_polling_delay_adapter_1 = __nccwpck_require__(10339); -const logger_workflow_polling_observer_adapter_1 = __nccwpck_require__(52883); -const system_workflow_queue_clock_adapter_1 = __nccwpck_require__(49664); -const system_workflow_polling_random_adapter_1 = __nccwpck_require__(32679); -const github_workflow_client_factory_1 = __nccwpck_require__(29839); +const wait_for_previous_workflow_runs_use_case_1 = __nccwpck_require__(64317); +const active_previous_workflow_runs_repository_1 = __nccwpck_require__(42351); +const timer_workflow_polling_delay_adapter_1 = __nccwpck_require__(283); +const logger_workflow_polling_observer_adapter_1 = __nccwpck_require__(46177); +const system_workflow_queue_clock_adapter_1 = __nccwpck_require__(89437); +const system_workflow_polling_random_adapter_1 = __nccwpck_require__(86649); +const github_workflow_client_factory_1 = __nccwpck_require__(7654); function createWaitForPreviousWorkflowRunsUseCase(token) { const client = (0, github_workflow_client_factory_1.createWorkflowRunsClient)().getClient(token); const delayPort = new timer_workflow_polling_delay_adapter_1.TimerWorkflowPollingDelayAdapter(); @@ -83116,7 +83110,7 @@ function createWaitForPreviousWorkflowRunsUseCase(token) { /***/ }), -/***/ 50183: +/***/ 83645: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -83263,7 +83257,7 @@ function isWithin(root, target) { /***/ }), -/***/ 16535: +/***/ 1906: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -83289,7 +83283,7 @@ function buildGitAuthenticationEnvironment(token, environment = process.env) { /***/ }), -/***/ 18606: +/***/ 89443: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -83329,9 +83323,9 @@ var __importStar = (this && this.__importStar) || (function () { })(); Object.defineProperty(exports, "__esModule", ({ value: true })); exports.GitCommitAdapter = void 0; -const exec = __importStar(__nccwpck_require__(18538)); -const git_authentication_environment_1 = __nccwpck_require__(16535); -const untrusted_command_environment_1 = __nccwpck_require__(2304); +const exec = __importStar(__nccwpck_require__(36086)); +const git_authentication_environment_1 = __nccwpck_require__(1906); +const untrusted_command_environment_1 = __nccwpck_require__(57156); class GitCommitAdapter { constructor(executeCommand = (program, args, options) => options ? exec.exec(program, args, { @@ -83396,7 +83390,7 @@ exports.GitCommitAdapter = GitCommitAdapter; /***/ }), -/***/ 19008: +/***/ 33451: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -83453,14 +83447,14 @@ function normalizeHttpsServerUrl(value) { /***/ }), -/***/ 77889: +/***/ 95215: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.OctokitBranchComparisonClientAdapter = exports.OctokitBranchClientAdapter = void 0; -const octokit_client_resolver_1 = __nccwpck_require__(54047); +const octokit_client_resolver_1 = __nccwpck_require__(91649); class OctokitBranchClientAdapter { getClient(token) { return (0, octokit_client_resolver_1.getOctokitClient)(token); } } @@ -83473,7 +83467,7 @@ exports.OctokitBranchComparisonClientAdapter = OctokitBranchComparisonClientAdap /***/ }), -/***/ 54047: +/***/ 91649: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -83513,7 +83507,7 @@ var __importStar = (this && this.__importStar) || (function () { })(); Object.defineProperty(exports, "__esModule", ({ value: true })); exports.getOctokitClient = getOctokitClient; -const github = __importStar(__nccwpck_require__(78227)); +const github = __importStar(__nccwpck_require__(87211)); function getOctokitClient(token) { return github.getOctokit(token); } @@ -83521,14 +83515,14 @@ function getOctokitClient(token) { /***/ }), -/***/ 41760: +/***/ 62696: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.OctokitCredentialHealthClientAdapter = void 0; -const octokit_client_resolver_1 = __nccwpck_require__(54047); +const octokit_client_resolver_1 = __nccwpck_require__(91649); class OctokitCredentialHealthClientAdapter { getClient(token) { return (0, octokit_client_resolver_1.getOctokitClient)(token); @@ -83539,14 +83533,14 @@ exports.OctokitCredentialHealthClientAdapter = OctokitCredentialHealthClientAdap /***/ }), -/***/ 46819: +/***/ 15246: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.OctokitDeploymentClientAdapter = void 0; -const octokit_client_resolver_1 = __nccwpck_require__(54047); +const octokit_client_resolver_1 = __nccwpck_require__(91649); class OctokitDeploymentClientAdapter { getClient(token) { return (0, octokit_client_resolver_1.getOctokitClient)(token); @@ -83557,14 +83551,14 @@ exports.OctokitDeploymentClientAdapter = OctokitDeploymentClientAdapter; /***/ }), -/***/ 29996: +/***/ 39787: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.OctokitOwnerTypeClientAdapter = exports.OctokitOrganizationMembersClientAdapter = exports.OctokitActorAuthorizationClientAdapter = exports.OctokitAuthenticatedUserClientAdapter = void 0; -const octokit_client_resolver_1 = __nccwpck_require__(54047); +const octokit_client_resolver_1 = __nccwpck_require__(91649); class OctokitAuthenticatedUserClientAdapter { getClient(token) { return (0, octokit_client_resolver_1.getOctokitClient)(token); } } @@ -83585,14 +83579,14 @@ exports.OctokitOwnerTypeClientAdapter = OctokitOwnerTypeClientAdapter; /***/ }), -/***/ 77179: +/***/ 73387: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.OctokitIssueTitleClientAdapter = exports.OctokitIssueMetadataClientAdapter = exports.OctokitIssueInactivityClientAdapter = exports.OctokitIssueLifecycleClientAdapter = exports.OctokitIssueLabelsClientAdapter = exports.OctokitIssueLabelProvisioningClientAdapter = exports.OctokitIssueContentClientAdapter = exports.OctokitIssueAssignmentClientAdapter = void 0; -const octokit_client_resolver_1 = __nccwpck_require__(54047); +const octokit_client_resolver_1 = __nccwpck_require__(91649); class OctokitIssueAssignmentClientAdapter { getClient(token) { return (0, octokit_client_resolver_1.getOctokitClient)(token); } } @@ -83629,14 +83623,14 @@ exports.OctokitIssueTitleClientAdapter = OctokitIssueTitleClientAdapter; /***/ }), -/***/ 68505: +/***/ 39191: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.OctokitGraphqlTransportClientAdapter = void 0; -const octokit_client_resolver_1 = __nccwpck_require__(54047); +const octokit_client_resolver_1 = __nccwpck_require__(91649); class OctokitGraphqlTransportClientAdapter { getClient(token) { return (0, octokit_client_resolver_1.getOctokitClient)(token); } } @@ -83645,14 +83639,14 @@ exports.OctokitGraphqlTransportClientAdapter = OctokitGraphqlTransportClientAdap /***/ }), -/***/ 1397: +/***/ 38632: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.OctokitPullRequestReviewCommentClientAdapter = exports.OctokitPullRequestReviewerClientAdapter = exports.OctokitPullRequestLifecycleClientAdapter = exports.OctokitPullRequestChangesClientAdapter = void 0; -const octokit_client_resolver_1 = __nccwpck_require__(54047); +const octokit_client_resolver_1 = __nccwpck_require__(91649); class OctokitPullRequestChangesClientAdapter { getClient(token) { return (0, octokit_client_resolver_1.getOctokitClient)(token); @@ -83681,14 +83675,14 @@ exports.OctokitPullRequestReviewCommentClientAdapter = OctokitPullRequestReviewC /***/ }), -/***/ 5334: +/***/ 51480: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.OctokitReleaseClientAdapter = void 0; -const octokit_client_resolver_1 = __nccwpck_require__(54047); +const octokit_client_resolver_1 = __nccwpck_require__(91649); class OctokitReleaseClientAdapter { getClient(token) { return (0, octokit_client_resolver_1.getOctokitClient)(token); } } @@ -83697,14 +83691,14 @@ exports.OctokitReleaseClientAdapter = OctokitReleaseClientAdapter; /***/ }), -/***/ 81329: +/***/ 93883: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.OctokitRepositoryVariablesClientAdapter = void 0; -const octokit_client_resolver_1 = __nccwpck_require__(54047); +const octokit_client_resolver_1 = __nccwpck_require__(91649); class OctokitRepositoryVariablesClientAdapter { getClient(token) { return (0, octokit_client_resolver_1.getOctokitClient)(token); @@ -83715,14 +83709,14 @@ exports.OctokitRepositoryVariablesClientAdapter = OctokitRepositoryVariablesClie /***/ }), -/***/ 86719: +/***/ 36430: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.OctokitWorkflowDispatchClientAdapter = exports.OctokitWorkflowRunsClientAdapter = void 0; -const octokit_client_resolver_1 = __nccwpck_require__(54047); +const octokit_client_resolver_1 = __nccwpck_require__(91649); class OctokitWorkflowRunsClientAdapter { getClient(token) { return (0, octokit_client_resolver_1.getOctokitClient)(token); @@ -83739,7 +83733,7 @@ exports.OctokitWorkflowDispatchClientAdapter = OctokitWorkflowDispatchClientAdap /***/ }), -/***/ 96997: +/***/ 79506: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -83752,7 +83746,7 @@ exports.PROJECT_BOARD_ITEM_PAGE_LIMIT = 500; /***/ }), -/***/ 72762: +/***/ 56932: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -83760,7 +83754,7 @@ exports.PROJECT_BOARD_ITEM_PAGE_LIMIT = 500; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createLoggerAdapter = createLoggerAdapter; exports.createLogReportAdapter = createLogReportAdapter; -const logger_1 = __nccwpck_require__(91151); +const logger_1 = __nccwpck_require__(50135); /** Adapts the process/GitHub logger to the semantic application port. */ function createLoggerAdapter() { return { @@ -83785,14 +83779,14 @@ function createLogReportAdapter() { /***/ }), -/***/ 59844: +/***/ 36337: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.LoggerAgentExecutionObserverAdapter = void 0; -const logger_1 = __nccwpck_require__(91151); +const logger_1 = __nccwpck_require__(50135); class LoggerAgentExecutionObserverAdapter { observe(observation) { if (observation.state === 'completed' || observation.state === 'failed') { @@ -83807,14 +83801,14 @@ exports.LoggerAgentExecutionObserverAdapter = LoggerAgentExecutionObserverAdapte /***/ }), -/***/ 34685: +/***/ 55375: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.LoggerBugbotTelemetryAdapter = void 0; -const logging_ports_1 = __nccwpck_require__(6152); +const logging_ports_1 = __nccwpck_require__(73001); class LoggerBugbotTelemetryAdapter { publish(snapshot) { (0, logging_ports_1.logInfo)(`[bugbot.telemetry] ${JSON.stringify(snapshot)}`); @@ -83825,14 +83819,14 @@ exports.LoggerBugbotTelemetryAdapter = LoggerBugbotTelemetryAdapter; /***/ }), -/***/ 52883: +/***/ 46177: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.LoggerWorkflowPollingObserverAdapter = void 0; -const logger_1 = __nccwpck_require__(91151); +const logger_1 = __nccwpck_require__(50135); class LoggerWorkflowPollingObserverAdapter { noActivePreviousRuns() { (0, logger_1.logDebugInfo)('✅ No previous runs active. Continuing...'); @@ -83856,7 +83850,7 @@ exports.LoggerWorkflowPollingObserverAdapter = LoggerWorkflowPollingObserverAdap /***/ }), -/***/ 35849: +/***/ 71976: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -83901,14 +83895,14 @@ const os = __importStar(__nccwpck_require__(70612)); const path = __importStar(__nccwpck_require__(49411)); const node_child_process_1 = __nccwpck_require__(17718); const node_util_1 = __nccwpck_require__(47261); -const pre_branch_sdd_1 = __nccwpck_require__(34730); -const git_authentication_environment_1 = __nccwpck_require__(16535); +const pre_branch_sdd_1 = __nccwpck_require__(54078); +const git_authentication_environment_1 = __nccwpck_require__(1906); const runFile = (0, node_util_1.promisify)(node_child_process_1.execFile); const SHA = /^[a-f0-9]{40}$/i; const BRANCH = /^[a-zA-Z0-9][a-zA-Z0-9._/-]*$/; // The shared catalog validator is CommonJS so the setup CLI and bundled Action use identical rules. // eslint-disable-next-line @typescript-eslint/no-require-imports -const validator = __nccwpck_require__(29617); +const validator = __nccwpck_require__(47743); /** Isolates SDD validation in a detached temporary worktree before the linked branch is created. */ class PreBranchSddWorkspaceAdapter { constructor(repositoryRoot = process.cwd(), token = '') { @@ -84203,7 +84197,7 @@ function writeSpecFile(target, content, exists) { /***/ }), -/***/ 78572: +/***/ 93230: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -84243,10 +84237,10 @@ var __importStar = (this && this.__importStar) || (function () { })(); Object.defineProperty(exports, "__esModule", ({ value: true })); exports.GithubSetupApprovalReadinessAdapter = void 0; -const github = __importStar(__nccwpck_require__(78227)); +const github = __importStar(__nccwpck_require__(87211)); const node_fs_1 = __nccwpck_require__(87561); const node_path_1 = __nccwpck_require__(49411); -const setup_approval_workflow_1 = __nccwpck_require__(9512); +const setup_approval_workflow_1 = __nccwpck_require__(56189); class GithubSetupApprovalReadinessAdapter { async inspect(owner, repository, setupToken, configuration) { const octokit = github.getOctokit(setupToken); @@ -84337,7 +84331,7 @@ function array(value) { return Array.isArray(value) ? value : []; } /***/ }), -/***/ 47020: +/***/ 23702: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -84474,7 +84468,7 @@ function safeMessage(error) { /***/ }), -/***/ 56098: +/***/ 57550: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -84534,7 +84528,7 @@ exports.SetupGithubIdentityQueryAdapter = SetupGithubIdentityQueryAdapter; /***/ }), -/***/ 1489: +/***/ 37570: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -84576,9 +84570,9 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SetupRemoteCredentialHealthBootstrapAdapter = exports.SetupRemoteCredentialHealthQueryAdapter = void 0; const node_fs_1 = __nccwpck_require__(87561); const path = __importStar(__nccwpck_require__(49411)); -const setup_workflow_catalog_1 = __nccwpck_require__(24596); -const deployment_configuration_1 = __nccwpck_require__(22495); -const credential_health_workflow_visibility_1 = __nccwpck_require__(57628); +const setup_workflow_catalog_1 = __nccwpck_require__(37008); +const deployment_configuration_1 = __nccwpck_require__(5664); +const credential_health_workflow_visibility_1 = __nccwpck_require__(85071); const WORKFLOW_ID = setup_workflow_catalog_1.SETUP_CREDENTIAL_HEALTH_WORKFLOW_FILE; const INPUT_BY_SECRET = { PAT: 'check_pat', @@ -84834,16 +84828,16 @@ function readHealthWorkflow() { /***/ }), -/***/ 67758: +/***/ 90378: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SetupTokenPermissionQueryAdapter = void 0; -const github_error_policy_1 = __nccwpck_require__(58791); -const bounded_concurrency_policy_1 = __nccwpck_require__(35596); -const setup_token_permission_evidence_policy_1 = __nccwpck_require__(65640); +const github_error_policy_1 = __nccwpck_require__(26189); +const bounded_concurrency_policy_1 = __nccwpck_require__(50189); +const setup_token_permission_evidence_policy_1 = __nccwpck_require__(19750); const SETUP_PERMISSION_PROBE_CONCURRENCY = 4; const MAX_GITHUB_DEFAULT_BRANCH_LENGTH = 255; /** Maps safe GitHub reads to semantic permission evidence without test mutations. */ @@ -85141,16 +85135,16 @@ function repositoryRoot(owner, repository) { /***/ }), -/***/ 5729: +/***/ 23376: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SetupReconcileWorkspaceAdapter = exports.SetupDoctorWorkspaceQueryAdapter = exports.SetupWorkspaceMutationAdapter = void 0; -const setup_files_1 = __nccwpck_require__(59126); -const cli_context_1 = __nccwpck_require__(21307); -const repository_agent_guidance_1 = __nccwpck_require__(38445); +const setup_files_1 = __nccwpck_require__(30542); +const cli_context_1 = __nccwpck_require__(34760); +const repository_agent_guidance_1 = __nccwpck_require__(45689); class SetupWorkspaceMutationAdapter { prepare(selection) { const workspace = process.cwd(); @@ -85209,7 +85203,7 @@ exports.SetupReconcileWorkspaceAdapter = SetupReconcileWorkspaceAdapter; /***/ }), -/***/ 86457: +/***/ 62557: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -85226,7 +85220,7 @@ exports.SystemIssueInactivityClockAdapter = SystemIssueInactivityClockAdapter; /***/ }), -/***/ 32679: +/***/ 86649: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -85243,7 +85237,7 @@ exports.SystemWorkflowPollingRandomAdapter = SystemWorkflowPollingRandomAdapter; /***/ }), -/***/ 49664: +/***/ 89437: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -85260,7 +85254,7 @@ exports.SystemWorkflowQueueClockAdapter = SystemWorkflowQueueClockAdapter; /***/ }), -/***/ 20846: +/***/ 20015: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -85280,7 +85274,7 @@ exports.TimerBranchPropagationDelayAdapter = TimerBranchPropagationDelayAdapter; /***/ }), -/***/ 71942: +/***/ 25288: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -85297,7 +85291,7 @@ exports.TimerDelayAdapter = TimerDelayAdapter; /***/ }), -/***/ 10339: +/***/ 283: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -85314,7 +85308,7 @@ exports.TimerWorkflowPollingDelayAdapter = TimerWorkflowPollingDelayAdapter; /***/ }), -/***/ 2304: +/***/ 57156: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -85369,15 +85363,15 @@ function prepareUntrustedCommandEnvironment(source = process.env) { /***/ }), -/***/ 92540: +/***/ 93855: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.ContentInterface = void 0; -const logger_1 = __nccwpck_require__(91151); -const application_error_1 = __nccwpck_require__(75999); +const logger_1 = __nccwpck_require__(50135); +const application_error_1 = __nccwpck_require__(2965); class ContentInterface { constructor() { this.getContent = (description) => { @@ -85466,16 +85460,16 @@ exports.ContentInterface = ContentInterface; /***/ }), -/***/ 60608: +/***/ 58588: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.IssueContentInterface = void 0; -const logger_1 = __nccwpck_require__(91151); -const content_interface_1 = __nccwpck_require__(92540); -const application_error_1 = __nccwpck_require__(75999); +const logger_1 = __nccwpck_require__(50135); +const content_interface_1 = __nccwpck_require__(93855); +const application_error_1 = __nccwpck_require__(2965); class IssueContentInterface extends content_interface_1.ContentInterface { constructor(issueDescriptionPort) { super(); @@ -85512,18 +85506,18 @@ exports.IssueContentInterface = IssueContentInterface; /***/ }), -/***/ 40188: +/***/ 51068: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.ConfigurationHandler = void 0; -const config_1 = __nccwpck_require__(90450); -const logger_1 = __nccwpck_require__(91151); -const issue_content_interface_1 = __nccwpck_require__(60608); -const configuration_payload_policy_1 = __nccwpck_require__(58043); -const application_error_1 = __nccwpck_require__(75999); +const config_1 = __nccwpck_require__(98013); +const logger_1 = __nccwpck_require__(50135); +const issue_content_interface_1 = __nccwpck_require__(58588); +const configuration_payload_policy_1 = __nccwpck_require__(23509); +const application_error_1 = __nccwpck_require__(2965); class ConfigurationHandler extends issue_content_interface_1.IssueContentInterface { constructor() { super(...arguments); @@ -85559,14 +85553,14 @@ exports.ConfigurationHandler = ConfigurationHandler; /***/ }), -/***/ 58043: +/***/ 23509: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildConfigurationPayload = buildConfigurationPayload; -const config_1 = __nccwpck_require__(90450); +const config_1 = __nccwpck_require__(98013); function buildConfigurationPayload(execution, storedRaw) { const current = execution.currentConfiguration; const stored = parseStoredConfiguration(storedRaw); @@ -85616,7 +85610,7 @@ function mergeMissingValues(payload, stored) { /***/ }), -/***/ 49029: +/***/ 91606: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -85627,7 +85621,7 @@ exports.getAnswerIssueHelpPrompt = getAnswerIssueHelpPrompt; * Prompt for the initial reply when a user opens a question/help issue. * Filled by the prompt provider; use getAnswerIssueHelpPrompt(). */ -const fill_1 = __nccwpck_require__(2559); +const fill_1 = __nccwpck_require__(58865); const TEMPLATE = `The user has just opened a question/help issue. Provide a helpful initial response to their question or request below. Be concise and actionable. Write every human-readable sentence in {{targetLocale}} while preserving code identifiers, paths, refs, commands, and URLs verbatim. Return a JSON object with \`outputLocale\` set exactly to \`{{targetLocale}}\` and \`answer\` containing the Markdown response. @@ -85651,14 +85645,14 @@ function getAnswerIssueHelpPrompt(params) { /***/ }), -/***/ 84434: +/***/ 93843: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.getBranchSyncConflictsPrompt = getBranchSyncConflictsPrompt; -const fill_1 = __nccwpck_require__(2559); +const fill_1 = __nccwpck_require__(58865); const TEMPLATE = `You are resolving a merge that is already in progress in {{owner}}/{{repo}}. Parent branch: {{parentBranch}} @@ -85674,7 +85668,7 @@ function getBranchSyncConflictsPrompt(params) { /***/ }), -/***/ 56998: +/***/ 69433: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -85684,7 +85678,7 @@ exports.getBugbotPrompt = getBugbotPrompt; /** * Prompt for Bugbot detection (detect potential problems on push). */ -const fill_1 = __nccwpck_require__(2559); +const fill_1 = __nccwpck_require__(58865); const TEMPLATE = `You are analyzing the latest code changes for potential bugs and issues. Write every human-readable finding title, description, evidence, and suggestion in {{targetLocale}}. Preserve identifiers, code, symbols, paths, refs, commands, and URLs verbatim. Echo \`outputLocale\` exactly as \`{{targetLocale}}\`. @@ -85740,7 +85734,7 @@ function getBugbotPrompt(params) { /***/ }), -/***/ 37925: +/***/ 17929: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -85750,8 +85744,8 @@ exports.getBugbotFixPrompt = getBugbotFixPrompt; /** * Prompt for Bugbot autofix (fix selected findings in workspace). */ -const fill_1 = __nccwpck_require__(2559); -const untrusted_content_1 = __nccwpck_require__(67057); +const fill_1 = __nccwpck_require__(58865); +const untrusted_content_1 = __nccwpck_require__(12334); const TEMPLATE = `${untrusted_content_1.UNTRUSTED_CONTENT_POLICY} You are in the repository workspace. Your task is to fix the reported code findings (bugs, vulnerabilities, or quality issues) listed below, and only those. The user has explicitly requested these fixes. @@ -85790,7 +85784,7 @@ function getBugbotFixPrompt(params) { /***/ }), -/***/ 10399: +/***/ 56693: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -85800,7 +85794,7 @@ exports.getBugbotFixIntentPrompt = getBugbotFixIntentPrompt; /** * Prompt for detecting the action requested by a user comment. */ -const fill_1 = __nccwpck_require__(2559); +const fill_1 = __nccwpck_require__(58865); const TEMPLATE = `You are analyzing a user comment on an issue or pull request to classify the requested Copilot action. The available actions are: fix reported findings, apply a general repository change, run a read-only code review, or answer a question. {{projectContextInstruction}} @@ -85825,7 +85819,7 @@ function getBugbotFixIntentPrompt(params) { /***/ }), -/***/ 63425: +/***/ 39837: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -85833,7 +85827,7 @@ function getBugbotFixIntentPrompt(params) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.getAdaptCommentLanguagePrompt = getAdaptCommentLanguagePrompt; /** Builds the single, schema-constrained request adaptation prompt. */ -const fill_1 = __nccwpck_require__(2559); +const fill_1 = __nccwpck_require__(58865); const ADAPT_TEMPLATE = ` You adapt user-provided prose to {{locale}} for internal interpretation. @@ -85860,7 +85854,7 @@ function getAdaptCommentLanguagePrompt(params) { /***/ }), -/***/ 74623: +/***/ 30975: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -85870,7 +85864,7 @@ exports.getCheckProgressPrompt = getCheckProgressPrompt; /** * Prompt for assessing issue progress from branch diff (CheckProgressUseCase). */ -const fill_1 = __nccwpck_require__(2559); +const fill_1 = __nccwpck_require__(58865); const TEMPLATE = `You are in the repository workspace. Assess the progress of issue #{{issueNumber}} using the full diff between the base (parent) branch and the current branch. Write every human-readable sentence in {{targetLocale}}. Preserve code identifiers, paths, refs, commands, URLs, percentages, and JSON keys verbatim. Echo \`outputLocale\` exactly as \`{{targetLocale}}\`. @@ -85905,7 +85899,7 @@ function getCheckProgressPrompt(params) { /***/ }), -/***/ 32506: +/***/ 41306: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -85915,7 +85909,7 @@ exports.getCliDoPrompt = getCliDoPrompt; /** * Prompt for CLI "copilot do" command: project context + user prompt. */ -const fill_1 = __nccwpck_require__(2559); +const fill_1 = __nccwpck_require__(58865); const TEMPLATE = `{{projectContextInstruction}} {{userPrompt}}`; @@ -85926,7 +85920,7 @@ function getCliDoPrompt(params) { /***/ }), -/***/ 2559: +/***/ 58865: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -85937,7 +85931,7 @@ exports.fillTemplate = fillTemplate; * Replaces {{paramName}} placeholders in a template with values from params. * Missing keys are left as {{paramName}}. */ -const untrusted_content_1 = __nccwpck_require__(67057); +const untrusted_content_1 = __nccwpck_require__(12334); const UNTRUSTED_TEMPLATE_KEYS = new Set([ 'commentBody', 'description', @@ -85979,7 +85973,7 @@ function fillTemplate(template, params) { /***/ }), -/***/ 69518: +/***/ 71854: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -85991,40 +85985,40 @@ exports.getPrompt = getPrompt; * Prompt provider: one file per prompt, each exports a getter that fills the template with params. * Use getPrompt(name, params) for a generic call or import the typed getter (e.g. getAnswerIssueHelpPrompt). */ -const answer_issue_help_1 = __nccwpck_require__(49029); -const think_1 = __nccwpck_require__(43146); -const update_pull_request_description_1 = __nccwpck_require__(10063); -const user_request_1 = __nccwpck_require__(63103); -const recommend_steps_1 = __nccwpck_require__(69039); -const check_progress_1 = __nccwpck_require__(74623); -const check_comment_language_1 = __nccwpck_require__(63425); -const cli_do_1 = __nccwpck_require__(32506); -const bugbot_1 = __nccwpck_require__(56998); -const bugbot_fix_1 = __nccwpck_require__(37925); -const bugbot_fix_intent_1 = __nccwpck_require__(10399); -var fill_1 = __nccwpck_require__(2559); +const answer_issue_help_1 = __nccwpck_require__(91606); +const think_1 = __nccwpck_require__(18366); +const update_pull_request_description_1 = __nccwpck_require__(16030); +const user_request_1 = __nccwpck_require__(50929); +const recommend_steps_1 = __nccwpck_require__(49700); +const check_progress_1 = __nccwpck_require__(30975); +const check_comment_language_1 = __nccwpck_require__(39837); +const cli_do_1 = __nccwpck_require__(41306); +const bugbot_1 = __nccwpck_require__(69433); +const bugbot_fix_1 = __nccwpck_require__(17929); +const bugbot_fix_intent_1 = __nccwpck_require__(56693); +var fill_1 = __nccwpck_require__(58865); Object.defineProperty(exports, "fillTemplate", ({ enumerable: true, get: function () { return fill_1.fillTemplate; } })); -var answer_issue_help_2 = __nccwpck_require__(49029); +var answer_issue_help_2 = __nccwpck_require__(91606); Object.defineProperty(exports, "getAnswerIssueHelpPrompt", ({ enumerable: true, get: function () { return answer_issue_help_2.getAnswerIssueHelpPrompt; } })); -var think_2 = __nccwpck_require__(43146); +var think_2 = __nccwpck_require__(18366); Object.defineProperty(exports, "getThinkPrompt", ({ enumerable: true, get: function () { return think_2.getThinkPrompt; } })); -var update_pull_request_description_2 = __nccwpck_require__(10063); +var update_pull_request_description_2 = __nccwpck_require__(16030); Object.defineProperty(exports, "getUpdatePullRequestDescriptionPrompt", ({ enumerable: true, get: function () { return update_pull_request_description_2.getUpdatePullRequestDescriptionPrompt; } })); -var user_request_2 = __nccwpck_require__(63103); +var user_request_2 = __nccwpck_require__(50929); Object.defineProperty(exports, "getUserRequestPrompt", ({ enumerable: true, get: function () { return user_request_2.getUserRequestPrompt; } })); -var recommend_steps_2 = __nccwpck_require__(69039); +var recommend_steps_2 = __nccwpck_require__(49700); Object.defineProperty(exports, "getRecommendStepsPrompt", ({ enumerable: true, get: function () { return recommend_steps_2.getRecommendStepsPrompt; } })); -var check_progress_2 = __nccwpck_require__(74623); +var check_progress_2 = __nccwpck_require__(30975); Object.defineProperty(exports, "getCheckProgressPrompt", ({ enumerable: true, get: function () { return check_progress_2.getCheckProgressPrompt; } })); -var check_comment_language_2 = __nccwpck_require__(63425); +var check_comment_language_2 = __nccwpck_require__(39837); Object.defineProperty(exports, "getAdaptCommentLanguagePrompt", ({ enumerable: true, get: function () { return check_comment_language_2.getAdaptCommentLanguagePrompt; } })); -var cli_do_2 = __nccwpck_require__(32506); +var cli_do_2 = __nccwpck_require__(41306); Object.defineProperty(exports, "getCliDoPrompt", ({ enumerable: true, get: function () { return cli_do_2.getCliDoPrompt; } })); -var bugbot_2 = __nccwpck_require__(56998); +var bugbot_2 = __nccwpck_require__(69433); Object.defineProperty(exports, "getBugbotPrompt", ({ enumerable: true, get: function () { return bugbot_2.getBugbotPrompt; } })); -var bugbot_fix_2 = __nccwpck_require__(37925); +var bugbot_fix_2 = __nccwpck_require__(17929); Object.defineProperty(exports, "getBugbotFixPrompt", ({ enumerable: true, get: function () { return bugbot_fix_2.getBugbotFixPrompt; } })); -var bugbot_fix_intent_2 = __nccwpck_require__(10399); +var bugbot_fix_intent_2 = __nccwpck_require__(56693); Object.defineProperty(exports, "getBugbotFixIntentPrompt", ({ enumerable: true, get: function () { return bugbot_fix_intent_2.getBugbotFixIntentPrompt; } })); /** Known prompt names for getPrompt() */ exports.PROMPT_NAMES = { @@ -86067,14 +86061,14 @@ function getPrompt(name, params) { /***/ }), -/***/ 64005: +/***/ 22907: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.getLocalizeMessageCatalogPrompt = getLocalizeMessageCatalogPrompt; -const message_catalog_1 = __nccwpck_require__(27097); +const message_catalog_1 = __nccwpck_require__(5313); /** Builds a bounded request that translates prose values, never renderer structure. */ function getLocalizeMessageCatalogPrompt(params) { const pluralCategories = (0, message_catalog_1.catalogPluralCategories)(params.targetLocale); @@ -86093,7 +86087,7 @@ function getLocalizeMessageCatalogPrompt(params) { /***/ }), -/***/ 69039: +/***/ 49700: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -86103,7 +86097,7 @@ exports.getRecommendStepsPrompt = getRecommendStepsPrompt; /** * Prompt for recommending implementation steps from an issue (RecommendStepsUseCase). */ -const fill_1 = __nccwpck_require__(2559); +const fill_1 = __nccwpck_require__(58865); const TEMPLATE = `Based on the following issue description, produce a concise implementation plan. Return three to eight logically ordered steps (for example: contract, implementation, tests, and documentation). Each step needs a short action title and zero to two brief supporting details. Add one specific, verifiable acceptance criterion for the whole plan. Write every human-readable field in {{targetLocale}}. Preserve code identifiers, repository-relative paths, refs, and commands verbatim. Do not write Markdown or headings inside fields; the product owns presentation. Echo \`outputLocale\` exactly as \`{{targetLocale}}\`. @@ -86142,7 +86136,7 @@ function previousRecommendationInstruction(format) { /***/ }), -/***/ 43146: +/***/ 18366: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -86152,7 +86146,7 @@ exports.getThinkPrompt = getThinkPrompt; /** * Prompt for the Think use case (answer to @mention in issue/PR comment). */ -const fill_1 = __nccwpck_require__(2559); +const fill_1 = __nccwpck_require__(58865); const TEMPLATE = `You are a helpful assistant. Answer the following question concisely in {{targetLocale}}, using the context below when relevant. Format your answer in **markdown** (headings, lists, code blocks where useful) so it is easy to read. Do not include the question in your response. Preserve code identifiers, paths, refs, commands, and URLs verbatim. Return a JSON object with \`outputLocale\` set exactly to \`{{targetLocale}}\` and \`answer\` containing the Markdown response. Every human-readable sentence in \`answer\` must use the target locale. @@ -86171,7 +86165,7 @@ function getThinkPrompt(params) { /***/ }), -/***/ 10063: +/***/ 16030: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -86181,7 +86175,7 @@ exports.getUpdatePullRequestDescriptionPrompt = getUpdatePullRequestDescriptionP /** * Prompt for generating a concise PR description from an optional issue and the diff. */ -const fill_1 = __nccwpck_require__(2559); +const fill_1 = __nccwpck_require__(58865); const TEMPLATE = `You are in the repository workspace. Your task is to write a concise, review-ready pull request description from the branch diff and any linked issue. Write every human-readable sentence in {{targetLocale}}. Preserve code identifiers, paths, refs, commands, URLs, issue/PR references, and conventional title prefixes verbatim. Echo \`outputLocale\` exactly as \`{{targetLocale}}\`. @@ -86222,7 +86216,7 @@ function getUpdatePullRequestDescriptionPrompt(params) { /***/ }), -/***/ 63103: +/***/ 50929: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -86232,7 +86226,7 @@ exports.getUserRequestPrompt = getUserRequestPrompt; /** * Prompt for the Do user request use case (generic "do this" in repo). */ -const fill_1 = __nccwpck_require__(2559); +const fill_1 = __nccwpck_require__(58865); const TEMPLATE = `You are in the repository workspace. The user has asked you to do something. Perform their request by editing files and running commands directly in the workspace. Do not output diffs for someone else to apply. {{projectContextInstruction}} @@ -86258,7 +86252,7 @@ function getUserRequestPrompt(params) { /***/ }), -/***/ 63550: +/***/ 72472: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -86488,7 +86482,7 @@ function round(value) { /***/ }), -/***/ 2899: +/***/ 2141: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -86498,7 +86492,7 @@ exports.loadBugbotBenchmark = loadBugbotBenchmark; exports.loadBugbotPredictions = loadBugbotPredictions; exports.evaluateBugbotBenchmark = evaluateBugbotBenchmark; const promises_1 = __nccwpck_require__(93977); -const bugbot_quality_eval_1 = __nccwpck_require__(15467); +const bugbot_quality_eval_1 = __nccwpck_require__(70846); async function loadBugbotBenchmark(path) { const parsed = JSON.parse(await (0, promises_1.readFile)(path, 'utf8')); if (!isRecord(parsed) || parsed.schemaVersion !== 1 || !Array.isArray(parsed.cases)) { @@ -86589,7 +86583,7 @@ function isRecord(value) { /***/ }), -/***/ 19235: +/***/ 38817: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -86597,9 +86591,9 @@ function isRecord(value) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runBugbotBenchmarkAgent = runBugbotBenchmarkAgent; exports.buildBugbotBenchmarkPrompt = buildBugbotBenchmarkPrompt; -const schema_1 = __nccwpck_require__(16808); -const untrusted_content_1 = __nccwpck_require__(67057); -const prepare_bugbot_findings_policy_1 = __nccwpck_require__(3496); +const schema_1 = __nccwpck_require__(98135); +const untrusted_content_1 = __nccwpck_require__(12334); +const prepare_bugbot_findings_policy_1 = __nccwpck_require__(73654); const MAX_BENCHMARK_CASES = 200; /** Executes the real configured findings agent against every case, sequentially. */ async function runBugbotBenchmarkAgent(corpus, agent, configuration) { @@ -86652,7 +86646,7 @@ function extractBenchmarkFindings(response) { /***/ }), -/***/ 15467: +/***/ 70846: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -86661,7 +86655,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.DEFAULT_BUGBOT_QUALITY_THRESHOLDS = void 0; exports.evaluateBugbotFindings = evaluateBugbotFindings; exports.evaluateBugbotQualityGate = evaluateBugbotQualityGate; -const finding_identity_1 = __nccwpck_require__(91853); +const finding_identity_1 = __nccwpck_require__(657); exports.DEFAULT_BUGBOT_QUALITY_THRESHOLDS = { precision: 0.9, recall: 0.85, @@ -86769,7 +86763,7 @@ function format(value) { /***/ }), -/***/ 92816: +/***/ 61146: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -86821,7 +86815,7 @@ exports.injectJsonAsMarkdownBlock = injectJsonAsMarkdownBlock; /***/ }), -/***/ 91151: +/***/ 50135: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -86839,7 +86833,7 @@ exports.logError = logError; exports.logDebugInfo = logDebugInfo; exports.logDebugWarning = logDebugWarning; exports.logDebugError = logDebugError; -const secret_redaction_1 = __nccwpck_require__(254); +const secret_redaction_1 = __nccwpck_require__(93523); let loggerDebug = false; let loggerRemote = false; let structuredLogging = false; @@ -87002,7 +86996,7 @@ function logDebugError(message) { /***/ }), -/***/ 63907: +/***/ 36158: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -87015,7 +87009,7 @@ exports.PROJECT_CONTEXT_INSTRUCTION = `**Important – use full project context: /***/ }), -/***/ 38445: +/***/ 45689: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -87059,9 +87053,9 @@ exports.inspectRepositoryAgentGuidance = inspectRepositoryAgentGuidance; const fs = __importStar(__nccwpck_require__(87561)); const path = __importStar(__nccwpck_require__(49411)); const node_crypto_1 = __nccwpck_require__(6005); -const issue_workflow_profile_1 = __nccwpck_require__(26744); -const repository_agent_guidance_policy_1 = __nccwpck_require__(67402); -const logger_1 = __nccwpck_require__(91151); +const issue_workflow_profile_1 = __nccwpck_require__(62721); +const repository_agent_guidance_policy_1 = __nccwpck_require__(47992); +const logger_1 = __nccwpck_require__(50135); const MANAGED_ROLES = Object.freeze({ [repository_agent_guidance_policy_1.REPOSITORY_AGENT_PROFILE_PATH]: 'profile', [repository_agent_guidance_policy_1.REPOSITORY_AGENT_GUIDE_PATH]: 'guide', @@ -87480,7 +87474,7 @@ function inspectIssueWorkflowProjection(cwd, configuration) { /***/ }), -/***/ 254: +/***/ 93523: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -87509,7 +87503,7 @@ function redactKnownEnvironmentSecrets(value, environment = process.env) { /***/ }), -/***/ 90102: +/***/ 80501: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -87552,7 +87546,7 @@ exports.copySetupFile = copySetupFile; exports.copySetupDirectory = copySetupDirectory; const fs = __importStar(__nccwpck_require__(57147)); const path = __importStar(__nccwpck_require__(71017)); -const logger_1 = __nccwpck_require__(91151); +const logger_1 = __nccwpck_require__(50135); function copySetupFile(source, destination, displaySource, displayDestination, options = {}) { if (!fs.existsSync(source)) return { copied: 0, skipped: 0 }; @@ -87584,7 +87578,7 @@ function copySetupDirectory(sourceDirectory, destinationDirectory, fileFilter, d /***/ }), -/***/ 59126: +/***/ 30542: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -87630,13 +87624,13 @@ exports.getSetupToken = getSetupToken; exports.hasValidSetupToken = hasValidSetupToken; const fs = __importStar(__nccwpck_require__(57147)); const path = __importStar(__nccwpck_require__(71017)); -const setup_file_copy_1 = __nccwpck_require__(90102); -const logger_1 = __nccwpck_require__(91151); -const setup_workflow_catalog_1 = __nccwpck_require__(24596); -const issue_workflow_profile_1 = __nccwpck_require__(26744); -const setup_issue_workflow_policy_1 = __nccwpck_require__(81182); -const repository_agent_guidance_1 = __nccwpck_require__(38445); -const setup_approval_workflow_1 = __nccwpck_require__(9512); +const setup_file_copy_1 = __nccwpck_require__(80501); +const logger_1 = __nccwpck_require__(50135); +const setup_workflow_catalog_1 = __nccwpck_require__(37008); +const issue_workflow_profile_1 = __nccwpck_require__(62721); +const setup_issue_workflow_policy_1 = __nccwpck_require__(47280); +const repository_agent_guidance_1 = __nccwpck_require__(45689); +const setup_approval_workflow_1 = __nccwpck_require__(56189); /** * Ensure .github, .github/workflows and .github/ISSUE_TEMPLATE exist; create them if missing. * @param cwd - Directory (repo root) @@ -87896,7 +87890,7 @@ function hasValidSetupToken(cwd, override) { /***/ }), -/***/ 46103: +/***/ 83142: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -87966,14 +87960,14 @@ function getTaskEmoji(taskId) { /***/ }), -/***/ 46267: +/***/ 58747: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.extractIssueNumberFromPush = exports.extractIssueNumberFromBranch = void 0; -const positive_integer_policy_1 = __nccwpck_require__(19879); +const positive_integer_policy_1 = __nccwpck_require__(45613); const extractIssueNumberFromBranch = (branchName) => { const match = branchName?.match(/[a-zA-Z]+\/([0-9]+)-.*/); if (match) { @@ -87994,7 +87988,7 @@ exports.extractIssueNumberFromPush = extractIssueNumberFromPush; /***/ }), -/***/ 61788: +/***/ 61047: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -88038,7 +88032,7 @@ exports.getActionInputs = getActionInputs; exports.getActionInputsWithDefaults = getActionInputsWithDefaults; const fs = __importStar(__nccwpck_require__(57147)); const path = __importStar(__nccwpck_require__(71017)); -const yaml = __importStar(__nccwpck_require__(783)); +const yaml = __importStar(__nccwpck_require__(87969)); /** * Resolves action.yml from the copilot package root, not cwd. * When run as CLI from another repo, cwd is that repo; action.yml lives next to the bundle. @@ -88394,14 +88388,14 @@ module.exports = require("util"); /***/ }), -/***/ 12239: +/***/ 37579: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { -const { Argument } = __nccwpck_require__(62253); -const { Command } = __nccwpck_require__(51335); -const { CommanderError, InvalidArgumentError } = __nccwpck_require__(5022); -const { Help } = __nccwpck_require__(10320); -const { Option } = __nccwpck_require__(2430); +const { Argument } = __nccwpck_require__(99938); +const { Command } = __nccwpck_require__(26777); +const { CommanderError, InvalidArgumentError } = __nccwpck_require__(95989); +const { Help } = __nccwpck_require__(16706); +const { Option } = __nccwpck_require__(47969); exports.program = new Command(); @@ -88425,10 +88419,10 @@ exports.InvalidOptionArgumentError = InvalidArgumentError; // Deprecated /***/ }), -/***/ 62253: +/***/ 99938: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { -const { InvalidArgumentError } = __nccwpck_require__(5022); +const { InvalidArgumentError } = __nccwpck_require__(95989); class Argument { /** @@ -88581,7 +88575,7 @@ exports.humanReadableArgName = humanReadableArgName; /***/ }), -/***/ 51335: +/***/ 26777: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { const EventEmitter = (__nccwpck_require__(15673).EventEmitter); @@ -88590,11 +88584,11 @@ const path = __nccwpck_require__(49411); const fs = __nccwpck_require__(87561); const process = __nccwpck_require__(97742); -const { Argument, humanReadableArgName } = __nccwpck_require__(62253); -const { CommanderError } = __nccwpck_require__(5022); -const { Help } = __nccwpck_require__(10320); -const { Option, DualOptions } = __nccwpck_require__(2430); -const { suggestSimilar } = __nccwpck_require__(57754); +const { Argument, humanReadableArgName } = __nccwpck_require__(99938); +const { CommanderError } = __nccwpck_require__(95989); +const { Help } = __nccwpck_require__(16706); +const { Option, DualOptions } = __nccwpck_require__(47969); +const { suggestSimilar } = __nccwpck_require__(78149); class Command extends EventEmitter { /** @@ -91097,7 +91091,7 @@ exports.Command = Command; /***/ }), -/***/ 5022: +/***/ 95989: /***/ ((__unused_webpack_module, exports) => { /** @@ -91143,10 +91137,10 @@ exports.InvalidArgumentError = InvalidArgumentError; /***/ }), -/***/ 10320: +/***/ 16706: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { -const { humanReadableArgName } = __nccwpck_require__(62253); +const { humanReadableArgName } = __nccwpck_require__(99938); /** * TypeScript import types for JSDoc, used by Visual Studio Code IntelliSense and `npm run typescript-checkJS` @@ -91670,10 +91664,10 @@ exports.Help = Help; /***/ }), -/***/ 2430: +/***/ 47969: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { -const { InvalidArgumentError } = __nccwpck_require__(5022); +const { InvalidArgumentError } = __nccwpck_require__(95989); class Option { /** @@ -92007,7 +92001,7 @@ exports.DualOptions = DualOptions; /***/ }), -/***/ 57754: +/***/ 78149: /***/ ((__unused_webpack_module, exports) => { const maxDistance = 3; @@ -92115,7 +92109,7 @@ exports.suggestSimilar = suggestSimilar; /***/ }), -/***/ 29617: +/***/ 47743: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { /* module decorator */ module = __nccwpck_require__.nmd(module); @@ -92417,7 +92411,7 @@ module.exports = { /***/ }), -/***/ 922: +/***/ 47216: /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __nccwpck_require__) => { "use strict"; @@ -92429,7 +92423,7 @@ __nccwpck_require__.d(__webpack_exports__, { "Octokit": () => (/* binding */ Octokit) }); -;// CONCATENATED MODULE: ./node_modules/.pnpm/universal-user-agent@7.0.3/node_modules/universal-user-agent/index.js +;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/universal-user-agent@7.0.3/node_modules/universal-user-agent/index.js function getUserAgent() { if (typeof navigator === "object" && "userAgent" in navigator) { return navigator.userAgent; @@ -92444,7 +92438,7 @@ function getUserAgent() { return ""; } -;// CONCATENATED MODULE: ./node_modules/.pnpm/before-after-hook@4.0.0/node_modules/before-after-hook/lib/register.js +;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/before-after-hook@4.0.0/node_modules/before-after-hook/lib/register.js // @ts-check function register(state, name, method, options) { @@ -92473,7 +92467,7 @@ function register(state, name, method, options) { }); } -;// CONCATENATED MODULE: ./node_modules/.pnpm/before-after-hook@4.0.0/node_modules/before-after-hook/lib/add.js +;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/before-after-hook@4.0.0/node_modules/before-after-hook/lib/add.js // @ts-check function addHook(state, kind, name, hook) { @@ -92521,7 +92515,7 @@ function addHook(state, kind, name, hook) { }); } -;// CONCATENATED MODULE: ./node_modules/.pnpm/before-after-hook@4.0.0/node_modules/before-after-hook/lib/remove.js +;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/before-after-hook@4.0.0/node_modules/before-after-hook/lib/remove.js // @ts-check function removeHook(state, name, method) { @@ -92542,7 +92536,7 @@ function removeHook(state, name, method) { state.registry[name].splice(index, 1); } -;// CONCATENATED MODULE: ./node_modules/.pnpm/before-after-hook@4.0.0/node_modules/before-after-hook/index.js +;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/before-after-hook@4.0.0/node_modules/before-after-hook/index.js // @ts-check @@ -92589,7 +92583,7 @@ function Collection() { /* harmony default export */ const before_after_hook = ({ Singular, Collection }); -;// CONCATENATED MODULE: ./node_modules/.pnpm/@octokit+endpoint@11.0.5/node_modules/@octokit/endpoint/dist-bundle/index.js +;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/@octokit+endpoint@11.0.5/node_modules/@octokit/endpoint/dist-bundle/index.js // pkg/dist-src/defaults.js @@ -92935,7 +92929,7 @@ function withDefaults(oldDefaults, newDefaults) { var endpoint = withDefaults(null, DEFAULTS); -;// CONCATENATED MODULE: ./node_modules/.pnpm/content-type@3.0.0/node_modules/content-type/dist/index.js +;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/content-type@3.0.0/node_modules/content-type/dist/index.js /*! * content-type * Copyright(c) 2015 Douglas Christopher Wilson @@ -93108,7 +93102,7 @@ function qstring(str) { throw new TypeError(`Invalid parameter value: ${str}`); } //# sourceMappingURL=index.js.map -;// CONCATENATED MODULE: ./node_modules/.pnpm/json-with-bigint@3.5.12/node_modules/json-with-bigint/json-with-bigint.js +;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/json-with-bigint@3.5.12/node_modules/json-with-bigint/json-with-bigint.js const intRegex = /^-?\d+$/; const noiseValue = /^-?\d+n+$/; // Noise - strings that match the custom format before being converted to it const originalStringify = JSON.stringify; @@ -93709,7 +93703,7 @@ const JSONParse = (text, reviver) => { -;// CONCATENATED MODULE: ./node_modules/.pnpm/@octokit+request-error@7.1.2/node_modules/@octokit/request-error/dist-src/index.js +;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/@octokit+request-error@7.1.2/node_modules/@octokit/request-error/dist-src/index.js class RequestError extends Error { name; /** @@ -93750,7 +93744,7 @@ class RequestError extends Error { } -;// CONCATENATED MODULE: ./node_modules/.pnpm/@octokit+request@10.0.16/node_modules/@octokit/request/dist-bundle/index.js +;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/@octokit+request@10.0.16/node_modules/@octokit/request/dist-bundle/index.js // pkg/dist-src/index.js @@ -93957,7 +93951,7 @@ var request = dist_bundle_withDefaults(endpoint, defaults_default); /* v8 ignore next -- @preserve */ /* v8 ignore else -- @preserve */ -;// CONCATENATED MODULE: ./node_modules/.pnpm/@octokit+graphql@9.0.5/node_modules/@octokit/graphql/dist-bundle/index.js +;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/@octokit+graphql@9.0.5/node_modules/@octokit/graphql/dist-bundle/index.js // pkg/dist-src/index.js @@ -94088,7 +94082,7 @@ function withCustomRequest(customRequest) { /* v8 ignore if -- @preserve */ -;// CONCATENATED MODULE: ./node_modules/.pnpm/@octokit+auth-token@6.0.0/node_modules/@octokit/auth-token/dist-bundle/index.js +;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/@octokit+auth-token@6.0.0/node_modules/@octokit/auth-token/dist-bundle/index.js // pkg/dist-src/is-jwt.js var b64url = "(?:[a-zA-Z0-9_-]+)"; var sep = "\\."; @@ -94143,11 +94137,11 @@ var createTokenAuth = function createTokenAuth2(token) { }; -;// CONCATENATED MODULE: ./node_modules/.pnpm/@octokit+core@7.0.8/node_modules/@octokit/core/dist-src/version.js +;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/@octokit+core@7.0.8/node_modules/@octokit/core/dist-src/version.js const version_VERSION = "7.0.8"; -;// CONCATENATED MODULE: ./node_modules/.pnpm/@octokit+core@7.0.8/node_modules/@octokit/core/dist-src/index.js +;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/@octokit+core@7.0.8/node_modules/@octokit/core/dist-src/index.js @@ -94291,7 +94285,7 @@ class Octokit { /***/ }), -/***/ 36738: +/***/ 55347: /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __nccwpck_require__) => { "use strict"; @@ -94716,7 +94710,7 @@ paginateRest.VERSION = VERSION; /***/ }), -/***/ 50305: +/***/ 57496: /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __nccwpck_require__) => { "use strict"; @@ -94729,12 +94723,12 @@ __nccwpck_require__.d(__webpack_exports__, { "restEndpointMethods": () => (/* binding */ restEndpointMethods) }); -;// CONCATENATED MODULE: ./node_modules/.pnpm/@octokit+plugin-rest-endpoint-methods@17.0.0_@octokit+core@7.0.8/node_modules/@octokit/plugin-rest-endpoint-methods/dist-src/version.js +;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/@octokit+plugin-rest-endpoint-methods@17.0.0_@octokit+core@7.0.8/node_modules/@octokit/plugin-rest-endpoint-methods/dist-src/version.js const VERSION = "17.0.0"; //# sourceMappingURL=version.js.map -;// CONCATENATED MODULE: ./node_modules/.pnpm/@octokit+plugin-rest-endpoint-methods@17.0.0_@octokit+core@7.0.8/node_modules/@octokit/plugin-rest-endpoint-methods/dist-src/generated/endpoints.js +;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/@octokit+plugin-rest-endpoint-methods@17.0.0_@octokit+core@7.0.8/node_modules/@octokit/plugin-rest-endpoint-methods/dist-src/generated/endpoints.js const Endpoints = { actions: { addCustomLabelsToSelfHostedRunnerForOrg: [ @@ -97028,7 +97022,7 @@ var endpoints_default = Endpoints; //# sourceMappingURL=endpoints.js.map -;// CONCATENATED MODULE: ./node_modules/.pnpm/@octokit+plugin-rest-endpoint-methods@17.0.0_@octokit+core@7.0.8/node_modules/@octokit/plugin-rest-endpoint-methods/dist-src/endpoints-to-methods.js +;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/@octokit+plugin-rest-endpoint-methods@17.0.0_@octokit+core@7.0.8/node_modules/@octokit/plugin-rest-endpoint-methods/dist-src/endpoints-to-methods.js const endpointMethodsMap = /* @__PURE__ */ new Map(); for (const [scope, endpoints] of Object.entries(endpoints_default)) { @@ -97154,7 +97148,7 @@ function decorate(octokit, scope, methodName, defaults, decorations) { //# sourceMappingURL=endpoints-to-methods.js.map -;// CONCATENATED MODULE: ./node_modules/.pnpm/@octokit+plugin-rest-endpoint-methods@17.0.0_@octokit+core@7.0.8/node_modules/@octokit/plugin-rest-endpoint-methods/dist-src/index.js +;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/@octokit+plugin-rest-endpoint-methods@17.0.0_@octokit+core@7.0.8/node_modules/@octokit/plugin-rest-endpoint-methods/dist-src/index.js function restEndpointMethods(octokit) { @@ -97178,7 +97172,7 @@ legacyRestEndpointMethods.VERSION = VERSION; /***/ }), -/***/ 11652: +/***/ 32634: /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __nccwpck_require__) => { "use strict"; @@ -97193,7 +97187,7 @@ __nccwpck_require__.d(__webpack_exports__, { // EXTERNAL MODULE: external "node:process" var external_node_process_ = __nccwpck_require__(97742); -;// CONCATENATED MODULE: ./node_modules/.pnpm/ansi-regex@6.3.0/node_modules/ansi-regex/index.js +;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/ansi-regex@6.3.0/node_modules/ansi-regex/index.js function ansiRegex({onlyFirst = false} = {}) { // Valid string terminator sequences are BEL, ESC\, and 0x9c const ST = '(?:\\u0007|\\u001B\\u005C|\\u009C)'; @@ -97210,7 +97204,7 @@ function ansiRegex({onlyFirst = false} = {}) { return new RegExp(pattern, onlyFirst ? undefined : 'g'); } -;// CONCATENATED MODULE: ./node_modules/.pnpm/strip-ansi@7.2.0/node_modules/strip-ansi/index.js +;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/strip-ansi@7.2.0/node_modules/strip-ansi/index.js const regex = ansiRegex(); @@ -97231,7 +97225,7 @@ function stripAnsi(string) { return string.replace(regex, ''); } -;// CONCATENATED MODULE: ./node_modules/.pnpm/get-east-asian-width@1.6.0/node_modules/get-east-asian-width/lookup-data.js +;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/get-east-asian-width@1.6.0/node_modules/get-east-asian-width/lookup-data.js // Generated by scripts/build.js const ambiguousMinimalCodePoint = 161; @@ -97254,7 +97248,7 @@ const wideMinimalCodePoint = 4352; const wideMaximumCodePoint = 262141; const wideRanges = [4352, 4447, 8986, 8987, 9001, 9002, 9193, 9196, 9200, 9200, 9203, 9203, 9725, 9726, 9748, 9749, 9776, 9783, 9800, 9811, 9855, 9855, 9866, 9871, 9875, 9875, 9889, 9889, 9898, 9899, 9917, 9918, 9924, 9925, 9934, 9934, 9940, 9940, 9962, 9962, 9970, 9971, 9973, 9973, 9978, 9978, 9981, 9981, 9989, 9989, 9994, 9995, 10024, 10024, 10060, 10060, 10062, 10062, 10067, 10069, 10071, 10071, 10133, 10135, 10160, 10160, 10175, 10175, 11035, 11036, 11088, 11088, 11093, 11093, 11904, 11929, 11931, 12019, 12032, 12245, 12272, 12287, 12289, 12350, 12353, 12438, 12441, 12543, 12549, 12591, 12593, 12686, 12688, 12773, 12783, 12830, 12832, 12871, 12880, 42124, 42128, 42182, 43360, 43388, 44032, 55203, 63744, 64255, 65040, 65049, 65072, 65106, 65108, 65126, 65128, 65131, 94176, 94180, 94192, 94198, 94208, 101589, 101631, 101662, 101760, 101874, 110576, 110579, 110581, 110587, 110589, 110590, 110592, 110882, 110898, 110898, 110928, 110930, 110933, 110933, 110948, 110951, 110960, 111355, 119552, 119638, 119648, 119670, 126980, 126980, 127183, 127183, 127374, 127374, 127377, 127386, 127488, 127490, 127504, 127547, 127552, 127560, 127568, 127569, 127584, 127589, 127744, 127776, 127789, 127797, 127799, 127868, 127870, 127891, 127904, 127946, 127951, 127955, 127968, 127984, 127988, 127988, 127992, 128062, 128064, 128064, 128066, 128252, 128255, 128317, 128331, 128334, 128336, 128359, 128378, 128378, 128405, 128406, 128420, 128420, 128507, 128591, 128640, 128709, 128716, 128716, 128720, 128722, 128725, 128728, 128732, 128735, 128747, 128748, 128756, 128764, 128992, 129003, 129008, 129008, 129292, 129338, 129340, 129349, 129351, 129535, 129648, 129660, 129664, 129674, 129678, 129734, 129736, 129736, 129741, 129756, 129759, 129770, 129775, 129784, 131072, 196605, 196608, 262141]; -;// CONCATENATED MODULE: ./node_modules/.pnpm/get-east-asian-width@1.6.0/node_modules/get-east-asian-width/utilities.js +;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/get-east-asian-width@1.6.0/node_modules/get-east-asian-width/utilities.js /** Binary search on a sorted flat array of [start, end] pairs. @@ -97280,7 +97274,7 @@ const utilities_isInRange = (ranges, codePoint) => { return false; }; -;// CONCATENATED MODULE: ./node_modules/.pnpm/get-east-asian-width@1.6.0/node_modules/get-east-asian-width/lookup.js +;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/get-east-asian-width@1.6.0/node_modules/get-east-asian-width/lookup.js @@ -97400,7 +97394,7 @@ function lookup_getCategory(codePoint) { return 'neutral'; } -;// CONCATENATED MODULE: ./node_modules/.pnpm/get-east-asian-width@1.6.0/node_modules/get-east-asian-width/index.js +;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/get-east-asian-width@1.6.0/node_modules/get-east-asian-width/index.js function validate(codePoint) { @@ -97432,9 +97426,9 @@ function eastAsianWidth(codePoint, {ambiguousAsWide = false} = {}) { // Private exports for https://github.com/sindresorhus/is-fullwidth-code-point -// EXTERNAL MODULE: ./node_modules/.pnpm/emoji-regex@10.6.0/node_modules/emoji-regex/index.js -var emoji_regex = __nccwpck_require__(33104); -;// CONCATENATED MODULE: ./node_modules/.pnpm/string-width@7.2.0/node_modules/string-width/index.js +// EXTERNAL MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/emoji-regex@10.6.0/node_modules/emoji-regex/index.js +var emoji_regex = __nccwpck_require__(25863); +;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/string-width@7.2.0/node_modules/string-width/index.js @@ -97518,9 +97512,9 @@ function stringWidth(string, options = {}) { return width; } -// EXTERNAL MODULE: ./node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/index.js + 4 modules -var source = __nccwpck_require__(8578); -;// CONCATENATED MODULE: ./node_modules/.pnpm/widest-line@5.0.0/node_modules/widest-line/index.js +// EXTERNAL MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/index.js + 4 modules +var source = __nccwpck_require__(43920); +;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/widest-line@5.0.0/node_modules/widest-line/index.js function widestLine(string) { @@ -97533,9 +97527,9 @@ function widestLine(string) { return lineWidth; } -// EXTERNAL MODULE: ./node_modules/.pnpm/cli-boxes@3.0.0/node_modules/cli-boxes/index.js -var cli_boxes = __nccwpck_require__(77755); -;// CONCATENATED MODULE: ./node_modules/.pnpm/camelcase@8.0.0/node_modules/camelcase/index.js +// EXTERNAL MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/cli-boxes@3.0.0/node_modules/cli-boxes/index.js +var cli_boxes = __nccwpck_require__(76291); +;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/camelcase@8.0.0/node_modules/camelcase/index.js const UPPERCASE = /[\p{Lu}]/u; const LOWERCASE = /[\p{Ll}]/u; const LEADING_CAPITAL = /^[\p{Lu}](?![\p{Lu}])/gu; @@ -97647,9 +97641,9 @@ function camelCase(input, options) { return postProcess(input, toUpperCase); } -// EXTERNAL MODULE: ./node_modules/.pnpm/ansi-align@3.0.1/node_modules/ansi-align/index.js -var ansi_align = __nccwpck_require__(61570); -;// CONCATENATED MODULE: ./node_modules/.pnpm/ansi-styles@6.2.3/node_modules/ansi-styles/index.js +// EXTERNAL MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/ansi-align@3.0.1/node_modules/ansi-align/index.js +var ansi_align = __nccwpck_require__(6465); +;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/ansi-styles@6.2.3/node_modules/ansi-styles/index.js const ANSI_BACKGROUND_OFFSET = 10; const wrapAnsi16 = (offset = 0) => code => `\u001B[${code + offset}m`; @@ -97874,7 +97868,7 @@ const ansiStyles = assembleStyles(); /* harmony default export */ const ansi_styles = (ansiStyles); -;// CONCATENATED MODULE: ./node_modules/.pnpm/wrap-ansi@9.0.2/node_modules/wrap-ansi/index.js +;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/wrap-ansi@9.0.2/node_modules/wrap-ansi/index.js @@ -98098,7 +98092,7 @@ function wrapAnsi(string, columns, options) { .join('\n'); } -;// CONCATENATED MODULE: ./node_modules/.pnpm/boxen@8.0.1/node_modules/boxen/index.js +;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/boxen@8.0.1/node_modules/boxen/index.js @@ -98479,7 +98473,7 @@ function boxen(text, options) { /***/ }), -/***/ 8578: +/***/ 43920: /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __nccwpck_require__) => { "use strict"; @@ -98503,7 +98497,7 @@ __nccwpck_require__.d(__webpack_exports__, { "supportsColorStderr": () => (/* binding */ stderrColor) }); -;// CONCATENATED MODULE: ./node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/vendor/ansi-styles/index.js +;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/vendor/ansi-styles/index.js const ANSI_BACKGROUND_OFFSET = 10; const wrapAnsi16 = (offset = 0) => code => `\u001B[${code + offset}m`; @@ -98734,7 +98728,7 @@ var external_node_process_ = __nccwpck_require__(97742); var external_node_os_ = __nccwpck_require__(70612); ;// CONCATENATED MODULE: external "node:tty" const external_node_tty_namespaceObject = require("node:tty"); -;// CONCATENATED MODULE: ./node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/vendor/supports-color/index.js +;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/vendor/supports-color/index.js @@ -98926,7 +98920,7 @@ const supportsColor = { /* harmony default export */ const supports_color = (supportsColor); -;// CONCATENATED MODULE: ./node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/utilities.js +;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/utilities.js // TODO: When targeting Node.js 16, use `String.prototype.replaceAll`. function stringReplaceAll(string, substring, replacer) { let index = string.indexOf(substring); @@ -98961,7 +98955,7 @@ function stringEncaseCRLFWithFirstIndex(string, prefix, postfix, index) { return returnValue; } -;// CONCATENATED MODULE: ./node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/index.js +;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/index.js @@ -99174,7 +99168,7 @@ const chalkStderr = createChalk({level: stderrColor ? stderrColor.level : 0}); /***/ }), -/***/ 57227: +/***/ 70106: /***/ ((module) => { "use strict"; @@ -99275,7 +99269,7 @@ module.exports = JSON.parse('{"revision":"2026-09-24.p1-c.3","providers":{"codex /******/ // module cache are used so entry inlining is disabled /******/ // startup /******/ // Load entry module and return exports -/******/ var __webpack_exports__ = __nccwpck_require__(__nccwpck_require__.s = 55711); +/******/ var __webpack_exports__ = __nccwpck_require__(__nccwpck_require__.s = 81627); /******/ module.exports = __webpack_exports__; /******/ /******/ })() From c98812bc841c044d46d2c9f888a963256d953b78 Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Tue, 29 Sep 2026 09:57:26 +0200 Subject: [PATCH 27/50] codex-setup-temporary-github-auth: regenerate CLI bundle with locked dependencies --- build/cli/index.js | 5936 ++++++++++++++++++++++---------------------- 1 file changed, 2968 insertions(+), 2968 deletions(-) diff --git a/build/cli/index.js b/build/cli/index.js index b9fd33c45..bec55979e 100755 --- a/build/cli/index.js +++ b/build/cli/index.js @@ -2,7 +2,7 @@ /******/ (() => { // webpackBootstrap /******/ var __webpack_modules__ = ({ -/***/ 36086: +/***/ 18538: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -53,7 +53,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.exec = exec; exports.getExecOutput = getExecOutput; const string_decoder_1 = __nccwpck_require__(71576); -const tr = __importStar(__nccwpck_require__(55908)); +const tr = __importStar(__nccwpck_require__(4094)); /** * Exec a command. * Output will be streamed to the live console. @@ -125,7 +125,7 @@ function getExecOutput(commandLine, args, options) { /***/ }), -/***/ 55908: +/***/ 4094: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -179,8 +179,8 @@ const os = __importStar(__nccwpck_require__(22037)); const events = __importStar(__nccwpck_require__(82361)); const child = __importStar(__nccwpck_require__(32081)); const path = __importStar(__nccwpck_require__(71017)); -const io = __importStar(__nccwpck_require__(15476)); -const ioUtil = __importStar(__nccwpck_require__(90188)); +const io = __importStar(__nccwpck_require__(34166)); +const ioUtil = __importStar(__nccwpck_require__(4813)); const timers_1 = __nccwpck_require__(39512); /* eslint-disable @typescript-eslint/unbound-method */ const IS_WINDOWS = process.platform === 'win32'; @@ -757,7 +757,7 @@ class ExecState extends events.EventEmitter { /***/ }), -/***/ 81103: +/***/ 26402: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -820,7 +820,7 @@ exports.Context = Context; /***/ }), -/***/ 87211: +/***/ 78227: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -861,8 +861,8 @@ var __importStar = (this && this.__importStar) || (function () { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.context = void 0; exports.getOctokit = getOctokit; -const Context = __importStar(__nccwpck_require__(81103)); -const utils_1 = __nccwpck_require__(76954); +const Context = __importStar(__nccwpck_require__(26402)); +const utils_1 = __nccwpck_require__(33536); exports.context = new Context.Context(); /** * Returns a hydrated octokit ready to use for GitHub Actions @@ -878,7 +878,7 @@ function getOctokit(token, options, ...additionalPlugins) { /***/ }), -/***/ 8423: +/***/ 92746: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -931,8 +931,8 @@ exports.getProxyAgent = getProxyAgent; exports.getProxyAgentDispatcher = getProxyAgentDispatcher; exports.getProxyFetch = getProxyFetch; exports.getApiBaseUrl = getApiBaseUrl; -const httpClient = __importStar(__nccwpck_require__(33843)); -const undici_1 = __nccwpck_require__(79868); +const httpClient = __importStar(__nccwpck_require__(75784)); +const undici_1 = __nccwpck_require__(18381); function getAuthString(token, options) { if (!token && !options.auth) { throw new Error('Parameter token or opts.auth is required'); @@ -964,7 +964,7 @@ function getApiBaseUrl() { /***/ }), -/***/ 76954: +/***/ 33536: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -1005,12 +1005,12 @@ var __importStar = (this && this.__importStar) || (function () { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.GitHub = exports.defaults = exports.context = void 0; exports.getOctokitOptions = getOctokitOptions; -const Context = __importStar(__nccwpck_require__(81103)); -const Utils = __importStar(__nccwpck_require__(8423)); +const Context = __importStar(__nccwpck_require__(26402)); +const Utils = __importStar(__nccwpck_require__(92746)); // octokit + plugins -const core_1 = __nccwpck_require__(47216); -const plugin_rest_endpoint_methods_1 = __nccwpck_require__(57496); -const plugin_paginate_rest_1 = __nccwpck_require__(55347); +const core_1 = __nccwpck_require__(922); +const plugin_rest_endpoint_methods_1 = __nccwpck_require__(50305); +const plugin_paginate_rest_1 = __nccwpck_require__(36738); exports.context = new Context.Context(); const baseUrl = Utils.getApiBaseUrl(); exports.defaults = { @@ -1040,7 +1040,7 @@ function getOctokitOptions(token, options) { /***/ }), -/***/ 33843: +/***/ 75784: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -1094,9 +1094,9 @@ exports.getProxyUrl = getProxyUrl; exports.isHttps = isHttps; const http = __importStar(__nccwpck_require__(13685)); const https = __importStar(__nccwpck_require__(95687)); -const pm = __importStar(__nccwpck_require__(67906)); -const tunnel = __importStar(__nccwpck_require__(98787)); -const undici_1 = __nccwpck_require__(79868); +const pm = __importStar(__nccwpck_require__(34583)); +const tunnel = __importStar(__nccwpck_require__(64249)); +const undici_1 = __nccwpck_require__(18381); var HttpCodes; (function (HttpCodes) { HttpCodes[HttpCodes["OK"] = 200] = "OK"; @@ -1784,7 +1784,7 @@ const lowercaseKeys = (obj) => Object.keys(obj).reduce((c, k) => ((c[k.toLowerCa /***/ }), -/***/ 67906: +/***/ 34583: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -1885,7 +1885,7 @@ class DecodedURL extends URL { /***/ }), -/***/ 90188: +/***/ 4813: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -2116,7 +2116,7 @@ function getCmdPath() { /***/ }), -/***/ 15476: +/***/ 34166: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -2172,7 +2172,7 @@ exports.which = which; exports.findInPath = findInPath; const assert_1 = __nccwpck_require__(39491); const path = __importStar(__nccwpck_require__(71017)); -const ioUtil = __importStar(__nccwpck_require__(90188)); +const ioUtil = __importStar(__nccwpck_require__(4813)); /** * Copies a file or folder. * Based off of shelljs - https://github.com/shelljs/shelljs/blob/9237f66c52e5daa40458f94f9565e18e8132f5a6/src/cp.js @@ -2435,13 +2435,13 @@ function copyFile(srcFile, destFile, force) { /***/ }), -/***/ 6465: +/***/ 61570: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const stringWidth = __nccwpck_require__(78963) +const stringWidth = __nccwpck_require__(77486) function ansiAlign (text, opts) { if (!text) return text @@ -2504,7 +2504,7 @@ function fullDiff (maxWidth, curWidth) { /***/ }), -/***/ 16083: +/***/ 75207: /***/ ((module) => { "use strict"; @@ -2522,12 +2522,12 @@ module.exports = ({onlyFirst = false} = {}) => { /***/ }), -/***/ 76291: +/***/ 77755: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const cliBoxes = __nccwpck_require__(70106); +const cliBoxes = __nccwpck_require__(57227); module.exports = cliBoxes; // TODO: Remove this for the next major release @@ -2536,7 +2536,7 @@ module.exports["default"] = cliBoxes; /***/ }), -/***/ 25863: +/***/ 33104: /***/ ((module) => { module.exports = () => { @@ -2547,7 +2547,7 @@ module.exports = () => { /***/ }), -/***/ 44393: +/***/ 29311: /***/ ((module) => { "use strict"; @@ -2561,7 +2561,7 @@ module.exports = function () { /***/ }), -/***/ 22439: +/***/ 24063: /***/ ((module) => { "use strict"; @@ -2619,7 +2619,7 @@ module.exports["default"] = isFullwidthCodePoint; /***/ }), -/***/ 87969: +/***/ 783: /***/ ((__unused_webpack_module, exports) => { /*! js-yaml 5.4.1 https://github.com/nodeca/js-yaml @license MIT */ @@ -6350,19 +6350,19 @@ exports.visit = visit; /***/ }), -/***/ 18342: +/***/ 75430: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; -exports.quote = __nccwpck_require__(17833); -exports.parse = __nccwpck_require__(71663); +exports.quote = __nccwpck_require__(91017); +exports.parse = __nccwpck_require__(79131); /***/ }), -/***/ 71663: +/***/ 79131: /***/ ((module) => { "use strict"; @@ -6700,7 +6700,7 @@ module.exports = function parse(s, env, opts) { /***/ }), -/***/ 17833: +/***/ 91017: /***/ ((module) => { "use strict"; @@ -6773,14 +6773,14 @@ module.exports = function quote(xs) { /***/ }), -/***/ 78963: +/***/ 77486: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const stripAnsi = __nccwpck_require__(83941); -const isFullwidthCodePoint = __nccwpck_require__(22439); -const emojiRegex = __nccwpck_require__(44393); +const stripAnsi = __nccwpck_require__(10828); +const isFullwidthCodePoint = __nccwpck_require__(24063); +const emojiRegex = __nccwpck_require__(29311); const stringWidth = string => { if (typeof string !== 'string' || string.length === 0) { @@ -6828,27 +6828,27 @@ module.exports["default"] = stringWidth; /***/ }), -/***/ 83941: +/***/ 10828: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const ansiRegex = __nccwpck_require__(16083); +const ansiRegex = __nccwpck_require__(75207); module.exports = string => typeof string === 'string' ? string.replace(ansiRegex(), '') : string; /***/ }), -/***/ 98787: +/***/ 64249: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -module.exports = __nccwpck_require__(27222); +module.exports = __nccwpck_require__(30709); /***/ }), -/***/ 27222: +/***/ 30709: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -7120,7 +7120,7 @@ exports.debug = debug; // for test /***/ }), -/***/ 37124: +/***/ 24258: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { (function(nacl) { @@ -9518,34 +9518,34 @@ nacl.setPRNG = function(fn) { /***/ }), -/***/ 79868: +/***/ 18381: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const Client = __nccwpck_require__(41234) -const Dispatcher = __nccwpck_require__(11588) -const Pool = __nccwpck_require__(3639) -const BalancedPool = __nccwpck_require__(58591) -const Agent = __nccwpck_require__(78590) -const ProxyAgent = __nccwpck_require__(76930) -const EnvHttpProxyAgent = __nccwpck_require__(52673) -const RetryAgent = __nccwpck_require__(4420) -const errors = __nccwpck_require__(5425) -const util = __nccwpck_require__(39141) +const Client = __nccwpck_require__(85849) +const Dispatcher = __nccwpck_require__(66071) +const Pool = __nccwpck_require__(80229) +const BalancedPool = __nccwpck_require__(68255) +const Agent = __nccwpck_require__(73274) +const ProxyAgent = __nccwpck_require__(87187) +const EnvHttpProxyAgent = __nccwpck_require__(29941) +const RetryAgent = __nccwpck_require__(55184) +const errors = __nccwpck_require__(35990) +const util = __nccwpck_require__(50011) const { InvalidArgumentError } = errors -const api = __nccwpck_require__(56796) -const buildConnector = __nccwpck_require__(28786) -const MockClient = __nccwpck_require__(56607) -const MockAgent = __nccwpck_require__(36294) -const MockPool = __nccwpck_require__(60834) -const mockErrors = __nccwpck_require__(63739) -const RetryHandler = __nccwpck_require__(30160) -const { getGlobalDispatcher, setGlobalDispatcher } = __nccwpck_require__(12621) -const DecoratorHandler = __nccwpck_require__(63859) -const RedirectHandler = __nccwpck_require__(71779) -const createRedirectInterceptor = __nccwpck_require__(96329) +const api = __nccwpck_require__(20617) +const buildConnector = __nccwpck_require__(41429) +const MockClient = __nccwpck_require__(4227) +const MockAgent = __nccwpck_require__(46432) +const MockPool = __nccwpck_require__(36575) +const mockErrors = __nccwpck_require__(19329) +const RetryHandler = __nccwpck_require__(64524) +const { getGlobalDispatcher, setGlobalDispatcher } = __nccwpck_require__(19405) +const DecoratorHandler = __nccwpck_require__(41738) +const RedirectHandler = __nccwpck_require__(64014) +const createRedirectInterceptor = __nccwpck_require__(40928) Object.assign(Dispatcher.prototype, api) @@ -9563,10 +9563,10 @@ module.exports.DecoratorHandler = DecoratorHandler module.exports.RedirectHandler = RedirectHandler module.exports.createRedirectInterceptor = createRedirectInterceptor module.exports.interceptors = { - redirect: __nccwpck_require__(37849), - retry: __nccwpck_require__(19182), - dump: __nccwpck_require__(13413), - dns: __nccwpck_require__(58033) + redirect: __nccwpck_require__(74872), + retry: __nccwpck_require__(79637), + dump: __nccwpck_require__(12493), + dns: __nccwpck_require__(20346) } module.exports.buildConnector = buildConnector @@ -9628,7 +9628,7 @@ function makeDispatcher (fn) { module.exports.setGlobalDispatcher = setGlobalDispatcher module.exports.getGlobalDispatcher = getGlobalDispatcher -const fetchImpl = (__nccwpck_require__(9526).fetch) +const fetchImpl = (__nccwpck_require__(78329).fetch) module.exports.fetch = async function fetch (init, options = undefined) { try { return await fetchImpl(init, options) @@ -9640,39 +9640,39 @@ module.exports.fetch = async function fetch (init, options = undefined) { throw err } } -module.exports.Headers = __nccwpck_require__(66089).Headers -module.exports.Response = __nccwpck_require__(98579).Response -module.exports.Request = __nccwpck_require__(3891).Request -module.exports.FormData = __nccwpck_require__(26697).FormData +module.exports.Headers = __nccwpck_require__(10561).Headers +module.exports.Response = __nccwpck_require__(51132).Response +module.exports.Request = __nccwpck_require__(83211).Request +module.exports.FormData = __nccwpck_require__(62598).FormData module.exports.File = globalThis.File ?? (__nccwpck_require__(72254).File) -module.exports.FileReader = __nccwpck_require__(73002).FileReader +module.exports.FileReader = __nccwpck_require__(65153).FileReader -const { setGlobalOrigin, getGlobalOrigin } = __nccwpck_require__(82470) +const { setGlobalOrigin, getGlobalOrigin } = __nccwpck_require__(13924) module.exports.setGlobalOrigin = setGlobalOrigin module.exports.getGlobalOrigin = getGlobalOrigin -const { CacheStorage } = __nccwpck_require__(10471) -const { kConstruct } = __nccwpck_require__(93639) +const { CacheStorage } = __nccwpck_require__(11069) +const { kConstruct } = __nccwpck_require__(50591) // Cache & CacheStorage are tightly coupled with fetch. Even if it may run // in an older version of Node, it doesn't have any use without fetch. module.exports.caches = new CacheStorage(kConstruct) -const { deleteCookie, getCookies, getSetCookies, setCookie } = __nccwpck_require__(30035) +const { deleteCookie, getCookies, getSetCookies, setCookie } = __nccwpck_require__(15855) module.exports.deleteCookie = deleteCookie module.exports.getCookies = getCookies module.exports.getSetCookies = getSetCookies module.exports.setCookie = setCookie -const { parseMIMEType, serializeAMimeType } = __nccwpck_require__(29192) +const { parseMIMEType, serializeAMimeType } = __nccwpck_require__(96730) module.exports.parseMIMEType = parseMIMEType module.exports.serializeAMimeType = serializeAMimeType -const { CloseEvent, ErrorEvent, MessageEvent } = __nccwpck_require__(46055) -module.exports.WebSocket = __nccwpck_require__(1468).WebSocket +const { CloseEvent, ErrorEvent, MessageEvent } = __nccwpck_require__(69459) +module.exports.WebSocket = __nccwpck_require__(16416).WebSocket module.exports.CloseEvent = CloseEvent module.exports.ErrorEvent = ErrorEvent module.exports.MessageEvent = MessageEvent @@ -9688,18 +9688,18 @@ module.exports.MockPool = MockPool module.exports.MockAgent = MockAgent module.exports.mockErrors = mockErrors -const { EventSource } = __nccwpck_require__(90109) +const { EventSource } = __nccwpck_require__(6731) module.exports.EventSource = EventSource /***/ }), -/***/ 52872: +/***/ 97433: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -const { addAbortListener } = __nccwpck_require__(39141) -const { RequestAbortedError } = __nccwpck_require__(5425) +const { addAbortListener } = __nccwpck_require__(50011) +const { RequestAbortedError } = __nccwpck_require__(35990) const kListener = Symbol('kListener') const kSignal = Symbol('kSignal') @@ -9759,7 +9759,7 @@ module.exports = { /***/ }), -/***/ 15921: +/***/ 93671: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -9767,9 +9767,9 @@ module.exports = { const assert = __nccwpck_require__(98061) const { AsyncResource } = __nccwpck_require__(92761) -const { InvalidArgumentError, SocketError } = __nccwpck_require__(5425) -const util = __nccwpck_require__(39141) -const { addSignal, removeSignal } = __nccwpck_require__(52872) +const { InvalidArgumentError, SocketError } = __nccwpck_require__(35990) +const util = __nccwpck_require__(50011) +const { addSignal, removeSignal } = __nccwpck_require__(97433) class ConnectHandler extends AsyncResource { constructor (opts, callback) { @@ -9875,7 +9875,7 @@ module.exports = connect /***/ }), -/***/ 69785: +/***/ 281: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -9890,10 +9890,10 @@ const { InvalidArgumentError, InvalidReturnValueError, RequestAbortedError -} = __nccwpck_require__(5425) -const util = __nccwpck_require__(39141) +} = __nccwpck_require__(35990) +const util = __nccwpck_require__(50011) const { AsyncResource } = __nccwpck_require__(92761) -const { addSignal, removeSignal } = __nccwpck_require__(52872) +const { addSignal, removeSignal } = __nccwpck_require__(97433) const assert = __nccwpck_require__(98061) const kResume = Symbol('resume') @@ -10134,17 +10134,17 @@ module.exports = pipeline /***/ }), -/***/ 67177: +/***/ 26562: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; const assert = __nccwpck_require__(98061) -const { Readable } = __nccwpck_require__(12217) -const { InvalidArgumentError, RequestAbortedError } = __nccwpck_require__(5425) -const util = __nccwpck_require__(39141) -const { getResolveErrorBodyCallback } = __nccwpck_require__(23292) +const { Readable } = __nccwpck_require__(93401) +const { InvalidArgumentError, RequestAbortedError } = __nccwpck_require__(35990) +const util = __nccwpck_require__(50011) +const { getResolveErrorBodyCallback } = __nccwpck_require__(80710) const { AsyncResource } = __nccwpck_require__(92761) class RequestHandler extends AsyncResource { @@ -10356,7 +10356,7 @@ module.exports.RequestHandler = RequestHandler /***/ }), -/***/ 39777: +/***/ 75059: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -10364,11 +10364,11 @@ module.exports.RequestHandler = RequestHandler const assert = __nccwpck_require__(98061) const { finished, PassThrough } = __nccwpck_require__(84492) -const { InvalidArgumentError, InvalidReturnValueError } = __nccwpck_require__(5425) -const util = __nccwpck_require__(39141) -const { getResolveErrorBodyCallback } = __nccwpck_require__(23292) +const { InvalidArgumentError, InvalidReturnValueError } = __nccwpck_require__(35990) +const util = __nccwpck_require__(50011) +const { getResolveErrorBodyCallback } = __nccwpck_require__(80710) const { AsyncResource } = __nccwpck_require__(92761) -const { addSignal, removeSignal } = __nccwpck_require__(52872) +const { addSignal, removeSignal } = __nccwpck_require__(97433) class StreamHandler extends AsyncResource { constructor (opts, factory, callback) { @@ -10584,16 +10584,16 @@ module.exports = stream /***/ }), -/***/ 7981: +/***/ 23792: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { InvalidArgumentError, SocketError } = __nccwpck_require__(5425) +const { InvalidArgumentError, SocketError } = __nccwpck_require__(35990) const { AsyncResource } = __nccwpck_require__(92761) -const util = __nccwpck_require__(39141) -const { addSignal, removeSignal } = __nccwpck_require__(52872) +const util = __nccwpck_require__(50011) +const { addSignal, removeSignal } = __nccwpck_require__(97433) const assert = __nccwpck_require__(98061) class UpgradeHandler extends AsyncResource { @@ -10700,22 +10700,22 @@ module.exports = upgrade /***/ }), -/***/ 56796: +/***/ 20617: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -module.exports.request = __nccwpck_require__(67177) -module.exports.stream = __nccwpck_require__(39777) -module.exports.pipeline = __nccwpck_require__(69785) -module.exports.upgrade = __nccwpck_require__(7981) -module.exports.connect = __nccwpck_require__(15921) +module.exports.request = __nccwpck_require__(26562) +module.exports.stream = __nccwpck_require__(75059) +module.exports.pipeline = __nccwpck_require__(281) +module.exports.upgrade = __nccwpck_require__(23792) +module.exports.connect = __nccwpck_require__(93671) /***/ }), -/***/ 12217: +/***/ 93401: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -10725,9 +10725,9 @@ module.exports.connect = __nccwpck_require__(15921) const assert = __nccwpck_require__(98061) const { Readable } = __nccwpck_require__(84492) -const { RequestAbortedError, NotSupportedError, InvalidArgumentError, AbortError } = __nccwpck_require__(5425) -const util = __nccwpck_require__(39141) -const { ReadableStreamFrom } = __nccwpck_require__(39141) +const { RequestAbortedError, NotSupportedError, InvalidArgumentError, AbortError } = __nccwpck_require__(35990) +const util = __nccwpck_require__(50011) +const { ReadableStreamFrom } = __nccwpck_require__(50011) const kConsume = Symbol('kConsume') const kReading = Symbol('kReading') @@ -11108,15 +11108,15 @@ module.exports = { Readable: BodyReadable, chunksDecode } /***/ }), -/***/ 23292: +/***/ 80710: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { const assert = __nccwpck_require__(98061) const { ResponseStatusCodeError -} = __nccwpck_require__(5425) +} = __nccwpck_require__(35990) -const { chunksDecode } = __nccwpck_require__(12217) +const { chunksDecode } = __nccwpck_require__(93401) const CHUNK_LIMIT = 128 * 1024 async function getResolveErrorBodyCallback ({ callback, body, contentType, statusCode, statusMessage, headers }) { @@ -11208,7 +11208,7 @@ module.exports = { /***/ }), -/***/ 28786: +/***/ 41429: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -11216,9 +11216,9 @@ module.exports = { const net = __nccwpck_require__(87503) const assert = __nccwpck_require__(98061) -const util = __nccwpck_require__(39141) -const { InvalidArgumentError, ConnectTimeoutError } = __nccwpck_require__(5425) -const timers = __nccwpck_require__(52372) +const util = __nccwpck_require__(50011) +const { InvalidArgumentError, ConnectTimeoutError } = __nccwpck_require__(35990) +const timers = __nccwpck_require__(77512) function noop () {} @@ -11456,7 +11456,7 @@ module.exports = buildConnector /***/ }), -/***/ 41233: +/***/ 53451: /***/ ((module) => { "use strict"; @@ -11582,7 +11582,7 @@ module.exports = { /***/ }), -/***/ 59241: +/***/ 65543: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -11792,7 +11792,7 @@ module.exports = { /***/ }), -/***/ 5425: +/***/ 35990: /***/ ((module) => { "use strict"; @@ -12225,7 +12225,7 @@ module.exports = { /***/ }), -/***/ 48356: +/***/ 13484: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -12234,7 +12234,7 @@ module.exports = { const { InvalidArgumentError, NotSupportedError -} = __nccwpck_require__(5425) +} = __nccwpck_require__(35990) const assert = __nccwpck_require__(98061) const { isValidHTTPToken, @@ -12249,9 +12249,9 @@ const { validateHandler, getServerName, normalizedMethodRecords -} = __nccwpck_require__(39141) -const { channels } = __nccwpck_require__(59241) -const { headerNameLowerCasedRecord } = __nccwpck_require__(41233) +} = __nccwpck_require__(50011) +const { channels } = __nccwpck_require__(65543) +const { headerNameLowerCasedRecord } = __nccwpck_require__(53451) // Verifies that a given path is valid does not contain control chars \x00 to \x20 const invalidPathRegex = /[^\u0021-\u00ff]/ @@ -12649,7 +12649,7 @@ module.exports = Request /***/ }), -/***/ 53606: +/***/ 13638: /***/ ((module) => { module.exports = { @@ -12723,7 +12723,7 @@ module.exports = { /***/ }), -/***/ 71228: +/***/ 30723: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -12732,7 +12732,7 @@ module.exports = { const { wellknownHeaderNames, headerNameLowerCasedRecord -} = __nccwpck_require__(41233) +} = __nccwpck_require__(53451) class TstNode { /** @type {any} */ @@ -12883,14 +12883,14 @@ module.exports = { /***/ }), -/***/ 39141: +/***/ 50011: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; const assert = __nccwpck_require__(98061) -const { kDestroyed, kBodyUsed, kListeners, kBody } = __nccwpck_require__(53606) +const { kDestroyed, kBodyUsed, kListeners, kBody } = __nccwpck_require__(13638) const { IncomingMessage } = __nccwpck_require__(88849) const stream = __nccwpck_require__(84492) const net = __nccwpck_require__(87503) @@ -12898,9 +12898,9 @@ const { Blob } = __nccwpck_require__(72254) const nodeUtil = __nccwpck_require__(47261) const { stringify } = __nccwpck_require__(39630) const { EventEmitter: EE } = __nccwpck_require__(15673) -const { InvalidArgumentError } = __nccwpck_require__(5425) -const { headerNameLowerCasedRecord } = __nccwpck_require__(41233) -const { tree } = __nccwpck_require__(71228) +const { InvalidArgumentError } = __nccwpck_require__(35990) +const { headerNameLowerCasedRecord } = __nccwpck_require__(53451) +const { tree } = __nccwpck_require__(30723) const [nodeMajor, nodeMinor] = process.versions.node.split('.').map(v => Number(v)) @@ -13610,19 +13610,19 @@ module.exports = { /***/ }), -/***/ 78590: +/***/ 73274: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { InvalidArgumentError } = __nccwpck_require__(5425) -const { kClients, kRunning, kClose, kDestroy, kDispatch, kInterceptors } = __nccwpck_require__(53606) -const DispatcherBase = __nccwpck_require__(92548) -const Pool = __nccwpck_require__(3639) -const Client = __nccwpck_require__(41234) -const util = __nccwpck_require__(39141) -const createRedirectInterceptor = __nccwpck_require__(96329) +const { InvalidArgumentError } = __nccwpck_require__(35990) +const { kClients, kRunning, kClose, kDestroy, kDispatch, kInterceptors } = __nccwpck_require__(13638) +const DispatcherBase = __nccwpck_require__(39504) +const Pool = __nccwpck_require__(80229) +const Client = __nccwpck_require__(85849) +const util = __nccwpck_require__(50011) +const createRedirectInterceptor = __nccwpck_require__(40928) const kOnConnect = Symbol('onConnect') const kOnDisconnect = Symbol('onDisconnect') @@ -13747,7 +13747,7 @@ module.exports = Agent /***/ }), -/***/ 58591: +/***/ 68255: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -13756,7 +13756,7 @@ module.exports = Agent const { BalancedPoolMissingUpstreamError, InvalidArgumentError -} = __nccwpck_require__(5425) +} = __nccwpck_require__(35990) const { PoolBase, kClients, @@ -13764,10 +13764,10 @@ const { kAddClient, kRemoveClient, kGetDispatcher -} = __nccwpck_require__(24458) -const Pool = __nccwpck_require__(3639) -const { kUrl, kInterceptors } = __nccwpck_require__(53606) -const { parseOrigin } = __nccwpck_require__(39141) +} = __nccwpck_require__(1467) +const Pool = __nccwpck_require__(80229) +const { kUrl, kInterceptors } = __nccwpck_require__(13638) +const { parseOrigin } = __nccwpck_require__(50011) const kFactory = Symbol('factory') const kOptions = Symbol('options') @@ -13964,7 +13964,7 @@ module.exports = BalancedPool /***/ }), -/***/ 9581: +/***/ 14429: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -13973,9 +13973,9 @@ module.exports = BalancedPool /* global WebAssembly */ const assert = __nccwpck_require__(98061) -const util = __nccwpck_require__(39141) -const { channels } = __nccwpck_require__(59241) -const timers = __nccwpck_require__(52372) +const util = __nccwpck_require__(50011) +const { channels } = __nccwpck_require__(65543) +const timers = __nccwpck_require__(77512) const { RequestContentLengthMismatchError, ResponseContentLengthMismatchError, @@ -13988,7 +13988,7 @@ const { BodyTimeoutError, HTTPParserError, ResponseExceededMaxSizeError -} = __nccwpck_require__(5425) +} = __nccwpck_require__(35990) const { kUrl, kReset, @@ -14021,9 +14021,9 @@ const { kOnError, kResume, kHTTPContext -} = __nccwpck_require__(53606) +} = __nccwpck_require__(13638) -const constants = __nccwpck_require__(41227) +const constants = __nccwpck_require__(41721) const EMPTY_BUF = Buffer.alloc(0) const FastBuffer = Buffer[Symbol.species] const addListener = util.addListener @@ -14035,11 +14035,11 @@ const kSocketUsed = Symbol('kSocketUsed') let extractBody async function lazyllhttp () { - const llhttpWasmData = process.env.JEST_WORKER_ID ? __nccwpck_require__(72134) : undefined + const llhttpWasmData = process.env.JEST_WORKER_ID ? __nccwpck_require__(46081) : undefined let mod try { - mod = await WebAssembly.compile(__nccwpck_require__(81820)) + mod = await WebAssembly.compile(__nccwpck_require__(97877)) } catch (e) { /* istanbul ignore next */ @@ -14047,7 +14047,7 @@ async function lazyllhttp () { // being enabled, but the occurring of this other error // * https://github.com/emscripten-core/emscripten/issues/11495 // got me to remove that check to avoid breaking Node 12. - mod = await WebAssembly.compile(llhttpWasmData || __nccwpck_require__(72134)) + mod = await WebAssembly.compile(llhttpWasmData || __nccwpck_require__(46081)) } return await WebAssembly.instantiate(mod, { @@ -14963,7 +14963,7 @@ function writeH1 (client, request) { if (util.isFormDataLike(body)) { if (!extractBody) { - extractBody = (__nccwpck_require__(27134).extractBody) + extractBody = (__nccwpck_require__(12749).extractBody) } const [bodyStream, contentType] = extractBody(body) @@ -15476,7 +15476,7 @@ module.exports = connectH1 /***/ }), -/***/ 2003: +/***/ 34879: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -15484,13 +15484,13 @@ module.exports = connectH1 const assert = __nccwpck_require__(98061) const { pipeline } = __nccwpck_require__(84492) -const util = __nccwpck_require__(39141) +const util = __nccwpck_require__(50011) const { RequestContentLengthMismatchError, RequestAbortedError, SocketError, InformationalError -} = __nccwpck_require__(5425) +} = __nccwpck_require__(35990) const { kUrl, kReset, @@ -15509,7 +15509,7 @@ const { kResume, kSize, kHTTPContext -} = __nccwpck_require__(53606) +} = __nccwpck_require__(13638) const kOpenStreams = Symbol('open streams') @@ -15868,7 +15868,7 @@ function writeH2 (client, request) { let contentLength = util.bodyLength(body) if (util.isFormDataLike(body)) { - extractBody ??= (__nccwpck_require__(27134).extractBody) + extractBody ??= (__nccwpck_require__(12749).extractBody) const [bodyStream, contentType] = extractBody(body) headers['content-type'] = contentType @@ -16228,7 +16228,7 @@ module.exports = connectH2 /***/ }), -/***/ 41234: +/***/ 85849: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -16239,16 +16239,16 @@ module.exports = connectH2 const assert = __nccwpck_require__(98061) const net = __nccwpck_require__(87503) const http = __nccwpck_require__(88849) -const util = __nccwpck_require__(39141) -const { channels } = __nccwpck_require__(59241) -const Request = __nccwpck_require__(48356) -const DispatcherBase = __nccwpck_require__(92548) +const util = __nccwpck_require__(50011) +const { channels } = __nccwpck_require__(65543) +const Request = __nccwpck_require__(13484) +const DispatcherBase = __nccwpck_require__(39504) const { InvalidArgumentError, InformationalError, ClientDestroyedError -} = __nccwpck_require__(5425) -const buildConnector = __nccwpck_require__(28786) +} = __nccwpck_require__(35990) +const buildConnector = __nccwpck_require__(41429) const { kUrl, kServerName, @@ -16290,9 +16290,9 @@ const { kHTTPContext, kMaxConcurrentStreams, kResume -} = __nccwpck_require__(53606) -const connectH1 = __nccwpck_require__(9581) -const connectH2 = __nccwpck_require__(2003) +} = __nccwpck_require__(13638) +const connectH1 = __nccwpck_require__(14429) +const connectH2 = __nccwpck_require__(34879) let deprecatedInterceptorWarned = false const kClosedResolve = Symbol('kClosedResolve') @@ -16599,7 +16599,7 @@ class Client extends DispatcherBase { } } -const createRedirectInterceptor = __nccwpck_require__(96329) +const createRedirectInterceptor = __nccwpck_require__(40928) function onError (client, err) { if ( @@ -16859,19 +16859,19 @@ module.exports = Client /***/ }), -/***/ 92548: +/***/ 39504: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const Dispatcher = __nccwpck_require__(11588) +const Dispatcher = __nccwpck_require__(66071) const { ClientDestroyedError, ClientClosedError, InvalidArgumentError -} = __nccwpck_require__(5425) -const { kDestroy, kClose, kClosed, kDestroyed, kDispatch, kInterceptors } = __nccwpck_require__(53606) +} = __nccwpck_require__(35990) +const { kDestroy, kClose, kClosed, kDestroyed, kDispatch, kInterceptors } = __nccwpck_require__(13638) const kOnDestroyed = Symbol('onDestroyed') const kOnClosed = Symbol('onClosed') @@ -17066,7 +17066,7 @@ module.exports = DispatcherBase /***/ }), -/***/ 11588: +/***/ 66071: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -17139,16 +17139,16 @@ module.exports = Dispatcher /***/ }), -/***/ 52673: +/***/ 29941: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const DispatcherBase = __nccwpck_require__(92548) -const { kClose, kDestroy, kClosed, kDestroyed, kDispatch, kNoProxyAgent, kHttpProxyAgent, kHttpsProxyAgent } = __nccwpck_require__(53606) -const ProxyAgent = __nccwpck_require__(76930) -const Agent = __nccwpck_require__(78590) +const DispatcherBase = __nccwpck_require__(39504) +const { kClose, kDestroy, kClosed, kDestroyed, kDispatch, kNoProxyAgent, kHttpProxyAgent, kHttpsProxyAgent } = __nccwpck_require__(13638) +const ProxyAgent = __nccwpck_require__(87187) +const Agent = __nccwpck_require__(73274) const DEFAULT_PORTS = { 'http:': 80, @@ -17307,7 +17307,7 @@ module.exports = EnvHttpProxyAgent /***/ }), -/***/ 79468: +/***/ 27092: /***/ ((module) => { "use strict"; @@ -17432,16 +17432,16 @@ module.exports = class FixedQueue { /***/ }), -/***/ 24458: +/***/ 1467: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const DispatcherBase = __nccwpck_require__(92548) -const FixedQueue = __nccwpck_require__(79468) -const { kConnected, kSize, kRunning, kPending, kQueued, kBusy, kFree, kUrl, kClose, kDestroy, kDispatch } = __nccwpck_require__(53606) -const PoolStats = __nccwpck_require__(41872) +const DispatcherBase = __nccwpck_require__(39504) +const FixedQueue = __nccwpck_require__(27092) +const { kConnected, kSize, kRunning, kPending, kQueued, kBusy, kFree, kUrl, kClose, kDestroy, kDispatch } = __nccwpck_require__(13638) +const PoolStats = __nccwpck_require__(48309) const kClients = Symbol('clients') const kNeedDrain = Symbol('needDrain') @@ -17634,10 +17634,10 @@ module.exports = { /***/ }), -/***/ 41872: +/***/ 48309: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -const { kFree, kConnected, kPending, kQueued, kRunning, kSize } = __nccwpck_require__(53606) +const { kFree, kConnected, kPending, kQueued, kRunning, kSize } = __nccwpck_require__(13638) const kPool = Symbol('pool') class PoolStats { @@ -17675,7 +17675,7 @@ module.exports = PoolStats /***/ }), -/***/ 3639: +/***/ 80229: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -17687,14 +17687,14 @@ const { kNeedDrain, kAddClient, kGetDispatcher -} = __nccwpck_require__(24458) -const Client = __nccwpck_require__(41234) +} = __nccwpck_require__(1467) +const Client = __nccwpck_require__(85849) const { InvalidArgumentError -} = __nccwpck_require__(5425) -const util = __nccwpck_require__(39141) -const { kUrl, kInterceptors } = __nccwpck_require__(53606) -const buildConnector = __nccwpck_require__(28786) +} = __nccwpck_require__(35990) +const util = __nccwpck_require__(50011) +const { kUrl, kInterceptors } = __nccwpck_require__(13638) +const buildConnector = __nccwpck_require__(41429) const kOptions = Symbol('options') const kConnections = Symbol('connections') @@ -17790,20 +17790,20 @@ module.exports = Pool /***/ }), -/***/ 76930: +/***/ 87187: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { kProxy, kClose, kDestroy, kDispatch, kInterceptors } = __nccwpck_require__(53606) +const { kProxy, kClose, kDestroy, kDispatch, kInterceptors } = __nccwpck_require__(13638) const { URL } = __nccwpck_require__(41041) -const Agent = __nccwpck_require__(78590) -const Pool = __nccwpck_require__(3639) -const DispatcherBase = __nccwpck_require__(92548) -const { InvalidArgumentError, RequestAbortedError, SecureProxyConnectionError } = __nccwpck_require__(5425) -const buildConnector = __nccwpck_require__(28786) -const Client = __nccwpck_require__(41234) +const Agent = __nccwpck_require__(73274) +const Pool = __nccwpck_require__(80229) +const DispatcherBase = __nccwpck_require__(39504) +const { InvalidArgumentError, RequestAbortedError, SecureProxyConnectionError } = __nccwpck_require__(35990) +const buildConnector = __nccwpck_require__(41429) +const Client = __nccwpck_require__(85849) const kAgent = Symbol('proxy agent') const kClient = Symbol('proxy client') @@ -18072,14 +18072,14 @@ module.exports = ProxyAgent /***/ }), -/***/ 4420: +/***/ 55184: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const Dispatcher = __nccwpck_require__(11588) -const RetryHandler = __nccwpck_require__(30160) +const Dispatcher = __nccwpck_require__(66071) +const RetryHandler = __nccwpck_require__(64524) class RetryAgent extends Dispatcher { #agent = null @@ -18115,7 +18115,7 @@ module.exports = RetryAgent /***/ }), -/***/ 12621: +/***/ 19405: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -18124,8 +18124,8 @@ module.exports = RetryAgent // We include a version number for the Dispatcher API. In case of breaking changes, // this version number must be increased to avoid conflicts. const globalDispatcher = Symbol.for('undici.globalDispatcher.1') -const { InvalidArgumentError } = __nccwpck_require__(5425) -const Agent = __nccwpck_require__(78590) +const { InvalidArgumentError } = __nccwpck_require__(35990) +const Agent = __nccwpck_require__(73274) if (getGlobalDispatcher() === undefined) { setGlobalDispatcher(new Agent()) @@ -18155,7 +18155,7 @@ module.exports = { /***/ }), -/***/ 63859: +/***/ 41738: /***/ ((module) => { "use strict"; @@ -18207,16 +18207,16 @@ module.exports = class DecoratorHandler { /***/ }), -/***/ 71779: +/***/ 64014: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const util = __nccwpck_require__(39141) -const { kBodyUsed } = __nccwpck_require__(53606) +const util = __nccwpck_require__(50011) +const { kBodyUsed } = __nccwpck_require__(13638) const assert = __nccwpck_require__(98061) -const { InvalidArgumentError } = __nccwpck_require__(5425) +const { InvalidArgumentError } = __nccwpck_require__(35990) const EE = __nccwpck_require__(15673) const redirectableStatusCodes = [300, 301, 302, 303, 307, 308] @@ -18447,21 +18447,21 @@ module.exports = RedirectHandler /***/ }), -/***/ 30160: +/***/ 64524: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; const assert = __nccwpck_require__(98061) -const { kRetryHandlerDefaultRetry } = __nccwpck_require__(53606) -const { RequestRetryError } = __nccwpck_require__(5425) +const { kRetryHandlerDefaultRetry } = __nccwpck_require__(13638) +const { RequestRetryError } = __nccwpck_require__(35990) const { isDisturbed, parseHeaders, parseRangeHeader, wrapRequestBody -} = __nccwpck_require__(39141) +} = __nccwpck_require__(50011) function calculateRetryAfterHeader (retryAfter) { const current = Date.now() @@ -18889,15 +18889,15 @@ module.exports = RetryHandler /***/ }), -/***/ 58033: +/***/ 20346: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; const { isIP } = __nccwpck_require__(87503) const { lookup } = __nccwpck_require__(30604) -const DecoratorHandler = __nccwpck_require__(63859) -const { InvalidArgumentError, InformationalError } = __nccwpck_require__(5425) +const DecoratorHandler = __nccwpck_require__(41738) +const { InvalidArgumentError, InformationalError } = __nccwpck_require__(35990) const maxInt = Math.pow(2, 31) - 1 class DNSInstance { @@ -19272,15 +19272,15 @@ module.exports = interceptorOpts => { /***/ }), -/***/ 13413: +/***/ 12493: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const util = __nccwpck_require__(39141) -const { InvalidArgumentError, RequestAbortedError } = __nccwpck_require__(5425) -const DecoratorHandler = __nccwpck_require__(63859) +const util = __nccwpck_require__(50011) +const { InvalidArgumentError, RequestAbortedError } = __nccwpck_require__(35990) +const DecoratorHandler = __nccwpck_require__(41738) class DumpHandler extends DecoratorHandler { #maxSize = 1024 * 1024 @@ -19403,13 +19403,13 @@ module.exports = createDumpInterceptor /***/ }), -/***/ 96329: +/***/ 40928: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const RedirectHandler = __nccwpck_require__(71779) +const RedirectHandler = __nccwpck_require__(64014) function createRedirectInterceptor ({ maxRedirections: defaultMaxRedirections }) { return (dispatch) => { @@ -19432,12 +19432,12 @@ module.exports = createRedirectInterceptor /***/ }), -/***/ 37849: +/***/ 74872: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const RedirectHandler = __nccwpck_require__(71779) +const RedirectHandler = __nccwpck_require__(64014) module.exports = opts => { const globalMaxRedirections = opts?.maxRedirections @@ -19464,12 +19464,12 @@ module.exports = opts => { /***/ }), -/***/ 19182: +/***/ 79637: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const RetryHandler = __nccwpck_require__(30160) +const RetryHandler = __nccwpck_require__(64524) module.exports = globalOpts => { return dispatch => { @@ -19491,14 +19491,14 @@ module.exports = globalOpts => { /***/ }), -/***/ 41227: +/***/ 41721: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SPECIAL_HEADERS = exports.HEADER_STATE = exports.MINOR = exports.MAJOR = exports.CONNECTION_TOKEN_CHARS = exports.HEADER_CHARS = exports.TOKEN = exports.STRICT_TOKEN = exports.HEX = exports.URL_CHAR = exports.STRICT_URL_CHAR = exports.USERINFO_CHARS = exports.MARK = exports.ALPHANUM = exports.NUM = exports.HEX_MAP = exports.NUM_MAP = exports.ALPHA = exports.FINISH = exports.H_METHOD_MAP = exports.METHOD_MAP = exports.METHODS_RTSP = exports.METHODS_ICE = exports.METHODS_HTTP = exports.METHODS = exports.LENIENT_FLAGS = exports.FLAGS = exports.TYPE = exports.ERROR = void 0; -const utils_1 = __nccwpck_require__(8318); +const utils_1 = __nccwpck_require__(69573); // C headers var ERROR; (function (ERROR) { @@ -19776,7 +19776,7 @@ exports.SPECIAL_HEADERS = { /***/ }), -/***/ 72134: +/***/ 46081: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -19789,7 +19789,7 @@ module.exports = Buffer.from('AGFzbQEAAAABJwdgAX8Bf2ADf39/AX9gAX8AYAJ/fwBgBH9/f3 /***/ }), -/***/ 81820: +/***/ 97877: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -19802,7 +19802,7 @@ module.exports = Buffer.from('AGFzbQEAAAABJwdgAX8Bf2ADf39/AX9gAX8AYAJ/fwBgBH9/f3 /***/ }), -/***/ 8318: +/***/ 69573: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -19824,14 +19824,14 @@ exports.enumToMap = enumToMap; /***/ }), -/***/ 36294: +/***/ 46432: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { kClients } = __nccwpck_require__(53606) -const Agent = __nccwpck_require__(78590) +const { kClients } = __nccwpck_require__(13638) +const Agent = __nccwpck_require__(73274) const { kAgent, kMockAgentSet, @@ -19842,14 +19842,14 @@ const { kGetNetConnect, kOptions, kFactory -} = __nccwpck_require__(53353) -const MockClient = __nccwpck_require__(56607) -const MockPool = __nccwpck_require__(60834) -const { matchValue, buildMockOptions } = __nccwpck_require__(58405) -const { InvalidArgumentError, UndiciError } = __nccwpck_require__(5425) -const Dispatcher = __nccwpck_require__(11588) -const Pluralizer = __nccwpck_require__(90583) -const PendingInterceptorsFormatter = __nccwpck_require__(63535) +} = __nccwpck_require__(63822) +const MockClient = __nccwpck_require__(4227) +const MockPool = __nccwpck_require__(36575) +const { matchValue, buildMockOptions } = __nccwpck_require__(38053) +const { InvalidArgumentError, UndiciError } = __nccwpck_require__(35990) +const Dispatcher = __nccwpck_require__(66071) +const Pluralizer = __nccwpck_require__(97472) +const PendingInterceptorsFormatter = __nccwpck_require__(56155) class MockAgent extends Dispatcher { constructor (opts) { @@ -19992,15 +19992,15 @@ module.exports = MockAgent /***/ }), -/***/ 56607: +/***/ 4227: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; const { promisify } = __nccwpck_require__(47261) -const Client = __nccwpck_require__(41234) -const { buildMockDispatch } = __nccwpck_require__(58405) +const Client = __nccwpck_require__(85849) +const { buildMockDispatch } = __nccwpck_require__(38053) const { kDispatches, kMockAgent, @@ -20009,10 +20009,10 @@ const { kOrigin, kOriginalDispatch, kConnected -} = __nccwpck_require__(53353) -const { MockInterceptor } = __nccwpck_require__(14791) -const Symbols = __nccwpck_require__(53606) -const { InvalidArgumentError } = __nccwpck_require__(5425) +} = __nccwpck_require__(63822) +const { MockInterceptor } = __nccwpck_require__(9238) +const Symbols = __nccwpck_require__(13638) +const { InvalidArgumentError } = __nccwpck_require__(35990) /** * MockClient provides an API that extends the Client to influence the mockDispatches. @@ -20059,13 +20059,13 @@ module.exports = MockClient /***/ }), -/***/ 63739: +/***/ 19329: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { UndiciError } = __nccwpck_require__(5425) +const { UndiciError } = __nccwpck_require__(35990) const kMockNotMatchedError = Symbol.for('undici.error.UND_MOCK_ERR_MOCK_NOT_MATCHED') @@ -20095,13 +20095,13 @@ module.exports = { /***/ }), -/***/ 14791: +/***/ 9238: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { getResponseData, buildKey, addMockDispatch } = __nccwpck_require__(58405) +const { getResponseData, buildKey, addMockDispatch } = __nccwpck_require__(38053) const { kDispatches, kDispatchKey, @@ -20109,9 +20109,9 @@ const { kDefaultTrailers, kContentLength, kMockDispatch -} = __nccwpck_require__(53353) -const { InvalidArgumentError } = __nccwpck_require__(5425) -const { buildURL } = __nccwpck_require__(39141) +} = __nccwpck_require__(63822) +const { InvalidArgumentError } = __nccwpck_require__(35990) +const { buildURL } = __nccwpck_require__(50011) /** * Defines the scope API for an interceptor reply @@ -20310,15 +20310,15 @@ module.exports.MockScope = MockScope /***/ }), -/***/ 60834: +/***/ 36575: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; const { promisify } = __nccwpck_require__(47261) -const Pool = __nccwpck_require__(3639) -const { buildMockDispatch } = __nccwpck_require__(58405) +const Pool = __nccwpck_require__(80229) +const { buildMockDispatch } = __nccwpck_require__(38053) const { kDispatches, kMockAgent, @@ -20327,10 +20327,10 @@ const { kOrigin, kOriginalDispatch, kConnected -} = __nccwpck_require__(53353) -const { MockInterceptor } = __nccwpck_require__(14791) -const Symbols = __nccwpck_require__(53606) -const { InvalidArgumentError } = __nccwpck_require__(5425) +} = __nccwpck_require__(63822) +const { MockInterceptor } = __nccwpck_require__(9238) +const Symbols = __nccwpck_require__(13638) +const { InvalidArgumentError } = __nccwpck_require__(35990) /** * MockPool provides an API that extends the Pool to influence the mockDispatches. @@ -20377,7 +20377,7 @@ module.exports = MockPool /***/ }), -/***/ 53353: +/***/ 63822: /***/ ((module) => { "use strict"; @@ -20408,21 +20408,21 @@ module.exports = { /***/ }), -/***/ 58405: +/***/ 38053: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { MockNotMatchedError } = __nccwpck_require__(63739) +const { MockNotMatchedError } = __nccwpck_require__(19329) const { kDispatches, kMockAgent, kOriginalDispatch, kOrigin, kGetNetConnect -} = __nccwpck_require__(53353) -const { buildURL } = __nccwpck_require__(39141) +} = __nccwpck_require__(63822) +const { buildURL } = __nccwpck_require__(50011) const { STATUS_CODES } = __nccwpck_require__(88849) const { types: { @@ -20783,7 +20783,7 @@ module.exports = { /***/ }), -/***/ 63535: +/***/ 56155: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -20834,7 +20834,7 @@ module.exports = class PendingInterceptorsFormatter { /***/ }), -/***/ 90583: +/***/ 97472: /***/ ((module) => { "use strict"; @@ -20871,7 +20871,7 @@ module.exports = class Pluralizer { /***/ }), -/***/ 52372: +/***/ 77512: /***/ ((module) => { "use strict"; @@ -21302,21 +21302,21 @@ module.exports = { /***/ }), -/***/ 34713: +/***/ 12714: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { kConstruct } = __nccwpck_require__(93639) -const { urlEquals, getFieldValues } = __nccwpck_require__(52789) -const { kEnumerableProperty, isDisturbed } = __nccwpck_require__(39141) -const { webidl } = __nccwpck_require__(69293) -const { Response, cloneResponse, fromInnerResponse } = __nccwpck_require__(98579) -const { Request, fromInnerRequest } = __nccwpck_require__(3891) -const { kState } = __nccwpck_require__(15575) -const { fetching } = __nccwpck_require__(9526) -const { urlIsHttpHttpsScheme, createDeferredPromise, readAllBytes } = __nccwpck_require__(37458) +const { kConstruct } = __nccwpck_require__(50591) +const { urlEquals, getFieldValues } = __nccwpck_require__(99205) +const { kEnumerableProperty, isDisturbed } = __nccwpck_require__(50011) +const { webidl } = __nccwpck_require__(2227) +const { Response, cloneResponse, fromInnerResponse } = __nccwpck_require__(51132) +const { Request, fromInnerRequest } = __nccwpck_require__(83211) +const { kState } = __nccwpck_require__(14935) +const { fetching } = __nccwpck_require__(78329) +const { urlIsHttpHttpsScheme, createDeferredPromise, readAllBytes } = __nccwpck_require__(98730) const assert = __nccwpck_require__(98061) /** @@ -22169,16 +22169,16 @@ module.exports = { /***/ }), -/***/ 10471: +/***/ 11069: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { kConstruct } = __nccwpck_require__(93639) -const { Cache } = __nccwpck_require__(34713) -const { webidl } = __nccwpck_require__(69293) -const { kEnumerableProperty } = __nccwpck_require__(39141) +const { kConstruct } = __nccwpck_require__(50591) +const { Cache } = __nccwpck_require__(12714) +const { webidl } = __nccwpck_require__(2227) +const { kEnumerableProperty } = __nccwpck_require__(50011) class CacheStorage { /** @@ -22329,28 +22329,28 @@ module.exports = { /***/ }), -/***/ 93639: +/***/ 50591: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; module.exports = { - kConstruct: (__nccwpck_require__(53606).kConstruct) + kConstruct: (__nccwpck_require__(13638).kConstruct) } /***/ }), -/***/ 52789: +/***/ 99205: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; const assert = __nccwpck_require__(98061) -const { URLSerializer } = __nccwpck_require__(29192) -const { isValidHeaderName } = __nccwpck_require__(37458) +const { URLSerializer } = __nccwpck_require__(96730) +const { isValidHeaderName } = __nccwpck_require__(98730) /** * @see https://url.spec.whatwg.org/#concept-url-equals @@ -22395,7 +22395,7 @@ module.exports = { /***/ }), -/***/ 40384: +/***/ 16155: /***/ ((module) => { "use strict"; @@ -22415,16 +22415,16 @@ module.exports = { /***/ }), -/***/ 30035: +/***/ 15855: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { parseSetCookie } = __nccwpck_require__(94017) -const { stringify } = __nccwpck_require__(82559) -const { webidl } = __nccwpck_require__(69293) -const { Headers } = __nccwpck_require__(66089) +const { parseSetCookie } = __nccwpck_require__(80742) +const { stringify } = __nccwpck_require__(93989) +const { webidl } = __nccwpck_require__(2227) +const { Headers } = __nccwpck_require__(10561) /** * @typedef {Object} Cookie @@ -22607,15 +22607,15 @@ module.exports = { /***/ }), -/***/ 94017: +/***/ 80742: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { maxNameValuePairSize, maxAttributeValueSize } = __nccwpck_require__(40384) -const { isCTLExcludingHtab } = __nccwpck_require__(82559) -const { collectASequenceOfCodePointsFast } = __nccwpck_require__(29192) +const { maxNameValuePairSize, maxAttributeValueSize } = __nccwpck_require__(16155) +const { isCTLExcludingHtab } = __nccwpck_require__(93989) +const { collectASequenceOfCodePointsFast } = __nccwpck_require__(96730) const assert = __nccwpck_require__(98061) /** @@ -22925,7 +22925,7 @@ module.exports = { /***/ }), -/***/ 82559: +/***/ 93989: /***/ ((module) => { "use strict"; @@ -23285,13 +23285,13 @@ module.exports = { /***/ }), -/***/ 65199: +/***/ 41408: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; const { Transform } = __nccwpck_require__(84492) -const { isASCIINumber, isValidLastEventId } = __nccwpck_require__(44665) +const { isASCIINumber, isValidLastEventId } = __nccwpck_require__(19079) /** * @type {number[]} BOM @@ -23786,23 +23786,23 @@ module.exports = { /***/ }), -/***/ 90109: +/***/ 6731: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; const { pipeline } = __nccwpck_require__(84492) -const { fetching } = __nccwpck_require__(9526) -const { makeRequest } = __nccwpck_require__(3891) -const { webidl } = __nccwpck_require__(69293) -const { EventSourceStream } = __nccwpck_require__(65199) -const { parseMIMEType } = __nccwpck_require__(29192) -const { createFastMessageEvent } = __nccwpck_require__(46055) -const { isNetworkError } = __nccwpck_require__(98579) -const { delay } = __nccwpck_require__(44665) -const { kEnumerableProperty } = __nccwpck_require__(39141) -const { environmentSettingsObject } = __nccwpck_require__(37458) +const { fetching } = __nccwpck_require__(78329) +const { makeRequest } = __nccwpck_require__(83211) +const { webidl } = __nccwpck_require__(2227) +const { EventSourceStream } = __nccwpck_require__(41408) +const { parseMIMEType } = __nccwpck_require__(96730) +const { createFastMessageEvent } = __nccwpck_require__(69459) +const { isNetworkError } = __nccwpck_require__(51132) +const { delay } = __nccwpck_require__(19079) +const { kEnumerableProperty } = __nccwpck_require__(50011) +const { environmentSettingsObject } = __nccwpck_require__(98730) let experimentalWarned = false @@ -24274,7 +24274,7 @@ module.exports = { /***/ }), -/***/ 44665: +/***/ 19079: /***/ ((module) => { "use strict"; @@ -24319,13 +24319,13 @@ module.exports = { /***/ }), -/***/ 27134: +/***/ 12749: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const util = __nccwpck_require__(39141) +const util = __nccwpck_require__(50011) const { ReadableStreamFrom, isBlobLike, @@ -24335,16 +24335,16 @@ const { fullyReadBody, extractMimeType, utf8DecodeBytes -} = __nccwpck_require__(37458) -const { FormData } = __nccwpck_require__(26697) -const { kState } = __nccwpck_require__(15575) -const { webidl } = __nccwpck_require__(69293) +} = __nccwpck_require__(98730) +const { FormData } = __nccwpck_require__(62598) +const { kState } = __nccwpck_require__(14935) +const { webidl } = __nccwpck_require__(2227) const { Blob } = __nccwpck_require__(72254) const assert = __nccwpck_require__(98061) const { isErrored, isDisturbed } = __nccwpck_require__(84492) const { isArrayBuffer } = __nccwpck_require__(93746) -const { serializeAMimeType } = __nccwpck_require__(29192) -const { multipartFormDataParser } = __nccwpck_require__(48294) +const { serializeAMimeType } = __nccwpck_require__(96730) +const { multipartFormDataParser } = __nccwpck_require__(25152) let random try { @@ -24856,7 +24856,7 @@ module.exports = { /***/ }), -/***/ 83686: +/***/ 54823: /***/ ((module) => { "use strict"; @@ -24988,7 +24988,7 @@ module.exports = { /***/ }), -/***/ 29192: +/***/ 96730: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -25740,13 +25740,13 @@ module.exports = { /***/ }), -/***/ 58330: +/***/ 61451: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { kConnected, kSize } = __nccwpck_require__(53606) +const { kConnected, kSize } = __nccwpck_require__(13638) class CompatWeakRef { constructor (value) { @@ -25794,15 +25794,15 @@ module.exports = function () { /***/ }), -/***/ 37170: +/***/ 60027: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; const { Blob, File } = __nccwpck_require__(72254) -const { kState } = __nccwpck_require__(15575) -const { webidl } = __nccwpck_require__(69293) +const { kState } = __nccwpck_require__(14935) +const { webidl } = __nccwpck_require__(2227) // TODO(@KhafraDev): remove class FileLike { @@ -25928,17 +25928,17 @@ module.exports = { FileLike, isFileLike } /***/ }), -/***/ 48294: +/***/ 25152: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { isUSVString, bufferToLowerCasedHeaderName } = __nccwpck_require__(39141) -const { utf8DecodeBytes } = __nccwpck_require__(37458) -const { HTTP_TOKEN_CODEPOINTS, isomorphicDecode } = __nccwpck_require__(29192) -const { isFileLike } = __nccwpck_require__(37170) -const { makeEntry } = __nccwpck_require__(26697) +const { isUSVString, bufferToLowerCasedHeaderName } = __nccwpck_require__(50011) +const { utf8DecodeBytes } = __nccwpck_require__(98730) +const { HTTP_TOKEN_CODEPOINTS, isomorphicDecode } = __nccwpck_require__(96730) +const { isFileLike } = __nccwpck_require__(60027) +const { makeEntry } = __nccwpck_require__(62598) const assert = __nccwpck_require__(98061) const { File: NodeFile } = __nccwpck_require__(72254) @@ -26410,17 +26410,17 @@ module.exports = { /***/ }), -/***/ 26697: +/***/ 62598: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { isBlobLike, iteratorMixin } = __nccwpck_require__(37458) -const { kState } = __nccwpck_require__(15575) -const { kEnumerableProperty } = __nccwpck_require__(39141) -const { FileLike, isFileLike } = __nccwpck_require__(37170) -const { webidl } = __nccwpck_require__(69293) +const { isBlobLike, iteratorMixin } = __nccwpck_require__(98730) +const { kState } = __nccwpck_require__(14935) +const { kEnumerableProperty } = __nccwpck_require__(50011) +const { FileLike, isFileLike } = __nccwpck_require__(60027) +const { webidl } = __nccwpck_require__(2227) const { File: NativeFile } = __nccwpck_require__(72254) const nodeUtil = __nccwpck_require__(47261) @@ -26670,7 +26670,7 @@ module.exports = { FormData, makeEntry } /***/ }), -/***/ 82470: +/***/ 13924: /***/ ((module) => { "use strict"; @@ -26718,7 +26718,7 @@ module.exports = { /***/ }), -/***/ 66089: +/***/ 10561: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -26726,14 +26726,14 @@ module.exports = { -const { kConstruct } = __nccwpck_require__(53606) -const { kEnumerableProperty } = __nccwpck_require__(39141) +const { kConstruct } = __nccwpck_require__(13638) +const { kEnumerableProperty } = __nccwpck_require__(50011) const { iteratorMixin, isValidHeaderName, isValidHeaderValue -} = __nccwpck_require__(37458) -const { webidl } = __nccwpck_require__(69293) +} = __nccwpck_require__(98730) +const { webidl } = __nccwpck_require__(2227) const assert = __nccwpck_require__(98061) const util = __nccwpck_require__(47261) @@ -27413,7 +27413,7 @@ module.exports = { /***/ }), -/***/ 9526: +/***/ 78329: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -27427,9 +27427,9 @@ const { filterResponse, makeResponse, fromInnerResponse -} = __nccwpck_require__(98579) -const { HeadersList } = __nccwpck_require__(66089) -const { Request, cloneRequest } = __nccwpck_require__(3891) +} = __nccwpck_require__(51132) +const { HeadersList } = __nccwpck_require__(10561) +const { Request, cloneRequest } = __nccwpck_require__(83211) const zlib = __nccwpck_require__(65628) const { bytesMatch, @@ -27465,23 +27465,23 @@ const { buildContentRange, createInflate, extractMimeType -} = __nccwpck_require__(37458) -const { kState, kDispatcher } = __nccwpck_require__(15575) +} = __nccwpck_require__(98730) +const { kState, kDispatcher } = __nccwpck_require__(14935) const assert = __nccwpck_require__(98061) -const { safelyExtractBody, extractBody } = __nccwpck_require__(27134) +const { safelyExtractBody, extractBody } = __nccwpck_require__(12749) const { redirectStatusSet, nullBodyStatus, safeMethodsSet, requestBodyHeader, subresourceSet -} = __nccwpck_require__(83686) +} = __nccwpck_require__(54823) const EE = __nccwpck_require__(15673) const { Readable, pipeline, finished } = __nccwpck_require__(84492) -const { addAbortListener, isErrored, isReadable, bufferToLowerCasedHeaderName } = __nccwpck_require__(39141) -const { dataURLProcessor, serializeAMimeType, minimizeSupportedMimeType } = __nccwpck_require__(29192) -const { getGlobalDispatcher } = __nccwpck_require__(12621) -const { webidl } = __nccwpck_require__(69293) +const { addAbortListener, isErrored, isReadable, bufferToLowerCasedHeaderName } = __nccwpck_require__(50011) +const { dataURLProcessor, serializeAMimeType, minimizeSupportedMimeType } = __nccwpck_require__(96730) +const { getGlobalDispatcher } = __nccwpck_require__(19405) +const { webidl } = __nccwpck_require__(2227) const { STATUS_CODES } = __nccwpck_require__(88849) const GET_OR_HEAD = ['GET', 'HEAD'] @@ -29693,7 +29693,7 @@ module.exports = { /***/ }), -/***/ 3891: +/***/ 83211: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -29701,16 +29701,16 @@ module.exports = { -const { extractBody, mixinBody, cloneBody, bodyUnusable } = __nccwpck_require__(27134) -const { Headers, fill: fillHeaders, HeadersList, setHeadersGuard, getHeadersGuard, setHeadersList, getHeadersList } = __nccwpck_require__(66089) -const { FinalizationRegistry } = __nccwpck_require__(58330)() -const util = __nccwpck_require__(39141) +const { extractBody, mixinBody, cloneBody, bodyUnusable } = __nccwpck_require__(12749) +const { Headers, fill: fillHeaders, HeadersList, setHeadersGuard, getHeadersGuard, setHeadersList, getHeadersList } = __nccwpck_require__(10561) +const { FinalizationRegistry } = __nccwpck_require__(61451)() +const util = __nccwpck_require__(50011) const nodeUtil = __nccwpck_require__(47261) const { isValidHTTPToken, sameOrigin, environmentSettingsObject -} = __nccwpck_require__(37458) +} = __nccwpck_require__(98730) const { forbiddenMethodsSet, corsSafeListedMethodsSet, @@ -29720,12 +29720,12 @@ const { requestCredentials, requestCache, requestDuplex -} = __nccwpck_require__(83686) +} = __nccwpck_require__(54823) const { kEnumerableProperty, normalizedMethodRecordsBase, normalizedMethodRecords } = util -const { kHeaders, kSignal, kState, kDispatcher } = __nccwpck_require__(15575) -const { webidl } = __nccwpck_require__(69293) -const { URLSerializer } = __nccwpck_require__(29192) -const { kConstruct } = __nccwpck_require__(53606) +const { kHeaders, kSignal, kState, kDispatcher } = __nccwpck_require__(14935) +const { webidl } = __nccwpck_require__(2227) +const { URLSerializer } = __nccwpck_require__(96730) +const { kConstruct } = __nccwpck_require__(13638) const assert = __nccwpck_require__(98061) const { getMaxListeners, setMaxListeners, getEventListeners, defaultMaxListeners } = __nccwpck_require__(15673) @@ -30738,15 +30738,15 @@ module.exports = { Request, makeRequest, fromInnerRequest, cloneRequest } /***/ }), -/***/ 98579: +/***/ 51132: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { Headers, HeadersList, fill, getHeadersGuard, setHeadersGuard, setHeadersList } = __nccwpck_require__(66089) -const { extractBody, cloneBody, mixinBody, hasFinalizationRegistry, streamRegistry, bodyUnusable } = __nccwpck_require__(27134) -const util = __nccwpck_require__(39141) +const { Headers, HeadersList, fill, getHeadersGuard, setHeadersGuard, setHeadersList } = __nccwpck_require__(10561) +const { extractBody, cloneBody, mixinBody, hasFinalizationRegistry, streamRegistry, bodyUnusable } = __nccwpck_require__(12749) +const util = __nccwpck_require__(50011) const nodeUtil = __nccwpck_require__(47261) const { kEnumerableProperty } = util const { @@ -30758,16 +30758,16 @@ const { isErrorLike, isomorphicEncode, environmentSettingsObject: relevantRealm -} = __nccwpck_require__(37458) +} = __nccwpck_require__(98730) const { redirectStatusSet, nullBodyStatus -} = __nccwpck_require__(83686) -const { kState, kHeaders } = __nccwpck_require__(15575) -const { webidl } = __nccwpck_require__(69293) -const { FormData } = __nccwpck_require__(26697) -const { URLSerializer } = __nccwpck_require__(29192) -const { kConstruct } = __nccwpck_require__(53606) +} = __nccwpck_require__(54823) +const { kState, kHeaders } = __nccwpck_require__(14935) +const { webidl } = __nccwpck_require__(2227) +const { FormData } = __nccwpck_require__(62598) +const { URLSerializer } = __nccwpck_require__(96730) +const { kConstruct } = __nccwpck_require__(13638) const assert = __nccwpck_require__(98061) const { types } = __nccwpck_require__(47261) @@ -31356,7 +31356,7 @@ module.exports = { /***/ }), -/***/ 15575: +/***/ 14935: /***/ ((module) => { "use strict"; @@ -31373,7 +31373,7 @@ module.exports = { /***/ }), -/***/ 37458: +/***/ 98730: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -31381,14 +31381,14 @@ module.exports = { const { Transform } = __nccwpck_require__(84492) const zlib = __nccwpck_require__(65628) -const { redirectStatusSet, referrerPolicySet: referrerPolicyTokens, badPortsSet } = __nccwpck_require__(83686) -const { getGlobalOrigin } = __nccwpck_require__(82470) -const { collectASequenceOfCodePoints, collectAnHTTPQuotedString, removeChars, parseMIMEType } = __nccwpck_require__(29192) +const { redirectStatusSet, referrerPolicySet: referrerPolicyTokens, badPortsSet } = __nccwpck_require__(54823) +const { getGlobalOrigin } = __nccwpck_require__(13924) +const { collectASequenceOfCodePoints, collectAnHTTPQuotedString, removeChars, parseMIMEType } = __nccwpck_require__(96730) const { performance } = __nccwpck_require__(38846) -const { isBlobLike, ReadableStreamFrom, isValidHTTPToken, normalizedMethodRecordsBase } = __nccwpck_require__(39141) +const { isBlobLike, ReadableStreamFrom, isValidHTTPToken, normalizedMethodRecordsBase } = __nccwpck_require__(50011) const assert = __nccwpck_require__(98061) const { isUint8Array } = __nccwpck_require__(93746) -const { webidl } = __nccwpck_require__(69293) +const { webidl } = __nccwpck_require__(2227) let supportedHashes = [] @@ -33013,7 +33013,7 @@ module.exports = { /***/ }), -/***/ 69293: +/***/ 2227: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -33021,7 +33021,7 @@ module.exports = { const { types, inspect } = __nccwpck_require__(47261) const { markAsUncloneable } = __nccwpck_require__(24086) -const { toUSVString } = __nccwpck_require__(39141) +const { toUSVString } = __nccwpck_require__(50011) /** @type {import('../../../types/webidl').Webidl} */ const webidl = {} @@ -33716,7 +33716,7 @@ module.exports = { /***/ }), -/***/ 24696: +/***/ 74973: /***/ ((module) => { "use strict"; @@ -34014,7 +34014,7 @@ module.exports = { /***/ }), -/***/ 73002: +/***/ 65153: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -34024,16 +34024,16 @@ const { staticPropertyDescriptors, readOperation, fireAProgressEvent -} = __nccwpck_require__(76274) +} = __nccwpck_require__(24277) const { kState, kError, kResult, kEvents, kAborted -} = __nccwpck_require__(1673) -const { webidl } = __nccwpck_require__(69293) -const { kEnumerableProperty } = __nccwpck_require__(39141) +} = __nccwpck_require__(30010) +const { webidl } = __nccwpck_require__(2227) +const { kEnumerableProperty } = __nccwpck_require__(50011) class FileReader extends EventTarget { constructor () { @@ -34366,13 +34366,13 @@ module.exports = { /***/ }), -/***/ 37663: +/***/ 53788: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { webidl } = __nccwpck_require__(69293) +const { webidl } = __nccwpck_require__(2227) const kState = Symbol('ProgressEvent state') @@ -34452,7 +34452,7 @@ module.exports = { /***/ }), -/***/ 1673: +/***/ 30010: /***/ ((module) => { "use strict"; @@ -34470,7 +34470,7 @@ module.exports = { /***/ }), -/***/ 76274: +/***/ 24277: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -34482,10 +34482,10 @@ const { kResult, kAborted, kLastProgressEventFired -} = __nccwpck_require__(1673) -const { ProgressEvent } = __nccwpck_require__(37663) -const { getEncoding } = __nccwpck_require__(24696) -const { serializeAMimeType, parseMIMEType } = __nccwpck_require__(29192) +} = __nccwpck_require__(30010) +const { ProgressEvent } = __nccwpck_require__(53788) +const { getEncoding } = __nccwpck_require__(74973) +const { serializeAMimeType, parseMIMEType } = __nccwpck_require__(96730) const { types } = __nccwpck_require__(47261) const { StringDecoder } = __nccwpck_require__(71576) const { btoa } = __nccwpck_require__(72254) @@ -34869,28 +34869,28 @@ module.exports = { /***/ }), -/***/ 72007: +/***/ 17299: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { uid, states, sentCloseFrameState, emptyBuffer, opcodes } = __nccwpck_require__(26566) +const { uid, states, sentCloseFrameState, emptyBuffer, opcodes } = __nccwpck_require__(44285) const { kReadyState, kSentClose, kByteParser, kReceivedClose, kResponse -} = __nccwpck_require__(6608) -const { fireEvent, failWebsocketConnection, isClosing, isClosed, isEstablished, parseExtensions } = __nccwpck_require__(22887) -const { channels } = __nccwpck_require__(59241) -const { CloseEvent } = __nccwpck_require__(46055) -const { makeRequest } = __nccwpck_require__(3891) -const { fetching } = __nccwpck_require__(9526) -const { Headers, getHeadersList } = __nccwpck_require__(66089) -const { getDecodeSplit } = __nccwpck_require__(37458) -const { WebsocketFrameSend } = __nccwpck_require__(56248) +} = __nccwpck_require__(34939) +const { fireEvent, failWebsocketConnection, isClosing, isClosed, isEstablished, parseExtensions } = __nccwpck_require__(93194) +const { channels } = __nccwpck_require__(65543) +const { CloseEvent } = __nccwpck_require__(69459) +const { makeRequest } = __nccwpck_require__(83211) +const { fetching } = __nccwpck_require__(78329) +const { Headers, getHeadersList } = __nccwpck_require__(10561) +const { getDecodeSplit } = __nccwpck_require__(98730) +const { WebsocketFrameSend } = __nccwpck_require__(84618) /** @type {import('crypto')} */ let crypto @@ -35248,7 +35248,7 @@ module.exports = { /***/ }), -/***/ 26566: +/***/ 44285: /***/ ((module) => { "use strict"; @@ -35322,15 +35322,15 @@ module.exports = { /***/ }), -/***/ 46055: +/***/ 69459: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { webidl } = __nccwpck_require__(69293) -const { kEnumerableProperty } = __nccwpck_require__(39141) -const { kConstruct } = __nccwpck_require__(53606) +const { webidl } = __nccwpck_require__(2227) +const { kEnumerableProperty } = __nccwpck_require__(50011) +const { kConstruct } = __nccwpck_require__(13638) const { MessagePort } = __nccwpck_require__(24086) /** @@ -35659,13 +35659,13 @@ module.exports = { /***/ }), -/***/ 56248: +/***/ 84618: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { maxUnsigned16Bit } = __nccwpck_require__(26566) +const { maxUnsigned16Bit } = __nccwpck_require__(44285) const BUFFER_SIZE = 16386 @@ -35763,15 +35763,15 @@ module.exports = { /***/ }), -/***/ 77885: +/***/ 7133: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; const { createInflateRaw, Z_DEFAULT_WINDOWBITS } = __nccwpck_require__(65628) -const { isValidClientWindowBits } = __nccwpck_require__(22887) -const { MessageSizeExceededError } = __nccwpck_require__(5425) +const { isValidClientWindowBits } = __nccwpck_require__(93194) +const { MessageSizeExceededError } = __nccwpck_require__(35990) const tail = Buffer.from([0x00, 0x00, 0xff, 0xff]) const kBuffer = Symbol('kBuffer') @@ -35876,7 +35876,7 @@ module.exports = { PerMessageDeflate } /***/ }), -/***/ 48756: +/***/ 46080: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -35884,9 +35884,9 @@ module.exports = { PerMessageDeflate } const { Writable } = __nccwpck_require__(84492) const assert = __nccwpck_require__(98061) -const { parserStates, opcodes, states, emptyBuffer, sentCloseFrameState } = __nccwpck_require__(26566) -const { kReadyState, kSentClose, kResponse, kReceivedClose } = __nccwpck_require__(6608) -const { channels } = __nccwpck_require__(59241) +const { parserStates, opcodes, states, emptyBuffer, sentCloseFrameState } = __nccwpck_require__(44285) +const { kReadyState, kSentClose, kResponse, kReceivedClose } = __nccwpck_require__(34939) +const { channels } = __nccwpck_require__(65543) const { isValidStatusCode, isValidOpcode, @@ -35896,11 +35896,11 @@ const { isControlFrame, isTextBinaryFrame, isContinuationFrame -} = __nccwpck_require__(22887) -const { WebsocketFrameSend } = __nccwpck_require__(56248) -const { closeWebSocketConnection } = __nccwpck_require__(72007) -const { PerMessageDeflate } = __nccwpck_require__(77885) -const { MessageSizeExceededError } = __nccwpck_require__(5425) +} = __nccwpck_require__(93194) +const { WebsocketFrameSend } = __nccwpck_require__(84618) +const { closeWebSocketConnection } = __nccwpck_require__(17299) +const { PerMessageDeflate } = __nccwpck_require__(7133) +const { MessageSizeExceededError } = __nccwpck_require__(35990) function failWebsocketConnectionWithCode (ws, code, reason) { closeWebSocketConnection(ws, code, reason, Buffer.byteLength(reason)) @@ -36397,15 +36397,15 @@ module.exports = { /***/ }), -/***/ 10774: +/***/ 26515: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { WebsocketFrameSend } = __nccwpck_require__(56248) -const { opcodes, sendHints } = __nccwpck_require__(26566) -const FixedQueue = __nccwpck_require__(79468) +const { WebsocketFrameSend } = __nccwpck_require__(84618) +const { opcodes, sendHints } = __nccwpck_require__(44285) +const FixedQueue = __nccwpck_require__(27092) /** @type {typeof Uint8Array} */ const FastBuffer = Buffer[Symbol.species] @@ -36509,7 +36509,7 @@ module.exports = { SendQueue } /***/ }), -/***/ 6608: +/***/ 34939: /***/ ((module) => { "use strict"; @@ -36529,17 +36529,17 @@ module.exports = { /***/ }), -/***/ 22887: +/***/ 93194: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { kReadyState, kController, kResponse, kBinaryType, kWebSocketURL } = __nccwpck_require__(6608) -const { states, opcodes } = __nccwpck_require__(26566) -const { ErrorEvent, createFastMessageEvent } = __nccwpck_require__(46055) +const { kReadyState, kController, kResponse, kBinaryType, kWebSocketURL } = __nccwpck_require__(34939) +const { states, opcodes } = __nccwpck_require__(44285) +const { ErrorEvent, createFastMessageEvent } = __nccwpck_require__(69459) const { isUtf8 } = __nccwpck_require__(72254) -const { collectASequenceOfCodePointsFast, removeHTTPWhitespace } = __nccwpck_require__(29192) +const { collectASequenceOfCodePointsFast, removeHTTPWhitespace } = __nccwpck_require__(96730) /* globals Blob */ @@ -36859,16 +36859,16 @@ module.exports = { /***/ }), -/***/ 1468: +/***/ 16416: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { webidl } = __nccwpck_require__(69293) -const { URLSerializer } = __nccwpck_require__(29192) -const { environmentSettingsObject } = __nccwpck_require__(37458) -const { staticPropertyDescriptors, states, sentCloseFrameState, sendHints } = __nccwpck_require__(26566) +const { webidl } = __nccwpck_require__(2227) +const { URLSerializer } = __nccwpck_require__(96730) +const { environmentSettingsObject } = __nccwpck_require__(98730) +const { staticPropertyDescriptors, states, sentCloseFrameState, sendHints } = __nccwpck_require__(44285) const { kWebSocketURL, kReadyState, @@ -36877,21 +36877,21 @@ const { kResponse, kSentClose, kByteParser -} = __nccwpck_require__(6608) +} = __nccwpck_require__(34939) const { isConnecting, isEstablished, isClosing, isValidSubprotocol, fireEvent -} = __nccwpck_require__(22887) -const { establishWebSocketConnection, closeWebSocketConnection } = __nccwpck_require__(72007) -const { ByteParser } = __nccwpck_require__(48756) -const { kEnumerableProperty, isBlobLike } = __nccwpck_require__(39141) -const { getGlobalDispatcher } = __nccwpck_require__(12621) +} = __nccwpck_require__(93194) +const { establishWebSocketConnection, closeWebSocketConnection } = __nccwpck_require__(17299) +const { ByteParser } = __nccwpck_require__(46080) +const { kEnumerableProperty, isBlobLike } = __nccwpck_require__(50011) +const { getGlobalDispatcher } = __nccwpck_require__(19405) const { types } = __nccwpck_require__(47261) -const { ErrorEvent, CloseEvent } = __nccwpck_require__(46055) -const { SendQueue } = __nccwpck_require__(10774) +const { ErrorEvent, CloseEvent } = __nccwpck_require__(69459) +const { SendQueue } = __nccwpck_require__(26515) // https://websockets.spec.whatwg.org/#interface-definition class WebSocket extends EventTarget { @@ -37462,7 +37462,7 @@ module.exports = { /***/ }), -/***/ 58486: +/***/ 98143: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -37489,14 +37489,14 @@ function resolveActionInput(additionalParams, actionInputs, key) { /***/ }), -/***/ 43128: +/***/ 81248: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildAgentTasks = buildAgentTasks; -const agent_configuration_input_policy_1 = __nccwpck_require__(60436); +const agent_configuration_input_policy_1 = __nccwpck_require__(7699); /** Builds the validated findings/fixer pair used by both action lifecycles. */ function buildAgentTasks(values, environment = process.env) { return (0, agent_configuration_input_policy_1.buildAgentTaskConfiguration)(values, environment); @@ -37505,7 +37505,7 @@ function buildAgentTasks(values, environment = process.env) { /***/ }), -/***/ 65289: +/***/ 71404: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -37513,9 +37513,9 @@ function buildAgentTasks(values, environment = process.env) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildAgentTasksFromInputs = buildAgentTasksFromInputs; exports.buildAgentTasksFromValues = buildAgentTasksFromValues; -const input_keys_1 = __nccwpck_require__(83725); -const agent_configuration_builder_1 = __nccwpck_require__(43128); -const agent_1 = __nccwpck_require__(95407); +const input_keys_1 = __nccwpck_require__(88539); +const agent_configuration_builder_1 = __nccwpck_require__(81248); +const agent_1 = __nccwpck_require__(89040); function buildAgentTasksFromInputs(read) { const provider = read(input_keys_1.INPUT_KEYS.AGENT_PROVIDER)?.trim() || agent_1.DEFAULT_AGENT_PROVIDER; const modelProvider = read(input_keys_1.INPUT_KEYS.AGENT_MODEL_PROVIDER)?.trim() @@ -37565,14 +37565,14 @@ function buildAgentTasksFromValues(values) { /***/ }), -/***/ 45364: +/***/ 30085: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildBranches = buildBranches; -const branches_1 = __nccwpck_require__(94871); +const branches_1 = __nccwpck_require__(29506); function buildBranches(values) { return new branches_1.Branches(values.main, values.defaultBranch, values.development, values.featureTree, values.bugfixTree, values.hotfixTree, values.releaseTree, values.docsTree, values.choreTree); } @@ -37580,29 +37580,29 @@ function buildBranches(values) { /***/ }), -/***/ 23134: +/***/ 42238: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.mainRun = mainRun; -const result_1 = __nccwpck_require__(61444); -const logger_1 = __nccwpck_require__(50135); -const main_run_route_1 = __nccwpck_require__(78693); -const execution_setup_composition_root_1 = __nccwpck_require__(71774); -const setup_execution_boundary_1 = __nccwpck_require__(56062); -const main_run_route_composition_root_1 = __nccwpck_require__(7473); -const repository_context_1 = __nccwpck_require__(57421); -const logging_ports_1 = __nccwpck_require__(73001); -const logger_adapter_1 = __nccwpck_require__(56932); -const lifecycle_synchronization_context_1 = __nccwpck_require__(724); -const agent_activity_policy_1 = __nccwpck_require__(69527); -const push_single_action_contexts_1 = __nccwpck_require__(87805); -const main_run_lifecycle_1 = __nccwpck_require__(70730); -const issue_workflow_runtime_policy_1 = __nccwpck_require__(84598); -const application_error_1 = __nccwpck_require__(2965); -const issue_start_policy_1 = __nccwpck_require__(20953); +const result_1 = __nccwpck_require__(73817); +const logger_1 = __nccwpck_require__(91151); +const main_run_route_1 = __nccwpck_require__(8466); +const execution_setup_composition_root_1 = __nccwpck_require__(83965); +const setup_execution_boundary_1 = __nccwpck_require__(45805); +const main_run_route_composition_root_1 = __nccwpck_require__(4706); +const repository_context_1 = __nccwpck_require__(78958); +const logging_ports_1 = __nccwpck_require__(6152); +const logger_adapter_1 = __nccwpck_require__(72762); +const lifecycle_synchronization_context_1 = __nccwpck_require__(28121); +const agent_activity_policy_1 = __nccwpck_require__(15375); +const push_single_action_contexts_1 = __nccwpck_require__(47841); +const main_run_lifecycle_1 = __nccwpck_require__(916); +const issue_workflow_runtime_policy_1 = __nccwpck_require__(77734); +const application_error_1 = __nccwpck_require__(75999); +const issue_start_policy_1 = __nccwpck_require__(90332); async function mainRun(execution, projectBoardCommandPort, latestTagQueryPort, compositionSurface, lifecycleStateUseCase, agentActivityUseCase, prepareRuntime) { (0, logging_ports_1.configureApplicationLogger)((0, logger_adapter_1.createLoggerAdapter)()); (0, logging_ports_1.setGlobalLoggerDebug)(execution.debug, execution.inputs === undefined); @@ -37754,7 +37754,7 @@ function applyAgentActivityOutcome(execution, outcome) { /***/ }), -/***/ 28645: +/***/ 19094: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -37769,15 +37769,15 @@ exports.buildEmoji = buildEmoji; exports.buildTokens = buildTokens; exports.buildLabels = buildLabels; exports.buildIssueTypes = buildIssueTypes; -const emoji_1 = __nccwpck_require__(24750); -const issue_1 = __nccwpck_require__(2224); -const issue_types_1 = __nccwpck_require__(67189); -const labels_1 = __nccwpck_require__(54505); -const locale_1 = __nccwpck_require__(31159); -const pull_request_1 = __nccwpck_require__(28347); -const projects_1 = __nccwpck_require__(95230); -const tokens_1 = __nccwpck_require__(47356); -const workflows_1 = __nccwpck_require__(70513); +const emoji_1 = __nccwpck_require__(24146); +const issue_1 = __nccwpck_require__(46760); +const issue_types_1 = __nccwpck_require__(27357); +const labels_1 = __nccwpck_require__(79463); +const locale_1 = __nccwpck_require__(9832); +const pull_request_1 = __nccwpck_require__(55713); +const projects_1 = __nccwpck_require__(13231); +const tokens_1 = __nccwpck_require__(44153); +const workflows_1 = __nccwpck_require__(45790); function buildProjects(values) { return new projects_1.Projects(values.projects, values.issueCreated, values.pullRequestCreated, values.issueInProgress, values.pullRequestInProgress); } @@ -37809,17 +37809,17 @@ function buildIssueTypes(values) { /***/ }), -/***/ 15672: +/***/ 30098: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.readDeploymentConfiguration = readDeploymentConfiguration; -const application_error_1 = __nccwpck_require__(2965); -const deployment_configuration_1 = __nccwpck_require__(5664); -const input_keys_1 = __nccwpck_require__(83725); -const merge_queue_readiness_1 = __nccwpck_require__(36637); +const application_error_1 = __nccwpck_require__(75999); +const deployment_configuration_1 = __nccwpck_require__(22495); +const input_keys_1 = __nccwpck_require__(88539); +const merge_queue_readiness_1 = __nccwpck_require__(12515); function readDeploymentConfiguration(getInput, branches) { const errors = []; const readEnum = (key, allowed, fallback) => { @@ -37872,14 +37872,14 @@ function readBoolean(value, fallback, name, errors) { /***/ }), -/***/ 98884: +/***/ 20236: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildExecution = buildExecution; -const execution_1 = __nccwpck_require__(99925); +const execution_1 = __nccwpck_require__(31546); function buildExecution(components) { return new execution_1.Execution(components); } @@ -37887,7 +37887,7 @@ function buildExecution(components) { /***/ }), -/***/ 89280: +/***/ 18330: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -37912,7 +37912,7 @@ function parseIssueWorkflowBoolean(value, inputName, defaultValue) { /***/ }), -/***/ 85160: +/***/ 47165: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -37950,7 +37950,7 @@ function parseStrictInteger(value) { /***/ }), -/***/ 39364: +/***/ 68841: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -37967,24 +37967,24 @@ function parseDelimitedValues(value) { /***/ }), -/***/ 70609: +/***/ 76102: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runLocalAction = runLocalAction; -const local_action_composition_root_1 = __nccwpck_require__(36426); -const common_action_1 = __nccwpck_require__(23134); -const local_action_output_1 = __nccwpck_require__(91598); -const local_action_configuration_1 = __nccwpck_require__(89324); -const local_action_execution_1 = __nccwpck_require__(13196); -const repository_context_1 = __nccwpck_require__(57421); -const agent_activity_composition_root_1 = __nccwpck_require__(11013); -const application_error_context_1 = __nccwpck_require__(15491); -const input_keys_1 = __nccwpck_require__(83725); -const local_single_action_policy_1 = __nccwpck_require__(87929); -const publication_message_catalog_1 = __nccwpck_require__(46042); +const local_action_composition_root_1 = __nccwpck_require__(34760); +const common_action_1 = __nccwpck_require__(42238); +const local_action_output_1 = __nccwpck_require__(94290); +const local_action_configuration_1 = __nccwpck_require__(66645); +const local_action_execution_1 = __nccwpck_require__(47047); +const repository_context_1 = __nccwpck_require__(78958); +const agent_activity_composition_root_1 = __nccwpck_require__(94253); +const application_error_context_1 = __nccwpck_require__(4034); +const input_keys_1 = __nccwpck_require__(88539); +const local_single_action_policy_1 = __nccwpck_require__(99190); +const publication_message_catalog_1 = __nccwpck_require__(34223); async function runLocalAction(additionalParams, options = {}) { return (0, application_error_context_1.runAtApplicationErrorBoundary)(async () => { const requestedAction = additionalParams[input_keys_1.INPUT_KEYS.SINGLE_ACTION]; @@ -38010,15 +38010,15 @@ async function runLocalAction(additionalParams, options = {}) { /***/ }), -/***/ 89324: +/***/ 66645: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildLocalActionConfiguration = buildLocalActionConfiguration; -const yml_utils_1 = __nccwpck_require__(61047); -const local_action_configuration_sections_1 = __nccwpck_require__(52511); +const yml_utils_1 = __nccwpck_require__(61788); +const local_action_configuration_sections_1 = __nccwpck_require__(27946); async function buildLocalActionConfiguration(additionalParams, projectRepository) { const actionInputs = (0, yml_utils_1.getActionInputsWithDefaults)(); const core = (0, local_action_configuration_sections_1.readLocalCoreConfiguration)(additionalParams, actionInputs); @@ -38039,7 +38039,7 @@ async function buildLocalActionConfiguration(additionalParams, projectRepository /***/ }), -/***/ 52511: +/***/ 27946: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -38050,19 +38050,19 @@ exports.readLocalAgentConfiguration = readLocalAgentConfiguration; exports.readLocalProjectConfiguration = readLocalProjectConfiguration; exports.readLocalLabelsAndIssueTypes = readLocalLabelsAndIssueTypes; exports.readLocalWorkflowConfiguration = readLocalWorkflowConfiguration; -const locale_1 = __nccwpck_require__(31159); -const bugbot_constants_1 = __nccwpck_require__(16868); -const input_keys_1 = __nccwpck_require__(83725); -const input_boolean_policy_1 = __nccwpck_require__(89280); -const action_input_source_1 = __nccwpck_require__(58486); -const project_details_loader_1 = __nccwpck_require__(60181); -const input_number_policy_1 = __nccwpck_require__(85160); -const input_values_policy_1 = __nccwpck_require__(39364); -const agent_input_builder_1 = __nccwpck_require__(65289); -const pull_request_description_1 = __nccwpck_require__(25623); -const issue_inactivity_1 = __nccwpck_require__(7703); -const review_configuration_1 = __nccwpck_require__(19249); -const deployment_configuration_builder_1 = __nccwpck_require__(15672); +const locale_1 = __nccwpck_require__(9832); +const bugbot_constants_1 = __nccwpck_require__(51389); +const input_keys_1 = __nccwpck_require__(88539); +const input_boolean_policy_1 = __nccwpck_require__(18330); +const action_input_source_1 = __nccwpck_require__(98143); +const project_details_loader_1 = __nccwpck_require__(73448); +const input_number_policy_1 = __nccwpck_require__(47165); +const input_values_policy_1 = __nccwpck_require__(68841); +const agent_input_builder_1 = __nccwpck_require__(71404); +const pull_request_description_1 = __nccwpck_require__(45315); +const issue_inactivity_1 = __nccwpck_require__(38572); +const review_configuration_1 = __nccwpck_require__(3994); +const deployment_configuration_builder_1 = __nccwpck_require__(30098); function input(additionalParams, actionInputs, key) { return (0, action_input_source_1.resolveActionInput)(additionalParams, actionInputs, key); } @@ -38302,22 +38302,22 @@ function readLocalWorkflowConfiguration(additionalParams, actionInputs) { /***/ }), -/***/ 13196: +/***/ 47047: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildLocalActionExecution = buildLocalActionExecution; -const ai_1 = __nccwpck_require__(1465); -const hotfix_1 = __nccwpck_require__(26127); -const release_1 = __nccwpck_require__(40810); -const single_action_1 = __nccwpck_require__(11457); -const welcome_1 = __nccwpck_require__(42046); -const execution_builder_1 = __nccwpck_require__(98884); -const configuration_builders_1 = __nccwpck_require__(28645); -const branches_builder_1 = __nccwpck_require__(45364); -const size_threshold_builder_1 = __nccwpck_require__(5916); +const ai_1 = __nccwpck_require__(37478); +const hotfix_1 = __nccwpck_require__(18537); +const release_1 = __nccwpck_require__(74715); +const single_action_1 = __nccwpck_require__(45898); +const welcome_1 = __nccwpck_require__(49834); +const execution_builder_1 = __nccwpck_require__(20236); +const configuration_builders_1 = __nccwpck_require__(19094); +const branches_builder_1 = __nccwpck_require__(30085); +const size_threshold_builder_1 = __nccwpck_require__(39757); function buildLocalActionExecution(configuration, additionalParams) { const { debug, singleAction, singleActionIssue, singleActionVersion, singleActionTitle, singleActionChangelog, singleActionMessage, singleActionCommentId, singleActionCommentMode, singleActionOperationId, inactivityThresholdHours, commitPrefixBuilder, issueManagedBranches, preBranchSdd, reopenIssueOnPush, issueDesiredAssigneesCount, pullRequestDesiredAssigneesCount, pullRequestDesiredReviewersCount, titleEmoji, branchManagementEmoji, token, agentModel, aiPullRequestDescriptionMode, aiMembersOnly, aiIgnoreFiles, aiIncludeReasoning, bugbotSeverity, bugbotCommentLimit, bugbotFixVerifyCommands, bugbotReviewConfiguration, agentTasks, bugLabel, bugfixLabel, hotfixLabel, enhancementLabel, featureLabel, releaseLabel, questionLabel, helpLabel, deployLabel, deployedLabel, docsLabel, documentationLabel, choreLabel, maintenanceLabel, priorityHighLabel, priorityMediumLabel, priorityLowLabel, priorityNoneLabel, sizeXxlLabel, sizeXlLabel, sizeLLabel, sizeMLabel, sizeSLabel, sizeXsLabel, lifecycle, issueTypeTask, issueTypeTaskDescription, issueTypeTaskColor, issueTypeBug, issueTypeBugDescription, issueTypeBugColor, issueTypeFeature, issueTypeFeatureDescription, issueTypeFeatureColor, issueTypeDocumentation, issueTypeDocumentationDescription, issueTypeDocumentationColor, issueTypeMaintenance, issueTypeMaintenanceDescription, issueTypeMaintenanceColor, issueTypeHotfix, issueTypeHotfixDescription, issueTypeHotfixColor, issueTypeRelease, issueTypeReleaseDescription, issueTypeReleaseColor, issueTypeQuestion, issueTypeQuestionDescription, issueTypeQuestionColor, issueTypeHelp, issueTypeHelpDescription, issueTypeHelpColor, repositoryLocale, issueLocale, pullRequestLocale, sizeXxlThresholdLines, sizeXxlThresholdFiles, sizeXxlThresholdCommits, sizeXlThresholdLines, sizeXlThresholdFiles, sizeXlThresholdCommits, sizeLThresholdLines, sizeLThresholdFiles, sizeLThresholdCommits, sizeMThresholdLines, sizeMThresholdFiles, sizeMThresholdCommits, sizeSThresholdLines, sizeSThresholdFiles, sizeSThresholdCommits, sizeXsThresholdLines, sizeXsThresholdFiles, sizeXsThresholdCommits, mainBranch, developmentBranch, featureTree, bugfixTree, hotfixTree, releaseTree, docsTree, choreTree, releaseWorkflow, hotfixWorkflow, projects, projectColumnIssueCreated, projectColumnPullRequestCreated, projectColumnIssueInProgress, projectColumnPullRequestInProgress, welcomeTitle, welcomeMessages, deployment, } = configuration; return (0, execution_builder_1.buildExecution)({ @@ -38387,7 +38387,7 @@ function buildLocalActionExecution(configuration, additionalParams) { /***/ }), -/***/ 91598: +/***/ 94290: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -38397,14 +38397,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) { }; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.renderLocalActionResults = renderLocalActionResults; -const chalk_1 = __importDefault(__nccwpck_require__(43920)); -const boxen_1 = __importDefault(__nccwpck_require__(32634)); -const product_identity_1 = __nccwpck_require__(44908); -const application_error_presentation_policy_1 = __nccwpck_require__(47255); -const result_1 = __nccwpck_require__(61444); -const untrusted_content_1 = __nccwpck_require__(12334); -const publication_message_catalog_1 = __nccwpck_require__(46042); -const logger_1 = __nccwpck_require__(50135); +const chalk_1 = __importDefault(__nccwpck_require__(8578)); +const boxen_1 = __importDefault(__nccwpck_require__(11652)); +const product_identity_1 = __nccwpck_require__(18739); +const application_error_presentation_policy_1 = __nccwpck_require__(95067); +const result_1 = __nccwpck_require__(73817); +const untrusted_content_1 = __nccwpck_require__(67057); +const publication_message_catalog_1 = __nccwpck_require__(34223); +const logger_1 = __nccwpck_require__(91151); function renderLocalActionResults(results, catalog = publication_message_catalog_1.ENGLISH_PUBLICATION_CATALOG) { let content = ''; const failed = results.filter(result => result.errors.length > 0 || !result.success).length; @@ -38468,14 +38468,14 @@ function directAnswer(payload) { /***/ }), -/***/ 60059: +/***/ 28586: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.dispatchMainRunRoute = dispatchMainRunRoute; -const logger_1 = __nccwpck_require__(50135); +const logger_1 = __nccwpck_require__(91151); async function dispatchMainRunRoute(route, execution, handlers) { switch (route) { case 'single-action': @@ -38504,7 +38504,7 @@ async function dispatchMainRunRoute(route, execution, handlers) { /***/ }), -/***/ 70730: +/***/ 916: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -38520,15 +38520,15 @@ exports.logWelcomeMessage = logWelcomeMessage; exports.runTokenExecution = runTokenExecution; exports.runNoIssueExecution = runNoIssueExecution; exports.runMainRoute = runMainRoute; -const chalk_1 = __importDefault(__nccwpck_require__(43920)); -const boxen_1 = __importDefault(__nccwpck_require__(32634)); -const result_1 = __nccwpck_require__(61444); -const product_identity_1 = __nccwpck_require__(44908); -const logger_1 = __nccwpck_require__(50135); -const main_run_dispatcher_1 = __nccwpck_require__(60059); -const workflow_context_1 = __nccwpck_require__(13835); -const workflow_queue_composition_root_1 = __nccwpck_require__(57010); -const application_error_1 = __nccwpck_require__(2965); +const chalk_1 = __importDefault(__nccwpck_require__(8578)); +const boxen_1 = __importDefault(__nccwpck_require__(11652)); +const result_1 = __nccwpck_require__(73817); +const product_identity_1 = __nccwpck_require__(18739); +const logger_1 = __nccwpck_require__(91151); +const main_run_dispatcher_1 = __nccwpck_require__(28586); +const workflow_context_1 = __nccwpck_require__(55224); +const workflow_queue_composition_root_1 = __nccwpck_require__(21598); +const application_error_1 = __nccwpck_require__(75999); exports.WORKFLOW_QUEUE_FAILURE_MESSAGE = 'Workflow queue check failed; sequential execution was not bypassed.'; /** * Keeps provider diagnostics out of the action's externally visible failure @@ -38634,7 +38634,7 @@ async function runMainRoute(execution, route, routeHandlers) { /***/ }), -/***/ 78693: +/***/ 8466: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -38659,7 +38659,7 @@ function resolveMainRunRoute(input) { /***/ }), -/***/ 60181: +/***/ 73448: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -38684,7 +38684,7 @@ async function loadProjectDetails(projectRepository, projectIds, owner, token) { /***/ }), -/***/ 57421: +/***/ 78958: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -38714,7 +38714,7 @@ function requireRepositoryCoordinates(value) { /***/ }), -/***/ 56062: +/***/ 45805: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -38722,7 +38722,7 @@ function requireRepositoryCoordinates(value) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.projectSetupExecutionContext = projectSetupExecutionContext; exports.applySetupExecutionResult = applySetupExecutionResult; -const input_keys_1 = __nccwpck_require__(83725); +const input_keys_1 = __nccwpck_require__(88539); function projectSetupExecutionContext(source) { const configuredIssue = readConfiguredIssue(source.inputs); return Object.freeze({ @@ -38842,15 +38842,15 @@ function readConfiguredIssue(inputs) { /***/ }), -/***/ 5916: +/***/ 39757: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildSizeThresholds = buildSizeThresholds; -const size_threshold_1 = __nccwpck_require__(7267); -const size_thresholds_1 = __nccwpck_require__(97155); +const size_threshold_1 = __nccwpck_require__(6362); +const size_thresholds_1 = __nccwpck_require__(54820); function buildSizeThresholds(values) { return new size_thresholds_1.SizeThresholds(new size_threshold_1.SizeThreshold(values.xxl.lines, values.xxl.files, values.xxl.commits), new size_threshold_1.SizeThreshold(values.xl.lines, values.xl.files, values.xl.commits), new size_threshold_1.SizeThreshold(values.l.lines, values.l.files, values.l.commits), new size_threshold_1.SizeThreshold(values.m.lines, values.m.files, values.m.commits), new size_threshold_1.SizeThreshold(values.s.lines, values.s.files, values.s.commits), new size_threshold_1.SizeThreshold(values.xs.lines, values.xs.files, values.xs.commits)); } @@ -38858,7 +38858,7 @@ function buildSizeThresholds(values) { /***/ }), -/***/ 13835: +/***/ 55224: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -38887,7 +38887,7 @@ function resolveWorkflowIdentifier(workflowRef) { /***/ }), -/***/ 83725: +/***/ 88539: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -39098,7 +39098,7 @@ exports.INPUT_KEYS = { /***/ }), -/***/ 44908: +/***/ 18739: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -39110,7 +39110,7 @@ exports.TITLE = 'Copilot'; /***/ }), -/***/ 2965: +/***/ 75999: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -39118,9 +39118,9 @@ exports.TITLE = 'Copilot'; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.ApplicationError = exports.APPLICATION_ERROR_RECOVERY_IDS = exports.APPLICATION_ERROR_METADATA = void 0; exports.toApplicationError = toApplicationError; -const application_error_1 = __nccwpck_require__(28206); -const application_error_context_1 = __nccwpck_require__(15491); -var application_error_2 = __nccwpck_require__(28206); +const application_error_1 = __nccwpck_require__(97790); +const application_error_context_1 = __nccwpck_require__(4034); +var application_error_2 = __nccwpck_require__(97790); Object.defineProperty(exports, "APPLICATION_ERROR_METADATA", ({ enumerable: true, get: function () { return application_error_2.APPLICATION_ERROR_METADATA; } })); Object.defineProperty(exports, "APPLICATION_ERROR_RECOVERY_IDS", ({ enumerable: true, get: function () { return application_error_2.APPLICATION_ERROR_RECOVERY_IDS; } })); /** Creates a semantic error and owns correlation identity outside the pure model. */ @@ -39144,7 +39144,7 @@ function toApplicationError(error, code, message, options = {}) { /***/ }), -/***/ 15491: +/***/ 4034: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -39156,7 +39156,7 @@ exports.runWithApplicationErrorCorrelation = runWithApplicationErrorCorrelation; exports.runAtApplicationErrorBoundary = runAtApplicationErrorBoundary; const node_async_hooks_1 = __nccwpck_require__(92761); const node_crypto_1 = __nccwpck_require__(6005); -const application_error_1 = __nccwpck_require__(28206); +const application_error_1 = __nccwpck_require__(97790); const applicationErrorCorrelation = new node_async_hooks_1.AsyncLocalStorage(); function getApplicationErrorCorrelationId() { return applicationErrorCorrelation.getStore(); @@ -39180,7 +39180,7 @@ function runAtApplicationErrorBoundary(operation) { /***/ }), -/***/ 93638: +/***/ 38313: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -39199,7 +39199,7 @@ exports.SetupInteractionCancelledError = SetupInteractionCancelledError; /***/ }), -/***/ 24342: +/***/ 79966: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -39218,14 +39218,14 @@ function replaceAgentActivityLabel(currentLabels, activityLabel, active) { /***/ }), -/***/ 69527: +/***/ 15375: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.shouldTrackAgentActivity = shouldTrackAgentActivity; -const agent_1 = __nccwpck_require__(95407); +const agent_1 = __nccwpck_require__(89040); /** Decides whether a route can invoke an agent for its current event. */ function shouldTrackAgentActivity(execution, route) { if (!hasTarget(execution)) @@ -39278,7 +39278,7 @@ function hasTarget(execution) { /***/ }), -/***/ 60436: +/***/ 7699: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -39287,8 +39287,8 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildAgentConfiguration = buildAgentConfiguration; exports.mergeAgentTaskValues = mergeAgentTaskValues; exports.buildAgentTaskConfiguration = buildAgentTaskConfiguration; -const agent_configuration_validation_policy_1 = __nccwpck_require__(4345); -const agent_executable_policy_1 = __nccwpck_require__(53773); +const agent_configuration_validation_policy_1 = __nccwpck_require__(60596); +const agent_executable_policy_1 = __nccwpck_require__(12570); function buildAgentConfiguration(values, environment) { const provider = (0, agent_configuration_validation_policy_1.resolveAgentProvider)(values.provider.trim().toLowerCase()); const modelProvider = (0, agent_configuration_validation_policy_1.resolveModelProvider)(values.modelProvider, environment, provider); @@ -39336,7 +39336,7 @@ function hasTaskOverride(value) { /***/ }), -/***/ 4345: +/***/ 60596: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -39349,7 +39349,7 @@ exports.assertProviderModelCompatibility = assertProviderModelCompatibility; exports.resolveModel = resolveModel; exports.resolveEffort = resolveEffort; exports.assertModelAllowlisted = assertModelAllowlisted; -const application_error_1 = __nccwpck_require__(2965); +const application_error_1 = __nccwpck_require__(75999); exports.SUPPORTED_AGENT_PROVIDERS = ['opencode', 'cursor', 'codex']; function resolveAgentProvider(value) { if (exports.SUPPORTED_AGENT_PROVIDERS.includes(value)) @@ -39410,15 +39410,15 @@ function assertIdentifier(value, message, pattern = /^[a-z0-9][a-z0-9_-]*$/i) { /***/ }), -/***/ 53773: +/***/ 12570: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.validateAgentExecutableSelection = validateAgentExecutableSelection; -const agent_1 = __nccwpck_require__(95407); -const application_error_1 = __nccwpck_require__(2965); +const agent_1 = __nccwpck_require__(89040); +const application_error_1 = __nccwpck_require__(75999); /** Accepts only the provider basename or one absolute path to that binary. */ function validateAgentExecutableSelection(configuration) { const selected = configuration.executable?.trim(); @@ -39437,17 +39437,17 @@ function validateAgentExecutableSelection(configuration) { /***/ }), -/***/ 3341: +/***/ 25690: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildProviderExecutionPolicy = buildProviderExecutionPolicy; -const codex_execution_plan_policy_1 = __nccwpck_require__(30680); -const cursor_execution_plan_policy_1 = __nccwpck_require__(91102); -const opencode_execution_plan_policy_1 = __nccwpck_require__(14679); -const provider_execution_policy_1 = __nccwpck_require__(81815); +const codex_execution_plan_policy_1 = __nccwpck_require__(87204); +const cursor_execution_plan_policy_1 = __nccwpck_require__(93955); +const opencode_execution_plan_policy_1 = __nccwpck_require__(99100); +const provider_execution_policy_1 = __nccwpck_require__(50480); /** Static exhaustive dispatch: providers cannot register or bypass policy at runtime. */ function buildProviderExecutionPolicy(input) { const provider = input.configuration.provider; @@ -39462,16 +39462,16 @@ function buildProviderExecutionPolicy(input) { /***/ }), -/***/ 30680: +/***/ 87204: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildCodexExecutionPolicy = buildCodexExecutionPolicy; -const agent_execution_plan_1 = __nccwpck_require__(25901); -const provider_execution_policy_1 = __nccwpck_require__(81815); -const strict_output_schema_policy_1 = __nccwpck_require__(85197); +const agent_execution_plan_1 = __nccwpck_require__(12253); +const provider_execution_policy_1 = __nccwpck_require__(50480); +const strict_output_schema_policy_1 = __nccwpck_require__(56743); function buildCodexExecutionPolicy(input) { const { configuration } = input; if (configuration.provider !== 'codex') @@ -39532,15 +39532,15 @@ function tomlString(value) { /***/ }), -/***/ 91102: +/***/ 93955: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildCursorExecutionPolicy = buildCursorExecutionPolicy; -const agent_execution_plan_1 = __nccwpck_require__(25901); -const provider_execution_policy_1 = __nccwpck_require__(81815); +const agent_execution_plan_1 = __nccwpck_require__(12253); +const provider_execution_policy_1 = __nccwpck_require__(50480); function buildCursorExecutionPolicy(input) { const { configuration } = input; if (configuration.provider !== 'cursor') @@ -39607,15 +39607,15 @@ function buildCursorExecutionPolicy(input) { /***/ }), -/***/ 14679: +/***/ 99100: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildOpenCodeExecutionPolicy = buildOpenCodeExecutionPolicy; -const agent_execution_plan_1 = __nccwpck_require__(25901); -const provider_execution_policy_1 = __nccwpck_require__(81815); +const agent_execution_plan_1 = __nccwpck_require__(12253); +const provider_execution_policy_1 = __nccwpck_require__(50480); const READONLY_AGENT = 'copilot-controlled-readonly'; const FIXER_AGENT = 'copilot-controlled-fixer'; function buildOpenCodeExecutionPolicy(input) { @@ -39705,7 +39705,7 @@ function buildOpenCodeExecutionPolicy(input) { /***/ }), -/***/ 81815: +/***/ 50480: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -39724,7 +39724,7 @@ function managedArtifactPath(runtimeDirectory, relativePath) { /***/ }), -/***/ 85197: +/***/ 56743: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -39791,7 +39791,7 @@ function isRecord(value) { /***/ }), -/***/ 2584: +/***/ 30601: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -39801,7 +39801,7 @@ exports.AGENT_OUTPUT_LOCALE_SCHEMA_PROPERTY = exports.PRODUCT_FACING_AGENT_TASKS exports.productFacingAgentQueryOptions = productFacingAgentQueryOptions; exports.validateAgentOutputLocale = validateAgentOutputLocale; exports.agentOutputLocaleFailureMessage = agentOutputLocaleFailureMessage; -const locale_1 = __nccwpck_require__(64552); +const locale_1 = __nccwpck_require__(15386); exports.PRODUCT_FACING_AGENT_TASKS = [ 'think', 'answer-issue-help', @@ -39877,7 +39877,7 @@ function agentOutputLocaleFailureMessage(validation) { /***/ }), -/***/ 63523: +/***/ 25603: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -39885,7 +39885,7 @@ function agentOutputLocaleFailureMessage(validation) { /** Shared structured-response contracts used by agent-backed application flows. */ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.PULL_REQUEST_DESCRIPTION_RESPONSE_SCHEMA = exports.RECOMMEND_STEPS_RESPONSE_SCHEMA = exports.THINK_RESPONSE_SCHEMA = exports.LANGUAGE_ADAPTATION_RESPONSE_SCHEMA = void 0; -const agent_output_locale_policy_1 = __nccwpck_require__(2584); +const agent_output_locale_policy_1 = __nccwpck_require__(30601); exports.LANGUAGE_ADAPTATION_RESPONSE_SCHEMA = { type: 'object', properties: { @@ -40025,7 +40025,7 @@ exports.PULL_REQUEST_DESCRIPTION_RESPONSE_SCHEMA = { /***/ }), -/***/ 2601: +/***/ 85712: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -40057,7 +40057,7 @@ function resolveThinkAgentTask(commandName, destinationType) { /***/ }), -/***/ 67880: +/***/ 64809: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -40066,9 +40066,9 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.readEnglishApplicationErrorMessage = exports.APPLICATION_ERROR_CATALOG_DEFINITIONS = exports.SPANISH_APPLICATION_ERROR_DEFINITION = exports.ENGLISH_APPLICATION_ERROR_DEFINITION = exports.SPANISH_APPLICATION_ERROR_MESSAGES = exports.ENGLISH_APPLICATION_ERROR_MESSAGES = exports.APPLICATION_ERROR_MESSAGE_IDS = exports.APPLICATION_ERROR_CODES = void 0; exports.resolveStaticApplicationErrorCatalog = resolveStaticApplicationErrorCatalog; exports.resolveApplicationErrorCatalog = resolveApplicationErrorCatalog; -const application_error_1 = __nccwpck_require__(28206); -const message_catalog_1 = __nccwpck_require__(5313); -const resolved_message_catalog_policy_1 = __nccwpck_require__(98248); +const application_error_1 = __nccwpck_require__(97790); +const message_catalog_1 = __nccwpck_require__(27097); +const resolved_message_catalog_policy_1 = __nccwpck_require__(55069); const LABEL_KEYS = Object.freeze([ 'impact', 'errorCode', 'action', 'retainedState', 'retryable', 'yes', 'no', 'reference', ]); @@ -40331,7 +40331,7 @@ exports.readEnglishApplicationErrorMessage = readEnglishApplicationErrorMessage; /***/ }), -/***/ 47255: +/***/ 95067: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -40340,7 +40340,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildApplicationErrorPresentation = buildApplicationErrorPresentation; exports.renderApplicationErrorText = renderApplicationErrorText; exports.renderApplicationErrorMarkdown = renderApplicationErrorMarkdown; -const application_error_message_catalog_1 = __nccwpck_require__(67880); +const application_error_message_catalog_1 = __nccwpck_require__(64809); /** Shared semantic view model for terminal, GitHub, and API presentation. */ function buildApplicationErrorPresentation(error, message = application_error_message_catalog_1.readEnglishApplicationErrorMessage) { const descriptor = error.recovery @@ -40388,7 +40388,7 @@ function renderApplicationErrorMarkdown(error, message = application_error_messa /***/ }), -/***/ 24591: +/***/ 36904: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -40495,7 +40495,7 @@ exports.SPANISH_APPROVAL_DOCTOR_MESSAGES = Object.freeze({ /***/ }), -/***/ 2426: +/***/ 85918: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -40532,7 +40532,7 @@ function selectConfirmedAssignees(requestedMembers, assignedMembers) { /***/ }), -/***/ 50189: +/***/ 35596: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -40574,7 +40574,7 @@ async function runWithConcurrencyLimit(tasks, limit) { /***/ }), -/***/ 59489: +/***/ 97307: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -40613,7 +40613,7 @@ function findPreviousIssueBranch(branches, issueNumber, branchTypes) { /***/ }), -/***/ 71641: +/***/ 89245: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -40622,8 +40622,8 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.BRANCH_SYNC_CATALOG_DEFINITIONS = exports.SPANISH_BRANCH_SYNC_DEFINITION = exports.ENGLISH_BRANCH_SYNC_DEFINITION = exports.BRANCH_SYNC_MESSAGE_IDS = void 0; exports.resolveStaticBranchSyncCatalog = resolveStaticBranchSyncCatalog; exports.resolveBranchSyncCatalog = resolveBranchSyncCatalog; -const message_catalog_1 = __nccwpck_require__(5313); -const resolved_message_catalog_policy_1 = __nccwpck_require__(98248); +const message_catalog_1 = __nccwpck_require__(27097); +const resolved_message_catalog_policy_1 = __nccwpck_require__(55069); exports.BRANCH_SYNC_MESSAGE_IDS = Object.freeze([ 'branchSync.stale.heading', 'branchSync.stale.behind', @@ -40706,7 +40706,7 @@ async function resolveBranchSyncCatalog(locale, configuration, resolver) { /***/ }), -/***/ 90741: +/***/ 79895: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -40722,10 +40722,10 @@ exports.buildBranchSyncStatusCommentUrl = buildBranchSyncStatusCommentUrl; exports.isStaleBranchSyncComment = isStaleBranchSyncComment; exports.buildStaleBranchSyncComment = buildStaleBranchSyncComment; exports.buildAlignedBranchSyncComment = buildAlignedBranchSyncComment; -const github_user_policy_1 = __nccwpck_require__(19596); -const git_object_id_1 = __nccwpck_require__(36924); -const publication_identity_policy_1 = __nccwpck_require__(12590); -const github_comment_publication_policy_1 = __nccwpck_require__(22913); +const github_user_policy_1 = __nccwpck_require__(84403); +const git_object_id_1 = __nccwpck_require__(88623); +const publication_identity_policy_1 = __nccwpck_require__(45403); +const github_comment_publication_policy_1 = __nccwpck_require__(72712); function selectBranchDependenciesForPush(dependencies, pushedBranch) { const selected = dependencies.filter((dependency) => dependency.parentBranch === pushedBranch || dependency.workingBranch === pushedBranch); @@ -40859,7 +40859,7 @@ function safeLinkLabel(value) { /***/ }), -/***/ 16868: +/***/ 51389: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -40876,7 +40876,7 @@ exports.BUGBOT_MIN_SEVERITY = 'low'; /***/ }), -/***/ 51471: +/***/ 31601: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -40886,9 +40886,9 @@ exports.BugbotDiffPlanLimitError = exports.MAX_REVIEW_DIFF_NORMALIZED_INPUT_LENG exports.buildReviewDiffPlan = buildReviewDiffPlan; exports.splitReviewDiffPatch = splitReviewDiffPatch; const node_crypto_1 = __nccwpck_require__(6005); -const untrusted_content_1 = __nccwpck_require__(12334); -const git_object_id_1 = __nccwpck_require__(36924); -const file_ignore_policy_1 = __nccwpck_require__(56498); +const untrusted_content_1 = __nccwpck_require__(67057); +const git_object_id_1 = __nccwpck_require__(88623); +const file_ignore_policy_1 = __nccwpck_require__(20542); exports.MAX_REVIEW_DIFF_PARTITION_LENGTH = 64000; exports.MAX_REVIEW_DIFF_FRAGMENT_LENGTH = 12000; exports.MAX_REVIEW_DIFF_PARTITIONS = 64; @@ -41097,7 +41097,7 @@ function stableDiffPartitionDigest(value) { /***/ }), -/***/ 10580: +/***/ 52771: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -41119,7 +41119,7 @@ function selectPullRequestOwnerForPushReview(context) { /***/ }), -/***/ 80639: +/***/ 98024: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -41141,10 +41141,10 @@ exports.replaceMarkerInBody = replaceMarkerInBody; exports.extractTitleFromBody = extractTitleFromBody; exports.buildCommentBody = buildCommentBody; exports.buildResolvedFindingNote = buildResolvedFindingNote; -const bugbot_constants_1 = __nccwpck_require__(16868); -const application_error_1 = __nccwpck_require__(2965); -const github_comment_publication_policy_1 = __nccwpck_require__(22913); -const bugbot_message_catalog_1 = __nccwpck_require__(84479); +const bugbot_constants_1 = __nccwpck_require__(51389); +const application_error_1 = __nccwpck_require__(75999); +const github_comment_publication_policy_1 = __nccwpck_require__(72712); +const bugbot_message_catalog_1 = __nccwpck_require__(7406); /** Maximum lossless finding identity accepted by the marker contract. */ exports.MAX_FINDING_ID_LENGTH = 200; /** Safe character set for finding IDs in regex (alphanumeric, path/segment chars). */ @@ -41294,14 +41294,14 @@ function buildResolvedFindingNote(resolution, catalog = (0, bugbot_message_catal /***/ }), -/***/ 9298: +/***/ 53822: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.projectBugbotFindingStatuses = projectBugbotFindingStatuses; -const review_state_1 = __nccwpck_require__(17271); +const review_state_1 = __nccwpck_require__(79200); /** Projects durable comment markers and the current analysis into a stable finding state. */ function projectBugbotFindingStatuses(existingByFindingId, activeFindings, resolvedFindingIds = new Set(), resolvedFindingResolutions = new Map()) { const ids = new Set([ @@ -41347,7 +41347,7 @@ function countStatuses(statuses) { /***/ }), -/***/ 84479: +/***/ 7406: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -41358,8 +41358,8 @@ exports.resolveStaticBugbotCatalog = resolveStaticBugbotCatalog; exports.resolveBugbotCatalog = resolveBugbotCatalog; exports.renderBugbotDiagnostic = renderBugbotDiagnostic; exports.bugbotDiagnosticOperatorMessage = bugbotDiagnosticOperatorMessage; -const message_catalog_1 = __nccwpck_require__(5313); -const resolved_message_catalog_policy_1 = __nccwpck_require__(98248); +const message_catalog_1 = __nccwpck_require__(27097); +const resolved_message_catalog_policy_1 = __nccwpck_require__(55069); exports.BUGBOT_MESSAGE_IDS = Object.freeze([ 'bugbot.status.heading.partial', 'bugbot.status.heading.verification', @@ -41677,7 +41677,7 @@ function bugbotDiagnosticOperatorMessage(diagnostic) { /***/ }), -/***/ 83782: +/***/ 57555: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -41708,17 +41708,17 @@ function formatBugbotPartitionCompletion(input) { /***/ }), -/***/ 95220: +/***/ 85821: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.projectBugbotProviderEvidence = projectBugbotProviderEvidence; -const bugbot_finding_marker_policy_1 = __nccwpck_require__(80639); -const bugbot_constants_1 = __nccwpck_require__(16868); -const github_user_policy_1 = __nccwpck_require__(19596); -const review_state_1 = __nccwpck_require__(17271); +const bugbot_finding_marker_policy_1 = __nccwpck_require__(98024); +const bugbot_constants_1 = __nccwpck_require__(51389); +const github_user_policy_1 = __nccwpck_require__(84403); +const review_state_1 = __nccwpck_require__(79200); /** * Converts a provider snapshot into semantic finding evidence. Issue and PR * destinations are projected independently and then folded conservatively, so @@ -41906,7 +41906,7 @@ function containsBugbotFindingMarkerSyntax(body) { /***/ }), -/***/ 54271: +/***/ 78128: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -42031,7 +42031,7 @@ function hasMissingNonCleanDurableDestination(findingId, finding, observed) { /***/ }), -/***/ 15950: +/***/ 89189: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -42055,16 +42055,16 @@ function filterEligibleBugbotResolutionIds(claimedIds, eligibleIds, existingByFi /***/ }), -/***/ 22443: +/***/ 98117: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.projectBugbotResultFindingStates = projectBugbotResultFindingStates; -const result_1 = __nccwpck_require__(61444); -const review_state_1 = __nccwpck_require__(17271); -const bugbot_telemetry_projection_policy_1 = __nccwpck_require__(59783); +const result_1 = __nccwpck_require__(73817); +const review_state_1 = __nccwpck_require__(79200); +const bugbot_telemetry_projection_policy_1 = __nccwpck_require__(43244); const BUGBOT_FINDING_STATE_SET = new Set(review_state_1.BUGBOT_FINDING_STATES); const OUTCOMES_REQUIRING_FINDING_STATES = new Set([ 'completed', @@ -42127,7 +42127,7 @@ function isNonNegativeSafeInteger(value) { /***/ }), -/***/ 11719: +/***/ 83288: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -42135,8 +42135,8 @@ function isNonNegativeSafeInteger(value) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.selectOwnedBugbotReviews = selectOwnedBugbotReviews; exports.isTrustedBugbotAuthor = isTrustedBugbotAuthor; -const github_user_policy_1 = __nccwpck_require__(19596); -const bugbot_finding_marker_policy_1 = __nccwpck_require__(80639); +const github_user_policy_1 = __nccwpck_require__(84403); +const bugbot_finding_marker_policy_1 = __nccwpck_require__(98024); /** Associates trusted review summaries with every child finding they own. */ function selectOwnedBugbotReviews(input) { const findingById = new Map(input.findings.map((finding) => [finding.id, finding])); @@ -42184,7 +42184,7 @@ function addFinding(findingsByReview, reviewIdentity, findingId) { /***/ }), -/***/ 77193: +/***/ 43799: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -42198,10 +42198,10 @@ exports.isBugbotStatusComment = isBugbotStatusComment; exports.renderBugbotStatusCard = renderBugbotStatusCard; exports.renderBugbotReviewSnapshot = renderBugbotReviewSnapshot; exports.buildNewBugbotReviewSnapshotHeader = buildNewBugbotReviewSnapshotHeader; -const review_state_1 = __nccwpck_require__(17271); -const github_comment_publication_policy_1 = __nccwpck_require__(22913); -const publication_identity_policy_1 = __nccwpck_require__(12590); -const bugbot_message_catalog_1 = __nccwpck_require__(84479); +const review_state_1 = __nccwpck_require__(79200); +const github_comment_publication_policy_1 = __nccwpck_require__(72712); +const publication_identity_policy_1 = __nccwpck_require__(45403); +const bugbot_message_catalog_1 = __nccwpck_require__(7406); exports.BUGBOT_STATUS_MARKER_PREFIX = 'copilot-bugbot-status'; exports.BUGBOT_REVIEW_MARKER_PREFIX = 'copilot-bugbot-review'; exports.BUGBOT_REVIEW_STATUS_START = '`; @@ -43671,7 +43671,7 @@ function shortSha(value) { /***/ }), -/***/ 56498: +/***/ 20542: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -43733,7 +43733,7 @@ function fileMatchesIgnorePatterns(filePath, ignorePatterns) { /***/ }), -/***/ 22913: +/***/ 72712: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -43742,8 +43742,8 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.sanitizeAgentMarkdown = sanitizeAgentMarkdown; exports.sanitizePublishedError = sanitizePublishedError; exports.escapeHtml = escapeHtml; -const untrusted_content_1 = __nccwpck_require__(12334); -const secret_redaction_1 = __nccwpck_require__(93523); +const untrusted_content_1 = __nccwpck_require__(67057); +const secret_redaction_1 = __nccwpck_require__(254); /** * Model output is untrusted too. Keep useful Markdown, but neutralize the * GitHub automation surfaces that could create side effects when published. @@ -43786,7 +43786,7 @@ function neutralizeGithubControls(value) { /***/ }), -/***/ 78447: +/***/ 74902: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -43795,8 +43795,8 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.INACTIVITY_CATALOG_DEFINITIONS = exports.SPANISH_INACTIVITY_DEFINITION = exports.ENGLISH_INACTIVITY_DEFINITION = exports.INACTIVITY_MESSAGE_IDS = void 0; exports.resolveStaticInactivityCatalog = resolveStaticInactivityCatalog; exports.resolveInactivityCatalog = resolveInactivityCatalog; -const message_catalog_1 = __nccwpck_require__(5313); -const resolved_message_catalog_policy_1 = __nccwpck_require__(98248); +const message_catalog_1 = __nccwpck_require__(27097); +const resolved_message_catalog_policy_1 = __nccwpck_require__(55069); exports.INACTIVITY_MESSAGE_IDS = Object.freeze([ 'inactivity.closure.heading', 'inactivity.closure.reason', @@ -43890,7 +43890,7 @@ async function resolveInactivityCatalog(locale, configuration, resolver) { /***/ }), -/***/ 62453: +/***/ 1572: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -43898,7 +43898,7 @@ async function resolveInactivityCatalog(locale, configuration, resolver) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildInactivityClosureComment = buildInactivityClosureComment; exports.buildInactivitySummarySteps = buildInactivitySummarySteps; -const publication_identity_policy_1 = __nccwpck_require__(12590); +const publication_identity_policy_1 = __nccwpck_require__(45403); function buildInactivityClosureComment(input) { const digest = (0, publication_identity_policy_1.createSemanticDigest)({ updatedAt: input.candidate.updatedAt, @@ -43939,16 +43939,16 @@ function buildInactivitySummarySteps(input) { /***/ }), -/***/ 36432: +/***/ 73160: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildInitialLabelProvisioningPlan = buildInitialLabelProvisioningPlan; -const progress_labels_1 = __nccwpck_require__(71285); -const copilot_lifecycle_1 = __nccwpck_require__(4227); -const issue_start_policy_1 = __nccwpck_require__(20953); +const progress_labels_1 = __nccwpck_require__(97890); +const copilot_lifecycle_1 = __nccwpck_require__(72418); +const issue_start_policy_1 = __nccwpck_require__(90332); const normalizeLabelName = (name) => name.trim().toLowerCase(); function configuredLabelDefinitions(labels) { const metadata = [ @@ -44032,15 +44032,15 @@ function buildInitialLabelProvisioningPlan(labels, existingLabelNames) { /***/ }), -/***/ 28956: +/***/ 61899: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.resolveIssueCommentPublicationRequest = resolveIssueCommentPublicationRequest; -const comment_content_policy_1 = __nccwpck_require__(2324); -const input_keys_1 = __nccwpck_require__(83725); +const comment_content_policy_1 = __nccwpck_require__(77454); +const input_keys_1 = __nccwpck_require__(88539); function resolveIssueCommentPublicationRequest(input) { if (!(0, comment_content_policy_1.hasVisibleCommentContent)(input.message)) { return new Error(`${input_keys_1.INPUT_KEYS.SINGLE_ACTION_MESSAGE} must contain a visible message.`); @@ -44072,15 +44072,15 @@ function resolveMode(mode, commentId) { /***/ }), -/***/ 87929: +/***/ 99190: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.assertLocalSingleActionAllowed = assertLocalSingleActionAllowed; -const action_types_1 = __nccwpck_require__(8960); -const application_error_1 = __nccwpck_require__(2965); +const action_types_1 = __nccwpck_require__(19625); +const application_error_1 = __nccwpck_require__(75999); const GITHUB_WORKFLOW_ONLY_ACTIONS = [ action_types_1.ACTIONS.CREATE_TAG, action_types_1.ACTIONS.CREATE_RELEASE, @@ -44099,7 +44099,7 @@ function assertLocalSingleActionAllowed(requestedAction) { /***/ }), -/***/ 32742: +/***/ 55078: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -44154,7 +44154,7 @@ function buildManagedBranchPresentation(input) { /***/ }), -/***/ 22459: +/***/ 56033: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -44164,7 +44164,7 @@ exports.SPANISH_MERGE_QUEUE_MESSAGES = exports.ENGLISH_MERGE_QUEUE_MESSAGES = ex exports.renderMergeQueueReadinessFailure = renderMergeQueueReadinessFailure; exports.producerStateMessageId = producerStateMessageId; exports.boundedMergeQueueDiagnostic = boundedMergeQueueDiagnostic; -const sensitive_text_1 = __nccwpck_require__(98209); +const sensitive_text_1 = __nccwpck_require__(47122); exports.MERGE_QUEUE_MESSAGE_IDS = Object.freeze([ 'mergeQueue.readiness.failure', 'mergeQueue.readiness.incompleteEvidence', @@ -44287,7 +44287,7 @@ function boundedMergeQueueDiagnostic(value) { /***/ }), -/***/ 39631: +/***/ 39267: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -44323,7 +44323,7 @@ function mergeSetupOverrides(fileOverrides, flagOverrides) { /***/ }), -/***/ 71285: +/***/ 97890: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -44357,7 +44357,7 @@ function progressPercentToColor(percent) { /***/ }), -/***/ 12590: +/***/ 45403: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -44374,7 +44374,7 @@ exports.buildPublicationTransitionMarker = buildPublicationTransitionMarker; exports.parsePublicationTransitionMarker = parsePublicationTransitionMarker; exports.buildDuplicateMarker = buildDuplicateMarker; const node_crypto_1 = __nccwpck_require__(6005); -const github_publication_1 = __nccwpck_require__(75905); +const github_publication_1 = __nccwpck_require__(35793); exports.PUBLICATION_SCHEMA = '1'; exports.PUBLICATION_MARKER_PREFIX = 'copilot:publication'; exports.PUBLICATION_DUPLICATE_MARKER_PREFIX = 'copilot:publication-duplicate'; @@ -44505,7 +44505,7 @@ function stableSerialize(value) { /***/ }), -/***/ 46042: +/***/ 34223: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -44516,9 +44516,9 @@ exports.publicationLocaleNeedsDynamicCatalog = publicationLocaleNeedsDynamicCata exports.resolveStaticPublicationCatalog = resolveStaticPublicationCatalog; exports.resolvePublicationCatalog = resolvePublicationCatalog; exports.toPublicationCatalog = toPublicationCatalog; -const message_catalog_1 = __nccwpck_require__(5313); -const locale_1 = __nccwpck_require__(64552); -const application_error_message_catalog_1 = __nccwpck_require__(67880); +const message_catalog_1 = __nccwpck_require__(27097); +const locale_1 = __nccwpck_require__(15386); +const application_error_message_catalog_1 = __nccwpck_require__(64809); const PUBLICATION_SURFACE_MESSAGE_IDS = Object.freeze([ 'publication.implementationPlan', 'publication.planReady', @@ -44917,7 +44917,7 @@ exports.SPANISH_PUBLICATION_CATALOG = toPublicationCatalog(Object.freeze({ /***/ }), -/***/ 11035: +/***/ 79719: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -44929,8 +44929,8 @@ exports.buildDuplicateCompactionPublicationPayload = buildDuplicateCompactionPub exports.duplicateCompactionPublicationOutcomes = duplicateCompactionPublicationOutcomes; exports.buildTransitionPublicationPayload = buildTransitionPublicationPayload; exports.transitionPublicationOutcomes = transitionPublicationOutcomes; -const result_1 = __nccwpck_require__(61444); -const github_publication_1 = __nccwpck_require__(75905); +const result_1 = __nccwpck_require__(73817); +const github_publication_1 = __nccwpck_require__(35793); const MAX_REPORTED_COMMENT_IDS = 20; const MAX_REPORTED_TRANSITIONS = 20; /** Builds bounded evidence for a commit-derived result that was intentionally suppressed. */ @@ -45042,7 +45042,7 @@ function areOrderedUniquePositiveIntegers(values) { /***/ }), -/***/ 87855: +/***/ 43268: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -45050,7 +45050,7 @@ function areOrderedUniquePositiveIntegers(values) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.MAX_PULL_REQUEST_DESCRIPTION_LENGTH = void 0; exports.renderPullRequestDescriptionContent = renderPullRequestDescriptionContent; -const github_comment_publication_policy_1 = __nccwpck_require__(22913); +const github_comment_publication_policy_1 = __nccwpck_require__(72712); exports.MAX_PULL_REQUEST_DESCRIPTION_LENGTH = 12000; const CONTENT_KEYS = Object.freeze([ 'outputLocale', @@ -45242,7 +45242,7 @@ function renderList(values) { /***/ }), -/***/ 83857: +/***/ 39410: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -45282,7 +45282,7 @@ function createSha256(value) { /***/ }), -/***/ 47992: +/***/ 67402: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -45294,9 +45294,9 @@ exports.renderRepositoryAgentArtifacts = renderRepositoryAgentArtifacts; exports.renderRepositoryAgentGuide = renderRepositoryAgentGuide; exports.renderRepositoryAgentSkill = renderRepositoryAgentSkill; exports.renderRepositoryAgentPointerBlock = renderRepositoryAgentPointerBlock; -const issue_workflow_profile_1 = __nccwpck_require__(62721); -const setup_issue_workflow_policy_1 = __nccwpck_require__(47280); -const issue_start_policy_1 = __nccwpck_require__(20953); +const issue_workflow_profile_1 = __nccwpck_require__(26744); +const setup_issue_workflow_policy_1 = __nccwpck_require__(81182); +const issue_start_policy_1 = __nccwpck_require__(90332); exports.REPOSITORY_AGENT_PROFILE_PATH = '.copilot/repository-profile.json'; exports.REPOSITORY_AGENT_GUIDE_PATH = '.copilot/AGENT_GUIDE.md'; exports.REPOSITORY_AGENT_SKILL_PATH = '.agents/skills/copilot-repository-workflow/SKILL.md'; @@ -45452,7 +45452,7 @@ function renderRepositoryAgentPointerBlock() { /***/ }), -/***/ 98248: +/***/ 55069: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -45461,8 +45461,8 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.toResolvedMessageCatalogView = toResolvedMessageCatalogView; exports.resolveStaticMessageCatalogView = resolveStaticMessageCatalogView; exports.resolveMessageCatalogView = resolveMessageCatalogView; -const message_catalog_1 = __nccwpck_require__(5313); -const locale_1 = __nccwpck_require__(64552); +const message_catalog_1 = __nccwpck_require__(27097); +const locale_1 = __nccwpck_require__(15386); function toResolvedMessageCatalogView(resolved) { return Object.freeze({ locale: resolved.resolvedLocale, @@ -45528,7 +45528,7 @@ async function resolveMessageCatalogView(locale, ids, sourceCatalog, bundledCata /***/ }), -/***/ 49532: +/***/ 88350: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -45575,7 +45575,7 @@ function calculateReviewersStillNeeded(desiredCount, currentCount, confirmedCoun /***/ }), -/***/ 55150: +/***/ 81985: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -45587,18 +45587,18 @@ exports.hasOwnedPrimaryIssuePublication = hasOwnedPrimaryIssuePublication; exports.selectSemanticReplyIntents = selectSemanticReplyIntents; exports.renderSemanticReply = renderSemanticReply; exports.renderSemanticStatus = renderSemanticStatus; -const github_publication_1 = __nccwpck_require__(75905); -const github_user_policy_1 = __nccwpck_require__(19596); -const result_1 = __nccwpck_require__(61444); -const github_comment_publication_policy_1 = __nccwpck_require__(22913); -const publication_identity_policy_1 = __nccwpck_require__(12590); -const publication_message_catalog_1 = __nccwpck_require__(46042); -const copilot_interaction_policy_1 = __nccwpck_require__(8964); -const status_command_policy_1 = __nccwpck_require__(62186); -const comment_translation_policy_1 = __nccwpck_require__(22406); -const application_error_presentation_policy_1 = __nccwpck_require__(47255); -const git_object_id_1 = __nccwpck_require__(36924); -const implementation_plan_1 = __nccwpck_require__(52620); +const github_publication_1 = __nccwpck_require__(35793); +const github_user_policy_1 = __nccwpck_require__(84403); +const result_1 = __nccwpck_require__(73817); +const github_comment_publication_policy_1 = __nccwpck_require__(72712); +const publication_identity_policy_1 = __nccwpck_require__(45403); +const publication_message_catalog_1 = __nccwpck_require__(34223); +const copilot_interaction_policy_1 = __nccwpck_require__(90108); +const status_command_policy_1 = __nccwpck_require__(3449); +const comment_translation_policy_1 = __nccwpck_require__(27150); +const application_error_presentation_policy_1 = __nccwpck_require__(95067); +const git_object_id_1 = __nccwpck_require__(88623); +const implementation_plan_1 = __nccwpck_require__(77001); function selectSemanticStatusIntents(context) { return Object.freeze(context.results.flatMap(result => { if (!result.executed || !result.success) @@ -46001,15 +46001,15 @@ function safeDigest(value) { /***/ }), -/***/ 8794: +/***/ 53296: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildApprovalDoctorChecks = buildApprovalDoctorChecks; -const pull_request_approval_policy_1 = __nccwpck_require__(53553); -const setup_doctor_message_catalog_1 = __nccwpck_require__(67183); +const pull_request_approval_policy_1 = __nccwpck_require__(98820); +const setup_doctor_message_catalog_1 = __nccwpck_require__(80226); /** Stable ordered checks; unknown prerequisites never project native-approval readiness. */ function buildApprovalDoctorChecks(input) { const checks = []; @@ -46092,14 +46092,14 @@ function check(id, status, summary, action, blockedBy = []) { /***/ }), -/***/ 6802: +/***/ 85881: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.cloneSetupConfiguration = cloneSetupConfiguration; -const setup_configuration_defaults_1 = __nccwpck_require__(31713); +const setup_configuration_defaults_1 = __nccwpck_require__(23381); /** Returns a reference-isolated configuration snapshot without serializing it. */ function cloneSetupConfiguration(configuration) { const agents = Object.fromEntries(setup_configuration_defaults_1.SETUP_AGENT_TASKS.map((task) => [ @@ -46146,7 +46146,7 @@ function cloneSetupConfiguration(configuration) { /***/ }), -/***/ 31713: +/***/ 23381: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -46158,12 +46158,12 @@ exports.createDefaultSetupStorageConfiguration = createDefaultSetupStorageConfig exports.createDefaultSetupConfiguration = createDefaultSetupConfiguration; exports.mergeSetupConfiguration = mergeSetupConfiguration; exports.normalizeSetupConfigurationLocales = normalizeSetupConfigurationLocales; -const agent_1 = __nccwpck_require__(95407); -const issue_inactivity_1 = __nccwpck_require__(7703); -const deployment_configuration_1 = __nccwpck_require__(5664); -const locale_1 = __nccwpck_require__(64552); -const issue_workflow_profile_1 = __nccwpck_require__(62721); -const pull_request_approval_policy_1 = __nccwpck_require__(53553); +const agent_1 = __nccwpck_require__(89040); +const issue_inactivity_1 = __nccwpck_require__(38572); +const deployment_configuration_1 = __nccwpck_require__(22495); +const locale_1 = __nccwpck_require__(15386); +const issue_workflow_profile_1 = __nccwpck_require__(26744); +const pull_request_approval_policy_1 = __nccwpck_require__(98820); exports.SETUP_AGENT_TASKS = [ 'planner', 'findings', @@ -46368,7 +46368,7 @@ function normalizeSetupConfigurationLocales(configuration) { /***/ }), -/***/ 44018: +/***/ 87770: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -46379,15 +46379,15 @@ exports.buildSetupPlan = buildSetupPlan; exports.setupPlanGuardPaths = setupPlanGuardPaths; exports.buildSetupRepositoryVariables = buildSetupRepositoryVariables; exports.buildSetupActionInputs = buildSetupActionInputs; -const pull_request_description_1 = __nccwpck_require__(25623); -const setup_workflow_catalog_1 = __nccwpck_require__(37008); -const setup_configuration_defaults_1 = __nccwpck_require__(31713); -const setup_configuration_storage_policy_1 = __nccwpck_require__(60368); -const setup_credential_requirement_policy_1 = __nccwpck_require__(61975); +const pull_request_description_1 = __nccwpck_require__(45315); +const setup_workflow_catalog_1 = __nccwpck_require__(24596); +const setup_configuration_defaults_1 = __nccwpck_require__(23381); +const setup_configuration_storage_policy_1 = __nccwpck_require__(2554); +const setup_credential_requirement_policy_1 = __nccwpck_require__(43562); Object.defineProperty(exports, "buildSetupCredentialRequirements", ({ enumerable: true, get: function () { return setup_credential_requirement_policy_1.buildSetupCredentialRequirements; } })); -const locale_1 = __nccwpck_require__(64552); -const issue_workflow_profile_1 = __nccwpck_require__(62721); -const setup_issue_workflow_policy_1 = __nccwpck_require__(47280); +const locale_1 = __nccwpck_require__(15386); +const issue_workflow_profile_1 = __nccwpck_require__(26744); +const setup_issue_workflow_policy_1 = __nccwpck_require__(81182); function buildSetupPlan(configuration, mergeQueueReadiness = [], approvalReadiness = []) { const workflowFiles = (0, setup_workflow_catalog_1.enabledSetupWorkflowFiles)((0, setup_issue_workflow_policy_1.effectiveIssueWorkflowFeatures)(configuration)) .filter(file => file !== 'copilot_pull_request_approval.yml' || configuration.pullRequestApproval.mode !== 'off'); @@ -46664,7 +46664,7 @@ function unique(values) { /***/ }), -/***/ 93015: +/***/ 56637: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -46685,16 +46685,16 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) { }; Object.defineProperty(exports, "__esModule", ({ value: true })); /** Public setup-policy boundary. Each concern is implemented in a focused policy module. */ -__exportStar(__nccwpck_require__(31713), exports); -__exportStar(__nccwpck_require__(44018), exports); -__exportStar(__nccwpck_require__(60368), exports); -__exportStar(__nccwpck_require__(31156), exports); -__exportStar(__nccwpck_require__(47280), exports); +__exportStar(__nccwpck_require__(23381), exports); +__exportStar(__nccwpck_require__(87770), exports); +__exportStar(__nccwpck_require__(2554), exports); +__exportStar(__nccwpck_require__(13339), exports); +__exportStar(__nccwpck_require__(81182), exports); /***/ }), -/***/ 60368: +/***/ 2554: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -46714,7 +46714,7 @@ exports.validateSetupStorageAgainstRemote = validateSetupStorageAgainstRemote; exports.validateSetupManagedResourceInventory = validateSetupManagedResourceInventory; exports.usesOrganizationStorage = usesOrganizationStorage; exports.validateStorageConfiguration = validateStorageConfiguration; -const setup_configuration_defaults_1 = __nccwpck_require__(31713); +const setup_configuration_defaults_1 = __nccwpck_require__(23381); function resolveSetupResourceScope(policy, name) { return policy.overrides[name] ?? policy.defaultScope; } @@ -46925,22 +46925,22 @@ function mergeStoragePolicy(base, override) { /***/ }), -/***/ 31156: +/***/ 13339: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.validateSetupConfiguration = validateSetupConfiguration; -const setup_configuration_defaults_1 = __nccwpck_require__(31713); -const agent_configuration_validation_policy_1 = __nccwpck_require__(4345); -const setup_configuration_storage_policy_1 = __nccwpck_require__(60368); -const issue_inactivity_1 = __nccwpck_require__(7703); -const deployment_configuration_1 = __nccwpck_require__(5664); -const locale_1 = __nccwpck_require__(64552); -const issue_workflow_profile_1 = __nccwpck_require__(62721); -const setup_issue_workflow_policy_1 = __nccwpck_require__(47280); -const pull_request_approval_policy_1 = __nccwpck_require__(53553); +const setup_configuration_defaults_1 = __nccwpck_require__(23381); +const agent_configuration_validation_policy_1 = __nccwpck_require__(60596); +const setup_configuration_storage_policy_1 = __nccwpck_require__(2554); +const issue_inactivity_1 = __nccwpck_require__(38572); +const deployment_configuration_1 = __nccwpck_require__(22495); +const locale_1 = __nccwpck_require__(15386); +const issue_workflow_profile_1 = __nccwpck_require__(26744); +const setup_issue_workflow_policy_1 = __nccwpck_require__(81182); +const pull_request_approval_policy_1 = __nccwpck_require__(98820); function validateSetupConfiguration(configuration, options = {}) { const errors = []; errors.push(...(0, pull_request_approval_policy_1.validatePullRequestApprovalPolicy)(configuration.pullRequestApproval, options.allowIncompleteApproval === true)); @@ -47098,14 +47098,14 @@ function validateLocale(errors, label, value, optional) { /***/ }), -/***/ 61975: +/***/ 43562: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildSetupCredentialRequirements = buildSetupCredentialRequirements; -const setup_configuration_defaults_1 = __nccwpck_require__(31713); +const setup_configuration_defaults_1 = __nccwpck_require__(23381); const SECRET_BY_MODEL_PROVIDER = { openai: 'OPENAI_API_KEY', anthropic: 'ANTHROPIC_API_KEY', @@ -47213,7 +47213,7 @@ function uniqueDefined(current, next) { /***/ }), -/***/ 67183: +/***/ 80226: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -47222,10 +47222,10 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SETUP_DOCTOR_CATALOG_DEFINITIONS = exports.SPANISH_SETUP_DOCTOR_DEFINITION = exports.ENGLISH_SETUP_DOCTOR_DEFINITION = exports.SETUP_DOCTOR_MESSAGE_IDS = void 0; exports.resolveStaticSetupDoctorCatalog = resolveStaticSetupDoctorCatalog; exports.resolveSetupDoctorCatalog = resolveSetupDoctorCatalog; -const message_catalog_1 = __nccwpck_require__(5313); -const resolved_message_catalog_policy_1 = __nccwpck_require__(98248); -const merge_queue_message_catalog_1 = __nccwpck_require__(22459); -const approval_doctor_message_catalog_1 = __nccwpck_require__(24591); +const message_catalog_1 = __nccwpck_require__(27097); +const resolved_message_catalog_policy_1 = __nccwpck_require__(55069); +const merge_queue_message_catalog_1 = __nccwpck_require__(56033); +const approval_doctor_message_catalog_1 = __nccwpck_require__(36904); const DOCTOR_ONLY_MESSAGE_IDS = Object.freeze([ 'doctor.title', 'doctor.title.partial', @@ -47551,7 +47551,7 @@ function resolveSetupDoctorCatalog(locale, configuration, resolver) { /***/ }), -/***/ 81332: +/***/ 67615: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -47562,10 +47562,10 @@ exports.doctorCheck = doctorCheck; exports.skippedDoctorCheck = skippedDoctorCheck; exports.normalizedDoctorPathId = normalizedDoctorPathId; exports.buildLocaleDoctorChecks = buildLocaleDoctorChecks; -const locale_1 = __nccwpck_require__(64552); -const message_catalog_1 = __nccwpck_require__(5313); -const agent_1 = __nccwpck_require__(95407); -const setup_doctor_message_catalog_1 = __nccwpck_require__(67183); +const locale_1 = __nccwpck_require__(15386); +const message_catalog_1 = __nccwpck_require__(27097); +const agent_1 = __nccwpck_require__(89040); +const setup_doctor_message_catalog_1 = __nccwpck_require__(80226); const EMPTY_TOTALS = { pass: 0, warn: 0, @@ -47656,7 +47656,7 @@ function localeDoctorCheck(scope, configured, effective, inheritedWhenEmpty, dyn /***/ }), -/***/ 36206: +/***/ 67323: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -47664,7 +47664,7 @@ function localeDoctorCheck(scope, configured, effective, inheritedWhenEmpty, dyn Object.defineProperty(exports, "__esModule", ({ value: true })); exports.selectedInitialLabels = selectedInitialLabels; exports.selectedInitialIssueTypes = selectedInitialIssueTypes; -const setup_issue_workflow_policy_1 = __nccwpck_require__(47280); +const setup_issue_workflow_policy_1 = __nccwpck_require__(81182); /** Removes workflow-specific resources that are not part of the effective profile. */ function selectedInitialLabels(labels, configuration) { if (!configuration) @@ -47723,7 +47723,7 @@ function selectedInitialIssueTypes(issueTypes, configuration) { /***/ }), -/***/ 47280: +/***/ 81182: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -47733,7 +47733,7 @@ exports.effectiveIssueWorkflowProfile = effectiveIssueWorkflowProfile; exports.effectiveIssueWorkflowFeatures = effectiveIssueWorkflowFeatures; exports.effectiveIssueWorkflowLabels = effectiveIssueWorkflowLabels; exports.effectiveIssueFormLabels = effectiveIssueFormLabels; -const issue_workflow_profile_1 = __nccwpck_require__(62721); +const issue_workflow_profile_1 = __nccwpck_require__(26744); /** Applies feature switches to the explicit issue workflow selection. */ function effectiveIssueWorkflowProfile(configuration) { const configured = configuration.issueWorkflows?.enabled ?? issue_workflow_profile_1.ISSUE_WORKFLOW_KINDS; @@ -47786,7 +47786,7 @@ function effectiveIssueFormLabels(configuration) { /***/ }), -/***/ 55254: +/***/ 53289: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -47826,7 +47826,7 @@ function buildSetupJourneyView(repository, stage, mutationStarted, outcome, choi /***/ }), -/***/ 96850: +/***/ 54718: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -47906,7 +47906,7 @@ function buildSetupPatCreationUrl(input) { /***/ }), -/***/ 66964: +/***/ 30748: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -47915,7 +47915,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.fixedSetupPatIntentQuestionIds = fixedSetupPatIntentQuestionIds; exports.setupPatIntentNeedsOwnerKind = setupPatIntentNeedsOwnerKind; exports.setupPatIntentOwnerConflict = setupPatIntentOwnerConflict; -const setup_token_permission_policy_1 = __nccwpck_require__(10947); +const setup_token_permission_policy_1 = __nccwpck_require__(99590); /** Local inputs with explicit precedence are decisions, not questions. */ function fixedSetupPatIntentQuestionIds(overrides, skipVariables, skipSecrets) { const fixed = []; @@ -47960,7 +47960,7 @@ function setupPatIntentOwnerConflict(configuration, ownerKind) { /***/ }), -/***/ 48581: +/***/ 10267: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -47979,7 +47979,7 @@ function summarizeSetupPermissions(requirements) { /***/ }), -/***/ 65207: +/***/ 6009: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -47992,9 +47992,9 @@ exports.transitionSetupQuestionnaire = transitionSetupQuestionnaire; exports.enterSetupConfirmation = enterSetupConfirmation; exports.finishSetupQuestionnaire = finishSetupQuestionnaire; exports.setupQuestionnaireStateLabel = setupQuestionnaireStateLabel; -const setup_configuration_clone_policy_1 = __nccwpck_require__(6802); -const setup_configuration_defaults_1 = __nccwpck_require__(31713); -const issue_workflow_profile_1 = __nccwpck_require__(62721); +const setup_configuration_clone_policy_1 = __nccwpck_require__(85881); +const setup_configuration_defaults_1 = __nccwpck_require__(23381); +const issue_workflow_profile_1 = __nccwpck_require__(26744); const AGENT_PROVIDERS = ['codex', 'opencode', 'cursor']; const MODEL_PROVIDERS = ['openai', 'anthropic', 'google', 'openrouter', 'opencode', 'local']; const PERMISSION_INTENT_QUESTION_IDS = new Set([ @@ -48492,7 +48492,7 @@ function projectLabel(field) { /***/ }), -/***/ 41599: +/***/ 92567: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -48524,7 +48524,7 @@ function sameSetupRemoteFacts(left, right) { /***/ }), -/***/ 19750: +/***/ 65640: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -48601,7 +48601,7 @@ function unverifiable(requirement, message) { /***/ }), -/***/ 10947: +/***/ 99590: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -48614,10 +48614,10 @@ exports.buildSetupPatIntentUncertainty = buildSetupPatIntentUncertainty; exports.requiredSetupPatPermissionDelta = requiredSetupPatPermissionDelta; exports.buildWorkflowPatPermissionRequirements = buildWorkflowPatPermissionRequirements; exports.normalizePermissionRequirements = normalizePermissionRequirements; -const setup_configuration_plan_1 = __nccwpck_require__(44018); -const setup_credential_requirement_policy_1 = __nccwpck_require__(61975); -const setup_issue_workflow_policy_1 = __nccwpck_require__(47280); -const setup_configuration_storage_policy_1 = __nccwpck_require__(60368); +const setup_configuration_plan_1 = __nccwpck_require__(87770); +const setup_credential_requirement_policy_1 = __nccwpck_require__(43562); +const setup_issue_workflow_policy_1 = __nccwpck_require__(81182); +const setup_configuration_storage_policy_1 = __nccwpck_require__(2554); const requirement = (input) => ({ id: `${input.role}.${input.scope}.${input.permission.toLowerCase().replace(/[^a-z0-9]+/gu, '-')}`, applicability: 'required', @@ -48847,7 +48847,7 @@ function levelRank(level) { /***/ }), -/***/ 62186: +/***/ 3449: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -48856,9 +48856,9 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildCopilotStatusSnapshot = buildCopilotStatusSnapshot; exports.buildCopilotStatusResult = buildCopilotStatusResult; exports.formatCopilotStatus = formatCopilotStatus; -const result_1 = __nccwpck_require__(61444); -const bugbot_result_finding_state_projection_policy_1 = __nccwpck_require__(22443); -const publication_message_catalog_1 = __nccwpck_require__(46042); +const result_1 = __nccwpck_require__(73817); +const bugbot_result_finding_state_projection_policy_1 = __nccwpck_require__(98117); +const publication_message_catalog_1 = __nccwpck_require__(34223); /** Builds a read-only status snapshot from the facts already loaded by setup. */ function buildCopilotStatusSnapshot(execution) { const issueLabels = [...(execution.labels?.currentIssueLabels ?? [])]; @@ -48947,7 +48947,7 @@ function formatCopilotStatus(snapshot, locale = 'en-US', catalog = (0, publicati /***/ }), -/***/ 97549: +/***/ 43193: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -48976,7 +48976,7 @@ function calculateJitteredWorkflowDelay(baseDelayMilliseconds, randomValue, poli /***/ }), -/***/ 73001: +/***/ 6152: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -49039,7 +49039,7 @@ function setGlobalLoggerDebug(debug, isRemote = false) { /***/ }), -/***/ 81504: +/***/ 46445: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -49090,7 +49090,7 @@ function toPullRequestReviewOperationError(error, operation, context) { /***/ }), -/***/ 55294: +/***/ 77658: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -49105,13 +49105,13 @@ exports.deploymentSuccess = deploymentSuccess; exports.blockedDeploymentResult = blockedDeploymentResult; exports.shouldRecordUnexpectedFailure = shouldRecordUnexpectedFailure; exports.semanticCleanupError = semanticCleanupError; -const application_error_1 = __nccwpck_require__(2965); -const deployment_plan_policy_1 = __nccwpck_require__(86485); -const deployment_presentation_policy_1 = __nccwpck_require__(85458); -const deployment_message_catalog_1 = __nccwpck_require__(71375); -const deployment_operation_1 = __nccwpck_require__(17176); -const merge_queue_readiness_1 = __nccwpck_require__(36637); -const result_1 = __nccwpck_require__(61444); +const application_error_1 = __nccwpck_require__(75999); +const deployment_plan_policy_1 = __nccwpck_require__(8352); +const deployment_presentation_policy_1 = __nccwpck_require__(83221); +const deployment_message_catalog_1 = __nccwpck_require__(79364); +const deployment_operation_1 = __nccwpck_require__(92730); +const merge_queue_readiness_1 = __nccwpck_require__(12515); +const result_1 = __nccwpck_require__(73817); exports.DEPLOYMENT_ORCHESTRATION_TASK_ID = "DeploymentOrchestrationUseCase"; class DeploymentOrchestrationRuntime { constructor(dependencies, stateBoundary) { @@ -49395,16 +49395,16 @@ function failureCategory(operation) { /***/ }), -/***/ 55649: +/***/ 27827: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SupersededDeploymentInvocationError = exports.DeploymentStateBoundary = void 0; -const deployment_state_fence_1 = __nccwpck_require__(32481); -const deployment_lifecycle_policy_1 = __nccwpck_require__(83013); -const application_error_1 = __nccwpck_require__(2965); +const deployment_state_fence_1 = __nccwpck_require__(72369); +const deployment_lifecycle_policy_1 = __nccwpck_require__(54037); +const application_error_1 = __nccwpck_require__(75999); class DeploymentStateBoundary { constructor(state, labels) { this.state = state; @@ -49484,14 +49484,14 @@ exports.SupersededDeploymentInvocationError = SupersededDeploymentInvocationErro /***/ }), -/***/ 21113: +/***/ 41601: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.CheckProgressUseCase = void 0; -const check_progress_workflow_1 = __nccwpck_require__(54747); +const check_progress_workflow_1 = __nccwpck_require__(94343); /** Application boundary for assessing and publishing issue progress. */ class CheckProgressUseCase { constructor(issueDescriptionQueryPort, issueLabelsPort, issueProgressPort, branchRepository, pullRequestRepository, aiRepository, publicationSourceQuery) { @@ -49521,21 +49521,21 @@ exports.CheckProgressUseCase = CheckProgressUseCase; /***/ }), -/***/ 54747: +/***/ 94343: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runCheckProgressWorkflow = runCheckProgressWorkflow; -const result_1 = __nccwpck_require__(61444); -const logging_ports_1 = __nccwpck_require__(73001); -const task_emoji_1 = __nccwpck_require__(83142); -const sync_progress_labels_to_open_pull_requests_1 = __nccwpck_require__(24603); -const progress_summary_builder_1 = __nccwpck_require__(96279); -const progress_analysis_workflow_1 = __nccwpck_require__(67608); -const application_error_1 = __nccwpck_require__(2965); -const publication_outcome_policy_1 = __nccwpck_require__(11035); +const result_1 = __nccwpck_require__(73817); +const logging_ports_1 = __nccwpck_require__(6152); +const task_emoji_1 = __nccwpck_require__(46103); +const sync_progress_labels_to_open_pull_requests_1 = __nccwpck_require__(18277); +const progress_summary_builder_1 = __nccwpck_require__(62721); +const progress_analysis_workflow_1 = __nccwpck_require__(88729); +const application_error_1 = __nccwpck_require__(75999); +const publication_outcome_policy_1 = __nccwpck_require__(79719); /** Publishes a completed progress assessment after the analysis workflow succeeds. */ async function runCheckProgressWorkflow(param, taskId, dependencies) { (0, logging_ports_1.logInfo)(`${(0, task_emoji_1.getTaskEmoji)(taskId)} Executing ${taskId}.`); @@ -49642,14 +49642,14 @@ function logProgressAssessment(progress, summary, reasoning, remaining) { /***/ }), -/***/ 56690: +/***/ 84579: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.CloseInactiveIssuesUseCase = void 0; -const close_inactive_issues_workflow_1 = __nccwpck_require__(12632); +const close_inactive_issues_workflow_1 = __nccwpck_require__(86288); /** Application boundary for the scheduled inactivity-maintenance action. */ class CloseInactiveIssuesUseCase { constructor(issueQueryPort, issueClosurePort, clock, catalogResolver) { @@ -49673,19 +49673,19 @@ exports.CloseInactiveIssuesUseCase = CloseInactiveIssuesUseCase; /***/ }), -/***/ 12632: +/***/ 86288: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runCloseInactiveIssuesWorkflow = runCloseInactiveIssuesWorkflow; -const result_1 = __nccwpck_require__(61444); -const issue_inactivity_1 = __nccwpck_require__(7703); -const logging_ports_1 = __nccwpck_require__(73001); -const application_error_1 = __nccwpck_require__(2965); -const inactivity_message_catalog_1 = __nccwpck_require__(78447); -const inactivity_notification_policy_1 = __nccwpck_require__(62453); +const result_1 = __nccwpck_require__(73817); +const issue_inactivity_1 = __nccwpck_require__(38572); +const logging_ports_1 = __nccwpck_require__(6152); +const application_error_1 = __nccwpck_require__(75999); +const inactivity_message_catalog_1 = __nccwpck_require__(74902); +const inactivity_notification_policy_1 = __nccwpck_require__(1572); const TASK_ID = 'CloseInactiveIssuesUseCase'; /** Scans waiting issues and closes only candidates that remain inactive. */ async function runCloseInactiveIssuesWorkflow(param, dependencies) { @@ -49839,7 +49839,7 @@ function unique(values) { /***/ }), -/***/ 59528: +/***/ 76549: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -49848,8 +49848,8 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.validateReleaseInput = validateReleaseInput; exports.normalizeVersion = normalizeVersion; exports.versionForRelease = versionForRelease; -const input_keys_1 = __nccwpck_require__(83725); -const application_error_1 = __nccwpck_require__(2965); +const input_keys_1 = __nccwpck_require__(88539); +const application_error_1 = __nccwpck_require__(75999); const SEMVER_PATTERN = /^\d+(\.\d+){0,2}$/; function validateReleaseInput(input) { if (!input.version.length) @@ -49877,16 +49877,16 @@ function versionForRelease(version) { /***/ }), -/***/ 68781: +/***/ 25258: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.CreateReleaseUseCase = void 0; -const logging_ports_1 = __nccwpck_require__(73001); -const task_emoji_1 = __nccwpck_require__(83142); -const create_release_workflow_1 = __nccwpck_require__(96528); +const logging_ports_1 = __nccwpck_require__(6152); +const task_emoji_1 = __nccwpck_require__(46103); +const create_release_workflow_1 = __nccwpck_require__(75138); class CreateReleaseUseCase { constructor(repositoryReleasePort) { this.repositoryReleasePort = repositoryReleasePort; @@ -49902,18 +49902,18 @@ exports.CreateReleaseUseCase = CreateReleaseUseCase; /***/ }), -/***/ 96528: +/***/ 75138: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runCreateRelease = runCreateRelease; -const result_1 = __nccwpck_require__(61444); -const logging_ports_1 = __nccwpck_require__(73001); -const create_release_policy_1 = __nccwpck_require__(59528); -const deployment_continuation_guard_1 = __nccwpck_require__(47063); -const application_error_1 = __nccwpck_require__(2965); +const result_1 = __nccwpck_require__(73817); +const logging_ports_1 = __nccwpck_require__(6152); +const create_release_policy_1 = __nccwpck_require__(76549); +const deployment_continuation_guard_1 = __nccwpck_require__(1779); +const application_error_1 = __nccwpck_require__(75999); async function runCreateRelease(param, taskId, repositoryReleasePort) { const operation = param.operation; const continuationError = (0, deployment_continuation_guard_1.validateDeploymentContinuation)(operation, param.requestedOperationId, ["publishing"], param.requestedVersion); @@ -49964,16 +49964,16 @@ function failureResult(taskId, message, code) { /***/ }), -/***/ 27977: +/***/ 22120: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.CreateTagUseCase = void 0; -const logging_ports_1 = __nccwpck_require__(73001); -const task_emoji_1 = __nccwpck_require__(83142); -const create_tag_workflow_1 = __nccwpck_require__(69061); +const logging_ports_1 = __nccwpck_require__(6152); +const task_emoji_1 = __nccwpck_require__(46103); +const create_tag_workflow_1 = __nccwpck_require__(23539); class CreateTagUseCase { constructor(repositoryReleasePort) { this.repositoryReleasePort = repositoryReleasePort; @@ -49989,17 +49989,17 @@ exports.CreateTagUseCase = CreateTagUseCase; /***/ }), -/***/ 69061: +/***/ 23539: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runCreateTag = runCreateTag; -const result_1 = __nccwpck_require__(61444); -const logging_ports_1 = __nccwpck_require__(73001); -const deployment_continuation_guard_1 = __nccwpck_require__(47063); -const application_error_1 = __nccwpck_require__(2965); +const result_1 = __nccwpck_require__(73817); +const logging_ports_1 = __nccwpck_require__(6152); +const deployment_continuation_guard_1 = __nccwpck_require__(1779); +const application_error_1 = __nccwpck_require__(75999); async function runCreateTag(param, taskId, repositoryTagPort) { const validationFailure = validateTagInput(param, taskId); if (validationFailure) @@ -50039,14 +50039,14 @@ function noTagResult(taskId, tagName) { /***/ }), -/***/ 57402: +/***/ 28399: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.AcceptPromotionHandler = void 0; -const deployment_orchestration_runtime_1 = __nccwpck_require__(55294); +const deployment_orchestration_runtime_1 = __nccwpck_require__(77658); class AcceptPromotionHandler { constructor(runtime) { this.runtime = runtime; @@ -50101,17 +50101,17 @@ function matchesPromotion(operation, pullRequest) { /***/ }), -/***/ 92224: +/***/ 46361: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.ConfirmPublicationHandler = void 0; -const application_error_1 = __nccwpck_require__(2965); -const deployment_plan_policy_1 = __nccwpck_require__(86485); -const deployment_operation_1 = __nccwpck_require__(17176); -const deployment_orchestration_runtime_1 = __nccwpck_require__(55294); +const application_error_1 = __nccwpck_require__(75999); +const deployment_plan_policy_1 = __nccwpck_require__(8352); +const deployment_operation_1 = __nccwpck_require__(92730); +const deployment_orchestration_runtime_1 = __nccwpck_require__(77658); class ConfirmPublicationHandler { constructor(runtime, reconciliation) { this.runtime = runtime; @@ -50202,17 +50202,17 @@ function requireProductionSha(operation) { /***/ }), -/***/ 58024: +/***/ 85138: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.ContinueDeploymentHandler = void 0; -const application_error_1 = __nccwpck_require__(2965); -const deployment_operation_1 = __nccwpck_require__(17176); -const managed_pull_request_1 = __nccwpck_require__(7975); -const deployment_orchestration_runtime_1 = __nccwpck_require__(55294); +const application_error_1 = __nccwpck_require__(75999); +const deployment_operation_1 = __nccwpck_require__(92730); +const managed_pull_request_1 = __nccwpck_require__(95914); +const deployment_orchestration_runtime_1 = __nccwpck_require__(77658); class ContinueDeploymentHandler { constructor(runtime, acceptPromotion, reconciliation) { this.runtime = runtime; @@ -50288,17 +50288,17 @@ function assertSameRepository(context, pullRequest) { /***/ }), -/***/ 13502: +/***/ 43877: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.PreparePromotionHandler = void 0; -const application_error_1 = __nccwpck_require__(2965); -const deployment_plan_policy_1 = __nccwpck_require__(86485); -const deployment_operation_1 = __nccwpck_require__(17176); -const deployment_orchestration_runtime_1 = __nccwpck_require__(55294); +const application_error_1 = __nccwpck_require__(75999); +const deployment_plan_policy_1 = __nccwpck_require__(8352); +const deployment_operation_1 = __nccwpck_require__(92730); +const deployment_orchestration_runtime_1 = __nccwpck_require__(77658); class PreparePromotionHandler { constructor(runtime, acceptPromotion) { this.runtime = runtime; @@ -50455,16 +50455,16 @@ function pendingPromotion(operation, pullRequest) { /***/ }), -/***/ 36195: +/***/ 3344: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.ReconciliationHandler = void 0; -const deployment_plan_policy_1 = __nccwpck_require__(86485); -const deployment_operation_1 = __nccwpck_require__(17176); -const deployment_orchestration_runtime_1 = __nccwpck_require__(55294); +const deployment_plan_policy_1 = __nccwpck_require__(8352); +const deployment_operation_1 = __nccwpck_require__(92730); +const deployment_orchestration_runtime_1 = __nccwpck_require__(77658); class ReconciliationHandler { constructor(runtime) { this.runtime = runtime; @@ -50604,17 +50604,17 @@ exports.ReconciliationHandler = ReconciliationHandler; /***/ }), -/***/ 93088: +/***/ 66571: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.RecordFailureHandler = void 0; -const application_error_1 = __nccwpck_require__(2965); -const result_1 = __nccwpck_require__(61444); -const deployment_operation_1 = __nccwpck_require__(17176); -const deployment_orchestration_runtime_1 = __nccwpck_require__(55294); +const application_error_1 = __nccwpck_require__(75999); +const result_1 = __nccwpck_require__(73817); +const deployment_operation_1 = __nccwpck_require__(92730); +const deployment_orchestration_runtime_1 = __nccwpck_require__(77658); class RecordFailureHandler { constructor(runtime) { this.runtime = runtime; @@ -50653,23 +50653,23 @@ function failureCategory(phase) { /***/ }), -/***/ 67132: +/***/ 36850: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.DeploymentOrchestrationUseCase = void 0; -const application_error_1 = __nccwpck_require__(2965); -const deployment_orchestration_runtime_1 = __nccwpck_require__(55294); -const deployment_state_boundary_1 = __nccwpck_require__(55649); -const result_1 = __nccwpck_require__(61444); -const accept_promotion_handler_1 = __nccwpck_require__(57402); -const confirm_publication_handler_1 = __nccwpck_require__(92224); -const continue_deployment_handler_1 = __nccwpck_require__(58024); -const prepare_promotion_handler_1 = __nccwpck_require__(13502); -const reconciliation_handler_1 = __nccwpck_require__(36195); -const record_failure_handler_1 = __nccwpck_require__(93088); +const application_error_1 = __nccwpck_require__(75999); +const deployment_orchestration_runtime_1 = __nccwpck_require__(77658); +const deployment_state_boundary_1 = __nccwpck_require__(27827); +const result_1 = __nccwpck_require__(73817); +const accept_promotion_handler_1 = __nccwpck_require__(28399); +const confirm_publication_handler_1 = __nccwpck_require__(46361); +const continue_deployment_handler_1 = __nccwpck_require__(85138); +const prepare_promotion_handler_1 = __nccwpck_require__(43877); +const reconciliation_handler_1 = __nccwpck_require__(3344); +const record_failure_handler_1 = __nccwpck_require__(66571); class DeploymentOrchestrationUseCase { constructor(dependencies) { this.taskId = deployment_orchestration_runtime_1.DEPLOYMENT_ORCHESTRATION_TASK_ID; @@ -50726,14 +50726,14 @@ exports.DeploymentOrchestrationUseCase = DeploymentOrchestrationUseCase; /***/ }), -/***/ 72387: +/***/ 38575: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.findIssueBranch = findIssueBranch; -const logging_ports_1 = __nccwpck_require__(73001); +const logging_ports_1 = __nccwpck_require__(6152); async function findIssueBranch(param, repository) { if (param.pushedBranch) return param.pushedBranch; @@ -50751,14 +50751,14 @@ async function findIssueBranch(param, repository) { /***/ }), -/***/ 50658: +/***/ 84837: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.InitialSetupUseCase = void 0; -const initial_setup_workflow_1 = __nccwpck_require__(32599); +const initial_setup_workflow_1 = __nccwpck_require__(18079); /** Application boundary for provisioning a repository for Copilot automation. */ class InitialSetupUseCase { constructor(authenticatedUserPort, initialLabelProvisioningPort, issueTypeProvisioningPort, latestTagQueryPort, repositoryDefaultBranchPort, repositoryTagPort, setupWorkspacePort, setupRepositoryVariablesPort, setupRepositorySecretsPort, setupRemoteConfigurationReadPort) { @@ -50794,21 +50794,21 @@ exports.InitialSetupUseCase = InitialSetupUseCase; /***/ }), -/***/ 32599: +/***/ 18079: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runInitialSetupWorkflow = runInitialSetupWorkflow; -const result_1 = __nccwpck_require__(61444); -const version_policy_1 = __nccwpck_require__(36707); -const logging_ports_1 = __nccwpck_require__(73001); -const task_emoji_1 = __nccwpck_require__(83142); -const setup_resource_provisioning_1 = __nccwpck_require__(64709); -const application_error_1 = __nccwpck_require__(2965); -const setup_issue_resource_policy_1 = __nccwpck_require__(36206); -const setup_configuration_policy_1 = __nccwpck_require__(93015); +const result_1 = __nccwpck_require__(73817); +const version_policy_1 = __nccwpck_require__(8381); +const logging_ports_1 = __nccwpck_require__(6152); +const task_emoji_1 = __nccwpck_require__(46103); +const setup_resource_provisioning_1 = __nccwpck_require__(94894); +const application_error_1 = __nccwpck_require__(75999); +const setup_issue_resource_policy_1 = __nccwpck_require__(67323); +const setup_configuration_policy_1 = __nccwpck_require__(56637); const TASK_ID = 'InitialSetupUseCase'; /** Runs repository setup as an ordered application workflow with explicit port dependencies. */ async function runInitialSetupWorkflow(request, dependencies) { @@ -50994,7 +50994,7 @@ function fromMessages(messages, code) { /***/ }), -/***/ 724: +/***/ 28121: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -51084,20 +51084,20 @@ function projectEvidence(source) { /***/ }), -/***/ 34342: +/***/ 84542: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.ObserveBranchSyncUseCase = void 0; -const result_1 = __nccwpck_require__(61444); -const branch_sync_notification_policy_1 = __nccwpck_require__(90741); -const logging_ports_1 = __nccwpck_require__(73001); -const application_error_1 = __nccwpck_require__(2965); -const branch_sync_message_catalog_1 = __nccwpck_require__(71641); -const transition_notification_workflow_1 = __nccwpck_require__(93066); -const publication_outcome_policy_1 = __nccwpck_require__(11035); +const result_1 = __nccwpck_require__(73817); +const branch_sync_notification_policy_1 = __nccwpck_require__(79895); +const logging_ports_1 = __nccwpck_require__(6152); +const application_error_1 = __nccwpck_require__(75999); +const branch_sync_message_catalog_1 = __nccwpck_require__(89245); +const transition_notification_workflow_1 = __nccwpck_require__(1725); +const publication_outcome_policy_1 = __nccwpck_require__(79719); const TASK_ID = "ObserveBranchSyncUseCase"; /** * Cheap push-time observer. It only queries branch relationships/comparisons @@ -51218,24 +51218,24 @@ function failure(message, cause, payload) { /***/ }), -/***/ 67608: +/***/ 88729: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.analyzeProgress = analyzeProgress; -const agent_1 = __nccwpck_require__(71889); -const result_1 = __nccwpck_require__(61444); -const agent_task_policy_1 = __nccwpck_require__(2601); -const prompts_1 = __nccwpck_require__(71854); -const logging_ports_1 = __nccwpck_require__(73001); -const project_context_instruction_1 = __nccwpck_require__(36158); -const find_issue_branch_1 = __nccwpck_require__(72387); -const progress_prerequisite_policy_1 = __nccwpck_require__(96802); -const progress_response_1 = __nccwpck_require__(68049); -const application_error_1 = __nccwpck_require__(2965); -const agent_output_locale_policy_1 = __nccwpck_require__(2584); +const agent_1 = __nccwpck_require__(79937); +const result_1 = __nccwpck_require__(73817); +const agent_task_policy_1 = __nccwpck_require__(85712); +const prompts_1 = __nccwpck_require__(69518); +const logging_ports_1 = __nccwpck_require__(6152); +const project_context_instruction_1 = __nccwpck_require__(63907); +const find_issue_branch_1 = __nccwpck_require__(38575); +const progress_prerequisite_policy_1 = __nccwpck_require__(31001); +const progress_response_1 = __nccwpck_require__(64264); +const application_error_1 = __nccwpck_require__(75999); +const agent_output_locale_policy_1 = __nccwpck_require__(30601); /** Loads progress context and asks the configured agent for an assessment. */ async function analyzeProgress(param, taskId, dependencies) { const issueNumber = param.issueNumber; @@ -51320,7 +51320,7 @@ function failure(taskId, message, code) { /***/ }), -/***/ 96802: +/***/ 31001: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -51346,7 +51346,7 @@ function validateProgressPrerequisites(input) { /***/ }), -/***/ 68049: +/***/ 64264: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -51354,8 +51354,8 @@ function validateProgressPrerequisites(input) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.PROGRESS_RESPONSE_SCHEMA = void 0; exports.parseProgressResponse = parseProgressResponse; -const agent_output_locale_policy_1 = __nccwpck_require__(2584); -const application_error_1 = __nccwpck_require__(2965); +const agent_output_locale_policy_1 = __nccwpck_require__(30601); +const application_error_1 = __nccwpck_require__(75999); exports.PROGRESS_RESPONSE_SCHEMA = { type: 'object', properties: { @@ -51385,7 +51385,7 @@ function parseProgressResponse(response, targetLocale) { /***/ }), -/***/ 96279: +/***/ 62721: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -51417,16 +51417,16 @@ function buildProgressSummaryMessage({ summary, progress, remaining, reasoning } /***/ }), -/***/ 31153: +/***/ 68891: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.PublishGithubActionUseCase = void 0; -const logging_ports_1 = __nccwpck_require__(73001); -const task_emoji_1 = __nccwpck_require__(83142); -const publish_github_action_workflow_1 = __nccwpck_require__(77361); +const logging_ports_1 = __nccwpck_require__(6152); +const task_emoji_1 = __nccwpck_require__(46103); +const publish_github_action_workflow_1 = __nccwpck_require__(63037); class PublishGithubActionUseCase { constructor(repositoryTagPort, repositoryReleasePort) { this.repositoryTagPort = repositoryTagPort; @@ -51443,18 +51443,18 @@ exports.PublishGithubActionUseCase = PublishGithubActionUseCase; /***/ }), -/***/ 77361: +/***/ 63037: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runPublishGithubAction = runPublishGithubAction; -const result_1 = __nccwpck_require__(61444); -const input_keys_1 = __nccwpck_require__(83725); -const logging_ports_1 = __nccwpck_require__(73001); -const deployment_continuation_guard_1 = __nccwpck_require__(47063); -const application_error_1 = __nccwpck_require__(2965); +const result_1 = __nccwpck_require__(73817); +const input_keys_1 = __nccwpck_require__(88539); +const logging_ports_1 = __nccwpck_require__(6152); +const deployment_continuation_guard_1 = __nccwpck_require__(1779); +const application_error_1 = __nccwpck_require__(75999); async function runPublishGithubAction(param, taskId, repositoryTagPort, repositoryReleasePort) { const validationFailure = validateVersion(param, taskId); if (validationFailure) @@ -51499,16 +51499,16 @@ function failureResult(taskId, sourceTag, targetTag) { /***/ }), -/***/ 23758: +/***/ 61313: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.PublishIssueCommentUseCase = void 0; -const logging_ports_1 = __nccwpck_require__(73001); -const task_emoji_1 = __nccwpck_require__(83142); -const publish_issue_comment_workflow_1 = __nccwpck_require__(95981); +const logging_ports_1 = __nccwpck_require__(6152); +const task_emoji_1 = __nccwpck_require__(46103); +const publish_issue_comment_workflow_1 = __nccwpck_require__(30626); /** Application boundary for creating or updating a specific issue comment. */ class PublishIssueCommentUseCase { constructor(issueCommentPort) { @@ -51525,16 +51525,16 @@ exports.PublishIssueCommentUseCase = PublishIssueCommentUseCase; /***/ }), -/***/ 95981: +/***/ 30626: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runPublishIssueComment = runPublishIssueComment; -const result_1 = __nccwpck_require__(61444); -const logging_ports_1 = __nccwpck_require__(73001); -const application_error_1 = __nccwpck_require__(2965); +const result_1 = __nccwpck_require__(73817); +const logging_ports_1 = __nccwpck_require__(6152); +const application_error_1 = __nccwpck_require__(75999); async function runPublishIssueComment(param, taskId, issueCommentPort) { if (param.kind === 'invalid') { return [new result_1.Result({ @@ -51583,19 +51583,19 @@ function appendCommentContent(previous, addition) { /***/ }), -/***/ 78348: +/***/ 65928: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildRecommendationResult = buildRecommendationResult; -const result_1 = __nccwpck_require__(61444); -const recommendation_policy_1 = __nccwpck_require__(83857); -const logging_ports_1 = __nccwpck_require__(73001); -const application_error_1 = __nccwpck_require__(2965); -const agent_output_locale_policy_1 = __nccwpck_require__(2584); -const implementation_plan_1 = __nccwpck_require__(52620); +const result_1 = __nccwpck_require__(73817); +const recommendation_policy_1 = __nccwpck_require__(39410); +const logging_ports_1 = __nccwpck_require__(6152); +const application_error_1 = __nccwpck_require__(75999); +const agent_output_locale_policy_1 = __nccwpck_require__(30601); +const implementation_plan_1 = __nccwpck_require__(77001); function buildRecommendationResult(param, taskId, response, issueDescriptionFingerprint, previousRecommendation, issueNumber) { const extracted = extractImplementationPlan(response, param.targetLocale); if (!extracted) { @@ -51682,14 +51682,14 @@ function hasOnlyResponseKeys(payload) { /***/ }), -/***/ 33526: +/***/ 73746: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.RecommendStepsUseCase = void 0; -const recommend_steps_workflow_1 = __nccwpck_require__(17415); +const recommend_steps_workflow_1 = __nccwpck_require__(77522); /** Application boundary for generating non-duplicated implementation guidance. */ class RecommendStepsUseCase { constructor(issueDescriptionQueryPort, aiRepository) { @@ -51709,26 +51709,26 @@ exports.RecommendStepsUseCase = RecommendStepsUseCase; /***/ }), -/***/ 17415: +/***/ 77522: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runRecommendStepsWorkflow = runRecommendStepsWorkflow; -const agent_1 = __nccwpck_require__(71889); -const result_1 = __nccwpck_require__(61444); -const agent_task_policy_1 = __nccwpck_require__(2601); -const recommendation_policy_1 = __nccwpck_require__(83857); -const prompts_1 = __nccwpck_require__(71854); -const logging_ports_1 = __nccwpck_require__(73001); -const project_context_instruction_1 = __nccwpck_require__(36158); -const task_emoji_1 = __nccwpck_require__(83142); -const recommend_steps_result_policy_1 = __nccwpck_require__(78348); -const application_error_1 = __nccwpck_require__(2965); -const agent_response_schemas_1 = __nccwpck_require__(63523); -const agent_output_locale_policy_1 = __nccwpck_require__(2584); -const implementation_plan_1 = __nccwpck_require__(52620); +const agent_1 = __nccwpck_require__(79937); +const result_1 = __nccwpck_require__(73817); +const agent_task_policy_1 = __nccwpck_require__(85712); +const recommendation_policy_1 = __nccwpck_require__(39410); +const prompts_1 = __nccwpck_require__(69518); +const logging_ports_1 = __nccwpck_require__(6152); +const project_context_instruction_1 = __nccwpck_require__(63907); +const task_emoji_1 = __nccwpck_require__(46103); +const recommend_steps_result_policy_1 = __nccwpck_require__(65928); +const application_error_1 = __nccwpck_require__(75999); +const agent_response_schemas_1 = __nccwpck_require__(25603); +const agent_output_locale_policy_1 = __nccwpck_require__(30601); +const implementation_plan_1 = __nccwpck_require__(77001); /** Runs the recommendation policy and agent interaction for an issue. */ async function runRecommendStepsWorkflow(param, taskId, dependencies) { (0, logging_ports_1.logInfo)(`${(0, task_emoji_1.getTaskEmoji)(taskId)} Executing ${taskId}.`); @@ -51825,7 +51825,7 @@ function failure(taskId, message, code) { /***/ }), -/***/ 64709: +/***/ 94894: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -51835,9 +51835,9 @@ exports.ensureRepositoryVariables = ensureRepositoryVariables; exports.ensureRepositorySecrets = ensureRepositorySecrets; exports.resolveRemoteConfiguration = resolveRemoteConfiguration; exports.groupSetupResources = groupSetupResources; -const setup_configuration_policy_1 = __nccwpck_require__(93015); -const logging_ports_1 = __nccwpck_require__(73001); -const application_error_1 = __nccwpck_require__(2965); +const setup_configuration_policy_1 = __nccwpck_require__(56637); +const logging_ports_1 = __nccwpck_require__(6152); +const application_error_1 = __nccwpck_require__(75999); async function ensureRepositoryVariables(context, dependencies, setupConfiguration, remoteConfiguration) { if (!setupConfiguration?.manageRepositoryVariables || !dependencies.setupRepositoryVariablesPort) { return { errors: [] }; @@ -51991,15 +51991,15 @@ async function upsertSecretGroups(context, port, groups) { /***/ }), -/***/ 24603: +/***/ 18277: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.syncProgressLabelsToOpenPullRequests = syncProgressLabelsToOpenPullRequests; -const progress_labels_1 = __nccwpck_require__(71285); -const logging_ports_1 = __nccwpck_require__(73001); +const progress_labels_1 = __nccwpck_require__(97890); +const logging_ports_1 = __nccwpck_require__(6152); async function syncProgressLabelsToOpenPullRequests(branch, progress, issueRepository, pullRequestRepository) { const roundedProgress = Math.min(100, Math.max(0, Math.round(progress / 5) * 5)); const newProgressLabel = `${roundedProgress}%`; @@ -52018,16 +52018,16 @@ async function syncProgressLabelsToOpenPullRequests(branch, progress, issueRepos /***/ }), -/***/ 83269: +/***/ 44880: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SynchronizeAgentActivityUseCase = void 0; -const agent_activity_label_policy_1 = __nccwpck_require__(24342); -const logging_ports_1 = __nccwpck_require__(73001); -const application_error_1 = __nccwpck_require__(2965); +const agent_activity_label_policy_1 = __nccwpck_require__(79966); +const logging_ports_1 = __nccwpck_require__(6152); +const application_error_1 = __nccwpck_require__(75999); /** * Maintains the temporary agent-activity label around a complete route. * Cleanup is deliberately best-effort so a label outage never hides the @@ -52082,16 +52082,16 @@ function sameLabels(left, right) { /***/ }), -/***/ 70937: +/***/ 4643: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runBranchSyncCommand = runBranchSyncCommand; -const result_1 = __nccwpck_require__(61444); -const branch_sync_command_1 = __nccwpck_require__(7465); -const application_error_1 = __nccwpck_require__(2965); +const result_1 = __nccwpck_require__(73817); +const branch_sync_command_1 = __nccwpck_require__(51114); +const application_error_1 = __nccwpck_require__(75999); /** Authorizes and runs an explicit or natural-language branch synchronization request. */ async function runBranchSyncCommand(context, options, args, authorization) { const parsed = (0, branch_sync_command_1.parseBranchSyncCommandArguments)(args); @@ -52127,7 +52127,7 @@ function unauthorized(taskId) { /***/ }), -/***/ 52921: +/***/ 82113: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -52138,10 +52138,10 @@ exports.branchSyncConflictEligibilityError = branchSyncConflictEligibilityError; exports.completedBranchSyncResult = completedBranchSyncResult; exports.unavailableBranchSyncResult = unavailableBranchSyncResult; exports.failedBranchSyncResult = failedBranchSyncResult; -const agent_1 = __nccwpck_require__(71889); -const result_1 = __nccwpck_require__(61444); -const workspace_changes_1 = __nccwpck_require__(51578); -const application_error_1 = __nccwpck_require__(2965); +const agent_1 = __nccwpck_require__(79937); +const result_1 = __nccwpck_require__(73817); +const workspace_changes_1 = __nccwpck_require__(93370); +const application_error_1 = __nccwpck_require__(75999); exports.BRANCH_SYNC_TASK_ID = "SyncBranchUseCase"; const MAX_AGENT_CONFLICT_PATHS = 20; function branchSyncConflictEligibilityError(preparation, useAgent, agentConfiguration) { @@ -52203,18 +52203,18 @@ function failedBranchSyncResult(reason, cause) { /***/ }), -/***/ 43725: +/***/ 392: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SyncBranchUseCase = void 0; -const branch_sync_conflicts_1 = __nccwpck_require__(93843); -const logging_ports_1 = __nccwpck_require__(73001); -const verify_command_policy_1 = __nccwpck_require__(32739); -const verify_command_runner_1 = __nccwpck_require__(71843); -const branch_sync_execution_policy_1 = __nccwpck_require__(52921); +const branch_sync_conflicts_1 = __nccwpck_require__(84434); +const logging_ports_1 = __nccwpck_require__(6152); +const verify_command_policy_1 = __nccwpck_require__(96031); +const verify_command_runner_1 = __nccwpck_require__(57742); +const branch_sync_execution_policy_1 = __nccwpck_require__(82113); /** Performs a race-safe parent-to-child merge and invokes the fixer only for eligible conflicts. */ class SyncBranchUseCase { constructor(dependencies, workspace, fixer, authenticatedUser, git) { @@ -52338,14 +52338,14 @@ exports.SyncBranchUseCase = SyncBranchUseCase; /***/ }), -/***/ 11677: +/***/ 55721: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.CheckCliUpdateUseCase = void 0; -const cli_version_1 = __nccwpck_require__(30717); +const cli_version_1 = __nccwpck_require__(27089); /** Checks for a newer published CLI version without coupling the application to npm. */ class CheckCliUpdateUseCase { constructor(cliUpdateCheckPort) { @@ -52363,18 +52363,18 @@ exports.CheckCliUpdateUseCase = CheckCliUpdateUseCase; /***/ }), -/***/ 97791: +/***/ 42442: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runCommentAutomationAction = runCommentAutomationAction; -const result_1 = __nccwpck_require__(61444); -const commit_autofix_and_resolve_workflow_1 = __nccwpck_require__(75112); -const commit_user_request_workflow_1 = __nccwpck_require__(17359); -const logging_ports_1 = __nccwpck_require__(73001); -const application_error_1 = __nccwpck_require__(2965); +const result_1 = __nccwpck_require__(73817); +const commit_autofix_and_resolve_workflow_1 = __nccwpck_require__(93455); +const commit_user_request_workflow_1 = __nccwpck_require__(43393); +const logging_ports_1 = __nccwpck_require__(6152); +const application_error_1 = __nccwpck_require__(75999); /** Runs the selected mutating action and returns any result records it produces. */ async function runCommentAutomationAction(param, options, route, intentPayload) { if (route === "review") @@ -52451,7 +52451,7 @@ async function runDoUserRequestAction(param, options, intentPayload) { /***/ }), -/***/ 99496: +/***/ 63134: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -52459,15 +52459,15 @@ async function runDoUserRequestAction(param, options, intentPayload) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runExplicitCommentCommand = runExplicitCommentCommand; exports.invalidCommentCommandResult = invalidCommentCommandResult; -const result_1 = __nccwpck_require__(61444); -const status_command_policy_1 = __nccwpck_require__(62186); -const copilot_interaction_policy_1 = __nccwpck_require__(8964); -const review_command_1 = __nccwpck_require__(22551); -const commit_user_request_workflow_1 = __nccwpck_require__(17359); -const workspace_mutation_guard_1 = __nccwpck_require__(12627); -const branch_sync_comment_command_1 = __nccwpck_require__(70937); -const application_error_1 = __nccwpck_require__(2965); -const bugbot_review_operation_context_1 = __nccwpck_require__(50616); +const result_1 = __nccwpck_require__(73817); +const status_command_policy_1 = __nccwpck_require__(3449); +const copilot_interaction_policy_1 = __nccwpck_require__(90108); +const review_command_1 = __nccwpck_require__(1811); +const commit_user_request_workflow_1 = __nccwpck_require__(43393); +const workspace_mutation_guard_1 = __nccwpck_require__(24243); +const branch_sync_comment_command_1 = __nccwpck_require__(4643); +const application_error_1 = __nccwpck_require__(75999); +const bugbot_review_operation_context_1 = __nccwpck_require__(16660); const LEARNED_BUGBOT_RULE_PATH = '.copilot/BUGBOT.learned.md'; /** Executes deterministic /copilot commands without routing them through intent detection. */ async function runExplicitCommentCommand(param, options, command, actorAuthorizationPort) { @@ -52629,16 +52629,16 @@ function invalidCommentCommandResult(taskId, reason) { /***/ }), -/***/ 74745: +/***/ 46187: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.completeCommentAutomation = completeCommentAutomation; -const bugbot_fix_intent_payload_1 = __nccwpck_require__(56352); -const logging_ports_1 = __nccwpck_require__(73001); -const comment_automation_action_workflow_1 = __nccwpck_require__(97791); +const bugbot_fix_intent_payload_1 = __nccwpck_require__(25734); +const logging_ports_1 = __nccwpck_require__(6152); +const comment_automation_action_workflow_1 = __nccwpck_require__(42442); async function completeCommentAutomation(param, options, decision) { logUnauthorizedActionSkip(decision); if (decision.route === 'think') { @@ -52658,7 +52658,7 @@ function logUnauthorizedActionSkip(decision) { /***/ }), -/***/ 70719: +/***/ 37055: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -52666,14 +52666,14 @@ function logUnauthorizedActionSkip(decision) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.projectCommentAutomationContext = projectCommentAutomationContext; exports.withCommentLanguageAdaptation = withCommentLanguageAdaptation; -const status_command_policy_1 = __nccwpck_require__(62186); -const comment_language_translation_workflow_1 = __nccwpck_require__(78212); -const think_workflow_1 = __nccwpck_require__(14720); -const bugbot_review_operation_context_1 = __nccwpck_require__(50616); -const push_single_action_contexts_1 = __nccwpck_require__(87805); -const think_request_policy_1 = __nccwpck_require__(98559); -const think_input_policy_1 = __nccwpck_require__(12636); -const copilot_command_1 = __nccwpck_require__(87134); +const status_command_policy_1 = __nccwpck_require__(3449); +const comment_language_translation_workflow_1 = __nccwpck_require__(72770); +const think_workflow_1 = __nccwpck_require__(36450); +const bugbot_review_operation_context_1 = __nccwpck_require__(16660); +const push_single_action_contexts_1 = __nccwpck_require__(47841); +const think_request_policy_1 = __nccwpck_require__(23995); +const think_input_policy_1 = __nccwpck_require__(59687); +const copilot_command_1 = __nccwpck_require__(11771); function projectCommentAutomationContext(source, language, userComment) { const review = (0, bugbot_review_operation_context_1.projectBugbotReviewOperationContext)(source); return Object.freeze({ @@ -52743,18 +52743,18 @@ function withCommentLanguageAdaptation(context, adaptation) { /***/ }), -/***/ 55136: +/***/ 46175: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.resolveCommentAutomationDecision = resolveCommentAutomationDecision; -const logging_ports_1 = __nccwpck_require__(73001); -const bugbot_fix_intent_payload_1 = __nccwpck_require__(56352); -const comment_automation_route_policy_1 = __nccwpck_require__(34038); -const copilot_comment_request_1 = __nccwpck_require__(81916); -const copilot_command_1 = __nccwpck_require__(87134); +const logging_ports_1 = __nccwpck_require__(6152); +const bugbot_fix_intent_payload_1 = __nccwpck_require__(25734); +const comment_automation_route_policy_1 = __nccwpck_require__(47058); +const copilot_comment_request_1 = __nccwpck_require__(86819); +const copilot_command_1 = __nccwpck_require__(11771); async function resolveCommentAutomationDecision(param, options, actorAuthorizationPort) { (0, logging_ports_1.logInfo)("Running bugbot fix intent detection (before Think)."); const intentResults = await options.intentUseCase.invoke(param.bugbot.fixIntent); @@ -52778,15 +52778,15 @@ function logIntent(intentPayload) { /***/ }), -/***/ 33657: +/***/ 10554: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runNaturalLanguageCommentAutomation = runNaturalLanguageCommentAutomation; -const comment_automation_decision_workflow_1 = __nccwpck_require__(55136); -const comment_automation_completion_workflow_1 = __nccwpck_require__(74745); +const comment_automation_decision_workflow_1 = __nccwpck_require__(46175); +const comment_automation_completion_workflow_1 = __nccwpck_require__(46187); /** Runs the natural-language comment pipeline after deterministic commands are excluded. */ async function runNaturalLanguageCommentAutomation(param, options, actorAuthorizationPort, languageResults) { const decision = await (0, comment_automation_decision_workflow_1.resolveCommentAutomationDecision)(param, options, actorAuthorizationPort); @@ -52800,14 +52800,14 @@ async function runNaturalLanguageCommentAutomation(param, options, actorAuthoriz /***/ }), -/***/ 34038: +/***/ 47058: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.resolveCommentAutomationRoute = resolveCommentAutomationRoute; -const bugbot_fix_intent_payload_1 = __nccwpck_require__(56352); +const bugbot_fix_intent_payload_1 = __nccwpck_require__(25734); function resolveCommentAutomationRoute(payload, allowedToModifyFiles, botMentioned = false, explicitMutationCommand = false) { if (!botMentioned && !explicitMutationCommand) return 'think'; @@ -52825,24 +52825,24 @@ function resolveCommentAutomationRoute(payload, allowedToModifyFiles, botMention /***/ }), -/***/ 91490: +/***/ 9661: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runCommentAutomation = runCommentAutomation; -const result_1 = __nccwpck_require__(61444); -const logging_ports_1 = __nccwpck_require__(73001); -const copilot_comment_request_1 = __nccwpck_require__(81916); -const copilot_command_1 = __nccwpck_require__(87134); -const comment_automation_command_workflow_1 = __nccwpck_require__(99496); -const comment_automation_natural_language_workflow_1 = __nccwpck_require__(33657); -const application_error_1 = __nccwpck_require__(2965); -const branch_sync_command_1 = __nccwpck_require__(7465); -const branch_sync_comment_command_1 = __nccwpck_require__(70937); -const comment_automation_context_1 = __nccwpck_require__(70719); -const comment_language_translation_workflow_1 = __nccwpck_require__(78212); +const result_1 = __nccwpck_require__(73817); +const logging_ports_1 = __nccwpck_require__(6152); +const copilot_comment_request_1 = __nccwpck_require__(86819); +const copilot_command_1 = __nccwpck_require__(11771); +const comment_automation_command_workflow_1 = __nccwpck_require__(63134); +const comment_automation_natural_language_workflow_1 = __nccwpck_require__(10554); +const application_error_1 = __nccwpck_require__(75999); +const branch_sync_command_1 = __nccwpck_require__(51114); +const branch_sync_comment_command_1 = __nccwpck_require__(4643); +const comment_automation_context_1 = __nccwpck_require__(37055); +const comment_language_translation_workflow_1 = __nccwpck_require__(72770); async function runCommentAutomation(initialParam, options, actorAuthorizationPort) { (0, logging_ports_1.logInfo)(`${options.taskId} started.`); let languageResults = []; @@ -52904,19 +52904,19 @@ async function runCommentAutomation(initialParam, options, actorAuthorizationPor /***/ }), -/***/ 84014: +/***/ 28001: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.CommitUseCase = void 0; -const result_1 = __nccwpck_require__(61444); -const logging_ports_1 = __nccwpck_require__(73001); -const task_emoji_1 = __nccwpck_require__(83142); -const application_error_1 = __nccwpck_require__(2965); -const bugbot_review_operation_context_1 = __nccwpck_require__(50616); -const push_single_action_contexts_1 = __nccwpck_require__(87805); +const result_1 = __nccwpck_require__(73817); +const logging_ports_1 = __nccwpck_require__(6152); +const task_emoji_1 = __nccwpck_require__(46103); +const application_error_1 = __nccwpck_require__(75999); +const bugbot_review_operation_context_1 = __nccwpck_require__(16660); +const push_single_action_contexts_1 = __nccwpck_require__(47841); class CommitUseCase { constructor(notifyNewCommitUseCase, checkChangesIssueSizeUseCase, detectPotentialProblemsUseCase, checkProgressUseCase, actorAuthorizationPort) { this.notifyNewCommitUseCase = notifyNewCommitUseCase; @@ -52970,18 +52970,18 @@ exports.CommitUseCase = CommitUseCase; /***/ }), -/***/ 83966: +/***/ 71813: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.ExecutionBranchVersionResolver = void 0; -const result_1 = __nccwpck_require__(61444); -const version_resolution_application_policy_1 = __nccwpck_require__(28787); -const version_resolution_outcome_policy_1 = __nccwpck_require__(23448); -const version_resolution_result_policy_1 = __nccwpck_require__(86641); -const version_resolution_policy_1 = __nccwpck_require__(20610); +const result_1 = __nccwpck_require__(73817); +const version_resolution_application_policy_1 = __nccwpck_require__(40231); +const version_resolution_outcome_policy_1 = __nccwpck_require__(43496); +const version_resolution_result_policy_1 = __nccwpck_require__(11730); +const version_resolution_policy_1 = __nccwpck_require__(92373); class ExecutionBranchVersionResolver { constructor(latestTagQueryPort, getReleaseVersion, getReleaseType, getHotfixVersion) { this.latestTagQueryPort = latestTagQueryPort; @@ -53079,7 +53079,7 @@ function unchanged(context) { /***/ }), -/***/ 66185: +/***/ 63436: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -53087,8 +53087,8 @@ function unchanged(context) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.resolveEventIssueNumber = resolveEventIssueNumber; exports.resolveSingleActionIssueNumber = resolveSingleActionIssueNumber; -const positive_integer_policy_1 = __nccwpck_require__(45613); -const title_utils_1 = __nccwpck_require__(58747); +const positive_integer_policy_1 = __nccwpck_require__(19879); +const title_utils_1 = __nccwpck_require__(46267); function resolveEventIssueNumber(context) { let issueNumber; if (context.isIssue) @@ -53182,14 +53182,14 @@ function currentSingleAction(context) { /***/ }), -/***/ 78531: +/***/ 90972: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.resolveExecutionIssueNumber = resolveExecutionIssueNumber; -const execution_issue_number_policy_1 = __nccwpck_require__(66185); +const execution_issue_number_policy_1 = __nccwpck_require__(63436); async function resolveExecutionIssueNumber(context, issueRepository) { return context.isSingleAction ? (0, execution_issue_number_policy_1.resolveSingleActionIssueNumber)(context, issueRepository) @@ -53199,14 +53199,14 @@ async function resolveExecutionIssueNumber(context, issueRepository) { /***/ }), -/***/ 51778: +/***/ 88512: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SetupExecutionUseCase = void 0; -const setup_execution_workflow_1 = __nccwpck_require__(70395); +const setup_execution_workflow_1 = __nccwpck_require__(42285); class SetupExecutionUseCase { constructor(issueSetupPort, organizationSetupPort, configurationPort, branchVersionResolver) { this.issueSetupPort = issueSetupPort; @@ -53229,19 +53229,19 @@ exports.SetupExecutionUseCase = SetupExecutionUseCase; /***/ }), -/***/ 70395: +/***/ 42285: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runSetupExecution = runSetupExecution; -const application_error_1 = __nccwpck_require__(2965); -const initial_labels_policy_1 = __nccwpck_require__(21435); -const previous_branch_state_policy_1 = __nccwpck_require__(25491); -const issue_workflow_profile_1 = __nccwpck_require__(62721); -const logging_ports_1 = __nccwpck_require__(73001); -const resolve_execution_issue_number_1 = __nccwpck_require__(78531); +const application_error_1 = __nccwpck_require__(75999); +const initial_labels_policy_1 = __nccwpck_require__(50293); +const previous_branch_state_policy_1 = __nccwpck_require__(43630); +const issue_workflow_profile_1 = __nccwpck_require__(26744); +const logging_ports_1 = __nccwpck_require__(6152); +const resolve_execution_issue_number_1 = __nccwpck_require__(90972); async function runSetupExecution(context, dependencies) { (0, logging_ports_1.setGlobalLoggerDebug)(context.debug, context.local); const tokenUser = await loadTokenUser(context, dependencies.organizationSetupPort); @@ -53414,18 +53414,18 @@ function positiveIssueNumberOrUndefined(value) { /***/ }), -/***/ 14502: +/***/ 72042: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.IssueCommentUseCase = void 0; -const result_1 = __nccwpck_require__(61444); -const comment_automation_use_case_1 = __nccwpck_require__(91490); -const check_issue_comment_language_use_case_1 = __nccwpck_require__(34670); -const comment_automation_context_1 = __nccwpck_require__(70719); -const pull_request_workflow_context_1 = __nccwpck_require__(90587); +const result_1 = __nccwpck_require__(73817); +const comment_automation_use_case_1 = __nccwpck_require__(9661); +const check_issue_comment_language_use_case_1 = __nccwpck_require__(93152); +const comment_automation_context_1 = __nccwpck_require__(37055); +const pull_request_workflow_context_1 = __nccwpck_require__(73447); class IssueCommentUseCase { constructor(languageUseCase, intentUseCase, thinkUseCase, autofixUseCase, doUserRequestUseCase, actorAuthorizationPort, bugbotGitMutationPort, dismissBugbotFindingsUseCase, reviewPotentialProblemsUseCase, updatePullRequestDescriptionUseCase, rememberBugbotRuleUseCase, syncBranchUseCase, preBranchSddContinuation) { this.languageUseCase = languageUseCase; @@ -53490,24 +53490,24 @@ exports.IssueCommentUseCase = IssueCommentUseCase; /***/ }), -/***/ 43712: +/***/ 65281: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.IssueUseCase = void 0; -const result_1 = __nccwpck_require__(61444); -const application_error_1 = __nccwpck_require__(2965); -const logging_ports_1 = __nccwpck_require__(73001); -const task_emoji_1 = __nccwpck_require__(83142); -const issue_workflow_1 = __nccwpck_require__(95967); -const check_permissions_workflow_1 = __nccwpck_require__(98658); -const update_title_workflow_1 = __nccwpck_require__(89641); -const project_content_link_workflow_1 = __nccwpck_require__(72383); -const issue_workflow_context_1 = __nccwpck_require__(13765); -const push_single_action_contexts_1 = __nccwpck_require__(87805); -const issue_start_policy_1 = __nccwpck_require__(20953); +const result_1 = __nccwpck_require__(73817); +const application_error_1 = __nccwpck_require__(75999); +const logging_ports_1 = __nccwpck_require__(6152); +const task_emoji_1 = __nccwpck_require__(46103); +const issue_workflow_1 = __nccwpck_require__(661); +const check_permissions_workflow_1 = __nccwpck_require__(17102); +const update_title_workflow_1 = __nccwpck_require__(50029); +const project_content_link_workflow_1 = __nccwpck_require__(89064); +const issue_workflow_context_1 = __nccwpck_require__(98005); +const push_single_action_contexts_1 = __nccwpck_require__(47841); +const issue_start_policy_1 = __nccwpck_require__(90332); class IssueUseCase { constructor(recommendStepsUseCase, answerIssueHelpUseCase, workflowSteps, issueCommentQueryPort, actorAuthorizationPort, preBranchSddGate) { this.recommendStepsUseCase = recommendStepsUseCase; @@ -53649,18 +53649,18 @@ function applyBranchConfigurationPatch(param, patch) { /***/ }), -/***/ 95967: +/***/ 661: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runIssueWorkflow = runIssueWorkflow; -const result_1 = __nccwpck_require__(61444); -const logging_ports_1 = __nccwpck_require__(73001); -const copilot_interaction_policy_1 = __nccwpck_require__(8964); -const semantic_result_publication_policy_1 = __nccwpck_require__(55150); -const application_error_1 = __nccwpck_require__(2965); +const result_1 = __nccwpck_require__(73817); +const logging_ports_1 = __nccwpck_require__(6152); +const copilot_interaction_policy_1 = __nccwpck_require__(90108); +const semantic_result_publication_policy_1 = __nccwpck_require__(81985); +const application_error_1 = __nccwpck_require__(75999); /** Coordinates issue lifecycle steps in their required sequential order. */ async function runIssueWorkflow(context, taskId, ports) { const results = []; @@ -53811,7 +53811,7 @@ function issueWorkflowOutcome(results, branchConfigurationPatch, recommendationS /***/ }), -/***/ 13765: +/***/ 98005: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -53821,7 +53821,7 @@ exports.branchPreparationOutcome = branchPreparationOutcome; exports.projectIssueWorkflowStepContexts = projectIssueWorkflowStepContexts; exports.projectAssignmentContext = projectAssignmentContext; exports.copyProjects = copyProjects; -const issue_start_policy_1 = __nccwpck_require__(20953); +const issue_start_policy_1 = __nccwpck_require__(90332); function branchPreparationOutcome(results, configurationPatch = {}) { return Object.freeze({ results: Object.freeze([...results]), @@ -53973,7 +53973,7 @@ function selectIssueType(source) { /***/ }), -/***/ 53803: +/***/ 99961: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -53981,11 +53981,11 @@ function selectIssueType(source) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.ResolveMessageCatalogUseCase = void 0; exports.buildCatalogResponseSchema = buildCatalogResponseSchema; -const agent_task_policy_1 = __nccwpck_require__(2601); -const message_catalog_1 = __nccwpck_require__(5313); -const locale_1 = __nccwpck_require__(64552); -const localize_message_catalog_1 = __nccwpck_require__(22907); -const logging_ports_1 = __nccwpck_require__(73001); +const agent_task_policy_1 = __nccwpck_require__(85712); +const message_catalog_1 = __nccwpck_require__(27097); +const locale_1 = __nccwpck_require__(15386); +const localize_message_catalog_1 = __nccwpck_require__(64005); +const logging_ports_1 = __nccwpck_require__(6152); class ResolveMessageCatalogUseCase { constructor(language) { this.language = language; @@ -54119,17 +54119,17 @@ function buildCatalogResponseSchema(source, ids, targetLocale = 'en-US') { /***/ }), -/***/ 94097: +/***/ 29415: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.PullRequestReviewCommentUseCase = void 0; -const comment_automation_use_case_1 = __nccwpck_require__(91490); -const check_pull_request_comment_language_use_case_1 = __nccwpck_require__(5988); -const comment_automation_context_1 = __nccwpck_require__(70719); -const pull_request_workflow_context_1 = __nccwpck_require__(90587); +const comment_automation_use_case_1 = __nccwpck_require__(9661); +const check_pull_request_comment_language_use_case_1 = __nccwpck_require__(21729); +const comment_automation_context_1 = __nccwpck_require__(37055); +const pull_request_workflow_context_1 = __nccwpck_require__(73447); class PullRequestReviewCommentUseCase { constructor(languageUseCase, intentUseCase, thinkUseCase, autofixUseCase, doUserRequestUseCase, actorAuthorizationPort, bugbotGitMutationPort, dismissBugbotFindingsUseCase, reviewPotentialProblemsUseCase, updatePullRequestDescriptionUseCase, rememberBugbotRuleUseCase, syncBranchUseCase) { this.languageUseCase = languageUseCase; @@ -54181,20 +54181,20 @@ exports.PullRequestReviewCommentUseCase = PullRequestReviewCommentUseCase; /***/ }), -/***/ 93567: +/***/ 27259: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.PullRequestUseCase = void 0; -const logging_ports_1 = __nccwpck_require__(73001); -const task_emoji_1 = __nccwpck_require__(83142); -const pull_request_workflow_1 = __nccwpck_require__(14671); -const update_title_workflow_1 = __nccwpck_require__(89641); -const project_content_link_workflow_1 = __nccwpck_require__(72383); -const pull_request_workflow_context_1 = __nccwpck_require__(90587); -const bugbot_review_operation_context_1 = __nccwpck_require__(50616); +const logging_ports_1 = __nccwpck_require__(6152); +const task_emoji_1 = __nccwpck_require__(46103); +const pull_request_workflow_1 = __nccwpck_require__(95238); +const update_title_workflow_1 = __nccwpck_require__(50029); +const project_content_link_workflow_1 = __nccwpck_require__(89064); +const pull_request_workflow_context_1 = __nccwpck_require__(73447); +const bugbot_review_operation_context_1 = __nccwpck_require__(16660); class PullRequestUseCase { constructor(updatePullRequestDescriptionUseCase, workflowSteps, reviewPotentialProblemsUseCase, actorAuthorizationPort) { this.updatePullRequestDescriptionUseCase = updatePullRequestDescriptionUseCase; @@ -54249,16 +54249,16 @@ function projectPullRequestWorkflowRouteContext(param) { /***/ }), -/***/ 14671: +/***/ 95238: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runPullRequestWorkflow = runPullRequestWorkflow; -const result_1 = __nccwpck_require__(61444); -const logging_ports_1 = __nccwpck_require__(73001); -const application_error_1 = __nccwpck_require__(2965); +const result_1 = __nccwpck_require__(73817); +const logging_ports_1 = __nccwpck_require__(6152); +const application_error_1 = __nccwpck_require__(75999); /** Coordinates pull-request lifecycle actions while preserving their sequential order. */ async function runPullRequestWorkflow(context, taskId, ports) { try { @@ -54337,7 +54337,7 @@ function logPullRequestState(context) { /***/ }), -/***/ 90587: +/***/ 73447: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -54345,7 +54345,7 @@ function logPullRequestState(context) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.projectPullRequestWorkflowStepContexts = projectPullRequestWorkflowStepContexts; exports.projectPullRequestDescriptionContext = projectPullRequestDescriptionContext; -const issue_workflow_context_1 = __nccwpck_require__(13765); +const issue_workflow_context_1 = __nccwpck_require__(98005); function projectPullRequestWorkflowStepContexts(source) { const projects = (0, issue_workflow_context_1.copyProjects)(source.project.getProjects()); return Object.freeze({ @@ -54405,7 +54405,7 @@ function projectPullRequestDescriptionContext(source) { /***/ }), -/***/ 87805: +/***/ 47841: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -54424,9 +54424,9 @@ exports.projectChangeSizeContext = projectChangeSizeContext; exports.projectInitialSetupContext = projectInitialSetupContext; exports.projectIssueCommentActionContext = projectIssueCommentActionContext; exports.projectAgentActivityContext = projectAgentActivityContext; -const recommendation_state_1 = __nccwpck_require__(21602); -const issue_comment_publication_policy_1 = __nccwpck_require__(28956); -const git_object_id_1 = __nccwpck_require__(36924); +const recommendation_state_1 = __nccwpck_require__(68514); +const issue_comment_publication_policy_1 = __nccwpck_require__(61899); +const git_object_id_1 = __nccwpck_require__(88623); function projectDeploymentPublicationContext(source) { return Object.freeze({ requestedOperationId: source.singleAction.operationId, @@ -54642,7 +54642,7 @@ function deepFreezeCopy(value) { /***/ }), -/***/ 89463: +/***/ 29475: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -54650,10 +54650,10 @@ function deepFreezeCopy(value) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.PreBranchSddGateUseCase = void 0; const node_crypto_1 = __nccwpck_require__(6005); -const result_1 = __nccwpck_require__(61444); -const issue_start_policy_1 = __nccwpck_require__(20953); -const pre_branch_sdd_1 = __nccwpck_require__(54078); -const application_error_1 = __nccwpck_require__(2965); +const result_1 = __nccwpck_require__(73817); +const issue_start_policy_1 = __nccwpck_require__(90332); +const pre_branch_sdd_1 = __nccwpck_require__(34730); +const application_error_1 = __nccwpck_require__(75999); const ANALYSIS_SCHEMA = { type: 'object', properties: { @@ -54933,15 +54933,15 @@ function buildDraftPrompt(context, snapshot, plan, answers, currentSdd) { /***/ }), -/***/ 39967: +/***/ 60830: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.AuditConfiguredSetupPatUseCase = void 0; -const setup_pat_creation_url_policy_1 = __nccwpck_require__(96850); -const setup_token_permission_policy_1 = __nccwpck_require__(10947); +const setup_pat_creation_url_policy_1 = __nccwpck_require__(54718); +const setup_token_permission_policy_1 = __nccwpck_require__(99590); /** Rechecks the final plan without granting permission based on the browser preview. */ class AuditConfiguredSetupPatUseCase { constructor(context, ports) { @@ -54996,20 +54996,20 @@ exports.AuditConfiguredSetupPatUseCase = AuditConfiguredSetupPatUseCase; /***/ }), -/***/ 39: +/***/ 87328: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SetupDoctorUseCase = void 0; -const locale_1 = __nccwpck_require__(64552); -const setup_configuration_policy_1 = __nccwpck_require__(93015); -const setup_doctor_report_policy_1 = __nccwpck_require__(81332); -const bounded_concurrency_policy_1 = __nccwpck_require__(50189); -const setup_doctor_message_catalog_1 = __nccwpck_require__(67183); -const setup_approval_doctor_policy_1 = __nccwpck_require__(8794); -const pull_request_approval_policy_1 = __nccwpck_require__(53553); +const locale_1 = __nccwpck_require__(15386); +const setup_configuration_policy_1 = __nccwpck_require__(56637); +const setup_doctor_report_policy_1 = __nccwpck_require__(67615); +const bounded_concurrency_policy_1 = __nccwpck_require__(35596); +const setup_doctor_message_catalog_1 = __nccwpck_require__(80226); +const setup_approval_doctor_policy_1 = __nccwpck_require__(53296); +const pull_request_approval_policy_1 = __nccwpck_require__(98820); class SetupDoctorUseCase { constructor(dependencies) { this.dependencies = dependencies; @@ -55414,35 +55414,35 @@ function doctorCatalogLocale(configuration) { /***/ }), -/***/ 24711: +/***/ 36888: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SetupCredentialsUseCase = exports.SetupQuestionnaireController = exports.SetupWizardUseCase = void 0; -var setup_wizard_use_case_1 = __nccwpck_require__(27649); +var setup_wizard_use_case_1 = __nccwpck_require__(43433); Object.defineProperty(exports, "SetupWizardUseCase", ({ enumerable: true, get: function () { return setup_wizard_use_case_1.SetupWizardUseCase; } })); -var setup_questionnaire_controller_1 = __nccwpck_require__(20526); +var setup_questionnaire_controller_1 = __nccwpck_require__(41644); Object.defineProperty(exports, "SetupQuestionnaireController", ({ enumerable: true, get: function () { return setup_questionnaire_controller_1.SetupQuestionnaireController; } })); -var setup_credentials_use_case_1 = __nccwpck_require__(82634); +var setup_credentials_use_case_1 = __nccwpck_require__(67438); Object.defineProperty(exports, "SetupCredentialsUseCase", ({ enumerable: true, get: function () { return setup_credentials_use_case_1.SetupCredentialsUseCase; } })); /***/ }), -/***/ 14236: +/***/ 9890: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SetupMergeQueueReadinessUseCase = void 0; -const deployment_plan_policy_1 = __nccwpck_require__(86485); -const merge_queue_readiness_1 = __nccwpck_require__(36637); -const setup_doctor_report_policy_1 = __nccwpck_require__(81332); -const setup_doctor_message_catalog_1 = __nccwpck_require__(67183); -const merge_queue_message_catalog_1 = __nccwpck_require__(22459); +const deployment_plan_policy_1 = __nccwpck_require__(8352); +const merge_queue_readiness_1 = __nccwpck_require__(12515); +const setup_doctor_report_policy_1 = __nccwpck_require__(67615); +const setup_doctor_message_catalog_1 = __nccwpck_require__(80226); +const merge_queue_message_catalog_1 = __nccwpck_require__(56033); class SetupMergeQueueReadinessUseCase { constructor(targets, catalogResolver) { this.targets = targets; @@ -55590,21 +55590,21 @@ function uniqueTargets(targets) { /***/ }), -/***/ 99264: +/***/ 69277: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.PrepareSetupPatIntentUseCase = void 0; -const application_error_1 = __nccwpck_require__(2965); -const setup_interaction_cancelled_error_1 = __nccwpck_require__(93638); -const setup_configuration_policy_1 = __nccwpck_require__(93015); -const setup_questionnaire_policy_1 = __nccwpck_require__(65207); -const setup_pat_intent_policy_1 = __nccwpck_require__(66964); -const setup_token_permission_policy_1 = __nccwpck_require__(10947); -const setup_pat_creation_url_policy_1 = __nccwpck_require__(96850); -const setup_wizard_use_case_1 = __nccwpck_require__(27649); +const application_error_1 = __nccwpck_require__(75999); +const setup_interaction_cancelled_error_1 = __nccwpck_require__(38313); +const setup_configuration_policy_1 = __nccwpck_require__(56637); +const setup_questionnaire_policy_1 = __nccwpck_require__(6009); +const setup_pat_intent_policy_1 = __nccwpck_require__(30748); +const setup_token_permission_policy_1 = __nccwpck_require__(99590); +const setup_pat_creation_url_policy_1 = __nccwpck_require__(54718); +const setup_wizard_use_case_1 = __nccwpck_require__(43433); /** Frontend-neutral preparation; GitHub still issues the PAT in its own UI. */ class PrepareSetupPatIntentUseCase { constructor(ports) { @@ -55683,15 +55683,15 @@ exports.PrepareSetupPatIntentUseCase = PrepareSetupPatIntentUseCase; /***/ }), -/***/ 82634: +/***/ 67438: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SetupCredentialsUseCase = void 0; -const application_error_1 = __nccwpck_require__(2965); -const setup_configuration_storage_policy_1 = __nccwpck_require__(60368); +const application_error_1 = __nccwpck_require__(75999); +const setup_configuration_storage_policy_1 = __nccwpck_require__(2554); /** Coordinates secret collection and validation without placing secret values in config files. */ class SetupCredentialsUseCase { constructor(prompt, validation, secrets, remoteHealth, tokenPermissions, permissionPresenter) { @@ -55914,14 +55914,14 @@ function isAcceptedCredentialCheck(requirement, check) { /***/ }), -/***/ 32060: +/***/ 8419: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SetupJourneyUseCase = void 0; -const setup_journey_policy_1 = __nccwpck_require__(55254); +const setup_journey_policy_1 = __nccwpck_require__(53289); /** Tracks semantic milestones, independently of the CLI's rendering. */ class SetupJourneyUseCase { constructor(repository, presenter) { @@ -55982,15 +55982,15 @@ exports.SetupJourneyUseCase = SetupJourneyUseCase; /***/ }), -/***/ 20526: +/***/ 41644: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SetupQuestionnaireController = void 0; -const setup_questionnaire_policy_1 = __nccwpck_require__(65207); -const application_error_1 = __nccwpck_require__(2965); +const setup_questionnaire_policy_1 = __nccwpck_require__(6009); +const application_error_1 = __nccwpck_require__(75999); class SetupQuestionnaireController { constructor(terminal, renderer) { this.terminal = terminal; @@ -56031,14 +56031,14 @@ function toEvent(input) { /***/ }), -/***/ 64888: +/***/ 11797: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SetupTokenPermissionsUseCase = void 0; -const setup_token_permission_evidence_policy_1 = __nccwpck_require__(19750); +const setup_token_permission_evidence_policy_1 = __nccwpck_require__(65640); /** Validates PAT identity first, then runs only read-only permission probes. */ class SetupTokenPermissionsUseCase { constructor(credentials, permissions) { @@ -56095,7 +56095,7 @@ exports.SetupTokenPermissionsUseCase = SetupTokenPermissionsUseCase; /***/ }), -/***/ 27649: +/***/ 43433: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -56103,12 +56103,12 @@ exports.SetupTokenPermissionsUseCase = SetupTokenPermissionsUseCase; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SetupWizardUseCase = void 0; exports.buildInitialSetupConfiguration = buildInitialSetupConfiguration; -const application_error_1 = __nccwpck_require__(2965); -const setup_configuration_policy_1 = __nccwpck_require__(93015); -const setup_questionnaire_policy_1 = __nccwpck_require__(65207); -const setup_configuration_clone_policy_1 = __nccwpck_require__(6802); -const setup_doctor_message_catalog_1 = __nccwpck_require__(67183); -const pull_request_approval_policy_1 = __nccwpck_require__(53553); +const application_error_1 = __nccwpck_require__(75999); +const setup_configuration_policy_1 = __nccwpck_require__(56637); +const setup_questionnaire_policy_1 = __nccwpck_require__(6009); +const setup_configuration_clone_policy_1 = __nccwpck_require__(85881); +const setup_doctor_message_catalog_1 = __nccwpck_require__(80226); +const pull_request_approval_policy_1 = __nccwpck_require__(98820); class SetupWizardUseCase { constructor(dependencies) { this.dependencies = dependencies; @@ -56315,14 +56315,14 @@ function unavailableRemoteConfiguration() { /***/ }), -/***/ 36411: +/***/ 35697: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.VerifyGuidedWorkflowPatIdentityUseCase = void 0; -const application_error_1 = __nccwpck_require__(2965); +const application_error_1 = __nccwpck_require__(75999); /** Binds a guided runtime PAT to the bot account chosen before token entry. */ class VerifyGuidedWorkflowPatIdentityUseCase { constructor(identities) { @@ -56341,15 +56341,15 @@ exports.VerifyGuidedWorkflowPatIdentityUseCase = VerifyGuidedWorkflowPatIdentity /***/ }), -/***/ 47936: +/***/ 23388: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.VerifySetupPatBootstrapUseCase = void 0; -const application_error_1 = __nccwpck_require__(2965); -const setup_pat_creation_url_policy_1 = __nccwpck_require__(96850); +const application_error_1 = __nccwpck_require__(75999); +const setup_pat_creation_url_policy_1 = __nccwpck_require__(54718); /** Initial read-only gate shared by terminal and browser setup presentations. */ class VerifySetupPatBootstrapUseCase { constructor(ports) { @@ -56382,16 +56382,16 @@ exports.VerifySetupPatBootstrapUseCase = VerifySetupPatBootstrapUseCase; /***/ }), -/***/ 75598: +/***/ 5303: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.VerifyWebSetupApplyUseCase = void 0; -const application_error_1 = __nccwpck_require__(2965); -const setup_interaction_cancelled_error_1 = __nccwpck_require__(93638); -const setup_remote_facts_policy_1 = __nccwpck_require__(41599); +const application_error_1 = __nccwpck_require__(75999); +const setup_interaction_cancelled_error_1 = __nccwpck_require__(38313); +const setup_remote_facts_policy_1 = __nccwpck_require__(92567); /** Authorizes one web Apply against the facts the operator actually reviewed. */ class VerifyWebSetupApplyUseCase { constructor(ports) { @@ -56453,19 +56453,19 @@ exports.VerifyWebSetupApplyUseCase = VerifyWebSetupApplyUseCase; /***/ }), -/***/ 73840: +/***/ 73572: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SingleActionUseCase = void 0; -const logging_ports_1 = __nccwpck_require__(73001); -const task_emoji_1 = __nccwpck_require__(83142); -const single_action_workflow_1 = __nccwpck_require__(86940); -const think_workflow_1 = __nccwpck_require__(14720); -const bugbot_review_operation_context_1 = __nccwpck_require__(50616); -const push_single_action_contexts_1 = __nccwpck_require__(87805); +const logging_ports_1 = __nccwpck_require__(6152); +const task_emoji_1 = __nccwpck_require__(46103); +const single_action_workflow_1 = __nccwpck_require__(6130); +const think_workflow_1 = __nccwpck_require__(36450); +const bugbot_review_operation_context_1 = __nccwpck_require__(16660); +const push_single_action_contexts_1 = __nccwpck_require__(47841); class SingleActionUseCase { constructor(publishGithubActionUseCase, createReleaseUseCase, createTagUseCase, thinkUseCase, initialSetupUseCase, checkProgressUseCase, detectPotentialProblemsUseCase, recommendStepsUseCase, closeInactiveIssuesUseCase, actorAuthorizationPort, publishIssueCommentUseCase, observeBranchSyncUseCase, deploymentOrchestrationUseCase) { this.publishGithubActionUseCase = publishGithubActionUseCase; @@ -56558,16 +56558,16 @@ function isAgentBackedSingleAction(param) { /***/ }), -/***/ 86940: +/***/ 6130: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runSingleActionWorkflow = runSingleActionWorkflow; -const result_1 = __nccwpck_require__(61444); -const logging_ports_1 = __nccwpck_require__(73001); -const application_error_1 = __nccwpck_require__(2965); +const result_1 = __nccwpck_require__(73817); +const logging_ports_1 = __nccwpck_require__(6152); +const application_error_1 = __nccwpck_require__(75999); async function runSingleActionWorkflow(dispatch, taskId, ports) { if (dispatch.kind === 'invalid') { (0, logging_ports_1.logDebugInfo)(`Single action is not valid: ${dispatch.action}. Skipping.`); @@ -56621,24 +56621,24 @@ function singleActionFailure(action, taskId, error) { /***/ }), -/***/ 92650: +/***/ 4658: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.analyzeBugbotRevision = analyzeBugbotRevision; -const bugbot_reconciliation_policy_1 = __nccwpck_require__(54271); -const logging_ports_1 = __nccwpck_require__(73001); -const limit_comments_1 = __nccwpck_require__(9306); -const finding_1 = __nccwpck_require__(82048); -const build_bugbot_prompt_1 = __nccwpck_require__(88971); -const prepare_bugbot_findings_1 = __nccwpck_require__(13325); -const query_bugbot_findings_1 = __nccwpck_require__(41038); -const bugbot_resolution_eligibility_policy_1 = __nccwpck_require__(15950); -const bounded_concurrency_policy_1 = __nccwpck_require__(50189); -const bugbot_partition_aggregation_1 = __nccwpck_require__(49942); -const application_error_1 = __nccwpck_require__(2965); +const bugbot_reconciliation_policy_1 = __nccwpck_require__(78128); +const logging_ports_1 = __nccwpck_require__(6152); +const limit_comments_1 = __nccwpck_require__(31643); +const finding_1 = __nccwpck_require__(31011); +const build_bugbot_prompt_1 = __nccwpck_require__(52483); +const prepare_bugbot_findings_1 = __nccwpck_require__(85016); +const query_bugbot_findings_1 = __nccwpck_require__(13059); +const bugbot_resolution_eligibility_policy_1 = __nccwpck_require__(89189); +const bounded_concurrency_policy_1 = __nccwpck_require__(35596); +const bugbot_partition_aggregation_1 = __nccwpck_require__(84575); +const application_error_1 = __nccwpck_require__(75999); /** Pure analysis phase: query, validate, normalize, deduplicate and reconcile; never mutates the SCM. */ async function analyzeBugbotRevision(execution, context, dependencies) { dependencies.telemetry.observeContext(context); @@ -56720,16 +56720,16 @@ function suppressDismissedFindings(execution, context, prepared) { /***/ }), -/***/ 60017: +/***/ 20793: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.applyDetectedFindings = applyDetectedFindings; -const mark_findings_resolved_use_case_1 = __nccwpck_require__(74985); -const publish_findings_use_case_1 = __nccwpck_require__(60836); -const pull_request_review_errors_1 = __nccwpck_require__(81504); +const mark_findings_resolved_use_case_1 = __nccwpck_require__(96963); +const publish_findings_use_case_1 = __nccwpck_require__(88442); +const pull_request_review_errors_1 = __nccwpck_require__(46445); async function applyDetectedFindings(operation, context, prepared, publicationPorts, resolutionPorts, catalog) { try { await (0, publish_findings_use_case_1.publishFindings)({ @@ -56763,7 +56763,7 @@ async function applyDetectedFindings(operation, context, prepared, publicationPo /***/ }), -/***/ 35010: +/***/ 98158: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -56771,8 +56771,8 @@ async function applyDetectedFindings(operation, context, prepared, publicationPo Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runBugbotAutofixCommitAndPush = runBugbotAutofixCommitAndPush; exports.runUserRequestCommitAndPush = runUserRequestCommitAndPush; -const commit_message_policy_1 = __nccwpck_require__(37803); -const commit_and_push_workflow_1 = __nccwpck_require__(30978); +const commit_message_policy_1 = __nccwpck_require__(85518); +const commit_and_push_workflow_1 = __nccwpck_require__(53708); async function runBugbotAutofixCommitAndPush(context, options, gitCommitPort) { const branch = options?.branchOverride ?? context.branch; return (0, commit_and_push_workflow_1.runCommitAndPushWorkflow)(context, { @@ -56797,17 +56797,17 @@ async function runUserRequestCommitAndPush(context, options, gitCommitPort) { /***/ }), -/***/ 99776: +/***/ 79698: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.finalizeBugbotAutofix = finalizeBugbotAutofix; -const result_1 = __nccwpck_require__(61444); -const logging_ports_1 = __nccwpck_require__(73001); -const workspace_mutation_guard_1 = __nccwpck_require__(12627); -const application_error_1 = __nccwpck_require__(2965); +const result_1 = __nccwpck_require__(73817); +const logging_ports_1 = __nccwpck_require__(6152); +const workspace_mutation_guard_1 = __nccwpck_require__(24243); +const application_error_1 = __nccwpck_require__(75999); async function finalizeBugbotAutofix(context, idsToFix, workspacePathsBefore, branchCheckedOut, responseText, gitCommitPort) { if (!responseText) { (0, logging_ports_1.logError)('Bugbot autofix: no response from configured build agent.'); @@ -56838,21 +56838,21 @@ function failure(semanticError) { /***/ }), -/***/ 30398: +/***/ 67170: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.prepareBugbotAutofix = prepareBugbotAutofix; -const result_1 = __nccwpck_require__(61444); -const finding_1 = __nccwpck_require__(82048); -const build_bugbot_fix_prompt_1 = __nccwpck_require__(48942); -const load_bugbot_context_use_case_1 = __nccwpck_require__(56378); -const bugbot_context_request_1 = __nccwpck_require__(72881); -const logging_ports_1 = __nccwpck_require__(73001); -const workspace_mutation_guard_1 = __nccwpck_require__(12627); -const application_error_1 = __nccwpck_require__(2965); +const result_1 = __nccwpck_require__(73817); +const finding_1 = __nccwpck_require__(31011); +const build_bugbot_fix_prompt_1 = __nccwpck_require__(89819); +const load_bugbot_context_use_case_1 = __nccwpck_require__(4050); +const bugbot_context_request_1 = __nccwpck_require__(98299); +const logging_ports_1 = __nccwpck_require__(6152); +const workspace_mutation_guard_1 = __nccwpck_require__(24243); +const application_error_1 = __nccwpck_require__(75999); async function prepareBugbotAutofix(operation, targetFindingIds, userComment, providedContext, branchOverride, contextPorts, gitCommitPort) { const canonicalHint = providedContext?.canonicalPullRequest; const targetBranch = branchOverride?.trim() || canonicalHint?.headRef; @@ -56915,14 +56915,14 @@ function failure(semanticError) { /***/ }), -/***/ 92886: +/***/ 45446: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.BugbotAutofixUseCase = void 0; -const bugbot_autofix_workflow_1 = __nccwpck_require__(60366); +const bugbot_autofix_workflow_1 = __nccwpck_require__(69600); /** Application boundary for safe, agent-driven remediation of Bugbot findings. */ class BugbotAutofixUseCase { constructor(aiRepository, contextPorts, gitCommitPort) { @@ -56944,20 +56944,20 @@ exports.BugbotAutofixUseCase = BugbotAutofixUseCase; /***/ }), -/***/ 60366: +/***/ 69600: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runBugbotAutofixWorkflow = runBugbotAutofixWorkflow; -const agent_1 = __nccwpck_require__(71889); -const result_1 = __nccwpck_require__(61444); -const logging_ports_1 = __nccwpck_require__(73001); -const task_emoji_1 = __nccwpck_require__(83142); -const bugbot_autofix_postflight_1 = __nccwpck_require__(99776); -const bugbot_autofix_preflight_1 = __nccwpck_require__(30398); -const application_error_1 = __nccwpck_require__(2965); +const agent_1 = __nccwpck_require__(79937); +const result_1 = __nccwpck_require__(73817); +const logging_ports_1 = __nccwpck_require__(6152); +const task_emoji_1 = __nccwpck_require__(46103); +const bugbot_autofix_postflight_1 = __nccwpck_require__(79698); +const bugbot_autofix_preflight_1 = __nccwpck_require__(67170); +const application_error_1 = __nccwpck_require__(75999); const TASK_ID = 'BugbotAutofixUseCase'; /** Coordinates preflight, agent execution and postflight workspace safety. */ async function runBugbotAutofixWorkflow(param, dependencies) { @@ -57000,14 +57000,14 @@ function newResultFailure(semanticError) { /***/ }), -/***/ 72881: +/***/ 98299: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.projectBugbotContextRequest = projectBugbotContextRequest; -const positive_integer_policy_1 = __nccwpck_require__(45613); +const positive_integer_policy_1 = __nccwpck_require__(19879); function projectBugbotContextRequest(context, options) { const issueNumber = (0, positive_integer_policy_1.parsePositiveSafeInteger)(options?.issueNumberOverride ?? context.target.issueNumber); const pullRequestNumber = (0, positive_integer_policy_1.parsePositiveSafeInteger)(options?.pullRequestNumberOverride @@ -57041,7 +57041,7 @@ function projectBugbotContextRequest(context, options) { /***/ }), -/***/ 7993: +/***/ 62946: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -57049,11 +57049,11 @@ function projectBugbotContextRequest(context, options) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.parseBugbotFindingComments = parseBugbotFindingComments; exports.collectPreviousBugbotFindings = collectPreviousBugbotFindings; -const build_bugbot_fix_prompt_1 = __nccwpck_require__(48942); -const bugbot_finding_marker_policy_1 = __nccwpck_require__(80639); -const finding_1 = __nccwpck_require__(82048); -const github_user_policy_1 = __nccwpck_require__(19596); -const review_state_1 = __nccwpck_require__(17271); +const build_bugbot_fix_prompt_1 = __nccwpck_require__(89819); +const bugbot_finding_marker_policy_1 = __nccwpck_require__(98024); +const finding_1 = __nccwpck_require__(31011); +const github_user_policy_1 = __nccwpck_require__(84403); +const review_state_1 = __nccwpck_require__(79200); function parseBugbotFindingComments(issueComments, pullRequestCommentsByNumber, trustedAuthorLogin, reviewThreadStatesByPullRequest = new Map()) { const existingByFindingId = parseIssueFindingMarkers(issueComments, trustedAuthorLogin); const pullRequestFindings = parsePullRequestFindingMarkers(pullRequestCommentsByNumber, trustedAuthorLogin, reviewThreadStatesByPullRequest); @@ -57174,7 +57174,7 @@ function collectPreviousBugbotFindings(issueComments, existingByFindingId, prFin /***/ }), -/***/ 56352: +/***/ 25734: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -57213,7 +57213,7 @@ function canRunDoUserRequest(payload) { /***/ }), -/***/ 49942: +/***/ 84575: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -57221,7 +57221,7 @@ function canRunDoUserRequest(payload) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.MAX_AGGREGATE_PARTITION_FINDINGS = void 0; exports.aggregateBugbotPartitionResponses = aggregateBugbotPartitionResponses; -const application_error_1 = __nccwpck_require__(2965); +const application_error_1 = __nccwpck_require__(75999); const MAX_PARTITION_FINDINGS_PER_RESPONSE = 200; exports.MAX_AGGREGATE_PARTITION_FINDINGS = 2000; const MAX_OWNER_RESOLUTIONS = 500; @@ -57274,7 +57274,7 @@ function invalidAggregate(message) { /***/ }), -/***/ 9819: +/***/ 3346: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -57282,8 +57282,8 @@ function invalidAggregate(message) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.MAX_PREVIOUS_FINDINGS_BLOCK_LENGTH = exports.MAX_PREVIOUS_FINDINGS = void 0; exports.buildPreviousFindingsContext = buildPreviousFindingsContext; -const untrusted_content_1 = __nccwpck_require__(12334); -const build_bugbot_fix_prompt_1 = __nccwpck_require__(48942); +const untrusted_content_1 = __nccwpck_require__(67057); +const build_bugbot_fix_prompt_1 = __nccwpck_require__(89819); exports.MAX_PREVIOUS_FINDINGS = 100; exports.MAX_PREVIOUS_FINDINGS_BLOCK_LENGTH = 48000; function buildPreviousFindingsContext(previousFindings) { @@ -57344,7 +57344,7 @@ function timestamp(value) { /***/ }), -/***/ 36905: +/***/ 50536: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -57354,9 +57354,9 @@ exports.buildReviewDiffBlock = buildReviewDiffBlock; exports.buildReviewDiffContext = buildReviewDiffContext; exports.buildReviewConversationBlock = buildReviewConversationBlock; exports.buildReviewConversationContext = buildReviewConversationContext; -const github_user_policy_1 = __nccwpck_require__(19596); -const untrusted_content_1 = __nccwpck_require__(12334); -const bugbot_diff_partition_policy_1 = __nccwpck_require__(51471); +const github_user_policy_1 = __nccwpck_require__(84403); +const untrusted_content_1 = __nccwpck_require__(67057); +const bugbot_diff_partition_policy_1 = __nccwpck_require__(31601); const MAX_CONVERSATION_LENGTH = 24000; const MAX_CONVERSATION_ITEMS = 50; const MAX_CONVERSATION_ITEM_LENGTH = 2000; @@ -57439,7 +57439,7 @@ function isBot(author, botLogin) { /***/ }), -/***/ 26699: +/***/ 14307: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -57462,7 +57462,7 @@ async function hasNewerBugbotRevision(context, ports) { /***/ }), -/***/ 50616: +/***/ 16660: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -57474,7 +57474,7 @@ exports.withBugbotReviewOverrides = withBugbotReviewOverrides; exports.projectBugbotFixIntentContext = projectBugbotFixIntentContext; exports.projectBugbotAutofixOperationContext = projectBugbotAutofixOperationContext; exports.projectBugbotCommitContext = projectBugbotCommitContext; -const positive_integer_policy_1 = __nccwpck_require__(45613); +const positive_integer_policy_1 = __nccwpck_require__(19879); /** Copies only the non-secret facts required to select canonical Bugbot context. */ function projectBugbotContextSelectionContext(source) { const reviewConfiguration = source.ai.getBugbotReviewConfiguration(); @@ -57620,7 +57620,7 @@ function normalizeExpectedHeadSha(eventName, inputs) { /***/ }), -/***/ 45270: +/***/ 25011: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -57628,7 +57628,7 @@ function normalizeExpectedHeadSha(eventName, inputs) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.MAX_BUGBOT_RULES_LENGTH = exports.MAX_BUGBOT_RULE_LENGTH = void 0; exports.buildBugbotReviewRuleSet = buildBugbotReviewRuleSet; -const untrusted_content_1 = __nccwpck_require__(12334); +const untrusted_content_1 = __nccwpck_require__(67057); exports.MAX_BUGBOT_RULE_LENGTH = 30000; exports.MAX_BUGBOT_RULES_LENGTH = 100000; function buildBugbotReviewRuleSet(organizationRules, repositoryRules) { @@ -57681,14 +57681,14 @@ function deduplicateRules(rules) { /***/ }), -/***/ 14285: +/***/ 46790: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.BugbotReviewTelemetry = void 0; -const bugbot_finding_status_policy_1 = __nccwpck_require__(9298); +const bugbot_finding_status_policy_1 = __nccwpck_require__(53822); const systemClock = { now: () => Date.now(), isoNow: () => new Date().toISOString(), @@ -57890,7 +57890,7 @@ function sanitizeMetricName(value) { /***/ }), -/***/ 41672: +/***/ 18799: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -57901,9 +57901,9 @@ function sanitizeMetricName(value) { */ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildBugbotFixIntentPrompt = buildBugbotFixIntentPrompt; -const prompts_1 = __nccwpck_require__(71854); -const project_context_instruction_1 = __nccwpck_require__(36158); -const sanitize_user_comment_for_prompt_1 = __nccwpck_require__(28636); +const prompts_1 = __nccwpck_require__(69518); +const project_context_instruction_1 = __nccwpck_require__(63907); +const sanitize_user_comment_for_prompt_1 = __nccwpck_require__(59828); const MAX_TITLE_LENGTH = 200; const MAX_FILE_LENGTH = 256; function safeForPrompt(s, maxLen) { @@ -57953,7 +57953,7 @@ function buildParentBlock(parentCommentBody) { /***/ }), -/***/ 48942: +/***/ 89819: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -57962,10 +57962,10 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.MAX_FINDING_BODY_LENGTH = void 0; exports.truncateFindingBody = truncateFindingBody; exports.buildBugbotFixPrompt = buildBugbotFixPrompt; -const prompts_1 = __nccwpck_require__(71854); -const project_context_instruction_1 = __nccwpck_require__(36158); -const sanitize_user_comment_for_prompt_1 = __nccwpck_require__(28636); -const untrusted_content_1 = __nccwpck_require__(12334); +const prompts_1 = __nccwpck_require__(69518); +const project_context_instruction_1 = __nccwpck_require__(63907); +const sanitize_user_comment_for_prompt_1 = __nccwpck_require__(59828); +const untrusted_content_1 = __nccwpck_require__(67057); /** Maximum characters for a single finding's full comment body to avoid prompt bloat and token limits. */ exports.MAX_FINDING_BODY_LENGTH = 12000; const TRUNCATION_SUFFIX = "\n\n[... truncated for length ...]"; @@ -58022,7 +58022,7 @@ function buildBugbotFixPrompt(param, context, targetFindingIds, userComment, ver /***/ }), -/***/ 88971: +/***/ 52483: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -58036,10 +58036,10 @@ function buildBugbotFixPrompt(param, context, targetFindingIds, userComment, ver */ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildBugbotPrompt = buildBugbotPrompt; -const prompts_1 = __nccwpck_require__(71854); -const project_context_instruction_1 = __nccwpck_require__(36158); -const review_configuration_1 = __nccwpck_require__(19249); -const file_ignore_policy_1 = __nccwpck_require__(56498); +const prompts_1 = __nccwpck_require__(69518); +const project_context_instruction_1 = __nccwpck_require__(63907); +const review_configuration_1 = __nccwpck_require__(3994); +const file_ignore_policy_1 = __nccwpck_require__(20542); const MAX_IGNORE_BLOCK_LENGTH = 2000; const GIT_OBJECT_ID = /^[0-9a-f]{7,64}$/i; function buildBugbotPrompt(param, context, assignment) { @@ -58161,18 +58161,18 @@ function normalizedObjectId(value) { /***/ }), -/***/ 66117: +/***/ 49629: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runCommitAndPushPreflight = runCommitAndPushPreflight; -const logging_ports_1 = __nccwpck_require__(73001); -const git_branch_checkout_1 = __nccwpck_require__(96838); -const verify_command_policy_1 = __nccwpck_require__(32739); -const verify_command_runner_1 = __nccwpck_require__(71843); -const workspace_changes_1 = __nccwpck_require__(51578); +const logging_ports_1 = __nccwpck_require__(6152); +const git_branch_checkout_1 = __nccwpck_require__(68549); +const verify_command_policy_1 = __nccwpck_require__(96031); +const verify_command_runner_1 = __nccwpck_require__(57742); +const workspace_changes_1 = __nccwpck_require__(93370); async function runCommitAndPushPreflight(context, options, gitCommitPort) { if (!options.branch?.trim()) { return { status: "failure", error: "No branch to commit to." }; @@ -58209,16 +58209,16 @@ async function runVerification(context, gitCommitPort) { /***/ }), -/***/ 30978: +/***/ 53708: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runCommitAndPushWorkflow = runCommitAndPushWorkflow; -const logging_ports_1 = __nccwpck_require__(73001); -const commit_and_push_preflight_1 = __nccwpck_require__(66117); -const application_error_1 = __nccwpck_require__(2965); +const logging_ports_1 = __nccwpck_require__(6152); +const commit_and_push_preflight_1 = __nccwpck_require__(49629); +const application_error_1 = __nccwpck_require__(75999); async function runCommitAndPushWorkflow(context, options, gitCommitPort) { const preflight = await (0, commit_and_push_preflight_1.runCommitAndPushPreflight)(context, options, gitCommitPort); if (preflight.status === 'failure') { @@ -58253,16 +58253,16 @@ async function runCommitAndPushWorkflow(context, options, gitCommitPort) { /***/ }), -/***/ 75112: +/***/ 93455: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.commitAutofixAndResolveFindings = commitAutofixAndResolveFindings; -const logging_ports_1 = __nccwpck_require__(73001); -const bugbot_autofix_commit_1 = __nccwpck_require__(35010); -const github_comment_publication_policy_1 = __nccwpck_require__(22913); +const logging_ports_1 = __nccwpck_require__(6152); +const bugbot_autofix_commit_1 = __nccwpck_require__(98158); +const github_comment_publication_policy_1 = __nccwpck_require__(72712); async function commitAutofixAndResolveFindings(context, payload, autofixResults, gitCommitPort) { const lastAutofix = autofixResults.at(-1); if (!lastAutofix?.success) { @@ -58296,7 +58296,7 @@ async function commitAutofixAndResolveFindings(context, payload, autofixResults, /***/ }), -/***/ 37803: +/***/ 85518: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -58350,18 +58350,18 @@ function buildUserRequestCommitMessage(issueNumber) { /***/ }), -/***/ 17359: +/***/ 43393: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.commitUserRequestIfSuccessful = commitUserRequestIfSuccessful; -const logging_ports_1 = __nccwpck_require__(73001); -const bugbot_autofix_commit_1 = __nccwpck_require__(35010); -const result_1 = __nccwpck_require__(61444); -const github_comment_publication_policy_1 = __nccwpck_require__(22913); -const application_error_1 = __nccwpck_require__(2965); +const logging_ports_1 = __nccwpck_require__(6152); +const bugbot_autofix_commit_1 = __nccwpck_require__(98158); +const result_1 = __nccwpck_require__(73817); +const github_comment_publication_policy_1 = __nccwpck_require__(72712); +const application_error_1 = __nccwpck_require__(75999); async function commitUserRequestIfSuccessful(context, branchOverride, results, gitCommitPort) { if (!results.at(-1)?.success) { (0, logging_ports_1.logInfo)('Do user request did not succeed; skipping commit.'); @@ -58394,7 +58394,7 @@ async function commitUserRequestIfSuccessful(context, branchOverride, results, g /***/ }), -/***/ 71392: +/***/ 62908: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -58427,7 +58427,7 @@ function deduplicateFindings(findings) { /***/ }), -/***/ 58778: +/***/ 14796: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -58436,7 +58436,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.selectBugbotCommentBody = selectBugbotCommentBody; exports.buildUnresolvedFindingSummaries = buildUnresolvedFindingSummaries; exports.parseBugbotFixIntentResponse = parseBugbotFixIntentResponse; -const bugbot_finding_marker_policy_1 = __nccwpck_require__(80639); +const bugbot_finding_marker_policy_1 = __nccwpck_require__(98024); /** Selects the user-authored comment that can trigger intent detection. */ function selectBugbotCommentBody(sources) { if (sources.issue.isIssueComment) @@ -58481,16 +58481,16 @@ function unique(values) { /***/ }), -/***/ 50385: +/***/ 76234: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.DetectBugbotFixIntentUseCase = void 0; -const logging_ports_1 = __nccwpck_require__(73001); -const task_emoji_1 = __nccwpck_require__(83142); -const detect_bugbot_fix_intent_workflow_1 = __nccwpck_require__(59265); +const logging_ports_1 = __nccwpck_require__(6152); +const task_emoji_1 = __nccwpck_require__(46103); +const detect_bugbot_fix_intent_workflow_1 = __nccwpck_require__(88390); const TASK_ID = "DetectBugbotFixIntentUseCase"; /** Application boundary for detecting Bugbot fix intent in user comments. */ class DetectBugbotFixIntentUseCase { @@ -58512,23 +58512,23 @@ exports.DetectBugbotFixIntentUseCase = DetectBugbotFixIntentUseCase; /***/ }), -/***/ 59265: +/***/ 88390: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runDetectBugbotFixIntentWorkflow = runDetectBugbotFixIntentWorkflow; -const agent_1 = __nccwpck_require__(71889); -const agent_task_policy_1 = __nccwpck_require__(2601); -const logging_ports_1 = __nccwpck_require__(73001); -const result_1 = __nccwpck_require__(61444); -const copilot_command_1 = __nccwpck_require__(87134); -const build_bugbot_fix_intent_prompt_1 = __nccwpck_require__(41672); -const load_bugbot_context_use_case_1 = __nccwpck_require__(56378); -const bugbot_context_request_1 = __nccwpck_require__(72881); -const schema_1 = __nccwpck_require__(98135); -const detect_bugbot_fix_intent_policy_1 = __nccwpck_require__(58778); +const agent_1 = __nccwpck_require__(79937); +const agent_task_policy_1 = __nccwpck_require__(85712); +const logging_ports_1 = __nccwpck_require__(6152); +const result_1 = __nccwpck_require__(73817); +const copilot_command_1 = __nccwpck_require__(11771); +const build_bugbot_fix_intent_prompt_1 = __nccwpck_require__(18799); +const load_bugbot_context_use_case_1 = __nccwpck_require__(4050); +const bugbot_context_request_1 = __nccwpck_require__(98299); +const schema_1 = __nccwpck_require__(16808); +const detect_bugbot_fix_intent_policy_1 = __nccwpck_require__(14796); const TASK_ID = "DetectBugbotFixIntentUseCase"; /** Detects whether a comment requests a finding fix, repository change, or read-only review. */ async function runDetectBugbotFixIntentWorkflow(param, ports) { @@ -58665,21 +58665,21 @@ async function resolveParentCommentBody(param, contextPorts) { /***/ }), -/***/ 8677: +/***/ 37685: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.DismissBugbotFindingsUseCase = void 0; -const result_1 = __nccwpck_require__(61444); -const load_bugbot_context_use_case_1 = __nccwpck_require__(56378); -const bugbot_context_request_1 = __nccwpck_require__(72881); -const mark_findings_resolved_workflow_1 = __nccwpck_require__(29578); -const bugbot_finding_marker_policy_1 = __nccwpck_require__(80639); -const logging_ports_1 = __nccwpck_require__(73001); -const application_error_1 = __nccwpck_require__(2965); -const bugbot_message_catalog_1 = __nccwpck_require__(84479); +const result_1 = __nccwpck_require__(73817); +const load_bugbot_context_use_case_1 = __nccwpck_require__(4050); +const bugbot_context_request_1 = __nccwpck_require__(98299); +const mark_findings_resolved_workflow_1 = __nccwpck_require__(65916); +const bugbot_finding_marker_policy_1 = __nccwpck_require__(98024); +const logging_ports_1 = __nccwpck_require__(6152); +const application_error_1 = __nccwpck_require__(75999); +const bugbot_message_catalog_1 = __nccwpck_require__(7406); /** Dismisses only findings present in the current persisted Bugbot context. */ class DismissBugbotFindingsUseCase { constructor(dependencies) { @@ -58750,14 +58750,14 @@ async function loadDismissContext(operation, ports) { /***/ }), -/***/ 9306: +/***/ 31643: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.applyCommentLimit = applyCommentLimit; -const bugbot_constants_1 = __nccwpck_require__(16868); +const bugbot_constants_1 = __nccwpck_require__(51389); /** * Applies the max-comments limit: returns the first N findings to publish individually, * and overflow count + titles for a single "revisar en local" summary comment. @@ -58778,7 +58778,7 @@ function applyCommentLimit(findings, maxComments = bugbot_constants_1.BUGBOT_MAX /***/ }), -/***/ 56378: +/***/ 4050: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -58786,16 +58786,16 @@ function applyCommentLimit(findings, maxComments = bugbot_constants_1.BUGBOT_MAX Object.defineProperty(exports, "__esModule", ({ value: true })); exports.preflightBugbotContext = preflightBugbotContext; exports.loadBugbotContext = loadBugbotContext; -const application_error_1 = __nccwpck_require__(2965); -const bounded_concurrency_policy_1 = __nccwpck_require__(50189); -const context_1 = __nccwpck_require__(32721); -const logging_ports_1 = __nccwpck_require__(73001); -const bugbot_finding_context_1 = __nccwpck_require__(7993); -const bugbot_previous_findings_context_1 = __nccwpck_require__(9819); -const bugbot_diff_partition_policy_1 = __nccwpck_require__(51471); -const bugbot_review_context_1 = __nccwpck_require__(36905); -const file_ignore_policy_1 = __nccwpck_require__(56498); -const bugbot_review_rules_1 = __nccwpck_require__(45270); +const application_error_1 = __nccwpck_require__(75999); +const bounded_concurrency_policy_1 = __nccwpck_require__(35596); +const context_1 = __nccwpck_require__(14712); +const logging_ports_1 = __nccwpck_require__(6152); +const bugbot_finding_context_1 = __nccwpck_require__(62946); +const bugbot_previous_findings_context_1 = __nccwpck_require__(3346); +const bugbot_diff_partition_policy_1 = __nccwpck_require__(31601); +const bugbot_review_context_1 = __nccwpck_require__(50536); +const file_ignore_policy_1 = __nccwpck_require__(20542); +const bugbot_review_rules_1 = __nccwpck_require__(25011); /** Resolves and validates the provider-owned PR identity without loading review context. */ async function preflightBugbotContext(request, ports) { const selection = await selectCanonicalPullRequest(request, ports); @@ -58963,14 +58963,14 @@ function toPrContext(identity, snapshot) { /***/ }), -/***/ 50980: +/***/ 44861: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.loadBugbotReconciliationSnapshot = loadBugbotReconciliationSnapshot; -const pull_request_review_errors_1 = __nccwpck_require__(81504); +const pull_request_review_errors_1 = __nccwpck_require__(46445); /** * Acquires one coherent final snapshot around two head guards. Surface reads * run concurrently, while the second guard rejects data collected across a @@ -59061,32 +59061,32 @@ function stateOf(result) { /***/ }), -/***/ 74985: +/***/ 96963: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.markFindingsResolved = void 0; -var mark_findings_resolved_workflow_1 = __nccwpck_require__(29578); +var mark_findings_resolved_workflow_1 = __nccwpck_require__(65916); Object.defineProperty(exports, "markFindingsResolved", ({ enumerable: true, get: function () { return mark_findings_resolved_workflow_1.markFindingsResolved; } })); /***/ }), -/***/ 29578: +/***/ 65916: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.markFindingsResolved = markFindingsResolved; -const pull_request_review_errors_1 = __nccwpck_require__(81504); -const logging_ports_1 = __nccwpck_require__(73001); -const resolve_issue_finding_1 = __nccwpck_require__(101); -const resolve_pull_request_finding_1 = __nccwpck_require__(72038); -const review_state_1 = __nccwpck_require__(17271); -const application_error_1 = __nccwpck_require__(2965); +const pull_request_review_errors_1 = __nccwpck_require__(46445); +const logging_ports_1 = __nccwpck_require__(6152); +const resolve_issue_finding_1 = __nccwpck_require__(35300); +const resolve_pull_request_finding_1 = __nccwpck_require__(64567); +const review_state_1 = __nccwpck_require__(79200); +const application_error_1 = __nccwpck_require__(75999); async function markFindingsResolved(param) { const errors = []; for (const [findingId, existing] of Object.entries(param.context.existingByFindingId)) { @@ -59170,7 +59170,7 @@ function addResolutionError(errors, destination) { /***/ }), -/***/ 33308: +/***/ 70124: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -59233,14 +59233,14 @@ function resolveFindingPathForPr(findingFile, prFiles) { /***/ }), -/***/ 13325: +/***/ 85016: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.prepareBugbotFindings = prepareBugbotFindings; -const prepare_bugbot_findings_policy_1 = __nccwpck_require__(73654); +const prepare_bugbot_findings_policy_1 = __nccwpck_require__(3496); function prepareBugbotFindings(response, ignorePatterns, minSeverityValue, maxComments, maxAgentFindings) { const normalized = (0, prepare_bugbot_findings_policy_1.normalizeBugbotResponse)(response, maxAgentFindings); return normalized === undefined @@ -59255,7 +59255,7 @@ function prepareBugbotFindings(response, ignorePatterns, minSeverityValue, maxCo /***/ }), -/***/ 73654: +/***/ 3496: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -59264,14 +59264,14 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.MIN_AGENT_FINDING_CONFIDENCE = exports.MAX_AGENT_RESOLVED_FINDINGS = exports.MAX_AGENT_FINDINGS = void 0; exports.normalizeBugbotResponse = normalizeBugbotResponse; exports.prepareFindings = prepareFindings; -const deduplicate_findings_1 = __nccwpck_require__(71392); -const file_ignore_policy_1 = __nccwpck_require__(56498); -const limit_comments_1 = __nccwpck_require__(9306); -const bugbot_finding_marker_policy_1 = __nccwpck_require__(80639); -const path_validation_1 = __nccwpck_require__(33308); -const severity_1 = __nccwpck_require__(2624); -const finding_identity_1 = __nccwpck_require__(657); -const sensitive_text_1 = __nccwpck_require__(98209); +const deduplicate_findings_1 = __nccwpck_require__(62908); +const file_ignore_policy_1 = __nccwpck_require__(20542); +const limit_comments_1 = __nccwpck_require__(31643); +const bugbot_finding_marker_policy_1 = __nccwpck_require__(98024); +const path_validation_1 = __nccwpck_require__(70124); +const severity_1 = __nccwpck_require__(14626); +const finding_identity_1 = __nccwpck_require__(91853); +const sensitive_text_1 = __nccwpck_require__(47122); /** Hard cap for model-controlled arrays before any filtering or publication. */ exports.MAX_AGENT_FINDINGS = 500; exports.MAX_AGENT_RESOLVED_FINDINGS = 500; @@ -59404,7 +59404,7 @@ function isRecord(value) { /***/ }), -/***/ 60836: +/***/ 88442: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -59415,10 +59415,10 @@ function isRecord(value) { */ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.publishFindings = publishFindings; -const finding_1 = __nccwpck_require__(82048); -const publish_issue_finding_comment_1 = __nccwpck_require__(62743); -const publish_pr_review_comments_1 = __nccwpck_require__(34439); -const publish_overflow_comment_1 = __nccwpck_require__(2810); +const finding_1 = __nccwpck_require__(31011); +const publish_issue_finding_comment_1 = __nccwpck_require__(84950); +const publish_pr_review_comments_1 = __nccwpck_require__(50352); +const publish_overflow_comment_1 = __nccwpck_require__(10974); async function publishFindings(param) { const { operation, context, findings, commitSha, overflowCount = 0, overflowTitles = [], ports, catalog } = param; const { existingByFindingId, canonicalPullRequest, prContext } = context; @@ -59450,15 +59450,15 @@ async function publishFindings(param) { /***/ }), -/***/ 62743: +/***/ 84950: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.publishIssueFindingComment = publishIssueFindingComment; -const bugbot_finding_marker_policy_1 = __nccwpck_require__(80639); -const logging_ports_1 = __nccwpck_require__(73001); +const bugbot_finding_marker_policy_1 = __nccwpck_require__(98024); +const logging_ports_1 = __nccwpck_require__(6152); async function publishIssueFindingComment(repository, issueNumber, finding, existing, commitSha, catalog) { const body = (0, bugbot_finding_marker_policy_1.buildCommentBody)(finding, false, undefined, { catalog }); const options = commitSha ? { commitSha } : undefined; @@ -59474,16 +59474,16 @@ async function publishIssueFindingComment(repository, issueNumber, finding, exis /***/ }), -/***/ 2810: +/***/ 10974: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.publishOverflowComment = publishOverflowComment; -const logging_ports_1 = __nccwpck_require__(73001); -const github_comment_publication_policy_1 = __nccwpck_require__(22913); -const bugbot_message_catalog_1 = __nccwpck_require__(84479); +const logging_ports_1 = __nccwpck_require__(6152); +const github_comment_publication_policy_1 = __nccwpck_require__(72712); +const bugbot_message_catalog_1 = __nccwpck_require__(7406); async function publishOverflowComment(repository, issueNumber, overflowCount, overflowTitles, commitSha, catalog = (0, bugbot_message_catalog_1.resolveStaticBugbotCatalog)('en-US')) { if (overflowCount <= 0) return; @@ -59503,19 +59503,19 @@ ${catalog.message('bugbot.overflow.body', { count: `**${overflowCount}**` }, ove /***/ }), -/***/ 34439: +/***/ 50352: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.PullRequestReviewCommentPublisher = void 0; -const bugbot_finding_marker_policy_1 = __nccwpck_require__(80639); -const path_validation_1 = __nccwpck_require__(33308); -const logging_ports_1 = __nccwpck_require__(73001); -const github_comment_publication_policy_1 = __nccwpck_require__(22913); -const bugbot_review_presentation_policy_1 = __nccwpck_require__(77193); -const bugbot_message_catalog_1 = __nccwpck_require__(84479); +const bugbot_finding_marker_policy_1 = __nccwpck_require__(98024); +const path_validation_1 = __nccwpck_require__(70124); +const logging_ports_1 = __nccwpck_require__(6152); +const github_comment_publication_policy_1 = __nccwpck_require__(72712); +const bugbot_review_presentation_policy_1 = __nccwpck_require__(43799); +const bugbot_message_catalog_1 = __nccwpck_require__(7406); class PullRequestReviewCommentPublisher { constructor(options) { this.options = options; @@ -59669,7 +59669,7 @@ function sanitizeSummaryText(value, maximum) { /***/ }), -/***/ 41038: +/***/ 13059: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -59677,11 +59677,11 @@ function sanitizeSummaryText(value, maximum) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.queryBugbotFindings = queryBugbotFindings; exports.queryBugbotPartitionFindings = queryBugbotPartitionFindings; -const agent_task_policy_1 = __nccwpck_require__(2601); -const schema_1 = __nccwpck_require__(98135); -const agent_output_locale_policy_1 = __nccwpck_require__(2584); -const application_error_1 = __nccwpck_require__(2965); -const logging_ports_1 = __nccwpck_require__(73001); +const agent_task_policy_1 = __nccwpck_require__(85712); +const schema_1 = __nccwpck_require__(16808); +const agent_output_locale_policy_1 = __nccwpck_require__(30601); +const application_error_1 = __nccwpck_require__(75999); +const logging_ports_1 = __nccwpck_require__(6152); const MAX_PARTITION_QUERY_ATTEMPTS = 3; function bugbotQueryOptions(schema) { return (0, agent_output_locale_policy_1.productFacingAgentQueryOptions)('bugbot-review', schema); @@ -59734,19 +59734,19 @@ async function queryBugbotPartitionFindings(repository, configuration, prompt, t /***/ }), -/***/ 39174: +/***/ 57515: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.reconcileBugbotReviewState = reconcileBugbotReviewState; -const review_projection_1 = __nccwpck_require__(23272); -const bugbot_reconciliation_policy_1 = __nccwpck_require__(54271); -const bugbot_provider_projection_policy_1 = __nccwpck_require__(95220); -const bugbot_finding_marker_policy_1 = __nccwpck_require__(80639); -const load_bugbot_reconciliation_snapshot_use_case_1 = __nccwpck_require__(50980); -const synchronize_bugbot_review_presentation_use_case_1 = __nccwpck_require__(861); +const review_projection_1 = __nccwpck_require__(80859); +const bugbot_reconciliation_policy_1 = __nccwpck_require__(78128); +const bugbot_provider_projection_policy_1 = __nccwpck_require__(85821); +const bugbot_finding_marker_policy_1 = __nccwpck_require__(98024); +const load_bugbot_reconciliation_snapshot_use_case_1 = __nccwpck_require__(44861); +const synchronize_bugbot_review_presentation_use_case_1 = __nccwpck_require__(44491); /** * Orchestrates final Bugbot reconciliation. Provider acquisition, pure state * planning, and presentation mutations are deliberately owned by dedicated @@ -59814,15 +59814,15 @@ function toSafeOperationMessage(error) { /***/ }), -/***/ 83711: +/***/ 17437: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.RememberBugbotRuleUseCase = void 0; -const result_1 = __nccwpck_require__(61444); -const application_error_1 = __nccwpck_require__(2965); +const result_1 = __nccwpck_require__(73817); +const application_error_1 = __nccwpck_require__(75999); /** Stores an explicitly approved, repository-versioned Bugbot rule. */ class RememberBugbotRuleUseCase { constructor(rules) { @@ -59857,14 +59857,14 @@ exports.RememberBugbotRuleUseCase = RememberBugbotRuleUseCase; /***/ }), -/***/ 101: +/***/ 35300: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.resolveIssueFinding = resolveIssueFinding; -const bugbot_finding_marker_policy_1 = __nccwpck_require__(80639); +const bugbot_finding_marker_policy_1 = __nccwpck_require__(98024); async function resolveIssueFinding(repository, resolution, catalog) { const body = resolution.comment.body; const marker = (0, bugbot_finding_marker_policy_1.parseMarker)(body).find((candidate) => candidate.findingId === resolution.findingId); @@ -59881,15 +59881,15 @@ async function resolveIssueFinding(repository, resolution, catalog) { /***/ }), -/***/ 72038: +/***/ 64567: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.resolvePullRequestFinding = resolvePullRequestFinding; -const pull_request_review_errors_1 = __nccwpck_require__(81504); -const bugbot_finding_marker_policy_1 = __nccwpck_require__(80639); +const pull_request_review_errors_1 = __nccwpck_require__(46445); +const bugbot_finding_marker_policy_1 = __nccwpck_require__(98024); async function resolvePullRequestFinding(repository, resolution, catalog) { const comments = await repository.listPullRequestReviewComments(resolution.pullRequestNumber); const comment = comments.find((candidate) => candidate.identity === resolution.commentIdentity); @@ -59918,7 +59918,7 @@ async function resolvePullRequestFinding(repository, resolution, catalog) { /***/ }), -/***/ 28636: +/***/ 59828: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -59964,7 +59964,7 @@ function sanitizeUserCommentForPrompt(raw) { /***/ }), -/***/ 98135: +/***/ 16808: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -59976,8 +59976,8 @@ function sanitizeUserCommentForPrompt(raw) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.BUGBOT_FIX_INTENT_RESPONSE_SCHEMA = exports.BUGBOT_PARTITION_RESPONSE_SCHEMA = exports.BUGBOT_RESPONSE_SCHEMA = void 0; exports.buildBugbotPartitionResponseSchema = buildBugbotPartitionResponseSchema; -const bugbot_finding_marker_policy_1 = __nccwpck_require__(80639); -const agent_output_locale_policy_1 = __nccwpck_require__(2584); +const bugbot_finding_marker_policy_1 = __nccwpck_require__(98024); +const agent_output_locale_policy_1 = __nccwpck_require__(30601); /** Detection returns findings and explicit lifecycle changes for prior finding IDs. */ exports.BUGBOT_RESPONSE_SCHEMA = { type: 'object', @@ -60114,7 +60114,7 @@ exports.BUGBOT_FIX_INTENT_RESPONSE_SCHEMA = { /***/ }), -/***/ 2624: +/***/ 14626: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -60151,19 +60151,19 @@ function meetsMinSeverity(findingSeverity, minSeverity) { /***/ }), -/***/ 861: +/***/ 44491: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.synchronizeBugbotReviewPresentation = synchronizeBugbotReviewPresentation; -const application_error_1 = __nccwpck_require__(2965); -const bugbot_review_presentation_policy_1 = __nccwpck_require__(77193); -const bugbot_review_ownership_policy_1 = __nccwpck_require__(11719); -const review_projection_1 = __nccwpck_require__(23272); -const publication_identity_policy_1 = __nccwpck_require__(12590); -const bugbot_message_catalog_1 = __nccwpck_require__(84479); +const application_error_1 = __nccwpck_require__(75999); +const bugbot_review_presentation_policy_1 = __nccwpck_require__(43799); +const bugbot_review_ownership_policy_1 = __nccwpck_require__(83288); +const review_projection_1 = __nccwpck_require__(80859); +const publication_identity_policy_1 = __nccwpck_require__(45403); +const bugbot_message_catalog_1 = __nccwpck_require__(7406); const REVIEW_UPDATE_BATCH_SIZE = 20; const MAX_REVIEW_UPDATES_PER_RUN = 100; const REVIEW_UPDATE_CONCURRENCY = 4; @@ -60341,7 +60341,7 @@ async function mapWithConcurrency(values, concurrency, operation) { /***/ }), -/***/ 32739: +/***/ 96031: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -60383,7 +60383,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.MAX_VERIFY_COMMANDS = void 0; exports.parseVerifyCommand = parseVerifyCommand; exports.limitVerifyCommands = limitVerifyCommands; -const shellQuote = __importStar(__nccwpck_require__(18342)); +const shellQuote = __importStar(__nccwpck_require__(75430)); exports.MAX_VERIFY_COMMANDS = 20; function parseVerifyCommand(cmd) { const trimmed = cmd.trim(); @@ -60409,15 +60409,15 @@ function limitVerifyCommands(commands) { /***/ }), -/***/ 71843: +/***/ 57742: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runVerifyCommands = runVerifyCommands; -const logging_ports_1 = __nccwpck_require__(73001); -const verify_command_policy_1 = __nccwpck_require__(32739); +const logging_ports_1 = __nccwpck_require__(6152); +const verify_command_policy_1 = __nccwpck_require__(96031); async function runVerifyCommands(commands, execute) { for (const command of commands) { const result = await executeVerifyCommand(command, execute); @@ -60468,7 +60468,7 @@ function isSensitiveArgumentName(argument) { /***/ }), -/***/ 51578: +/***/ 93370: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -60547,16 +60547,16 @@ async function hasWorkspaceChanges(gitCommitPort) { /***/ }), -/***/ 75384: +/***/ 28356: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.CheckChangesIssueSizeUseCase = void 0; -const logging_ports_1 = __nccwpck_require__(73001); -const task_emoji_1 = __nccwpck_require__(83142); -const check_changes_issue_size_workflow_1 = __nccwpck_require__(51875); +const logging_ports_1 = __nccwpck_require__(6152); +const task_emoji_1 = __nccwpck_require__(46103); +const check_changes_issue_size_workflow_1 = __nccwpck_require__(43250); class CheckChangesIssueSizeUseCase { constructor(projectBoardCommandPort, issueRepository, pullRequestRepository, branchChangeSizePort) { this.projectBoardCommandPort = projectBoardCommandPort; @@ -60580,17 +60580,17 @@ exports.CheckChangesIssueSizeUseCase = CheckChangesIssueSizeUseCase; /***/ }), -/***/ 51875: +/***/ 43250: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runCheckChangesIssueSize = runCheckChangesIssueSize; -const result_1 = __nccwpck_require__(61444); -const logging_ports_1 = __nccwpck_require__(73001); -const update_change_size_labels_1 = __nccwpck_require__(65146); -const application_error_1 = __nccwpck_require__(2965); +const result_1 = __nccwpck_require__(73817); +const logging_ports_1 = __nccwpck_require__(6152); +const update_change_size_labels_1 = __nccwpck_require__(51200); +const application_error_1 = __nccwpck_require__(75999); async function runCheckChangesIssueSize(param, taskId, dependencies) { try { const baseBranch = param.baseBranch; @@ -60649,14 +60649,14 @@ function logSize(size, githubSize, reason, currentLabel) { /***/ }), -/***/ 65545: +/***/ 6287: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.DetectPotentialProblemsUseCase = void 0; -const detect_potential_problems_workflow_1 = __nccwpck_require__(40555); +const detect_potential_problems_workflow_1 = __nccwpck_require__(37033); /** Application boundary for detecting, publishing and resolving Bugbot findings. */ class DetectPotentialProblemsUseCase { constructor(aiRepository, scm, telemetryPort, catalogResolver) { @@ -60680,30 +60680,30 @@ exports.DetectPotentialProblemsUseCase = DetectPotentialProblemsUseCase; /***/ }), -/***/ 40555: +/***/ 37033: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runDetectPotentialProblemsWorkflow = runDetectPotentialProblemsWorkflow; -const agent_1 = __nccwpck_require__(71889); -const result_1 = __nccwpck_require__(61444); -const task_emoji_1 = __nccwpck_require__(83142); -const logging_ports_1 = __nccwpck_require__(73001); -const pull_request_review_errors_1 = __nccwpck_require__(81504); -const load_bugbot_context_use_case_1 = __nccwpck_require__(56378); -const bugbot_context_request_1 = __nccwpck_require__(72881); -const apply_detected_findings_1 = __nccwpck_require__(60017); -const bugbot_finding_status_policy_1 = __nccwpck_require__(9298); -const bugbot_review_telemetry_1 = __nccwpck_require__(14285); -const analyze_bugbot_revision_use_case_1 = __nccwpck_require__(92650); -const bugbot_review_freshness_1 = __nccwpck_require__(26699); -const reconcile_bugbot_review_state_use_case_1 = __nccwpck_require__(39174); -const application_error_1 = __nccwpck_require__(2965); -const bugbot_event_ownership_policy_1 = __nccwpck_require__(10580); -const bugbot_message_catalog_1 = __nccwpck_require__(84479); -const bugbot_partition_completion_policy_1 = __nccwpck_require__(83782); +const agent_1 = __nccwpck_require__(79937); +const result_1 = __nccwpck_require__(73817); +const task_emoji_1 = __nccwpck_require__(46103); +const logging_ports_1 = __nccwpck_require__(6152); +const pull_request_review_errors_1 = __nccwpck_require__(46445); +const load_bugbot_context_use_case_1 = __nccwpck_require__(4050); +const bugbot_context_request_1 = __nccwpck_require__(98299); +const apply_detected_findings_1 = __nccwpck_require__(20793); +const bugbot_finding_status_policy_1 = __nccwpck_require__(53822); +const bugbot_review_telemetry_1 = __nccwpck_require__(46790); +const analyze_bugbot_revision_use_case_1 = __nccwpck_require__(4658); +const bugbot_review_freshness_1 = __nccwpck_require__(14307); +const reconcile_bugbot_review_state_use_case_1 = __nccwpck_require__(57515); +const application_error_1 = __nccwpck_require__(75999); +const bugbot_event_ownership_policy_1 = __nccwpck_require__(52771); +const bugbot_message_catalog_1 = __nccwpck_require__(7406); +const bugbot_partition_completion_policy_1 = __nccwpck_require__(57555); const TASK_ID = 'DetectPotentialProblemsUseCase'; /** Coordinates Bugbot context, analysis and finding publication behind application ports. */ async function runDetectPotentialProblemsWorkflow(reviewContext, dependencies) { @@ -61035,15 +61035,15 @@ function resolvePublicationCatalog(operation, dependencies, publishesToPullReque /***/ }), -/***/ 96838: +/***/ 68549: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.checkoutBranch = checkoutBranch; -const application_error_1 = __nccwpck_require__(2965); -const logging_ports_1 = __nccwpck_require__(73001); +const application_error_1 = __nccwpck_require__(75999); +const logging_ports_1 = __nccwpck_require__(6152); const STASH_MESSAGE = 'bugbot-autofix-before-checkout'; async function hasUncommittedChanges(port) { let output = ''; @@ -61096,16 +61096,16 @@ async function restoreStashedChanges(port) { /***/ }), -/***/ 77749: +/***/ 33276: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.NotifyNewCommitOnIssueUseCase = void 0; -const logging_ports_1 = __nccwpck_require__(73001); -const task_emoji_1 = __nccwpck_require__(83142); -const notify_new_commit_on_issue_workflow_1 = __nccwpck_require__(22712); +const logging_ports_1 = __nccwpck_require__(6152); +const task_emoji_1 = __nccwpck_require__(46103); +const notify_new_commit_on_issue_workflow_1 = __nccwpck_require__(46101); class NotifyNewCommitOnIssueUseCase { constructor(issueRepository) { this.issueRepository = issueRepository; @@ -61121,16 +61121,16 @@ exports.NotifyNewCommitOnIssueUseCase = NotifyNewCommitOnIssueUseCase; /***/ }), -/***/ 22712: +/***/ 46101: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runNotifyNewCommitOnIssueWorkflow = runNotifyNewCommitOnIssueWorkflow; -const result_1 = __nccwpck_require__(61444); -const logging_ports_1 = __nccwpck_require__(73001); -const application_error_1 = __nccwpck_require__(2965); +const result_1 = __nccwpck_require__(73817); +const logging_ports_1 = __nccwpck_require__(6152); +const application_error_1 = __nccwpck_require__(75999); async function runNotifyNewCommitOnIssueWorkflow(param, taskId, issueRepository) { const result = []; try { @@ -61158,7 +61158,7 @@ async function runNotifyNewCommitOnIssueWorkflow(param, taskId, issueRepository) /***/ }), -/***/ 65146: +/***/ 51200: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -61194,7 +61194,7 @@ async function updateIssueAndRelatedPullRequests(request, ports) { /***/ }), -/***/ 39633: +/***/ 19004: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -61206,15 +61206,15 @@ async function updateIssueAndRelatedPullRequests(request, ports) { */ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.DoUserRequestUseCase = void 0; -const agent_1 = __nccwpck_require__(71889); -const prompts_1 = __nccwpck_require__(71854); -const logging_ports_1 = __nccwpck_require__(73001); -const task_emoji_1 = __nccwpck_require__(83142); -const result_1 = __nccwpck_require__(61444); -const project_context_instruction_1 = __nccwpck_require__(36158); -const sanitize_user_comment_for_prompt_1 = __nccwpck_require__(28636); -const workspace_mutation_guard_1 = __nccwpck_require__(12627); -const application_error_1 = __nccwpck_require__(2965); +const agent_1 = __nccwpck_require__(79937); +const prompts_1 = __nccwpck_require__(69518); +const logging_ports_1 = __nccwpck_require__(6152); +const task_emoji_1 = __nccwpck_require__(46103); +const result_1 = __nccwpck_require__(73817); +const project_context_instruction_1 = __nccwpck_require__(63907); +const sanitize_user_comment_for_prompt_1 = __nccwpck_require__(59828); +const workspace_mutation_guard_1 = __nccwpck_require__(24243); +const application_error_1 = __nccwpck_require__(75999); const TASK_ID = "DoUserRequestUseCase"; class DoUserRequestUseCase { constructor(aiRepository, gitCommitPort) { @@ -61308,7 +61308,7 @@ function failure(error) { /***/ }), -/***/ 12627: +/***/ 24243: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -61317,9 +61317,9 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.MAX_AUTOMATED_CHANGED_PATHS = void 0; exports.prepareWorkspaceMutation = prepareWorkspaceMutation; exports.finalizeWorkspaceMutation = finalizeWorkspaceMutation; -const application_error_1 = __nccwpck_require__(2965); -const git_branch_checkout_1 = __nccwpck_require__(96838); -const workspace_changes_1 = __nccwpck_require__(51578); +const application_error_1 = __nccwpck_require__(75999); +const git_branch_checkout_1 = __nccwpck_require__(68549); +const workspace_changes_1 = __nccwpck_require__(93370); exports.MAX_AUTOMATED_CHANGED_PATHS = 100; /** Establishes a clean and deterministic repository boundary before an agent may mutate files. */ async function prepareWorkspaceMutation(gitCommitPort, options) { @@ -61373,15 +61373,15 @@ async function inspectWorkspace(gitCommitPort, phase) { /***/ }), -/***/ 3389: +/***/ 72063: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.extractStructuredAnswer = extractStructuredAnswer; -const agent_output_locale_policy_1 = __nccwpck_require__(2584); -const application_error_1 = __nccwpck_require__(2965); +const agent_output_locale_policy_1 = __nccwpck_require__(30601); +const application_error_1 = __nccwpck_require__(75999); function extractStructuredAnswer(response, targetLocale) { if (response == null) return ''; @@ -61396,16 +61396,16 @@ function extractStructuredAnswer(response, targetLocale) { /***/ }), -/***/ 88960: +/***/ 18846: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.CheckPermissionsUseCase = void 0; -const logging_ports_1 = __nccwpck_require__(73001); -const task_emoji_1 = __nccwpck_require__(83142); -const check_permissions_workflow_1 = __nccwpck_require__(98658); +const logging_ports_1 = __nccwpck_require__(6152); +const task_emoji_1 = __nccwpck_require__(46103); +const check_permissions_workflow_1 = __nccwpck_require__(17102); class CheckPermissionsUseCase { constructor(organizationMembersPort) { this.organizationMembersPort = organizationMembersPort; @@ -61423,7 +61423,7 @@ exports.CheckPermissionsUseCase = CheckPermissionsUseCase; /***/ }), -/***/ 98658: +/***/ 17102: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -61431,9 +61431,9 @@ exports.CheckPermissionsUseCase = CheckPermissionsUseCase; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.projectCheckPermissionsContext = projectCheckPermissionsContext; exports.runCheckPermissionsWorkflow = runCheckPermissionsWorkflow; -const result_1 = __nccwpck_require__(61444); -const logging_ports_1 = __nccwpck_require__(73001); -const application_error_1 = __nccwpck_require__(2965); +const result_1 = __nccwpck_require__(73817); +const logging_ports_1 = __nccwpck_require__(6152); +const application_error_1 = __nccwpck_require__(75999); function projectCheckPermissionsContext(source) { const issueTarget = source.isIssue; return Object.freeze({ @@ -61497,7 +61497,7 @@ function buildInactiveResult(param, taskId) { /***/ }), -/***/ 78212: +/***/ 72770: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -61506,16 +61506,16 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.CommentLanguageTranslationWorkflow = exports.TRANSLATED_COMMENT_MARKER = void 0; exports.projectCommentLanguageRequest = projectCommentLanguageRequest; exports.getCommentLanguageAdaptationPayload = getCommentLanguageAdaptationPayload; -const result_1 = __nccwpck_require__(61444); -const agent_task_policy_1 = __nccwpck_require__(2601); -const agent_response_schemas_1 = __nccwpck_require__(63523); -const prompts_1 = __nccwpck_require__(71854); -const logging_ports_1 = __nccwpck_require__(73001); -const task_emoji_1 = __nccwpck_require__(83142); -const comment_translation_policy_1 = __nccwpck_require__(22406); -const locale_1 = __nccwpck_require__(64552); -const application_error_1 = __nccwpck_require__(2965); -var comment_translation_policy_2 = __nccwpck_require__(22406); +const result_1 = __nccwpck_require__(73817); +const agent_task_policy_1 = __nccwpck_require__(85712); +const agent_response_schemas_1 = __nccwpck_require__(25603); +const prompts_1 = __nccwpck_require__(69518); +const logging_ports_1 = __nccwpck_require__(6152); +const task_emoji_1 = __nccwpck_require__(46103); +const comment_translation_policy_1 = __nccwpck_require__(27150); +const locale_1 = __nccwpck_require__(15386); +const application_error_1 = __nccwpck_require__(75999); +var comment_translation_policy_2 = __nccwpck_require__(27150); Object.defineProperty(exports, "TRANSLATED_COMMENT_MARKER", ({ enumerable: true, get: function () { return comment_translation_policy_2.TRANSLATED_COMMENT_MARKER; } })); function projectCommentLanguageRequest(source) { return Object.freeze({ @@ -61656,7 +61656,7 @@ function languageAdaptationPayload(status, targetLocale, interpretedComment, sou /***/ }), -/***/ 26147: +/***/ 56334: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -61700,7 +61700,7 @@ function toCamelCase(input) { /***/ }), -/***/ 93499: +/***/ 79544: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -61723,14 +61723,14 @@ async function cleanupDuplicateComment(context, comments, sourceIsCurrent) { /***/ }), -/***/ 54191: +/***/ 65440: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildCommitPrefix = buildCommitPrefix; -const commit_prefix_transform_policy_1 = __nccwpck_require__(26147); +const commit_prefix_transform_policy_1 = __nccwpck_require__(56334); function buildCommitPrefix(branchName, transforms, onUnknownTransform) { return transforms .split(',') @@ -61741,18 +61741,18 @@ function buildCommitPrefix(branchName, transforms, onUnknownTransform) { /***/ }), -/***/ 85276: +/***/ 59946: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.GetHotfixVersionUseCase = void 0; -const result_1 = __nccwpck_require__(61444); -const content_utils_1 = __nccwpck_require__(61146); -const logging_ports_1 = __nccwpck_require__(73001); -const task_emoji_1 = __nccwpck_require__(83142); -const application_error_1 = __nccwpck_require__(2965); +const result_1 = __nccwpck_require__(73817); +const content_utils_1 = __nccwpck_require__(92816); +const logging_ports_1 = __nccwpck_require__(6152); +const task_emoji_1 = __nccwpck_require__(46103); +const application_error_1 = __nccwpck_require__(75999); class GetHotfixVersionUseCase { constructor(issueRepository) { this.issueRepository = issueRepository; @@ -61833,18 +61833,18 @@ function isPositiveIssueNumber(value) { /***/ }), -/***/ 65633: +/***/ 64410: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.GetReleaseTypeUseCase = void 0; -const result_1 = __nccwpck_require__(61444); -const content_utils_1 = __nccwpck_require__(61146); -const logging_ports_1 = __nccwpck_require__(73001); -const task_emoji_1 = __nccwpck_require__(83142); -const application_error_1 = __nccwpck_require__(2965); +const result_1 = __nccwpck_require__(73817); +const content_utils_1 = __nccwpck_require__(92816); +const logging_ports_1 = __nccwpck_require__(6152); +const task_emoji_1 = __nccwpck_require__(46103); +const application_error_1 = __nccwpck_require__(75999); class GetReleaseTypeUseCase { constructor(issueRepository) { this.issueRepository = issueRepository; @@ -61914,18 +61914,18 @@ function isPositiveIssueNumber(value) { /***/ }), -/***/ 8709: +/***/ 70587: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.GetReleaseVersionUseCase = void 0; -const result_1 = __nccwpck_require__(61444); -const content_utils_1 = __nccwpck_require__(61146); -const logging_ports_1 = __nccwpck_require__(73001); -const task_emoji_1 = __nccwpck_require__(83142); -const application_error_1 = __nccwpck_require__(2965); +const result_1 = __nccwpck_require__(73817); +const content_utils_1 = __nccwpck_require__(92816); +const logging_ports_1 = __nccwpck_require__(6152); +const task_emoji_1 = __nccwpck_require__(46103); +const application_error_1 = __nccwpck_require__(75999); class GetReleaseVersionUseCase { constructor(issueRepository) { this.issueRepository = issueRepository; @@ -61996,7 +61996,7 @@ function isPositiveIssueNumber(value) { /***/ }), -/***/ 72383: +/***/ 89064: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -62005,10 +62005,10 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.projectIssueContentLinkContext = projectIssueContentLinkContext; exports.projectPullRequestContentLinkContext = projectPullRequestContentLinkContext; exports.runProjectContentLinkWorkflow = runProjectContentLinkWorkflow; -const result_1 = __nccwpck_require__(61444); -const logging_ports_1 = __nccwpck_require__(73001); -const task_emoji_1 = __nccwpck_require__(83142); -const application_error_1 = __nccwpck_require__(2965); +const result_1 = __nccwpck_require__(73817); +const logging_ports_1 = __nccwpck_require__(6152); +const task_emoji_1 = __nccwpck_require__(46103); +const application_error_1 = __nccwpck_require__(75999); function projectIssueContentLinkContext(source) { return projectContext(source, 'issue', source.issue.number, source.project.getProjectColumnIssueCreated()); } @@ -62088,23 +62088,23 @@ function capitalize(value) { /***/ }), -/***/ 81581: +/***/ 40558: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runThinkAnswerWorkflow = runThinkAnswerWorkflow; -const result_1 = __nccwpck_require__(61444); -const agent_task_policy_1 = __nccwpck_require__(2601); -const agent_response_schemas_1 = __nccwpck_require__(63523); -const prompts_1 = __nccwpck_require__(71854); -const logging_ports_1 = __nccwpck_require__(73001); -const project_context_instruction_1 = __nccwpck_require__(36158); -const agent_answer_policy_1 = __nccwpck_require__(3389); -const github_comment_publication_policy_1 = __nccwpck_require__(22913); -const application_error_1 = __nccwpck_require__(2965); -const agent_output_locale_policy_1 = __nccwpck_require__(2584); +const result_1 = __nccwpck_require__(73817); +const agent_task_policy_1 = __nccwpck_require__(85712); +const agent_response_schemas_1 = __nccwpck_require__(25603); +const prompts_1 = __nccwpck_require__(69518); +const logging_ports_1 = __nccwpck_require__(6152); +const project_context_instruction_1 = __nccwpck_require__(63907); +const agent_answer_policy_1 = __nccwpck_require__(72063); +const github_comment_publication_policy_1 = __nccwpck_require__(72712); +const application_error_1 = __nccwpck_require__(75999); +const agent_output_locale_policy_1 = __nccwpck_require__(30601); async function runThinkAnswerWorkflow(param, taskId, request, dependencies) { const issueDescription = await loadIssueDescription(request.issueNumberForContext, dependencies.issueDescriptionQueryPort); const contextBlock = issueDescription @@ -62180,7 +62180,7 @@ async function queryThinkAnswer(param, prompt, repository, targetLocale) { /***/ }), -/***/ 12636: +/***/ 59687: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -62203,7 +62203,7 @@ function extractMentionQuestion(commentBody, tokenUser) { /***/ }), -/***/ 98559: +/***/ 23995: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -62211,10 +62211,10 @@ function extractMentionQuestion(commentBody, tokenUser) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.resolveThinkRequest = resolveThinkRequest; exports.buildExplicitCommandQuestion = buildExplicitCommandQuestion; -const copilot_command_1 = __nccwpck_require__(87134); -const copilot_comment_request_1 = __nccwpck_require__(81916); -const think_input_policy_1 = __nccwpck_require__(12636); -const sanitize_user_comment_for_prompt_1 = __nccwpck_require__(28636); +const copilot_command_1 = __nccwpck_require__(11771); +const copilot_comment_request_1 = __nccwpck_require__(86819); +const think_input_policy_1 = __nccwpck_require__(59687); +const sanitize_user_comment_for_prompt_1 = __nccwpck_require__(59828); /** Resolves the comment input and destination without performing I/O. */ function resolveThinkRequest(param) { const commentBody = (0, think_input_policy_1.getThinkCommentBody)({ @@ -62277,14 +62277,14 @@ function buildExplicitCommandQuestion(command) { /***/ }), -/***/ 25099: +/***/ 89255: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.ThinkUseCase = void 0; -const think_workflow_1 = __nccwpck_require__(14720); +const think_workflow_1 = __nccwpck_require__(36450); class ThinkUseCase { constructor(issueDescriptionQueryPort, aiRepository) { this.issueDescriptionQueryPort = issueDescriptionQueryPort; @@ -62303,7 +62303,7 @@ exports.ThinkUseCase = ThinkUseCase; /***/ }), -/***/ 14720: +/***/ 36450: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -62311,14 +62311,14 @@ exports.ThinkUseCase = ThinkUseCase; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.projectThinkContext = projectThinkContext; exports.runThinkWorkflow = runThinkWorkflow; -const agent_1 = __nccwpck_require__(71889); -const result_1 = __nccwpck_require__(61444); -const logging_ports_1 = __nccwpck_require__(73001); -const think_request_policy_1 = __nccwpck_require__(98559); -const think_answer_workflow_1 = __nccwpck_require__(81581); -const agent_task_policy_1 = __nccwpck_require__(2601); -const application_error_1 = __nccwpck_require__(2965); -const locale_1 = __nccwpck_require__(64552); +const agent_1 = __nccwpck_require__(79937); +const result_1 = __nccwpck_require__(73817); +const logging_ports_1 = __nccwpck_require__(6152); +const think_request_policy_1 = __nccwpck_require__(23995); +const think_answer_workflow_1 = __nccwpck_require__(40558); +const agent_task_policy_1 = __nccwpck_require__(85712); +const application_error_1 = __nccwpck_require__(75999); +const locale_1 = __nccwpck_require__(15386); function projectThinkContext(source) { const request = (0, think_request_policy_1.resolveThinkRequest)(source); const tokenUser = source.tokenUser?.trim(); @@ -62402,7 +62402,7 @@ function logSkipReason(reason, tokenUser) { /***/ }), -/***/ 93066: +/***/ 1725: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -62411,10 +62411,10 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.MAX_TRANSITION_NOTIFICATION_LINKS = exports.MAX_TRANSITION_NOTIFICATION_CHARACTERS = void 0; exports.reconcileTransitionNotification = reconcileTransitionNotification; exports.renderTransitionNotification = renderTransitionNotification; -const github_publication_1 = __nccwpck_require__(75905); -const github_user_policy_1 = __nccwpck_require__(19596); -const publication_identity_policy_1 = __nccwpck_require__(12590); -const duplicate_comment_cleanup_workflow_1 = __nccwpck_require__(93499); +const github_publication_1 = __nccwpck_require__(35793); +const github_user_policy_1 = __nccwpck_require__(84403); +const publication_identity_policy_1 = __nccwpck_require__(45403); +const duplicate_comment_cleanup_workflow_1 = __nccwpck_require__(79544); exports.MAX_TRANSITION_NOTIFICATION_CHARACTERS = 400; exports.MAX_TRANSITION_NOTIFICATION_LINKS = 2; /** Creates one immutable action notification per exact transition fingerprint. */ @@ -62501,16 +62501,16 @@ function outcome(effect, canonicalCommentId, duplicatesRemoved = 0, duplicatesCo /***/ }), -/***/ 21376: +/***/ 20556: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.UpdateTitleUseCase = void 0; -const logging_ports_1 = __nccwpck_require__(73001); -const task_emoji_1 = __nccwpck_require__(83142); -const update_title_workflow_1 = __nccwpck_require__(89641); +const logging_ports_1 = __nccwpck_require__(6152); +const task_emoji_1 = __nccwpck_require__(46103); +const update_title_workflow_1 = __nccwpck_require__(50029); class UpdateTitleUseCase { constructor(issueRepository) { this.issueRepository = issueRepository; @@ -62535,7 +62535,7 @@ exports.UpdateTitleUseCase = UpdateTitleUseCase; /***/ }), -/***/ 89641: +/***/ 50029: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -62545,9 +62545,9 @@ exports.projectUpdateTitleContext = projectUpdateTitleContext; exports.runIssueTitleUpdate = runIssueTitleUpdate; exports.runPullRequestTitleUpdate = runPullRequestTitleUpdate; exports.titleUpdateFailure = titleUpdateFailure; -const result_1 = __nccwpck_require__(61444); -const application_error_1 = __nccwpck_require__(2965); -const positive_integer_policy_1 = __nccwpck_require__(45613); +const result_1 = __nccwpck_require__(73817); +const application_error_1 = __nccwpck_require__(75999); +const positive_integer_policy_1 = __nccwpck_require__(19879); function projectUpdateTitleContext(source) { if (source.isIssue) { return Object.freeze({ @@ -62649,14 +62649,14 @@ function skippedResult(taskId) { /***/ }), -/***/ 41713: +/***/ 10706: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.AnswerIssueHelpUseCase = void 0; -const answer_issue_help_workflow_1 = __nccwpck_require__(75001); +const answer_issue_help_workflow_1 = __nccwpck_require__(86428); /** Application boundary for the initial response to question/help issues. */ class AnswerIssueHelpUseCase { constructor(aiRepository) { @@ -62674,25 +62674,25 @@ exports.AnswerIssueHelpUseCase = AnswerIssueHelpUseCase; /***/ }), -/***/ 75001: +/***/ 86428: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runAnswerIssueHelpWorkflow = runAnswerIssueHelpWorkflow; -const agent_1 = __nccwpck_require__(71889); -const result_1 = __nccwpck_require__(61444); -const agent_task_policy_1 = __nccwpck_require__(2601); -const agent_response_schemas_1 = __nccwpck_require__(63523); -const prompts_1 = __nccwpck_require__(71854); -const logging_ports_1 = __nccwpck_require__(73001); -const project_context_instruction_1 = __nccwpck_require__(36158); -const task_emoji_1 = __nccwpck_require__(83142); -const agent_answer_policy_1 = __nccwpck_require__(3389); -const github_comment_publication_policy_1 = __nccwpck_require__(22913); -const application_error_1 = __nccwpck_require__(2965); -const agent_output_locale_policy_1 = __nccwpck_require__(2584); +const agent_1 = __nccwpck_require__(79937); +const result_1 = __nccwpck_require__(73817); +const agent_task_policy_1 = __nccwpck_require__(85712); +const agent_response_schemas_1 = __nccwpck_require__(25603); +const prompts_1 = __nccwpck_require__(69518); +const logging_ports_1 = __nccwpck_require__(6152); +const project_context_instruction_1 = __nccwpck_require__(63907); +const task_emoji_1 = __nccwpck_require__(46103); +const agent_answer_policy_1 = __nccwpck_require__(72063); +const github_comment_publication_policy_1 = __nccwpck_require__(72712); +const application_error_1 = __nccwpck_require__(75999); +const agent_output_locale_policy_1 = __nccwpck_require__(30601); const TASK_ID = 'AnswerIssueHelpUseCase'; /** Posts one contextual answer for a newly opened question/help issue. */ async function runAnswerIssueHelpWorkflow(param, dependencies) { @@ -62777,14 +62777,14 @@ function skipped() { /***/ }), -/***/ 18189: +/***/ 55523: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.AssignMemberToIssueUseCase = void 0; -const assign_members_workflow_1 = __nccwpck_require__(78123); +const assign_members_workflow_1 = __nccwpck_require__(42343); /** Application boundary for assigning issue or pull-request members. */ class AssignMemberToIssueUseCase { constructor(issueRepository, projectRepository) { @@ -62804,18 +62804,18 @@ exports.AssignMemberToIssueUseCase = AssignMemberToIssueUseCase; /***/ }), -/***/ 78123: +/***/ 42343: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runAssignMembersWorkflow = runAssignMembersWorkflow; -const result_1 = __nccwpck_require__(61444); -const logging_ports_1 = __nccwpck_require__(73001); -const task_emoji_1 = __nccwpck_require__(83142); -const assignee_assignment_policy_1 = __nccwpck_require__(2426); -const application_error_1 = __nccwpck_require__(2965); +const result_1 = __nccwpck_require__(73817); +const logging_ports_1 = __nccwpck_require__(6152); +const task_emoji_1 = __nccwpck_require__(46103); +const assignee_assignment_policy_1 = __nccwpck_require__(85918); +const application_error_1 = __nccwpck_require__(75999); const TASK_ID = 'AssignMemberToIssueUseCase'; /** Assigns the creator and remaining project members according to the pure assignment policy. */ async function runAssignMembersWorkflow(param, dependencies) { @@ -62878,14 +62878,14 @@ function assignmentResult(success, step) { /***/ }), -/***/ 1093: +/***/ 80174: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.AssignReviewersToIssueUseCase = void 0; -const assign_reviewers_workflow_1 = __nccwpck_require__(35521); +const assign_reviewers_workflow_1 = __nccwpck_require__(97260); /** Application boundary for requesting the configured number of reviewers. */ class AssignReviewersToIssueUseCase { constructor(issueRepository, pullRequestRepository, projectRepository) { @@ -62907,19 +62907,19 @@ exports.AssignReviewersToIssueUseCase = AssignReviewersToIssueUseCase; /***/ }), -/***/ 35521: +/***/ 97260: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runAssignReviewersWorkflow = runAssignReviewersWorkflow; -const result_1 = __nccwpck_require__(61444); -const pull_request_review_errors_1 = __nccwpck_require__(81504); -const logging_ports_1 = __nccwpck_require__(73001); -const task_emoji_1 = __nccwpck_require__(83142); -const reviewer_assignment_policy_1 = __nccwpck_require__(49532); -const application_error_1 = __nccwpck_require__(2965); +const result_1 = __nccwpck_require__(73817); +const pull_request_review_errors_1 = __nccwpck_require__(46445); +const logging_ports_1 = __nccwpck_require__(6152); +const task_emoji_1 = __nccwpck_require__(46103); +const reviewer_assignment_policy_1 = __nccwpck_require__(88350); +const application_error_1 = __nccwpck_require__(75999); const TASK_ID = 'AssignReviewersToIssueUseCase'; /** Selects and requests reviewers without coupling the use-case boundary to GitHub. */ async function runAssignReviewersWorkflow(param, dependencies) { @@ -62999,7 +62999,7 @@ function failureResult(step) { /***/ }), -/***/ 50133: +/***/ 29988: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -63021,16 +63021,16 @@ function selectBranchPreparationStrategy(flags) { /***/ }), -/***/ 46243: +/***/ 19511: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.CheckPriorityIssueSizeUseCase = void 0; -const logging_ports_1 = __nccwpck_require__(73001); -const task_emoji_1 = __nccwpck_require__(83142); -const priority_size_check_use_case_1 = __nccwpck_require__(81753); +const logging_ports_1 = __nccwpck_require__(6152); +const task_emoji_1 = __nccwpck_require__(46103); +const priority_size_check_use_case_1 = __nccwpck_require__(98060); class CheckPriorityIssueSizeUseCase { constructor(projectBoardPriorityPort) { this.projectBoardPriorityPort = projectBoardPriorityPort; @@ -63046,18 +63046,18 @@ exports.CheckPriorityIssueSizeUseCase = CheckPriorityIssueSizeUseCase; /***/ }), -/***/ 88705: +/***/ 46753: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.CloseIssueAfterMergingUseCase = void 0; -const result_1 = __nccwpck_require__(61444); -const logging_ports_1 = __nccwpck_require__(73001); -const task_emoji_1 = __nccwpck_require__(83142); -const application_error_1 = __nccwpck_require__(2965); -const positive_integer_policy_1 = __nccwpck_require__(45613); +const result_1 = __nccwpck_require__(73817); +const logging_ports_1 = __nccwpck_require__(6152); +const task_emoji_1 = __nccwpck_require__(46103); +const application_error_1 = __nccwpck_require__(75999); +const positive_integer_policy_1 = __nccwpck_require__(19879); class CloseIssueAfterMergingUseCase { constructor(issueRepository) { this.issueRepository = issueRepository; @@ -63119,17 +63119,17 @@ exports.CloseIssueAfterMergingUseCase = CloseIssueAfterMergingUseCase; /***/ }), -/***/ 71594: +/***/ 86675: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.CloseNotAllowedIssueUseCase = void 0; -const result_1 = __nccwpck_require__(61444); -const logging_ports_1 = __nccwpck_require__(73001); -const task_emoji_1 = __nccwpck_require__(83142); -const application_error_1 = __nccwpck_require__(2965); +const result_1 = __nccwpck_require__(73817); +const logging_ports_1 = __nccwpck_require__(6152); +const task_emoji_1 = __nccwpck_require__(46103); +const application_error_1 = __nccwpck_require__(75999); class CloseNotAllowedIssueUseCase { constructor(issueRepository) { this.issueRepository = issueRepository; @@ -63180,18 +63180,18 @@ exports.CloseNotAllowedIssueUseCase = CloseNotAllowedIssueUseCase; /***/ }), -/***/ 2608: +/***/ 33445: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runDeployAddedWorkflow = runDeployAddedWorkflow; -const result_1 = __nccwpck_require__(61444); -const content_utils_1 = __nccwpck_require__(61146); -const logging_ports_1 = __nccwpck_require__(73001); -const deploy_workflow_policy_1 = __nccwpck_require__(61524); -const application_error_1 = __nccwpck_require__(2965); +const result_1 = __nccwpck_require__(73817); +const content_utils_1 = __nccwpck_require__(92816); +const logging_ports_1 = __nccwpck_require__(6152); +const deploy_workflow_policy_1 = __nccwpck_require__(8428); +const application_error_1 = __nccwpck_require__(75999); async function runDeployAddedWorkflow(param, taskId, branchWorkflowPort, moveIssueToInProgressUseCase) { const plan = (0, deploy_workflow_policy_1.resolveDeployWorkflowPlan)(param); if (!plan) @@ -63234,16 +63234,16 @@ async function runDeployAddedWorkflow(param, taskId, branchWorkflowPort, moveIss /***/ }), -/***/ 56723: +/***/ 27708: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.DeployAddedUseCase = void 0; -const logging_ports_1 = __nccwpck_require__(73001); -const task_emoji_1 = __nccwpck_require__(83142); -const deploy_added_workflow_1 = __nccwpck_require__(2608); +const logging_ports_1 = __nccwpck_require__(6152); +const task_emoji_1 = __nccwpck_require__(46103); +const deploy_added_workflow_1 = __nccwpck_require__(33445); class DeployAddedUseCase { constructor(branchWorkflowPort, moveIssueToInProgressUseCase) { this.branchWorkflowPort = branchWorkflowPort; @@ -63260,14 +63260,14 @@ exports.DeployAddedUseCase = DeployAddedUseCase; /***/ }), -/***/ 34835: +/***/ 34100: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.LinkIssueProjectUseCase = void 0; -const project_content_link_workflow_1 = __nccwpck_require__(72383); +const project_content_link_workflow_1 = __nccwpck_require__(89064); /** Application boundary for linking issues to configured ProjectV2 boards. */ class LinkIssueProjectUseCase { constructor(projectContentPort) { @@ -63283,17 +63283,17 @@ exports.LinkIssueProjectUseCase = LinkIssueProjectUseCase; /***/ }), -/***/ 79453: +/***/ 52309: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.MoveIssueToInProgressUseCase = void 0; -const result_1 = __nccwpck_require__(61444); -const logging_ports_1 = __nccwpck_require__(73001); -const task_emoji_1 = __nccwpck_require__(83142); -const application_error_1 = __nccwpck_require__(2965); +const result_1 = __nccwpck_require__(73817); +const logging_ports_1 = __nccwpck_require__(6152); +const task_emoji_1 = __nccwpck_require__(46103); +const application_error_1 = __nccwpck_require__(75999); class MoveIssueToInProgressUseCase { constructor(projectRepository) { this.projectRepository = projectRepository; @@ -63339,22 +63339,22 @@ exports.MoveIssueToInProgressUseCase = MoveIssueToInProgressUseCase; /***/ }), -/***/ 35833: +/***/ 67546: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.PrepareBranchesUseCase = void 0; -const result_1 = __nccwpck_require__(61444); -const logging_ports_1 = __nccwpck_require__(73001); -const task_emoji_1 = __nccwpck_require__(83142); -const branch_preparation_strategy_1 = __nccwpck_require__(50133); -const prepare_managed_branch_1 = __nccwpck_require__(28983); -const prepare_hotfix_branch_1 = __nccwpck_require__(88395); -const prepare_release_branch_1 = __nccwpck_require__(16712); -const application_error_1 = __nccwpck_require__(2965); -const issue_workflow_context_1 = __nccwpck_require__(13765); +const result_1 = __nccwpck_require__(73817); +const logging_ports_1 = __nccwpck_require__(6152); +const task_emoji_1 = __nccwpck_require__(46103); +const branch_preparation_strategy_1 = __nccwpck_require__(29988); +const prepare_managed_branch_1 = __nccwpck_require__(29928); +const prepare_hotfix_branch_1 = __nccwpck_require__(96318); +const prepare_release_branch_1 = __nccwpck_require__(83059); +const application_error_1 = __nccwpck_require__(75999); +const issue_workflow_context_1 = __nccwpck_require__(98005); class PrepareBranchesUseCase { constructor(branchListQueryPort, branchNamePort, remoteBranchSyncPort, commitTagQueryPort, linkedBranchCommandPort, branchPropagationDelayPort, moveIssueToInProgressUseCase) { this.branchListQueryPort = branchListQueryPort; @@ -63432,16 +63432,16 @@ exports.PrepareBranchesUseCase = PrepareBranchesUseCase; /***/ }), -/***/ 88395: +/***/ 96318: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.prepareHotfixBranch = prepareHotfixBranch; -const result_1 = __nccwpck_require__(61444); -const logging_ports_1 = __nccwpck_require__(73001); -const issue_workflow_context_1 = __nccwpck_require__(13765); +const result_1 = __nccwpck_require__(73817); +const logging_ports_1 = __nccwpck_require__(6152); +const issue_workflow_context_1 = __nccwpck_require__(98005); async function prepareHotfixBranch(param, commitTagQuery, linkedBranchCommand, branches, taskId) { const { hotfix } = param; if (hotfix.baseVersion === undefined || @@ -63501,20 +63501,20 @@ async function prepareHotfixBranch(param, commitTagQuery, linkedBranchCommand, b /***/ }), -/***/ 28983: +/***/ 29928: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.prepareManagedBranch = prepareManagedBranch; -const result_1 = __nccwpck_require__(61444); -const branch_preparation_policy_1 = __nccwpck_require__(59489); -const managed_branch_result_policy_1 = __nccwpck_require__(32742); -const logging_ports_1 = __nccwpck_require__(73001); -const application_error_1 = __nccwpck_require__(2965); -const execute_script_use_case_1 = __nccwpck_require__(54191); -const issue_workflow_context_1 = __nccwpck_require__(13765); +const result_1 = __nccwpck_require__(73817); +const branch_preparation_policy_1 = __nccwpck_require__(97307); +const managed_branch_result_policy_1 = __nccwpck_require__(55078); +const logging_ports_1 = __nccwpck_require__(6152); +const application_error_1 = __nccwpck_require__(75999); +const execute_script_use_case_1 = __nccwpck_require__(65440); +const issue_workflow_context_1 = __nccwpck_require__(98005); async function prepareManagedBranch(param, issueTitle, branches, taskId, dependencies) { (0, logging_ports_1.logDebugInfo)(`Branch type: ${param.managementBranch}`); const decision = (0, branch_preparation_policy_1.decideManagedBranchPreparation)({ @@ -63598,17 +63598,17 @@ async function buildConfiguredCommitPrefix(param, branchName) { /***/ }), -/***/ 16712: +/***/ 83059: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.prepareReleaseBranch = prepareReleaseBranch; -const result_1 = __nccwpck_require__(61444); -const execute_script_use_case_1 = __nccwpck_require__(54191); -const logging_ports_1 = __nccwpck_require__(73001); -const issue_workflow_context_1 = __nccwpck_require__(13765); +const result_1 = __nccwpck_require__(73817); +const execute_script_use_case_1 = __nccwpck_require__(65440); +const logging_ports_1 = __nccwpck_require__(6152); +const issue_workflow_context_1 = __nccwpck_require__(98005); async function prepareReleaseBranch(param, linkedBranchCommand, branches, taskId) { const { release } = param; if (release.version === undefined || release.branch === undefined) { @@ -63698,7 +63698,7 @@ function buildReleaseReminder(param, releaseUrl, developmentUrl, mainUrl) { /***/ }), -/***/ 46093: +/***/ 16530: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -63717,17 +63717,17 @@ function resolveGithubPriorityLabel(priority, labels) { /***/ }), -/***/ 81753: +/***/ 98060: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runPrioritySizeCheck = runPrioritySizeCheck; -const result_1 = __nccwpck_require__(61444); -const logging_ports_1 = __nccwpck_require__(73001); -const priority_label_policy_1 = __nccwpck_require__(46093); -const application_error_1 = __nccwpck_require__(2965); +const result_1 = __nccwpck_require__(73817); +const logging_ports_1 = __nccwpck_require__(6152); +const priority_label_policy_1 = __nccwpck_require__(16530); +const application_error_1 = __nccwpck_require__(75999); async function runPrioritySizeCheck(param, taskId, projectRepository) { try { return await applyPriorityToProjects(param, taskId, projectRepository); @@ -63773,16 +63773,16 @@ async function applyPriorityToProjects(param, taskId, projectRepository) { /***/ }), -/***/ 10027: +/***/ 71836: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.ReconcileBranchReadinessUseCase = void 0; -const issue_start_policy_1 = __nccwpck_require__(20953); -const result_1 = __nccwpck_require__(61444); -const application_error_1 = __nccwpck_require__(2965); +const issue_start_policy_1 = __nccwpck_require__(90332); +const result_1 = __nccwpck_require__(73817); +const application_error_1 = __nccwpck_require__(75999); /** Projects verified remote facts into the managed `branched` output label. */ class ReconcileBranchReadinessUseCase { constructor(linkedBranch, labels) { @@ -63856,7 +63856,7 @@ exports.ReconcileBranchReadinessUseCase = ReconcileBranchReadinessUseCase; /***/ }), -/***/ 11944: +/***/ 57836: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -63874,18 +63874,18 @@ function selectIssueBranchesToRemove(branches, issueNumber, branchTypes) { /***/ }), -/***/ 92405: +/***/ 15608: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.RemoveIssueBranchesUseCase = void 0; -const result_1 = __nccwpck_require__(61444); -const logging_ports_1 = __nccwpck_require__(73001); -const task_emoji_1 = __nccwpck_require__(83142); -const remove_issue_branches_policy_1 = __nccwpck_require__(11944); -const application_error_1 = __nccwpck_require__(2965); +const result_1 = __nccwpck_require__(73817); +const logging_ports_1 = __nccwpck_require__(6152); +const task_emoji_1 = __nccwpck_require__(46103); +const remove_issue_branches_policy_1 = __nccwpck_require__(57836); +const application_error_1 = __nccwpck_require__(75999); /** * Remove any branch created for this issue */ @@ -63949,17 +63949,17 @@ async function removeIssueBranch(param, taskId, branchName, branchLifecyclePort) /***/ }), -/***/ 26142: +/***/ 67129: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.RemoveNotNeededBranchesUseCase = void 0; -const result_1 = __nccwpck_require__(61444); -const logging_ports_1 = __nccwpck_require__(73001); -const task_emoji_1 = __nccwpck_require__(83142); -const application_error_1 = __nccwpck_require__(2965); +const result_1 = __nccwpck_require__(73817); +const logging_ports_1 = __nccwpck_require__(6152); +const task_emoji_1 = __nccwpck_require__(46103); +const application_error_1 = __nccwpck_require__(75999); class RemoveNotNeededBranchesUseCase { constructor(branchLifecyclePort, branchNamePort) { this.branchLifecyclePort = branchLifecyclePort; @@ -64043,17 +64043,17 @@ exports.RemoveNotNeededBranchesUseCase = RemoveNotNeededBranchesUseCase; /***/ }), -/***/ 25514: +/***/ 38222: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.UpdateIssueTypeUseCase = void 0; -const result_1 = __nccwpck_require__(61444); -const logging_ports_1 = __nccwpck_require__(73001); -const task_emoji_1 = __nccwpck_require__(83142); -const application_error_1 = __nccwpck_require__(2965); +const result_1 = __nccwpck_require__(73817); +const logging_ports_1 = __nccwpck_require__(6152); +const task_emoji_1 = __nccwpck_require__(46103); +const application_error_1 = __nccwpck_require__(75999); class UpdateIssueTypeUseCase { constructor(issueRepository) { this.issueRepository = issueRepository; @@ -64086,7 +64086,7 @@ exports.UpdateIssueTypeUseCase = UpdateIssueTypeUseCase; /***/ }), -/***/ 34670: +/***/ 93152: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -64094,7 +64094,7 @@ exports.UpdateIssueTypeUseCase = UpdateIssueTypeUseCase; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.CheckIssueCommentLanguageUseCase = void 0; exports.projectIssueCommentLanguageRequest = projectIssueCommentLanguageRequest; -const comment_language_translation_workflow_1 = __nccwpck_require__(78212); +const comment_language_translation_workflow_1 = __nccwpck_require__(72770); function projectIssueCommentLanguageRequest(source) { return (0, comment_language_translation_workflow_1.projectCommentLanguageRequest)({ commentBody: source.issue.commentBody, @@ -64122,16 +64122,16 @@ exports.CheckIssueCommentLanguageUseCase = CheckIssueCommentLanguageUseCase; /***/ }), -/***/ 61696: +/***/ 12738: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.CheckPriorityPullRequestSizeUseCase = void 0; -const logging_ports_1 = __nccwpck_require__(73001); -const task_emoji_1 = __nccwpck_require__(83142); -const priority_size_check_use_case_1 = __nccwpck_require__(81753); +const logging_ports_1 = __nccwpck_require__(6152); +const task_emoji_1 = __nccwpck_require__(46103); +const priority_size_check_use_case_1 = __nccwpck_require__(98060); class CheckPriorityPullRequestSizeUseCase { constructor(projectBoardPriorityPort) { this.projectBoardPriorityPort = projectBoardPriorityPort; @@ -64147,18 +64147,18 @@ exports.CheckPriorityPullRequestSizeUseCase = CheckPriorityPullRequestSizeUseCas /***/ }), -/***/ 64280: +/***/ 38259: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.LinkPullRequestIssueUseCase = void 0; -const result_1 = __nccwpck_require__(61444); -const logging_ports_1 = __nccwpck_require__(73001); -const task_emoji_1 = __nccwpck_require__(83142); -const link_pull_request_issue_workflow_1 = __nccwpck_require__(90858); -const application_error_1 = __nccwpck_require__(2965); +const result_1 = __nccwpck_require__(73817); +const logging_ports_1 = __nccwpck_require__(6152); +const task_emoji_1 = __nccwpck_require__(46103); +const link_pull_request_issue_workflow_1 = __nccwpck_require__(19033); +const application_error_1 = __nccwpck_require__(75999); class LinkPullRequestIssueUseCase { constructor(pullRequestIssueLinkPort, eventualConsistencyDelayPort) { this.pullRequestIssueLinkPort = pullRequestIssueLinkPort; @@ -64215,7 +64215,7 @@ function describeRecovery(error) { /***/ }), -/***/ 90858: +/***/ 19033: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -64223,9 +64223,9 @@ function describeRecovery(error) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.PullRequestIssueLinkOperationError = void 0; exports.runLinkPullRequestIssue = runLinkPullRequestIssue; -const result_1 = __nccwpck_require__(61444); -const positive_integer_policy_1 = __nccwpck_require__(45613); -const deployment_configuration_1 = __nccwpck_require__(5664); +const result_1 = __nccwpck_require__(73817); +const positive_integer_policy_1 = __nccwpck_require__(19879); +const deployment_configuration_1 = __nccwpck_require__(22495); const LINK_MARKER_PREFIX = '/iu; function resolveOpenBranchDependencies(issues, pullRequests) { const candidates = []; @@ -72408,14 +72408,14 @@ function uniqueValidDependencies(candidates) { /***/ }), -/***/ 78769: +/***/ 9627: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.BranchDependencyRepository = void 0; -const branch_dependency_policy_1 = __nccwpck_require__(64454); +const branch_dependency_policy_1 = __nccwpck_require__(54874); const OPEN_DEPENDENCIES_QUERY = ` query BranchSyncDependencies($owner: String!, $repo: String!, $issuesCursor: String, $pullsCursor: String) { repository(owner: $owner, name: $repo) { @@ -72534,7 +72534,7 @@ function withCause(message, cause) { /***/ }), -/***/ 26781: +/***/ 77509: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -72559,7 +72559,7 @@ exports.DeploymentContinuationRepository = DeploymentContinuationRepository; /***/ }), -/***/ 86534: +/***/ 91985: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -72596,17 +72596,17 @@ exports.DeploymentPresentationRepository = DeploymentPresentationRepository; /***/ }), -/***/ 59536: +/***/ 3182: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.DeploymentStateRepositoryFactory = void 0; -const deployment_state_fence_1 = __nccwpck_require__(32481); -const config_1 = __nccwpck_require__(98013); -const configuration_handler_1 = __nccwpck_require__(51068); -const configuration_payload_policy_1 = __nccwpck_require__(23509); +const deployment_state_fence_1 = __nccwpck_require__(72369); +const config_1 = __nccwpck_require__(90450); +const configuration_handler_1 = __nccwpck_require__(40188); +const configuration_payload_policy_1 = __nccwpck_require__(58043); class DeploymentStateRepositoryFactory { constructor(issues) { this.issues = issues; @@ -72675,14 +72675,14 @@ function isRecord(value) { /***/ }), -/***/ 35315: +/***/ 85886: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.GithubDeploymentGitRepository = void 0; -const application_error_1 = __nccwpck_require__(2965); +const application_error_1 = __nccwpck_require__(75999); class GithubDeploymentGitRepository { constructor(clientProvider) { this.clientProvider = clientProvider; @@ -72795,14 +72795,14 @@ function isNotFound(error) { /***/ }), -/***/ 57536: +/***/ 96483: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.GithubManagedPullRequestRepository = void 0; -const managed_pull_request_1 = __nccwpck_require__(7975); +const managed_pull_request_1 = __nccwpck_require__(95914); class GithubManagedPullRequestRepository { constructor(clientProvider) { this.clientProvider = clientProvider; @@ -72906,7 +72906,7 @@ function mapPullRequest(value, owner, repository) { /***/ }), -/***/ 46950: +/***/ 55527: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -72946,7 +72946,7 @@ var __importStar = (this && this.__importStar) || (function () { })(); Object.defineProperty(exports, "__esModule", ({ value: true })); exports.GithubTargetMergeCapabilitiesInspector = void 0; -const yaml = __importStar(__nccwpck_require__(87969)); +const yaml = __importStar(__nccwpck_require__(783)); class GithubTargetMergeCapabilitiesInspector { constructor(clientProvider) { this.clientProvider = clientProvider; @@ -73526,7 +73526,7 @@ function isNotFound(error) { /***/ }), -/***/ 72119: +/***/ 26331: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -73566,11 +73566,11 @@ var __importStar = (this && this.__importStar) || (function () { })(); Object.defineProperty(exports, "__esModule", ({ value: true })); exports.GitCliRepository = void 0; -const exec = __importStar(__nccwpck_require__(36086)); -const logger_1 = __nccwpck_require__(50135); -const version_policy_1 = __nccwpck_require__(36707); -const git_authentication_environment_1 = __nccwpck_require__(1906); -const application_error_1 = __nccwpck_require__(2965); +const exec = __importStar(__nccwpck_require__(18538)); +const logger_1 = __nccwpck_require__(91151); +const version_policy_1 = __nccwpck_require__(8381); +const git_authentication_environment_1 = __nccwpck_require__(16535); +const application_error_1 = __nccwpck_require__(75999); /** * Repository for Git operations executed via CLI (exec). * Isolated to allow unit tests with mocked @actions/exec. @@ -73669,7 +73669,7 @@ exports.GitCliRepository = GitCliRepository; /***/ }), -/***/ 85071: +/***/ 57628: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -73677,8 +73677,8 @@ exports.GitCliRepository = GitCliRepository; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.inspectMissingCredentialHealthWorkflow = inspectMissingCredentialHealthWorkflow; exports.inspectCredentialHealthWorkflowAtRef = inspectCredentialHealthWorkflowAtRef; -const setup_workflow_catalog_1 = __nccwpck_require__(37008); -const github_error_policy_1 = __nccwpck_require__(26189); +const setup_workflow_catalog_1 = __nccwpck_require__(24596); +const github_error_policy_1 = __nccwpck_require__(58791); /** A workflow API 404 is confirmed absence only after two independent Contents reads. */ async function inspectMissingCredentialHealthWorkflow(getContent, owner, repository, ref) { const state = await inspectCredentialHealthWorkflowAtRef(getContent, owner, repository, ref); @@ -73716,7 +73716,7 @@ async function inspectCredentialHealthWorkflowAtRef(getContent, owner, repositor /***/ }), -/***/ 26189: +/***/ 58791: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -73791,14 +73791,14 @@ function readHeader(headers, expected) { /***/ }), -/***/ 64064: +/***/ 2761: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.paginateCursor = paginateCursor; -const logger_1 = __nccwpck_require__(50135); +const logger_1 = __nccwpck_require__(91151); /** * Iterates cursor-based API pages while enforcing a finite boundary and a * valid cursor transition. Consumers can `break` early when they find the @@ -73829,7 +73829,7 @@ async function* paginateCursor(fetchPage, options = {}) { /***/ }), -/***/ 24347: +/***/ 44812: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -73859,15 +73859,15 @@ function requireObject(data, operation) { /***/ }), -/***/ 97590: +/***/ 52644: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.GithubPublicationSourceRepository = void 0; -const application_error_1 = __nccwpck_require__(2965); -const git_object_id_1 = __nccwpck_require__(36924); +const application_error_1 = __nccwpck_require__(75999); +const git_object_id_1 = __nccwpck_require__(88623); /** Reads the authoritative branch head without exposing Octokit to application code. */ class GithubPublicationSourceRepository { constructor(clientProvider) { @@ -73891,14 +73891,14 @@ exports.GithubPublicationSourceRepository = GithubPublicationSourceRepository; /***/ }), -/***/ 85949: +/***/ 88593: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.BugbotIssueCommentQueryRepository = void 0; -const application_error_1 = __nccwpck_require__(2965); +const application_error_1 = __nccwpck_require__(75999); /** Reads the newest Bugbot issue/PR conversation comments with a fixed two-page budget. */ class BugbotIssueCommentQueryRepository { constructor(githubClient) { @@ -73982,7 +73982,7 @@ exports.BugbotIssueCommentQueryRepository = BugbotIssueCommentQueryRepository; /***/ }), -/***/ 15589: +/***/ 82726: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -74002,7 +74002,7 @@ exports.BugbotIssueRepository = BugbotIssueRepository; /***/ }), -/***/ 32004: +/***/ 91153: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -74028,15 +74028,15 @@ exports.ExecutionIssueSetupRepository = ExecutionIssueSetupRepository; /***/ }), -/***/ 7223: +/***/ 75023: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.IssueAssignmentRepository = void 0; -const logger_1 = __nccwpck_require__(50135); -const application_error_1 = __nccwpck_require__(2965); +const logger_1 = __nccwpck_require__(91151); +const application_error_1 = __nccwpck_require__(75999); class IssueAssignmentRepository { constructor(githubClient) { this.githubClient = githubClient; @@ -74075,7 +74075,7 @@ exports.IssueAssignmentRepository = IssueAssignmentRepository; /***/ }), -/***/ 80674: +/***/ 23231: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -74095,18 +74095,18 @@ exports.IssueClosureRepository = IssueClosureRepository; /***/ }), -/***/ 43338: +/***/ 2313: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.IssueContentRepository = void 0; -const comment_content_policy_1 = __nccwpck_require__(2324); -const logger_1 = __nccwpck_require__(50135); -const github_pagination_policy_1 = __nccwpck_require__(24347); -const application_error_1 = __nccwpck_require__(2965); -const github_error_policy_1 = __nccwpck_require__(26189); +const comment_content_policy_1 = __nccwpck_require__(77454); +const logger_1 = __nccwpck_require__(91151); +const github_pagination_policy_1 = __nccwpck_require__(44812); +const application_error_1 = __nccwpck_require__(75999); +const github_error_policy_1 = __nccwpck_require__(58791); class IssueContentRepository { constructor(githubClient) { this.githubClient = githubClient; @@ -74234,14 +74234,14 @@ exports.IssueContentRepository = IssueContentRepository; /***/ }), -/***/ 5533: +/***/ 28868: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.IssueInactivityRepository = void 0; -const github_pagination_policy_1 = __nccwpck_require__(24347); +const github_pagination_policy_1 = __nccwpck_require__(44812); /** Reads the provider's issue activity timestamp and waiting-state labels. */ class IssueInactivityRepository { constructor(githubClient) { @@ -74295,18 +74295,18 @@ function toSnapshot(issue) { /***/ }), -/***/ 4532: +/***/ 59699: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.IssueLabelProvisioningRepository = void 0; -const initial_label_provisioning_policy_1 = __nccwpck_require__(36432); -const logger_1 = __nccwpck_require__(50135); -const github_error_policy_1 = __nccwpck_require__(26189); -const github_pagination_policy_1 = __nccwpck_require__(24347); -const application_error_1 = __nccwpck_require__(2965); +const initial_label_provisioning_policy_1 = __nccwpck_require__(73160); +const logger_1 = __nccwpck_require__(91151); +const github_error_policy_1 = __nccwpck_require__(58791); +const github_pagination_policy_1 = __nccwpck_require__(44812); +const application_error_1 = __nccwpck_require__(75999); class IssueLabelProvisioningRepository { constructor(githubClient) { this.githubClient = githubClient; @@ -74372,16 +74372,16 @@ function mapLabelMutationError(name, error) { /***/ }), -/***/ 27825: +/***/ 45725: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.IssueLabelRepository = void 0; -const logger_1 = __nccwpck_require__(50135); -const github_pagination_policy_1 = __nccwpck_require__(24347); -const application_error_1 = __nccwpck_require__(2965); +const logger_1 = __nccwpck_require__(91151); +const github_pagination_policy_1 = __nccwpck_require__(44812); +const application_error_1 = __nccwpck_require__(75999); class IssueLabelRepository { constructor(githubClient) { this.githubClient = githubClient; @@ -74423,14 +74423,14 @@ exports.IssueLabelRepository = IssueLabelRepository; /***/ }), -/***/ 44023: +/***/ 8346: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.IssueLifecycleRepository = void 0; -const logger_1 = __nccwpck_require__(50135); +const logger_1 = __nccwpck_require__(91151); class IssueLifecycleRepository { constructor(githubClient) { this.githubClient = githubClient; @@ -74455,16 +74455,16 @@ exports.IssueLifecycleRepository = IssueLifecycleRepository; /***/ }), -/***/ 37674: +/***/ 11333: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.IssueMetadataRepository = void 0; -const logger_1 = __nccwpck_require__(50135); -const milestone_1 = __nccwpck_require__(22410); -const application_error_1 = __nccwpck_require__(2965); +const logger_1 = __nccwpck_require__(91151); +const milestone_1 = __nccwpck_require__(2016); +const application_error_1 = __nccwpck_require__(75999); class IssueMetadataRepository { constructor(metadataClient, graphqlClient) { this.metadataClient = metadataClient; @@ -74542,7 +74542,7 @@ exports.IssueMetadataRepository = IssueMetadataRepository; /***/ }), -/***/ 24682: +/***/ 907: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -74562,15 +74562,15 @@ exports.IssueNotificationRepository = IssueNotificationRepository; /***/ }), -/***/ 58763: +/***/ 66610: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.IssueProgressLabelRepository = void 0; -const logger_1 = __nccwpck_require__(50135); -const progress_labels_1 = __nccwpck_require__(71285); +const logger_1 = __nccwpck_require__(91151); +const progress_labels_1 = __nccwpck_require__(97890); class IssueProgressLabelRepository { constructor(issueLabelRepository) { this.issueLabelRepository = issueLabelRepository; @@ -74592,7 +74592,7 @@ exports.IssueProgressLabelRepository = IssueProgressLabelRepository; /***/ }), -/***/ 64844: +/***/ 26674: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -74615,16 +74615,16 @@ exports.IssueProgressTrackingRepository = IssueProgressTrackingRepository; /***/ }), -/***/ 89116: +/***/ 10121: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.IssueTitleRepository = void 0; -const issue_emoji_policy_1 = __nccwpck_require__(70469); -const issue_title_policy_1 = __nccwpck_require__(16208); -const issue_title_update_1 = __nccwpck_require__(60886); +const issue_emoji_policy_1 = __nccwpck_require__(81201); +const issue_title_policy_1 = __nccwpck_require__(83179); +const issue_title_update_1 = __nccwpck_require__(9229); class IssueTitleRepository { constructor(issueTitleClient, issueMetadataRepository) { this.issueTitleClient = issueTitleClient; @@ -74660,7 +74660,7 @@ exports.IssueTitleRepository = IssueTitleRepository; /***/ }), -/***/ 60886: +/***/ 9229: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -74668,8 +74668,8 @@ exports.IssueTitleRepository = IssueTitleRepository; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.updateIssueTitle = updateIssueTitle; exports.withTitleUpdateLogging = withTitleUpdateLogging; -const logger_1 = __nccwpck_require__(50135); -const application_error_1 = __nccwpck_require__(2965); +const logger_1 = __nccwpck_require__(91151); +const application_error_1 = __nccwpck_require__(75999); async function updateIssueTitle(client, owner, repository, currentTitle, nextTitle, issueNumber, token) { if (nextTitle === currentTitle) return undefined; @@ -74690,16 +74690,16 @@ async function withTitleUpdateLogging(update) { /***/ }), -/***/ 55913: +/***/ 19118: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.IssueTypeAssignmentRepository = void 0; -const logger_1 = __nccwpck_require__(50135); -const issue_type_assignment_workflow_1 = __nccwpck_require__(82994); -const application_error_1 = __nccwpck_require__(2965); +const logger_1 = __nccwpck_require__(91151); +const issue_type_assignment_workflow_1 = __nccwpck_require__(40102); +const application_error_1 = __nccwpck_require__(75999); class IssueTypeAssignmentRepository { constructor(getIssueId, graphqlClient) { this.getIssueId = getIssueId; @@ -74721,7 +74721,7 @@ exports.IssueTypeAssignmentRepository = IssueTypeAssignmentRepository; /***/ }), -/***/ 82994: +/***/ 40102: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -74729,8 +74729,8 @@ exports.IssueTypeAssignmentRepository = IssueTypeAssignmentRepository; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.IssueTypeCreationSkippedError = void 0; exports.assignIssueType = assignIssueType; -const logger_1 = __nccwpck_require__(50135); -const application_error_1 = __nccwpck_require__(2965); +const logger_1 = __nccwpck_require__(91151); +const application_error_1 = __nccwpck_require__(75999); async function assignIssueType(getIssueId, client, owner, repository, issueNumber, selected, token) { (0, logger_1.logDebugInfo)(`Setting issue type for issue ${issueNumber} to ${selected.name}`); const issueId = await getIssueId(owner, repository, issueNumber, token); @@ -74801,7 +74801,7 @@ exports.IssueTypeCreationSkippedError = IssueTypeCreationSkippedError; /***/ }), -/***/ 39335: +/***/ 62726: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -74826,7 +74826,7 @@ function configuredIssueTypes(issueTypes) { /***/ }), -/***/ 98235: +/***/ 89634: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -74834,10 +74834,10 @@ function configuredIssueTypes(issueTypes) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.ensureIssueType = ensureIssueType; exports.ensureIssueTypes = ensureIssueTypes; -const logger_1 = __nccwpck_require__(50135); -const issue_type_configuration_1 = __nccwpck_require__(39335); -const issue_type_queries_1 = __nccwpck_require__(49841); -const application_error_1 = __nccwpck_require__(2965); +const logger_1 = __nccwpck_require__(91151); +const issue_type_configuration_1 = __nccwpck_require__(62726); +const issue_type_queries_1 = __nccwpck_require__(73192); +const application_error_1 = __nccwpck_require__(75999); async function ensureIssueType(client, owner, name, description, color) { try { const existingTypes = await (0, issue_type_queries_1.listIssueTypes)(client, owner); @@ -74885,7 +74885,7 @@ function ensureConfiguredIssueType(client, owner, configured) { /***/ }), -/***/ 49841: +/***/ 73192: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -74949,15 +74949,15 @@ async function createIssueType(client, owner, name, description, color) { /***/ }), -/***/ 87600: +/***/ 4858: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.IssueTypeRepository = void 0; -const issue_type_queries_1 = __nccwpck_require__(49841); -const issue_type_ensure_workflow_1 = __nccwpck_require__(98235); +const issue_type_queries_1 = __nccwpck_require__(73192); +const issue_type_ensure_workflow_1 = __nccwpck_require__(89634); class IssueTypeRepository { constructor(graphqlClient) { this.graphqlClient = graphqlClient; @@ -74972,7 +74972,7 @@ exports.IssueTypeRepository = IssueTypeRepository; /***/ }), -/***/ 70469: +/***/ 81201: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -75012,7 +75012,7 @@ function firstMatchingEmoji(rules, labels) { /***/ }), -/***/ 16208: +/***/ 83179: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -75063,16 +75063,16 @@ function normalizePullRequestSourceTitle(title, issueNumber) { /***/ }), -/***/ 54771: +/***/ 96711: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.ActorAuthorizationRepository = void 0; -const logger_1 = __nccwpck_require__(50135); -const actor_modification_policy_1 = __nccwpck_require__(74888); -const application_error_1 = __nccwpck_require__(2965); +const logger_1 = __nccwpck_require__(91151); +const actor_modification_policy_1 = __nccwpck_require__(34737); +const application_error_1 = __nccwpck_require__(75999); class ActorAuthorizationRepository { constructor(githubClient) { this.githubClient = githubClient; @@ -75143,7 +75143,7 @@ function logUnlessNotFound(error, operation) { /***/ }), -/***/ 85397: +/***/ 11454: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -75174,7 +75174,7 @@ exports.AuthenticatedUserRepository = AuthenticatedUserRepository; /***/ }), -/***/ 59939: +/***/ 84916: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -75182,7 +75182,7 @@ exports.AuthenticatedUserRepository = AuthenticatedUserRepository; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.listOrganizationTeams = listOrganizationTeams; exports.listOrganizationTeamMembers = listOrganizationTeamMembers; -const github_pagination_policy_1 = __nccwpck_require__(24347); +const github_pagination_policy_1 = __nccwpck_require__(44812); async function listOrganizationTeams(client, organization) { const teams = []; for await (const response of client.paginate.iterator(client.rest.teams.list, { @@ -75219,17 +75219,17 @@ function isRecord(value) { /***/ }), -/***/ 14553: +/***/ 845: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.OrganizationMembersRepository = void 0; -const logger_1 = __nccwpck_require__(50135); -const project_members_policy_1 = __nccwpck_require__(52983); -const organization_members_query_1 = __nccwpck_require__(59939); -const application_error_1 = __nccwpck_require__(2965); +const logger_1 = __nccwpck_require__(91151); +const project_members_policy_1 = __nccwpck_require__(41370); +const organization_members_query_1 = __nccwpck_require__(84916); +const application_error_1 = __nccwpck_require__(75999); class OrganizationMembersRepository { constructor(githubClient) { this.githubClient = githubClient; @@ -75277,14 +75277,14 @@ exports.OrganizationMembersRepository = OrganizationMembersRepository; /***/ }), -/***/ 76220: +/***/ 98952: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.ProjectBoardCommandRepository = void 0; -const project_board_field_update_1 = __nccwpck_require__(40805); +const project_board_field_update_1 = __nccwpck_require__(31603); /** GitHub GraphQL adapter for ProjectV2 field mutations. */ class ProjectBoardCommandRepository { constructor(projectBoardContentQueryPort, graphqlClient) { @@ -75307,16 +75307,16 @@ exports.ProjectBoardCommandRepository = ProjectBoardCommandRepository; /***/ }), -/***/ 10442: +/***/ 73579: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.getProjectBoardDetail = getProjectBoardDetail; -const logger_1 = __nccwpck_require__(50135); -const project_detail_1 = __nccwpck_require__(74263); -const application_error_1 = __nccwpck_require__(2965); +const logger_1 = __nccwpck_require__(91151); +const project_detail_1 = __nccwpck_require__(33428); +const application_error_1 = __nccwpck_require__(75999); /** Reads a ProjectV2 without leaking GitHub's owner-specific GraphQL shape. */ async function getProjectBoardDetail(ownerTypeClient, graphqlClient, projectId, owner, token) { try { @@ -75380,7 +75380,7 @@ function validateProjectId(projectId) { /***/ }), -/***/ 40805: +/***/ 31603: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -75388,9 +75388,9 @@ function validateProjectId(projectId) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.setProjectBoardSingleSelectField = setProjectBoardSingleSelectField; exports.setProjectBoardSingleSelectFieldByItemId = setProjectBoardSingleSelectFieldByItemId; -const project_board_provider_limits_1 = __nccwpck_require__(79506); -const logger_1 = __nccwpck_require__(50135); -const github_pagination_adapter_1 = __nccwpck_require__(64064); +const project_board_provider_limits_1 = __nccwpck_require__(96997); +const logger_1 = __nccwpck_require__(91151); +const github_pagination_adapter_1 = __nccwpck_require__(2761); const FIELD_QUERY = ` query($projectId: ID!, $after: String) { node(id: $projectId) { @@ -75530,7 +75530,7 @@ async function findProjectItem(client, project, itemId, fieldName) { /***/ }), -/***/ 59112: +/***/ 63552: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -75538,9 +75538,9 @@ async function findProjectItem(client, project, itemId, fieldName) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.getProjectItemId = getProjectItemId; exports.getProjectItemIdByContentId = getProjectItemIdByContentId; -const project_board_provider_limits_1 = __nccwpck_require__(79506); -const logger_1 = __nccwpck_require__(50135); -const github_pagination_adapter_1 = __nccwpck_require__(64064); +const project_board_provider_limits_1 = __nccwpck_require__(96997); +const logger_1 = __nccwpck_require__(91151); +const github_pagination_adapter_1 = __nccwpck_require__(2761); const CONTENT_QUERY = ` query($owner: String!, $repo: String!, $number: Int!) { repository(owner: $owner, name: $repo) { @@ -75610,14 +75610,14 @@ async function findProjectItemId(client, project, contentId) { /***/ }), -/***/ 5367: +/***/ 79285: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.ProjectBoardLinkRepository = void 0; -const logger_1 = __nccwpck_require__(50135); +const logger_1 = __nccwpck_require__(91151); class ProjectBoardLinkRepository { constructor(projectBoardQueryPort, graphqlClient) { this.projectBoardQueryPort = projectBoardQueryPort; @@ -75644,15 +75644,15 @@ exports.ProjectBoardLinkRepository = ProjectBoardLinkRepository; /***/ }), -/***/ 17726: +/***/ 97301: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.ProjectBoardQueryRepository = void 0; -const project_board_detail_query_1 = __nccwpck_require__(10442); -const project_board_item_query_1 = __nccwpck_require__(59112); +const project_board_detail_query_1 = __nccwpck_require__(73579); +const project_board_item_query_1 = __nccwpck_require__(63552); class ProjectBoardQueryRepository { constructor(ownerTypeClient, graphqlClient) { this.ownerTypeClient = ownerTypeClient; @@ -75667,7 +75667,7 @@ exports.ProjectBoardQueryRepository = ProjectBoardQueryRepository; /***/ }), -/***/ 52983: +/***/ 41370: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -75706,7 +75706,7 @@ function selectAvailableMembers(members, currentMembers, requested) { /***/ }), -/***/ 66202: +/***/ 55165: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -75737,17 +75737,17 @@ exports.BugbotPullRequestRepository = BugbotPullRequestRepository; /***/ }), -/***/ 65797: +/***/ 71564: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.PullRequestChangesRepository = void 0; -const logger_1 = __nccwpck_require__(50135); -const pull_request_review_errors_1 = __nccwpck_require__(81504); -const github_pagination_policy_1 = __nccwpck_require__(24347); -const application_error_1 = __nccwpck_require__(2965); +const logger_1 = __nccwpck_require__(91151); +const pull_request_review_errors_1 = __nccwpck_require__(46445); +const github_pagination_policy_1 = __nccwpck_require__(44812); +const application_error_1 = __nccwpck_require__(75999); class PullRequestChangesRepository { constructor(githubClient) { this.githubClient = githubClient; @@ -75917,15 +75917,15 @@ exports.PullRequestChangesRepository = PullRequestChangesRepository; /***/ }), -/***/ 57177: +/***/ 24189: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.PullRequestLifecycleRepository = void 0; -const logger_1 = __nccwpck_require__(50135); -const application_error_1 = __nccwpck_require__(2965); +const logger_1 = __nccwpck_require__(91151); +const application_error_1 = __nccwpck_require__(75999); class PullRequestLifecycleRepository { constructor(githubClient) { this.githubClient = githubClient; @@ -76109,15 +76109,15 @@ function toBugbotPullRequestIdentity(value) { /***/ }), -/***/ 48987: +/***/ 17120: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.PullRequestReviewCommentCommandRepository = void 0; -const pull_request_review_errors_1 = __nccwpck_require__(81504); -const github_pagination_policy_1 = __nccwpck_require__(24347); +const pull_request_review_errors_1 = __nccwpck_require__(46445); +const github_pagination_policy_1 = __nccwpck_require__(44812); class PullRequestReviewCommentCommandRepository { constructor(createClient, graphqlClient, queryClient) { this.createClient = createClient; @@ -76233,15 +76233,15 @@ exports.PullRequestReviewCommentCommandRepository = PullRequestReviewCommentComm /***/ }), -/***/ 31371: +/***/ 44085: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.PullRequestReviewCommentQueryRepository = void 0; -const pull_request_review_errors_1 = __nccwpck_require__(81504); -const github_pagination_policy_1 = __nccwpck_require__(24347); +const pull_request_review_errors_1 = __nccwpck_require__(46445); +const github_pagination_policy_1 = __nccwpck_require__(44812); function toReviewComment(comment) { if (typeof comment.node_id !== "string" || comment.node_id.length === 0) { throw new Error("Review comment identity is unavailable."); @@ -76376,14 +76376,14 @@ exports.PullRequestReviewCommentQueryRepository = PullRequestReviewCommentQueryR /***/ }), -/***/ 20491: +/***/ 2307: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.findPullRequestReviewThread = findPullRequestReviewThread; -const pull_request_review_errors_1 = __nccwpck_require__(81504); +const pull_request_review_errors_1 = __nccwpck_require__(46445); const THREADS_QUERY = ` query ($owner: String!, $repo: String!, $prNumber: Int!, $threadsAfter: String) { repository(owner: $owner, name: $repo) { @@ -76477,16 +76477,16 @@ function nextConnectionCursor(pageInfo, seenCursors) { /***/ }), -/***/ 64125: +/***/ 23314: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.PullRequestReviewThreadRepository = void 0; -const logger_1 = __nccwpck_require__(50135); -const pull_request_review_errors_1 = __nccwpck_require__(81504); -const pull_request_review_thread_locator_1 = __nccwpck_require__(20491); +const logger_1 = __nccwpck_require__(91151); +const pull_request_review_errors_1 = __nccwpck_require__(46445); +const pull_request_review_thread_locator_1 = __nccwpck_require__(2307); /** GitHub GraphQL adapter for locating and resolving a pull-request review thread. */ class PullRequestReviewThreadRepository { constructor(githubClient) { @@ -76658,15 +76658,15 @@ exports.PullRequestReviewThreadRepository = PullRequestReviewThreadRepository; /***/ }), -/***/ 45908: +/***/ 13779: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.PullRequestReviewerRepository = void 0; -const pull_request_review_errors_1 = __nccwpck_require__(81504); -const github_pagination_policy_1 = __nccwpck_require__(24347); +const pull_request_review_errors_1 = __nccwpck_require__(46445); +const github_pagination_policy_1 = __nccwpck_require__(44812); const COMPLETED_REVIEW_STATES = new Set([ "APPROVED", "CHANGES_REQUESTED", @@ -76752,15 +76752,15 @@ exports.PullRequestReviewerRepository = PullRequestReviewerRepository; /***/ }), -/***/ 38818: +/***/ 96578: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.RepositoryDefaultBranchRepository = void 0; -const logger_1 = __nccwpck_require__(50135); -const application_error_1 = __nccwpck_require__(2965); +const logger_1 = __nccwpck_require__(91151); +const application_error_1 = __nccwpck_require__(75999); class RepositoryDefaultBranchRepository { constructor(githubClient) { this.githubClient = githubClient; @@ -76783,22 +76783,22 @@ exports.RepositoryDefaultBranchRepository = RepositoryDefaultBranchRepository; /***/ }), -/***/ 9204: +/***/ 42075: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.RepositoryReleasePublicationRepository = void 0; -const logger_1 = __nccwpck_require__(50135); -const release_content_policy_1 = __nccwpck_require__(87013); -const release_transition_policy_1 = __nccwpck_require__(85551); -const release_tag_policy_1 = __nccwpck_require__(61708); -const repository_release_query_1 = __nccwpck_require__(37052); -const application_error_1 = __nccwpck_require__(2965); -const github_error_policy_1 = __nccwpck_require__(26189); -const repository_tag_query_1 = __nccwpck_require__(71677); -const deployment_publication_1 = __nccwpck_require__(32982); +const logger_1 = __nccwpck_require__(91151); +const release_content_policy_1 = __nccwpck_require__(56818); +const release_transition_policy_1 = __nccwpck_require__(27673); +const release_tag_policy_1 = __nccwpck_require__(62748); +const repository_release_query_1 = __nccwpck_require__(10766); +const application_error_1 = __nccwpck_require__(75999); +const github_error_policy_1 = __nccwpck_require__(58791); +const repository_tag_query_1 = __nccwpck_require__(46772); +const deployment_publication_1 = __nccwpck_require__(6912); class RepositoryReleasePublicationRepository { constructor(githubClient) { this.githubClient = githubClient; @@ -76959,7 +76959,7 @@ function verifiedReleaseUrl(release, tag, expectedName, expectedBody, operationI /***/ }), -/***/ 37052: +/***/ 10766: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -76981,7 +76981,7 @@ async function listRepositoryReleases(client, owner, repository) { /***/ }), -/***/ 71677: +/***/ 46772: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -76989,8 +76989,8 @@ async function listRepositoryReleases(client, owner, repository) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.findRepositoryTag = findRepositoryTag; exports.getRepositoryTagSha = getRepositoryTagSha; -const github_error_policy_1 = __nccwpck_require__(26189); -const release_tag_policy_1 = __nccwpck_require__(61708); +const github_error_policy_1 = __nccwpck_require__(58791); +const release_tag_policy_1 = __nccwpck_require__(62748); async function findRepositoryTag(client, owner, repository, tag) { try { const { data } = await client.rest.git.getRef({ owner, repo: repository, ref: (0, release_tag_policy_1.tagReference)(tag) }); @@ -77019,17 +77019,17 @@ async function getRepositoryTagSha(client, owner, repository, tag) { /***/ }), -/***/ 83842: +/***/ 58717: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.RepositoryTagRepository = void 0; -const logger_1 = __nccwpck_require__(50135); -const release_tag_policy_1 = __nccwpck_require__(61708); -const repository_tag_query_1 = __nccwpck_require__(71677); -const application_error_1 = __nccwpck_require__(2965); +const logger_1 = __nccwpck_require__(91151); +const release_tag_policy_1 = __nccwpck_require__(62748); +const repository_tag_query_1 = __nccwpck_require__(46772); +const application_error_1 = __nccwpck_require__(75999); class RepositoryTagRepository { constructor(githubClient) { this.githubClient = githubClient; @@ -77129,7 +77129,7 @@ exports.RepositoryTagRepository = RepositoryTagRepository; /***/ }), -/***/ 87013: +/***/ 56818: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -77153,7 +77153,7 @@ function hasReleaseContent(release) { /***/ }), -/***/ 61708: +/***/ 62748: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -77175,7 +77175,7 @@ function releaseName(version, title) { /***/ }), -/***/ 85551: +/***/ 27673: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -77193,7 +77193,7 @@ function releaseIdAsString(id) { /***/ }), -/***/ 73307: +/***/ 28493: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -77204,10 +77204,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.RepositorySecretsCommandRepository = exports.RepositoryVariablesCommandRepository = exports.SetupRemoteConfigurationQueryRepository = exports.RepositoryVariablesQueryRepository = exports.RepositorySecretNamesQueryRepository = void 0; exports.encryptSecret = encryptSecret; -const setup_workflow_catalog_1 = __nccwpck_require__(37008); -const github_error_policy_1 = __nccwpck_require__(26189); -const credential_health_workflow_visibility_1 = __nccwpck_require__(85071); -const tweetnacl_1 = __importDefault(__nccwpck_require__(37124)); +const setup_workflow_catalog_1 = __nccwpck_require__(24596); +const github_error_policy_1 = __nccwpck_require__(58791); +const credential_health_workflow_visibility_1 = __nccwpck_require__(57628); +const tweetnacl_1 = __importDefault(__nccwpck_require__(24258)); const node_crypto_1 = __nccwpck_require__(6005); class GithubActionsResourceTransport { constructor(githubClient) { @@ -77572,15 +77572,15 @@ function encryptSecret(value, base64PublicKey) { /***/ }), -/***/ 42351: +/***/ 40941: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.ActivePreviousWorkflowRunsRepository = void 0; -const workflow_status_1 = __nccwpck_require__(37003); -const workflow_runs_retry_1 = __nccwpck_require__(11946); +const workflow_status_1 = __nccwpck_require__(1462); +const workflow_runs_retry_1 = __nccwpck_require__(86434); const NO_OP_DELAY_PORT = { wait: async () => undefined }; const SYSTEM_CLOCK = { nowMilliseconds: () => Date.now() }; const SYSTEM_RANDOM = { next: () => Math.random() }; @@ -77657,7 +77657,7 @@ function isActivePreviousRun(run, query) { /***/ }), -/***/ 70051: +/***/ 29509: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -77684,7 +77684,7 @@ exports.WorkflowDispatchRepository = WorkflowDispatchRepository; /***/ }), -/***/ 11946: +/***/ 86434: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -77692,7 +77692,7 @@ exports.WorkflowDispatchRepository = WorkflowDispatchRepository; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.WorkflowQueueDeadlineError = exports.WORKFLOW_RUNS_RETRY_POLICY = void 0; exports.withWorkflowRunsRetry = withWorkflowRunsRetry; -const workflow_queue_policy_1 = __nccwpck_require__(97549); +const workflow_queue_policy_1 = __nccwpck_require__(43193); exports.WORKFLOW_RUNS_RETRY_POLICY = { maximumAttempts: 5, rateLimitMaximumAttempts: 5, @@ -77848,7 +77848,7 @@ function firstNumericValue(...values) { /***/ }), -/***/ 37003: +/***/ 1462: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -77878,7 +77878,7 @@ exports.WORKFLOW_ACTIVE_STATUSES = [ /***/ }), -/***/ 95407: +/***/ 89040: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -77901,7 +77901,7 @@ function isAgentConfigurationReady(configuration) { /***/ }), -/***/ 25901: +/***/ 12253: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -77919,7 +77919,7 @@ function workspaceModeForCapability(capability) { /***/ }), -/***/ 7465: +/***/ 51114: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -77976,7 +77976,7 @@ function escapeRegExp(value) { /***/ }), -/***/ 32721: +/***/ 14712: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -77985,7 +77985,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.selectCanonicalBugbotPullRequest = selectCanonicalBugbotPullRequest; exports.summarizeBugbotCoverage = summarizeBugbotCoverage; exports.completeBugbotSourceCoverage = completeBugbotSourceCoverage; -const git_object_id_1 = __nccwpck_require__(36924); +const git_object_id_1 = __nccwpck_require__(88623); function selectCanonicalBugbotPullRequest(target, candidates, source) { if (source === "exact-head" && candidates.length === 0) return { kind: "none" }; @@ -78069,7 +78069,7 @@ function matchesConstrainedHead(target, candidate) { /***/ }), -/***/ 82048: +/***/ 31011: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -78125,7 +78125,7 @@ function identitiesAreCompatible(existing, finding) { /***/ }), -/***/ 657: +/***/ 91853: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -78200,7 +78200,7 @@ function fnv1a(value) { /***/ }), -/***/ 22551: +/***/ 1811: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -78251,7 +78251,7 @@ function invalid(reason) { /***/ }), -/***/ 19249: +/***/ 3994: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -78320,14 +78320,14 @@ function resolveBugbotReviewEffort(configured, complexity) { /***/ }), -/***/ 23272: +/***/ 80859: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildBugbotReviewProjection = buildBugbotReviewProjection; -const review_state_1 = __nccwpck_require__(17271); +const review_state_1 = __nccwpck_require__(79200); function buildBugbotReviewProjection(input) { const findings = [...input.findings].sort((left, right) => left.id.localeCompare(right.id)); const counts = (0, review_state_1.countBugbotFindingStates)(findings.map((finding) => finding.state)); @@ -78382,7 +78382,7 @@ function stableDigest(value) { /***/ }), -/***/ 17271: +/***/ 79200: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -78456,7 +78456,7 @@ function countActionableBugbotFindings(counts) { /***/ }), -/***/ 30717: +/***/ 27089: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -78524,7 +78524,7 @@ function isNewerCliVersion(installedVersion, publishedVersion) { /***/ }), -/***/ 2324: +/***/ 77454: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -78547,7 +78547,7 @@ function hasVisibleCommentContent(value) { /***/ }), -/***/ 87134: +/***/ 11771: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -78628,7 +78628,7 @@ function parseCopilotCommand(raw) { /***/ }), -/***/ 81916: +/***/ 86819: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -78636,7 +78636,7 @@ function parseCopilotCommand(raw) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.containsBotMention = containsBotMention; exports.isCopilotCommentRequest = isCopilotCommentRequest; -const copilot_command_1 = __nccwpck_require__(87134); +const copilot_command_1 = __nccwpck_require__(11771); /** Matches GitHub usernames case-insensitively without matching a larger username. */ function containsBotMention(commentBody, tokenUser) { const normalizedUser = tokenUser.trim().replace(/^@/u, ''); @@ -78657,7 +78657,7 @@ function isCopilotCommentRequest(commentBody, botLogin) { /***/ }), -/***/ 4227: +/***/ 72418: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -78782,7 +78782,7 @@ function lifecycleStateFromLabels(currentLabels, labels = exports.DEFAULT_COPILO /***/ }), -/***/ 5664: +/***/ 22495: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -78899,12 +78899,12 @@ function parseDeploymentEnum(value, allowed, fallback) { ? { value: normalized, valid: true } : { value: fallback, valid: false }; } -const merge_queue_readiness_1 = __nccwpck_require__(36637); +const merge_queue_readiness_1 = __nccwpck_require__(12515); /***/ }), -/***/ 17176: +/***/ 92730: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -78918,8 +78918,8 @@ exports.completeReconciliationTarget = completeReconciliationTarget; exports.sanitizeDeploymentMessage = sanitizeDeploymentMessage; exports.isDeploymentOperationSnapshot = isDeploymentOperationSnapshot; exports.requiredDeploymentFailure = requiredDeploymentFailure; -const deployment_configuration_1 = __nccwpck_require__(5664); -const locale_1 = __nccwpck_require__(64552); +const deployment_configuration_1 = __nccwpck_require__(22495); +const locale_1 = __nccwpck_require__(15386); exports.DEPLOYMENT_PHASES = [ "preparing", "promotion_pr_pending", @@ -79127,7 +79127,7 @@ function hasOnlyKeys(value, allowed) { /***/ }), -/***/ 32982: +/***/ 6912: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -79160,7 +79160,7 @@ function parseDeploymentPublicationMarker(body) { /***/ }), -/***/ 32481: +/***/ 72369: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -79170,7 +79170,7 @@ exports.readDeploymentOperationState = readDeploymentOperationState; exports.deploymentStateFence = deploymentStateFence; exports.nextDeploymentRevision = nextDeploymentRevision; exports.decideDeploymentStateSave = decideDeploymentStateSave; -const deployment_operation_1 = __nccwpck_require__(17176); +const deployment_operation_1 = __nccwpck_require__(92730); function readDeploymentOperationState(value) { if (value === undefined || value === null) return { kind: "absent" }; @@ -79252,7 +79252,7 @@ function isRecord(value) { /***/ }), -/***/ 36924: +/***/ 88623: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -79273,7 +79273,7 @@ function canonicalGitObjectId(value) { /***/ }), -/***/ 77025: +/***/ 21486: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -79291,7 +79291,7 @@ function isPullRequestConversationComment(input) { /***/ }), -/***/ 75905: +/***/ 35793: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -79328,7 +79328,7 @@ function publicationTargetToken(target) { /***/ }), -/***/ 19596: +/***/ 84403: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -79344,7 +79344,7 @@ function githubUsersMatch(left, right) { /***/ }), -/***/ 52620: +/***/ 77001: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -79415,7 +79415,7 @@ function hasOnlyKeys(value, allowed) { /***/ }), -/***/ 7703: +/***/ 38572: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -79473,7 +79473,7 @@ function normalize(value) { /***/ }), -/***/ 20953: +/***/ 90332: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -79514,7 +79514,7 @@ function branchIsReady(input) { /***/ }), -/***/ 62721: +/***/ 26744: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -79699,7 +79699,7 @@ function isAutomaticOrVersion(value) { /***/ }), -/***/ 84598: +/***/ 77734: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -79774,7 +79774,7 @@ function decideIssueWorkflowRuntime(context) { /***/ }), -/***/ 64552: +/***/ 15386: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -79878,7 +79878,7 @@ function optionalLocale(value) { /***/ }), -/***/ 7975: +/***/ 95914: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -79910,7 +79910,7 @@ function isSafeOperationId(value) { /***/ }), -/***/ 36637: +/***/ 12515: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -80047,7 +80047,7 @@ function hasUnsafeControlCharacter(value) { /***/ }), -/***/ 5313: +/***/ 27097: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -80060,7 +80060,7 @@ exports.validateDynamicCatalogMessages = validateDynamicCatalogMessages; exports.renderCatalogMessage = renderCatalogMessage; exports.catalogPlaceholders = catalogPlaceholders; exports.catalogPluralCategories = catalogPluralCategories; -const locale_1 = __nccwpck_require__(64552); +const locale_1 = __nccwpck_require__(15386); exports.MESSAGE_CATALOG_VERSION = '3'; exports.CATALOG_PLURAL_CATEGORIES = Object.freeze([ 'zero', @@ -80217,7 +80217,7 @@ function placeholdersForTemplate(value) { /***/ }), -/***/ 45613: +/***/ 19879: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -80247,7 +80247,7 @@ function parsePositiveSafeInteger(value) { /***/ }), -/***/ 54078: +/***/ 34730: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -80439,7 +80439,7 @@ function isRecord(value) { /***/ }), -/***/ 53553: +/***/ 98820: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -80619,7 +80619,7 @@ function enumArray(value, allowed, label, max, errors) { /***/ }), -/***/ 25623: +/***/ 45315: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -80678,7 +80678,7 @@ function shouldAutomaticallyUpdatePullRequestDescription(mode) { /***/ }), -/***/ 98209: +/***/ 47122: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -80701,7 +80701,7 @@ function redactSensitiveText(value) { /***/ }), -/***/ 12334: +/***/ 67057: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -80812,7 +80812,7 @@ function normalizeOrigin(origin) { /***/ }), -/***/ 56189: +/***/ 9512: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -80834,7 +80834,7 @@ function renderApprovalObserverWorkflow(template, policy) { /***/ }), -/***/ 37008: +/***/ 24596: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -80880,7 +80880,7 @@ function featureEnabled(feature, features) { /***/ }), -/***/ 73447: +/***/ 11800: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -80892,12 +80892,12 @@ const node_child_process_1 = __nccwpck_require__(17718); const node_fs_1 = __nccwpck_require__(87561); const node_os_1 = __nccwpck_require__(70612); const node_path_1 = __nccwpck_require__(49411); -const agent_execution_policy_dispatcher_1 = __nccwpck_require__(3341); -const agent_execution_plan_1 = __nccwpck_require__(25901); -const agent_cli_contracts_1 = __nccwpck_require__(99483); -const agent_executable_policy_1 = __nccwpck_require__(53773); -const agent_authentication_1 = __nccwpck_require__(67354); -const agent_runtime_manifest_1 = __nccwpck_require__(18890); +const agent_execution_policy_dispatcher_1 = __nccwpck_require__(25690); +const agent_execution_plan_1 = __nccwpck_require__(12253); +const agent_cli_contracts_1 = __nccwpck_require__(48254); +const agent_executable_policy_1 = __nccwpck_require__(12570); +const agent_authentication_1 = __nccwpck_require__(51371); +const agent_runtime_manifest_1 = __nccwpck_require__(57104); const DEFAULT_SYSTEM = { resolveExecutable: resolveExecutablePath, readVersion(executable, environment) { @@ -81086,7 +81086,7 @@ function definedEnvironment(environment) { /***/ }), -/***/ 18890: +/***/ 57104: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -81130,7 +81130,7 @@ function assertInstalledAgentRuntimeVersion(provider, output) { /***/ }), -/***/ 85401: +/***/ 51520: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -81174,14 +81174,14 @@ exports.BoundBugbotGitMutationAdapter = BoundBugbotGitMutationAdapter; /***/ }), -/***/ 51697: +/***/ 81849: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.BranchSyncWorkspaceAdapter = void 0; -const workspace_changes_1 = __nccwpck_require__(51578); +const workspace_changes_1 = __nccwpck_require__(93370); /** Owns Git's merge state while keeping credentials confined to fetch/push subprocesses. */ class BranchSyncWorkspaceAdapter { constructor(git) { @@ -81335,7 +81335,7 @@ function invalid(reason) { /***/ }), -/***/ 83605: +/***/ 76182: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -81347,7 +81347,7 @@ exports.COPILOT_PACKAGE_NAME = '@vypdev/copilot'; /***/ }), -/***/ 21570: +/***/ 62007: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -81358,7 +81358,7 @@ exports.resolveUpdateCheckCachePath = resolveUpdateCheckCachePath; const node_fs_1 = __nccwpck_require__(87561); const node_os_1 = __nccwpck_require__(70612); const node_path_1 = __nccwpck_require__(49411); -const copilot_package_1 = __nccwpck_require__(83605); +const copilot_package_1 = __nccwpck_require__(76182); exports.NPM_REGISTRY_URL = `https://registry.npmjs.org/${encodeURIComponent(copilot_package_1.COPILOT_PACKAGE_NAME)}`; exports.UPDATE_CHECK_CACHE_TTL_MS = 24 * 60 * 60 * 1000; exports.UPDATE_CHECK_TIMEOUT_MS = 1500; @@ -81461,7 +81461,7 @@ exports.NpmCliUpdateCheckAdapter = NpmCliUpdateCheckAdapter; /***/ }), -/***/ 28021: +/***/ 64975: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -81470,7 +81470,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.PnpmCliUpgradeAdapter = void 0; exports.resolvePnpmExecutable = resolvePnpmExecutable; const node_child_process_1 = __nccwpck_require__(17718); -const copilot_package_1 = __nccwpck_require__(83605); +const copilot_package_1 = __nccwpck_require__(76182); function resolvePnpmExecutable(platform = process.platform) { return platform === 'win32' ? 'pnpm.cmd' : 'pnpm'; } @@ -81513,15 +81513,15 @@ exports.PnpmCliUpgradeAdapter = PnpmCliUpgradeAdapter; /***/ }), -/***/ 43758: +/***/ 233: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createActorAuthorizationRepository = createActorAuthorizationRepository; -const github_identity_client_factory_1 = __nccwpck_require__(17930); -const actor_authorization_repository_1 = __nccwpck_require__(54771); +const github_identity_client_factory_1 = __nccwpck_require__(93081); +const actor_authorization_repository_1 = __nccwpck_require__(96711); function createActorAuthorizationRepository() { return new actor_authorization_repository_1.ActorAuthorizationRepository((0, github_identity_client_factory_1.createActorAuthorizationClient)()); } @@ -81529,16 +81529,16 @@ function createActorAuthorizationRepository() { /***/ }), -/***/ 11013: +/***/ 94253: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createSynchronizeAgentActivityUseCase = createSynchronizeAgentActivityUseCase; -const synchronize_agent_activity_use_case_1 = __nccwpck_require__(83269); -const issue_labels_composition_root_1 = __nccwpck_require__(94185); -const lifecycle_capability_port_binding_1 = __nccwpck_require__(28822); +const synchronize_agent_activity_use_case_1 = __nccwpck_require__(44880); +const issue_labels_composition_root_1 = __nccwpck_require__(34780); +const lifecycle_capability_port_binding_1 = __nccwpck_require__(85785); function createSynchronizeAgentActivityUseCase(binding) { return new synchronize_agent_activity_use_case_1.SynchronizeAgentActivityUseCase((0, lifecycle_capability_port_binding_1.bindIssueLabels)((0, issue_labels_composition_root_1.createIssueLabelRepository)(), binding)); } @@ -81546,7 +81546,7 @@ function createSynchronizeAgentActivityUseCase(binding) { /***/ }), -/***/ 7753: +/***/ 85079: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -81555,12 +81555,12 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createFindingsQueryPort = createFindingsQueryPort; exports.createFixerQueryPort = createFixerQueryPort; exports.createLanguageQueryPort = createLanguageQueryPort; -const agent_cli_client_1 = __nccwpck_require__(64596); -const agent_execution_planner_1 = __nccwpck_require__(73447); -const logger_agent_execution_observer_adapter_1 = __nccwpck_require__(36337); -const findings_agent_adapter_1 = __nccwpck_require__(94109); -const fixer_agent_adapter_1 = __nccwpck_require__(99972); -const language_agent_adapter_1 = __nccwpck_require__(76206); +const agent_cli_client_1 = __nccwpck_require__(68570); +const agent_execution_planner_1 = __nccwpck_require__(11800); +const logger_agent_execution_observer_adapter_1 = __nccwpck_require__(59844); +const findings_agent_adapter_1 = __nccwpck_require__(27725); +const fixer_agent_adapter_1 = __nccwpck_require__(62259); +const language_agent_adapter_1 = __nccwpck_require__(10573); function defaultInfrastructure() { return { cli: new agent_cli_client_1.AgentCliClient(new agent_execution_planner_1.AgentExecutionPlanner(), new logger_agent_execution_observer_adapter_1.LoggerAgentExecutionObserverAdapter()), @@ -81579,15 +81579,15 @@ function createLanguageQueryPort(infrastructure = defaultInfrastructure()) { /***/ }), -/***/ 55549: +/***/ 33885: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createAuthenticatedUserCompositionRoot = createAuthenticatedUserCompositionRoot; -const github_identity_client_factory_1 = __nccwpck_require__(17930); -const authenticated_user_repository_1 = __nccwpck_require__(85397); +const github_identity_client_factory_1 = __nccwpck_require__(93081); +const authenticated_user_repository_1 = __nccwpck_require__(11454); function createAuthenticatedUserCompositionRoot() { return new authenticated_user_repository_1.AuthenticatedUserRepository((0, github_identity_client_factory_1.createAuthenticatedUserClient)()); } @@ -81595,29 +81595,29 @@ function createAuthenticatedUserCompositionRoot() { /***/ }), -/***/ 40733: +/***/ 67395: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createBugbotCompositionRoot = createBugbotCompositionRoot; -const github_issue_client_factory_1 = __nccwpck_require__(15161); -const github_project_client_factory_1 = __nccwpck_require__(63165); -const github_pull_request_client_factory_1 = __nccwpck_require__(46236); -const bugbot_issue_repository_1 = __nccwpck_require__(15589); -const issue_content_repository_1 = __nccwpck_require__(43338); -const bugbot_issue_comment_query_repository_1 = __nccwpck_require__(85949); -const bugbot_pull_request_repository_1 = __nccwpck_require__(66202); -const pull_request_changes_repository_1 = __nccwpck_require__(65797); -const pull_request_lifecycle_repository_1 = __nccwpck_require__(57177); -const pull_request_review_comment_command_repository_1 = __nccwpck_require__(48987); -const pull_request_review_comment_query_repository_1 = __nccwpck_require__(31371); -const pull_request_review_thread_repository_1 = __nccwpck_require__(64125); -const workspace_bugbot_rules_repository_1 = __nccwpck_require__(83645); -const logger_bugbot_telemetry_adapter_1 = __nccwpck_require__(55375); -const github_bugbot_review_navigation_adapter_1 = __nccwpck_require__(33451); -const bugbot_scm_port_factory_1 = __nccwpck_require__(29985); +const github_issue_client_factory_1 = __nccwpck_require__(95883); +const github_project_client_factory_1 = __nccwpck_require__(23691); +const github_pull_request_client_factory_1 = __nccwpck_require__(9068); +const bugbot_issue_repository_1 = __nccwpck_require__(82726); +const issue_content_repository_1 = __nccwpck_require__(2313); +const bugbot_issue_comment_query_repository_1 = __nccwpck_require__(88593); +const bugbot_pull_request_repository_1 = __nccwpck_require__(55165); +const pull_request_changes_repository_1 = __nccwpck_require__(71564); +const pull_request_lifecycle_repository_1 = __nccwpck_require__(24189); +const pull_request_review_comment_command_repository_1 = __nccwpck_require__(17120); +const pull_request_review_comment_query_repository_1 = __nccwpck_require__(44085); +const pull_request_review_thread_repository_1 = __nccwpck_require__(23314); +const workspace_bugbot_rules_repository_1 = __nccwpck_require__(50183); +const logger_bugbot_telemetry_adapter_1 = __nccwpck_require__(34685); +const github_bugbot_review_navigation_adapter_1 = __nccwpck_require__(19008); +const bugbot_scm_port_factory_1 = __nccwpck_require__(19937); function createBugbotCompositionRoot(binding) { const issueContent = new issue_content_repository_1.IssueContentRepository((0, github_issue_client_factory_1.createIssueContentClient)()); const issue = new bugbot_issue_repository_1.BugbotIssueRepository(issueContent); @@ -81644,7 +81644,7 @@ function createBugbotCompositionRoot(binding) { /***/ }), -/***/ 29985: +/***/ 19937: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -81726,28 +81726,28 @@ exports.BugbotScmPortFactory = BugbotScmPortFactory; /***/ }), -/***/ 25949: +/***/ 21531: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createCheckProgressCompositionRoot = createCheckProgressCompositionRoot; -const github_branch_client_factory_1 = __nccwpck_require__(32112); -const github_issue_client_factory_1 = __nccwpck_require__(15161); -const github_pull_request_client_factory_1 = __nccwpck_require__(46236); -const check_progress_use_case_1 = __nccwpck_require__(21113); -const agent_capability_composition_root_1 = __nccwpck_require__(7753); -const issue_content_repository_1 = __nccwpck_require__(43338); -const issue_label_repository_1 = __nccwpck_require__(27825); -const issue_progress_label_repository_1 = __nccwpck_require__(58763); -const issue_progress_tracking_repository_1 = __nccwpck_require__(64844); -const branch_lifecycle_repository_1 = __nccwpck_require__(88216); -const pull_request_lifecycle_repository_1 = __nccwpck_require__(57177); -const github_publication_source_repository_1 = __nccwpck_require__(97590); -const shared_capability_port_binding_1 = __nccwpck_require__(20918); -const lifecycle_capability_port_binding_1 = __nccwpck_require__(28822); -const push_single_action_capability_port_binding_1 = __nccwpck_require__(54110); +const github_branch_client_factory_1 = __nccwpck_require__(30144); +const github_issue_client_factory_1 = __nccwpck_require__(95883); +const github_pull_request_client_factory_1 = __nccwpck_require__(9068); +const check_progress_use_case_1 = __nccwpck_require__(41601); +const agent_capability_composition_root_1 = __nccwpck_require__(85079); +const issue_content_repository_1 = __nccwpck_require__(2313); +const issue_label_repository_1 = __nccwpck_require__(45725); +const issue_progress_label_repository_1 = __nccwpck_require__(66610); +const issue_progress_tracking_repository_1 = __nccwpck_require__(26674); +const branch_lifecycle_repository_1 = __nccwpck_require__(19504); +const pull_request_lifecycle_repository_1 = __nccwpck_require__(24189); +const github_publication_source_repository_1 = __nccwpck_require__(52644); +const shared_capability_port_binding_1 = __nccwpck_require__(47399); +const lifecycle_capability_port_binding_1 = __nccwpck_require__(85785); +const push_single_action_capability_port_binding_1 = __nccwpck_require__(49417); function createCheckProgressCompositionRoot(binding) { const labels = new issue_label_repository_1.IssueLabelRepository((0, github_issue_client_factory_1.createIssueLabelsClient)()); const content = new issue_content_repository_1.IssueContentRepository((0, github_issue_client_factory_1.createIssueContentClient)()); @@ -81757,15 +81757,15 @@ function createCheckProgressCompositionRoot(binding) { /***/ }), -/***/ 89267: +/***/ 78998: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createCliUpdateCheckUseCase = createCliUpdateCheckUseCase; -const check_cli_update_use_case_1 = __nccwpck_require__(11677); -const npm_cli_update_check_adapter_1 = __nccwpck_require__(21570); +const check_cli_update_use_case_1 = __nccwpck_require__(55721); +const npm_cli_update_check_adapter_1 = __nccwpck_require__(62007); function createCliUpdateCheckUseCase() { return new check_cli_update_use_case_1.CheckCliUpdateUseCase(new npm_cli_update_check_adapter_1.NpmCliUpdateCheckAdapter()); } @@ -81773,15 +81773,15 @@ function createCliUpdateCheckUseCase() { /***/ }), -/***/ 91561: +/***/ 74142: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createUpgradeCliUseCase = createUpgradeCliUseCase; -const upgrade_cli_use_case_1 = __nccwpck_require__(45773); -const pnpm_cli_upgrade_adapter_1 = __nccwpck_require__(28021); +const upgrade_cli_use_case_1 = __nccwpck_require__(45762); +const pnpm_cli_upgrade_adapter_1 = __nccwpck_require__(64975); function createUpgradeCliUseCase(cliUpgradePort = new pnpm_cli_upgrade_adapter_1.PnpmCliUpgradeAdapter()) { return new upgrade_cli_use_case_1.UpgradeCliUseCase(cliUpgradePort); } @@ -81789,19 +81789,19 @@ function createUpgradeCliUseCase(cliUpgradePort = new pnpm_cli_upgrade_adapter_1 /***/ }), -/***/ 84724: +/***/ 98313: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createExecutionIssueSetupCompositionRoot = createExecutionIssueSetupCompositionRoot; -const github_issue_client_factory_1 = __nccwpck_require__(15161); -const github_project_client_factory_1 = __nccwpck_require__(63165); -const execution_issue_setup_repository_1 = __nccwpck_require__(32004); -const issue_content_repository_1 = __nccwpck_require__(43338); -const issue_label_repository_1 = __nccwpck_require__(27825); -const issue_metadata_repository_1 = __nccwpck_require__(37674); +const github_issue_client_factory_1 = __nccwpck_require__(95883); +const github_project_client_factory_1 = __nccwpck_require__(23691); +const execution_issue_setup_repository_1 = __nccwpck_require__(91153); +const issue_content_repository_1 = __nccwpck_require__(2313); +const issue_label_repository_1 = __nccwpck_require__(45725); +const issue_metadata_repository_1 = __nccwpck_require__(11333); function createExecutionIssueSetupCompositionRoot() { return new execution_issue_setup_repository_1.ExecutionIssueSetupRepository(new issue_metadata_repository_1.IssueMetadataRepository((0, github_issue_client_factory_1.createIssueMetadataClient)(), (0, github_project_client_factory_1.createGraphqlTransportClient)()), new issue_content_repository_1.IssueContentRepository((0, github_issue_client_factory_1.createIssueContentClient)()), new issue_label_repository_1.IssueLabelRepository((0, github_issue_client_factory_1.createIssueLabelsClient)())); } @@ -81809,21 +81809,21 @@ function createExecutionIssueSetupCompositionRoot() { /***/ }), -/***/ 71774: +/***/ 83965: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createSetupExecutionUseCase = createSetupExecutionUseCase; -const execution_branch_version_resolver_1 = __nccwpck_require__(83966); -const setup_execution_use_case_1 = __nccwpck_require__(51778); -const get_hotfix_version_use_case_1 = __nccwpck_require__(85276); -const get_release_type_use_case_1 = __nccwpck_require__(65633); -const get_release_version_use_case_1 = __nccwpck_require__(8709); -const configuration_handler_1 = __nccwpck_require__(51068); -const authenticated_user_composition_root_1 = __nccwpck_require__(55549); -const execution_issue_setup_composition_root_1 = __nccwpck_require__(84724); +const execution_branch_version_resolver_1 = __nccwpck_require__(71813); +const setup_execution_use_case_1 = __nccwpck_require__(88512); +const get_hotfix_version_use_case_1 = __nccwpck_require__(59946); +const get_release_type_use_case_1 = __nccwpck_require__(64410); +const get_release_version_use_case_1 = __nccwpck_require__(70587); +const configuration_handler_1 = __nccwpck_require__(40188); +const authenticated_user_composition_root_1 = __nccwpck_require__(33885); +const execution_issue_setup_composition_root_1 = __nccwpck_require__(98313); function createSetupExecutionUseCase(latestTagQueryPort, credentials) { const rawIssueSetupPort = (0, execution_issue_setup_composition_root_1.createExecutionIssueSetupCompositionRoot)(); const rawOrganizationSetupPort = (0, authenticated_user_composition_root_1.createAuthenticatedUserCompositionRoot)(); @@ -81855,14 +81855,14 @@ function createSetupExecutionUseCase(latestTagQueryPort, credentials) { /***/ }), -/***/ 32112: +/***/ 30144: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createBranchComparisonClient = exports.createBranchClient = void 0; -const octokit_branch_adapters_1 = __nccwpck_require__(95215); +const octokit_branch_adapters_1 = __nccwpck_require__(77889); const createBranchClient = () => new octokit_branch_adapters_1.OctokitBranchClientAdapter(); exports.createBranchClient = createBranchClient; const createBranchComparisonClient = () => new octokit_branch_adapters_1.OctokitBranchComparisonClientAdapter(); @@ -81871,15 +81871,15 @@ exports.createBranchComparisonClient = createBranchComparisonClient; /***/ }), -/***/ 17930: +/***/ 93081: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createRepositoryVariablesClient = exports.createOrganizationMembersClient = exports.createActorAuthorizationClient = exports.createAuthenticatedUserClient = void 0; -const octokit_identity_adapters_1 = __nccwpck_require__(39787); -const octokit_repository_variables_adapter_1 = __nccwpck_require__(93883); +const octokit_identity_adapters_1 = __nccwpck_require__(29996); +const octokit_repository_variables_adapter_1 = __nccwpck_require__(81329); const createAuthenticatedUserClient = () => new octokit_identity_adapters_1.OctokitAuthenticatedUserClientAdapter(); exports.createAuthenticatedUserClient = createAuthenticatedUserClient; const createActorAuthorizationClient = () => new octokit_identity_adapters_1.OctokitActorAuthorizationClientAdapter(); @@ -81892,14 +81892,14 @@ exports.createRepositoryVariablesClient = createRepositoryVariablesClient; /***/ }), -/***/ 15161: +/***/ 95883: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createIssueTitleClient = exports.createIssueMetadataClient = exports.createIssueInactivityClient = exports.createIssueLifecycleClient = exports.createIssueLabelsClient = exports.createIssueLabelProvisioningClient = exports.createIssueContentClient = exports.createIssueAssignmentClient = void 0; -const octokit_issue_adapters_1 = __nccwpck_require__(73387); +const octokit_issue_adapters_1 = __nccwpck_require__(77179); const createIssueAssignmentClient = () => new octokit_issue_adapters_1.OctokitIssueAssignmentClientAdapter(); exports.createIssueAssignmentClient = createIssueAssignmentClient; const createIssueContentClient = () => new octokit_issue_adapters_1.OctokitIssueContentClientAdapter(); @@ -81920,15 +81920,15 @@ exports.createIssueTitleClient = createIssueTitleClient; /***/ }), -/***/ 63165: +/***/ 23691: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createOwnerTypeClient = exports.createGraphqlTransportClient = void 0; -const octokit_project_adapters_1 = __nccwpck_require__(39191); -const octokit_identity_adapters_1 = __nccwpck_require__(39787); +const octokit_project_adapters_1 = __nccwpck_require__(68505); +const octokit_identity_adapters_1 = __nccwpck_require__(29996); const createGraphqlTransportClient = () => new octokit_project_adapters_1.OctokitGraphqlTransportClientAdapter(); exports.createGraphqlTransportClient = createGraphqlTransportClient; const createOwnerTypeClient = () => new octokit_identity_adapters_1.OctokitOwnerTypeClientAdapter(); @@ -81937,14 +81937,14 @@ exports.createOwnerTypeClient = createOwnerTypeClient; /***/ }), -/***/ 46236: +/***/ 9068: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createPullRequestReviewCommentClient = exports.createPullRequestReviewerClient = exports.createPullRequestLifecycleClient = exports.createPullRequestChangesClient = void 0; -const octokit_pull_request_adapters_1 = __nccwpck_require__(38632); +const octokit_pull_request_adapters_1 = __nccwpck_require__(1397); const createPullRequestChangesClient = () => new octokit_pull_request_adapters_1.OctokitPullRequestChangesClientAdapter(); exports.createPullRequestChangesClient = createPullRequestChangesClient; const createPullRequestLifecycleClient = () => new octokit_pull_request_adapters_1.OctokitPullRequestLifecycleClientAdapter(); @@ -81957,28 +81957,28 @@ exports.createPullRequestReviewCommentClient = createPullRequestReviewCommentCli /***/ }), -/***/ 66236: +/***/ 76706: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createReleaseClient = void 0; -const octokit_release_adapters_1 = __nccwpck_require__(51480); +const octokit_release_adapters_1 = __nccwpck_require__(5334); const createReleaseClient = () => new octokit_release_adapters_1.OctokitReleaseClientAdapter(); exports.createReleaseClient = createReleaseClient; /***/ }), -/***/ 7654: +/***/ 29839: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createWorkflowDispatchClient = exports.createWorkflowRunsClient = void 0; -const octokit_workflow_adapters_1 = __nccwpck_require__(36430); +const octokit_workflow_adapters_1 = __nccwpck_require__(86719); const createWorkflowRunsClient = () => new octokit_workflow_adapters_1.OctokitWorkflowRunsClientAdapter(); exports.createWorkflowRunsClient = createWorkflowRunsClient; const createWorkflowDispatchClient = () => new octokit_workflow_adapters_1.OctokitWorkflowDispatchClientAdapter(); @@ -81987,28 +81987,28 @@ exports.createWorkflowDispatchClient = createWorkflowDispatchClient; /***/ }), -/***/ 84423: +/***/ 84138: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createInitialSetupCompositionRoot = createInitialSetupCompositionRoot; -const github_identity_client_factory_1 = __nccwpck_require__(17930); -const github_issue_client_factory_1 = __nccwpck_require__(15161); -const github_project_client_factory_1 = __nccwpck_require__(63165); -const github_release_client_factory_1 = __nccwpck_require__(66236); -const issue_label_provisioning_repository_1 = __nccwpck_require__(4532); -const issue_type_repository_1 = __nccwpck_require__(87600); -const authenticated_user_repository_1 = __nccwpck_require__(85397); -const repository_default_branch_repository_1 = __nccwpck_require__(38818); -const repository_tag_repository_1 = __nccwpck_require__(83842); -const git_cli_repository_1 = __nccwpck_require__(72119); -const initial_setup_use_case_composition_1 = __nccwpck_require__(97792); -const setup_workspace_adapter_1 = __nccwpck_require__(23376); -const repository_variables_repository_1 = __nccwpck_require__(73307); -const github_identity_client_factory_2 = __nccwpck_require__(17930); -const push_single_action_capability_port_binding_1 = __nccwpck_require__(54110); +const github_identity_client_factory_1 = __nccwpck_require__(93081); +const github_issue_client_factory_1 = __nccwpck_require__(95883); +const github_project_client_factory_1 = __nccwpck_require__(23691); +const github_release_client_factory_1 = __nccwpck_require__(76706); +const issue_label_provisioning_repository_1 = __nccwpck_require__(59699); +const issue_type_repository_1 = __nccwpck_require__(4858); +const authenticated_user_repository_1 = __nccwpck_require__(11454); +const repository_default_branch_repository_1 = __nccwpck_require__(96578); +const repository_tag_repository_1 = __nccwpck_require__(58717); +const git_cli_repository_1 = __nccwpck_require__(26331); +const initial_setup_use_case_composition_1 = __nccwpck_require__(93141); +const setup_workspace_adapter_1 = __nccwpck_require__(5729); +const repository_variables_repository_1 = __nccwpck_require__(28493); +const github_identity_client_factory_2 = __nccwpck_require__(93081); +const push_single_action_capability_port_binding_1 = __nccwpck_require__(49417); function createInitialSetupCompositionRoot(binding) { const labelProvisioning = new issue_label_provisioning_repository_1.IssueLabelProvisioningRepository((0, github_issue_client_factory_1.createIssueLabelProvisioningClient)()); const githubResourceClient = (0, github_identity_client_factory_2.createRepositoryVariablesClient)(); @@ -82018,14 +82018,14 @@ function createInitialSetupCompositionRoot(binding) { /***/ }), -/***/ 97792: +/***/ 93141: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.composeInitialSetupUseCase = composeInitialSetupUseCase; -const initial_setup_use_case_1 = __nccwpck_require__(50658); +const initial_setup_use_case_1 = __nccwpck_require__(84837); function composeInitialSetupUseCase(...dependencies) { return new initial_setup_use_case_1.InitialSetupUseCase(...dependencies); } @@ -82033,15 +82033,15 @@ function composeInitialSetupUseCase(...dependencies) { /***/ }), -/***/ 82808: +/***/ 62255: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createIssueContentCompositionRoot = createIssueContentCompositionRoot; -const github_issue_client_factory_1 = __nccwpck_require__(15161); -const issue_content_repository_1 = __nccwpck_require__(43338); +const github_issue_client_factory_1 = __nccwpck_require__(95883); +const issue_content_repository_1 = __nccwpck_require__(2313); function createIssueContentCompositionRoot() { return new issue_content_repository_1.IssueContentRepository((0, github_issue_client_factory_1.createIssueContentClient)()); } @@ -82049,20 +82049,20 @@ function createIssueContentCompositionRoot() { /***/ }), -/***/ 36937: +/***/ 74914: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createCloseInactiveIssuesUseCase = createCloseInactiveIssuesUseCase; -const close_inactive_issues_use_case_1 = __nccwpck_require__(56690); -const issue_inactivity_repository_1 = __nccwpck_require__(5533); -const system_issue_inactivity_clock_adapter_1 = __nccwpck_require__(62557); -const github_issue_client_factory_1 = __nccwpck_require__(15161); -const issue_interaction_composition_root_1 = __nccwpck_require__(33777); -const lifecycle_capability_port_binding_1 = __nccwpck_require__(28822); -const push_single_action_capability_port_binding_1 = __nccwpck_require__(54110); +const close_inactive_issues_use_case_1 = __nccwpck_require__(84579); +const issue_inactivity_repository_1 = __nccwpck_require__(28868); +const system_issue_inactivity_clock_adapter_1 = __nccwpck_require__(86457); +const github_issue_client_factory_1 = __nccwpck_require__(95883); +const issue_interaction_composition_root_1 = __nccwpck_require__(92503); +const lifecycle_capability_port_binding_1 = __nccwpck_require__(85785); +const push_single_action_capability_port_binding_1 = __nccwpck_require__(49417); function createCloseInactiveIssuesUseCase(binding, catalogResolver) { return new close_inactive_issues_use_case_1.CloseInactiveIssuesUseCase((0, push_single_action_capability_port_binding_1.bindIssueInactivityQuery)(new issue_inactivity_repository_1.IssueInactivityRepository((0, github_issue_client_factory_1.createIssueInactivityClient)()), binding), (0, lifecycle_capability_port_binding_1.bindIssueClosure)((0, issue_interaction_composition_root_1.createIssueClosureRepository)(), binding), new system_issue_inactivity_clock_adapter_1.SystemIssueInactivityClockAdapter(), catalogResolver); } @@ -82070,7 +82070,7 @@ function createCloseInactiveIssuesUseCase(binding, catalogResolver) { /***/ }), -/***/ 33777: +/***/ 92503: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -82078,11 +82078,11 @@ function createCloseInactiveIssuesUseCase(binding, catalogResolver) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createIssueClosureRepository = createIssueClosureRepository; exports.createIssueNotificationRepository = createIssueNotificationRepository; -const github_issue_client_factory_1 = __nccwpck_require__(15161); -const issue_content_repository_1 = __nccwpck_require__(43338); -const issue_lifecycle_repository_1 = __nccwpck_require__(44023); -const issue_closure_repository_1 = __nccwpck_require__(80674); -const issue_notification_repository_1 = __nccwpck_require__(24682); +const github_issue_client_factory_1 = __nccwpck_require__(95883); +const issue_content_repository_1 = __nccwpck_require__(2313); +const issue_lifecycle_repository_1 = __nccwpck_require__(8346); +const issue_closure_repository_1 = __nccwpck_require__(23231); +const issue_notification_repository_1 = __nccwpck_require__(907); function createIssueClosureRepository() { return new issue_closure_repository_1.IssueClosureRepository(new issue_lifecycle_repository_1.IssueLifecycleRepository((0, github_issue_client_factory_1.createIssueLifecycleClient)()), new issue_content_repository_1.IssueContentRepository((0, github_issue_client_factory_1.createIssueContentClient)())); } @@ -82093,15 +82093,15 @@ function createIssueNotificationRepository() { /***/ }), -/***/ 94185: +/***/ 34780: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createIssueLabelRepository = createIssueLabelRepository; -const github_issue_client_factory_1 = __nccwpck_require__(15161); -const issue_label_repository_1 = __nccwpck_require__(27825); +const github_issue_client_factory_1 = __nccwpck_require__(95883); +const issue_label_repository_1 = __nccwpck_require__(45725); function createIssueLabelRepository() { return new issue_label_repository_1.IssueLabelRepository((0, github_issue_client_factory_1.createIssueLabelsClient)()); } @@ -82109,14 +82109,14 @@ function createIssueLabelRepository() { /***/ }), -/***/ 91667: +/***/ 21239: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.composeIssueUseCase = composeIssueUseCase; -const issue_use_case_1 = __nccwpck_require__(43712); +const issue_use_case_1 = __nccwpck_require__(65281); function composeIssueUseCase(...dependencies) { return new issue_use_case_1.IssueUseCase(...dependencies); } @@ -82124,57 +82124,57 @@ function composeIssueUseCase(...dependencies) { /***/ }), -/***/ 97844: +/***/ 43022: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createIssueUseCaseCompositionRoot = createIssueUseCaseCompositionRoot; -const github_branch_client_factory_1 = __nccwpck_require__(32112); -const github_issue_client_factory_1 = __nccwpck_require__(15161); -const github_project_client_factory_1 = __nccwpck_require__(63165); -const github_workflow_client_factory_1 = __nccwpck_require__(7654); -const recommend_steps_use_case_1 = __nccwpck_require__(33526); -const check_permissions_use_case_1 = __nccwpck_require__(88960); -const update_title_use_case_1 = __nccwpck_require__(21376); -const assign_members_to_issue_use_case_1 = __nccwpck_require__(18189); -const check_priority_issue_size_use_case_1 = __nccwpck_require__(46243); -const close_not_allowed_issue_use_case_1 = __nccwpck_require__(71594); -const label_deploy_added_use_case_1 = __nccwpck_require__(56723); -const link_issue_project_use_case_1 = __nccwpck_require__(34835); -const move_issue_to_in_progress_1 = __nccwpck_require__(79453); -const prepare_branches_use_case_1 = __nccwpck_require__(35833); -const remove_issue_branches_use_case_1 = __nccwpck_require__(92405); -const remove_not_needed_branches_use_case_1 = __nccwpck_require__(26142); -const update_issue_type_use_case_1 = __nccwpck_require__(25514); -const answer_issue_help_use_case_1 = __nccwpck_require__(41713); -const branch_lifecycle_repository_1 = __nccwpck_require__(88216); -const branch_name_repository_1 = __nccwpck_require__(94776); -const linked_branch_repository_1 = __nccwpck_require__(23822); -const linked_branch_readiness_repository_1 = __nccwpck_require__(69983); -const reconcile_branch_readiness_use_case_1 = __nccwpck_require__(10027); -const pre_branch_sdd_gate_use_case_1 = __nccwpck_require__(89463); -const pre_branch_sdd_workspace_adapter_1 = __nccwpck_require__(71976); -const push_single_action_capability_port_binding_1 = __nccwpck_require__(54110); -const issue_labels_composition_root_1 = __nccwpck_require__(94185); -const git_cli_repository_1 = __nccwpck_require__(72119); -const issue_assignment_repository_1 = __nccwpck_require__(7223); -const issue_closure_repository_1 = __nccwpck_require__(80674); -const issue_content_repository_1 = __nccwpck_require__(43338); -const issue_lifecycle_repository_1 = __nccwpck_require__(44023); -const issue_metadata_repository_1 = __nccwpck_require__(37674); -const issue_title_repository_1 = __nccwpck_require__(89116); -const issue_type_assignment_repository_1 = __nccwpck_require__(55913); -const workflow_dispatch_repository_1 = __nccwpck_require__(70051); -const timer_branch_propagation_delay_adapter_1 = __nccwpck_require__(20015); -const agent_capability_composition_root_1 = __nccwpck_require__(7753); -const issue_use_case_composition_1 = __nccwpck_require__(91667); -const organization_members_composition_root_1 = __nccwpck_require__(17705); -const project_board_composition_root_1 = __nccwpck_require__(17798); -const actor_authorization_composition_root_1 = __nccwpck_require__(43758); -const shared_capability_port_binding_1 = __nccwpck_require__(20918); -const lifecycle_capability_port_binding_1 = __nccwpck_require__(28822); +const github_branch_client_factory_1 = __nccwpck_require__(30144); +const github_issue_client_factory_1 = __nccwpck_require__(95883); +const github_project_client_factory_1 = __nccwpck_require__(23691); +const github_workflow_client_factory_1 = __nccwpck_require__(29839); +const recommend_steps_use_case_1 = __nccwpck_require__(73746); +const check_permissions_use_case_1 = __nccwpck_require__(18846); +const update_title_use_case_1 = __nccwpck_require__(20556); +const assign_members_to_issue_use_case_1 = __nccwpck_require__(55523); +const check_priority_issue_size_use_case_1 = __nccwpck_require__(19511); +const close_not_allowed_issue_use_case_1 = __nccwpck_require__(86675); +const label_deploy_added_use_case_1 = __nccwpck_require__(27708); +const link_issue_project_use_case_1 = __nccwpck_require__(34100); +const move_issue_to_in_progress_1 = __nccwpck_require__(52309); +const prepare_branches_use_case_1 = __nccwpck_require__(67546); +const remove_issue_branches_use_case_1 = __nccwpck_require__(15608); +const remove_not_needed_branches_use_case_1 = __nccwpck_require__(67129); +const update_issue_type_use_case_1 = __nccwpck_require__(38222); +const answer_issue_help_use_case_1 = __nccwpck_require__(10706); +const branch_lifecycle_repository_1 = __nccwpck_require__(19504); +const branch_name_repository_1 = __nccwpck_require__(61887); +const linked_branch_repository_1 = __nccwpck_require__(78009); +const linked_branch_readiness_repository_1 = __nccwpck_require__(79421); +const reconcile_branch_readiness_use_case_1 = __nccwpck_require__(71836); +const pre_branch_sdd_gate_use_case_1 = __nccwpck_require__(29475); +const pre_branch_sdd_workspace_adapter_1 = __nccwpck_require__(35849); +const push_single_action_capability_port_binding_1 = __nccwpck_require__(49417); +const issue_labels_composition_root_1 = __nccwpck_require__(34780); +const git_cli_repository_1 = __nccwpck_require__(26331); +const issue_assignment_repository_1 = __nccwpck_require__(75023); +const issue_closure_repository_1 = __nccwpck_require__(23231); +const issue_content_repository_1 = __nccwpck_require__(2313); +const issue_lifecycle_repository_1 = __nccwpck_require__(8346); +const issue_metadata_repository_1 = __nccwpck_require__(11333); +const issue_title_repository_1 = __nccwpck_require__(10121); +const issue_type_assignment_repository_1 = __nccwpck_require__(19118); +const workflow_dispatch_repository_1 = __nccwpck_require__(29509); +const timer_branch_propagation_delay_adapter_1 = __nccwpck_require__(20846); +const agent_capability_composition_root_1 = __nccwpck_require__(85079); +const issue_use_case_composition_1 = __nccwpck_require__(21239); +const organization_members_composition_root_1 = __nccwpck_require__(50603); +const project_board_composition_root_1 = __nccwpck_require__(37194); +const actor_authorization_composition_root_1 = __nccwpck_require__(233); +const shared_capability_port_binding_1 = __nccwpck_require__(47399); +const lifecycle_capability_port_binding_1 = __nccwpck_require__(85785); function createIssueUseCaseCompositionRoot(binding) { const issueMetadata = new issue_metadata_repository_1.IssueMetadataRepository((0, github_issue_client_factory_1.createIssueMetadataClient)(), (0, github_project_client_factory_1.createGraphqlTransportClient)()); const issueContent = new issue_content_repository_1.IssueContentRepository((0, github_issue_client_factory_1.createIssueContentClient)()); @@ -82221,7 +82221,7 @@ function createIssueUseCaseCompositionRoot(binding) { /***/ }), -/***/ 28822: +/***/ 85785: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -82242,7 +82242,7 @@ exports.bindPullRequestIssueLink = bindPullRequestIssueLink; exports.bindIssueLabels = bindIssueLabels; exports.bindPullRequestHeadSha = bindPullRequestHeadSha; exports.bindPullRequestDescription = bindPullRequestDescription; -const project_detail_1 = __nccwpck_require__(74263); +const project_detail_1 = __nccwpck_require__(33428); function bindActorAuthorization(port, binding) { return Object.freeze({ isActorAllowedToModifyFiles: (actor) => port.isActorAllowedToModifyFiles(binding.owner, binding.repository, actor, binding.token), @@ -82344,17 +82344,17 @@ function toProjectDetail(project) { /***/ }), -/***/ 36426: +/***/ 34760: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createLocalActionCompositionRoot = createLocalActionCompositionRoot; -const resolve_message_catalog_use_case_1 = __nccwpck_require__(53803); -const git_cli_repository_1 = __nccwpck_require__(72119); -const project_board_composition_root_1 = __nccwpck_require__(17798); -const agent_capability_composition_root_1 = __nccwpck_require__(7753); +const resolve_message_catalog_use_case_1 = __nccwpck_require__(99961); +const git_cli_repository_1 = __nccwpck_require__(26331); +const project_board_composition_root_1 = __nccwpck_require__(37194); +const agent_capability_composition_root_1 = __nccwpck_require__(85079); /** * Owns the concrete dependencies shared by the local action lifecycle. * Keeping them in one root preserves the project-board query/command scope and @@ -82372,7 +82372,7 @@ function createLocalActionCompositionRoot() { /***/ }), -/***/ 7473: +/***/ 4706: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -82383,71 +82383,71 @@ exports.createIssueCommentUseCaseCompositionRoot = createIssueCommentUseCaseComp exports.createPullRequestReviewCommentUseCaseCompositionRoot = createPullRequestReviewCommentUseCaseCompositionRoot; exports.createCommitUseCaseCompositionRoot = createCommitUseCaseCompositionRoot; exports.createMainRunRouteCompositionRoot = createMainRunRouteCompositionRoot; -const commit_use_case_1 = __nccwpck_require__(84014); -const issue_comment_use_case_1 = __nccwpck_require__(14502); -const pull_request_review_comment_use_case_1 = __nccwpck_require__(94097); -const single_action_use_case_1 = __nccwpck_require__(73840); -const create_release_use_case_1 = __nccwpck_require__(68781); -const create_tag_use_case_1 = __nccwpck_require__(27977); -const publish_github_action_use_case_1 = __nccwpck_require__(31153); -const publish_issue_comment_use_case_1 = __nccwpck_require__(23758); -const recommend_steps_use_case_1 = __nccwpck_require__(33526); -const check_changes_issue_size_use_case_1 = __nccwpck_require__(75384); -const bugbot_autofix_use_case_1 = __nccwpck_require__(92886); -const detect_bugbot_fix_intent_use_case_1 = __nccwpck_require__(50385); -const dismiss_bugbot_findings_use_case_1 = __nccwpck_require__(8677); -const remember_bugbot_rule_use_case_1 = __nccwpck_require__(83711); -const detect_potential_problems_use_case_1 = __nccwpck_require__(65545); -const notify_new_commit_on_issue_use_case_1 = __nccwpck_require__(77749); -const user_request_use_case_1 = __nccwpck_require__(39633); -const think_use_case_1 = __nccwpck_require__(25099); -const check_issue_comment_language_use_case_1 = __nccwpck_require__(34670); -const check_pull_request_comment_language_use_case_1 = __nccwpck_require__(5988); -const comment_language_translation_workflow_1 = __nccwpck_require__(78212); -const branch_compare_repository_1 = __nccwpck_require__(81122); -const repository_release_publication_repository_1 = __nccwpck_require__(9204); -const repository_tag_repository_1 = __nccwpck_require__(83842); -const git_commit_adapter_1 = __nccwpck_require__(89443); -const bound_bugbot_git_mutation_adapter_1 = __nccwpck_require__(85401); -const actor_authorization_composition_root_1 = __nccwpck_require__(43758); -const agent_capability_composition_root_1 = __nccwpck_require__(7753); -const authenticated_user_composition_root_1 = __nccwpck_require__(55549); -const bugbot_composition_root_1 = __nccwpck_require__(40733); -const check_progress_composition_root_1 = __nccwpck_require__(25949); -const github_branch_client_factory_1 = __nccwpck_require__(32112); -const github_pull_request_client_factory_1 = __nccwpck_require__(46236); -const github_release_client_factory_1 = __nccwpck_require__(66236); -const initial_setup_composition_root_1 = __nccwpck_require__(84423); -const issue_content_composition_root_1 = __nccwpck_require__(82808); -const issue_interaction_composition_root_1 = __nccwpck_require__(33777); -const issue_labels_composition_root_1 = __nccwpck_require__(94185); -const issue_use_case_composition_root_1 = __nccwpck_require__(97844); -const pull_request_use_case_composition_root_1 = __nccwpck_require__(75176); -const organization_members_composition_root_1 = __nccwpck_require__(17705); -const update_pull_request_description_use_case_1 = __nccwpck_require__(10016); -const pull_request_lifecycle_repository_1 = __nccwpck_require__(57177); -const issue_inactivity_composition_root_1 = __nccwpck_require__(36937); -const github_project_client_factory_1 = __nccwpck_require__(63165); -const branch_dependency_repository_1 = __nccwpck_require__(78769); -const branch_sync_workspace_adapter_1 = __nccwpck_require__(51697); -const observe_branch_sync_use_case_1 = __nccwpck_require__(34342); -const sync_branch_use_case_1 = __nccwpck_require__(43725); -const deployment_orchestration_use_case_1 = __nccwpck_require__(67132); -const github_deployment_git_repository_1 = __nccwpck_require__(35315); -const github_managed_pull_request_repository_1 = __nccwpck_require__(57536); -const github_target_merge_capabilities_inspector_1 = __nccwpck_require__(46950); -const deployment_continuation_repository_1 = __nccwpck_require__(26781); -const deployment_presentation_repository_1 = __nccwpck_require__(86534); -const deployment_state_repository_1 = __nccwpck_require__(59536); -const octokit_deployment_adapter_1 = __nccwpck_require__(15246); -const workflow_dispatch_repository_1 = __nccwpck_require__(70051); -const github_workflow_client_factory_1 = __nccwpck_require__(7654); +const commit_use_case_1 = __nccwpck_require__(28001); +const issue_comment_use_case_1 = __nccwpck_require__(72042); +const pull_request_review_comment_use_case_1 = __nccwpck_require__(29415); +const single_action_use_case_1 = __nccwpck_require__(73572); +const create_release_use_case_1 = __nccwpck_require__(25258); +const create_tag_use_case_1 = __nccwpck_require__(22120); +const publish_github_action_use_case_1 = __nccwpck_require__(68891); +const publish_issue_comment_use_case_1 = __nccwpck_require__(61313); +const recommend_steps_use_case_1 = __nccwpck_require__(73746); +const check_changes_issue_size_use_case_1 = __nccwpck_require__(28356); +const bugbot_autofix_use_case_1 = __nccwpck_require__(45446); +const detect_bugbot_fix_intent_use_case_1 = __nccwpck_require__(76234); +const dismiss_bugbot_findings_use_case_1 = __nccwpck_require__(37685); +const remember_bugbot_rule_use_case_1 = __nccwpck_require__(17437); +const detect_potential_problems_use_case_1 = __nccwpck_require__(6287); +const notify_new_commit_on_issue_use_case_1 = __nccwpck_require__(33276); +const user_request_use_case_1 = __nccwpck_require__(19004); +const think_use_case_1 = __nccwpck_require__(89255); +const check_issue_comment_language_use_case_1 = __nccwpck_require__(93152); +const check_pull_request_comment_language_use_case_1 = __nccwpck_require__(21729); +const comment_language_translation_workflow_1 = __nccwpck_require__(72770); +const branch_compare_repository_1 = __nccwpck_require__(95859); +const repository_release_publication_repository_1 = __nccwpck_require__(42075); +const repository_tag_repository_1 = __nccwpck_require__(58717); +const git_commit_adapter_1 = __nccwpck_require__(18606); +const bound_bugbot_git_mutation_adapter_1 = __nccwpck_require__(51520); +const actor_authorization_composition_root_1 = __nccwpck_require__(233); +const agent_capability_composition_root_1 = __nccwpck_require__(85079); +const authenticated_user_composition_root_1 = __nccwpck_require__(33885); +const bugbot_composition_root_1 = __nccwpck_require__(67395); +const check_progress_composition_root_1 = __nccwpck_require__(21531); +const github_branch_client_factory_1 = __nccwpck_require__(30144); +const github_pull_request_client_factory_1 = __nccwpck_require__(9068); +const github_release_client_factory_1 = __nccwpck_require__(76706); +const initial_setup_composition_root_1 = __nccwpck_require__(84138); +const issue_content_composition_root_1 = __nccwpck_require__(62255); +const issue_interaction_composition_root_1 = __nccwpck_require__(92503); +const issue_labels_composition_root_1 = __nccwpck_require__(34780); +const issue_use_case_composition_root_1 = __nccwpck_require__(43022); +const pull_request_use_case_composition_root_1 = __nccwpck_require__(70636); +const organization_members_composition_root_1 = __nccwpck_require__(50603); +const update_pull_request_description_use_case_1 = __nccwpck_require__(75089); +const pull_request_lifecycle_repository_1 = __nccwpck_require__(24189); +const issue_inactivity_composition_root_1 = __nccwpck_require__(74914); +const github_project_client_factory_1 = __nccwpck_require__(23691); +const branch_dependency_repository_1 = __nccwpck_require__(9627); +const branch_sync_workspace_adapter_1 = __nccwpck_require__(81849); +const observe_branch_sync_use_case_1 = __nccwpck_require__(84542); +const sync_branch_use_case_1 = __nccwpck_require__(392); +const deployment_orchestration_use_case_1 = __nccwpck_require__(36850); +const github_deployment_git_repository_1 = __nccwpck_require__(85886); +const github_managed_pull_request_repository_1 = __nccwpck_require__(96483); +const github_target_merge_capabilities_inspector_1 = __nccwpck_require__(55527); +const deployment_continuation_repository_1 = __nccwpck_require__(77509); +const deployment_presentation_repository_1 = __nccwpck_require__(91985); +const deployment_state_repository_1 = __nccwpck_require__(3182); +const octokit_deployment_adapter_1 = __nccwpck_require__(46819); +const workflow_dispatch_repository_1 = __nccwpck_require__(29509); +const github_workflow_client_factory_1 = __nccwpck_require__(29839); const node_crypto_1 = __nccwpck_require__(6005); -const resolve_message_catalog_use_case_1 = __nccwpck_require__(53803); -const shared_capability_port_binding_1 = __nccwpck_require__(20918); -const lifecycle_capability_port_binding_1 = __nccwpck_require__(28822); -const lifecycle_capability_port_binding_2 = __nccwpck_require__(28822); -const push_single_action_capability_port_binding_1 = __nccwpck_require__(54110); +const resolve_message_catalog_use_case_1 = __nccwpck_require__(99961); +const shared_capability_port_binding_1 = __nccwpck_require__(47399); +const lifecycle_capability_port_binding_1 = __nccwpck_require__(85785); +const lifecycle_capability_port_binding_2 = __nccwpck_require__(85785); +const push_single_action_capability_port_binding_1 = __nccwpck_require__(49417); function createDetectPotentialProblemsUseCase(binding) { const bugbot = (0, bugbot_composition_root_1.createBugbotCompositionRoot)(binding); return new detect_potential_problems_use_case_1.DetectPotentialProblemsUseCase((0, agent_capability_composition_root_1.createFindingsQueryPort)(), bugbot.scm, bugbot.telemetry, new resolve_message_catalog_use_case_1.ResolveMessageCatalogUseCase((0, agent_capability_composition_root_1.createLanguageQueryPort)())); @@ -82564,15 +82564,15 @@ function bugbotBinding(execution) { /***/ }), -/***/ 17705: +/***/ 50603: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createOrganizationMembersCompositionRoot = createOrganizationMembersCompositionRoot; -const github_identity_client_factory_1 = __nccwpck_require__(17930); -const organization_members_repository_1 = __nccwpck_require__(14553); +const github_identity_client_factory_1 = __nccwpck_require__(93081); +const organization_members_repository_1 = __nccwpck_require__(845); function createOrganizationMembersCompositionRoot() { return new organization_members_repository_1.OrganizationMembersRepository((0, github_identity_client_factory_1.createOrganizationMembersClient)()); } @@ -82580,17 +82580,17 @@ function createOrganizationMembersCompositionRoot() { /***/ }), -/***/ 17798: +/***/ 37194: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createProjectBoardCompositionRoot = createProjectBoardCompositionRoot; -const github_project_client_factory_1 = __nccwpck_require__(63165); -const project_board_command_repository_1 = __nccwpck_require__(76220); -const project_board_link_repository_1 = __nccwpck_require__(5367); -const project_board_query_repository_1 = __nccwpck_require__(17726); +const github_project_client_factory_1 = __nccwpck_require__(23691); +const project_board_command_repository_1 = __nccwpck_require__(98952); +const project_board_link_repository_1 = __nccwpck_require__(79285); +const project_board_query_repository_1 = __nccwpck_require__(97301); function createProjectBoardCompositionRoot() { const query = new project_board_query_repository_1.ProjectBoardQueryRepository((0, github_project_client_factory_1.createOwnerTypeClient)(), (0, github_project_client_factory_1.createGraphqlTransportClient)()); return { @@ -82603,15 +82603,15 @@ function createProjectBoardCompositionRoot() { /***/ }), -/***/ 39876: +/***/ 72651: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createPullRequestReviewerCompositionRoot = createPullRequestReviewerCompositionRoot; -const pull_request_reviewer_repository_1 = __nccwpck_require__(45908); -const github_pull_request_client_factory_1 = __nccwpck_require__(46236); +const pull_request_reviewer_repository_1 = __nccwpck_require__(13779); +const github_pull_request_client_factory_1 = __nccwpck_require__(9068); function createPullRequestReviewerCompositionRoot() { return new pull_request_reviewer_repository_1.PullRequestReviewerRepository((0, github_pull_request_client_factory_1.createPullRequestReviewerClient)()); } @@ -82619,14 +82619,14 @@ function createPullRequestReviewerCompositionRoot() { /***/ }), -/***/ 23969: +/***/ 24: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.composePullRequestUseCase = composePullRequestUseCase; -const pull_request_use_case_1 = __nccwpck_require__(93567); +const pull_request_use_case_1 = __nccwpck_require__(27259); function composePullRequestUseCase(...dependencies) { return new pull_request_use_case_1.PullRequestUseCase(...dependencies); } @@ -82634,45 +82634,45 @@ function composePullRequestUseCase(...dependencies) { /***/ }), -/***/ 75176: +/***/ 70636: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createPullRequestUseCaseCompositionRoot = createPullRequestUseCaseCompositionRoot; -const github_issue_client_factory_1 = __nccwpck_require__(15161); -const github_project_client_factory_1 = __nccwpck_require__(63165); -const github_pull_request_client_factory_1 = __nccwpck_require__(46236); -const update_pull_request_description_use_case_1 = __nccwpck_require__(10016); -const update_title_use_case_1 = __nccwpck_require__(21376); -const assign_members_to_issue_use_case_1 = __nccwpck_require__(18189); -const assign_reviewers_to_issue_use_case_1 = __nccwpck_require__(1093); -const close_issue_after_merging_use_case_1 = __nccwpck_require__(88705); -const check_priority_pull_request_size_use_case_1 = __nccwpck_require__(61696); -const link_pull_request_issue_use_case_1 = __nccwpck_require__(64280); -const link_pull_request_project_use_case_1 = __nccwpck_require__(42469); -const sync_size_and_progress_labels_from_issue_to_pr_use_case_1 = __nccwpck_require__(37698); -const agent_capability_composition_root_1 = __nccwpck_require__(7753); -const issue_assignment_repository_1 = __nccwpck_require__(7223); -const issue_closure_repository_1 = __nccwpck_require__(80674); -const issue_content_repository_1 = __nccwpck_require__(43338); -const issue_label_repository_1 = __nccwpck_require__(27825); -const issue_lifecycle_repository_1 = __nccwpck_require__(44023); -const issue_metadata_repository_1 = __nccwpck_require__(37674); -const issue_title_repository_1 = __nccwpck_require__(89116); -const pull_request_lifecycle_repository_1 = __nccwpck_require__(57177); -const pull_request_use_case_composition_1 = __nccwpck_require__(23969); -const pull_request_reviewer_composition_root_1 = __nccwpck_require__(39876); -const organization_members_composition_root_1 = __nccwpck_require__(17705); -const project_board_composition_root_1 = __nccwpck_require__(17798); -const timer_delay_adapter_1 = __nccwpck_require__(25288); -const detect_potential_problems_use_case_1 = __nccwpck_require__(65545); -const resolve_message_catalog_use_case_1 = __nccwpck_require__(53803); -const bugbot_composition_root_1 = __nccwpck_require__(40733); -const actor_authorization_composition_root_1 = __nccwpck_require__(43758); -const shared_capability_port_binding_1 = __nccwpck_require__(20918); -const lifecycle_capability_port_binding_1 = __nccwpck_require__(28822); +const github_issue_client_factory_1 = __nccwpck_require__(95883); +const github_project_client_factory_1 = __nccwpck_require__(23691); +const github_pull_request_client_factory_1 = __nccwpck_require__(9068); +const update_pull_request_description_use_case_1 = __nccwpck_require__(75089); +const update_title_use_case_1 = __nccwpck_require__(20556); +const assign_members_to_issue_use_case_1 = __nccwpck_require__(55523); +const assign_reviewers_to_issue_use_case_1 = __nccwpck_require__(80174); +const close_issue_after_merging_use_case_1 = __nccwpck_require__(46753); +const check_priority_pull_request_size_use_case_1 = __nccwpck_require__(12738); +const link_pull_request_issue_use_case_1 = __nccwpck_require__(38259); +const link_pull_request_project_use_case_1 = __nccwpck_require__(57169); +const sync_size_and_progress_labels_from_issue_to_pr_use_case_1 = __nccwpck_require__(89085); +const agent_capability_composition_root_1 = __nccwpck_require__(85079); +const issue_assignment_repository_1 = __nccwpck_require__(75023); +const issue_closure_repository_1 = __nccwpck_require__(23231); +const issue_content_repository_1 = __nccwpck_require__(2313); +const issue_label_repository_1 = __nccwpck_require__(45725); +const issue_lifecycle_repository_1 = __nccwpck_require__(8346); +const issue_metadata_repository_1 = __nccwpck_require__(11333); +const issue_title_repository_1 = __nccwpck_require__(10121); +const pull_request_lifecycle_repository_1 = __nccwpck_require__(24189); +const pull_request_use_case_composition_1 = __nccwpck_require__(24); +const pull_request_reviewer_composition_root_1 = __nccwpck_require__(72651); +const organization_members_composition_root_1 = __nccwpck_require__(50603); +const project_board_composition_root_1 = __nccwpck_require__(37194); +const timer_delay_adapter_1 = __nccwpck_require__(71942); +const detect_potential_problems_use_case_1 = __nccwpck_require__(6287); +const resolve_message_catalog_use_case_1 = __nccwpck_require__(99961); +const bugbot_composition_root_1 = __nccwpck_require__(67395); +const actor_authorization_composition_root_1 = __nccwpck_require__(233); +const shared_capability_port_binding_1 = __nccwpck_require__(47399); +const lifecycle_capability_port_binding_1 = __nccwpck_require__(85785); function createPullRequestUseCaseCompositionRoot(binding) { const issueLifecycle = new issue_lifecycle_repository_1.IssueLifecycleRepository((0, github_issue_client_factory_1.createIssueLifecycleClient)()); const issueContent = new issue_content_repository_1.IssueContentRepository((0, github_issue_client_factory_1.createIssueContentClient)()); @@ -82708,7 +82708,7 @@ function createPullRequestUseCaseCompositionRoot(binding) { /***/ }), -/***/ 54110: +/***/ 49417: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -82932,7 +82932,7 @@ function bindSetupRemoteConfiguration(port, binding) { /***/ }), -/***/ 45203: +/***/ 69084: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -82940,13 +82940,13 @@ function bindSetupRemoteConfiguration(port, binding) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createSetupCredentialsUseCase = createSetupCredentialsUseCase; exports.createSetupRemoteConfigurationReadPort = createSetupRemoteConfigurationReadPort; -const setup_credentials_use_case_1 = __nccwpck_require__(82634); -const setup_credential_validation_adapter_1 = __nccwpck_require__(23702); -const repository_variables_repository_1 = __nccwpck_require__(73307); -const github_identity_client_factory_1 = __nccwpck_require__(17930); -const setup_remote_credential_health_adapter_1 = __nccwpck_require__(37570); -const octokit_credential_health_adapter_1 = __nccwpck_require__(62696); -const setup_token_permissions_composition_root_1 = __nccwpck_require__(43759); +const setup_credentials_use_case_1 = __nccwpck_require__(67438); +const setup_credential_validation_adapter_1 = __nccwpck_require__(47020); +const repository_variables_repository_1 = __nccwpck_require__(28493); +const github_identity_client_factory_1 = __nccwpck_require__(93081); +const setup_remote_credential_health_adapter_1 = __nccwpck_require__(1489); +const octokit_credential_health_adapter_1 = __nccwpck_require__(41760); +const setup_token_permissions_composition_root_1 = __nccwpck_require__(64132); function createSetupCredentialsUseCase(prompt, permissionPresenter, options = {}) { const secretNames = new repository_variables_repository_1.RepositorySecretNamesQueryRepository((0, github_identity_client_factory_1.createRepositoryVariablesClient)()); return new setup_credentials_use_case_1.SetupCredentialsUseCase(prompt, new setup_credential_validation_adapter_1.SetupCredentialValidationAdapter(), secretNames, options.allowPreApplyHealthWorkflow === false @@ -82962,7 +82962,7 @@ function createSetupRemoteConfigurationReadPort() { /***/ }), -/***/ 90620: +/***/ 56360: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -82970,19 +82970,19 @@ function createSetupRemoteConfigurationReadPort() { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createSetupMergeQueueReadinessUseCase = createSetupMergeQueueReadinessUseCase; exports.createSetupDoctorUseCase = createSetupDoctorUseCase; -const doctor_use_case_1 = __nccwpck_require__(39); -const setup_credential_validation_adapter_1 = __nccwpck_require__(23702); -const repository_variables_repository_1 = __nccwpck_require__(73307); -const github_identity_client_factory_1 = __nccwpck_require__(17930); -const setup_workspace_adapter_1 = __nccwpck_require__(23376); -const setup_remote_credential_health_adapter_1 = __nccwpck_require__(37570); -const octokit_credential_health_adapter_1 = __nccwpck_require__(62696); -const github_target_merge_capabilities_inspector_1 = __nccwpck_require__(46950); -const octokit_deployment_adapter_1 = __nccwpck_require__(15246); -const merge_queue_readiness_use_case_1 = __nccwpck_require__(14236); -const resolve_message_catalog_use_case_1 = __nccwpck_require__(53803); -const agent_capability_composition_root_1 = __nccwpck_require__(7753); -const setup_approval_readiness_adapter_1 = __nccwpck_require__(93230); +const doctor_use_case_1 = __nccwpck_require__(87328); +const setup_credential_validation_adapter_1 = __nccwpck_require__(47020); +const repository_variables_repository_1 = __nccwpck_require__(28493); +const github_identity_client_factory_1 = __nccwpck_require__(93081); +const setup_workspace_adapter_1 = __nccwpck_require__(5729); +const setup_remote_credential_health_adapter_1 = __nccwpck_require__(1489); +const octokit_credential_health_adapter_1 = __nccwpck_require__(41760); +const github_target_merge_capabilities_inspector_1 = __nccwpck_require__(55527); +const octokit_deployment_adapter_1 = __nccwpck_require__(46819); +const merge_queue_readiness_use_case_1 = __nccwpck_require__(9890); +const resolve_message_catalog_use_case_1 = __nccwpck_require__(99961); +const agent_capability_composition_root_1 = __nccwpck_require__(85079); +const setup_approval_readiness_adapter_1 = __nccwpck_require__(78572); function createSetupMergeQueueReadinessUseCase(catalogResolver = new resolve_message_catalog_use_case_1.ResolveMessageCatalogUseCase((0, agent_capability_composition_root_1.createLanguageQueryPort)())) { return new merge_queue_readiness_use_case_1.SetupMergeQueueReadinessUseCase(new github_target_merge_capabilities_inspector_1.GithubTargetMergeCapabilitiesInspector(new octokit_deployment_adapter_1.OctokitDeploymentClientAdapter()), catalogResolver); } @@ -83003,16 +83003,16 @@ function createSetupDoctorUseCase() { /***/ }), -/***/ 43759: +/***/ 64132: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createSetupTokenPermissionsUseCase = createSetupTokenPermissionsUseCase; -const setup_token_permissions_use_case_1 = __nccwpck_require__(64888); -const setup_credential_validation_adapter_1 = __nccwpck_require__(23702); -const setup_token_permission_query_adapter_1 = __nccwpck_require__(90378); +const setup_token_permissions_use_case_1 = __nccwpck_require__(11797); +const setup_credential_validation_adapter_1 = __nccwpck_require__(47020); +const setup_token_permission_query_adapter_1 = __nccwpck_require__(67758); function createSetupTokenPermissionsUseCase() { return new setup_token_permissions_use_case_1.SetupTokenPermissionsUseCase(new setup_credential_validation_adapter_1.SetupCredentialValidationAdapter(), new setup_token_permission_query_adapter_1.SetupTokenPermissionQueryAdapter()); } @@ -83020,7 +83020,7 @@ function createSetupTokenPermissionsUseCase() { /***/ }), -/***/ 20918: +/***/ 47399: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -83034,7 +83034,7 @@ exports.bindIssueCommentQuery = bindIssueCommentQuery; exports.bindIssueCommentUpdate = bindIssueCommentUpdate; exports.bindIssueTitle = bindIssueTitle; exports.bindProjectContent = bindProjectContent; -const project_detail_1 = __nccwpck_require__(74263); +const project_detail_1 = __nccwpck_require__(33428); function bindPublicationSourceQuery(port, binding) { return Object.freeze({ getBranchHeadSha: (branch) => port.getBranchHeadSha(binding.owner, binding.repository, branch, binding.token), @@ -83086,20 +83086,20 @@ function toProjectDetail(project) { /***/ }), -/***/ 57010: +/***/ 21598: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createWaitForPreviousWorkflowRunsUseCase = createWaitForPreviousWorkflowRunsUseCase; -const wait_for_previous_workflow_runs_use_case_1 = __nccwpck_require__(64317); -const active_previous_workflow_runs_repository_1 = __nccwpck_require__(42351); -const timer_workflow_polling_delay_adapter_1 = __nccwpck_require__(283); -const logger_workflow_polling_observer_adapter_1 = __nccwpck_require__(46177); -const system_workflow_queue_clock_adapter_1 = __nccwpck_require__(89437); -const system_workflow_polling_random_adapter_1 = __nccwpck_require__(86649); -const github_workflow_client_factory_1 = __nccwpck_require__(7654); +const wait_for_previous_workflow_runs_use_case_1 = __nccwpck_require__(38301); +const active_previous_workflow_runs_repository_1 = __nccwpck_require__(40941); +const timer_workflow_polling_delay_adapter_1 = __nccwpck_require__(10339); +const logger_workflow_polling_observer_adapter_1 = __nccwpck_require__(52883); +const system_workflow_queue_clock_adapter_1 = __nccwpck_require__(49664); +const system_workflow_polling_random_adapter_1 = __nccwpck_require__(32679); +const github_workflow_client_factory_1 = __nccwpck_require__(29839); function createWaitForPreviousWorkflowRunsUseCase(token) { const client = (0, github_workflow_client_factory_1.createWorkflowRunsClient)().getClient(token); const delayPort = new timer_workflow_polling_delay_adapter_1.TimerWorkflowPollingDelayAdapter(); @@ -83110,7 +83110,7 @@ function createWaitForPreviousWorkflowRunsUseCase(token) { /***/ }), -/***/ 83645: +/***/ 50183: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -83257,7 +83257,7 @@ function isWithin(root, target) { /***/ }), -/***/ 1906: +/***/ 16535: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -83283,7 +83283,7 @@ function buildGitAuthenticationEnvironment(token, environment = process.env) { /***/ }), -/***/ 89443: +/***/ 18606: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -83323,9 +83323,9 @@ var __importStar = (this && this.__importStar) || (function () { })(); Object.defineProperty(exports, "__esModule", ({ value: true })); exports.GitCommitAdapter = void 0; -const exec = __importStar(__nccwpck_require__(36086)); -const git_authentication_environment_1 = __nccwpck_require__(1906); -const untrusted_command_environment_1 = __nccwpck_require__(57156); +const exec = __importStar(__nccwpck_require__(18538)); +const git_authentication_environment_1 = __nccwpck_require__(16535); +const untrusted_command_environment_1 = __nccwpck_require__(2304); class GitCommitAdapter { constructor(executeCommand = (program, args, options) => options ? exec.exec(program, args, { @@ -83390,7 +83390,7 @@ exports.GitCommitAdapter = GitCommitAdapter; /***/ }), -/***/ 33451: +/***/ 19008: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -83447,14 +83447,14 @@ function normalizeHttpsServerUrl(value) { /***/ }), -/***/ 95215: +/***/ 77889: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.OctokitBranchComparisonClientAdapter = exports.OctokitBranchClientAdapter = void 0; -const octokit_client_resolver_1 = __nccwpck_require__(91649); +const octokit_client_resolver_1 = __nccwpck_require__(54047); class OctokitBranchClientAdapter { getClient(token) { return (0, octokit_client_resolver_1.getOctokitClient)(token); } } @@ -83467,7 +83467,7 @@ exports.OctokitBranchComparisonClientAdapter = OctokitBranchComparisonClientAdap /***/ }), -/***/ 91649: +/***/ 54047: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -83507,7 +83507,7 @@ var __importStar = (this && this.__importStar) || (function () { })(); Object.defineProperty(exports, "__esModule", ({ value: true })); exports.getOctokitClient = getOctokitClient; -const github = __importStar(__nccwpck_require__(87211)); +const github = __importStar(__nccwpck_require__(78227)); function getOctokitClient(token) { return github.getOctokit(token); } @@ -83515,14 +83515,14 @@ function getOctokitClient(token) { /***/ }), -/***/ 62696: +/***/ 41760: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.OctokitCredentialHealthClientAdapter = void 0; -const octokit_client_resolver_1 = __nccwpck_require__(91649); +const octokit_client_resolver_1 = __nccwpck_require__(54047); class OctokitCredentialHealthClientAdapter { getClient(token) { return (0, octokit_client_resolver_1.getOctokitClient)(token); @@ -83533,14 +83533,14 @@ exports.OctokitCredentialHealthClientAdapter = OctokitCredentialHealthClientAdap /***/ }), -/***/ 15246: +/***/ 46819: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.OctokitDeploymentClientAdapter = void 0; -const octokit_client_resolver_1 = __nccwpck_require__(91649); +const octokit_client_resolver_1 = __nccwpck_require__(54047); class OctokitDeploymentClientAdapter { getClient(token) { return (0, octokit_client_resolver_1.getOctokitClient)(token); @@ -83551,14 +83551,14 @@ exports.OctokitDeploymentClientAdapter = OctokitDeploymentClientAdapter; /***/ }), -/***/ 39787: +/***/ 29996: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.OctokitOwnerTypeClientAdapter = exports.OctokitOrganizationMembersClientAdapter = exports.OctokitActorAuthorizationClientAdapter = exports.OctokitAuthenticatedUserClientAdapter = void 0; -const octokit_client_resolver_1 = __nccwpck_require__(91649); +const octokit_client_resolver_1 = __nccwpck_require__(54047); class OctokitAuthenticatedUserClientAdapter { getClient(token) { return (0, octokit_client_resolver_1.getOctokitClient)(token); } } @@ -83579,14 +83579,14 @@ exports.OctokitOwnerTypeClientAdapter = OctokitOwnerTypeClientAdapter; /***/ }), -/***/ 73387: +/***/ 77179: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.OctokitIssueTitleClientAdapter = exports.OctokitIssueMetadataClientAdapter = exports.OctokitIssueInactivityClientAdapter = exports.OctokitIssueLifecycleClientAdapter = exports.OctokitIssueLabelsClientAdapter = exports.OctokitIssueLabelProvisioningClientAdapter = exports.OctokitIssueContentClientAdapter = exports.OctokitIssueAssignmentClientAdapter = void 0; -const octokit_client_resolver_1 = __nccwpck_require__(91649); +const octokit_client_resolver_1 = __nccwpck_require__(54047); class OctokitIssueAssignmentClientAdapter { getClient(token) { return (0, octokit_client_resolver_1.getOctokitClient)(token); } } @@ -83623,14 +83623,14 @@ exports.OctokitIssueTitleClientAdapter = OctokitIssueTitleClientAdapter; /***/ }), -/***/ 39191: +/***/ 68505: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.OctokitGraphqlTransportClientAdapter = void 0; -const octokit_client_resolver_1 = __nccwpck_require__(91649); +const octokit_client_resolver_1 = __nccwpck_require__(54047); class OctokitGraphqlTransportClientAdapter { getClient(token) { return (0, octokit_client_resolver_1.getOctokitClient)(token); } } @@ -83639,14 +83639,14 @@ exports.OctokitGraphqlTransportClientAdapter = OctokitGraphqlTransportClientAdap /***/ }), -/***/ 38632: +/***/ 1397: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.OctokitPullRequestReviewCommentClientAdapter = exports.OctokitPullRequestReviewerClientAdapter = exports.OctokitPullRequestLifecycleClientAdapter = exports.OctokitPullRequestChangesClientAdapter = void 0; -const octokit_client_resolver_1 = __nccwpck_require__(91649); +const octokit_client_resolver_1 = __nccwpck_require__(54047); class OctokitPullRequestChangesClientAdapter { getClient(token) { return (0, octokit_client_resolver_1.getOctokitClient)(token); @@ -83675,14 +83675,14 @@ exports.OctokitPullRequestReviewCommentClientAdapter = OctokitPullRequestReviewC /***/ }), -/***/ 51480: +/***/ 5334: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.OctokitReleaseClientAdapter = void 0; -const octokit_client_resolver_1 = __nccwpck_require__(91649); +const octokit_client_resolver_1 = __nccwpck_require__(54047); class OctokitReleaseClientAdapter { getClient(token) { return (0, octokit_client_resolver_1.getOctokitClient)(token); } } @@ -83691,14 +83691,14 @@ exports.OctokitReleaseClientAdapter = OctokitReleaseClientAdapter; /***/ }), -/***/ 93883: +/***/ 81329: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.OctokitRepositoryVariablesClientAdapter = void 0; -const octokit_client_resolver_1 = __nccwpck_require__(91649); +const octokit_client_resolver_1 = __nccwpck_require__(54047); class OctokitRepositoryVariablesClientAdapter { getClient(token) { return (0, octokit_client_resolver_1.getOctokitClient)(token); @@ -83709,14 +83709,14 @@ exports.OctokitRepositoryVariablesClientAdapter = OctokitRepositoryVariablesClie /***/ }), -/***/ 36430: +/***/ 86719: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.OctokitWorkflowDispatchClientAdapter = exports.OctokitWorkflowRunsClientAdapter = void 0; -const octokit_client_resolver_1 = __nccwpck_require__(91649); +const octokit_client_resolver_1 = __nccwpck_require__(54047); class OctokitWorkflowRunsClientAdapter { getClient(token) { return (0, octokit_client_resolver_1.getOctokitClient)(token); @@ -83733,7 +83733,7 @@ exports.OctokitWorkflowDispatchClientAdapter = OctokitWorkflowDispatchClientAdap /***/ }), -/***/ 79506: +/***/ 96997: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -83746,7 +83746,7 @@ exports.PROJECT_BOARD_ITEM_PAGE_LIMIT = 500; /***/ }), -/***/ 56932: +/***/ 72762: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -83754,7 +83754,7 @@ exports.PROJECT_BOARD_ITEM_PAGE_LIMIT = 500; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createLoggerAdapter = createLoggerAdapter; exports.createLogReportAdapter = createLogReportAdapter; -const logger_1 = __nccwpck_require__(50135); +const logger_1 = __nccwpck_require__(91151); /** Adapts the process/GitHub logger to the semantic application port. */ function createLoggerAdapter() { return { @@ -83779,14 +83779,14 @@ function createLogReportAdapter() { /***/ }), -/***/ 36337: +/***/ 59844: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.LoggerAgentExecutionObserverAdapter = void 0; -const logger_1 = __nccwpck_require__(50135); +const logger_1 = __nccwpck_require__(91151); class LoggerAgentExecutionObserverAdapter { observe(observation) { if (observation.state === 'completed' || observation.state === 'failed') { @@ -83801,14 +83801,14 @@ exports.LoggerAgentExecutionObserverAdapter = LoggerAgentExecutionObserverAdapte /***/ }), -/***/ 55375: +/***/ 34685: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.LoggerBugbotTelemetryAdapter = void 0; -const logging_ports_1 = __nccwpck_require__(73001); +const logging_ports_1 = __nccwpck_require__(6152); class LoggerBugbotTelemetryAdapter { publish(snapshot) { (0, logging_ports_1.logInfo)(`[bugbot.telemetry] ${JSON.stringify(snapshot)}`); @@ -83819,14 +83819,14 @@ exports.LoggerBugbotTelemetryAdapter = LoggerBugbotTelemetryAdapter; /***/ }), -/***/ 46177: +/***/ 52883: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.LoggerWorkflowPollingObserverAdapter = void 0; -const logger_1 = __nccwpck_require__(50135); +const logger_1 = __nccwpck_require__(91151); class LoggerWorkflowPollingObserverAdapter { noActivePreviousRuns() { (0, logger_1.logDebugInfo)('✅ No previous runs active. Continuing...'); @@ -83850,7 +83850,7 @@ exports.LoggerWorkflowPollingObserverAdapter = LoggerWorkflowPollingObserverAdap /***/ }), -/***/ 71976: +/***/ 35849: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -83895,14 +83895,14 @@ const os = __importStar(__nccwpck_require__(70612)); const path = __importStar(__nccwpck_require__(49411)); const node_child_process_1 = __nccwpck_require__(17718); const node_util_1 = __nccwpck_require__(47261); -const pre_branch_sdd_1 = __nccwpck_require__(54078); -const git_authentication_environment_1 = __nccwpck_require__(1906); +const pre_branch_sdd_1 = __nccwpck_require__(34730); +const git_authentication_environment_1 = __nccwpck_require__(16535); const runFile = (0, node_util_1.promisify)(node_child_process_1.execFile); const SHA = /^[a-f0-9]{40}$/i; const BRANCH = /^[a-zA-Z0-9][a-zA-Z0-9._/-]*$/; // The shared catalog validator is CommonJS so the setup CLI and bundled Action use identical rules. // eslint-disable-next-line @typescript-eslint/no-require-imports -const validator = __nccwpck_require__(47743); +const validator = __nccwpck_require__(29617); /** Isolates SDD validation in a detached temporary worktree before the linked branch is created. */ class PreBranchSddWorkspaceAdapter { constructor(repositoryRoot = process.cwd(), token = '') { @@ -84197,7 +84197,7 @@ function writeSpecFile(target, content, exists) { /***/ }), -/***/ 93230: +/***/ 78572: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -84237,10 +84237,10 @@ var __importStar = (this && this.__importStar) || (function () { })(); Object.defineProperty(exports, "__esModule", ({ value: true })); exports.GithubSetupApprovalReadinessAdapter = void 0; -const github = __importStar(__nccwpck_require__(87211)); +const github = __importStar(__nccwpck_require__(78227)); const node_fs_1 = __nccwpck_require__(87561); const node_path_1 = __nccwpck_require__(49411); -const setup_approval_workflow_1 = __nccwpck_require__(56189); +const setup_approval_workflow_1 = __nccwpck_require__(9512); class GithubSetupApprovalReadinessAdapter { async inspect(owner, repository, setupToken, configuration) { const octokit = github.getOctokit(setupToken); @@ -84331,7 +84331,7 @@ function array(value) { return Array.isArray(value) ? value : []; } /***/ }), -/***/ 23702: +/***/ 47020: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -84468,7 +84468,7 @@ function safeMessage(error) { /***/ }), -/***/ 57550: +/***/ 56098: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -84528,7 +84528,7 @@ exports.SetupGithubIdentityQueryAdapter = SetupGithubIdentityQueryAdapter; /***/ }), -/***/ 37570: +/***/ 1489: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -84570,9 +84570,9 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SetupRemoteCredentialHealthBootstrapAdapter = exports.SetupRemoteCredentialHealthQueryAdapter = void 0; const node_fs_1 = __nccwpck_require__(87561); const path = __importStar(__nccwpck_require__(49411)); -const setup_workflow_catalog_1 = __nccwpck_require__(37008); -const deployment_configuration_1 = __nccwpck_require__(5664); -const credential_health_workflow_visibility_1 = __nccwpck_require__(85071); +const setup_workflow_catalog_1 = __nccwpck_require__(24596); +const deployment_configuration_1 = __nccwpck_require__(22495); +const credential_health_workflow_visibility_1 = __nccwpck_require__(57628); const WORKFLOW_ID = setup_workflow_catalog_1.SETUP_CREDENTIAL_HEALTH_WORKFLOW_FILE; const INPUT_BY_SECRET = { PAT: 'check_pat', @@ -84828,16 +84828,16 @@ function readHealthWorkflow() { /***/ }), -/***/ 90378: +/***/ 67758: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SetupTokenPermissionQueryAdapter = void 0; -const github_error_policy_1 = __nccwpck_require__(26189); -const bounded_concurrency_policy_1 = __nccwpck_require__(50189); -const setup_token_permission_evidence_policy_1 = __nccwpck_require__(19750); +const github_error_policy_1 = __nccwpck_require__(58791); +const bounded_concurrency_policy_1 = __nccwpck_require__(35596); +const setup_token_permission_evidence_policy_1 = __nccwpck_require__(65640); const SETUP_PERMISSION_PROBE_CONCURRENCY = 4; const MAX_GITHUB_DEFAULT_BRANCH_LENGTH = 255; /** Maps safe GitHub reads to semantic permission evidence without test mutations. */ @@ -85135,16 +85135,16 @@ function repositoryRoot(owner, repository) { /***/ }), -/***/ 23376: +/***/ 5729: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SetupReconcileWorkspaceAdapter = exports.SetupDoctorWorkspaceQueryAdapter = exports.SetupWorkspaceMutationAdapter = void 0; -const setup_files_1 = __nccwpck_require__(30542); -const cli_context_1 = __nccwpck_require__(34760); -const repository_agent_guidance_1 = __nccwpck_require__(45689); +const setup_files_1 = __nccwpck_require__(59126); +const cli_context_1 = __nccwpck_require__(21307); +const repository_agent_guidance_1 = __nccwpck_require__(38445); class SetupWorkspaceMutationAdapter { prepare(selection) { const workspace = process.cwd(); @@ -85203,7 +85203,7 @@ exports.SetupReconcileWorkspaceAdapter = SetupReconcileWorkspaceAdapter; /***/ }), -/***/ 62557: +/***/ 86457: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -85220,7 +85220,7 @@ exports.SystemIssueInactivityClockAdapter = SystemIssueInactivityClockAdapter; /***/ }), -/***/ 86649: +/***/ 32679: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -85237,7 +85237,7 @@ exports.SystemWorkflowPollingRandomAdapter = SystemWorkflowPollingRandomAdapter; /***/ }), -/***/ 89437: +/***/ 49664: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -85254,7 +85254,7 @@ exports.SystemWorkflowQueueClockAdapter = SystemWorkflowQueueClockAdapter; /***/ }), -/***/ 20015: +/***/ 20846: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -85274,7 +85274,7 @@ exports.TimerBranchPropagationDelayAdapter = TimerBranchPropagationDelayAdapter; /***/ }), -/***/ 25288: +/***/ 71942: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -85291,7 +85291,7 @@ exports.TimerDelayAdapter = TimerDelayAdapter; /***/ }), -/***/ 283: +/***/ 10339: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -85308,7 +85308,7 @@ exports.TimerWorkflowPollingDelayAdapter = TimerWorkflowPollingDelayAdapter; /***/ }), -/***/ 57156: +/***/ 2304: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -85363,15 +85363,15 @@ function prepareUntrustedCommandEnvironment(source = process.env) { /***/ }), -/***/ 93855: +/***/ 92540: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.ContentInterface = void 0; -const logger_1 = __nccwpck_require__(50135); -const application_error_1 = __nccwpck_require__(2965); +const logger_1 = __nccwpck_require__(91151); +const application_error_1 = __nccwpck_require__(75999); class ContentInterface { constructor() { this.getContent = (description) => { @@ -85460,16 +85460,16 @@ exports.ContentInterface = ContentInterface; /***/ }), -/***/ 58588: +/***/ 60608: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.IssueContentInterface = void 0; -const logger_1 = __nccwpck_require__(50135); -const content_interface_1 = __nccwpck_require__(93855); -const application_error_1 = __nccwpck_require__(2965); +const logger_1 = __nccwpck_require__(91151); +const content_interface_1 = __nccwpck_require__(92540); +const application_error_1 = __nccwpck_require__(75999); class IssueContentInterface extends content_interface_1.ContentInterface { constructor(issueDescriptionPort) { super(); @@ -85506,18 +85506,18 @@ exports.IssueContentInterface = IssueContentInterface; /***/ }), -/***/ 51068: +/***/ 40188: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.ConfigurationHandler = void 0; -const config_1 = __nccwpck_require__(98013); -const logger_1 = __nccwpck_require__(50135); -const issue_content_interface_1 = __nccwpck_require__(58588); -const configuration_payload_policy_1 = __nccwpck_require__(23509); -const application_error_1 = __nccwpck_require__(2965); +const config_1 = __nccwpck_require__(90450); +const logger_1 = __nccwpck_require__(91151); +const issue_content_interface_1 = __nccwpck_require__(60608); +const configuration_payload_policy_1 = __nccwpck_require__(58043); +const application_error_1 = __nccwpck_require__(75999); class ConfigurationHandler extends issue_content_interface_1.IssueContentInterface { constructor() { super(...arguments); @@ -85553,14 +85553,14 @@ exports.ConfigurationHandler = ConfigurationHandler; /***/ }), -/***/ 23509: +/***/ 58043: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildConfigurationPayload = buildConfigurationPayload; -const config_1 = __nccwpck_require__(98013); +const config_1 = __nccwpck_require__(90450); function buildConfigurationPayload(execution, storedRaw) { const current = execution.currentConfiguration; const stored = parseStoredConfiguration(storedRaw); @@ -85610,7 +85610,7 @@ function mergeMissingValues(payload, stored) { /***/ }), -/***/ 91606: +/***/ 49029: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -85621,7 +85621,7 @@ exports.getAnswerIssueHelpPrompt = getAnswerIssueHelpPrompt; * Prompt for the initial reply when a user opens a question/help issue. * Filled by the prompt provider; use getAnswerIssueHelpPrompt(). */ -const fill_1 = __nccwpck_require__(58865); +const fill_1 = __nccwpck_require__(2559); const TEMPLATE = `The user has just opened a question/help issue. Provide a helpful initial response to their question or request below. Be concise and actionable. Write every human-readable sentence in {{targetLocale}} while preserving code identifiers, paths, refs, commands, and URLs verbatim. Return a JSON object with \`outputLocale\` set exactly to \`{{targetLocale}}\` and \`answer\` containing the Markdown response. @@ -85645,14 +85645,14 @@ function getAnswerIssueHelpPrompt(params) { /***/ }), -/***/ 93843: +/***/ 84434: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.getBranchSyncConflictsPrompt = getBranchSyncConflictsPrompt; -const fill_1 = __nccwpck_require__(58865); +const fill_1 = __nccwpck_require__(2559); const TEMPLATE = `You are resolving a merge that is already in progress in {{owner}}/{{repo}}. Parent branch: {{parentBranch}} @@ -85668,7 +85668,7 @@ function getBranchSyncConflictsPrompt(params) { /***/ }), -/***/ 69433: +/***/ 56998: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -85678,7 +85678,7 @@ exports.getBugbotPrompt = getBugbotPrompt; /** * Prompt for Bugbot detection (detect potential problems on push). */ -const fill_1 = __nccwpck_require__(58865); +const fill_1 = __nccwpck_require__(2559); const TEMPLATE = `You are analyzing the latest code changes for potential bugs and issues. Write every human-readable finding title, description, evidence, and suggestion in {{targetLocale}}. Preserve identifiers, code, symbols, paths, refs, commands, and URLs verbatim. Echo \`outputLocale\` exactly as \`{{targetLocale}}\`. @@ -85734,7 +85734,7 @@ function getBugbotPrompt(params) { /***/ }), -/***/ 17929: +/***/ 37925: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -85744,8 +85744,8 @@ exports.getBugbotFixPrompt = getBugbotFixPrompt; /** * Prompt for Bugbot autofix (fix selected findings in workspace). */ -const fill_1 = __nccwpck_require__(58865); -const untrusted_content_1 = __nccwpck_require__(12334); +const fill_1 = __nccwpck_require__(2559); +const untrusted_content_1 = __nccwpck_require__(67057); const TEMPLATE = `${untrusted_content_1.UNTRUSTED_CONTENT_POLICY} You are in the repository workspace. Your task is to fix the reported code findings (bugs, vulnerabilities, or quality issues) listed below, and only those. The user has explicitly requested these fixes. @@ -85784,7 +85784,7 @@ function getBugbotFixPrompt(params) { /***/ }), -/***/ 56693: +/***/ 10399: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -85794,7 +85794,7 @@ exports.getBugbotFixIntentPrompt = getBugbotFixIntentPrompt; /** * Prompt for detecting the action requested by a user comment. */ -const fill_1 = __nccwpck_require__(58865); +const fill_1 = __nccwpck_require__(2559); const TEMPLATE = `You are analyzing a user comment on an issue or pull request to classify the requested Copilot action. The available actions are: fix reported findings, apply a general repository change, run a read-only code review, or answer a question. {{projectContextInstruction}} @@ -85819,7 +85819,7 @@ function getBugbotFixIntentPrompt(params) { /***/ }), -/***/ 39837: +/***/ 63425: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -85827,7 +85827,7 @@ function getBugbotFixIntentPrompt(params) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.getAdaptCommentLanguagePrompt = getAdaptCommentLanguagePrompt; /** Builds the single, schema-constrained request adaptation prompt. */ -const fill_1 = __nccwpck_require__(58865); +const fill_1 = __nccwpck_require__(2559); const ADAPT_TEMPLATE = ` You adapt user-provided prose to {{locale}} for internal interpretation. @@ -85854,7 +85854,7 @@ function getAdaptCommentLanguagePrompt(params) { /***/ }), -/***/ 30975: +/***/ 74623: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -85864,7 +85864,7 @@ exports.getCheckProgressPrompt = getCheckProgressPrompt; /** * Prompt for assessing issue progress from branch diff (CheckProgressUseCase). */ -const fill_1 = __nccwpck_require__(58865); +const fill_1 = __nccwpck_require__(2559); const TEMPLATE = `You are in the repository workspace. Assess the progress of issue #{{issueNumber}} using the full diff between the base (parent) branch and the current branch. Write every human-readable sentence in {{targetLocale}}. Preserve code identifiers, paths, refs, commands, URLs, percentages, and JSON keys verbatim. Echo \`outputLocale\` exactly as \`{{targetLocale}}\`. @@ -85899,7 +85899,7 @@ function getCheckProgressPrompt(params) { /***/ }), -/***/ 41306: +/***/ 32506: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -85909,7 +85909,7 @@ exports.getCliDoPrompt = getCliDoPrompt; /** * Prompt for CLI "copilot do" command: project context + user prompt. */ -const fill_1 = __nccwpck_require__(58865); +const fill_1 = __nccwpck_require__(2559); const TEMPLATE = `{{projectContextInstruction}} {{userPrompt}}`; @@ -85920,7 +85920,7 @@ function getCliDoPrompt(params) { /***/ }), -/***/ 58865: +/***/ 2559: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -85931,7 +85931,7 @@ exports.fillTemplate = fillTemplate; * Replaces {{paramName}} placeholders in a template with values from params. * Missing keys are left as {{paramName}}. */ -const untrusted_content_1 = __nccwpck_require__(12334); +const untrusted_content_1 = __nccwpck_require__(67057); const UNTRUSTED_TEMPLATE_KEYS = new Set([ 'commentBody', 'description', @@ -85973,7 +85973,7 @@ function fillTemplate(template, params) { /***/ }), -/***/ 71854: +/***/ 69518: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -85985,40 +85985,40 @@ exports.getPrompt = getPrompt; * Prompt provider: one file per prompt, each exports a getter that fills the template with params. * Use getPrompt(name, params) for a generic call or import the typed getter (e.g. getAnswerIssueHelpPrompt). */ -const answer_issue_help_1 = __nccwpck_require__(91606); -const think_1 = __nccwpck_require__(18366); -const update_pull_request_description_1 = __nccwpck_require__(16030); -const user_request_1 = __nccwpck_require__(50929); -const recommend_steps_1 = __nccwpck_require__(49700); -const check_progress_1 = __nccwpck_require__(30975); -const check_comment_language_1 = __nccwpck_require__(39837); -const cli_do_1 = __nccwpck_require__(41306); -const bugbot_1 = __nccwpck_require__(69433); -const bugbot_fix_1 = __nccwpck_require__(17929); -const bugbot_fix_intent_1 = __nccwpck_require__(56693); -var fill_1 = __nccwpck_require__(58865); +const answer_issue_help_1 = __nccwpck_require__(49029); +const think_1 = __nccwpck_require__(43146); +const update_pull_request_description_1 = __nccwpck_require__(10063); +const user_request_1 = __nccwpck_require__(63103); +const recommend_steps_1 = __nccwpck_require__(69039); +const check_progress_1 = __nccwpck_require__(74623); +const check_comment_language_1 = __nccwpck_require__(63425); +const cli_do_1 = __nccwpck_require__(32506); +const bugbot_1 = __nccwpck_require__(56998); +const bugbot_fix_1 = __nccwpck_require__(37925); +const bugbot_fix_intent_1 = __nccwpck_require__(10399); +var fill_1 = __nccwpck_require__(2559); Object.defineProperty(exports, "fillTemplate", ({ enumerable: true, get: function () { return fill_1.fillTemplate; } })); -var answer_issue_help_2 = __nccwpck_require__(91606); +var answer_issue_help_2 = __nccwpck_require__(49029); Object.defineProperty(exports, "getAnswerIssueHelpPrompt", ({ enumerable: true, get: function () { return answer_issue_help_2.getAnswerIssueHelpPrompt; } })); -var think_2 = __nccwpck_require__(18366); +var think_2 = __nccwpck_require__(43146); Object.defineProperty(exports, "getThinkPrompt", ({ enumerable: true, get: function () { return think_2.getThinkPrompt; } })); -var update_pull_request_description_2 = __nccwpck_require__(16030); +var update_pull_request_description_2 = __nccwpck_require__(10063); Object.defineProperty(exports, "getUpdatePullRequestDescriptionPrompt", ({ enumerable: true, get: function () { return update_pull_request_description_2.getUpdatePullRequestDescriptionPrompt; } })); -var user_request_2 = __nccwpck_require__(50929); +var user_request_2 = __nccwpck_require__(63103); Object.defineProperty(exports, "getUserRequestPrompt", ({ enumerable: true, get: function () { return user_request_2.getUserRequestPrompt; } })); -var recommend_steps_2 = __nccwpck_require__(49700); +var recommend_steps_2 = __nccwpck_require__(69039); Object.defineProperty(exports, "getRecommendStepsPrompt", ({ enumerable: true, get: function () { return recommend_steps_2.getRecommendStepsPrompt; } })); -var check_progress_2 = __nccwpck_require__(30975); +var check_progress_2 = __nccwpck_require__(74623); Object.defineProperty(exports, "getCheckProgressPrompt", ({ enumerable: true, get: function () { return check_progress_2.getCheckProgressPrompt; } })); -var check_comment_language_2 = __nccwpck_require__(39837); +var check_comment_language_2 = __nccwpck_require__(63425); Object.defineProperty(exports, "getAdaptCommentLanguagePrompt", ({ enumerable: true, get: function () { return check_comment_language_2.getAdaptCommentLanguagePrompt; } })); -var cli_do_2 = __nccwpck_require__(41306); +var cli_do_2 = __nccwpck_require__(32506); Object.defineProperty(exports, "getCliDoPrompt", ({ enumerable: true, get: function () { return cli_do_2.getCliDoPrompt; } })); -var bugbot_2 = __nccwpck_require__(69433); +var bugbot_2 = __nccwpck_require__(56998); Object.defineProperty(exports, "getBugbotPrompt", ({ enumerable: true, get: function () { return bugbot_2.getBugbotPrompt; } })); -var bugbot_fix_2 = __nccwpck_require__(17929); +var bugbot_fix_2 = __nccwpck_require__(37925); Object.defineProperty(exports, "getBugbotFixPrompt", ({ enumerable: true, get: function () { return bugbot_fix_2.getBugbotFixPrompt; } })); -var bugbot_fix_intent_2 = __nccwpck_require__(56693); +var bugbot_fix_intent_2 = __nccwpck_require__(10399); Object.defineProperty(exports, "getBugbotFixIntentPrompt", ({ enumerable: true, get: function () { return bugbot_fix_intent_2.getBugbotFixIntentPrompt; } })); /** Known prompt names for getPrompt() */ exports.PROMPT_NAMES = { @@ -86061,14 +86061,14 @@ function getPrompt(name, params) { /***/ }), -/***/ 22907: +/***/ 64005: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.getLocalizeMessageCatalogPrompt = getLocalizeMessageCatalogPrompt; -const message_catalog_1 = __nccwpck_require__(5313); +const message_catalog_1 = __nccwpck_require__(27097); /** Builds a bounded request that translates prose values, never renderer structure. */ function getLocalizeMessageCatalogPrompt(params) { const pluralCategories = (0, message_catalog_1.catalogPluralCategories)(params.targetLocale); @@ -86087,7 +86087,7 @@ function getLocalizeMessageCatalogPrompt(params) { /***/ }), -/***/ 49700: +/***/ 69039: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -86097,7 +86097,7 @@ exports.getRecommendStepsPrompt = getRecommendStepsPrompt; /** * Prompt for recommending implementation steps from an issue (RecommendStepsUseCase). */ -const fill_1 = __nccwpck_require__(58865); +const fill_1 = __nccwpck_require__(2559); const TEMPLATE = `Based on the following issue description, produce a concise implementation plan. Return three to eight logically ordered steps (for example: contract, implementation, tests, and documentation). Each step needs a short action title and zero to two brief supporting details. Add one specific, verifiable acceptance criterion for the whole plan. Write every human-readable field in {{targetLocale}}. Preserve code identifiers, repository-relative paths, refs, and commands verbatim. Do not write Markdown or headings inside fields; the product owns presentation. Echo \`outputLocale\` exactly as \`{{targetLocale}}\`. @@ -86136,7 +86136,7 @@ function previousRecommendationInstruction(format) { /***/ }), -/***/ 18366: +/***/ 43146: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -86146,7 +86146,7 @@ exports.getThinkPrompt = getThinkPrompt; /** * Prompt for the Think use case (answer to @mention in issue/PR comment). */ -const fill_1 = __nccwpck_require__(58865); +const fill_1 = __nccwpck_require__(2559); const TEMPLATE = `You are a helpful assistant. Answer the following question concisely in {{targetLocale}}, using the context below when relevant. Format your answer in **markdown** (headings, lists, code blocks where useful) so it is easy to read. Do not include the question in your response. Preserve code identifiers, paths, refs, commands, and URLs verbatim. Return a JSON object with \`outputLocale\` set exactly to \`{{targetLocale}}\` and \`answer\` containing the Markdown response. Every human-readable sentence in \`answer\` must use the target locale. @@ -86165,7 +86165,7 @@ function getThinkPrompt(params) { /***/ }), -/***/ 16030: +/***/ 10063: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -86175,7 +86175,7 @@ exports.getUpdatePullRequestDescriptionPrompt = getUpdatePullRequestDescriptionP /** * Prompt for generating a concise PR description from an optional issue and the diff. */ -const fill_1 = __nccwpck_require__(58865); +const fill_1 = __nccwpck_require__(2559); const TEMPLATE = `You are in the repository workspace. Your task is to write a concise, review-ready pull request description from the branch diff and any linked issue. Write every human-readable sentence in {{targetLocale}}. Preserve code identifiers, paths, refs, commands, URLs, issue/PR references, and conventional title prefixes verbatim. Echo \`outputLocale\` exactly as \`{{targetLocale}}\`. @@ -86216,7 +86216,7 @@ function getUpdatePullRequestDescriptionPrompt(params) { /***/ }), -/***/ 50929: +/***/ 63103: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -86226,7 +86226,7 @@ exports.getUserRequestPrompt = getUserRequestPrompt; /** * Prompt for the Do user request use case (generic "do this" in repo). */ -const fill_1 = __nccwpck_require__(58865); +const fill_1 = __nccwpck_require__(2559); const TEMPLATE = `You are in the repository workspace. The user has asked you to do something. Perform their request by editing files and running commands directly in the workspace. Do not output diffs for someone else to apply. {{projectContextInstruction}} @@ -86252,7 +86252,7 @@ function getUserRequestPrompt(params) { /***/ }), -/***/ 72472: +/***/ 63550: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -86482,7 +86482,7 @@ function round(value) { /***/ }), -/***/ 2141: +/***/ 2899: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -86492,7 +86492,7 @@ exports.loadBugbotBenchmark = loadBugbotBenchmark; exports.loadBugbotPredictions = loadBugbotPredictions; exports.evaluateBugbotBenchmark = evaluateBugbotBenchmark; const promises_1 = __nccwpck_require__(93977); -const bugbot_quality_eval_1 = __nccwpck_require__(70846); +const bugbot_quality_eval_1 = __nccwpck_require__(15467); async function loadBugbotBenchmark(path) { const parsed = JSON.parse(await (0, promises_1.readFile)(path, 'utf8')); if (!isRecord(parsed) || parsed.schemaVersion !== 1 || !Array.isArray(parsed.cases)) { @@ -86583,7 +86583,7 @@ function isRecord(value) { /***/ }), -/***/ 38817: +/***/ 19235: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -86591,9 +86591,9 @@ function isRecord(value) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.runBugbotBenchmarkAgent = runBugbotBenchmarkAgent; exports.buildBugbotBenchmarkPrompt = buildBugbotBenchmarkPrompt; -const schema_1 = __nccwpck_require__(98135); -const untrusted_content_1 = __nccwpck_require__(12334); -const prepare_bugbot_findings_policy_1 = __nccwpck_require__(73654); +const schema_1 = __nccwpck_require__(16808); +const untrusted_content_1 = __nccwpck_require__(67057); +const prepare_bugbot_findings_policy_1 = __nccwpck_require__(3496); const MAX_BENCHMARK_CASES = 200; /** Executes the real configured findings agent against every case, sequentially. */ async function runBugbotBenchmarkAgent(corpus, agent, configuration) { @@ -86646,7 +86646,7 @@ function extractBenchmarkFindings(response) { /***/ }), -/***/ 70846: +/***/ 15467: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -86655,7 +86655,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.DEFAULT_BUGBOT_QUALITY_THRESHOLDS = void 0; exports.evaluateBugbotFindings = evaluateBugbotFindings; exports.evaluateBugbotQualityGate = evaluateBugbotQualityGate; -const finding_identity_1 = __nccwpck_require__(657); +const finding_identity_1 = __nccwpck_require__(91853); exports.DEFAULT_BUGBOT_QUALITY_THRESHOLDS = { precision: 0.9, recall: 0.85, @@ -86763,7 +86763,7 @@ function format(value) { /***/ }), -/***/ 61146: +/***/ 92816: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -86815,7 +86815,7 @@ exports.injectJsonAsMarkdownBlock = injectJsonAsMarkdownBlock; /***/ }), -/***/ 50135: +/***/ 91151: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -86833,7 +86833,7 @@ exports.logError = logError; exports.logDebugInfo = logDebugInfo; exports.logDebugWarning = logDebugWarning; exports.logDebugError = logDebugError; -const secret_redaction_1 = __nccwpck_require__(93523); +const secret_redaction_1 = __nccwpck_require__(254); let loggerDebug = false; let loggerRemote = false; let structuredLogging = false; @@ -86996,7 +86996,7 @@ function logDebugError(message) { /***/ }), -/***/ 36158: +/***/ 63907: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -87009,7 +87009,7 @@ exports.PROJECT_CONTEXT_INSTRUCTION = `**Important – use full project context: /***/ }), -/***/ 45689: +/***/ 38445: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -87053,9 +87053,9 @@ exports.inspectRepositoryAgentGuidance = inspectRepositoryAgentGuidance; const fs = __importStar(__nccwpck_require__(87561)); const path = __importStar(__nccwpck_require__(49411)); const node_crypto_1 = __nccwpck_require__(6005); -const issue_workflow_profile_1 = __nccwpck_require__(62721); -const repository_agent_guidance_policy_1 = __nccwpck_require__(47992); -const logger_1 = __nccwpck_require__(50135); +const issue_workflow_profile_1 = __nccwpck_require__(26744); +const repository_agent_guidance_policy_1 = __nccwpck_require__(67402); +const logger_1 = __nccwpck_require__(91151); const MANAGED_ROLES = Object.freeze({ [repository_agent_guidance_policy_1.REPOSITORY_AGENT_PROFILE_PATH]: 'profile', [repository_agent_guidance_policy_1.REPOSITORY_AGENT_GUIDE_PATH]: 'guide', @@ -87474,7 +87474,7 @@ function inspectIssueWorkflowProjection(cwd, configuration) { /***/ }), -/***/ 93523: +/***/ 254: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -87503,7 +87503,7 @@ function redactKnownEnvironmentSecrets(value, environment = process.env) { /***/ }), -/***/ 80501: +/***/ 90102: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -87546,7 +87546,7 @@ exports.copySetupFile = copySetupFile; exports.copySetupDirectory = copySetupDirectory; const fs = __importStar(__nccwpck_require__(57147)); const path = __importStar(__nccwpck_require__(71017)); -const logger_1 = __nccwpck_require__(50135); +const logger_1 = __nccwpck_require__(91151); function copySetupFile(source, destination, displaySource, displayDestination, options = {}) { if (!fs.existsSync(source)) return { copied: 0, skipped: 0 }; @@ -87578,7 +87578,7 @@ function copySetupDirectory(sourceDirectory, destinationDirectory, fileFilter, d /***/ }), -/***/ 30542: +/***/ 59126: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -87624,13 +87624,13 @@ exports.getSetupToken = getSetupToken; exports.hasValidSetupToken = hasValidSetupToken; const fs = __importStar(__nccwpck_require__(57147)); const path = __importStar(__nccwpck_require__(71017)); -const setup_file_copy_1 = __nccwpck_require__(80501); -const logger_1 = __nccwpck_require__(50135); -const setup_workflow_catalog_1 = __nccwpck_require__(37008); -const issue_workflow_profile_1 = __nccwpck_require__(62721); -const setup_issue_workflow_policy_1 = __nccwpck_require__(47280); -const repository_agent_guidance_1 = __nccwpck_require__(45689); -const setup_approval_workflow_1 = __nccwpck_require__(56189); +const setup_file_copy_1 = __nccwpck_require__(90102); +const logger_1 = __nccwpck_require__(91151); +const setup_workflow_catalog_1 = __nccwpck_require__(24596); +const issue_workflow_profile_1 = __nccwpck_require__(26744); +const setup_issue_workflow_policy_1 = __nccwpck_require__(81182); +const repository_agent_guidance_1 = __nccwpck_require__(38445); +const setup_approval_workflow_1 = __nccwpck_require__(9512); /** * Ensure .github, .github/workflows and .github/ISSUE_TEMPLATE exist; create them if missing. * @param cwd - Directory (repo root) @@ -87890,7 +87890,7 @@ function hasValidSetupToken(cwd, override) { /***/ }), -/***/ 83142: +/***/ 46103: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -87960,14 +87960,14 @@ function getTaskEmoji(taskId) { /***/ }), -/***/ 58747: +/***/ 46267: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.extractIssueNumberFromPush = exports.extractIssueNumberFromBranch = void 0; -const positive_integer_policy_1 = __nccwpck_require__(45613); +const positive_integer_policy_1 = __nccwpck_require__(19879); const extractIssueNumberFromBranch = (branchName) => { const match = branchName?.match(/[a-zA-Z]+\/([0-9]+)-.*/); if (match) { @@ -87988,7 +87988,7 @@ exports.extractIssueNumberFromPush = extractIssueNumberFromPush; /***/ }), -/***/ 61047: +/***/ 61788: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -88032,7 +88032,7 @@ exports.getActionInputs = getActionInputs; exports.getActionInputsWithDefaults = getActionInputsWithDefaults; const fs = __importStar(__nccwpck_require__(57147)); const path = __importStar(__nccwpck_require__(71017)); -const yaml = __importStar(__nccwpck_require__(87969)); +const yaml = __importStar(__nccwpck_require__(783)); /** * Resolves action.yml from the copilot package root, not cwd. * When run as CLI from another repo, cwd is that repo; action.yml lives next to the bundle. @@ -88388,14 +88388,14 @@ module.exports = require("util"); /***/ }), -/***/ 37579: +/***/ 12239: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { -const { Argument } = __nccwpck_require__(99938); -const { Command } = __nccwpck_require__(26777); -const { CommanderError, InvalidArgumentError } = __nccwpck_require__(95989); -const { Help } = __nccwpck_require__(16706); -const { Option } = __nccwpck_require__(47969); +const { Argument } = __nccwpck_require__(62253); +const { Command } = __nccwpck_require__(51335); +const { CommanderError, InvalidArgumentError } = __nccwpck_require__(5022); +const { Help } = __nccwpck_require__(10320); +const { Option } = __nccwpck_require__(2430); exports.program = new Command(); @@ -88419,10 +88419,10 @@ exports.InvalidOptionArgumentError = InvalidArgumentError; // Deprecated /***/ }), -/***/ 99938: +/***/ 62253: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { -const { InvalidArgumentError } = __nccwpck_require__(95989); +const { InvalidArgumentError } = __nccwpck_require__(5022); class Argument { /** @@ -88575,7 +88575,7 @@ exports.humanReadableArgName = humanReadableArgName; /***/ }), -/***/ 26777: +/***/ 51335: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { const EventEmitter = (__nccwpck_require__(15673).EventEmitter); @@ -88584,11 +88584,11 @@ const path = __nccwpck_require__(49411); const fs = __nccwpck_require__(87561); const process = __nccwpck_require__(97742); -const { Argument, humanReadableArgName } = __nccwpck_require__(99938); -const { CommanderError } = __nccwpck_require__(95989); -const { Help } = __nccwpck_require__(16706); -const { Option, DualOptions } = __nccwpck_require__(47969); -const { suggestSimilar } = __nccwpck_require__(78149); +const { Argument, humanReadableArgName } = __nccwpck_require__(62253); +const { CommanderError } = __nccwpck_require__(5022); +const { Help } = __nccwpck_require__(10320); +const { Option, DualOptions } = __nccwpck_require__(2430); +const { suggestSimilar } = __nccwpck_require__(57754); class Command extends EventEmitter { /** @@ -91091,7 +91091,7 @@ exports.Command = Command; /***/ }), -/***/ 95989: +/***/ 5022: /***/ ((__unused_webpack_module, exports) => { /** @@ -91137,10 +91137,10 @@ exports.InvalidArgumentError = InvalidArgumentError; /***/ }), -/***/ 16706: +/***/ 10320: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { -const { humanReadableArgName } = __nccwpck_require__(99938); +const { humanReadableArgName } = __nccwpck_require__(62253); /** * TypeScript import types for JSDoc, used by Visual Studio Code IntelliSense and `npm run typescript-checkJS` @@ -91664,10 +91664,10 @@ exports.Help = Help; /***/ }), -/***/ 47969: +/***/ 2430: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { -const { InvalidArgumentError } = __nccwpck_require__(95989); +const { InvalidArgumentError } = __nccwpck_require__(5022); class Option { /** @@ -92001,7 +92001,7 @@ exports.DualOptions = DualOptions; /***/ }), -/***/ 78149: +/***/ 57754: /***/ ((__unused_webpack_module, exports) => { const maxDistance = 3; @@ -92109,7 +92109,7 @@ exports.suggestSimilar = suggestSimilar; /***/ }), -/***/ 47743: +/***/ 29617: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { /* module decorator */ module = __nccwpck_require__.nmd(module); @@ -92411,7 +92411,7 @@ module.exports = { /***/ }), -/***/ 47216: +/***/ 922: /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __nccwpck_require__) => { "use strict"; @@ -92423,7 +92423,7 @@ __nccwpck_require__.d(__webpack_exports__, { "Octokit": () => (/* binding */ Octokit) }); -;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/universal-user-agent@7.0.3/node_modules/universal-user-agent/index.js +;// CONCATENATED MODULE: ./node_modules/.pnpm/universal-user-agent@7.0.3/node_modules/universal-user-agent/index.js function getUserAgent() { if (typeof navigator === "object" && "userAgent" in navigator) { return navigator.userAgent; @@ -92438,7 +92438,7 @@ function getUserAgent() { return ""; } -;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/before-after-hook@4.0.0/node_modules/before-after-hook/lib/register.js +;// CONCATENATED MODULE: ./node_modules/.pnpm/before-after-hook@4.0.0/node_modules/before-after-hook/lib/register.js // @ts-check function register(state, name, method, options) { @@ -92467,7 +92467,7 @@ function register(state, name, method, options) { }); } -;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/before-after-hook@4.0.0/node_modules/before-after-hook/lib/add.js +;// CONCATENATED MODULE: ./node_modules/.pnpm/before-after-hook@4.0.0/node_modules/before-after-hook/lib/add.js // @ts-check function addHook(state, kind, name, hook) { @@ -92515,7 +92515,7 @@ function addHook(state, kind, name, hook) { }); } -;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/before-after-hook@4.0.0/node_modules/before-after-hook/lib/remove.js +;// CONCATENATED MODULE: ./node_modules/.pnpm/before-after-hook@4.0.0/node_modules/before-after-hook/lib/remove.js // @ts-check function removeHook(state, name, method) { @@ -92536,7 +92536,7 @@ function removeHook(state, name, method) { state.registry[name].splice(index, 1); } -;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/before-after-hook@4.0.0/node_modules/before-after-hook/index.js +;// CONCATENATED MODULE: ./node_modules/.pnpm/before-after-hook@4.0.0/node_modules/before-after-hook/index.js // @ts-check @@ -92583,7 +92583,7 @@ function Collection() { /* harmony default export */ const before_after_hook = ({ Singular, Collection }); -;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/@octokit+endpoint@11.0.5/node_modules/@octokit/endpoint/dist-bundle/index.js +;// CONCATENATED MODULE: ./node_modules/.pnpm/@octokit+endpoint@11.0.5/node_modules/@octokit/endpoint/dist-bundle/index.js // pkg/dist-src/defaults.js @@ -92929,7 +92929,7 @@ function withDefaults(oldDefaults, newDefaults) { var endpoint = withDefaults(null, DEFAULTS); -;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/content-type@3.0.0/node_modules/content-type/dist/index.js +;// CONCATENATED MODULE: ./node_modules/.pnpm/content-type@3.0.0/node_modules/content-type/dist/index.js /*! * content-type * Copyright(c) 2015 Douglas Christopher Wilson @@ -93102,7 +93102,7 @@ function qstring(str) { throw new TypeError(`Invalid parameter value: ${str}`); } //# sourceMappingURL=index.js.map -;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/json-with-bigint@3.5.12/node_modules/json-with-bigint/json-with-bigint.js +;// CONCATENATED MODULE: ./node_modules/.pnpm/json-with-bigint@3.5.12/node_modules/json-with-bigint/json-with-bigint.js const intRegex = /^-?\d+$/; const noiseValue = /^-?\d+n+$/; // Noise - strings that match the custom format before being converted to it const originalStringify = JSON.stringify; @@ -93703,7 +93703,7 @@ const JSONParse = (text, reviver) => { -;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/@octokit+request-error@7.1.2/node_modules/@octokit/request-error/dist-src/index.js +;// CONCATENATED MODULE: ./node_modules/.pnpm/@octokit+request-error@7.1.2/node_modules/@octokit/request-error/dist-src/index.js class RequestError extends Error { name; /** @@ -93744,7 +93744,7 @@ class RequestError extends Error { } -;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/@octokit+request@10.0.16/node_modules/@octokit/request/dist-bundle/index.js +;// CONCATENATED MODULE: ./node_modules/.pnpm/@octokit+request@10.0.16/node_modules/@octokit/request/dist-bundle/index.js // pkg/dist-src/index.js @@ -93951,7 +93951,7 @@ var request = dist_bundle_withDefaults(endpoint, defaults_default); /* v8 ignore next -- @preserve */ /* v8 ignore else -- @preserve */ -;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/@octokit+graphql@9.0.5/node_modules/@octokit/graphql/dist-bundle/index.js +;// CONCATENATED MODULE: ./node_modules/.pnpm/@octokit+graphql@9.0.5/node_modules/@octokit/graphql/dist-bundle/index.js // pkg/dist-src/index.js @@ -94082,7 +94082,7 @@ function withCustomRequest(customRequest) { /* v8 ignore if -- @preserve */ -;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/@octokit+auth-token@6.0.0/node_modules/@octokit/auth-token/dist-bundle/index.js +;// CONCATENATED MODULE: ./node_modules/.pnpm/@octokit+auth-token@6.0.0/node_modules/@octokit/auth-token/dist-bundle/index.js // pkg/dist-src/is-jwt.js var b64url = "(?:[a-zA-Z0-9_-]+)"; var sep = "\\."; @@ -94137,11 +94137,11 @@ var createTokenAuth = function createTokenAuth2(token) { }; -;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/@octokit+core@7.0.8/node_modules/@octokit/core/dist-src/version.js +;// CONCATENATED MODULE: ./node_modules/.pnpm/@octokit+core@7.0.8/node_modules/@octokit/core/dist-src/version.js const version_VERSION = "7.0.8"; -;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/@octokit+core@7.0.8/node_modules/@octokit/core/dist-src/index.js +;// CONCATENATED MODULE: ./node_modules/.pnpm/@octokit+core@7.0.8/node_modules/@octokit/core/dist-src/index.js @@ -94285,7 +94285,7 @@ class Octokit { /***/ }), -/***/ 55347: +/***/ 36738: /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __nccwpck_require__) => { "use strict"; @@ -94710,7 +94710,7 @@ paginateRest.VERSION = VERSION; /***/ }), -/***/ 57496: +/***/ 50305: /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __nccwpck_require__) => { "use strict"; @@ -94723,12 +94723,12 @@ __nccwpck_require__.d(__webpack_exports__, { "restEndpointMethods": () => (/* binding */ restEndpointMethods) }); -;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/@octokit+plugin-rest-endpoint-methods@17.0.0_@octokit+core@7.0.8/node_modules/@octokit/plugin-rest-endpoint-methods/dist-src/version.js +;// CONCATENATED MODULE: ./node_modules/.pnpm/@octokit+plugin-rest-endpoint-methods@17.0.0_@octokit+core@7.0.8/node_modules/@octokit/plugin-rest-endpoint-methods/dist-src/version.js const VERSION = "17.0.0"; //# sourceMappingURL=version.js.map -;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/@octokit+plugin-rest-endpoint-methods@17.0.0_@octokit+core@7.0.8/node_modules/@octokit/plugin-rest-endpoint-methods/dist-src/generated/endpoints.js +;// CONCATENATED MODULE: ./node_modules/.pnpm/@octokit+plugin-rest-endpoint-methods@17.0.0_@octokit+core@7.0.8/node_modules/@octokit/plugin-rest-endpoint-methods/dist-src/generated/endpoints.js const Endpoints = { actions: { addCustomLabelsToSelfHostedRunnerForOrg: [ @@ -97022,7 +97022,7 @@ var endpoints_default = Endpoints; //# sourceMappingURL=endpoints.js.map -;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/@octokit+plugin-rest-endpoint-methods@17.0.0_@octokit+core@7.0.8/node_modules/@octokit/plugin-rest-endpoint-methods/dist-src/endpoints-to-methods.js +;// CONCATENATED MODULE: ./node_modules/.pnpm/@octokit+plugin-rest-endpoint-methods@17.0.0_@octokit+core@7.0.8/node_modules/@octokit/plugin-rest-endpoint-methods/dist-src/endpoints-to-methods.js const endpointMethodsMap = /* @__PURE__ */ new Map(); for (const [scope, endpoints] of Object.entries(endpoints_default)) { @@ -97148,7 +97148,7 @@ function decorate(octokit, scope, methodName, defaults, decorations) { //# sourceMappingURL=endpoints-to-methods.js.map -;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/@octokit+plugin-rest-endpoint-methods@17.0.0_@octokit+core@7.0.8/node_modules/@octokit/plugin-rest-endpoint-methods/dist-src/index.js +;// CONCATENATED MODULE: ./node_modules/.pnpm/@octokit+plugin-rest-endpoint-methods@17.0.0_@octokit+core@7.0.8/node_modules/@octokit/plugin-rest-endpoint-methods/dist-src/index.js function restEndpointMethods(octokit) { @@ -97172,7 +97172,7 @@ legacyRestEndpointMethods.VERSION = VERSION; /***/ }), -/***/ 32634: +/***/ 11652: /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __nccwpck_require__) => { "use strict"; @@ -97187,7 +97187,7 @@ __nccwpck_require__.d(__webpack_exports__, { // EXTERNAL MODULE: external "node:process" var external_node_process_ = __nccwpck_require__(97742); -;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/ansi-regex@6.3.0/node_modules/ansi-regex/index.js +;// CONCATENATED MODULE: ./node_modules/.pnpm/ansi-regex@6.3.0/node_modules/ansi-regex/index.js function ansiRegex({onlyFirst = false} = {}) { // Valid string terminator sequences are BEL, ESC\, and 0x9c const ST = '(?:\\u0007|\\u001B\\u005C|\\u009C)'; @@ -97204,7 +97204,7 @@ function ansiRegex({onlyFirst = false} = {}) { return new RegExp(pattern, onlyFirst ? undefined : 'g'); } -;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/strip-ansi@7.2.0/node_modules/strip-ansi/index.js +;// CONCATENATED MODULE: ./node_modules/.pnpm/strip-ansi@7.2.0/node_modules/strip-ansi/index.js const regex = ansiRegex(); @@ -97225,7 +97225,7 @@ function stripAnsi(string) { return string.replace(regex, ''); } -;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/get-east-asian-width@1.6.0/node_modules/get-east-asian-width/lookup-data.js +;// CONCATENATED MODULE: ./node_modules/.pnpm/get-east-asian-width@1.6.0/node_modules/get-east-asian-width/lookup-data.js // Generated by scripts/build.js const ambiguousMinimalCodePoint = 161; @@ -97248,7 +97248,7 @@ const wideMinimalCodePoint = 4352; const wideMaximumCodePoint = 262141; const wideRanges = [4352, 4447, 8986, 8987, 9001, 9002, 9193, 9196, 9200, 9200, 9203, 9203, 9725, 9726, 9748, 9749, 9776, 9783, 9800, 9811, 9855, 9855, 9866, 9871, 9875, 9875, 9889, 9889, 9898, 9899, 9917, 9918, 9924, 9925, 9934, 9934, 9940, 9940, 9962, 9962, 9970, 9971, 9973, 9973, 9978, 9978, 9981, 9981, 9989, 9989, 9994, 9995, 10024, 10024, 10060, 10060, 10062, 10062, 10067, 10069, 10071, 10071, 10133, 10135, 10160, 10160, 10175, 10175, 11035, 11036, 11088, 11088, 11093, 11093, 11904, 11929, 11931, 12019, 12032, 12245, 12272, 12287, 12289, 12350, 12353, 12438, 12441, 12543, 12549, 12591, 12593, 12686, 12688, 12773, 12783, 12830, 12832, 12871, 12880, 42124, 42128, 42182, 43360, 43388, 44032, 55203, 63744, 64255, 65040, 65049, 65072, 65106, 65108, 65126, 65128, 65131, 94176, 94180, 94192, 94198, 94208, 101589, 101631, 101662, 101760, 101874, 110576, 110579, 110581, 110587, 110589, 110590, 110592, 110882, 110898, 110898, 110928, 110930, 110933, 110933, 110948, 110951, 110960, 111355, 119552, 119638, 119648, 119670, 126980, 126980, 127183, 127183, 127374, 127374, 127377, 127386, 127488, 127490, 127504, 127547, 127552, 127560, 127568, 127569, 127584, 127589, 127744, 127776, 127789, 127797, 127799, 127868, 127870, 127891, 127904, 127946, 127951, 127955, 127968, 127984, 127988, 127988, 127992, 128062, 128064, 128064, 128066, 128252, 128255, 128317, 128331, 128334, 128336, 128359, 128378, 128378, 128405, 128406, 128420, 128420, 128507, 128591, 128640, 128709, 128716, 128716, 128720, 128722, 128725, 128728, 128732, 128735, 128747, 128748, 128756, 128764, 128992, 129003, 129008, 129008, 129292, 129338, 129340, 129349, 129351, 129535, 129648, 129660, 129664, 129674, 129678, 129734, 129736, 129736, 129741, 129756, 129759, 129770, 129775, 129784, 131072, 196605, 196608, 262141]; -;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/get-east-asian-width@1.6.0/node_modules/get-east-asian-width/utilities.js +;// CONCATENATED MODULE: ./node_modules/.pnpm/get-east-asian-width@1.6.0/node_modules/get-east-asian-width/utilities.js /** Binary search on a sorted flat array of [start, end] pairs. @@ -97274,7 +97274,7 @@ const utilities_isInRange = (ranges, codePoint) => { return false; }; -;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/get-east-asian-width@1.6.0/node_modules/get-east-asian-width/lookup.js +;// CONCATENATED MODULE: ./node_modules/.pnpm/get-east-asian-width@1.6.0/node_modules/get-east-asian-width/lookup.js @@ -97394,7 +97394,7 @@ function lookup_getCategory(codePoint) { return 'neutral'; } -;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/get-east-asian-width@1.6.0/node_modules/get-east-asian-width/index.js +;// CONCATENATED MODULE: ./node_modules/.pnpm/get-east-asian-width@1.6.0/node_modules/get-east-asian-width/index.js function validate(codePoint) { @@ -97426,9 +97426,9 @@ function eastAsianWidth(codePoint, {ambiguousAsWide = false} = {}) { // Private exports for https://github.com/sindresorhus/is-fullwidth-code-point -// EXTERNAL MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/emoji-regex@10.6.0/node_modules/emoji-regex/index.js -var emoji_regex = __nccwpck_require__(25863); -;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/string-width@7.2.0/node_modules/string-width/index.js +// EXTERNAL MODULE: ./node_modules/.pnpm/emoji-regex@10.6.0/node_modules/emoji-regex/index.js +var emoji_regex = __nccwpck_require__(33104); +;// CONCATENATED MODULE: ./node_modules/.pnpm/string-width@7.2.0/node_modules/string-width/index.js @@ -97512,9 +97512,9 @@ function stringWidth(string, options = {}) { return width; } -// EXTERNAL MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/index.js + 4 modules -var source = __nccwpck_require__(43920); -;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/widest-line@5.0.0/node_modules/widest-line/index.js +// EXTERNAL MODULE: ./node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/index.js + 4 modules +var source = __nccwpck_require__(8578); +;// CONCATENATED MODULE: ./node_modules/.pnpm/widest-line@5.0.0/node_modules/widest-line/index.js function widestLine(string) { @@ -97527,9 +97527,9 @@ function widestLine(string) { return lineWidth; } -// EXTERNAL MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/cli-boxes@3.0.0/node_modules/cli-boxes/index.js -var cli_boxes = __nccwpck_require__(76291); -;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/camelcase@8.0.0/node_modules/camelcase/index.js +// EXTERNAL MODULE: ./node_modules/.pnpm/cli-boxes@3.0.0/node_modules/cli-boxes/index.js +var cli_boxes = __nccwpck_require__(77755); +;// CONCATENATED MODULE: ./node_modules/.pnpm/camelcase@8.0.0/node_modules/camelcase/index.js const UPPERCASE = /[\p{Lu}]/u; const LOWERCASE = /[\p{Ll}]/u; const LEADING_CAPITAL = /^[\p{Lu}](?![\p{Lu}])/gu; @@ -97641,9 +97641,9 @@ function camelCase(input, options) { return postProcess(input, toUpperCase); } -// EXTERNAL MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/ansi-align@3.0.1/node_modules/ansi-align/index.js -var ansi_align = __nccwpck_require__(6465); -;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/ansi-styles@6.2.3/node_modules/ansi-styles/index.js +// EXTERNAL MODULE: ./node_modules/.pnpm/ansi-align@3.0.1/node_modules/ansi-align/index.js +var ansi_align = __nccwpck_require__(61570); +;// CONCATENATED MODULE: ./node_modules/.pnpm/ansi-styles@6.2.3/node_modules/ansi-styles/index.js const ANSI_BACKGROUND_OFFSET = 10; const wrapAnsi16 = (offset = 0) => code => `\u001B[${code + offset}m`; @@ -97868,7 +97868,7 @@ const ansiStyles = assembleStyles(); /* harmony default export */ const ansi_styles = (ansiStyles); -;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/wrap-ansi@9.0.2/node_modules/wrap-ansi/index.js +;// CONCATENATED MODULE: ./node_modules/.pnpm/wrap-ansi@9.0.2/node_modules/wrap-ansi/index.js @@ -98092,7 +98092,7 @@ function wrapAnsi(string, columns, options) { .join('\n'); } -;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/boxen@8.0.1/node_modules/boxen/index.js +;// CONCATENATED MODULE: ./node_modules/.pnpm/boxen@8.0.1/node_modules/boxen/index.js @@ -98473,7 +98473,7 @@ function boxen(text, options) { /***/ }), -/***/ 43920: +/***/ 8578: /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __nccwpck_require__) => { "use strict"; @@ -98497,7 +98497,7 @@ __nccwpck_require__.d(__webpack_exports__, { "supportsColorStderr": () => (/* binding */ stderrColor) }); -;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/vendor/ansi-styles/index.js +;// CONCATENATED MODULE: ./node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/vendor/ansi-styles/index.js const ANSI_BACKGROUND_OFFSET = 10; const wrapAnsi16 = (offset = 0) => code => `\u001B[${code + offset}m`; @@ -98728,7 +98728,7 @@ var external_node_process_ = __nccwpck_require__(97742); var external_node_os_ = __nccwpck_require__(70612); ;// CONCATENATED MODULE: external "node:tty" const external_node_tty_namespaceObject = require("node:tty"); -;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/vendor/supports-color/index.js +;// CONCATENATED MODULE: ./node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/vendor/supports-color/index.js @@ -98920,7 +98920,7 @@ const supportsColor = { /* harmony default export */ const supports_color = (supportsColor); -;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/utilities.js +;// CONCATENATED MODULE: ./node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/utilities.js // TODO: When targeting Node.js 16, use `String.prototype.replaceAll`. function stringReplaceAll(string, substring, replacer) { let index = string.indexOf(substring); @@ -98955,7 +98955,7 @@ function stringEncaseCRLFWithFirstIndex(string, prefix, postfix, index) { return returnValue; } -;// CONCATENATED MODULE: ../../../Users/efrain.espada@feverup.com/Development/copilot/node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/index.js +;// CONCATENATED MODULE: ./node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/index.js @@ -99168,7 +99168,7 @@ const chalkStderr = createChalk({level: stderrColor ? stderrColor.level : 0}); /***/ }), -/***/ 70106: +/***/ 57227: /***/ ((module) => { "use strict"; @@ -99269,7 +99269,7 @@ module.exports = JSON.parse('{"revision":"2026-09-24.p1-c.3","providers":{"codex /******/ // module cache are used so entry inlining is disabled /******/ // startup /******/ // Load entry module and return exports -/******/ var __webpack_exports__ = __nccwpck_require__(__nccwpck_require__.s = 81627); +/******/ var __webpack_exports__ = __nccwpck_require__(__nccwpck_require__.s = 55711); /******/ module.exports = __webpack_exports__; /******/ /******/ })() From d56896a7e68c6e7aec33d72c1e2026c0486c6ce2 Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Tue, 29 Sep 2026 10:12:32 +0200 Subject: [PATCH 28/50] codex-setup-temporary-github-auth: cover web authorization races and option defaults --- src/__tests__/cli.test.ts | 69 +++++++++++++++++++ .../__tests__/setup_command_options.test.ts | 11 +++ src/cli/__tests__/setup_presenters.test.ts | 13 ++++ src/cli/__tests__/web_setup_adapters.test.ts | 21 ++++++ 4 files changed, 114 insertions(+) diff --git a/src/__tests__/cli.test.ts b/src/__tests__/cli.test.ts index cd4611101..53f7231ce 100644 --- a/src/__tests__/cli.test.ts +++ b/src/__tests__/cli.test.ts @@ -572,6 +572,18 @@ describe('CLI', () => { expect(logError).toHaveBeenCalledWith(expect.objectContaining({ message: expect.stringContaining('Check out a branch') })); }); + it('reports a blocked browser session if launch fails before the journey starts', async () => { + (openWebSetupBrowser as jest.Mock).mockImplementationOnce(() => { throw new Error('Browser launch failed'); }); + const finish = jest.spyOn(WebSetupBridge.prototype, 'finish'); + try { + await program.parseAsync(['node', 'cli', 'setup', '--web']); + expect(finish).toHaveBeenCalledWith('blocked', expect.stringContaining('No further setup changes')); + expect(mockTokenPermissionInspect).not.toHaveBeenCalled(); + expect(runLocalAction).not.toHaveBeenCalled(); + expect(process.exitCode).toBe(1); + } finally { finish.mockRestore(); } + }); + it.each([ [undefined, 130], ['decline', undefined], @@ -668,6 +680,20 @@ describe('CLI', () => { expect(process.exitCode).toBe(130); }); + it.each(['cancelled', 'expired'] as const)('does not Apply after the browser session is %s', async state => { + ask.mockImplementation(async function (this: WebSetupBridge, prompt: WebSetupPrompt) { + if (prompt.title === 'Apply this setup now?') { + if (state === 'cancelled') this.cancel(); + else this.finish('blocked', 'The local session expired.'); + return 'Apply setup'; + } + return answerWebPrompt(prompt); + }); + await program.parseAsync(['node', 'cli', 'setup', '--web', '--pr-approval-mode', 'off', '--skip-secrets']); + expect(runLocalAction).not.toHaveBeenCalled(); + expect(process.exitCode).toBe(state === 'cancelled' ? 130 : 1); + }); + it('reports partial completion when an approved action fails', async () => { (runLocalAction as jest.Mock).mockResolvedValueOnce([{ success: false, errors: ['provider failed'] }]); await program.parseAsync(['node', 'cli', 'setup', '--web', '--pr-approval-mode', 'off', '--skip-secrets']); @@ -710,12 +736,55 @@ describe('CLI', () => { expect(process.exitCode).toBe(1); }); + it('fails closed when the GitHub remote becomes unresolvable just before Apply', async () => { + let remoteReads = 0; + (execSync as jest.Mock).mockImplementation((command: string) => Buffer.from( + command === 'git rev-parse HEAD' ? 'a'.repeat(40) + : command === 'git rev-parse --show-toplevel' ? process.cwd() + : command === 'git symbolic-ref --quiet --short HEAD' ? 'develop' + : command === 'git config --get remote.origin.url' && ++remoteReads > 1 + ? 'not-a-github-remote' : 'https://github.com/test-owner/test-repo.git', + )); + await program.parseAsync(['node', 'cli', 'setup', '--web', '--pr-approval-mode', 'off', '--skip-secrets']); + expect(remoteReads).toBeGreaterThan(1); + expect(runLocalAction).not.toHaveBeenCalled(); + expect(process.exitCode).toBe(1); + }); + + it('fails closed when the attached branch disappears just before Apply', async () => { + let branchReads = 0; + (execSync as jest.Mock).mockImplementation((command: string) => { + if (command === 'git symbolic-ref --quiet --short HEAD' && ++branchReads > 1) throw new Error('detached HEAD'); + return Buffer.from(command === 'git rev-parse HEAD' ? 'a'.repeat(40) + : command === 'git rev-parse --show-toplevel' ? process.cwd() + : command === 'git symbolic-ref --quiet --short HEAD' ? 'develop' + : 'https://github.com/test-owner/test-repo.git'); + }); + await program.parseAsync(['node', 'cli', 'setup', '--web', '--pr-approval-mode', 'off', '--skip-secrets']); + expect(branchReads).toBeGreaterThan(1); + expect(runLocalAction).not.toHaveBeenCalled(); + expect(process.exitCode).toBe(1); + }); + it('rejects unattended CLI approval flags in web mode', async () => { await program.parseAsync(['node', 'cli', 'setup', '--web', '--yes']); expect(startWebSetupServer).not.toHaveBeenCalled(); expect(runLocalAction).not.toHaveBeenCalled(); expect(process.exitCode).toBe(1); }); + + it.each([ + ['--non-interactive'], + ['--token', 'github_pat_operator_test_token'], + ['--workflow-pat', 'github_pat_bot_test_token'], + ['--secret', 'PAT=github_pat_bot_test_token'], + ['--confirm-unverifiable-write-permissions'], + ])('rejects incompatible web option %s before opening the browser', async (...flags) => { + await program.parseAsync(['node', 'cli', 'setup', '--web', ...flags]); + expect(startWebSetupServer).not.toHaveBeenCalled(); + expect(runLocalAction).not.toHaveBeenCalled(); + expect(process.exitCode).toBe(1); + }); }); const guidedTerminal = (answer?: (prompt: string) => string | undefined) => ({ isInteractive: () => true, diff --git a/src/cli/__tests__/setup_command_options.test.ts b/src/cli/__tests__/setup_command_options.test.ts index 1e078d361..98a294128 100644 --- a/src/cli/__tests__/setup_command_options.test.ts +++ b/src/cli/__tests__/setup_command_options.test.ts @@ -62,6 +62,17 @@ describe('setup command option adapter', () => { secrets: { defaultScope: 'organization', organizationVisibility: 'selected', overrides: { PAT: 'repository' } }, }); }); + + test('keeps unspecified approval mode and resource overrides absent', () => { + const result = loadSetupOverrides({ + prApprovalCheck: ['CI|42|ci.yml'], + variablesScope: 'repository', secretsScope: 'repository', + }); + expect(result.pullRequestApproval?.mode).toBeUndefined(); + expect(result.pullRequestApproval?.testChecks).toEqual([{ name: 'CI', sourceAppId: 42, workflowName: 'ci.yml' }]); + expect(result.storage?.variables?.overrides).toEqual({}); + expect(result.storage?.secrets?.overrides).toEqual({}); + }); }); describe('setup override merge policy', () => { diff --git a/src/cli/__tests__/setup_presenters.test.ts b/src/cli/__tests__/setup_presenters.test.ts index bfc5a2d07..7c0236308 100644 --- a/src/cli/__tests__/setup_presenters.test.ts +++ b/src/cli/__tests__/setup_presenters.test.ts @@ -474,6 +474,19 @@ describe('setup presenters and prompt-specific adapters', () => { } finally { log.mockRestore(); } }); + it('does not invent workflow PAT requirements when the manual guide has none', async () => { + const log = jest.spyOn(console, 'log').mockImplementation(); + try { + const adapter = new SetupCredentialPromptAdapter(terminal([ + { kind: 'value', value: '2' }, { kind: 'value', value: 'manual-bot-token' }, + ]), {}); + adapter.configureWorkflowPatGuide('https://github.com/settings/personal-access-tokens/new', async () => ({ login: 'bot', id: 1 })); + await expect(adapter.requestWorkflowPat({ name: 'PAT', kind: 'workflowPat', description: 'Runtime token' })) + .resolves.toEqual({ name: 'PAT', value: 'manual-bot-token' }); + expect(log.mock.calls.flat().join('\n')).not.toContain('Workflow PAT permissions required'); + } finally { log.mockRestore(); } + }); + it('supports explicit existing-credential choices and propagates interrupted secret input', async () => { const log = jest.spyOn(console, 'log').mockImplementation(); const requirement = { name: 'PAT', kind: 'workflowPat' as const, description: 'Runtime token' }; diff --git a/src/cli/__tests__/web_setup_adapters.test.ts b/src/cli/__tests__/web_setup_adapters.test.ts index 54ecc4c62..651ef643b 100644 --- a/src/cli/__tests__/web_setup_adapters.test.ts +++ b/src/cli/__tests__/web_setup_adapters.test.ts @@ -43,6 +43,15 @@ describe('semantic web setup adapters', () => { expect(JSON.stringify(bridge.snapshot())).not.toContain('github_pat_'); }); + test('labels a legacy questionnaire without a phase as the full wizard', async () => { + const bridge = new WebSetupBridge('owner/repo'); + const ask = jest.spyOn(bridge, 'ask').mockResolvedValueOnce(undefined); + const initial = createSetupPermissionIntentQuestionnaire(buildInitialSetupConfiguration({ mode: 'interactive' })); + const result = await new WebSetupQuestionnaireCollector(bridge).collect({ ...initial, phase: undefined }, {}); + expect(ask).toHaveBeenCalledWith(expect.objectContaining({ phase: 'full', pass: 1 })); + expect(result.terminal).toBe('cancelled'); + }); + test('invalid answer stays on the same policy question and exposes validation', async () => { const bridge = new WebSetupBridge('owner/repo'); const collector = new WebSetupQuestionnaireCollector(bridge); @@ -216,6 +225,18 @@ describe('semantic web setup adapters', () => { expect(prompt.guidedWorkflowBotIdentity).toBeUndefined(); }); + test('manual bot entry without prepared requirements does not invent a permission table', async () => { + const bridge = new WebSetupBridge('owner/repo'); + const prompt = new WebSetupCredentialPrompt(bridge); + prompt.configureWorkflowPatGuide('https://github.com/settings/personal-access-tokens/new', async () => ({ login: 'bot', id: 1 })); + const pending = prompt.requestWorkflowPat({ name: 'PAT', kind: 'workflowPat', description: 'runtime' }); + answer(bridge, 'Manual PAT'); + await next(); + expect(bridge.snapshot().permissions).toBeUndefined(); + answer(bridge, 'manual_fake_pat'); + expect((await pending)?.value).toBe('manual_fake_pat'); + }); + test('API key and existing credential decisions stay in separate secret/choice prompts', async () => { const bridge = new WebSetupBridge('owner/repo'); const prompt = new WebSetupCredentialPrompt(bridge); From 5af21ecbee7fd851c27ed6e81fb0c6524f46e6b6 Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Tue, 29 Sep 2026 10:33:02 +0200 Subject: [PATCH 29/50] codex-setup-temporary-github-auth: preserve explicit empty issue workflow selection --- docs/issues/configurable-workflows.mdx | 2 ++ specs/CATALOG.md | 4 ++-- specs/catalog.json | 2 +- ...figurable-issue-workflows-and-admission.md | 24 +++++++++++++++---- .../setup_questionnaire_policy.test.ts | 4 ++++ .../__tests__/setup_terminal_driver.test.ts | 17 +++++++++++++ src/cli/setup_terminal_driver.ts | 2 +- 7 files changed, 46 insertions(+), 9 deletions(-) diff --git a/docs/issues/configurable-workflows.mdx b/docs/issues/configurable-workflows.mdx index 980a8079e..a47592daf 100644 --- a/docs/issues/configurable-workflows.mdx +++ b/docs/issues/configurable-workflows.mdx @@ -7,6 +7,8 @@ description: Select Issue Forms and understand live runtime admission. `copilot setup` uses one ordered workflow catalog for Issue Forms, routing labels, native Issue Types, branch roles, release dependencies, runtime admission, and generated agent guidance. Interactive setup starts with **All** selected: use Space to toggle a kind and Enter to confirm. +In the terminal, Enter without toggling keeps **All**. Pressing Space while **All** is selected clears every kind; Enter then submits that empty selection explicitly instead of restoring the default. If issue automation is still enabled, setup stops before applying changes and explains that you must select at least one kind. To configure no issue workflows, turn off issue automation at the earlier question. In the text-only fallback, type `none` to submit an empty selection explicitly. + For non-interactive setup, pass stable IDs: ```bash diff --git a/specs/CATALOG.md b/specs/CATALOG.md index 27b72d123..f3158357a 100644 --- a/specs/CATALOG.md +++ b/specs/CATALOG.md @@ -28,7 +28,7 @@ debt or convert unknown historic intent into a design decision. | `pull-request-lifecycle` | Implemented | Enrich linked and unlinked pull requests with safe issue linkage, projects, metadata, reviewers, concise descriptions, and distinct workflow evidence | [Pull request lifecycle and enrichment](./pull-request-lifecycle-and-enrichment.md) | 48 paths · 2026-09-16 | | `agent-runtime` | Implemented | Resolve, provision, authenticate, authorize, and execute only the agent roles reachable by a run | [Agent runtime, provider, model, and role routing](./agent-runtime-provider-and-model-routing.md) + 1 companion | 54 paths · 2026-09-24 | | `cli-and-single-actions` | As-built baseline | Expose bounded local commands and workflow-dispatched operations through the shared application core | [CLI and single-action execution](./cli-and-single-action-execution.md) | 33 paths · 2026-09-16 | -| `configurable-issue-workflows` | Implemented | Select one canonical set of issue workflows and enforce its forms, dependencies, branch policy, runtime admission, migration, and diagnosis | [Configurable issue workflows and fail-closed admission](./configurable-issue-workflows-and-admission.md) | 88 paths · 2026-09-23 | +| `configurable-issue-workflows` | Implemented | Select one canonical set of issue workflows and enforce its forms, dependencies, branch policy, runtime admission, migration, and diagnosis | [Configurable issue workflows and fail-closed admission](./configurable-issue-workflows-and-admission.md) | 88 paths · 2026-09-29 | | `repository-agent-collaboration` | Implemented | Generate safe repository-local profiles, guidance, and a skill for agents contributing through configured issues, Action-managed branches, pull requests, and deployment boundaries | [Repository agent collaboration contract](./repository-agent-collaboration-contract.md) | 39 paths · 2026-09-16 | | `guarded-pull-request-approval` | Proposed | Specify and locally implement revision-bound, evidence-gated native bot approvals for eligible human pull requests, with safe setup defaults, read-only doctor checks, and explainable recovery | [Guarded pull-request approval and setup readiness](./guarded-pull-request-approval.md) + 1 companion | 57 paths · 2026-09-17 | @@ -235,7 +235,7 @@ debt or convert unknown historic intent into a design decision. ### `configurable-issue-workflows` — Configurable issue workflows and fail-closed admission - Owner: Copilot maintainers -- Last verified: 2026-09-23 +- Last verified: 2026-09-29 - Specifications: [`specs/configurable-issue-workflows-and-admission.md`](./configurable-issue-workflows-and-admission.md) - Workflows: [`.github/workflows/copilot_commit.yml`](../.github/workflows/copilot_commit.yml) · [`.github/workflows/copilot_deployment_orchestration.yml`](../.github/workflows/copilot_deployment_orchestration.yml) · [`.github/workflows/copilot_issue.yml`](../.github/workflows/copilot_issue.yml) · [`.github/workflows/copilot_issue_comment.yml`](../.github/workflows/copilot_issue_comment.yml) · [`.github/workflows/copilot_pull_request.yml`](../.github/workflows/copilot_pull_request.yml) · [`.github/workflows/copilot_pull_request_comment.yml`](../.github/workflows/copilot_pull_request_comment.yml) · [`.github/workflows/copilot_pull_request_review_state.yml`](../.github/workflows/copilot_pull_request_review_state.yml) · [`.github/workflows/hotfix_workflow.yml`](../.github/workflows/hotfix_workflow.yml) · [`.github/workflows/release_workflow.yml`](../.github/workflows/release_workflow.yml) · [`setup/workflows/copilot_commit.yml`](../setup/workflows/copilot_commit.yml) · [`setup/workflows/copilot_deployment_orchestration.yml`](../setup/workflows/copilot_deployment_orchestration.yml) · [`setup/workflows/copilot_issue.yml`](../setup/workflows/copilot_issue.yml) · [`setup/workflows/copilot_issue_comment.yml`](../setup/workflows/copilot_issue_comment.yml) · [`setup/workflows/copilot_pull_request.yml`](../setup/workflows/copilot_pull_request.yml) · [`setup/workflows/copilot_pull_request_comment.yml`](../setup/workflows/copilot_pull_request_comment.yml) · [`setup/workflows/copilot_pull_request_review_state.yml`](../setup/workflows/copilot_pull_request_review_state.yml) · [`setup/workflows/hotfix_workflow.yml`](../setup/workflows/hotfix_workflow.yml) · [`setup/workflows/release_workflow.yml`](../setup/workflows/release_workflow.yml) - Entrypoints: [`action.yml`](../action.yml) · [`src/actions/github_action.ts`](../src/actions/github_action.ts) · [`src/actions/common_action.ts`](../src/actions/common_action.ts) · [`src/cli/commands/setup.ts`](../src/cli/commands/setup.ts) · [`src/cli/commands/doctor.ts`](../src/cli/commands/doctor.ts) diff --git a/specs/catalog.json b/specs/catalog.json index 4a2da6346..be156ceb4 100644 --- a/specs/catalog.json +++ b/specs/catalog.json @@ -1498,7 +1498,7 @@ "status": "implemented", "scope": "Select one canonical set of issue workflows and enforce its forms, dependencies, branch policy, runtime admission, migration, and diagnosis", "owner": "Copilot maintainers", - "lastVerified": "2026-09-23", + "lastVerified": "2026-09-29", "specs": [ "specs/configurable-issue-workflows-and-admission.md" ], diff --git a/specs/configurable-issue-workflows-and-admission.md b/specs/configurable-issue-workflows-and-admission.md index 94553bcf0..1db1903d8 100644 --- a/specs/configurable-issue-workflows-and-admission.md +++ b/specs/configurable-issue-workflows-and-admission.md @@ -3,7 +3,7 @@ - Status: Implemented — automated local gates complete; controlled live GitHub UX evidence remains pending - Date: 2026-09-16 - Catalog capability ID: `configurable-issue-workflows` -- Last verified: 2026-09-16 +- Last verified: 2026-09-29 - Owners: Copilot maintainers - Scope: one selectable issue-workflow catalog that drives setup assets, runtime admission, branch behavior, release dependencies, diagnosis, and user guidance - Related issues/PRs: none yet; related SDDs are listed in section 20 @@ -281,7 +281,12 @@ domain changes. 2. When issue handling is enabled, the terminal presents the multi-select. Up and Down move, Space toggles, and Enter confirms. Selecting `All` checks every kind; toggling it while all are checked clears them. A child toggle - recomputes the `All` state. + recomputes the `All` state. Enter without changing the default confirms all + kinds. After explicitly clearing every kind, Enter submits the `none` + sentinel, not an empty answer that would restore the default. If issue + automation remains enabled, validation explains that at least one kind is + required and no setup changes start; disabling issue automation permits an + empty selection. 3. The catalog expands the selection into Issue Forms, effective configured labels, native Issue Type projections, required workflow files, branch roles, body schemas, runtime routes, and documentation/guidance facts. @@ -558,6 +563,10 @@ Runtime profile: COPILOT_ISSUE_WORKFLOW_PROFILE (schema 1) TTY rendering MUST remain usable without color and at 80 columns. Cursor state uses both `❯` and text/checkbox state; color is never the only indicator. +Clearing `All` and pressing Enter must visibly retain the empty selection and +surface the existing cross-field validation message while issues are enabled; +an untouched Enter must retain the default selection. The text-input fallback +accepts `none` for the same explicit empty selection. ### 9.3 Representative runtime views @@ -731,12 +740,12 @@ rows count only when they assert a distinct decision branch. | Area | Minimum distinct cases | Behaviors/risks covered | |---|---:|---| | Catalog, profile, configuration, classifier | 26 | seven kinds, aliases, all/empty/unknown/duplicate/schema cases, zero/one/multiple groups, no fallback, cross-field rules | -| Setup planning, selection, rendering, reconciliation | 24 | Space/Enter/All, fallback input, cancel/EOF, dependencies, effective labels, managed/unmanaged drift, retire/backup, idempotency | +| Setup planning, selection, rendering, reconciliation | 27 | unchanged Enter/default All, Space clearing All then Enter/explicit none, empty-selection validation, fallback input, cancel/EOF, dependencies, effective labels, managed/unmanaged drift, retire/backup, idempotency | | Runtime admission, state, replay, continuation | 32 | passive/explicit matrix, queue/live state, disabled/unmanaged/conflict, body validation, legacy, continuation, durable operations, unlinked PR, zero-count assignment before target validation, deferred members-only lookup for every requested provider task, denied/failing authorization with fail-closed task configuration | | Adapters and provider contracts | 12 | Variable, issue snapshot, state, labels, org/no-org Issue Types, permission/rate-limit/error mapping | | Workflows, packaging, doctor, architecture | 16 | all workflow inputs, package contents, npm smoke, query-only doctor, mutation reachability, single catalog, parser/form contract | | UI, localization, security, integration, migration | 18 | five UI states, no-color/narrow, sanitization, comment budget, no secrets, old config/profile migration, dogfood and rollback | -| **Total** | **128** | No double counting | +| **Total** | **131** | No double counting | The issue-workflow domain and setup/rendering decision policies named by the `Configurable issue workflows and repository agent guidance` coverage budget @@ -771,6 +780,11 @@ validated against setup forms and profile fixtures. 1. Given a fresh interactive setup, when the selector opens, then all seven kinds are checked and Space/Enter produces the canonical ordered selection. +1a. Given the default selection, when the owner presses Enter without toggling, + then all seven kinds remain selected. Given the owner toggles `All` off and + presses Enter, then the terminal submits explicit `none`, never the default; + with issue automation still enabled, setup explains that at least one kind + is required before applying changes. 2. Given release and hotfix are deselected, when setup is confirmed, then their forms/workflows/resources are absent or safely retired, the explicit profile omits them, and all other selected forms remain. @@ -823,7 +837,7 @@ validated against setup forms and profile fixtures. | Requirement | Policy/use case/adapter/presentation | Test or evidence | Documentation | |---|---|---|---| | single seven-kind catalog | domain catalog | catalog completeness and uniqueness tests | configurable workflows page | -| multi-select default All | questionnaire policy + terminal adapter | key-sequence, fallback, cancel tests | setup guide | +| multi-select default All and explicit empty | questionnaire policy + terminal adapter + validation | unchanged Enter, Space/Enter clear-all, `none` parsing and empty-selection validation, fallback, cancel tests | setup guide and configurable workflows page | | deterministic setup expansion | planning/reconciliation use cases | plan, effective-label, drift tests | setup and config pages | | optional native Issue Types | capability adapter | org/no-org/permission tests | permissions section | | pre-mutation admission | admission use case + composition | zero-reachable-mutation and deferred-authorization integration tests | operator decision tree | diff --git a/src/application/policies/__tests__/setup_questionnaire_policy.test.ts b/src/application/policies/__tests__/setup_questionnaire_policy.test.ts index c21e644ff..65d995557 100644 --- a/src/application/policies/__tests__/setup_questionnaire_policy.test.ts +++ b/src/application/policies/__tests__/setup_questionnaire_policy.test.ts @@ -1,4 +1,5 @@ import { createDefaultSetupConfiguration } from '../setup_configuration_policy'; +import { validateSetupConfiguration } from '../setup_configuration_validation'; import { createSetupQuestionnaire, createSetupPermissionIntentQuestionnaire, @@ -56,6 +57,9 @@ describe('setup questionnaire policy', () => { const state = advanceTo(createSetupQuestionnaire(createDefaultSetupConfiguration()), 'issueWorkflows.enabled'); const cleared = transitionSetupQuestionnaire(state, { kind: 'answer', value: 'none' }); expect(cleared.draft.issueWorkflows.enabled).toEqual([]); + expect(validateSetupConfiguration(cleared.draft)).toContain( + 'At least one issue workflow must be enabled when issue automation is enabled.', + ); }); it('enters review immediately when the permission-intent phase has no open questions', () => { diff --git a/src/cli/__tests__/setup_terminal_driver.test.ts b/src/cli/__tests__/setup_terminal_driver.test.ts index cd3749aa2..57c461d79 100644 --- a/src/cli/__tests__/setup_terminal_driver.test.ts +++ b/src/cli/__tests__/setup_terminal_driver.test.ts @@ -128,6 +128,23 @@ describe('NodeTerminalDriver', () => { expect(mockStdin.setRawMode).toHaveBeenLastCalledWith(false); }); + it('keeps the default All selection on unchanged Enter', async () => { + const pending = new NodeTerminalDriver().readMultiSelect( + 'Issue workflows', ['All', 'feature — Feature', 'help — Help'], ['feature', 'help'], + ); + mockInputHandlers.get('data')?.(Buffer.from('\n')); + await expect(pending).resolves.toEqual({ kind: 'value', value: 'feature,help' }); + }); + + it('submits explicit none when the owner clears All and confirms', async () => { + const pending = new NodeTerminalDriver().readMultiSelect( + 'Issue workflows', ['All', 'feature — Feature', 'help — Help'], ['feature', 'help'], + ); + mockInputHandlers.get('data')?.(Buffer.from(' \n')); + await expect(pending).resolves.toEqual({ kind: 'value', value: 'none' }); + expect(mockStdin.setRawMode).toHaveBeenLastCalledWith(false); + }); + it.each([ ['data', '\u0003', 'cancel'], ['data', '\u0004', 'end-of-input'], diff --git a/src/cli/setup_terminal_driver.ts b/src/cli/setup_terminal_driver.ts index 7ac2bcacf..e75ff0dc5 100644 --- a/src/cli/setup_terminal_driver.ts +++ b/src/cli/setup_terminal_driver.ts @@ -143,7 +143,7 @@ export class NodeTerminalDriver implements TerminalDriver { else value.add(id); render(); } else if (character === '\r' || character === '\n') { - finish({ kind: 'value', value: [...value].join(',') }); + finish({ kind: 'value', value: value.size === 0 ? 'none' : [...value].join(',') }); return; } } From ad0ac962879aed545391f4929f0bccea6036fc11 Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Tue, 29 Sep 2026 10:35:54 +0200 Subject: [PATCH 30/50] build(cli): bundle explicit issue workflow selection --- build/cli/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/cli/index.js b/build/cli/index.js index bec55979e..5eaca03c0 100755 --- a/build/cli/index.js +++ b/build/cli/index.js @@ -67879,7 +67879,7 @@ class NodeTerminalDriver { render(); } else if (character === '\r' || character === '\n') { - finish({ kind: 'value', value: [...value].join(',') }); + finish({ kind: 'value', value: value.size === 0 ? 'none' : [...value].join(',') }); return; } } From bf5b17fe850ea8630dba01773d0e0a7464033a1d Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Tue, 29 Sep 2026 10:53:40 +0200 Subject: [PATCH 31/50] codex-setup-temporary-github-auth: require repository root for web apply --- docs/how-to-use.mdx | 4 +- specs/local-web-setup-assistant.md | 19 ++++++-- src/__tests__/cli.test.ts | 57 ++++++++++++++++++++++ src/cli/__tests__/web_setup_server.test.ts | 30 +++++++++++- src/cli/commands/setup.ts | 7 ++- 5 files changed, 108 insertions(+), 9 deletions(-) diff --git a/docs/how-to-use.mdx b/docs/how-to-use.mdx index f627f14f4..beb18eb00 100644 --- a/docs/how-to-use.mdx +++ b/docs/how-to-use.mdx @@ -40,7 +40,9 @@ Once installed, the `copilot` command is available globally. Repository-dependen Prefer a visual walkthrough? Run `copilot setup --web` from the repository root on an attached Git branch. A detached HEAD is rejected before credentials -are requested; check out a branch first. The CLI starts a short-lived page on +are requested; check out a branch first. Running from a subdirectory is also +rejected before the browser opens or a PAT is requested; change to the +repository root printed in the error and rerun. The CLI starts a short-lived page on `127.0.0.1` and opens your browser; if opening fails, paste the printed local URL into a browser on this computer. Enter the 16-character pairing code shown in the terminal before setup state diff --git a/specs/local-web-setup-assistant.md b/specs/local-web-setup-assistant.md index c137728da..6a10f8ce7 100644 --- a/specs/local-web-setup-assistant.md +++ b/specs/local-web-setup-assistant.md @@ -222,6 +222,13 @@ exactly what completed and what remains. opening the browser or collecting credentials. Detached HEAD or an unreadable branch fails immediately with checkout guidance; no fallback branch name may be inferred for this guarded session. + Web setup MUST also reject invocation from a repository subdirectory before + HTTP or PAT collection. Its current Apply boundary uses process-relative + checkout paths; accepting a subdirectory would make the approved drift + snapshot inspect a different destination. The error identifies the + canonical repository root and tells the operator to change directory and + rerun. Canonical path comparison permits a symlink spelling of that same + root, but never a nested directory. 2. Bind `127.0.0.1:0`, record the assigned port, create an unpredictable one-run session key, a separate 16-hex-character pairing code, and first controller lease in process memory. Print the pairing code only in the @@ -740,7 +747,7 @@ still offers the terminal setup and doctor paths. ## 14. Testing strategy and numeric budget -The floor is **102 distinct cases**, derived from shared-engine parity, +The floor is **103 distinct cases**, derived from shared-engine parity, six-stage transitions, two PAT roles, local HTTP abuse, packaged installs, drift, and partial mutation. Each test/parameterized behavior counts once; existing CLI tests are retained, not re-counted as new web evidence. @@ -752,9 +759,9 @@ existing CLI tests are retained, not re-counted as new web evidence. | GitHub/workspace/HTTP adapters | 10 | identity, missing/unknown grants, org approval, Secret scope, bounded errors and provider mapping | | CLI/packaging/workflow contracts | 10 | flag combinations, browser-open fallback, asset manifest, npm pack/global install, unchanged Action/API bundles | | UI/accessibility/localization/content | 18 | pending/action/blocked/partial/complete, plan diff, narrow/zoom/keyboard/focus/no-color, both palettes/system toggle and contrast, English fallback, escaping | -| Integration/compatibility/recovery | 12 | terminal-web parity, manual/environment/dry-run, drift, partial write, doctor reconciliation | +| Integration/compatibility/recovery | 13 | terminal-web parity, manual/environment/dry-run, drift, partial write, doctor reconciliation, root-versus-subdirectory launch | | Security/abuse | 18 | Host/Origin/CSRF, terminal pairing and attempt cap, session-key enforcement on every other API route, CORS, replay, path traversal, XSS/CSP, secret leaks, no GET mutation, body/time/connection limits, atomic lock publication | -| **Total** | **102** | No double counting | +| **Total** | **103** | No double counting | Within the 18 UI cases, cover at least one render/interaction for each prompt presenter, one revision-change form reset, secret clearing before dispatch, @@ -798,7 +805,9 @@ help for `--web` explains local-only scope and the `--non-interactive` conflict. branch, `copilot setup --web` opens a bundled local page showing the exact repository and six stages; no source checkout or Vite server is needed. A detached HEAD is - rejected before a browser opens or any PAT is requested. + rejected before a browser opens or any PAT is requested. Invocation from a + subdirectory is likewise rejected with the canonical root path; the + approved snapshot and Apply can therefore never use different roots. 2. Given a failed browser opener, the CLI prints the loopback URL and keeps serving; given a failed bind or missing assets, it stops without a false partial setup claim and offers terminal fallback. @@ -865,7 +874,7 @@ help for `--web` explains local-only scope and the `--non-interactive` conflict. | Shared setup engine/parity (§4.1, §8) | application coordinator + existing policies | scenarios 3, 5, 15; import/schema checks | architecture | | Bounded config/compatibility (§6.2–7) | CLI parser + config policy | scenarios 3–4, 15–16 | configuration | | Separate PAT roles/evidence (§4.3, §6) | permission/identity/credential use cases | scenarios 6–7, 11, 13, 16 | authentication, credentials | -| Revision-bound Apply/recovery (§6.3, §10) | session coordinator + execution boundary | scenarios 8–11 | troubleshooting, provisioning | +| Revision-bound Apply/recovery (§6.1, §6.3, §10) | CLI root precondition + session coordinator + execution boundary | scenarios 1, 8–11; nested-path launch regression | troubleshooting, provisioning | | Browser security/privacy (§4.3, §11) | loopback HTTP/asset adapters + redacted presenter | scenarios 9, 12–13 | authentication, architecture | | Accessible truthful UX (§9) | Svelte presenter + message catalog | scenarios 5–7, 10–11, 13–14 | how-to-use, troubleshooting | diff --git a/src/__tests__/cli.test.ts b/src/__tests__/cli.test.ts index 53f7231ce..58a8be044 100644 --- a/src/__tests__/cli.test.ts +++ b/src/__tests__/cli.test.ts @@ -4,6 +4,7 @@ */ import { execSync } from 'child_process'; +import { join } from 'node:path'; import { program } from '../cli'; import { runLocalAction } from '../actions/local_action'; import { ACTIONS } from '../data/model/action_types'; @@ -13,6 +14,7 @@ import { WebSetupBridge } from '../cli/web_setup_bridge'; import { WebSetupQuestionnaireCollector } from '../cli/web_setup_adapters'; import { startWebSetupServer, openWebSetupBrowser } from '../cli/web_setup_server'; import { captureSetupApplySnapshot, setupApplySnapshotMatches } from '../cli/setup_apply_snapshot'; +import { acquireSetupSessionGuard } from '../cli/setup_session_guard'; import { createSetupReviewState } from '../application/policies/setup_questionnaire_policy'; import type { WebSetupPrompt } from '../application/contracts/web_setup_view'; import { SetupDoctorWorkspaceQueryAdapter } from '../infrastructure/setup_workspace_adapter'; @@ -572,6 +574,29 @@ describe('CLI', () => { expect(logError).toHaveBeenCalledWith(expect.objectContaining({ message: expect.stringContaining('Check out a branch') })); }); + it('rejects web setup from a subdirectory before opening the browser or collecting a PAT', async () => { + const root = process.cwd(); + const nested = join(root, 'src'); + const cwd = jest.spyOn(process, 'cwd').mockReturnValue(nested); + (execSync as jest.Mock).mockImplementation((command: string) => Buffer.from( + command === 'git rev-parse --show-toplevel' ? root + : command === 'git config --get remote.origin.url' ? 'https://github.com/test-owner/test-repo.git' + : 'true', + )); + try { + await program.parseAsync(['node', 'cli', 'setup', '--web']); + expect(startWebSetupServer).not.toHaveBeenCalled(); + expect(acquireSetupSessionGuard).not.toHaveBeenCalled(); + expect(mockTokenPermissionInspect).not.toHaveBeenCalled(); + expect(runLocalAction).not.toHaveBeenCalled(); + expect(process.exitCode).toBe(1); + const { logError } = require('../utils/logger'); + expect(logError).toHaveBeenCalledWith(expect.objectContaining({ + message: expect.stringContaining(`repository root (${root})`), + })); + } finally { cwd.mockRestore(); } + }); + it('reports a blocked browser session if launch fails before the journey starts', async () => { (openWebSetupBrowser as jest.Mock).mockImplementationOnce(() => { throw new Error('Browser launch failed'); }); const finish = jest.spyOn(WebSetupBridge.prototype, 'finish'); @@ -616,6 +641,27 @@ describe('CLI', () => { expect(runLocalAction).toHaveBeenCalledTimes(1); }); + it('warns when the final guided plan no longer needs earlier PAT grants', async () => { + ask.mockImplementation(async (prompt: WebSetupPrompt) => { + if (prompt.title === 'How will you provide your setup PAT?') return 'Guided GitHub link'; + if (prompt.title === 'What kind of GitHub account owns this repository?') return 'Personal account'; + if (prompt.title === 'Review these provisional setup PAT grants') return 'Continue to GitHub'; + if (prompt.title.includes('Is that the intended operator account?')) return 'Yes, continue'; + return answerWebPrompt(prompt); + }); + collect.mockImplementationOnce(async initial => createSetupReviewState(initial.draft)) + .mockImplementationOnce(async initial => createSetupReviewState({ + ...initial.draft, manageRepositoryVariables: false, + })); + mockTokenPermissionInspect.mockResolvedValueOnce({ + role: 'setup', account: 'operator', identityStatus: 'valid', identityMessage: 'verified', + ready: true, confirmationRequired: false, checks: [], + }); + await program.parseAsync(['node', 'cli', 'setup', '--web', '--pr-approval-mode', 'off', '--skip-secrets']); + const { logInfo } = require('../utils/logger'); + expect(logInfo).toHaveBeenCalledWith(expect.stringContaining('Your PAT may have excess access')); + }); + it('keeps web dry-run local and never asks for either PAT or Apply', async () => { await program.parseAsync(['node', 'cli', 'setup', '--web', '--dry-run', '--pr-approval-mode', 'off']); expect(ask.mock.calls.map(call => call[0].title)).not.toContain('Temporary setup PAT'); @@ -736,6 +782,17 @@ describe('CLI', () => { expect(process.exitCode).toBe(1); }); + it('fails closed when the approved file snapshot is unavailable', async () => { + (captureSetupApplySnapshot as jest.Mock).mockReturnValueOnce(undefined); + await program.parseAsync(['node', 'cli', 'setup', '--web', '--pr-approval-mode', 'off', '--skip-secrets']); + expect(runLocalAction).not.toHaveBeenCalled(); + expect(process.exitCode).toBe(1); + const { logError } = require('../utils/logger'); + expect(logError).toHaveBeenCalledWith(expect.objectContaining({ + message: expect.stringContaining('approved setup evidence is incomplete'), + })); + }); + it('fails closed when the GitHub remote becomes unresolvable just before Apply', async () => { let remoteReads = 0; (execSync as jest.Mock).mockImplementation((command: string) => Buffer.from( diff --git a/src/cli/__tests__/web_setup_server.test.ts b/src/cli/__tests__/web_setup_server.test.ts index 4419ccc1c..9827fc9b3 100644 --- a/src/cli/__tests__/web_setup_server.test.ts +++ b/src/cli/__tests__/web_setup_server.test.ts @@ -1,7 +1,7 @@ import { mkdtempSync, mkdirSync, rmSync, symlinkSync, unlinkSync, writeFileSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; -import { request } from 'node:http'; +import { request, ServerResponse } from 'node:http'; import { connect, type Socket } from 'node:net'; import { WebSetupBridge } from '../web_setup_bridge'; import { startWebSetupServer, type WebSetupServer } from '../web_setup_server'; @@ -63,6 +63,34 @@ describe('local web setup server', () => { expect((await fetch(`${server.url}assets/%2e%2e/index.html`)).status).toBe(404); }); + test('ends a response safely if an asset write fails after headers were sent', async () => { + const end = jest.spyOn(ServerResponse.prototype, 'end').mockImplementationOnce(() => { + throw new Error('simulated asset write failure'); + }); + try { + const response = await fetch(server.url); + expect(response.status).toBe(200); + expect(await response.text()).toBe(''); + expect((await fetch(server.url)).status).toBe(200); + } finally { end.mockRestore(); } + }); + + test('uses the packaged web asset location when no override is supplied', async () => { + const filesystem = require('node:fs/promises') as typeof import('node:fs/promises'); + const original = filesystem.realpath; + const packaged = join(__dirname, '..', '..', 'web'); + const realpath = jest.spyOn(filesystem, 'realpath').mockImplementation(async path => + String(path) === packaged ? original(root) : original(path)); + let defaultServer: WebSetupServer | undefined; + try { + defaultServer = await startWebSetupServer(new WebSetupBridge('owner/repo')); + expect((await fetch(defaultServer.url)).status).toBe(200); + } finally { + if (defaultServer) await defaultServer.close(); + realpath.mockRestore(); + } + }); + test('the public loopback URL contains no secret and API access requires terminal pairing', async () => { const launch = new URL(server.url); const key = sessionKeys.get(launch.origin)!; diff --git a/src/cli/commands/setup.ts b/src/cli/commands/setup.ts index e64c05a5a..d25c28a8a 100644 --- a/src/cli/commands/setup.ts +++ b/src/cli/commands/setup.ts @@ -3,7 +3,7 @@ import { runLocalAction } from '../../actions/local_action'; import { TITLE } from '../../application/contracts/product_identity'; import { getSetupToken } from '../../utils/setup_files'; import { logError, logInfo } from '../../utils/logger'; -import { getCurrentAttachedBranch, getCurrentHeadSha, getGitInfo, getGitRepositoryRoot, isInsideGitRepo } from '../../cli_context'; +import { getCurrentAttachedBranch, getCurrentHeadSha, getGitInfo, getGitRepositoryRoot, isGitRepositoryRoot, isInsideGitRepo } from '../../cli_context'; import { buildSetupParams } from './setup_policy'; import { collectApprovalCheck, collectScope, collectSecret, loadSetupOverrides } from '../setup_command_options'; import { SetupQuestionnaireController, SetupWizardUseCase } from '../../application/usecases/setup'; @@ -115,8 +115,11 @@ export function registerSetupCommand(program: Command): void { return; } logInfo(`📦 Repository: ${gitInfo.owner}/${gitInfo.repo}`); - releaseSetupGuard = acquireSetupSessionGuard(cwd); const checkoutRoot = webBridge ? getGitRepositoryRoot(cwd) : cwd; + if (webBridge && !isGitRepositoryRoot(cwd)) { + throw new ApplicationError('configuration.invalid', `Web setup must start from the repository root (${checkoutRoot}). Change to that directory and rerun before creating PATs. No local setup session started.`); + } + releaseSetupGuard = acquireSetupSessionGuard(cwd); const initialBranch = webBridge ? getCurrentAttachedBranch(cwd) : undefined; const initialHead = webBridge ? getCurrentHeadSha() : undefined; if (webBridge && (!initialBranch || !initialHead)) { From 095952aa2fe4265c090985771ed68b8e2756d337 Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Tue, 29 Sep 2026 10:56:16 +0200 Subject: [PATCH 32/50] build(cli): package web checkout root guard --- build/cli/index.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/build/cli/index.js b/build/cli/index.js index 5eaca03c0..ad3a0ea86 100755 --- a/build/cli/index.js +++ b/build/cli/index.js @@ -65945,8 +65945,11 @@ function registerSetupCommand(program) { return; } (0, logger_1.logInfo)(`📦 Repository: ${gitInfo.owner}/${gitInfo.repo}`); - releaseSetupGuard = (0, setup_session_guard_1.acquireSetupSessionGuard)(cwd); const checkoutRoot = webBridge ? (0, cli_context_1.getGitRepositoryRoot)(cwd) : cwd; + if (webBridge && !(0, cli_context_1.isGitRepositoryRoot)(cwd)) { + throw new application_error_1.ApplicationError('configuration.invalid', `Web setup must start from the repository root (${checkoutRoot}). Change to that directory and rerun before creating PATs. No local setup session started.`); + } + releaseSetupGuard = (0, setup_session_guard_1.acquireSetupSessionGuard)(cwd); const initialBranch = webBridge ? (0, cli_context_1.getCurrentAttachedBranch)(cwd) : undefined; const initialHead = webBridge ? (0, cli_context_1.getCurrentHeadSha)() : undefined; if (webBridge && (!initialBranch || !initialHead)) { From 6bc7e51fa26c6356a2e0f641175ee0b7580e4e8b Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Tue, 29 Sep 2026 11:07:52 +0200 Subject: [PATCH 33/50] docs(spec): align web setup test budget references --- specs/local-web-setup-assistant.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/specs/local-web-setup-assistant.md b/specs/local-web-setup-assistant.md index 6a10f8ce7..babc91453 100644 --- a/specs/local-web-setup-assistant.md +++ b/specs/local-web-setup-assistant.md @@ -905,7 +905,7 @@ application coordinator and full UI/accessibility acceptance gates. These facts are **not** release acceptance. The orchestration in `src/cli/commands/setup.ts` still needs extraction into the prescribed -application-level session coordinator; the 102-case budget, full human +application-level session coordinator; the 103-case budget, full human cross-platform/accessibility review, exact per-resource progress/partial evidence, and adversarial concurrency/idle/crash suite remain open. The catalog stays `proposed` until the definition of done is evidenced. Existing @@ -924,7 +924,7 @@ empty issue-workflow selection, drift, cancellation, and package isolation. Typecheck, lint, Svelte diagnostics, full build, catalog, documentation, workflow, npm-package validation, and package smoke checks passed without real PATs or setup dogfooding. Human browser/accessibility and cross-platform -review, the formal 102-case-by-area acceptance mapping, and the complete +review, the formal 103-case-by-area acceptance mapping, and the complete application-level session coordinator remain open release gates. The generated bundle synchronization check runs after the source/build commit is staged. @@ -950,7 +950,7 @@ bundle synchronization check runs after the source/build commit is staged. application decision engine with enforceable dependency rules. - [ ] Local HTTP, controller, PAT, plan revision, and Apply defenses pass the adversarial/security budget with no secret in browser storage or logs. -- [ ] All 102 distinct new web cases by area pass without real PATs or +- [ ] All 103 distinct new web cases by area pass without real PATs or dogfooding; the repository and changed-module coverage thresholds already pass for the current implementation slice. - [ ] Global npm-pack install serves complete local assets; Action/API bundles From fee890696f8b7a88f77ec94d5c75e9c008285c95 Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Tue, 29 Sep 2026 11:22:35 +0200 Subject: [PATCH 34/50] codex-setup-temporary-github-auth: guard guidance retirement during web apply --- docs/how-to-use.mdx | 4 ++++ specs/local-web-setup-assistant.md | 9 +++++++- .../policies/setup_configuration_plan.ts | 18 ++++++++++++++- .../__tests__/setup_apply_snapshot.test.ts | 23 ++++++++++++++++++- 4 files changed, 51 insertions(+), 3 deletions(-) diff --git a/docs/how-to-use.mdx b/docs/how-to-use.mdx index beb18eb00..5907760a0 100644 --- a/docs/how-to-use.mdx +++ b/docs/how-to-use.mdx @@ -77,6 +77,10 @@ inspection and PAT checks; it does not dispatch or temporarily install a credential-health workflow. Re-enter an existing bot PAT for its grant audit. An optional existing provider Secret that you choose to keep may remain `unverifiable`; check runtime health with `copilot doctor` after installation. +Before Apply, the assistant also checks for changes to managed guidance files +and their ownership manifest. This includes files that may be retired when +repository agent guidance is turned off; if they changed after plan approval, +setup stops before modifying them and asks you to review a fresh plan. In the browser questionnaire, an issue-workflow choice defaulted to **All** stays selected if you continue unchanged; clearing that selection submits **none** explicitly. diff --git a/specs/local-web-setup-assistant.md b/specs/local-web-setup-assistant.md index babc91453..7a7a8360f 100644 --- a/specs/local-web-setup-assistant.md +++ b/specs/local-web-setup-assistant.md @@ -496,6 +496,11 @@ Both terminal and web routes use the same questionnaire parser for this choice. `ISSUE_TEMPLATE/name.yml` MUST be translated to their actual checkout destinations under `.github/` for this comparison. Include managed assets that a changed selection may retire, not only files displayed as selected. + The guard set always includes the repository-agent guidance manifest, profile, + guide, skill, and managed `AGENTS.md` pointer destination. Disabling guidance + can retire manifest-owned artifacts; a changed manifest or any allowlisted + artifact after approval MUST invalidate Apply before reconciliation. These + paths are guard evidence even when omitted from the plan's selected files. - The shared execution boundary owns idempotency and partial facts. The browser uses bounded polling or server events for **read-only** progress; reconnecting to the same live process retrieves redacted current state, @@ -830,7 +835,9 @@ help for `--web` explains local-only scope and the `--non-interactive` conflict. 8. Given a current approved plan and verified credentials, one click applies it once. Duplicate click returns the same operation; stale revision, changed checkout file, changed remote identity, or new permission need - returns to review with no new mutation. + returns to review with no new mutation. This includes a changed guidance + manifest or any managed guidance artifact when guidance is disabled and + its prior artifacts would be retired. 9. Given an invalid/stale tab event or second tab, no action occurs until the new tab explicitly takes control; the first tab then cannot submit. Given a concurrent terminal or web setup in the same checkout, the diff --git a/src/application/policies/setup_configuration_plan.ts b/src/application/policies/setup_configuration_plan.ts index e97c350fe..65ef6060b 100644 --- a/src/application/policies/setup_configuration_plan.ts +++ b/src/application/policies/setup_configuration_plan.ts @@ -15,6 +15,13 @@ import { buildSetupCredentialRequirements } from './setup_credential_requirement import { resolveLocaleProfile } from '../../domain/locale'; import { ISSUE_WORKFLOW_CATALOG, ISSUE_WORKFLOW_KINDS, issueWorkflowFormFiles, serializeIssueWorkflowProfile } from '../../domain/issue_workflow_profile'; import { effectiveIssueWorkflowFeatures, effectiveIssueWorkflowProfile } from './setup_issue_workflow_policy'; +import { + REPOSITORY_AGENT_GUIDE_PATH, + REPOSITORY_AGENT_MANIFEST_PATH, + REPOSITORY_AGENT_POINTER_PATH, + REPOSITORY_AGENT_PROFILE_PATH, + REPOSITORY_AGENT_SKILL_PATH, +} from './repository_agent_guidance_policy'; export { buildSetupCredentialRequirements }; @@ -80,7 +87,16 @@ export function setupPlanGuardPaths(plan: Readonly): string[] { ...['release_workflow.yml', 'hotfix_workflow.yml', 'copilot_deployment_orchestration.yml'] .map(file => `.github/workflows/${file}`), ]; - return [...new Set([...selected, ...retiredCandidates])].sort(); + // The manifest can authorize retirement even when guidance is disabled and + // its artifacts are absent from the presentation plan. + const guidanceCandidates = [ + REPOSITORY_AGENT_MANIFEST_PATH, + REPOSITORY_AGENT_PROFILE_PATH, + REPOSITORY_AGENT_GUIDE_PATH, + REPOSITORY_AGENT_SKILL_PATH, + REPOSITORY_AGENT_POINTER_PATH, + ]; + return [...new Set([...selected, ...retiredCandidates, ...guidanceCandidates])].sort(); } export function buildSetupRepositoryVariables(configuration: SetupConfiguration): SetupVariable[] { diff --git a/src/cli/__tests__/setup_apply_snapshot.test.ts b/src/cli/__tests__/setup_apply_snapshot.test.ts index 26e815cef..1f4915fbc 100644 --- a/src/cli/__tests__/setup_apply_snapshot.test.ts +++ b/src/cli/__tests__/setup_apply_snapshot.test.ts @@ -1,6 +1,6 @@ import { mkdtempSync, mkdirSync, rmSync, symlinkSync, writeFileSync } from 'node:fs'; import { tmpdir } from 'node:os'; -import { join } from 'node:path'; +import { dirname, join } from 'node:path'; import { captureSetupApplySnapshot, setupApplySnapshotMatches } from '../setup_apply_snapshot'; import { createDefaultSetupConfiguration } from '../../application/policies/setup_configuration_policy'; import { buildSetupPlan, setupPlanGuardPaths } from '../../application/policies/setup_configuration_plan'; @@ -65,4 +65,25 @@ describe('web setup apply snapshot', () => { expect(paths).toContain('.github/ISSUE_TEMPLATE/release.yml'); expect(paths).toContain('.github/ISSUE_TEMPLATE/config.yml'); }); + + test.each([ + '.copilot/setup-manifest.json', + '.copilot/repository-profile.json', + '.copilot/AGENT_GUIDE.md', + '.agents/skills/copilot-repository-workflow/SKILL.md', + 'AGENTS.md', + ])('guards %s against drift even when repository guidance is disabled', name => { + const configuration = createDefaultSetupConfiguration(); + configuration.repositoryAgentGuidance = { ...configuration.repositoryAgentGuidance, enabled: false }; + const plan = buildSetupPlan(configuration); + expect(plan.selectedFiles).not.toContain(name); + const paths = setupPlanGuardPaths(plan); + expect(paths).toContain(name); + const file = join(root, name); + mkdirSync(dirname(file), { recursive: true }); + writeFileSync(file, 'reviewed'); + const reviewed = captureSetupApplySnapshot(root, paths); + writeFileSync(file, 'changed after approval'); + expect(setupApplySnapshotMatches(root, paths, reviewed)).toBe(false); + }); }); From e36729dc555fee175ae0a1d85049ede9bed2a497 Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Tue, 29 Sep 2026 11:25:34 +0200 Subject: [PATCH 35/50] build: bundle guidance drift guard for CLI and Action --- build/cli/index.js | 12 +++++++++++- build/github_action/index.js | 12 +++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/build/cli/index.js b/build/cli/index.js index ad3a0ea86..ac4d8ee91 100755 --- a/build/cli/index.js +++ b/build/cli/index.js @@ -46388,6 +46388,7 @@ Object.defineProperty(exports, "buildSetupCredentialRequirements", ({ enumerable const locale_1 = __nccwpck_require__(15386); const issue_workflow_profile_1 = __nccwpck_require__(26744); const setup_issue_workflow_policy_1 = __nccwpck_require__(81182); +const repository_agent_guidance_policy_1 = __nccwpck_require__(67402); function buildSetupPlan(configuration, mergeQueueReadiness = [], approvalReadiness = []) { const workflowFiles = (0, setup_workflow_catalog_1.enabledSetupWorkflowFiles)((0, setup_issue_workflow_policy_1.effectiveIssueWorkflowFeatures)(configuration)) .filter(file => file !== 'copilot_pull_request_approval.yml' || configuration.pullRequestApproval.mode !== 'off'); @@ -46449,7 +46450,16 @@ function setupPlanGuardPaths(plan) { ...['release_workflow.yml', 'hotfix_workflow.yml', 'copilot_deployment_orchestration.yml'] .map(file => `.github/workflows/${file}`), ]; - return [...new Set([...selected, ...retiredCandidates])].sort(); + // The manifest can authorize retirement even when guidance is disabled and + // its artifacts are absent from the presentation plan. + const guidanceCandidates = [ + repository_agent_guidance_policy_1.REPOSITORY_AGENT_MANIFEST_PATH, + repository_agent_guidance_policy_1.REPOSITORY_AGENT_PROFILE_PATH, + repository_agent_guidance_policy_1.REPOSITORY_AGENT_GUIDE_PATH, + repository_agent_guidance_policy_1.REPOSITORY_AGENT_SKILL_PATH, + repository_agent_guidance_policy_1.REPOSITORY_AGENT_POINTER_PATH, + ]; + return [...new Set([...selected, ...retiredCandidates, ...guidanceCandidates])].sort(); } function buildSetupRepositoryVariables(configuration) { const variables = []; diff --git a/build/github_action/index.js b/build/github_action/index.js index 1a2d4a819..2e6d39f59 100644 --- a/build/github_action/index.js +++ b/build/github_action/index.js @@ -49103,6 +49103,7 @@ Object.defineProperty(exports, "buildSetupCredentialRequirements", ({ enumerable const locale_1 = __nccwpck_require__(15386); const issue_workflow_profile_1 = __nccwpck_require__(26744); const setup_issue_workflow_policy_1 = __nccwpck_require__(81182); +const repository_agent_guidance_policy_1 = __nccwpck_require__(67402); function buildSetupPlan(configuration, mergeQueueReadiness = [], approvalReadiness = []) { const workflowFiles = (0, setup_workflow_catalog_1.enabledSetupWorkflowFiles)((0, setup_issue_workflow_policy_1.effectiveIssueWorkflowFeatures)(configuration)) .filter(file => file !== 'copilot_pull_request_approval.yml' || configuration.pullRequestApproval.mode !== 'off'); @@ -49164,7 +49165,16 @@ function setupPlanGuardPaths(plan) { ...['release_workflow.yml', 'hotfix_workflow.yml', 'copilot_deployment_orchestration.yml'] .map(file => `.github/workflows/${file}`), ]; - return [...new Set([...selected, ...retiredCandidates])].sort(); + // The manifest can authorize retirement even when guidance is disabled and + // its artifacts are absent from the presentation plan. + const guidanceCandidates = [ + repository_agent_guidance_policy_1.REPOSITORY_AGENT_MANIFEST_PATH, + repository_agent_guidance_policy_1.REPOSITORY_AGENT_PROFILE_PATH, + repository_agent_guidance_policy_1.REPOSITORY_AGENT_GUIDE_PATH, + repository_agent_guidance_policy_1.REPOSITORY_AGENT_SKILL_PATH, + repository_agent_guidance_policy_1.REPOSITORY_AGENT_POINTER_PATH, + ]; + return [...new Set([...selected, ...retiredCandidates, ...guidanceCandidates])].sort(); } function buildSetupRepositoryVariables(configuration) { const variables = []; From 6f325cc4b35088a382cba1a9d6e1190f5020c493 Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Tue, 29 Sep 2026 11:59:37 +0200 Subject: [PATCH 36/50] fix(setup-web): require pairing code for tab takeover --- build/cli/index.js | 25 ++++++---- build/web/assets/index-CWAStamn.js | 2 - build/web/assets/index-CYUoGCTl.js | 2 + build/web/index.html | 2 +- docs/how-to-use.mdx | 10 +++- specs/local-web-setup-assistant.md | 50 +++++++++++++------ src/__tests__/cli.test.ts | 14 ++++++ src/cli/__tests__/web_setup_bridge.test.ts | 5 +- .../web_setup_browser_session.test.ts | 27 +++++----- src/cli/__tests__/web_setup_server.test.ts | 24 ++++++--- src/cli/web_setup_bridge.ts | 9 ++-- src/cli/web_setup_server.ts | 13 +++-- web/src/App.svelte | 3 +- web/src/components/PairingPanel.svelte | 9 ++-- web/src/session/setupSession.ts | 14 +++--- 15 files changed, 134 insertions(+), 75 deletions(-) delete mode 100644 build/web/assets/index-CWAStamn.js create mode 100644 build/web/assets/index-CYUoGCTl.js diff --git a/build/cli/index.js b/build/cli/index.js index ac4d8ee91..730a85034 100755 --- a/build/cli/index.js +++ b/build/cli/index.js @@ -68296,7 +68296,6 @@ class WebSetupBridge { constructor(repository) { this.revision = 0; this.subscribers = new Set(); - this.takeoverTicket = (0, node_crypto_1.randomBytes)(32).toString('hex'); this.bootstrapped = false; this.view = { revision: 0, repository }; } @@ -68312,13 +68311,10 @@ class WebSetupBridge { // A second tab starts read-only. Its explicit takeover rotates the controller capability. const first = !this.bootstrapped; this.bootstrapped = true; - return { controller: first, ...(first ? { capability: this.controller } : {}), takeoverTicket: this.takeoverTicket }; + return { controller: first, ...(first ? { capability: this.controller } : {}) }; } - takeOver(ticket) { - if (!sameCapability(ticket, this.takeoverTicket)) - return undefined; + takeOver() { this.controller = (0, node_crypto_1.randomBytes)(32).toString('hex'); - this.takeoverTicket = (0, node_crypto_1.randomBytes)(32).toString('hex'); this.publish({ message: { tone: 'info', text: 'Control moved to this tab. The previous tab is now read-only.' } }); return this.controller; } @@ -68511,11 +68507,18 @@ async function startWebSetupServer(bridge, assets = (0, node_path_1.join)(__dirn return; } const body = await readJson(request); - const ticket = typeof body.ticket === 'string' ? body.ticket : ''; - const capability = bridge.takeOver(ticket); - if (capability) - armIdle(); - respond(response, capability ? 200 : 403, capability ? { capability } : { error: 'Invalid takeover ticket.' }); + if (failedPairings >= 5) { + respond(response, 429, { error: 'Too many pairing attempts. Restart setup.' }); + return; + } + if (!matchesHexSecret(body.code, pairingCode)) { + failedPairings += 1; + respond(response, 403, { error: 'Incorrect pairing code. Check the launching output.' }); + return; + } + const capability = bridge.takeOver(); + armIdle(); + respond(response, 200, { capability }); return; } if (request.method === 'POST' && request.url === '/api/answer') { diff --git a/build/web/assets/index-CWAStamn.js b/build/web/assets/index-CWAStamn.js deleted file mode 100644 index a1821bd02..000000000 --- a/build/web/assets/index-CWAStamn.js +++ /dev/null @@ -1,2 +0,0 @@ -(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})(),typeof window<`u`&&((window.__svelte??={}).v??=new Set).add(`5`);var e=!1;function t(){e=!0}t();var n={},r=Symbol(`uninitialized`),i=`http://www.w3.org/1999/xhtml`,a=Array.isArray,o=Array.prototype.indexOf,s=Array.prototype.includes,c=Array.from,l=Object.defineProperty,u=Object.getOwnPropertyDescriptor,d=Object.getOwnPropertyDescriptors,f=Object.prototype,p=Array.prototype,m=Object.getPrototypeOf,h=Object.isExtensible,g=()=>{};function _(e){return e()}function v(e){for(var t=0;t{e=n,t=r}),resolve:e,reject:t}}var b=1024,x=2048,S=4096,ee=8192,te=16384,ne=32768,re=1<<25,ie=65536,ae=1<<18,oe=1<<19,se=1<<20,ce=1<<25,le=1<<21,ue=1<<22,de=1<<23,fe=Symbol(`$state`),pe=Symbol(`component`),me=Symbol(`legacy props`),he=Symbol(``),ge=Symbol(`attributes`),_e=Symbol(`class`),ve=Symbol(`style`),ye=Symbol(`text`),be=Symbol(`form reset`),xe=new class extends Error{name=`StaleReactionError`;message="The reaction that called `getAbortSignal()` was re-run or destroyed"},Se=!!globalThis.document?.contentType&&globalThis.document.contentType.includes(`xml`);function Ce(){console.warn(`https://svelte.dev/e/derived_inert`)}function we(e){console.warn(`https://svelte.dev/e/hydration_mismatch`)}function Te(){console.warn(`https://svelte.dev/e/select_multiple_invalid_value`)}function Ee(){console.warn(`https://svelte.dev/e/svelte_boundary_reset_noop`)}var C=!1;function De(e){C=e}var w;function T(e){if(e===null)throw we(),n;return w=e}function Oe(){return T(an(w))}function E(e){if(C){if(an(w)!==null)throw we(),n;w=e}}function ke(e=1){if(C){for(var t=e,n=w;t--;)n=an(n);w=n}}function Ae(e=!0){for(var t=0,n=w;;){if(n.nodeType===8){var r=n.data;if(r===`]`){if(t===0)return n;--t}else(r===`[`||r===`[!`||r[0]===`[`&&!isNaN(Number(r.slice(1))))&&(t+=1)}var i=an(n);e&&n.remove(),n=i}}function je(e){if(!e||e.nodeType!==8)throw we(),n;return e.data}function Me(e){return e===this.v}function Ne(e,t){return e==e?e!==t||typeof e==`object`&&!!e||typeof e==`function`:t==t}function Pe(e){return!Ne(e,this.v)}function Fe(e){throw Error(`https://svelte.dev/e/lifecycle_outside_component`)}function Ie(){throw Error(`https://svelte.dev/e/async_derived_orphan`)}function Le(e,t,n){throw Error(`https://svelte.dev/e/each_key_duplicate`)}function Re(e){throw Error(`https://svelte.dev/e/effect_in_teardown`)}function ze(){throw Error(`https://svelte.dev/e/effect_in_unowned_derived`)}function Be(e){throw Error(`https://svelte.dev/e/effect_orphan`)}function Ve(){throw Error(`https://svelte.dev/e/effect_update_depth_exceeded`)}function He(e){throw Error(`https://svelte.dev/e/props_invalid_value`)}function Ue(){throw Error(`https://svelte.dev/e/state_descriptors_fixed`)}function We(){throw Error(`https://svelte.dev/e/state_prototype_fixed`)}function Ge(){throw Error(`https://svelte.dev/e/state_unsafe_mutation`)}function Ke(){throw Error(`https://svelte.dev/e/svelte_boundary_reset_onerror`)}var D=null;function qe(e){D=e}function O(t,n=!1,r){D={p:D,i:!1,c:null,e:null,s:t,x:null,r:H,l:e&&!n?{s:null,u:null,$:[]}:null}}function k(e){var t=D,n=t.e;if(n!==null){t.e=null;for(var r of n)yn(r)}return e!==void 0&&(t.x=e),t.i=!0,D=t.p,Je(e)}function Je(e={}){return l(e,pe,{value:!0}),e}function Ye(){return!e||D!==null&&D.l===null}var Xe=[];function Ze(){var e=Xe;Xe=[],v(e)}function Qe(e){if(Xe.length===0&&!wt){var t=Xe;queueMicrotask(()=>{t===Xe&&Ze()})}Xe.push(e)}function $e(){for(;Xe.length>0;)Ze()}var et=~(x|S|b);function A(e,t){e.f=e.f&et|t}function tt(e){e.f&512||e.deps===null?A(e,b):A(e,S)}function nt(e,t,n){e.f&2048?t.add(e):e.f&4096&&n.add(e),A(e,b)}var rt=!1;function it(){rt||(rt=!0,document.addEventListener(`reset`,e=>{Promise.resolve().then(()=>{if(!e.defaultPrevented)for(let t of e.target.elements)t[be]?.()})},{capture:!0}))}function at(e){var t=V,n=H;Un(null),Wn(null);try{return e()}finally{Un(t),Wn(n)}}function ot(e,t,n,r=n){e.addEventListener(t,()=>at(n));let i=e[be];e[be]=i?()=>{i(),r(!0)}:()=>r(!0),it()}function st(e,t,n,r){let i=Ye()?dt:mt;var a=e.filter(e=>!e.settled),o=t.map(i);if(n.length===0&&a.length===0){r(o);return}var s=H,c=ct(),l=a.length===1?a[0].promise:a.length>1?Promise.all(a.map(e=>e.promise)):null;function u(e){if(!(s.f&16384)){c();try{r([...o,...e])}catch(e){fn(e,s)}lt()}}var d=ut();if(n.length===0){l.then(()=>u([])).finally(d);return}function f(){Promise.all(n.map(e=>pt(e))).then(u).catch(e=>fn(e,s)).finally(d)}l?l.then(()=>{c(),f(),lt()}):f()}function ct(){var e=H,t=V,n=D,r=j;return function(i=!0){Wn(e),Un(t),qe(n),i&&!(e.f&16384)&&(r?.activate(),r?.apply())}}function lt(e=!0){Wn(null),Un(null),qe(null),e&&j?.deactivate()}function ut(){var e=H,t=e.b,n=j,r=!!t?.is_rendered();return t?.update_pending_count(1,n),n.increment(r,e),()=>{t?.update_pending_count(-1,n),n.decrement(r,e)}}function dt(e){var t=2|x;return H!==null&&(H.f|=oe),{ctx:D,deps:null,effects:null,equals:Me,f:t,fn:e,reactions:null,rv:0,v:r,wv:0,parent:H,ac:null}}var ft=Symbol(`obsolete`);function pt(e,t,n){let i=H;i===null&&Ie();var a=void 0,o=Vt(r),s=!V,c=new Set;return Tn(()=>{var t=H,n=y();a=n.promise;try{Promise.resolve(e()).then(n.resolve,e=>{e!==xe&&n.reject(e)}).finally(lt)}catch(e){n.reject(e),lt()}var r=j;if(s){if(t.f&32768)var l=ut();if(i.b?.is_rendered())r.async_deriveds.get(t)?.reject(ft);else for(let e of c.values())e.reject(ft);c.add(n),r.async_deriveds.set(t,n)}let u=(e,t=void 0)=>{l?.(),c.delete(n),t!==ft&&(r.activate(),t?(o.f|=de,Gt(o,t)):(o.f&8388608&&(o.f^=de),Gt(o,e)),r.deactivate())};n.promise.then(u,e=>u(null,e||`unknown`))}),_n(()=>{for(let e of c)e.reject(ft)}),new Promise(e=>{function t(n){function r(){n===a?e(o):t(a)}n.then(r,r)}t(a)})}function mt(e){let t=dt(e);return t.equals=Pe,t}function ht(e){var t=e.effects;if(t!==null){e.effects=null;for(var n=0;n{t.ac.abort(xe),t.ac=null}),t.fn!==null&&(t.teardown=g),ar(t,0),kn(t))}function yt(e){if(e.effects!==null)for(let t of e.effects)t.teardown&&t.fn!==null&&or(t)}var bt=null,j=null,xt=null,St=null,Ct=null,wt=!1,Tt=!1,Et=null,Dt=null,Ot=0,kt=1,At=class e{id=kt++;#e=!1;linked=!0;#t=null;#n=null;async_deriveds=new Map;current=new Map;previous=new Map;#r=new Set;#i=new Set;#a=0;#o=new Map;#s=null;#c=[];#l=[];#u=new Set;#d=new Set;#f=new Map;#p=new Set;is_fork=!1;#m=!1;constructor(){bt===null?bt=this:(bt.#n=this,this.#t=bt),bt=this}#h(){if(this.is_fork)return!0;for(let n of this.#o.keys()){for(var e=n,t=!1;e.parent!==null;){if(this.#f.has(e)){t=!0;break}e=e.parent}if(!t)return!0}return!1}skip_effect(e){this.#f.has(e)||this.#f.set(e,{d:[],m:[]}),this.#p.delete(e)}unskip_effect(e,t=e=>this.schedule(e)){var n=this.#f.get(e);if(n){this.#f.delete(e);for(var r of n.d)A(r,x),t(r);for(r of n.m)A(r,S),t(r)}this.#p.add(e)}#g(){var e=[];for(let i of this.#c)if(!(i.f&16384||!(i.f&6144))){for(var t=i,n=!1;t.parent!==null;){t=t.parent;var r=t.f;if(r&96){if(!(r&1024)){n=!0;break}t.f^=b}}n||e.push(t)}return this.#c=[],e}#_(){this.#e=!0;for(let e of this.#u)this.#d.delete(e),A(e,x),this.schedule(e);for(let e of this.#d)A(e,S),this.schedule(e);this.apply();for(var t=Et=[],n=[],r=Dt=[];this.#c.length>0;){Ot++>1e3&&(this.#S(),Mt());for(let e of this.#g())try{this.#v(e,t,n)}catch(t){throw Lt(e),this.#h()||this.discard(),t}}if(j=null,r.length>0){var i=e.ensure();for(let e of r)i.schedule(e)}if(Et=null,Dt=null,this.#h()){this.#x(n),this.#x(t);for(let[e,t]of this.#f)It(e,t);r.length>0&&j.#_();return}let a=this.#y();if(a){this.#x(n),this.#x(t),a.#b(this);return}this.#u.clear(),this.#d.clear();for(let e of this.#r)e(this);this.#r.clear(),xt=this,Pt(n),Pt(t),xt=null,this.#s?.resolve();var o=j;if(this.#a===0&&(this.#c.length===0||o!==null)&&this.#S(),this.#c.length>0){if(o!==null){for(let e of this.#c)o.#c.push(e);this.#c=[]}else o=this}o!==null&&(zt.clear(),o.#_())}#v(e,t,n){e.f^=b;for(var r=e.first;r!==null;){var i=r.f,a=!!(i&96);if(!(a&&i&1024||i&8192||this.#f.has(r))&&r.fn!==null){a?r.f^=b:i&4?t.push(r):er(r)&&(i&16&&this.#d.add(r),or(r));var o=r.first;if(o!==null){r=o;continue}}for(;r!==null;){var s=r.next;if(s!==null){r=s;break}r=r.parent}}}#y(){for(var e=this.#t;e!==null;){if(!e.is_fork){for(let[t,[,n]]of this.current)if(e.current.has(t)&&!n)return e}e=e.#t}return null}#b(e){for(let[t,n]of e.current)!this.previous.has(t)&&e.previous.has(t)&&this.previous.set(t,e.previous.get(t)),this.current.set(t,n);for(let[t,n]of e.async_deriveds){let e=this.async_deriveds.get(t);e&&n.promise.then(e.resolve).catch(e.reject)}e.async_deriveds.clear(),this.transfer_effects(e.#u,e.#d);let t=e=>{var n=e.reactions;if(n!==null&&!(e.f&2&&!(e.f&6144)))for(let e of n){var r=e.f;if(r&2)t(e);else{var i=e;r&4194320&&!this.async_deriveds.has(i)&&(this.#d.delete(i),A(i,x),this.schedule(i))}}};for(let e of this.current.keys())t(e);this.oncommit(()=>e.discard()),e.#S(),j=this,this.#_()}#x(e){for(var t=0;t{this.#m=!1,this.linked&&this.flush()}))}transfer_effects(e,t){for(let t of e)this.#u.add(t);for(let e of t)this.#d.add(e);e.clear(),t.clear()}oncommit(e){this.#r.add(e)}ondiscard(e){this.#i.add(e)}settled(){return(this.#s??=y()).promise}static ensure(){if(j===null){let t=j=new e;!Tt&&!wt&&Qe(()=>{t.#e||t.flush()})}return j}apply(){St=null}schedule(e){if(Ct=e,e.b?.is_pending&&e.f&16777228&&!(e.f&32768)){e.b.defer_effect(e);return}this.#c.push(e)}#S(){if(this.linked){var e=this.#t,t=this.#n;e===null||(e.#n=t),t===null?bt=e:t.#t=e,this.linked=!1}}};function jt(e){var t=wt;wt=!0;try{var n;for(e&&(j!==null&&!j.is_fork&&j.flush(),n=e());;){if($e(),j===null)return n;j.flush()}}finally{wt=t}}function Mt(){try{Ve()}catch(e){fn(e,Ct)}}var Nt=null;function Pt(e){var t=e.length;if(t!==0){for(var n=0;n0)){zt.clear();for(let e of Nt){if(e.f&24576)continue;let t=[e],n=e.parent;for(;n!==null;)Nt.has(n)&&(Nt.delete(n),t.push(n)),n=n.parent;for(let e=t.length-1;e>=0;e--){let n=t[e];n.f&24576||or(n)}}Nt.clear()}}Nt=null}}function Ft(e){j.schedule(e)}function It(e,t){if(!(e.f&32&&e.f&1024)){e.f&2048?t.d.push(e):e.f&4096&&t.m.push(e),A(e,b);for(var n=e.first;n!==null;)It(n,t),n=n.next}}function Lt(e){A(e,b);for(var t=e.first;t!==null;)Lt(t),t=t.next}var Rt=new Set,zt=new Map,Bt=!1;function Vt(e,t){return{f:0,v:e,reactions:null,equals:Me,rv:0,wv:0}}function Ht(e,t){let n=Vt(e,t);return Kn(n),n}function M(t,n=!1,r=!0){let i=Vt(t);return n||(i.equals=Pe),e&&r&&D!==null&&D.l!==null&&(D.l.s??=[]).push(i),i}function N(e,t,n=!1){return V!==null&&(!Hn||V.f&131072)&&Ye()&&V.f&4325394&&(Gn===null||!Gn.has(e))&&Ge(),Gt(e,n?Yt(t):t,Dt)}var Ut=null,Wt=0;function Gt(e,t,n=null){if(!e.equals(t)){Bn?zt.set(e,t):zt.has(e)||zt.set(e,e.v);var r=At.ensure();if(r.capture(e,t),e.f&2){let t=e;e.f&2048&>(t),St===null&&tt(t)}e.wv=$n(),Ut=null,Wt=0,Jt(e,x,n),Ut=null,Ye()&&H!==null&&H.f&1024&&!(H.f&96)&&(qn===null?Jn([e]):qn.push(e)),!r.is_fork&&Rt.size>0&&!Bt&&Kt()}return t}function Kt(){Bt=!1;for(let e of Rt){e.f&1024&&A(e,S);let t;try{t=er(e)}catch{t=!0}t&&or(e)}Rt.clear()}function qt(e){N(e,e.v+1)}function Jt(e,t,n){var r=e.reactions;if(r!==null){var i=Ye(),a=r.length;if(Wt+=a,Wt>1e5&&Ut===null&&(Ut=new Set),Ut!==null){if(Ut.has(e))return;Ut.add(e)}for(var o=0;o{if(Zn===c)return e();var t=V,n=Zn;Un(null),Qn(c);var r=e();return Un(t),Qn(n),r};return i&&n.set(`length`,Ht(e.length,s)),new Proxy(e,{defineProperty(e,t,r){(!(`value`in r)||r.configurable===!1||r.enumerable===!1||r.writable===!1)&&Ue();var i=n.get(t);return i===void 0?l(()=>{var e=Ht(r.value,s);return n.set(t,e),e}):N(i,r.value,!0),!0},deleteProperty(e,t){var i=n.get(t);if(i===void 0){if(t in e){let e=l(()=>Ht(r,s));n.set(t,e),qt(o)}}else N(i,r),qt(o);return!0},get(t,i,a){if(i===fe)return e;var o=n.get(i),c=i in t;if(o===void 0&&(!c||u(t,i)?.writable)&&(o=l(()=>Ht(Yt(c?t[i]:r),s)),n.set(i,o)),o!==void 0){var d=G(o);return d===r?void 0:d}return Reflect.get(t,i,a)},getOwnPropertyDescriptor(e,t){this.has?.(e,t);var i=Reflect.getOwnPropertyDescriptor(e,t),a=n.get(t);if(a!==void 0){var o=G(a);if(o===r)return;if(i&&`value`in i)i.value=o;else return{enumerable:!0,configurable:!0,value:o,writable:!0}}return i},has(e,t){if(t===fe)return!0;var i=n.get(t),a=i!==void 0&&i.v!==r||Reflect.has(e,t);return(i!==void 0||H!==null&&(!a||u(e,t)?.writable))&&(i===void 0&&(i=l(()=>Ht(a?Yt(e[t]):r,s)),n.set(t,i)),G(i)===r)?!1:a},set(e,t,a,c){var d=n.get(t),f=t in e;if(i&&t===`length`)for(var p=a;pHt(r,s)),n.set(p+``,m)):N(m,r)}if(d===void 0)(!f||u(e,t)?.writable)&&(d=l(()=>Ht(void 0,s)),N(d,Yt(a)),n.set(t,d));else{f=d.v!==r;var h=l(()=>Yt(a));N(d,h)}var g=Reflect.getOwnPropertyDescriptor(e,t);if(g?.set&&g.set.call(c,a),!f){if(i&&typeof t==`string`){var _=n.get(`length`),v=Number(t);Number.isInteger(v)&&v>=_.v&&N(_,v+1)}qt(o)}return!0},ownKeys(e){G(o);var t=Reflect.ownKeys(e).filter(e=>{var t=n.get(e);return t===void 0||t.v!==r});for(var[i,a]of n)a.v!==r&&!(i in e)&&t.push(i);return t},setPrototypeOf(){We()}})}function Xt(e){try{if(typeof e==`object`&&e&&fe in e)return e[fe]}catch{}return e}function Zt(e,t){return Object.is(Xt(e),Xt(t))}var Qt,$t,en,tn;function nn(){if(Qt===void 0){Qt=window,$t=/Firefox/.test(navigator.userAgent);var e=Element.prototype,t=Node.prototype,n=Text.prototype;en=u(t,`firstChild`).get,tn=u(t,`nextSibling`).get,h(e)&&(e[_e]=void 0,e[ge]=null,e[ve]=void 0,e.__e=void 0),h(n)&&(n[ye]=void 0)}}function P(e=``){return document.createTextNode(e)}function rn(e){return en.call(e)}function an(e){return tn.call(e)}function F(e,t){if(!C)return rn(e);var n=rn(w);if(n===null)n=w.appendChild(P());else if(t&&n.nodeType!==3){var r=P();return n?.before(r),T(r),r}return t&&un(n),T(n),n}function on(e,t=!1){if(!C){var n=rn(e);return n instanceof Comment&&n.data===``?an(n):n}if(t){if(w?.nodeType!==3){var r=P();return w?.before(r),T(r),r}un(w)}return w}function I(e,t=!1){if(!C)return rn(e);var n=F(e,t);return E(e),n}function L(e,t=1,n=!1){let r=C?w:e;for(var i;t--;)i=r,r=an(r);if(!C)return r;if(n){if(r?.nodeType!==3){var a=P();return r===null?i?.after(a):r.before(a),T(a),a}un(r)}return T(r),r}function sn(e){e.textContent=``}function cn(){return!1}function ln(e,t,n){return t==null||t===`http://www.w3.org/1999/xhtml`?n?document.createElement(e,{is:n}):document.createElement(e):n?document.createElementNS(t,e,{is:n}):document.createElementNS(t,e)}function un(e){if(e.nodeValue.length<65536)return;let t=e.nextSibling;for(;t!==null&&t.nodeType===3;)t.remove(),e.nodeValue+=t.nodeValue,t=e.nextSibling}function dn(e){var t=H;if(t===null)return V.f|=de,e;if(!(t.f&32768)&&!(t.f&4))throw e;fn(e,t)}function fn(e,t){if(!(t!==null&&t.f&16384)){for(;t!==null;){if(t.f&128&&!(t.f&33570816)){if(!(t.f&32768))throw e;try{t.b.error(e);return}catch(t){e=t}}t=t.parent}throw e}}function pn(e){H===null&&(V===null&&Be(e),ze()),Bn&&Re(e)}function mn(e,t){var n=t.last;n===null?t.last=t.first=e:(n.next=e,e.prev=n,t.last=e)}function hn(e,t){var n=H;n!==null&&n.f&8192&&(e|=ee);var r={ctx:D,deps:null,nodes:null,f:e|x|512,first:null,fn:t,last:null,next:null,parent:n,b:n&&n.b,prev:null,teardown:null,wv:0,ac:null};j?.register_created_effect(r);var i=r;if(e&4)Et===null?At.ensure().schedule(r):Et.push(r);else if(t!==null){try{or(r)}catch(e){throw B(r),e}i.deps===null&&i.teardown===null&&i.nodes===null&&i.first===i.last&&!(i.f&524288)&&(i=i.first,e&16&&e&65536&&i!==null&&(i.f|=ie))}if(i!==null&&(i.parent=n,n!==null&&mn(i,n),V!==null&&V.f&2&&!(e&64))){var a=V;(a.effects??=[]).push(i)}return r}function gn(){return V!==null&&!Hn}function _n(e){let t=hn(8,null);return A(t,b),t.teardown=e,t}function vn(e){pn(`$effect`);var t=H.f;if(!V&&t&32&&D!==null&&!D.i){var n=D;(n.e??=[]).push(e)}else return yn(e)}function yn(e){return hn(4|se,e)}function bn(e){return pn(`$effect.pre`),hn(8|se,e)}function xn(e){At.ensure();let t=hn(64|oe,e);return(e={})=>new Promise(n=>{e.outro?Nn(t,()=>{B(t),n(void 0)}):(B(t),n(void 0))})}function Sn(e){return hn(4,e)}function Cn(e,t){var n=D,r={effect:null,ran:!1,deps:e};n.l.$.push(r),r.effect=En(()=>{if(e(),!r.ran){r.ran=!0;var n=H;try{Wn(n.parent),K(t)}finally{Wn(n)}}})}function wn(){var e=D;En(()=>{for(var t of e.l.$){t.deps();var n=t.effect;n.f&1024&&n.deps!==null&&A(n,S),er(n)&&or(n),t.ran=!1}})}function Tn(e){return hn(ue|oe,e)}function En(e,t=0){return hn(8|t,e)}function R(e,t=[],n=[],r=[]){st(r,t,n,t=>{hn(8,()=>{e(...t.map(G))})})}function Dn(e,t=0){return hn(16|t,e)}function z(e){return hn(32|oe,e)}function On(e){var t=e.teardown;if(t!==null){let n=Bn,r=V;Vn(!0),Un(null);try{t.call(null)}catch(t){fn(t,e.parent)}finally{Vn(n),Un(r)}}}function kn(e,t=!1){var n=e.first;for(e.first=e.last=null;n!==null;){let e=n.ac;e!==null&&at(()=>{e.abort(xe)});var r=n.next;n.f&64?n.parent=null:B(n,t),n=r}}function An(e){for(var t=e.first;t!==null;){var n=t.next;t.f&32||B(t),t=n}}function B(e,t=!0){var n=!1;(t||e.f&262144)&&e.nodes!==null&&e.nodes.end!==null&&(jn(e.nodes.start,e.nodes.end),n=!0),e.f|=re,kn(e,t&&!n),ar(e,0);var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)e.stop();On(e),e.f^=re,e.f|=te;var i=e.parent;i!==null&&i.first!==null&&Mn(e),e.next=e.prev=e.teardown=e.ctx=e.deps=e.fn=e.nodes=e.ac=e.b=null}function jn(e,t){for(;e!==null;){var n=e===t?null:an(e);e.remove(),e=n}}function Mn(e){var t=e.parent,n=e.prev,r=e.next;n!==null&&(n.next=r),r!==null&&(r.prev=n),t!==null&&(t.first===e&&(t.first=r),t.last===e&&(t.last=n))}function Nn(e,t,n=!0){var r=[];e.f|=256,Pn(e,r,!0);var i=()=>{n&&B(e),t&&t()},a=r.length;if(a>0){var o=()=>--a||i();for(var s of r)s.out(o)}else i()}function Pn(e,t,n){if(!(e.f&8192)){e.f^=ee;var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)(e.is_global||n)&&t.push(e);for(var i=e.first;i!==null;){var a=i.next;if(!(i.f&64)){var o=!!(i.f&65536)||!!(i.f&32)&&!!(e.f&16);Pn(i,t,o?n:!1)}i=a}}}function Fn(e){e.f&=-257,In(e,!0)}function In(e,t){if(!(e.f&256)&&e.f&8192){e.f^=ee,e.f&1024||(A(e,x),At.ensure().schedule(e));for(var n=e.first;n!==null;){var r=n.next,i=!!(n.f&65536)||!!(n.f&32);In(n,i?t:!1),n=r}var a=e.nodes&&e.nodes.t;if(a!==null)for(let e of a)(e.is_global||t)&&e.in()}}function Ln(e,t){if(e.nodes)for(var n=e.nodes.start,r=e.nodes.end;n!==null;){var i=n===r?null:an(n);t.append(n),n=i}}var Rn=null,zn=!1,Bn=!1;function Vn(e){Bn=e}var V=null,Hn=!1;function Un(e){V=e}var H=null;function Wn(e){H=e}var Gn=null;function Kn(e){V!==null&&(V.f&2097152||V.f&2)&&(Gn??=new Set).add(e)}var U=null,W=0,qn=null;function Jn(e){qn=e}var Yn=1,Xn=0,Zn=Xn;function Qn(e){Zn=e}function $n(){return++Yn}function er(e){var t=e.f;if(t&2048)return!0;if(t&4096){for(var n=e.deps,r=n.length,i=0;ie.wv)return!0}t&512&&St===null&&A(e,b)}return!1}function tr(e,t,n=!0){var r=e.reactions;if(r!==null&&!(Gn!==null&&Gn.has(e)))for(var i=0;i{e.ac.abort(xe)}),e.ac=null);try{e.f|=le;var u=e.fn,d=u();e.f|=ne;var f=rr(e);if(Ye()&&qn!==null&&!Hn&&f!==null&&!(e.f&6146))for(var p=0;p0)for(t.length=W+U.length,r=0;r{c.ac.abort(xe),c.ac=null,A(c,x)}),vt(c),ar(c,0)}}function ar(e,t){var n=e.deps;if(n!==null)for(var r=t;rn?.call(this,e))}return e.startsWith(`pointer`)||e.startsWith(`touch`)||e===`wheel`?(i.__removed=!1,Qe(()=>{i.__removed||t.addEventListener(e,i,r)})):t.addEventListener(e,i,r),i}function hr(e,t,n,r,i){var a={capture:r,passive:i},o=mr(e,t,n,a);(t===document.body||t===window||t===document||t instanceof HTMLMediaElement)&&_n(()=>{o.__removed=!0,t.removeEventListener(e,o,a)})}function gr(e,t,n){(t[dr]??={})[e]=n}function _r(e){for(var t=0;t{yr=!1,vr=null}));var o=0,s=vr===e&&e[dr];if(s){var c=i.indexOf(s);if(c!==-1&&(t===document||t===window)){e[dr]=t;return}var u=i.indexOf(t);if(u===-1)return;c<=u&&(o=c)}if(a=i[o]||e.target,a!==t){l(e,`currentTarget`,{configurable:!0,get(){return a||n}});var d=V,f=H;Un(null),Wn(null);try{for(var p,m=[];a!==null&&a!==t;){try{var h=a[dr]?.[r];h!=null&&(!a.disabled||e.target===a)&&h.call(a,e)}catch(e){p?m.push(e):p=e}if(e.cancelBubble)break;o++,a=o{throw e});throw p}}finally{e[dr]=t,delete e.currentTarget,Un(d),Wn(f)}}}var xr=globalThis?.window?.trustedTypes&&globalThis.window.trustedTypes.createPolicy(`svelte-trusted-html`,{createHTML:e=>e});function Sr(e){return xr?.createHTML(e)??e}function Cr(e){var t=ln(`template`);return t.innerHTML=Sr(e.replaceAll(``,``)),t.content}function wr(e,t){var n=H;n.nodes===null&&(n.nodes={start:e,end:t,a:null,t:null})}function J(e,t){var n=!!(t&1),r=!!(t&2),i,a=!e.startsWith(``);return()=>{if(C)return wr(w,null),w;i===void 0&&(i=Cr(a?e:``+e),n||(i=rn(i)));var t=r||$t?document.importNode(i,!0):i.cloneNode(!0);if(n){var o=rn(t),s=t.lastChild;wr(o,s)}else wr(t,t);return t}}function Tr(){if(C)return wr(w,null),w;var e=document.createDocumentFragment(),t=document.createComment(``),n=P();return e.append(t,n),wr(t,n),e}function Y(e,t){if(C){var n=H;(!(n.f&32768)||n.nodes.end===null)&&(n.nodes.end=w),Oe();return}e!==null&&e.before(t)}[...`allowfullscreen.async.autofocus.autoplay.checked.controls.default.disabled.formnovalidate.indeterminate.inert.ismap.loop.multiple.muted.nomodule.novalidate.open.playsinline.readonly.required.reversed.seamless.selected.webkitdirectory.defer.disablepictureinpicture.disableremoteplayback`.split(`.`)];var Er=[`touchstart`,`touchmove`];function Dr(e){return Er.includes(e)}function Or(e){let t=0,n=Vt(0),r;return()=>{gn()&&(G(n),En(()=>(t===0&&(r=K(()=>e(()=>qt(n)))),t+=1,()=>{Qe(()=>{--t,t===0&&(r?.(),r=void 0,qt(n))})})))}}var kr=ie|oe;function Ar(e,t,n,r){new jr(e,t,n,r)}var jr=class{parent;is_pending=!1;transform_error;#e;#t=C?w:null;#n;#r;#i;#a=null;#o=null;#s=null;#c=null;#l=0;#u=0;#d=!1;#f=new Set;#p=new Set;#m=null;#h=Or(()=>(this.#m=Vt(this.#l),()=>{this.#m=null}));constructor(e,t,n,r){this.#e=e,this.#n=t,this.#r=e=>{var t=H;t.b=this,t.f|=128,n(e)},this.parent=H.b,this.transform_error=r??this.parent?.transform_error??(e=>e),this.#i=Dn(()=>{if(C){let e=this.#t;Oe();let t=e.data===`[!`;if(e.data.startsWith(`[?`)){let t=JSON.parse(e.data.slice(2));this.#_(t)}else t?this.#y():this.#g()}else this.#b()},kr),C&&(this.#e=w)}#g(){try{this.#a=z(()=>this.#r(this.#e))}catch(e){this.error(e)}}#_(e){let t=this.#n.failed,{reset:n,invoke_onerror:r}=this.#v(e);Qe(r),t&&(this.#s=z(()=>{t(this.#e,()=>e,()=>n)}))}#v(e){var t=!1,n=!1;let r=()=>{if(t){Ee();return}t=!0,n&&Ke(),this.#s!==null&&Nn(this.#s,()=>{this.#s=null}),this.#S(()=>{this.#b()})};return{reset:r,invoke_onerror:()=>{try{n=!0,this.#n.onerror?.(e,r),n=!1}catch(e){fn(e,this.#i&&this.#i.parent)}}}}#y(){let e=this.#n.pending;e&&(this.is_pending=!0,this.#o=z(()=>e(this.#e)),Qe(()=>{var e=this.#c=document.createDocumentFragment(),t=P(),n=!1;if(e.append(t),this.#a=this.#S(()=>{try{return z(()=>this.#r(t))}catch(e){try{this.error(e),n=!0}catch(e){fn(e,this.#i.parent)}return null}}),this.#a===null){this.#c=null,n&&this.#x(j);return}this.#u===0&&(this.#e.before(e),this.#c=null,Nn(this.#o,()=>{this.#o=null}),this.#x(j))}))}#b(){try{if(this.is_pending=this.has_pending_snippet(),this.#u=0,this.#l=0,this.#a=z(()=>{this.#r(this.#e)}),this.#u>0){var e=this.#c=document.createDocumentFragment();Ln(this.#a,e);let t=this.#n.pending;this.#o=z(()=>t(this.#e))}else this.#x(j)}catch(e){this.error(e)}}#x(e){this.is_pending=!1,e.transfer_effects(this.#f,this.#p)}defer_effect(e){nt(e,this.#f,this.#p)}is_rendered(){return!this.is_pending&&(!this.parent||this.parent.is_rendered())}has_pending_snippet(){return!!this.#n.pending}#S(e){var t=H,n=V,r=D;Wn(this.#i),Un(this.#i),qe(this.#i.ctx);try{return At.ensure(),e()}finally{Wn(t),Un(n),qe(r)}}#C(e,t){if(!this.has_pending_snippet()){this.parent&&this.parent.#C(e,t);return}this.#u+=e,this.#u===0&&(this.#x(t),this.#o&&Nn(this.#o,()=>{this.#o=null}),this.#c&&=(this.#e.before(this.#c),null))}update_pending_count(e,t){this.#C(e,t),this.#l+=e,!(!this.#m||this.#d)&&(this.#d=!0,Qe(()=>{this.#d=!1,this.#m&&Gt(this.#m,this.#l)}))}get_effect_pending(){return this.#h(),G(this.#m)}error(e){if(!this.#n.onerror&&!this.#n.failed)throw e;j?.is_fork?(this.#a&&j.skip_effect(this.#a),this.#o&&j.skip_effect(this.#o),this.#s&&j.skip_effect(this.#s),j.oncommit(()=>{this.#w(e)})):this.#w(e)}#w(e){this.#a&&=(B(this.#a),null),this.#o&&=(B(this.#o),null),this.#s&&=(B(this.#s),null),C&&(T(this.#t),ke(),T(Ae()));let t=this.#n.failed,n=e=>{let{reset:n,invoke_onerror:r}=this.#v(e);r(),t&&(this.#s=this.#S(()=>{try{return z(()=>{var r=H;r.b=this,r.f|=128,t(this.#e,()=>e,()=>n)})}catch(e){return fn(e,this.#i.parent),null}}))};Qe(()=>{var t;try{t=this.transform_error(e)}catch(e){fn(e,this.#i&&this.#i.parent);return}typeof t==`object`&&t&&typeof t.then==`function`?t.then(n,e=>fn(e,this.#i&&this.#i.parent)):n(t)})}};function X(e,t){var n=t==null?``:typeof t==`object`?`${t}`:t;n!==(e[ye]??=e.nodeValue)&&(e[ye]=n,e.nodeValue=`${n}`)}function Mr(e,t){return Pr(e,t)}var Nr=new Map;function Pr(e,{target:t,anchor:r,props:i={},events:a,context:o,intro:s=!0,transformError:l}){nn();var u=void 0,d=xn(()=>{var s=r??t.appendChild(P());Ar(s,{pending:()=>{}},t=>{O({});var r=D;if(o&&(r.c=o),a&&(i.$$events=a),C&&wr(t,null),u=e(t,i)||Je(),C&&(H.nodes.end=w,w===null||w.nodeType!==8||w.data!==`]`))throw we(),n;k()},l);var d=new Set,f=e=>{for(var n=0;n{for(var e of d)for(let r of[t,document]){var n=Nr.get(r),i=n.get(e);--i==0?(r.removeEventListener(e,br),n.delete(e),n.size===0&&Nr.delete(r)):n.set(e,i)}pr.delete(f),s!==r&&s.parentNode?.removeChild(s)}});return Fr.set(u,d),u}var Fr=new WeakMap,Ir=class{anchor;#e=new Map;#t=new Map;#n=new Map;#r=new Set;#i=!0;constructor(e,t=!0){this.anchor=e,this.#i=t}#a=e=>{if(this.#e.has(e)){var t=this.#e.get(e),n=this.#t.get(t);if(n)Fn(n),this.#r.delete(t);else{var r=this.#n.get(t);r&&(Fn(r.effect),this.#t.set(t,r.effect),this.#n.delete(t),r.fragment.lastChild.remove(),this.anchor.before(r.fragment),n=r.effect)}for(let[t,n]of this.#e){if(this.#e.delete(t),t===e)break;let r=this.#n.get(n);r&&(B(r.effect),this.#n.delete(n))}for(let[e,r]of this.#t){if(e===t||this.#r.has(e))continue;let i=()=>{if(Array.from(this.#e.values()).includes(e)){var t=document.createDocumentFragment();Ln(r,t),t.append(P()),this.#n.set(e,{effect:r,fragment:t})}else B(r);this.#r.delete(e),this.#t.delete(e)};this.#i||!n?(this.#r.add(e),Nn(r,i,!1)):i()}}};#o=e=>{this.#e.delete(e);let t=Array.from(this.#e.values());for(let[e,n]of this.#n)t.includes(e)||(B(n.effect),this.#n.delete(e))};ensure(e,t){var n=j,r=cn();if(t&&!this.#t.has(e)&&!this.#n.has(e)){if(r){var i=document.createDocumentFragment(),a=P();i.append(a),this.#n.set(e,{effect:z(()=>t(a)),fragment:i})}else this.#t.set(e,z(()=>t(this.anchor)))}if(this.#e.set(n,e),r){for(let[t,r]of this.#t)t===e?n.unskip_effect(r):n.skip_effect(r);for(let[t,r]of this.#n)t===e?n.unskip_effect(r.effect):n.skip_effect(r.effect);n.oncommit(this.#a),n.ondiscard(this.#o)}else C&&(this.anchor=w),this.#a(n)}};function Lr(t){D===null&&Fe(`onMount`),e&&D.l!==null?Rr(D).m.push(t):vn(()=>{let e=K(t);if(typeof e==`function`)return e})}function Rr(e){var t=e.l;return t.u??={a:[],b:[],m:[]}}function Z(e,t,n=!1){var r;C&&(r=w,Oe());var i=new Ir(e),a=n?ie:0;function o(e,t){if(C){var n=je(r);if(e!==parseInt(n.substring(1))){var a=Ae();T(a),i.anchor=a,De(!1),i.ensure(e,t),De(!0);return}}i.ensure(e,t)}Dn(()=>{var e=!1;t((t,n=0)=>{e=!0,o(n,t)}),e||o(-1,null)},a)}var zr=Symbol(`NaN`);function Br(e,t,n){C&&Oe();var r=new Ir(e),i=!Ye();Dn(()=>{var e=t();e!==e&&(e=zr),i&&typeof e==`object`&&e&&(e={}),r.ensure(e,n)})}function Vr(e,t){return t}function Hr(e,t,n){for(var r=[],i=t.length,a,o=t.length,s=0;s{if(a){if(a.pending.delete(n),a.done.add(n),a.pending.size===0){var t=e.outrogroups;Ur(e,c(a.done)),t.delete(a),t.size===0&&(e.outrogroups=null)}}else--o},!1)}if(o===0){var l=r.length===0&&n!==null&&e.pending.size===0;if(l){var u=n,d=u.parentNode;sn(d),d.append(u),e.items.clear()}Ur(e,t,!l)}else a={pending:new Set(t),done:new Set},(e.outrogroups??=new Set).add(a)}function Ur(e,t,n=!0){var r;if(e.pending.size>0){r=new Set;for(let t of e.pending.values())for(let n of t)r.add(e.items.get(n).e)}for(var i=0;i{var e=n();return a(e)?e:e==null?[]:c(e)}),p,m=new Map,h=!0;function g(e){v.effect.f&16384||(v.pending.delete(e),v.fallback=d,qr(v,p,s,t,r),d!==null&&(p.length===0?d.f&33554432?(d.f^=ce,Yr(d,null,s)):Fn(d):Nn(d,()=>{d=null})))}function _(e){v.pending.delete(e)}var v={effect:Dn(()=>{p=G(f);var e=p.length;let a=!1;C&&je(s)===`[!`!=(e===0)&&(s=Ae(),T(s),De(!1),a=!0);for(var c=new Set,u=j,v=cn(),y=0;yo(s)):(d=z(()=>o(Wr??=P())),d.f|=ce)),e>c.size&&Le(``,``,``),C&&e>0&&T(Ae()),!h){if(m.set(u,c),v){for(let[e,t]of l)c.has(e)||u.skip_effect(t.e);u.oncommit(g),u.ondiscard(_)}else g(u)}a&&De(!0),G(f)}),flags:t,items:l,pending:m,outrogroups:null,fallback:d};h=!1,C&&(s=w)}function Kr(e){for(;e!==null&&!(e.f&32);)e=e.next;return e}function qr(e,t,n,r,i){var a=!!(r&8),o=t.length,s=e.items,l=Kr(e.effect.first),u,d=null,f,p=[],m=[],h,g,_,v;if(a)for(v=0;v0){var re=r&4&&o===0?n:null;if(a){for(v=0;v{if(f!==void 0)for(_ of f)_.nodes?.a?.apply()})}function Jr(e,t,n,r,i,a,o,s){var c=o&1?o&16?Vt(n):M(n,!1,!1):null,l=o&2?Vt(i):null;return{v:c,i:l,e:z(()=>(a(t,c??n,l??i,s),()=>{e.delete(r)}))}}function Yr(e,t,n){if(e.nodes)for(var r=e.nodes.start,i=e.nodes.end,a=t&&!(t.f&33554432)?t.nodes.start:n;r!==null;){var o=an(r);if(a.before(r),r===i)return;r=o}}function Xr(e,t,n){t===null?e.effect.first=n:t.next=n,n===null?e.effect.last=t:n.prev=t}function Zr(e,t){let n=null,r=C;var i;if(C){n=w;for(var a=rn(document.head);a!==null&&(a.nodeType!==8||a.data!==e);)a=an(a);if(a===null)De(!1);else{var o=an(a);a.remove(),T(o)}}C||(i=document.head.appendChild(P()));try{Dn(()=>{var e=z(()=>t(i));e.f|=ae,C||(e.nodes===null?e.nodes={start:i,end:i,a:null,t:null}:e.nodes.end=i)})}finally{r&&(De(!0),T(n))}}function Qr(e){var t,n,r=``;if(typeof e==`string`||typeof e==`number`)r+=e;else if(typeof e==`object`){if(Array.isArray(e)){var i=e.length;for(t=0;t=0;){var s=o+a;(o===0||ti.includes(r[o-1]))&&(s===r.length||ti.includes(r[s]))?r=(o===0?``:r.substring(0,o))+r.substring(s+1):o=s}}return r===``?null:r}function ri(e,t,n,r,i,a){var o=e[_e];if(C||o!==n||o===void 0){var s=ni(n,r,a);(!C||s!==e.getAttribute(`class`))&&(s==null?e.removeAttribute(`class`):t?e.className=s:e.setAttribute(`class`,s)),e[_e]=n}else if(a&&i!==a)for(var c in a){var l=!!a[c];(i==null||l!==!!i[c])&&e.classList.toggle(c,l)}return a}function ii(e,t){t?e.hasAttribute(`selected`)||e.setAttribute(`selected`,``):e.removeAttribute(`selected`)}function ai(e,t){var n=e.__defaultValue,r=e.multiple,i=r?n??[]:null;if(!r||a(i)){var o=e.selectedIndex,s=t&&r?new Set(e.selectedOptions):null;for(var c of e.options){var l=li(c);ii(c,r?i.includes(l):Zt(l,n))}if(t){if(s!==null)for(c of e.options){var u=s.has(c);c.selected!==u&&(c.selected=u)}else e.selectedIndex!==o&&(e.selectedIndex=o)}}}function oi(e,t,n=!1){if(e.multiple){if(t==null)return;if(!a(t))return Te();for(var r of e.options)r.selected=t.includes(li(r));return}for(r of e.options)if(Zt(li(r),t)){r.selected=!0;return}(!n||t!==void 0)&&(e.selectedIndex=-1)}function si(e){var t=new MutationObserver(t=>{t.every(ui)||(`__defaultValue`in e&&ai(e,!1),`__value`in e&&oi(e,e.__value))});t.observe(e,{childList:!0,subtree:!0,attributes:!0,attributeFilter:[`value`]}),_n(()=>{t.disconnect()})}function ci(e,t,n=t){var r=new WeakSet,i=!0;ot(e,`change`,t=>{var i=t?`[selected]`:`:checked`,a;if(e.multiple)a=[].map.call(e.querySelectorAll(i),li);else{var o=e.querySelector(i)??e.querySelector(`option:not([disabled])`);a=o&&li(o)}n(a),e.__value=a,j!==null&&r.add(j)}),Sn(()=>{var a=t();if(e===document.activeElement){var o=j;if(r.has(o))return}if(oi(e,a,i),i&&a===void 0){var s=e.querySelector(`:checked`);s!==null&&(a=li(s),n(a))}e.__value=a,i=!1})}function li(e){return`__value`in e?e.__value:e.value}function ui(e){if(e.target.closest(`selectedcontent`)!==null)return!0;if(e.type===`childList`){var t=[...e.addedNodes,...e.removedNodes];return t.length>0&&t.every(e=>e.nodeName===`SELECTEDCONTENT`)}return!1}var di=Symbol(`is custom element`),fi=Symbol(`is html`),pi=Se?`link`:`LINK`;function mi(e){if(C){var t=!1,n=()=>{if(!t){if(t=!0,e.hasAttribute(`value`)){var n=e.value;Q(e,`value`,null),e.value=n}if(e.hasAttribute(`checked`)){var r=e.checked;Q(e,`checked`,null),e.checked=r}}};e[be]=n,Qe(n),it()}}function hi(e,t){var n=gi(e);n.checked!==(n.checked=t??void 0)&&(e.checked=t)}function Q(e,t,n,r){var i=gi(e);C&&(i[t]=e.getAttribute(t),t===`src`||t===`srcset`||t===`href`&&e.nodeName===pi)||i[t]!==(i[t]=n)&&(t===`loading`&&(e[he]=n),n==null?e.removeAttribute(t):typeof n!=`string`&&vi(e).has(t)?e[t]=n:e.setAttribute(t,n))}function gi(e){return e[ge]??={[di]:e.nodeName.includes(`-`),[fi]:e.namespaceURI===i}}var _i=new Map;function vi(e){var t=e.getAttribute(`is`)||e.nodeName,n=_i.get(t);if(n)return n;_i.set(t,n=new Set);for(var r,i=e,a=Element.prototype;a!==i;){for(var o in r=d(i),r)r[o].set&&o!==`innerHTML`&&o!==`textContent`&&o!==`innerText`&&n.add(o);i=m(i)}return n}function yi(e,t,n=t){var r=new WeakSet;ot(e,`input`,async i=>{var a=i?e.defaultValue:e.value;if(a=bi(e)?xi(a):a,n(a),j!==null&&r.add(j),await sr(),a!==(a=t())){var o=e.selectionStart,s=e.selectionEnd,c=e.value.length;if(e.value=a??``,s!==null){var l=e.value.length;o===s&&s===c&&l>c?(e.selectionStart=l,e.selectionEnd=l):(e.selectionStart=o,e.selectionEnd=Math.min(s,l))}}}),(C&&e.defaultValue!==e.value||K(t)==null&&e.value)&&(n(bi(e)?xi(e.value):e.value),j!==null&&r.add(j)),En(()=>{var n=t();if(e===document.activeElement){var i=j;if(r.has(i))return}bi(e)&&n===xi(e.value)||(e.type!==`date`||n||e.value)&&n!==e.value&&(e.value=n??``)})}function bi(e){var t=e.type;return t===`number`||t===`range`}function xi(e){return e===``?null:+e}function Si(e=!1){let t=D,n=t.l.u;if(!n)return;let r=()=>q(t.s);if(e){let e=0,n={},i=dt(()=>{let r=!1,i=t.s;for(let e in i)i[e]!==n[e]&&(n[e]=i[e],r=!0);return r&&e++,e});r=()=>G(i)}n.b.length&&bn(()=>{Ci(t,r),v(n.b)}),vn(()=>{let e=K(()=>n.m.map(_));return()=>{for(let t of e)typeof t==`function`&&t()}}),n.a.length&&vn(()=>{Ci(t,r),v(n.a)})}function Ci(e,t){if(e.l.s)for(let t of e.l.s)G(t);t()}function wi(e,t,n){if(e==null)return t(void 0),n&&n(void 0),g;let r=K(()=>e.subscribe(t,n));return r.unsubscribe?()=>r.unsubscribe():r}var Ti=[];function Ei(e,t=g){let n=null,r=new Set;function i(t){if(Ne(e,t)&&(e=t,n)){let t=!Ti.length;for(let t of r)t[1](),Ti.push(t,e);if(t){for(let e=0;e{r.delete(c),r.size===0&&n&&(n(),n=null)}}return{set:i,update:a,subscribe:o}}function Di(e){let t;return wi(e,e=>t=e)(),t}var Oi=!1,ki=Symbol(`unmounted`);function Ai(e,t,n){let r=n[t]??={store:null,source:M(void 0),unsubscribe:g};if(r.store!==e&&!(ki in n)){if(r.unsubscribe(),r.store=e??null,e==null)r.source.v=void 0,r.unsubscribe=g;else{var i=!0;r.unsubscribe=wi(e,e=>{i?r.source.v=e:N(r.source,e)}),i=!1}}return e&&ki in n?Di(e):G(r.source)}function ji(){let e={};function t(){_n(()=>{for(var t in e)e[t].unsubscribe();l(e,ki,{enumerable:!1,value:!0})})}return[e,t]}function Mi(e){var t=Oi;try{return Oi=!1,[e(),Oi]}finally{Oi=t}}function $(t,n,r,i){var a=!e||!!(r&2),o=!!(r&8),s=!!(r&16),c=i,l=!0,d=void 0,f=()=>s&&a?(d??=dt(i),G(d)):(l&&(l=!1,c=s?K(i):i),c);let p;if(o){var m=fe in t||me in t;p=u(t,n)?.set??(m&&n in t?e=>t[n]=e:void 0)}var h,g=!1;o?[h,g]=Mi(()=>t[n]):h=t[n],h===void 0&&i!==void 0&&(h=f(),p&&(a&&He(n),p(h)));var _=a?()=>{var e=t[n];return e===void 0?f():(l=!0,e)}:()=>{var e=t[n];return e!==void 0&&(c=void 0),e===void 0?c:e};if(a&&!(r&4))return _;if(p){var v=t.$$legacy;return(function(e,t){return arguments.length>0?((!a||!t||v||g)&&p(t?_():e),e):_()})}var y=!1,b=(r&1?dt:mt)(()=>(y=!1,_()));o&&G(b);var x=H;return(function(e,t){if(arguments.length>0){let n=t?G(b):a&&o?Yt(e):e;return N(b,n),y=!0,c!==void 0&&(c=n),e}return Bn&&y||x.f&16384?b.v:G(b)})}function Ni(e){let t=Ei({paired:!!e,controller:!1,busy:!1,error:``}),n=e,r,i=``,a={paired:!!e,controller:!1,busy:!1,error:``},o=!1;function s(e){a={...a,...e},t.set(a)}async function c(e=!1){if(!o&&n){o=!0;try{let t=await fetch(`/api/state`,{cache:`no-store`,headers:{"X-Setup-Session-Key":n}});if(!t.ok)throw Error(`The local setup session is unavailable.`);s({view:await t.json(),...e?{}:{error:``}})}catch{s({view:void 0,error:`Connection lost. The CLI may have stopped. Check the terminal before trying again.`})}finally{o=!1}}}async function l(){if(n)try{let e=await fetch(`/api/bootstrap`,{cache:`no-store`,headers:{"X-Setup-Session-Key":n}});if(!e.ok)throw Error(`Could not join this local session.`);let t=await e.json();r=t.capability,i=t.takeoverTicket,s({controller:t.controller,error:``}),await c()}catch{n=void 0,r=void 0,i=``,s({view:void 0,paired:!1,controller:!1,error:`Could not connect to the local setup session. Check the terminal and pair again.`})}}async function u(e){if(!(a.busy||a.paired)){s({busy:!0,error:``});try{let t=await fetch(`/api/pair`,{method:`POST`,cache:`no-store`,headers:{"Content-Type":`application/json`},body:JSON.stringify({code:e.trim().toLowerCase()})}),r=await t.json();if(!t.ok)throw Error(String(r.error??`Pairing was rejected.`));if(typeof r.sessionKey!=`string`||!/^[a-f0-9]{64}$/.test(r.sessionKey))throw Error(`Invalid local pairing response.`);n=r.sessionKey,s({paired:!0,error:``}),await l()}catch(e){s({error:e instanceof Error?e.message:`Could not pair this browser.`})}finally{s({busy:!1})}}}async function d(e,t,i=!0){let a=await fetch(e,{method:`POST`,cache:`no-store`,headers:{"Content-Type":`application/json`,"X-Setup-Session-Key":n,...i&&r?{"X-Setup-Capability":r}:{}},body:JSON.stringify(t)}),o=await a.json();if(!a.ok)throw Error(String(o.error??`The request was rejected.`));return o}async function f(e,t){if(!a.busy&&a.controller&&a.view?.promptRevision===e){s({busy:!0,error:``});try{await d(`/api/answer`,{revision:e,value:t}),await c()}catch(e){let t=e instanceof Error?e.message:`Could not submit this answer.`;s({error:t}),/read-only|Control moved/.test(t)?await l():await c(!0)}finally{s({busy:!1})}}}async function p(){if(a.controller&&!a.busy){s({busy:!0,error:``});try{await d(`/api/cancel`,{}),await c()}catch(e){let t=e instanceof Error?e.message:`Cancellation failed.`;s({error:t}),/read-only|Control moved/.test(t)&&await l()}finally{s({busy:!1})}}}async function m(){try{let e=await d(`/api/takeover`,{ticket:i},!1);r=String(e.capability),s({controller:!0,error:``}),await c()}catch(e){s({error:e instanceof Error?e.message:`Takeover failed.`}),await l()}}async function h(){try{await d(`/api/close`,{})}catch{}}return{subscribe:t.subscribe,pair:u,connect:l,refresh:c,submit:f,cancel:p,takeOver:m,close:h}}var Pi=J(`
                                                                                • `),Fi=J(``);function Ii(e,t){O(t,!1);let n=$(t,`journey`,8),r=[`Repository`,`Setup choices`,`Setup PAT`,`Plan`,`Bot PAT & credentials`,`Apply`];Si();var i=Fi(),a=L(F(i),4);Gr(a,5,()=>r,Vr,(e,t,r)=>{var i=Pi();let a;var o=F(i),s=I(o,!0),c=I(L(o,2),!0);E(i),R(e=>{Q(i,`aria-current`,(q(n()),K(()=>n()?.position===r+1?`step`:void 0))),a=ri(i,1,``,null,a,{current:n()?.position===r+1,completed:(n()?.position??0)>r+1}),X(s,e),X(c,G(t))},[()=>(q(n()),K(()=>(n()?.position??0)>r+1?`✓`:String(r+1).padStart(2,`0`)))]),Y(e,i)}),E(a),ke(2),E(i),Y(e,i),k()}var Li=J(`
                                                                                  `);function Ri(e,t){O(t,!1);let n=M(`system`);Cn(()=>G(n),()=>{document.documentElement.dataset.theme=G(n)}),wn();var r=Li(),i=F(r);let a;var o=L(i,2);let s;var c=L(o,2);let l;E(r),R(()=>{Q(i,`aria-pressed`,G(n)===`system`),a=ri(i,1,``,null,a,{active:G(n)===`system`}),Q(o,`aria-pressed`,G(n)===`light`),s=ri(o,1,``,null,s,{active:G(n)===`light`}),Q(c,`aria-pressed`,G(n)===`dark`),l=ri(c,1,``,null,l,{active:G(n)===`dark`})}),gr(`click`,i,()=>N(n,`system`)),gr(`click`,o,()=>N(n,`light`)),gr(`click`,c,()=>N(n,`dark`)),Y(e,r),k()}_r([`click`]);var zi=J(`
                                                                                  LOCAL SESSION
                                                                                  `);function Bi(e,t){let n=$(t,`repository`,8);var r=zi(),i=F(r),a=I(L(F(i),2),!0);E(i);var o=L(i,2);Ri(L(F(o)),{}),E(o),E(r),R(()=>X(a,n()??`Connecting…`)),Y(e,r)}var Vi=J(`

                                                                                  `,1);function Hi(e,t){O(t,!1);let n=M(),r=$(t,`view`,8);Cn(()=>q(r()),()=>{N(n,r()?.outcome===`complete`?`Setup complete.`:r()?.outcome===`dry-run`?`Preview complete.`:r()?.outcome===`cancelled`?`Setup cancelled.`:r()?.outcome?`Setup needs attention.`:r()?.prompt?.title??`Preparing your setup…`)}),wn(),Si();var i=Vi(),a=on(i),o=L(F(a)),s=I(L(o));E(a);var c=L(a,2),l=I(c,!0),u=I(L(c,2),!0);R((e,t)=>{X(o,` ${e??``} `),X(s,`${t??``} / 06`),X(l,G(n)),X(u,(q(r()),K(()=>r()?.outcome?`The terminal contains the detailed result. Review the next steps below before closing this session.`:`One decision at a time. Your choices shape the permissions, plan and credentials needed for this repository.`)))},[()=>(q(r()),K(()=>r()?.journey?.current?.toUpperCase()??`GETTING READY`)),()=>(q(r()),K(()=>String(r()?.journey?.position??1).padStart(2,`0`)))]),Y(e,i),k()}function Ui(e){try{let t=new URL(e??``);return t.protocol===`https:`&&t.hostname===`github.com`&&(t.pathname===`/settings/personal-access-tokens`||t.pathname.startsWith(`/settings/personal-access-tokens/`))?t.toString():void 0}catch{return}}var Wi=J(``),Gi=J(``);function Ki(e,t){let n=$(t,`label`,8),r=$(t,`variant`,8,`primary`),i=$(t,`disabled`,8,!1),a=$(t,`arrow`,8,!1),o=$(t,`onClick`,8);var s=Gi(),c=F(s,!0),l=L(c),u=e=>{Y(e,Wi())};Z(l,e=>{a()&&e(u)}),E(s),R(()=>{ri(s,1,ei(r())),s.disabled=i(),X(c,n())}),gr(`click`,s,function(...e){o()?.apply(this,e)}),Y(e,s)}_r([`click`]);var qi=J(`Open GitHub link ↗`),Ji=J(`

                                                                                  `);function Yi(e,t){O(t,!1);let n=M(),r=$(t,`tone`,8,`info`),i=$(t,`title`,8),a=$(t,`message`,8),o=$(t,`link`,8,void 0),s=$(t,`actionLabel`,8,void 0),c=$(t,`onAction`,8,void 0);Cn(()=>q(o()),()=>{N(n,Ui(o()))}),wn(),Si();var l=Ji(),u=F(l),d=I(u,!0),f=L(u),p=I(f,!0),m=L(f,2),h=e=>{var t=qi();R(()=>Q(t,`href`,G(n))),Y(e,t)};Z(m,e=>{G(n)&&e(h)});var g=L(m,2),_=e=>{Ki(e,{get label(){return s()},variant:`secondary`,get onClick(){return c()}})};Z(g,e=>{s()&&c()&&e(_)}),E(l),R(()=>{ri(l,1,`banner ${r()??``}`),Q(l,`role`,r()===`error`?`alert`:`status`),X(d,i()),X(p,a())}),Y(e,l),k()}function Xi(e){let t=String(e.question.defaultValue),n=t.split(`,`).map(e=>e.trim()).filter(Boolean);return{value:t,selected:e.question.kind===`multi-select`?n.includes(`All`)&&e.question.choices?.includes(`All`)?[`All`]:(e.question.choices??[]).filter(e=>e!==`All`&&n.includes(e.split(` — `)[0])):e.question.kind===`scope-overrides`?n:[]}}function Zi(e,t){return t===`All`?e.includes(`All`)?[]:[`All`]:e.includes(t)?e.filter(e=>e!==t):[...e.filter(e=>e!==`All`),t]}function Qi(e,t,n){return e.question.kind===`scope-overrides`||e.question.kind===`multi-select`?n.length?n.join(`,`):`none`:t}var $i=J(`PERMISSION PREVIEW`),ea=J(`
                                                                                  `),ta=J(``),na=J(``),ra=J(``),ia=J(`
                                                                                  `),aa=J(``),oa=J(`

                                                                                  `,1);function sa(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`controller`,8),i=$(t,`busy`,8),a=$(t,`onSubmit`,8),o=Xi(n()),s=M(o.value),c=M(o.selected);Si();var l=oa(),u=on(l),d=F(u),f=I(d,!0),p=L(d),m=e=>{Y(e,$i())};Z(p,e=>{q(n()),K(()=>n().phase===`permission-intent`)&&e(m)}),E(u);var h=L(u,2),g=e=>{var t=ea(),i=F(t);let a;var o=L(i);let c;E(t),R(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),Q(i,`aria-pressed`,G(s)===`yes`||G(s)===`true`),i.disabled=!r(),a=ri(i,1,``,null,a,{selected:G(s)===`yes`||G(s)===`true`}),Q(o,`aria-pressed`,G(s)===`no`||G(s)===`false`),o.disabled=!r(),c=ri(o,1,``,null,c,{selected:G(s)===`no`||G(s)===`false`})}),gr(`click`,i,()=>N(s,`yes`)),gr(`click`,o,()=>N(s,`no`)),Y(e,t)},_=e=>{var t=na();Gr(t,5,()=>(q(n()),K(()=>n().question.choices??[])),Vr,(e,t)=>{var n=ta(),r=I(n,!0),i={};R(()=>{X(r,G(t)),i!==(i=G(t))&&(n.value=(n.__value=i)??``)}),Y(e,n)}),E(t),si(t),R(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),t.disabled=!r()}),ci(t,()=>G(s),e=>N(s,e)),Y(e,t)},v=e=>{var t=ia();Gr(t,5,()=>(q(n()),K(()=>n().question.kind===`multi-select`?n().question.choices??[]:n().question.allowedNames??[])),Vr,(e,t)=>{var n=ra(),i=F(n);mi(i);var a=I(L(i),!0);E(n),R(e=>{hi(i,e),i.disabled=!r(),X(a,G(t))},[()=>(G(c),G(t),K(()=>G(c).includes(G(t))))]),gr(`change`,i,()=>N(c,Zi(G(c),G(t)))),Y(e,n)}),E(t),R(()=>Q(t,`aria-label`,(q(n()),K(()=>n().question.label)))),Y(e,t)},y=e=>{var t=aa();mi(t),R(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),Q(t,`type`,(q(n()),K(()=>n().question.kind===`number`?`number`:`text`))),Q(t,`min`,(q(n()),K(()=>n().question.kind===`number`?0:void 0))),t.disabled=!r()}),yi(t,()=>G(s),e=>N(s,e)),Y(e,t)};Z(h,e=>{q(n()),K(()=>n().question.kind===`boolean`)?e(g):(q(n()),K(()=>n().question.kind===`choice`)?e(_,1):(q(n()),K(()=>n().question.kind===`multi-select`||n().question.kind===`scope-overrides`)?e(v,2):e(y,-1)))});var b=L(h,2),x=I(b),S=L(b,2);{let e=mt(()=>!r()||i());Ki(S,{label:`Continue`,arrow:!0,onClick:()=>a()(Qi(n(),G(s),G(c))),get disabled(){return G(e)}})}R((e,t)=>{X(f,e),X(x,`Suggested answer: ${t??``} · You can review choices again before creating your setup PAT.`)},[()=>(q(n()),K(()=>n().question.label.replace(` (Space toggles, Enter confirms)`,``))),()=>(q(n()),K(()=>String(n().question.defaultValue)||`none`))]),Y(e,l),k()}_r([`click`,`change`]);var ca=J(``),la=J(`
                                                                                  `);function ua(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`controller`,8),i=$(t,`busy`,8),a=$(t,`onSubmit`,8);Si();var o=la();Gr(o,5,()=>(q(n()),K(()=>n().choices)),Vr,(e,t)=>{var n=ca(),o=I(F(n),!0);ke(),E(n),R(()=>{n.disabled=!r()||i(),X(o,G(t))}),gr(`click`,n,()=>a()(G(t))),Y(e,n)}),E(o),Y(e,o),k()}_r([`click`]);var da=J(`Open the official GitHub PAT form

                                                                                  Check the signed-in account, select Only select repositories, then select this repository. GitHub handles 2FA and creates the PAT.

                                                                                  `,1),fa=J(`

                                                                                  Sent only to this local process. It will not be shown again or saved in browser storage.

                                                                                  `),pa=J(` `,1);function ma(e,t){O(t,!1);let n=M(),r=$(t,`prompt`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8),s=M(``);function c(){let e=G(s);r().kind===`secret`&&N(s,``),o()(e)}Cn(()=>q(r()),()=>{N(n,Ui(r().link))}),wn(),Si();var l=pa(),u=on(l),d=e=>{var t=da(),r=on(t);ke(),R(()=>Q(r,`href`,G(n))),Y(e,t)};Z(u,e=>{G(n)&&e(d)});var f=L(u,2),p=I(f,!0),m=L(f,2);mi(m);var h=L(m,2),g=e=>{Y(e,fa())};Z(h,e=>{q(r()),K(()=>r().kind===`secret`)&&e(g)});var _=L(h,2);{let e=mt(()=>(q(i()),q(a()),q(r()),G(s),K(()=>!i()||a()||!r().optional&&!G(s).trim())));Ki(_,{label:`Continue`,arrow:!0,onClick:c,get disabled(){return G(e)}})}R(()=>{X(p,(q(r()),K(()=>r().kind===`secret`?`Paste the value here`:`Your answer`))),Q(m,`type`,(q(r()),K(()=>r().kind===`secret`?`password`:`text`))),m.disabled=!i(),Q(m,`placeholder`,(q(r()),K(()=>r().kind===`secret`?`Hidden after submission`:`Type your answer`)))}),yi(m,()=>G(s),e=>N(s,e)),Y(e,l),k()}var ha=J(`
                                                                                • `),ga=J(`

                                                                                    `),_a=J(`

                                                                                    Before you continue

                                                                                      `),va=J(`

                                                                                      Review exactly what this run may change. The bot PAT and any additional credentials are collected next.

                                                                                      `,1);function ya(e,t){O(t,!1);let n=M(),r=$(t,`prompt`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8);Cn(()=>q(r()),()=>{N(n,[{title:`Files`,items:r().plan.files},{title:`Workflows`,items:r().plan.workflows},{title:`Variables`,items:r().plan.variables},{title:`Secret names`,items:r().plan.secrets}])}),wn(),Si();var s=va(),c=L(on(s),2);Gr(c,5,()=>G(n),Vr,(e,t)=>{var n=ga(),r=F(n),i=F(r),a=I(L(i),!0);E(r);var o=L(r);Gr(o,5,()=>(G(t),K(()=>G(t).items)),Vr,(e,t)=>{var n=ha(),r=I(n,!0);R(()=>X(r,G(t))),Y(e,n)}),E(o),E(n),R(()=>{X(i,`${G(t),K(()=>G(t).title)??``} `),X(a,(G(t),K(()=>G(t).items.length)))}),Y(e,n)}),E(c);var l=L(c,2),u=e=>{var t=_a(),n=L(F(t));Gr(n,5,()=>(q(r()),K(()=>r().plan.warnings)),Vr,(e,t)=>{var n=ha(),r=I(n,!0);R(()=>X(r,G(t))),Y(e,n)}),E(n),E(t),Y(e,t)};Z(l,e=>{q(r()),K(()=>r().plan.warnings.length)&&e(u)});var d=L(l,2),f=F(d);{let e=mt(()=>!i()||a());Ki(f,{label:`Stop here`,variant:`secondary`,onClick:()=>o()(`decline`),get disabled(){return G(e)}})}var p=L(f);{let e=mt(()=>!i()||a());Ki(p,{label:`Approve this plan`,arrow:!0,onClick:()=>o()(`approve`),get disabled(){return G(e)}})}E(d),Y(e,s),k()}var ba=J(`

                                                                                      `),xa=J(`
                                                                                      CURRENT DECISION
                                                                                      `);function Sa(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`revision`,8),i=$(t,`promptRevision`,8),a=$(t,`controller`,8),o=$(t,`busy`,8),s=$(t,`onSubmit`,8);Si();var c=xa(),l=F(c),u=I(L(F(l)));E(l);var d=L(l,2),f=e=>{var t=ba(),r=I(t,!0);R(()=>X(r,(q(n()),K(()=>n().description)))),Y(e,t)};Z(d,e=>{q(n()),K(()=>`description`in n()&&n().description)&&e(f)}),Br(L(d,2),i,e=>{var t=Tr(),r=on(t),i=e=>{sa(e,{get prompt(){return n()},get controller(){return a()},get busy(){return o()},get onSubmit(){return s()}})},c=e=>{ua(e,{get prompt(){return n()},get controller(){return a()},get busy(){return o()},get onSubmit(){return s()}})},l=e=>{ma(e,{get prompt(){return n()},get controller(){return a()},get busy(){return o()},get onSubmit(){return s()}})},u=e=>{ya(e,{get prompt(){return n()},get controller(){return a()},get busy(){return o()},get onSubmit(){return s()}})};Z(r,e=>{q(n()),K(()=>n().kind===`question`)?e(i):(q(n()),K(()=>n().kind===`choice`||n().kind===`confirm`)?e(c,1):(q(n()),K(()=>n().kind===`text`||n().kind===`secret`)?e(l,2):(q(n()),K(()=>n().kind===`plan`)&&e(u,3))))}),Y(e,t)}),E(c),R(()=>X(u,`SESSION ${r()??``}`)),Y(e,c),k()}var Ca=J(` `),wa=J(`
                                                                                    • `),Ta=J(`

                                                                                        Conditional grants depend on the choices and GitHub facts. A final audit runs before setup changes.
                                                                                        `),Ea=J(`

                                                                                        Permissions follow your choices

                                                                                        We’ll show the exact grants before you create each PAT. Nothing is created just by opening this page.

                                                                                        `),Da=J(``);function Oa(e,t){O(t,!1);let n=$(t,`view`,8);Si();var r=Da(),i=F(r),a=I(L(F(i),3),!0);E(i);var o=L(i,2),s=e=>{var t=Ta(),r=L(F(t)),i=I(r),a=L(r),o=I(a,!0),s=L(a,2);Gr(s,5,()=>(q(n()),K(()=>n().permissions.report?.checks??n().permissions.requirements??[])),Vr,(e,t)=>{var n=wa(),r=F(n),i=F(r),a=L(i),o=I(a),s=I(L(a),!0);E(r);var c=L(r),l=F(c,!0),u=L(l),d=e=>{var n=Ca(),r=I(n,!0);R(()=>X(r,(G(t),K(()=>G(t).status)))),Y(e,n)};Z(u,e=>{`status`in G(t)&&e(d)}),E(c),E(n),R(()=>{X(i,`${G(t),K(()=>G(t).permission)??``} `),X(o,`${G(t),K(()=>G(t).scope)??``} · ${G(t),K(()=>G(t).applicability)??``}`),X(s,(G(t),K(()=>G(t).reason))),X(l,(G(t),K(()=>G(t).level)))}),Y(e,n)}),E(s),ke(2),E(t),R(()=>{X(i,`${q(n()),K(()=>n().permissions.role===`setup`?`Setup PAT`:`Bot PAT`)??``} access`),X(o,(q(n()),K(()=>n().permissions.report?`Read-only access check`:`Provisional least-privilege grants`)))}),Y(e,t)},c=e=>{Y(e,Ea())};Z(o,e=>{q(n()),K(()=>n().permissions)?e(s):e(c,-1)}),E(r),R(()=>X(a,(q(n()),K(()=>n().repository)))),Y(e,r),k()}var ka=J(`

                                                                                        `);function Aa(e,t){O(t,!1);let n=M(),r=M(),i=$(t,`outcome`,8),a=$(t,`controller`,8),o=$(t,`onClose`,8);Cn(()=>q(i()),()=>{N(n,i()===`complete`?`Your configuration was applied`:i()===`dry-run`?`No changes were made`:i()===`cancelled`||i()===`blocked`?`No setup changes started`:`Check partial changes before retrying`)}),Cn(()=>q(i()),()=>{N(r,i()===`complete`?`The temporary setup PAT is not revoked automatically. Delete it in GitHub after confirming your setup. Keep the bot PAT until you rotate the installed Secret.`:i()===`partial`?`A bot Secret write or another change may have succeeded. Inspect GitHub and run copilot doctor before replacing or deleting its PAT.`:`Copilot did not begin applying setup changes. PATs you created in GitHub still exist until you delete them there.`)}),wn();var s=ka(),c=F(s),l=I(c,!0),u=L(c),d=I(u,!0),f=L(u),p=I(f,!0),m=L(f,2),h=e=>{Ki(e,{label:`Close local session`,get onClick(){return o()}})};Z(m,e=>{a()&&e(h)}),E(s),R(()=>{X(l,i()===`complete`?`✓`:`!`),X(d,G(n)),X(p,G(r))}),Y(e,s),k()}var ja=J(`

                                                                                        Working on the next step

                                                                                        The local process is checking your answers and preparing the next decision. Keep this page open.

                                                                                        `);function Ma(e){Y(e,ja())}var Na=J(`
                                                                                        PRIVATE LOCAL SESSION

                                                                                        Pair this browser

                                                                                        Find the 16-character pairing code in the terminal that started copilot setup --web. Enter it here to view or control this setup. The code is never placed in the browser URL or stored after this page closes.

                                                                                        Keep the code private. After refreshing this page, enter it again to reconnect.

                                                                                        `);function Pa(e,t){O(t,!1);let n=$(t,`busy`,8),r=$(t,`onPair`,8),i=M(``);function a(){let e=G(i);N(i,``),r()(e)}Si();var o=Na(),s=L(F(o),6),c=L(F(s),2);mi(c);var l=L(c,4);{let e=mt(()=>(q(n()),G(i),K(()=>n()||G(i).trim().length!==16)));Ki(l,{label:`Connect to local setup`,arrow:!0,onClick:a,get disabled(){return G(e)}})}E(s),E(o),R(()=>c.disabled=n()),hr(`submit`,s,e=>{e.preventDefault(),a()}),yi(c,()=>G(i),e=>N(i,e)),Y(e,o),k()}var Fa=J(``),Ia=J(`
                                                                                        `),La=J(``),Ra=J(`
                                                                                        `,1),za=J(`
                                                                                        LOCALHOST ONLY NO CLOUD SETUP ACCOUNT GITHUB OWNS PAT ISSUANCE
                                                                                        `);function Ba(e,t){O(t,!1);let n=()=>Ai(a,`$session`,r),[r,i]=ji(),a=Ni();Lr(()=>{let e=window.setInterval(()=>{n().paired&&!n().view?.outcome&&a.refresh()},900);return()=>window.clearInterval(e)});async function o(){window.confirm(`Cancel this local setup session? PATs already created in GitHub will still exist.`)&&await a.cancel()}async function s(e){let t=n().view?.promptRevision;t!==void 0&&await a.submit(t,e)}Si();var c=za();Zr(`16t12jp`,e=>{Y(e,Fa())});var l=F(c);{let e=mt(()=>n().view?.journey);Ii(l,{get journey(){return G(e)}})}var u=L(l,2),d=F(u);{let e=mt(()=>n().view?.repository);Bi(d,{get repository(){return G(e)}})}var f=L(d,2),p=F(f),m=e=>{Hi(e,{get view(){return n().view}})};Z(p,e=>{n().paired&&e(m)});var h=L(p,2),g=e=>{var t=Ia(),r=L(F(t));E(t),R(()=>X(r,` Reviewing saved choices — pass ${n().view.journey.choiceReviewPass??``}. This is the same setup run, not a restart.`)),Y(e,t)};Z(h,e=>{n().view?.journey?.choiceReviewPass&&n().view.journey.choiceReviewPass>1&&!n().view.outcome&&e(g)});var _=L(h,2),v=e=>{Yi(e,{tone:`warning`,title:`Read-only tab`,message:`Another tab controls this session. You can review progress here or explicitly take over.`,actionLabel:`Take control in this tab`,get onAction(){return a.takeOver}})};Z(_,e=>{!n().controller&&n().view&&e(v)});var y=L(_,2),b=e=>{Yi(e,{tone:`error`,title:`Needs attention`,get message(){return n().error}})};Z(y,e=>{n().error&&e(b)});var x=L(y,2),S=e=>{{let t=mt(()=>n().view.message.tone===`success`?`Checked`:n().view.message.tone===`warning`?`Please note`:n().view.message.tone===`error`?`Needs attention`:`Progress update`);Yi(e,{get tone(){return n().view.message.tone},get title(){return G(t)},get message(){return n().view.message.text},get link(){return n().view.message.link}})}};Z(x,e=>{n().view?.message&&e(S)});var ee=L(x,2),te=e=>{Pa(e,{get busy(){return n().busy},get onPair(){return a.pair}})},ne=e=>{Aa(e,{get outcome(){return n().view.outcome},get controller(){return n().controller},get onClose(){return a.close}})},re=e=>{var t=Ra(),r=on(t),i=F(r);Sa(i,{get prompt(){return n().view.prompt},get revision(){return n().view.revision},get promptRevision(){return n().view.promptRevision},get controller(){return n().controller},get busy(){return n().busy},onSubmit:s}),Oa(L(i,2),{get view(){return n().view}}),E(r);var a=L(r,2),c=e=>{var t=La();R(()=>t.disabled=n().busy),gr(`click`,t,o),Y(e,t)};Z(a,e=>{n().controller&&n().view.journey?.current!==`Apply`&&e(c)}),Y(e,t)},ie=e=>{Ma(e,{})};Z(ee,e=>{n().paired?n().view?.outcome?e(ne,1):n().view?.prompt?e(re,2):e(ie,-1):e(te)}),ke(2),E(f),E(u),E(c),Y(e,c),k(),i()}_r([`click`]),Mr(Ba,{target:document.getElementById(`app`)}); \ No newline at end of file diff --git a/build/web/assets/index-CYUoGCTl.js b/build/web/assets/index-CYUoGCTl.js new file mode 100644 index 000000000..ec9b5fbd0 --- /dev/null +++ b/build/web/assets/index-CYUoGCTl.js @@ -0,0 +1,2 @@ +(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})(),typeof window<`u`&&((window.__svelte??={}).v??=new Set).add(`5`);var e=!1;function t(){e=!0}t();var n={},r=Symbol(`uninitialized`),i=`http://www.w3.org/1999/xhtml`,a=Array.isArray,o=Array.prototype.indexOf,s=Array.prototype.includes,c=Array.from,l=Object.defineProperty,u=Object.getOwnPropertyDescriptor,d=Object.getOwnPropertyDescriptors,f=Object.prototype,p=Array.prototype,m=Object.getPrototypeOf,h=Object.isExtensible,g=()=>{};function _(e){return e()}function v(e){for(var t=0;t{e=n,t=r}),resolve:e,reject:t}}var b=1024,x=2048,S=4096,ee=8192,te=16384,ne=32768,re=1<<25,ie=65536,ae=1<<18,oe=1<<19,se=1<<20,ce=1<<25,le=1<<21,ue=1<<22,de=1<<23,fe=Symbol(`$state`),pe=Symbol(`component`),me=Symbol(`legacy props`),he=Symbol(``),ge=Symbol(`attributes`),_e=Symbol(`class`),ve=Symbol(`style`),ye=Symbol(`text`),be=Symbol(`form reset`),xe=new class extends Error{name=`StaleReactionError`;message="The reaction that called `getAbortSignal()` was re-run or destroyed"},Se=!!globalThis.document?.contentType&&globalThis.document.contentType.includes(`xml`);function Ce(){console.warn(`https://svelte.dev/e/derived_inert`)}function we(e){console.warn(`https://svelte.dev/e/hydration_mismatch`)}function Te(){console.warn(`https://svelte.dev/e/select_multiple_invalid_value`)}function Ee(){console.warn(`https://svelte.dev/e/svelte_boundary_reset_noop`)}var C=!1;function De(e){C=e}var w;function T(e){if(e===null)throw we(),n;return w=e}function Oe(){return T(an(w))}function E(e){if(C){if(an(w)!==null)throw we(),n;w=e}}function ke(e=1){if(C){for(var t=e,n=w;t--;)n=an(n);w=n}}function Ae(e=!0){for(var t=0,n=w;;){if(n.nodeType===8){var r=n.data;if(r===`]`){if(t===0)return n;--t}else(r===`[`||r===`[!`||r[0]===`[`&&!isNaN(Number(r.slice(1))))&&(t+=1)}var i=an(n);e&&n.remove(),n=i}}function je(e){if(!e||e.nodeType!==8)throw we(),n;return e.data}function Me(e){return e===this.v}function Ne(e,t){return e==e?e!==t||typeof e==`object`&&!!e||typeof e==`function`:t==t}function Pe(e){return!Ne(e,this.v)}function Fe(e){throw Error(`https://svelte.dev/e/lifecycle_outside_component`)}function Ie(){throw Error(`https://svelte.dev/e/async_derived_orphan`)}function Le(e,t,n){throw Error(`https://svelte.dev/e/each_key_duplicate`)}function Re(e){throw Error(`https://svelte.dev/e/effect_in_teardown`)}function ze(){throw Error(`https://svelte.dev/e/effect_in_unowned_derived`)}function Be(e){throw Error(`https://svelte.dev/e/effect_orphan`)}function Ve(){throw Error(`https://svelte.dev/e/effect_update_depth_exceeded`)}function He(e){throw Error(`https://svelte.dev/e/props_invalid_value`)}function Ue(){throw Error(`https://svelte.dev/e/state_descriptors_fixed`)}function We(){throw Error(`https://svelte.dev/e/state_prototype_fixed`)}function Ge(){throw Error(`https://svelte.dev/e/state_unsafe_mutation`)}function Ke(){throw Error(`https://svelte.dev/e/svelte_boundary_reset_onerror`)}var D=null;function qe(e){D=e}function O(t,n=!1,r){D={p:D,i:!1,c:null,e:null,s:t,x:null,r:H,l:e&&!n?{s:null,u:null,$:[]}:null}}function k(e){var t=D,n=t.e;if(n!==null){t.e=null;for(var r of n)yn(r)}return e!==void 0&&(t.x=e),t.i=!0,D=t.p,Je(e)}function Je(e={}){return l(e,pe,{value:!0}),e}function Ye(){return!e||D!==null&&D.l===null}var Xe=[];function Ze(){var e=Xe;Xe=[],v(e)}function Qe(e){if(Xe.length===0&&!wt){var t=Xe;queueMicrotask(()=>{t===Xe&&Ze()})}Xe.push(e)}function $e(){for(;Xe.length>0;)Ze()}var et=~(x|S|b);function A(e,t){e.f=e.f&et|t}function tt(e){e.f&512||e.deps===null?A(e,b):A(e,S)}function nt(e,t,n){e.f&2048?t.add(e):e.f&4096&&n.add(e),A(e,b)}var rt=!1;function it(){rt||(rt=!0,document.addEventListener(`reset`,e=>{Promise.resolve().then(()=>{if(!e.defaultPrevented)for(let t of e.target.elements)t[be]?.()})},{capture:!0}))}function at(e){var t=V,n=H;Un(null),Wn(null);try{return e()}finally{Un(t),Wn(n)}}function ot(e,t,n,r=n){e.addEventListener(t,()=>at(n));let i=e[be];e[be]=i?()=>{i(),r(!0)}:()=>r(!0),it()}function st(e,t,n,r){let i=Ye()?dt:mt;var a=e.filter(e=>!e.settled),o=t.map(i);if(n.length===0&&a.length===0){r(o);return}var s=H,c=ct(),l=a.length===1?a[0].promise:a.length>1?Promise.all(a.map(e=>e.promise)):null;function u(e){if(!(s.f&16384)){c();try{r([...o,...e])}catch(e){fn(e,s)}lt()}}var d=ut();if(n.length===0){l.then(()=>u([])).finally(d);return}function f(){Promise.all(n.map(e=>pt(e))).then(u).catch(e=>fn(e,s)).finally(d)}l?l.then(()=>{c(),f(),lt()}):f()}function ct(){var e=H,t=V,n=D,r=j;return function(i=!0){Wn(e),Un(t),qe(n),i&&!(e.f&16384)&&(r?.activate(),r?.apply())}}function lt(e=!0){Wn(null),Un(null),qe(null),e&&j?.deactivate()}function ut(){var e=H,t=e.b,n=j,r=!!t?.is_rendered();return t?.update_pending_count(1,n),n.increment(r,e),()=>{t?.update_pending_count(-1,n),n.decrement(r,e)}}function dt(e){var t=2|x;return H!==null&&(H.f|=oe),{ctx:D,deps:null,effects:null,equals:Me,f:t,fn:e,reactions:null,rv:0,v:r,wv:0,parent:H,ac:null}}var ft=Symbol(`obsolete`);function pt(e,t,n){let i=H;i===null&&Ie();var a=void 0,o=Vt(r),s=!V,c=new Set;return Tn(()=>{var t=H,n=y();a=n.promise;try{Promise.resolve(e()).then(n.resolve,e=>{e!==xe&&n.reject(e)}).finally(lt)}catch(e){n.reject(e),lt()}var r=j;if(s){if(t.f&32768)var l=ut();if(i.b?.is_rendered())r.async_deriveds.get(t)?.reject(ft);else for(let e of c.values())e.reject(ft);c.add(n),r.async_deriveds.set(t,n)}let u=(e,t=void 0)=>{l?.(),c.delete(n),t!==ft&&(r.activate(),t?(o.f|=de,Gt(o,t)):(o.f&8388608&&(o.f^=de),Gt(o,e)),r.deactivate())};n.promise.then(u,e=>u(null,e||`unknown`))}),_n(()=>{for(let e of c)e.reject(ft)}),new Promise(e=>{function t(n){function r(){n===a?e(o):t(a)}n.then(r,r)}t(a)})}function mt(e){let t=dt(e);return t.equals=Pe,t}function ht(e){var t=e.effects;if(t!==null){e.effects=null;for(var n=0;n{t.ac.abort(xe),t.ac=null}),t.fn!==null&&(t.teardown=g),ar(t,0),kn(t))}function yt(e){if(e.effects!==null)for(let t of e.effects)t.teardown&&t.fn!==null&&or(t)}var bt=null,j=null,xt=null,St=null,Ct=null,wt=!1,Tt=!1,Et=null,Dt=null,Ot=0,kt=1,At=class e{id=kt++;#e=!1;linked=!0;#t=null;#n=null;async_deriveds=new Map;current=new Map;previous=new Map;#r=new Set;#i=new Set;#a=0;#o=new Map;#s=null;#c=[];#l=[];#u=new Set;#d=new Set;#f=new Map;#p=new Set;is_fork=!1;#m=!1;constructor(){bt===null?bt=this:(bt.#n=this,this.#t=bt),bt=this}#h(){if(this.is_fork)return!0;for(let n of this.#o.keys()){for(var e=n,t=!1;e.parent!==null;){if(this.#f.has(e)){t=!0;break}e=e.parent}if(!t)return!0}return!1}skip_effect(e){this.#f.has(e)||this.#f.set(e,{d:[],m:[]}),this.#p.delete(e)}unskip_effect(e,t=e=>this.schedule(e)){var n=this.#f.get(e);if(n){this.#f.delete(e);for(var r of n.d)A(r,x),t(r);for(r of n.m)A(r,S),t(r)}this.#p.add(e)}#g(){var e=[];for(let i of this.#c)if(!(i.f&16384||!(i.f&6144))){for(var t=i,n=!1;t.parent!==null;){t=t.parent;var r=t.f;if(r&96){if(!(r&1024)){n=!0;break}t.f^=b}}n||e.push(t)}return this.#c=[],e}#_(){this.#e=!0;for(let e of this.#u)this.#d.delete(e),A(e,x),this.schedule(e);for(let e of this.#d)A(e,S),this.schedule(e);this.apply();for(var t=Et=[],n=[],r=Dt=[];this.#c.length>0;){Ot++>1e3&&(this.#S(),Mt());for(let e of this.#g())try{this.#v(e,t,n)}catch(t){throw Lt(e),this.#h()||this.discard(),t}}if(j=null,r.length>0){var i=e.ensure();for(let e of r)i.schedule(e)}if(Et=null,Dt=null,this.#h()){this.#x(n),this.#x(t);for(let[e,t]of this.#f)It(e,t);r.length>0&&j.#_();return}let a=this.#y();if(a){this.#x(n),this.#x(t),a.#b(this);return}this.#u.clear(),this.#d.clear();for(let e of this.#r)e(this);this.#r.clear(),xt=this,Pt(n),Pt(t),xt=null,this.#s?.resolve();var o=j;if(this.#a===0&&(this.#c.length===0||o!==null)&&this.#S(),this.#c.length>0){if(o!==null){for(let e of this.#c)o.#c.push(e);this.#c=[]}else o=this}o!==null&&(zt.clear(),o.#_())}#v(e,t,n){e.f^=b;for(var r=e.first;r!==null;){var i=r.f,a=!!(i&96);if(!(a&&i&1024||i&8192||this.#f.has(r))&&r.fn!==null){a?r.f^=b:i&4?t.push(r):er(r)&&(i&16&&this.#d.add(r),or(r));var o=r.first;if(o!==null){r=o;continue}}for(;r!==null;){var s=r.next;if(s!==null){r=s;break}r=r.parent}}}#y(){for(var e=this.#t;e!==null;){if(!e.is_fork){for(let[t,[,n]]of this.current)if(e.current.has(t)&&!n)return e}e=e.#t}return null}#b(e){for(let[t,n]of e.current)!this.previous.has(t)&&e.previous.has(t)&&this.previous.set(t,e.previous.get(t)),this.current.set(t,n);for(let[t,n]of e.async_deriveds){let e=this.async_deriveds.get(t);e&&n.promise.then(e.resolve).catch(e.reject)}e.async_deriveds.clear(),this.transfer_effects(e.#u,e.#d);let t=e=>{var n=e.reactions;if(n!==null&&!(e.f&2&&!(e.f&6144)))for(let e of n){var r=e.f;if(r&2)t(e);else{var i=e;r&4194320&&!this.async_deriveds.has(i)&&(this.#d.delete(i),A(i,x),this.schedule(i))}}};for(let e of this.current.keys())t(e);this.oncommit(()=>e.discard()),e.#S(),j=this,this.#_()}#x(e){for(var t=0;t{this.#m=!1,this.linked&&this.flush()}))}transfer_effects(e,t){for(let t of e)this.#u.add(t);for(let e of t)this.#d.add(e);e.clear(),t.clear()}oncommit(e){this.#r.add(e)}ondiscard(e){this.#i.add(e)}settled(){return(this.#s??=y()).promise}static ensure(){if(j===null){let t=j=new e;!Tt&&!wt&&Qe(()=>{t.#e||t.flush()})}return j}apply(){St=null}schedule(e){if(Ct=e,e.b?.is_pending&&e.f&16777228&&!(e.f&32768)){e.b.defer_effect(e);return}this.#c.push(e)}#S(){if(this.linked){var e=this.#t,t=this.#n;e===null||(e.#n=t),t===null?bt=e:t.#t=e,this.linked=!1}}};function jt(e){var t=wt;wt=!0;try{var n;for(e&&(j!==null&&!j.is_fork&&j.flush(),n=e());;){if($e(),j===null)return n;j.flush()}}finally{wt=t}}function Mt(){try{Ve()}catch(e){fn(e,Ct)}}var Nt=null;function Pt(e){var t=e.length;if(t!==0){for(var n=0;n0)){zt.clear();for(let e of Nt){if(e.f&24576)continue;let t=[e],n=e.parent;for(;n!==null;)Nt.has(n)&&(Nt.delete(n),t.push(n)),n=n.parent;for(let e=t.length-1;e>=0;e--){let n=t[e];n.f&24576||or(n)}}Nt.clear()}}Nt=null}}function Ft(e){j.schedule(e)}function It(e,t){if(!(e.f&32&&e.f&1024)){e.f&2048?t.d.push(e):e.f&4096&&t.m.push(e),A(e,b);for(var n=e.first;n!==null;)It(n,t),n=n.next}}function Lt(e){A(e,b);for(var t=e.first;t!==null;)Lt(t),t=t.next}var Rt=new Set,zt=new Map,Bt=!1;function Vt(e,t){return{f:0,v:e,reactions:null,equals:Me,rv:0,wv:0}}function Ht(e,t){let n=Vt(e,t);return Kn(n),n}function M(t,n=!1,r=!0){let i=Vt(t);return n||(i.equals=Pe),e&&r&&D!==null&&D.l!==null&&(D.l.s??=[]).push(i),i}function N(e,t,n=!1){return V!==null&&(!Hn||V.f&131072)&&Ye()&&V.f&4325394&&(Gn===null||!Gn.has(e))&&Ge(),Gt(e,n?Yt(t):t,Dt)}var Ut=null,Wt=0;function Gt(e,t,n=null){if(!e.equals(t)){Bn?zt.set(e,t):zt.has(e)||zt.set(e,e.v);var r=At.ensure();if(r.capture(e,t),e.f&2){let t=e;e.f&2048&>(t),St===null&&tt(t)}e.wv=$n(),Ut=null,Wt=0,Jt(e,x,n),Ut=null,Ye()&&H!==null&&H.f&1024&&!(H.f&96)&&(qn===null?Jn([e]):qn.push(e)),!r.is_fork&&Rt.size>0&&!Bt&&Kt()}return t}function Kt(){Bt=!1;for(let e of Rt){e.f&1024&&A(e,S);let t;try{t=er(e)}catch{t=!0}t&&or(e)}Rt.clear()}function qt(e){N(e,e.v+1)}function Jt(e,t,n){var r=e.reactions;if(r!==null){var i=Ye(),a=r.length;if(Wt+=a,Wt>1e5&&Ut===null&&(Ut=new Set),Ut!==null){if(Ut.has(e))return;Ut.add(e)}for(var o=0;o{if(Zn===c)return e();var t=V,n=Zn;Un(null),Qn(c);var r=e();return Un(t),Qn(n),r};return i&&n.set(`length`,Ht(e.length,s)),new Proxy(e,{defineProperty(e,t,r){(!(`value`in r)||r.configurable===!1||r.enumerable===!1||r.writable===!1)&&Ue();var i=n.get(t);return i===void 0?l(()=>{var e=Ht(r.value,s);return n.set(t,e),e}):N(i,r.value,!0),!0},deleteProperty(e,t){var i=n.get(t);if(i===void 0){if(t in e){let e=l(()=>Ht(r,s));n.set(t,e),qt(o)}}else N(i,r),qt(o);return!0},get(t,i,a){if(i===fe)return e;var o=n.get(i),c=i in t;if(o===void 0&&(!c||u(t,i)?.writable)&&(o=l(()=>Ht(Yt(c?t[i]:r),s)),n.set(i,o)),o!==void 0){var d=G(o);return d===r?void 0:d}return Reflect.get(t,i,a)},getOwnPropertyDescriptor(e,t){this.has?.(e,t);var i=Reflect.getOwnPropertyDescriptor(e,t),a=n.get(t);if(a!==void 0){var o=G(a);if(o===r)return;if(i&&`value`in i)i.value=o;else return{enumerable:!0,configurable:!0,value:o,writable:!0}}return i},has(e,t){if(t===fe)return!0;var i=n.get(t),a=i!==void 0&&i.v!==r||Reflect.has(e,t);return(i!==void 0||H!==null&&(!a||u(e,t)?.writable))&&(i===void 0&&(i=l(()=>Ht(a?Yt(e[t]):r,s)),n.set(t,i)),G(i)===r)?!1:a},set(e,t,a,c){var d=n.get(t),f=t in e;if(i&&t===`length`)for(var p=a;pHt(r,s)),n.set(p+``,m)):N(m,r)}if(d===void 0)(!f||u(e,t)?.writable)&&(d=l(()=>Ht(void 0,s)),N(d,Yt(a)),n.set(t,d));else{f=d.v!==r;var h=l(()=>Yt(a));N(d,h)}var g=Reflect.getOwnPropertyDescriptor(e,t);if(g?.set&&g.set.call(c,a),!f){if(i&&typeof t==`string`){var _=n.get(`length`),v=Number(t);Number.isInteger(v)&&v>=_.v&&N(_,v+1)}qt(o)}return!0},ownKeys(e){G(o);var t=Reflect.ownKeys(e).filter(e=>{var t=n.get(e);return t===void 0||t.v!==r});for(var[i,a]of n)a.v!==r&&!(i in e)&&t.push(i);return t},setPrototypeOf(){We()}})}function Xt(e){try{if(typeof e==`object`&&e&&fe in e)return e[fe]}catch{}return e}function Zt(e,t){return Object.is(Xt(e),Xt(t))}var Qt,$t,en,tn;function nn(){if(Qt===void 0){Qt=window,$t=/Firefox/.test(navigator.userAgent);var e=Element.prototype,t=Node.prototype,n=Text.prototype;en=u(t,`firstChild`).get,tn=u(t,`nextSibling`).get,h(e)&&(e[_e]=void 0,e[ge]=null,e[ve]=void 0,e.__e=void 0),h(n)&&(n[ye]=void 0)}}function P(e=``){return document.createTextNode(e)}function rn(e){return en.call(e)}function an(e){return tn.call(e)}function F(e,t){if(!C)return rn(e);var n=rn(w);if(n===null)n=w.appendChild(P());else if(t&&n.nodeType!==3){var r=P();return n?.before(r),T(r),r}return t&&un(n),T(n),n}function on(e,t=!1){if(!C){var n=rn(e);return n instanceof Comment&&n.data===``?an(n):n}if(t){if(w?.nodeType!==3){var r=P();return w?.before(r),T(r),r}un(w)}return w}function I(e,t=!1){if(!C)return rn(e);var n=F(e,t);return E(e),n}function L(e,t=1,n=!1){let r=C?w:e;for(var i;t--;)i=r,r=an(r);if(!C)return r;if(n){if(r?.nodeType!==3){var a=P();return r===null?i?.after(a):r.before(a),T(a),a}un(r)}return T(r),r}function sn(e){e.textContent=``}function cn(){return!1}function ln(e,t,n){return t==null||t===`http://www.w3.org/1999/xhtml`?n?document.createElement(e,{is:n}):document.createElement(e):n?document.createElementNS(t,e,{is:n}):document.createElementNS(t,e)}function un(e){if(e.nodeValue.length<65536)return;let t=e.nextSibling;for(;t!==null&&t.nodeType===3;)t.remove(),e.nodeValue+=t.nodeValue,t=e.nextSibling}function dn(e){var t=H;if(t===null)return V.f|=de,e;if(!(t.f&32768)&&!(t.f&4))throw e;fn(e,t)}function fn(e,t){if(!(t!==null&&t.f&16384)){for(;t!==null;){if(t.f&128&&!(t.f&33570816)){if(!(t.f&32768))throw e;try{t.b.error(e);return}catch(t){e=t}}t=t.parent}throw e}}function pn(e){H===null&&(V===null&&Be(e),ze()),Bn&&Re(e)}function mn(e,t){var n=t.last;n===null?t.last=t.first=e:(n.next=e,e.prev=n,t.last=e)}function hn(e,t){var n=H;n!==null&&n.f&8192&&(e|=ee);var r={ctx:D,deps:null,nodes:null,f:e|x|512,first:null,fn:t,last:null,next:null,parent:n,b:n&&n.b,prev:null,teardown:null,wv:0,ac:null};j?.register_created_effect(r);var i=r;if(e&4)Et===null?At.ensure().schedule(r):Et.push(r);else if(t!==null){try{or(r)}catch(e){throw B(r),e}i.deps===null&&i.teardown===null&&i.nodes===null&&i.first===i.last&&!(i.f&524288)&&(i=i.first,e&16&&e&65536&&i!==null&&(i.f|=ie))}if(i!==null&&(i.parent=n,n!==null&&mn(i,n),V!==null&&V.f&2&&!(e&64))){var a=V;(a.effects??=[]).push(i)}return r}function gn(){return V!==null&&!Hn}function _n(e){let t=hn(8,null);return A(t,b),t.teardown=e,t}function vn(e){pn(`$effect`);var t=H.f;if(!V&&t&32&&D!==null&&!D.i){var n=D;(n.e??=[]).push(e)}else return yn(e)}function yn(e){return hn(4|se,e)}function bn(e){return pn(`$effect.pre`),hn(8|se,e)}function xn(e){At.ensure();let t=hn(64|oe,e);return(e={})=>new Promise(n=>{e.outro?Nn(t,()=>{B(t),n(void 0)}):(B(t),n(void 0))})}function Sn(e){return hn(4,e)}function Cn(e,t){var n=D,r={effect:null,ran:!1,deps:e};n.l.$.push(r),r.effect=En(()=>{if(e(),!r.ran){r.ran=!0;var n=H;try{Wn(n.parent),K(t)}finally{Wn(n)}}})}function wn(){var e=D;En(()=>{for(var t of e.l.$){t.deps();var n=t.effect;n.f&1024&&n.deps!==null&&A(n,S),er(n)&&or(n),t.ran=!1}})}function Tn(e){return hn(ue|oe,e)}function En(e,t=0){return hn(8|t,e)}function R(e,t=[],n=[],r=[]){st(r,t,n,t=>{hn(8,()=>{e(...t.map(G))})})}function Dn(e,t=0){return hn(16|t,e)}function z(e){return hn(32|oe,e)}function On(e){var t=e.teardown;if(t!==null){let n=Bn,r=V;Vn(!0),Un(null);try{t.call(null)}catch(t){fn(t,e.parent)}finally{Vn(n),Un(r)}}}function kn(e,t=!1){var n=e.first;for(e.first=e.last=null;n!==null;){let e=n.ac;e!==null&&at(()=>{e.abort(xe)});var r=n.next;n.f&64?n.parent=null:B(n,t),n=r}}function An(e){for(var t=e.first;t!==null;){var n=t.next;t.f&32||B(t),t=n}}function B(e,t=!0){var n=!1;(t||e.f&262144)&&e.nodes!==null&&e.nodes.end!==null&&(jn(e.nodes.start,e.nodes.end),n=!0),e.f|=re,kn(e,t&&!n),ar(e,0);var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)e.stop();On(e),e.f^=re,e.f|=te;var i=e.parent;i!==null&&i.first!==null&&Mn(e),e.next=e.prev=e.teardown=e.ctx=e.deps=e.fn=e.nodes=e.ac=e.b=null}function jn(e,t){for(;e!==null;){var n=e===t?null:an(e);e.remove(),e=n}}function Mn(e){var t=e.parent,n=e.prev,r=e.next;n!==null&&(n.next=r),r!==null&&(r.prev=n),t!==null&&(t.first===e&&(t.first=r),t.last===e&&(t.last=n))}function Nn(e,t,n=!0){var r=[];e.f|=256,Pn(e,r,!0);var i=()=>{n&&B(e),t&&t()},a=r.length;if(a>0){var o=()=>--a||i();for(var s of r)s.out(o)}else i()}function Pn(e,t,n){if(!(e.f&8192)){e.f^=ee;var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)(e.is_global||n)&&t.push(e);for(var i=e.first;i!==null;){var a=i.next;if(!(i.f&64)){var o=!!(i.f&65536)||!!(i.f&32)&&!!(e.f&16);Pn(i,t,o?n:!1)}i=a}}}function Fn(e){e.f&=-257,In(e,!0)}function In(e,t){if(!(e.f&256)&&e.f&8192){e.f^=ee,e.f&1024||(A(e,x),At.ensure().schedule(e));for(var n=e.first;n!==null;){var r=n.next,i=!!(n.f&65536)||!!(n.f&32);In(n,i?t:!1),n=r}var a=e.nodes&&e.nodes.t;if(a!==null)for(let e of a)(e.is_global||t)&&e.in()}}function Ln(e,t){if(e.nodes)for(var n=e.nodes.start,r=e.nodes.end;n!==null;){var i=n===r?null:an(n);t.append(n),n=i}}var Rn=null,zn=!1,Bn=!1;function Vn(e){Bn=e}var V=null,Hn=!1;function Un(e){V=e}var H=null;function Wn(e){H=e}var Gn=null;function Kn(e){V!==null&&(V.f&2097152||V.f&2)&&(Gn??=new Set).add(e)}var U=null,W=0,qn=null;function Jn(e){qn=e}var Yn=1,Xn=0,Zn=Xn;function Qn(e){Zn=e}function $n(){return++Yn}function er(e){var t=e.f;if(t&2048)return!0;if(t&4096){for(var n=e.deps,r=n.length,i=0;ie.wv)return!0}t&512&&St===null&&A(e,b)}return!1}function tr(e,t,n=!0){var r=e.reactions;if(r!==null&&!(Gn!==null&&Gn.has(e)))for(var i=0;i{e.ac.abort(xe)}),e.ac=null);try{e.f|=le;var u=e.fn,d=u();e.f|=ne;var f=rr(e);if(Ye()&&qn!==null&&!Hn&&f!==null&&!(e.f&6146))for(var p=0;p0)for(t.length=W+U.length,r=0;r{c.ac.abort(xe),c.ac=null,A(c,x)}),vt(c),ar(c,0)}}function ar(e,t){var n=e.deps;if(n!==null)for(var r=t;rn?.call(this,e))}return e.startsWith(`pointer`)||e.startsWith(`touch`)||e===`wheel`?(i.__removed=!1,Qe(()=>{i.__removed||t.addEventListener(e,i,r)})):t.addEventListener(e,i,r),i}function hr(e,t,n,r,i){var a={capture:r,passive:i},o=mr(e,t,n,a);(t===document.body||t===window||t===document||t instanceof HTMLMediaElement)&&_n(()=>{o.__removed=!0,t.removeEventListener(e,o,a)})}function gr(e,t,n){(t[dr]??={})[e]=n}function _r(e){for(var t=0;t{yr=!1,vr=null}));var o=0,s=vr===e&&e[dr];if(s){var c=i.indexOf(s);if(c!==-1&&(t===document||t===window)){e[dr]=t;return}var u=i.indexOf(t);if(u===-1)return;c<=u&&(o=c)}if(a=i[o]||e.target,a!==t){l(e,`currentTarget`,{configurable:!0,get(){return a||n}});var d=V,f=H;Un(null),Wn(null);try{for(var p,m=[];a!==null&&a!==t;){try{var h=a[dr]?.[r];h!=null&&(!a.disabled||e.target===a)&&h.call(a,e)}catch(e){p?m.push(e):p=e}if(e.cancelBubble)break;o++,a=o{throw e});throw p}}finally{e[dr]=t,delete e.currentTarget,Un(d),Wn(f)}}}var xr=globalThis?.window?.trustedTypes&&globalThis.window.trustedTypes.createPolicy(`svelte-trusted-html`,{createHTML:e=>e});function Sr(e){return xr?.createHTML(e)??e}function Cr(e){var t=ln(`template`);return t.innerHTML=Sr(e.replaceAll(``,``)),t.content}function wr(e,t){var n=H;n.nodes===null&&(n.nodes={start:e,end:t,a:null,t:null})}function J(e,t){var n=!!(t&1),r=!!(t&2),i,a=!e.startsWith(``);return()=>{if(C)return wr(w,null),w;i===void 0&&(i=Cr(a?e:``+e),n||(i=rn(i)));var t=r||$t?document.importNode(i,!0):i.cloneNode(!0);if(n){var o=rn(t),s=t.lastChild;wr(o,s)}else wr(t,t);return t}}function Tr(e=``){if(!C){var t=P(e+``);return wr(t,t),t}var n=w;return n.nodeType===3?un(n):(n.before(n=P()),T(n)),wr(n,n),n}function Er(){if(C)return wr(w,null),w;var e=document.createDocumentFragment(),t=document.createComment(``),n=P();return e.append(t,n),wr(t,n),e}function Y(e,t){if(C){var n=H;(!(n.f&32768)||n.nodes.end===null)&&(n.nodes.end=w),Oe();return}e!==null&&e.before(t)}[...`allowfullscreen.async.autofocus.autoplay.checked.controls.default.disabled.formnovalidate.indeterminate.inert.ismap.loop.multiple.muted.nomodule.novalidate.open.playsinline.readonly.required.reversed.seamless.selected.webkitdirectory.defer.disablepictureinpicture.disableremoteplayback`.split(`.`)];var Dr=[`touchstart`,`touchmove`];function Or(e){return Dr.includes(e)}function kr(e){let t=0,n=Vt(0),r;return()=>{gn()&&(G(n),En(()=>(t===0&&(r=K(()=>e(()=>qt(n)))),t+=1,()=>{Qe(()=>{--t,t===0&&(r?.(),r=void 0,qt(n))})})))}}var Ar=ie|oe;function jr(e,t,n,r){new Mr(e,t,n,r)}var Mr=class{parent;is_pending=!1;transform_error;#e;#t=C?w:null;#n;#r;#i;#a=null;#o=null;#s=null;#c=null;#l=0;#u=0;#d=!1;#f=new Set;#p=new Set;#m=null;#h=kr(()=>(this.#m=Vt(this.#l),()=>{this.#m=null}));constructor(e,t,n,r){this.#e=e,this.#n=t,this.#r=e=>{var t=H;t.b=this,t.f|=128,n(e)},this.parent=H.b,this.transform_error=r??this.parent?.transform_error??(e=>e),this.#i=Dn(()=>{if(C){let e=this.#t;Oe();let t=e.data===`[!`;if(e.data.startsWith(`[?`)){let t=JSON.parse(e.data.slice(2));this.#_(t)}else t?this.#y():this.#g()}else this.#b()},Ar),C&&(this.#e=w)}#g(){try{this.#a=z(()=>this.#r(this.#e))}catch(e){this.error(e)}}#_(e){let t=this.#n.failed,{reset:n,invoke_onerror:r}=this.#v(e);Qe(r),t&&(this.#s=z(()=>{t(this.#e,()=>e,()=>n)}))}#v(e){var t=!1,n=!1;let r=()=>{if(t){Ee();return}t=!0,n&&Ke(),this.#s!==null&&Nn(this.#s,()=>{this.#s=null}),this.#S(()=>{this.#b()})};return{reset:r,invoke_onerror:()=>{try{n=!0,this.#n.onerror?.(e,r),n=!1}catch(e){fn(e,this.#i&&this.#i.parent)}}}}#y(){let e=this.#n.pending;e&&(this.is_pending=!0,this.#o=z(()=>e(this.#e)),Qe(()=>{var e=this.#c=document.createDocumentFragment(),t=P(),n=!1;if(e.append(t),this.#a=this.#S(()=>{try{return z(()=>this.#r(t))}catch(e){try{this.error(e),n=!0}catch(e){fn(e,this.#i.parent)}return null}}),this.#a===null){this.#c=null,n&&this.#x(j);return}this.#u===0&&(this.#e.before(e),this.#c=null,Nn(this.#o,()=>{this.#o=null}),this.#x(j))}))}#b(){try{if(this.is_pending=this.has_pending_snippet(),this.#u=0,this.#l=0,this.#a=z(()=>{this.#r(this.#e)}),this.#u>0){var e=this.#c=document.createDocumentFragment();Ln(this.#a,e);let t=this.#n.pending;this.#o=z(()=>t(this.#e))}else this.#x(j)}catch(e){this.error(e)}}#x(e){this.is_pending=!1,e.transfer_effects(this.#f,this.#p)}defer_effect(e){nt(e,this.#f,this.#p)}is_rendered(){return!this.is_pending&&(!this.parent||this.parent.is_rendered())}has_pending_snippet(){return!!this.#n.pending}#S(e){var t=H,n=V,r=D;Wn(this.#i),Un(this.#i),qe(this.#i.ctx);try{return At.ensure(),e()}finally{Wn(t),Un(n),qe(r)}}#C(e,t){if(!this.has_pending_snippet()){this.parent&&this.parent.#C(e,t);return}this.#u+=e,this.#u===0&&(this.#x(t),this.#o&&Nn(this.#o,()=>{this.#o=null}),this.#c&&=(this.#e.before(this.#c),null))}update_pending_count(e,t){this.#C(e,t),this.#l+=e,!(!this.#m||this.#d)&&(this.#d=!0,Qe(()=>{this.#d=!1,this.#m&&Gt(this.#m,this.#l)}))}get_effect_pending(){return this.#h(),G(this.#m)}error(e){if(!this.#n.onerror&&!this.#n.failed)throw e;j?.is_fork?(this.#a&&j.skip_effect(this.#a),this.#o&&j.skip_effect(this.#o),this.#s&&j.skip_effect(this.#s),j.oncommit(()=>{this.#w(e)})):this.#w(e)}#w(e){this.#a&&=(B(this.#a),null),this.#o&&=(B(this.#o),null),this.#s&&=(B(this.#s),null),C&&(T(this.#t),ke(),T(Ae()));let t=this.#n.failed,n=e=>{let{reset:n,invoke_onerror:r}=this.#v(e);r(),t&&(this.#s=this.#S(()=>{try{return z(()=>{var r=H;r.b=this,r.f|=128,t(this.#e,()=>e,()=>n)})}catch(e){return fn(e,this.#i.parent),null}}))};Qe(()=>{var t;try{t=this.transform_error(e)}catch(e){fn(e,this.#i&&this.#i.parent);return}typeof t==`object`&&t&&typeof t.then==`function`?t.then(n,e=>fn(e,this.#i&&this.#i.parent)):n(t)})}};function X(e,t){var n=t==null?``:typeof t==`object`?`${t}`:t;n!==(e[ye]??=e.nodeValue)&&(e[ye]=n,e.nodeValue=`${n}`)}function Nr(e,t){return Fr(e,t)}var Pr=new Map;function Fr(e,{target:t,anchor:r,props:i={},events:a,context:o,intro:s=!0,transformError:l}){nn();var u=void 0,d=xn(()=>{var s=r??t.appendChild(P());jr(s,{pending:()=>{}},t=>{O({});var r=D;if(o&&(r.c=o),a&&(i.$$events=a),C&&wr(t,null),u=e(t,i)||Je(),C&&(H.nodes.end=w,w===null||w.nodeType!==8||w.data!==`]`))throw we(),n;k()},l);var d=new Set,f=e=>{for(var n=0;n{for(var e of d)for(let r of[t,document]){var n=Pr.get(r),i=n.get(e);--i==0?(r.removeEventListener(e,br),n.delete(e),n.size===0&&Pr.delete(r)):n.set(e,i)}pr.delete(f),s!==r&&s.parentNode?.removeChild(s)}});return Ir.set(u,d),u}var Ir=new WeakMap,Lr=class{anchor;#e=new Map;#t=new Map;#n=new Map;#r=new Set;#i=!0;constructor(e,t=!0){this.anchor=e,this.#i=t}#a=e=>{if(this.#e.has(e)){var t=this.#e.get(e),n=this.#t.get(t);if(n)Fn(n),this.#r.delete(t);else{var r=this.#n.get(t);r&&(Fn(r.effect),this.#t.set(t,r.effect),this.#n.delete(t),r.fragment.lastChild.remove(),this.anchor.before(r.fragment),n=r.effect)}for(let[t,n]of this.#e){if(this.#e.delete(t),t===e)break;let r=this.#n.get(n);r&&(B(r.effect),this.#n.delete(n))}for(let[e,r]of this.#t){if(e===t||this.#r.has(e))continue;let i=()=>{if(Array.from(this.#e.values()).includes(e)){var t=document.createDocumentFragment();Ln(r,t),t.append(P()),this.#n.set(e,{effect:r,fragment:t})}else B(r);this.#r.delete(e),this.#t.delete(e)};this.#i||!n?(this.#r.add(e),Nn(r,i,!1)):i()}}};#o=e=>{this.#e.delete(e);let t=Array.from(this.#e.values());for(let[e,n]of this.#n)t.includes(e)||(B(n.effect),this.#n.delete(e))};ensure(e,t){var n=j,r=cn();if(t&&!this.#t.has(e)&&!this.#n.has(e)){if(r){var i=document.createDocumentFragment(),a=P();i.append(a),this.#n.set(e,{effect:z(()=>t(a)),fragment:i})}else this.#t.set(e,z(()=>t(this.anchor)))}if(this.#e.set(n,e),r){for(let[t,r]of this.#t)t===e?n.unskip_effect(r):n.skip_effect(r);for(let[t,r]of this.#n)t===e?n.unskip_effect(r.effect):n.skip_effect(r.effect);n.oncommit(this.#a),n.ondiscard(this.#o)}else C&&(this.anchor=w),this.#a(n)}};function Rr(t){D===null&&Fe(`onMount`),e&&D.l!==null?zr(D).m.push(t):vn(()=>{let e=K(t);if(typeof e==`function`)return e})}function zr(e){var t=e.l;return t.u??={a:[],b:[],m:[]}}function Z(e,t,n=!1){var r;C&&(r=w,Oe());var i=new Lr(e),a=n?ie:0;function o(e,t){if(C){var n=je(r);if(e!==parseInt(n.substring(1))){var a=Ae();T(a),i.anchor=a,De(!1),i.ensure(e,t),De(!0);return}}i.ensure(e,t)}Dn(()=>{var e=!1;t((t,n=0)=>{e=!0,o(n,t)}),e||o(-1,null)},a)}var Br=Symbol(`NaN`);function Vr(e,t,n){C&&Oe();var r=new Lr(e),i=!Ye();Dn(()=>{var e=t();e!==e&&(e=Br),i&&typeof e==`object`&&e&&(e={}),r.ensure(e,n)})}function Hr(e,t){return t}function Ur(e,t,n){for(var r=[],i=t.length,a,o=t.length,s=0;s{if(a){if(a.pending.delete(n),a.done.add(n),a.pending.size===0){var t=e.outrogroups;Wr(e,c(a.done)),t.delete(a),t.size===0&&(e.outrogroups=null)}}else--o},!1)}if(o===0){var l=r.length===0&&n!==null&&e.pending.size===0;if(l){var u=n,d=u.parentNode;sn(d),d.append(u),e.items.clear()}Wr(e,t,!l)}else a={pending:new Set(t),done:new Set},(e.outrogroups??=new Set).add(a)}function Wr(e,t,n=!0){var r;if(e.pending.size>0){r=new Set;for(let t of e.pending.values())for(let n of t)r.add(e.items.get(n).e)}for(var i=0;i{var e=n();return a(e)?e:e==null?[]:c(e)}),p,m=new Map,h=!0;function g(e){v.effect.f&16384||(v.pending.delete(e),v.fallback=d,Jr(v,p,s,t,r),d!==null&&(p.length===0?d.f&33554432?(d.f^=ce,Xr(d,null,s)):Fn(d):Nn(d,()=>{d=null})))}function _(e){v.pending.delete(e)}var v={effect:Dn(()=>{p=G(f);var e=p.length;let a=!1;C&&je(s)===`[!`!=(e===0)&&(s=Ae(),T(s),De(!1),a=!0);for(var c=new Set,u=j,v=cn(),y=0;yo(s)):(d=z(()=>o(Gr??=P())),d.f|=ce)),e>c.size&&Le(``,``,``),C&&e>0&&T(Ae()),!h){if(m.set(u,c),v){for(let[e,t]of l)c.has(e)||u.skip_effect(t.e);u.oncommit(g),u.ondiscard(_)}else g(u)}a&&De(!0),G(f)}),flags:t,items:l,pending:m,outrogroups:null,fallback:d};h=!1,C&&(s=w)}function qr(e){for(;e!==null&&!(e.f&32);)e=e.next;return e}function Jr(e,t,n,r,i){var a=!!(r&8),o=t.length,s=e.items,l=qr(e.effect.first),u,d=null,f,p=[],m=[],h,g,_,v;if(a)for(v=0;v0){var re=r&4&&o===0?n:null;if(a){for(v=0;v{if(f!==void 0)for(_ of f)_.nodes?.a?.apply()})}function Yr(e,t,n,r,i,a,o,s){var c=o&1?o&16?Vt(n):M(n,!1,!1):null,l=o&2?Vt(i):null;return{v:c,i:l,e:z(()=>(a(t,c??n,l??i,s),()=>{e.delete(r)}))}}function Xr(e,t,n){if(e.nodes)for(var r=e.nodes.start,i=e.nodes.end,a=t&&!(t.f&33554432)?t.nodes.start:n;r!==null;){var o=an(r);if(a.before(r),r===i)return;r=o}}function Zr(e,t,n){t===null?e.effect.first=n:t.next=n,n===null?e.effect.last=t:n.prev=t}function Qr(e,t){let n=null,r=C;var i;if(C){n=w;for(var a=rn(document.head);a!==null&&(a.nodeType!==8||a.data!==e);)a=an(a);if(a===null)De(!1);else{var o=an(a);a.remove(),T(o)}}C||(i=document.head.appendChild(P()));try{Dn(()=>{var e=z(()=>t(i));e.f|=ae,C||(e.nodes===null?e.nodes={start:i,end:i,a:null,t:null}:e.nodes.end=i)})}finally{r&&(De(!0),T(n))}}function $r(e){var t,n,r=``;if(typeof e==`string`||typeof e==`number`)r+=e;else if(typeof e==`object`){if(Array.isArray(e)){var i=e.length;for(t=0;t=0;){var s=o+a;(o===0||ni.includes(r[o-1]))&&(s===r.length||ni.includes(r[s]))?r=(o===0?``:r.substring(0,o))+r.substring(s+1):o=s}}return r===``?null:r}function ii(e,t,n,r,i,a){var o=e[_e];if(C||o!==n||o===void 0){var s=ri(n,r,a);(!C||s!==e.getAttribute(`class`))&&(s==null?e.removeAttribute(`class`):t?e.className=s:e.setAttribute(`class`,s)),e[_e]=n}else if(a&&i!==a)for(var c in a){var l=!!a[c];(i==null||l!==!!i[c])&&e.classList.toggle(c,l)}return a}function ai(e,t){t?e.hasAttribute(`selected`)||e.setAttribute(`selected`,``):e.removeAttribute(`selected`)}function oi(e,t){var n=e.__defaultValue,r=e.multiple,i=r?n??[]:null;if(!r||a(i)){var o=e.selectedIndex,s=t&&r?new Set(e.selectedOptions):null;for(var c of e.options){var l=ui(c);ai(c,r?i.includes(l):Zt(l,n))}if(t){if(s!==null)for(c of e.options){var u=s.has(c);c.selected!==u&&(c.selected=u)}else e.selectedIndex!==o&&(e.selectedIndex=o)}}}function si(e,t,n=!1){if(e.multiple){if(t==null)return;if(!a(t))return Te();for(var r of e.options)r.selected=t.includes(ui(r));return}for(r of e.options)if(Zt(ui(r),t)){r.selected=!0;return}(!n||t!==void 0)&&(e.selectedIndex=-1)}function ci(e){var t=new MutationObserver(t=>{t.every(di)||(`__defaultValue`in e&&oi(e,!1),`__value`in e&&si(e,e.__value))});t.observe(e,{childList:!0,subtree:!0,attributes:!0,attributeFilter:[`value`]}),_n(()=>{t.disconnect()})}function li(e,t,n=t){var r=new WeakSet,i=!0;ot(e,`change`,t=>{var i=t?`[selected]`:`:checked`,a;if(e.multiple)a=[].map.call(e.querySelectorAll(i),ui);else{var o=e.querySelector(i)??e.querySelector(`option:not([disabled])`);a=o&&ui(o)}n(a),e.__value=a,j!==null&&r.add(j)}),Sn(()=>{var a=t();if(e===document.activeElement){var o=j;if(r.has(o))return}if(si(e,a,i),i&&a===void 0){var s=e.querySelector(`:checked`);s!==null&&(a=ui(s),n(a))}e.__value=a,i=!1})}function ui(e){return`__value`in e?e.__value:e.value}function di(e){if(e.target.closest(`selectedcontent`)!==null)return!0;if(e.type===`childList`){var t=[...e.addedNodes,...e.removedNodes];return t.length>0&&t.every(e=>e.nodeName===`SELECTEDCONTENT`)}return!1}var fi=Symbol(`is custom element`),pi=Symbol(`is html`),mi=Se?`link`:`LINK`;function hi(e){if(C){var t=!1,n=()=>{if(!t){if(t=!0,e.hasAttribute(`value`)){var n=e.value;Q(e,`value`,null),e.value=n}if(e.hasAttribute(`checked`)){var r=e.checked;Q(e,`checked`,null),e.checked=r}}};e[be]=n,Qe(n),it()}}function gi(e,t){var n=_i(e);n.checked!==(n.checked=t??void 0)&&(e.checked=t)}function Q(e,t,n,r){var i=_i(e);C&&(i[t]=e.getAttribute(t),t===`src`||t===`srcset`||t===`href`&&e.nodeName===mi)||i[t]!==(i[t]=n)&&(t===`loading`&&(e[he]=n),n==null?e.removeAttribute(t):typeof n!=`string`&&yi(e).has(t)?e[t]=n:e.setAttribute(t,n))}function _i(e){return e[ge]??={[fi]:e.nodeName.includes(`-`),[pi]:e.namespaceURI===i}}var vi=new Map;function yi(e){var t=e.getAttribute(`is`)||e.nodeName,n=vi.get(t);if(n)return n;vi.set(t,n=new Set);for(var r,i=e,a=Element.prototype;a!==i;){for(var o in r=d(i),r)r[o].set&&o!==`innerHTML`&&o!==`textContent`&&o!==`innerText`&&n.add(o);i=m(i)}return n}function bi(e,t,n=t){var r=new WeakSet;ot(e,`input`,async i=>{var a=i?e.defaultValue:e.value;if(a=xi(e)?Si(a):a,n(a),j!==null&&r.add(j),await sr(),a!==(a=t())){var o=e.selectionStart,s=e.selectionEnd,c=e.value.length;if(e.value=a??``,s!==null){var l=e.value.length;o===s&&s===c&&l>c?(e.selectionStart=l,e.selectionEnd=l):(e.selectionStart=o,e.selectionEnd=Math.min(s,l))}}}),(C&&e.defaultValue!==e.value||K(t)==null&&e.value)&&(n(xi(e)?Si(e.value):e.value),j!==null&&r.add(j)),En(()=>{var n=t();if(e===document.activeElement){var i=j;if(r.has(i))return}xi(e)&&n===Si(e.value)||(e.type!==`date`||n||e.value)&&n!==e.value&&(e.value=n??``)})}function xi(e){var t=e.type;return t===`number`||t===`range`}function Si(e){return e===``?null:+e}function Ci(e=!1){let t=D,n=t.l.u;if(!n)return;let r=()=>q(t.s);if(e){let e=0,n={},i=dt(()=>{let r=!1,i=t.s;for(let e in i)i[e]!==n[e]&&(n[e]=i[e],r=!0);return r&&e++,e});r=()=>G(i)}n.b.length&&bn(()=>{wi(t,r),v(n.b)}),vn(()=>{let e=K(()=>n.m.map(_));return()=>{for(let t of e)typeof t==`function`&&t()}}),n.a.length&&vn(()=>{wi(t,r),v(n.a)})}function wi(e,t){if(e.l.s)for(let t of e.l.s)G(t);t()}function Ti(e,t,n){if(e==null)return t(void 0),n&&n(void 0),g;let r=K(()=>e.subscribe(t,n));return r.unsubscribe?()=>r.unsubscribe():r}var Ei=[];function Di(e,t=g){let n=null,r=new Set;function i(t){if(Ne(e,t)&&(e=t,n)){let t=!Ei.length;for(let t of r)t[1](),Ei.push(t,e);if(t){for(let e=0;e{r.delete(c),r.size===0&&n&&(n(),n=null)}}return{set:i,update:a,subscribe:o}}function Oi(e){let t;return Ti(e,e=>t=e)(),t}var ki=!1,Ai=Symbol(`unmounted`);function ji(e,t,n){let r=n[t]??={store:null,source:M(void 0),unsubscribe:g};if(r.store!==e&&!(Ai in n)){if(r.unsubscribe(),r.store=e??null,e==null)r.source.v=void 0,r.unsubscribe=g;else{var i=!0;r.unsubscribe=Ti(e,e=>{i?r.source.v=e:N(r.source,e)}),i=!1}}return e&&Ai in n?Oi(e):G(r.source)}function Mi(){let e={};function t(){_n(()=>{for(var t in e)e[t].unsubscribe();l(e,Ai,{enumerable:!1,value:!0})})}return[e,t]}function Ni(e){var t=ki;try{return ki=!1,[e(),ki]}finally{ki=t}}function $(t,n,r,i){var a=!e||!!(r&2),o=!!(r&8),s=!!(r&16),c=i,l=!0,d=void 0,f=()=>s&&a?(d??=dt(i),G(d)):(l&&(l=!1,c=s?K(i):i),c);let p;if(o){var m=fe in t||me in t;p=u(t,n)?.set??(m&&n in t?e=>t[n]=e:void 0)}var h,g=!1;o?[h,g]=Ni(()=>t[n]):h=t[n],h===void 0&&i!==void 0&&(h=f(),p&&(a&&He(n),p(h)));var _=a?()=>{var e=t[n];return e===void 0?f():(l=!0,e)}:()=>{var e=t[n];return e!==void 0&&(c=void 0),e===void 0?c:e};if(a&&!(r&4))return _;if(p){var v=t.$$legacy;return(function(e,t){return arguments.length>0?((!a||!t||v||g)&&p(t?_():e),e):_()})}var y=!1,b=(r&1?dt:mt)(()=>(y=!1,_()));o&&G(b);var x=H;return(function(e,t){if(arguments.length>0){let n=t?G(b):a&&o?Yt(e):e;return N(b,n),y=!0,c!==void 0&&(c=n),e}return Bn&&y||x.f&16384?b.v:G(b)})}function Pi(e){let t=Di({paired:!!e,controller:!1,busy:!1,error:``}),n=e,r,i={paired:!!e,controller:!1,busy:!1,error:``},a=!1;function o(e){i={...i,...e},t.set(i)}async function s(e=!1){if(!a&&n){a=!0;try{let t=await fetch(`/api/state`,{cache:`no-store`,headers:{"X-Setup-Session-Key":n}});if(!t.ok)throw Error(`The local setup session is unavailable.`);o({view:await t.json(),...e?{}:{error:``}})}catch{o({view:void 0,error:`Connection lost. The CLI may have stopped. Check the terminal before trying again.`})}finally{a=!1}}}async function c(){if(n)try{let e=await fetch(`/api/bootstrap`,{cache:`no-store`,headers:{"X-Setup-Session-Key":n}});if(!e.ok)throw Error(`Could not join this local session.`);let t=await e.json();r=t.capability,o({controller:t.controller,error:``}),await s()}catch{n=void 0,r=void 0,o({view:void 0,paired:!1,controller:!1,error:`Could not connect to the local setup session. Check the terminal and pair again.`})}}async function l(e){if(!(i.busy||i.paired)){o({busy:!0,error:``});try{let t=await fetch(`/api/pair`,{method:`POST`,cache:`no-store`,headers:{"Content-Type":`application/json`},body:JSON.stringify({code:e.trim().toLowerCase()})}),r=await t.json();if(!t.ok)throw Error(String(r.error??`Pairing was rejected.`));if(typeof r.sessionKey!=`string`||!/^[a-f0-9]{64}$/.test(r.sessionKey))throw Error(`Invalid local pairing response.`);n=r.sessionKey,o({paired:!0,error:``}),await c()}catch(e){o({error:e instanceof Error?e.message:`Could not pair this browser.`})}finally{o({busy:!1})}}}async function u(e,t,i=!0){let a=await fetch(e,{method:`POST`,cache:`no-store`,headers:{"Content-Type":`application/json`,"X-Setup-Session-Key":n,...i&&r?{"X-Setup-Capability":r}:{}},body:JSON.stringify(t)}),o=await a.json();if(!a.ok)throw Error(String(o.error??`The request was rejected.`));return o}async function d(e,t){if(!i.busy&&i.controller&&i.view?.promptRevision===e){o({busy:!0,error:``});try{await u(`/api/answer`,{revision:e,value:t}),await s()}catch(e){let t=e instanceof Error?e.message:`Could not submit this answer.`;o({error:t}),/read-only|Control moved/.test(t)?await c():await s(!0)}finally{o({busy:!1})}}}async function f(){if(i.controller&&!i.busy){o({busy:!0,error:``});try{await u(`/api/cancel`,{}),await s()}catch(e){let t=e instanceof Error?e.message:`Cancellation failed.`;o({error:t}),/read-only|Control moved/.test(t)&&await c()}finally{o({busy:!1})}}}async function p(e){if(!(i.busy||!i.paired||i.controller)){o({busy:!0,error:``});try{let t=await u(`/api/takeover`,{code:e.trim().toLowerCase()},!1);r=String(t.capability),o({controller:!0,error:``}),await s()}catch(e){o({error:e instanceof Error?e.message:`Takeover failed.`}),await s(!0)}finally{o({busy:!1})}}}async function m(){try{await u(`/api/close`,{})}catch{}}return{subscribe:t.subscribe,pair:l,connect:c,refresh:s,submit:d,cancel:f,takeOver:p,close:m}}var Fi=J(`
                                                                                      • `),Ii=J(``);function Li(e,t){O(t,!1);let n=$(t,`journey`,8),r=[`Repository`,`Setup choices`,`Setup PAT`,`Plan`,`Bot PAT & credentials`,`Apply`];Ci();var i=Ii(),a=L(F(i),4);Kr(a,5,()=>r,Hr,(e,t,r)=>{var i=Fi();let a;var o=F(i),s=I(o,!0),c=I(L(o,2),!0);E(i),R(e=>{Q(i,`aria-current`,(q(n()),K(()=>n()?.position===r+1?`step`:void 0))),a=ii(i,1,``,null,a,{current:n()?.position===r+1,completed:(n()?.position??0)>r+1}),X(s,e),X(c,G(t))},[()=>(q(n()),K(()=>(n()?.position??0)>r+1?`✓`:String(r+1).padStart(2,`0`)))]),Y(e,i)}),E(a),ke(2),E(i),Y(e,i),k()}var Ri=J(`
                                                                                        `);function zi(e,t){O(t,!1);let n=M(`system`);Cn(()=>G(n),()=>{document.documentElement.dataset.theme=G(n)}),wn();var r=Ri(),i=F(r);let a;var o=L(i,2);let s;var c=L(o,2);let l;E(r),R(()=>{Q(i,`aria-pressed`,G(n)===`system`),a=ii(i,1,``,null,a,{active:G(n)===`system`}),Q(o,`aria-pressed`,G(n)===`light`),s=ii(o,1,``,null,s,{active:G(n)===`light`}),Q(c,`aria-pressed`,G(n)===`dark`),l=ii(c,1,``,null,l,{active:G(n)===`dark`})}),gr(`click`,i,()=>N(n,`system`)),gr(`click`,o,()=>N(n,`light`)),gr(`click`,c,()=>N(n,`dark`)),Y(e,r),k()}_r([`click`]);var Bi=J(`
                                                                                        LOCAL SESSION
                                                                                        `);function Vi(e,t){let n=$(t,`repository`,8);var r=Bi(),i=F(r),a=I(L(F(i),2),!0);E(i);var o=L(i,2);zi(L(F(o)),{}),E(o),E(r),R(()=>X(a,n()??`Connecting…`)),Y(e,r)}var Hi=J(`

                                                                                        `,1);function Ui(e,t){O(t,!1);let n=M(),r=$(t,`view`,8);Cn(()=>q(r()),()=>{N(n,r()?.outcome===`complete`?`Setup complete.`:r()?.outcome===`dry-run`?`Preview complete.`:r()?.outcome===`cancelled`?`Setup cancelled.`:r()?.outcome?`Setup needs attention.`:r()?.prompt?.title??`Preparing your setup…`)}),wn(),Ci();var i=Hi(),a=on(i),o=L(F(a)),s=I(L(o));E(a);var c=L(a,2),l=I(c,!0),u=I(L(c,2),!0);R((e,t)=>{X(o,` ${e??``} `),X(s,`${t??``} / 06`),X(l,G(n)),X(u,(q(r()),K(()=>r()?.outcome?`The terminal contains the detailed result. Review the next steps below before closing this session.`:`One decision at a time. Your choices shape the permissions, plan and credentials needed for this repository.`)))},[()=>(q(r()),K(()=>r()?.journey?.current?.toUpperCase()??`GETTING READY`)),()=>(q(r()),K(()=>String(r()?.journey?.position??1).padStart(2,`0`)))]),Y(e,i),k()}function Wi(e){try{let t=new URL(e??``);return t.protocol===`https:`&&t.hostname===`github.com`&&(t.pathname===`/settings/personal-access-tokens`||t.pathname.startsWith(`/settings/personal-access-tokens/`))?t.toString():void 0}catch{return}}var Gi=J(``),Ki=J(``);function qi(e,t){let n=$(t,`label`,8),r=$(t,`variant`,8,`primary`),i=$(t,`disabled`,8,!1),a=$(t,`arrow`,8,!1),o=$(t,`onClick`,8);var s=Ki(),c=F(s,!0),l=L(c),u=e=>{Y(e,Gi())};Z(l,e=>{a()&&e(u)}),E(s),R(()=>{ii(s,1,ti(r())),s.disabled=i(),X(c,n())}),gr(`click`,s,function(...e){o()?.apply(this,e)}),Y(e,s)}_r([`click`]);var Ji=J(`Open GitHub link ↗`),Yi=J(`

                                                                                        `);function Xi(e,t){O(t,!1);let n=M(),r=$(t,`tone`,8,`info`),i=$(t,`title`,8),a=$(t,`message`,8),o=$(t,`link`,8,void 0),s=$(t,`actionLabel`,8,void 0),c=$(t,`onAction`,8,void 0);Cn(()=>q(o()),()=>{N(n,Wi(o()))}),wn(),Ci();var l=Yi(),u=F(l),d=I(u,!0),f=L(u),p=I(f,!0),m=L(f,2),h=e=>{var t=Ji();R(()=>Q(t,`href`,G(n))),Y(e,t)};Z(m,e=>{G(n)&&e(h)});var g=L(m,2),_=e=>{qi(e,{get label(){return s()},variant:`secondary`,get onClick(){return c()}})};Z(g,e=>{s()&&c()&&e(_)}),E(l),R(()=>{ii(l,1,`banner ${r()??``}`),Q(l,`role`,r()===`error`?`alert`:`status`),X(d,i()),X(p,a())}),Y(e,l),k()}function Zi(e){let t=String(e.question.defaultValue),n=t.split(`,`).map(e=>e.trim()).filter(Boolean);return{value:t,selected:e.question.kind===`multi-select`?n.includes(`All`)&&e.question.choices?.includes(`All`)?[`All`]:(e.question.choices??[]).filter(e=>e!==`All`&&n.includes(e.split(` — `)[0])):e.question.kind===`scope-overrides`?n:[]}}function Qi(e,t){return t===`All`?e.includes(`All`)?[]:[`All`]:e.includes(t)?e.filter(e=>e!==t):[...e.filter(e=>e!==`All`),t]}function $i(e,t,n){return e.question.kind===`scope-overrides`||e.question.kind===`multi-select`?n.length?n.join(`,`):`none`:t}var ea=J(`PERMISSION PREVIEW`),ta=J(`
                                                                                        `),na=J(``),ra=J(``),ia=J(``),aa=J(`
                                                                                        `),oa=J(``),sa=J(`

                                                                                        `,1);function ca(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`controller`,8),i=$(t,`busy`,8),a=$(t,`onSubmit`,8),o=Zi(n()),s=M(o.value),c=M(o.selected);Ci();var l=sa(),u=on(l),d=F(u),f=I(d,!0),p=L(d),m=e=>{Y(e,ea())};Z(p,e=>{q(n()),K(()=>n().phase===`permission-intent`)&&e(m)}),E(u);var h=L(u,2),g=e=>{var t=ta(),i=F(t);let a;var o=L(i);let c;E(t),R(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),Q(i,`aria-pressed`,G(s)===`yes`||G(s)===`true`),i.disabled=!r(),a=ii(i,1,``,null,a,{selected:G(s)===`yes`||G(s)===`true`}),Q(o,`aria-pressed`,G(s)===`no`||G(s)===`false`),o.disabled=!r(),c=ii(o,1,``,null,c,{selected:G(s)===`no`||G(s)===`false`})}),gr(`click`,i,()=>N(s,`yes`)),gr(`click`,o,()=>N(s,`no`)),Y(e,t)},_=e=>{var t=ra();Kr(t,5,()=>(q(n()),K(()=>n().question.choices??[])),Hr,(e,t)=>{var n=na(),r=I(n,!0),i={};R(()=>{X(r,G(t)),i!==(i=G(t))&&(n.value=(n.__value=i)??``)}),Y(e,n)}),E(t),ci(t),R(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),t.disabled=!r()}),li(t,()=>G(s),e=>N(s,e)),Y(e,t)},v=e=>{var t=aa();Kr(t,5,()=>(q(n()),K(()=>n().question.kind===`multi-select`?n().question.choices??[]:n().question.allowedNames??[])),Hr,(e,t)=>{var n=ia(),i=F(n);hi(i);var a=I(L(i),!0);E(n),R(e=>{gi(i,e),i.disabled=!r(),X(a,G(t))},[()=>(G(c),G(t),K(()=>G(c).includes(G(t))))]),gr(`change`,i,()=>N(c,Qi(G(c),G(t)))),Y(e,n)}),E(t),R(()=>Q(t,`aria-label`,(q(n()),K(()=>n().question.label)))),Y(e,t)},y=e=>{var t=oa();hi(t),R(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),Q(t,`type`,(q(n()),K(()=>n().question.kind===`number`?`number`:`text`))),Q(t,`min`,(q(n()),K(()=>n().question.kind===`number`?0:void 0))),t.disabled=!r()}),bi(t,()=>G(s),e=>N(s,e)),Y(e,t)};Z(h,e=>{q(n()),K(()=>n().question.kind===`boolean`)?e(g):(q(n()),K(()=>n().question.kind===`choice`)?e(_,1):(q(n()),K(()=>n().question.kind===`multi-select`||n().question.kind===`scope-overrides`)?e(v,2):e(y,-1)))});var b=L(h,2),x=I(b),S=L(b,2);{let e=mt(()=>!r()||i());qi(S,{label:`Continue`,arrow:!0,onClick:()=>a()($i(n(),G(s),G(c))),get disabled(){return G(e)}})}R((e,t)=>{X(f,e),X(x,`Suggested answer: ${t??``} · You can review choices again before creating your setup PAT.`)},[()=>(q(n()),K(()=>n().question.label.replace(` (Space toggles, Enter confirms)`,``))),()=>(q(n()),K(()=>String(n().question.defaultValue)||`none`))]),Y(e,l),k()}_r([`click`,`change`]);var la=J(``),ua=J(`
                                                                                        `);function da(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`controller`,8),i=$(t,`busy`,8),a=$(t,`onSubmit`,8);Ci();var o=ua();Kr(o,5,()=>(q(n()),K(()=>n().choices)),Hr,(e,t)=>{var n=la(),o=I(F(n),!0);ke(),E(n),R(()=>{n.disabled=!r()||i(),X(o,G(t))}),gr(`click`,n,()=>a()(G(t))),Y(e,n)}),E(o),Y(e,o),k()}_r([`click`]);var fa=J(`Open the official GitHub PAT form

                                                                                        Check the signed-in account, select Only select repositories, then select this repository. GitHub handles 2FA and creates the PAT.

                                                                                        `,1),pa=J(`

                                                                                        Sent only to this local process. It will not be shown again or saved in browser storage.

                                                                                        `),ma=J(` `,1);function ha(e,t){O(t,!1);let n=M(),r=$(t,`prompt`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8),s=M(``);function c(){let e=G(s);r().kind===`secret`&&N(s,``),o()(e)}Cn(()=>q(r()),()=>{N(n,Wi(r().link))}),wn(),Ci();var l=ma(),u=on(l),d=e=>{var t=fa(),r=on(t);ke(),R(()=>Q(r,`href`,G(n))),Y(e,t)};Z(u,e=>{G(n)&&e(d)});var f=L(u,2),p=I(f,!0),m=L(f,2);hi(m);var h=L(m,2),g=e=>{Y(e,pa())};Z(h,e=>{q(r()),K(()=>r().kind===`secret`)&&e(g)});var _=L(h,2);{let e=mt(()=>(q(i()),q(a()),q(r()),G(s),K(()=>!i()||a()||!r().optional&&!G(s).trim())));qi(_,{label:`Continue`,arrow:!0,onClick:c,get disabled(){return G(e)}})}R(()=>{X(p,(q(r()),K(()=>r().kind===`secret`?`Paste the value here`:`Your answer`))),Q(m,`type`,(q(r()),K(()=>r().kind===`secret`?`password`:`text`))),m.disabled=!i(),Q(m,`placeholder`,(q(r()),K(()=>r().kind===`secret`?`Hidden after submission`:`Type your answer`)))}),bi(m,()=>G(s),e=>N(s,e)),Y(e,l),k()}var ga=J(`
                                                                                      • `),_a=J(`

                                                                                          `),va=J(`

                                                                                          Before you continue

                                                                                            `),ya=J(`

                                                                                            Review exactly what this run may change. The bot PAT and any additional credentials are collected next.

                                                                                            `,1);function ba(e,t){O(t,!1);let n=M(),r=$(t,`prompt`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8);Cn(()=>q(r()),()=>{N(n,[{title:`Files`,items:r().plan.files},{title:`Workflows`,items:r().plan.workflows},{title:`Variables`,items:r().plan.variables},{title:`Secret names`,items:r().plan.secrets}])}),wn(),Ci();var s=ya(),c=L(on(s),2);Kr(c,5,()=>G(n),Hr,(e,t)=>{var n=_a(),r=F(n),i=F(r),a=I(L(i),!0);E(r);var o=L(r);Kr(o,5,()=>(G(t),K(()=>G(t).items)),Hr,(e,t)=>{var n=ga(),r=I(n,!0);R(()=>X(r,G(t))),Y(e,n)}),E(o),E(n),R(()=>{X(i,`${G(t),K(()=>G(t).title)??``} `),X(a,(G(t),K(()=>G(t).items.length)))}),Y(e,n)}),E(c);var l=L(c,2),u=e=>{var t=va(),n=L(F(t));Kr(n,5,()=>(q(r()),K(()=>r().plan.warnings)),Hr,(e,t)=>{var n=ga(),r=I(n,!0);R(()=>X(r,G(t))),Y(e,n)}),E(n),E(t),Y(e,t)};Z(l,e=>{q(r()),K(()=>r().plan.warnings.length)&&e(u)});var d=L(l,2),f=F(d);{let e=mt(()=>!i()||a());qi(f,{label:`Stop here`,variant:`secondary`,onClick:()=>o()(`decline`),get disabled(){return G(e)}})}var p=L(f);{let e=mt(()=>!i()||a());qi(p,{label:`Approve this plan`,arrow:!0,onClick:()=>o()(`approve`),get disabled(){return G(e)}})}E(d),Y(e,s),k()}var xa=J(`

                                                                                            `),Sa=J(`
                                                                                            CURRENT DECISION
                                                                                            `);function Ca(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`revision`,8),i=$(t,`promptRevision`,8),a=$(t,`controller`,8),o=$(t,`busy`,8),s=$(t,`onSubmit`,8);Ci();var c=Sa(),l=F(c),u=I(L(F(l)));E(l);var d=L(l,2),f=e=>{var t=xa(),r=I(t,!0);R(()=>X(r,(q(n()),K(()=>n().description)))),Y(e,t)};Z(d,e=>{q(n()),K(()=>`description`in n()&&n().description)&&e(f)}),Vr(L(d,2),i,e=>{var t=Er(),r=on(t),i=e=>{ca(e,{get prompt(){return n()},get controller(){return a()},get busy(){return o()},get onSubmit(){return s()}})},c=e=>{da(e,{get prompt(){return n()},get controller(){return a()},get busy(){return o()},get onSubmit(){return s()}})},l=e=>{ha(e,{get prompt(){return n()},get controller(){return a()},get busy(){return o()},get onSubmit(){return s()}})},u=e=>{ba(e,{get prompt(){return n()},get controller(){return a()},get busy(){return o()},get onSubmit(){return s()}})};Z(r,e=>{q(n()),K(()=>n().kind===`question`)?e(i):(q(n()),K(()=>n().kind===`choice`||n().kind===`confirm`)?e(c,1):(q(n()),K(()=>n().kind===`text`||n().kind===`secret`)?e(l,2):(q(n()),K(()=>n().kind===`plan`)&&e(u,3))))}),Y(e,t)}),E(c),R(()=>X(u,`SESSION ${r()??``}`)),Y(e,c),k()}var wa=J(` `),Ta=J(`
                                                                                          • `),Ea=J(`

                                                                                              Conditional grants depend on the choices and GitHub facts. A final audit runs before setup changes.
                                                                                              `),Da=J(`

                                                                                              Permissions follow your choices

                                                                                              We’ll show the exact grants before you create each PAT. Nothing is created just by opening this page.

                                                                                              `),Oa=J(``);function ka(e,t){O(t,!1);let n=$(t,`view`,8);Ci();var r=Oa(),i=F(r),a=I(L(F(i),3),!0);E(i);var o=L(i,2),s=e=>{var t=Ea(),r=L(F(t)),i=I(r),a=L(r),o=I(a,!0),s=L(a,2);Kr(s,5,()=>(q(n()),K(()=>n().permissions.report?.checks??n().permissions.requirements??[])),Hr,(e,t)=>{var n=Ta(),r=F(n),i=F(r),a=L(i),o=I(a),s=I(L(a),!0);E(r);var c=L(r),l=F(c,!0),u=L(l),d=e=>{var n=wa(),r=I(n,!0);R(()=>X(r,(G(t),K(()=>G(t).status)))),Y(e,n)};Z(u,e=>{`status`in G(t)&&e(d)}),E(c),E(n),R(()=>{X(i,`${G(t),K(()=>G(t).permission)??``} `),X(o,`${G(t),K(()=>G(t).scope)??``} · ${G(t),K(()=>G(t).applicability)??``}`),X(s,(G(t),K(()=>G(t).reason))),X(l,(G(t),K(()=>G(t).level)))}),Y(e,n)}),E(s),ke(2),E(t),R(()=>{X(i,`${q(n()),K(()=>n().permissions.role===`setup`?`Setup PAT`:`Bot PAT`)??``} access`),X(o,(q(n()),K(()=>n().permissions.report?`Read-only access check`:`Provisional least-privilege grants`)))}),Y(e,t)},c=e=>{Y(e,Da())};Z(o,e=>{q(n()),K(()=>n().permissions)?e(s):e(c,-1)}),E(r),R(()=>X(a,(q(n()),K(()=>n().repository)))),Y(e,r),k()}var Aa=J(`

                                                                                              `);function ja(e,t){O(t,!1);let n=M(),r=M(),i=$(t,`outcome`,8),a=$(t,`controller`,8),o=$(t,`onClose`,8);Cn(()=>q(i()),()=>{N(n,i()===`complete`?`Your configuration was applied`:i()===`dry-run`?`No changes were made`:i()===`cancelled`||i()===`blocked`?`No setup changes started`:`Check partial changes before retrying`)}),Cn(()=>q(i()),()=>{N(r,i()===`complete`?`The temporary setup PAT is not revoked automatically. Delete it in GitHub after confirming your setup. Keep the bot PAT until you rotate the installed Secret.`:i()===`partial`?`A bot Secret write or another change may have succeeded. Inspect GitHub and run copilot doctor before replacing or deleting its PAT.`:`Copilot did not begin applying setup changes. PATs you created in GitHub still exist until you delete them there.`)}),wn();var s=Aa(),c=F(s),l=I(c,!0),u=L(c),d=I(u,!0),f=L(u),p=I(f,!0),m=L(f,2),h=e=>{qi(e,{label:`Close local session`,get onClick(){return o()}})};Z(m,e=>{a()&&e(h)}),E(s),R(()=>{X(l,i()===`complete`?`✓`:`!`),X(d,G(n)),X(p,G(r))}),Y(e,s),k()}var Ma=J(`

                                                                                              Working on the next step

                                                                                              The local process is checking your answers and preparing the next decision. Keep this page open.

                                                                                              `);function Na(e){Y(e,Ma())}var Pa=J(`Find the 16-character pairing code in the terminal that started copilot setup --web. Enter it here to view or control this setup. The code is never placed in the browser URL or stored after this page closes.`,1),Fa=J(`
                                                                                              PRIVATE LOCAL SESSION

                                                                                              Keep the code private. After refreshing this page, enter it again to reconnect.

                                                                                              `);function Ia(e,t){O(t,!1);let n=$(t,`busy`,8),r=$(t,`onPair`,8),i=$(t,`mode`,8,`pair`),a=M(``);function o(){let e=G(a);N(a,``),r()(e)}Ci();var s=Fa(),c=L(F(s),2),l=I(F(c),!0);E(c);var u=L(c,2),d=F(u),f=e=>{var t=Pa();ke(2),Y(e,t)},p=e=>{Y(e,Tr(`Re-enter the pairing code from the launching terminal to take control. The previous tab will become read-only.`))};Z(d,e=>{i()===`pair`?e(f):e(p,-1)}),E(u);var m=L(u,2),h=L(F(m),2);hi(h);var g=L(h,4);{let e=mt(()=>i()===`pair`?`Connect to local setup`:`Take control in this tab`),t=mt(()=>(q(n()),G(a),K(()=>n()||G(a).trim().length!==16)));qi(g,{get label(){return G(e)},arrow:!0,onClick:o,get disabled(){return G(t)}})}E(m),E(s),R(()=>{Q(s,`aria-label`,i()===`pair`?`Pair this browser with the local setup session`:`Take control of this local setup session`),X(l,i()===`pair`?`Pair this browser`:`Take control in this tab`),h.disabled=n()}),hr(`submit`,m,e=>{e.preventDefault(),o()}),bi(h,()=>G(a),e=>N(a,e)),Y(e,s),k()}var La=J(``),Ra=J(`
                                                                                              `),za=J(` `,1),Ba=J(``),Va=J(`
                                                                                              `,1),Ha=J(`
                                                                                              LOCALHOST ONLY NO CLOUD SETUP ACCOUNT GITHUB OWNS PAT ISSUANCE
                                                                                              `);function Ua(e,t){O(t,!1);let n=()=>ji(a,`$session`,r),[r,i]=Mi(),a=Pi();Rr(()=>{let e=window.setInterval(()=>{n().paired&&!n().view?.outcome&&a.refresh()},900);return()=>window.clearInterval(e)});async function o(){window.confirm(`Cancel this local setup session? PATs already created in GitHub will still exist.`)&&await a.cancel()}async function s(e){let t=n().view?.promptRevision;t!==void 0&&await a.submit(t,e)}Ci();var c=Ha();Qr(`16t12jp`,e=>{Y(e,La())});var l=F(c);{let e=mt(()=>n().view?.journey);Li(l,{get journey(){return G(e)}})}var u=L(l,2),d=F(u);{let e=mt(()=>n().view?.repository);Vi(d,{get repository(){return G(e)}})}var f=L(d,2),p=F(f),m=e=>{Ui(e,{get view(){return n().view}})};Z(p,e=>{n().paired&&e(m)});var h=L(p,2),g=e=>{var t=Ra(),r=L(F(t));E(t),R(()=>X(r,` Reviewing saved choices — pass ${n().view.journey.choiceReviewPass??``}. This is the same setup run, not a restart.`)),Y(e,t)};Z(h,e=>{n().view?.journey?.choiceReviewPass&&n().view.journey.choiceReviewPass>1&&!n().view.outcome&&e(g)});var _=L(h,2),v=e=>{var t=za(),r=on(t);Xi(r,{tone:`warning`,title:`Read-only tab`,message:`Another tab controls this session. Enter the pairing code from the launching terminal to take over.`}),Ia(L(r,2),{mode:`takeover`,get busy(){return n().busy},get onPair(){return a.takeOver}}),Y(e,t)};Z(_,e=>{!n().controller&&n().view&&e(v)});var y=L(_,2),b=e=>{Xi(e,{tone:`error`,title:`Needs attention`,get message(){return n().error}})};Z(y,e=>{n().error&&e(b)});var x=L(y,2),S=e=>{{let t=mt(()=>n().view.message.tone===`success`?`Checked`:n().view.message.tone===`warning`?`Please note`:n().view.message.tone===`error`?`Needs attention`:`Progress update`);Xi(e,{get tone(){return n().view.message.tone},get title(){return G(t)},get message(){return n().view.message.text},get link(){return n().view.message.link}})}};Z(x,e=>{n().view?.message&&e(S)});var ee=L(x,2),te=e=>{Ia(e,{get busy(){return n().busy},get onPair(){return a.pair}})},ne=e=>{ja(e,{get outcome(){return n().view.outcome},get controller(){return n().controller},get onClose(){return a.close}})},re=e=>{var t=Va(),r=on(t),i=F(r);Ca(i,{get prompt(){return n().view.prompt},get revision(){return n().view.revision},get promptRevision(){return n().view.promptRevision},get controller(){return n().controller},get busy(){return n().busy},onSubmit:s}),ka(L(i,2),{get view(){return n().view}}),E(r);var a=L(r,2),c=e=>{var t=Ba();R(()=>t.disabled=n().busy),gr(`click`,t,o),Y(e,t)};Z(a,e=>{n().controller&&n().view.journey?.current!==`Apply`&&e(c)}),Y(e,t)},ie=e=>{Na(e,{})};Z(ee,e=>{n().paired?n().view?.outcome?e(ne,1):n().view?.prompt?e(re,2):e(ie,-1):e(te)}),ke(2),E(f),E(u),E(c),Y(e,c),k(),i()}_r([`click`]),Nr(Ua,{target:document.getElementById(`app`)}); \ No newline at end of file diff --git a/build/web/index.html b/build/web/index.html index efbc470c8..f5ae56b62 100644 --- a/build/web/index.html +++ b/build/web/index.html @@ -5,7 +5,7 @@ Copilot · Setup studio - + diff --git a/docs/how-to-use.mdx b/docs/how-to-use.mdx index 5907760a0..a7d3f4614 100644 --- a/docs/how-to-use.mdx +++ b/docs/how-to-use.mdx @@ -50,6 +50,10 @@ appears. The URL contains no secret; keep the pairing code private. Loopback by itself does not restrict access to your OS account, and anyone on this computer with the code could join the session. After a page refresh, enter the code again. Neither the code nor the session key is sent to GitHub. +An input TTY is not required for `--web`, but you must be able to read the +launcher's stdout (or a privately captured task log) to obtain the code. If +that output was discarded, stop and relaunch from a readable terminal; there +is no code-recovery link in the browser. Treat any captured output as private. The page shows the same six setup stages, keeps the review pass visibly part of the current run, presents permissions and the final plan, and asks for a separate final **Apply setup** approval. Use the System/Light/Dark control in @@ -57,8 +61,10 @@ the top bar to choose a readable palette; the choice lasts only in that tab. PAT values are submitted to the local process through masked fields and are not restored after refreshing the page. A new setup question starts with its own suggested answer; status updates do not erase an answer you are typing. -A second tab is read-only until you -explicitly take control there. The terminal remains the default with +A second tab is read-only until you explicitly re-enter the launcher's pairing +code there to take control; this invalidates the first tab's control. The +pairing code authorizes this takeover, so share it only with someone you trust +to control the setup. The terminal remains the default with `copilot setup`. The web mode still opens GitHub's official form in a separate tab for each diff --git a/specs/local-web-setup-assistant.md b/specs/local-web-setup-assistant.md index 7a7a8360f..1fca8b4dd 100644 --- a/specs/local-web-setup-assistant.md +++ b/specs/local-web-setup-assistant.md @@ -216,8 +216,12 @@ exactly what completed and what remains. process can replace it between a read and an unlink. Publish a fully written lock record atomically; failed writes MUST NOT leave a blocking empty lock. Web - mode does not require a TTY: the browser is the interactive surface, and - a printed local URL is available if automatic opening is unavailable. + mode does not require an input TTY: the browser is the interactive surface, + but the operator MUST be able to read the launcher's stdout, either directly + or in a privately captured task log, to obtain the pairing code and local + URL. An invocation whose stdout is discarded cannot be paired; rerun from + a readable terminal or captured-output task. Copilot never copies the code + into the browser URL or a persistent diagnostic log. Web setup MUST verify an attached Git branch and canonical HEAD before opening the browser or collecting credentials. Detached HEAD or an unreadable branch fails immediately with checkout guidance; no fallback branch name @@ -231,12 +235,17 @@ exactly what completed and what remains. root, but never a nested directory. 2. Bind `127.0.0.1:0`, record the assigned port, create an unpredictable one-run session key, a separate 16-hex-character pairing code, and first - controller lease in process memory. Print the pairing code only in the - terminal, without adding it to accumulated diagnostics, then open the + controller lease in process memory. Print the pairing code only to the + launcher's stdout, without adding it to accumulated diagnostics, then open the default browser to the public `http://127.0.0.1:/` URL. The initial page asks for the code before any setup state is shown. A same-origin POST exchanges it for the session key held only in browser memory; five invalid - attempts lock pairing until a new setup run. If opening fails, print the + attempts lock pairing until a new setup run. A second paired tab remains + read-only until its operator explicitly re-enters that same code for a + takeover POST; no takeover ticket is distributed in bootstrap. Failed + takeover codes share the bounded attempt counter. A successful takeover + rotates the controller capability and invalidates the previous tab. + If opening fails, print the public URL and instructions; serving continues. Neither code nor key may appear in URL, history, cookies, browser storage, or accumulated logs. If binding or packaged @@ -319,7 +328,8 @@ exactly what completed and what remains. guided link for that role and provides the existing full permission table and manual compatibility path; never auto-select a broader classic PAT. - Leaving the page or closing its tab does not prove cancellation or - revocation. A second tab starts read-only and may explicitly take over; + revocation. A second tab starts read-only and may take over only after its + operator explicitly re-enters the pairing code from the launching output; takeover rotates the controller lease, invalidates pending responses from the old tab, and shows the current server-owned phase. No PAT value is rehydrated into either tab. Browser Back revisits a *view*; it cannot undo @@ -671,11 +681,13 @@ for the temporary setup PAT and to bot-PAT rotation guidance separately. 1. **Boundary:** the web server is loopback-only, but TCP loopback does not identify or isolate the launching OS user: another local user can connect to the port. The browser must enter a cryptographically random code shown - only in the launching terminal; the server exchanges it for a one-run + only in the launcher's readable stdout; the server exchanges it for a one-run session key. That key is required for every API read and mutation, including bootstrap and takeover. Possession of the pairing code grants local session - access, so it must not be shared. A malicious process that can read the - terminal, browser extensions with page access, or a compromised browser + access including explicit takeover, so it must not be shared. If a task runner + captures stdout, that private capture must be protected like the code. A + malicious process that can read that output, browser extensions with page + access, or a compromised browser remain outside this boundary. The product must say so honestly. 2. **Request defense:** reject `Host` not exactly `127.0.0.1:`, proxy/forwarded host headers, unexpected `Origin`/`Referer` on mutations, @@ -686,11 +698,15 @@ for the temporary setup PAT and to bot-PAT rotation guidance separately. and key are not sent in an HTTP URL or stored in a cookie/localStorage/ sessionStorage; the browser sends the key in a custom header to every subsequent API route. In addition, - state-changing requests require a separate one-run, cryptographically - random controller capability in a custom header plus a revision check; + state-changing answer, cancel, and close requests require a separate one-run, + cryptographically random controller capability in a custom header; answers + additionally require a revision check. Pairing and takeover instead require + the pairing code with bounded wrong-code attempts; that capability is delivered only by an authenticated same-origin no-store bootstrap response, never a URL or browser storage. Reject missing/invalid - keys or capabilities and rotate the controller capability on tab takeover. + keys or capabilities and rotate the controller capability on code-authorized + tab takeover. Bootstrap never discloses a takeover credential to read-only + tabs. These controls defend cross-site requests and host confusion, but do not prove the identity of a local OS user. The local server sets no cookies and ignores, never logs, any Cookie header @@ -839,7 +855,9 @@ help for `--web` explains local-only scope and the `--non-interactive` conflict. manifest or any managed guidance artifact when guidance is disabled and its prior artifacts would be retired. 9. Given an invalid/stale tab event or second tab, no action occurs until the - new tab explicitly takes control; the first tab then cannot submit. + new tab re-enters the launcher-output pairing code and explicitly takes + control; the first tab then cannot submit. Bootstrap to a read-only tab + contains no takeover credential, and wrong-code attempts are bounded. Given a concurrent terminal or web setup in the same checkout, the per-checkout guard blocks its Apply before any mutation. 10. Given cancel/idle expiry before Apply, the process closes without setup @@ -867,8 +885,10 @@ help for `--web` explains local-only scope and the `--non-interactive` conflict. 15. Given no `--web`, existing terminal, unattended, and dry-run contracts remain unchanged. Build/package/architecture checks detect missing UI assets, policy duplication, and Svelte leaking into Action/API bundles. -16. Given a desktop browser but no terminal TTY, web mode still permits - explicit browser decisions. Given an existing unreadable Secret `PAT`, +16. Given a desktop browser but no input TTY and privately readable captured + stdout, web mode still permits pairing and explicit browser decisions; if + stdout was discarded, the operator must relaunch with readable output. + Given an existing unreadable Secret `PAT`, the UI does not claim to recover its value and follows the current re-entry/preservation policy. Given an environment-supplied setup PAT, exit never claims to have removed it from the parent shell. diff --git a/src/__tests__/cli.test.ts b/src/__tests__/cli.test.ts index 58a8be044..8ecf9fdfc 100644 --- a/src/__tests__/cli.test.ts +++ b/src/__tests__/cli.test.ts @@ -541,6 +541,20 @@ describe('CLI', () => { expect(process.exitCode).toBeUndefined(); }); + it('prints the pairing code to stdout even without an interactive TTY', async () => { + const descriptor = Object.getOwnPropertyDescriptor(process.stdout, 'isTTY'); + Object.defineProperty(process.stdout, 'isTTY', { configurable: true, value: false }); + try { + await program.parseAsync(['node', 'cli', 'setup', '--web', '--pr-approval-mode', 'off', '--skip-secrets']); + const { logInfo } = require('../utils/logger'); + expect(logInfo).toHaveBeenCalledWith(expect.stringContaining('0123456789abcdef'), false, undefined, true); + expect(startWebSetupServer).toHaveBeenCalledTimes(1); + } finally { + if (descriptor) Object.defineProperty(process.stdout, 'isTTY', descriptor); + else Reflect.deleteProperty(process.stdout, 'isTTY'); + } + }); + it('stops before acquiring a PAT when repository confirmation is declined', async () => { ask.mockResolvedValueOnce('Stop and choose another checkout'); await program.parseAsync(['node', 'cli', 'setup', '--web']); diff --git a/src/cli/__tests__/web_setup_bridge.test.ts b/src/cli/__tests__/web_setup_bridge.test.ts index d5f31f452..9f3517de4 100644 --- a/src/cli/__tests__/web_setup_bridge.test.ts +++ b/src/cli/__tests__/web_setup_bridge.test.ts @@ -20,11 +20,10 @@ describe('WebSetupBridge', () => { expect(first.controller).toBe(true); expect(second.controller).toBe(false); expect(second.capability).toBeUndefined(); - expect(bridge.takeOver('invalid')).toBeUndefined(); - const replacement = bridge.takeOver(second.takeoverTicket)!; + expect(JSON.stringify(second)).not.toContain('takeoverTicket'); + const replacement = bridge.takeOver(); expect(bridge.isController(first.capability!)).toBe(false); expect(bridge.isController(replacement)).toBe(true); - expect(bridge.takeOver(second.takeoverTicket)).toBeUndefined(); }); test('cancellation resolves a pending decision and forbids future prompts', async () => { diff --git a/src/cli/__tests__/web_setup_browser_session.test.ts b/src/cli/__tests__/web_setup_browser_session.test.ts index f98fd5d9f..5f9a08092 100644 --- a/src/cli/__tests__/web_setup_browser_session.test.ts +++ b/src/cli/__tests__/web_setup_browser_session.test.ts @@ -162,7 +162,7 @@ describe('browser session transport', () => { const requests: Array<{ path: string; options?: RequestInit }> = []; globalThis.fetch = jest.fn(async (path: string, options?: RequestInit) => { requests.push({ path, options }); - if (path === '/api/bootstrap') return response({ controller: false, takeoverTicket: 'ticket' }); + if (path === '/api/bootstrap') return response({ controller: false }); if (path === '/api/state') return response(view); if (path === '/api/takeover') return response({ capability: 'new-capability' }); if (path === '/api/answer') return response({ ok: true }); @@ -175,9 +175,10 @@ describe('browser session transport', () => { await session.connect(); await session.submit(7, 'blocked'); expect(requests.some(request => request.path === '/api/answer')).toBe(false); - await session.takeOver(); + await session.takeOver(' 0123456789ABCDEF '); expect(controller).toBe(true); expect(requests.find(request => request.path === '/api/takeover')?.options?.headers).not.toHaveProperty('X-Setup-Capability'); + expect(JSON.parse(String(requests.find(request => request.path === '/api/takeover')?.options?.body))).toEqual({ code: '0123456789abcdef' }); await session.submit(7, 'allowed'); expect(requests.find(request => request.path === '/api/answer')?.options?.headers).toEqual(expect.objectContaining({ 'X-Setup-Capability': 'new-capability' })); }); @@ -301,10 +302,10 @@ describe('browser session transport', () => { expect(latest).toContain('Cancellation failed.'); }); - test('an unexpected takeover transport failure rechecks who controls the session', async () => { + test('an unexpected takeover transport failure keeps the tab read-only', async () => { let bootstrapReads = 0; globalThis.fetch = jest.fn(async (path: string) => { - if (path === '/api/bootstrap') { bootstrapReads += 1; return response({ controller: false, takeoverTicket: 'ticket' }); } + if (path === '/api/bootstrap') { bootstrapReads += 1; return response({ controller: false }); } if (path === '/api/state') return response(view); if (path === '/api/takeover') throw 'transport unavailable'; throw new Error('Unexpected route'); @@ -314,8 +315,8 @@ describe('browser session transport', () => { let controller = true; session.subscribe(state => { controller = state.controller; }); await session.connect(); - await session.takeOver(); - expect(bootstrapReads).toBe(2); + await session.takeOver('0123456789abcdef'); + expect(bootstrapReads).toBe(1); expect(controller).toBe(false); }); @@ -360,24 +361,24 @@ describe('browser session transport', () => { await first; }); - test('a failed takeover re-reads the current controller instead of retaining authority', async () => { + test('a failed takeover retains read-only state and explains the rejected code', async () => { const requests: string[] = []; globalThis.fetch = jest.fn(async (path: string) => { requests.push(path); - if (path === '/api/bootstrap') return response({ controller: false, takeoverTicket: 'ticket' }); + if (path === '/api/bootstrap') return response({ controller: false }); if (path === '/api/state') return response(view); - if (path === '/api/takeover') return response({ error: 'Invalid takeover ticket.' }, 403); + if (path === '/api/takeover') return response({ error: 'Incorrect pairing code.' }, 403); throw new Error('Unexpected route'); }) as typeof fetch; const session = createSetupSession(TEST_SESSION_KEY); - let latest: { controller: boolean; busy: boolean } | undefined; + let latest: { controller: boolean; busy: boolean; error: string } | undefined; session.subscribe(state => { latest = state; }); await session.connect(); - await session.takeOver(); + await session.takeOver('0123456789abcdef'); - expect(requests.filter(path => path === '/api/bootstrap')).toHaveLength(2); - expect(latest).toMatchObject({ controller: false, busy: false }); + expect(requests.filter(path => path === '/api/bootstrap')).toHaveLength(1); + expect(latest).toMatchObject({ controller: false, busy: false, error: 'Incorrect pairing code.' }); }); test('failed bootstrap and state requests are reported without claiming a live session', async () => { diff --git a/src/cli/__tests__/web_setup_server.test.ts b/src/cli/__tests__/web_setup_server.test.ts index 9827fc9b3..ad91037ed 100644 --- a/src/cli/__tests__/web_setup_server.test.ts +++ b/src/cli/__tests__/web_setup_server.test.ts @@ -234,10 +234,12 @@ describe('local web setup server', () => { test('a second tab explicitly takes over and invalidates the first tab capability', async () => { const first = await (await fetch(`${server.url}api/bootstrap`)).json() as { capability: string }; - const second = await (await fetch(`${server.url}api/bootstrap`)).json() as { controller: boolean; takeoverTicket: string }; + const second = await (await fetch(`${server.url}api/bootstrap`)).json() as { controller: boolean; capability?: string; takeoverTicket?: string }; expect(second.controller).toBe(false); - expect((await jsonPost(server.url, 'api/takeover', { ticket: 'wrong' })).status).toBe(403); - const takeover = await jsonPost(server.url, 'api/takeover', { ticket: second.takeoverTicket }); + expect(second.capability).toBeUndefined(); + expect(second.takeoverTicket).toBeUndefined(); + expect((await jsonPost(server.url, 'api/takeover', { code: 'wrong' })).status).toBe(403); + const takeover = await jsonPost(server.url, 'api/takeover', { code: server.pairingCode }); expect(takeover.status).toBe(200); const { capability } = await takeover.json() as { capability: string }; const pending = bridge.ask({ kind: 'choice', title: 'Proceed?', choices: ['yes', 'no'] }); @@ -245,7 +247,17 @@ describe('local web setup server', () => { expect((await jsonPost(server.url, 'api/answer', { revision, value: 'yes' }, { 'X-Setup-Capability': first.capability })).status).toBe(403); expect((await jsonPost(server.url, 'api/answer', { revision, value: 'yes' }, { 'X-Setup-Capability': capability })).status).toBe(200); expect(await pending).toBe('yes'); - expect((await jsonPost(server.url, 'api/takeover', { ticket: second.takeoverTicket })).status).toBe(403); + }); + + test('limits wrong-code takeover attempts without returning a controller capability', async () => { + await fetch(`${server.url}api/bootstrap`); + await fetch(`${server.url}api/bootstrap`); + for (let attempt = 0; attempt < 5; attempt += 1) { + const rejected = await jsonPost(server.url, 'api/takeover', { code: 'wrong' }); + expect(rejected.status).toBe(403); + expect(await rejected.json()).not.toHaveProperty('capability'); + } + expect((await jsonPost(server.url, 'api/takeover', { code: server.pairingCode })).status).toBe(429); }); test('cancel requires the controller and never claims to cancel in-flight Apply', async () => { @@ -352,13 +364,13 @@ describe('local web setup server', () => { expect((await wrongType('api/close')).status).toBe(415); }); - test('mutating requests without a controller capability or a takeover ticket do nothing', async () => { + test('mutating requests without a controller capability do nothing', async () => { const origin = server.url.slice(0, -1); const pending = bridge.ask({ kind: 'secret', title: 'PAT' }); const revision = bridge.snapshot().promptRevision; expect((await jsonPost(server.url, 'api/answer', { revision, value: 'ignored' })).status).toBe(403); expect((await jsonPost(server.url, 'api/cancel', {})).status).toBe(403); - expect((await jsonPost(server.url, 'api/takeover', { ticket: 42 })).status).toBe(403); + expect((await jsonPost(server.url, 'api/takeover', { code: 42 })).status).toBe(403); expect((await fetch(`${server.url}api/answer`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ revision, value: 'ignored' }) })).status).toBe(403); expect(origin).toContain('127.0.0.1'); bridge.cancel(); diff --git a/src/cli/web_setup_bridge.ts b/src/cli/web_setup_bridge.ts index 0df008e2e..a0d577dac 100644 --- a/src/cli/web_setup_bridge.ts +++ b/src/cli/web_setup_bridge.ts @@ -12,7 +12,6 @@ export class WebSetupBridge { private subscribers = new Set<(view: WebSetupView) => void>(); private controller?: string; private lastAnsweredRevision?: number; - private takeoverTicket = randomBytes(32).toString('hex'); constructor(repository: string) { this.view = { revision: 0, repository }; @@ -26,20 +25,18 @@ export class WebSetupBridge { return () => this.subscribers.delete(listener); } - bootstrap(): { controller: boolean; capability?: string; takeoverTicket: string } { + bootstrap(): { controller: boolean; capability?: string } { if (!this.controller) this.controller = randomBytes(32).toString('hex'); // A second tab starts read-only. Its explicit takeover rotates the controller capability. const first = !this.bootstrapped; this.bootstrapped = true; - return { controller: first, ...(first ? { capability: this.controller } : {}), takeoverTicket: this.takeoverTicket }; + return { controller: first, ...(first ? { capability: this.controller } : {}) }; } private bootstrapped = false; - takeOver(ticket: string): string | undefined { - if (!sameCapability(ticket, this.takeoverTicket)) return undefined; + takeOver(): string { this.controller = randomBytes(32).toString('hex'); - this.takeoverTicket = randomBytes(32).toString('hex'); this.publish({ message: { tone: 'info', text: 'Control moved to this tab. The previous tab is now read-only.' } }); return this.controller; } diff --git a/src/cli/web_setup_server.ts b/src/cli/web_setup_server.ts index cd9457639..147d65c89 100644 --- a/src/cli/web_setup_server.ts +++ b/src/cli/web_setup_server.ts @@ -95,10 +95,15 @@ export async function startWebSetupServer(bridge: WebSetupBridge, assets = join( if (request.method === 'POST' && request.url === '/api/takeover') { if (request.headers['content-type'] !== 'application/json') { respond(response, 415, { error: 'JSON required.' }); return; } const body = await readJson(request); - const ticket = typeof body.ticket === 'string' ? body.ticket : ''; - const capability = bridge.takeOver(ticket); - if (capability) armIdle(); - respond(response, capability ? 200 : 403, capability ? { capability } : { error: 'Invalid takeover ticket.' }); + if (failedPairings >= 5) { respond(response, 429, { error: 'Too many pairing attempts. Restart setup.' }); return; } + if (!matchesHexSecret(body.code, pairingCode)) { + failedPairings += 1; + respond(response, 403, { error: 'Incorrect pairing code. Check the launching output.' }); + return; + } + const capability = bridge.takeOver(); + armIdle(); + respond(response, 200, { capability }); return; } if (request.method === 'POST' && request.url === '/api/answer') { diff --git a/web/src/App.svelte b/web/src/App.svelte index f5069966a..b05d0da5a 100644 --- a/web/src/App.svelte +++ b/web/src/App.svelte @@ -41,7 +41,8 @@
                                                                                              Reviewing saved choices — pass {$session.view.journey.choiceReviewPass}. This is the same setup run, not a restart.
                                                                                              {/if} {#if !$session.controller && $session.view} - + + {/if} {#if $session.error}{/if} {#if $session.view?.message} diff --git a/web/src/components/PairingPanel.svelte b/web/src/components/PairingPanel.svelte index 8e9f0c91a..0194fe6f7 100644 --- a/web/src/components/PairingPanel.svelte +++ b/web/src/components/PairingPanel.svelte @@ -2,6 +2,7 @@ import ActionButton from './ActionButton.svelte'; export let busy: boolean; export let onPair: (code: string) => Promise; + export let mode: 'pair' | 'takeover' = 'pair'; let code = ''; function submit(): void { @@ -11,14 +12,14 @@ } -
                                                                                              +
                                                                                              PRIVATE LOCAL SESSION
                                                                                              -

                                                                                              Pair this browser

                                                                                              -

                                                                                              Find the 16-character pairing code in the terminal that started copilot setup --web. Enter it here to view or control this setup. The code is never placed in the browser URL or stored after this page closes.

                                                                                              +

                                                                                              {mode === 'pair' ? 'Pair this browser' : 'Take control in this tab'}

                                                                                              +

                                                                                              {#if mode === 'pair'}Find the 16-character pairing code in the terminal that started copilot setup --web. Enter it here to view or control this setup. The code is never placed in the browser URL or stored after this page closes.{:else}Re-enter the pairing code from the launching terminal to take control. The previous tab will become read-only.{/if}

                                                                                              { event.preventDefault(); submit(); }}>

                                                                                              Keep the code private. After refreshing this page, enter it again to reconnect.

                                                                                              - +
                                                                                              diff --git a/web/src/session/setupSession.ts b/web/src/session/setupSession.ts index 00a4012d3..92f916749 100644 --- a/web/src/session/setupSession.ts +++ b/web/src/session/setupSession.ts @@ -12,14 +12,12 @@ interface SessionState { interface Bootstrap { controller: boolean; capability?: string; - takeoverTicket: string; } export function createSetupSession(initialSessionKey?: string) { const state = writable({ paired: Boolean(initialSessionKey), controller: false, busy: false, error: '' }); let sessionKey = initialSessionKey; let capability: string | undefined; - let takeoverTicket = ''; let current: SessionState = { paired: Boolean(initialSessionKey), controller: false, busy: false, error: '' }; let loading = false; @@ -49,13 +47,11 @@ export function createSetupSession(initialSessionKey?: string) { if (!response.ok) throw new Error('Could not join this local session.'); const bootstrap = await response.json() as Bootstrap; capability = bootstrap.capability; - takeoverTicket = bootstrap.takeoverTicket; set({ controller: bootstrap.controller, error: '' }); await refresh(); } catch { sessionKey = undefined; capability = undefined; - takeoverTicket = ''; set({ view: undefined, paired: false, controller: false, error: 'Could not connect to the local setup session. Check the terminal and pair again.' }); } } @@ -124,15 +120,19 @@ export function createSetupSession(initialSessionKey?: string) { } } - async function takeOver(): Promise { + async function takeOver(code: string): Promise { + if (current.busy || !current.paired || current.controller) return; + set({ busy: true, error: '' }); try { - const result = await post('/api/takeover', { ticket: takeoverTicket }, false); + const result = await post('/api/takeover', { code: code.trim().toLowerCase() }, false); capability = String(result.capability); set({ controller: true, error: '' }); await refresh(); } catch (cause) { set({ error: cause instanceof Error ? cause.message : 'Takeover failed.' }); - await connect(); + await refresh(true); + } finally { + set({ busy: false }); } } From 34264ae3268e9a40e7241bc87c3e4af9dad15339 Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Tue, 29 Sep 2026 12:10:19 +0200 Subject: [PATCH 37/50] test(setup-web): cover takeover guard branches --- .../web_setup_browser_session.test.ts | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/cli/__tests__/web_setup_browser_session.test.ts b/src/cli/__tests__/web_setup_browser_session.test.ts index 5f9a08092..346a2c848 100644 --- a/src/cli/__tests__/web_setup_browser_session.test.ts +++ b/src/cli/__tests__/web_setup_browser_session.test.ts @@ -183,6 +183,31 @@ describe('browser session transport', () => { expect(requests.find(request => request.path === '/api/answer')?.options?.headers).toEqual(expect.objectContaining({ 'X-Setup-Capability': 'new-capability' })); }); + test('takeover cannot start before pairing, from the controller tab, or twice concurrently', async () => { + const unpaired = createSetupSession(); + globalThis.fetch = jest.fn() as typeof fetch; + await unpaired.takeOver('0123456789abcdef'); + expect(globalThis.fetch).not.toHaveBeenCalled(); + + let releaseTakeover!: (response: Response) => void; + const pendingTakeover = new Promise(resolve => { releaseTakeover = resolve; }); + globalThis.fetch = jest.fn(async (path: string) => { + if (path === '/api/bootstrap') return response({ controller: false }); + if (path === '/api/state') return response(view); + if (path === '/api/takeover') return pendingTakeover; + throw new Error('Unexpected route'); + }) as typeof fetch; + const session = createSetupSession(TEST_SESSION_KEY); + await session.connect(); + const first = session.takeOver('0123456789abcdef'); + await session.takeOver('0123456789abcdef'); + expect((globalThis.fetch as jest.Mock).mock.calls.filter(([path]) => path === '/api/takeover')).toHaveLength(1); + releaseTakeover(response({ capability: 'new-controller' })); + await first; + await session.takeOver('0123456789abcdef'); + expect((globalThis.fetch as jest.Mock).mock.calls.filter(([path]) => path === '/api/takeover')).toHaveLength(1); + }); + test('a rejected answer refreshes the question while keeping the error visible and the PAT private', async () => { const requests: string[] = []; globalThis.fetch = jest.fn(async (path: string) => { From 1e91f70b2957d9851b68c593db04b48e24a7b583 Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Tue, 29 Sep 2026 18:35:58 +0200 Subject: [PATCH 38/50] codex-setup-temporary-github-auth: guide first-run CLI and web configuration with evidence and recovery --- build/cli/index.js | 2617 ++++++++++++++++- build/github_action/index.js | 164 +- build/web/assets/index-7TY0kYbf.css | 1 + build/web/assets/index-CJHJzy9C.js | 3 + build/web/assets/index-CYUoGCTl.js | 2 - build/web/assets/index-lEwj5VdR.css | 1 - build/web/index.html | 4 +- docs/authentication.mdx | 30 +- docs/configuration-checklist.mdx | 2 +- docs/how-to-use.mdx | 97 +- docs/issues/assignees-and-projects.mdx | 27 +- docs/pull-requests/guarded-approval.mdx | 18 + docs/single-actions/workflow-and-cli.mdx | 3 +- scripts/render-web-setup-component.cjs | 8 +- specs/CATALOG.md | 10 +- specs/catalog.json | 41 +- ...-pull-request-approval-setup-and-doctor.md | 32 + specs/local-web-setup-assistant.md | 646 +++- ...up-configuration-credentials-and-doctor.md | 19 +- src/__tests__/cli.test.ts | 12 +- src/__tests__/cli_context_branch.test.ts | 23 +- src/application/contracts/web_setup_view.ts | 92 +- .../setup_configuration_policy.test.ts | 2 +- .../setup_pat_creation_url_policy.test.ts | 9 +- .../__tests__/setup_pat_intent_policy.test.ts | 6 +- .../setup_project_selection_policy.test.ts | 37 + ...etup_question_documentation_policy.test.ts | 49 + .../setup_question_purpose_policy.test.ts | 93 + .../setup_questionnaire_policy.test.ts | 202 +- .../setup_token_permission_policy.test.ts | 11 +- .../policies/setup_configuration_plan.ts | 2 +- .../setup_configuration_validation.ts | 12 + .../policies/setup_pat_creation_url_policy.ts | 3 +- .../policies/setup_pat_intent_policy.ts | 10 +- .../setup_project_selection_policy.ts | 57 + .../setup_question_documentation_policy.ts | 70 + .../policies/setup_question_guidance_fr.ts | 79 + .../setup_question_guidance_policy.ts | 139 + .../policies/setup_question_guidance_pt.ts | 79 + .../policies/setup_question_labels/fr.ts | 86 + .../policies/setup_question_labels/pt.ts | 86 + .../policies/setup_question_labels_fr_pt.ts | 36 + .../policies/setup_question_purpose/fr.ts | 51 + .../policies/setup_question_purpose/pt.ts | 51 + .../policies/setup_question_purpose_fr_pt.ts | 51 + .../policies/setup_question_purpose_policy.ts | 104 + .../policies/setup_question_translations.ts | 105 + .../policies/setup_questionnaire_policy.ts | 271 +- .../policies/setup_token_permission_policy.ts | 25 +- .../setup_approval_check_discovery_port.ts | 5 + .../ports/setup_project_discovery_port.ts | 6 + src/application/ports/setup_terminal_ports.ts | 14 +- .../__tests__/initial_setup_use_case.test.ts | 27 +- .../actions/initial_setup_workflow.ts | 54 +- .../setup/__tests__/doctor_use_case.test.ts | 11 + .../prepare_setup_pat_intent_use_case.test.ts | 14 + .../setup_questionnaire_controller.test.ts | 36 +- .../__tests__/setup_wizard_use_case.test.ts | 118 +- .../usecases/setup/doctor_use_case.ts | 4 +- .../prepare_setup_pat_intent_use_case.ts | 16 +- .../setup/setup_questionnaire_controller.ts | 56 +- .../usecases/setup/setup_wizard_use_case.ts | 113 +- src/cli/__tests__/setup_presenters.test.ts | 50 + .../__tests__/setup_result_receipt.test.ts | 62 + .../__tests__/setup_terminal_driver.test.ts | 9 + src/cli/__tests__/web_setup_adapters.test.ts | 2 +- src/cli/__tests__/web_setup_bridge.test.ts | 109 + .../web_setup_browser_session.test.ts | 161 + src/cli/__tests__/web_setup_catalog.test.ts | 270 ++ .../__tests__/web_setup_components.test.ts | 281 +- src/cli/__tests__/web_setup_server.test.ts | 37 + .../__tests__/web_setup_ui_helpers.test.ts | 102 +- src/cli/commands/doctor.ts | 4 +- src/cli/commands/setup.ts | 60 +- src/cli/setup_confirmation_adapter.ts | 36 +- src/cli/setup_credential_prompt_adapter.ts | 42 +- src/cli/setup_plan_presenter.ts | 4 + src/cli/setup_question_renderer.ts | 94 +- src/cli/setup_result_receipt.ts | 55 + src/cli/setup_terminal_driver.ts | 8 + src/cli/web_setup_adapters.ts | 130 +- src/cli/web_setup_bridge.ts | 136 +- src/cli/web_setup_server.ts | 48 + src/cli_context.ts | 14 +- .../repository_variables_repository.ts | 3 + .../__tests__/pull_request_approval.test.ts | 7 + src/domain/pull_request_approval_policy.ts | 3 + src/domain/setup.ts | 11 + src/domain/setup_questionnaire.ts | 60 +- ...p_approval_check_discovery_adapter.test.ts | 85 + ...ub_setup_project_discovery_adapter.test.ts | 63 + .../github_repository_variables_protocol.ts | 1 + ..._setup_approval_check_discovery_adapter.ts | 94 + .../github_setup_project_discovery_adapter.ts | 76 + web/src/App.svelte | 47 +- web/src/components/ChoicePrompt.svelte | 4 +- web/src/components/ContextPanel.svelte | 16 +- .../components/CoverageCheckEvidence.svelte | 16 + web/src/components/CredentialPrompt.svelte | 12 +- web/src/components/DiscoveryNotice.svelte | 23 + web/src/components/LanguageSwitch.svelte | 15 + web/src/components/PairingPanel.svelte | 18 +- web/src/components/PlanDecisionSummary.svelte | 46 + web/src/components/PlanPrompt.svelte | 38 +- web/src/components/ProducerSelector.svelte | 51 + web/src/components/ProjectSelector.svelte | 32 + web/src/components/PromptCard.svelte | 16 +- web/src/components/QuestionGuidance.svelte | 18 + web/src/components/QuestionPrompt.svelte | 81 +- web/src/components/ResultPanel.svelte | 56 +- web/src/components/SetupHeader.svelte | 7 +- web/src/components/SetupIntro.svelte | 19 +- web/src/components/SetupSidebar.svelte | 14 +- web/src/components/StatusBanner.svelte | 4 +- web/src/components/ThemeSwitch.svelte | 10 +- web/src/components/WaitingPanel.svelte | 7 +- web/src/i18n/agentRoleNames.ts | 12 + web/src/i18n/catalog.ts | 28 + web/src/i18n/checkEvidence.ts | 12 + web/src/i18n/en.ts | 101 + web/src/i18n/errors/en.ts | 38 + web/src/i18n/errors/es.ts | 40 + web/src/i18n/errors/fr.ts | 40 + web/src/i18n/errors/pt.ts | 40 + web/src/i18n/es.ts | 103 + web/src/i18n/featureNames.ts | 21 + web/src/i18n/fr.ts | 103 + web/src/i18n/localeStore.ts | 4 + web/src/i18n/messageCopy.ts | 34 + web/src/i18n/messages/en.ts | 36 + web/src/i18n/messages/es.ts | 36 + web/src/i18n/messages/fr.ts | 36 + web/src/i18n/messages/pt.ts | 36 + web/src/i18n/options/es.ts | 16 + web/src/i18n/options/fr.ts | 18 + web/src/i18n/options/pt.ts | 18 + web/src/i18n/permissionCopy.ts | 17 + web/src/i18n/permissionTerms.ts | 31 + web/src/i18n/permissionTerms/en.ts | 7 + web/src/i18n/permissionTerms/es.ts | 7 + web/src/i18n/permissionTerms/fr.ts | 7 + web/src/i18n/permissionTerms/pt.ts | 7 + web/src/i18n/permissions/en.ts | 53 + web/src/i18n/permissions/es.ts | 52 + web/src/i18n/permissions/fr.ts | 52 + web/src/i18n/permissions/pt.ts | 52 + web/src/i18n/planWarningCopy.ts | 19 + web/src/i18n/planWarnings/en.ts | 14 + web/src/i18n/planWarnings/es.ts | 16 + web/src/i18n/planWarnings/fr.ts | 16 + web/src/i18n/planWarnings/pt.ts | 16 + web/src/i18n/promptCopy.ts | 35 + web/src/i18n/prompts/en.ts | 24 + web/src/i18n/prompts/es.ts | 23 + web/src/i18n/prompts/fr.ts | 23 + web/src/i18n/prompts/pt.ts | 23 + web/src/i18n/pt.ts | 103 + web/src/i18n/questionOptions.ts | 33 + web/src/i18n/sessionErrors.ts | 20 + web/src/lib/focusOnRevision.ts | 15 + web/src/lib/githubLink.ts | 35 + web/src/lib/helpLink.ts | 11 + web/src/lib/questionAnswer.ts | 11 +- web/src/session/setupSession.ts | 50 +- web/src/styles/controls.css | 32 +- web/src/styles/feedback.css | 9 +- web/src/styles/foundation.css | 1 + web/src/styles/layout.css | 10 +- web/src/styles/responsive.css | 2 + 169 files changed, 9816 insertions(+), 501 deletions(-) create mode 100644 build/web/assets/index-7TY0kYbf.css create mode 100644 build/web/assets/index-CJHJzy9C.js delete mode 100644 build/web/assets/index-CYUoGCTl.js delete mode 100644 build/web/assets/index-lEwj5VdR.css create mode 100644 src/application/policies/__tests__/setup_project_selection_policy.test.ts create mode 100644 src/application/policies/__tests__/setup_question_documentation_policy.test.ts create mode 100644 src/application/policies/__tests__/setup_question_purpose_policy.test.ts create mode 100644 src/application/policies/setup_project_selection_policy.ts create mode 100644 src/application/policies/setup_question_documentation_policy.ts create mode 100644 src/application/policies/setup_question_guidance_fr.ts create mode 100644 src/application/policies/setup_question_guidance_policy.ts create mode 100644 src/application/policies/setup_question_guidance_pt.ts create mode 100644 src/application/policies/setup_question_labels/fr.ts create mode 100644 src/application/policies/setup_question_labels/pt.ts create mode 100644 src/application/policies/setup_question_labels_fr_pt.ts create mode 100644 src/application/policies/setup_question_purpose/fr.ts create mode 100644 src/application/policies/setup_question_purpose/pt.ts create mode 100644 src/application/policies/setup_question_purpose_fr_pt.ts create mode 100644 src/application/policies/setup_question_purpose_policy.ts create mode 100644 src/application/policies/setup_question_translations.ts create mode 100644 src/application/ports/setup_approval_check_discovery_port.ts create mode 100644 src/application/ports/setup_project_discovery_port.ts create mode 100644 src/cli/__tests__/setup_result_receipt.test.ts create mode 100644 src/cli/__tests__/web_setup_catalog.test.ts create mode 100644 src/cli/setup_result_receipt.ts create mode 100644 src/infrastructure/__tests__/github_setup_approval_check_discovery_adapter.test.ts create mode 100644 src/infrastructure/__tests__/github_setup_project_discovery_adapter.test.ts create mode 100644 src/infrastructure/github_setup_approval_check_discovery_adapter.ts create mode 100644 src/infrastructure/github_setup_project_discovery_adapter.ts create mode 100644 web/src/components/CoverageCheckEvidence.svelte create mode 100644 web/src/components/DiscoveryNotice.svelte create mode 100644 web/src/components/LanguageSwitch.svelte create mode 100644 web/src/components/PlanDecisionSummary.svelte create mode 100644 web/src/components/ProducerSelector.svelte create mode 100644 web/src/components/ProjectSelector.svelte create mode 100644 web/src/components/QuestionGuidance.svelte create mode 100644 web/src/i18n/agentRoleNames.ts create mode 100644 web/src/i18n/catalog.ts create mode 100644 web/src/i18n/checkEvidence.ts create mode 100644 web/src/i18n/en.ts create mode 100644 web/src/i18n/errors/en.ts create mode 100644 web/src/i18n/errors/es.ts create mode 100644 web/src/i18n/errors/fr.ts create mode 100644 web/src/i18n/errors/pt.ts create mode 100644 web/src/i18n/es.ts create mode 100644 web/src/i18n/featureNames.ts create mode 100644 web/src/i18n/fr.ts create mode 100644 web/src/i18n/localeStore.ts create mode 100644 web/src/i18n/messageCopy.ts create mode 100644 web/src/i18n/messages/en.ts create mode 100644 web/src/i18n/messages/es.ts create mode 100644 web/src/i18n/messages/fr.ts create mode 100644 web/src/i18n/messages/pt.ts create mode 100644 web/src/i18n/options/es.ts create mode 100644 web/src/i18n/options/fr.ts create mode 100644 web/src/i18n/options/pt.ts create mode 100644 web/src/i18n/permissionCopy.ts create mode 100644 web/src/i18n/permissionTerms.ts create mode 100644 web/src/i18n/permissionTerms/en.ts create mode 100644 web/src/i18n/permissionTerms/es.ts create mode 100644 web/src/i18n/permissionTerms/fr.ts create mode 100644 web/src/i18n/permissionTerms/pt.ts create mode 100644 web/src/i18n/permissions/en.ts create mode 100644 web/src/i18n/permissions/es.ts create mode 100644 web/src/i18n/permissions/fr.ts create mode 100644 web/src/i18n/permissions/pt.ts create mode 100644 web/src/i18n/planWarningCopy.ts create mode 100644 web/src/i18n/planWarnings/en.ts create mode 100644 web/src/i18n/planWarnings/es.ts create mode 100644 web/src/i18n/planWarnings/fr.ts create mode 100644 web/src/i18n/planWarnings/pt.ts create mode 100644 web/src/i18n/promptCopy.ts create mode 100644 web/src/i18n/prompts/en.ts create mode 100644 web/src/i18n/prompts/es.ts create mode 100644 web/src/i18n/prompts/fr.ts create mode 100644 web/src/i18n/prompts/pt.ts create mode 100644 web/src/i18n/pt.ts create mode 100644 web/src/i18n/questionOptions.ts create mode 100644 web/src/i18n/sessionErrors.ts create mode 100644 web/src/lib/focusOnRevision.ts create mode 100644 web/src/lib/helpLink.ts diff --git a/build/cli/index.js b/build/cli/index.js index 730a85034..e189d5af0 100755 --- a/build/cli/index.js +++ b/build/cli/index.js @@ -46657,7 +46657,7 @@ function buildSetupWarnings(configuration) { warnings.push('Inactive issue closure is enabled; waiting issues are closed after the configured inactivity threshold and can be reopened with a new comment.'); } if (configuration.projects.ids.trim()) { - warnings.push('Project IDs must be accessible to the PAT and use the expected project column names.'); + warnings.push('Selected Project numbers must be accessible to the bot PAT, and all four configured Status values must exist in every selected Project.'); } if ((0, setup_configuration_defaults_1.setupAgentTasksForFeatures)(configuration).some(task => configuration.agents[task].provider === 'cursor')) { warnings.push('Cursor is an experimental runtime in Copilot and requires a compatible preinstalled CLI plus CURSOR_API_KEY; Copilot has no automatic Cursor installer.'); @@ -46951,8 +46951,20 @@ const locale_1 = __nccwpck_require__(15386); const issue_workflow_profile_1 = __nccwpck_require__(26744); const setup_issue_workflow_policy_1 = __nccwpck_require__(81182); const pull_request_approval_policy_1 = __nccwpck_require__(98820); +const setup_project_selection_policy_1 = __nccwpck_require__(73750); function validateSetupConfiguration(configuration, options = {}) { const errors = []; + const projectSelection = (0, setup_project_selection_policy_1.parseSetupProjectSelection)(configuration.projects.ids); + if ('error' in projectSelection || projectSelection.value !== configuration.projects.ids) { + errors.push('Project IDs must be a comma-separated list of 1–10 distinct positive Project URL numbers; PVT_ node IDs are not accepted.'); + } + if (configuration.projects.ids) { + for (const [name, value] of Object.entries(configuration.projects).filter(([name]) => name.endsWith('Column'))) { + if (typeof value !== 'string' || !value.trim() || value.length > 100 || /[\p{Cc}\p{Cf}]/u.test(value)) { + errors.push(`Project ${name} must name one existing single-line Status option (1–100 characters).`); + } + } + } errors.push(...(0, pull_request_approval_policy_1.validatePullRequestApprovalPolicy)(configuration.pullRequestApproval, options.allowIncompleteApproval === true)); if (configuration.actionInputs['pr-approval-policy'] !== undefined) { errors.push('pr-approval-policy cannot be overridden through actionInputs.'); @@ -47854,6 +47866,7 @@ const QUERY_PERMISSIONS = { Issues: 'issues', Actions: 'actions', Administration: 'administration', + Checks: 'checks', Workflows: 'workflows', 'Pull requests': 'pull_requests', }, @@ -47874,7 +47887,7 @@ class UnsupportedSetupPatLinkError extends Error { } } exports.UnsupportedSetupPatLinkError = UnsupportedSetupPatLinkError; -/** Builds only documented GitHub form fields; never accepts credential material. */ +/** Builds known GitHub form fields; Checks is accepted by the form but omitted from the published URL table. Never accepts credential material. */ function buildSetupPatCreationUrl(input) { if (!/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/.test(input.owner) || !/^[A-Za-z0-9._-]{1,100}$/.test(input.repository) @@ -47938,7 +47951,7 @@ function fixedSetupPatIntentQuestionIds(overrides, skipVariables, skipSecrets) { if (overrides.pullRequestApproval?.mode !== undefined) fixed.push('pullRequestApproval.mode'); if (overrides.projects?.ids !== undefined) - fixed.push('projects.ids'); + fixed.push('projects.enabled', 'projects.ids'); if (overrides.createInitialTag !== undefined) fixed.push('createInitialTag'); if (skipVariables || overrides.manageRepositoryVariables !== undefined) @@ -47953,18 +47966,18 @@ function fixedSetupPatIntentQuestionIds(overrides, skipVariables, skipSecrets) { } return fixed; } -function setupPatIntentNeedsOwnerKind(configuration) { - return (0, setup_token_permission_policy_1.buildSetupPatIntentPermissionRequirements)(configuration, 'Organization') +function setupPatIntentNeedsOwnerKind(configuration, projectsWanted = configuration.projects.ids.trim().length > 0) { + return (0, setup_token_permission_policy_1.buildSetupPatIntentPermissionRequirements)(configuration, 'Organization', projectsWanted) .some(requirement => requirement.scope === 'organization') || (configuration.manageRepositorySecrets && configuration.storage.secrets.preserveExisting) || (configuration.manageRepositoryVariables && configuration.storage.variables.preserveExisting); } -function setupPatIntentOwnerConflict(configuration, ownerKind) { +function setupPatIntentOwnerConflict(configuration, ownerKind, projectsWanted = configuration.projects.ids.trim().length > 0) { return ownerKind === 'User' && ((configuration.manageRepositorySecrets && (configuration.storage.secrets.defaultScope === 'organization' || Object.values(configuration.storage.secrets.overrides).includes('organization'))) || (configuration.manageRepositoryVariables && (configuration.storage.variables.defaultScope === 'organization' || Object.values(configuration.storage.variables.overrides).includes('organization'))) - || configuration.projects.ids.trim().length > 0); + || projectsWanted); } @@ -47987,6 +48000,1142 @@ function summarizeSetupPermissions(requirements) { } +/***/ }), + +/***/ 73750: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.parseSetupProjectSelection = parseSetupProjectSelection; +exports.sharedProjectStatusOptions = sharedProjectStatusOptions; +exports.validateDiscoveredProjectStatuses = validateDiscoveredProjectStatuses; +function parseSetupProjectSelection(raw, owner) { + const input = raw.normalize('NFKC').trim(); + if (!input || input.toLowerCase() === 'none') + return { value: '' }; + const parts = input.split(',').map(part => part.trim()); + if (parts.length > 10 || parts.some(part => !part)) + return { error: 'Choose at most 10 Projects; separate numbers or URLs with commas.' }; + const numbers = []; + for (const part of parts) { + let numberText = part; + if (part.startsWith('https://')) { + if (!owner) + return { error: 'A Project URL needs a known repository owner; enter its positive number instead.' }; + try { + const url = new URL(part); + const match = url.pathname.match(/^\/(?:orgs|users)\/([^/]+)\/projects\/([1-9]\d*)\/?$/u); + if (url.origin !== 'https://github.com' || url.search || url.hash || url.username || url.password + || !match || decodeURIComponent(match[1]).toLowerCase() !== owner.toLowerCase()) { + return { error: `Use a GitHub Project URL belonging to ${owner}, without query parameters.` }; + } + numberText = match[2]; + } + catch { + return { error: 'Enter a valid GitHub Project URL or positive Project number.' }; + } + } + if (!/^[1-9]\d*$/u.test(numberText)) + return { error: 'Enter the positive Project number from its GitHub URL, not a PVT_ GraphQL ID.' }; + const number = Number(numberText); + if (!Number.isSafeInteger(number) || number > 2147483647) + return { error: 'Project numbers must be positive integers at most 2147483647.' }; + if (numbers.includes(number)) + return { error: `Project ${number} was selected more than once.` }; + numbers.push(number); + } + return { value: numbers.join(',') }; +} +function sharedProjectStatusOptions(projectNumbers, projects) { + const numbers = projectNumbers.split(',').map(Number).filter(Boolean); + if (!numbers.length) + return { state: 'unavailable', options: [] }; + const selected = numbers.map(number => projects.find(project => project.number === number)); + if (selected.some(project => !project?.statusOptions?.length)) + return { state: 'unavailable', options: [] }; + const [first, ...rest] = selected; + const common = first.statusOptions.filter(option => rest.every(project => project.statusOptions.includes(option))); + return common.length ? { state: 'observed', options: common } : { state: 'incompatible', options: [] }; +} +/** A discovered mismatch is unsafe even if values arrived through --config rather than the interactive selector. */ +function validateDiscoveredProjectStatuses(configuration, discovery) { + if (!configuration.projects.ids || !discovery || discovery.status !== 'observed') + return []; + const common = sharedProjectStatusOptions(configuration.projects.ids, discovery.candidates); + if (common.state === 'incompatible') + return ['Selected Projects have no common Status option. Choose compatible Projects.']; + if (common.state !== 'observed') + return []; + const names = [configuration.projects.issueCreatedColumn, configuration.projects.pullRequestCreatedColumn, + configuration.projects.issueInProgressColumn, configuration.projects.pullRequestInProgressColumn]; + return names.filter(name => !common.options.includes(name)).map(name => `Status value "${name}" is not available in every selected Project.`); +} + + +/***/ }), + +/***/ 75280: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.setupQuestionDocumentation = setupQuestionDocumentation; +const docs = { + features: { title: 'Copilot features and workflow triggers', url: 'https://docs.page/vypdev/copilot/features' }, + issueWorkflows: { title: 'Copilot issue workflow setup', url: 'https://docs.page/vypdev/copilot/issues/workflow-setup' }, + branchManagement: { title: 'Issue branch management', url: 'https://docs.page/vypdev/copilot/issues/branch-management' }, + preBranchSdd: { title: 'Pre-branch design documents', url: 'https://docs.page/vypdev/copilot/issues/pre-branch-sdds' }, + issueLifecycle: { title: 'Issue notifications and automatic closure', url: 'https://docs.page/vypdev/copilot/issues/notifications-and-auto-close' }, + assignments: { title: 'Assignees and GitHub Projects', url: 'https://docs.page/vypdev/copilot/issues/assignees-and-projects' }, + pullRequestWorkflows: { title: 'Pull-request workflow setup', url: 'https://docs.page/vypdev/copilot/pull-requests/workflow-setup' }, + pullRequestDescription: { title: 'AI pull-request descriptions', url: 'https://docs.page/vypdev/copilot/pull-requests/ai-description' }, + repositoryGuidance: { title: 'Repository guidance for agents', url: 'https://docs.page/vypdev/copilot/agents/repository-collaboration' }, + runtime: { title: 'Agent runtime selection', url: 'https://docs.page/vypdev/copilot/agents/runtime-selection' }, + model: { title: 'Agent model selection', url: 'https://docs.page/vypdev/copilot/agents/model-selection' }, + command: { title: 'Agent CLI configuration', url: 'https://docs.page/vypdev/copilot/agents/cli-configuration' }, + repository: { title: 'Copilot repository configuration', url: 'https://docs.page/vypdev/copilot/configuration' }, + deployment: { title: 'Release and hotfix orchestration', url: 'https://docs.page/vypdev/copilot/issues/deployment-orchestration' }, + bugbot: { title: 'Bugbot configuration', url: 'https://docs.page/vypdev/copilot/bugbot/configuration' }, + bugbotVerification: { title: 'Bugbot autofix verification commands', url: 'https://docs.page/vypdev/copilot/bugbot/verification-commands' }, + approval: { title: 'Guarded pull-request approval', url: 'https://docs.page/vypdev/copilot/pull-requests/guarded-approval' }, + githubStatusChecks: { title: 'GitHub: status checks and required checks', url: 'https://docs.github.com/en/pull-requests/reference/status-checks' }, + projects: { title: 'Assignees and GitHub Projects', url: 'https://docs.page/vypdev/copilot/issues/assignees-and-projects' }, + githubProjects: { title: 'GitHub: About Projects', url: 'https://docs.github.com/en/issues/planning-and-tracking-with-projects/learning-about-projects/about-projects' }, + githubStatus: { title: 'GitHub: About single-select fields', url: 'https://docs.github.com/en/issues/planning-and-tracking-with-projects/understanding-fields/about-single-select-fields' }, + provisioning: { title: 'Copilot setup and provisioning', url: 'https://docs.page/vypdev/copilot/how-to-use' }, + storage: { title: 'GitHub Actions Secrets and Variables', url: 'https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/use-secrets' }, +}; +/** Links are selected from source-controlled constants, never derived from answers or remote text. */ +function setupQuestionDocumentation(question) { + const id = question.id; + if (id === 'issueWorkflows.enabled') + return docs.issueWorkflows; + if (id === 'features.issues') + return docs.issueWorkflows; + if (id === 'features.pullRequests') + return docs.pullRequestWorkflows; + if (id === 'repository.issueManagedBranches' || /^repository\.(feature|bugfix|hotfix|release|docs|chore)Tree$/u.test(id)) + return docs.branchManagement; + if (id === 'repository.preBranchSdd') + return docs.preBranchSdd; + if (id === 'repository.inactivityThresholdHours' || id === 'features.inactiveIssueClosure') + return docs.issueLifecycle; + if (id === 'repository.desiredAssigneesCount' || id === 'repository.desiredReviewersCount') + return docs.assignments; + if (id === 'ai.pullRequestDescriptionMode') + return docs.pullRequestDescription; + if (id === 'ai.bugbotFixVerifyCommands') + return docs.bugbotVerification; + if (id === 'projects.ids') + return docs.githubProjects; + if (id === 'pullRequestApproval.testChecks') + return docs.githubStatusChecks; + if (/^projects\.(issue|pullRequest)(Created|InProgress)Column$/u.test(id) || id === 'projects.statusVerified') + return docs.githubStatus; + if (id.startsWith('repositoryAgentGuidance.')) + return docs.repositoryGuidance; + if (id.startsWith('agents.')) { + if (id.endsWith('.provider')) + return docs.runtime; + if (id.endsWith('.executable')) + return docs.command; + return docs.model; + } + if (id === 'ai.provisioningMode') + return docs.command; + const byState = { + capabilities: docs.features, + 'agent-runtime': docs.runtime, + 'agent-model-defaults': docs.model, + 'agent-role-overrides': docs.model, + repository: docs.repository, + deployment: docs.deployment, + bugbot: docs.bugbot, + 'pull-request-approval': docs.approval, + projects: docs.projects, + provisioning: docs.provisioning, + storage: docs.storage, + }; + return byState[question.stateId] ?? docs.provisioning; +} + + +/***/ }), + +/***/ 49513: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.frenchQuestionExplanation = frenchQuestionExplanation; +const setup_question_labels_fr_pt_1 = __nccwpck_require__(28247); +const setup_question_purpose_fr_pt_1 = __nccwpck_require__(98807); +const where = { + capabilities: 'La configuration écrit les workflows choisis dans ce dépôt et ne demande que les autorisations GitHub nécessaires.', + 'agent-runtime': 'Les workflows GitHub Actions générés lancent cet agent sur leur runner ; rien n’est installé sur cet ordinateur.', + 'agent-model-defaults': 'Le modèle et la commande communs sont enregistrés dans la configuration du dépôt lue par les workflows.', + 'agent-role-overrides': 'Cette exception propre à une tâche est enregistrée dans le dépôt et lue uniquement lorsque cette tâche s’exécute.', + repository: 'Le profil du dépôt et les workflows générés utilisent cette valeur pour les futurs événements de branches, tickets et pull requests.', + deployment: 'Le profil du dépôt commande les futurs workflows de version et de correctif urgent ; répondre ne publie rien.', + bugbot: 'Le workflow généré lit ce réglage dans la configuration du dépôt ou les Variables GitHub Actions sélectionnées.', + 'pull-request-approval': 'L’approbation encadrée utilise les identités exactes des producteurs CI et les preuves des exécutions GitHub.', + projects: 'Les Projects choisis et leurs valeurs du champ Status seront utilisés par l’automatisation future des tickets et pull requests.', + provisioning: 'Après la confirmation finale, la configuration peut créer ou mettre à jour les fichiers et ressources GitHub Actions choisis.', + storage: 'GitHub Actions stocke ces ressources au niveau du dépôt ou de l’organisation ; ce choix change leur visibilité et les autorisations du PAT.', +}; +const section = { + capabilities: { summary: 'Choisissez les automatisations que Copilot installera.', when: 'Ce choix influence les workflows, les autorisations GitHub et les questions suivantes.', example: 'Désactivez une fonction que vous ne prévoyez pas d’utiliser.', effect: 'Seules les fonctions sélectionnées figureront dans le plan.', verify: 'Examinez le plan avant d’appliquer les changements.' }, + 'agent-runtime': { summary: 'Choisissez l’agent CLI pour cette tâche.', when: 'Il sera utilisé lorsque la fonction sélectionnée s’exécutera dans GitHub Actions.', example: 'Codex est lancé avec la commande codex.', effect: 'L’Action lance le fournisseur choisi, sans solution de remplacement implicite.', verify: 'Vérifiez que le runner dispose du CLI et des identifiants nécessaires.' }, + 'agent-model-defaults': { summary: 'Définissez les modèles utilisés par défaut pour les tâches de l’agent.', when: 'Ils s’appliquent sauf si vous configurez chaque tâche séparément.', example: 'Gardez le modèle proposé si vous n’avez pas de besoin particulier.', effect: 'L’Action transmet ces valeurs au CLI sélectionné.', verify: 'Examinez le plan et les modèles autorisés sur le runner.' }, + 'agent-role-overrides': { summary: 'Personnalisez cette tâche de l’agent.', when: 'Uniquement si vous avez activé la configuration indépendante des tâches.', example: 'Utilisez un modèle différent pour la revue et la planification.', effect: 'Seule cette tâche utilise cette exception.', verify: 'Examinez les valeurs de chaque tâche dans le plan.' }, + repository: { summary: 'Définissez comment Copilot traite votre dépôt.', when: 'Ce réglage agit sur les workflows et futurs événements de tickets ou pull requests.', example: 'Indiquez le véritable nom de votre branche de développement.', effect: 'L’automatisation future suivra les branches et règles choisies.', verify: 'Examinez les fichiers prévus et le profil du dépôt.' }, + deployment: { summary: 'Définissez le comportement des versions et correctifs urgents.', when: 'Ce réglage n’importe que si ces workflows sont activés.', example: 'Gardez la stratégie par défaut sauf si votre organisation des branches diffère.', effect: 'Il modifie la gestion des branches et pull requests de réconciliation.', verify: 'Examinez la partie versions et correctifs du plan.' }, + bugbot: { summary: 'Définissez comment Bugbot analyse et signale les changements.', when: 'Ce réglage sert lorsque les fonctions de revue IA s’exécutent.', example: 'Par défaut, les résultats admissibles sont publiés sans bloquer toutes les pull requests.', effect: 'Il change les futures publications et diagnostics de revue.', verify: 'Examinez les Variables Bugbot du plan et les résultats de revue.' }, + 'pull-request-approval': { summary: 'Choisissez les preuves exigées avant que le bot recommande ou soumette une approbation.', when: 'Ce réglage ne s’applique que si l’automatisation des pull requests est activée.', example: '« Recommend » informe une personne ; « guarded » peut approuver sur GitHub.', effect: 'Une vérification verte affichée ici ne suffit jamais à approuver une pull request.', verify: 'Inspectez les preuves CI, Bugbot et les règles de branche.' }, + projects: { summary: 'Choisissez une valeur Status existante pour une transition de ticket ou PR.', when: 'Seulement si vous intégrez des Projects.', example: 'Todo à la création ; In Progress au début du travail.', effect: 'L’automatisation modifiera le champ Status, pas une colonne visuelle.', verify: 'Vérifiez les options Status de chaque Project choisi.' }, + provisioning: { summary: 'Choisissez les ressources GitHub Actions gérées par la configuration.', when: 'Cela influence les autorisations du PAT et les écritures prévues.', example: 'Gardez les Secrets activés si le PAT du bot doit être installé.', effect: 'Les ressources sélectionnées pourront être créées ou mises à jour après approbation.', verify: 'Examinez les noms exacts des ressources dans le plan.' }, + storage: { summary: 'Choisissez où résident les Variables et Secrets GitHub Actions.', when: 'Ce réglage s’applique quand leur création est activée.', example: 'Le dépôt est le périmètre par défaut le plus simple.', effect: 'Il change la visibilité, les autorisations et l’ordre de priorité.', verify: 'Examinez le périmètre et les avertissements de masquage dans le plan.' }, +}; +const special = { + 'agents.findings.executable': { summary: 'Choisissez la commande de l’agent sur le runner GitHub Actions, pas sur cet ordinateur.', when: 'Ne la changez que si un agent personnalisé est délibérément installé sur le runner.', example: 'Laissez vide pour codex, opencode ou agent selon le fournisseur.', effect: 'Le chemin personnalisé est utilisé pour les tâches choisies et n’est jamais installé automatiquement.', verify: 'Vérifiez que le runner possède exactement cet exécutable avant d’activer le workflow.' }, + 'ai.includeReasoning': { summary: 'Demandez des explications supplémentaires si la réponse du fournisseur les contient.', when: 'Réservé aux diagnostics avancés ; le parcours CLI actuel ne fournit pas de parties de raisonnement séparées.', example: 'Laissez désactivé pour une configuration normale.', effect: 'Cela peut ajouter du texte du fournisseur, sans garantir des métadonnées brèves.', verify: 'Inspectez une réponse structurée contrôlée ; ne supposez pas que l’option a produit plus de texte.' }, + 'ai.bugbotDryRun': { summary: 'Gardez Bugbot en mode analyse seule pour ses futures exécutions.', when: 'Utile pour une évaluation ; incompatible avec les preuves nécessaires à l’approbation.', example: 'Choisissez Non pour publier les revues normales.', effect: 'Bugbot analyse sans publier de résultat ni modifier le dépôt. Ce n’est pas setup --dry-run.', verify: 'Inspectez le résultat du workflow Bugbot : le mode analyse seule ne publie ni revue ni vérification.' }, + 'ai.bugbotOrganizationRules': { summary: 'Définissez des consignes générales pour Bugbot, une règle par ligne.', when: 'Utile si l’équipe partage des critères de revue dans le dépôt configuré.', example: 'Signaler les changements qui contournent l’isolation des clients.', effect: 'Ces règles précèdent celles du dépôt ; le périmètre de la Variable détermine le stockage.', verify: 'Inspectez la Variable configurée et activez le traçage des sources de règles.' }, + 'ai.provisioningMode': { summary: 'Décidez comment l’Action trouve ou installe l’agent CLI.', when: 'Ce choix s’applique sur le runner au démarrage d’une tâche IA activée.', example: 'Auto réutilise un CLI installé ou installe une version fixée de Codex/OpenCode.', effect: 'Always réinstalle les versions examinées ; Disabled exige un CLI préinstallé. Cursor doit être préinstallé.', verify: 'Inspectez l’étape de préparation et la version du binaire rapportée par le runner.' }, + 'pullRequestApproval.testChecks': { summary: 'Choisissez les jobs CI que le bot peut considérer comme preuve de tests indépendante.', when: 'Obligatoire pour les modes Recommend et Guarded.', example: 'Sélectionnez le job Tests exact, son ID d’App GitHub et son workflow dans une exécution récente.', effect: 'Seules les identités exactes listées satisfont la condition d’approbation.', verify: 'Ouvrez l’exécution liée et vérifiez le job, l’App et le résultat pour le commit courant.' }, + 'pullRequestApproval.producerAttested': { summary: 'Confirmez avoir inspecté le producteur CI exact et son étape obligatoire de couverture.', when: 'Obligatoire avant que le mode Guarded puisse approuver.', example: 'Vérifiez que le job Tests échoue si le seuil de couverture n’est pas atteint.', effect: 'Votre confirmation est enregistrée ; Copilot ne la déduit pas d’une vérification verte.', verify: 'Inspectez le fichier du workflow et une exécution réelle avant de répondre Oui.' }, + 'pullRequestApproval.coverage.mode': { summary: 'Choisissez comment prouver la couverture exigée du code modifié.', when: 'Ce choix s’applique lorsque l’approbation de PR est activée.', example: 'Check : le CI impose le seuil. Numeric : un workflow fiable publie des décomptes limités.', effect: 'Check fait confiance au garde CI ; Numeric lit copilot-diff-coverage-v1 et compare un seuil.', verify: 'Inspectez respectivement la condition d’échec du CI ou l’artefact du rapporteur.' }, + 'pullRequestApproval.coverage.checkName': { summary: 'Sélectionnez la vérification fiable qui échoue sous le seuil de couverture.', when: 'Obligatoire dans les deux modes de preuve.', example: 'Utilisez le même job Tests exact que dans l’étape précédente.', effect: 'Le succès d’une autre vérification ou App ne remplace pas ce garde.', verify: 'Vérifiez que l’étape de couverture est obligatoire, pas seulement informative.' }, + 'projects.enabled': { summary: 'Décidez si les futurs tickets et PR doivent utiliser des Projects GitHub existants.', when: 'Avant de créer le PAT de configuration pour prévoir le droit de lecture des Projects.', example: 'Oui si l’équipe utilise un Project de l’organisation ; Non pour ignorer cette intégration.', effect: 'Oui prévoit Projects: read de l’organisation si nécessaire. Aucun Project n’est modifié maintenant.', verify: 'Vérifiez les droits du PAT ; les Projects précis seront choisis après son autorisation.' }, + 'projects.ids': { summary: 'Choisissez les Projects existants que Copilot pourra actualiser plus tard.', when: 'Après la vérification du PAT ; si la liste est inaccessible, utilisez la saisie manuelle.', example: 'Pour https://github.com/orgs/acme/projects/5, choisissez la carte ou saisissez 5, jamais PVT_…', effect: 'Leurs numéros seront enregistrés ; aucun élément Project n’est modifié maintenant.', verify: 'Ouvrez chaque Project et vérifiez propriétaire et numéro avant de confirmer le plan.' }, +}; +function howToChoose(question) { + if (question.id === 'pullRequestApproval.coverage.checkName') + return 'Choisissez l’une des vérifications fiables ci-dessus. Ouvrez son exécution et son workflow : l’étape de couverture doit faire échouer le job si le seuil n’est pas atteint. Un résultat vert ne suffit pas.'; + if (question.id === 'pullRequestApproval.producerAttested') + return 'Répondez Oui uniquement après avoir vérifié chaque nom, ID d’App et workflow choisis, ainsi que l’étape de couverture obligatoire du check retenu. Sinon, répondez Non et restez en mode recommandation.'; + if (question.id === 'pullRequestApproval.coverage.artifactWorkflowName') + return 'Saisissez le nom exact d’un workflow fiable choisi qui publie copilot-diff-coverage-v1 pour cette PR et ses commits de base et de tête. Ne devinez pas le nom du workflow.'; + if (question.id === 'projects.statusVerified') + return 'Ouvrez chaque Project choisi sur GitHub, inspectez son champ Status et comparez les quatre valeurs exactes ci-dessus. Répondez Oui uniquement si toutes existent dans chaque Project ; Non revient au choix des Projects.'; + if (/^projects\.(issue|pullRequest)(Created|InProgress)Column$/u.test(question.id)) + return 'Choisissez une option du champ Status présente dans tous les Projects sélectionnés. Si les options ne sont pas lisibles, ouvrez chaque Project sur GitHub et saisissez la même valeur existante ; des valeurs différentes par Project ne sont pas prises en charge.'; + switch (question.kind) { + case 'boolean': return 'Choisissez Oui pour activer ou Non pour désactiver ; la réponse suggérée apparaît plus bas.'; + case 'producer-select': return 'Inspectez chaque exécution candidate sur GitHub, puis choisissez le job, l’ID d’App et le workflow exacts. Ne saisissez manuellement que si aucun candidat vérifié n’apparaît.'; + case 'project-select': return 'Choisissez par titre et URL. Saisissez le numéro positif ou l’URL GitHub exacte si un Project manque ; les ID PVT_ sont invalides.'; + case 'scope-overrides': return 'Sélectionnez uniquement les noms hérités à remplacer volontairement dans le dépôt. Laissez vide pour conserver les valeurs de l’organisation.'; + case 'multi-select': return 'Cochez les workflows que vous utiliserez. Vous pouvez en choisir plusieurs ; vérifiez leurs autorisations avant de créer un PAT.'; + case 'choice': return 'Choisissez une valeur après avoir lu ses conséquences ; la valeur enregistrée n’est pas traduite.'; + case 'number': return 'Saisissez un entier dans la plage indiquée ; gardez la valeur suggérée en cas de doute.'; + default: return 'Saisissez la valeur exacte utilisée par votre dépôt ou runner ; ne laissez vide que si la question le permet.'; + } +} +function frenchQuestionExplanation(question, documentation) { + const copy = special[question.id] ?? section[question.stateId]; + return { + label: (0, setup_question_labels_fr_pt_1.translatedQuestionLabel)(question, 'fr'), + ...copy, + summary: special[question.id] ? copy.summary : ((0, setup_question_purpose_fr_pt_1.setupQuestionPurposeFrPt)(question, 'fr') ?? copy.summary), + where: where[question.stateId], + how: howToChoose(question), + why: `Cette décision permet d’accorder le plan, les autorisations du PAT et l’automatisation future avant d’appliquer des changements. ${copy.when}`, + documentation: { title: 'Documentation de cette option', url: documentation.url }, + }; +} + + +/***/ }), + +/***/ 42775: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.setupQuestionPresentation = setupQuestionPresentation; +const setup_question_translations_1 = __nccwpck_require__(3927); +const setup_question_documentation_policy_1 = __nccwpck_require__(75280); +const setup_question_purpose_policy_1 = __nccwpck_require__(77947); +const setup_question_guidance_fr_1 = __nccwpck_require__(49513); +const setup_question_guidance_pt_1 = __nccwpck_require__(14440); +const location = { + capabilities: { en: 'Setup writes the selected workflow files into this repository and requests only the GitHub permissions those workflows need.', es: 'Setup escribe los workflows seleccionados en este repositorio y solicita solo los permisos de GitHub necesarios.' }, + 'agent-runtime': { en: 'The generated GitHub Actions workflows invoke this agent on their runner; this does not install an agent on your computer.', es: 'Los workflows de GitHub Actions invocan este agente en su runner; no se instala en tu ordenador.' }, + 'agent-model-defaults': { en: 'The shared model and command defaults are stored in the repository configuration consumed by the generated workflows.', es: 'Los valores comunes de modelo y comando se guardan en la configuración del repositorio que usan los workflows.' }, + 'agent-role-overrides': { en: 'This task-specific override is stored in the repository configuration and read only when that task runs.', es: 'Esta excepción por tarea se guarda en la configuración del repositorio y se lee cuando se ejecuta esa tarea.' }, + repository: { en: 'The repository profile and generated workflows use this value for future branch, issue and pull-request events.', es: 'El perfil del repositorio y los workflows generados usan este valor en futuros eventos de ramas, issues y pull requests.' }, + deployment: { en: 'The repository profile controls later release and hotfix workflows; nothing is released by answering this question.', es: 'El perfil del repositorio controla los futuros workflows de release y hotfix; responder no publica ninguna versión.' }, + bugbot: { en: 'The generated workflow reads this setting from repository configuration or selected GitHub Actions Variables when Bugbot runs.', es: 'El workflow lee este ajuste de la configuración o las Variables de GitHub Actions seleccionadas al ejecutar Bugbot.' }, + 'pull-request-approval': { en: 'The guarded-approval configuration uses exact CI producer identities and evidence from GitHub pull-request runs.', es: 'La aprobación protegida usa identidades exactas de los productores de CI y pruebas de las ejecuciones de PR en GitHub.' }, + projects: { en: 'Future issue and pull-request automation uses the selected GitHub Projects and their Status field values.', es: 'La automatización futura de issues y pull requests usa los GitHub Projects y los valores de su campo Status.' }, + provisioning: { en: 'After the final Apply confirmation, setup may create or update the selected files and GitHub Actions resources.', es: 'Tras confirmar Aplicar, setup podrá crear o actualizar los archivos y recursos de GitHub Actions elegidos.' }, + storage: { en: 'GitHub Actions stores these resources at repository or organization scope; the scope changes visibility and required PAT grants.', es: 'GitHub Actions guarda estos recursos en el repositorio o la organización; el ámbito cambia la visibilidad y los permisos del PAT.' }, +}; +const special = { + 'agents.findings.executable': { + en: { summary: 'Choose the agent command used on the GitHub Actions runner, not on this computer.', when: 'Only change this for a runner with a deliberately installed custom agent binary.', example: 'Leave empty for codex, opencode, or agent according to the provider.', effect: 'A custom path is shared unless a task has its own override; it is never installed automatically.', verify: 'Check the runner has this exact executable before enabling the workflow.' }, + es: { summary: 'Elige el comando del agente en el runner de GitHub Actions, no en este ordenador.', when: 'Cámbialo solo si el runner tiene instalado expresamente otro binario.', example: 'Déjalo vacío para usar codex, opencode o agent según el proveedor.', effect: 'La ruta personalizada se comparte salvo que una tarea tenga su propia excepción; nunca se instala automáticamente.', verify: 'Comprueba que el runner tiene exactamente ese ejecutable.' }, + }, + 'ai.includeReasoning': { + en: { summary: 'Ask for additional provider reasoning when the agent response exposes it.', when: 'Advanced diagnostics only; the current string-only CLI path does not provide separate reasoning parts.', example: 'Keep this off for normal setup.', effect: 'May add provider-produced explanation text, not guaranteed concise metadata.', verify: 'Inspect a controlled structured response; do not assume this toggle produced extra text.' }, + es: { summary: 'Solicita razonamiento adicional si la respuesta del proveedor lo ofrece.', when: 'Solo para diagnósticos avanzados; el CLI actual devuelve texto sin partes de razonamiento separadas.', example: 'Déjalo desactivado en una configuración normal.', effect: 'Podría añadir texto del proveedor; no garantiza metadatos breves.', verify: 'Comprueba una respuesta estructurada controlada; no presupongas que la opción tuvo efecto.' }, + }, + 'ai.bugbotDryRun': { + en: { summary: 'Keep Bugbot in analysis-only mode for future runs.', when: 'Useful during evaluation; incompatible with PR approval evidence.', example: 'Choose No to publish normal reviews.', effect: 'Bugbot analyzes but does not publish findings or make SCM changes. This is not setup --dry-run.', verify: 'Inspect the Bugbot workflow result; no published review or Check should appear from dry-run.' }, + es: { summary: 'Mantiene Bugbot en modo solo análisis para las futuras ejecuciones.', when: 'Útil durante una evaluación; incompatible con la evidencia de aprobación de PR.', example: 'Elige No para publicar revisiones normalmente.', effect: 'Bugbot analiza pero no publica hallazgos ni modifica el repositorio. No es setup --dry-run.', verify: 'Revisa el resultado de Bugbot; el modo ensayo no publica revisión ni Check.' }, + }, + 'ai.bugbotOrganizationRules': { + en: { summary: 'Set broad Bugbot review instructions, one rule per line.', when: 'Use when your team needs review criteria shared across its configured repository.', example: 'Flag changes that bypass tenant isolation.', effect: 'These rules run before repository rules; the selected Variable scope determines storage, not the title.', verify: 'Inspect the configured Variable and enable rule-source tracing for a review.' }, + es: { summary: 'Define criterios generales de revisión para Bugbot, una regla por línea.', when: 'Úsalo si el equipo necesita criterios comunes en el repositorio configurado.', example: 'Señala cambios que omitan el aislamiento entre clientes.', effect: 'Se aplican antes que las reglas del repositorio; el ámbito de la Variable determina dónde se guardan.', verify: 'Revisa la Variable configurada y activa el rastreo de fuentes de reglas.' }, + }, + 'ai.provisioningMode': { + en: { summary: 'Decide how the Action finds or installs the selected agent CLI.', when: 'Applies on the runner when an enabled AI task starts.', example: 'Auto reuses an installed CLI or installs pinned Codex/OpenCode when missing.', effect: 'Always reinstalls reviewed defaults; Disabled requires a preinstalled CLI. Cursor must be preinstalled.', verify: 'Inspect the runner provisioning step and its reported binary version.' }, + es: { summary: 'Decide cómo encuentra o instala la Action el agente CLI.', when: 'Se aplica en el runner cuando empieza una tarea de IA.', example: 'Auto reutiliza el CLI existente o instala una versión fijada de Codex/OpenCode si falta.', effect: 'Always reinstala versiones fijadas; Disabled exige instalación previa. Cursor siempre se instala aparte.', verify: 'Revisa el paso de preparación y la versión del binario en el runner.' }, + }, + 'pullRequestApproval.testChecks': { + en: { summary: 'Choose CI jobs the approval bot may trust as independent test evidence.', when: 'Required for recommend or guarded approval.', example: 'Select the exact Tests job, its GitHub App ID, and parent workflow from a recent run.', effect: 'Only the listed exact producer identities can satisfy the approval gate.', verify: 'Open the linked workflow run and confirm the job, App, and current-head result.' }, + es: { summary: 'Selecciona los jobs de CI que el bot puede considerar pruebas fiables.', when: 'Obligatorio para las aprobaciones recomendadas o protegidas.', example: 'Elige el job Tests, su ID de GitHub App y el workflow de una ejecución reciente.', effect: 'Solo esas identidades exactas podrán satisfacer la condición de aprobación.', verify: 'Abre la ejecución vinculada y comprueba job, App y resultado para el commit actual.' }, + }, + 'pullRequestApproval.producerAttested': { + en: { summary: 'Confirm that you inspected the exact CI producer and its coverage-enforcing step.', when: 'Required before guarded mode can ever submit an approval.', example: 'Verify the selected Tests job fails when the coverage budget fails.', effect: 'Your assertion is recorded; Copilot does not infer it from a green check.', verify: 'Inspect the workflow file and an actual CI run before selecting Yes.' }, + es: { summary: 'Confirma que comprobaste el productor exacto de CI y su paso obligatorio de cobertura.', when: 'Necesario antes de que el modo protegido pueda aprobar.', example: 'Comprueba que el job Tests falla cuando no se alcanza la cobertura mínima.', effect: 'Se registra tu confirmación; Copilot no la deduce de un check verde.', verify: 'Revisa el workflow y una ejecución real antes de elegir Sí.' }, + }, + 'pullRequestApproval.coverage.mode': { + en: { summary: 'Choose how approval proves the changed-code coverage requirement.', when: 'Applies when PR approval is enabled.', example: 'Check: CI enforces the budget. Numeric: a trusted workflow publishes bounded counts.', effect: 'Check mode trusts a selected CI gate; numeric mode reads copilot-diff-coverage-v1 and compares a threshold.', verify: 'Inspect the CI failure condition or the reporter artifact, respectively.' }, + es: { summary: 'Elige cómo se demuestra la cobertura del código modificado.', when: 'Se aplica si habilitas la aprobación de PR.', example: 'Check: CI exige el mínimo. Numeric: un workflow fiable publica recuentos de líneas.', effect: 'Check confía en una condición de CI; numeric lee copilot-diff-coverage-v1 y compara un umbral.', verify: 'Comprueba la condición de fallo del CI o el artefacto del reporter.' }, + }, + 'pullRequestApproval.coverage.checkName': { + en: { summary: 'Select the trusted check that fails when coverage is below budget.', when: 'Required for both coverage evidence modes.', example: 'Use the same exact Tests check selected in the previous step.', effect: 'A success from another check or App cannot substitute for this gate.', verify: 'Inspect the selected job and confirm its coverage step is mandatory, not advisory.' }, + es: { summary: 'Selecciona el check fiable que falla si no se alcanza la cobertura mínima.', when: 'Obligatorio en ambos modos de evidencia.', example: 'Usa el mismo check Tests elegido en el paso anterior.', effect: 'Un éxito de otro check o App no sustituye esta condición.', verify: 'Comprueba que el paso de cobertura es obligatorio, no solo informativo.' }, + }, + 'projects.enabled': { + en: { summary: 'Decide whether future issue and PR automation should use existing GitHub Projects.', when: 'Ask now, before creating the setup PAT, so its Project read permission can be scoped correctly.', example: 'Choose Yes if your team already tracks work in an organization Project; choose No to skip it.', effect: 'Yes includes organization Projects: read in the setup PAT when applicable. No Project is changed now.', verify: 'Review the PAT permission table; exact Projects are selected after GitHub authorizes the PAT.' }, + es: { summary: 'Decide si la automatización futura de issues y PR usará Projects existentes.', when: 'Se pregunta antes de crear el PAT de configuración para ajustar el permiso de lectura de Projects.', example: 'Elige Sí si tu equipo usa un Project de la organización; No para omitirlo.', effect: 'Sí incluye Projects: read de la organización en el PAT cuando aplica. Ahora no se modifica ningún Project.', verify: 'Revisa los permisos del PAT; elegirás los Projects concretos tras autorizarlo en GitHub.' }, + }, + 'projects.ids': { + en: { summary: 'Choose the existing Projects that Copilot may update in future issue and PR workflows.', when: 'After the setup PAT is checked, GitHub may list accessible organization Projects. Personal Projects or unavailable lists need manual entry.', example: 'For https://github.com/orgs/acme/projects/5, select the project card or enter 5; never enter PVT_…', effect: 'Setup stores Project numbers in repository configuration; it does not create or edit Project items now.', verify: 'Open each linked Project and check its owner and URL number before approving the plan.' }, + es: { summary: 'Elige los Projects existentes que Copilot podrá actualizar en futuros flujos de issues y PR.', when: 'Después de comprobar el PAT, GitHub puede listar Projects accesibles de la organización. Para Projects personales o fallos de consulta, introdúcelos manualmente.', example: 'Para https://github.com/orgs/acme/projects/5, marca la tarjeta o escribe 5; nunca PVT_…', effect: 'Setup guarda números de Project en la configuración; ahora no crea ni edita elementos.', verify: 'Abre cada Project enlazado y comprueba el dueño y número de la URL antes de aprobar el plan.' }, + }, +}; +const section = { + capabilities: { en: { summary: 'Choose which automation Copilot will install.', when: 'This affects workflows, GitHub permissions, and later questions.', example: 'Disable a feature you do not plan to use.', effect: 'Only selected capabilities are planned.', verify: 'Review the generated setup plan before Apply.' }, es: { summary: 'Elige qué automatizaciones instalará Copilot.', when: 'Afecta a workflows, permisos de GitHub y preguntas posteriores.', example: 'Desactiva una función que no vayas a usar.', effect: 'Solo se planifican las funciones seleccionadas.', verify: 'Revisa el plan antes de aplicar cambios.' } }, + 'agent-runtime': { en: { summary: 'Choose the agent CLI for this task.', when: 'Applies when the selected feature runs in GitHub Actions.', example: 'Codex runs through the codex CLI.', effect: 'The Action invokes the selected provider, never an implicit fallback.', verify: 'Check the runner has the selected CLI and credentials.' }, es: { summary: 'Elige el agente CLI para esta tarea.', when: 'Se aplica al ejecutar la función elegida en GitHub Actions.', example: 'Codex usa el CLI codex.', effect: 'La Action usa ese proveedor, sin sustitución implícita.', verify: 'Comprueba el CLI y las credenciales del runner.' } }, + 'agent-model-defaults': { en: { summary: 'Set the model defaults shared by agent tasks.', when: 'Used unless you configure each task separately.', example: 'Keep the reviewed model by accepting the suggested value.', effect: 'The Action passes these values to the selected CLI.', verify: 'Check the plan and runner model allowlist.' }, es: { summary: 'Define el modelo común para las tareas del agente.', when: 'Se usa salvo que configures cada tarea por separado.', example: 'Acepta el modelo revisado que aparece como sugerencia.', effect: 'La Action pasa estos valores al CLI elegido.', verify: 'Revisa el plan y la lista de modelos permitidos.' } }, + 'agent-role-overrides': { en: { summary: 'Override this one agent task.', when: 'Only when independent task configuration is enabled.', example: 'Use a different model for review than for planning.', effect: 'Only this task uses the override.', verify: 'Inspect the per-task plan values.' }, es: { summary: 'Personaliza esta tarea del agente.', when: 'Solo si activaste la configuración independiente por tarea.', example: 'Usa un modelo distinto para revisión y planificación.', effect: 'Solo esta tarea usa el valor personalizado.', verify: 'Revisa los valores de cada tarea en el plan.' } }, + repository: { en: { summary: 'Set how Copilot treats your repository.', when: 'Applies to generated workflows and future issue/PR events.', example: 'Use your actual development branch name.', effect: 'Future automation follows the chosen branch and workflow rules.', verify: 'Review the planned files and repository profile.' }, es: { summary: 'Define cómo Copilot tratará tu repositorio.', when: 'Se aplica a los workflows y futuros eventos de issues/PR.', example: 'Indica el nombre real de tu rama de desarrollo.', effect: 'La automatización seguirá las ramas y reglas elegidas.', verify: 'Revisa los archivos del plan y el perfil del repositorio.' } }, + deployment: { en: { summary: 'Choose release and hotfix behavior.', when: 'Only matters when those workflows are enabled.', example: 'Keep the default strategy unless your branching policy differs.', effect: 'Changes how release branches and reconciliation PRs are managed.', verify: 'Inspect the release/hotfix section of the plan.' }, es: { summary: 'Define el comportamiento de releases y hotfixes.', when: 'Importa si activaste esos workflows.', example: 'Conserva la estrategia predeterminada salvo que tus ramas funcionen distinto.', effect: 'Cambia la gestión de ramas y PR de reconciliación.', verify: 'Revisa la sección de releases y hotfixes del plan.' } }, + bugbot: { en: { summary: 'Choose how Bugbot analyzes and reports code changes.', when: 'Used when AI review features run.', example: 'The default publishes eligible findings without blocking all PRs.', effect: 'Changes future review publication and diagnostics.', verify: 'Inspect the Bugbot Variables in the plan and later review results.' }, es: { summary: 'Define cómo Bugbot analiza y comunica cambios de código.', when: 'Se usa cuando se ejecutan funciones de revisión con IA.', example: 'Por defecto publica hallazgos aptos sin bloquear todos los PR.', effect: 'Cambia futuras revisiones y diagnósticos.', verify: 'Revisa las Variables de Bugbot en el plan y sus resultados.' } }, + 'pull-request-approval': { en: { summary: 'Choose evidence required before the bot recommends or submits PR approval.', when: 'Only applies if PR automation is enabled.', example: 'Recommend informs a human; guarded may submit a native approval.', effect: 'No PR is approved solely because this page shows green checks.', verify: 'Inspect the trusted CI, Bugbot, and branch-rule evidence.' }, es: { summary: 'Elige las pruebas necesarias para recomendar o aprobar un PR.', when: 'Solo se aplica si activaste la automatización de PR.', example: 'Recommend informa a una persona; guarded puede publicar una aprobación.', effect: 'Ningún PR se aprueba solo porque esta pantalla muestre checks verdes.', verify: 'Revisa CI, Bugbot y las reglas de rama.' } }, + projects: { en: { summary: 'Choose an existing Project Status value for an issue or PR transition.', when: 'Only when Projects integration is selected.', example: 'Todo when an issue is created; In Progress when work starts.', effect: 'Future automation updates the Status field, not a visual board column.', verify: 'Open each selected Project and inspect its Status field options.' }, es: { summary: 'Elige un valor Status existente para una transición de issue o PR.', when: 'Solo si elegiste integrar Projects.', example: 'Todo al crear un issue; In Progress al empezar el trabajo.', effect: 'La automatización futura actualiza el campo Status, no una columna visual.', verify: 'Abre cada Project y revisa las opciones de su campo Status.' } }, + provisioning: { en: { summary: 'Choose which GitHub Actions resources setup manages.', when: 'Affects PAT grants and setup writes.', example: 'Keep Secrets enabled if the bot PAT must be installed.', effect: 'Selected resources may be created or updated after approval.', verify: 'Inspect exact resource names in the plan.' }, es: { summary: 'Elige qué recursos de GitHub Actions gestionará setup.', when: 'Afecta a permisos del PAT y cambios de configuración.', example: 'Mantén Secrets si hay que instalar el PAT del bot.', effect: 'Los recursos seleccionados podrán crearse o actualizarse tras aprobar.', verify: 'Revisa los nombres exactos en el plan.' } }, + storage: { en: { summary: 'Choose where GitHub Actions Variables and Secrets live.', when: 'Applies when provisioning is enabled.', example: 'Repository scope is the simplest default.', effect: 'Affects visibility, permission grants, and precedence.', verify: 'Check the selected scope and shadow warnings in the plan.' }, es: { summary: 'Elige dónde se guardan Variables y Secrets de GitHub Actions.', when: 'Se aplica si activaste su configuración.', example: 'El ámbito de repositorio es el predeterminado más sencillo.', effect: 'Afecta a visibilidad, permisos y precedencia.', verify: 'Revisa el ámbito y los avisos de superposición en el plan.' } }, +}; +function setupQuestionPresentation(question) { + const copy = special[question.id] ?? section[question.stateId]; + const purpose = (0, setup_question_purpose_policy_1.setupQuestionPurpose)(question); + const documentation = (0, setup_question_documentation_policy_1.setupQuestionDocumentation)(question); + const genericHow = question.kind === 'boolean' + ? { en: 'Choose Yes to enable this behavior or No to leave it off; the suggested answer appears below.', es: 'Elige Sí para activarlo o No para dejarlo desactivado; abajo verás la respuesta sugerida.' } + : question.kind === 'producer-select' + ? { en: 'Inspect each candidate run on GitHub, then select its exact job, source App ID and workflow. A listed run is observed, not proof of a required coverage gate; use manual entry for a missing producer.', es: 'Abre cada ejecución candidata en GitHub y comprueba el job, la App y el workflow exactos. Una ejecución listada es observada, no prueba que exija cobertura; usa la entrada manual si falta un productor.' } + : question.kind === 'project-select' + ? { en: 'Select Projects by title and URL. If one is missing, enter its positive URL number or exact GitHub URL; PVT_ IDs are not valid.', es: 'Marca Projects por título y URL. Si falta uno, introduce su número positivo o URL exacta de GitHub; los IDs PVT_ no valen.' } + : question.kind === 'scope-overrides' + ? { en: 'Select only inherited names you deliberately want to replace at repository scope. Leave empty to keep organization values.', es: 'Selecciona solo los nombres heredados que quieras sustituir en el repositorio. Vacío conserva los valores de la organización.' } + : question.kind === 'multi-select' + ? { en: 'Toggle the listed workflows you intend to use. You can select more than one; review their GitHub permissions before creating a PAT.', es: 'Marca los workflows que usarás. Puedes elegir varios; revisa sus permisos de GitHub antes de crear el PAT.' } + : question.kind === 'choice' + ? { en: 'Select one of the listed values after reading its consequence; the stored value is not translated.', es: 'Elige una de las opciones tras revisar sus consecuencias; el valor guardado no se traduce.' } + : question.kind === 'number' + ? { en: 'Enter a whole number within the range described in the question; accept the suggested value when unsure.', es: 'Introduce un número entero dentro del intervalo indicado; acepta el sugerido si tienes dudas.' } + : { en: 'Enter the exact value used by your repository or runner; leave it empty only when the question says empty is allowed.', es: 'Introduce el valor exacto de tu repositorio o runner; déjalo vacío solo si la pregunta lo permite.' }; + const howById = { + 'pullRequestApproval.coverage.checkName': { + en: 'Select one of the trusted checks above. Open its linked run and workflow file; the coverage step must fail this job when the budget fails. A green result alone is not proof.', + es: 'Elige uno de los checks fiables anteriores. Abre su ejecución y workflow; el paso de cobertura debe hacer fallar el job si no se alcanza el mínimo. Un resultado verde no basta.', + }, + 'pullRequestApproval.producerAttested': { + en: 'Answer Yes only after inspecting every selected name, App ID and workflow, plus the coverage-enforcing step of the check you just chose. Otherwise answer No and stay in recommendation mode.', + es: 'Responde Sí solo tras comprobar cada nombre, ID de App y workflow, además del paso obligatorio de cobertura del check elegido. Si no, responde No y mantén el modo recomendación.', + }, + 'pullRequestApproval.coverage.artifactWorkflowName': { + en: 'Enter the exact name of a trusted selected workflow that publishes copilot-diff-coverage-v1 for this PR/head/base. Do not enter an artifact filename or a guessed workflow name.', + es: 'Escribe el nombre exacto de un workflow fiable seleccionado que publique copilot-diff-coverage-v1 para este PR/head/base. No pongas un archivo ni un nombre supuesto.', + }, + 'projects.statusVerified': { + en: 'Open every selected Project in GitHub, inspect its Status field, and compare the exact four values shown above. Choose Yes only when all four exist in every Project; No returns to Project selection.', + es: 'Abre cada Project elegido en GitHub, revisa su campo Status y compara los cuatro valores exactos anteriores. Elige Sí solo si todos existen en cada Project; No vuelve a la selección de Projects.', + }, + }; + const statusHow = /^projects\.(issue|pullRequest)(Created|InProgress)Column$/u.test(question.id) + ? { en: 'Choose an option shown in every selected Project’s Status field. If options cannot be read, open each Project in GitHub and enter the same exact existing option; different names per Project are not supported.', + es: 'Elige una opción del campo Status de todos los Projects seleccionados. Si no se pueden consultar, abre cada Project y escribe el mismo valor existente; no se admiten nombres distintos por Project.' } + : undefined; + const how = howById[question.id] ?? statusHow ?? genericHow; + const where = location[question.stateId]; + return { + en: { label: question.label.replace(' (Space toggles, Enter confirms)', ''), ...copy.en, + summary: special[question.id] ? copy.en.summary : (purpose?.en ?? copy.en.summary), + where: where.en, how: how.en, why: `This choice is requested now so the plan, token permissions and future automation agree. ${copy.en.when}`, documentation }, + es: { label: (0, setup_question_translations_1.spanishQuestionLabel)(question), ...copy.es, + summary: special[question.id] ? copy.es.summary : (purpose?.es ?? copy.es.summary), + where: where.es, how: how.es, why: `Esta elección permite ajustar el plan, los permisos del PAT y la automatización futura antes de aplicar cambios. ${copy.es.when}`, documentation }, + fr: (0, setup_question_guidance_fr_1.frenchQuestionExplanation)(question, documentation), + pt: (0, setup_question_guidance_pt_1.portugueseQuestionExplanation)(question, documentation), + }; +} + + +/***/ }), + +/***/ 14440: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.portugueseQuestionExplanation = portugueseQuestionExplanation; +const setup_question_labels_fr_pt_1 = __nccwpck_require__(28247); +const setup_question_purpose_fr_pt_1 = __nccwpck_require__(98807); +const where = { + capabilities: 'A configuração escreve os fluxos escolhidos neste repositório e pede apenas as permissões GitHub necessárias.', + 'agent-runtime': 'Os fluxos GitHub Actions gerados executam este agente no respetivo runner; nada é instalado neste computador.', + 'agent-model-defaults': 'O modelo e o comando comuns são guardados na configuração do repositório usada pelos fluxos gerados.', + 'agent-role-overrides': 'Esta exceção para uma tarefa é guardada no repositório e lida apenas quando essa tarefa é executada.', + repository: 'O perfil do repositório e os fluxos gerados usam este valor em futuros eventos de ramos, questões e pull requests.', + deployment: 'O perfil do repositório controla futuros fluxos de release e hotfix; responder não publica nada.', + bugbot: 'O fluxo gerado lê esta definição da configuração ou das Variables do GitHub Actions selecionadas.', + 'pull-request-approval': 'A aprovação protegida usa identidades exatas dos produtores CI e provas das execuções de pull requests no GitHub.', + projects: 'Os Projects escolhidos e os valores do campo Status serão usados pela futura automatização de questões e pull requests.', + provisioning: 'Após a confirmação final, a configuração pode criar ou atualizar os ficheiros e recursos do GitHub Actions escolhidos.', + storage: 'O GitHub Actions guarda estes recursos no repositório ou na organização; o âmbito altera a visibilidade e as permissões do PAT.', +}; +const section = { + capabilities: { summary: 'Escolha as automatizações que o Copilot irá instalar.', when: 'Isto afeta os fluxos, as permissões GitHub e as perguntas seguintes.', example: 'Desative uma função que não pretende utilizar.', effect: 'Só as funções selecionadas entram no plano.', verify: 'Reveja o plano antes de aplicar alterações.' }, + 'agent-runtime': { summary: 'Escolha o agente CLI para esta tarefa.', when: 'Aplica-se quando a função selecionada é executada no GitHub Actions.', example: 'O Codex é executado através do comando codex.', effect: 'A Action executa o fornecedor escolhido, sem substituição implícita.', verify: 'Confirme que o runner tem o CLI e as credenciais necessárias.' }, + 'agent-model-defaults': { summary: 'Defina os modelos predefinidos comuns às tarefas do agente.', when: 'Usam-se salvo se configurar cada tarefa separadamente.', example: 'Mantenha o modelo sugerido se não tiver uma necessidade específica.', effect: 'A Action passa estes valores ao CLI escolhido.', verify: 'Reveja o plano e os modelos permitidos no runner.' }, + 'agent-role-overrides': { summary: 'Personalize esta tarefa do agente.', when: 'Apenas se tiver ativado a configuração independente por tarefa.', example: 'Use um modelo diferente para revisão e planeamento.', effect: 'A exceção só se aplica a esta tarefa.', verify: 'Reveja os valores de cada tarefa no plano.' }, + repository: { summary: 'Defina como o Copilot trata o seu repositório.', when: 'Aplica-se aos fluxos gerados e a futuros eventos de questões ou pull requests.', example: 'Indique o nome real do ramo de desenvolvimento.', effect: 'A futura automatização segue os ramos e as regras escolhidos.', verify: 'Reveja os ficheiros planeados e o perfil do repositório.' }, + deployment: { summary: 'Defina o comportamento de releases e hotfixes.', when: 'Só importa se esses fluxos estiverem ativados.', example: 'Mantenha a estratégia predefinida salvo se a política de ramos for diferente.', effect: 'Altera a gestão de ramos e pull requests de reconciliação.', verify: 'Reveja a secção de releases e hotfixes do plano.' }, + bugbot: { summary: 'Defina como o Bugbot analisa e comunica alterações.', when: 'Usa-se quando as funções de revisão por IA são executadas.', example: 'Por predefinição, publica resultados elegíveis sem bloquear todas as pull requests.', effect: 'Altera futuras publicações e diagnósticos de revisão.', verify: 'Reveja as Variables do Bugbot no plano e os resultados posteriores.' }, + 'pull-request-approval': { summary: 'Escolha as provas exigidas antes de o bot recomendar ou submeter uma aprovação.', when: 'Só se aplica se a automatização de pull requests estiver ativa.', example: '«Recommend» informa uma pessoa; «guarded» pode aprovar no GitHub.', effect: 'Uma verificação verde nesta página nunca aprova uma pull request por si só.', verify: 'Inspecione as provas CI, o Bugbot e as regras de ramos.' }, + projects: { summary: 'Escolha um valor Status existente para uma transição de questão ou PR.', when: 'Apenas se integrar Projects.', example: 'Todo na criação; In Progress no início do trabalho.', effect: 'A automatização atualiza o campo Status, não uma coluna visual.', verify: 'Verifique as opções Status de cada Project escolhido.' }, + provisioning: { summary: 'Escolha os recursos do GitHub Actions geridos pela configuração.', when: 'Isto afeta as permissões do PAT e as alterações previstas.', example: 'Mantenha os Secrets ativos se for necessário instalar o PAT do bot.', effect: 'Os recursos selecionados poderão ser criados ou atualizados após aprovação.', verify: 'Reveja os nomes exatos dos recursos no plano.' }, + storage: { summary: 'Escolha onde ficam as Variables e Secrets do GitHub Actions.', when: 'Aplica-se quando a sua criação está ativa.', example: 'O âmbito do repositório é a opção predefinida mais simples.', effect: 'Altera visibilidade, permissões e precedência.', verify: 'Confirme o âmbito e os avisos de sobreposição no plano.' }, +}; +const special = { + 'agents.findings.executable': { summary: 'Escolha o comando do agente no runner GitHub Actions, não neste computador.', when: 'Altere-o apenas se tiver instalado deliberadamente outro agente no runner.', example: 'Deixe vazio para codex, opencode ou agent, conforme o fornecedor.', effect: 'O caminho personalizado é usado pelas tarefas escolhidas e nunca é instalado automaticamente.', verify: 'Confirme que o runner tem exatamente este executável antes de ativar o fluxo.' }, + 'ai.includeReasoning': { summary: 'Peça explicações adicionais se a resposta do fornecedor as disponibilizar.', when: 'Só para diagnóstico avançado; o percurso CLI atual não fornece partes de raciocínio separadas.', example: 'Mantenha desativado numa configuração normal.', effect: 'Pode acrescentar texto do fornecedor, sem garantir metadados breves.', verify: 'Inspecione uma resposta estruturada controlada; não presuma que a opção produziu texto adicional.' }, + 'ai.bugbotDryRun': { summary: 'Mantenha o Bugbot em modo apenas de análise nas próximas execuções.', when: 'Útil numa avaliação; incompatível com provas de aprovação.', example: 'Escolha Não para publicar revisões normais.', effect: 'O Bugbot analisa sem publicar resultados nem alterar o repositório. Não é setup --dry-run.', verify: 'Inspecione o resultado do fluxo Bugbot: a simulação não publica revisão nem verificação.' }, + 'ai.bugbotOrganizationRules': { summary: 'Defina instruções gerais para o Bugbot, uma regra por linha.', when: 'Use se a equipa precisar de critérios de revisão partilhados no repositório configurado.', example: 'Assinalar alterações que contornem o isolamento entre clientes.', effect: 'Estas regras precedem as do repositório; o âmbito da Variable determina o armazenamento.', verify: 'Inspecione a Variable configurada e ative o rastreio das fontes das regras.' }, + 'ai.provisioningMode': { summary: 'Decida como a Action encontra ou instala o agente CLI.', when: 'Aplica-se no runner quando começa uma tarefa de IA ativa.', example: 'Auto reutiliza um CLI instalado ou instala uma versão fixa de Codex/OpenCode.', effect: 'Always reinstala as versões revistas; Disabled exige um CLI pré-instalado. Cursor tem de estar pré-instalado.', verify: 'Inspecione a etapa de preparação e a versão do binário comunicada pelo runner.' }, + 'pullRequestApproval.testChecks': { summary: 'Escolha os jobs CI que o bot pode aceitar como prova independente de testes.', when: 'Obrigatório para os modos Recommend e Guarded.', example: 'Selecione o job Tests exato, o ID da App GitHub e o workflow de uma execução recente.', effect: 'Só as identidades exatas listadas satisfazem a condição de aprovação.', verify: 'Abra a execução associada e confirme job, App e resultado do commit atual.' }, + 'pullRequestApproval.producerAttested': { summary: 'Confirme que inspecionou o produtor CI exato e a sua etapa obrigatória de cobertura.', when: 'Obrigatório antes de o modo Guarded poder aprovar.', example: 'Confirme que o job Tests falha se o limite de cobertura não for atingido.', effect: 'A sua confirmação fica registada; o Copilot não a deduz de uma verificação verde.', verify: 'Inspecione o ficheiro do workflow e uma execução real antes de escolher Sim.' }, + 'pullRequestApproval.coverage.mode': { summary: 'Escolha como comprovar a cobertura exigida do código alterado.', when: 'Aplica-se quando a aprovação de PR está ativa.', example: 'Check: o CI exige o limite. Numeric: um workflow fiável publica contagens limitadas.', effect: 'Check confia numa condição CI; Numeric lê copilot-diff-coverage-v1 e compara o limite.', verify: 'Inspecione, respetivamente, a condição de falha CI ou o artefacto do relatório.' }, + 'pullRequestApproval.coverage.checkName': { summary: 'Selecione a verificação fiável que falha abaixo do limite de cobertura.', when: 'Obrigatório nos dois modos de prova.', example: 'Use o mesmo job Tests exato da etapa anterior.', effect: 'O sucesso de outra verificação ou App não substitui esta condição.', verify: 'Confirme que a etapa de cobertura é obrigatória e não apenas informativa.' }, + 'projects.enabled': { summary: 'Decida se futuras questões e PR devem usar Projects GitHub existentes.', when: 'Antes de criar o PAT de configuração para prever o acesso de leitura a Projects.', example: 'Sim se a equipa usa um Project da organização; Não para ignorar.', effect: 'Sim inclui Projects: read da organização quando necessário. Nenhum Project é alterado agora.', verify: 'Reveja as permissões do PAT; escolherá os Projects concretos depois de o autorizar.' }, + 'projects.ids': { summary: 'Selecione os Projects existentes que o Copilot poderá atualizar futuramente.', when: 'Após verificar o PAT; se a lista não estiver disponível, introduza os dados manualmente.', example: 'Para https://github.com/orgs/acme/projects/5, marque o cartão ou introduza 5, nunca PVT_…', effect: 'Os números ficam guardados; nenhum item de Project é alterado agora.', verify: 'Abra cada Project e confirme proprietário e número antes de aprovar o plano.' }, +}; +function howToChoose(question) { + if (question.id === 'pullRequestApproval.coverage.checkName') + return 'Escolha uma das verificações fiáveis acima. Abra a execução e o workflow: o passo de cobertura tem de fazer falhar o job quando o limite não é atingido. Um resultado verde não basta.'; + if (question.id === 'pullRequestApproval.producerAttested') + return 'Responda Sim apenas depois de verificar cada nome, ID da App e workflow escolhido, bem como o passo obrigatório de cobertura do check selecionado. Caso contrário, responda Não e mantenha o modo de recomendação.'; + if (question.id === 'pullRequestApproval.coverage.artifactWorkflowName') + return 'Introduza o nome exato de um workflow fiável selecionado que publique copilot-diff-coverage-v1 para este PR e os seus commits base e head. Não adivinhe o nome do workflow.'; + if (question.id === 'projects.statusVerified') + return 'Abra cada Project escolhido no GitHub, inspecione o campo Status e compare os quatro valores exatos acima. Responda Sim apenas se todos existirem em cada Project; Não regressa à seleção de Projects.'; + if (/^projects\.(issue|pullRequest)(Created|InProgress)Column$/u.test(question.id)) + return 'Escolha uma opção do campo Status presente em todos os Projects selecionados. Se não conseguir consultar as opções, abra cada Project no GitHub e introduza o mesmo valor existente; valores diferentes por Project não são suportados.'; + switch (question.kind) { + case 'boolean': return 'Escolha Sim para ativar ou Não para desativar; a resposta sugerida aparece abaixo.'; + case 'producer-select': return 'Inspecione cada execução candidata no GitHub e escolha o job, ID da App e workflow exatos. Introduza manualmente apenas se não houver candidato verificado.'; + case 'project-select': return 'Selecione pelo título e URL. Se faltar um Project, introduza o número positivo ou URL exato do GitHub; IDs PVT_ não são válidos.'; + case 'scope-overrides': return 'Selecione apenas os nomes herdados que pretende substituir no repositório. Deixe vazio para conservar os valores da organização.'; + case 'multi-select': return 'Assinale os fluxos que pretende usar. Pode escolher vários; reveja as permissões antes de criar um PAT.'; + case 'choice': return 'Escolha um valor após ler as consequências; o valor guardado não é traduzido.'; + case 'number': return 'Introduza um número inteiro no intervalo indicado; mantenha o valor sugerido se tiver dúvidas.'; + default: return 'Introduza o valor exato usado pelo repositório ou runner; deixe vazio apenas se a pergunta o permitir.'; + } +} +function portugueseQuestionExplanation(question, documentation) { + const copy = special[question.id] ?? section[question.stateId]; + return { + label: (0, setup_question_labels_fr_pt_1.translatedQuestionLabel)(question, 'pt'), + ...copy, + summary: special[question.id] ? copy.summary : ((0, setup_question_purpose_fr_pt_1.setupQuestionPurposeFrPt)(question, 'pt') ?? copy.summary), + where: where[question.stateId], + how: howToChoose(question), + why: `Esta decisão permite alinhar o plano, as permissões do PAT e a futura automatização antes de aplicar alterações. ${copy.when}`, + documentation: { title: 'Documentação desta opção', url: documentation.url }, + }; +} + + +/***/ }), + +/***/ 55765: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.questionLabelsFr = void 0; +/** French presentation labels; semantic question IDs remain unchanged. */ +exports.questionLabelsFr = { + 'features.issues': 'Automatiser les tickets : branches, étiquettes, projets et cycle de vie', + 'features.pullRequests': 'Automatiser les pull requests : revue, description et cycle de vie', + 'features.commits': 'Automatiser les commits : progression, taille et analyse Bugbot', + 'features.issueComments': 'Répondre aux commentaires des tickets et permettre les corrections Bugbot', + 'features.pullRequestComments': 'Répondre aux commentaires des pull requests et permettre les corrections Bugbot', + 'features.agentProvisioning': 'Vérifier l’installation des agents CLI dans GitHub Actions', + 'features.credentialHealth': 'Vérifier l’état des identifiants distants', + 'features.inactiveIssueClosure': 'Fermer les tickets inactifs après le délai défini', + 'features.issueTemplates': 'Installer les modèles de ticket', + 'features.pullRequestTemplate': 'Installer le modèle de pull request', + 'issueWorkflows.enabled': 'Types de workflows de ticket à activer', + 'repositoryAgentGuidance.enabled': 'Générer des instructions pour les agents dans le dépôt ?', + 'repositoryAgentGuidance.agentsPointer': 'Comment trouver les instructions depuis AGENTS.md', + 'agents.findings.modelProvider': 'Fournisseur de modèle partagé (sauf réglage propre à une tâche)', + 'agents.findings.model': 'Modèle partagé (sauf réglage propre à une tâche)', + 'agents.findings.effort': 'Effort de raisonnement partagé (les réglages par tâche sont conservés)', + 'agents.findings.executable': 'Exécutable partagé validé (les réglages par tâche sont conservés)', + 'agents.configureIndependently': 'Configurer le modèle et la commande séparément pour chaque tâche ?', + 'repository.mainBranch': 'Branche de production', + 'repository.developmentBranch': 'Branche de développement', + 'repository.featureTree': 'Préfixe des branches de fonctionnalité', + 'repository.bugfixTree': 'Préfixe des branches de correction', + 'repository.hotfixTree': 'Préfixe des branches de correctif urgent', + 'repository.releaseTree': 'Préfixe des branches de version', + 'repository.docsTree': 'Préfixe des branches de documentation', + 'repository.choreTree': 'Préfixe des branches de maintenance', + 'repository.issueManagedBranches': 'L’Action peut-elle créer des branches liées aux tickets ?', + 'repository.preBranchSdd': 'Exiger un SDD avant de créer certaines branches ?', + 'repository.reopenIssueOnPush': 'Rouvrir un ticket fermé quand sa branche reçoit des commits ?', + 'repository.desiredAssigneesCount': 'Nombre souhaité de responsables par ticket', + 'repository.desiredReviewersCount': 'Nombre souhaité de réviseurs par pull request', + 'repository.inactivityThresholdHours': 'Heures d’inactivité avant la fermeture d’un ticket en attente', + 'repository.repositoryLocale': 'Langue des messages du dépôt', + 'repository.issueLocale': 'Langue des tickets (vide : hériter)', + 'repository.pullRequestLocale': 'Langue des pull requests (vide : hériter)', + 'repository.commitPrefixTransforms': 'Transformation des préfixes de commit', + 'repository.releaseReconciliationStrategy': 'Stratégie de réconciliation des versions', + 'repository.hotfixReconciliationStrategy': 'Stratégie de réconciliation des correctifs urgents', + 'repository.reconciliationPullRequestMode': 'Mode des pull requests de réconciliation', + 'repository.reconciliationBackmergeMode': 'Mode de fusion de retour', + 'repository.hotfixActiveReleasePolicy': 'Destination du correctif pendant une version active', + 'repository.reconciliationTree': 'Préfixe des branches de réconciliation', + 'repository.reconciliationCleanup': 'Nettoyage des branches après réconciliation', + 'repository.reconciliationIssueCompletion': 'Sort du ticket après réconciliation', + 'repository.orchestrationPresentationMode': 'Niveau de détail du centre de contrôle des versions', + 'repository.orchestrationDiagrams': 'Afficher des diagrammes accessibles pour les versions ?', + 'repository.orchestrationCommentMode': 'Comment publier les commentaires du cycle de version', + 'ai.pullRequestDescriptionMode': 'Comment mettre à jour la description des pull requests', + 'ai.ignoreFiles': 'Fichiers que l’IA doit ignorer', + 'ai.membersOnly': 'Limiter le traitement par IA aux membres du dépôt ?', + 'ai.includeReasoning': 'Inclure des explications supplémentaires du fournisseur ?', + 'ai.bugbotSeverity': 'Gravité minimale des résultats publiés par Bugbot', + 'ai.bugbotCommentLimit': 'Nombre maximal de commentaires Bugbot par exécution', + 'ai.bugbotFixVerifyCommands': 'Commandes de vérification des corrections Bugbot', + 'ai.bugbotDryRun': 'Analyser avec Bugbot sans publier de changements ?', + 'ai.bugbotEffort': 'Profondeur de l’analyse Bugbot', + 'ai.bugbotReviewDrafts': 'Analyser les pull requests en brouillon ?', + 'ai.bugbotTraceRules': 'Indiquer quelles sources de règles ont été appliquées ?', + 'ai.bugbotSuggestedChanges': 'Publier des suggestions de modification sûres ?', + 'ai.bugbotTelemetry': 'Enregistrer des métriques Bugbot sans contenu ?', + 'ai.bugbotFailOnUnresolved': 'Faire échouer la vérification si des résultats restent ouverts ?', + 'ai.bugbotOrganizationRules': 'Règles Bugbot communes, une par ligne', + 'ai.provisioningMode': 'Comment préparer l’agent CLI sur le runner', + 'pullRequestApproval.mode': 'Que peut faire le bot pour approuver les pull requests ?', + 'pullRequestApproval.testChecks': 'Quelles vérifications CI sont fiables pour approuver ?', + 'pullRequestApproval.producerAttested': 'Avez-vous vérifié le job, l’App et l’étape obligatoire de couverture ?', + 'pullRequestApproval.coverage.mode': 'Comment prouver la couverture requise', + 'pullRequestApproval.coverage.checkName': 'Vérification fiable imposant la couverture', + 'pullRequestApproval.coverage.minDiffPercent': 'Couverture minimale des lignes modifiées (0–100)', + 'pullRequestApproval.coverage.artifactWorkflowName': 'Workflow publiant copilot-diff-coverage-v1', + 'pullRequestApproval.coverage.reporterAttested': 'Avez-vous vérifié l’installation du rapporteur numérique ?', + 'projects.enabled': 'Intégrer des Projects GitHub existants ?', + 'projects.ids': 'Choisir des Projects existants ou saisir leurs numéros d’URL', + 'projects.statusVerified': 'Avez-vous vérifié sur GitHub les quatre valeurs Status exactes de chaque Project choisi ?', + 'projects.issueCreatedColumn': 'Valeur Status des nouveaux tickets', + 'projects.pullRequestCreatedColumn': 'Valeur Status des nouvelles pull requests', + 'projects.issueInProgressColumn': 'Valeur Status des tickets en cours', + 'projects.pullRequestInProgressColumn': 'Valeur Status des pull requests en cours', + createInitialTag: 'Créer v1.0.0 si aucune étiquette de version n’existe ?', + manageRepositoryVariables: 'Créer ou mettre à jour les Variables GitHub Actions ?', + manageRepositorySecrets: 'Valider et configurer les Secrets GitHub Actions ?', +}; + + +/***/ }), + +/***/ 6958: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.questionLabelsPt = void 0; +/** Portuguese presentation labels; semantic question IDs remain unchanged. */ +exports.questionLabelsPt = { + 'features.issues': 'Automatizar questões: ramos, etiquetas, projetos e ciclo de vida', + 'features.pullRequests': 'Automatizar pull requests: revisão, descrição e ciclo de vida', + 'features.commits': 'Automatizar commits: progresso, tamanho e análise do Bugbot', + 'features.issueComments': 'Responder a comentários de questões e permitir correções do Bugbot', + 'features.pullRequestComments': 'Responder a comentários de pull requests e permitir correções do Bugbot', + 'features.agentProvisioning': 'Verificar a instalação dos agentes CLI no GitHub Actions', + 'features.credentialHealth': 'Verificar o estado das credenciais remotas', + 'features.inactiveIssueClosure': 'Fechar questões inativas após o prazo definido', + 'features.issueTemplates': 'Instalar modelos de questão', + 'features.pullRequestTemplate': 'Instalar o modelo de pull request', + 'issueWorkflows.enabled': 'Tipos de fluxo de questões a ativar', + 'repositoryAgentGuidance.enabled': 'Gerar instruções para agentes no repositório?', + 'repositoryAgentGuidance.agentsPointer': 'Como encontrar as instruções a partir de AGENTS.md', + 'agents.findings.modelProvider': 'Fornecedor de modelo partilhado (salvo definição própria de uma tarefa)', + 'agents.findings.model': 'Modelo partilhado (salvo definição própria de uma tarefa)', + 'agents.findings.effort': 'Esforço de raciocínio partilhado (as definições por tarefa são preservadas)', + 'agents.findings.executable': 'Executável partilhado validado (as definições por tarefa são preservadas)', + 'agents.configureIndependently': 'Configurar modelo e comando separadamente para cada tarefa?', + 'repository.mainBranch': 'Ramo de produção', + 'repository.developmentBranch': 'Ramo de desenvolvimento', + 'repository.featureTree': 'Prefixo dos ramos de funcionalidade', + 'repository.bugfixTree': 'Prefixo dos ramos de correção', + 'repository.hotfixTree': 'Prefixo dos ramos de hotfix', + 'repository.releaseTree': 'Prefixo dos ramos de release', + 'repository.docsTree': 'Prefixo dos ramos de documentação', + 'repository.choreTree': 'Prefixo dos ramos de manutenção', + 'repository.issueManagedBranches': 'A Action pode criar ramos associados a questões?', + 'repository.preBranchSdd': 'Exigir um SDD antes de criar determinados ramos?', + 'repository.reopenIssueOnPush': 'Reabrir uma questão fechada quando o seu ramo recebe commits?', + 'repository.desiredAssigneesCount': 'Número pretendido de responsáveis por questão', + 'repository.desiredReviewersCount': 'Número pretendido de revisores por pull request', + 'repository.inactivityThresholdHours': 'Horas de inatividade antes de fechar uma questão em espera', + 'repository.repositoryLocale': 'Idioma das mensagens do repositório', + 'repository.issueLocale': 'Idioma das questões (vazio: herdar)', + 'repository.pullRequestLocale': 'Idioma das pull requests (vazio: herdar)', + 'repository.commitPrefixTransforms': 'Transformação dos prefixos dos commits', + 'repository.releaseReconciliationStrategy': 'Estratégia de reconciliação de releases', + 'repository.hotfixReconciliationStrategy': 'Estratégia de reconciliação de hotfixes', + 'repository.reconciliationPullRequestMode': 'Modo das pull requests de reconciliação', + 'repository.reconciliationBackmergeMode': 'Modo da fusão de retorno', + 'repository.hotfixActiveReleasePolicy': 'Destino do hotfix durante uma release ativa', + 'repository.reconciliationTree': 'Prefixo dos ramos de reconciliação', + 'repository.reconciliationCleanup': 'Limpeza de ramos após a reconciliação', + 'repository.reconciliationIssueCompletion': 'O que fazer à questão após a reconciliação', + 'repository.orchestrationPresentationMode': 'Nível de detalhe do centro de controlo de releases', + 'repository.orchestrationDiagrams': 'Mostrar diagramas acessíveis para releases?', + 'repository.orchestrationCommentMode': 'Como publicar os comentários do ciclo de release', + 'ai.pullRequestDescriptionMode': 'Como atualizar a descrição das pull requests', + 'ai.ignoreFiles': 'Ficheiros que a IA deve ignorar', + 'ai.membersOnly': 'Limitar o processamento por IA aos membros do repositório?', + 'ai.includeReasoning': 'Incluir explicações adicionais do fornecedor?', + 'ai.bugbotSeverity': 'Gravidade mínima dos resultados publicados pelo Bugbot', + 'ai.bugbotCommentLimit': 'Número máximo de comentários do Bugbot por execução', + 'ai.bugbotFixVerifyCommands': 'Comandos de verificação das correções do Bugbot', + 'ai.bugbotDryRun': 'Analisar com o Bugbot sem publicar alterações?', + 'ai.bugbotEffort': 'Profundidade da análise do Bugbot', + 'ai.bugbotReviewDrafts': 'Rever pull requests em rascunho?', + 'ai.bugbotTraceRules': 'Indicar que fontes de regras foram aplicadas?', + 'ai.bugbotSuggestedChanges': 'Publicar sugestões de alteração seguras?', + 'ai.bugbotTelemetry': 'Registar métricas do Bugbot sem conteúdo?', + 'ai.bugbotFailOnUnresolved': 'Fazer falhar a verificação se houver resultados por resolver?', + 'ai.bugbotOrganizationRules': 'Regras Bugbot partilhadas, uma por linha', + 'ai.provisioningMode': 'Como preparar o agente CLI no runner', + 'pullRequestApproval.mode': 'O que pode o bot fazer na aprovação de pull requests?', + 'pullRequestApproval.testChecks': 'Que verificações CI são fiáveis para aprovar?', + 'pullRequestApproval.producerAttested': 'Verificou o job, a App e a etapa obrigatória de cobertura?', + 'pullRequestApproval.coverage.mode': 'Como comprovar a cobertura exigida', + 'pullRequestApproval.coverage.checkName': 'Verificação fiável que exige cobertura', + 'pullRequestApproval.coverage.minDiffPercent': 'Cobertura mínima das linhas alteradas (0–100)', + 'pullRequestApproval.coverage.artifactWorkflowName': 'Workflow que publica copilot-diff-coverage-v1', + 'pullRequestApproval.coverage.reporterAttested': 'Verificou a instalação do relatório numérico?', + 'projects.enabled': 'Integrar Projects GitHub existentes?', + 'projects.ids': 'Selecionar Projects existentes ou introduzir os números dos URL', + 'projects.statusVerified': 'Confirmou no GitHub os quatro valores Status exatos de cada Project escolhido?', + 'projects.issueCreatedColumn': 'Valor Status das novas questões', + 'projects.pullRequestCreatedColumn': 'Valor Status das novas pull requests', + 'projects.issueInProgressColumn': 'Valor Status das questões em curso', + 'projects.pullRequestInProgressColumn': 'Valor Status das pull requests em curso', + createInitialTag: 'Criar v1.0.0 se ainda não existir uma etiqueta de versão?', + manageRepositoryVariables: 'Criar ou atualizar as Variables do GitHub Actions?', + manageRepositorySecrets: 'Validar e configurar os Secrets do GitHub Actions?', +}; + + +/***/ }), + +/***/ 28247: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.questionLabelsFrPt = void 0; +exports.translatedQuestionLabel = translatedQuestionLabel; +const setup_question_translations_1 = __nccwpck_require__(3927); +const fr_1 = __nccwpck_require__(55765); +const pt_1 = __nccwpck_require__(6958); +exports.questionLabelsFrPt = { fr: fr_1.questionLabelsFr, pt: pt_1.questionLabelsPt }; +const roleNames = { + fr: { planner: 'Planification', findings: 'Résultats', reviewer: 'Revue', fixer: 'Correction', tester: 'Tests' }, + pt: { planner: 'Planeamento', findings: 'Resultados', reviewer: 'Revisão', fixer: 'Correção', tester: 'Testes' }, +}; +function translatedQuestionLabel(question, locale) { + if (locale === 'en') + return question.label.replace(' (Space toggles, Enter confirms)', ''); + if (locale === 'es') + return (0, setup_question_translations_1.spanishQuestionLabel)(question); + const exact = exports.questionLabelsFrPt[locale][question.id]; + if (exact) + return exact; + const agent = question.id.match(/^agents\.(planner|findings|reviewer|fixer|tester)\.(provider|modelProvider|model|effort|executable)$/u); + if (agent) { + const fields = { + fr: { provider: 'agent CLI', modelProvider: 'fournisseur du modèle', model: 'modèle', effort: 'effort de raisonnement', executable: 'commande exécutable' }, + pt: { provider: 'agente CLI', modelProvider: 'fornecedor do modelo', model: 'modelo', effort: 'esforço de raciocínio', executable: 'comando executável' }, + }; + return `${roleNames[locale][agent[1]]} : ${fields[locale][agent[2]]}`; + } + const storage = question.id.match(/^storage\.(variables|secrets)\.(defaultScope|organizationVisibility|preserveExisting|overrides)$/u); + if (storage) { + const resources = { fr: { variables: 'Variables', secrets: 'Secrets' }, pt: { variables: 'Variables', secrets: 'Secrets' } }; + const fields = { + fr: { defaultScope: 'périmètre par défaut', organizationVisibility: 'visibilité dans l’organisation', preserveExisting: 'conserver les ressources existantes', overrides: 'exceptions de périmètre' }, + pt: { defaultScope: 'âmbito predefinido', organizationVisibility: 'visibilidade na organização', preserveExisting: 'conservar recursos existentes', overrides: 'exceções de âmbito' }, + }; + return `${resources[locale][storage[1]]} : ${fields[locale][storage[2]]}`; + } + return question.label; +} + + +/***/ }), + +/***/ 92139: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.purposesFr = void 0; +/** French question-specific purposes. */ +exports.purposesFr = { + 'issueWorkflows.enabled': 'Choisissez les workflows de ticket que Copilot pourra exécuter ; chacun agit différemment sur les branches, étiquettes et automatisations.', + 'repositoryAgentGuidance.enabled': 'Générez des instructions pour aider les agents IA à travailler en sécurité dans ce projet.', + 'repositoryAgentGuidance.agentsPointer': 'Décidez si le fichier AGENTS.md racine renvoie aux instructions générées, est créé s’il manque, ou reste intact.', + 'agents.configureIndependently': 'Attribuez des fournisseurs et modèles distincts à la planification, aux résultats, à la revue, à la correction et aux tests.', + 'repository.mainBranch': 'Indiquez la branche de production utilisée comme référence par les versions et correctifs urgents.', + 'repository.developmentBranch': 'Indiquez la branche d’intégration habituelle visée par la création de branches et la réconciliation.', + 'repository.issueManagedBranches': 'Autorisez l’Action à créer une branche liée lorsqu’un ticket passe en cours.', + 'repository.preBranchSdd': 'Exigez un document de conception approuvé avant certaines branches de fonctionnalité ou de changement de contrat.', + 'repository.reopenIssueOnPush': 'Rouvrez un ticket terminé quand de nouveaux commits arrivent sur sa branche liée.', + 'repository.desiredAssigneesCount': 'Définissez combien de personnes Copilot affecte à un nouveau ticket ; zéro désactive l’affectation automatique.', + 'repository.desiredReviewersCount': 'Définissez combien de réviseurs Copilot sollicite pour une pull request ; zéro désactive les demandes automatiques.', + 'repository.inactivityThresholdHours': 'Définissez combien de temps un ticket reste sans activité avant que le workflow activé puisse le fermer.', + 'repository.repositoryLocale': 'Choisissez la balise de langue BCP-47 des messages Copilot sur GitHub ; elle ne change pas la langue de cette page.', + 'repository.issueLocale': 'Changez la langue des messages GitHub pour les tickets ; laissez vide pour hériter de la langue du dépôt.', + 'repository.pullRequestLocale': 'Changez la langue des messages GitHub pour les pull requests ; laissez vide pour hériter de la langue du dépôt.', + 'repository.commitPrefixTransforms': 'Définissez les substitutions de préfixes de commit ; laissez vide si vos conventions n’en ont pas besoin.', + 'repository.releaseReconciliationStrategy': 'Choisissez comment les changements d’une version terminée reviennent dans le développement sans perdre leur filiation.', + 'repository.hotfixReconciliationStrategy': 'Choisissez comment un correctif urgent de production est reporté sur les branches en cours.', + 'repository.reconciliationPullRequestMode': 'Choisissez si les pull requests de réconciliation sont créées, fusionnées, mises en file ou laissées à une personne.', + 'repository.reconciliationBackmergeMode': 'Choisissez une fusion de retour directe ou passant par une branche de synchronisation.', + 'repository.hotfixActiveReleasePolicy': 'Choisissez où propager un correctif urgent lorsqu’une branche de version est déjà active.', + 'repository.reconciliationCleanup': 'Choisissez les branches temporaires à supprimer après une réconciliation réussie.', + 'repository.reconciliationIssueCompletion': 'Choisissez si le ticket à l’origine de la réconciliation se ferme ou reste ouvert pour suivi.', + 'repository.orchestrationPresentationMode': 'Choisissez le niveau de progression et de détail affiché dans le centre de contrôle GitHub des versions.', + 'repository.orchestrationDiagrams': 'Incluez des diagrammes Mermaid accessibles dans les informations de version.', + 'repository.orchestrationCommentMode': 'Choisissez si les commentaires de version sont mis à jour ou publiés à chaque étape importante.', + 'ai.pullRequestDescriptionMode': 'Choisissez si l’IA remplace, complète, préserve ou ne modifie jamais les descriptions des pull requests.', + 'ai.ignoreFiles': 'Indiquez les motifs de fichiers à exclure de la revue IA ; ces fichiers restent visibles sur GitHub.', + 'ai.membersOnly': 'N’autorisez le traitement IA que pour les demandes des membres du dépôt, pas pour tous les contributeurs externes.', + 'ai.bugbotSeverity': 'Fixez la gravité minimale publiée par Bugbot ; les résultats moins graves restent non publiés.', + 'ai.bugbotCommentLimit': 'Limitez les commentaires Bugbot par exécution pour ne pas submerger une pull request.', + 'ai.bugbotFixVerifyCommands': 'Indiquez les commandes qui doivent réussir avant qu’une correction automatique Bugbot soit considérée comme vérifiée.', + 'ai.bugbotEffort': 'Choisissez la profondeur des revues Bugbot ; un effort supérieur peut durer et consommer davantage.', + 'ai.bugbotReviewDrafts': 'Décidez si Bugbot analyse les pull requests en brouillon avant qu’elles soient prêtes.', + 'ai.bugbotTraceRules': 'Ajoutez l’origine de chaque règle de revue appliquée dans les résumés Bugbot pour faciliter l’audit.', + 'ai.bugbotSuggestedChanges': 'Autorisez Bugbot à joindre des suggestions de code sûres aux résultats publiés.', + 'ai.bugbotTelemetry': 'Enregistrez des métriques opérationnelles Bugbot sans stocker le contenu du dépôt.', + 'ai.bugbotFailOnUnresolved': 'Faites échouer la vérification Bugbot tant que des résultats exploitables restent ouverts.', + 'pullRequestApproval.mode': 'Choisissez si le bot recommande une approbation, peut approuver GitHub sous garde, ou n’intervient pas.', + 'pullRequestApproval.coverage.minDiffPercent': 'Définissez le pourcentage minimal de lignes modifiées couvertes qu’un rapporteur numérique fiable doit prouver.', + 'pullRequestApproval.coverage.artifactWorkflowName': 'Indiquez le workflow fiable exact qui publie l’artefact copilot-diff-coverage-v1.', + 'pullRequestApproval.coverage.reporterAttested': 'Confirmez avoir inspecté le rapporteur numérique dans ce workflow exact, et non seulement sa vérification verte.', + 'projects.enabled': 'Décidez si Copilot doit ajouter tickets et pull requests à des Projects existants ; le PAT servira ensuite à lister ceux de l’organisation.', + 'projects.ids': 'Choisissez des Projects existants par leur titre ou saisissez le numéro positif de leur URL ; les ID PVT_ ne conviennent pas.', + 'projects.statusVerified': 'Confirmez que les quatre options Status choisies existent dans chaque Project lorsque GitHub n’a pas pu vérifier leurs champs.', + createInitialTag: 'Créez v1.0.0 seulement si le dépôt n’a encore aucune étiquette de version.', + manageRepositoryVariables: 'Autorisez la création ou mise à jour des Variables GitHub Actions nécessaires aux workflows choisis.', + manageRepositorySecrets: 'Autorisez la validation et l’installation des Secrets GitHub Actions requis, dont le PAT du bot si nécessaire.', +}; + + +/***/ }), + +/***/ 48284: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.purposesPt = void 0; +/** Portuguese question-specific purposes. */ +exports.purposesPt = { + 'issueWorkflows.enabled': 'Escolha os fluxos de questões que o Copilot poderá executar; cada um afeta de forma diferente ramos, etiquetas e automatizações.', + 'repositoryAgentGuidance.enabled': 'Gere instruções para ajudar os agentes de IA a trabalhar com segurança neste projeto.', + 'repositoryAgentGuidance.agentsPointer': 'Decida se o AGENTS.md da raiz aponta para as instruções geradas, é criado se faltar ou permanece intacto.', + 'agents.configureIndependently': 'Defina fornecedores e modelos distintos para planeamento, resultados, revisão, correção e testes.', + 'repository.mainBranch': 'Indique o ramo de produção usado como referência por releases e hotfixes.', + 'repository.developmentBranch': 'Indique o ramo de integração habitual usado na criação de ramos e na reconciliação.', + 'repository.issueManagedBranches': 'Permita que a Action crie um ramo associado quando uma questão passa a estar em curso.', + 'repository.preBranchSdd': 'Exija um documento de desenho aprovado antes de determinados ramos de funcionalidade ou de alteração de contratos.', + 'repository.reopenIssueOnPush': 'Reabra uma questão concluída quando forem enviados novos commits para o ramo associado.', + 'repository.desiredAssigneesCount': 'Defina quantas pessoas o Copilot atribui a uma nova questão; zero desativa a atribuição automática.', + 'repository.desiredReviewersCount': 'Defina quantos revisores o Copilot solicita para uma pull request; zero desativa os pedidos automáticos.', + 'repository.inactivityThresholdHours': 'Defina quanto tempo uma questão fica sem atividade antes de o fluxo ativado a poder fechar.', + 'repository.repositoryLocale': 'Escolha a etiqueta BCP-47 das mensagens do Copilot no GitHub; não altera o idioma desta página.', + 'repository.issueLocale': 'Altere o idioma das mensagens GitHub para questões; deixe vazio para herdar o idioma do repositório.', + 'repository.pullRequestLocale': 'Altere o idioma das mensagens GitHub para pull requests; deixe vazio para herdar o idioma do repositório.', + 'repository.commitPrefixTransforms': 'Defina substituições dos prefixos dos commits; deixe vazio se as suas convenções não precisarem delas.', + 'repository.releaseReconciliationStrategy': 'Escolha como as alterações de uma release concluída regressam ao desenvolvimento sem perder a sua origem.', + 'repository.hotfixReconciliationStrategy': 'Escolha como um hotfix de produção é propagado para os ramos em curso.', + 'repository.reconciliationPullRequestMode': 'Escolha se as pull requests de reconciliação são criadas, integradas, colocadas em fila ou deixadas a uma pessoa.', + 'repository.reconciliationBackmergeMode': 'Escolha uma fusão de retorno direta ou através de um ramo de sincronização.', + 'repository.hotfixActiveReleasePolicy': 'Escolha para onde propagar um hotfix quando já existe um ramo de release ativo.', + 'repository.reconciliationCleanup': 'Escolha que ramos temporários serão eliminados após uma reconciliação bem-sucedida.', + 'repository.reconciliationIssueCompletion': 'Escolha se a questão que iniciou a reconciliação é fechada ou fica aberta para acompanhamento.', + 'repository.orchestrationPresentationMode': 'Escolha o nível de progresso e detalhe apresentado no centro de controlo GitHub das releases.', + 'repository.orchestrationDiagrams': 'Inclua diagramas Mermaid acessíveis na informação sobre releases.', + 'repository.orchestrationCommentMode': 'Escolha se os comentários da release são atualizados ou publicados em cada marco.', + 'ai.pullRequestDescriptionMode': 'Escolha se a IA substitui, acrescenta, preserva ou nunca altera as descrições das pull requests.', + 'ai.ignoreFiles': 'Indique padrões de ficheiros a excluir da revisão por IA; continuam visíveis no GitHub.', + 'ai.membersOnly': 'Permita o processamento por IA apenas para pedidos de membros do repositório, não de quaisquer colaboradores externos.', + 'ai.bugbotSeverity': 'Defina a gravidade mínima publicada pelo Bugbot; resultados menos graves não são publicados.', + 'ai.bugbotCommentLimit': 'Limite os comentários do Bugbot por execução para não sobrecarregar uma pull request.', + 'ai.bugbotFixVerifyCommands': 'Indique os comandos que têm de passar antes de uma correção automática do Bugbot ser considerada verificada.', + 'ai.bugbotEffort': 'Escolha a profundidade das revisões do Bugbot; mais esforço pode demorar e consumir mais recursos.', + 'ai.bugbotReviewDrafts': 'Decida se o Bugbot revê pull requests em rascunho antes de estarem prontas.', + 'ai.bugbotTraceRules': 'Inclua a origem de cada regra de revisão aplicada nos resumos do Bugbot para facilitar auditorias.', + 'ai.bugbotSuggestedChanges': 'Permita ao Bugbot anexar sugestões de código seguras aos resultados publicados.', + 'ai.bugbotTelemetry': 'Registe métricas operacionais do Bugbot sem guardar conteúdo do repositório.', + 'ai.bugbotFailOnUnresolved': 'Faça falhar a verificação do Bugbot enquanto existirem resultados acionáveis por resolver.', + 'pullRequestApproval.mode': 'Escolha se o bot recomenda aprovação, pode aprovar no GitHub sob condições ou não intervém.', + 'pullRequestApproval.coverage.minDiffPercent': 'Defina a percentagem mínima de linhas alteradas cobertas que um relatório numérico fiável tem de provar.', + 'pullRequestApproval.coverage.artifactWorkflowName': 'Indique o workflow fiável exato que publica o artefacto copilot-diff-coverage-v1.', + 'pullRequestApproval.coverage.reporterAttested': 'Confirme que inspecionou o relatório numérico nesse workflow exato, e não apenas uma verificação verde.', + 'projects.enabled': 'Decida se o Copilot deve adicionar questões e pull requests a Projects existentes; o PAT será usado depois para listar os da organização.', + 'projects.ids': 'Selecione Projects existentes pelo título ou introduza o número positivo do URL; IDs PVT_ não são usados.', + 'projects.statusVerified': 'Confirme que as quatro opções Status escolhidas existem em todos os Projects quando o GitHub não conseguiu verificar os campos.', + createInitialTag: 'Crie v1.0.0 apenas se o repositório ainda não tiver uma etiqueta de versão.', + manageRepositoryVariables: 'Permita criar ou atualizar as Variables do GitHub Actions necessárias aos fluxos escolhidos.', + manageRepositorySecrets: 'Permita validar e instalar os Secrets do GitHub Actions necessários, incluindo o PAT do bot quando aplicável.', +}; + + +/***/ }), + +/***/ 98807: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.purposesFrPt = void 0; +exports.setupQuestionPurposeFrPt = setupQuestionPurposeFrPt; +const fr_1 = __nccwpck_require__(92139); +const pt_1 = __nccwpck_require__(48284); +exports.purposesFrPt = { fr: fr_1.purposesFr, pt: pt_1.purposesPt }; +function setupQuestionPurposeFrPt(question, locale) { + const exact = exports.purposesFrPt[locale][question.id]; + if (exact) + return exact; + if (question.id.startsWith('features.')) + return locale === 'fr' + ? 'Activez ou désactivez cette fonction. Si vous la désactivez, cette configuration n’installera ni son automatisation ni ses autorisations conditionnelles.' + : 'Ative ou desative esta função. Se a desativar, esta configuração não instalará a automatização nem pedirá as permissões condicionais correspondentes.'; + if (/^agents\.[^.]+\.provider$/u.test(question.id)) + return locale === 'fr' + ? 'Choisissez l’agent CLI de cette tâche dans GitHub Actions ; ce fournisseur détermine la commande et les identifiants du runner.' + : 'Escolha o agente CLI desta tarefa no GitHub Actions; o fornecedor determina o comando e as credenciais do runner.'; + const setting = question.id.match(/^agents\.[^.]+\.(modelProvider|model|effort|executable)$/u)?.[1]; + if (setting) { + const fields = { + fr: { modelProvider: 'le service fournissant le modèle et ses identifiants', model: 'le nom exact du modèle autorisé par le fournisseur', effort: 'l’effort de raisonnement (ou vide pour la valeur du fournisseur)', executable: 'la commande présente sur le runner GitHub Actions, pas sur cet ordinateur' }, + pt: { modelProvider: 'o serviço que fornece o modelo e as suas credenciais', model: 'o nome exato do modelo permitido pelo fornecedor', effort: 'o esforço de raciocínio (ou vazio para usar a predefinição do fornecedor)', executable: 'o comando disponível no runner GitHub Actions, não neste computador' }, + }; + const scope = question.stateId === 'agent-model-defaults' + ? (locale === 'fr' ? 'pour toutes les tâches actives' : 'para todas as tarefas ativas') + : (locale === 'fr' ? 'pour cette tâche' : 'para esta tarefa'); + return `${locale === 'fr' ? 'Définissez' : 'Defina'} ${fields[locale][setting]} ${scope}.`; + } + if (/^repository\.(feature|bugfix|hotfix|release|docs|chore|reconciliation)Tree$/u.test(question.id)) + return locale === 'fr' + ? 'Définissez le préfixe des branches créées par Copilot pour ce type de travail ; il doit suivre votre convention de nommage.' + : 'Defina o prefixo dos ramos criados pelo Copilot para este tipo de trabalho; deve seguir as suas regras de nomes.'; + if (/^projects\.(issue|pullRequest)(Created|InProgress)Column$/u.test(question.id)) + return locale === 'fr' + ? 'Choisissez la valeur du champ Status appliquée à la création ou au début du travail ; ce n’est pas le nom d’une colonne visuelle.' + : 'Escolha o valor do campo Status aplicado na criação ou no início do trabalho; não é o nome de uma coluna visual.'; + const storage = question.id.match(/^storage\.(variables|secrets)\.(defaultScope|organizationVisibility|preserveExisting|overrides)$/u); + if (storage) { + const resource = storage[1] === 'variables' ? 'Variables' : 'Secrets'; + const field = storage[2]; + if (field === 'defaultScope') + return locale === 'fr' + ? `Choisissez si les nouvelles ${resource} sont stockées dans le dépôt ou l’organisation ; ce dernier périmètre peut exiger davantage d’autorisations du PAT.` + : `Escolha se as novas ${resource} ficam no repositório ou na organização; este último âmbito pode exigir mais permissões do PAT.`; + if (field === 'organizationVisibility') + return locale === 'fr' + ? `Choisissez les dépôts pouvant utiliser les ${resource} de l’organisation ; « selected » est l’accès le plus restreint.` + : `Escolha os repositórios que podem usar as ${resource} da organização; «selected» é a visibilidade mais restrita.`; + if (field === 'preserveExisting') + return locale === 'fr' + ? `Conservez les ${resource} existantes déjà applicables au lieu de les écraser pendant la configuration.` + : `Conserve as ${resource} existentes e aplicáveis em vez de as substituir durante a configuração.`; + return locale === 'fr' + ? `Sélectionnez les ${resource} héritées de l’organisation à définir plutôt dans le dépôt.` + : `Selecione as ${resource} herdadas da organização que pretende definir no repositório.`; + } + return undefined; +} + + +/***/ }), + +/***/ 77947: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.setupQuestionPurposes = void 0; +exports.setupQuestionPurpose = setupQuestionPurpose; +/** Field-specific meaning for choices whose label alone is easy to misinterpret. */ +exports.setupQuestionPurposes = { + 'issueWorkflows.enabled': { en: 'Choose the issue workflows Copilot may run; each type has different branch, label and automation effects.', es: 'Elige los flujos de issues que podrá ejecutar Copilot; cada tipo tiene efectos distintos sobre ramas, etiquetas y automatización.' }, + 'repositoryAgentGuidance.enabled': { en: 'Generate repository instructions that tell AI agents how to work safely in this project.', es: 'Genera instrucciones para que los agentes de IA trabajen con seguridad en este proyecto.' }, + 'repositoryAgentGuidance.agentsPointer': { en: 'Choose whether the root AGENTS.md points to generated instructions, is created if missing, or stays untouched.', es: 'Elige si el AGENTS.md raíz apunta a las instrucciones generadas, se crea si falta o permanece intacto.' }, + 'agents.configureIndependently': { en: 'Give planning, findings, review, fixing and testing separate provider and model settings instead of shared defaults.', es: 'Da a planificación, hallazgos, revisión, corrección y pruebas ajustes distintos de proveedor y modelo.' }, + 'repository.mainBranch': { en: 'Name the production branch; release and hotfix automation use it as their production reference.', es: 'Indica la rama de producción; las automatizaciones de release y hotfix la usan como referencia.' }, + 'repository.developmentBranch': { en: 'Name the normal integration branch; branch creation and reconciliation target it.', es: 'Indica la rama de integración habitual; la creación de ramas y la reconciliación la usan.' }, + 'repository.issueManagedBranches': { en: 'Allow the Action to create a linked branch when an issue enters an in-progress state.', es: 'Permite a la Action crear una rama vinculada cuando un issue pasa a «en curso».' }, + 'repository.preBranchSdd': { en: 'Require an approved design document before feature or contract-changing branches are created.', es: 'Exige un diseño aprobado antes de crear ramas de funcionalidad o cambios de contrato.' }, + 'repository.reopenIssueOnPush': { en: 'Reopen a completed issue when someone pushes more work to its linked branch.', es: 'Reabre un issue completado si alguien añade cambios a su rama vinculada.' }, + 'repository.desiredAssigneesCount': { en: 'Set how many people Copilot assigns to a new issue; zero disables automatic assignment.', es: 'Define cuántas personas asigna Copilot a un issue nuevo; cero desactiva la asignación automática.' }, + 'repository.desiredReviewersCount': { en: 'Set how many reviewers Copilot requests for a pull request; zero disables automatic requests.', es: 'Define cuántos revisores solicita Copilot para un pull request; cero desactiva la solicitud automática.' }, + 'repository.inactivityThresholdHours': { en: 'Set how long an issue waits without activity before the enabled inactivity workflow may close it.', es: 'Define cuánto tiempo espera sin actividad un issue antes de que el flujo habilitado pueda cerrarlo.' }, + 'repository.repositoryLocale': { en: 'Choose the BCP-47 language tag for Copilot messages on GitHub; this does not change the setup page language.', es: 'Elige la etiqueta BCP-47 de los mensajes de Copilot en GitHub; no cambia el idioma de esta página.' }, + 'repository.issueLocale': { en: 'Override the GitHub message language for issues; leave empty to inherit the repository language.', es: 'Cambia el idioma de los mensajes de issues; vacío hereda el idioma del repositorio.' }, + 'repository.pullRequestLocale': { en: 'Override the GitHub message language for pull requests; leave empty to inherit the repository language.', es: 'Cambia el idioma de los mensajes de pull requests; vacío hereda el idioma del repositorio.' }, + 'repository.commitPrefixTransforms': { en: 'Define commit-prefix rewrites used by commit automation; leave empty if your conventions need no mapping.', es: 'Define sustituciones de prefijos de commits; déjalo vacío si tus convenciones no necesitan cambios.' }, + 'repository.releaseReconciliationStrategy': { en: 'Choose how completed release changes return to development without losing production lineage.', es: 'Elige cómo vuelven los cambios de una release a desarrollo sin perder su relación con producción.' }, + 'repository.hotfixReconciliationStrategy': { en: 'Choose how an emergency production fix is carried back to ongoing branches.', es: 'Elige cómo se incorpora un arreglo urgente de producción a las demás ramas activas.' }, + 'repository.reconciliationPullRequestMode': { en: 'Choose whether reconciliation PRs are created, merged automatically, queued, or left for a human.', es: 'Elige si los PR de reconciliación se crean, fusionan automáticamente, encolan o quedan para una persona.' }, + 'repository.reconciliationBackmergeMode': { en: 'Choose whether the return merge is direct or goes through a synchronization branch.', es: 'Elige si la integración de vuelta es directa o pasa por una rama de sincronización.' }, + 'repository.hotfixActiveReleasePolicy': { en: 'Choose where a hotfix propagates when a release branch is already active.', es: 'Elige a dónde se propaga un hotfix si ya hay una rama de release activa.' }, + 'repository.reconciliationCleanup': { en: 'Choose which temporary branches are deleted after successful reconciliation.', es: 'Elige qué ramas temporales se eliminan tras una reconciliación correcta.' }, + 'repository.reconciliationIssueCompletion': { en: 'Choose whether the issue that launched reconciliation closes or stays open for follow-up.', es: 'Elige si el issue que inició la reconciliación se cierra o sigue abierto.' }, + 'repository.orchestrationPresentationMode': { en: 'Choose how much release progress and detail appears in the GitHub control-center view.', es: 'Elige cuánto progreso y detalle muestra el centro de control de releases en GitHub.' }, + 'repository.orchestrationDiagrams': { en: 'Include accessible Mermaid diagrams in release status information.', es: 'Incluye diagramas Mermaid accesibles en la información de releases.' }, + 'repository.orchestrationCommentMode': { en: 'Choose whether release lifecycle comments update in place or are posted at milestones.', es: 'Elige si los comentarios de la release se actualizan o se publican en cada hito.' }, + 'ai.pullRequestDescriptionMode': { en: 'Choose whether AI replaces, appends to, preserves, or never edits pull-request descriptions.', es: 'Elige si la IA sustituye, amplía, conserva o nunca modifica las descripciones de pull requests.' }, + 'ai.ignoreFiles': { en: 'List file patterns the AI review should skip; this does not hide those files on GitHub.', es: 'Indica patrones de archivos que la revisión con IA debe omitir; no los oculta en GitHub.' }, + 'ai.membersOnly': { en: 'Allow AI processing only for requests from repository members, not arbitrary external contributors.', es: 'Permite el procesamiento con IA solo para miembros del repositorio, no para colaboradores externos.' }, + 'ai.bugbotSeverity': { en: 'Set the lowest severity Bugbot publishes; lower-severity findings remain unpublished.', es: 'Define la gravedad mínima que publica Bugbot; los hallazgos menores no se publican.' }, + 'ai.bugbotCommentLimit': { en: 'Cap the number of Bugbot review comments in one run to avoid overwhelming a pull request.', es: 'Limita los comentarios de Bugbot por ejecución para no saturar un pull request.' }, + 'ai.bugbotFixVerifyCommands': { en: 'Specify commands that must pass before Bugbot considers an automatic fix verified.', es: 'Indica los comandos que deben pasar antes de considerar verificada una corrección de Bugbot.' }, + 'ai.bugbotEffort': { en: 'Choose the depth of Bugbot reviews; higher effort can take longer and use more model capacity.', es: 'Elige la profundidad de las revisiones de Bugbot; más esfuerzo puede tardar y consumir más.' }, + 'ai.bugbotReviewDrafts': { en: 'Decide whether Bugbot reviews draft pull requests before they are marked ready.', es: 'Decide si Bugbot revisa pull requests en borrador antes de que estén listos.' }, + 'ai.bugbotTraceRules': { en: 'Include the source of each applied review rule in Bugbot summaries for auditability.', es: 'Incluye la procedencia de las reglas aplicadas en los resúmenes de Bugbot para facilitar auditorías.' }, + 'ai.bugbotSuggestedChanges': { en: 'Allow Bugbot to attach safe inline code suggestions to published findings.', es: 'Permite a Bugbot adjuntar sugerencias de código seguras a los hallazgos publicados.' }, + 'ai.bugbotTelemetry': { en: 'Record operational Bugbot metrics without recording repository content.', es: 'Registra métricas operativas de Bugbot sin guardar contenido del repositorio.' }, + 'ai.bugbotFailOnUnresolved': { en: 'Make the Bugbot workflow check fail while actionable findings remain unresolved.', es: 'Hace fallar el check de Bugbot mientras queden hallazgos accionables sin resolver.' }, + 'pullRequestApproval.mode': { en: 'Choose whether the bot recommends approval, may submit a guarded GitHub approval, or does neither.', es: 'Elige si el bot recomienda aprobar, puede publicar una aprobación protegida o no interviene.' }, + 'pullRequestApproval.coverage.minDiffPercent': { en: 'Set the minimum percentage of changed lines that a trusted numeric reporter must prove are covered.', es: 'Define el porcentaje mínimo de líneas modificadas cubiertas que debe acreditar un reporter numérico fiable.' }, + 'pullRequestApproval.coverage.artifactWorkflowName': { en: 'Name the exact trusted workflow that publishes the copilot-diff-coverage-v1 artifact.', es: 'Indica el workflow fiable exacto que publica el artefacto copilot-diff-coverage-v1.' }, + 'pullRequestApproval.coverage.reporterAttested': { en: 'Confirm you inspected the numeric coverage reporter in that exact workflow, not just its green check.', es: 'Confirma que revisaste el reporter numérico en ese workflow exacto, no solo su check verde.' }, + 'projects.enabled': { en: 'Decide whether Copilot should add issues and pull requests to existing GitHub Projects; the setup PAT is needed to list private organization Projects later.', es: 'Decide si Copilot debe añadir issues y pull requests a Projects existentes; el PAT de setup hará falta después para consultar Projects privados de la organización.' }, + 'projects.ids': { en: 'Choose existing Projects by title after PAT verification, or enter the positive number in each Project URL; PVT_ node IDs are not used.', es: 'Elige Projects existentes por título tras verificar el PAT o introduce el número positivo de cada URL; no se usan IDs de nodo PVT_.' }, + 'projects.statusVerified': { en: 'Confirm that all four chosen Status options actually exist in every selected Project when GitHub could not verify their fields.', es: 'Confirma que las cuatro opciones Status existen en todos los Projects elegidos cuando GitHub no pudo comprobar sus campos.' }, + createInitialTag: { en: 'Create v1.0.0 only if this repository has no version tag yet.', es: 'Crea v1.0.0 solo si este repositorio todavía no tiene un tag de versión.' }, + manageRepositoryVariables: { en: 'Allow setup to create or update GitHub Actions Variables required by selected workflows.', es: 'Permite a setup crear o actualizar Variables de GitHub Actions necesarias para los workflows elegidos.' }, + manageRepositorySecrets: { en: 'Allow setup to validate and install required GitHub Actions Secrets, including the bot PAT when needed.', es: 'Permite a setup validar e instalar Secrets de GitHub Actions, incluido el PAT del bot cuando haga falta.' }, +}; +function setupQuestionPurpose(question) { + const exact = exports.setupQuestionPurposes[question.id]; + if (exact) + return exact; + if (question.id.startsWith('features.')) + return { + en: `Enable or disable ${question.label.toLowerCase()}. Disabling it removes its automation and conditional permission needs from this setup.`, + es: 'Activa o desactiva esta función. Si la desactivas, setup no instalará su automatización ni solicitará sus permisos condicionales.', + }; + if (/^agents\.[^.]+\.provider$/u.test(question.id)) + return { + en: 'Choose the agent CLI for this task in GitHub Actions; the provider determines the runner command and credentials.', + es: 'Elige el agente CLI de esta tarea en GitHub Actions; determina el comando y las credenciales del runner.', + }; + const agentSetting = question.id.match(/^agents\.[^.]+\.(modelProvider|model|effort|executable)$/u)?.[1]; + if (agentSetting) { + const shared = question.stateId === 'agent-model-defaults'; + const scope = shared ? { en: 'enabled agent tasks without a per-role override', es: 'las tareas activas del agente sin una excepción propia' } + : { en: 'this agent task', es: 'esta tarea del agente' }; + const setting = { + modelProvider: { en: 'the service that supplies the model and its credentials', es: 'el servicio que proporciona el modelo y sus credenciales' }, + model: { en: 'the exact model name allowed by the selected provider', es: 'el nombre exacto del modelo permitido por el proveedor elegido' }, + effort: { en: 'the reasoning-effort level, or leave empty for the provider default', es: 'el nivel de razonamiento, o vacío para usar el valor del proveedor' }, + executable: { en: 'the executable available on the GitHub Actions runner, not this computer', es: 'el ejecutable disponible en el runner de GitHub Actions, no en este ordenador' }, + }; + return { + en: `Set ${setting[agentSetting].en} for ${scope.en}.`, + es: `Define ${setting[agentSetting].es} para ${scope.es}.`, + }; + } + if (/^repository\.(feature|bugfix|hotfix|release|docs|chore|reconciliation)Tree$/u.test(question.id)) + return { + en: 'Set the prefix of branches Copilot creates for this work type; it must match your naming policy.', + es: 'Define el prefijo de las ramas que Copilot crea para este tipo de trabajo; debe seguir tus reglas de nombres.', + }; + if (/^projects\.(issue|pullRequest)(Created|InProgress)Column$/u.test(question.id)) + return { + en: 'Choose the existing Status field option applied when this issue or pull request is created or enters progress; it is not a board-view column name.', + es: 'Elige la opción existente del campo Status al crear este issue o pull request o pasarlo a «en curso»; no es el nombre de una columna visual.', + }; + const storageSetting = question.id.match(/^storage\.(variables|secrets)\.(defaultScope|organizationVisibility|preserveExisting|overrides)$/u); + if (storageSetting) { + const resource = storageSetting[1] === 'variables' ? 'Variables' : 'Secrets'; + const setting = { + defaultScope: { en: `Choose whether new ${resource} live in the repository or organization; organization storage can need extra PAT grants.`, es: `Elige si los ${resource} nuevos se guardan en el repositorio o la organización; este último ámbito puede exigir más permisos del PAT.` }, + organizationVisibility: { en: `Choose which repositories can use organization ${resource}; selected is the narrowest visibility.`, es: `Elige qué repositorios pueden usar los ${resource} de la organización; «selected» es la visibilidad más restringida.` }, + preserveExisting: { en: `Keep effective existing ${resource} instead of overwriting them during setup.`, es: `Conserva los ${resource} existentes que ya se aplican, en lugar de sobrescribirlos durante setup.` }, + overrides: { en: `Select inherited organization ${resource} that should instead be set at repository scope.`, es: `Selecciona los ${resource} heredados de la organización que quieras definir en el repositorio.` }, + }; + return setting[storageSetting[2]]; + } + return undefined; +} + + +/***/ }), + +/***/ 3927: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.spanishQuestionLabels = void 0; +exports.spanishQuestionLabel = spanishQuestionLabel; +exports.spanishQuestionLabels = { + 'features.issues': 'Automatizar issues: ramas, etiquetas, proyectos y ciclo de vida', + 'features.pullRequests': 'Automatizar pull requests: revisión, descripción y ciclo de vida', + 'features.commits': 'Automatizar commits: progreso, tamaño y análisis de Bugbot', + 'features.issueComments': 'Responder a comentarios de issues y permitir correcciones de Bugbot', + 'features.pullRequestComments': 'Responder a comentarios de pull requests y permitir correcciones de Bugbot', + 'features.agentProvisioning': 'Comprobar la instalación de agentes CLI en GitHub Actions', + 'features.credentialHealth': 'Comprobar el estado de las credenciales remotas', + 'features.inactiveIssueClosure': 'Cerrar issues sin actividad tras el plazo configurado', + 'features.issueTemplates': 'Instalar plantillas de issues', + 'features.pullRequestTemplate': 'Instalar plantilla de pull request', + 'issueWorkflows.enabled': 'Tipos de flujo de issues que quieres activar', + 'repositoryAgentGuidance.enabled': '¿Generar instrucciones para agentes en el repositorio?', + 'repositoryAgentGuidance.agentsPointer': 'Cómo descubrir las instrucciones desde AGENTS.md', + 'agents.findings.modelProvider': 'Proveedor de modelo compartido (salvo configuración propia de una tarea)', + 'agents.findings.model': 'Modelo compartido (salvo configuración propia de una tarea)', + 'agents.findings.effort': 'Esfuerzo de razonamiento compartido (los ajustes por tarea se conservan)', + 'agents.findings.executable': 'Ejecutable compartido validado (los ajustes por tarea se conservan)', + 'agents.configureIndependently': '¿Configurar modelo y comando por tarea?', + 'repository.mainBranch': 'Rama de producción', + 'repository.developmentBranch': 'Rama de desarrollo', + 'repository.featureTree': 'Prefijo de ramas de funcionalidad', + 'repository.bugfixTree': 'Prefijo de ramas de corrección', + 'repository.hotfixTree': 'Prefijo de ramas de hotfix', + 'repository.releaseTree': 'Prefijo de ramas de release', + 'repository.docsTree': 'Prefijo de ramas de documentación', + 'repository.choreTree': 'Prefijo de ramas de mantenimiento', + 'repository.issueManagedBranches': '¿Puede la Action crear ramas vinculadas a issues?', + 'repository.preBranchSdd': '¿Exigir un SDD antes de crear ciertas ramas?', + 'repository.reopenIssueOnPush': '¿Reabrir issues cerrados al actualizar su rama?', + 'repository.desiredAssigneesCount': 'Número deseado de personas asignadas a issues', + 'repository.desiredReviewersCount': 'Número deseado de revisores de pull requests', + 'repository.inactivityThresholdHours': 'Horas sin actividad antes de cerrar un issue en espera', + 'repository.repositoryLocale': 'Idioma de los mensajes del repositorio', + 'repository.issueLocale': 'Idioma de los issues (vacío: heredar)', + 'repository.pullRequestLocale': 'Idioma de los pull requests (vacío: heredar)', + 'repository.commitPrefixTransforms': 'Transformación de prefijos de commits', + 'repository.releaseReconciliationStrategy': 'Estrategia para reconciliar releases', + 'repository.hotfixReconciliationStrategy': 'Estrategia para reconciliar hotfixes', + 'repository.reconciliationPullRequestMode': 'Modo de pull requests de reconciliación', + 'repository.reconciliationBackmergeMode': 'Modo de integración de vuelta', + 'repository.hotfixActiveReleasePolicy': 'Destino del hotfix durante una release activa', + 'repository.reconciliationTree': 'Prefijo de ramas de reconciliación', + 'repository.reconciliationCleanup': 'Limpieza de ramas tras reconciliar', + 'repository.reconciliationIssueCompletion': 'Qué hacer con el issue al terminar', + 'repository.orchestrationPresentationMode': 'Nivel de detalle del centro de control de releases', + 'repository.orchestrationDiagrams': '¿Mostrar diagramas accesibles de releases?', + 'repository.orchestrationCommentMode': 'Cómo publicar comentarios del ciclo de release', + 'ai.pullRequestDescriptionMode': 'Cómo actualizar la descripción de los pull requests', + 'ai.ignoreFiles': 'Archivos que la IA debe ignorar', + 'ai.membersOnly': '¿Limitar el procesamiento de IA a miembros del repositorio?', + 'ai.includeReasoning': '¿Incluir el razonamiento adicional del proveedor?', + 'ai.bugbotSeverity': 'Gravedad mínima para publicar hallazgos de Bugbot', + 'ai.bugbotCommentLimit': 'Máximo de comentarios de Bugbot por ejecución', + 'ai.bugbotFixVerifyCommands': 'Comandos para verificar correcciones de Bugbot', + 'ai.bugbotDryRun': '¿Analizar sin publicar cambios de Bugbot?', + 'ai.bugbotEffort': 'Profundidad del análisis de Bugbot', + 'ai.bugbotReviewDrafts': '¿Revisar pull requests en borrador?', + 'ai.bugbotTraceRules': '¿Indicar qué fuentes de reglas se aplicaron?', + 'ai.bugbotSuggestedChanges': '¿Publicar sugerencias de cambio seguras?', + 'ai.bugbotTelemetry': '¿Registrar métricas de Bugbot sin contenido?', + 'ai.bugbotFailOnUnresolved': '¿Bloquear el check si quedan hallazgos sin resolver?', + 'ai.bugbotOrganizationRules': 'Reglas generales de Bugbot, una por línea', + 'ai.provisioningMode': 'Cómo preparar el agente CLI en el runner', + 'pullRequestApproval.mode': '¿Qué puede hacer el bot con las aprobaciones de PR?', + 'pullRequestApproval.testChecks': '¿Qué checks de CI son fiables para aprobar PRs?', + 'pullRequestApproval.producerAttested': '¿Has comprobado el job, la App y el paso obligatorio de cobertura?', + 'pullRequestApproval.coverage.mode': 'Cómo demostrar que se cumple la cobertura', + 'pullRequestApproval.coverage.checkName': 'Check fiable que exige la cobertura', + 'pullRequestApproval.coverage.minDiffPercent': 'Cobertura mínima de líneas modificadas (0–100)', + 'pullRequestApproval.coverage.artifactWorkflowName': 'Workflow que publica copilot-diff-coverage-v1', + 'pullRequestApproval.coverage.reporterAttested': '¿Has comprobado que el reporter numérico está instalado?', + 'projects.enabled': '¿Quieres integrar Projects de GitHub?', + 'projects.ids': 'Selecciona Projects existentes o indica los números de sus URL', + 'projects.statusVerified': '¿Has comprobado en GitHub los cuatro valores Status exactos de cada Project elegido?', + 'projects.issueCreatedColumn': 'Estado Status de nuevos issues', + 'projects.pullRequestCreatedColumn': 'Estado Status de nuevos pull requests', + 'projects.issueInProgressColumn': 'Estado Status de issues en curso', + 'projects.pullRequestInProgressColumn': 'Estado Status de pull requests en curso', + createInitialTag: '¿Crear v1.0.0 si todavía no existe ningún tag?', + manageRepositoryVariables: '¿Crear o actualizar Variables de GitHub Actions?', + manageRepositorySecrets: '¿Validar y configurar Secrets de GitHub Actions?', +}; +const roleNames = { + planner: 'Planificación', findings: 'Hallazgos', reviewer: 'Revisión', fixer: 'Corrección', tester: 'Pruebas', +}; +function spanishQuestionLabel(question) { + if (exports.spanishQuestionLabels[question.id]) + return exports.spanishQuestionLabels[question.id]; + const agent = question.id.match(/^agents\.(planner|findings|reviewer|fixer|tester)\.(provider|modelProvider|model|effort|executable)$/u); + if (agent) { + const field = { provider: 'agente CLI', modelProvider: 'proveedor del modelo', model: 'modelo', effort: 'esfuerzo', executable: 'comando ejecutable' }; + return `${roleNames[agent[1]]}: ${field[agent[2]]}`; + } + const storage = question.id.match(/^storage\.(variables|secrets)\.(defaultScope|organizationVisibility|preserveExisting|overrides)$/u); + if (storage) { + const resource = storage[1] === 'variables' ? 'Variables' : 'Secrets'; + const field = { defaultScope: 'ámbito predeterminado', organizationVisibility: 'visibilidad en la organización', preserveExisting: 'conservar los existentes', overrides: 'excepciones de ámbito' }; + return `${resource}: ${field[storage[2]]}`; + } + return question.label; +} + + /***/ }), /***/ 6009: @@ -47998,35 +49147,49 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.createSetupQuestionnaire = createSetupQuestionnaire; exports.createSetupPermissionIntentQuestionnaire = createSetupPermissionIntentQuestionnaire; exports.createSetupReviewState = createSetupReviewState; +exports.refreshSetupQuestionnaireQuestion = refreshSetupQuestionnaireQuestion; +exports.setupQuestionnaireProgress = setupQuestionnaireProgress; +exports.reopenSetupQuestionnaireGroup = reopenSetupQuestionnaireGroup; +exports.setupQuestionIdsForGroup = setupQuestionIdsForGroup; +exports.setupBasicSkippedQuestionIds = setupBasicSkippedQuestionIds; +exports.setupEditableGroups = setupEditableGroups; exports.transitionSetupQuestionnaire = transitionSetupQuestionnaire; exports.enterSetupConfirmation = enterSetupConfirmation; exports.finishSetupQuestionnaire = finishSetupQuestionnaire; exports.setupQuestionnaireStateLabel = setupQuestionnaireStateLabel; +exports.setupQuestionContentInventory = setupQuestionContentInventory; const setup_configuration_clone_policy_1 = __nccwpck_require__(85881); const setup_configuration_defaults_1 = __nccwpck_require__(23381); const issue_workflow_profile_1 = __nccwpck_require__(26744); +const setup_project_selection_policy_1 = __nccwpck_require__(73750); const AGENT_PROVIDERS = ['codex', 'opencode', 'cursor']; const MODEL_PROVIDERS = ['openai', 'anthropic', 'google', 'openrouter', 'opencode', 'local']; const PERMISSION_INTENT_QUESTION_IDS = new Set([ 'features.issues', 'features.pullRequests', 'issueWorkflows.enabled', - 'pullRequestApproval.mode', 'projects.ids', 'createInitialTag', + 'pullRequestApproval.mode', 'projects.enabled', 'createInitialTag', 'manageRepositoryVariables', 'manageRepositorySecrets', 'storage.variables.defaultScope', 'storage.variables.preserveExisting', 'storage.secrets.defaultScope', 'storage.secrets.preserveExisting', ]); function createSetupQuestionnaire(configuration, context = {}) { const draft = (0, setup_configuration_clone_policy_1.cloneSetupConfiguration)(configuration); - const question = questions(draft, false, context, 'full')[0]; + const independently = hasIndependentAgentSettings(draft); + const question = questions(draft, independently, context, 'full')[0]; return question - ? { stateId: question.stateId, draft, question, terminal: 'collecting', configureIndependently: false, phase: 'full' } - : { stateId: 'review', draft, terminal: 'review', configureIndependently: false, phase: 'full' }; + ? { stateId: question.stateId, draft, question, terminal: 'collecting', configureIndependently: independently, phase: 'full' } + : { stateId: 'review', draft, terminal: 'review', configureIndependently: independently, phase: 'full' }; +} +function hasIndependentAgentSettings(draft) { + const shared = draft.agents.findings; + return setup_configuration_defaults_1.SETUP_AGENT_TASKS.filter(task => task !== 'findings').some(task => ['modelProvider', 'model', 'effort', 'executable'].some(field => draft.agents[task][field] !== shared[field])); } function createSetupPermissionIntentQuestionnaire(configuration, context = {}) { const draft = (0, setup_configuration_clone_policy_1.cloneSetupConfiguration)(configuration); + const projectsWanted = context.projectsWanted ?? Boolean(draft.projects.ids.trim()); const question = questions(draft, false, context, 'permission-intent')[0]; return question - ? { stateId: question.stateId, draft, question, terminal: 'collecting', configureIndependently: false, phase: 'permission-intent', answeredQuestionIds: [] } - : { stateId: 'review', draft, terminal: 'review', configureIndependently: false, phase: 'permission-intent', answeredQuestionIds: [] }; + ? { stateId: question.stateId, draft, question, terminal: 'collecting', configureIndependently: false, phase: 'permission-intent', answeredQuestionIds: [], projectsWanted } + : { stateId: 'review', draft, terminal: 'review', configureIndependently: false, phase: 'permission-intent', answeredQuestionIds: [], projectsWanted }; } function createSetupReviewState(configuration) { return { @@ -48036,6 +49199,68 @@ function createSetupReviewState(configuration) { configureIndependently: false, }; } +/** Re-project the current question after a read-only discovery without replaying answers. */ +function refreshSetupQuestionnaireQuestion(state, context) { + if (state.terminal !== 'collecting' || !state.question) + return state; + const question = questions(state.draft, state.configureIndependently, context, state.phase ?? 'full') + .find(candidate => candidate.id === state.question?.id); + return question ? { ...state, question, validation: undefined } : state; +} +/** The denominator follows the currently applicable, unskipped questions. */ +function setupQuestionnaireProgress(state, context) { + if (state.terminal !== 'collecting' || !state.question) + return undefined; + const visible = questions(state.draft, state.configureIndependently, context, state.phase ?? 'full'); + const index = visible.findIndex(item => item.id === state.question?.id); + if (index < 0) + return undefined; + const group = state.question.stateId; + const groupQuestions = visible.filter(item => item.stateId === group); + return { position: index + 1, total: visible.length, groupPosition: groupQuestions.findIndex(item => item.id === state.question?.id) + 1, + groupTotal: groupQuestions.length, group }; +} +/** Reopen an already answered group for final-plan correction without clearing unrelated values. */ +function reopenSetupQuestionnaireGroup(state, group, context) { + if (state.terminal !== 'review') + return undefined; + const first = questions(state.draft, state.configureIndependently, context, 'full').find(item => item.stateId === group); + return first ? { ...state, stateId: first.stateId, terminal: 'collecting', question: first, validation: undefined, + phase: 'full', answeredQuestionIds: [] } : undefined; +} +function setupQuestionIdsForGroup(group) { + return definitions().filter(item => item.stateId === group).map(item => item.id); +} +/** Basic changes presentation only: security- and permission-driving decisions stay visible. */ +function setupBasicSkippedQuestionIds(configuration) { + const defaults = configuration ? (0, setup_configuration_defaults_1.createDefaultSetupConfiguration)() : undefined; + const advancedRepository = new Set([ + 'featureTree', 'bugfixTree', 'hotfixTree', 'releaseTree', 'docsTree', 'choreTree', + 'reconciliationTree', 'reopenIssueOnPush', 'inactivityThresholdHours', + 'issueLocale', 'pullRequestLocale', 'commitPrefixTransforms', + ]); + const advancedBugbot = new Set([ + 'pullRequestDescriptionMode', 'ignoreFiles', 'includeReasoning', 'bugbotCommentLimit', + 'bugbotFixVerifyCommands', 'bugbotEffort', 'bugbotReviewDrafts', 'bugbotTraceRules', + 'bugbotSuggestedChanges', 'bugbotOrganizationRules', + ]); + return definitions().filter(definition => definition.id === 'agents.findings.effort' + || definition.id === 'agents.findings.executable' + || (definition.id.startsWith('agents.') && definition.id.endsWith('.provider') && definition.id !== 'agents.findings.provider') + || (definition.id.startsWith('repository.') && advancedRepository.has(definition.id.slice('repository.'.length))) + || (definition.id.startsWith('ai.') && advancedBugbot.has(definition.id.slice('ai.'.length)))).filter(definition => !configuration || JSON.stringify(valueAtPath(configuration, definition.id)) + === JSON.stringify(valueAtPath(defaults, definition.id))).map(definition => definition.id); +} +function valueAtPath(value, path) { + return path.split('.').reduce((current, key) => current && typeof current === 'object' + ? current[key] : undefined, value); +} +function setupEditableGroups(configuration) { + // Projects can be enabled at review even if the operator declined it before + // the PAT handoff. The re-run audits any newly required grant before Apply. + const visible = questions(configuration, hasIndependentAgentSettings(configuration), { projectsWanted: true }, 'full'); + return [...new Set(visible.map(item => item.stateId))]; +} function transitionSetupQuestionnaire(state, event, context = {}) { if (state.terminal !== 'collecting' || !state.question) return state; @@ -48047,8 +49272,26 @@ function transitionSetupQuestionnaire(state, event, context = {}) { configureIndependently: state.configureIndependently, phase: state.phase, answeredQuestionIds: state.answeredQuestionIds, + projectsWanted: state.projectsWanted, }; } + if (event.kind === 'back') { + const visible = questions(state.draft, state.configureIndependently, context, state.phase ?? 'full'); + const index = visible.findIndex(item => item.id === state.question?.id); + if (index <= 0) + return { ...state, validation: 'This is the first question in this pass. Review it or cancel setup.' }; + const previous = visible[index - 1]; + return { ...state, stateId: previous.stateId, question: previous, validation: undefined, + answeredQuestionIds: state.answeredQuestionIds?.filter(id => visible.findIndex(item => item.id === id) < index - 1) }; + } + if (state.question.id === 'projects.statusVerified' && ['n', 'no', 'false', '0'].includes(event.value.normalize('NFKC').trim().toLowerCase())) { + const selection = questions(state.draft, state.configureIndependently, context, state.phase ?? 'full') + .find(question => question.id === 'projects.ids'); + if (selection) + return { ...state, question: selection, stateId: 'projects', + validation: 'Status values were not confirmed. Choose compatible Projects, then review their Status options again.', + answeredQuestionIds: state.answeredQuestionIds?.filter(id => id !== 'projects.ids' && !id.startsWith('projects.')) }; + } const parsed = parseAnswer(state.question, event.value); if ('error' in parsed) { return { @@ -48060,11 +49303,16 @@ function transitionSetupQuestionnaire(state, event, context = {}) { const configureIndependently = state.question.id === 'agents.configureIndependently' ? Boolean(parsed.value) : state.configureIndependently; - const draft = applyAnswer(state.draft, state.question, parsed.value); + const draft = applyAnswer(state.draft, state.question, parsed.value, state.configureIndependently); + const projectsWanted = state.question.id === 'projects.enabled' ? Boolean(parsed.value) : state.projectsWanted; const answeredQuestionIds = [...(state.answeredQuestionIds ?? []), state.question.id]; const nextQuestions = questions(draft, configureIndependently, context, state.phase ?? 'full'); - const nextIndex = nextQuestions.findIndex((question) => question.id === state.question?.id); - const next = nextQuestions[nextIndex + 1]; + // A just-answered question may become inapplicable (for example, clearing + // Projects removes its dependent fields). Advance by canonical definition + // order; indexing the new visible list at -1 would restart the wizard. + const definitionOrder = definitions().map(definition => definition.id); + const currentOrder = definitionOrder.indexOf(state.question.id); + const next = nextQuestions.find(question => definitionOrder.indexOf(question.id) > currentOrder); return next ? { stateId: next.stateId, @@ -48074,8 +49322,9 @@ function transitionSetupQuestionnaire(state, event, context = {}) { configureIndependently, phase: state.phase, answeredQuestionIds, + projectsWanted, } - : { stateId: 'review', draft, terminal: 'review', configureIndependently, phase: state.phase, answeredQuestionIds }; + : { stateId: 'review', draft, terminal: 'review', configureIndependently, phase: state.phase, answeredQuestionIds, projectsWanted }; } function enterSetupConfirmation(state) { if (state.terminal !== 'review') @@ -48112,7 +49361,7 @@ function setupQuestionnaireStateLabel(stateId) { })[stateId]; } function questions(draft, independently, context, phase) { - return definitions().filter((definition) => (phase === 'full' || PERMISSION_INTENT_QUESTION_IDS.has(definition.id)) + return definitions().filter((definition) => (phase === 'full' ? definition.id !== 'projects.enabled' : PERMISSION_INTENT_QUESTION_IDS.has(definition.id)) && !context.skipQuestionIds?.includes(definition.id) && (definition.applies?.(draft, independently, context) ?? true)) .map((definition) => toQuestion(definition, draft, context)); @@ -48150,20 +49399,30 @@ function definitions() { ...setup_configuration_defaults_1.SETUP_AGENT_TASKS.map((task) => ({ stateId: 'agent-runtime', id: `agents.${task}.provider`, label: `${formatTask(task)} runtime`, kind: 'choice', choices: AGENT_PROVIDERS, })), - { stateId: 'agent-model-defaults', id: 'agents.findings.modelProvider', label: 'Model provider for all tasks', kind: 'choice', choices: MODEL_PROVIDERS }, - { stateId: 'agent-model-defaults', id: 'agents.findings.model', label: 'Model name for all tasks', kind: 'text' }, - { stateId: 'agent-model-defaults', id: 'agents.findings.effort', label: 'Reasoning effort for all tasks (empty uses provider default)', kind: 'text' }, - { stateId: 'agent-model-defaults', id: 'agents.findings.executable', label: 'Validated executable for all tasks (empty uses the manifest basename)', kind: 'text' }, - { stateId: 'agent-model-defaults', id: 'agents.configureIndependently', label: 'Configure model provider, model, effort, and executable independently for every task?', kind: 'boolean', read: () => false }, + { stateId: 'agent-model-defaults', id: 'agents.findings.modelProvider', label: 'Shared model provider (unless a role has its own setting)', kind: 'choice', choices: MODEL_PROVIDERS }, + { stateId: 'agent-model-defaults', id: 'agents.findings.model', label: 'Shared model name (unless a role has its own setting)', kind: 'text' }, + { stateId: 'agent-model-defaults', id: 'agents.findings.effort', label: 'Shared reasoning effort (empty uses provider default; per-role overrides stay separate)', kind: 'text' }, + { stateId: 'agent-model-defaults', id: 'agents.findings.executable', label: 'Shared validated executable (empty uses manifest basename; per-role overrides stay separate)', kind: 'text' }, + { stateId: 'agent-model-defaults', id: 'agents.configureIndependently', label: 'Configure model provider, model, effort, and executable independently for every task?', kind: 'boolean', read: draft => hasIndependentAgentSettings(draft) }, ...setup_configuration_defaults_1.SETUP_AGENT_TASKS.filter((task) => task !== 'findings').flatMap((task) => agentOverrideQuestions(task)), ...repositoryQuestions(), ...deploymentQuestions(), ...bugbotQuestions(), ...approvalQuestions(), - { stateId: 'projects', id: 'projects.ids', label: 'GitHub Project IDs (comma-separated, empty skips integration)', kind: 'text' }, + { stateId: 'projects', id: 'projects.enabled', label: 'Integrate existing GitHub Projects with issue and pull-request automation?', kind: 'boolean', + read: (draft, context) => context.projectsWanted ?? Boolean(draft.projects.ids.trim()), + applies: draft => draft.features.issues !== false || draft.features.pullRequests !== false }, + { stateId: 'projects', id: 'projects.ids', label: 'Select existing GitHub Projects (or enter Project numbers from their URLs)', kind: 'text', + applies: (draft, _independent, context) => (draft.features.issues !== false || draft.features.pullRequests !== false) && context.projectsWanted !== false }, ...['issueCreatedColumn', 'pullRequestCreatedColumn', 'issueInProgressColumn', 'pullRequestInProgressColumn'].map((field) => ({ stateId: 'projects', id: `projects.${field}`, label: projectLabel(field), kind: 'text', applies: (config) => Boolean(config.projects.ids.trim()), })), + { stateId: 'projects', id: 'projects.statusVerified', + label: 'Have you checked every selected Project in GitHub and confirmed all four exact Status values?', + kind: 'boolean', read: () => false, + applies: (draft, _independently, context) => Boolean(draft.projects.ids.trim()) + && (0, setup_project_selection_policy_1.sharedProjectStatusOptions)(draft.projects.ids, context.projectDiscovery?.candidates ?? []).state === 'unavailable', + }, { stateId: 'provisioning', id: 'createInitialTag', label: 'Create v1.0.0 when no version tag exists?', kind: 'boolean' }, { stateId: 'provisioning', id: 'manageRepositoryVariables', label: 'Create/update GitHub Actions Variables?', kind: 'boolean' }, { stateId: 'provisioning', id: 'manageRepositorySecrets', label: 'Validate and provision required GitHub Actions Secrets?', kind: 'boolean' }, @@ -48171,6 +49430,12 @@ function definitions() { ...storageQuestions('secrets'), ]; } +/** Stable content inventory for documentation and localization audits; never answers questions. */ +function setupQuestionContentInventory() { + return definitions().map(({ stateId, id, label, kind, choices }) => ({ + stateId, id, label, kind, choices, defaultValue: '', + })); +} function agentOverrideQuestions(task) { const applies = (_draft, independently) => independently; return [ @@ -48252,12 +49517,6 @@ function approvalQuestions() { read: draft => draft.pullRequestApproval.testChecks.map(check => `${check.name}|${check.sourceAppId}|${check.workflowName}`).join(';'), applies: draft => draft.features.pullRequests !== false && draft.pullRequestApproval.mode !== 'off', }, - { - stateId: 'pull-request-approval', id: 'pullRequestApproval.producerAttested', - label: 'Have you verified each exact check, source App ID, workflow, and coverage-enforcing CI step?', - kind: 'boolean', - applies: draft => draft.features.pullRequests !== false && draft.pullRequestApproval.mode !== 'off', - }, { stateId: 'pull-request-approval', id: 'pullRequestApproval.coverage.mode', label: 'Coverage evidence mode', kind: 'choice', choices: ['check', 'numeric'], @@ -48266,7 +49525,7 @@ function approvalQuestions() { { stateId: 'pull-request-approval', id: 'pullRequestApproval.coverage.checkName', label: 'Exact trusted check that enforces the coverage budget (no inferred percentage)', - kind: 'text', + kind: 'choice', applies: draft => draft.features.pullRequests !== false && draft.pullRequestApproval.mode !== 'off', }, { @@ -48293,6 +49552,12 @@ function approvalQuestions() { applies: draft => draft.features.pullRequests !== false && draft.pullRequestApproval.mode !== 'off' && draft.pullRequestApproval.coverage.mode === 'numeric', }, + { + stateId: 'pull-request-approval', id: 'pullRequestApproval.producerAttested', + label: 'Have you verified each exact check, source App ID, workflow, and coverage-enforcing CI step?', + kind: 'boolean', + applies: draft => draft.features.pullRequests !== false && draft.pullRequestApproval.mode !== 'off', + }, ]; } function storageQuestions(kind) { @@ -48320,17 +49585,54 @@ function choice(field, label, choices) { return { stateId: 'deployment', id: `repository.${field}`, label, kind: 'choice', choices }; } function toQuestion(definition, draft, context) { + const branchScopedCandidates = context.approvalCheckCandidates?.map(candidate => candidate.requiredByRuleset?.branch !== draft.repository.developmentBranch + ? { ...candidate, requiredByRuleset: undefined } : candidate); + const producerCandidates = definition.id === 'pullRequestApproval.testChecks' ? branchScopedCandidates + : definition.id === 'pullRequestApproval.coverage.checkName' ? branchScopedCandidates?.filter(candidate => draft.pullRequestApproval.testChecks.some(check => check.name === candidate.name + && check.sourceAppId === candidate.sourceAppId && check.workflowName === candidate.workflowName)) : undefined; + const coverageChoices = definition.id === 'pullRequestApproval.coverage.checkName' + ? [...new Set(draft.pullRequestApproval.testChecks.map(check => check.name))] : undefined; const allowedNames = definition.kind === 'scope-overrides' ? inheritedNames(definition.id.includes('.variables.') ? 'variables' : 'secrets', draft, context) : undefined; + const projectQuestion = definition.id === 'projects.ids'; + const statusQuestion = /^projects\.(issue|pullRequest)(Created|InProgress)Column$/u.test(definition.id); + const projectCandidates = context.projectDiscovery?.candidates ?? []; + const projectStatus = statusQuestion + ? (0, setup_project_selection_policy_1.sharedProjectStatusOptions)(draft.projects.ids, projectCandidates) : undefined; return { stateId: definition.stateId, id: definition.id, label: definition.label, - kind: definition.kind, - defaultValue: definition.read?.(draft) ?? readPath(draft, definition.id, allowedNames), - ...(definition.choices ? { choices: definition.choices } : {}), + kind: definition.id === 'pullRequestApproval.testChecks' ? 'producer-select' + : projectQuestion ? 'project-select' + : statusQuestion && projectStatus?.state === 'observed' ? 'choice' : definition.kind, + defaultValue: definition.read?.(draft, context) ?? readPath(draft, definition.id, allowedNames), + ...(coverageChoices ? { choices: coverageChoices } : projectStatus?.state === 'observed' + ? { choices: projectStatus.options } : definition.choices ? { choices: definition.choices } : {}), ...(allowedNames ? { allowedNames } : {}), + ...(producerCandidates?.length ? { producerCandidates } : {}), + ...(definition.id === 'pullRequestApproval.coverage.checkName' + ? { trustedProducers: draft.pullRequestApproval.testChecks } : {}), + ...(definition.id === 'pullRequestApproval.testChecks' && context.approvalCheckDiscoveryStatus + ? { discoveryStatus: context.approvalCheckDiscoveryStatus, discoveryTruncated: context.approvalCheckDiscoveryTruncated, + discoveryRetryRemaining: context.discoveryRetryRemaining?.checks ?? 0 } : {}), + ...(projectQuestion ? { discoveryStatus: context.projectDiscovery?.status ?? 'unavailable', + discoveryTruncated: context.projectDiscovery?.truncated, + ...(context.projectDiscovery && context.projectDiscovery.status !== 'unsupported' && context.discoveryRetryRemaining + ? { discoveryRetryRemaining: context.discoveryRetryRemaining.projects } : {}), + projectCandidates, projectOwner: context.projectOwner } : {}), + ...(statusQuestion && projectStatus ? { statusOptionState: projectStatus.state } : {}), + ...(definition.id === 'projects.statusVerified' ? { projectStatusValues: [ + { transition: 'issueCreated', value: draft.projects.issueCreatedColumn }, + { transition: 'pullRequestCreated', value: draft.projects.pullRequestCreatedColumn }, + { transition: 'issueInProgress', value: draft.projects.issueInProgressColumn }, + { transition: 'pullRequestInProgress', value: draft.projects.pullRequestInProgressColumn }, + ] } : {}), + ...(definition.id === 'repository.mainBranch' && context.branchSources + ? { suggestionSource: context.branchSources.main } : {}), + ...(definition.id === 'repository.developmentBranch' && context.branchSources + ? { suggestionSource: context.branchSources.development } : {}), }; } function readPath(configuration, path, allowedNames) { @@ -48343,6 +49645,39 @@ function readPath(configuration, path, allowedNames) { } function parseAnswer(question, raw) { const input = raw.normalize('NFKC').trim(); + if (question.id === 'projects.statusVerified') + return ['y', 'yes', 'true', '1'].includes(input.toLowerCase()) + ? { value: true } : { error: 'Open every selected Project in GitHub and confirm that all four exact Status values exist. Answer Yes after checking, or No to choose Projects again.' }; + if (question.id === 'projects.ids') { + const parsed = (0, setup_project_selection_policy_1.parseSetupProjectSelection)(input || String(question.defaultValue), question.projectOwner); + if ('error' in parsed) + return parsed; + const status = (0, setup_project_selection_policy_1.sharedProjectStatusOptions)(parsed.value, question.projectCandidates ?? []); + if (status.state === 'incompatible') + return { error: 'Selected Projects have no common Status option. Choose compatible Projects or configure them separately.' }; + return parsed; + } + if (question.id === 'pullRequestApproval.testChecks') { + const entries = (input || String(question.defaultValue)).split(';').map(item => item.trim()).filter(Boolean) + .flatMap(item => item.split(',').map(value => value.trim()).filter(Boolean)) + .map(item => { + const index = Number(item) - 1; + const candidate = Number.isSafeInteger(index) && /^[1-9]\d*$/u.test(item) ? question.producerCandidates?.[index] : undefined; + return candidate ? `${candidate.name}|${candidate.sourceAppId}|${candidate.workflowName}` : item; + }); + if (entries.length < 1 || entries.length > 8 || entries.some(entry => !/^[^|;\r\n]{1,100}\|[1-9][0-9]*\|[^|;\r\n]{1,100}$/u.test(entry))) { + return { error: 'Select 1–8 observed checks or enter exact name|App ID|workflow tuples.' }; + } + if (new Set(entries).size !== entries.length) + return { error: 'A trusted check was selected more than once.' }; + const names = entries.map(entry => entry.split('|', 1)[0]); + if (new Set(names).size !== names.length) + return { error: 'Two trusted producers use the same check name. Coverage stores only one name; choose one producer or rename the CI jobs before continuing.' }; + return { value: entries.join(';') }; + } + if (!input && question.statusOptionState === 'observed' && !question.choices?.includes(String(question.defaultValue))) { + return { error: 'The saved Status value is not available in every selected Project. Choose a listed Status option.' }; + } if (!input && question.kind !== 'scope-overrides') return { value: question.defaultValue }; if (question.kind === 'text') @@ -48383,9 +49718,23 @@ function parseAnswer(question, raw) { return { error: `Unknown inherited resource name(s): ${unknown.join(', ')}.` }; return { value: Object.fromEntries(requested.map((name) => [name, 'repository'])) }; } -function applyAnswer(configuration, question, value) { +function applyAnswer(configuration, question, value, independently) { const draft = (0, setup_configuration_clone_policy_1.cloneSetupConfiguration)(configuration); - if (question.id === 'agents.configureIndependently') + if (question.id === 'agents.configureIndependently') { + if (!value) + for (const task of setup_configuration_defaults_1.SETUP_AGENT_TASKS.filter(task => task !== 'findings')) { + draft.agents[task] = { ...draft.agents[task], modelProvider: draft.agents.findings.modelProvider, + model: draft.agents.findings.model, effort: draft.agents.findings.effort, + executable: draft.agents.findings.executable }; + } + return draft; + } + if (question.id === 'projects.enabled') { + if (!value) + draft.projects.ids = ''; + return draft; + } + if (question.id === 'projects.statusVerified') return draft; if (question.id === 'features.issues' && value === false) { draft.features.issues = false; @@ -48434,8 +49783,9 @@ function applyAnswer(configuration, question, value) { } if (['agents.findings.modelProvider', 'agents.findings.model', 'agents.findings.effort', 'agents.findings.executable'].includes(question.id)) { const field = question.id.split('.')[2]; - for (const task of setup_configuration_defaults_1.SETUP_AGENT_TASKS) + for (const task of independently ? ['findings'] : setup_configuration_defaults_1.SETUP_AGENT_TASKS) { draft.agents[task] = { ...draft.agents[task], [field]: value }; + } return draft; } const parts = question.id.split('.'); @@ -48638,20 +49988,24 @@ const requirement = (input) => ({ * interactive configuration does not exist before the setup PAT prompt. */ function buildSetupPatPermissionRequirements() { - return normalizePermissionRequirements([ + // Keep conditional read and write paths separate here: collapsing Actions + // into one write row would hide the approval-only read requirement. + return [ requirement({ role: 'setup', scope: 'repository', permission: 'Metadata', level: 'read', reason: 'Resolve repository identity and visibility.', probe: 'metadata' }), requirement({ role: 'setup', scope: 'repository', permission: 'Contents', level: 'read', reason: 'Inspect installed workflows and repository files.', probe: 'contents' }), requirement({ role: 'setup', scope: 'repository', permission: 'Secrets', level: 'write', applicability: 'conditional', condition: 'Secret provisioning enabled', reason: 'Inspect and provision selected GitHub Actions Secrets.', probe: 'secrets' }), requirement({ role: 'setup', scope: 'repository', permission: 'Variables', level: 'write', applicability: 'conditional', condition: 'Variable provisioning enabled', reason: 'Inspect and provision selected GitHub Actions Variables.', probe: 'variables' }), requirement({ role: 'setup', scope: 'repository', permission: 'Issues', level: 'write', applicability: 'conditional', condition: 'Issue workflows enabled', reason: 'Provision labels and issue resources.', probe: 'issues' }), requirement({ role: 'setup', scope: 'repository', permission: 'Actions', level: 'write', applicability: 'conditional', condition: 'Credential health enabled', reason: 'Inspect and dispatch credential-health workflows.', probe: 'actions' }), + requirement({ role: 'setup', scope: 'repository', permission: 'Actions', level: 'read', applicability: 'conditional', condition: 'Pull-request approval enabled', reason: 'Inspect CI workflow runs and jobs for exact producer identities.', probe: 'actions' }), + requirement({ role: 'setup', scope: 'repository', permission: 'Checks', level: 'read', applicability: 'conditional', condition: 'Pull-request approval enabled', reason: 'Discover exact CI check and producer identities.', probe: 'checks' }), requirement({ role: 'setup', scope: 'repository', permission: 'Administration', level: 'read', applicability: 'conditional', condition: 'Release, hotfix, or guarded approval enabled', reason: 'Inspect branch protection and rulesets.', probe: 'administration' }), requirement({ role: 'setup', scope: 'repository', permission: 'Workflows', level: 'write', applicability: 'conditional', condition: 'Temporary health workflow required', reason: 'Bootstrap a missing credential-health workflow.', probe: 'workflows' }), requirement({ role: 'setup', scope: 'organization', permission: 'Secrets', level: 'write', applicability: 'conditional', condition: 'Organization Secret storage selected', reason: 'Inspect and provision organization Actions Secrets.', probe: 'secrets' }), requirement({ role: 'setup', scope: 'organization', permission: 'Variables', level: 'write', applicability: 'conditional', condition: 'Organization Variable storage selected', reason: 'Inspect and provision organization Actions Variables.', probe: 'variables' }), requirement({ role: 'setup', scope: 'organization', permission: 'Issue Types', level: 'write', applicability: 'conditional', condition: 'Issue type automation enabled', reason: 'Provision and assign configured issue types.', probe: 'issue-types' }), - requirement({ role: 'setup', scope: 'organization', permission: 'Projects', level: 'write', applicability: 'conditional', condition: 'Organization Projects selected', reason: 'Inspect and configure selected Projects.', probe: 'projects' }), - ]); + requirement({ role: 'setup', scope: 'organization', permission: 'Projects', level: 'read', applicability: 'conditional', condition: 'Organization Projects selected', reason: 'Inspect selected Projects and their Status options; setup does not edit Project items.', probe: 'projects' }), + ]; } /** * Recomputes setup-PAT permissions after the operator has approved the final @@ -48664,8 +50018,8 @@ function buildConfiguredSetupPatPermissionRequirements(configuration, remote) { return buildSetupPatRequirements(configuration, remote?.ownerType === 'Organization' || remote?.ownerType === 'Unknown', remote); } /** Grants justified by local choices alone; remote-only conditions stay unresolved. */ -function buildSetupPatIntentPermissionRequirements(configuration, ownerKind) { - return buildSetupPatRequirements(configuration, ownerKind === 'Organization'); +function buildSetupPatIntentPermissionRequirements(configuration, ownerKind, projectsWanted = configuration.projects.ids.trim().length > 0) { + return buildSetupPatRequirements(configuration, ownerKind === 'Organization', undefined, projectsWanted); } function buildSetupPatIntentUncertainty(configuration, ownerKind) { const unknown = []; @@ -48691,7 +50045,7 @@ function requiredSetupPatPermissionDelta(before, after) { || (previous.get(`${item.scope}:${item.permission.toLowerCase()}`) === 'read' && item.level === 'write'))) .map(item => `${item.scope} ${item.permission} ${item.level}`); } -function buildSetupPatRequirements(configuration, organization, remote) { +function buildSetupPatRequirements(configuration, organization, remote, projectsWanted = configuration.projects.ids.trim().length > 0) { const repositorySecretNames = (0, setup_credential_requirement_policy_1.buildSetupCredentialRequirements)(configuration) .map(credential => credential.name); const repositoryVariableNames = (0, setup_configuration_plan_1.buildSetupRepositoryVariables)(configuration) @@ -48708,6 +50062,7 @@ function buildSetupPatRequirements(configuration, organization, remote) { || configuration.features.hotfix || enabledIssueWorkflowKinds.some(kind => kind === 'release' || kind === 'hotfix'); const guardedApproval = configuration.pullRequestApproval.mode === 'guarded'; + const approvalEnabled = configuration.pullRequestApproval.mode !== 'off'; const hasExistingCredential = repositorySecretNames.some(name => remote?.repositorySecrets.includes(name) || remote?.organizationSecrets.includes(name)); const needsCredentialHealth = configuration.manageRepositorySecrets && hasExistingCredential; const needsCredentialHealthBootstrap = needsCredentialHealth @@ -48735,6 +50090,10 @@ function buildSetupPatRequirements(configuration, organization, remote) { role: 'setup', scope: 'repository', permission: 'Actions', level: 'write', reason: 'Dispatch credential-health checks for existing Secrets.', probe: 'actions', })] : []), + ...(approvalEnabled ? [ + requirement({ role: 'setup', scope: 'repository', permission: 'Actions', level: 'read', reason: 'Inspect CI workflow runs and jobs for approval evidence.', probe: 'actions' }), + requirement({ role: 'setup', scope: 'repository', permission: 'Checks', level: 'read', reason: 'Discover exact CI check and producer identities.', probe: 'checks' }), + ] : []), ...(needsCredentialHealthBootstrap ? [ requirement({ role: 'setup', scope: 'repository', permission: 'Contents', level: 'write', reason: 'Temporarily install credential health when its workflow is not confirmed installed.', probe: 'contents' }), requirement({ role: 'setup', scope: 'repository', permission: 'Workflows', level: 'write', reason: 'Temporarily install credential health when its workflow is not confirmed installed.', probe: 'workflows' }), @@ -48755,9 +50114,9 @@ function buildSetupPatRequirements(configuration, organization, remote) { role: 'setup', scope: 'organization', permission: 'Issue Types', level: 'write', reason: 'Provision native issue types for the selected workflows.', probe: 'issue-types', })] : []), - ...(organization && configuration.projects.ids.trim().length > 0 ? [requirement({ - role: 'setup', scope: 'organization', permission: 'Projects', level: 'write', - reason: 'Inspect and configure the selected organization Projects.', probe: 'projects', + ...(organization && projectsWanted ? [requirement({ + role: 'setup', scope: 'organization', permission: 'Projects', level: 'read', + reason: 'Inspect selected Projects and their Status options; setup does not edit Project items.', probe: 'projects', })] : []), ]); } @@ -50825,18 +52184,32 @@ async function runInitialSetupWorkflow(request, dependencies) { (0, logging_ports_1.logInfo)(`${(0, task_emoji_1.getTaskEmoji)(TASK_ID)} Executing ${TASK_ID}.`); const steps = []; const errors = []; + const configuration = request.setupConfiguration; + const effects = [ + { id: 'files', state: 'not-started', scope: 'local' }, + { id: 'secrets', state: 'not-started', scope: resourceScope(configuration, 'secrets') }, + { id: 'labels', state: 'not-started', scope: 'repository' }, + { id: 'issue-types', state: 'not-started', scope: 'repository' }, + { id: 'variables', state: 'not-started', scope: resourceScope(configuration, 'variables') }, + { id: 'initial-tag', state: 'not-started', scope: 'repository' }, + ]; + const mark = (id, state) => { + const index = effects.findIndex(effect => effect.id === id); + effects[index] = { ...effects[index], state }; + }; + const receipt = () => buildResult(errors, steps, effects); try { const setupConfiguration = request.setupConfiguration; if (!dependencies.setupWorkspacePort.hasValidToken()) { (0, logging_ports_1.logInfo)(' 🛑 Setup requires the setup PAT provided for this command with a valid token.'); errors.push(new application_error_1.ApplicationError('authorization.credential-invalid', 'A valid setup PAT must be provided to run setup. It is separate from the workflow PAT Secret.')); - return [buildResult(errors, steps)]; + return [receipt()]; } (0, logging_ports_1.logInfo)('🔐 Checking GitHub access...'); const githubAccess = await verifyGitHubAccess(request, dependencies.authenticatedUserPort); if (!githubAccess.success) { errors.push(...githubAccess.errors); - return [buildResult(errors, steps)]; + return [receipt()]; } steps.push(`✅ GitHub access verified: ${githubAccess.user}`); const remoteConfigurationErrors = []; @@ -50847,7 +52220,7 @@ async function runInitialSetupWorkflow(request, dependencies) { if (remoteConfigurationErrors.length === 0) { errors.push(new application_error_1.ApplicationError('provider.unavailable', 'Could not inspect existing GitHub Actions resource scopes. Restore inventory access and rerun setup.')); } - return [buildResult(errors, steps)]; + return [receipt()]; } const inventoryErrors = [ ...(0, setup_configuration_policy_1.validateSetupStorageAgainstRemote)(setupConfiguration, remoteConfiguration), @@ -50858,7 +52231,7 @@ async function runInitialSetupWorkflow(request, dependencies) { ]; if (inventoryErrors.length > 0) { errors.push(...fromMessages(inventoryErrors, 'provider.unavailable')); - return [buildResult(errors, steps)]; + return [receipt()]; } } (0, logging_ports_1.logInfo)('📋 Ensuring .github and copying setup files...'); @@ -50870,15 +52243,25 @@ async function runInitialSetupWorkflow(request, dependencies) { approvedWorkflowFiles: request.workflowUpdates, } : {}), }; + mark('files', 'needs-inspection'); const filesResult = dependencies.setupWorkspacePort.prepare(workspaceSelection); + mark('files', filesResult.copied > 0 ? 'completed' : 'skipped'); steps.push(`✅ Setup files: ${filesResult.copied} copied, ${filesResult.skipped} already existed`); + const secretValues = Number(Boolean(request.setupCredentials?.workflowPat)) + (request.setupCredentials?.apiKeys.length ?? 0); + if (setupConfiguration?.manageRepositorySecrets && secretValues > 0) + mark('secrets', 'needs-inspection'); const secrets = await (0, setup_resource_provisioning_1.ensureRepositorySecrets)(request, dependencies, setupConfiguration, remoteConfiguration); + mark('secrets', secrets.errors.length ? 'needs-inspection' + : setupConfiguration?.manageRepositorySecrets && secretValues > 0 ? 'completed' : 'skipped'); if (secrets.step) steps.push(secrets.step); if (secrets.errors.length > 0) errors.push(...fromMessages(secrets.errors, 'authorization.credential-invalid')); (0, logging_ports_1.logInfo)('🏷️ Checking configured and progress labels...'); + mark('labels', 'needs-inspection'); const labels = await ensureInitialLabels(request, dependencies.initialLabelProvisioningPort, setupConfiguration); + mark('labels', !labels.completed || labels.configured.errors.length || labels.progress.errors.length + ? 'needs-inspection' : labels.configured.created + labels.progress.created > 0 ? 'completed' : 'skipped'); if (!labels.completed) { errors.push(labels.error); } @@ -50887,30 +52270,40 @@ async function runInitialSetupWorkflow(request, dependencies) { appendLabelSummary(steps, errors, labels.progress, 'Progress labels'); } (0, logging_ports_1.logInfo)('📋 Checking issue types...'); + mark('issue-types', 'needs-inspection'); const issueTypes = await ensureIssueTypes(request, dependencies.issueTypeProvisioningPort, setupConfiguration); + mark('issue-types', !issueTypes.success ? 'needs-inspection' : issueTypes.created > 0 ? 'completed' : 'skipped'); if (!issueTypes.success) { errors.push(...fromMessages(issueTypes.errors, 'provider.unavailable')); } else { steps.push(`✅ Issue types checked: ${issueTypes.created} created, ${issueTypes.existing} already existed`); } + if (setupConfiguration?.manageRepositoryVariables) + mark('variables', 'needs-inspection'); const variables = await (0, setup_resource_provisioning_1.ensureRepositoryVariables)(request, dependencies, setupConfiguration, remoteConfiguration); + mark('variables', variables.errors.length ? 'needs-inspection' + : setupConfiguration?.manageRepositoryVariables ? 'completed' : 'skipped'); if (variables.step) steps.push(variables.step); if (variables.errors.length > 0) errors.push(...fromMessages(variables.errors, 'provider.unavailable')); + if (setupConfiguration?.createInitialTag !== false) + mark('initial-tag', 'needs-inspection'); const defaultVersion = await ensureDefaultVersion(request, dependencies, setupConfiguration); + mark('initial-tag', defaultVersion.error ? 'needs-inspection' + : defaultVersion.step?.includes('created on branch') ? 'completed' : 'skipped'); if (defaultVersion.step) steps.push(defaultVersion.step); if (defaultVersion.error) errors.push(defaultVersion.error); - return [buildResult(errors, steps)]; + return [receipt()]; } catch (error) { const semanticError = (0, application_error_1.toApplicationError)(error, 'workflow.failed', 'Error running initial setup.'); (0, logging_ports_1.logError)(semanticError); errors.push(semanticError); - return [buildResult(errors, steps)]; + return [receipt()]; } } async function verifyGitHubAccess(_request, repository) { @@ -50988,15 +52381,22 @@ function appendLabelSummary(steps, errors, summary, labelType) { steps.push(`✅ ${labelType} checked: ${summary.created} created, ${summary.existing} already existed`); } } -function buildResult(errors, steps) { +function buildResult(errors, steps, effects) { return new result_1.Result({ id: TASK_ID, success: errors.length === 0, executed: true, steps, + payload: { setupReceipt: { version: 1, effects: effects.map(effect => ({ ...effect })) } }, errors: errors.length > 0 ? errors : undefined, }); } +function resourceScope(configuration, kind) { + const policy = configuration?.storage[kind]; + if (!policy) + return 'repository'; + return Object.values(policy.overrides).some(scope => scope !== policy.defaultScope) ? 'mixed' : policy.defaultScope; +} function fromMessages(messages, code) { return messages.map(message => new application_error_1.ApplicationError(code, message)); } @@ -55180,7 +56580,7 @@ class SetupDoctorUseCase { const remoteSecrets = new Set([...remote.repositorySecrets, ...remote.organizationSecrets]); const present = requirements.filter((requirement) => remoteSecrets.has(requirement.name)); let health; - if (present.length > 0) { + if (present.length > 0 && !request.readOnly) { try { health = await this.dependencies.remoteHealth.validateExisting(request.owner, request.repository, request.setupToken, request.configuration.repository.mainBranch, present); } @@ -55628,24 +57028,26 @@ class PrepareSetupPatIntentUseCase { skipRepositorySecrets: request.skipRepositorySecrets, }); let pass = 1; + let projectsWanted = Boolean(draft.projects.ids.trim()); while (true) { - const context = { skipQuestionIds: fixedQuestionIds }; + const context = { skipQuestionIds: fixedQuestionIds, projectsWanted }; const intent = await this.ports.collect((0, setup_questionnaire_policy_1.createSetupPermissionIntentQuestionnaire)(draft, context), context, pass); if (intent.terminal === 'cancelled') throw new setup_interaction_cancelled_error_1.SetupInteractionCancelledError(); draft = intent.draft; - const ownerKind = (0, setup_pat_intent_policy_1.setupPatIntentNeedsOwnerKind)(draft) ? await this.ports.chooseOwnerKind() : 'User'; + projectsWanted = Boolean(draft.projects.ids.trim()) || (intent.projectsWanted ?? projectsWanted); + const ownerKind = (0, setup_pat_intent_policy_1.setupPatIntentNeedsOwnerKind)(draft, projectsWanted) ? await this.ports.chooseOwnerKind() : 'User'; if (ownerKind === 'unknown') { this.ports.onManual('owner-unknown'); return { kind: 'manual' }; } - const ownerConflict = (0, setup_pat_intent_policy_1.setupPatIntentOwnerConflict)(draft, ownerKind); + const ownerConflict = (0, setup_pat_intent_policy_1.setupPatIntentOwnerConflict)(draft, ownerKind, projectsWanted); const errors = (0, setup_configuration_policy_1.validateSetupConfiguration)(draft, { allowIncompleteApproval: true }); - const requirements = (0, setup_token_permission_policy_1.buildSetupPatIntentPermissionRequirements)(draft, ownerKind); + const requirements = (0, setup_token_permission_policy_1.buildSetupPatIntentPermissionRequirements)(draft, ownerKind, projectsWanted); this.ports.advanceToSetupPat(); this.ports.showPreview({ draft, requirements, uncertain: (0, setup_token_permission_policy_1.buildSetupPatIntentUncertainty)(draft, ownerKind), - ownerConflict, errors, pass, + ownerConflict, errors, pass, projectsWanted, }); let decision; do { @@ -55675,6 +57077,7 @@ class PrepareSetupPatIntentUseCase { ownerKind, permissionIntent: { draft, + projectsWanted, answeredQuestionIds: [...new Set([...fixedQuestionIds, ...(intent.answeredQuestionIds ?? [])])], }, }; @@ -56006,12 +57409,14 @@ class SetupQuestionnaireController { this.terminal = terminal; this.renderer = renderer; } - async collect(initial, context) { + async collect(initial, context, discoveryRefresh) { if (!this.terminal.isInteractive()) { throw new application_error_1.ApplicationError('configuration.invalid', 'Interactive setup requires an interactive terminal. Use --non-interactive with explicit configuration.'); } this.renderer.showIntroduction(); let state = initial; + let currentContext = context; + let pendingProjectSelection; let visibleState; while (state.terminal === 'collecting' && state.question) { if (visibleState !== state.stateId) { @@ -56020,10 +57425,48 @@ class SetupQuestionnaireController { } if (state.validation) this.renderer.showValidation(state.validation); - const input = state.question.kind === 'multi-select' && this.terminal.readMultiSelect - ? await this.terminal.readMultiSelect(this.renderer.renderPrompt(state.question), state.question.choices ?? [], parseSelectedDefaults(state.question.defaultValue)) - : await this.terminal.readText(this.renderer.renderPrompt(state.question)); - state = (0, setup_questionnaire_policy_1.transitionSetupQuestionnaire)(state, toEvent(input), context); + let input = (state.question.kind === 'multi-select' || state.question.kind === 'project-select') && this.terminal.readMultiSelect + ? await this.terminal.readMultiSelect(this.renderer.renderPrompt(state.question, (0, setup_questionnaire_policy_1.setupQuestionnaireProgress)(state, currentContext)), state.question.kind === 'project-select' + ? [...(state.question.projectCandidates ?? []).map(candidate => `${candidate.number} — ${candidate.title} (${candidate.url})`), 'manual — Enter Project number or URL', + ...(state.question.discoveryRetryRemaining ? ['retry — Retry GitHub Project discovery'] : [])] + : state.question.choices ?? [], state.question.kind === 'project-select' && pendingProjectSelection + ? pendingProjectSelection : parseSelectedDefaults(state.question.defaultValue), this.renderer.renderHelp(state.question)) + : await this.terminal.readText(this.renderer.renderPrompt(state.question, (0, setup_questionnaire_policy_1.setupQuestionnaireProgress)(state, currentContext))); + if (state.question.kind === 'project-select' && input.kind === 'value' && input.value.split(',').includes('manual') + && !input.value.split(',').includes('retry')) { + const manual = await this.terminal.readText('Enter additional Project numbers or GitHub URLs, comma-separated (empty adds none): '); + input = manual.kind === 'value' + ? { kind: 'value', value: [input.value.replace(/(?:^|,)manual(?:,|$)/gu, ',').replace(/^,|,$/gu, ''), manual.value] + .filter(value => value && value !== 'none').join(',') || 'none' } : manual; + } + if (input.kind === 'value' && input.value.trim() === '?') { + this.renderer.showHelp(state.question); + continue; + } + if (input.kind === 'value' && input.value.trim().toLowerCase() === ':back') { + pendingProjectSelection = undefined; + state = (0, setup_questionnaire_policy_1.transitionSetupQuestionnaire)(state, { kind: 'back' }, currentContext); + continue; + } + const kind = state.question.id === 'projects.ids' ? 'projects' + : state.question.id === 'pullRequestApproval.testChecks' ? 'checks' : undefined; + if (kind && input.kind === 'value' && (input.value.trim().toLowerCase() === 'r' + || input.value.split(',').includes('retry'))) { + if (kind === 'projects') + pendingProjectSelection = input.value.split(',').filter(value => value !== 'retry'); + if (!state.question.discoveryRetryRemaining || !discoveryRefresh) { + this.renderer.showValidation('No discovery retries remain. Use the manual option or continue.'); + continue; + } + const refreshed = await discoveryRefresh.refresh(kind); + if (refreshed) { + currentContext = refreshed; + state = (0, setup_questionnaire_policy_1.refreshSetupQuestionnaireQuestion)(state, currentContext); + } + continue; + } + pendingProjectSelection = undefined; + state = (0, setup_questionnaire_policy_1.transitionSetupQuestionnaire)(state, toEvent(input), currentContext); } if (state.terminal === 'cancelled') this.renderer.showCancelled(); @@ -56119,6 +57562,7 @@ const setup_questionnaire_policy_1 = __nccwpck_require__(6009); const setup_configuration_clone_policy_1 = __nccwpck_require__(85881); const setup_doctor_message_catalog_1 = __nccwpck_require__(80226); const pull_request_approval_policy_1 = __nccwpck_require__(98820); +const setup_project_selection_policy_1 = __nccwpck_require__(73750); class SetupWizardUseCase { constructor(dependencies) { this.dependencies = dependencies; @@ -56127,6 +57571,8 @@ class SetupWizardUseCase { const defaults = buildInitialSetupConfiguration(request); const effectiveOverrides = request.overrides; const initial = request.permissionIntent ? (0, setup_configuration_clone_policy_1.cloneSetupConfiguration)(request.permissionIntent.draft) : defaults; + const basicSkippedQuestionIds = request.presentationMode === 'basic' + ? request.basicSkippedQuestionIds ?? (0, setup_questionnaire_policy_1.setupBasicSkippedQuestionIds)(initial) : []; let remoteConfiguration; if (request.remoteTarget) { try { @@ -56136,18 +57582,73 @@ class SetupWizardUseCase { remoteConfiguration = unavailableRemoteConfiguration(); } } + const explicitMainBranch = request.overrides?.repository?.mainBranch !== undefined; + if (!explicitMainBranch && remoteConfiguration?.defaultBranch) { + initial.repository.mainBranch = remoteConfiguration.defaultBranch; + } const defaultValidationErrors = (0, setup_configuration_policy_1.validateSetupConfiguration)(initial, { allowIncompleteApproval: true }); if (defaultValidationErrors.length > 0) { throw new application_error_1.ApplicationError('configuration.invalid', `Invalid setup configuration:\n${defaultValidationErrors.map((error) => `- ${error}`).join('\n')}`); } - const context = { + let approvalDiscovery = request.mode === 'interactive' && initial.pullRequestApproval.mode !== 'off' + && request.remoteTarget && this.dependencies.approvalCheckDiscovery + ? await this.dependencies.approvalCheckDiscovery.discover(request.remoteTarget.owner, request.remoteTarget.repository, request.remoteTarget.token, initial.repository.developmentBranch).catch(() => ({ status: 'unavailable', candidates: [], truncated: false })) : undefined; + let projectDiscovery = request.mode === 'interactive' + && (request.permissionIntent?.projectsWanted !== false || request.revision?.group === 'projects') + && request.remoteTarget && this.dependencies.projectDiscovery + ? await this.dependencies.projectDiscovery.discover(request.remoteTarget.owner, remoteConfiguration?.ownerType ?? 'Unknown', request.remoteTarget.token).catch(() => ({ status: 'unavailable', candidates: [] })) : undefined; + let context = { ...(remoteConfiguration ? { remote: remoteConfiguration } : {}), + branchSources: { main: explicitMainBranch ? 'configuration' : remoteConfiguration?.defaultBranch ? 'github' : 'default', + development: request.overrides?.repository?.developmentBranch !== undefined ? 'configuration' + : request.developmentBranchObservedLocally ? 'local' : 'default' }, variableNames: (0, setup_configuration_policy_1.buildSetupRepositoryVariables)(initial).map((variable) => variable.name), secretNames: (0, setup_configuration_policy_1.buildSetupCredentialRequirements)(initial).map((requirement) => requirement.name), - ...(request.permissionIntent ? { skipQuestionIds: request.permissionIntent.answeredQuestionIds } : {}), + ...(request.revision ? { skipQuestionIds: [...new Set([ + ...request.revision.answeredQuestionIds, + ...basicSkippedQuestionIds, + ])].filter(id => !(0, setup_questionnaire_policy_1.setupQuestionIdsForGroup)(request.revision.group).includes(id)), + projectsWanted: request.revision.group === 'projects' || Boolean(initial.projects.ids.trim()) } : {}), + ...(!request.revision ? { skipQuestionIds: [...new Set([ + ...(request.permissionIntent?.answeredQuestionIds ?? []), + ...(request.permissionIntent?.projectsWanted === false ? ['projects.ids'] : []), + ...basicSkippedQuestionIds, + ])], ...(request.permissionIntent ? { projectsWanted: request.permissionIntent.projectsWanted } : {}) } : {}), + ...(approvalDiscovery ? { approvalCheckCandidates: approvalDiscovery.candidates, + approvalCheckDiscoveryStatus: approvalDiscovery.status, + approvalCheckDiscoveryTruncated: approvalDiscovery.truncated } : {}), + ...(projectDiscovery ? { projectDiscovery } : {}), + ...(request.remoteTarget ? { projectOwner: request.remoteTarget.owner } : {}), + discoveryRetryRemaining: { checks: approvalDiscovery ? 2 : 0, + projects: projectDiscovery && projectDiscovery.status !== 'unsupported' ? 2 : 0 }, + }; + const discoveryRefresh = { + refresh: async (kind) => { + const target = request.remoteTarget; + const remaining = context.discoveryRetryRemaining?.[kind] ?? 0; + if (!target || remaining <= 0) + return undefined; + if (kind === 'checks') { + if (!this.dependencies.approvalCheckDiscovery) + return undefined; + approvalDiscovery = await this.dependencies.approvalCheckDiscovery.discover(target.owner, target.repository, target.token, initial.repository.developmentBranch).catch(() => ({ status: 'unavailable', candidates: [], truncated: false })); + context = { ...context, approvalCheckCandidates: approvalDiscovery.candidates, + approvalCheckDiscoveryStatus: approvalDiscovery.status, + approvalCheckDiscoveryTruncated: approvalDiscovery.truncated, + discoveryRetryRemaining: { ...context.discoveryRetryRemaining, checks: remaining - 1 } }; + } + else { + if (!this.dependencies.projectDiscovery) + return undefined; + projectDiscovery = await this.dependencies.projectDiscovery.discover(target.owner, remoteConfiguration?.ownerType ?? 'Unknown', target.token).catch(() => ({ status: 'unavailable', candidates: [] })); + context = { ...context, projectDiscovery, + discoveryRetryRemaining: { ...context.discoveryRetryRemaining, projects: remaining - 1 } }; + } + return context; + }, }; const questionnaire = request.mode === 'interactive' - ? await this.collectInteractive(initial, context) + ? await this.collectInteractive(initial, context, discoveryRefresh) : (0, setup_questionnaire_policy_1.createSetupReviewState)(initial); if (questionnaire.terminal === 'cancelled') { return { @@ -56164,7 +57665,10 @@ class SetupWizardUseCase { // default must not outlive an explicit decision to disable PR automation. collectedConfiguration.pullRequestApproval = { ...collectedConfiguration.pullRequestApproval, mode: 'off' }; } - const validationErrors = (0, setup_configuration_policy_1.validateSetupConfiguration)(collectedConfiguration, { allowIncompleteApproval: request.previewOnly === true }); + const validationErrors = [ + ...(0, setup_configuration_policy_1.validateSetupConfiguration)(collectedConfiguration, { allowIncompleteApproval: request.previewOnly === true }), + ...(0, setup_project_selection_policy_1.validateDiscoveredProjectStatuses)(collectedConfiguration, projectDiscovery), + ]; if (validationErrors.length > 0) { throw new application_error_1.ApplicationError('configuration.invalid', `Invalid setup configuration:\n${validationErrors.map((error) => `- ${error}`).join('\n')}`); } @@ -56261,9 +57765,30 @@ class SetupWizardUseCase { } } const plan = (0, setup_configuration_policy_1.buildSetupPlan)(configuration, readiness, approvalReadiness); + if (basicSkippedQuestionIds.length) { + const byGroup = new Map(); + for (const item of (0, setup_questionnaire_policy_1.setupQuestionContentInventory)()) { + if (basicSkippedQuestionIds.includes(item.id) && !request.reviewedGroups?.includes(item.stateId) + && request.revision?.group !== item.stateId) + byGroup.set(item.stateId, (byGroup.get(item.stateId) ?? 0) + 1); + } + plan.presentationDefaults = [...byGroup].map(([group, count]) => ({ group, count })); + } this.dependencies.planPresenter.present(plan); const confirmation = (0, setup_questionnaire_policy_1.enterSetupConfirmation)(questionnaire); const decision = await this.dependencies.confirmation.confirm(plan); + if (decision.kind === 'revise') { + if (request.mode !== 'interactive') + throw new application_error_1.ApplicationError('configuration.invalid', 'Plan editing requires interactive setup.'); + const answeredQuestionIds = [...new Set([ + ...(request.revision?.answeredQuestionIds ?? []), + ...(request.permissionIntent?.answeredQuestionIds ?? []), + ...(questionnaire.answeredQuestionIds ?? []), + ])]; + return this.execute({ ...request, overrides: (0, setup_configuration_clone_policy_1.cloneSetupConfiguration)(configuration), permissionIntent: undefined, + basicSkippedQuestionIds, reviewedGroups: [...new Set([...(request.reviewedGroups ?? []), decision.group])], + revision: { group: decision.group, answeredQuestionIds } }); + } const completed = (0, setup_questionnaire_policy_1.finishSetupQuestionnaire)(confirmation, decision.kind === 'approved'); if (completed.terminal === 'cancelled') { return { @@ -56281,11 +57806,11 @@ class SetupWizardUseCase { ...(remoteConfiguration ? { remoteConfiguration } : {}), }; } - collectInteractive(defaults, context) { + collectInteractive(defaults, context, discoveryRefresh) { if (!this.dependencies.collector) { throw new application_error_1.ApplicationError('configuration.invalid', 'Interactive setup requires a questionnaire collector.'); } - return this.dependencies.collector.collect((0, setup_questionnaire_policy_1.createSetupQuestionnaire)(defaults, context), context); + return this.dependencies.collector.collect((0, setup_questionnaire_policy_1.createSetupQuestionnaire)(defaults, context), context, discoveryRefresh); } } exports.SetupWizardUseCase = SetupWizardUseCase; @@ -65594,9 +67119,10 @@ const setup_credential_prompt_adapter_1 = __nccwpck_require__(93232); function registerDoctorCommand(program) { program .command('doctor') - .description('Verify Copilot workflows, Variables, Secrets, and setup PAT without changing repository configuration') + .description('Verify Copilot resources; use --read-only to avoid dispatching credential-health Actions') .option('-t, --token ', 'Setup PAT (or PERSONAL_ACCESS_TOKEN from the environment)') .option('--config ', 'YAML or JSON setup configuration used as the expected contract') + .option('--read-only', 'Inspect metadata and installed resources without dispatching credential-health Actions', false) .option('--non-interactive', 'Do not prompt; use --token or PERSONAL_ACCESS_TOKEN', false) .action(async (options) => { const terminal = options.nonInteractive ? undefined : (0, setup_terminal_driver_1.createInteractiveTerminalDriver)(); @@ -65624,6 +67150,7 @@ function registerDoctorCommand(program) { repository: gitInfo.repo, setupToken: token, configuration: expected, + readOnly: Boolean(options.readOnly), }); new setup_doctor_presenter_1.SetupDoctorPresenter(diagnosis.catalog).present(diagnosis.report); if (!diagnosis.report.healthy) @@ -65866,6 +67393,8 @@ const setup_credentials_composition_root_1 = __nccwpck_require__(69084); const setup_doctor_composition_root_1 = __nccwpck_require__(56360); const setup_workspace_adapter_1 = __nccwpck_require__(5729); const setup_approval_readiness_adapter_1 = __nccwpck_require__(78572); +const github_setup_approval_check_discovery_adapter_1 = __nccwpck_require__(42294); +const github_setup_project_discovery_adapter_1 = __nccwpck_require__(29564); const application_error_1 = __nccwpck_require__(75999); const setup_terminal_driver_1 = __nccwpck_require__(5462); const setup_question_renderer_1 = __nccwpck_require__(89481); @@ -65887,6 +67416,7 @@ const setup_apply_snapshot_1 = __nccwpck_require__(84136); const setup_session_guard_1 = __nccwpck_require__(53104); const web_setup_server_1 = __nccwpck_require__(63080); const web_setup_adapters_1 = __nccwpck_require__(60574); +const setup_result_receipt_1 = __nccwpck_require__(44132); function registerSetupCommand(program) { program .command('setup') @@ -65977,7 +67507,7 @@ function registerSetupCommand(program) { if (!options.nonInteractive) { journey = new setup_journey_use_case_1.SetupJourneyUseCase(`${gitInfo.owner}/${gitInfo.repo}`, webBridge ? new web_setup_adapters_1.WebSetupJourneyPresenter(webBridge) : new setup_journey_presenter_1.ConsoleSetupJourneyPresenter()); if (webBridge) { - const target = await webBridge.ask({ kind: 'confirm', title: 'Confirm this repository', + const target = await webBridge.ask({ kind: 'confirm', title: 'Confirm this repository', copyId: 'repository.confirm', copyValues: { repository: `${gitInfo.owner}/${gitInfo.repo}`, branch: initialBranch ?? '' }, description: `This local checkout resolves to ${gitInfo.owner}/${gitInfo.repo} on branch ${initialBranch}. Confirm the target before configuring PAT access or files.`, choices: ['Yes, this is my repository', 'Stop and choose another checkout'] }); if (target === undefined) @@ -65990,10 +67520,23 @@ function registerSetupCommand(program) { journey.advance('choices'); } const overrides = (0, setup_command_options_1.loadSetupOverrides)(options); + let presentationMode = 'custom'; + if (!options.nonInteractive && !options.dryRun) { + if (webBridge) { + const depth = await webBridge.ask({ kind: 'choice', title: 'Choose setup detail', copyId: 'setup.depth', + choices: ['Basic guided setup', 'Customize every setting'], defaultValue: 'Basic guided setup' }); + if (depth === undefined) + throw new setup_credential_prompt_adapter_1.SetupTerminalCancelledError(); + presentationMode = depth === 'Basic guided setup' ? 'basic' : 'custom'; + } + else if (credentialPrompt instanceof setup_credential_prompt_adapter_1.SetupCredentialPromptAdapter) { + presentationMode = await credentialPrompt.chooseSetupPresentationMode(); + } + } let setupPatPermissions = (0, setup_token_permission_policy_1.buildSetupPatPermissionRequirements)(); let token = (0, setup_files_1.getSetupToken)(cwd, options.token); if (webBridge && token) { - const choice = await webBridge.ask({ kind: 'choice', title: 'An environment setup PAT is available', + const choice = await webBridge.ask({ kind: 'choice', title: 'An environment setup PAT is available', copyId: 'setup.environmentPat', description: 'Its value stays in the CLI process and is never sent to this page. Exiting Copilot cannot unset your parent shell variable.', choices: ['Use the environment PAT', 'Create or enter a different PAT'] }); if (choice === undefined) @@ -66017,7 +67560,7 @@ function registerSetupCommand(program) { .collect(initial, context), chooseOwnerKind: () => credentialPrompt.chooseSetupOwnerKind(), review: () => credentialPrompt.reviewSetupPatIntent(), - showPreview: ({ draft, requirements, uncertain, ownerConflict, errors, pass }) => { + showPreview: ({ draft, requirements, uncertain, ownerConflict, errors, pass, projectsWanted }) => { if (ownerConflict) (0, logger_1.logInfo)('This plan selects organization storage or Projects, but the owner was declared a personal account. Revise the choices or use the manual PAT path.'); if (errors.length) @@ -66026,8 +67569,14 @@ function registerSetupCommand(program) { (0, logger_1.logInfo)('Choice review complete. Returning to setup PAT permission review.'); (0, logger_1.logInfo)('Permission intent:'); (0, logger_1.logInfo)(` Initial tag: ${draft.createInitialTag ? 'yes' : 'no'}; issue workflows: ${draft.features.issues ? draft.issueWorkflows.enabled.join(', ') || 'none' : 'disabled'}; PR approval: ${draft.pullRequestApproval.mode}`); - (0, logger_1.logInfo)(` Secrets: ${draft.manageRepositorySecrets ? draft.storage.secrets.defaultScope : 'off'}; Variables: ${draft.manageRepositoryVariables ? draft.storage.variables.defaultScope : 'off'}; Projects: ${draft.projects.ids.trim() || 'none'}`); - webBridge?.message(`Permission preview: issue workflows ${draft.features.issues ? draft.issueWorkflows.enabled.join(', ') || 'none' : 'disabled'}; PR approval ${draft.pullRequestApproval.mode}; Secrets ${draft.manageRepositorySecrets ? draft.storage.secrets.defaultScope : 'off'}; Variables ${draft.manageRepositoryVariables ? draft.storage.variables.defaultScope : 'off'}; Projects ${draft.projects.ids.trim() || 'none'}.`, 'info'); + (0, logger_1.logInfo)(` Secrets: ${draft.manageRepositorySecrets ? draft.storage.secrets.defaultScope : 'off'}; Variables: ${draft.manageRepositoryVariables ? draft.storage.variables.defaultScope : 'off'}; Projects: ${projectsWanted ? 'yes (choose exact Projects after PAT)' : 'none'}`); + webBridge?.message(`Permission preview: issue workflows ${draft.features.issues ? draft.issueWorkflows.enabled.join(', ') || 'none' : 'disabled'}; PR approval ${draft.pullRequestApproval.mode}; Secrets ${draft.manageRepositorySecrets ? draft.storage.secrets.defaultScope : 'off'}; Variables ${draft.manageRepositoryVariables ? draft.storage.variables.defaultScope : 'off'}; Projects ${projectsWanted ? 'yes (choose after PAT)' : 'none'}.`, 'info', undefined, 'permission.preview', { + issues: draft.features.issues ? draft.issueWorkflows.enabled.join('|') || 'none' : 'disabled', + approval: draft.pullRequestApproval.mode, + secrets: draft.manageRepositorySecrets ? draft.storage.secrets.defaultScope : 'off', + variables: draft.manageRepositoryVariables ? draft.storage.variables.defaultScope : 'off', + projects: projectsWanted ? 'yes' : 'none', + }); permissionPresenter.showRequirements('setup', requirements); if (uncertain.length) (0, logger_1.logInfo)(`May need after GitHub inspection:\n${uncertain.map(item => ` - ${item}`).join('\n')}`); @@ -66114,6 +67663,8 @@ function registerSetupCommand(program) { remoteConfiguration: remoteConfigurationReader, mergeQueueReadiness: (0, setup_doctor_composition_root_1.createSetupMergeQueueReadinessUseCase)(), approvalReadiness: new setup_approval_readiness_adapter_1.GithubSetupApprovalReadinessAdapter(), + approvalCheckDiscovery: new github_setup_approval_check_discovery_adapter_1.GithubSetupApprovalCheckDiscoveryAdapter(), + projectDiscovery: new github_setup_project_discovery_adapter_1.GithubSetupProjectDiscoveryAdapter(), }); const result = await wizard.execute({ mode: options.nonInteractive ? 'non-interactive' : 'interactive', @@ -66122,6 +67673,8 @@ function registerSetupCommand(program) { skipRepositoryVariables: Boolean(options.skipVariables), skipRepositorySecrets: Boolean(options.skipSecrets), previewOnly: Boolean(options.dryRun), + presentationMode, + developmentBranchObservedLocally: (0, cli_context_1.hasLocalOrTrackedGitBranch)(cwd, overrides.repository?.developmentBranch ?? 'develop'), ...(token ? { remoteTarget: { owner: gitInfo.owner, repository: gitInfo.repo, token } } : {}), }); if (result.status === 'cancelled') { @@ -66135,6 +67688,7 @@ function registerSetupCommand(program) { } if (result.status === 'blocked') { journey?.finish('blocked'); + webBridge?.resultReason(result.reason === 'setup-permissions-unavailable' ? 'permissions' : 'storage'); (0, logger_1.logError)(new application_error_1.ApplicationError(result.reason === 'setup-permissions-unavailable' ? 'authorization.credential-invalid' : 'provider.unavailable', `${result.reason === 'setup-permissions-unavailable' ? 'Setup is blocked by missing or unconfirmed PAT permissions:' : 'Setup is blocked by unavailable remote storage:'}\n${result.errors.map(error => `- ${error}`).join('\n')}`)); @@ -66206,7 +67760,7 @@ function registerSetupCommand(program) { } const authorization = await new verify_web_setup_apply_use_case_1.VerifyWebSetupApplyUseCase({ confirm: async () => { - const answer = await webBridge.ask({ kind: 'confirm', title: 'Apply this setup now?', + const answer = await webBridge.ask({ kind: 'confirm', title: 'Apply this setup now?', copyId: 'apply.confirm', description: 'This is the final approval. Local files and selected GitHub resources may change. A partial result may require inspection before retrying.', choices: ['Apply setup', 'Stop without applying'] }); return answer === undefined ? undefined : answer === 'Apply setup' ? 'apply' : 'stop'; @@ -66243,17 +67797,33 @@ function registerSetupCommand(program) { journey?.markMutationStarted(); setupApplyStarted = true; const actionResults = await (0, local_action_1.runLocalAction)(params); + webBridge?.effects((0, setup_result_receipt_1.setupResultEffects)(actionResults)); if (actionResults.some(actionResult => !actionResult.success || actionResult.errors.length > 0)) { + const failure = (0, setup_result_receipt_1.setupActionResultFailure)(actionResults); + if (failure) + webBridge?.resultReason(failure.reasonCode, failure.diagnosticRef); journey?.finish('partial'); (0, logger_1.logInfo)('Setup reported failures or partial completion. If a bot PAT was supplied, its Secret may already have been written; inspect the result and GitHub Secret name/scope before retrying or revoking it.'); process.exitCode = 1; } else { + if (webBridge && token) { + const doctorToken = token; + webBridge.configureReadOnlyDoctor(async () => { + const diagnosis = await (0, setup_doctor_composition_root_1.createSetupDoctorUseCase)().execute({ owner: gitInfo.owner, + repository: gitInfo.repo, setupToken: doctorToken, configuration, readOnly: true }); + return { healthy: diagnosis.report.healthy, ...diagnosis.report.totals }; + }); + } journey?.finish('complete'); } } catch (error) { journey?.finish(setupMutationStarted ? 'partial' : error instanceof setup_credential_prompt_adapter_1.SetupTerminalCancelledError ? 'cancelled' : 'blocked'); + const normalizedError = error instanceof setup_credential_prompt_adapter_1.SetupTerminalCancelledError ? undefined + : (0, application_error_1.toApplicationError)(error, 'workflow.failed', 'Setup failed.'); + webBridge?.resultReason(error instanceof setup_credential_prompt_adapter_1.SetupTerminalCancelledError ? 'cancelled' + : (0, setup_result_receipt_1.setupResultReason)(normalizedError.code), normalizedError?.correlationId); if (setupMutationStarted && !setupApplyStarted) { (0, logger_1.logInfo)('A temporary credential-health workflow create was attempted before Apply. Inspect the selected branch and GitHub workflow history before retrying; a failed request may still have reached GitHub.'); } @@ -66280,7 +67850,7 @@ function registerSetupCommand(program) { webBridge.finish(outcome, outcome === 'complete' ? 'Setup completed. Delete the temporary setup PAT in GitHub; keep the bot PAT while its Secret is in use.' : outcome === 'dry-run' ? 'Dry run complete. No files or GitHub resources changed.' - : outcome === 'partial' ? 'Setup may be partial. Inspect GitHub resources and run copilot doctor before retrying.' + : outcome === 'partial' ? 'Setup may be partial. Inspect GitHub resources and run copilot doctor --read-only before retrying.' : 'No further setup changes will be applied. Any PAT already created in GitHub still exists until you delete it there.'); (0, logger_1.logInfo)('The local browser page shows the result. Choose “Close local session” there, or stop this command with Ctrl+C.'); await webServer.closed; @@ -66939,6 +68509,7 @@ function containsCredentialMaterial(value, insideStorage = false) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.DryRunSetupPlanConfirmation = exports.SetupPlanConfirmationAdapter = void 0; const setup_prompt_rendering_1 = __nccwpck_require__(83434); +const setup_questionnaire_policy_1 = __nccwpck_require__(6009); class SetupPlanConfirmationAdapter { constructor(terminal, assumeYes) { this.terminal = terminal; @@ -66949,14 +68520,38 @@ class SetupPlanConfirmationAdapter { return { kind: 'approved' }; if (!this.terminal) return { kind: 'declined' }; - const target = plan.configuration.manageRepositoryVariables - ? 'the repository and GitHub Variables' - : 'the repository'; + const target = plan.configuration.manageRepositoryVariables || plan.configuration.manageRepositorySecrets + ? 'repository files and selected GitHub Actions resources' + : 'repository files'; + const groups = (0, setup_questionnaire_policy_1.setupEditableGroups)(plan.configuration); while (true) { - const result = await this.terminal.readText(`Apply this setup plan to ${target}? ${(0, setup_prompt_rendering_1.color)('[N]', 90)}: `); + const result = await this.terminal.readText(`Apply this setup plan to ${target}? Type ? for details or :edit to change an answer. ${(0, setup_prompt_rendering_1.color)('[N]', 90)}: `); if (result.kind !== 'value') return { kind: 'cancelled' }; const value = result.value.normalize('NFKC').trim().toLowerCase(); + if (value === '?') { + console.log((0, setup_prompt_rendering_1.renderBox)([ + `This is the final approval. The plan lists ${plan.selectedFiles.length} file(s), ${plan.variables.length} Variable(s), and ${plan.requiredSecrets.length} Secret name(s).`, + 'Yes starts the listed local and GitHub setup writes. No leaves the plan unapplied.', + 'A failure after writes begin may leave partial changes; inspect the result and run copilot doctor --read-only before retrying.', + 'PATs created on GitHub are not deleted automatically if you decline or cancel.', + 'Read more: https://docs.page/vypdev/copilot/how-to-use', + ].join('\n'), 'Before applying setup')); + continue; + } + if (value === ':edit') { + console.log(groups.map((group, index) => ` ${index + 1}) ${(0, setup_questionnaire_policy_1.setupQuestionnaireStateLabel)(group)}`).join('\n')); + const selected = await this.terminal.readText('Choose a section number (empty returns to the plan): '); + if (selected.kind !== 'value') + return { kind: 'cancelled' }; + const index = Number(selected.value.trim()) - 1; + if (/^[1-9]\d*$/u.test(selected.value.trim()) && Number.isSafeInteger(index) && groups[index]) { + return { kind: 'revise', group: groups[index] }; + } + if (selected.value.trim()) + console.log((0, setup_prompt_rendering_1.color)('Choose one of the listed section numbers.', 33)); + continue; + } if (!value || ['n', 'no', 'false', '0'].includes(value)) return { kind: 'declined' }; if (['y', 'yes', 'true', '1'].includes(value)) @@ -66988,6 +68583,8 @@ const setup_token_permission_presenter_1 = __nccwpck_require__(63206); const setup_interaction_cancelled_error_1 = __nccwpck_require__(38313); /** @deprecated Use the presentation-neutral cancellation signal in new adapters. */ exports.SetupTerminalCancelledError = setup_interaction_cancelled_error_1.SetupInteractionCancelledError; +const AUTHENTICATION_GUIDE = 'https://docs.page/vypdev/copilot/authentication'; +const GITHUB_PAT_SETTINGS = 'https://github.com/settings/personal-access-tokens'; class SetupCredentialPromptAdapter { constructor(terminal, credentialValues, confirmUnverifiableWritePermissions = false) { this.terminal = terminal; @@ -66998,24 +68595,30 @@ class SetupCredentialPromptAdapter { } configureSetupPatGuide(url) { this.setupPatGuide = url; } get usedGuidedSetupPat() { return this.guidedSetup; } + async chooseSetupPresentationMode() { + if (!this.terminal) + return 'custom'; + const choice = await this.readChoice('How much configuration detail would you like to review now?', ['Basic guided setup', 'Customize every setting'], 'Basic guided setup', 'Basic keeps every permission, security, branch-role, Projects, approval, and storage decision visible. It uses existing defaults for selected advanced agent, branch-prefix, and Bugbot settings. The final plan shows their consequences and lets you edit any section before Apply. Customize asks every applicable question. Neither path changes GitHub before your final approval.'); + return choice === 'Basic guided setup' ? 'basic' : 'custom'; + } async chooseSetupPatMethod() { if (!this.terminal) return 'manual'; this.setupMethodChosen = true; - this.guidedSetup = (await this.readChoice('How would you like to provide the setup PAT?', ['guided link', 'manual PAT'], 'guided link')) === 'guided link'; + this.guidedSetup = (await this.readChoice('How would you like to provide the setup PAT?', ['guided link', 'manual PAT'], 'guided link', `Guided opens GitHub's official fine-grained PAT form with proposed grants. Manual means you create the PAT yourself and enter it here. In either case GitHub handles account sign-in and 2FA; Copilot never revokes the token automatically.\nRead more: ${AUTHENTICATION_GUIDE}`)) === 'guided link'; return this.guidedSetup ? 'guided' : 'manual'; } useManualSetupPat() { this.guidedSetup = false; this.setupPatGuide = undefined; this.setupMethodChosen = true; } async chooseSetupOwnerKind() { if (!this.terminal) return 'unknown'; - const choice = await this.readChoice('Is the GitHub repository owner an organization or a personal account?', ['organization', 'personal account', 'not sure']); + const choice = await this.readChoice('Is the GitHub repository owner an organization or a personal account?', ['organization', 'personal account', 'not sure'], undefined, `The owner is the name before / in owner/repository. Organization-owned repositories can require organization-level grants or SSO approval; a personal account cannot. Check the repository header on GitHub if unsure.\nRead more: ${AUTHENTICATION_GUIDE}`); return choice === 'organization' ? 'Organization' : choice === 'personal account' ? 'User' : 'unknown'; } async reviewSetupPatIntent() { if (!this.terminal) return 'manual'; - const choice = await this.readChoice('Review these intended grants before opening GitHub. What would you like to do?', ['continue to GitHub', 'review all setup choices again', 'view full permission table', 'enter a PAT manually']); + const choice = await this.readChoice('Review these intended grants before opening GitHub. What would you like to do?', ['continue to GitHub', 'review all setup choices again', 'view full permission table', 'enter a PAT manually'], undefined, `These grants are provisional: your choices and GitHub visibility determine the final least-privilege PAT permissions. Reviewing choices does not restart this setup run or apply changes.\nRead more: ${AUTHENTICATION_GUIDE}`); if (choice === 'review all setup choices again') return 'revise'; if (choice === 'view full permission table') @@ -67036,7 +68639,7 @@ class SetupCredentialPromptAdapter { if (!account || !/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/.test(account)) return false; console.log(`GitHub authenticated the setup PAT as @${account}.`); - return (await this.readChoice('Is this the account you intended to configure with?', ['yes', 'no'], 'yes')) === 'yes'; + return (await this.readChoice('Is this the account you intended to configure with?', ['yes', 'no'], 'yes', `Use the operator account that is authorized to configure this repository and organization. A different account's PAT may have different access even if the form looked correct. Select no to stop safely.\nRead more: ${AUTHENTICATION_GUIDE}`)) === 'yes'; } showSetupPatCleanupReminder() { if (!this.guidedSetup || !this.setupPatGuide) @@ -67058,7 +68661,7 @@ class SetupCredentialPromptAdapter { if (!this.terminal) return undefined; if (this.setupPatGuide && !this.setupMethodChosen) { - this.guidedSetup = (await this.readChoice('How would you like to provide the setup PAT?', ['guided link', 'manual PAT'], 'guided link')) === 'guided link'; + this.guidedSetup = (await this.readChoice('How would you like to provide the setup PAT?', ['guided link', 'manual PAT'], 'guided link', `Guided opens GitHub's official form; manual uses a PAT you made yourself. Both are entered only into this local command.\nRead more: ${AUTHENTICATION_GUIDE}`)) === 'guided link'; if (this.guidedSetup) { console.log((0, setup_prompt_rendering_1.renderBox)('Provisional link: Open this GitHub link in your browser, sign in as the account configuring this repository, complete any 2FA or SSO, and review the prefilled fine-grained permissions. GitHub owns token creation; Copilot never handles your web session. Change All repositories to Only select repositories and select ONLY this repository. Remote inspection may require a corrected token later.', 'Create setup PAT in GitHub', 33)); console.log(this.setupPatGuide); @@ -67071,6 +68674,7 @@ class SetupCredentialPromptAdapter { console.log('Copy the one-time token from GitHub and paste it below. It is hidden and used only for this setup run.'); } console.log((0, setup_prompt_rendering_1.renderBox)('Enter a GitHub setup PAT. It is used in memory for this run only and is never stored. The workflow PAT is a different bot-account token and is requested separately.', 'Setup PAT', 33)); + console.log(`PAT creation and cleanup: ${AUTHENTICATION_GUIDE}\nGitHub PAT settings: ${GITHUB_PAT_SETTINGS}`); return this.readSecret('Setup PAT'); } async confirmUnverifiableTokenPermissions(report) { @@ -67108,12 +68712,13 @@ class SetupCredentialPromptAdapter { return; console.log((0, setup_prompt_rendering_1.renderBox)('The workflow PAT is not the setup PAT. Runtime credentials are stored remotely as GitHub Actions Secrets. GitHub never reveals existing Secret values; health is checked through the repository workflow.', 'Workflow credentials', 33)); console.log(`Credential options: ${requirements.map((requirement) => requirement.name).join(', ')}`); + console.log(`Why these credentials are separate: ${AUTHENTICATION_GUIDE}`); } async requestWorkflowPat(requirement, current) { if (this.terminal && !this.credentialValues[requirement.name]?.trim() && this.workflowPatGuide) { let choice; do { - choice = await this.readChoice('How would you like to provide the bot workflow PAT?', ['guided link', 'manual PAT', 'view full permission table'], 'guided link'); + choice = await this.readChoice('How would you like to provide the bot workflow PAT?', ['guided link', 'manual PAT', 'view full permission table'], 'guided link', `Use a PAT from the dedicated bot account, not the operator setup PAT. Guided opens GitHub's form; manual keeps the permission table visible. The bot PAT is installed as an Actions Secret only after Apply.\nRead more: ${AUTHENTICATION_GUIDE}`); if (choice === 'view full permission table' && this.workflowPatRequirements) { console.log((0, setup_token_permission_presenter_1.renderSetupTokenPermissionRequirements)('workflow', this.workflowPatRequirements)); } @@ -67136,10 +68741,14 @@ class SetupCredentialPromptAdapter { } async readBotLogin() { while (true) { - const result = await this.terminal.readText('Expected GitHub bot login (without @): '); + const result = await this.terminal.readText('Expected GitHub bot login (without @; type ? for help): '); if (result.kind !== 'value') throw new exports.SetupTerminalCancelledError(); const login = result.value.trim(); + if (login === '?') { + console.log((0, setup_prompt_rendering_1.renderBox)(`Enter the exact GitHub username of the separate bot account, without @. Copilot resolves its numeric account ID and compares it with the PAT before any Secret is written. It does not sign in as the bot or store its web credentials.\nRead more: ${AUTHENTICATION_GUIDE}`, 'About the bot account')); + continue; + } if (/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/.test(login)) return login; console.log((0, setup_prompt_rendering_1.color)('Enter a valid GitHub account login.', 33)); @@ -67154,7 +68763,7 @@ class SetupCredentialPromptAdapter { if (!this.terminal) return 'keep'; console.log(`Existing ${requirement.name}: ${check.status}. ${check.message}`); - return this.readChoice(`How should Copilot handle the existing ${requirement.name}?`, ['keep', 'replace', 'skip'], check.status === 'valid' ? 'keep' : 'replace'); + return this.readChoice(`How should Copilot handle the existing ${requirement.name}?`, ['keep', 'replace', 'skip'], check.status === 'valid' ? 'keep' : 'replace', `Keep retains the existing Secret; GitHub does not reveal its value for inspection. Replace asks for a new credential and may update the Secret after Apply. Skip leaves this optional credential unconfigured. Check the plan before approving writes.\nRead more: ${AUTHENTICATION_GUIDE}`); } showCredentialChecks(checks) { if (checks.length === 0) @@ -67178,16 +68787,21 @@ class SetupCredentialPromptAdapter { throw new exports.SetupTerminalCancelledError(); return result.value.trim(); } - async readChoice(label, choices, defaultValue) { + async readChoice(label, choices, defaultValue, help) { while (true) { const lines = choices.map((choice, index) => ` ${index + 1}) ${choice}${choice === defaultValue ? (0, setup_prompt_rendering_1.color)(' (default)', 90) : ''}`); const result = await this.terminal.readText([ label, ...lines, + ...(help ? ['Type ? for more detail without selecting an answer.'] : []), `Select 1-${choices.length}${defaultValue ? ` ${(0, setup_prompt_rendering_1.color)(`[${choices.indexOf(defaultValue) + 1}]`, 90)}` : ''}: `, ].join('\n')); if (result.kind !== 'value') throw new exports.SetupTerminalCancelledError(); + if (result.value.trim() === '?' && help) { + console.log((0, setup_prompt_rendering_1.renderBox)(help, 'About this credential choice')); + continue; + } if (!result.value.trim() && defaultValue) return defaultValue; const index = Number(result.value) - 1; @@ -67369,6 +68983,8 @@ function renderSetupPlan(plan) { ` Outcome: ${approval.mode === 'off' ? 'disabled' : approval.mode === 'recommend' ? 'recommendation only' : 'eligible PRs may be approved after default-branch installation and live evidence'}`, ' Native approval still requires readable stale-dismissal rules and a distinct runtime PAT bot.', '', (0, setup_prompt_rendering_1.color)('Repository changes', 36), + ` Production/development branches: ${plan.configuration.repository.mainBranch} / ${plan.configuration.repository.developmentBranch}`, + ` Projects: ${plan.configuration.projects.ids || '(none)'}`, ` Files selected: ${plan.selectedFiles.length}`, ` Variables to upsert: ${plan.configuration.manageRepositoryVariables ? plan.variables.length : 0}`, ` Secret options to validate/provision: ${plan.configuration.manageRepositorySecrets ? plan.credentialRequirements.length : 0}`, @@ -67376,6 +68992,8 @@ function renderSetupPlan(plan) { ` Secret storage: ${storageLabel(plan.configuration.storage.secrets)}`, ' Labels and issue types: always checked by Copilot setup', ` Initial tag: ${plan.configuration.createInitialTag ? 'v1.0.0 when no version tag exists' : 'disabled'}`, '', + ...(plan.presentationDefaults?.length ? [(0, setup_prompt_rendering_1.color)('Advanced defaults retained in basic setup', 36), + ...plan.presentationDefaults.map(item => ` ${item.group}: ${item.count} settings not asked; use :edit at plan confirmation to review or change.`), ''] : []), ...(plan.mergeQueueReadiness.length > 0 ? [ (0, setup_prompt_rendering_1.color)('Merge queue readiness', 36), ...plan.mergeQueueReadiness.map((check) => ` ${(0, setup_prompt_rendering_1.doctorIcon)(check.status)} ${check.id}: ${check.summary}`), @@ -67554,6 +69172,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.ConsoleSetupQuestionRenderer = void 0; const setup_prompt_rendering_1 = __nccwpck_require__(83434); const setup_questionnaire_policy_1 = __nccwpck_require__(6009); +const setup_question_guidance_policy_1 = __nccwpck_require__(42775); class ConsoleSetupQuestionRenderer { constructor(phase = 'full', choiceReviewPass = 1) { this.phase = phase; @@ -67581,27 +69200,65 @@ class ConsoleSetupQuestionRenderer { showState(stateId) { console.log((0, setup_prompt_rendering_1.color)(`\n${(0, setup_questionnaire_policy_1.setupQuestionnaireStateLabel)(stateId)}\n`, 36)); } - renderPrompt(question) { + renderPrompt(question, progress) { + const help = (0, setup_question_guidance_policy_1.setupQuestionPresentation)(question).en; + const step = progress ? `${(0, setup_questionnaire_policy_1.setupQuestionnaireStateLabel)(progress.group)} — question ${progress.groupPosition} of ${progress.groupTotal} (overall ${progress.position} of ${progress.total}).\n` : ''; + const source = question.suggestionSource === 'github' ? ' (observed from authenticated GitHub repository metadata)' + : question.suggestionSource === 'local' ? ' (observed in this local checkout; confirm it exists on GitHub)' + : question.suggestionSource === 'configuration' ? ' (provided by your configuration)' + : question.suggestionSource === 'default' ? ' (product default; not verified against GitHub)' : ''; + const heading = `${step}${question.label}\n ${help.summary}\n Suggested: ${formatDefault(question.defaultValue)}${source}. ${help.documentation.title}: ${help.documentation.url}\n Type ? for detailed help; type :back to return to the previous question without clearing saved answers.${discoveryNote(question)}`; + const reviewedStatuses = question.projectStatusValues?.map(item => ` ${item.transition}: ${item.value}`).join('\n'); const fallback = formatDefault(question.defaultValue); if (question.kind === 'choice') { const choices = question.choices ?? []; - const lines = choices.map((choice, index) => ` ${index + 1}) ${choice}${choice === question.defaultValue ? (0, setup_prompt_rendering_1.color)(' (default)', 90) : ''}`); - return [question.label, ...lines, `Select 1-${choices.length} ${(0, setup_prompt_rendering_1.color)(`[${choices.indexOf(String(question.defaultValue)) + 1}]`, 90)}: `].join('\n'); + const lines = choices.map((choice, index) => ` ${index + 1}) ${choice}${question.id === 'pullRequestApproval.coverage.checkName' + ? (() => { + const producer = question.trustedProducers?.find(item => item.name === choice); + return producer ? ` — ${producer.workflowName} · App ${producer.sourceAppId}` : ''; + })() : ''}${choice === question.defaultValue ? (0, setup_prompt_rendering_1.color)(' (default)', 90) : ''}`); + return [heading, ...lines, `Select 1-${choices.length} ${(0, setup_prompt_rendering_1.color)(`[${choices.indexOf(String(question.defaultValue)) + 1}]`, 90)}: `].join('\n'); } if (question.kind === 'multi-select') { - const selected = new Set(formatDefault(question.defaultValue).split(',').map(item => item.trim()).filter(Boolean)); - const choices = question.choices ?? []; - const lines = choices.map((choice, index) => { - const workflowId = choice === 'All' ? 'all' : choice.split(' — ')[0]; - const checked = selected.has('all') || selected.has(workflowId) ? '●' : '○'; - return ` ${checked} ${index === 0 ? 'All' : choice}`; - }); - return [question.label, ...lines, 'Use ↑/↓ and Space to toggle; Enter to confirm.'].join('\n'); + return [heading, 'Use ↑/↓ and Space to toggle; Enter to confirm. Press ? for help or B for the previous question.'].join('\n'); + } + if (question.kind === 'producer-select') { + const choices = question.producerCandidates ?? []; + const lines = choices.map((candidate, index) => ` ${index + 1}) ${candidate.name} · App ${candidate.sourceAppId} · ${candidate.workflowName} · ${candidate.conclusion} · ${candidate.headSha.slice(0, 7)} · ${candidate.observedAt ?? 'date unavailable'}\n ${candidate.runUrl}\n ${candidate.requiredByRuleset ? `Required on ${candidate.requiredByRuleset.branch} by active ruleset: ${candidate.requiredByRuleset.sourceUrl}` : 'Required by branch rule: not checked'}`); + return [heading, ...lines, 'Enter check numbers separated by commas (for example 1,2), or exact name|App ID|workflow tuples separated by semicolons.', + 'A listed ruleset proves only the exact required check/App pair on that target branch. "Not checked" is not evidence that the check is optional; inspect branch protection too.', + 'A suggested check is not proof of coverage. Inspect its workflow and required step before attesting.', + ` ${(0, setup_prompt_rendering_1.color)(`[${fallback}]`, 90)}: `].join('\n'); + } + if (question.kind === 'project-select') { + return [heading, + 'Use ↑/↓ and Space to choose Projects; B returns to the previous question. Their numbers come from the GitHub URL, not PVT_ node IDs.', + 'Select "Manual entry" if a Project is missing. Select "Retry" to query GitHub again without restarting setup.', + 'All selected Projects must share each chosen Status value; this setup cannot map different values per Project.'].join('\n'); } if (question.kind === 'scope-overrides' && question.allowedNames?.length) { - return `${question.label}\n Available: ${question.allowedNames.join(', ')}; enter "none" to inherit all\n ${(0, setup_prompt_rendering_1.color)(`[${fallback}]`, 90)}: `; + return `${heading}\n Available: ${question.allowedNames.join(', ')}; enter "none" to inherit all\n ${(0, setup_prompt_rendering_1.color)(`[${fallback}]`, 90)}: `; } - return `${question.label} ${(0, setup_prompt_rendering_1.color)(`[${fallback}]`, 90)}: `; + return `${heading}${reviewedStatuses ? `\n Verify these exact Status values in every selected Project:\n${reviewedStatuses}` : ''}${question.statusOptionState === 'unavailable' + ? '\n Status options could not be verified for every Project. Check the exact existing value in every selected Project before continuing.' : ''}${question.statusOptionState === 'incompatible' + ? '\n Selected Projects have no common Status values. Return to Project selection and choose compatible Projects.' : ''}\n ${(0, setup_prompt_rendering_1.color)(`[${fallback}]`, 90)}: `; + } + renderHelp(question) { + const help = (0, setup_question_guidance_policy_1.setupQuestionPresentation)(question).en; + return [ + `What: ${help.summary}`, + `When: ${help.when}`, + `Where: ${help.where}`, + `How: ${help.how}`, + `Why: ${help.why}`, + `Example: ${help.example}`, + `Effect: ${help.effect}`, + `Verify: ${help.verify}`, + `Read more — ${help.documentation.title}: ${help.documentation.url}`, + ].join('\n'); + } + showHelp(question) { + console.log((0, setup_prompt_rendering_1.renderBox)(this.renderHelp(question), 'About this setup choice')); } showValidation(message) { console.log((0, setup_prompt_rendering_1.color)(message, 33)); @@ -67616,6 +69273,100 @@ function formatDefault(value) { return value ? 'Y' : 'N'; return String(value) || 'none'; } +function discoveryNote(question) { + const status = question.discoveryStatus; + if (!status) + return ''; + const check = { + observed: 'Recent CI jobs were found. Inspect the linked runs before trusting a producer.', + 'no-recent-runs': 'No recent PR CI runs were found. Run normal CI or enter an exact producer manually.', + 'no-verifiable-checks': 'Recent runs exist, but exact job/App identity could not be verified. Use manual entry after inspecting GitHub.', + 'permission-denied': 'GitHub denied CI discovery. Give the setup PAT Actions: read and Checks: read, or enter a verified producer manually.', + unavailable: 'CI discovery failed; this does not mean there are no checks. Retry or use verified manual entry.', + }; + const project = { + observed: 'Existing organization Projects are listed below. Inspect each GitHub URL before selecting it.', + empty: 'The bounded GitHub query returned no accessible Projects; this does not prove none exist. Check organization access or enter a verified number manually.', + 'permission-denied': 'GitHub denied Project discovery. Check organization Projects: read on the setup PAT, or enter numbers manually.', + unavailable: 'Project discovery failed; this does not mean no Projects exist. Use a verified number or retry.', + unsupported: 'Fine-grained PATs cannot list personal Projects through this GitHub API. Use the number in an existing Project URL.', + }; + const note = question.id === 'projects.ids' ? project[status] : check[status]; + const sample = status === 'observed' || status === 'empty' || status === 'no-recent-runs' || status === 'no-verifiable-checks' + ? question.id === 'projects.ids' + ? '\n Search scope: at most 30 accessible organization Projects from two pages; up to 100 fields per Project.' + : '\n Search scope: up to 20 recent PR workflow runs; at most 15 runs and 100 checks per commit are inspected.' + : ''; + return note ? `\n ${note}${sample}${question.discoveryTruncated ? '\n Only a bounded sample was inspected; use manual entry for missing items.' : ''}${question.discoveryRetryRemaining ? `\n Type r to retry GitHub discovery (${question.discoveryRetryRemaining} read-only attempts left).` : ''}` : ''; +} + + +/***/ }), + +/***/ 44132: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.setupResultReason = setupResultReason; +exports.setupResultEffects = setupResultEffects; +exports.setupActionResultFailure = setupActionResultFailure; +const result_1 = __nccwpck_require__(73817); +function setupResultReason(code) { + if (!code) + return 'unknown'; + if (code.startsWith('authorization.')) + return 'permissions'; + if (code.startsWith('configuration.') || code === 'validation.invalid-input') + return 'configuration'; + if (code === 'provider.rate-limited') + return 'rate-limit'; + if (code.startsWith('provider.') || code === 'timeout') + return 'provider'; + return 'unknown'; +} +/** Provider errors are never serialized; failed steps remain potentially applied. */ +function setupResultEffects(results) { + for (const result of results) { + const receipt = (0, result_1.getResultPayload)((0, result_1.getResultPayload)(result.payload)?.setupReceipt); + if (receipt?.version !== 1 || !Array.isArray(receipt.effects)) + continue; + const parsed = receipt.effects.map(parseEffect); + if (parsed.length === EFFECT_IDS.length && parsed.every(Boolean) + && EFFECT_IDS.every(id => parsed.some(effect => effect?.id === id))) + return parsed; + } + return results.map((result, index) => ({ + id: safeEffectId(result.id, index), + state: !result.success || result.errors.length > 0 ? 'needs-inspection' + : result.executed ? 'completed' : 'skipped', + })); +} +const EFFECT_IDS = ['files', 'secrets', 'labels', 'issue-types', 'variables', 'initial-tag']; +const EFFECT_STATES = ['completed', 'skipped', 'needs-inspection', 'not-started']; +const EFFECT_SCOPES = ['local', 'repository', 'organization', 'mixed']; +function parseEffect(value) { + const effect = (0, result_1.getResultPayload)(value); + if (!effect || !EFFECT_IDS.includes(effect.id) + || !EFFECT_STATES.includes(effect.state) + || !EFFECT_SCOPES.includes(effect.scope)) + return undefined; + return { id: effect.id, state: effect.state, scope: effect.scope }; +} +function setupActionResultFailure(results) { + const first = results.flatMap(result => result.errors)[0]; + if (!first) + return results.some(result => !result.success) ? { reasonCode: 'unknown' } : undefined; + if (typeof first !== 'object') + return { reasonCode: 'unknown' }; + return { reasonCode: setupResultReason(first.code), + ...(first.correlationId ? { diagnosticRef: first.correlationId } : {}) }; +} +function safeEffectId(value, index) { + return EFFECT_IDS.includes(value) ? value + : value === 'InitialSetupUseCase' ? 'setup-workflow' : `step-${index + 1}`; +} /***/ }), @@ -67820,7 +69571,7 @@ class NodeTerminalDriver { input.once('end', onEnd); }); } - async readMultiSelect(prompt, choices, selected) { + async readMultiSelect(prompt, choices, selected, helpText) { if (this.closed) return { kind: 'end-of-input' }; const input = node_process_1.stdin; @@ -67881,6 +69632,16 @@ class NodeTerminalDriver { finish({ kind: 'end-of-input' }); return; } + if (character === 'b' || character === 'B') { + finish({ kind: 'value', value: ':back' }); + return; + } + if (character === '?' && helpText) { + node_process_1.stdout.write(`\n${helpText}\n\n`); + rendered = false; + render(); + continue; + } if (character === ' ') { const id = choices[index] === 'All' ? 'all' : choices[index].split(' — ')[0]; if (id === 'all') @@ -68094,7 +69855,9 @@ exports.SetupWorkflowUpdatePromptAdapter = SetupWorkflowUpdatePromptAdapter; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.WebSetupCredentialPrompt = exports.WebSetupJourneyPresenter = exports.WebSetupPermissionPresenter = exports.WebSetupWorkflowUpdatePrompt = exports.WebSetupPlanConfirmation = exports.WebSetupPlanPresenter = exports.WebSetupQuestionnaireCollector = void 0; +exports.validationCopy = validationCopy; const setup_questionnaire_policy_1 = __nccwpck_require__(6009); +const setup_question_guidance_policy_1 = __nccwpck_require__(42775); const setup_interaction_cancelled_error_1 = __nccwpck_require__(38313); const web_setup_bridge_1 = __nccwpck_require__(21518); class WebSetupQuestionnaireCollector { @@ -68102,16 +69865,45 @@ class WebSetupQuestionnaireCollector { this.bridge = bridge; this.pass = pass; } - async collect(initial, context) { + async collect(initial, context, discoveryRefresh) { let state = initial; + let currentContext = context; while (state.terminal === 'collecting' && state.question) { - if (state.validation) - this.bridge.message(state.validation, 'warning'); + if (state.validation) { + const copy = validationCopy(state.validation) ?? { id: 'validation.unknown' }; + this.bridge.message(state.validation, 'warning', undefined, copy.id, copy.values); + } const value = await this.bridge.ask({ kind: 'question', title: (0, setup_questionnaire_policy_1.setupQuestionnaireStateLabel)(state.stateId), - question: state.question, phase: state.phase ?? 'full', pass: this.pass, + question: state.question, presentation: (0, setup_question_guidance_policy_1.setupQuestionPresentation)(state.question), phase: state.phase ?? 'full', pass: this.pass, + progress: (0, setup_questionnaire_policy_1.setupQuestionnaireProgress)(state, currentContext), canGoBack: ((0, setup_questionnaire_policy_1.setupQuestionnaireProgress)(state, currentContext)?.position ?? 1) > 1, + }, discoveryRefresh && state.question.discoveryRetryRemaining ? async () => { + const kind = state.question?.id === 'projects.ids' ? 'projects' + : state.question?.id === 'pullRequestApproval.testChecks' ? 'checks' : undefined; + if (!kind) + return undefined; + const refreshed = await discoveryRefresh.refresh(kind); + if (!refreshed) + return undefined; + const refreshedState = (0, setup_questionnaire_policy_1.refreshSetupQuestionnaireQuestion)(state, refreshed); + return refreshedState.question ? { prompt: { kind: 'question', + title: (0, setup_questionnaire_policy_1.setupQuestionnaireStateLabel)(refreshedState.stateId), + question: refreshedState.question, presentation: (0, setup_question_guidance_policy_1.setupQuestionPresentation)(refreshedState.question), + phase: refreshedState.phase ?? 'full', pass: this.pass, + progress: (0, setup_questionnaire_policy_1.setupQuestionnaireProgress)(refreshedState, refreshed), + canGoBack: ((0, setup_questionnaire_policy_1.setupQuestionnaireProgress)(refreshedState, refreshed)?.position ?? 1) > 1 }, + commit: () => { currentContext = refreshed; state = refreshedState; } } : undefined; + } : undefined, () => { + const previous = (0, setup_questionnaire_policy_1.transitionSetupQuestionnaire)(state, { kind: 'back' }, currentContext); + if (previous.question?.id === state.question?.id || !previous.question) + return undefined; + return { prompt: { kind: 'question', title: (0, setup_questionnaire_policy_1.setupQuestionnaireStateLabel)(previous.stateId), + question: previous.question, presentation: (0, setup_question_guidance_policy_1.setupQuestionPresentation)(previous.question), + phase: previous.phase ?? 'full', pass: this.pass, progress: (0, setup_questionnaire_policy_1.setupQuestionnaireProgress)(previous, currentContext), + canGoBack: ((0, setup_questionnaire_policy_1.setupQuestionnaireProgress)(previous, currentContext)?.position ?? 1) > 1 }, + commit: () => { state = previous; } }; }); - state = (0, setup_questionnaire_policy_1.transitionSetupQuestionnaire)(state, value === undefined ? { kind: 'cancel' } : { kind: 'answer', value }, context); + state = (0, setup_questionnaire_policy_1.transitionSetupQuestionnaire)(state, value === undefined ? { kind: 'cancel' } : { kind: 'answer', value }, currentContext); } return state; } @@ -68122,7 +69914,7 @@ class WebSetupPlanPresenter { this.bridge = bridge; } present(plan) { - this.bridge.message(`Plan ready: ${plan.selectedFiles.length} files, ${plan.variables.length} Variables and ${plan.requiredSecrets.length} Secret names. Review it before continuing.`); + this.bridge.message(`Plan ready: ${plan.selectedFiles.length} files, ${plan.variables.length} Variables and ${plan.requiredSecrets.length} Secret names. Review it before continuing.`, 'info', undefined, 'plan.ready', { files: String(plan.selectedFiles.length), variables: String(plan.variables.length), secrets: String(plan.requiredSecrets.length) }); } } exports.WebSetupPlanPresenter = WebSetupPlanPresenter; @@ -68131,7 +69923,14 @@ class WebSetupPlanConfirmation { this.bridge = bridge; } async confirm(plan) { - const response = await this.bridge.ask({ kind: 'plan', title: 'Review your setup plan', plan: (0, web_setup_bridge_1.toWebSetupPlan)(plan) }); + const groups = (0, setup_questionnaire_policy_1.setupEditableGroups)(plan.configuration); + const response = await this.bridge.ask({ kind: 'plan', title: 'Review your setup plan', copyId: 'plan.review', plan: (0, web_setup_bridge_1.toWebSetupPlan)(plan), editGroups: groups }); + if (response?.startsWith('revise:')) { + const group = response.slice('revise:'.length); + if (groups.includes(group)) + return { kind: 'revise', group }; + throw new Error('Invalid setup section.'); + } return { kind: response === undefined ? 'cancelled' : response === 'approve' ? 'approved' : 'declined' }; } } @@ -68150,6 +69949,7 @@ class WebSetupWorkflowUpdatePrompt { kind: 'confirm', title: 'Update existing workflows?', description: changed.map(item => `${item.destination} (${item.status})`).join('\n'), choices: ['Keep existing', 'Update setup-managed workflows'], + copyId: 'workflow.update', copyValues: { files: changed.map(item => item.destination).join(', ') }, }); if (answer === undefined) throw new setup_interaction_cancelled_error_1.SetupInteractionCancelledError(); @@ -68183,40 +69983,40 @@ class WebSetupCredentialPrompt { configureSetupPatGuide(url) { this.setupGuide = url; } useManualSetupPat() { this.guidedSetup = false; this.setupGuide = undefined; } async chooseSetupPatMethod() { - this.guidedSetup = await this.choice('How will you provide your setup PAT?', ['Guided GitHub link', 'Manual PAT']) === 'Guided GitHub link'; + this.guidedSetup = await this.choice('How will you provide your setup PAT?', ['Guided GitHub link', 'Manual PAT'], undefined, 'setupPat.method') === 'Guided GitHub link'; return this.guidedSetup ? 'guided' : 'manual'; } async chooseSetupOwnerKind() { - const answer = await this.choice('What kind of GitHub account owns this repository?', ['Organization', 'Personal account', 'Not sure']); + const answer = await this.choice('What kind of GitHub account owns this repository?', ['Organization', 'Personal account', 'Not sure'], undefined, 'setupPat.ownerKind'); return answer === 'Organization' ? 'Organization' : answer === 'Personal account' ? 'User' : 'unknown'; } async reviewSetupPatIntent() { - const answer = await this.choice('Review these provisional setup PAT grants', ['Continue to GitHub', 'Review setup choices again', 'View full permission table', 'Enter a PAT manually']); + const answer = await this.choice('Review these provisional setup PAT grants', ['Continue to GitHub', 'Review setup choices again', 'View full permission table', 'Enter a PAT manually'], undefined, 'setupPat.review'); return answer === 'Review setup choices again' ? 'revise' : answer === 'View full permission table' ? 'details' : answer === 'Enter a PAT manually' ? 'manual' : 'continue'; } async requestSetupPat() { - return this.secret('Temporary setup PAT', 'Use the operator account in GitHub. Complete 2FA there, switch to Only select repositories, select this repository, and copy the generated token here. This token is for this run only; delete it in GitHub afterwards.', this.guidedSetup ? this.setupGuide : undefined); + return this.secret('Temporary setup PAT', 'Use the operator account in GitHub. Complete 2FA there, switch to Only select repositories, select this repository, and copy the generated token here. This token is for this run only; delete it in GitHub afterwards.', this.guidedSetup ? this.setupGuide : undefined, false, 'setupPat.entry'); } async confirmGuidedSetupAccount(account) { if (!this.guidedSetup) return true; if (!account) return false; - return await this.choice(`GitHub authenticated the setup PAT as @${account}. Is that the intended operator account?`, ['Yes, continue', 'No, stop']) === 'Yes, continue'; + return await this.choice(`GitHub authenticated the setup PAT as @${account}. Is that the intended operator account?`, ['Yes, continue', 'No, stop'], undefined, 'setupPat.confirmAccount', { account }) === 'Yes, continue'; } showUpdatedSetupPatLink(url, stage, delta) { - this.bridge.message(`Setup PAT ${stage === 'final' ? 'permissions changed' : 'access failed'}. No setup mutation started. ${delta?.join(', ') ?? ''} Create a corrected PAT using the updated GitHub link.`, 'warning', url); + this.bridge.message(`Setup PAT ${stage === 'final' ? 'permissions changed' : 'access failed'}. No setup mutation started. ${delta?.join(', ') ?? ''} Create a corrected PAT using the updated GitHub link.`, 'warning', url, stage === 'final' ? 'setupPat.corrected.final' : 'setupPat.corrected.bootstrap', { grants: delta?.join(', ') ?? '' }); } showSetupPatCleanupReminder() { if (this.guidedSetup) - this.bridge.message('Delete the temporary setup PAT in GitHub Settings after this run. Closing Copilot does not revoke it.', 'warning', 'https://github.com/settings/personal-access-tokens'); + this.bridge.message('Delete the temporary setup PAT in GitHub Settings after this run. Closing Copilot does not revoke it.', 'warning', 'https://github.com/settings/personal-access-tokens', 'setupPat.cleanup'); } async confirmUnverifiableTokenPermissions(report) { const writes = report.checks.filter(item => item.applicability === 'required' && item.level === 'write' && item.status === 'unverifiable'); if (!report.confirmationRequired || writes.length === 0) return false; - return await this.choice('GitHub cannot safely prove these write grants without a mutation. Confirm they are configured exactly as shown.', ['No, stop', 'Yes, I checked them']) === 'Yes, I checked them'; + return await this.choice('GitHub cannot safely prove these write grants without a mutation. Confirm they are configured exactly as shown.', ['No, stop', 'Yes, I checked them'], undefined, 'setupPat.confirmWrites') === 'Yes, I checked them'; } configureWorkflowPatGuide(url, resolveIdentity, requirements) { this.workflowGuide = url; @@ -68224,15 +70024,15 @@ class WebSetupCredentialPrompt { this.workflowRequirements = requirements; } explainCredentialSeparation(requirements) { - this.bridge.message(`The bot PAT is separate from your setup PAT. Runtime credentials (${requirements.map(item => item.name).join(', ')}) become GitHub Actions Secrets; existing Secret values cannot be read back. This browser flow will not dispatch or install a credential-health workflow before Apply. Re-enter an existing bot PAT so its grants can be audited.`, 'info'); + this.bridge.message(`The bot PAT is separate from your setup PAT. Runtime credentials (${requirements.map(item => item.name).join(', ')}) become GitHub Actions Secrets; existing Secret values cannot be read back. This browser flow will not dispatch or install a credential-health workflow before Apply. Re-enter an existing bot PAT so its grants can be audited.`, 'info', undefined, 'botPat.separation', { names: requirements.map(item => item.name).join(', ') }); } async requestWorkflowPat(requirement, current) { let guide; let botInfo = ''; if (this.workflowGuide) { - const method = await this.choice('How will you provide the bot PAT?', ['Guided GitHub link', 'Manual PAT']); + const method = await this.choice('How will you provide the bot PAT?', ['Guided GitHub link', 'Manual PAT'], undefined, 'botPat.method'); if (method === 'Guided GitHub link') { - const login = await this.text('Expected GitHub bot login', 'Enter the bot account login, without @. We will verify its numeric account ID against the token.'); + const login = await this.text('Expected GitHub bot login', 'Enter the bot account login, without @. We will verify its numeric account ID against the token.', 'botPat.login'); if (!login || !/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/.test(login)) throw new Error('Enter a valid GitHub bot login.'); this.botIdentity = await this.resolveBot(login); @@ -68242,42 +70042,77 @@ class WebSetupCredentialPrompt { else if (this.workflowRequirements) this.bridge.requirements('workflow', this.workflowRequirements); } - const value = await this.secret(`${requirement.name} — bot account PAT`, `${botInfo}Use the bot account, select only the intended repository and review all grants. Suggested expiry is 90 days. ${current ? `Existing Secret: ${current.status}; its value cannot be read back.` : ''}`, guide); + const value = await this.secret(`${requirement.name} — bot account PAT`, `${botInfo}Use the bot account, select only the intended repository and review all grants. Suggested expiry is 90 days. ${current ? `Existing Secret: ${current.status}; its value cannot be read back.` : ''}`, guide, false, guide ? 'botPat.entry.guided' : 'botPat.entry.manual', { name: requirement.name, account: this.botIdentity?.login ?? '', accountId: String(this.botIdentity?.id ?? ''), existing: current?.status ?? '' }); return value ? { name: requirement.name, value } : undefined; } async requestApiKey(requirement, current) { - const value = await this.secret(`${requirement.name} — ${requirement.provider ?? 'provider'} API key`, current?.message, undefined, Boolean(requirement.alternativeGroups?.length)); + const value = await this.secret(`${requirement.name} — ${requirement.provider ?? 'provider'} API key`, current?.message, undefined, Boolean(requirement.alternativeGroups?.length), 'credential.apiKey', { name: requirement.name, provider: requirement.provider ?? 'provider' }); return value ? { name: requirement.name, value } : undefined; } async chooseExistingCredential(requirement, check) { - const answer = await this.choice(`Existing ${requirement.name}: ${check.status}`, ['keep', 'replace', 'skip'], check.message); + const answer = await this.choice(`Existing ${requirement.name}: ${check.status}`, ['keep', 'replace', 'skip'], check.message, 'credential.existing', { name: requirement.name, status: check.status }); return answer; } showCredentialChecks(checks) { - this.bridge.message(checks.map(item => `${item.name}: ${item.status} — ${item.message}`).join('\n'), checks.some(item => item.status === 'invalid') ? 'warning' : 'success'); + this.bridge.message(checks.map(item => `${item.name}: ${item.status} — ${item.message}`).join('\n'), checks.some(item => item.status === 'invalid') ? 'warning' : 'success', undefined, 'credential.checks', { names: checks.map(item => item.name).join(', '), count: String(checks.length) }, checks.map(item => ({ name: item.name, status: item.status }))); } - async choice(title, choices, description) { - const answer = await this.bridge.ask({ kind: 'choice', title, choices, description }); + async choice(title, choices, description, copyId, copyValues) { + const answer = await this.bridge.ask({ kind: 'choice', title, choices, description, copyId, copyValues }); if (answer === undefined) throw new setup_interaction_cancelled_error_1.SetupInteractionCancelledError(); if (!choices.includes(answer)) throw new Error('Invalid setup choice.'); return answer; } - async text(title, description) { - const answer = await this.bridge.ask({ kind: 'text', title, description }); + async text(title, description, copyId) { + const answer = await this.bridge.ask({ kind: 'text', title, description, copyId }); if (answer === undefined) throw new setup_interaction_cancelled_error_1.SetupInteractionCancelledError(); return answer.trim(); } - async secret(title, description, link, optional = false) { - const answer = await this.bridge.ask({ kind: 'secret', title, description, optional, link }); + async secret(title, description, link, optional = false, copyId, copyValues) { + const answer = await this.bridge.ask({ kind: 'secret', title, description, optional, link, copyId, copyValues }); if (answer === undefined) throw new setup_interaction_cancelled_error_1.SetupInteractionCancelledError(); return answer.trim(); } } exports.WebSetupCredentialPrompt = WebSetupCredentialPrompt; +function validationCopy(message) { + const fixed = { + 'Select 1–8 observed checks or enter exact name|App ID|workflow tuples.': 'validation.producers', + 'Two trusted producers use the same check name. Coverage stores only one name; choose one producer or rename the CI jobs before continuing.': 'validation.duplicateNames', + 'Open every selected Project in GitHub and confirm that all four exact Status values exist. Answer Yes after checking, or No to choose Projects again.': 'validation.projectStatusVerified', + 'Status values were not confirmed. Choose compatible Projects, then review their Status options again.': 'validation.projectStatusRedo', + 'Enter a non-negative whole number.': 'validation.number', + 'Enter yes or no.': 'validation.boolean', + 'Select one of the listed options.': 'validation.choice', + 'This is the first question in this pass. Review it or cancel setup.': 'validation.firstQuestion', + 'A trusted check was selected more than once.': 'validation.duplicateProducer', + 'The saved Status value is not available in every selected Project. Choose a listed Status option.': 'validation.savedStatus', + 'Selected Projects have no common Status option. Choose compatible Projects or configure them separately.': 'validation.projectIncompatible', + 'Choose at most 10 Projects; separate numbers or URLs with commas.': 'validation.projectLimit', + 'A Project URL needs a known repository owner; enter its positive number instead.': 'validation.projectOwnerNeeded', + 'Enter a valid GitHub Project URL or positive Project number.': 'validation.projectUrl', + 'Enter the positive Project number from its GitHub URL, not a PVT_ GraphQL ID.': 'validation.projectNumber', + 'Project numbers must be positive integers at most 2147483647.': 'validation.projectNumberRange', + }; + if (fixed[message]) + return { id: fixed[message] }; + const inherited = message.match(/^Unknown inherited resource name\(s\): (.+)\.$/u); + if (inherited) + return { id: 'validation.unknownResource', values: { names: inherited[1] } }; + const workflows = message.match(/^Unknown issue workflow\(s\): (.+)\.$/u); + if (workflows) + return { id: 'validation.unknownWorkflow', values: { names: workflows[1] } }; + const owner = message.match(/^Use a GitHub Project URL belonging to ([^,]+), without query parameters\.$/u); + if (owner) + return { id: 'validation.projectOwnerMismatch', values: { owner: owner[1] } }; + const duplicate = message.match(/^Project ([1-9]\d*) was selected more than once\.$/u); + if (duplicate) + return { id: 'validation.projectDuplicate', values: { number: duplicate[1] } }; + return undefined; +} /***/ }), @@ -68296,6 +70131,7 @@ class WebSetupBridge { constructor(repository) { this.revision = 0; this.subscribers = new Set(); + this.doctorAttempts = 0; this.bootstrapped = false; this.view = { revision: 0, repository }; } @@ -68315,25 +70151,65 @@ class WebSetupBridge { } takeOver() { this.controller = (0, node_crypto_1.randomBytes)(32).toString('hex'); - this.publish({ message: { tone: 'info', text: 'Control moved to this tab. The previous tab is now read-only.' } }); + this.publish({ message: { tone: 'info', text: 'Control moved to this tab. The previous tab is now read-only.', copyId: 'session.controlMoved' } }); return this.controller; } isController(capability) { return Boolean(this.controller && sameCapability(capability, this.controller)); } - async ask(prompt) { + async ask(prompt, refresh, navigateBack) { if (this.pending || this.view.outcome) throw new Error('A setup decision is already pending or the session has ended.'); const revision = this.revision + 1; this.publish({ prompt, promptRevision: revision }); - return new Promise(resolve => { this.pending = { revision, resolve }; }); + return new Promise(resolve => { this.pending = { revision, resolve, refresh, navigateBack }; }); + } + back(revision) { + const pending = this.pending; + if (!pending || pending.revision !== revision || this.view.outcome) + return 'stale'; + if (!pending.navigateBack || pending.refreshing) + return 'unavailable'; + const result = pending.navigateBack(); + if (!result) + return 'unavailable'; + result.commit(); + pending.revision = this.revision + 1; + this.publish({ prompt: result.prompt, promptRevision: pending.revision, message: undefined }); + return 'updated'; + } + async retryDiscovery(revision) { + const pending = this.pending; + if (!pending || pending.revision !== revision || this.view.outcome) + return 'stale'; + if (!pending.refresh || pending.refreshing) + return 'unavailable'; + const controller = this.controller; + pending.refreshing = true; + try { + const result = await pending.refresh(); + if (this.pending !== pending || this.view.outcome || controller !== this.controller) + return 'stale'; + if (!result) + return 'unavailable'; + result.commit(); + // Keep promptRevision stable so the browser retains unsent manual and checkbox input. + this.publish({ prompt: result.prompt }); + return 'updated'; + } + finally { + pending.refreshing = false; + } } answer(revision, value) { - if (!this.pending || this.pending.revision !== revision || this.view.outcome) + if (!this.pending || this.pending.revision !== revision || this.pending.refreshing || this.view.outcome) return false; const prompt = this.view.prompt; if (prompt && (prompt.kind === 'choice' || prompt.kind === 'confirm') && !prompt.choices.includes(value)) return false; + if (prompt?.kind === 'plan' && value !== 'approve' && value !== 'decline' + && !prompt.editGroups?.some(group => value === `revise:${group}`)) + return false; const pending = this.pending; this.pending = undefined; this.lastAnsweredRevision = revision; @@ -68342,18 +70218,48 @@ class WebSetupBridge { return true; } wasAnswered(revision) { return this.lastAnsweredRevision === revision; } + configureReadOnlyDoctor(run) { + this.readOnlyDoctor = run; + } + async runReadOnlyDoctor() { + if (this.view.outcome !== 'complete' || !this.readOnlyDoctor) + return 'unavailable'; + if (this.view.doctor?.status === 'running') + return 'busy'; + if (this.view.doctor?.status === 'complete') + return 'complete'; + if (this.doctorAttempts >= 2) + return 'unavailable'; + this.doctorAttempts += 1; + this.publish({ doctor: { status: 'running' } }); + try { + const summary = await this.readOnlyDoctor(); + const counts = [summary.pass, summary.warn, summary.fail, summary.skipped]; + if (counts.some(value => !Number.isSafeInteger(value) || value < 0)) + throw new Error('Invalid doctor summary.'); + this.publish({ doctor: { status: 'complete', healthy: summary.healthy === true, + pass: summary.pass, warn: summary.warn, fail: summary.fail, skipped: summary.skipped } }); + return 'complete'; + } + catch { + this.publish({ doctor: { status: 'failed' } }); + return 'failed'; + } + } cancel() { if (this.view.journey?.mutationStarted || this.view.outcome) return false; const pending = this.pending; this.pending = undefined; - this.publish({ prompt: undefined, promptRevision: undefined, outcome: 'cancelled', message: { tone: 'warning', text: 'Setup cancelled before applying further changes. Any PAT created at GitHub still exists until you delete it there.' } }); + this.publish({ prompt: undefined, promptRevision: undefined, outcome: 'cancelled', resultDetail: { + reasonCode: 'cancelled', stoppedStage: this.view.journey?.current ?? 'Preparation', mutationStarted: false, + }, message: { tone: 'warning', text: 'Setup cancelled before applying further changes. Any PAT created at GitHub still exists until you delete it there.', copyId: 'session.cancelled' } }); pending?.resolve(undefined); return true; } setJourney(journey) { this.publish({ journey }); } - message(text, tone = 'info', link) { - this.publish({ message: { tone, text, ...(link ? { link } : {}) } }); + message(text, tone = 'info', link, copyId, copyValues, credentialChecks) { + this.publish({ message: { tone, text, ...(link ? { link } : {}), copyId, copyValues, credentialChecks } }); } requirements(role, requirements) { this.publish({ permissions: { role, requirements, report: undefined } }); @@ -68361,12 +70267,37 @@ class WebSetupBridge { report(report) { this.publish({ permissions: { role: report.role, requirements: this.view.permissions?.requirements, report } }); } + resultReason(reasonCode, diagnosticRef) { + if (this.view.outcome) + return; + this.publish({ resultDetail: { + reasonCode, + stoppedStage: this.view.journey?.current ?? 'Preparation', + mutationStarted: this.view.journey?.mutationStarted === true, + ...(this.view.resultDetail?.effects ? { effects: this.view.resultDetail.effects } : {}), + ...(diagnosticRef && /^[0-9a-f-]{36}$/u.test(diagnosticRef) ? { diagnosticRef } : {}), + } }); + } + effects(effects) { + if (this.view.outcome) + return; + this.publish({ resultDetail: { reasonCode: this.view.resultDetail?.reasonCode ?? 'unknown', + stoppedStage: this.view.journey?.current ?? 'Preparation', + mutationStarted: this.view.journey?.mutationStarted === true, effects, + ...(this.view.resultDetail?.diagnosticRef ? { diagnosticRef: this.view.resultDetail.diagnosticRef } : {}) } }); + } finish(outcome, text) { if (this.view.outcome) return; this.pending?.resolve(undefined); this.pending = undefined; - this.publish({ prompt: undefined, promptRevision: undefined, outcome, message: { tone: outcome === 'complete' || outcome === 'dry-run' ? 'success' : 'warning', text } }); + this.publish({ prompt: undefined, promptRevision: undefined, outcome, message: { tone: outcome === 'complete' || outcome === 'dry-run' ? 'success' : 'warning', text }, + ...(this.view.resultDetail ? {} : { resultDetail: { + reasonCode: outcome === 'cancelled' ? 'cancelled' : outcome === 'blocked' ? 'unknown' : 'unknown', + stoppedStage: this.view.journey?.current ?? 'Preparation', + mutationStarted: this.view.journey?.mutationStarted === true, + } }), + }); } publish(change) { this.revision += 1; @@ -68389,6 +70320,36 @@ function sameCapability(provided, expected) { } function toWebSetupPlan(plan) { return { + presentationDefaults: plan.presentationDefaults ?? [], + decisions: { + enabledCapabilities: Object.entries(plan.configuration.features).filter(([, enabled]) => enabled).map(([name]) => name), + agentRouting: Object.entries(plan.configuration.agents).map(([role, agent]) => ({ role, + provider: agent.provider, modelProvider: agent.modelProvider, model: agent.model })), + issueWorkflows: plan.configuration.features.issues ? plan.configuration.issueWorkflows.enabled : [], + productionBranch: plan.configuration.repository.mainBranch, + developmentBranch: plan.configuration.repository.developmentBranch, + approvalMode: plan.configuration.pullRequestApproval.mode, + trustedChecks: plan.configuration.pullRequestApproval.testChecks.map(check => ({ name: check.name, + sourceAppId: check.sourceAppId, workflowName: check.workflowName })), + producerAttested: plan.configuration.pullRequestApproval.producerAttested, + coverageMode: plan.configuration.pullRequestApproval.coverage.mode, + coverageCheck: plan.configuration.pullRequestApproval.coverage.checkName, + ...(plan.configuration.pullRequestApproval.coverage.mode === 'numeric' ? { + coverageMinimum: plan.configuration.pullRequestApproval.coverage.minDiffPercent, + coverageArtifactWorkflow: plan.configuration.pullRequestApproval.coverage.artifactWorkflowName, + coverageReporterAttested: plan.configuration.pullRequestApproval.coverage.reporterAttested, + } : {}), + projectNumbers: plan.configuration.projects.ids.split(',').filter(Boolean), + projectStatuses: [ + { transition: 'issueCreated', value: plan.configuration.projects.issueCreatedColumn }, + { transition: 'pullRequestCreated', value: plan.configuration.projects.pullRequestCreatedColumn }, + { transition: 'issueInProgress', value: plan.configuration.projects.issueInProgressColumn }, + { transition: 'pullRequestInProgress', value: plan.configuration.projects.pullRequestInProgressColumn }, + ], + variableScope: plan.configuration.manageRepositoryVariables ? plan.configuration.storage.variables.defaultScope : 'disabled', + secretScope: plan.configuration.manageRepositorySecrets ? plan.configuration.storage.secrets.defaultScope : 'disabled', + initialTag: plan.configuration.createInitialTag, + }, files: plan.selectedFiles, workflows: plan.workflowFiles, variables: plan.variables.map(variable => variable.name), @@ -68446,6 +70407,7 @@ async function startWebSetupServer(bridge, assets = (0, node_path_1.join)(__dirn clearTimeout(idleTimer); idleTimer = setTimeout(() => { if (!bridge.snapshot().journey?.mutationStarted && !bridge.snapshot().outcome) { + bridge.resultReason('session-expired'); bridge.finish('blocked', 'This local setup session expired after 30 minutes without a decision. Start a new setup run; GitHub PATs are not revoked automatically.'); } }, 30 * 60 * 1000); @@ -68547,6 +70509,89 @@ async function startWebSetupServer(bridge, assets = (0, node_path_1.join)(__dirn : { error: 'This question changed. Refresh the current state.' }); return; } + if (request.method === 'POST' && request.url === '/api/retry-discovery') { + const capability = String(request.headers['x-setup-capability'] ?? ''); + if (!bridge.isController(capability)) { + respond(response, 403, { error: 'This tab is read-only.' }); + return; + } + if (request.headers['content-type'] !== 'application/json') { + respond(response, 415, { error: 'JSON required.' }); + return; + } + const body = await readJson(request); + if (!Number.isSafeInteger(body.revision) || body.revision <= 0) { + respond(response, 400, { error: 'Invalid question revision.' }); + return; + } + if (!bridge.isController(capability)) { + respond(response, 403, { error: 'Control moved to another tab.' }); + return; + } + const result = await bridge.retryDiscovery(body.revision); + if (!bridge.isController(capability)) { + respond(response, 403, { error: 'Control moved to another tab.' }); + return; + } + if (result === 'updated') + armIdle(); + respond(response, result === 'updated' ? 200 : 409, result === 'updated' + ? { updated: true } : { error: result === 'stale' ? 'This question changed. Refresh the current state.' + : 'Discovery cannot be retried here. Use the manual option.' }); + return; + } + if (request.method === 'POST' && request.url === '/api/back') { + const capability = String(request.headers['x-setup-capability'] ?? ''); + if (!bridge.isController(capability)) { + respond(response, 403, { error: 'This tab is read-only.' }); + return; + } + if (request.headers['content-type'] !== 'application/json') { + respond(response, 415, { error: 'JSON required.' }); + return; + } + const body = await readJson(request); + if (!Number.isSafeInteger(body.revision) || body.revision <= 0) { + respond(response, 400, { error: 'Invalid question revision.' }); + return; + } + if (!bridge.isController(capability)) { + respond(response, 403, { error: 'Control moved to another tab.' }); + return; + } + const result = bridge.back(body.revision); + if (result === 'updated') + armIdle(); + respond(response, result === 'updated' ? 200 : 409, result === 'updated' + ? { updated: true } : { error: result === 'stale' ? 'This question changed. Refresh the current state.' + : 'No earlier question is available here.' }); + return; + } + if (request.method === 'POST' && request.url === '/api/doctor') { + const capability = String(request.headers['x-setup-capability'] ?? ''); + if (!bridge.isController(capability)) { + respond(response, 403, { error: 'This tab is read-only.' }); + return; + } + if (request.headers['content-type'] !== 'application/json') { + respond(response, 415, { error: 'JSON required.' }); + return; + } + await readJson(request); + if (!bridge.isController(capability)) { + respond(response, 403, { error: 'Control moved to another tab.' }); + return; + } + const result = await bridge.runReadOnlyDoctor(); + if (!bridge.isController(capability)) { + respond(response, 403, { error: 'Control moved to another tab.' }); + return; + } + respond(response, result === 'complete' ? 200 : 409, result === 'complete' + ? { checked: true } : { error: result === 'failed' ? 'Read-only verification failed. Check the terminal.' + : 'Read-only verification is unavailable or already running.' }); + return; + } if (request.method === 'POST' && request.url === '/api/cancel') { if (!bridge.isController(String(request.headers['x-setup-capability'] ?? ''))) { respond(response, 403, { error: 'This tab is read-only.' }); @@ -68648,6 +70693,7 @@ async function startWebSetupServer(bridge, assets = (0, node_path_1.join)(__dirn armIdle(); const hardTimer = setTimeout(() => { if (!bridge.snapshot().journey?.mutationStarted && !bridge.snapshot().outcome) { + bridge.resultReason('session-expired'); bridge.finish('blocked', 'This local setup session reached its four-hour limit. Start a new run; no prior approval can be replayed.'); } }, 4 * 60 * 60 * 1000); @@ -68701,6 +70747,7 @@ exports.cleanCliArg = cleanCliArg; exports.getGitInfo = getGitInfo; exports.getCurrentBranch = getCurrentBranch; exports.getCurrentAttachedBranch = getCurrentAttachedBranch; +exports.hasLocalOrTrackedGitBranch = hasLocalOrTrackedGitBranch; exports.getCurrentHeadSha = getCurrentHeadSha; exports.isInsideGitRepo = isInsideGitRepo; exports.getGitRepositoryRoot = getGitRepositoryRoot; @@ -68745,6 +70792,19 @@ function getCurrentAttachedBranch(cwd) { return undefined; } } +/** Positive local evidence only; a missing ref says nothing about remote branches. */ +function hasLocalOrTrackedGitBranch(cwd, branch) { + if (!/^[A-Za-z0-9][A-Za-z0-9._/-]{0,99}$/u.test(branch) || branch.includes('..') || branch.endsWith('.lock')) + return false; + for (const ref of [`refs/heads/${branch}`, `refs/remotes/origin/${branch}`]) { + try { + (0, child_process_1.execFileSync)('git', ['show-ref', '--verify', '--quiet', ref], { cwd, stdio: 'pipe' }); + return true; + } + catch { /* Try the other explicit ref. */ } + } + return false; +} /** Returns the canonical object ID for the workspace revision being analyzed. */ function getCurrentHeadSha() { try { @@ -77225,6 +79285,7 @@ const github_error_policy_1 = __nccwpck_require__(58791); const credential_health_workflow_visibility_1 = __nccwpck_require__(57628); const tweetnacl_1 = __importDefault(__nccwpck_require__(24258)); const node_crypto_1 = __nccwpck_require__(6005); +const deployment_configuration_1 = __nccwpck_require__(22495); class GithubActionsResourceTransport { constructor(githubClient) { this.githubClient = githubClient; @@ -77260,6 +79321,8 @@ class GithubActionsResourceTransport { const credentialHealthWorkflow = await this.inspectDefaultCredentialHealthWorkflow(client, owner, repository); return { ownerType, + ...(typeof metadata.default_branch === 'string' && (0, deployment_configuration_1.isSafeBranchTree)(metadata.default_branch) + ? { defaultBranch: metadata.default_branch } : {}), repositoryId: metadata.id, repositoryVisibility, repositorySecrets: repositorySecretsResult.resources, @@ -80537,6 +82600,7 @@ function validatePullRequestApprovalPolicy(value, allowIncomplete = false) { } else { const identities = new Set(); + const names = new Set(); for (const item of value.testChecks) { if (!isRecord(item)) { errors.push('Each test check must be an object.'); @@ -80550,6 +82614,9 @@ function validatePullRequestApprovalPolicy(value, allowIncomplete = false) { if (identities.has(identity)) errors.push('Test checks cannot contain duplicate producer identities.'); identities.add(identity); + if (value.mode !== 'off' && !allowIncomplete && names.has(String(item.name))) + errors.push('Trusted check names must be unique because coverage stores only a check name.'); + names.add(String(item.name)); } } if (typeof value.producerAttested !== 'boolean') @@ -83760,6 +85827,274 @@ exports.PROJECT_BOARD_ITEM_PAGE_LIMIT = void 0; exports.PROJECT_BOARD_ITEM_PAGE_LIMIT = 500; +/***/ }), + +/***/ 42294: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.GithubSetupApprovalCheckDiscoveryAdapter = void 0; +const github = __importStar(__nccwpck_require__(78227)); +/** Bounded, read-only GitHub evidence. Unavailable permissions yield no suggestions, never invented identities. */ +class GithubSetupApprovalCheckDiscoveryAdapter { + async discover(owner, repository, token, targetBranch) { + const octokit = github.getOctokit(token); + // Active rules need only Metadata: read. Only repository-owned rulesets + // get an exact, safe detail link; inherited rules remain unverified here. + let required = new Map(); + if (targetBranch && /^[A-Za-z0-9][A-Za-z0-9._/-]{0,99}$/u.test(targetBranch)) { + try { + const response = await octokit.request('GET /repos/{owner}/{repo}/rules/branches/{branch}', { + owner, repo: repository, branch: targetBranch, per_page: 100, + }); + for (const rule of response.data) { + if (rule.type !== 'required_status_checks' || rule.ruleset_source_type !== 'Repository' + || rule.ruleset_source?.toLowerCase() !== `${owner}/${repository}`.toLowerCase() + || !Number.isSafeInteger(rule.ruleset_id) || rule.ruleset_id <= 0) + continue; + for (const check of rule.parameters?.required_status_checks ?? []) { + if (safeProducerName(check.context ?? '') && Number.isSafeInteger(check.integration_id) && check.integration_id > 0) { + required.set(`${check.context}\u0000${check.integration_id}`, `https://github.com/${encodeURIComponent(owner)}/${encodeURIComponent(repository)}/rules/${rule.ruleset_id}`); + } + } + } + } + catch { + required = new Map(); + } + } + let recent; + try { + recent = await octokit.rest.actions.listWorkflowRunsForRepo({ owner, repo: repository, event: 'pull_request', per_page: 20 }); + } + catch (error) { + return { status: discoveryFailure(error), candidates: [] }; + } + if (recent.data.workflow_runs.length === 0) + return { status: 'no-recent-runs', candidates: [] }; + const candidates = new Map(); + const checksByHead = new Map(); + try { + for (const run of recent.data.workflow_runs.slice(0, 15)) { + const headSha = run.head_sha; + if (!/^[a-f0-9]{40}$/iu.test(headSha)) + continue; + if (!run.name || run.name.startsWith('Copilot -') || run.status !== 'completed') + continue; + let checks = checksByHead.get(headSha); + if (!checks) { + const response = await octokit.rest.checks.listForRef({ owner, repo: repository, ref: headSha, filter: 'all', per_page: 100 }); + checks = response.data.check_runs; + checksByHead.set(headSha, checks); + } + const jobs = await octokit.rest.actions.listJobsForWorkflowRunAttempt({ + owner, repo: repository, run_id: run.id, attempt_number: run.run_attempt, per_page: 100, + }); + for (const job of jobs.data.jobs) { + const id = Number(job.check_run_url?.match(/\/check-runs\/(\d+)$/u)?.[1]); + const check = checks.find(item => item.id === id && item.head_sha === run.head_sha); + if (!check?.app?.id || !Number.isSafeInteger(check.app.id) + || !safeProducerName(check.name) || !safeProducerName(run.name) + || check.name === 'Copilot / Approval') + continue; + const identity = `${check.name}\u0000${check.app.id}\u0000${run.name}`; + if (!candidates.has(identity)) + candidates.set(identity, { + name: check.name, sourceAppId: check.app.id, workflowName: run.name, + ...(check.app.name && safeProducerName(check.app.name) ? { sourceAppName: check.app.name } : {}), + runUrl: `https://github.com/${encodeURIComponent(owner)}/${encodeURIComponent(repository)}/actions/runs/${run.id}`, + headSha, conclusion: check.conclusion ?? 'unknown', + ...(run.created_at && Number.isFinite(Date.parse(run.created_at)) ? { observedAt: run.created_at } : {}), + ...(required.has(`${check.name}\u0000${check.app.id}`) ? { requiredByRuleset: { + branch: targetBranch, sourceUrl: required.get(`${check.name}\u0000${check.app.id}`), + } } : {}), + }); + if (candidates.size >= 30) + return { status: 'observed', candidates: [...candidates.values()], truncated: true }; + } + } + } + catch (error) { + return { status: discoveryFailure(error), candidates: [] }; + } + return { status: candidates.size > 0 ? 'observed' : 'no-verifiable-checks', candidates: [...candidates.values()], + ...(recent.data.workflow_runs.length > 15 ? { truncated: true } : {}) }; + } +} +exports.GithubSetupApprovalCheckDiscoveryAdapter = GithubSetupApprovalCheckDiscoveryAdapter; +function discoveryFailure(error) { + const status = typeof error === 'object' && error !== null && 'status' in error ? Number(error.status) : undefined; + return status === 401 || status === 403 ? 'permission-denied' : 'unavailable'; +} +function safeProducerName(value) { + return typeof value === 'string' && value.trim() === value && /^[^\p{Cc}\p{Cf}${}<>|;]{1,100}$/u.test(value); +} + + +/***/ }), + +/***/ 29564: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.GithubSetupProjectDiscoveryAdapter = void 0; +const github = __importStar(__nccwpck_require__(78227)); +/** Bounded, read-only organization Project inventory; personal fine-grained PATs cannot use GitHub's user REST listing. */ +class GithubSetupProjectDiscoveryAdapter { + async discover(owner, ownerType, token) { + if (ownerType === 'User') + return { status: 'unsupported', candidates: [] }; + if (ownerType !== 'Organization') + return { status: 'unavailable', candidates: [] }; + const octokit = github.getOctokit(token); + const candidates = []; + let after; + let truncated = false; + try { + for (let page = 0; page < 2; page += 1) { + const response = await octokit.request('GET /orgs/{org}/projectsV2', { + org: owner, per_page: 50, ...(after ? { after } : {}), + }); + for (const row of response.data) { + if (!Number.isSafeInteger(row.number) || Number(row.number) < 1 || row.state === 'closed' + || typeof row.title !== 'string' || !safeDisplayText(row.title)) + continue; + const number = Number(row.number); + candidates.push({ number, title: row.title, owner, + url: `https://github.com/orgs/${encodeURIComponent(owner)}/projects/${number}` }); + } + after = nextCursor(response.headers.link); + if (!after) + break; + if (candidates.length >= 30) { + truncated = true; + break; + } + if (page === 1) + truncated = true; + } + } + catch (error) { + return { status: discoveryFailure(error), candidates: [] }; + } + const unique = [...new Map(candidates.map(candidate => [candidate.number, candidate])).values()]; + if (unique.length > 30) + truncated = true; + const inspected = await Promise.all(unique.slice(0, 30).map(async (candidate) => { + try { + const response = await octokit.request('GET /orgs/{org}/projectsV2/{project_number}/fields', { + org: owner, project_number: candidate.number, per_page: 100, + }); + if (nextCursor(response.headers.link)) + return candidate; + const status = response.data.find(field => field.name === 'Status' && field.data_type === 'single_select'); + const options = status?.options?.map(option => typeof option.name === 'string' ? option.name : option.name?.raw) + .filter((name) => typeof name === 'string' && safeDisplayText(name)); + return options?.length ? { ...candidate, statusOptions: [...new Set(options)] } : candidate; + } + catch { + return candidate; + } + })); + return { status: inspected.length ? 'observed' : 'empty', candidates: inspected, ...(truncated ? { truncated: true } : {}) }; + } +} +exports.GithubSetupProjectDiscoveryAdapter = GithubSetupProjectDiscoveryAdapter; +function nextCursor(link) { + if (typeof link !== 'string') + return undefined; + const next = link.split(',').find(part => /;\s*rel="next"/u.test(part)); + const urlText = next?.match(/<([^>]+)>/u)?.[1]; + if (!urlText) + return undefined; + try { + const url = new URL(urlText); + const cursor = url.searchParams.get('after'); + return url.hostname === 'api.github.com' && cursor && cursor.length <= 200 ? cursor : undefined; + } + catch { + return undefined; + } +} +function safeDisplayText(value) { + return value.trim() === value && value.length > 0 && value.length <= 120 && !/[\p{Cc}\p{Cf}<>]/u.test(value); +} +function discoveryFailure(error) { + const status = typeof error === 'object' && error !== null && 'status' in error ? Number(error.status) : undefined; + return status === 401 || status === 403 ? 'permission-denied' : 'unavailable'; +} + + /***/ }), /***/ 72762: diff --git a/build/github_action/index.js b/build/github_action/index.js index 2e6d39f59..4ffc992b4 100644 --- a/build/github_action/index.js +++ b/build/github_action/index.js @@ -49372,7 +49372,7 @@ function buildSetupWarnings(configuration) { warnings.push('Inactive issue closure is enabled; waiting issues are closed after the configured inactivity threshold and can be reopened with a new comment.'); } if (configuration.projects.ids.trim()) { - warnings.push('Project IDs must be accessible to the PAT and use the expected project column names.'); + warnings.push('Selected Project numbers must be accessible to the bot PAT, and all four configured Status values must exist in every selected Project.'); } if ((0, setup_configuration_defaults_1.setupAgentTasksForFeatures)(configuration).some(task => configuration.agents[task].provider === 'cursor')) { warnings.push('Cursor is an experimental runtime in Copilot and requires a compatible preinstalled CLI plus CURSOR_API_KEY; Copilot has no automatic Cursor installer.'); @@ -49666,8 +49666,20 @@ const locale_1 = __nccwpck_require__(15386); const issue_workflow_profile_1 = __nccwpck_require__(26744); const setup_issue_workflow_policy_1 = __nccwpck_require__(81182); const pull_request_approval_policy_1 = __nccwpck_require__(98820); +const setup_project_selection_policy_1 = __nccwpck_require__(73750); function validateSetupConfiguration(configuration, options = {}) { const errors = []; + const projectSelection = (0, setup_project_selection_policy_1.parseSetupProjectSelection)(configuration.projects.ids); + if ('error' in projectSelection || projectSelection.value !== configuration.projects.ids) { + errors.push('Project IDs must be a comma-separated list of 1–10 distinct positive Project URL numbers; PVT_ node IDs are not accepted.'); + } + if (configuration.projects.ids) { + for (const [name, value] of Object.entries(configuration.projects).filter(([name]) => name.endsWith('Column'))) { + if (typeof value !== 'string' || !value.trim() || value.length > 100 || /[\p{Cc}\p{Cf}]/u.test(value)) { + errors.push(`Project ${name} must name one existing single-line Status option (1–100 characters).`); + } + } + } errors.push(...(0, pull_request_approval_policy_1.validatePullRequestApprovalPolicy)(configuration.pullRequestApproval, options.allowIncompleteApproval === true)); if (configuration.actionInputs['pr-approval-policy'] !== undefined) { errors.push('pr-approval-policy cannot be overridden through actionInputs.'); @@ -50066,6 +50078,80 @@ function effectiveIssueFormLabels(configuration) { } +/***/ }), + +/***/ 73750: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.parseSetupProjectSelection = parseSetupProjectSelection; +exports.sharedProjectStatusOptions = sharedProjectStatusOptions; +exports.validateDiscoveredProjectStatuses = validateDiscoveredProjectStatuses; +function parseSetupProjectSelection(raw, owner) { + const input = raw.normalize('NFKC').trim(); + if (!input || input.toLowerCase() === 'none') + return { value: '' }; + const parts = input.split(',').map(part => part.trim()); + if (parts.length > 10 || parts.some(part => !part)) + return { error: 'Choose at most 10 Projects; separate numbers or URLs with commas.' }; + const numbers = []; + for (const part of parts) { + let numberText = part; + if (part.startsWith('https://')) { + if (!owner) + return { error: 'A Project URL needs a known repository owner; enter its positive number instead.' }; + try { + const url = new URL(part); + const match = url.pathname.match(/^\/(?:orgs|users)\/([^/]+)\/projects\/([1-9]\d*)\/?$/u); + if (url.origin !== 'https://github.com' || url.search || url.hash || url.username || url.password + || !match || decodeURIComponent(match[1]).toLowerCase() !== owner.toLowerCase()) { + return { error: `Use a GitHub Project URL belonging to ${owner}, without query parameters.` }; + } + numberText = match[2]; + } + catch { + return { error: 'Enter a valid GitHub Project URL or positive Project number.' }; + } + } + if (!/^[1-9]\d*$/u.test(numberText)) + return { error: 'Enter the positive Project number from its GitHub URL, not a PVT_ GraphQL ID.' }; + const number = Number(numberText); + if (!Number.isSafeInteger(number) || number > 2147483647) + return { error: 'Project numbers must be positive integers at most 2147483647.' }; + if (numbers.includes(number)) + return { error: `Project ${number} was selected more than once.` }; + numbers.push(number); + } + return { value: numbers.join(',') }; +} +function sharedProjectStatusOptions(projectNumbers, projects) { + const numbers = projectNumbers.split(',').map(Number).filter(Boolean); + if (!numbers.length) + return { state: 'unavailable', options: [] }; + const selected = numbers.map(number => projects.find(project => project.number === number)); + if (selected.some(project => !project?.statusOptions?.length)) + return { state: 'unavailable', options: [] }; + const [first, ...rest] = selected; + const common = first.statusOptions.filter(option => rest.every(project => project.statusOptions.includes(option))); + return common.length ? { state: 'observed', options: common } : { state: 'incompatible', options: [] }; +} +/** A discovered mismatch is unsafe even if values arrived through --config rather than the interactive selector. */ +function validateDiscoveredProjectStatuses(configuration, discovery) { + if (!configuration.projects.ids || !discovery || discovery.status !== 'observed') + return []; + const common = sharedProjectStatusOptions(configuration.projects.ids, discovery.candidates); + if (common.state === 'incompatible') + return ['Selected Projects have no common Status option. Choose compatible Projects.']; + if (common.state !== 'observed') + return []; + const names = [configuration.projects.issueCreatedColumn, configuration.projects.pullRequestCreatedColumn, + configuration.projects.issueInProgressColumn, configuration.projects.pullRequestInProgressColumn]; + return names.filter(name => !common.options.includes(name)).map(name => `Status value "${name}" is not available in every selected Project.`); +} + + /***/ }), /***/ 3449: @@ -52036,18 +52122,32 @@ async function runInitialSetupWorkflow(request, dependencies) { (0, logging_ports_1.logInfo)(`${(0, task_emoji_1.getTaskEmoji)(TASK_ID)} Executing ${TASK_ID}.`); const steps = []; const errors = []; + const configuration = request.setupConfiguration; + const effects = [ + { id: 'files', state: 'not-started', scope: 'local' }, + { id: 'secrets', state: 'not-started', scope: resourceScope(configuration, 'secrets') }, + { id: 'labels', state: 'not-started', scope: 'repository' }, + { id: 'issue-types', state: 'not-started', scope: 'repository' }, + { id: 'variables', state: 'not-started', scope: resourceScope(configuration, 'variables') }, + { id: 'initial-tag', state: 'not-started', scope: 'repository' }, + ]; + const mark = (id, state) => { + const index = effects.findIndex(effect => effect.id === id); + effects[index] = { ...effects[index], state }; + }; + const receipt = () => buildResult(errors, steps, effects); try { const setupConfiguration = request.setupConfiguration; if (!dependencies.setupWorkspacePort.hasValidToken()) { (0, logging_ports_1.logInfo)(' 🛑 Setup requires the setup PAT provided for this command with a valid token.'); errors.push(new application_error_1.ApplicationError('authorization.credential-invalid', 'A valid setup PAT must be provided to run setup. It is separate from the workflow PAT Secret.')); - return [buildResult(errors, steps)]; + return [receipt()]; } (0, logging_ports_1.logInfo)('🔐 Checking GitHub access...'); const githubAccess = await verifyGitHubAccess(request, dependencies.authenticatedUserPort); if (!githubAccess.success) { errors.push(...githubAccess.errors); - return [buildResult(errors, steps)]; + return [receipt()]; } steps.push(`✅ GitHub access verified: ${githubAccess.user}`); const remoteConfigurationErrors = []; @@ -52058,7 +52158,7 @@ async function runInitialSetupWorkflow(request, dependencies) { if (remoteConfigurationErrors.length === 0) { errors.push(new application_error_1.ApplicationError('provider.unavailable', 'Could not inspect existing GitHub Actions resource scopes. Restore inventory access and rerun setup.')); } - return [buildResult(errors, steps)]; + return [receipt()]; } const inventoryErrors = [ ...(0, setup_configuration_policy_1.validateSetupStorageAgainstRemote)(setupConfiguration, remoteConfiguration), @@ -52069,7 +52169,7 @@ async function runInitialSetupWorkflow(request, dependencies) { ]; if (inventoryErrors.length > 0) { errors.push(...fromMessages(inventoryErrors, 'provider.unavailable')); - return [buildResult(errors, steps)]; + return [receipt()]; } } (0, logging_ports_1.logInfo)('📋 Ensuring .github and copying setup files...'); @@ -52081,15 +52181,25 @@ async function runInitialSetupWorkflow(request, dependencies) { approvedWorkflowFiles: request.workflowUpdates, } : {}), }; + mark('files', 'needs-inspection'); const filesResult = dependencies.setupWorkspacePort.prepare(workspaceSelection); + mark('files', filesResult.copied > 0 ? 'completed' : 'skipped'); steps.push(`✅ Setup files: ${filesResult.copied} copied, ${filesResult.skipped} already existed`); + const secretValues = Number(Boolean(request.setupCredentials?.workflowPat)) + (request.setupCredentials?.apiKeys.length ?? 0); + if (setupConfiguration?.manageRepositorySecrets && secretValues > 0) + mark('secrets', 'needs-inspection'); const secrets = await (0, setup_resource_provisioning_1.ensureRepositorySecrets)(request, dependencies, setupConfiguration, remoteConfiguration); + mark('secrets', secrets.errors.length ? 'needs-inspection' + : setupConfiguration?.manageRepositorySecrets && secretValues > 0 ? 'completed' : 'skipped'); if (secrets.step) steps.push(secrets.step); if (secrets.errors.length > 0) errors.push(...fromMessages(secrets.errors, 'authorization.credential-invalid')); (0, logging_ports_1.logInfo)('🏷️ Checking configured and progress labels...'); + mark('labels', 'needs-inspection'); const labels = await ensureInitialLabels(request, dependencies.initialLabelProvisioningPort, setupConfiguration); + mark('labels', !labels.completed || labels.configured.errors.length || labels.progress.errors.length + ? 'needs-inspection' : labels.configured.created + labels.progress.created > 0 ? 'completed' : 'skipped'); if (!labels.completed) { errors.push(labels.error); } @@ -52098,30 +52208,40 @@ async function runInitialSetupWorkflow(request, dependencies) { appendLabelSummary(steps, errors, labels.progress, 'Progress labels'); } (0, logging_ports_1.logInfo)('📋 Checking issue types...'); + mark('issue-types', 'needs-inspection'); const issueTypes = await ensureIssueTypes(request, dependencies.issueTypeProvisioningPort, setupConfiguration); + mark('issue-types', !issueTypes.success ? 'needs-inspection' : issueTypes.created > 0 ? 'completed' : 'skipped'); if (!issueTypes.success) { errors.push(...fromMessages(issueTypes.errors, 'provider.unavailable')); } else { steps.push(`✅ Issue types checked: ${issueTypes.created} created, ${issueTypes.existing} already existed`); } + if (setupConfiguration?.manageRepositoryVariables) + mark('variables', 'needs-inspection'); const variables = await (0, setup_resource_provisioning_1.ensureRepositoryVariables)(request, dependencies, setupConfiguration, remoteConfiguration); + mark('variables', variables.errors.length ? 'needs-inspection' + : setupConfiguration?.manageRepositoryVariables ? 'completed' : 'skipped'); if (variables.step) steps.push(variables.step); if (variables.errors.length > 0) errors.push(...fromMessages(variables.errors, 'provider.unavailable')); + if (setupConfiguration?.createInitialTag !== false) + mark('initial-tag', 'needs-inspection'); const defaultVersion = await ensureDefaultVersion(request, dependencies, setupConfiguration); + mark('initial-tag', defaultVersion.error ? 'needs-inspection' + : defaultVersion.step?.includes('created on branch') ? 'completed' : 'skipped'); if (defaultVersion.step) steps.push(defaultVersion.step); if (defaultVersion.error) errors.push(defaultVersion.error); - return [buildResult(errors, steps)]; + return [receipt()]; } catch (error) { const semanticError = (0, application_error_1.toApplicationError)(error, 'workflow.failed', 'Error running initial setup.'); (0, logging_ports_1.logError)(semanticError); errors.push(semanticError); - return [buildResult(errors, steps)]; + return [receipt()]; } } async function verifyGitHubAccess(_request, repository) { @@ -52199,15 +52319,22 @@ function appendLabelSummary(steps, errors, summary, labelType) { steps.push(`✅ ${labelType} checked: ${summary.created} created, ${summary.existing} already existed`); } } -function buildResult(errors, steps) { +function buildResult(errors, steps, effects) { return new result_1.Result({ id: TASK_ID, success: errors.length === 0, executed: true, steps, + payload: { setupReceipt: { version: 1, effects: effects.map(effect => ({ ...effect })) } }, errors: errors.length > 0 ? errors : undefined, }); } +function resourceScope(configuration, kind) { + const policy = configuration?.storage[kind]; + if (!policy) + return 'repository'; + return Object.values(policy.overrides).some(scope => scope !== policy.defaultScope) ? 'mixed' : policy.defaultScope; +} function fromMessages(messages, code) { return messages.map(message => new application_error_1.ApplicationError(code, message)); } @@ -65246,6 +65373,7 @@ exports.cleanCliArg = cleanCliArg; exports.getGitInfo = getGitInfo; exports.getCurrentBranch = getCurrentBranch; exports.getCurrentAttachedBranch = getCurrentAttachedBranch; +exports.hasLocalOrTrackedGitBranch = hasLocalOrTrackedGitBranch; exports.getCurrentHeadSha = getCurrentHeadSha; exports.isInsideGitRepo = isInsideGitRepo; exports.getGitRepositoryRoot = getGitRepositoryRoot; @@ -65290,6 +65418,19 @@ function getCurrentAttachedBranch(cwd) { return undefined; } } +/** Positive local evidence only; a missing ref says nothing about remote branches. */ +function hasLocalOrTrackedGitBranch(cwd, branch) { + if (!/^[A-Za-z0-9][A-Za-z0-9._/-]{0,99}$/u.test(branch) || branch.includes('..') || branch.endsWith('.lock')) + return false; + for (const ref of [`refs/heads/${branch}`, `refs/remotes/origin/${branch}`]) { + try { + (0, child_process_1.execFileSync)('git', ['show-ref', '--verify', '--quiet', ref], { cwd, stdio: 'pipe' }); + return true; + } + catch { /* Try the other explicit ref. */ } + } + return false; +} /** Returns the canonical object ID for the workspace revision being analyzed. */ function getCurrentHeadSha() { try { @@ -74381,6 +74522,7 @@ const github_error_policy_1 = __nccwpck_require__(58791); const credential_health_workflow_visibility_1 = __nccwpck_require__(57628); const tweetnacl_1 = __importDefault(__nccwpck_require__(24258)); const node_crypto_1 = __nccwpck_require__(6005); +const deployment_configuration_1 = __nccwpck_require__(22495); class GithubActionsResourceTransport { constructor(githubClient) { this.githubClient = githubClient; @@ -74416,6 +74558,8 @@ class GithubActionsResourceTransport { const credentialHealthWorkflow = await this.inspectDefaultCredentialHealthWorkflow(client, owner, repository); return { ownerType, + ...(typeof metadata.default_branch === 'string' && (0, deployment_configuration_1.isSafeBranchTree)(metadata.default_branch) + ? { defaultBranch: metadata.default_branch } : {}), repositoryId: metadata.id, repositoryVisibility, repositorySecrets: repositorySecretsResult.resources, @@ -77762,6 +77906,7 @@ function validatePullRequestApprovalPolicy(value, allowIncomplete = false) { } else { const identities = new Set(); + const names = new Set(); for (const item of value.testChecks) { if (!isRecord(item)) { errors.push('Each test check must be an object.'); @@ -77775,6 +77920,9 @@ function validatePullRequestApprovalPolicy(value, allowIncomplete = false) { if (identities.has(identity)) errors.push('Test checks cannot contain duplicate producer identities.'); identities.add(identity); + if (value.mode !== 'off' && !allowIncomplete && names.has(String(item.name))) + errors.push('Trusted check names must be unique because coverage stores only a check name.'); + names.add(String(item.name)); } } if (typeof value.producerAttested !== 'boolean') diff --git a/build/web/assets/index-7TY0kYbf.css b/build/web/assets/index-7TY0kYbf.css new file mode 100644 index 000000000..51d26e612 --- /dev/null +++ b/build/web/assets/index-7TY0kYbf.css @@ -0,0 +1 @@ +:root{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light dark;--page:#f6f8f7;--side:#102423;--side-line:#31504b;--side-text:#e8f5f0;--surface:#fff;--surface-soft:#f3f7f5;--line:#d7e3df;--control-line:#748b82;--text:#18312c;--muted:#58736b;--accent:#176e5e;--accent-strong:#075743;--accent-tint:#dff4e9;--focus:#966000;--warn:#76510d;--warn-bg:#fff6df;--error:#a73434;--error-bg:#fff0ec;--shadow:0 18px 50px #1e403414;font-family:Inter,ui-sans-serif,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}@media (prefers-color-scheme:dark){:root{--lightningcss-light: ;--lightningcss-dark:initial}:root:not([data-theme=light]){--page:#0d1716;--side:#10201d;--side-line:#294a41;--side-text:#eaf7f1;--surface:#182722;--surface-soft:#20342d;--line:#355247;--control-line:#688f7f;--text:#eaf5ee;--muted:#adccbc;--accent:#7ed6ac;--accent-strong:#a4edc2;--accent-tint:#234b38;--focus:#ffca6a;--warn:#ffdd8a;--warn-bg:#463b21;--error:#ffc0b7;--error-bg:#4a2b2a;--shadow:0 18px 50px #00000024}}:root[data-theme=dark]{--page:#0d1716;--side:#10201d;--side-line:#294a41;--side-text:#eaf7f1;--surface:#182722;--surface-soft:#20342d;--line:#355247;--control-line:#688f7f;--text:#eaf5ee;--muted:#adccbc;--accent:#7ed6ac;--accent-strong:#a4edc2;--accent-tint:#234b38;--focus:#ffca6a;--warn:#ffdd8a;--warn-bg:#463b21;--error:#ffc0b7;--error-bg:#4a2b2a;--shadow:0 18px 50px #00000024}:root[data-theme=light]{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light}:root[data-theme=dark]{--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}*{box-sizing:border-box}body{background:var(--page);color:var(--text);margin:0}button,input,select{font:inherit}button{cursor:pointer}button:disabled{cursor:not-allowed;opacity:.5}:focus-visible{outline:3px solid var(--focus);outline-offset:3px}.visually-hidden{clip:rect(0, 0, 0, 0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}a{color:var(--accent-strong);text-underline-offset:3px}.card,.context-card{background:var(--surface);border:1px solid var(--line);box-shadow:var(--shadow);border-radius:15px}.primary{border:1px solid var(--accent-strong);background:var(--accent-strong);color:var(--side);border-radius:8px;min-height:43px;padding:12px 18px;font-size:12px;font-weight:800}:root[data-theme=light] .primary,:root:not([data-theme=dark]) .primary{color:#fff}@media (prefers-color-scheme:dark){:root:not([data-theme=light]) .primary{color:#0d2419}}.primary span{margin-left:18px}.primary:hover:not(:disabled){filter:brightness(1.1)}.secondary{border:1px solid var(--control-line);color:var(--text);background:var(--surface-soft);border-radius:8px;padding:11px 16px;font-size:12px;font-weight:700}@media (prefers-reduced-motion:reduce){*,:before,:after{scroll-behavior:auto!important;transition-duration:.01ms!important;animation-duration:.01ms!important}}.shell{grid-template-columns:minmax(250px,288px) minmax(0,1fr);min-height:100vh;display:grid}.sidebar{background:var(--side);color:var(--side-text);flex-direction:column;height:100vh;padding:34px 28px;display:flex;position:sticky;top:0}.brand{letter-spacing:-.035em;align-items:center;gap:13px;display:flex}.brand-mark{color:#0c3021;background:#75d9a0;border-radius:11px;place-items:center;width:37px;height:37px;font-size:26px;line-height:1;display:grid}.brand strong{font-size:23px;line-height:1;display:block}.brand small{letter-spacing:.23em;color:#aac8bd;margin-top:5px;font-size:9px;font-weight:800;display:block}.rail-caption{color:#9dbab0;letter-spacing:.18em;margin-block:78px 22px;margin-inline-start:7px;font-size:10px;font-weight:800}.steps{margin:0;padding:0;list-style:none;position:relative}.steps:before{content:"";top:22px;bottom:22px;background:var(--side-line);width:1px;position:absolute;inset-inline-start:19px}.steps li{color:#a6c2b7;border-radius:10px;align-items:center;gap:16px;min-height:55px;padding-block:8px;padding-inline:1px 12px;font-size:13px;font-weight:600;display:flex;position:relative}.steps li.current{color:#fff;background:#25443b}.steps li.completed{color:#dbf2e4}.step-index{border:1px solid var(--side-line);background:var(--side);letter-spacing:.04em;border-radius:50%;flex:0 0 37px;place-items:center;height:37px;font-size:11px;font-weight:800;display:grid}.steps .current .step-index{color:#102b1d;background:#80dba8;border-color:#80dba8}.steps .completed .step-index{color:#b9f8c9;background:#204c37;border-color:#45966b;font-size:15px}.sidebar-note{border:1px solid var(--side-line);background:#ffffff09;border-radius:13px;gap:13px;margin-top:auto;padding:19px 16px;display:flex}.sidebar-note>span{color:#8fe1ae;font-size:20px}.sidebar-note strong{font-size:12px}.sidebar-note p{color:#afcabe;margin:6px 0 0;font-size:11px;line-height:1.6}.main{min-width:0}.topbar{border-bottom:1px solid var(--line);background:var(--surface);justify-content:space-between;align-items:center;gap:16px;height:80px;padding:0 clamp(24px,4vw,70px);display:flex}.breadcrumb{align-items:center;gap:12px;min-width:0;font-size:12px;display:flex}.breadcrumb span:first-child{color:var(--muted);letter-spacing:.14em;font-size:10px;font-weight:800}.breadcrumb span:nth-child(2){color:var(--muted)}.breadcrumb strong{white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.top-actions{flex-shrink:0;align-items:center;gap:18px;display:flex}.local-pill{color:var(--accent-strong);background:var(--accent-tint);letter-spacing:.08em;white-space:nowrap;border-radius:6px;padding:8px 11px;font-size:10px;font-weight:800}.pulse-dot{background:currentColor;border-radius:50%;width:6px;height:6px;margin-right:5px;display:inline-block}.theme-switch{border:1px solid var(--line);background:var(--surface-soft);border-radius:8px;padding:3px;display:flex}.theme-switch button{color:var(--muted);background:0 0;border:0;border-radius:5px;min-width:31px;height:27px;font-size:11px}.theme-switch button.active{background:var(--surface);color:var(--text);font-weight:800;box-shadow:0 1px 4px #0000001f}.content{max-width:1320px;margin:0 auto;padding:52px clamp(24px,4vw,70px) 35px}.eyebrow{color:var(--accent-strong);letter-spacing:.18em;align-items:center;gap:10px;font-size:10px;font-weight:900;display:flex}.eyebrow-line{background:var(--accent);width:21px;height:2px}.eyebrow-count{color:var(--muted);letter-spacing:.09em;margin-inline-start:8px}h1{letter-spacing:-.045em;max-width:860px;margin:15px 0 13px;font-size:clamp(30px,3vw,45px);line-height:1.15}.lede{color:var(--muted);max-width:700px;margin:0 0 30px;font-size:14px;line-height:1.65}.workspace-grid{grid-template-columns:minmax(0,1.65fr) minmax(230px,.8fr);align-items:start;gap:19px;display:grid}.context-column{gap:17px;display:grid}.context-card{box-shadow:none;padding:25px}.context-icon{background:var(--accent-tint);width:32px;height:32px;color:var(--accent-strong);border-radius:8px;place-items:center;font-size:19px;display:grid}.context-card h2{letter-spacing:-.015em;margin:17px 0 7px;font-size:14px}.context-card p,.context-card>small{color:var(--muted);margin:0 0 12px;font-size:12px;line-height:1.65;display:block}.context-card code{background:var(--surface-soft);overflow-wrap:anywhere;border-radius:6px;padding:10px;font-size:11px;display:block}.permissions ul{max-height:270px;margin:8px 0 13px;padding:0;list-style:none;overflow:auto}.permissions li{border-bottom:1px solid var(--line);justify-content:space-between;gap:10px;padding:9px 0;font-size:11px;display:flex}.permissions li small{color:var(--muted);margin-top:3px;display:block}.permissions li strong{color:var(--accent-strong);text-transform:uppercase;font-size:9px}footer{color:var(--muted);opacity:.85;letter-spacing:.11em;margin-top:40px;font-size:9px;font-weight:700}footer span{margin:0 8px}[dir=rtl] .context-card code,[dir=rtl] .result-links code,[dir=rtl] .producer-option strong,[dir=rtl] .producer-option small,[dir=rtl] input[type=password]{direction:ltr;unicode-bidi:isolate}.decision-card{min-height:360px;padding:clamp(25px,3vw,40px)}.card-header{justify-content:space-between;align-items:center;gap:12px;margin-bottom:29px;display:flex}.card-kicker{color:var(--accent-strong);letter-spacing:.17em;font-size:10px;font-weight:900}.revision{color:var(--muted);letter-spacing:.08em;font-size:10px}.description{white-space:pre-line;color:var(--muted);margin-top:0;font-size:13px;line-height:1.65}.question-heading{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:10px;margin-bottom:15px;display:flex}.question-heading h2,.decision-card>label{margin:0 0 10px;font-size:15px;font-weight:700;line-height:1.4;display:block}.phase-tag{color:var(--accent-strong);background:var(--accent-tint);letter-spacing:.07em;border-radius:5px;padding:6px 8px;font-size:9px;font-weight:900}input[type=text],input[type=password],input[type=number],select{border:1px solid var(--control-line);background:var(--surface-soft);width:100%;min-height:46px;color:var(--text);border-radius:8px;padding:10px 13px}textarea{resize:vertical;border:1px solid var(--control-line);background:var(--surface-soft);width:100%;min-height:120px;color:var(--text);font:inherit;border-radius:8px;padding:12px 13px;line-height:1.5}.language-switch{color:var(--muted);white-space:nowrap;align-items:center;gap:7px;font-size:11px;font-weight:700;display:flex}.language-switch select{width:auto;max-width:140px;min-height:32px;padding:5px 8px;font-size:11px}.question-details{border:1px solid var(--line);background:var(--surface-soft);border-radius:8px;margin-bottom:20px;padding:13px 15px;font-size:12px}.question-details summary{cursor:pointer;color:var(--accent-strong);font-weight:800}.question-details dl{grid-template-columns:minmax(90px,130px) minmax(0,1fr);gap:10px 14px;margin:14px 0 0;line-height:1.55;display:grid}.question-details dt{color:var(--text);font-weight:750}.question-details dd{color:var(--muted);margin:0}.question-help-link{margin:-12px 0 18px;font-size:12px;font-weight:700}.discovery-actions{flex-wrap:wrap;align-items:center;gap:8px 14px;margin:4px 0 16px;display:flex}.discovery-actions .field-help{margin:0}.secondary-button{border:1px solid var(--control-line);background:var(--surface-soft);color:var(--accent-strong);font:inherit;border-radius:8px;padding:9px 13px;font-size:12px;font-weight:750}.secondary-button:hover:not(:disabled),.secondary-button:focus-visible{border-color:var(--accent);background:var(--accent-tint)}.secondary-button:disabled{opacity:.55;cursor:not-allowed}.status-review-list{color:var(--muted);margin:6px 0 18px;padding-inline-start:20px;font-size:12px;line-height:1.7}.status-review-list strong{color:var(--text)}.producer-grid{max-height:330px;margin-bottom:15px}.producer-option{cursor:pointer;align-items:flex-start}.producer-option>span{overflow-wrap:anywhere;gap:5px;min-width:0;display:grid}.producer-option small{color:var(--muted);font-size:10px;line-height:1.5}.producer-option a{font-size:11px}input[type=checkbox]{accent-color:var(--accent);width:17px;height:17px}.field-help{color:var(--muted);margin:14px 0 24px;font-size:12px;line-height:1.6}.segmented{gap:9px;display:flex}.segmented button{border:1px solid var(--control-line);color:var(--text);background:var(--surface-soft);border-radius:8px;flex:1;padding:13px;font-weight:700}.segmented button.selected{border-color:var(--accent);background:var(--accent-tint);color:var(--accent-strong)}.check-grid{gap:7px;max-height:280px;display:grid;overflow-y:auto}.check-option{background:var(--surface-soft);border:1px solid var(--control-line);border-radius:7px;align-items:center;gap:10px;padding:10px 13px;font-size:12px;display:flex}.choice-list{gap:9px;display:grid}.choice-card{text-align:left;background:var(--surface-soft);width:100%;min-height:52px;color:var(--text);border:1px solid var(--control-line);border-radius:8px;justify-content:space-between;align-items:center;padding:13px 15px;font-size:13px;font-weight:650;display:flex}.choice-card:hover:not(:disabled){border-color:var(--accent);background:var(--accent-tint)}.github-link{background:var(--accent-tint);border:1px solid var(--accent);border-radius:8px;margin:0 0 12px;padding:14px;font-size:13px;font-weight:800;text-decoration:none;display:block}.github-link span{float:right}.plan-sections{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;display:grid}.plan-decisions{background:var(--surface-soft);border:1px solid var(--line);border-radius:8px;margin-bottom:15px;padding:16px}.plan-decisions h3{margin:0 0 10px;font-size:13px}.plan-decisions dl{gap:8px;margin:0;display:grid}.plan-decisions dl>div{grid-template-columns:minmax(130px,35%) 1fr;gap:12px;font-size:12px;display:grid}.plan-decisions dt{color:var(--muted)}.plan-decisions dd{overflow-wrap:anywhere;margin:0}.plan-sections>div,.plan-warnings{background:var(--surface-soft);border:1px solid var(--line);border-radius:8px;padding:13px}.plan-sections h3,.plan-warnings h3{justify-content:space-between;margin:0 0 8px;font-size:12px;display:flex}.plan-sections h3 span{color:var(--accent-strong)}.plan-sections ul,.plan-warnings ul{overflow-wrap:anywhere;max-height:120px;margin:0;padding-inline-start:18px;font-size:11px;line-height:1.7;overflow:auto}.plan-warnings{color:var(--warn);background:var(--warn-bg);margin-top:10px}.plan-edit{border-top:1px solid var(--line);margin-top:20px;padding-top:14px}.plan-edit h3{margin:0 0 4px;font-size:14px}.plan-edit-actions{flex-wrap:wrap;gap:8px;display:flex}.button-row{justify-content:space-between;gap:10px;margin-top:18px;display:flex}.review-pass,.banner{white-space:pre-line;border-radius:8px;margin:0 0 20px;padding:14px 18px;font-size:12px;line-height:1.5}.review-pass{color:var(--accent-strong);background:var(--accent-tint);border:1px solid var(--accent)}.review-pass span{margin-inline-end:8px;font-weight:800}.banner{background:var(--surface-soft);border:1px solid var(--line)}.banner p{margin:5px 0 0}.banner.warning{color:var(--warn);background:var(--warn-bg);border-color:var(--warn)}.banner.error{color:var(--error);background:var(--error-bg);border-color:var(--error)}.banner.success{color:var(--accent-strong);background:var(--accent-tint);border-color:var(--accent)}.banner button{margin-top:12px}.cancel-link{color:var(--muted);background:0 0;border:0;margin-top:18px;padding:5px 0;font-size:12px;text-decoration:underline}.result-card,.waiting-card{max-width:750px;padding:36px}.result-icon{background:var(--accent-tint);width:43px;height:43px;color:var(--accent-strong);border-radius:50%;place-items:center;font-size:22px;display:grid}.result-card h2,.waiting-card h2{margin:18px 0 10px;font-size:21px}.result-card p,.waiting-card p{color:var(--muted);font-size:13px;line-height:1.6}.result-facts{border:1px solid var(--line);background:var(--surface-soft);border-radius:8px;margin:20px 0;padding:8px 17px}.result-facts p{margin:8px 0}.result-facts strong{color:var(--text)}.result-effects{border:1px solid var(--line);background:var(--surface-soft);border-radius:8px;margin:20px 0;padding:14px 17px}.result-effects h3{color:var(--text);margin:0 0 10px;font-size:13px}.result-effects ul{margin:0;padding-left:20px}.result-effects li{overflow-wrap:anywhere;padding:3px 0;font-size:12px}.result-links{flex-wrap:wrap;align-items:center;gap:20px;margin:22px 0;font-size:12px;display:flex}.result-links code{background:var(--surface-soft);border-radius:5px;padding:8px}.spinner{border:3px solid var(--line);border-top-color:var(--accent);border-radius:50%;width:25px;height:25px;animation:1s linear infinite spin}@keyframes spin{to{transform:rotate(360deg)}}@media (width<=1100px){.workspace-grid{grid-template-columns:1fr}.context-column{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (width<=780px){.shell{display:block}.sidebar{height:auto;padding:16px 20px;position:static}.rail-caption,.sidebar-note{display:none}.steps{gap:4px;margin-top:18px;display:flex;overflow-x:auto}.steps:before{display:none}.steps li{flex:none;gap:6px;min-height:37px;padding:4px 7px;font-size:11px}.step-index{flex-basis:26px;width:26px;height:26px}.topbar{flex-wrap:wrap;height:auto;min-height:65px;padding:12px 20px}.content{padding:28px 20px}}@media (width<=540px){.context-column,.plan-sections{grid-template-columns:1fr}.top-actions{justify-content:space-between;width:100%}.decision-card{padding:22px}.breadcrumb{max-width:100%}h1{font-size:29px}.plan-decisions dl>div{grid-template-columns:1fr;gap:2px}.question-details dl{grid-template-columns:1fr;gap:3px}.question-details dd{margin-bottom:9px}.language-switch{margin-inline-start:auto}} diff --git a/build/web/assets/index-CJHJzy9C.js b/build/web/assets/index-CJHJzy9C.js new file mode 100644 index 000000000..aafaa7925 --- /dev/null +++ b/build/web/assets/index-CJHJzy9C.js @@ -0,0 +1,3 @@ +(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})(),typeof window<`u`&&((window.__svelte??={}).v??=new Set).add(`5`);var e=!1;function t(){e=!0}t();var n={},r=Symbol(`uninitialized`),i=`http://www.w3.org/1999/xhtml`,a=Array.isArray,o=Array.prototype.indexOf,s=Array.prototype.includes,c=Array.from,l=Object.defineProperty,u=Object.getOwnPropertyDescriptor,d=Object.getOwnPropertyDescriptors,f=Object.prototype,p=Array.prototype,m=Object.getPrototypeOf,h=Object.isExtensible,g=()=>{};function _(e){return e()}function v(e){for(var t=0;t{e=n,t=r}),resolve:e,reject:t}}var b=1024,x=2048,S=4096,ee=8192,te=16384,ne=32768,re=1<<25,ie=65536,ae=1<<19,oe=1<<20,se=1<<25,ce=1<<21,le=1<<22,ue=1<<23,de=Symbol(`$state`),fe=Symbol(`component`),pe=Symbol(`legacy props`),me=Symbol(``),he=Symbol(`attributes`),ge=Symbol(`class`),_e=Symbol(`style`),ve=Symbol(`text`),ye=Symbol(`form reset`),be=new class extends Error{name=`StaleReactionError`;message="The reaction that called `getAbortSignal()` was re-run or destroyed"},xe=!!globalThis.document?.contentType&&globalThis.document.contentType.includes(`xml`);function Se(){console.warn(`https://svelte.dev/e/derived_inert`)}function Ce(e){console.warn(`https://svelte.dev/e/hydration_mismatch`)}function we(){console.warn(`https://svelte.dev/e/select_multiple_invalid_value`)}function Te(){console.warn(`https://svelte.dev/e/svelte_boundary_reset_noop`)}var C=!1;function Ee(e){C=e}var w;function De(e){if(e===null)throw Ce(),n;return w=e}function Oe(){return De(cn(w))}function T(e){if(C){if(cn(w)!==null)throw Ce(),n;w=e}}function ke(e=1){if(C){for(var t=e,n=w;t--;)n=cn(n);w=n}}function Ae(e=!0){for(var t=0,n=w;;){if(n.nodeType===8){var r=n.data;if(r===`]`){if(t===0)return n;--t}else(r===`[`||r===`[!`||r[0]===`[`&&!isNaN(Number(r.slice(1))))&&(t+=1)}var i=cn(n);e&&n.remove(),n=i}}function je(e){if(!e||e.nodeType!==8)throw Ce(),n;return e.data}function Me(e){return e===this.v}function Ne(e,t){return e==e?e!==t||typeof e==`object`&&!!e||typeof e==`function`:t==t}function Pe(e){return!Ne(e,this.v)}function Fe(e){throw Error(`https://svelte.dev/e/lifecycle_outside_component`)}function Ie(){throw Error(`https://svelte.dev/e/async_derived_orphan`)}function Le(e,t,n){throw Error(`https://svelte.dev/e/each_key_duplicate`)}function Re(e){throw Error(`https://svelte.dev/e/effect_in_teardown`)}function ze(){throw Error(`https://svelte.dev/e/effect_in_unowned_derived`)}function Be(e){throw Error(`https://svelte.dev/e/effect_orphan`)}function Ve(){throw Error(`https://svelte.dev/e/effect_update_depth_exceeded`)}function He(e){throw Error(`https://svelte.dev/e/props_invalid_value`)}function Ue(){throw Error(`https://svelte.dev/e/state_descriptors_fixed`)}function We(){throw Error(`https://svelte.dev/e/state_prototype_fixed`)}function Ge(){throw Error(`https://svelte.dev/e/state_unsafe_mutation`)}function Ke(){throw Error(`https://svelte.dev/e/svelte_boundary_reset_onerror`)}var E=null;function qe(e){E=e}function D(t,n=!1,r){E={p:E,i:!1,c:null,e:null,s:t,x:null,r:z,l:e&&!n?{s:null,u:null,$:[]}:null}}function O(e){var t=E,n=t.e;if(n!==null){t.e=null;for(var r of n)xn(r)}return e!==void 0&&(t.x=e),t.i=!0,E=t.p,Je(e)}function Je(e={}){return l(e,fe,{value:!0}),e}function Ye(){return!e||E!==null&&E.l===null}var Xe=[];function Ze(){var e=Xe;Xe=[],v(e)}function Qe(e){if(Xe.length===0&&!Et){var t=Xe;queueMicrotask(()=>{t===Xe&&Ze()})}Xe.push(e)}function $e(){for(;Xe.length>0;)Ze()}var et=~(x|S|b);function tt(e,t){e.f=e.f&et|t}function nt(e){e.f&512||e.deps===null?tt(e,b):tt(e,S)}function rt(e,t,n){e.f&2048?t.add(e):e.f&4096&&n.add(e),tt(e,b)}function it(e){C&&sn(e)!==null&&ln(e)}var at=!1;function ot(){at||(at=!0,document.addEventListener(`reset`,e=>{Promise.resolve().then(()=>{if(!e.defaultPrevented)for(let t of e.target.elements)t[ye]?.()})},{capture:!0}))}function st(e){var t=R,n=z;qn(null),Jn(null);try{return e()}finally{qn(t),Jn(n)}}function ct(e,t,n,r=n){e.addEventListener(t,()=>st(n));let i=e[ye];e[ye]=i?()=>{i(),r(!0)}:()=>r(!0),ot()}function lt(e,t,n,r){let i=Ye()?pt:k;var a=e.filter(e=>!e.settled),o=t.map(i);if(n.length===0&&a.length===0){r(o);return}var s=z,c=ut(),l=a.length===1?a[0].promise:a.length>1?Promise.all(a.map(e=>e.promise)):null;function u(e){if(!(s.f&16384)){c();try{r([...o,...e])}catch(e){mn(e,s)}dt()}}var d=ft();if(n.length===0){l.then(()=>u([])).finally(d);return}function f(){Promise.all(n.map(e=>ht(e))).then(u).catch(e=>mn(e,s)).finally(d)}l?l.then(()=>{c(),f(),dt()}):f()}function ut(){var e=z,t=R,n=E,r=A;return function(i=!0){Jn(e),qn(t),qe(n),i&&!(e.f&16384)&&(r?.activate(),r?.apply())}}function dt(e=!0){Jn(null),qn(null),qe(null),e&&A?.deactivate()}function ft(){var e=z,t=e.b,n=A,r=!!t?.is_rendered();return t?.update_pending_count(1,n),n.increment(r,e),()=>{t?.update_pending_count(-1,n),n.decrement(r,e)}}function pt(e){var t=2|x;return z!==null&&(z.f|=ae),{ctx:E,deps:null,effects:null,equals:Me,f:t,fn:e,reactions:null,rv:0,v:r,wv:0,parent:z,ac:null}}var mt=Symbol(`obsolete`);function ht(e,t,n){let i=z;i===null&&Ie();var a=void 0,o=Ut(r),s=!R,c=new Set;return Dn(()=>{var t=z,n=y();a=n.promise;try{Promise.resolve(e()).then(n.resolve,e=>{e!==be&&n.reject(e)}).finally(dt)}catch(e){n.reject(e),dt()}var r=A;if(s){if(t.f&32768)var l=ft();if(i.b?.is_rendered())r.async_deriveds.get(t)?.reject(mt);else for(let e of c.values())e.reject(mt);c.add(n),r.async_deriveds.set(t,n)}let u=(e,t=void 0)=>{l?.(),c.delete(n),t!==mt&&(r.activate(),t?(o.f|=ue,qt(o,t)):(o.f&8388608&&(o.f^=ue),qt(o,e)),r.deactivate())};n.promise.then(u,e=>u(null,e||`unknown`))}),yn(()=>{for(let e of c)e.reject(mt)}),new Promise(e=>{function t(n){function r(){n===a?e(o):t(a)}n.then(r,r)}t(a)})}function gt(e){let t=pt(e);return Xn(t),t}function k(e){let t=pt(e);return t.equals=Pe,t}function _t(e){var t=e.effects;if(t!==null){e.effects=null;for(var n=0;n{t.ac.abort(be),t.ac=null}),t.fn!==null&&(t.teardown=g),dr(t,0),Mn(t))}function xt(e){if(e.effects!==null)for(let t of e.effects)t.teardown&&t.fn!==null&&fr(t)}var St=null,A=null,Ct=null,wt=null,Tt=null,Et=!1,Dt=!1,Ot=null,kt=null,At=0,jt=1,Mt=class e{id=jt++;#e=!1;linked=!0;#t=null;#n=null;async_deriveds=new Map;current=new Map;previous=new Map;#r=new Set;#i=new Set;#a=0;#o=new Map;#s=null;#c=[];#l=[];#u=new Set;#d=new Set;#f=new Map;#p=new Set;is_fork=!1;#m=!1;constructor(){St===null?St=this:(St.#n=this,this.#t=St),St=this}#h(){if(this.is_fork)return!0;for(let n of this.#o.keys()){for(var e=n,t=!1;e.parent!==null;){if(this.#f.has(e)){t=!0;break}e=e.parent}if(!t)return!0}return!1}skip_effect(e){this.#f.has(e)||this.#f.set(e,{d:[],m:[]}),this.#p.delete(e)}unskip_effect(e,t=e=>this.schedule(e)){var n=this.#f.get(e);if(n){this.#f.delete(e);for(var r of n.d)tt(r,x),t(r);for(r of n.m)tt(r,S),t(r)}this.#p.add(e)}#g(){var e=[];for(let i of this.#c)if(!(i.f&16384||!(i.f&6144))){for(var t=i,n=!1;t.parent!==null;){t=t.parent;var r=t.f;if(r&96){if(!(r&1024)){n=!0;break}t.f^=b}}n||e.push(t)}return this.#c=[],e}#_(){this.#e=!0;for(let e of this.#u)this.#d.delete(e),tt(e,x),this.schedule(e);for(let e of this.#d)tt(e,S),this.schedule(e);this.apply();for(var t=Ot=[],n=[],r=kt=[];this.#c.length>0;){At++>1e3&&(this.#S(),Pt());for(let e of this.#g())try{this.#v(e,t,n)}catch(t){throw zt(e),this.#h()||this.discard(),t}}if(A=null,r.length>0){var i=e.ensure();for(let e of r)i.schedule(e)}if(Ot=null,kt=null,this.#h()){this.#x(n),this.#x(t);for(let[e,t]of this.#f)Rt(e,t);r.length>0&&A.#_();return}let a=this.#y();if(a){this.#x(n),this.#x(t),a.#b(this);return}this.#u.clear(),this.#d.clear();for(let e of this.#r)e(this);this.#r.clear(),Ct=this,It(n),It(t),Ct=null,this.#s?.resolve();var o=A;if(this.#a===0&&(this.#c.length===0||o!==null)&&this.#S(),this.#c.length>0){if(o!==null){for(let e of this.#c)o.#c.push(e);this.#c=[]}else o=this}o!==null&&(Vt.clear(),o.#_())}#v(e,t,n){e.f^=b;for(var r=e.first;r!==null;){var i=r.f,a=!!(i&96);if(!(a&&i&1024||i&8192||this.#f.has(r))&&r.fn!==null){a?r.f^=b:i&4?t.push(r):or(r)&&(i&16&&this.#d.add(r),fr(r));var o=r.first;if(o!==null){r=o;continue}}for(;r!==null;){var s=r.next;if(s!==null){r=s;break}r=r.parent}}}#y(){for(var e=this.#t;e!==null;){if(!e.is_fork){for(let[t,[,n]]of this.current)if(e.current.has(t)&&!n)return e}e=e.#t}return null}#b(e){for(let[t,n]of e.current)!this.previous.has(t)&&e.previous.has(t)&&this.previous.set(t,e.previous.get(t)),this.current.set(t,n);for(let[t,n]of e.async_deriveds){let e=this.async_deriveds.get(t);e&&n.promise.then(e.resolve).catch(e.reject)}e.async_deriveds.clear(),this.transfer_effects(e.#u,e.#d);let t=e=>{var n=e.reactions;if(n!==null&&!(e.f&2&&!(e.f&6144)))for(let e of n){var r=e.f;if(r&2)t(e);else{var i=e;r&4194320&&!this.async_deriveds.has(i)&&(this.#d.delete(i),tt(i,x),this.schedule(i))}}};for(let e of this.current.keys())t(e);this.oncommit(()=>e.discard()),e.#S(),A=this,this.#_()}#x(e){for(var t=0;t{this.#m=!1,this.linked&&this.flush()}))}transfer_effects(e,t){for(let t of e)this.#u.add(t);for(let e of t)this.#d.add(e);e.clear(),t.clear()}oncommit(e){this.#r.add(e)}ondiscard(e){this.#i.add(e)}settled(){return(this.#s??=y()).promise}static ensure(){if(A===null){let t=A=new e;!Dt&&!Et&&Qe(()=>{t.#e||t.flush()})}return A}apply(){wt=null}schedule(e){if(Tt=e,e.b?.is_pending&&e.f&16777228&&!(e.f&32768)){e.b.defer_effect(e);return}this.#c.push(e)}#S(){if(this.linked){var e=this.#t,t=this.#n;e===null||(e.#n=t),t===null?St=e:t.#t=e,this.linked=!1}}};function Nt(e){var t=Et;Et=!0;try{var n;for(e&&(A!==null&&!A.is_fork&&A.flush(),n=e());;){if($e(),A===null)return n;A.flush()}}finally{Et=t}}function Pt(){try{Ve()}catch(e){mn(e,Tt)}}var Ft=null;function It(e){var t=e.length;if(t!==0){for(var n=0;n0)){Vt.clear();for(let e of Ft){if(e.f&24576)continue;let t=[e],n=e.parent;for(;n!==null;)Ft.has(n)&&(Ft.delete(n),t.push(n)),n=n.parent;for(let e=t.length-1;e>=0;e--){let n=t[e];n.f&24576||fr(n)}}Ft.clear()}}Ft=null}}function Lt(e){A.schedule(e)}function Rt(e,t){if(!(e.f&32&&e.f&1024)){e.f&2048?t.d.push(e):e.f&4096&&t.m.push(e),tt(e,b);for(var n=e.first;n!==null;)Rt(n,t),n=n.next}}function zt(e){tt(e,b);for(var t=e.first;t!==null;)zt(t),t=t.next}var Bt=new Set,Vt=new Map,Ht=!1;function Ut(e,t){return{f:0,v:e,reactions:null,equals:Me,rv:0,wv:0}}function Wt(e,t){let n=Ut(e,t);return Xn(n),n}function j(t,n=!1,r=!0){let i=Ut(t);return n||(i.equals=Pe),e&&r&&E!==null&&E.l!==null&&(E.l.s??=[]).push(i),i}function M(e,t,n=!1){return R!==null&&(!Kn||R.f&131072)&&Ye()&&R.f&4325394&&(Yn===null||!Yn.has(e))&&Ge(),qt(e,n?Zt(t):t,kt)}var Gt=null,Kt=0;function qt(e,t,n=null){if(!e.equals(t)){Wn?Vt.set(e,t):Vt.has(e)||Vt.set(e,e.v);var r=Mt.ensure();if(r.capture(e,t),e.f&2){let t=e;e.f&2048&&vt(t),wt===null&&nt(t)}e.wv=ar(),Gt=null,Kt=0,Xt(e,x,n),Gt=null,Ye()&&z!==null&&z.f&1024&&!(z.f&96)&&($n===null?er([e]):$n.push(e)),!r.is_fork&&Bt.size>0&&!Ht&&Jt()}return t}function Jt(){Ht=!1;for(let e of Bt){e.f&1024&&tt(e,S);let t;try{t=or(e)}catch{t=!0}t&&fr(e)}Bt.clear()}function Yt(e){M(e,e.v+1)}function Xt(e,t,n){var r=e.reactions;if(r!==null){var i=Ye(),a=r.length;if(Kt+=a,Kt>1e5&&Gt===null&&(Gt=new Set),Gt!==null){if(Gt.has(e))return;Gt.add(e)}for(var o=0;o{if(rr===c)return e();var t=R,n=rr;qn(null),ir(c);var r=e();return qn(t),ir(n),r};return i&&n.set(`length`,Wt(e.length,s)),new Proxy(e,{defineProperty(e,t,r){(!(`value`in r)||r.configurable===!1||r.enumerable===!1||r.writable===!1)&&Ue();var i=n.get(t);return i===void 0?l(()=>{var e=Wt(r.value,s);return n.set(t,e),e}):M(i,r.value,!0),!0},deleteProperty(e,t){var i=n.get(t);if(i===void 0){if(t in e){let e=l(()=>Wt(r,s));n.set(t,e),Yt(o)}}else M(i,r),Yt(o);return!0},get(t,i,a){if(i===de)return e;var o=n.get(i),c=i in t;if(o===void 0&&(!c||u(t,i)?.writable)&&(o=l(()=>Wt(Zt(c?t[i]:r),s)),n.set(i,o)),o!==void 0){var d=B(o);return d===r?void 0:d}return Reflect.get(t,i,a)},getOwnPropertyDescriptor(e,t){this.has?.(e,t);var i=Reflect.getOwnPropertyDescriptor(e,t),a=n.get(t);if(a!==void 0){var o=B(a);if(o===r)return;if(i&&`value`in i)i.value=o;else return{enumerable:!0,configurable:!0,value:o,writable:!0}}return i},has(e,t){if(t===de)return!0;var i=n.get(t),a=i!==void 0&&i.v!==r||Reflect.has(e,t);return(i!==void 0||z!==null&&(!a||u(e,t)?.writable))&&(i===void 0&&(i=l(()=>Wt(a?Zt(e[t]):r,s)),n.set(t,i)),B(i)===r)?!1:a},set(e,t,a,c){var d=n.get(t),f=t in e;if(i&&t===`length`)for(var p=a;pWt(r,s)),n.set(p+``,m)):M(m,r)}if(d===void 0)(!f||u(e,t)?.writable)&&(d=l(()=>Wt(void 0,s)),M(d,Zt(a)),n.set(t,d));else{f=d.v!==r;var h=l(()=>Zt(a));M(d,h)}var g=Reflect.getOwnPropertyDescriptor(e,t);if(g?.set&&g.set.call(c,a),!f){if(i&&typeof t==`string`){var _=n.get(`length`),v=Number(t);Number.isInteger(v)&&v>=_.v&&M(_,v+1)}Yt(o)}return!0},ownKeys(e){B(o);var t=Reflect.ownKeys(e).filter(e=>{var t=n.get(e);return t===void 0||t.v!==r});for(var[i,a]of n)a.v!==r&&!(i in e)&&t.push(i);return t},setPrototypeOf(){We()}})}function Qt(e){try{if(typeof e==`object`&&e&&de in e)return e[de]}catch{}return e}function $t(e,t){return Object.is(Qt(e),Qt(t))}var en,tn,nn,rn;function an(){if(en===void 0){en=window,tn=/Firefox/.test(navigator.userAgent);var e=Element.prototype,t=Node.prototype,n=Text.prototype;nn=u(t,`firstChild`).get,rn=u(t,`nextSibling`).get,h(e)&&(e[ge]=void 0,e[he]=null,e[_e]=void 0,e.__e=void 0),h(n)&&(n[ve]=void 0)}}function on(e=``){return document.createTextNode(e)}function sn(e){return nn.call(e)}function cn(e){return rn.call(e)}function N(e,t){if(!C)return sn(e);var n=sn(w);if(n===null)n=w.appendChild(on());else if(t&&n.nodeType!==3){var r=on();return n?.before(r),De(r),r}return t&&fn(n),De(n),n}function P(e,t=!1){if(!C){var n=sn(e);return n instanceof Comment&&n.data===``?cn(n):n}if(t){if(w?.nodeType!==3){var r=on();return w?.before(r),De(r),r}fn(w)}return w}function F(e,t=!1){if(!C)return sn(e);var n=N(e,t);return T(e),n}function I(e,t=1,n=!1){let r=C?w:e;for(var i;t--;)i=r,r=cn(r);if(!C)return r;if(n){if(r?.nodeType!==3){var a=on();return r===null?i?.after(a):r.before(a),De(a),a}fn(r)}return De(r),r}function ln(e){e.textContent=``}function un(){return!1}function dn(e,t,n){return t==null||t===`http://www.w3.org/1999/xhtml`?n?document.createElement(e,{is:n}):document.createElement(e):n?document.createElementNS(t,e,{is:n}):document.createElementNS(t,e)}function fn(e){if(e.nodeValue.length<65536)return;let t=e.nextSibling;for(;t!==null&&t.nodeType===3;)t.remove(),e.nodeValue+=t.nodeValue,t=e.nextSibling}function pn(e){var t=z;if(t===null)return R.f|=ue,e;if(!(t.f&32768)&&!(t.f&4))throw e;mn(e,t)}function mn(e,t){if(!(t!==null&&t.f&16384)){for(;t!==null;){if(t.f&128&&!(t.f&33570816)){if(!(t.f&32768))throw e;try{t.b.error(e);return}catch(t){e=t}}t=t.parent}throw e}}function hn(e){z===null&&(R===null&&Be(e),ze()),Wn&&Re(e)}function gn(e,t){var n=t.last;n===null?t.last=t.first=e:(n.next=e,e.prev=n,t.last=e)}function _n(e,t){var n=z;n!==null&&n.f&8192&&(e|=ee);var r={ctx:E,deps:null,nodes:null,f:e|x|512,first:null,fn:t,last:null,next:null,parent:n,b:n&&n.b,prev:null,teardown:null,wv:0,ac:null};A?.register_created_effect(r);var i=r;if(e&4)Ot===null?Mt.ensure().schedule(r):Ot.push(r);else if(t!==null){try{fr(r)}catch(e){throw Pn(r),e}i.deps===null&&i.teardown===null&&i.nodes===null&&i.first===i.last&&!(i.f&524288)&&(i=i.first,e&16&&e&65536&&i!==null&&(i.f|=ie))}if(i!==null&&(i.parent=n,n!==null&&gn(i,n),R!==null&&R.f&2&&!(e&64))){var a=R;(a.effects??=[]).push(i)}return r}function vn(){return R!==null&&!Kn}function yn(e){let t=_n(8,null);return tt(t,b),t.teardown=e,t}function bn(e){hn(`$effect`);var t=z.f;if(!R&&t&32&&E!==null&&!E.i){var n=E;(n.e??=[]).push(e)}else return xn(e)}function xn(e){return _n(4|oe,e)}function Sn(e){return hn(`$effect.pre`),_n(8|oe,e)}function Cn(e){Mt.ensure();let t=_n(64|ae,e);return(e={})=>new Promise(n=>{e.outro?Ln(t,()=>{Pn(t),n(void 0)}):(Pn(t),n(void 0))})}function wn(e){return _n(4,e)}function Tn(e,t){var n=E,r={effect:null,ran:!1,deps:e};n.l.$.push(r),r.effect=On(()=>{if(e(),!r.ran){r.ran=!0;var n=z;try{Jn(n.parent),V(t)}finally{Jn(n)}}})}function En(){var e=E;On(()=>{for(var t of e.l.$){t.deps();var n=t.effect;n.f&1024&&n.deps!==null&&tt(n,S),or(n)&&fr(n),t.ran=!1}})}function Dn(e){return _n(le|ae,e)}function On(e,t=0){return _n(8|t,e)}function L(e,t=[],n=[],r=[]){lt(r,t,n,t=>{_n(8,()=>{e(...t.map(B))})})}function kn(e,t=0){return _n(16|t,e)}function An(e){return _n(32|ae,e)}function jn(e){var t=e.teardown;if(t!==null){let n=Wn,r=R;Gn(!0),qn(null);try{t.call(null)}catch(t){mn(t,e.parent)}finally{Gn(n),qn(r)}}}function Mn(e,t=!1){var n=e.first;for(e.first=e.last=null;n!==null;){let e=n.ac;e!==null&&st(()=>{e.abort(be)});var r=n.next;n.f&64?n.parent=null:Pn(n,t),n=r}}function Nn(e){for(var t=e.first;t!==null;){var n=t.next;t.f&32||Pn(t),t=n}}function Pn(e,t=!0){var n=!1;(t||e.f&262144)&&e.nodes!==null&&e.nodes.end!==null&&(Fn(e.nodes.start,e.nodes.end),n=!0),e.f|=re,Mn(e,t&&!n),dr(e,0);var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)e.stop();jn(e),e.f^=re,e.f|=te;var i=e.parent;i!==null&&i.first!==null&&In(e),e.next=e.prev=e.teardown=e.ctx=e.deps=e.fn=e.nodes=e.ac=e.b=null}function Fn(e,t){for(;e!==null;){var n=e===t?null:cn(e);e.remove(),e=n}}function In(e){var t=e.parent,n=e.prev,r=e.next;n!==null&&(n.next=r),r!==null&&(r.prev=n),t!==null&&(t.first===e&&(t.first=r),t.last===e&&(t.last=n))}function Ln(e,t,n=!0){var r=[];e.f|=256,Rn(e,r,!0);var i=()=>{n&&Pn(e),t&&t()},a=r.length;if(a>0){var o=()=>--a||i();for(var s of r)s.out(o)}else i()}function Rn(e,t,n){if(!(e.f&8192)){e.f^=ee;var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)(e.is_global||n)&&t.push(e);for(var i=e.first;i!==null;){var a=i.next;if(!(i.f&64)){var o=!!(i.f&65536)||!!(i.f&32)&&!!(e.f&16);Rn(i,t,o?n:!1)}i=a}}}function zn(e){e.f&=-257,Bn(e,!0)}function Bn(e,t){if(!(e.f&256)&&e.f&8192){e.f^=ee,e.f&1024||(tt(e,x),Mt.ensure().schedule(e));for(var n=e.first;n!==null;){var r=n.next,i=!!(n.f&65536)||!!(n.f&32);Bn(n,i?t:!1),n=r}var a=e.nodes&&e.nodes.t;if(a!==null)for(let e of a)(e.is_global||t)&&e.in()}}function Vn(e,t){if(e.nodes)for(var n=e.nodes.start,r=e.nodes.end;n!==null;){var i=n===r?null:cn(n);t.append(n),n=i}}var Hn=null,Un=!1,Wn=!1;function Gn(e){Wn=e}var R=null,Kn=!1;function qn(e){R=e}var z=null;function Jn(e){z=e}var Yn=null;function Xn(e){R!==null&&(R.f&2097152||R.f&2)&&(Yn??=new Set).add(e)}var Zn=null,Qn=0,$n=null;function er(e){$n=e}var tr=1,nr=0,rr=nr;function ir(e){rr=e}function ar(){return++tr}function or(e){var t=e.f;if(t&2048)return!0;if(t&4096){for(var n=e.deps,r=n.length,i=0;ie.wv)return!0}t&512&&wt===null&&tt(e,b)}return!1}function sr(e,t,n=!0){var r=e.reactions;if(r!==null&&!(Yn!==null&&Yn.has(e)))for(var i=0;i{e.ac.abort(be)}),e.ac=null);try{e.f|=ce;var u=e.fn,d=u();e.f|=ne;var f=lr(e);if(Ye()&&$n!==null&&!Kn&&f!==null&&!(e.f&6146))for(var p=0;p<$n.length;p++)sr($n[p],e);if(i!==null&&i!==e){if(nr++,i.deps!==null)for(let e=0;e0)for(t.length=Qn+Zn.length,r=0;r{c.ac.abort(be),c.ac=null,tt(c,x)}),bt(c),dr(c,0)}}function dr(e,t){var n=e.deps;if(n!==null)for(var r=t;rn?.call(this,e))}return e.startsWith(`pointer`)||e.startsWith(`touch`)||e===`wheel`?(i.__removed=!1,Qe(()=>{i.__removed||t.addEventListener(e,i,r)})):t.addEventListener(e,i,r),i}function xr(e,t,n,r,i){var a={capture:r,passive:i},o=br(e,t,n,a);(t===document.body||t===window||t===document||t instanceof HTMLMediaElement)&&yn(()=>{o.__removed=!0,t.removeEventListener(e,o,a)})}function Sr(e,t,n){(t[_r]??={})[e]=n}function Cr(e){for(var t=0;t{Tr=!1,wr=null}));var o=0,s=wr===e&&e[_r];if(s){var c=i.indexOf(s);if(c!==-1&&(t===document||t===window)){e[_r]=t;return}var u=i.indexOf(t);if(u===-1)return;c<=u&&(o=c)}if(a=i[o]||e.target,a!==t){l(e,`currentTarget`,{configurable:!0,get(){return a||n}});var d=R,f=z;qn(null),Jn(null);try{for(var p,m=[];a!==null&&a!==t;){try{var h=a[_r]?.[r];h!=null&&(!a.disabled||e.target===a)&&h.call(a,e)}catch(e){p?m.push(e):p=e}if(e.cancelBubble)break;o++,a=o{throw e});throw p}}finally{e[_r]=t,delete e.currentTarget,qn(d),Jn(f)}}}var Dr=globalThis?.window?.trustedTypes&&globalThis.window.trustedTypes.createPolicy(`svelte-trusted-html`,{createHTML:e=>e});function Or(e){return Dr?.createHTML(e)??e}function kr(e){var t=dn(`template`);return t.innerHTML=Or(e.replaceAll(``,``)),t.content}function Ar(e,t){var n=z;n.nodes===null&&(n.nodes={start:e,end:t,a:null,t:null})}function U(e,t){var n=!!(t&1),r=!!(t&2),i,a=!e.startsWith(``);return()=>{if(C)return Ar(w,null),w;i===void 0&&(i=kr(a?e:``+e),n||(i=sn(i)));var t=r||tn?document.importNode(i,!0):i.cloneNode(!0);if(n){var o=sn(t),s=t.lastChild;Ar(o,s)}else Ar(t,t);return t}}function jr(e=``){if(!C){var t=on(e+``);return Ar(t,t),t}var n=w;return n.nodeType===3?fn(n):(n.before(n=on()),De(n)),Ar(n,n),n}function Mr(){if(C)return Ar(w,null),w;var e=document.createDocumentFragment(),t=document.createComment(``),n=on();return e.append(t,n),Ar(t,n),e}function W(e,t){if(C){var n=z;(!(n.f&32768)||n.nodes.end===null)&&(n.nodes.end=w),Oe();return}e!==null&&e.before(t)}[...`allowfullscreen.async.autofocus.autoplay.checked.controls.default.disabled.formnovalidate.indeterminate.inert.ismap.loop.multiple.muted.nomodule.novalidate.open.playsinline.readonly.required.reversed.seamless.selected.webkitdirectory.defer.disablepictureinpicture.disableremoteplayback`.split(`.`)];var Nr=[`touchstart`,`touchmove`];function Pr(e){return Nr.includes(e)}function Fr(e){let t=0,n=Ut(0),r;return()=>{vn()&&(B(n),On(()=>(t===0&&(r=V(()=>e(()=>Yt(n)))),t+=1,()=>{Qe(()=>{--t,t===0&&(r?.(),r=void 0,Yt(n))})})))}}var Ir=ie|ae;function Lr(e,t,n,r){new Rr(e,t,n,r)}var Rr=class{parent;is_pending=!1;transform_error;#e;#t=C?w:null;#n;#r;#i;#a=null;#o=null;#s=null;#c=null;#l=0;#u=0;#d=!1;#f=new Set;#p=new Set;#m=null;#h=Fr(()=>(this.#m=Ut(this.#l),()=>{this.#m=null}));constructor(e,t,n,r){this.#e=e,this.#n=t,this.#r=e=>{var t=z;t.b=this,t.f|=128,n(e)},this.parent=z.b,this.transform_error=r??this.parent?.transform_error??(e=>e),this.#i=kn(()=>{if(C){let e=this.#t;Oe();let t=e.data===`[!`;if(e.data.startsWith(`[?`)){let t=JSON.parse(e.data.slice(2));this.#_(t)}else t?this.#y():this.#g()}else this.#b()},Ir),C&&(this.#e=w)}#g(){try{this.#a=An(()=>this.#r(this.#e))}catch(e){this.error(e)}}#_(e){let t=this.#n.failed,{reset:n,invoke_onerror:r}=this.#v(e);Qe(r),t&&(this.#s=An(()=>{t(this.#e,()=>e,()=>n)}))}#v(e){var t=!1,n=!1;let r=()=>{if(t){Te();return}t=!0,n&&Ke(),this.#s!==null&&Ln(this.#s,()=>{this.#s=null}),this.#S(()=>{this.#b()})};return{reset:r,invoke_onerror:()=>{try{n=!0,this.#n.onerror?.(e,r),n=!1}catch(e){mn(e,this.#i&&this.#i.parent)}}}}#y(){let e=this.#n.pending;e&&(this.is_pending=!0,this.#o=An(()=>e(this.#e)),Qe(()=>{var e=this.#c=document.createDocumentFragment(),t=on(),n=!1;if(e.append(t),this.#a=this.#S(()=>{try{return An(()=>this.#r(t))}catch(e){try{this.error(e),n=!0}catch(e){mn(e,this.#i.parent)}return null}}),this.#a===null){this.#c=null,n&&this.#x(A);return}this.#u===0&&(this.#e.before(e),this.#c=null,Ln(this.#o,()=>{this.#o=null}),this.#x(A))}))}#b(){try{if(this.is_pending=this.has_pending_snippet(),this.#u=0,this.#l=0,this.#a=An(()=>{this.#r(this.#e)}),this.#u>0){var e=this.#c=document.createDocumentFragment();Vn(this.#a,e);let t=this.#n.pending;this.#o=An(()=>t(this.#e))}else this.#x(A)}catch(e){this.error(e)}}#x(e){this.is_pending=!1,e.transfer_effects(this.#f,this.#p)}defer_effect(e){rt(e,this.#f,this.#p)}is_rendered(){return!this.is_pending&&(!this.parent||this.parent.is_rendered())}has_pending_snippet(){return!!this.#n.pending}#S(e){var t=z,n=R,r=E;Jn(this.#i),qn(this.#i),qe(this.#i.ctx);try{return Mt.ensure(),e()}finally{Jn(t),qn(n),qe(r)}}#C(e,t){if(!this.has_pending_snippet()){this.parent&&this.parent.#C(e,t);return}this.#u+=e,this.#u===0&&(this.#x(t),this.#o&&Ln(this.#o,()=>{this.#o=null}),this.#c&&=(this.#e.before(this.#c),null))}update_pending_count(e,t){this.#C(e,t),this.#l+=e,!(!this.#m||this.#d)&&(this.#d=!0,Qe(()=>{this.#d=!1,this.#m&&qt(this.#m,this.#l)}))}get_effect_pending(){return this.#h(),B(this.#m)}error(e){if(!this.#n.onerror&&!this.#n.failed)throw e;A?.is_fork?(this.#a&&A.skip_effect(this.#a),this.#o&&A.skip_effect(this.#o),this.#s&&A.skip_effect(this.#s),A.oncommit(()=>{this.#w(e)})):this.#w(e)}#w(e){this.#a&&=(Pn(this.#a),null),this.#o&&=(Pn(this.#o),null),this.#s&&=(Pn(this.#s),null),C&&(De(this.#t),ke(),De(Ae()));let t=this.#n.failed,n=e=>{let{reset:n,invoke_onerror:r}=this.#v(e);r(),t&&(this.#s=this.#S(()=>{try{return An(()=>{var r=z;r.b=this,r.f|=128,t(this.#e,()=>e,()=>n)})}catch(e){return mn(e,this.#i.parent),null}}))};Qe(()=>{var t;try{t=this.transform_error(e)}catch(e){mn(e,this.#i&&this.#i.parent);return}typeof t==`object`&&t&&typeof t.then==`function`?t.then(n,e=>mn(e,this.#i&&this.#i.parent)):n(t)})}};function G(e,t){var n=t==null?``:typeof t==`object`?`${t}`:t;n!==(e[ve]??=e.nodeValue)&&(e[ve]=n,e.nodeValue=`${n}`)}function zr(e,t){return Vr(e,t)}var Br=new Map;function Vr(e,{target:t,anchor:r,props:i={},events:a,context:o,intro:s=!0,transformError:l}){an();var u=void 0,d=Cn(()=>{var s=r??t.appendChild(on());Lr(s,{pending:()=>{}},t=>{D({});var r=E;if(o&&(r.c=o),a&&(i.$$events=a),C&&Ar(t,null),u=e(t,i)||Je(),C&&(z.nodes.end=w,w===null||w.nodeType!==8||w.data!==`]`))throw Ce(),n;O()},l);var d=new Set,f=e=>{for(var n=0;n{for(var e of d)for(let r of[t,document]){var n=Br.get(r),i=n.get(e);--i==0?(r.removeEventListener(e,Er),n.delete(e),n.size===0&&Br.delete(r)):n.set(e,i)}yr.delete(f),s!==r&&s.parentNode?.removeChild(s)}});return Hr.set(u,d),u}var Hr=new WeakMap,Ur=class{anchor;#e=new Map;#t=new Map;#n=new Map;#r=new Set;#i=!0;constructor(e,t=!0){this.anchor=e,this.#i=t}#a=e=>{if(this.#e.has(e)){var t=this.#e.get(e),n=this.#t.get(t);if(n)zn(n),this.#r.delete(t);else{var r=this.#n.get(t);r&&(zn(r.effect),this.#t.set(t,r.effect),this.#n.delete(t),r.fragment.lastChild.remove(),this.anchor.before(r.fragment),n=r.effect)}for(let[t,n]of this.#e){if(this.#e.delete(t),t===e)break;let r=this.#n.get(n);r&&(Pn(r.effect),this.#n.delete(n))}for(let[e,r]of this.#t){if(e===t||this.#r.has(e))continue;let i=()=>{if(Array.from(this.#e.values()).includes(e)){var t=document.createDocumentFragment();Vn(r,t),t.append(on()),this.#n.set(e,{effect:r,fragment:t})}else Pn(r);this.#r.delete(e),this.#t.delete(e)};this.#i||!n?(this.#r.add(e),Ln(r,i,!1)):i()}}};#o=e=>{this.#e.delete(e);let t=Array.from(this.#e.values());for(let[e,n]of this.#n)t.includes(e)||(Pn(n.effect),this.#n.delete(e))};ensure(e,t){var n=A,r=un();if(t&&!this.#t.has(e)&&!this.#n.has(e)){if(r){var i=document.createDocumentFragment(),a=on();i.append(a),this.#n.set(e,{effect:An(()=>t(a)),fragment:i})}else this.#t.set(e,An(()=>t(this.anchor)))}if(this.#e.set(n,e),r){for(let[t,r]of this.#t)t===e?n.unskip_effect(r):n.skip_effect(r);for(let[t,r]of this.#n)t===e?n.unskip_effect(r.effect):n.skip_effect(r.effect);n.oncommit(this.#a),n.ondiscard(this.#o)}else C&&(this.anchor=w),this.#a(n)}};function Wr(t){E===null&&Fe(`onMount`),e&&E.l!==null?Gr(E).m.push(t):bn(()=>{let e=V(t);if(typeof e==`function`)return e})}function Gr(e){var t=e.l;return t.u??={a:[],b:[],m:[]}}function K(e,t,n=!1){var r;C&&(r=w,Oe());var i=new Ur(e),a=n?ie:0;function o(e,t){if(C){var n=je(r);if(e!==parseInt(n.substring(1))){var a=Ae();De(a),i.anchor=a,Ee(!1),i.ensure(e,t),Ee(!0);return}}i.ensure(e,t)}kn(()=>{var e=!1;t((t,n=0)=>{e=!0,o(n,t)}),e||o(-1,null)},a)}var Kr=Symbol(`NaN`);function qr(e,t,n){C&&Oe();var r=new Ur(e),i=!Ye();kn(()=>{var e=t();e!==e&&(e=Kr),i&&typeof e==`object`&&e&&(e={}),r.ensure(e,n)})}function Jr(e,t){return t}function Yr(e,t,n){for(var r=[],i=t.length,a,o=t.length,s=0;s{if(a){if(a.pending.delete(n),a.done.add(n),a.pending.size===0){var t=e.outrogroups;Xr(e,c(a.done)),t.delete(a),t.size===0&&(e.outrogroups=null)}}else--o},!1)}if(o===0){var l=r.length===0&&n!==null&&e.pending.size===0;if(l){var u=n,d=u.parentNode;ln(d),d.append(u),e.items.clear()}Xr(e,t,!l)}else a={pending:new Set(t),done:new Set},(e.outrogroups??=new Set).add(a)}function Xr(e,t,n=!0){var r;if(e.pending.size>0){r=new Set;for(let t of e.pending.values())for(let n of t)r.add(e.items.get(n).e)}for(var i=0;i{var e=n();return a(e)?e:e==null?[]:c(e)}),p,m=new Map,h=!0;function g(e){v.effect.f&16384||(v.pending.delete(e),v.fallback=d,ei(v,p,s,t,r),d!==null&&(p.length===0?d.f&33554432?(d.f^=se,ni(d,null,s)):zn(d):Ln(d,()=>{d=null})))}function _(e){v.pending.delete(e)}var v={effect:kn(()=>{p=B(f);var e=p.length;let a=!1;C&&je(s)===`[!`!=(e===0)&&(s=Ae(),De(s),Ee(!1),a=!0);for(var c=new Set,u=A,v=un(),y=0;yo(s)):(d=An(()=>o(Zr??=on())),d.f|=se)),e>c.size&&Le(``,``,``),C&&e>0&&De(Ae()),!h){if(m.set(u,c),v){for(let[e,t]of l)c.has(e)||u.skip_effect(t.e);u.oncommit(g),u.ondiscard(_)}else g(u)}a&&Ee(!0),B(f)}),flags:t,items:l,pending:m,outrogroups:null,fallback:d};h=!1,C&&(s=w)}function $r(e){for(;e!==null&&!(e.f&32);)e=e.next;return e}function ei(e,t,n,r,i){var a=!!(r&8),o=t.length,s=e.items,l=$r(e.effect.first),u,d=null,f,p=[],m=[],h,g,_,v;if(a)for(v=0;v0){var re=r&4&&o===0?n:null;if(a){for(v=0;v{if(f!==void 0)for(_ of f)_.nodes?.a?.apply()})}function ti(e,t,n,r,i,a,o,s){var c=o&1?o&16?Ut(n):j(n,!1,!1):null,l=o&2?Ut(i):null;return{v:c,i:l,e:An(()=>(a(t,c??n,l??i,s),()=>{e.delete(r)}))}}function ni(e,t,n){if(e.nodes)for(var r=e.nodes.start,i=e.nodes.end,a=t&&!(t.f&33554432)?t.nodes.start:n;r!==null;){var o=cn(r);if(a.before(r),r===i)return;r=o}}function ri(e,t,n){t===null?e.effect.first=n:t.next=n,n===null?e.effect.last=t:n.prev=t}function ii(e,t,n){wn(()=>{var r=V(()=>t(e,n?.())||{});if(n&&r?.update){var i=!1,a={};On(()=>{var e=n();H(e),i&&Ne(a,e)&&(a=e,r.update(e))}),i=!0}if(r?.destroy)return()=>r.destroy()})}function ai(e){var t,n,r=``;if(typeof e==`string`||typeof e==`number`)r+=e;else if(typeof e==`object`){if(Array.isArray(e)){var i=e.length;for(t=0;t=0;){var s=o+a;(o===0||ci.includes(r[o-1]))&&(s===r.length||ci.includes(r[s]))?r=(o===0?``:r.substring(0,o))+r.substring(s+1):o=s}}return r===``?null:r}function ui(e,t,n,r,i,a){var o=e[ge];if(C||o!==n||o===void 0){var s=li(n,r,a);(!C||s!==e.getAttribute(`class`))&&(s==null?e.removeAttribute(`class`):t?e.className=s:e.setAttribute(`class`,s)),e[ge]=n}else if(a&&i!==a)for(var c in a){var l=!!a[c];(i==null||l!==!!i[c])&&e.classList.toggle(c,l)}return a}function di(e,t){t?e.hasAttribute(`selected`)||e.setAttribute(`selected`,``):e.removeAttribute(`selected`)}function fi(e,t){var n=e.__defaultValue,r=e.multiple,i=r?n??[]:null;if(!r||a(i)){var o=e.selectedIndex,s=t&&r?new Set(e.selectedOptions):null;for(var c of e.options){var l=gi(c);di(c,r?i.includes(l):$t(l,n))}if(t){if(s!==null)for(c of e.options){var u=s.has(c);c.selected!==u&&(c.selected=u)}else e.selectedIndex!==o&&(e.selectedIndex=o)}}}function pi(e,t,n=!1){if(e.multiple){if(t==null)return;if(!a(t))return we();for(var r of e.options)r.selected=t.includes(gi(r));return}for(r of e.options)if($t(gi(r),t)){r.selected=!0;return}(!n||t!==void 0)&&(e.selectedIndex=-1)}function mi(e){var t=new MutationObserver(t=>{t.every(_i)||(`__defaultValue`in e&&fi(e,!1),`__value`in e&&pi(e,e.__value))});t.observe(e,{childList:!0,subtree:!0,attributes:!0,attributeFilter:[`value`]}),yn(()=>{t.disconnect()})}function hi(e,t,n=t){var r=new WeakSet,i=!0;ct(e,`change`,t=>{var i=t?`[selected]`:`:checked`,a;if(e.multiple)a=[].map.call(e.querySelectorAll(i),gi);else{var o=e.querySelector(i)??e.querySelector(`option:not([disabled])`);a=o&&gi(o)}n(a),e.__value=a,A!==null&&r.add(A)}),wn(()=>{var a=t();if(e===document.activeElement){var o=A;if(r.has(o))return}if(pi(e,a,i),i&&a===void 0){var s=e.querySelector(`:checked`);s!==null&&(a=gi(s),n(a))}e.__value=a,i=!1})}function gi(e){return`__value`in e?e.__value:e.value}function _i(e){if(e.target.closest(`selectedcontent`)!==null)return!0;if(e.type===`childList`){var t=[...e.addedNodes,...e.removedNodes];return t.length>0&&t.every(e=>e.nodeName===`SELECTEDCONTENT`)}return!1}var vi=Symbol(`is custom element`),yi=Symbol(`is html`),bi=xe?`link`:`LINK`;function xi(e){if(C){var t=!1,n=()=>{if(!t){if(t=!0,e.hasAttribute(`value`)){var n=e.value;q(e,`value`,null),e.value=n}if(e.hasAttribute(`checked`)){var r=e.checked;q(e,`checked`,null),e.checked=r}}};e[ye]=n,Qe(n),ot()}}function Si(e,t){var n=Ci(e);n.checked!==(n.checked=t??void 0)&&(e.checked=t)}function q(e,t,n,r){var i=Ci(e);C&&(i[t]=e.getAttribute(t),t===`src`||t===`srcset`||t===`href`&&e.nodeName===bi)||i[t]!==(i[t]=n)&&(t===`loading`&&(e[me]=n),n==null?e.removeAttribute(t):typeof n!=`string`&&Ti(e).has(t)?e[t]=n:e.setAttribute(t,n))}function Ci(e){return e[he]??={[vi]:e.nodeName.includes(`-`),[yi]:e.namespaceURI===i}}var wi=new Map;function Ti(e){var t=e.getAttribute(`is`)||e.nodeName,n=wi.get(t);if(n)return n;wi.set(t,n=new Set);for(var r,i=e,a=Element.prototype;a!==i;){for(var o in r=d(i),r)r[o].set&&o!==`innerHTML`&&o!==`textContent`&&o!==`innerText`&&n.add(o);i=m(i)}return n}function Ei(e,t,n=t){var r=new WeakSet;ct(e,`input`,async i=>{var a=i?e.defaultValue:e.value;if(a=Di(e)?Oi(a):a,n(a),A!==null&&r.add(A),await pr(),a!==(a=t())){var o=e.selectionStart,s=e.selectionEnd,c=e.value.length;if(e.value=a??``,s!==null){var l=e.value.length;o===s&&s===c&&l>c?(e.selectionStart=l,e.selectionEnd=l):(e.selectionStart=o,e.selectionEnd=Math.min(s,l))}}}),(C&&e.defaultValue!==e.value||V(t)==null&&e.value)&&(n(Di(e)?Oi(e.value):e.value),A!==null&&r.add(A)),On(()=>{var n=t();if(e===document.activeElement){var i=A;if(r.has(i))return}Di(e)&&n===Oi(e.value)||(e.type!==`date`||n||e.value)&&n!==e.value&&(e.value=n??``)})}function Di(e){var t=e.type;return t===`number`||t===`range`}function Oi(e){return e===``?null:+e}function J(e=!1){let t=E,n=t.l.u;if(!n)return;let r=()=>H(t.s);if(e){let e=0,n={},i=pt(()=>{let r=!1,i=t.s;for(let e in i)i[e]!==n[e]&&(n[e]=i[e],r=!0);return r&&e++,e});r=()=>B(i)}n.b.length&&Sn(()=>{ki(t,r),v(n.b)}),bn(()=>{let e=V(()=>n.m.map(_));return()=>{for(let t of e)typeof t==`function`&&t()}}),n.a.length&&bn(()=>{ki(t,r),v(n.a)})}function ki(e,t){if(e.l.s)for(let t of e.l.s)B(t);t()}function Ai(e,t,n){if(e==null)return t(void 0),n&&n(void 0),g;let r=V(()=>e.subscribe(t,n));return r.unsubscribe?()=>r.unsubscribe():r}var ji=[];function Mi(e,t=g){let n=null,r=new Set;function i(t){if(Ne(e,t)&&(e=t,n)){let t=!ji.length;for(let t of r)t[1](),ji.push(t,e);if(t){for(let e=0;e{r.delete(c),r.size===0&&n&&(n(),n=null)}}return{set:i,update:a,subscribe:o}}function Ni(e){let t;return Ai(e,e=>t=e)(),t}var Pi=!1,Fi=Symbol(`unmounted`);function Y(e,t,n){let r=n[t]??={store:null,source:j(void 0),unsubscribe:g};if(r.store!==e&&!(Fi in n)){if(r.unsubscribe(),r.store=e??null,e==null)r.source.v=void 0,r.unsubscribe=g;else{var i=!0;r.unsubscribe=Ai(e,e=>{i?r.source.v=e:M(r.source,e)}),i=!1}}return e&&Fi in n?Ni(e):B(r.source)}function X(){let e={};function t(){yn(()=>{for(var t in e)e[t].unsubscribe();l(e,Fi,{enumerable:!1,value:!0})})}return[e,t]}function Ii(e){var t=Pi;try{return Pi=!1,[e(),Pi]}finally{Pi=t}}function Z(t,n,r,i){var a=!e||!!(r&2),o=!!(r&8),s=!!(r&16),c=i,l=!0,d=void 0,f=()=>s&&a?(d??=pt(i),B(d)):(l&&(l=!1,c=s?V(i):i),c);let p;if(o){var m=de in t||pe in t;p=u(t,n)?.set??(m&&n in t?e=>t[n]=e:void 0)}var h,g=!1;o?[h,g]=Ii(()=>t[n]):h=t[n],h===void 0&&i!==void 0&&(h=f(),p&&(a&&He(n),p(h)));var _=a?()=>{var e=t[n];return e===void 0?f():(l=!0,e)}:()=>{var e=t[n];return e!==void 0&&(c=void 0),e===void 0?c:e};if(a&&!(r&4))return _;if(p){var v=t.$$legacy;return(function(e,t){return arguments.length>0?((!a||!t||v||g)&&p(t?_():e),e):_()})}var y=!1,b=(r&1?pt:k)(()=>(y=!1,_()));o&&B(b);var x=z;return(function(e,t){if(arguments.length>0){let n=t?B(b):a&&o?Zt(e):e;return M(b,n),y=!0,c!==void 0&&(c=n),e}return Wn&&y||x.f&16384?b.v:B(b)})}function Li(e){let t=Mi({paired:!!e,controller:!1,busy:!1,error:``}),n=e,r,i={paired:!!e,controller:!1,busy:!1,error:``},a=!1;function o(e){i={...i,...e},t.set(i)}async function s(e=!1){if(!a&&n){a=!0;try{let t=await fetch(`/api/state`,{cache:`no-store`,headers:{"X-Setup-Session-Key":n}});if(!t.ok)throw Error(`The local setup session is unavailable.`);o({view:await t.json(),...e?{}:{error:``}})}catch{o({view:void 0,error:`Connection lost. The CLI may have stopped. Check the terminal before trying again.`})}finally{a=!1}}}async function c(){if(n)try{let e=await fetch(`/api/bootstrap`,{cache:`no-store`,headers:{"X-Setup-Session-Key":n}});if(!e.ok)throw Error(`Could not join this local session.`);let t=await e.json();r=t.capability,o({controller:t.controller,error:``}),await s()}catch{n=void 0,r=void 0,o({view:void 0,paired:!1,controller:!1,error:`Could not connect to the local setup session. Check the terminal and pair again.`})}}async function l(e){if(!(i.busy||i.paired)){o({busy:!0,error:``});try{let t=await fetch(`/api/pair`,{method:`POST`,cache:`no-store`,headers:{"Content-Type":`application/json`},body:JSON.stringify({code:e.trim().toLowerCase()})}),r=await t.json();if(!t.ok)throw Error(String(r.error??`Pairing was rejected.`));if(typeof r.sessionKey!=`string`||!/^[a-f0-9]{64}$/.test(r.sessionKey))throw Error(`Invalid local pairing response.`);n=r.sessionKey,o({paired:!0,error:``}),await c()}catch(e){o({error:e instanceof Error?e.message:`Could not pair this browser.`})}finally{o({busy:!1})}}}async function u(e,t,i=!0){let a=await fetch(e,{method:`POST`,cache:`no-store`,headers:{"Content-Type":`application/json`,"X-Setup-Session-Key":n,...i&&r?{"X-Setup-Capability":r}:{}},body:JSON.stringify(t)}),o=await a.json();if(!a.ok)throw Error(String(o.error??`The request was rejected.`));return o}async function d(e,t){let n=e instanceof Error?e.message:t;o({error:n}),/read-only|Control moved/.test(n)?await c():await s(!0)}async function f(e,t){if(!i.busy&&i.controller&&i.view?.promptRevision===e){o({busy:!0,error:``});try{await u(`/api/answer`,{revision:e,value:t}),await s()}catch(e){await d(e,`Could not submit this answer.`)}finally{o({busy:!1})}}}async function p(e){if(!i.busy&&i.controller&&i.view?.promptRevision===e){o({busy:!0,error:``});try{await u(`/api/retry-discovery`,{revision:e}),await s()}catch(e){await d(e,`Could not retry discovery.`)}finally{o({busy:!1})}}}async function m(e){if(!i.busy&&i.controller&&i.view?.promptRevision===e){o({busy:!0,error:``});try{await u(`/api/back`,{revision:e}),await s()}catch(e){await d(e,`Could not return to the previous question.`)}finally{o({busy:!1})}}}async function h(){if(i.controller&&!i.busy){o({busy:!0,error:``});try{await u(`/api/cancel`,{}),await s()}catch(e){let t=e instanceof Error?e.message:`Cancellation failed.`;o({error:t}),/read-only|Control moved/.test(t)&&await c()}finally{o({busy:!1})}}}async function g(e){if(!(i.busy||!i.paired||i.controller)){o({busy:!0,error:``});try{let t=await u(`/api/takeover`,{code:e.trim().toLowerCase()},!1);r=String(t.capability),o({controller:!0,error:``}),await s()}catch(e){o({error:e instanceof Error?e.message:`Takeover failed.`}),await s(!0)}finally{o({busy:!1})}}}async function _(){try{await u(`/api/close`,{})}catch{}}async function v(){if(!i.busy&&i.controller&&i.view?.outcome===`complete`){o({busy:!0,error:``,view:{...i.view,doctor:{status:`running`}}});try{await u(`/api/doctor`,{})}catch(e){o({error:e instanceof Error?e.message:`Read-only verification failed.`})}finally{await s(!0),o({busy:!1})}}}return{subscribe:t.subscribe,pair:l,connect:c,refresh:s,submit:f,retryDiscovery:p,back:m,cancel:h,takeOver:g,close:_,runDoctor:v}}var Ri={language:`Language`,english:`English`,spanish:`Español`,setup:`SETUP`,connecting:`Connecting…`,localSession:`LOCAL SESSION`,progress:`Setup progress`,studio:`SETUP STUDIO`,journey:`YOUR SETUP JOURNEY`,repository:`Repository`,choices:`Setup choices`,setupPat:`Setup PAT`,plan:`Plan`,botPat:`Bot PAT & credentials`,apply:`Apply`,localDesign:`Local by design`,localDesignBody:`This page runs on your computer. GitHub creates both PATs in its own browser tabs.`,preparing:`Preparing your setup…`,completeTitle:`Setup complete.`,previewTitle:`Preview complete.`,cancelledTitle:`Setup cancelled.`,blockedTitle:`Setup needs attention.`,gettingReady:`GETTING READY`,activeLede:`One decision at a time. Your choices shape the permissions, plan and credentials needed for this repository.`,resultLede:`Review the result and next action below. The terminal has additional technical detail.`,readOnly:`Read-only tab`,readOnlyBody:`Another tab controls this session. You can review progress here or explicitly take over.`,takeOver:`Take control in this tab`,attention:`Needs attention`,checked:`Checked`,pleaseNote:`Please note`,progressUpdate:`Progress update`,reviewPass:`Reviewing saved choices — pass {pass}. This is the same setup run, not a restart.`,cancelSetup:`Cancel setup`,cancelConfirm:`Cancel this local setup session? PATs already created in GitHub will still exist.`,footerLocal:`LOCALHOST ONLY`,footerCloud:`NO CLOUD SETUP ACCOUNT`,footerGithub:`GITHUB OWNS PAT ISSUANCE`,currentDecision:`CURRENT DECISION`,session:`SESSION`,continue:`Continue`,previousQuestion:`Previous question`,questionProgress:`Question {current} of {total} in this group · {overall} of {all} overall`,yes:`Yes`,no:`No`,permissionPreview:`PERMISSION PREVIEW`,changeAnswersTitle:`Change your answers`,changeAnswersHelp:`Return to a section without losing other answers. The plan and required PAT grants will be checked again.`,changeSection:`Change {section}`,editCapabilities:`Capabilities`,editRuntimes:`Agent runtimes`,editModels:`Agent models`,editRoleModels:`Per-role models`,editRepository:`Repository behavior`,editDeployment:`Release and hotfix`,editBugbot:`Bugbot`,editApproval:`PR approval`,editProjects:`Projects`,editProvisioning:`Provisioning`,editStorage:`Secrets and Variables`,suggested:`Suggested answer: {answer}. You can review choices again before creating your setup PAT.`,none:`none`,sourceGithub:`Observed from authenticated GitHub repository metadata.`,sourceConfig:`Provided by your configuration; GitHub has not replaced it.`,sourceDefault:`Product default; not verified against this repository.`,sourceLocal:`Observed in this local checkout; confirm this branch exists on GitHub before applying.`,whyMatters:`Why this matters`,whenApplies:`When it applies`,whereConfigured:`Where it is configured`,howToChoose:`How to choose`,whyRecommendation:`Why this matters`,example:`Example`,effect:`What changes`,verify:`How to check`,learnMore:`Read documentation about this setting`,resultApplied:`Your configuration was applied`,resultNoChanges:`No changes were made`,resultStopped:`No setup changes started`,resultPartial:`Check partial changes before retrying`,resultCompleteBody:`The temporary setup PAT is not revoked automatically. Delete it in GitHub after confirming your setup. Keep the bot PAT until you rotate the installed Secret.`,resultPartialBody:`A bot Secret write or another change may have succeeded. Inspect GitHub and run copilot doctor --read-only before replacing or deleting its PAT.`,resultNoChangesBody:`Copilot did not begin applying setup changes. PATs you created in GitHub still exist until you delete them there.`,whatHappened:`What happened`,alreadyChanged:`Already changed`,noChanges:`No repository or GitHub setup changes were started in this session.`,nextAction:`Next action`,reasonPermissions:`The setup PAT lacked or could not confirm required permissions.`,nextPermissions:`Check the displayed grants, correct the PAT in GitHub, then start a fresh setup run.`,reasonStorage:`The selected GitHub Actions storage could not be used safely.`,nextStorage:`Inspect the Variables/Secrets scope and existing resources, then retry.`,reasonConfiguration:`The chosen configuration could not be validated.`,nextConfiguration:`Review the terminal validation details, correct the choices, and retry.`,reasonExpired:`The local setup session expired before applying changes.`,nextExpired:`Start a new setup session; the old approval cannot be replayed.`,reasonCancelled:`This setup was cancelled before applying changes.`,nextCancelled:`Start a new run if you still want to configure this repository.`,reasonUnknown:`Setup stopped before Apply. The browser could not determine the exact cause.`,nextUnknown:`Check the final terminal error before retrying; do not assume a PAT was revoked.`,reasonProvider:`GitHub or another provider did not complete the requested operation.`,nextProvider:`Use the diagnostic reference to inspect the terminal result, check provider availability and access, then retry only after reviewing possible partial changes.`,reasonRateLimit:`GitHub temporarily limited the requests needed for setup.`,nextRateLimit:`Wait for the limit to reset. Inspect any completed changes before starting another setup run.`,diagnosticReference:`Diagnostic reference`,resourceReceipt:`Setup operation receipt`,effectCompleted:`Reported completed`,effectSkipped:`Reported skipped`,effectInspect:`Outcome needs inspection`,effectNotStarted:`Not started`,scopeLocal:`Local checkout`,scopeMixed:`Repository and organization`,receiptFiles:`Setup files`,receiptSecrets:`GitHub Actions Secrets`,receiptLabels:`Issue labels`,receiptIssueTypes:`Issue types`,receiptVariables:`GitHub Actions Variables`,receiptInitialTag:`Initial version tag`,inspectPartial:`Some setup changes may already be active. Inspect GitHub and the terminal result before retrying.`,patSettings:`Open GitHub PAT settings ↗`,closeSession:`Close local session`,doctorHelp:`After success, you can verify installed resources here without dispatching Actions. Or run copilot doctor --read-only from the repository root with the temporary setup PAT and the same non-secret --config file, if used.`,doctorRun:`Verify installed setup (read-only)`,doctorRunning:`Checking installed resources without dispatching Actions…`,doctorPassed:`Read-only verification found no failing checks.`,doctorWarnings:`Read-only verification needs attention.`,doctorFailed:`Read-only verification did not finish. Check the terminal or run the command below.`,doctorCounts:`{pass} passed · {warn} warnings · {fail} failed · {skipped} skipped.`,doctorSecretLimit:`Secret values cannot be verified in this mode.`,botRenewal:`The bot PAT stays in the selected GitHub Actions Secret for future runs. Its actual expiry is not verified here; record it in GitHub and rotate the Secret before expiry.`,working:`Working on the next step`,workingBody:`The local process is checking your answers and preparing the next decision. Keep this page open.`,repoFocus:`Repository in focus`,repoFocusBody:`All decisions in this session target only:`,access:`access`,readOnlyCheck:`Read-only access check`,provisionalGrants:`Provisional least-privilege grants`,conditionalGrants:`Conditional grants depend on the choices and GitHub facts. A final audit runs before setup changes.`,permissionUnknown:`This permission needs review. Check its exact grant and technical explanation in the terminal before continuing.`,permissionsFollow:`Permissions follow your choices`,permissionsFollowBody:`We’ll show the exact grants before you create each PAT. Nothing is created just by opening this page.`,files:`Files`,workflows:`Workflows`,variables:`Variables`,secretNames:`Secret names`,planBody:`Review exactly what this run may change. The bot PAT and any additional credentials are collected next.`,planChoices:`Your key decisions`,planEnabledCapabilities:`Enabled capabilities`,planBranchRoles:`Production / development branch`,planApprovalMode:`Pull-request approval`,planVariableScope:`Variables scope`,planSecretScope:`Secrets scope`,planInitialTag:`Create initial tag`,planAgentRouting:`Agent and model for each task`,planIssueWorkflows:`Issue workflows`,planTrustedChecks:`Trusted CI producers`,planCoverage:`Coverage evidence`,planProjectStatuses:`Project Status transitions`,planIssueResources:`Issue labels and issue types`,planIssueResourcesValue:`Checked or provisioned during Apply`,planProducerAttested:`CI identity and enforcing step verified by you`,planCoverageThreshold:`Minimum changed-line coverage`,planCoverageReporter:`Artifact-publishing workflow`,planReporterAttested:`Coverage reporter verified by you`,planAdvancedDefaults:`The plan also includes defaults for settings you did not change. Use Change below to inspect any section before approving.`,planUnknownWarning:`An additional plan warning could not be displayed here. Read the terminal warning before approving.`,planBasicDefaultsIntro:`Basic setup retained these advanced defaults. Open a section below to review or change them:`,scopeRepository:`Repository`,scopeOrganization:`Organization`,scopeDisabled:`Not provisioned`,approvalOff:`Off`,approvalRecommend:`Recommendation only`,approvalGuarded:`Guarded approval`,beforeContinue:`Before you continue`,stopHere:`Stop here`,approvePlan:`Approve this plan`,githubLink:`Open GitHub link ↗`,githubForm:`Open the official GitHub PAT form`,githubFormHelp:`Check the signed-in account, select Only select repositories, then select this repository. GitHub handles 2FA and creates the PAT.`,pasteHere:`Paste the value here`,yourAnswer:`Your answer`,hiddenAfter:`Hidden after submission`,typeAnswer:`Type your answer`,secretHelp:`Sent only to this local process. It will not be shown again or saved in browser storage.`,pairTitle:`Pair this browser`,pairLabel:`Pairing code from terminal`,pairPlaceholder:`16 hexadecimal characters`,pairBody:`Find the 16-character pairing code in the terminal that started copilot setup --web. Enter it here to view or control this setup. The code is never placed in the browser URL or stored after this page closes.`,pairHelp:`Keep the code private. After refreshing this page, enter it again to reconnect.`,pairButton:`Connect to local setup`,privateSession:`PRIVATE LOCAL SESSION`,theme:`Color theme`,themeAuto:`Auto`,themeSystem:`Follow system theme`,themeLight:`Light theme`,themeDark:`Dark theme`,selectOne:`Select one`,ciRun:`Open CI run on GitHub ↗`,manualCheck:`Check not listed? Enter exact name|App ID|workflow, separated by semicolons.`,checksObserved:`Recent CI jobs were found. Open each run and verify the exact job, App and enforcing coverage step before trusting it.`,checksNoRecent:`No recent pull-request workflow runs were found. Run your normal CI on a real pull request, or enter an exact producer manually.`,checksNoVerifiable:`Recent runs exist, but no job could be linked to an exact Check Run and App identity. Inspect GitHub or enter an exact producer manually.`,checksDenied:`GitHub did not allow CI discovery. Give the setup PAT Actions: read and Checks: read, or enter an exact producer manually.`,checksUnavailable:`GitHub CI discovery could not complete. This is not evidence that the repository has no checks. Retry setup or enter an exact producer manually.`,projectsObserved:`These existing Projects belong to the repository owner. Select only Projects this automation should update.`,projectsEmpty:`This bounded GitHub query returned no accessible organization Projects; it does not prove none exist. Check access or enter a verified Project number.`,projectsDenied:`GitHub did not allow Project discovery. Check organization Projects: read on the setup PAT, or enter Project numbers manually.`,projectsUnavailable:`GitHub Project discovery could not complete. This is not evidence that no Projects exist. Enter a verified Project number manually or retry.`,projectsUnsupported:`GitHub does not support listing personal Projects with a fine-grained PAT through this endpoint. Enter an existing Project number from its URL.`,discoveryTruncated:`Only a bounded sample of accessible Projects or recent checks was inspected. Use manual entry for an item not shown.`,checksDiscoveryScope:`Search scope: up to 20 recent pull-request workflow runs; at most 15 runs and 100 checks per commit are inspected.`,projectsDiscoveryScope:`Search scope: at most 30 accessible organization Projects from two pages; up to 100 fields are inspected per Project.`,retryDiscovery:`Retry GitHub discovery`,retryRemaining:`{count} read-only retries left. Your answers stay here.`,retryExhausted:`No discovery retries remain. Inspect GitHub and use the manual option if the item is missing.`,observationTimeUnknown:`observation time unavailable`,branchRequirementUnknown:`Required by branch rule: not checked`,branchRequirementObserved:`Required on {branch} by an active ruleset for this exact check and App.`,ciRule:`Open required-check ruleset ↗`,projectSharedStatus:`All selected Projects must share each chosen Status value. This setup cannot assign a different Status vocabulary per Project.`,projectSelectionNotObserved:`Previously selected Project numbers are retained, but no longer appear in this GitHub result. Verify each in GitHub or remove it.`,removeSelection:`Remove selection`,projectTransitionIssueCreated:`New issue`,projectTransitionPullRequestCreated:`New pull request`,projectTransitionIssueInProgress:`Issue in progress`,projectTransitionPullRequestInProgress:`Pull request in progress`,projectUrl:`Open Project on GitHub ↗`,projectManual:`Project not listed? Enter its positive number or exact GitHub URL. PVT_ IDs are not accepted.`,projectStatusUnavailable:`Status options could not be verified for every selected Project. Enter the exact existing Status value after checking each Project in GitHub.`,projectStatusIncompatible:`Selected Projects have no shared Status values. Choose compatible Projects before continuing.`,producerName:`Check/job name`,producerAppId:`Source GitHub App ID`,producerWorkflow:`Workflow name`,producerAdd:`Add exact check`,producerRemove:`Remove check`,producerManualHelp:`Use the exact identity shown on GitHub. Adding it does not attest that it enforces coverage.`,producerManualInvalid:`Enter a check name, positive numeric App ID and workflow name. Do not use | or ; in names.`,translationPreviewTitle:`Translation review in progress`,translationPreviewBody:`First-party setup questions are translated. Some dynamic GitHub or provider diagnostics may still appear in English while localization review finishes. Changing language does not change your answers.`,unknownLocalError:`An unexpected local setup error occurred. Check the terminal for details, then refresh or restart setup.`},zi={language:`Idioma`,english:`English`,spanish:`Español`,setup:`CONFIGURACIÓN`,connecting:`Conectando…`,localSession:`SESIÓN LOCAL`,progress:`Progreso de la configuración`,studio:`ASISTENTE DE CONFIGURACIÓN`,journey:`TU RECORRIDO`,repository:`Repositorio`,choices:`Opciones de configuración`,setupPat:`PAT de configuración`,plan:`Plan`,botPat:`PAT del bot y credenciales`,apply:`Aplicar`,localDesign:`Local por diseño`,localDesignBody:`Esta página se ejecuta en tu ordenador. GitHub crea ambos PAT en sus propias pestañas.`,preparing:`Preparando la configuración…`,completeTitle:`Configuración completada.`,previewTitle:`Vista previa completada.`,cancelledTitle:`Configuración cancelada.`,blockedTitle:`La configuración necesita atención.`,gettingReady:`PREPARANDO`,activeLede:`Una decisión cada vez. Tus elecciones determinan los permisos, el plan y las credenciales que necesita este repositorio.`,resultLede:`Revisa abajo el resultado y el siguiente paso. La terminal contiene más detalles técnicos.`,readOnly:`Pestaña de solo lectura`,readOnlyBody:`Otra pestaña controla esta sesión. Puedes ver el progreso aquí o tomar el control explícitamente.`,takeOver:`Tomar el control en esta pestaña`,attention:`Necesita atención`,checked:`Comprobado`,pleaseNote:`Ten en cuenta`,progressUpdate:`Actualización de progreso`,reviewPass:`Revisando las opciones guardadas — pasada {pass}. Es la misma ejecución, no un reinicio.`,cancelSetup:`Cancelar configuración`,cancelConfirm:`¿Cancelar esta sesión local? Los PAT ya creados en GitHub seguirán existiendo.`,footerLocal:`SOLO LOCALHOST`,footerCloud:`SIN CUENTA DE CONFIGURACIÓN EN LA NUBE`,footerGithub:`GITHUB EMITE LOS PAT`,currentDecision:`DECISIÓN ACTUAL`,session:`SESIÓN`,continue:`Continuar`,previousQuestion:`Pregunta anterior`,questionProgress:`Pregunta {current} de {total} en este grupo · {overall} de {all} en total`,yes:`Sí`,no:`No`,permissionPreview:`VISTA PREVIA DE PERMISOS`,changeAnswersTitle:`Cambiar respuestas`,changeAnswersHelp:`Vuelve a una sección sin perder las demás respuestas. Se comprobarán de nuevo el plan y los permisos necesarios del PAT.`,changeSection:`Cambiar {section}`,editCapabilities:`Funciones`,editRuntimes:`Agentes`,editModels:`Modelos de agentes`,editRoleModels:`Modelos por tarea`,editRepository:`Comportamiento del repositorio`,editDeployment:`Releases y hotfixes`,editBugbot:`Bugbot`,editApproval:`Aprobación de PR`,editProjects:`Projects`,editProvisioning:`Aprovisionamiento`,editStorage:`Secrets y Variables`,suggested:`Respuesta sugerida: {answer}. Podrás revisar las opciones antes de crear el PAT de configuración.`,none:`ninguna`,sourceGithub:`Consultado en los metadatos autenticados de este repositorio en GitHub.`,sourceConfig:`Valor de tu configuración; GitHub no lo ha sustituido.`,sourceDefault:`Valor predeterminado del producto; no verificado en este repositorio.`,sourceLocal:`Observado en este checkout local; confirma que la rama existe en GitHub antes de aplicar cambios.`,whyMatters:`Por qué importa`,whenApplies:`Cuándo se aplica`,whereConfigured:`Dónde se configura`,howToChoose:`Cómo elegir`,whyRecommendation:`Por qué importa`,example:`Ejemplo`,effect:`Qué cambia`,verify:`Cómo comprobarlo`,learnMore:`Leer documentación de esta opción`,resultApplied:`Tu configuración se ha aplicado`,resultNoChanges:`No se hicieron cambios`,resultStopped:`No se empezó a aplicar la configuración`,resultPartial:`Revisa los cambios parciales antes de reintentar`,resultCompleteBody:`El PAT temporal de configuración no se revoca automáticamente. Elimínalo en GitHub tras comprobar el resultado. Conserva el PAT del bot hasta que rotes el Secret instalado.`,resultPartialBody:`Puede haberse escrito el Secret del bot u otro recurso. Revisa GitHub y ejecuta copilot doctor --read-only antes de reemplazar o eliminar ese PAT.`,resultNoChangesBody:`Copilot no empezó a aplicar cambios de configuración. Los PAT creados en GitHub siguen existiendo hasta que los elimines allí.`,whatHappened:`Qué ha pasado`,alreadyChanged:`Qué ha cambiado`,noChanges:`No se iniciaron cambios de configuración en el repositorio ni en GitHub durante esta sesión.`,nextAction:`Siguiente paso`,reasonPermissions:`Faltan permisos del PAT de configuración o no pudieron confirmarse.`,nextPermissions:`Comprueba los permisos mostrados, corrige el PAT en GitHub e inicia una nueva sesión.`,reasonStorage:`No se pudo usar de forma segura el almacenamiento elegido de GitHub Actions.`,nextStorage:`Revisa el ámbito de Variables/Secrets y los recursos existentes; después, reinténtalo.`,reasonConfiguration:`No se pudo validar la configuración elegida.`,nextConfiguration:`Revisa el detalle de validación en la terminal, corrige las opciones y reinténtalo.`,reasonExpired:`La sesión local caducó antes de aplicar cambios.`,nextExpired:`Inicia una sesión nueva; la aprobación anterior no puede reutilizarse.`,reasonCancelled:`La sesión se canceló antes de aplicar cambios.`,nextCancelled:`Inicia una nueva ejecución si todavía quieres configurar este repositorio.`,reasonUnknown:`La configuración se detuvo antes de aplicar cambios. La web no pudo determinar la causa exacta.`,nextUnknown:`Consulta el último error de la terminal antes de reintentar; no supongas que se revocó ningún PAT.`,reasonProvider:`GitHub u otro proveedor no completó la operación solicitada.`,nextProvider:`Usa la referencia de diagnóstico para consultar la terminal, comprueba la disponibilidad y el acceso al proveedor y revisa posibles cambios parciales antes de reintentar.`,reasonRateLimit:`GitHub ha limitado temporalmente las solicitudes necesarias para la configuración.`,nextRateLimit:`Espera a que se restablezca el límite. Inspecciona los cambios completados antes de iniciar otra configuración.`,diagnosticReference:`Referencia de diagnóstico`,resourceReceipt:`Registro de operaciones de configuración`,effectCompleted:`Marcada como completada`,effectSkipped:`Marcada como omitida`,effectInspect:`Resultado pendiente de inspección`,effectNotStarted:`No iniciada`,scopeLocal:`Checkout local`,scopeMixed:`Repositorio y organización`,receiptFiles:`Archivos de configuración`,receiptSecrets:`Secrets de GitHub Actions`,receiptLabels:`Etiquetas de issues`,receiptIssueTypes:`Tipos de issue`,receiptVariables:`Variables de GitHub Actions`,receiptInitialTag:`Etiqueta de versión inicial`,inspectPartial:`Algunos cambios podrían estar activos. Revisa GitHub y el resultado de la terminal antes de reintentar.`,patSettings:`Abrir ajustes de PAT en GitHub ↗`,closeSession:`Cerrar sesión local`,doctorHelp:`Tras completar el setup, puedes comprobar aquí los recursos instalados sin lanzar Actions. También puedes ejecutar copilot doctor --read-only desde la raíz del repositorio con el PAT temporal y el mismo archivo --config no secreto, si lo usaste.`,doctorRun:`Comprobar instalación (solo lectura)`,doctorRunning:`Comprobando los recursos instalados sin lanzar Actions…`,doctorPassed:`La comprobación de solo lectura no encontró fallos.`,doctorWarnings:`La comprobación de solo lectura requiere atención.`,doctorFailed:`La comprobación de solo lectura no terminó. Revisa la terminal o ejecuta el comando indicado abajo.`,doctorCounts:`{pass} correctas · {warn} avisos · {fail} fallos · {skipped} omitidas.`,doctorSecretLimit:`Este modo no puede verificar los valores de Secrets.`,botRenewal:`El PAT del bot permanece en el Secret de GitHub Actions seleccionado para futuras ejecuciones. Su fecha real de caducidad no se verifica aquí; anótala en GitHub y rota el Secret antes de que venza.`,working:`Preparando el siguiente paso`,workingBody:`El proceso local comprueba tus respuestas y prepara la siguiente decisión. Mantén esta página abierta.`,repoFocus:`Repositorio seleccionado`,repoFocusBody:`Todas las decisiones de esta sesión afectan solo a:`,access:`acceso`,readOnlyCheck:`Comprobación de acceso de solo lectura`,provisionalGrants:`Permisos provisionales de mínimo privilegio`,conditionalGrants:`Los permisos condicionales dependen de tus elecciones y de GitHub. Se hará una auditoría final antes de cualquier cambio.`,permissionUnknown:`Este permiso requiere revisión. Comprueba en la terminal el permiso exacto y su explicación técnica antes de continuar.`,permissionsFollow:`Los permisos dependen de tus elecciones`,permissionsFollowBody:`Mostraremos los permisos exactos antes de crear cada PAT. Abrir esta página no crea nada.`,files:`Archivos`,workflows:`Workflows`,variables:`Variables`,secretNames:`Nombres de Secrets`,planBody:`Revisa exactamente lo que podría cambiar. Después se pedirán el PAT del bot y las demás credenciales.`,planChoices:`Tus decisiones principales`,planEnabledCapabilities:`Funciones activadas`,planBranchRoles:`Rama de producción / desarrollo`,planApprovalMode:`Aprobación de pull requests`,planVariableScope:`Ámbito de Variables`,planSecretScope:`Ámbito de Secrets`,planInitialTag:`Crear etiqueta inicial`,planAgentRouting:`Agente y modelo por tarea`,planIssueWorkflows:`Flujos de issues`,planTrustedChecks:`Productores de CI fiables`,planCoverage:`Prueba de cobertura`,planProjectStatuses:`Transiciones de Status en Projects`,planIssueResources:`Etiquetas y tipos de issue`,planIssueResourcesValue:`Se comprobarán o crearán al aplicar`,planProducerAttested:`Identidad de CI y paso obligatorio comprobados por ti`,planCoverageThreshold:`Cobertura mínima de líneas modificadas`,planCoverageReporter:`Workflow que publica el artefacto`,planReporterAttested:`Generador de cobertura comprobado por ti`,planAdvancedDefaults:`El plan también incluye valores predeterminados de ajustes que no cambiaste. Usa Cambiar más abajo para revisar cualquier sección antes de aprobar.`,planUnknownWarning:`Hay un aviso adicional del plan que no se puede mostrar aquí. Léelo en la terminal antes de aprobar.`,planBasicDefaultsIntro:`El modo básico conservó estos ajustes avanzados. Abre una sección más abajo para revisarlos o cambiarlos:`,scopeRepository:`Repositorio`,scopeOrganization:`Organización`,scopeDisabled:`Sin aprovisionar`,approvalOff:`Desactivada`,approvalRecommend:`Solo recomendaciones`,approvalGuarded:`Aprobación protegida`,beforeContinue:`Antes de continuar`,stopHere:`Detener aquí`,approvePlan:`Aprobar este plan`,githubLink:`Abrir enlace de GitHub ↗`,githubForm:`Abrir el formulario oficial de PAT de GitHub`,githubFormHelp:`Comprueba la cuenta activa, elige Only select repositories y selecciona este repositorio. GitHub gestiona el 2FA y crea el PAT.`,pasteHere:`Pega aquí el valor`,yourAnswer:`Tu respuesta`,hiddenAfter:`Oculto tras enviarlo`,typeAnswer:`Escribe tu respuesta`,secretHelp:`Se envía solo a este proceso local. No volverá a mostrarse ni se guardará en el almacenamiento del navegador.`,pairTitle:`Vincula este navegador`,pairLabel:`Código de vinculación de la terminal`,pairPlaceholder:`16 caracteres hexadecimales`,pairBody:`Busca el código de 16 caracteres en la terminal donde ejecutaste copilot setup --web. Introdúcelo para ver o controlar la sesión. No aparece en la URL ni se almacena al cerrar la página.`,pairHelp:`Mantén el código en privado. Tras actualizar la página, introdúcelo de nuevo para reconectar.`,pairButton:`Conectar a la configuración local`,privateSession:`SESIÓN LOCAL PRIVADA`,theme:`Tema de color`,themeAuto:`Sistema`,themeSystem:`Seguir tema del sistema`,themeLight:`Tema claro`,themeDark:`Tema oscuro`,selectOne:`Selecciona una opción`,ciRun:`Abrir ejecución de CI en GitHub ↗`,manualCheck:`¿No aparece el check? Introduce nombre|ID de App|workflow exactos, separados por punto y coma.`,checksObserved:`Se encontraron jobs recientes de CI. Abre cada ejecución y comprueba el job, la App y el paso obligatorio de cobertura antes de confiar en él.`,checksNoRecent:`No hay ejecuciones recientes de workflows de pull request. Ejecuta tu CI habitual en un PR real o introduce manualmente un productor exacto.`,checksNoVerifiable:`Hay ejecuciones recientes, pero ningún job pudo vincularse a un Check Run y una App concretos. Revisa GitHub o introduce el productor manualmente.`,checksDenied:`GitHub no permitió consultar los checks. Concede Actions: read y Checks: read al PAT de configuración, o introduce el productor manualmente.`,checksUnavailable:`No se pudo completar la consulta de CI. Esto no significa que el repositorio no tenga checks. Reintenta o introduce el productor manualmente.`,projectsObserved:`Estos Projects existentes pertenecen al dueño del repositorio. Selecciona solo los que deba actualizar la automatización.`,projectsEmpty:`Esta consulta limitada no devolvió Projects accesibles; eso no demuestra que no existan. Comprueba el acceso o introduce un número verificado.`,projectsDenied:`GitHub no permitió consultar Projects. Comprueba Projects: read de la organización en el PAT o introduce los números manualmente.`,projectsUnavailable:`No se pudo consultar Projects. Eso no demuestra que no existan. Introduce un número verificado o reintenta.`,projectsUnsupported:`GitHub no permite listar Projects personales con un PAT de permisos precisos mediante esta API. Introduce el número de la URL de un Project existente.`,discoveryTruncated:`Solo se inspeccionó una muestra limitada de Projects accesibles o checks recientes. Usa la entrada manual si falta uno.`,checksDiscoveryScope:`Alcance: hasta 20 ejecuciones recientes de workflows de PR; se inspeccionan como máximo 15 ejecuciones y 100 checks por commit.`,projectsDiscoveryScope:`Alcance: hasta 30 Projects de la organización accesibles en dos páginas; se inspeccionan hasta 100 campos por Project.`,retryDiscovery:`Reintentar búsqueda en GitHub`,retryRemaining:`Quedan {count} reintentos de solo lectura. Tus respuestas se conservan.`,retryExhausted:`No quedan reintentos. Consulta GitHub e introduce manualmente lo que falte.`,observationTimeUnknown:`fecha de observación no disponible`,branchRequirementUnknown:`Obligatorio según la regla de rama: no comprobado`,branchRequirementObserved:`Obligatorio en {branch} por un ruleset activo para este check y esta App exactos.`,ciRule:`Abrir ruleset del check obligatorio ↗`,projectSharedStatus:`Todos los Projects elegidos deben compartir cada valor Status. Este setup no asigna valores diferentes por Project.`,projectSelectionNotObserved:`Se conservan los números de Project elegidos antes, aunque ya no figuren en esta consulta. Compruébalos en GitHub o quítalos.`,removeSelection:`Quitar selección`,projectTransitionIssueCreated:`Issue nuevo`,projectTransitionPullRequestCreated:`Pull request nuevo`,projectTransitionIssueInProgress:`Issue en curso`,projectTransitionPullRequestInProgress:`Pull request en curso`,projectUrl:`Abrir Project en GitHub ↗`,projectManual:`¿Falta un Project? Introduce su número positivo o URL exacta de GitHub. No se aceptan IDs PVT_.`,projectStatusUnavailable:`No se pudieron verificar las opciones Status de todos los Projects elegidos. Comprueba cada Project en GitHub e introduce el valor exacto.`,projectStatusIncompatible:`Los Projects elegidos no comparten valores Status. Elige Projects compatibles antes de continuar.`,producerName:`Nombre del check/job`,producerAppId:`ID de la App de GitHub`,producerWorkflow:`Nombre del workflow`,producerAdd:`Añadir check exacto`,producerRemove:`Quitar check`,producerManualHelp:`Usa la identidad exacta de GitHub. Añadirla no acredita que exija la cobertura.`,producerManualInvalid:`Indica nombre, ID numérico positivo de App y workflow. No uses | ni ; en los nombres.`,translationPreviewTitle:`Revisión de la traducción en curso`,translationPreviewBody:`Las preguntas propias de la configuración ya están traducidas. Algunos diagnósticos dinámicos de GitHub o del proveedor aún pueden aparecer en inglés mientras termina la revisión. Cambiar de idioma no modifica tus respuestas.`,unknownLocalError:`Se ha producido un error inesperado en la configuración local. Consulta la terminal y actualiza la página o reinicia la configuración.`},Bi={language:`Langue`,english:`Anglais`,spanish:`Espagnol`,setup:`CONFIGURATION`,connecting:`Connexion…`,localSession:`SESSION LOCALE`,progress:`Progression`,studio:`ASSISTANT DE CONFIGURATION`,journey:`VOTRE PARCOURS`,repository:`Dépôt`,choices:`Choix`,setupPat:`PAT de configuration`,plan:`Plan`,botPat:`PAT du bot et identifiants`,apply:`Appliquer`,localDesign:`Local par conception`,localDesignBody:`Cette page fonctionne sur votre ordinateur. GitHub crée les deux PAT dans ses propres onglets.`,preparing:`Préparation…`,completeTitle:`Configuration terminée.`,previewTitle:`Aperçu terminé.`,cancelledTitle:`Configuration annulée.`,blockedTitle:`Une intervention est nécessaire.`,gettingReady:`PRÉPARATION`,activeLede:`Une décision à la fois. Vos choix déterminent les droits, le plan et les identifiants nécessaires.`,resultLede:`Consultez le résultat et la prochaine étape ci-dessous. Le terminal fournit les détails techniques.`,readOnly:`Onglet en lecture seule`,readOnlyBody:`Un autre onglet contrôle cette session. Vous pouvez suivre la progression ou prendre le contrôle.`,takeOver:`Prendre le contrôle`,attention:`Action nécessaire`,checked:`Vérifié`,pleaseNote:`À noter`,progressUpdate:`Mise à jour`,reviewPass:`Révision des choix enregistrés — passage {pass}. C’est la même session, pas un redémarrage.`,cancelSetup:`Annuler`,cancelConfirm:`Annuler cette session locale ? Les PAT créés sur GitHub continueront d’exister.`,footerLocal:`LOCALHOST UNIQUEMENT`,footerCloud:`AUCUN COMPTE CLOUD`,footerGithub:`GITHUB ÉMET LES PAT`,currentDecision:`DÉCISION ACTUELLE`,session:`SESSION`,continue:`Continuer`,previousQuestion:`Question précédente`,questionProgress:`Question {current} sur {total} dans ce groupe · {overall} sur {all} au total`,yes:`Oui`,no:`Non`,permissionPreview:`APERÇU DES DROITS`,changeAnswersTitle:`Modifier vos réponses`,changeAnswersHelp:`Revenez à une section sans perdre les autres réponses. Le plan et les droits PAT requis seront vérifiés à nouveau.`,changeSection:`Modifier {section}`,editCapabilities:`Fonctionnalités`,editRuntimes:`Agents`,editModels:`Modèles des agents`,editRoleModels:`Modèles par tâche`,editRepository:`Comportement du dépôt`,editDeployment:`Releases et correctifs`,editBugbot:`Bugbot`,editApproval:`Approbation des PR`,editProjects:`Projects`,editProvisioning:`Provisionnement`,editStorage:`Secrets et Variables`,suggested:`Réponse suggérée : {answer}. Vous pourrez revoir vos choix avant de créer le PAT.`,none:`aucune`,sourceGithub:`Observé dans les métadonnées authentifiées de ce dépôt GitHub.`,sourceConfig:`Fourni par votre configuration ; GitHub ne l’a pas remplacé.`,sourceDefault:`Valeur par défaut du produit ; non vérifiée dans ce dépôt.`,sourceLocal:`Observé dans ce dossier local ; vérifiez que cette branche existe sur GitHub avant d’appliquer les changements.`,whyMatters:`Pourquoi c’est important`,whenApplies:`Quand cela s’applique`,whereConfigured:`Où se fait le réglage`,howToChoose:`Comment choisir`,whyRecommendation:`Pourquoi c’est important`,example:`Exemple`,effect:`Ce qui change`,verify:`Comment vérifier`,learnMore:`Lire la documentation de ce réglage`,resultApplied:`Configuration appliquée`,resultNoChanges:`Aucun changement effectué`,resultStopped:`Aucun changement commencé`,resultPartial:`Vérifiez les changements partiels`,resultCompleteBody:`Le PAT temporaire n’est pas révoqué automatiquement. Supprimez-le sur GitHub après vérification. Gardez le PAT du bot jusqu’à rotation du Secret.`,resultPartialBody:`Un Secret ou une autre ressource a peut-être été modifié. Vérifiez GitHub et exécutez copilot doctor --read-only avant de remplacer ou supprimer un PAT.`,resultNoChangesBody:`Copilot n’a pas commencé à appliquer la configuration. Les PAT créés sur GitHub restent actifs jusqu’à leur suppression là-bas.`,whatHappened:`Ce qui s’est passé`,alreadyChanged:`Déjà modifié`,noChanges:`Aucun changement du dépôt ou de GitHub n’a commencé pendant cette session.`,nextAction:`Prochaine étape`,reasonPermissions:`Les droits nécessaires du PAT de configuration manquent ou n’ont pas pu être confirmés.`,nextPermissions:`Vérifiez les droits affichés, corrigez le PAT sur GitHub, puis recommencez.`,reasonStorage:`Le stockage GitHub Actions choisi ne peut pas être utilisé en sécurité.`,nextStorage:`Vérifiez la portée des Variables/Secrets et les ressources existantes, puis réessayez.`,reasonConfiguration:`La configuration choisie n’a pas pu être validée.`,nextConfiguration:`Lisez les détails du terminal, corrigez les choix et réessayez.`,reasonExpired:`La session locale a expiré avant toute modification.`,nextExpired:`Démarrez une nouvelle session ; l’ancienne approbation ne peut pas être réutilisée.`,reasonCancelled:`La configuration a été annulée avant toute modification.`,nextCancelled:`Démarrez une nouvelle session si vous souhaitez encore configurer ce dépôt.`,reasonUnknown:`La configuration s’est arrêtée avant l’application ; la cause exacte est inconnue.`,nextUnknown:`Consultez la dernière erreur du terminal avant de réessayer. Ne supposez pas qu’un PAT a été révoqué.`,reasonProvider:`GitHub ou un autre fournisseur n’a pas terminé l’opération demandée.`,nextProvider:`Utilisez la référence de diagnostic pour consulter le terminal, vérifiez la disponibilité et les accès, puis examinez tout changement partiel avant de réessayer.`,reasonRateLimit:`GitHub a temporairement limité les requêtes nécessaires à la configuration.`,nextRateLimit:`Attendez la réinitialisation de la limite. Examinez les changements effectués avant de recommencer.`,diagnosticReference:`Référence de diagnostic`,resourceReceipt:`Reçu des opérations de configuration`,effectCompleted:`Signalée comme terminée`,effectSkipped:`Signalée comme ignorée`,effectInspect:`Résultat à vérifier`,effectNotStarted:`Non commencée`,scopeLocal:`Dossier local`,scopeMixed:`Dépôt et organisation`,receiptFiles:`Fichiers de configuration`,receiptSecrets:`Secrets GitHub Actions`,receiptLabels:`Étiquettes des tickets`,receiptIssueTypes:`Types de ticket`,receiptVariables:`Variables GitHub Actions`,receiptInitialTag:`Première étiquette de version`,inspectPartial:`Des changements peuvent déjà être actifs. Vérifiez GitHub et le terminal avant de réessayer.`,patSettings:`Ouvrir les paramètres PAT GitHub ↗`,closeSession:`Fermer la session locale`,doctorHelp:`Après la configuration, vérifiez ici les ressources installées sans déclencher d’Actions. Vous pouvez aussi exécuter copilot doctor --read-only à la racine du dépôt avec le PAT temporaire et le même fichier --config non secret, le cas échéant.`,doctorRun:`Vérifier l’installation (lecture seule)`,doctorRunning:`Vérification des ressources installées sans déclencher d’Actions…`,doctorPassed:`La vérification en lecture seule n’a trouvé aucun échec.`,doctorWarnings:`La vérification en lecture seule demande une attention particulière.`,doctorFailed:`La vérification en lecture seule ne s’est pas terminée. Consultez le terminal ou exécutez la commande ci-dessous.`,doctorCounts:`{pass} réussis · {warn} avertissements · {fail} échecs · {skipped} ignorés.`,doctorSecretLimit:`Ce mode ne peut pas vérifier les valeurs des Secrets.`,botRenewal:`Le PAT du bot reste dans le secret GitHub Actions choisi pour les futures exécutions. Sa date d’expiration réelle n’est pas vérifiée ici ; notez-la sur GitHub et remplacez le secret avant cette date.`,working:`Préparation de l’étape suivante`,workingBody:`Le processus local vérifie vos réponses. Gardez cette page ouverte.`,repoFocus:`Dépôt concerné`,repoFocusBody:`Toutes les décisions de cette session concernent uniquement :`,access:`accès`,readOnlyCheck:`Vérification en lecture seule`,provisionalGrants:`Droits provisoires minimaux`,conditionalGrants:`Les droits conditionnels dépendent de vos choix et de GitHub. Un contrôle final précède toute modification.`,permissionUnknown:`Ce droit exige une vérification. Consultez le droit exact et son explication technique dans le terminal avant de continuer.`,permissionsFollow:`Les droits suivent vos choix`,permissionsFollowBody:`Les droits exacts sont affichés avant chaque PAT. Ouvrir cette page ne crée rien.`,files:`Fichiers`,workflows:`Workflows`,variables:`Variables`,secretNames:`Noms des Secrets`,planBody:`Vérifiez précisément ce qui pourrait changer. Le PAT du bot et les autres identifiants seront demandés ensuite.`,planChoices:`Vos décisions principales`,planEnabledCapabilities:`Fonctionnalités activées`,planBranchRoles:`Branche de production / développement`,planApprovalMode:`Approbation des pull requests`,planVariableScope:`Portée des variables`,planSecretScope:`Portée des secrets`,planInitialTag:`Créer la première étiquette`,planAgentRouting:`Agent et modèle par tâche`,planIssueWorkflows:`Workflows de tickets`,planTrustedChecks:`Producteurs CI de confiance`,planCoverage:`Preuve de couverture`,planProjectStatuses:`Transitions Status des Projects`,planIssueResources:`Étiquettes et types de ticket`,planIssueResourcesValue:`Vérifiés ou créés lors de l’application`,planProducerAttested:`Identité CI et étape obligatoire vérifiées par vous`,planCoverageThreshold:`Couverture minimale des lignes modifiées`,planCoverageReporter:`Workflow publiant l’artefact`,planReporterAttested:`Producteur du rapport de couverture vérifié par vous`,planAdvancedDefaults:`Le plan comprend aussi les valeurs par défaut des réglages non modifiés. Utilisez Modifier ci-dessous pour examiner une section avant de valider.`,planUnknownWarning:`Un avertissement supplémentaire du plan ne peut pas être affiché ici. Lisez-le dans le terminal avant de valider.`,planBasicDefaultsIntro:`Le parcours de base a conservé ces réglages avancés. Ouvrez une section ci-dessous pour les vérifier ou les modifier :`,scopeRepository:`Dépôt`,scopeOrganization:`Organisation`,scopeDisabled:`Non provisionné`,approvalOff:`Désactivée`,approvalRecommend:`Recommandation uniquement`,approvalGuarded:`Approbation protégée`,beforeContinue:`Avant de continuer`,stopHere:`Arrêter ici`,approvePlan:`Approuver ce plan`,githubLink:`Ouvrir le lien GitHub ↗`,githubForm:`Ouvrir le formulaire PAT officiel de GitHub`,githubFormHelp:`Vérifiez le compte connecté, choisissez Only select repositories et ce dépôt. GitHub gère la 2FA et crée le PAT.`,pasteHere:`Collez la valeur ici`,yourAnswer:`Votre réponse`,hiddenAfter:`Masquée après envoi`,typeAnswer:`Saisissez votre réponse`,secretHelp:`Envoyé uniquement au processus local. Ni réaffiché ni enregistré dans le navigateur.`,pairTitle:`Associer ce navigateur`,pairLabel:`Code du terminal`,pairPlaceholder:`16 caractères hexadécimaux`,pairBody:`Trouvez le code à 16 caractères dans le terminal qui a lancé copilot setup --web. Il ne figure pas dans l’URL et n’est pas conservé après fermeture.`,pairHelp:`Gardez ce code secret. Après actualisation, saisissez-le de nouveau.`,pairButton:`Se connecter à la configuration locale`,privateSession:`SESSION LOCALE PRIVÉE`,theme:`Thème`,themeAuto:`Auto`,themeSystem:`Suivre le système`,themeLight:`Thème clair`,themeDark:`Thème sombre`,selectOne:`Choisissez une option`,ciRun:`Voir l’exécution CI sur GitHub ↗`,manualCheck:`Check absent ? Saisissez nom|ID App|workflow exacts, séparés par des points-virgules.`,checksObserved:`Des jobs CI récents ont été trouvés. Ouvrez chaque exécution et vérifiez le job, l’App et l’étape obligatoire de couverture avant de lui faire confiance.`,checksNoRecent:`Aucune exécution récente de workflow de pull request. Lancez le CI habituel sur une vraie PR ou saisissez un producteur exact.`,checksNoVerifiable:`Des exécutions récentes existent, mais aucun job ne correspond à une identité exacte de Check Run et d’App. Vérifiez GitHub ou saisissez-la.`,checksDenied:`GitHub a refusé la découverte CI. Accordez Actions: read et Checks: read au PAT de configuration, ou saisissez un producteur exact.`,checksUnavailable:`La découverte CI a échoué. Cela ne prouve pas l’absence de checks. Réessayez ou saisissez un producteur exact.`,projectsObserved:`Ces Projects existants appartiennent au propriétaire du dépôt. Ne choisissez que ceux que l’automatisation doit modifier.`,projectsEmpty:`Cette requête GitHub limitée n’a renvoyé aucun Project accessible ; elle ne prouve pas leur absence. Vérifiez l’accès ou saisissez un numéro vérifié.`,projectsDenied:`GitHub a refusé la découverte des Projects. Vérifiez Projects: read au niveau organisation sur le PAT, ou saisissez les numéros.`,projectsUnavailable:`La découverte des Projects a échoué. Cela ne prouve pas leur absence. Saisissez un numéro vérifié ou réessayez.`,projectsUnsupported:`Cette API GitHub ne liste pas les Projects personnels avec un PAT à permissions fines. Saisissez le numéro figurant dans l’URL.`,discoveryTruncated:`Seul un échantillon limité de Projects accessibles ou checks récents a été inspecté. Saisissez manuellement un élément absent.`,checksDiscoveryScope:`Périmètre : jusqu’à 20 exécutions récentes de workflows de PR ; au plus 15 exécutions et 100 checks par commit sont inspectés.`,projectsDiscoveryScope:`Périmètre : au plus 30 Projects accessibles de l’organisation sur deux pages ; jusqu’à 100 champs sont inspectés par Project.`,retryDiscovery:`Relancer la recherche GitHub`,retryRemaining:`Il reste {count} essais en lecture seule. Vos réponses sont conservées.`,retryExhausted:`Plus aucun essai disponible. Vérifiez GitHub et saisissez manuellement tout élément manquant.`,observationTimeUnknown:`date d’observation indisponible`,branchRequirementUnknown:`Exigé par la règle de branche : non vérifié`,branchRequirementObserved:`Exigé sur {branch} par un ruleset actif pour ce contrôle et cette application précis.`,ciRule:`Ouvrir le ruleset du contrôle obligatoire ↗`,projectSharedStatus:`Tous les Projects choisis doivent partager chaque valeur Status. Cette configuration ne définit pas de valeurs différentes par Project.`,projectSelectionNotObserved:`Les numéros de Projects choisis auparavant sont conservés, mais n’apparaissent plus dans ce résultat GitHub. Vérifiez-les sur GitHub ou retirez-les.`,removeSelection:`Retirer la sélection`,projectTransitionIssueCreated:`Nouveau ticket`,projectTransitionPullRequestCreated:`Nouvelle pull request`,projectTransitionIssueInProgress:`Ticket en cours`,projectTransitionPullRequestInProgress:`Pull request en cours`,projectUrl:`Ouvrir le Project sur GitHub ↗`,projectManual:`Project absent ? Saisissez son numéro positif ou son URL GitHub exacte. Les ID PVT_ sont refusés.`,projectStatusUnavailable:`Les options Status n’ont pas pu être vérifiées pour tous les Projects. Vérifiez-les sur GitHub et saisissez la valeur exacte.`,projectStatusIncompatible:`Les Projects choisis ne partagent aucun Status. Choisissez des Projects compatibles avant de continuer.`,producerName:`Nom du check/job`,producerAppId:`ID de l’App GitHub source`,producerWorkflow:`Nom du workflow`,producerAdd:`Ajouter le check exact`,producerRemove:`Retirer le check`,producerManualHelp:`Utilisez l’identité exacte affichée sur GitHub. L’ajouter ne prouve pas que la couverture est imposée.`,producerManualInvalid:`Indiquez le nom, un ID numérique positif d’App et le workflow. N’utilisez ni | ni ; dans les noms.`,translationPreviewTitle:`Révision de la traduction en cours`,translationPreviewBody:`Les questions propres à la configuration sont traduites. Certains diagnostics dynamiques de GitHub ou du fournisseur peuvent encore apparaître en anglais pendant la révision. Changer de langue ne modifie pas vos réponses.`,unknownLocalError:`Une erreur inattendue est survenue pendant la configuration locale. Consultez le terminal, puis actualisez la page ou relancez la configuration.`},Vi={language:`Idioma`,english:`Inglês`,spanish:`Espanhol`,setup:`CONFIGURAÇÃO`,connecting:`A ligar…`,localSession:`SESSÃO LOCAL`,progress:`Progresso`,studio:`ASSISTENTE DE CONFIGURAÇÃO`,journey:`O SEU PERCURSO`,repository:`Repositório`,choices:`Opções`,setupPat:`PAT de configuração`,plan:`Plano`,botPat:`PAT do bot e credenciais`,apply:`Aplicar`,localDesign:`Local por conceção`,localDesignBody:`Esta página funciona no seu computador. O GitHub cria ambos os PAT nos seus próprios separadores.`,preparing:`A preparar a configuração…`,completeTitle:`Configuração concluída.`,previewTitle:`Pré-visualização concluída.`,cancelledTitle:`Configuração cancelada.`,blockedTitle:`A configuração requer atenção.`,gettingReady:`A PREPARAR`,activeLede:`Uma decisão de cada vez. As suas escolhas determinam permissões, plano e credenciais.`,resultLede:`Consulte abaixo o resultado e o próximo passo. O terminal contém mais detalhes técnicos.`,readOnly:`Separador só de leitura`,readOnlyBody:`Outro separador controla esta sessão. Pode acompanhar o progresso ou assumir o controlo.`,takeOver:`Assumir o controlo`,attention:`Requer atenção`,checked:`Verificado`,pleaseNote:`Atenção`,progressUpdate:`Atualização de progresso`,reviewPass:`A rever as escolhas guardadas — passagem {pass}. É a mesma execução, não um reinício.`,cancelSetup:`Cancelar configuração`,cancelConfirm:`Cancelar esta sessão local? Os PAT já criados no GitHub continuarão a existir.`,footerLocal:`APENAS LOCALHOST`,footerCloud:`SEM CONTA DE CONFIGURAÇÃO NA NUVEM`,footerGithub:`O GITHUB EMITE OS PAT`,currentDecision:`DECISÃO ATUAL`,session:`SESSÃO`,continue:`Continuar`,previousQuestion:`Pergunta anterior`,questionProgress:`Pergunta {current} de {total} neste grupo · {overall} de {all} no total`,yes:`Sim`,no:`Não`,permissionPreview:`PRÉ-VISUALIZAÇÃO DAS PERMISSÕES`,changeAnswersTitle:`Alterar respostas`,changeAnswersHelp:`Volte a uma secção sem perder as outras respostas. O plano e as permissões PAT necessárias serão verificados de novo.`,changeSection:`Alterar {section}`,editCapabilities:`Funcionalidades`,editRuntimes:`Agentes`,editModels:`Modelos dos agentes`,editRoleModels:`Modelos por tarefa`,editRepository:`Comportamento do repositório`,editDeployment:`Releases e correções`,editBugbot:`Bugbot`,editApproval:`Aprovação de PR`,editProjects:`Projects`,editProvisioning:`Provisionamento`,editStorage:`Secrets e Variables`,suggested:`Resposta sugerida: {answer}. Pode rever as opções antes de criar o PAT.`,none:`nenhuma`,sourceGithub:`Observado nos metadados autenticados deste repositório no GitHub.`,sourceConfig:`Fornecido pela sua configuração; o GitHub não o substituiu.`,sourceDefault:`Valor predefinido do produto; não verificado neste repositório.`,sourceLocal:`Observado neste checkout local; confirme que o ramo existe no GitHub antes de aplicar alterações.`,whyMatters:`Porque importa`,whenApplies:`Quando se aplica`,whereConfigured:`Onde se configura`,howToChoose:`Como escolher`,whyRecommendation:`Porque importa`,example:`Exemplo`,effect:`O que muda`,verify:`Como verificar`,learnMore:`Ler a documentação desta opção`,resultApplied:`A configuração foi aplicada`,resultNoChanges:`Nenhuma alteração efetuada`,resultStopped:`Nenhuma alteração iniciada`,resultPartial:`Verifique as alterações parciais`,resultCompleteBody:`O PAT temporário não é revogado automaticamente. Elimine-o no GitHub após verificar. Guarde o PAT do bot até rodar o Secret.`,resultPartialBody:`Um Secret ou outro recurso pode ter sido alterado. Verifique o GitHub e execute copilot doctor --read-only antes de substituir ou eliminar o PAT.`,resultNoChangesBody:`O Copilot não começou a aplicar alterações. Os PAT criados no GitHub permanecem até serem eliminados lá.`,whatHappened:`O que aconteceu`,alreadyChanged:`O que mudou`,noChanges:`Não foram iniciadas alterações no repositório nem no GitHub nesta sessão.`,nextAction:`Próximo passo`,reasonPermissions:`Faltam permissões do PAT de configuração ou não foi possível confirmá-las.`,nextPermissions:`Verifique as permissões, corrija o PAT no GitHub e inicie uma nova sessão.`,reasonStorage:`O armazenamento GitHub Actions escolhido não pôde ser usado com segurança.`,nextStorage:`Verifique o âmbito das Variables/Secrets e os recursos existentes; depois tente novamente.`,reasonConfiguration:`Não foi possível validar a configuração escolhida.`,nextConfiguration:`Leia os detalhes no terminal, corrija as opções e tente novamente.`,reasonExpired:`A sessão local expirou antes de aplicar alterações.`,nextExpired:`Inicie uma nova sessão; a aprovação anterior não pode ser reutilizada.`,reasonCancelled:`A configuração foi cancelada antes de aplicar alterações.`,nextCancelled:`Inicie outra execução se ainda quiser configurar o repositório.`,reasonUnknown:`A configuração parou antes de aplicar; a causa exata é desconhecida.`,nextUnknown:`Leia o último erro no terminal antes de tentar novamente. Não presuma que um PAT foi revogado.`,reasonProvider:`O GitHub ou outro fornecedor não concluiu a operação solicitada.`,nextProvider:`Use a referência de diagnóstico para consultar o terminal, verifique a disponibilidade e o acesso e reveja possíveis alterações parciais antes de tentar novamente.`,reasonRateLimit:`O GitHub limitou temporariamente os pedidos necessários para a configuração.`,nextRateLimit:`Aguarde a reposição do limite. Inspecione as alterações concluídas antes de iniciar outra configuração.`,diagnosticReference:`Referência de diagnóstico`,resourceReceipt:`Registo das operações de configuração`,effectCompleted:`Indicada como concluída`,effectSkipped:`Indicada como ignorada`,effectInspect:`Resultado por inspecionar`,effectNotStarted:`Não iniciada`,scopeLocal:`Checkout local`,scopeMixed:`Repositório e organização`,receiptFiles:`Ficheiros de configuração`,receiptSecrets:`Secrets do GitHub Actions`,receiptLabels:`Etiquetas das questões`,receiptIssueTypes:`Tipos de questão`,receiptVariables:`Variables do GitHub Actions`,receiptInitialTag:`Etiqueta de versão inicial`,inspectPartial:`Algumas alterações podem estar ativas. Verifique o GitHub e o terminal antes de tentar novamente.`,patSettings:`Abrir definições de PAT no GitHub ↗`,closeSession:`Fechar sessão local`,doctorHelp:`Depois de concluir a configuração, pode verificar aqui os recursos instalados sem iniciar Actions. Também pode executar copilot doctor --read-only na raiz do repositório com o PAT temporário e o mesmo ficheiro --config não secreto, caso o tenha usado.`,doctorRun:`Verificar instalação (só de leitura)`,doctorRunning:`A verificar os recursos instalados sem iniciar Actions…`,doctorPassed:`A verificação só de leitura não encontrou falhas.`,doctorWarnings:`A verificação só de leitura requer atenção.`,doctorFailed:`A verificação só de leitura não terminou. Consulte o terminal ou execute o comando abaixo.`,doctorCounts:`{pass} aprovados · {warn} avisos · {fail} falhas · {skipped} ignorados.`,doctorSecretLimit:`Este modo não consegue verificar os valores dos Secrets.`,botRenewal:`O PAT do bot permanece no Secret do GitHub Actions escolhido para execuções futuras. A data de validade real não é verificada aqui; registe-a no GitHub e substitua o Secret antes de expirar.`,working:`A preparar o próximo passo`,workingBody:`O processo local está a verificar as respostas. Mantenha esta página aberta.`,repoFocus:`Repositório em foco`,repoFocusBody:`Todas as decisões desta sessão afetam apenas:`,access:`acesso`,readOnlyCheck:`Verificação de acesso só de leitura`,provisionalGrants:`Permissões provisórias mínimas`,conditionalGrants:`As permissões condicionais dependem das escolhas e do GitHub. Haverá uma auditoria final antes das alterações.`,permissionUnknown:`Esta permissão exige revisão. Consulte a permissão exata e a explicação técnica no terminal antes de continuar.`,permissionsFollow:`As permissões seguem as suas escolhas`,permissionsFollowBody:`Mostramos as permissões exatas antes de criar cada PAT. Abrir esta página não cria nada.`,files:`Ficheiros`,workflows:`Workflows`,variables:`Variables`,secretNames:`Nomes dos Secrets`,planBody:`Reveja exatamente o que pode mudar. O PAT do bot e outras credenciais serão pedidos a seguir.`,planChoices:`As suas decisões principais`,planEnabledCapabilities:`Funcionalidades ativadas`,planBranchRoles:`Ramo de produção / desenvolvimento`,planApprovalMode:`Aprovação de pull requests`,planVariableScope:`Âmbito das Variables`,planSecretScope:`Âmbito dos Secrets`,planInitialTag:`Criar etiqueta inicial`,planAgentRouting:`Agente e modelo por tarefa`,planIssueWorkflows:`Fluxos de questões`,planTrustedChecks:`Produtores CI de confiança`,planCoverage:`Evidência de cobertura`,planProjectStatuses:`Transições Status dos Projects`,planIssueResources:`Etiquetas e tipos de questão`,planIssueResourcesValue:`Verificados ou criados ao aplicar`,planProducerAttested:`Identidade CI e passo obrigatório verificados por si`,planCoverageThreshold:`Cobertura mínima das linhas alteradas`,planCoverageReporter:`Workflow que publica o artefacto`,planReporterAttested:`Produtor do relatório de cobertura verificado por si`,planAdvancedDefaults:`O plano também inclui valores predefinidos para definições que não alterou. Use Alterar abaixo para rever qualquer secção antes de aprovar.`,planUnknownWarning:`Um aviso adicional do plano não pode ser apresentado aqui. Leia-o no terminal antes de aprovar.`,planBasicDefaultsIntro:`O percurso básico manteve estas definições avançadas. Abra uma secção abaixo para as rever ou alterar:`,scopeRepository:`Repositório`,scopeOrganization:`Organização`,scopeDisabled:`Sem aprovisionamento`,approvalOff:`Desativada`,approvalRecommend:`Apenas recomendações`,approvalGuarded:`Aprovação protegida`,beforeContinue:`Antes de continuar`,stopHere:`Parar aqui`,approvePlan:`Aprovar este plano`,githubLink:`Abrir ligação do GitHub ↗`,githubForm:`Abrir formulário oficial de PAT no GitHub`,githubFormHelp:`Confirme a conta ligada, escolha Only select repositories e este repositório. O GitHub gere 2FA e cria o PAT.`,pasteHere:`Cole o valor aqui`,yourAnswer:`A sua resposta`,hiddenAfter:`Oculto após envio`,typeAnswer:`Escreva a sua resposta`,secretHelp:`Enviado apenas ao processo local. Não será mostrado de novo nem guardado no navegador.`,pairTitle:`Emparelhar este navegador`,pairLabel:`Código do terminal`,pairPlaceholder:`16 caracteres hexadecimais`,pairBody:`Encontre o código de 16 caracteres no terminal onde iniciou copilot setup --web. Não aparece no URL nem fica guardado após fechar a página.`,pairHelp:`Mantenha o código privado. Após atualizar a página, introduza-o novamente.`,pairButton:`Ligar à configuração local`,privateSession:`SESSÃO LOCAL PRIVADA`,theme:`Tema`,themeAuto:`Automático`,themeSystem:`Seguir sistema`,themeLight:`Tema claro`,themeDark:`Tema escuro`,selectOne:`Selecione uma opção`,ciRun:`Abrir execução de CI no GitHub ↗`,manualCheck:`Check não listado? Indique nome|ID da App|workflow exatos, separados por ponto e vírgula.`,checksObserved:`Foram encontrados jobs recentes de CI. Abra cada execução e confirme o job, a App e o passo obrigatório de cobertura antes de confiar nele.`,checksNoRecent:`Não há execuções recentes de workflows de pull request. Execute o CI habitual numa PR real ou introduza um produtor exato.`,checksNoVerifiable:`Há execuções recentes, mas nenhum job foi ligado a um Check Run e App exatos. Consulte o GitHub ou introduza o produtor manualmente.`,checksDenied:`O GitHub recusou a consulta do CI. Conceda Actions: read e Checks: read ao PAT de configuração ou introduza um produtor exato.`,checksUnavailable:`A consulta do CI falhou. Isto não prova que o repositório não tenha checks. Tente novamente ou introduza um produtor exato.`,projectsObserved:`Estes Projects existentes pertencem ao proprietário do repositório. Selecione só os que a automatização deve atualizar.`,projectsEmpty:`Esta consulta limitada ao GitHub não devolveu Projects acessíveis; isso não prova que não existam. Verifique o acesso ou introduza um número confirmado.`,projectsDenied:`O GitHub recusou a consulta de Projects. Verifique Projects: read da organização no PAT ou introduza os números.`,projectsUnavailable:`Não foi possível consultar Projects. Isto não prova que não existam. Introduza um número verificado ou tente novamente.`,projectsUnsupported:`Esta API do GitHub não lista Projects pessoais com um PAT de permissões precisas. Introduza o número do URL de um Project existente.`,discoveryTruncated:`Só foi inspecionada uma amostra limitada de Projects acessíveis ou checks recentes. Introduza manualmente um elemento em falta.`,checksDiscoveryScope:`Âmbito: até 20 execuções recentes de workflows de PR; são inspecionadas no máximo 15 execuções e 100 checks por commit.`,projectsDiscoveryScope:`Âmbito: no máximo 30 Projects acessíveis da organização em duas páginas; são inspecionados até 100 campos por Project.`,retryDiscovery:`Repetir pesquisa no GitHub`,retryRemaining:`Restam {count} tentativas só de leitura. As suas respostas são mantidas.`,retryExhausted:`Não restam tentativas. Verifique o GitHub e introduza manualmente os itens em falta.`,observationTimeUnknown:`data de observação indisponível`,branchRequirementUnknown:`Exigido pela regra do ramo: não verificado`,branchRequirementObserved:`Exigido em {branch} por um ruleset ativo para esta verificação e esta App específicas.`,ciRule:`Abrir ruleset da verificação obrigatória ↗`,projectSharedStatus:`Todos os Projects escolhidos devem partilhar cada valor Status. Esta configuração não atribui valores diferentes por Project.`,projectSelectionNotObserved:`Os números de Projects escolhidos anteriormente são mantidos, mas já não aparecem neste resultado do GitHub. Confirme-os no GitHub ou remova-os.`,removeSelection:`Remover seleção`,projectTransitionIssueCreated:`Nova questão`,projectTransitionPullRequestCreated:`Novo pull request`,projectTransitionIssueInProgress:`Questão em curso`,projectTransitionPullRequestInProgress:`Pull request em curso`,projectUrl:`Abrir Project no GitHub ↗`,projectManual:`Project em falta? Introduza o número positivo ou o URL exato do GitHub. IDs PVT_ não são aceites.`,projectStatusUnavailable:`Não foi possível verificar as opções Status de todos os Projects. Consulte cada um no GitHub e introduza o valor exato.`,projectStatusIncompatible:`Os Projects escolhidos não partilham valores Status. Selecione Projects compatíveis antes de continuar.`,producerName:`Nome do check/job`,producerAppId:`ID da App GitHub de origem`,producerWorkflow:`Nome do workflow`,producerAdd:`Adicionar check exato`,producerRemove:`Remover check`,producerManualHelp:`Use a identidade exata apresentada no GitHub. Adicioná-la não prova que exige cobertura.`,producerManualInvalid:`Indique nome, ID numérico positivo da App e workflow. Não use | ou ; nos nomes.`,translationPreviewTitle:`Revisão da tradução em curso`,translationPreviewBody:`As perguntas próprias da configuração já estão traduzidas. Alguns diagnósticos dinâmicos do GitHub ou do fornecedor ainda podem aparecer em inglês durante a revisão. Mudar de idioma não altera as suas respostas.`,unknownLocalError:`Ocorreu um erro inesperado na configuração local. Consulte o terminal e atualize a página ou reinicie a configuração.`},Hi=[`en`,`es`,`fr`,`pt`],Ui={en:`English`,es:`Español`,fr:`Français`,pt:`Português`},Wi={en:Ri,es:zi,fr:Bi,pt:Vi};function Q(e,t,n={}){return(Wi[t]??Wi.en)[e].replace(/\{(\w+)\}/gu,(e,t)=>n[t]??``)}var Gi={Repository:`repository`,"Setup choices":`choices`,"Setup PAT":`setupPat`,Plan:`plan`,"Bot PAT & credentials":`botPat`,Apply:`apply`,Preparation:`gettingReady`};function Ki(e,t){return Q(Gi[e??``]??`gettingReady`,t)}var $=Mi(`en`),qi=U(`
                                                                                            • `),Ji=U(``);function Yi(e,t){D(t,!1);let n=()=>Y($,`$setupLocale`,r),[r,i]=X(),a=Z(t,`journey`,8),o=[`repository`,`choices`,`setupPat`,`plan`,`botPat`,`apply`];J();var s=Ji(),c=N(s),l=I(N(c)),u=F(I(N(l)),!0);T(l),T(c);var d=I(c,2),f=F(d,!0),p=I(d,2);Qr(p,5,()=>o,Jr,(e,t,r)=>{var i=qi();let o;var s=N(i),c=F(s,!0),l=F(I(s,2),!0);T(i),L((e,t)=>{q(i,`aria-current`,(H(a()),V(()=>a()?.position===r+1?`step`:void 0))),o=ui(i,1,``,null,o,{current:a()?.position===r+1,completed:(a()?.position??0)>r+1}),G(c,e),G(l,t)},[()=>(H(a()),V(()=>(a()?.position??0)>r+1?`✓`:String(r+1).padStart(2,`0`))),()=>(H(Q),B(t),n(),V(()=>Q(B(t),n())))]),W(e,i)}),T(p);var m=I(p,2),h=I(N(m)),g=N(h),_=F(g,!0),v=F(I(g),!0);T(h),T(m),T(s),L((e,t,n,r,i)=>{q(s,`aria-label`,e),G(u,t),G(f,n),G(_,r),G(v,i)},[()=>(H(Q),n(),V(()=>Q(`progress`,n()))),()=>(H(Q),n(),V(()=>Q(`studio`,n()))),()=>(H(Q),n(),V(()=>Q(`journey`,n()))),()=>(H(Q),n(),V(()=>Q(`localDesign`,n()))),()=>(H(Q),n(),V(()=>Q(`localDesignBody`,n())))]),W(e,s),O(),i()}var Xi=U(`
                                                                                              `);function Zi(e,t){D(t,!1);let n=()=>Y($,`$setupLocale`,r),[r,i]=X(),a=j(`system`);Tn(()=>B(a),()=>{document.documentElement.dataset.theme=B(a)}),En(),J();var o=Xi(),s=N(o);let c;var l=F(s,!0),u=I(s,2);let d;var f=I(u,2);let p;T(o),L((e,t,n,r,i,m,h,g)=>{q(o,`aria-label`,e),q(s,`aria-pressed`,B(a)===`system`),q(s,`aria-label`,t),q(s,`title`,n),c=ui(s,1,``,null,c,{active:B(a)===`system`}),G(l,r),q(u,`aria-pressed`,B(a)===`light`),q(u,`aria-label`,i),q(u,`title`,m),d=ui(u,1,``,null,d,{active:B(a)===`light`}),q(f,`aria-pressed`,B(a)===`dark`),q(f,`aria-label`,h),q(f,`title`,g),p=ui(f,1,``,null,p,{active:B(a)===`dark`})},[()=>(H(Q),n(),V(()=>Q(`theme`,n()))),()=>(H(Q),n(),V(()=>Q(`themeSystem`,n()))),()=>(H(Q),n(),V(()=>Q(`themeSystem`,n()))),()=>(H(Q),n(),V(()=>Q(`themeAuto`,n()))),()=>(H(Q),n(),V(()=>Q(`themeLight`,n()))),()=>(H(Q),n(),V(()=>Q(`themeLight`,n()))),()=>(H(Q),n(),V(()=>Q(`themeDark`,n()))),()=>(H(Q),n(),V(()=>Q(`themeDark`,n())))]),Sr(`click`,s,()=>M(a,`system`)),Sr(`click`,u,()=>M(a,`light`)),Sr(`click`,f,()=>M(a,`dark`)),W(e,o),O(),i()}Cr([`click`]);var Qi=U(``),$i=U(` `,1);function ea(e,t){D(t,!1);let n=()=>Y($,`$setupLocale`,r),[r,i]=X();function a(e){let t=e.currentTarget.value;Hi.includes(t)&&$.set(t)}J();var o=$i(),s=P(o),c=N(s),l=I(c);Qr(l,5,()=>Hi,Jr,(e,t)=>{var n=Qi(),r=F(n,!0),i={};L(()=>{G(r,Ui[B(t)]),i!==(i=B(t))&&(n.value=(n.__value=i)??``)}),W(e,n)}),T(l);var u;mi(l),T(s);var d=F(I(s,2),!0);L((e,t)=>{G(c,`${e??``} `),q(l,`aria-label`,t),u!==(u=n())&&(l.value=(l.__value=u)??``,pi(l,u)),G(d,Ui[n()])},[()=>Q(`language`,n()),()=>Q(`language`,n())]),Sr(`change`,l,a),W(e,o),O(),i()}Cr([`change`]);var ta=U(`
                                                                                              `);function na(e,t){D(t,!1);let n=()=>Y($,`$setupLocale`,r),[r,i]=X(),a=Z(t,`repository`,8);J();var o=ta(),s=N(o),c=N(s),l=F(c,!0),u=I(c,2),d=F(N(u),!0);T(u),T(s);var f=I(s,2),p=N(f),m=I(N(p));T(p);var h=I(p);ea(h,{}),Zi(I(h),{}),T(f),T(o),L((e,t,n)=>{G(l,e),G(d,t),G(m,` ${n??``}`)},[()=>(H(Q),n(),V(()=>Q(`setup`,n()))),()=>(H(a()),H(Q),n(),V(()=>a()??Q(`connecting`,n()))),()=>(H(Q),n(),V(()=>Q(`localSession`,n())))]),W(e,o),O(),i()}var ra={"repository.confirm":{title:`Confirm this repository`,description:`This checkout points to {repository} on branch {branch}. Confirm the target before configuring PAT access or files.`,choices:[`Yes, this is my repository`,`Stop and choose another checkout`]},"setup.depth":{title:`Choose how much detail to review`,description:`Basic still asks about permissions, security, branch roles, Projects, approval and storage. Selected advanced agent, branch-prefix and Bugbot settings keep their defaults; inspect and edit them in the final plan. Custom asks every applicable question. Neither path applies changes now.`,choices:[`Basic guided setup (recommended)`,`Customize every setting`]},"setup.environmentPat":{title:`An environment setup PAT is available`,description:`Its value stays in the CLI process and is never sent to this page. Exiting Copilot cannot unset your parent shell variable.`,choices:[`Use the environment PAT`,`Create or enter a different PAT`]},"plan.review":{title:`Review your setup plan`,description:`Inspect the affected files, workflows, Variables and Secret names before approving. No setup changes start by opening this plan.`},"workflow.update":{title:`Update existing workflows?`,description:`These existing workflow files differ from the setup plan: {files}. Keeping them may leave some new settings inactive.`,choices:[`Keep existing`,`Update setup-managed workflows`]},"setupPat.method":{title:`How will you provide your setup PAT?`,description:`This temporary PAT authorizes this one setup run. GitHub creates it under your operator account; Copilot cannot revoke it for you.`,choices:[`Guided GitHub link`,`Manual PAT`]},"setupPat.ownerKind":{title:`Who owns this repository on GitHub?`,description:`The owner determines which organization permissions may be needed. Check the repository header on GitHub if unsure.`,choices:[`Organization`,`Personal account`,`Not sure`]},"setupPat.review":{title:`Review provisional setup PAT permissions`,description:`These grants follow your choices so far. GitHub inspection may add a requirement; you will review any change before setup starts.`,choices:[`Continue to GitHub`,`Review setup choices again`,`View full permission table`,`Enter a PAT manually`]},"setupPat.entry":{title:`Temporary setup PAT`,description:`Open GitHub as your operator account, complete 2FA, choose “Only select repositories”, select this repository and copy the generated PAT here. This PAT is for this run only; delete it on GitHub afterwards.`},"setupPat.confirmAccount":{title:`GitHub authenticated the setup PAT as @{account}`,description:`Is this the operator account that should configure this repository? A wrong account must stop before any setup change.`,choices:[`Yes, continue`,`No, stop`]},"setupPat.confirmWrites":{title:`Confirm write permissions that GitHub cannot safely test`,description:`Some required write grants cannot be proven without a mutation. Check them in GitHub against the displayed permission table before confirming.`,choices:[`No, stop`,`Yes, I checked them`]},"botPat.method":{title:`How will you provide the bot PAT?`,description:`The bot PAT is separate from the setup PAT. It belongs to the account that will run future GitHub Actions and is stored as a Secret after approval.`,choices:[`Guided GitHub link`,`Manual PAT`]},"botPat.login":{title:`Expected GitHub bot login`,description:`Enter the bot account login without @. Copilot resolves its numeric GitHub ID and compares it with the PAT owner.`},"botPat.entry.guided":{title:`{name} — bot account PAT`,description:`Open GitHub as @{account} (account ID {accountId}), not as the setup operator. Select only this repository, review all grants and paste the PAT here. Suggested expiry: 90 days. An existing Secret value cannot be read back.`},"botPat.entry.manual":{title:`{name} — bot account PAT`,description:`Use the bot account, select only the intended repository and review all grants before pasting its PAT. Suggested expiry: 90 days. An existing Secret value cannot be read back.`},"credential.apiKey":{title:`{name} — {provider} API key`,description:`Paste the API key for {provider}. It is sent only to this local setup process and, if approved, installed as a GitHub Actions Secret. Existing Secret values cannot be read back.`},"credential.existing":{title:`Existing {name}: {status}`,description:`GitHub cannot reveal the current Secret value. Keep it only if you deliberately accept that its health cannot be verified here; replace or skip it otherwise.`,choices:[`Keep existing`,`Replace it`,`Skip this credential`]},"apply.confirm":{title:`Apply this setup now?`,description:`This is the final approval. Local files and selected GitHub resources may change. A partial result may require inspection before retrying.`,choices:[`Apply setup`,`Stop without applying`]}},ia={"repository.confirm":{title:`Confirma el repositorio`,description:`Este checkout apunta a {repository} en la rama {branch}. Comprueba el destino antes de configurar permisos del PAT o archivos.`,choices:[`Sí, es mi repositorio`,`Detener y elegir otro checkout`]},"setup.depth":{title:`Elige cuánto detalle quieres revisar`,description:`El modo básico sigue preguntando por permisos, seguridad, ramas, Projects, aprobación y almacenamiento. Algunos ajustes avanzados de agentes, prefijos de rama y Bugbot conservan sus valores predeterminados; podrás revisarlos y editarlos en el plan final. El modo personalizado pregunta por todos los ajustes aplicables. Ninguno aplica cambios todavía.`,choices:[`Configuración básica guiada (recomendada)`,`Personalizar todos los ajustes`]},"setup.environmentPat":{title:`Hay un PAT de configuración en el entorno`,description:`Su valor permanece en el proceso CLI y no se envía a esta página. Cerrar Copilot no elimina la variable de la terminal original.`,choices:[`Usar el PAT del entorno`,`Crear o introducir otro PAT`]},"plan.review":{title:`Revisa el plan de configuración`,description:`Comprueba los archivos, workflows, Variables y nombres de Secrets antes de aprobar. Abrir el plan no inicia ningún cambio.`},"workflow.update":{title:`¿Actualizar los workflows existentes?`,description:`Estos archivos difieren del plan: {files}. Si los conservas, algunos ajustes nuevos podrían no activarse.`,choices:[`Conservar los existentes`,`Actualizar los workflows gestionados`]},"setupPat.method":{title:`¿Cómo proporcionarás el PAT de configuración?`,description:`Este PAT temporal autoriza una sola ejecución. GitHub lo crea con tu cuenta de operador; Copilot no puede revocarlo por ti.`,choices:[`Enlace guiado de GitHub`,`Introducir PAT manualmente`]},"setupPat.ownerKind":{title:`¿Quién es propietario de este repositorio en GitHub?`,description:`El propietario determina qué permisos de organización pueden hacer falta. Consulta la cabecera del repositorio en GitHub si dudas.`,choices:[`Organización`,`Cuenta personal`,`No lo sé`]},"setupPat.review":{title:`Revisa los permisos provisionales del PAT de configuración`,description:`Estos permisos dependen de las opciones elegidas. Al inspeccionar GitHub podría aparecer otro requisito; lo revisarás antes de aplicar cambios.`,choices:[`Continuar en GitHub`,`Revisar de nuevo las opciones`,`Ver la tabla completa de permisos`,`Introducir un PAT manualmente`]},"setupPat.entry":{title:`PAT temporal de configuración`,description:`Abre GitHub con tu cuenta de operador, completa el 2FA, elige «Only select repositories», selecciona este repositorio y pega aquí el PAT generado. Es solo para esta ejecución; elimínalo en GitHub después.`},"setupPat.confirmAccount":{title:`GitHub autenticó el PAT de configuración como @{account}`,description:`¿Es la cuenta de operador que debe configurar este repositorio? Una cuenta incorrecta debe detener el proceso antes de cualquier cambio.`,choices:[`Sí, continuar`,`No, detener`]},"setupPat.confirmWrites":{title:`Confirma permisos de escritura que GitHub no puede probar sin cambios`,description:`Algunos permisos de escritura no pueden verificarse sin modificar recursos. Compruébalos en GitHub frente a la tabla mostrada antes de confirmar.`,choices:[`No, detener`,`Sí, los he comprobado`]},"botPat.method":{title:`¿Cómo proporcionarás el PAT del bot?`,description:`Es distinto del PAT de configuración. Pertenece a la cuenta que ejecutará las futuras GitHub Actions y se instalará como Secret tras la aprobación.`,choices:[`Enlace guiado de GitHub`,`Introducir PAT manualmente`]},"botPat.login":{title:`Usuario previsto del bot en GitHub`,description:`Introduce el nombre de usuario del bot sin @. Copilot consultará su ID numérico de GitHub y lo comparará con el propietario del PAT.`},"botPat.entry.guided":{title:`{name} — PAT de la cuenta bot`,description:`Abre GitHub como @{account} (ID {accountId}), no como operador de setup. Selecciona solo este repositorio, revisa todos los permisos y pega aquí el PAT. Caducidad sugerida: 90 días. El valor de un Secret existente no puede leerse.`},"botPat.entry.manual":{title:`{name} — PAT de la cuenta bot`,description:`Usa la cuenta bot, selecciona solo el repositorio previsto y revisa todos los permisos antes de pegar el PAT. Caducidad sugerida: 90 días. El valor de un Secret existente no puede leerse.`},"credential.apiKey":{title:`{name} — clave API de {provider}`,description:`Pega la clave API de {provider}. Solo se envía a este proceso local y, si apruebas el plan, se instala como Secret de GitHub Actions. Los valores existentes no pueden leerse.`},"credential.existing":{title:`{name} existente: {status}`,description:`GitHub no muestra el valor actual del Secret. Consérvalo solo si aceptas expresamente que aquí no se puede comprobar su estado; de lo contrario, sustitúyelo u omítelo.`,choices:[`Conservar`,`Sustituir`,`Omitir esta credencial`]},"apply.confirm":{title:`¿Aplicar la configuración ahora?`,description:`Esta es la aprobación final. Pueden cambiar archivos locales y recursos de GitHub seleccionados. Si el resultado es parcial, revísalo antes de reintentar.`,choices:[`Aplicar configuración`,`Detener sin aplicar`]}},aa={"repository.confirm":{title:`Confirmez ce dépôt`,description:`Ce dossier pointe vers {repository}, branche {branch}. Vérifiez la cible avant de configurer les accès PAT ou les fichiers.`,choices:[`Oui, c’est mon dépôt`,`Arrêter et choisir un autre dossier`]},"setup.depth":{title:`Choisissez le niveau de détail`,description:`Le parcours de base demande toujours les autorisations, la sécurité, les branches, Projects, l’approbation et le stockage. Certains réglages avancés des agents, préfixes de branche et Bugbot conservent leurs valeurs par défaut ; vous pourrez les examiner et les modifier dans le plan final. Le parcours personnalisé pose toutes les questions applicables. Aucun changement n’est appliqué à ce stade.`,choices:[`Configuration de base guidée (recommandée)`,`Personnaliser tous les réglages`]},"setup.environmentPat":{title:`Un PAT de configuration est disponible dans l’environnement`,description:`Sa valeur reste dans le processus CLI et n’est jamais envoyée à cette page. Quitter Copilot ne supprime pas la variable du shell parent.`,choices:[`Utiliser le PAT de l’environnement`,`Créer ou saisir un autre PAT`]},"plan.review":{title:`Examinez le plan de configuration`,description:`Vérifiez les fichiers, workflows, Variables et noms de Secrets avant d’approuver. Ouvrir le plan ne lance aucun changement.`},"workflow.update":{title:`Mettre à jour les workflows existants ?`,description:`Ces fichiers diffèrent du plan : {files}. Les conserver peut laisser certains nouveaux réglages inactifs.`,choices:[`Conserver les fichiers existants`,`Mettre à jour les workflows gérés`]},"setupPat.method":{title:`Comment fournirez-vous le PAT de configuration ?`,description:`Ce PAT temporaire autorise une seule exécution. GitHub le crée avec votre compte opérateur ; Copilot ne peut pas le révoquer à votre place.`,choices:[`Lien GitHub guidé`,`Saisir un PAT manuellement`]},"setupPat.ownerKind":{title:`À qui appartient ce dépôt sur GitHub ?`,description:`Le propriétaire détermine les éventuelles autorisations d’organisation. Consultez l’en-tête du dépôt sur GitHub en cas de doute.`,choices:[`Organisation`,`Compte personnel`,`Je ne sais pas`]},"setupPat.review":{title:`Examinez les autorisations provisoires du PAT de configuration`,description:`Ces autorisations suivent vos choix. L’inspection de GitHub peut révéler un besoin supplémentaire ; vous le reverrez avant tout changement.`,choices:[`Continuer sur GitHub`,`Revoir les choix de configuration`,`Voir toutes les autorisations`,`Saisir un PAT manuellement`]},"setupPat.entry":{title:`PAT temporaire de configuration`,description:`Ouvrez GitHub avec votre compte opérateur, effectuez la 2FA, choisissez « Only select repositories », sélectionnez ce dépôt puis collez ici le PAT généré. Il ne sert qu’à cette exécution ; supprimez-le sur GitHub ensuite.`},"setupPat.confirmAccount":{title:`GitHub a authentifié le PAT de configuration comme @{account}`,description:`Est-ce bien le compte opérateur autorisé à configurer ce dépôt ? Un autre compte doit arrêter le processus avant tout changement.`,choices:[`Oui, continuer`,`Non, arrêter`]},"setupPat.confirmWrites":{title:`Confirmez les droits d’écriture que GitHub ne peut pas vérifier sans changement`,description:`Certains droits d’écriture ne peuvent être prouvés sans modifier des ressources. Comparez-les au tableau affiché sur GitHub avant de confirmer.`,choices:[`Non, arrêter`,`Oui, je les ai vérifiés`]},"botPat.method":{title:`Comment fournirez-vous le PAT du bot ?`,description:`Il est distinct du PAT de configuration. Il appartient au compte qui exécutera les futures GitHub Actions et sera installé comme Secret après approbation.`,choices:[`Lien GitHub guidé`,`Saisir un PAT manuellement`]},"botPat.login":{title:`Identifiant GitHub attendu pour le bot`,description:`Saisissez l’identifiant du bot sans @. Copilot récupère son ID numérique GitHub et le compare au propriétaire du PAT.`},"botPat.entry.guided":{title:`{name} — PAT du compte bot`,description:`Ouvrez GitHub en tant que @{account} (ID {accountId}), pas en tant qu’opérateur. Sélectionnez uniquement ce dépôt, vérifiez tous les droits et collez le PAT ici. Expiration suggérée : 90 jours. La valeur d’un Secret existant ne peut pas être relue.`},"botPat.entry.manual":{title:`{name} — PAT du compte bot`,description:`Utilisez le compte bot, sélectionnez uniquement le dépôt voulu et vérifiez tous les droits avant de coller le PAT. Expiration suggérée : 90 jours. La valeur d’un Secret existant ne peut pas être relue.`},"credential.apiKey":{title:`{name} — clé API de {provider}`,description:`Collez la clé API de {provider}. Elle est envoyée uniquement au processus local puis, si vous approuvez le plan, installée comme Secret GitHub Actions. Les valeurs existantes ne peuvent pas être relues.`},"credential.existing":{title:`{name} existant : {status}`,description:`GitHub ne révèle pas la valeur actuelle du Secret. Ne la conservez que si vous acceptez explicitement de ne pas pouvoir vérifier son état ici ; sinon, remplacez-la ou ignorez-la.`,choices:[`Conserver`,`Remplacer`,`Ignorer cet identifiant`]},"apply.confirm":{title:`Appliquer la configuration maintenant ?`,description:`C’est l’approbation finale. Des fichiers locaux et ressources GitHub choisis peuvent changer. Un résultat partiel doit être inspecté avant une nouvelle tentative.`,choices:[`Appliquer la configuration`,`Arrêter sans appliquer`]}},oa={"repository.confirm":{title:`Confirme este repositório`,description:`Esta pasta aponta para {repository}, no ramo {branch}. Confirme o destino antes de configurar acessos PAT ou ficheiros.`,choices:[`Sim, é o meu repositório`,`Parar e escolher outra pasta`]},"setup.depth":{title:`Escolha o nível de detalhe`,description:`O percurso básico continua a perguntar sobre permissões, segurança, ramos, Projects, aprovação e armazenamento. Algumas definições avançadas de agentes, prefixos de ramos e Bugbot mantêm os valores predefinidos; poderá revê-las e editá-las no plano final. O percurso personalizado pergunta por todas as definições aplicáveis. Nenhum dos percursos aplica alterações nesta fase.`,choices:[`Configuração básica guiada (recomendada)`,`Personalizar todas as definições`]},"setup.environmentPat":{title:`Existe um PAT de configuração no ambiente`,description:`O valor permanece no processo CLI e nunca é enviado para esta página. Sair do Copilot não remove a variável da shell original.`,choices:[`Usar o PAT do ambiente`,`Criar ou introduzir outro PAT`]},"plan.review":{title:`Reveja o plano de configuração`,description:`Confirme ficheiros, fluxos, Variables e nomes de Secrets antes de aprovar. Abrir o plano não inicia alterações.`,choices:void 0},"workflow.update":{title:`Atualizar os fluxos existentes?`,description:`Estes ficheiros diferem do plano: {files}. Conservá-los pode deixar algumas definições novas inativas.`,choices:[`Conservar os existentes`,`Atualizar os fluxos geridos`]},"setupPat.method":{title:`Como irá fornecer o PAT de configuração?`,description:`Este PAT temporário autoriza uma única execução. O GitHub cria-o na sua conta de operador; o Copilot não pode revogá-lo por si.`,choices:[`Ligação guiada do GitHub`,`Introduzir PAT manualmente`]},"setupPat.ownerKind":{title:`Quem é o proprietário deste repositório no GitHub?`,description:`O proprietário determina as permissões de organização necessárias. Consulte o cabeçalho do repositório no GitHub se tiver dúvidas.`,choices:[`Organização`,`Conta pessoal`,`Não tenho a certeza`]},"setupPat.review":{title:`Reveja as permissões provisórias do PAT de configuração`,description:`Estas permissões seguem as suas escolhas. A inspeção do GitHub pode revelar outro requisito; irá revê-lo antes de qualquer alteração.`,choices:[`Continuar no GitHub`,`Rever as escolhas`,`Ver a tabela completa de permissões`,`Introduzir um PAT manualmente`]},"setupPat.entry":{title:`PAT temporário de configuração`,description:`Abra o GitHub com a sua conta de operador, conclua a 2FA, escolha «Only select repositories», selecione este repositório e cole aqui o PAT gerado. É apenas para esta execução; elimine-o no GitHub depois.`},"setupPat.confirmAccount":{title:`O GitHub autenticou o PAT de configuração como @{account}`,description:`É esta a conta de operador que deve configurar o repositório? Uma conta errada tem de parar o processo antes de qualquer alteração.`,choices:[`Sim, continuar`,`Não, parar`]},"setupPat.confirmWrites":{title:`Confirme permissões de escrita que o GitHub não pode testar sem alterações`,description:`Algumas permissões de escrita não podem ser provadas sem alterar recursos. Compare-as com a tabela no GitHub antes de confirmar.`,choices:[`Não, parar`,`Sim, já as verifiquei`]},"botPat.method":{title:`Como irá fornecer o PAT do bot?`,description:`É diferente do PAT de configuração. Pertence à conta que executará as futuras GitHub Actions e será instalado como Secret após aprovação.`,choices:[`Ligação guiada do GitHub`,`Introduzir PAT manualmente`]},"botPat.login":{title:`Utilizador GitHub esperado para o bot`,description:`Introduza o nome de utilizador do bot sem @. O Copilot consulta o ID numérico do GitHub e compara-o com o proprietário do PAT.`},"botPat.entry.guided":{title:`{name} — PAT da conta bot`,description:`Abra o GitHub como @{account} (ID {accountId}), não como operador. Selecione apenas este repositório, reveja todas as permissões e cole aqui o PAT. Validade sugerida: 90 dias. Não é possível voltar a ler o valor de um Secret existente.`},"botPat.entry.manual":{title:`{name} — PAT da conta bot`,description:`Use a conta bot, selecione apenas o repositório pretendido e reveja todas as permissões antes de colar o PAT. Validade sugerida: 90 dias. Não é possível voltar a ler o valor de um Secret existente.`},"credential.apiKey":{title:`{name} — chave API de {provider}`,description:`Cole a chave API de {provider}. Só é enviada para este processo local e, se aprovar o plano, instalada como Secret do GitHub Actions. Os valores existentes não podem voltar a ser lidos.`},"credential.existing":{title:`{name} existente: {status}`,description:`O GitHub não revela o valor atual do Secret. Conserve-o apenas se aceitar expressamente que aqui não se pode verificar o seu estado; caso contrário, substitua-o ou ignore-o.`,choices:[`Conservar`,`Substituir`,`Ignorar esta credencial`]},"apply.confirm":{title:`Aplicar a configuração agora?`,description:`Esta é a aprovação final. Ficheiros locais e recursos GitHub selecionados podem mudar. Um resultado parcial exige inspeção antes de tentar novamente.`,choices:[`Aplicar configuração`,`Parar sem aplicar`]}},sa={"session.controlMoved":`Control moved to this tab. The previous tab is now read-only.`,"session.cancelled":`Setup stopped before applying further changes. Any PAT created on GitHub still exists until you delete it there.`,"plan.ready":`Plan ready: {files} files, {variables} Variables and {secrets} Secret names. Review it before continuing.`,"permission.preview":`Permission preview: issue workflows {issues}; PR approval {approval}; Secrets {secrets}; Variables {variables}; Projects {projects}. Review the exact grants before creating the setup PAT.`,"setupPat.corrected.bootstrap":`The setup PAT could not access the repository. No setup changes started. Required grants: {grants}. Create a corrected PAT using the updated GitHub link.`,"setupPat.corrected.final":`Required setup PAT permissions changed after inspection. No setup changes started. New grants: {grants}. Create a corrected PAT using the updated GitHub link.`,"setupPat.cleanup":`Delete the temporary setup PAT in GitHub Settings after this run. Closing Copilot does not revoke it.`,"botPat.separation":`The bot PAT is separate from your setup PAT. These credentials become GitHub Actions Secrets: {names}. Existing Secret values cannot be read back. Re-enter an existing bot PAT so its grants can be checked. No credential-health workflow runs before Apply.`,"credential.checks":`Credential checks finished for {count} items. Each result is shown below. The terminal has technical details; an existing Secret value cannot be read back.`,"credential.status.valid":`Valid`,"credential.status.invalid":`Invalid`,"credential.status.missing":`Missing`,"credential.status.unverifiable":`Cannot be verified without a new value`,"credential.status.not_required":`Not required`,"validation.producers":`Select 1–8 observed checks or enter exact name|App ID|workflow tuples.`,"validation.duplicateNames":`Two trusted producers use the same check name. Coverage stores only the name: choose one producer or rename the CI jobs.`,"validation.projectStatusVerified":`Open every selected Project in GitHub and confirm that all four exact Status values exist. Answer Yes after checking, or No to choose Projects again.`,"validation.projectStatusRedo":`Status values were not confirmed. Choose compatible Projects, then review their Status options again.`,"validation.number":`Enter a non-negative whole number.`,"validation.boolean":`Enter yes or no.`,"validation.choice":`Select one of the listed options.`,"validation.unknownResource":`Unknown inherited resource names: {names}. Choose only names shown in the inherited list.`,"validation.unknownWorkflow":`Unknown issue workflows: {names}. Choose only the listed workflow types.`,"validation.firstQuestion":`This is the first question in this pass. Review it or cancel setup.`,"validation.duplicateProducer":`This trusted check was selected twice. Remove the duplicate selection.`,"validation.savedStatus":`The saved Status value is not available in every selected Project. Choose a listed option.`,"validation.projectIncompatible":`The selected Projects have no common Status option. Choose compatible Projects or configure them separately.`,"validation.projectLimit":`Choose at most 10 Projects; separate their numbers or URLs with commas.`,"validation.projectOwnerNeeded":`A Project URL needs a known repository owner; enter its positive number instead.`,"validation.projectOwnerMismatch":`Use a GitHub Project URL belonging to {owner}, without query parameters.`,"validation.projectUrl":`Enter a valid GitHub Project URL or positive Project number.`,"validation.projectNumber":`Enter the positive Project number from its GitHub URL, not a PVT_ GraphQL ID.`,"validation.projectNumberRange":`Project numbers must be positive integers at most 2147483647.`,"validation.projectDuplicate":`Project {number} was selected more than once.`,"validation.unknown":`This answer could not be accepted. Review the question and its help; the terminal has the technical detail.`},ca={"session.controlMoved":`El control ha pasado a esta pestaña. La anterior ahora es de solo lectura.`,"session.cancelled":`La configuración se detuvo antes de aplicar más cambios. Los PAT creados en GitHub siguen existiendo hasta que los elimines allí.`,"plan.ready":`Plan listo: {files} archivos, {variables} Variables y {secrets} nombres de Secrets. Revísalo antes de continuar.`,"permission.preview":`Vista previa de permisos: flujos de incidencias {issues}; aprobación de PR {approval}; Secrets {secrets}; Variables {variables}; Projects {projects}. Revisa los permisos exactos antes de crear el PAT de configuración.`,"setupPat.corrected.bootstrap":`El PAT de configuración no pudo acceder al repositorio. No se iniciaron cambios. Permisos necesarios: {grants}. Crea un PAT corregido desde el enlace actualizado de GitHub.`,"setupPat.corrected.final":`Los permisos necesarios del PAT cambiaron tras la inspección. No se iniciaron cambios. Permisos nuevos: {grants}. Crea un PAT corregido desde el enlace actualizado.`,"setupPat.cleanup":`Elimina el PAT temporal de configuración en los ajustes de GitHub después de esta ejecución. Cerrar Copilot no lo revoca.`,"botPat.separation":`El PAT del bot es distinto del de configuración. Estas credenciales se instalarán como Secrets de GitHub Actions: {names}. Los valores existentes no pueden leerse. Vuelve a introducir un PAT del bot para comprobar sus permisos. Antes de Aplicar no se ejecuta ningún workflow de comprobación.`,"credential.checks":`Se han comprobado {count} credenciales. Abajo aparece el resultado de cada una. La terminal contiene los detalles técnicos; el valor de un Secret existente no puede leerse.`,"credential.status.valid":`Válida`,"credential.status.invalid":`No válida`,"credential.status.missing":`Falta`,"credential.status.unverifiable":`No verificable sin un valor nuevo`,"credential.status.not_required":`No necesaria`,"validation.producers":`Selecciona entre 1 y 8 checks observados o introduce las tuplas exactas nombre|ID de App|workflow.`,"validation.duplicateNames":`Dos productores fiables tienen el mismo nombre de check. La cobertura solo guarda el nombre: elige uno o cambia el nombre de los jobs de CI.`,"validation.projectStatusVerified":`Abre cada Project elegido en GitHub y confirma los cuatro valores Status exactos. Responde Sí tras comprobarlo, o No para elegir Projects de nuevo.`,"validation.projectStatusRedo":`No se confirmaron los valores Status. Elige Projects compatibles y vuelve a revisar sus opciones Status.`,"validation.number":`Introduce un número entero no negativo.`,"validation.boolean":`Elige Sí o No.`,"validation.choice":`Elige una de las opciones mostradas.`,"validation.unknownResource":`Nombres de recursos heredados desconocidos: {names}. Elige solo nombres de la lista heredada.`,"validation.unknownWorkflow":`Flujos de issues desconocidos: {names}. Elige solo los tipos indicados.`,"validation.firstQuestion":`Esta es la primera pregunta de esta pasada. Revísala o cancela la configuración.`,"validation.duplicateProducer":`Has seleccionado dos veces el mismo check fiable. Quita la selección duplicada.`,"validation.savedStatus":`El valor Status guardado no existe en todos los Projects seleccionados. Elige una opción de la lista.`,"validation.projectIncompatible":`Los Projects elegidos no comparten ningún valor Status. Elige Projects compatibles o configúralos por separado.`,"validation.projectLimit":`Elige como máximo 10 Projects; separa sus números o URL con comas.`,"validation.projectOwnerNeeded":`Para usar la URL de un Project hay que conocer el propietario del repositorio; introduce su número positivo.`,"validation.projectOwnerMismatch":`Usa la URL de un Project de GitHub que pertenezca a {owner}, sin parámetros.`,"validation.projectUrl":`Introduce una URL válida de un Project de GitHub o su número positivo.`,"validation.projectNumber":`Introduce el número positivo del Project que aparece en su URL, no un ID GraphQL PVT_.`,"validation.projectNumberRange":`El número del Project debe ser un entero positivo no mayor de 2147483647.`,"validation.projectDuplicate":`Has seleccionado el Project {number} más de una vez.`,"validation.unknown":`No se pudo aceptar esta respuesta. Revisa la pregunta y su ayuda; la terminal contiene el detalle técnico.`},la={"session.controlMoved":`Le contrôle est passé à cet onglet. L’onglet précédent est maintenant en lecture seule.`,"session.cancelled":`La configuration s’est arrêtée avant de nouveaux changements. Tout PAT créé sur GitHub existe encore jusqu’à sa suppression là-bas.`,"plan.ready":`Plan prêt : {files} fichiers, {variables} Variables et {secrets} noms de Secrets. Examinez-le avant de continuer.`,"permission.preview":`Aperçu des droits : flux de tickets {issues} ; approbation des PR {approval} ; Secrets {secrets} ; Variables {variables} ; Projects {projects}. Vérifiez les droits exacts avant de créer le PAT de configuration.`,"setupPat.corrected.bootstrap":`Le PAT de configuration n’a pas pu accéder au dépôt. Aucun changement n’a commencé. Droits requis : {grants}. Créez un PAT corrigé avec le lien GitHub mis à jour.`,"setupPat.corrected.final":`Les droits requis du PAT ont changé après inspection. Aucun changement n’a commencé. Nouveaux droits : {grants}. Créez un PAT corrigé avec le lien mis à jour.`,"setupPat.cleanup":`Supprimez le PAT temporaire de configuration dans les paramètres GitHub après cette exécution. Fermer Copilot ne le révoque pas.`,"botPat.separation":`Le PAT du bot est distinct du PAT de configuration. Ces identifiants deviendront des Secrets GitHub Actions : {names}. Les valeurs existantes ne peuvent pas être relues. Saisissez à nouveau un PAT du bot pour vérifier ses droits. Aucun workflow de vérification ne démarre avant Appliquer.`,"credential.checks":`Vérification terminée pour {count} identifiants. Le résultat de chacun figure ci-dessous. Le terminal contient les détails techniques ; la valeur d’un Secret existant ne peut pas être relue.`,"credential.status.valid":`Valide`,"credential.status.invalid":`Invalide`,"credential.status.missing":`Manquant`,"credential.status.unverifiable":`Invérifiable sans nouvelle valeur`,"credential.status.not_required":`Non requis`,"validation.producers":`Choisissez 1 à 8 vérifications observées ou saisissez les triplets exacts nom|ID d’App|workflow.`,"validation.duplicateNames":`Deux producteurs fiables portent le même nom de vérification. La couverture ne stocke que ce nom : choisissez-en un ou renommez les jobs CI.`,"validation.projectStatusVerified":`Ouvrez chaque Project choisi sur GitHub et confirmez les quatre valeurs Status exactes. Répondez Oui après vérification, ou Non pour choisir à nouveau les Projects.`,"validation.projectStatusRedo":`Les valeurs Status n’ont pas été confirmées. Choisissez des Projects compatibles, puis revérifiez leurs options Status.`,"validation.number":`Saisissez un entier positif ou nul.`,"validation.boolean":`Choisissez Oui ou Non.`,"validation.choice":`Choisissez une des options affichées.`,"validation.unknownResource":`Noms de ressources héritées inconnus : {names}. Choisissez seulement les noms de la liste héritée.`,"validation.unknownWorkflow":`Workflows de ticket inconnus : {names}. Choisissez seulement les types proposés.`,"validation.firstQuestion":`C’est la première question de cette passe. Vérifiez-la ou annulez la configuration.`,"validation.duplicateProducer":`Cette vérification fiable a été choisie deux fois. Retirez le doublon.`,"validation.savedStatus":`La valeur Status enregistrée n’existe pas dans tous les Projects choisis. Sélectionnez une option proposée.`,"validation.projectIncompatible":`Les Projects choisis ne partagent aucune valeur Status. Choisissez des Projects compatibles ou configurez-les séparément.`,"validation.projectLimit":`Choisissez au plus 10 Projects ; séparez leurs numéros ou URL par des virgules.`,"validation.projectOwnerNeeded":`Une URL de Project exige de connaître le propriétaire du dépôt ; saisissez plutôt son numéro positif.`,"validation.projectOwnerMismatch":`Utilisez une URL de Project GitHub appartenant à {owner}, sans paramètres.`,"validation.projectUrl":`Saisissez une URL valide de Project GitHub ou son numéro positif.`,"validation.projectNumber":`Saisissez le numéro positif figurant dans l’URL du Project, pas un ID GraphQL PVT_.`,"validation.projectNumberRange":`Le numéro du Project doit être un entier positif inférieur ou égal à 2147483647.`,"validation.projectDuplicate":`Le Project {number} a été choisi plusieurs fois.`,"validation.unknown":`Cette réponse n’a pas été acceptée. Vérifiez la question et son aide ; le terminal contient le détail technique.`},ua={"session.controlMoved":`O controlo passou para este separador. O separador anterior é agora apenas de leitura.`,"session.cancelled":`A configuração parou antes de novas alterações. Os PAT criados no GitHub continuam a existir até os eliminar lá.`,"plan.ready":`Plano pronto: {files} ficheiros, {variables} Variables e {secrets} nomes de Secrets. Reveja-o antes de continuar.`,"permission.preview":`Pré-visualização das permissões: fluxos de issues {issues}; aprovação de PR {approval}; Secrets {secrets}; Variables {variables}; Projects {projects}. Reveja as permissões exatas antes de criar o PAT de configuração.`,"setupPat.corrected.bootstrap":`O PAT de configuração não conseguiu aceder ao repositório. Não começaram alterações. Permissões necessárias: {grants}. Crie um PAT corrigido com a ligação GitHub atualizada.`,"setupPat.corrected.final":`As permissões necessárias do PAT mudaram após inspeção. Não começaram alterações. Novas permissões: {grants}. Crie um PAT corrigido com a ligação atualizada.`,"setupPat.cleanup":`Elimine o PAT temporário de configuração nas definições do GitHub após esta execução. Fechar o Copilot não o revoga.`,"botPat.separation":`O PAT do bot é distinto do PAT de configuração. Estas credenciais serão Secrets do GitHub Actions: {names}. Os valores existentes não podem voltar a ser lidos. Reintroduza um PAT do bot para verificar as permissões. Nenhum fluxo de verificação é executado antes de Aplicar.`,"credential.checks":`Verificação concluída para {count} credenciais. O resultado de cada uma aparece abaixo. O terminal contém os detalhes técnicos; o valor de um Secret existente não pode voltar a ser lido.`,"credential.status.valid":`Válida`,"credential.status.invalid":`Inválida`,"credential.status.missing":`Em falta`,"credential.status.unverifiable":`Não verificável sem um novo valor`,"credential.status.not_required":`Não necessária`,"validation.producers":`Selecione 1 a 8 verificações observadas ou introduza os triplos exatos nome|ID da App|workflow.`,"validation.duplicateNames":`Dois produtores fiáveis têm o mesmo nome de check. A cobertura guarda apenas o nome: escolha um ou altere o nome dos jobs de CI.`,"validation.projectStatusVerified":`Abra cada Project escolhido no GitHub e confirme os quatro valores Status exatos. Responda Sim após verificar, ou Não para voltar a escolher Projects.`,"validation.projectStatusRedo":`Os valores Status não foram confirmados. Escolha Projects compatíveis e volte a verificar as opções Status.`,"validation.number":`Introduza um número inteiro não negativo.`,"validation.boolean":`Escolha Sim ou Não.`,"validation.choice":`Escolha uma das opções apresentadas.`,"validation.unknownResource":`Nomes de recursos herdados desconhecidos: {names}. Escolha apenas nomes da lista herdada.`,"validation.unknownWorkflow":`Fluxos de questões desconhecidos: {names}. Escolha apenas os tipos indicados.`,"validation.firstQuestion":`Esta é a primeira pergunta desta ronda. Reveja-a ou cancele a configuração.`,"validation.duplicateProducer":`Esta verificação de confiança foi selecionada duas vezes. Remova a seleção duplicada.`,"validation.savedStatus":`O valor Status guardado não existe em todos os Projects escolhidos. Selecione uma opção da lista.`,"validation.projectIncompatible":`Os Projects escolhidos não partilham qualquer valor Status. Escolha Projects compatíveis ou configure-os separadamente.`,"validation.projectLimit":`Escolha no máximo 10 Projects; separe os números ou URL por vírgulas.`,"validation.projectOwnerNeeded":`Uma URL de Project exige que se conheça o proprietário do repositório; introduza antes o número positivo.`,"validation.projectOwnerMismatch":`Use uma URL de Project do GitHub pertencente a {owner}, sem parâmetros.`,"validation.projectUrl":`Introduza uma URL válida de Project do GitHub ou o respetivo número positivo.`,"validation.projectNumber":`Introduza o número positivo apresentado na URL do Project, não um ID GraphQL PVT_.`,"validation.projectNumberRange":`O número do Project tem de ser um inteiro positivo até 2147483647.`,"validation.projectDuplicate":`O Project {number} foi selecionado mais do que uma vez.`,"validation.unknown":`Esta resposta não foi aceite. Reveja a pergunta e a ajuda; o terminal contém o detalhe técnico.`},da={es:{All:`Todos`,prompt:`Preguntar antes de crear el enlace`,"create-if-missing":`Crear el enlace si falta`,disabled:`Desactivado`,replace:`Sustituir`,append:`Añadir`,preserve:`Conservar`,info:`Informativa`,low:`Baja`,medium:`Media`,high:`Alta`,smart:`Adaptativa`,default:`Predeterminado`,auto:`Automático`,always:`Reinstalar siempre`,recommend:`Recomendar aprobación`,guarded:`Aprobar solo con garantías`,off:`Desactivado`,check:`Check de CI que exige la cobertura`,numeric:`Informe numérico verificable`,repository:`Repositorio`,organization:`Organización`,selected:`Repositorios seleccionados`,private:`Repositorios privados`,all:`Todos los repositorios`,"production-lineage":`Conservar el linaje de producción`,"canonical-gitflow":`Git-Flow canónico`,manual:`Manual`,"auto-merge":`Fusionar automáticamente`,"merge-queue":`Cola de integración`,"create-only":`Solo crear PR`,direct:`Integración directa`,"sync-branch":`Mediante rama de sincronización`,"prefer-release":`Priorizar la release`,development:`Desarrollo`,both:`Ambos destinos`,"source-only":`Solo rama de origen`,"sync-only":`Solo rama de sincronización`,none:`Ninguna`,close:`Cerrar el issue`,"keep-open":`Mantener abierto`,guided:`Guiado`,compact:`Compacto`,quiet:`Mínimo`,update:`Actualizar el comentario`,milestones:`Publicar en hitos`,feature:`Funcionalidad`,bugfix:`Corrección`,documentation:`Documentación`,chore:`Mantenimiento`,help:`Ayuda o pregunta`,hotfix:`Arreglo urgente`,release:`Release`},fr:{All:`Tous`,prompt:`Demander avant de créer le renvoi`,"create-if-missing":`Créer le renvoi s’il manque`,disabled:`Désactivé`,replace:`Remplacer`,append:`Ajouter`,preserve:`Conserver`,info:`Information`,low:`Faible`,medium:`Moyenne`,high:`Élevée`,smart:`Adaptatif`,default:`Par défaut`,auto:`Automatique`,always:`Toujours réinstaller`,recommend:`Recommander une approbation`,guarded:`Approuver sous garde`,off:`Désactivé`,check:`Vérification CI imposant la couverture`,numeric:`Rapport numérique vérifiable`,repository:`Dépôt`,organization:`Organisation`,selected:`Dépôts sélectionnés`,private:`Dépôts privés`,all:`Tous les dépôts`,"production-lineage":`Préserver la filiation de production`,"canonical-gitflow":`Git-Flow canonique`,manual:`Manuel`,"auto-merge":`Fusionner automatiquement`,"merge-queue":`File de fusion`,"create-only":`Créer la PR uniquement`,direct:`Fusion directe`,"sync-branch":`Par branche de synchronisation`,"prefer-release":`Privilégier la version`,development:`Développement`,both:`Les deux destinations`,"source-only":`Branche source seulement`,"sync-only":`Branche de synchronisation seulement`,none:`Aucune`,close:`Fermer le ticket`,"keep-open":`Garder ouvert`,guided:`Guidé`,compact:`Compact`,quiet:`Minimal`,update:`Mettre le commentaire à jour`,milestones:`Publier aux étapes clés`,feature:`Fonctionnalité`,bugfix:`Correction`,documentation:`Documentation`,chore:`Maintenance`,help:`Aide ou question`,hotfix:`Correctif urgent`,release:`Version`},pt:{All:`Todos`,prompt:`Perguntar antes de criar o apontador`,"create-if-missing":`Criar o apontador se faltar`,disabled:`Desativado`,replace:`Substituir`,append:`Acrescentar`,preserve:`Conservar`,info:`Informação`,low:`Baixa`,medium:`Média`,high:`Alta`,smart:`Adaptativo`,default:`Predefinido`,auto:`Automático`,always:`Reinstalar sempre`,recommend:`Recomendar aprovação`,guarded:`Aprovar sob condições`,off:`Desativado`,check:`Verificação CI que exige cobertura`,numeric:`Relatório numérico verificável`,repository:`Repositório`,organization:`Organização`,selected:`Repositórios selecionados`,private:`Repositórios privados`,all:`Todos os repositórios`,"production-lineage":`Preservar a linhagem de produção`,"canonical-gitflow":`Git-Flow canónico`,manual:`Manual`,"auto-merge":`Integrar automaticamente`,"merge-queue":`Fila de integração`,"create-only":`Criar apenas a PR`,direct:`Integração direta`,"sync-branch":`Através de ramo de sincronização`,"prefer-release":`Priorizar a release`,development:`Desenvolvimento`,both:`Ambos os destinos`,"source-only":`Apenas ramo de origem`,"sync-only":`Apenas ramo de sincronização`,none:`Nenhum`,close:`Fechar a questão`,"keep-open":`Manter aberta`,guided:`Guiado`,compact:`Compacto`,quiet:`Mínimo`,update:`Atualizar o comentário`,milestones:`Publicar nos marcos`,feature:`Funcionalidade`,bugfix:`Correção`,documentation:`Documentação`,chore:`Manutenção`,help:`Ajuda ou pergunta`,hotfix:`Hotfix`,release:`Release`}},fa=new Set([`codex`,`opencode`,`cursor`,`openai`,`anthropic`,`google`,`openrouter`,`local`]);function pa(e,t,n){if(n===`en`||fa.has(t))return t;let r=da[n];if(e===`issueWorkflows.enabled`&&t.includes(` — `)){let e=t.split(` — `,1)[0];if(r[e])return`${e} — ${r[e]}`}return e===`repository.reconciliationCleanup`&&t===`all`?{es:`Todas las ramas temporales`,fr:`Toutes les branches temporaires`,pt:`Todos os ramos temporários`}[n]:r[t]??t}var ma={en:sa,es:ca,fr:la,pt:ua};function ha(e,t){if(!e.copyId)return t===`en`?e.text:Q(`unknownLocalError`,t);let n=ma[t][e.copyId],r=e.copyValues??{},i=e.copyId===`permission.preview`?{...r,issues:(r.issues??``).split(`|`).map(e=>pa(`issueWorkflows.enabled`,e,t)).join(`, `),approval:pa(`pullRequestApproval.mode`,r.approval??``,t),secrets:pa(`storage.secrets.defaultScope`,r.secrets??``,t),variables:pa(`storage.variables.defaultScope`,r.variables??``,t),projects:pa(`projects.ids`,r.projects??``,t)}:r,a=n.replace(/\{([a-zA-Z]\w*)\}/gu,(e,t)=>i[t]??``);return e.copyId!==`credential.checks`||!e.credentialChecks?.length?a:`${a}\n${e.credentialChecks.map(e=>{let n=`credential.status.${e.status}`;return`${e.name}: ${ma[t][n]}`}).join(` +`)}`}var ga={en:ra,es:ia,fr:aa,pt:oa};function _a(e,t){return e.replace(/\{([a-zA-Z]\w*)\}/gu,(e,n)=>t[n]??``)}function va(e,t){if(!e.copyId)return;let n=ga[t][e.copyId],r={...e.copyValues??{}};if(e.copyId===`credential.existing`&&r.status&&[`valid`,`invalid`,`missing`,`unverifiable`,`not_required`].includes(r.status)){let e=`credential.status.${r.status}`;r.status=ma[t][e]}return{title:_a(n.title,r),description:_a(n.description,r),...n.choices?{choices:n.choices}:{}}}function ya(e,t,n){return va(e,t)?.choices?.[n]??e.choices[n]}var ba=U(`

                                                                                              `,1);function xa(e,t){D(t,!1);let n=()=>Y($,`$setupLocale`,r),[r,i]=X(),a=j(),o=Z(t,`view`,8);Tn(()=>(H(o()),n(),va),()=>{M(a,o()?.outcome===`complete`?Q(`completeTitle`,n()):o()?.outcome===`dry-run`?Q(`previewTitle`,n()):o()?.outcome===`cancelled`?Q(`cancelledTitle`,n()):o()?.outcome?Q(`blockedTitle`,n()):o()?.prompt?.kind===`question`?Q(`choices`,n()):o()?.prompt?.kind===`plan`?Q(`plan`,n()):o()?.prompt?va(o().prompt,n())?.title??o().prompt.title:Q(`preparing`,n()))}),En(),J();var s=ba(),c=P(s),l=I(N(c)),u=F(I(l));T(c);var d=I(c,2),f=F(d,!0),p=F(I(d,2),!0);L((e,t,n)=>{G(l,` ${e??``} `),G(u,`${t??``} / 06`),G(f,B(a)),G(p,n)},[()=>(H(Ki),H(o()),n(),V(()=>Ki(o()?.journey?.current,n()).toUpperCase())),()=>(H(o()),V(()=>String(o()?.journey?.position??1).padStart(2,`0`))),()=>(H(o()),H(Q),n(),V(()=>o()?.outcome?Q(`resultLede`,n()):Q(`activeLede`,n())))]),W(e,s),O(),i()}function Sa(e){try{let t=new URL(e??``);return t.protocol===`https:`&&t.hostname===`github.com`&&(t.pathname===`/settings/personal-access-tokens`||t.pathname.startsWith(`/settings/personal-access-tokens/`))?t.toString():void 0}catch{return}}function Ca(e){try{let t=new URL(e??``);return t.protocol===`https:`&&t.hostname===`github.com`&&!t.username&&!t.password&&!t.search&&!t.hash&&/^\/[A-Za-z0-9-]{1,39}\/[A-Za-z0-9._-]{1,100}\/actions\/runs\/[1-9][0-9]*$/u.test(t.pathname)?t.toString():void 0}catch{return}}function wa(e){try{let t=new URL(e??``);return t.protocol===`https:`&&t.hostname===`github.com`&&!t.username&&!t.password&&!t.search&&!t.hash&&/^\/[A-Za-z0-9-]{1,39}\/[A-Za-z0-9._-]{1,100}\/rules\/[1-9][0-9]*$/u.test(t.pathname)?t.toString():void 0}catch{return}}function Ta(e){try{let t=new URL(e??``);return t.protocol===`https:`&&t.hostname===`github.com`&&!t.username&&!t.password&&!t.search&&!t.hash&&/^\/(?:orgs|users)\/[A-Za-z0-9-]{1,39}\/projects\/[1-9][0-9]*$/u.test(t.pathname)?t.toString():void 0}catch{return}}var Ea=U(``),Da=U(``);function Oa(e,t){let n=Z(t,`label`,8),r=Z(t,`variant`,8,`primary`),i=Z(t,`disabled`,8,!1),a=Z(t,`arrow`,8,!1),o=Z(t,`onClick`,8);var s=Da(),c=N(s,!0),l=I(c),u=e=>{W(e,Ea())};K(l,e=>{a()&&e(u)}),T(s),L(()=>{ui(s,1,si(r())),s.disabled=i(),G(c,n())}),Sr(`click`,s,function(...e){o()?.apply(this,e)}),W(e,s)}Cr([`click`]);var ka=U(` `),Aa=U(`

                                                                                              `);function ja(e,t){D(t,!1);let n=()=>Y($,`$setupLocale`,r),[r,i]=X(),a=j(),o=Z(t,`tone`,8,`info`),s=Z(t,`title`,8),c=Z(t,`message`,8),l=Z(t,`link`,8,void 0),u=Z(t,`actionLabel`,8,void 0),d=Z(t,`onAction`,8,void 0);Tn(()=>H(l()),()=>{M(a,Sa(l()))}),En(),J();var f=Aa(),p=N(f),m=F(p,!0),h=I(p),g=F(h,!0),_=I(h,2),v=e=>{var t=ka(),r=F(t,!0);L(e=>{q(t,`href`,B(a)),G(r,e)},[()=>(H(Q),n(),V(()=>Q(`githubLink`,n())))]),W(e,t)};K(_,e=>{B(a)&&e(v)});var y=I(_,2),b=e=>{Oa(e,{get label(){return u()},variant:`secondary`,get onClick(){return d()}})};K(y,e=>{u()&&d()&&e(b)}),T(f),L(()=>{ui(f,1,`banner ${o()??``}`),q(f,`role`,o()===`error`?`alert`:`status`),G(m,s()),G(g,c())}),W(e,f),O(),i()}function Ma(e){let t=String(e.question.defaultValue),n=t.split(`,`).map(e=>e.trim()).filter(Boolean),r=e.question.kind===`multi-select`?n.includes(`All`)&&e.question.choices?.includes(`All`)?[`All`]:(e.question.choices??[]).filter(e=>e!==`All`&&n.includes(e.split(` — `)[0])):e.question.kind===`scope-overrides`?n:e.question.kind===`producer-select`?t.split(`;`).map(e=>e.trim()).filter(Boolean):e.question.kind===`project-select`?n.filter(t=>e.question.projectCandidates?.some(e=>String(e.number)===t)):[],i=e.question.kind===`project-select`?n.filter(e=>!r.includes(e)).join(`,`):t;return{value:e.question.kind===`producer-select`?``:i,selected:r}}function Na(e,t){return t===`All`?e.includes(`All`)?[]:[`All`]:e.includes(t)?e.filter(e=>e!==t):[...e.filter(e=>e!==`All`),t]}function Pa(e,t,n){return e.question.kind===`scope-overrides`||e.question.kind===`multi-select`?n.length?n.join(`,`):`none`:e.question.kind===`producer-select`?[...n,...t.split(`;`).map(e=>e.trim()).filter(Boolean)].join(`;`):e.question.kind===`project-select`?[...n,...t.split(`,`).map(e=>e.trim()).filter(Boolean)].join(`,`)||`none`:t}function Fa(e){if(e)try{let t=new URL(e);if(t.protocol!==`https:`||t.username||t.password||t.search)return;if(t.hostname===`docs.page`&&t.port===``&&t.pathname.startsWith(`/vypdev/copilot/`)||t.hostname===`docs.github.com`&&t.port===``&&t.pathname.startsWith(`/en/`))return t.href}catch{return}}var Ia=U(`

                                                                                              `),La=U(`

                                                                                              `),Ra=U(` `,1);function za(e,t){D(t,!1);let n=()=>Y($,`$setupLocale`,r),[r,i]=X(),a=j(),o=Z(t,`kind`,8),s=Z(t,`status`,8),c=Z(t,`truncated`,8,!1),l={observed:`checksObserved`,"no-recent-runs":`checksNoRecent`,"no-verifiable-checks":`checksNoVerifiable`,"permission-denied":`checksDenied`,unavailable:`checksUnavailable`},u={observed:`projectsObserved`,empty:`projectsEmpty`,"permission-denied":`projectsDenied`,unavailable:`projectsUnavailable`,unsupported:`projectsUnsupported`};Tn(()=>(H(s()),H(o())),()=>{M(a,s()?o()===`checks`?l[s()]:u[s()]:void 0)}),En(),J();var d=Ra(),f=P(d),p=e=>{var t=Ia(),r=F(t,!0);L(e=>G(r,e),[()=>(H(Q),B(a),n(),V(()=>Q(B(a),n())))]),W(e,t)};K(f,e=>{B(a)&&e(p)});var m=I(f,2),h=e=>{var t=La(),r=F(t,!0);L(e=>G(r,e),[()=>(H(Q),H(o()),n(),V(()=>Q(o()===`checks`?`checksDiscoveryScope`:`projectsDiscoveryScope`,n())))]),W(e,t)};K(m,e=>{(s()===`observed`||s()===`empty`||s()===`no-recent-runs`||s()===`no-verifiable-checks`)&&e(h)});var g=I(m,2),_=e=>{var t=La(),r=F(t,!0);L(e=>G(r,e),[()=>(H(Q),n(),V(()=>Q(`discoveryTruncated`,n())))]),W(e,t)};K(g,e=>{c()&&e(_)}),W(e,d),O(),i()}var Ba={en:{success:`Passed`,failure:`Failed`,cancelled:`Cancelled`,neutral:`Neutral`,skipped:`Skipped`,timed_out:`Timed out`,action_required:`Action required`,unknown:`Unknown outcome`},es:{success:`Correcto`,failure:`Falló`,cancelled:`Cancelado`,neutral:`Neutral`,skipped:`Omitido`,timed_out:`Agotó el tiempo`,action_required:`Requiere intervención`,unknown:`Resultado desconocido`},fr:{success:`Réussi`,failure:`Échoué`,cancelled:`Annulé`,neutral:`Neutre`,skipped:`Ignoré`,timed_out:`Délai dépassé`,action_required:`Action nécessaire`,unknown:`Résultat inconnu`},pt:{success:`Concluído`,failure:`Falhou`,cancelled:`Cancelado`,neutral:`Neutro`,skipped:`Ignorado`,timed_out:`Tempo esgotado`,action_required:`Ação necessária`,unknown:`Resultado desconhecido`}};function Va(e,t){return Ba[t][e]??Ba[t].unknown}var Ha=U(` `),Ua=U(`
                                                                                              `),Wa=U(`
                                                                                            • `),Ga=U(`
                                                                                                `),Ka=U(``),qa=U(`

                                                                                                `,1);function Ja(e,t){D(t,!1);let n=()=>Y($,`$setupLocale`,r),[r,i]=X(),a=j(),o=j(),s=Z(t,`candidates`,8),c=Z(t,`selected`,12),l=Z(t,`controller`,8),u=j(``),d=j(``),f=j(``),p=j(!1);function m(){let e=B(u).trim(),t=B(f).trim(),n=Number(B(d).trim());if(M(p,!e||!t||e.length>100||t.length>100||/[|;\r\n]/u.test(e+t)||!/^[1-9]\d*$/u.test(B(d).trim())||!Number.isSafeInteger(n)||c().length>=8),B(p))return;let r=`${e}|${n}|${t}`;c().includes(r)||c([...c(),r]),M(u,``),M(d,``),M(f,``)}Tn(()=>H(s()),()=>{M(a,new Set(s().map(e=>`${e.name}|${e.sourceAppId}|${e.workflowName}`)))}),Tn(()=>(H(c()),B(a)),()=>{M(o,c().filter(e=>!B(a).has(e)))}),En(),J();var h=qa(),g=P(h);Qr(g,5,s,Jr,(e,t)=>{let r=k(()=>(B(t),V(()=>`${B(t).name}|${B(t).sourceAppId}|${B(t).workflowName}`)));var i=Ua(),a=N(i),o=N(a);xi(o);var s=I(o,2),u=N(s),d=F(N(u),!0);T(u);var f=I(u),p=F(N(f));T(f);var m=F(I(f),!0);T(s),T(a);var h=I(a,2),g=e=>{var r=Ha(),i=F(r,!0);L((e,t)=>{q(r,`href`,e),G(i,t)},[()=>(H(Ca),B(t),V(()=>Ca(B(t).runUrl))),()=>(H(Q),n(),V(()=>Q(`ciRun`,n())))]),W(e,r)},_=gt(()=>(H(Ca),B(t),V(()=>Ca(B(t).runUrl))));K(h,e=>{B(_)&&e(g)});var v=I(h,2),y=e=>{var r=Ha(),i=F(r,!0);L((e,t)=>{q(r,`href`,e),G(i,t)},[()=>(H(wa),B(t),V(()=>wa(B(t).requiredByRuleset?.sourceUrl))),()=>(H(Q),n(),V(()=>Q(`ciRule`,n())))]),W(e,r)},b=gt(()=>(H(wa),B(t),V(()=>wa(B(t).requiredByRuleset?.sourceUrl))));K(v,e=>{B(b)&&e(y)}),T(i),L((e,n,r,i,a,s)=>{Si(o,e),o.disabled=n,G(d,(B(t),V(()=>B(t).name))),G(p,`${B(t),V(()=>B(t).workflowName)??``} · ${B(t),V(()=>B(t).sourceAppName??`GitHub App`)??``} ${B(t),V(()=>B(t).sourceAppId)??``} · ${r??``} · ${i??``} · ${a??``}`),G(m,s)},[()=>(H(c()),H(B(r)),V(()=>c().includes(B(r)))),()=>(H(l()),H(c()),H(B(r)),V(()=>!l()||c().length>=8&&!c().includes(B(r)))),()=>(H(Va),B(t),n(),V(()=>Va(B(t).conclusion,n()))),()=>(B(t),V(()=>B(t).headSha.slice(0,7))),()=>(B(t),H(Q),n(),V(()=>B(t).observedAt??Q(`observationTimeUnknown`,n()))),()=>(B(t),H(Q),n(),V(()=>B(t).requiredByRuleset?Q(`branchRequirementObserved`,n(),{branch:B(t).requiredByRuleset.branch}):Q(`branchRequirementUnknown`,n())))]),Sr(`change`,o,()=>c(Na(c(),B(r)))),W(e,i)}),T(g);var _=I(g,2),v=e=>{var t=Ga();Qr(t,5,()=>B(o),Jr,(e,t)=>{var r=Wa(),i=N(r),a=F(i,!0),o=I(i,2),s=F(o,!0);T(r),L(e=>{G(a,B(t)),o.disabled=!l(),G(s,e)},[()=>(H(Q),n(),V(()=>Q(`producerRemove`,n())))]),Sr(`click`,o,()=>c(Na(c(),B(t)))),W(e,r)}),T(t),W(e,t)};K(_,e=>{B(o),V(()=>B(o).length)&&e(v)});var y=I(_,2),b=F(y,!0),x=I(y,2),S=N(x),ee=N(S,!0),te=I(ee);xi(te),T(S);var ne=I(S,2),re=N(ne,!0),ie=I(re);xi(ie),T(ne);var ae=I(ne,2),oe=N(ae,!0),se=I(oe);xi(se),T(ae),T(x);var ce=I(x,2),le=F(ce,!0),ue=I(ce,2),de=e=>{var t=Ka(),r=F(t,!0);L(e=>G(r,e),[()=>(H(Q),n(),V(()=>Q(`producerManualInvalid`,n())))]),W(e,t)};K(ue,e=>{B(p)&&e(de)}),L((e,t,n,r,i,a)=>{q(g,`aria-label`,e),G(b,t),G(ee,n),te.disabled=!l(),G(re,r),ie.disabled=!l(),G(oe,i),se.disabled=!l(),ce.disabled=!l(),G(le,a)},[()=>(H(Q),n(),V(()=>Q(`checksObserved`,n()))),()=>(H(Q),n(),V(()=>Q(`producerManualHelp`,n()))),()=>(H(Q),n(),V(()=>Q(`producerName`,n()))),()=>(H(Q),n(),V(()=>Q(`producerAppId`,n()))),()=>(H(Q),n(),V(()=>Q(`producerWorkflow`,n()))),()=>(H(Q),n(),V(()=>Q(`producerAdd`,n())))]),Ei(te,()=>B(u),e=>M(u,e)),Ei(ie,()=>B(d),e=>M(d,e)),Ei(se,()=>B(f),e=>M(f,e)),Sr(`click`,ce,m),W(e,h),O(),i()}Cr([`change`,`click`]);var Ya=U(` `),Xa=U(`
                                                                                                `),Za=U(`
                                                                                              • `),Qa=U(`

                                                                                                  `,1),$a=U(`
                                                                                                  `,1);function eo(e,t){D(t,!1);let n=()=>Y($,`$setupLocale`,r),[r,i]=X(),a=j(),o=j(),s=Z(t,`candidates`,8),c=Z(t,`selected`,12),l=Z(t,`value`,12),u=Z(t,`controller`,8);Tn(()=>H(s()),()=>{M(a,new Set(s().map(e=>String(e.number))))}),Tn(()=>(H(c()),B(a)),()=>{M(o,c().filter(e=>!B(a).has(e)))}),En(),J();var d=$a(),f=P(d);Qr(f,5,s,Jr,(e,t)=>{var r=Xa(),i=N(r),a=N(i);xi(a);var o=I(a,2),s=N(o),l=F(N(s),!0);T(s);var d=I(s),f=F(N(d));T(d),T(o),T(i);var p=I(i,2),m=e=>{var r=Ya(),i=F(r,!0);L((e,t)=>{q(r,`href`,e),G(i,t)},[()=>(H(Ta),B(t),V(()=>Ta(B(t).url))),()=>(H(Q),n(),V(()=>Q(`projectUrl`,n())))]),W(e,r)},h=gt(()=>(H(Ta),B(t),V(()=>Ta(B(t).url))));K(p,e=>{B(h)&&e(m)}),T(r),L((e,n)=>{Si(a,e),a.disabled=n,G(l,(B(t),V(()=>B(t).title))),G(f,`${B(t),V(()=>B(t).owner)??``} · #${B(t),V(()=>B(t).number)??``}`)},[()=>(H(c()),B(t),V(()=>c().includes(String(B(t).number)))),()=>(H(u()),H(c()),B(t),V(()=>!u()||c().length>=10&&!c().includes(String(B(t).number))))]),Sr(`change`,a,()=>c(Na(c(),String(B(t).number)))),W(e,r)}),T(f);var p=I(f,2),m=e=>{var t=Qa(),r=P(t),i=F(r,!0),a=I(r,2);Qr(a,5,()=>B(o),Jr,(e,t)=>{var r=Za(),i=N(r),a=F(i),o=I(i,2),s=F(o,!0);T(r),L(e=>{G(a,`#${B(t)??``}`),o.disabled=!u(),G(s,e)},[()=>(H(Q),n(),V(()=>Q(`removeSelection`,n())))]),Sr(`click`,o,()=>c(Na(c(),B(t)))),W(e,r)}),T(a),L(e=>G(i,e),[()=>(H(Q),n(),V(()=>Q(`projectSelectionNotObserved`,n())))]),W(e,t)};K(p,e=>{B(o),V(()=>B(o).length)&&e(m)});var h=I(p,2),g=F(h,!0),_=I(h,2);xi(_),L((e,t)=>{q(f,`aria-label`,e),G(g,t),_.disabled=!u()},[()=>(H(Q),n(),V(()=>Q(`projectsObserved`,n()))),()=>(H(Q),n(),V(()=>Q(`projectManual`,n())))]),Ei(_,l),W(e,d),O(),i()}Cr([`change`,`click`]);var to=U(``),no=U(`
                                                                                                  `),ro=U(` `,1);function io(e,t){D(t,!1);let n=()=>Y($,`$setupLocale`,r),[r,i]=X(),a=Z(t,`explanation`,8),o=Z(t,`helpUrl`,8);J();var s=ro(),c=P(s),l=e=>{var t=to(),r=N(t),i=F(r);T(t),L(e=>{q(r,`href`,o()),G(i,`${e??``} ↗`)},[()=>(H(Q),n(),V(()=>Q(`learnMore`,n())))]),W(e,t)};K(c,e=>{o()&&e(l)});var u=I(c,2),d=e=>{var t=no(),r=N(t),i=F(r,!0),o=I(r),s=N(o),c=F(s,!0),l=I(s),u=F(l,!0),d=I(l,2),f=F(d,!0),p=I(d),m=F(p,!0),h=I(p,2),g=F(h,!0),_=I(h),v=F(_,!0),y=I(_,2),b=F(y,!0),x=I(y),S=F(x,!0),ee=I(x,2),te=F(ee,!0),ne=I(ee),re=F(ne,!0),ie=I(ne,2),ae=F(ie,!0),oe=I(ie),se=F(oe,!0),ce=I(oe,2),le=F(ce,!0),ue=F(I(ce),!0);T(o),T(t),L((e,t,n,r,o,s,l,d)=>{G(i,e),G(c,t),G(u,(H(a()),V(()=>a().when))),G(f,n),G(m,(H(a()),V(()=>a().where))),G(g,r),G(v,(H(a()),V(()=>a().how))),G(b,o),G(S,(H(a()),V(()=>a().why))),G(te,s),G(re,(H(a()),V(()=>a().example))),G(ae,l),G(se,(H(a()),V(()=>a().effect))),G(le,d),G(ue,(H(a()),V(()=>a().verify)))},[()=>(H(Q),n(),V(()=>Q(`whyMatters`,n()))),()=>(H(Q),n(),V(()=>Q(`whenApplies`,n()))),()=>(H(Q),n(),V(()=>Q(`whereConfigured`,n()))),()=>(H(Q),n(),V(()=>Q(`howToChoose`,n()))),()=>(H(Q),n(),V(()=>Q(`whyRecommendation`,n()))),()=>(H(Q),n(),V(()=>Q(`example`,n()))),()=>(H(Q),n(),V(()=>Q(`effect`,n()))),()=>(H(Q),n(),V(()=>Q(`verify`,n())))]),W(e,t)};K(u,e=>{a()&&e(d)}),W(e,s),O(),i()}var ao=U(`· `,1),oo=U(`

                                                                                                  `,1);function so(e,t){D(t,!1);let n=()=>Y($,`$setupLocale`,r),[r,i]=X(),a=Z(t,`candidate`,8);J();var o=oo(),s=P(o),c=N(s),l=F(c),u=I(c,2),d=e=>{var t=ao(),r=I(P(t)),i=F(r,!0);L((e,t)=>{q(r,`href`,e),G(i,t)},[()=>(H(Ca),H(a()),V(()=>Ca(a().runUrl))),()=>(H(Q),n(),V(()=>Q(`ciRun`,n())))]),W(e,t)},f=gt(()=>(H(Ca),H(a()),V(()=>Ca(a().runUrl))));K(u,e=>{B(f)&&e(d)}),T(s);var p=I(s,2),m=N(p),h=F(m),g=I(m),_=I(g),v=e=>{var t=ao(),r=I(P(t)),i=F(r,!0);L((e,t)=>{q(r,`href`,e),G(i,t)},[()=>(H(wa),H(a()),V(()=>wa(a().requiredByRuleset?.sourceUrl))),()=>(H(Q),n(),V(()=>Q(`ciRule`,n())))]),W(e,t)},y=gt(()=>(H(wa),H(a()),V(()=>wa(a().requiredByRuleset?.sourceUrl))));K(_,e=>{B(y)&&e(v)}),T(p),L((e,t,n,r)=>{G(l,`${H(a()),V(()=>a().workflowName)??``} · GitHub App ${H(a()),V(()=>a().sourceAppId)??``}`),G(h,`${e??``} · ${t??``} · ${n??``}`),G(g,` · ${r??``} `)},[()=>(H(Va),H(a()),n(),V(()=>Va(a().conclusion,n()))),()=>(H(a()),V(()=>a().headSha.slice(0,7))),()=>(H(a()),H(Q),n(),V(()=>a().observedAt??Q(`observationTimeUnknown`,n()))),()=>(H(a()),H(Q),n(),V(()=>a().requiredByRuleset?Q(`branchRequirementObserved`,n(),{branch:a().requiredByRuleset.branch}):Q(`branchRequirementUnknown`,n())))]),W(e,o),O(),i()}var co=U(` `),lo=U(`

                                                                                                  `),uo=U(`

                                                                                                  `),fo=U(` `,1),po=U(`

                                                                                                  `),mo=U(`
                                                                                                  `),ho=U(`

                                                                                                  `),go=U(``),_o=U(`
                                                                                                • `),vo=U(`
                                                                                                    `),yo=U(`
                                                                                                    `),bo=U(``),xo=U(``),So=U(` `,1),Co=U(``),wo=U(`
                                                                                                    `),To=U(``),Eo=U(``),Do=U(`

                                                                                                    `,1);function Oo(e,t){D(t,!1);let n=()=>Y($,`$setupLocale`,r),[r,i]=X(),a=j(),o=j(),s=j(),c=Z(t,`prompt`,8),l=Z(t,`controller`,8),u=Z(t,`busy`,8),d=Z(t,`onSubmit`,8),f=Z(t,`onRetryDiscovery`,8),p=Z(t,`onBack`,8),m=Ma(c()),h=j(m.value),g=j(m.selected),_={issueCreated:`projectTransitionIssueCreated`,pullRequestCreated:`projectTransitionPullRequestCreated`,issueInProgress:`projectTransitionIssueInProgress`,pullRequestInProgress:`projectTransitionPullRequestInProgress`};Tn(()=>(H(c()),n()),()=>{M(a,c().presentation?.[n()])}),Tn(()=>B(a),()=>{M(o,Fa(B(a)?.documentation?.url))}),Tn(()=>(H(c()),n(),pa),()=>{M(s,typeof c().question.defaultValue==`boolean`?Q(c().question.defaultValue?`yes`:`no`,n()):c().question.defaultValue===``?Q(`none`,n()):c().question.kind===`choice`||c().question.kind===`multi-select`?pa(c().question.id,String(c().question.defaultValue),n()):String(c().question.defaultValue))}),En(),J();var v=Do(),y=P(v),b=N(y),x=F(b,!0),S=I(b),ee=e=>{var t=co(),r=F(t,!0);L(e=>G(r,e),[()=>(H(Q),n(),V(()=>Q(`permissionPreview`,n())))]),W(e,t)};K(S,e=>{H(c()),V(()=>c().phase===`permission-intent`)&&e(ee)}),T(y);var te=I(y,2),ne=e=>{var t=lo(),r=F(t,!0);L(e=>G(r,e),[()=>(H(Q),n(),H(c()),V(()=>Q(`questionProgress`,n(),{current:String(c().progress.groupPosition),total:String(c().progress.groupTotal),overall:String(c().progress.position),all:String(c().progress.total)})))]),W(e,t)};K(te,e=>{H(c()),V(()=>c().progress)&&e(ne)});var re=I(te,2),ie=e=>{var t=uo(),n=F(t,!0);L(()=>G(n,(B(a),V(()=>B(a).summary)))),W(e,t)};K(re,e=>{B(a)&&e(ie)});var ae=I(re,2),oe=e=>{za(e,{kind:`checks`,get status(){return H(c()),V(()=>c().question.discoveryStatus)},get truncated(){return H(c()),V(()=>c().question.discoveryTruncated)}})};K(ae,e=>{H(c()),V(()=>c().question.id===`pullRequestApproval.testChecks`)&&e(oe)});var se=I(ae,2),ce=e=>{za(e,{kind:`projects`,get status(){return H(c()),V(()=>c().question.discoveryStatus)},get truncated(){return H(c()),V(()=>c().question.discoveryTruncated)}})};K(se,e=>{H(c()),V(()=>c().question.id===`projects.ids`)&&e(ce)});var le=I(se,2),ue=e=>{var t=mo(),r=N(t),i=e=>{var t=fo(),r=P(t),i=F(r,!0),a=F(I(r,2),!0);L((e,t)=>{r.disabled=!l()||u(),G(i,e),G(a,t)},[()=>(H(Q),n(),V(()=>Q(`retryDiscovery`,n()))),()=>(H(Q),n(),H(c()),V(()=>Q(`retryRemaining`,n(),{count:String(c().question.discoveryRetryRemaining)})))]),Sr(`click`,r,function(...e){f()?.apply(this,e)}),W(e,t)},a=e=>{var t=po(),r=F(t,!0);L(e=>G(r,e),[()=>(H(Q),n(),V(()=>Q(`retryExhausted`,n())))]),W(e,t)};K(r,e=>{H(c()),V(()=>c().question.discoveryRetryRemaining>0)?e(i):e(a,-1)}),T(t),W(e,t)};K(le,e=>{H(c()),V(()=>c().question.discoveryRetryRemaining!==void 0)&&e(ue)});var de=I(le,2),fe=e=>{var t=ho(),r=F(t,!0);L(e=>G(r,e),[()=>(H(Q),n(),V(()=>Q(`projectStatusUnavailable`,n())))]),W(e,t)};K(de,e=>{H(c()),V(()=>c().question.statusOptionState===`unavailable`)&&e(fe)});var pe=I(de,2),me=e=>{var t=go(),r=F(t,!0);L(e=>G(r,e),[()=>(H(Q),n(),V(()=>Q(`projectStatusIncompatible`,n())))]),W(e,t)};K(pe,e=>{H(c()),V(()=>c().question.statusOptionState===`incompatible`)&&e(me)});var he=I(pe,2),ge=e=>{var t=po(),r=F(t,!0);L(e=>G(r,e),[()=>(H(Q),n(),V(()=>Q(`projectSharedStatus`,n())))]),W(e,t)};K(he,e=>{H(c()),V(()=>c().question.id===`projects.ids`)&&e(ge)});var _e=I(he,2),ve=e=>{var t=vo();Qr(t,5,()=>(H(c()),V(()=>c().question.projectStatusValues)),Jr,(e,t)=>{var r=_o(),i=N(r),a=I(i),o=F(N(a),!0);T(a),T(r),L(e=>{G(i,`${e??``}: `),G(o,(B(t),V(()=>B(t).value)))},[()=>(H(Q),B(t),n(),V(()=>Q(_[B(t).transition],n())))]),W(e,r)}),T(t),W(e,t)};K(_e,e=>{H(c()),V(()=>c().question.projectStatusValues)&&e(ve)});var ye=I(_e,2),be=e=>{var t=yo(),r=N(t);let i;var o=F(r,!0),s=I(r);let u;var d=F(s,!0);T(t),L((e,n)=>{q(t,`aria-label`,(B(a),H(c()),V(()=>B(a)?.label??c().question.label))),q(r,`aria-pressed`,B(h)===`yes`||B(h)===`true`),r.disabled=!l(),i=ui(r,1,``,null,i,{selected:B(h)===`yes`||B(h)===`true`}),G(o,e),q(s,`aria-pressed`,B(h)===`no`||B(h)===`false`),s.disabled=!l(),u=ui(s,1,``,null,u,{selected:B(h)===`no`||B(h)===`false`}),G(d,n)},[()=>(H(Q),n(),V(()=>Q(`yes`,n()))),()=>(H(Q),n(),V(()=>Q(`no`,n())))]),Sr(`click`,r,()=>M(h,`yes`)),Sr(`click`,s,()=>M(h,`no`)),W(e,t)},xe=e=>{var t=So(),r=P(t),i=N(r),o=e=>{var t=bo(),r=F(t,!0);t.value=t.__value=``,L(e=>G(r,e),[()=>(H(Q),n(),V(()=>Q(`selectOne`,n())))]),W(e,t)};K(i,e=>{H(c()),V(()=>!c().question.defaultValue)&&e(o)}),Qr(I(i),1,()=>(H(c()),V(()=>c().question.choices??[])),Jr,(e,t)=>{let r=k(()=>(H(c()),B(t),V(()=>c().question.id===`pullRequestApproval.coverage.checkName`?c().question.trustedProducers?.find(e=>e.name===B(t)):void 0)));var i=xo(),a=F(i,!0),o={};L(e=>{G(a,e),o!==(o=B(t))&&(i.value=(i.__value=o)??``)},[()=>(H(B(r)),B(t),H(pa),H(c()),n(),V(()=>B(r)?`${B(t)} — ${B(r).workflowName} · App ${B(r).sourceAppId}`:pa(c().question.id,B(t),n())))]),W(e,i)}),T(r),mi(r);var s=I(r,2),u=e=>{var t=Mr();Qr(P(t),1,()=>(H(c()),B(h),V(()=>(c().question.producerCandidates??[]).filter(e=>e.name===B(h)))),Jr,(e,t)=>{so(e,{get candidate(){return B(t)}})}),W(e,t)};K(s,e=>{H(c()),V(()=>c().question.id===`pullRequestApproval.coverage.checkName`)&&e(u)}),L(()=>{q(r,`aria-label`,(B(a),H(c()),V(()=>B(a)?.label??c().question.label))),r.disabled=!l()}),hi(r,()=>B(h),e=>M(h,e)),W(e,t)},Se=e=>{{let t=k(()=>(H(c()),V(()=>c().question.producerCandidates??[])));Ja(e,{get candidates(){return B(t)},get controller(){return l()},get selected(){return B(g)},set selected(e){M(g,e)},$$legacy:!0})}},Ce=e=>{{let t=k(()=>(H(c()),V(()=>c().question.projectCandidates??[])));eo(e,{get candidates(){return B(t)},get controller(){return l()},get selected(){return B(g)},set selected(e){M(g,e)},get value(){return B(h)},set value(e){M(h,e)},$$legacy:!0})}},we=e=>{var t=wo();Qr(t,5,()=>(H(c()),V(()=>c().question.kind===`multi-select`?c().question.choices??[]:c().question.allowedNames??[])),Jr,(e,t)=>{var r=Co(),i=N(r);xi(i);var a=F(I(i),!0);T(r),L((e,t)=>{Si(i,e),i.disabled=!l(),G(a,t)},[()=>(B(g),B(t),V(()=>B(g).includes(B(t)))),()=>(H(c()),H(pa),B(t),n(),V(()=>c().question.kind===`multi-select`?pa(c().question.id,B(t),n()):B(t)))]),Sr(`change`,i,()=>M(g,Na(B(g),B(t)))),W(e,r)}),T(t),L(()=>q(t,`aria-label`,(B(a),H(c()),V(()=>B(a)?.label??c().question.label)))),W(e,t)},Te=e=>{var t=Mr(),n=P(t),r=e=>{var t=To();it(t),L(()=>{q(t,`aria-label`,(B(a),H(c()),V(()=>B(a)?.label??c().question.label))),t.disabled=!l()}),Ei(t,()=>B(h),e=>M(h,e)),W(e,t)},i=e=>{var t=Eo();xi(t),L(()=>{q(t,`aria-label`,(B(a),H(c()),V(()=>B(a)?.label??c().question.label))),q(t,`type`,(H(c()),V(()=>c().question.kind===`number`?`number`:`text`))),q(t,`min`,(H(c()),V(()=>c().question.kind===`number`?0:void 0))),t.disabled=!l()}),Ei(t,()=>B(h),e=>M(h,e)),W(e,t)};K(n,e=>{H(c()),V(()=>c().question.id===`ai.bugbotOrganizationRules`)?e(r):e(i,-1)}),W(e,t)};K(ye,e=>{H(c()),V(()=>c().question.kind===`boolean`)?e(be):(H(c()),V(()=>c().question.kind===`choice`)?e(xe,1):(H(c()),V(()=>c().question.kind===`producer-select`)?e(Se,2):(H(c()),V(()=>c().question.kind===`project-select`)?e(Ce,3):(H(c()),V(()=>c().question.kind===`multi-select`||c().question.kind===`scope-overrides`)?e(we,4):e(Te,-1)))))});var C=I(ye,2),Ee=F(C,!0),w=I(C,2),De=e=>{var t=lo(),r=F(t,!0);L(e=>G(r,e),[()=>(H(Q),H(c()),n(),V(()=>Q(c().question.suggestionSource===`github`?`sourceGithub`:c().question.suggestionSource===`local`?`sourceLocal`:c().question.suggestionSource===`configuration`?`sourceConfig`:`sourceDefault`,n())))]),W(e,t)};K(w,e=>{H(c()),V(()=>c().question.suggestionSource)&&e(De)});var Oe=I(w,2);io(Oe,{get explanation(){return B(a)},get helpUrl(){return B(o)}});var ke=I(Oe,2),Ae=N(ke),je=e=>{{let t=k(()=>(H(Q),n(),V(()=>Q(`previousQuestion`,n())))),r=k(()=>!l()||u());Oa(e,{get label(){return B(t)},variant:`secondary`,get onClick(){return p()},get disabled(){return B(r)}})}};K(Ae,e=>{H(c()),V(()=>c().canGoBack)&&e(je)});var Me=I(Ae,2);{let e=k(()=>(H(Q),n(),V(()=>Q(`continue`,n())))),t=k(()=>!l()||u());Oa(Me,{get label(){return B(e)},arrow:!0,onClick:()=>d()(Pa(c(),B(h),B(g))),get disabled(){return B(t)}})}T(ke),L((e,t)=>{G(x,e),G(Ee,t)},[()=>(B(a),H(c()),V(()=>B(a)?.label??c().question.label.replace(` (Space toggles, Enter confirms)`,``))),()=>(H(Q),n(),B(s),V(()=>Q(`suggested`,n(),{answer:B(s)})))]),W(e,v),O(),i()}Cr([`click`,`change`]);var ko=U(``),Ao=U(`
                                                                                                    `);function jo(e,t){D(t,!1);let n=()=>Y($,`$setupLocale`,r),[r,i]=X(),a=Z(t,`prompt`,8),o=Z(t,`controller`,8),s=Z(t,`busy`,8),c=Z(t,`onSubmit`,8);J();var l=Ao();Qr(l,5,()=>(H(a()),V(()=>a().choices)),Jr,(e,t,r)=>{var i=ko(),l=F(N(i),!0);ke(),T(i),L(e=>{i.disabled=!o()||s(),G(l,e)},[()=>(H(ya),H(a()),n(),V(()=>ya(a(),n(),r)))]),Sr(`click`,i,()=>c()(B(t))),W(e,i)}),T(l),W(e,l),O(),i()}Cr([`click`]);var Mo=U(`

                                                                                                    `,1),No=U(`

                                                                                                    `),Po=U(` `,1);function Fo(e,t){D(t,!1);let n=()=>Y($,`$setupLocale`,r),[r,i]=X(),a=j(),o=Z(t,`prompt`,8),s=Z(t,`controller`,8),c=Z(t,`busy`,8),l=Z(t,`onSubmit`,8),u=j(``);function d(){let e=B(u);o().kind===`secret`&&M(u,``),l()(e)}Tn(()=>H(o()),()=>{M(a,Sa(o().link))}),En(),J();var f=Po(),p=P(f),m=e=>{var t=Mo(),r=P(t),i=N(r);ke(),T(r);var o=F(I(r),!0);L((e,t)=>{q(r,`href`,B(a)),G(i,`${e??``} `),G(o,t)},[()=>(H(Q),n(),V(()=>Q(`githubForm`,n()))),()=>(H(Q),n(),V(()=>Q(`githubFormHelp`,n())))]),W(e,t)};K(p,e=>{B(a)&&e(m)});var h=I(p,2),g=F(h,!0),_=I(h,2);xi(_);var v=I(_,2),y=e=>{var t=No(),r=F(t,!0);L(e=>G(r,e),[()=>(H(Q),n(),V(()=>Q(`secretHelp`,n())))]),W(e,t)};K(v,e=>{H(o()),V(()=>o().kind===`secret`)&&e(y)});var b=I(v,2);{let e=k(()=>(H(Q),n(),V(()=>Q(`continue`,n())))),t=k(()=>(H(s()),H(c()),H(o()),B(u),V(()=>!s()||c()||!o().optional&&!B(u).trim())));Oa(b,{get label(){return B(e)},arrow:!0,onClick:d,get disabled(){return B(t)}})}L((e,t)=>{G(g,e),q(_,`type`,(H(o()),V(()=>o().kind===`secret`?`password`:`text`))),_.disabled=!s(),q(_,`placeholder`,t)},[()=>(H(Q),H(o()),n(),V(()=>Q(o().kind===`secret`?`pasteHere`:`yourAnswer`,n()))),()=>(H(Q),H(o()),n(),V(()=>Q(o().kind===`secret`?`hiddenAfter`:`typeAnswer`,n())))]),Ei(_,()=>B(u),e=>M(u,e)),W(e,f),O(),i()}var Io={"No issue workflow kind is enabled; issue events will remain unmanaged until a supported Issue Form and profile entry are enabled.":`No issue workflow kind is enabled; issue events will remain unmanaged until a supported Issue Form and profile entry are enabled.`,"Help / question issues remain branchless even when issue-managed-branches is enabled.":`Help / question issues remain branchless even when issue-managed-branches is enabled.`,"Release automation is installed, but release issue events are disabled by the selected issue workflow profile.":`Release automation is installed, but release issue events are disabled by the selected issue workflow profile.`,"Hotfix automation is installed, but hotfix issue events are disabled by the selected issue workflow profile.":`Hotfix automation is installed, but hotfix issue events are disabled by the selected issue workflow profile.`,"Repository agent guidance generation is disabled; collaborators will not receive the generated profile or workflow skill.":`Repository agent guidance generation is disabled; collaborators will not receive the generated profile or workflow skill.`,"Release and hotfix workflows require the workflow PAT Secret and a writable token.":`Release and hotfix workflows require the workflow PAT Secret and a writable token.`,"Merge queue mode fails closed unless every required producer is verified automatically or covered by an exact reviewed attestation.":`Merge queue mode fails closed unless every required producer is verified automatically or covered by an exact reviewed attestation.`,"Always-provision mode reinstalls only default Codex/OpenCode runtimes from pinned manifest packages; explicit executables are never replaced and Cursor must be preinstalled.":`Always-provision mode reinstalls only default Codex/OpenCode runtimes from pinned manifest packages; explicit executables are never replaced and Cursor must be preinstalled.`,"Inactive issue closure is enabled; waiting issues are closed after the configured inactivity threshold and can be reopened with a new comment.":`Inactive issue closure is enabled; waiting issues are closed after the configured inactivity threshold and can be reopened with a new comment.`,"Selected Project numbers must be accessible to the bot PAT, and all four configured Status values must exist in every selected Project.":`Selected Project numbers must be accessible to the bot PAT, and all four configured Status values must exist in every selected Project.`,"Cursor is an experimental runtime in Copilot and requires a compatible preinstalled CLI plus CURSOR_API_KEY; Copilot has no automatic Cursor installer.":`Cursor is an experimental runtime in Copilot and requires a compatible preinstalled CLI plus CURSOR_API_KEY; Copilot has no automatic Cursor installer.`,"Organization-level Secrets and Variables require organization permissions; selected access is the safest default and repository values take precedence.":`Organization-level Secrets and Variables require organization permissions; selected access is the safest default and repository values take precedence.`},Lo={en:Io,es:{"No issue workflow kind is enabled; issue events will remain unmanaged until a supported Issue Form and profile entry are enabled.":`No has activado ningún tipo de flujo de incidencias. Sus eventos no se gestionarán hasta que actives un formulario de incidencia y su entrada en el perfil.`,"Help / question issues remain branchless even when issue-managed-branches is enabled.":`Las incidencias de ayuda y consulta no crean ramas, aunque actives las ramas gestionadas por incidencias.`,"Release automation is installed, but release issue events are disabled by the selected issue workflow profile.":`Se instalará la automatización de versiones, pero el perfil elegido desactiva los eventos de incidencias de versiones.`,"Hotfix automation is installed, but hotfix issue events are disabled by the selected issue workflow profile.":`Se instalará la automatización de correcciones urgentes, pero el perfil elegido desactiva los eventos de sus incidencias.`,"Repository agent guidance generation is disabled; collaborators will not receive the generated profile or workflow skill.":`Has desactivado la generación de instrucciones para agentes del repositorio. Los colaboradores no recibirán el perfil ni la guía del flujo de trabajo generados.`,"Release and hotfix workflows require the workflow PAT Secret and a writable token.":`Los flujos de versiones y correcciones urgentes requieren el Secret con el PAT de la Action y un token con permisos de escritura.`,"Merge queue mode fails closed unless every required producer is verified automatically or covered by an exact reviewed attestation.":`La cola de merge bloqueará la operación si cada productor obligatorio no se verifica automáticamente o no dispone de una certificación exacta revisada.`,"Always-provision mode reinstalls only default Codex/OpenCode runtimes from pinned manifest packages; explicit executables are never replaced and Cursor must be preinstalled.":`El modo de provisión permanente solo reinstala los entornos predeterminados de Codex y OpenCode desde paquetes fijados en el manifiesto. No sustituye ejecutables explícitos; Cursor debe estar instalado previamente.`,"Inactive issue closure is enabled; waiting issues are closed after the configured inactivity threshold and can be reopened with a new comment.":`Está activado el cierre de incidencias inactivas. Las que estén en espera se cerrarán tras el plazo configurado y podrán reabrirse con un comentario.`,"Selected Project numbers must be accessible to the bot PAT, and all four configured Status values must exist in every selected Project.":`El PAT del bot debe tener acceso a los Projects seleccionados, y los cuatro valores de Status configurados deben existir en cada uno de ellos.`,"Cursor is an experimental runtime in Copilot and requires a compatible preinstalled CLI plus CURSOR_API_KEY; Copilot has no automatic Cursor installer.":`Cursor es un entorno experimental en Copilot. Requiere una CLI compatible ya instalada y CURSOR_API_KEY; Copilot no instala Cursor automáticamente.`,"Organization-level Secrets and Variables require organization permissions; selected access is the safest default and repository values take precedence.":`Los Secrets y Variables de organización requieren permisos en ella. Limitar el acceso a los repositorios elegidos es la opción más segura; los valores del repositorio tienen prioridad.`},fr:{"No issue workflow kind is enabled; issue events will remain unmanaged until a supported Issue Form and profile entry are enabled.":`Aucun type de flux de travail pour les tickets n’est activé. Leurs événements ne seront pas gérés tant qu’un formulaire de ticket pris en charge et son entrée de profil ne seront pas activés.`,"Help / question issues remain branchless even when issue-managed-branches is enabled.":`Les tickets d’aide ou de question ne créent pas de branche, même lorsque les branches gérées par les tickets sont activées.`,"Release automation is installed, but release issue events are disabled by the selected issue workflow profile.":`L’automatisation des versions sera installée, mais le profil choisi désactive les événements des tickets de version.`,"Hotfix automation is installed, but hotfix issue events are disabled by the selected issue workflow profile.":`L’automatisation des correctifs urgents sera installée, mais le profil choisi désactive les événements des tickets correspondants.`,"Repository agent guidance generation is disabled; collaborators will not receive the generated profile or workflow skill.":`La génération des consignes pour les agents du dépôt est désactivée. Les collaborateurs ne recevront ni le profil ni le guide de flux de travail générés.`,"Release and hotfix workflows require the workflow PAT Secret and a writable token.":`Les flux de version et de correctif urgent nécessitent le Secret contenant le PAT de l’Action et un jeton autorisé à écrire.`,"Merge queue mode fails closed unless every required producer is verified automatically or covered by an exact reviewed attestation.":`La file de fusion bloque l’opération si chaque producteur requis n’est pas vérifié automatiquement ou couvert par une attestation exacte et examinée.`,"Always-provision mode reinstalls only default Codex/OpenCode runtimes from pinned manifest packages; explicit executables are never replaced and Cursor must be preinstalled.":`Le mode de provisionnement permanent ne réinstalle que les environnements Codex et OpenCode par défaut depuis les paquets verrouillés du manifeste. Il ne remplace jamais les exécutables indiqués explicitement ; Cursor doit être préinstallé.`,"Inactive issue closure is enabled; waiting issues are closed after the configured inactivity threshold and can be reopened with a new comment.":`La fermeture des tickets inactifs est activée. Les tickets en attente seront fermés après le délai configuré et pourront être rouverts par un nouveau commentaire.`,"Selected Project numbers must be accessible to the bot PAT, and all four configured Status values must exist in every selected Project.":`Le PAT du bot doit avoir accès aux Projects sélectionnés, et les quatre valeurs Status configurées doivent exister dans chacun de ces Projects.`,"Cursor is an experimental runtime in Copilot and requires a compatible preinstalled CLI plus CURSOR_API_KEY; Copilot has no automatic Cursor installer.":`Cursor est un environnement expérimental dans Copilot. Il nécessite une CLI compatible déjà installée et CURSOR_API_KEY ; Copilot ne l’installe pas automatiquement.`,"Organization-level Secrets and Variables require organization permissions; selected access is the safest default and repository values take precedence.":`Les Secrets et Variables d’organisation nécessitent des droits sur celle-ci. Limiter l’accès aux dépôts sélectionnés est le choix le plus sûr ; les valeurs du dépôt prévalent.`},pt:{"No issue workflow kind is enabled; issue events will remain unmanaged until a supported Issue Form and profile entry are enabled.":`Não está ativo nenhum tipo de fluxo de trabalho para issues. Os respetivos eventos não serão geridos até ativar um formulário de issue suportado e a entrada correspondente no perfil.`,"Help / question issues remain branchless even when issue-managed-branches is enabled.":`As issues de ajuda ou perguntas não criam ramos, mesmo com os ramos geridos por issues ativados.`,"Release automation is installed, but release issue events are disabled by the selected issue workflow profile.":`A automação de versões será instalada, mas o perfil escolhido desativa os eventos das issues de versão.`,"Hotfix automation is installed, but hotfix issue events are disabled by the selected issue workflow profile.":`A automação de correções urgentes será instalada, mas o perfil escolhido desativa os eventos das respetivas issues.`,"Repository agent guidance generation is disabled; collaborators will not receive the generated profile or workflow skill.":`A criação de orientações para agentes do repositório está desativada. Os colaboradores não receberão o perfil nem o guia do fluxo de trabalho gerados.`,"Release and hotfix workflows require the workflow PAT Secret and a writable token.":`Os fluxos de versão e correção urgente exigem o Secret com o PAT da Action e um token com permissão de escrita.`,"Merge queue mode fails closed unless every required producer is verified automatically or covered by an exact reviewed attestation.":`A fila de integração bloqueia a operação se cada produtor obrigatório não for verificado automaticamente ou coberto por uma declaração exata e revista.`,"Always-provision mode reinstalls only default Codex/OpenCode runtimes from pinned manifest packages; explicit executables are never replaced and Cursor must be preinstalled.":`O modo de aprovisionamento permanente reinstala apenas os ambientes padrão de Codex e OpenCode a partir de pacotes fixados no manifesto. Nunca substitui executáveis indicados explicitamente; o Cursor tem de estar pré-instalado.`,"Inactive issue closure is enabled; waiting issues are closed after the configured inactivity threshold and can be reopened with a new comment.":`O fecho de issues inativas está ativado. As que aguardam resposta serão fechadas após o prazo configurado e poderão ser reabertas com um novo comentário.`,"Selected Project numbers must be accessible to the bot PAT, and all four configured Status values must exist in every selected Project.":`O PAT do bot tem de ter acesso aos Projects selecionados, e os quatro valores de Status configurados têm de existir em cada Project.`,"Cursor is an experimental runtime in Copilot and requires a compatible preinstalled CLI plus CURSOR_API_KEY; Copilot has no automatic Cursor installer.":`O Cursor é um ambiente experimental no Copilot. Exige uma CLI compatível já instalada e CURSOR_API_KEY; o Copilot não instala o Cursor automaticamente.`,"Organization-level Secrets and Variables require organization permissions; selected access is the safest default and repository values take precedence.":`Os Secrets e Variables da organização exigem permissões nessa organização. Restringir o acesso aos repositórios selecionados é a opção mais segura; os valores do repositório prevalecem.`}};function Ro(e,t){let n=Lo[t]??Io;return Object.prototype.hasOwnProperty.call(n,e)?n[e]:t===`en`?e:Q(`planUnknownWarning`,t)}var zo={en:{issues:`Issues`,pullRequests:`Pull requests`,commits:`Commits`,issueComments:`Issue comments`,pullRequestComments:`Pull-request comments`,release:`Releases`,hotfix:`Hotfixes`,agentProvisioning:`Agent provisioning`,credentialHealth:`Credential health`,inactiveIssueClosure:`Inactive issue closure`,issueTemplates:`Issue templates`,pullRequestTemplate:`Pull-request template`},es:{issues:`Issues`,pullRequests:`Pull requests`,commits:`Commits`,issueComments:`Comentarios en issues`,pullRequestComments:`Comentarios en pull requests`,release:`Releases`,hotfix:`Correcciones urgentes`,agentProvisioning:`Instalación de agentes`,credentialHealth:`Estado de credenciales`,inactiveIssueClosure:`Cierre de issues inactivos`,issueTemplates:`Plantillas de issues`,pullRequestTemplate:`Plantilla de pull requests`},fr:{issues:`Tickets`,pullRequests:`Pull requests`,commits:`Commits`,issueComments:`Commentaires des tickets`,pullRequestComments:`Commentaires des pull requests`,release:`Versions`,hotfix:`Correctifs urgents`,agentProvisioning:`Installation des agents`,credentialHealth:`État des identifiants`,inactiveIssueClosure:`Fermeture des tickets inactifs`,issueTemplates:`Modèles de tickets`,pullRequestTemplate:`Modèle de pull request`},pt:{issues:`Questões`,pullRequests:`Pull requests`,commits:`Commits`,issueComments:`Comentários nas questões`,pullRequestComments:`Comentários nas pull requests`,release:`Versões`,hotfix:`Correções urgentes`,agentProvisioning:`Instalação de agentes`,credentialHealth:`Estado das credenciais`,inactiveIssueClosure:`Fecho de questões inativas`,issueTemplates:`Modelos de questões`,pullRequestTemplate:`Modelo de pull request`}};function Bo(e,t){return zo[t][e]??e}var Vo={en:{planner:`Planner`,findings:`Findings analyst`,reviewer:`Reviewer`,fixer:`Fixer`,tester:`Tester`},es:{planner:`Planificador`,findings:`Analista de hallazgos`,reviewer:`Revisor`,fixer:`Corrector`,tester:`Probador`},fr:{planner:`Planificateur`,findings:`Analyste des problèmes`,reviewer:`Réviseur`,fixer:`Correcteur`,tester:`Testeur`},pt:{planner:`Planeador`,findings:`Analista de problemas`,reviewer:`Revisor`,fixer:`Corretor`,tester:`Testador`}};function Ho(e,t){return Vo[t][e]??e}var Uo=U(`
                                                                                                  • `),Wo=U(`
                                                                                                  • `),Go=U(`
                                                                                                      `),Ko=U(`· `,1),qo=U(`
                                                                                                      `,1),Jo=U(`
                                                                                                      `,1),Yo=U(`
                                                                                                        `),Xo=U(`

                                                                                                          /

                                                                                                          `);function Zo(e,t){D(t,!1);let n=()=>Y($,`$setupLocale`,r),[r,i]=X(),a=Z(t,`decisions`,8),o={issueCreated:`projectTransitionIssueCreated`,pullRequestCreated:`projectTransitionPullRequestCreated`,issueInProgress:`projectTransitionIssueInProgress`,pullRequestInProgress:`projectTransitionPullRequestInProgress`};function s(e){return Q(e===`repository`?`scopeRepository`:e===`organization`?`scopeOrganization`:`scopeDisabled`,n())}function c(e){return Q(e===`guarded`?`approvalGuarded`:e===`recommend`?`approvalRecommend`:`approvalOff`,n())}J();var l=Xo(),u=N(l),d=F(u,!0),f=I(u,2),p=N(f),m=N(p),h=F(m,!0),g=F(I(m),!0);T(p);var _=I(p,2),v=N(_),y=F(v,!0),b=F(I(v),!0);T(_);var x=I(_,2),S=N(x),ee=F(S,!0),te=I(S),ne=N(te);Qr(ne,5,()=>(H(a()),V(()=>a().agentRouting)),Jr,(e,t)=>{var r=Uo(),i=N(r),a=F(I(i));T(r),L(e=>{G(i,`${e??``}: `),G(a,`${B(t),V(()=>B(t).provider)??``} · ${B(t),V(()=>B(t).modelProvider)??``}/${B(t),V(()=>B(t).model)??``}`)},[()=>(H(Ho),B(t),n(),V(()=>Ho(B(t).role,n())))]),W(e,r)}),T(ne),T(te),T(x);var re=I(x,2),ie=N(re),ae=F(ie,!0),oe=I(ie),se=N(oe),ce=F(se,!0),le=F(I(se,2),!0);T(oe),T(re);var ue=I(re,2),de=N(ue),fe=F(de,!0),pe=F(I(de),!0);T(ue);var me=I(ue,2),he=e=>{var t=Jo(),r=P(t),i=N(r),o=F(i,!0),s=I(i),c=N(s),l=e=>{var t=Go();Qr(t,5,()=>(H(a()),V(()=>a().trustedChecks)),Jr,(e,t)=>{var r=Wo(),i=N(r),a=F(i,!0),o=I(i),s=F(I(o),!0);T(r),L(e=>{G(a,(B(t),V(()=>B(t).name))),G(o,` · ${e??``} ${B(t),V(()=>B(t).sourceAppId)??``} · `),G(s,(B(t),V(()=>B(t).workflowName)))},[()=>(H(Q),n(),V(()=>Q(`producerAppId`,n())))]),W(e,r)}),T(t),W(e,t)},u=e=>{var t=jr();L(e=>G(t,e),[()=>(H(Q),n(),V(()=>Q(`none`,n())))]),W(e,t)};K(c,e=>{H(a()),V(()=>a().trustedChecks.length)?e(l):e(u,-1)}),T(s),T(r);var d=I(r,2),f=N(d),p=F(f,!0),m=F(I(f),!0);T(d);var h=I(d,2),g=N(h),_=F(g,!0),v=I(g),y=N(v,!0),b=I(y),x=e=>{var t=Ko(),n=F(I(P(t)),!0);L(()=>G(n,(H(a()),V(()=>a().coverageCheck)))),W(e,t)};K(b,e=>{H(a()),V(()=>a().coverageCheck)&&e(x)}),T(v),T(h);var S=I(h,2),ee=e=>{var t=qo(),r=P(t),i=N(r),o=F(i,!0),s=F(I(i),!0);T(r);var c=I(r,2),l=N(c),u=F(l,!0),d=I(l),f=F(N(d),!0);T(d),T(c);var p=I(c,2),m=N(p),h=F(m,!0),g=F(I(m),!0);T(p),L((e,t,n,r,i,a)=>{G(o,e),G(s,t),G(u,n),G(f,r),G(h,i),G(g,a)},[()=>(H(Q),n(),V(()=>Q(`planCoverageThreshold`,n()))),()=>(H(a()),H(Q),n(),V(()=>a().coverageMinimum===void 0?Q(`none`,n()):`${a().coverageMinimum}%`)),()=>(H(Q),n(),V(()=>Q(`planCoverageReporter`,n()))),()=>(H(a()),H(Q),n(),V(()=>a().coverageArtifactWorkflow||Q(`none`,n()))),()=>(H(Q),n(),V(()=>Q(`planReporterAttested`,n()))),()=>(H(Q),H(a()),n(),V(()=>Q(a().coverageReporterAttested?`yes`:`no`,n())))]),W(e,t)};K(S,e=>{H(a()),V(()=>a().coverageMode===`numeric`)&&e(ee)}),L((e,t,n,r,i)=>{G(o,e),G(p,t),G(m,n),G(_,r),G(y,i)},[()=>(H(Q),n(),V(()=>Q(`planTrustedChecks`,n()))),()=>(H(Q),n(),V(()=>Q(`planProducerAttested`,n()))),()=>(H(Q),H(a()),n(),V(()=>Q(a().producerAttested?`yes`:`no`,n()))),()=>(H(Q),n(),V(()=>Q(`planCoverage`,n()))),()=>(H(pa),H(a()),n(),V(()=>pa(`pullRequestApproval.coverage.mode`,a().coverageMode,n())))]),W(e,t)};K(me,e=>{H(a()),V(()=>a().approvalMode!==`off`)&&e(he)});var ge=I(me,2),_e=N(ge),ve=F(_e,!0),ye=F(I(_e),!0);T(ge);var be=I(ge,2),xe=e=>{var t=Yo(),r=N(t),i=F(r,!0),s=I(r),c=N(s);Qr(c,5,()=>(H(a()),V(()=>a().projectStatuses)),Jr,(e,t)=>{var r=Uo(),i=N(r),a=F(I(i),!0);T(r),L(e=>{G(i,`${e??``}: `),G(a,(B(t),V(()=>B(t).value)))},[()=>(H(Q),B(t),n(),V(()=>Q(o[B(t).transition],n())))]),W(e,r)}),T(c),T(s),T(t),L(e=>G(i,e),[()=>(H(Q),n(),V(()=>Q(`planProjectStatuses`,n())))]),W(e,t)};K(be,e=>{H(a()),V(()=>a().projectNumbers.length)&&e(xe)});var Se=I(be,2),Ce=N(Se),we=F(Ce,!0),Te=F(I(Ce),!0);T(Se);var C=I(Se,2),Ee=N(C),w=F(Ee,!0),De=F(I(Ee),!0);T(C);var Oe=I(C,2),ke=N(Oe),Ae=F(ke,!0),je=F(I(ke),!0);T(Oe);var Me=I(Oe,2),Ne=N(Me),Pe=F(Ne,!0),Fe=F(I(Ne),!0);T(Me),T(f);var Ie=F(I(f,2),!0);T(l),L((e,t,n,r,i,o,s,c,l,u,f,p,m,_,v,x,S,te,ne,re)=>{G(d,e),G(h,t),G(g,n),G(y,r),G(b,i),G(ee,o),G(ae,s),G(ce,(H(a()),V(()=>a().productionBranch))),G(le,(H(a()),V(()=>a().developmentBranch))),G(fe,c),G(pe,l),G(ve,u),G(ye,f),G(we,p),G(Te,m),G(w,_),G(De,v),G(Ae,x),G(je,S),G(Pe,te),G(Fe,ne),G(Ie,re)},[()=>(H(Q),n(),V(()=>Q(`planChoices`,n()))),()=>(H(Q),n(),V(()=>Q(`planEnabledCapabilities`,n()))),()=>(H(a()),H(Bo),n(),H(Q),V(()=>a().enabledCapabilities.length?a().enabledCapabilities.map(e=>Bo(e,n())).join(`, `):Q(`none`,n()))),()=>(H(Q),n(),V(()=>Q(`planIssueWorkflows`,n()))),()=>(H(a()),H(pa),n(),H(Q),V(()=>a().issueWorkflows.length?a().issueWorkflows.map(e=>pa(`issueWorkflows.enabled`,e,n())).join(`, `):Q(`none`,n()))),()=>(H(Q),n(),V(()=>Q(`planAgentRouting`,n()))),()=>(H(Q),n(),V(()=>Q(`planBranchRoles`,n()))),()=>(H(Q),n(),V(()=>Q(`planApprovalMode`,n()))),()=>(H(a()),V(()=>c(a().approvalMode))),()=>(H(Q),n(),V(()=>Q(`editProjects`,n()))),()=>(H(a()),H(Q),n(),V(()=>a().projectNumbers.length?a().projectNumbers.map(e=>`#${e}`).join(`, `):Q(`none`,n()))),()=>(H(Q),n(),V(()=>Q(`planVariableScope`,n()))),()=>(H(a()),V(()=>s(a().variableScope))),()=>(H(Q),n(),V(()=>Q(`planSecretScope`,n()))),()=>(H(a()),V(()=>s(a().secretScope))),()=>(H(Q),n(),V(()=>Q(`planIssueResources`,n()))),()=>(H(Q),n(),V(()=>Q(`planIssueResourcesValue`,n()))),()=>(H(Q),n(),V(()=>Q(`planInitialTag`,n()))),()=>(H(Q),H(a()),n(),V(()=>Q(a().initialTag?`yes`:`no`,n()))),()=>(H(Q),n(),V(()=>Q(`planAdvancedDefaults`,n())))]),W(e,l),O(),i()}var Qo=U(`
                                                                                                        • `),$o=U(`

                                                                                                            `),es=U(`
                                                                                                          • `),ts=U(`

                                                                                                              `),ns=U(`

                                                                                                                `),rs=U(``),is=U(`

                                                                                                                `),as=U(`

                                                                                                                `,1);function os(e,t){D(t,!1);let n=()=>Y($,`$setupLocale`,r),[r,i]=X(),a=j(),o=Z(t,`prompt`,8),s=Z(t,`controller`,8),c=Z(t,`busy`,8),l=Z(t,`onSubmit`,8),u={capabilities:`editCapabilities`,"agent-runtime":`editRuntimes`,"agent-model-defaults":`editModels`,"agent-role-overrides":`editRoleModels`,repository:`editRepository`,deployment:`editDeployment`,bugbot:`editBugbot`,"pull-request-approval":`editApproval`,projects:`editProjects`,provisioning:`editProvisioning`,storage:`editStorage`};Tn(()=>(n(),H(o())),()=>{M(a,[{title:Q(`files`,n()),items:o().plan.files},{title:Q(`workflows`,n()),items:o().plan.workflows},{title:Q(`variables`,n()),items:o().plan.variables},{title:Q(`secretNames`,n()),items:o().plan.secrets}])}),En(),J();var d=as(),f=P(d),p=F(f,!0),m=I(f,2);Zo(m,{get decisions(){return H(o()),V(()=>o().plan.decisions)}});var h=I(m,2),g=e=>{var t=$o(),r=N(t),i=F(r,!0),a=I(r,2);Qr(a,5,()=>(H(o()),V(()=>o().plan.presentationDefaults)),Jr,(e,t)=>{var r=Qo(),i=F(r);L(e=>G(i,`${e??``}: ${B(t),V(()=>B(t).count)??``}`),[()=>(B(t),H(Q),n(),V(()=>u[B(t).group]?Q(u[B(t).group],n()):B(t).group))]),W(e,r)}),T(a),T(t),L(e=>G(i,e),[()=>(H(Q),n(),V(()=>Q(`planBasicDefaultsIntro`,n())))]),W(e,t)};K(h,e=>{H(o()),V(()=>o().plan.presentationDefaults.length)&&e(g)});var _=I(h,2);Qr(_,5,()=>B(a),Jr,(e,t)=>{var n=ts(),r=N(n),i=N(r),a=F(I(i),!0);T(r);var o=I(r);Qr(o,5,()=>(B(t),V(()=>B(t).items)),Jr,(e,t)=>{var n=es(),r=F(N(n),!0);T(n),L(()=>G(r,B(t))),W(e,n)}),T(o),T(n),L(()=>{G(i,`${B(t),V(()=>B(t).title)??``} `),G(a,(B(t),V(()=>B(t).items.length)))}),W(e,n)}),T(_);var v=I(_,2),y=e=>{var t=ns(),r=N(t),i=F(r,!0),a=I(r);Qr(a,5,()=>(H(o()),V(()=>o().plan.warnings)),Jr,(e,t)=>{var r=Qo(),i=F(r,!0);L(e=>G(i,e),[()=>(H(Ro),B(t),n(),V(()=>Ro(B(t),n())))]),W(e,r)}),T(a),T(t),L(e=>G(i,e),[()=>(H(Q),n(),V(()=>Q(`beforeContinue`,n())))]),W(e,t)};K(v,e=>{H(o()),V(()=>o().plan.warnings.length)&&e(y)});var b=I(v,2),x=e=>{var t=is(),r=N(t),i=F(r,!0),a=I(r),d=F(a,!0),f=I(a,2);Qr(f,5,()=>(H(o()),V(()=>o().editGroups)),Jr,(e,t)=>{var r=rs(),i=F(r,!0);L(e=>{r.disabled=!s()||c(),G(i,e)},[()=>(H(Q),n(),B(t),V(()=>Q(`changeSection`,n(),{section:Q(u[B(t)],n())})))]),Sr(`click`,r,()=>l()(`revise:${B(t)}`)),W(e,r)}),T(f),T(t),L((e,t)=>{G(i,e),G(d,t)},[()=>(H(Q),n(),V(()=>Q(`changeAnswersTitle`,n()))),()=>(H(Q),n(),V(()=>Q(`changeAnswersHelp`,n())))]),W(e,t)};K(b,e=>{H(o()),V(()=>o().editGroups?.length)&&e(x)});var S=I(b,2),ee=N(S);{let e=k(()=>(H(Q),n(),V(()=>Q(`stopHere`,n())))),t=k(()=>!s()||c());Oa(ee,{get label(){return B(e)},variant:`secondary`,onClick:()=>l()(`decline`),get disabled(){return B(t)}})}var te=I(ee);{let e=k(()=>(H(Q),n(),V(()=>Q(`approvePlan`,n())))),t=k(()=>!s()||c());Oa(te,{get label(){return B(e)},arrow:!0,onClick:()=>l()(`approve`),get disabled(){return B(t)}})}T(S),L(e=>G(p,e),[()=>(H(Q),n(),V(()=>Q(`planBody`,n())))]),W(e,d),O(),i()}Cr([`click`]);function ss(e,t){let n=t,r=!0,i=()=>queueMicrotask(()=>{r&&e.isConnected&&e.focus({preventScroll:!0})});return i(),{update(e){e!==n&&(n=e,i())},destroy(){r=!1}}}var cs=U(`

                                                                                                                `),ls=U(`

                                                                                                                `),us=U(`
                                                                                                                `);function ds(e,t){D(t,!1);let n=()=>Y($,`$setupLocale`,r),[r,i]=X(),a=j(),o=Z(t,`prompt`,8),s=Z(t,`revision`,8),c=Z(t,`promptRevision`,8),l=Z(t,`controller`,8),u=Z(t,`busy`,8),d=Z(t,`onSubmit`,8),f=Z(t,`onRetryDiscovery`,8),p=Z(t,`onBack`,8);Tn(()=>(H(o()),n()),()=>{M(a,va(o(),n()))}),En(),J();var m=us(),h=N(m),g=N(h),_=F(g,!0),v=F(I(g));T(h);var y=I(h,2),b=e=>{var t=cs(),n=F(t,!0);L(()=>G(n,(B(a),H(o()),V(()=>B(a)?.title??o().title)))),W(e,t)};K(y,e=>{H(o()),V(()=>o().kind!==`question`)&&e(b)});var x=I(y,2),S=e=>{var t=ls(),n=F(t,!0);L(()=>G(n,(B(a),H(o()),V(()=>B(a)?.description??(`description`in o()?o().description:``))))),W(e,t)};K(x,e=>{B(a),H(o()),V(()=>B(a)?.description||`description`in o()&&o().description)&&e(S)}),qr(I(x,2),c,e=>{var t=Mr(),n=P(t),r=e=>{Oo(e,{get prompt(){return o()},get controller(){return l()},get busy(){return u()},get onSubmit(){return d()},get onRetryDiscovery(){return f()},get onBack(){return p()}})},i=e=>{jo(e,{get prompt(){return o()},get controller(){return l()},get busy(){return u()},get onSubmit(){return d()}})},a=e=>{Fo(e,{get prompt(){return o()},get controller(){return l()},get busy(){return u()},get onSubmit(){return d()}})},s=e=>{os(e,{get prompt(){return o()},get controller(){return l()},get busy(){return u()},get onSubmit(){return d()}})};K(n,e=>{H(o()),V(()=>o().kind===`question`)?e(r):(H(o()),V(()=>o().kind===`choice`||o().kind===`confirm`)?e(i,1):(H(o()),V(()=>o().kind===`text`||o().kind===`secret`)?e(a,2):(H(o()),V(()=>o().kind===`plan`)&&e(s,3))))}),W(e,t)}),T(m),ii(m,(e,t)=>ss?.(e,t),c),L((e,t,n)=>{q(m,`aria-label`,e),G(_,t),G(v,`${n??``} ${s()??``}`)},[()=>(H(Q),n(),V(()=>Q(`currentDecision`,n()))),()=>(H(Q),n(),V(()=>Q(`currentDecision`,n()))),()=>(H(Q),n(),V(()=>Q(`session`,n())))]),W(e,m),O(),i()}var fs={Metadata:`Metadata`,Contents:`Contents`,Secrets:`Secrets`,Variables:`Variables`,Issues:`Issues`,Actions:`Actions`,Checks:`Checks`,Administration:`Administration`,Workflows:`Workflows`,"Issue Types":`Issue Types`,Projects:`Projects`,"Pull requests":`Pull requests`,Members:`Members`},ps={repository:`repository`,organization:`organization`,read:`Read`,write:`Write`,required:`Required`,conditional:`Conditional`,verified:`Verified`,missing:`Missing`,unverifiable:`Unverifiable`},ms={Metadata:`Metadatos`,Contents:`Contenido`,Secrets:`Secretos`,Variables:`Variables`,Issues:`Incidencias`,Actions:`Acciones`,Checks:`Comprobaciones`,Administration:`Administración`,Workflows:`Flujos de trabajo`,"Issue Types":`Tipos de incidencia`,Projects:`Proyectos`,"Pull requests":`Solicitudes de cambio`,Members:`Miembros`},hs={repository:`repositorio`,organization:`organización`,read:`Lectura`,write:`Escritura`,required:`Obligatorio`,conditional:`Condicional`,verified:`Verificado`,missing:`Faltante`,unverifiable:`No verificable`},gs={Metadata:`Métadonnées`,Contents:`Contenu`,Secrets:`Secrets`,Variables:`Variables`,Issues:`Tickets`,Actions:`Actions`,Checks:`Vérifications`,Administration:`Administration`,Workflows:`Flux de travail`,"Issue Types":`Types de ticket`,Projects:`Projets`,"Pull requests":`Demandes de tirage`,Members:`Membres`},_s={repository:`dépôt`,organization:`organisation`,read:`Lecture`,write:`Écriture`,required:`Obligatoire`,conditional:`Conditionnel`,verified:`Vérifié`,missing:`Manquant`,unverifiable:`Non vérifiable`},vs={Metadata:`Metadados`,Contents:`Conteúdo`,Secrets:`Segredos`,Variables:`Variáveis`,Issues:`Questões`,Actions:`Ações`,Checks:`Verificações`,Administration:`Administração`,Workflows:`Fluxos de trabalho`,"Issue Types":`Tipos de questão`,Projects:`Projetos`,"Pull requests":`Pedidos de alteração`,Members:`Membros`},ys={repository:`repositório`,organization:`organização`,read:`Leitura`,write:`Escrita`,required:`Obrigatório`,conditional:`Condicional`,verified:`Verificado`,missing:`Em falta`,unverifiable:`Não verificável`},bs={en:fs,es:ms,fr:gs,pt:vs},xs={en:ps,es:hs,fr:_s,pt:ys};function Ss(e,t){return xs[e][t]}function Cs(e,t){return Object.prototype.hasOwnProperty.call(bs[e],t)?bs[e][t]:t}function ws(e,t){if(t===`verified`||t===`missing`||t===`unverifiable`)return xs[e][t]}var Ts=`Resolve repository identity and visibility.(Inspect installed workflows and repository files.(Inspect and provision selected GitHub Actions Secrets.(Inspect and provision selected GitHub Actions Variables.(Provision labels and issue resources.(Inspect and dispatch credential-health workflows.(Inspect CI workflow runs and jobs for exact producer identities.(Discover exact CI check and producer identities.(Inspect branch protection and rulesets.(Bootstrap a missing credential-health workflow.(Inspect and provision organization Actions Secrets.(Inspect and provision organization Actions Variables.(Provision and assign configured issue types.(Inspect selected Projects and their Status options; setup does not edit Project items.(Create the initial repository tag when no version tag exists.(Inspect and provision selected repository Actions Secrets.(Inspect and provision selected repository Actions Variables.(Provision labels for the selected issue workflows.(Dispatch credential-health checks for existing Secrets.(Inspect CI workflow runs and jobs for approval evidence.(Temporarily install credential health when its workflow is not confirmed installed.(Inspect branch protection and effective rulesets.(Inspect and provision selected organization Actions Secrets.(Inspect and provision selected organization Actions Variables.(Provision native issue types for the selected workflows.(Resolve repository and collaborator metadata.(Dispatch selected release or hotfix workflows and check previous runs.(Check previous workflow runs before executing an enabled route.(Create managed branches, edit files, or merge selected release/hotfix changes.(Manage selected issue lifecycles, comments, and progress.(Manage selected pull request workflows, reviews, or autofix.(Verify current-head required checks and producer identities.(Load the guarded approval policy.(Select or authorize organization members for enabled workflows.(Assign configured organization issue types.(Update selected organization Projects.(Load the organization-scoped approval policy.(Secret provisioning enabled(Variable provisioning enabled(Issue workflows enabled(Credential health enabled(Pull-request approval enabled(Release, hotfix, or guarded approval enabled(Temporary health workflow required(Organization Secret storage selected(Organization Variable storage selected(Issue type automation enabled(Organization Projects selected`.split(`(`),Es={"Resolve repository identity and visibility.":`Comprobar la identidad y visibilidad del repositorio.`,"Inspect installed workflows and repository files.":`Examinar los workflows instalados y los archivos del repositorio.`,"Inspect and provision selected GitHub Actions Secrets.":`Examinar y configurar los Secrets de GitHub Actions seleccionados.`,"Inspect and provision selected GitHub Actions Variables.":`Examinar y configurar las Variables de GitHub Actions seleccionadas.`,"Provision labels and issue resources.":`Crear etiquetas y recursos de issues.`,"Inspect and dispatch credential-health workflows.":`Examinar y ejecutar los workflows de comprobación de credenciales.`,"Inspect CI workflow runs and jobs for exact producer identities.":`Examinar ejecuciones y jobs de CI para identificar sus productores exactos.`,"Discover exact CI check and producer identities.":`Identificar los checks de CI y sus productores exactos.`,"Inspect branch protection and rulesets.":`Examinar la protección de ramas y sus reglas.`,"Bootstrap a missing credential-health workflow.":`Instalar provisionalmente el workflow de comprobación de credenciales que falta.`,"Inspect and provision organization Actions Secrets.":`Examinar y configurar Secrets de Actions en la organización.`,"Inspect and provision organization Actions Variables.":`Examinar y configurar Variables de Actions en la organización.`,"Provision and assign configured issue types.":`Crear y asignar los tipos de issue configurados.`,"Inspect selected Projects and their Status options; setup does not edit Project items.":`Consultar los Projects seleccionados y sus opciones de Status; el setup no modifica los elementos de los Projects.`,"Create the initial repository tag when no version tag exists.":`Crear el tag inicial si el repositorio aún no tiene ninguno de versión.`,"Inspect and provision selected repository Actions Secrets.":`Examinar y configurar los Secrets de Actions seleccionados en el repositorio.`,"Inspect and provision selected repository Actions Variables.":`Examinar y configurar las Variables de Actions seleccionadas en el repositorio.`,"Provision labels for the selected issue workflows.":`Crear las etiquetas de los flujos de issues seleccionados.`,"Dispatch credential-health checks for existing Secrets.":`Ejecutar comprobaciones de credenciales para los Secrets existentes.`,"Inspect CI workflow runs and jobs for approval evidence.":`Examinar ejecuciones y jobs de CI como prueba para la aprobación.`,"Temporarily install credential health when its workflow is not confirmed installed.":`Instalar temporalmente la comprobación de credenciales si su workflow no está confirmado.`,"Inspect branch protection and effective rulesets.":`Examinar la protección de ramas y las reglas efectivas.`,"Inspect and provision selected organization Actions Secrets.":`Examinar y configurar los Secrets de Actions seleccionados en la organización.`,"Inspect and provision selected organization Actions Variables.":`Examinar y configurar las Variables de Actions seleccionadas en la organización.`,"Provision native issue types for the selected workflows.":`Crear tipos de issue nativos para los flujos seleccionados.`,"Resolve repository and collaborator metadata.":`Consultar los metadatos del repositorio y sus colaboradores.`,"Dispatch selected release or hotfix workflows and check previous runs.":`Ejecutar los flujos de release o hotfix seleccionados y comprobar ejecuciones anteriores.`,"Check previous workflow runs before executing an enabled route.":`Comprobar ejecuciones anteriores antes de iniciar un flujo habilitado.`,"Create managed branches, edit files, or merge selected release/hotfix changes.":`Crear ramas gestionadas, editar archivos o integrar cambios de release y hotfix.`,"Manage selected issue lifecycles, comments, and progress.":`Gestionar el ciclo de vida, los comentarios y el progreso de los issues seleccionados.`,"Manage selected pull request workflows, reviews, or autofix.":`Gestionar flujos de pull requests, revisiones o correcciones automáticas.`,"Verify current-head required checks and producer identities.":`Verificar los checks obligatorios y productores del commit actual.`,"Load the guarded approval policy.":`Leer la política de aprobación protegida.`,"Select or authorize organization members for enabled workflows.":`Seleccionar o autorizar miembros de la organización para los flujos activos.`,"Assign configured organization issue types.":`Asignar los tipos de issue configurados en la organización.`,"Update selected organization Projects.":`Actualizar los Projects seleccionados de la organización.`,"Load the organization-scoped approval policy.":`Leer la política de aprobación guardada en la organización.`,"Secret provisioning enabled":`Configuración de Secrets activada`,"Variable provisioning enabled":`Configuración de Variables activada`,"Issue workflows enabled":`Flujos de issues activados`,"Credential health enabled":`Comprobación de credenciales activada`,"Pull-request approval enabled":`Aprobación de pull requests activada`,"Release, hotfix, or guarded approval enabled":`Release, hotfix o aprobación protegida activados`,"Temporary health workflow required":`Hace falta un workflow temporal de comprobación`,"Organization Secret storage selected":`Se eligió guardar Secrets en la organización`,"Organization Variable storage selected":`Se eligió guardar Variables en la organización`,"Issue type automation enabled":`Automatización de tipos de issue activada`,"Organization Projects selected":`Se seleccionaron Projects de la organización`},Ds={"Resolve repository identity and visibility.":`Vérifier l’identité et la visibilité du dépôt.`,"Inspect installed workflows and repository files.":`Examiner les workflows installés et les fichiers du dépôt.`,"Inspect and provision selected GitHub Actions Secrets.":`Examiner et configurer les Secrets GitHub Actions choisis.`,"Inspect and provision selected GitHub Actions Variables.":`Examiner et configurer les Variables GitHub Actions choisies.`,"Provision labels and issue resources.":`Créer des étiquettes et des ressources pour les tickets.`,"Inspect and dispatch credential-health workflows.":`Examiner et lancer les workflows de vérification des identifiants.`,"Inspect CI workflow runs and jobs for exact producer identities.":`Examiner les exécutions et jobs CI pour identifier exactement leurs producteurs.`,"Discover exact CI check and producer identities.":`Identifier précisément les vérifications CI et leurs producteurs.`,"Inspect branch protection and rulesets.":`Examiner la protection des branches et les ensembles de règles.`,"Bootstrap a missing credential-health workflow.":`Installer provisoirement le workflow de vérification des identifiants manquant.`,"Inspect and provision organization Actions Secrets.":`Examiner et configurer les Secrets Actions de l’organisation.`,"Inspect and provision organization Actions Variables.":`Examiner et configurer les Variables Actions de l’organisation.`,"Provision and assign configured issue types.":`Créer et attribuer les types de ticket configurés.`,"Inspect selected Projects and their Status options; setup does not edit Project items.":`Consulter les Projects sélectionnés et leurs options Status ; la configuration ne modifie aucun élément de Project.`,"Create the initial repository tag when no version tag exists.":`Créer le tag initial si le dépôt ne possède encore aucun tag de version.`,"Inspect and provision selected repository Actions Secrets.":`Examiner et configurer les Secrets Actions choisis dans le dépôt.`,"Inspect and provision selected repository Actions Variables.":`Examiner et configurer les Variables Actions choisies dans le dépôt.`,"Provision labels for the selected issue workflows.":`Créer les étiquettes des workflows de ticket choisis.`,"Dispatch credential-health checks for existing Secrets.":`Lancer des vérifications d’identifiants pour les Secrets existants.`,"Inspect CI workflow runs and jobs for approval evidence.":`Examiner les exécutions et jobs CI comme preuve pour l’approbation.`,"Temporarily install credential health when its workflow is not confirmed installed.":`Installer provisoirement la vérification des identifiants si son workflow n’est pas confirmé.`,"Inspect branch protection and effective rulesets.":`Examiner la protection des branches et les règles applicables.`,"Inspect and provision selected organization Actions Secrets.":`Examiner et configurer les Secrets Actions choisis dans l’organisation.`,"Inspect and provision selected organization Actions Variables.":`Examiner et configurer les Variables Actions choisies dans l’organisation.`,"Provision native issue types for the selected workflows.":`Créer des types de ticket natifs pour les workflows choisis.`,"Resolve repository and collaborator metadata.":`Consulter les métadonnées du dépôt et de ses collaborateurs.`,"Dispatch selected release or hotfix workflows and check previous runs.":`Lancer les workflows de version ou correctif urgent choisis et vérifier les exécutions précédentes.`,"Check previous workflow runs before executing an enabled route.":`Vérifier les exécutions précédentes avant de lancer un parcours activé.`,"Create managed branches, edit files, or merge selected release/hotfix changes.":`Créer des branches gérées, modifier des fichiers ou fusionner les changements de version et correctif.`,"Manage selected issue lifecycles, comments, and progress.":`Gérer le cycle de vie, les commentaires et la progression des tickets choisis.`,"Manage selected pull request workflows, reviews, or autofix.":`Gérer les workflows de pull request, les revues ou les corrections automatiques.`,"Verify current-head required checks and producer identities.":`Vérifier les contrôles requis et leurs producteurs pour le commit courant.`,"Load the guarded approval policy.":`Lire la politique d’approbation encadrée.`,"Select or authorize organization members for enabled workflows.":`Sélectionner ou autoriser des membres de l’organisation pour les workflows activés.`,"Assign configured organization issue types.":`Attribuer les types de ticket configurés dans l’organisation.`,"Update selected organization Projects.":`Mettre à jour les projets de l’organisation choisis.`,"Load the organization-scoped approval policy.":`Lire la politique d’approbation stockée dans l’organisation.`,"Secret provisioning enabled":`Configuration des Secrets activée`,"Variable provisioning enabled":`Configuration des Variables activée`,"Issue workflows enabled":`Workflows de ticket activés`,"Credential health enabled":`Vérification des identifiants activée`,"Pull-request approval enabled":`Approbation des pull requests activée`,"Release, hotfix, or guarded approval enabled":`Version, correctif urgent ou approbation encadrée activés`,"Temporary health workflow required":`Workflow temporaire de vérification requis`,"Organization Secret storage selected":`Stockage des Secrets dans l’organisation choisi`,"Organization Variable storage selected":`Stockage des Variables dans l’organisation choisi`,"Issue type automation enabled":`Automatisation des types de ticket activée`,"Organization Projects selected":`Projets de l’organisation choisis`},Os={"Resolve repository identity and visibility.":`Verificar a identidade e visibilidade do repositório.`,"Inspect installed workflows and repository files.":`Inspecionar os fluxos instalados e os ficheiros do repositório.`,"Inspect and provision selected GitHub Actions Secrets.":`Inspecionar e configurar os Secrets do GitHub Actions selecionados.`,"Inspect and provision selected GitHub Actions Variables.":`Inspecionar e configurar as Variables do GitHub Actions selecionadas.`,"Provision labels and issue resources.":`Criar etiquetas e recursos para questões.`,"Inspect and dispatch credential-health workflows.":`Inspecionar e executar os fluxos de verificação de credenciais.`,"Inspect CI workflow runs and jobs for exact producer identities.":`Inspecionar execuções e jobs CI para identificar exatamente os seus produtores.`,"Discover exact CI check and producer identities.":`Identificar as verificações CI e os seus produtores exatos.`,"Inspect branch protection and rulesets.":`Inspecionar a proteção de ramos e os conjuntos de regras.`,"Bootstrap a missing credential-health workflow.":`Instalar temporariamente o fluxo de verificação de credenciais em falta.`,"Inspect and provision organization Actions Secrets.":`Inspecionar e configurar os Secrets de Actions da organização.`,"Inspect and provision organization Actions Variables.":`Inspecionar e configurar as Variables de Actions da organização.`,"Provision and assign configured issue types.":`Criar e atribuir os tipos de questão configurados.`,"Inspect selected Projects and their Status options; setup does not edit Project items.":`Consultar os Projects selecionados e as suas opções de Status; a configuração não altera os itens dos Projects.`,"Create the initial repository tag when no version tag exists.":`Criar a etiqueta inicial se o repositório ainda não tiver etiquetas de versão.`,"Inspect and provision selected repository Actions Secrets.":`Inspecionar e configurar os Secrets de Actions selecionados no repositório.`,"Inspect and provision selected repository Actions Variables.":`Inspecionar e configurar as Variables de Actions selecionadas no repositório.`,"Provision labels for the selected issue workflows.":`Criar etiquetas para os fluxos de questões selecionados.`,"Dispatch credential-health checks for existing Secrets.":`Executar verificações de credenciais para os Secrets existentes.`,"Inspect CI workflow runs and jobs for approval evidence.":`Inspecionar execuções e jobs CI como prova para aprovação.`,"Temporarily install credential health when its workflow is not confirmed installed.":`Instalar temporariamente a verificação de credenciais se o fluxo não estiver confirmado.`,"Inspect branch protection and effective rulesets.":`Inspecionar a proteção de ramos e as regras aplicáveis.`,"Inspect and provision selected organization Actions Secrets.":`Inspecionar e configurar os Secrets de Actions selecionados na organização.`,"Inspect and provision selected organization Actions Variables.":`Inspecionar e configurar as Variables de Actions selecionadas na organização.`,"Provision native issue types for the selected workflows.":`Criar tipos de questão nativos para os fluxos selecionados.`,"Resolve repository and collaborator metadata.":`Consultar os metadados do repositório e dos colaboradores.`,"Dispatch selected release or hotfix workflows and check previous runs.":`Executar os fluxos de release ou hotfix selecionados e verificar execuções anteriores.`,"Check previous workflow runs before executing an enabled route.":`Verificar execuções anteriores antes de iniciar um percurso ativo.`,"Create managed branches, edit files, or merge selected release/hotfix changes.":`Criar ramos geridos, editar ficheiros ou integrar alterações de release e hotfix.`,"Manage selected issue lifecycles, comments, and progress.":`Gerir o ciclo de vida, os comentários e o progresso das questões selecionadas.`,"Manage selected pull request workflows, reviews, or autofix.":`Gerir fluxos de pull requests, revisões ou correções automáticas.`,"Verify current-head required checks and producer identities.":`Verificar as verificações obrigatórias e os produtores do commit atual.`,"Load the guarded approval policy.":`Ler a política de aprovação protegida.`,"Select or authorize organization members for enabled workflows.":`Selecionar ou autorizar membros da organização para os fluxos ativos.`,"Assign configured organization issue types.":`Atribuir os tipos de questão configurados na organização.`,"Update selected organization Projects.":`Atualizar os Projetos selecionados da organização.`,"Load the organization-scoped approval policy.":`Ler a política de aprovação guardada na organização.`,"Secret provisioning enabled":`Configuração de Secrets ativa`,"Variable provisioning enabled":`Configuração de Variables ativa`,"Issue workflows enabled":`Fluxos de questões ativos`,"Credential health enabled":`Verificação de credenciais ativa`,"Pull-request approval enabled":`Aprovação de pull requests ativa`,"Release, hotfix, or guarded approval enabled":`Release, hotfix ou aprovação protegida ativos`,"Temporary health workflow required":`É necessário um fluxo temporário de verificação`,"Organization Secret storage selected":`Armazenamento dos Secrets na organização selecionado`,"Organization Variable storage selected":`Armazenamento das Variables na organização selecionado`,"Issue type automation enabled":`Automatização de tipos de questão ativa`,"Organization Projects selected":`Projetos da organização selecionados`},ks=new Set(Ts),As={es:Es,fr:Ds,pt:Os};function js(e,t){return e===`en`?t:ks.has(t)?As[e][t]:Q(`permissionUnknown`,e)}var Ms=U(` `),Ns=U(` `),Ps=U(`
                                                                                                              • `),Fs=U(`

                                                                                                                  `),Is=U(`

                                                                                                                  `),Ls=U(``);function Rs(e,t){D(t,!1);let n=()=>Y($,`$setupLocale`,r),[r,i]=X(),a=Z(t,`view`,8);J();var o=Ls(),s=N(o),c=I(N(s)),l=F(c,!0),u=I(c),d=F(u,!0),f=I(u),p=F(N(f),!0);T(f),T(s);var m=I(s,2),h=e=>{var t=Fs(),r=I(N(t)),i=F(r),o=I(r),s=F(o,!0),c=I(o,2);Qr(c,5,()=>(H(a()),V(()=>a().permissions.report?.checks??a().permissions.requirements??[])),Jr,(e,t)=>{var r=Ps(),i=N(r),a=N(i,!0),o=I(a),s=e=>{var n=Ms(),r=F(N(n));T(n),L(()=>G(r,`GitHub · ${B(t),V(()=>B(t).permission)??``}`)),W(e,n)},c=gt(()=>(n(),H(Cs),B(t),V(()=>n()!==`en`&&Cs(n(),B(t).permission)!==B(t).permission)));K(o,e=>{B(c)&&e(s)});var l=I(o),u=F(l),d=I(l),f=F(d,!0),p=I(d),m=e=>{var r=Ns(),i=F(r,!0);L(e=>G(i,e),[()=>(H(js),n(),B(t),V(()=>js(n(),B(t).condition)))]),W(e,r)};K(p,e=>{B(t),V(()=>B(t).condition)&&e(m)}),T(i);var h=I(i),g=N(h,!0),_=I(g),v=e=>{var r=Ns(),i=F(r,!0);L(e=>G(i,e),[()=>(H(ws),n(),B(t),V(()=>ws(n(),B(t).status)))]),W(e,r)},y=gt(()=>(B(t),H(ws),n(),V(()=>`status`in B(t)&&ws(n(),B(t).status))));K(_,e=>{B(y)&&e(v)}),T(h),T(r),L((e,t,n,r,i)=>{G(a,e),G(u,`${t??``} · ${n??``}`),G(f,r),G(g,i)},[()=>(H(Cs),n(),B(t),V(()=>Cs(n(),B(t).permission))),()=>(H(Ss),n(),B(t),V(()=>Ss(n(),B(t).scope))),()=>(H(Ss),n(),B(t),V(()=>Ss(n(),B(t).applicability))),()=>(H(js),n(),B(t),V(()=>js(n(),B(t).reason))),()=>(H(Ss),n(),B(t),V(()=>Ss(n(),B(t).level)))]),W(e,r)}),T(c);var l=F(I(c,2),!0);T(t),L((e,t,n,r)=>{G(i,`${e??``} ${t??``}`),G(s,n),G(l,r)},[()=>(H(a()),H(Q),n(),V(()=>a().permissions.role===`setup`?Q(`setupPat`,n()):Q(`botPat`,n()))),()=>(H(Q),n(),V(()=>Q(`access`,n()))),()=>(H(a()),H(Q),n(),V(()=>a().permissions.report?Q(`readOnlyCheck`,n()):Q(`provisionalGrants`,n()))),()=>(H(Q),n(),V(()=>Q(`conditionalGrants`,n())))]),W(e,t)},g=e=>{var t=Is(),r=I(N(t)),i=F(r,!0),a=F(I(r),!0);T(t),L((e,t)=>{G(i,e),G(a,t)},[()=>(H(Q),n(),V(()=>Q(`permissionsFollow`,n()))),()=>(H(Q),n(),V(()=>Q(`permissionsFollowBody`,n())))]),W(e,t)};K(m,e=>{H(a()),V(()=>a().permissions)?e(h):e(g,-1)}),T(o),L((e,t,n)=>{q(o,`aria-label`,e),G(l,t),G(d,n),G(p,(H(a()),V(()=>a().repository)))},[()=>(H(Q),n(),V(()=>Q(`setup`,n()))),()=>(H(Q),n(),V(()=>Q(`repoFocus`,n()))),()=>(H(Q),n(),V(()=>Q(`repoFocusBody`,n())))]),W(e,o),O(),i()}var zs=U(`

                                                                                                                  `),Bs=U(`

                                                                                                                  `),Vs=U(`

                                                                                                                  `),Hs=U(`
                                                                                                                • `),Us=U(`

                                                                                                                    `),Ws=U(`

                                                                                                                    `),Gs=U(`
                                                                                                                    `),Ks=U(`

                                                                                                                    `);function qs(e,t){D(t,!1);let n=()=>Y($,`$setupLocale`,r),[r,i]=X(),a=j(),o=j(),s=Z(t,`outcome`,8),c=Z(t,`controller`,8),l=Z(t,`onClose`,8),u=Z(t,`onDoctor`,8,async()=>void 0),d=Z(t,`doctor`,8,void 0),f=Z(t,`detail`,8,void 0),p={permissions:[`reasonPermissions`,`nextPermissions`],storage:[`reasonStorage`,`nextStorage`],configuration:[`reasonConfiguration`,`nextConfiguration`],"session-expired":[`reasonExpired`,`nextExpired`],cancelled:[`reasonCancelled`,`nextCancelled`],provider:[`reasonProvider`,`nextProvider`],"rate-limit":[`reasonRateLimit`,`nextRateLimit`],unknown:[`reasonUnknown`,`nextUnknown`]},m={files:`receiptFiles`,secrets:`receiptSecrets`,labels:`receiptLabels`,"issue-types":`receiptIssueTypes`,variables:`receiptVariables`,"initial-tag":`receiptInitialTag`};Tn(()=>(H(s()),n()),()=>{M(a,s()===`complete`?Q(`resultApplied`,n()):s()===`dry-run`?Q(`resultNoChanges`,n()):s()===`cancelled`||s()===`blocked`?Q(`resultStopped`,n()):Q(`resultPartial`,n()))}),Tn(()=>(H(s()),n()),()=>{M(o,s()===`complete`?Q(`resultCompleteBody`,n()):s()===`partial`?Q(`resultPartialBody`,n()):Q(`resultNoChangesBody`,n()))}),En(),J();var h=Ks(),g=N(h),_=F(g,!0),v=I(g),y=F(v,!0),b=I(v,2),x=e=>{var t=Vs(),r=N(t),i=N(r),a=F(i),o=I(i);T(r);var c=I(r,2),l=e=>{var t=zs(),r=N(t),i=F(r),a=I(r);T(t),L((e,t)=>{G(i,`${e??``}:`),G(a,` ${t??``}`)},[()=>(H(Q),n(),V(()=>Q(`progress`,n()))),()=>(H(Ki),H(f()),n(),V(()=>Ki(f().stoppedStage,n())))]),W(e,t)};K(c,e=>{H(f()),V(()=>f()?.stoppedStage)&&e(l)});var u=I(c,2),d=N(u),m=F(d),h=I(d);T(u);var g=I(u,2),_=N(g),v=F(_),y=I(_);T(g);var b=I(g,2),x=e=>{var t=Bs(),r=N(t),i=F(r),a=F(I(r,2),!0);T(t),L(e=>{G(i,`${e??``}:`),G(a,(H(f()),V(()=>f().diagnosticRef)))},[()=>(H(Q),n(),V(()=>Q(`diagnosticReference`,n())))]),W(e,t)};K(b,e=>{H(f()),V(()=>f()?.diagnosticRef)&&e(x)}),T(t),L((e,t,n,r,i,s)=>{G(a,`${e??``}:`),G(o,` ${t??``}`),G(m,`${n??``}:`),G(h,` ${r??``}`),G(v,`${i??``}:`),G(y,` ${s??``}`)},[()=>(H(Q),n(),V(()=>Q(`whatHappened`,n()))),()=>(H(Q),H(f()),n(),V(()=>Q(p[f()?.reasonCode??`unknown`][0],n()))),()=>(H(Q),n(),V(()=>Q(`alreadyChanged`,n()))),()=>(H(f()),H(s()),H(Q),n(),V(()=>f()?.mutationStarted||s()===`partial`?Q(`inspectPartial`,n()):Q(`noChanges`,n()))),()=>(H(Q),n(),V(()=>Q(`nextAction`,n()))),()=>(H(s()),H(Q),n(),H(f()),V(()=>s()===`partial`?`${Q(`inspectPartial`,n())} ${Q(p[f()?.reasonCode??`unknown`][1],n())}`:Q(p[f()?.reasonCode??`unknown`][1],n())))]),W(e,t)};K(b,e=>{(s()===`blocked`||s()===`cancelled`||s()===`partial`)&&e(x)});var S=I(b,2),ee=e=>{var t=Us(),r=N(t),i=F(r,!0),a=I(r,2);Qr(a,5,()=>(H(f()),V(()=>f().effects)),Jr,(e,t)=>{var r=Hs(),i=N(r),a=F(i,!0),o=I(i),s=I(o),c=e=>{var r=jr();L(e=>G(r,`· ${e??``}`),[()=>(H(Q),B(t),n(),V(()=>Q(B(t).scope===`local`?`scopeLocal`:B(t).scope===`organization`?`scopeOrganization`:B(t).scope===`mixed`?`scopeMixed`:`scopeRepository`,n())))]),W(e,r)};K(s,e=>{B(t),V(()=>B(t).scope)&&e(c)}),T(r),L((e,t)=>{G(a,e),G(o,` — ${t??``}`)},[()=>(B(t),H(Q),n(),V(()=>m[B(t).id]?Q(m[B(t).id],n()):B(t).id)),()=>(H(Q),B(t),n(),V(()=>Q(B(t).state===`completed`?`effectCompleted`:B(t).state===`skipped`?`effectSkipped`:B(t).state===`not-started`?`effectNotStarted`:`effectInspect`,n())))]),W(e,r)}),T(a),T(t),L(e=>G(i,e),[()=>(H(Q),n(),V(()=>Q(`resourceReceipt`,n())))]),W(e,t)};K(S,e=>{H(f()),V(()=>f()?.effects?.length)&&e(ee)});var te=I(S,2),ne=F(te,!0),re=I(te,2),ie=e=>{var t=Ws(),r=F(t,!0);L(e=>G(r,e),[()=>(H(Q),n(),V(()=>Q(`botRenewal`,n())))]),W(e,t)};K(re,e=>{s()===`complete`&&e(ie)});var ae=I(re,2),oe=F(ae,!0),se=I(ae,2),ce=e=>{var t=Gs(),r=N(t),i=e=>{var t=Ws(),r=F(t,!0);L(e=>G(r,e),[()=>(H(Q),n(),V(()=>Q(`doctorRunning`,n())))]),W(e,t)},a=e=>{var t=Ws(),r=F(t);L((e,t,n)=>G(r,`${e??``} ${t??``} ${n??``}`),[()=>(H(Q),H(d()),n(),V(()=>Q(d().healthy?`doctorPassed`:`doctorWarnings`,n()))),()=>(H(Q),n(),H(d()),V(()=>Q(`doctorCounts`,n(),{pass:String(d().pass??0),warn:String(d().warn??0),fail:String(d().fail??0),skipped:String(d().skipped??0)}))),()=>(H(Q),n(),V(()=>Q(`doctorSecretLimit`,n())))]),W(e,t)},o=e=>{var t=Ws(),r=F(t,!0);L(e=>G(r,e),[()=>(H(Q),n(),V(()=>Q(`doctorFailed`,n())))]),W(e,t)};K(r,e=>{H(d()),V(()=>d()?.status===`running`)?e(i):(H(d()),V(()=>d()?.status===`complete`)?e(a,1):(H(d()),V(()=>d()?.status===`failed`)&&e(o,2)))});var s=I(r,2),l=e=>{{let t=k(()=>(H(Q),n(),V(()=>Q(`doctorRun`,n()))));Oa(e,{get label(){return B(t)},variant:`secondary`,get onClick(){return u()}})}};K(s,e=>{H(c()),H(d()),V(()=>c()&&d()?.status!==`running`&&d()?.status!==`complete`)&&e(l)}),T(t),W(e,t)};K(se,e=>{s()===`complete`&&e(ce)});var le=I(se,2),ue=F(N(le),!0);ke(),T(le);var de=I(le),fe=e=>{{let t=k(()=>(H(Q),n(),V(()=>Q(`closeSession`,n()))));Oa(e,{get label(){return B(t)},get onClick(){return l()}})}};K(de,e=>{c()&&e(fe)}),T(h),ii(h,(e,t)=>ss?.(e,t),()=>1),L((e,t)=>{G(_,s()===`complete`?`✓`:`!`),G(y,B(a)),G(ne,B(o)),G(oe,e),G(ue,t)},[()=>(H(Q),n(),V(()=>Q(`doctorHelp`,n()))),()=>(H(Q),n(),V(()=>Q(`patSettings`,n())))]),W(e,h),O(),i()}var Js=U(`

                                                                                                                    `);function Ys(e,t){D(t,!1);let n=()=>Y($,`$setupLocale`,r),[r,i]=X();J();var a=Js(),o=I(N(a)),s=F(o,!0),c=F(I(o),!0);T(a),L((e,t)=>{G(s,e),G(c,t)},[()=>Q(`working`,n()),()=>Q(`workingBody`,n())]),W(e,a),O(),i()}var Xs=U(`

                                                                                                                    `);function Zs(e,t){D(t,!1);let n=()=>Y($,`$setupLocale`,r),[r,i]=X(),a=Z(t,`busy`,8),o=Z(t,`onPair`,8),s=Z(t,`mode`,8,`pair`),c=j(``);function l(){let e=B(c);M(c,``),o()(e)}J();var u=Xs(),d=N(u),f=F(N(d),!0);T(d);var p=I(d,2),m=F(N(p),!0);T(p);var h=I(p,2),g=F(h,!0),_=I(h,2),v=N(_),y=F(v,!0),b=I(v,2);xi(b);var x=I(b,2),S=F(x,!0),ee=I(x,2);{let e=k(()=>(H(Q),H(s()),n(),V(()=>Q(s()===`pair`?`pairButton`:`takeOver`,n())))),t=k(()=>(H(a()),B(c),V(()=>a()||B(c).trim().length!==16)));Oa(ee,{get label(){return B(e)},arrow:!0,onClick:l,get disabled(){return B(t)}})}T(_),T(u),L((e,t,n,r,i,o,s)=>{q(u,`aria-label`,e),G(f,t),G(m,n),G(g,r),G(y,i),b.disabled=a(),q(b,`placeholder`,o),G(S,s)},[()=>(H(Q),H(s()),n(),V(()=>Q(s()===`pair`?`pairTitle`:`takeOver`,n()))),()=>(H(Q),n(),V(()=>Q(`privateSession`,n()))),()=>(H(Q),H(s()),n(),V(()=>Q(s()===`pair`?`pairTitle`:`takeOver`,n()))),()=>(H(Q),H(s()),n(),V(()=>Q(s()===`pair`?`pairBody`:`readOnlyBody`,n()))),()=>(H(Q),n(),V(()=>Q(`pairLabel`,n()))),()=>(H(Q),n(),V(()=>Q(`pairPlaceholder`,n()))),()=>(H(Q),n(),V(()=>Q(`pairHelp`,n())))]),xr(`submit`,_,e=>{e.preventDefault(),l()}),Ei(b,()=>B(c),e=>M(c,e)),W(e,u),O(),i()}var Qs={"The local setup session is unavailable.":`The local setup session is unavailable. Check the terminal; you may need to restart setup.`,"Connection lost. The CLI may have stopped. Check the terminal before trying again.":`Connection lost. The CLI may have stopped. Check the terminal before trying again.`,"Could not join this local session.":`Could not join this local session. Check the terminal and pair again.`,"Could not connect to the local setup session. Check the terminal and pair again.":`Could not connect to the local setup session. Check the terminal and pair again.`,"Pairing was rejected.":`Pairing was rejected. Check the code in the launching terminal.`,"Invalid local pairing response.":`The local pairing response was invalid. Restart setup from the terminal.`,"Could not pair this browser.":`Could not pair this browser. Check the terminal and try again.`,"The request was rejected.":`The local request was rejected. Refresh the page and check the terminal.`,"Could not submit this answer.":`Could not submit this answer. Refresh the page and try again.`,"Cancellation failed.":`Cancellation failed. Check whether setup has already started applying before closing.`,"Takeover failed.":`Could not take control. Re-enter the pairing code from the launching terminal.`,"Invalid local host or request context.":`The local request context was rejected. Open the exact URL printed in the terminal.`,"Invalid request origin.":`The request came from another origin. Open the exact local URL printed in the terminal.`,"JSON required.":`The local request format was invalid. Refresh the page and try again.`,"Too many pairing attempts. Restart setup.":`Too many incorrect pairing attempts. Restart setup from the terminal.`,"Incorrect pairing code. Check the terminal.":`Incorrect pairing code. Check the launching terminal.`,"Incorrect pairing code. Check the launching output.":`Incorrect pairing code. Check the launching output.`,"Pair this browser using the code printed by the CLI.":`Pair this browser using the code printed by the CLI.`,"This tab is read-only.":`This tab is read-only. Enter the pairing code again to take control.`,"Invalid answer.":`The answer was invalid. Review the current question and try again.`,"Control moved to another tab.":`Control moved to another tab. This tab is now read-only.`,"This question changed. Refresh the current state.":`This question changed. Refresh the page before answering again.`,"This setup has already started applying or ended.":`Setup has already started applying or ended. Inspect the current result before retrying.`,"Only the controller can close a finished session.":`Only the controlling tab can close this finished session.`,"Method not allowed.":`This local request is not allowed. Refresh the page.`,"Not found.":`The requested local page was not found. Open the URL printed in the terminal.`,"Invalid local request.":`The local request was invalid. Refresh the page and try again.`,"Body too large.":`The submitted answer is too large. Shorten it and try again.`,"JSON object required.":`The local request format was invalid. Refresh the page and try again.`,"Could not retry discovery.":`Could not refresh the GitHub suggestions. Retry or use verified manual entry.`,"Could not return to the previous question.":`Could not return to the previous question. Refresh the page and try again.`,"Invalid question revision.":`The question changed. Refresh the page before going back.`,"No earlier question is available here.":`There is no earlier question in this stage. Continue or return to the plan review.`,"Read-only verification failed.":`Read-only verification failed. Check the terminal and retry once.`,"Read-only verification failed. Check the terminal.":`Read-only verification failed. Check the terminal and retry once.`,"Read-only verification is unavailable or already running.":`Read-only verification is unavailable or already running. Check the current result before retrying.`},$s={en:Qs,es:{"The local setup session is unavailable.":`La sesión local no está disponible. Revisa la terminal; quizá debas reiniciar la configuración.`,"Connection lost. The CLI may have stopped. Check the terminal before trying again.":`Se perdió la conexión. Es posible que el CLI se haya detenido. Revisa la terminal antes de reintentar.`,"Could not join this local session.":`No se pudo acceder a esta sesión local. Revisa la terminal y vuelve a vincular el navegador.`,"Could not connect to the local setup session. Check the terminal and pair again.":`No se pudo conectar con la sesión local. Revisa la terminal y vuelve a vincular el navegador.`,"Pairing was rejected.":`Se rechazó la vinculación. Comprueba el código en la terminal que inició el proceso.`,"Invalid local pairing response.":`La respuesta de vinculación local no es válida. Reinicia la configuración desde la terminal.`,"Could not pair this browser.":`No se pudo vincular este navegador. Revisa la terminal y vuelve a intentarlo.`,"The request was rejected.":`Se rechazó la petición local. Actualiza la página y revisa la terminal.`,"Could not submit this answer.":`No se pudo enviar la respuesta. Actualiza la página y vuelve a intentarlo.`,"Cancellation failed.":`No se pudo cancelar. Comprueba si ya se han empezado a aplicar cambios antes de cerrar.`,"Takeover failed.":`No se pudo tomar el control. Vuelve a introducir el código de la terminal inicial.`,"Invalid local host or request context.":`Se rechazó el contexto de la petición local. Abre la URL exacta que aparece en la terminal.`,"Invalid request origin.":`La petición llegó desde otro origen. Abre la URL local exacta de la terminal.`,"JSON required.":`El formato de la petición local no es válido. Actualiza la página y reinténtalo.`,"Too many pairing attempts. Restart setup.":`Demasiados intentos de vinculación fallidos. Reinicia la configuración desde la terminal.`,"Incorrect pairing code. Check the terminal.":`Código de vinculación incorrecto. Comprueba la terminal inicial.`,"Incorrect pairing code. Check the launching output.":`Código de vinculación incorrecto. Comprueba la salida de inicio.`,"Pair this browser using the code printed by the CLI.":`Vincula este navegador con el código mostrado por el CLI.`,"This tab is read-only.":`Esta pestaña es de solo lectura. Introduce otra vez el código para tomar el control.`,"Invalid answer.":`La respuesta no es válida. Revisa la pregunta actual e inténtalo de nuevo.`,"Control moved to another tab.":`El control pasó a otra pestaña. Esta ahora es de solo lectura.`,"This question changed. Refresh the current state.":`La pregunta cambió. Actualiza la página antes de volver a responder.`,"This setup has already started applying or ended.":`La configuración ya empezó a aplicarse o terminó. Revisa el resultado antes de reintentar.`,"Only the controller can close a finished session.":`Solo la pestaña que tiene el control puede cerrar esta sesión finalizada.`,"Method not allowed.":`Esta petición local no está permitida. Actualiza la página.`,"Not found.":`No se encontró la página local solicitada. Abre la URL de la terminal.`,"Invalid local request.":`La petición local no es válida. Actualiza la página y reinténtalo.`,"Body too large.":`La respuesta enviada es demasiado larga. Acórtala y reinténtalo.`,"JSON object required.":`El formato de la petición local no es válido. Actualiza la página y reinténtalo.`,"Could not retry discovery.":`No se pudieron actualizar las sugerencias de GitHub. Reinténtalo o introduce datos verificados manualmente.`,"Could not return to the previous question.":`No se pudo volver a la pregunta anterior. Actualiza la página y reinténtalo.`,"Invalid question revision.":`La pregunta cambió. Actualiza la página antes de volver atrás.`,"No earlier question is available here.":`No hay una pregunta anterior en esta etapa. Continúa o vuelve a revisar el plan.`,"Read-only verification failed.":`Falló la comprobación de solo lectura. Revisa la terminal y vuelve a intentarlo una vez.`,"Read-only verification failed. Check the terminal.":`Falló la comprobación de solo lectura. Revisa la terminal y vuelve a intentarlo una vez.`,"Read-only verification is unavailable or already running.":`La comprobación de solo lectura no está disponible o ya se está ejecutando. Revisa el resultado antes de reintentar.`},fr:{"The local setup session is unavailable.":`La session locale est indisponible. Vérifiez le terminal ; vous devrez peut-être relancer la configuration.`,"Connection lost. The CLI may have stopped. Check the terminal before trying again.":`Connexion perdue. Le CLI s’est peut-être arrêté. Vérifiez le terminal avant de réessayer.`,"Could not join this local session.":`Impossible de rejoindre cette session locale. Vérifiez le terminal et associez de nouveau le navigateur.`,"Could not connect to the local setup session. Check the terminal and pair again.":`Impossible de se connecter à la session locale. Vérifiez le terminal et associez de nouveau le navigateur.`,"Pairing was rejected.":`L’association a été refusée. Vérifiez le code dans le terminal de lancement.`,"Invalid local pairing response.":`La réponse d’association locale est invalide. Relancez la configuration depuis le terminal.`,"Could not pair this browser.":`Impossible d’associer ce navigateur. Vérifiez le terminal et réessayez.`,"The request was rejected.":`La demande locale a été refusée. Actualisez la page et vérifiez le terminal.`,"Could not submit this answer.":`Impossible d’envoyer cette réponse. Actualisez la page et réessayez.`,"Cancellation failed.":`Annulation impossible. Vérifiez si des changements ont déjà commencé avant de fermer.`,"Takeover failed.":`Impossible de prendre le contrôle. Ressaisissez le code du terminal de lancement.`,"Invalid local host or request context.":`Le contexte de la demande locale a été refusé. Ouvrez l’URL exacte affichée dans le terminal.`,"Invalid request origin.":`La demande vient d’une autre origine. Ouvrez l’URL locale exacte du terminal.`,"JSON required.":`Le format de la demande locale est invalide. Actualisez la page et réessayez.`,"Too many pairing attempts. Restart setup.":`Trop de tentatives d’association incorrectes. Relancez la configuration depuis le terminal.`,"Incorrect pairing code. Check the terminal.":`Code d’association incorrect. Vérifiez le terminal de lancement.`,"Incorrect pairing code. Check the launching output.":`Code d’association incorrect. Vérifiez la sortie de lancement.`,"Pair this browser using the code printed by the CLI.":`Associez ce navigateur avec le code affiché par le CLI.`,"This tab is read-only.":`Cet onglet est en lecture seule. Ressaisissez le code pour prendre le contrôle.`,"Invalid answer.":`La réponse est invalide. Revoyez la question actuelle et réessayez.`,"Control moved to another tab.":`Le contrôle est passé à un autre onglet. Celui-ci est maintenant en lecture seule.`,"This question changed. Refresh the current state.":`La question a changé. Actualisez la page avant de répondre de nouveau.`,"This setup has already started applying or ended.":`La configuration est déjà en cours d’application ou terminée. Inspectez le résultat avant de réessayer.`,"Only the controller can close a finished session.":`Seul l’onglet qui détient le contrôle peut fermer cette session terminée.`,"Method not allowed.":`Cette demande locale n’est pas autorisée. Actualisez la page.`,"Not found.":`La page locale demandée est introuvable. Ouvrez l’URL affichée dans le terminal.`,"Invalid local request.":`La demande locale est invalide. Actualisez la page et réessayez.`,"Body too large.":`La réponse envoyée est trop longue. Raccourcissez-la et réessayez.`,"JSON object required.":`Le format de la demande locale est invalide. Actualisez la page et réessayez.`,"Could not retry discovery.":`Impossible d’actualiser les suggestions GitHub. Réessayez ou saisissez des données vérifiées manuellement.`,"Could not return to the previous question.":`Impossible de revenir à la question précédente. Actualisez la page et réessayez.`,"Invalid question revision.":`La question a changé. Actualisez la page avant de revenir en arrière.`,"No earlier question is available here.":`Il n’y a pas de question précédente à cette étape. Continuez ou revenez à la révision du plan.`,"Read-only verification failed.":`La vérification en lecture seule a échoué. Consultez le terminal et réessayez une fois.`,"Read-only verification failed. Check the terminal.":`La vérification en lecture seule a échoué. Consultez le terminal et réessayez une fois.`,"Read-only verification is unavailable or already running.":`La vérification en lecture seule est indisponible ou déjà en cours. Consultez le résultat avant de réessayer.`},pt:{"The local setup session is unavailable.":`A sessão local não está disponível. Verifique o terminal; poderá ter de reiniciar a configuração.`,"Connection lost. The CLI may have stopped. Check the terminal before trying again.":`Ligação perdida. O CLI pode ter parado. Verifique o terminal antes de tentar novamente.`,"Could not join this local session.":`Não foi possível entrar nesta sessão local. Verifique o terminal e volte a emparelhar o navegador.`,"Could not connect to the local setup session. Check the terminal and pair again.":`Não foi possível ligar à sessão local. Verifique o terminal e volte a emparelhar o navegador.`,"Pairing was rejected.":`O emparelhamento foi recusado. Confirme o código no terminal de lançamento.`,"Invalid local pairing response.":`A resposta de emparelhamento local é inválida. Reinicie a configuração a partir do terminal.`,"Could not pair this browser.":`Não foi possível emparelhar este navegador. Verifique o terminal e tente novamente.`,"The request was rejected.":`O pedido local foi recusado. Atualize a página e verifique o terminal.`,"Could not submit this answer.":`Não foi possível enviar a resposta. Atualize a página e tente novamente.`,"Cancellation failed.":`Não foi possível cancelar. Confirme se a aplicação das alterações já começou antes de fechar.`,"Takeover failed.":`Não foi possível assumir o controlo. Volte a introduzir o código do terminal de lançamento.`,"Invalid local host or request context.":`O contexto do pedido local foi recusado. Abra o URL exato apresentado no terminal.`,"Invalid request origin.":`O pedido veio de outra origem. Abra o URL local exato do terminal.`,"JSON required.":`O formato do pedido local é inválido. Atualize a página e tente novamente.`,"Too many pairing attempts. Restart setup.":`Demasiadas tentativas incorretas de emparelhamento. Reinicie a configuração no terminal.`,"Incorrect pairing code. Check the terminal.":`Código de emparelhamento incorreto. Verifique o terminal de lançamento.`,"Incorrect pairing code. Check the launching output.":`Código de emparelhamento incorreto. Verifique a saída de lançamento.`,"Pair this browser using the code printed by the CLI.":`Emparelhe este navegador com o código apresentado pelo CLI.`,"This tab is read-only.":`Este separador é apenas de leitura. Reintroduza o código para assumir o controlo.`,"Invalid answer.":`A resposta é inválida. Reveja a pergunta atual e tente novamente.`,"Control moved to another tab.":`O controlo passou para outro separador. Este é agora apenas de leitura.`,"This question changed. Refresh the current state.":`A pergunta mudou. Atualize a página antes de voltar a responder.`,"This setup has already started applying or ended.":`A configuração já começou a ser aplicada ou terminou. Inspecione o resultado antes de tentar novamente.`,"Only the controller can close a finished session.":`Só o separador que detém o controlo pode fechar esta sessão terminada.`,"Method not allowed.":`Este pedido local não é permitido. Atualize a página.`,"Not found.":`A página local pedida não foi encontrada. Abra o URL apresentado no terminal.`,"Invalid local request.":`O pedido local é inválido. Atualize a página e tente novamente.`,"Body too large.":`A resposta enviada é demasiado longa. Encurte-a e tente novamente.`,"JSON object required.":`O formato do pedido local é inválido. Atualize a página e tente novamente.`,"Could not retry discovery.":`Não foi possível atualizar as sugestões do GitHub. Tente novamente ou introduza dados verificados manualmente.`,"Could not return to the previous question.":`Não foi possível voltar à pergunta anterior. Atualize a página e tente novamente.`,"Invalid question revision.":`A pergunta mudou. Atualize a página antes de voltar atrás.`,"No earlier question is available here.":`Não há uma pergunta anterior nesta etapa. Continue ou volte à revisão do plano.`,"Read-only verification failed.":`A verificação só de leitura falhou. Consulte o terminal e tente mais uma vez.`,"Read-only verification failed. Check the terminal.":`A verificação só de leitura falhou. Consulte o terminal e tente mais uma vez.`,"Read-only verification is unavailable or already running.":`A verificação só de leitura não está disponível ou já está em curso. Consulte o resultado antes de tentar novamente.`}};function ec(e,t){let n=$s[t]??Qs;return Object.prototype.hasOwnProperty.call(n,e)?n[e]:Q(`unknownLocalError`,t)}var tc=U(`
                                                                                                                    `),nc=U(` `,1),rc=U(``),ic=U(`
                                                                                                                    `,1),ac=U(`
                                                                                                                    `);function oc(e,t){D(t,!1);let n=()=>Y(o,`$session`,i),r=()=>Y($,`$setupLocale`,i),[i,a]=X(),o=Li();Wr(()=>{let e=$.subscribe(e=>{document.documentElement.lang=e,document.documentElement.dir=`ltr`,document.title=`Copilot · ${Q(`studio`,e)}`}),t=window.setInterval(()=>{n().paired&&!n().view?.outcome&&o.refresh()},900);return()=>{window.clearInterval(t),e()}});async function s(){window.confirm(Q(`cancelConfirm`,r()))&&await o.cancel()}async function c(e){let t=n().view?.promptRevision;t!==void 0&&await o.submit(t,e)}async function l(){let e=n().view?.promptRevision;e!==void 0&&await o.retryDiscovery(e)}async function u(){let e=n().view?.promptRevision;e!==void 0&&await o.back(e)}J();var d=ac(),f=N(d);{let e=k(()=>n().view?.journey);Yi(f,{get journey(){return B(e)}})}var p=I(f,2),m=N(p);{let e=k(()=>n().view?.repository);na(m,{get repository(){return B(e)}})}var h=I(m,2),g=N(h),_=e=>{{let t=k(()=>Q(`translationPreviewTitle`,r())),n=k(()=>Q(`translationPreviewBody`,r()));ja(e,{tone:`warning`,get title(){return B(t)},get message(){return B(n)}})}};K(g,e=>{r()!==`en`&&e(_)});var v=I(g,2),y=e=>{xa(e,{get view(){return n().view}})};K(v,e=>{n().paired&&e(y)});var b=I(v,2),x=e=>{var t=tc(),i=I(N(t));T(t),L(e=>G(i,` ${e??``}`),[()=>Q(`reviewPass`,r(),{pass:String(n().view.journey.choiceReviewPass)})]),W(e,t)};K(b,e=>{n().view?.journey?.choiceReviewPass&&n().view.journey.choiceReviewPass>1&&!n().view.outcome&&e(x)});var S=I(b,2),ee=e=>{var t=nc(),i=P(t);{let e=k(()=>Q(`readOnly`,r())),t=k(()=>Q(`readOnlyBody`,r()));ja(i,{tone:`warning`,get title(){return B(e)},get message(){return B(t)}})}Zs(I(i,2),{mode:`takeover`,get busy(){return n().busy},get onPair(){return o.takeOver}}),W(e,t)};K(S,e=>{!n().controller&&n().view&&e(ee)});var te=I(S,2),ne=e=>{{let t=k(()=>Q(`attention`,r())),i=k(()=>ec(n().error,r()));ja(e,{tone:`error`,get title(){return B(t)},get message(){return B(i)}})}};K(te,e=>{n().error&&e(ne)});var re=I(te,2),ie=e=>{{let t=k(()=>Q(n().view.message.tone===`success`?`checked`:n().view.message.tone===`warning`?`pleaseNote`:n().view.message.tone===`error`?`attention`:`progressUpdate`,r())),i=k(()=>ha(n().view.message,r()));ja(e,{get tone(){return n().view.message.tone},get title(){return B(t)},get message(){return B(i)},get link(){return n().view.message.link}})}};K(re,e=>{n().view?.message&&!n().view.outcome&&e(ie)});var ae=I(re,2),oe=e=>{Zs(e,{get busy(){return n().busy},get onPair(){return o.pair}})},se=e=>{qs(e,{get outcome(){return n().view.outcome},get detail(){return n().view.resultDetail},get doctor(){return n().view.doctor},get controller(){return n().controller},get onDoctor(){return o.runDoctor},get onClose(){return o.close}})},ce=e=>{var t=ic(),i=P(t),a=N(i);ds(a,{get prompt(){return n().view.prompt},get revision(){return n().view.revision},get promptRevision(){return n().view.promptRevision},get controller(){return n().controller},get busy(){return n().busy},onSubmit:c,onRetryDiscovery:l,onBack:u}),Rs(I(a,2),{get view(){return n().view}}),T(i);var o=I(i,2),d=e=>{var t=rc(),i=F(t,!0);L(e=>{t.disabled=n().busy,G(i,e)},[()=>Q(`cancelSetup`,r())]),Sr(`click`,t,s),W(e,t)};K(o,e=>{n().controller&&n().view.journey?.current!==`Apply`&&e(d)}),W(e,t)},le=e=>{Ys(e,{})};K(ae,e=>{n().paired?n().view?.outcome?e(se,1):n().view?.prompt?e(ce,2):e(le,-1):e(oe)});var ue=I(ae,2),de=N(ue),fe=I(de,2),pe=I(fe,2);T(ue),T(h),T(p),T(d),L((e,t,n)=>{G(de,`${e??``} `),G(fe,` ${t??``} `),G(pe,` ${n??``}`)},[()=>Q(`footerLocal`,r()),()=>Q(`footerCloud`,r()),()=>Q(`footerGithub`,r())]),W(e,d),O(),a()}Cr([`click`]),zr(oc,{target:document.getElementById(`app`)}); \ No newline at end of file diff --git a/build/web/assets/index-CYUoGCTl.js b/build/web/assets/index-CYUoGCTl.js deleted file mode 100644 index ec9b5fbd0..000000000 --- a/build/web/assets/index-CYUoGCTl.js +++ /dev/null @@ -1,2 +0,0 @@ -(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})(),typeof window<`u`&&((window.__svelte??={}).v??=new Set).add(`5`);var e=!1;function t(){e=!0}t();var n={},r=Symbol(`uninitialized`),i=`http://www.w3.org/1999/xhtml`,a=Array.isArray,o=Array.prototype.indexOf,s=Array.prototype.includes,c=Array.from,l=Object.defineProperty,u=Object.getOwnPropertyDescriptor,d=Object.getOwnPropertyDescriptors,f=Object.prototype,p=Array.prototype,m=Object.getPrototypeOf,h=Object.isExtensible,g=()=>{};function _(e){return e()}function v(e){for(var t=0;t{e=n,t=r}),resolve:e,reject:t}}var b=1024,x=2048,S=4096,ee=8192,te=16384,ne=32768,re=1<<25,ie=65536,ae=1<<18,oe=1<<19,se=1<<20,ce=1<<25,le=1<<21,ue=1<<22,de=1<<23,fe=Symbol(`$state`),pe=Symbol(`component`),me=Symbol(`legacy props`),he=Symbol(``),ge=Symbol(`attributes`),_e=Symbol(`class`),ve=Symbol(`style`),ye=Symbol(`text`),be=Symbol(`form reset`),xe=new class extends Error{name=`StaleReactionError`;message="The reaction that called `getAbortSignal()` was re-run or destroyed"},Se=!!globalThis.document?.contentType&&globalThis.document.contentType.includes(`xml`);function Ce(){console.warn(`https://svelte.dev/e/derived_inert`)}function we(e){console.warn(`https://svelte.dev/e/hydration_mismatch`)}function Te(){console.warn(`https://svelte.dev/e/select_multiple_invalid_value`)}function Ee(){console.warn(`https://svelte.dev/e/svelte_boundary_reset_noop`)}var C=!1;function De(e){C=e}var w;function T(e){if(e===null)throw we(),n;return w=e}function Oe(){return T(an(w))}function E(e){if(C){if(an(w)!==null)throw we(),n;w=e}}function ke(e=1){if(C){for(var t=e,n=w;t--;)n=an(n);w=n}}function Ae(e=!0){for(var t=0,n=w;;){if(n.nodeType===8){var r=n.data;if(r===`]`){if(t===0)return n;--t}else(r===`[`||r===`[!`||r[0]===`[`&&!isNaN(Number(r.slice(1))))&&(t+=1)}var i=an(n);e&&n.remove(),n=i}}function je(e){if(!e||e.nodeType!==8)throw we(),n;return e.data}function Me(e){return e===this.v}function Ne(e,t){return e==e?e!==t||typeof e==`object`&&!!e||typeof e==`function`:t==t}function Pe(e){return!Ne(e,this.v)}function Fe(e){throw Error(`https://svelte.dev/e/lifecycle_outside_component`)}function Ie(){throw Error(`https://svelte.dev/e/async_derived_orphan`)}function Le(e,t,n){throw Error(`https://svelte.dev/e/each_key_duplicate`)}function Re(e){throw Error(`https://svelte.dev/e/effect_in_teardown`)}function ze(){throw Error(`https://svelte.dev/e/effect_in_unowned_derived`)}function Be(e){throw Error(`https://svelte.dev/e/effect_orphan`)}function Ve(){throw Error(`https://svelte.dev/e/effect_update_depth_exceeded`)}function He(e){throw Error(`https://svelte.dev/e/props_invalid_value`)}function Ue(){throw Error(`https://svelte.dev/e/state_descriptors_fixed`)}function We(){throw Error(`https://svelte.dev/e/state_prototype_fixed`)}function Ge(){throw Error(`https://svelte.dev/e/state_unsafe_mutation`)}function Ke(){throw Error(`https://svelte.dev/e/svelte_boundary_reset_onerror`)}var D=null;function qe(e){D=e}function O(t,n=!1,r){D={p:D,i:!1,c:null,e:null,s:t,x:null,r:H,l:e&&!n?{s:null,u:null,$:[]}:null}}function k(e){var t=D,n=t.e;if(n!==null){t.e=null;for(var r of n)yn(r)}return e!==void 0&&(t.x=e),t.i=!0,D=t.p,Je(e)}function Je(e={}){return l(e,pe,{value:!0}),e}function Ye(){return!e||D!==null&&D.l===null}var Xe=[];function Ze(){var e=Xe;Xe=[],v(e)}function Qe(e){if(Xe.length===0&&!wt){var t=Xe;queueMicrotask(()=>{t===Xe&&Ze()})}Xe.push(e)}function $e(){for(;Xe.length>0;)Ze()}var et=~(x|S|b);function A(e,t){e.f=e.f&et|t}function tt(e){e.f&512||e.deps===null?A(e,b):A(e,S)}function nt(e,t,n){e.f&2048?t.add(e):e.f&4096&&n.add(e),A(e,b)}var rt=!1;function it(){rt||(rt=!0,document.addEventListener(`reset`,e=>{Promise.resolve().then(()=>{if(!e.defaultPrevented)for(let t of e.target.elements)t[be]?.()})},{capture:!0}))}function at(e){var t=V,n=H;Un(null),Wn(null);try{return e()}finally{Un(t),Wn(n)}}function ot(e,t,n,r=n){e.addEventListener(t,()=>at(n));let i=e[be];e[be]=i?()=>{i(),r(!0)}:()=>r(!0),it()}function st(e,t,n,r){let i=Ye()?dt:mt;var a=e.filter(e=>!e.settled),o=t.map(i);if(n.length===0&&a.length===0){r(o);return}var s=H,c=ct(),l=a.length===1?a[0].promise:a.length>1?Promise.all(a.map(e=>e.promise)):null;function u(e){if(!(s.f&16384)){c();try{r([...o,...e])}catch(e){fn(e,s)}lt()}}var d=ut();if(n.length===0){l.then(()=>u([])).finally(d);return}function f(){Promise.all(n.map(e=>pt(e))).then(u).catch(e=>fn(e,s)).finally(d)}l?l.then(()=>{c(),f(),lt()}):f()}function ct(){var e=H,t=V,n=D,r=j;return function(i=!0){Wn(e),Un(t),qe(n),i&&!(e.f&16384)&&(r?.activate(),r?.apply())}}function lt(e=!0){Wn(null),Un(null),qe(null),e&&j?.deactivate()}function ut(){var e=H,t=e.b,n=j,r=!!t?.is_rendered();return t?.update_pending_count(1,n),n.increment(r,e),()=>{t?.update_pending_count(-1,n),n.decrement(r,e)}}function dt(e){var t=2|x;return H!==null&&(H.f|=oe),{ctx:D,deps:null,effects:null,equals:Me,f:t,fn:e,reactions:null,rv:0,v:r,wv:0,parent:H,ac:null}}var ft=Symbol(`obsolete`);function pt(e,t,n){let i=H;i===null&&Ie();var a=void 0,o=Vt(r),s=!V,c=new Set;return Tn(()=>{var t=H,n=y();a=n.promise;try{Promise.resolve(e()).then(n.resolve,e=>{e!==xe&&n.reject(e)}).finally(lt)}catch(e){n.reject(e),lt()}var r=j;if(s){if(t.f&32768)var l=ut();if(i.b?.is_rendered())r.async_deriveds.get(t)?.reject(ft);else for(let e of c.values())e.reject(ft);c.add(n),r.async_deriveds.set(t,n)}let u=(e,t=void 0)=>{l?.(),c.delete(n),t!==ft&&(r.activate(),t?(o.f|=de,Gt(o,t)):(o.f&8388608&&(o.f^=de),Gt(o,e)),r.deactivate())};n.promise.then(u,e=>u(null,e||`unknown`))}),_n(()=>{for(let e of c)e.reject(ft)}),new Promise(e=>{function t(n){function r(){n===a?e(o):t(a)}n.then(r,r)}t(a)})}function mt(e){let t=dt(e);return t.equals=Pe,t}function ht(e){var t=e.effects;if(t!==null){e.effects=null;for(var n=0;n{t.ac.abort(xe),t.ac=null}),t.fn!==null&&(t.teardown=g),ar(t,0),kn(t))}function yt(e){if(e.effects!==null)for(let t of e.effects)t.teardown&&t.fn!==null&&or(t)}var bt=null,j=null,xt=null,St=null,Ct=null,wt=!1,Tt=!1,Et=null,Dt=null,Ot=0,kt=1,At=class e{id=kt++;#e=!1;linked=!0;#t=null;#n=null;async_deriveds=new Map;current=new Map;previous=new Map;#r=new Set;#i=new Set;#a=0;#o=new Map;#s=null;#c=[];#l=[];#u=new Set;#d=new Set;#f=new Map;#p=new Set;is_fork=!1;#m=!1;constructor(){bt===null?bt=this:(bt.#n=this,this.#t=bt),bt=this}#h(){if(this.is_fork)return!0;for(let n of this.#o.keys()){for(var e=n,t=!1;e.parent!==null;){if(this.#f.has(e)){t=!0;break}e=e.parent}if(!t)return!0}return!1}skip_effect(e){this.#f.has(e)||this.#f.set(e,{d:[],m:[]}),this.#p.delete(e)}unskip_effect(e,t=e=>this.schedule(e)){var n=this.#f.get(e);if(n){this.#f.delete(e);for(var r of n.d)A(r,x),t(r);for(r of n.m)A(r,S),t(r)}this.#p.add(e)}#g(){var e=[];for(let i of this.#c)if(!(i.f&16384||!(i.f&6144))){for(var t=i,n=!1;t.parent!==null;){t=t.parent;var r=t.f;if(r&96){if(!(r&1024)){n=!0;break}t.f^=b}}n||e.push(t)}return this.#c=[],e}#_(){this.#e=!0;for(let e of this.#u)this.#d.delete(e),A(e,x),this.schedule(e);for(let e of this.#d)A(e,S),this.schedule(e);this.apply();for(var t=Et=[],n=[],r=Dt=[];this.#c.length>0;){Ot++>1e3&&(this.#S(),Mt());for(let e of this.#g())try{this.#v(e,t,n)}catch(t){throw Lt(e),this.#h()||this.discard(),t}}if(j=null,r.length>0){var i=e.ensure();for(let e of r)i.schedule(e)}if(Et=null,Dt=null,this.#h()){this.#x(n),this.#x(t);for(let[e,t]of this.#f)It(e,t);r.length>0&&j.#_();return}let a=this.#y();if(a){this.#x(n),this.#x(t),a.#b(this);return}this.#u.clear(),this.#d.clear();for(let e of this.#r)e(this);this.#r.clear(),xt=this,Pt(n),Pt(t),xt=null,this.#s?.resolve();var o=j;if(this.#a===0&&(this.#c.length===0||o!==null)&&this.#S(),this.#c.length>0){if(o!==null){for(let e of this.#c)o.#c.push(e);this.#c=[]}else o=this}o!==null&&(zt.clear(),o.#_())}#v(e,t,n){e.f^=b;for(var r=e.first;r!==null;){var i=r.f,a=!!(i&96);if(!(a&&i&1024||i&8192||this.#f.has(r))&&r.fn!==null){a?r.f^=b:i&4?t.push(r):er(r)&&(i&16&&this.#d.add(r),or(r));var o=r.first;if(o!==null){r=o;continue}}for(;r!==null;){var s=r.next;if(s!==null){r=s;break}r=r.parent}}}#y(){for(var e=this.#t;e!==null;){if(!e.is_fork){for(let[t,[,n]]of this.current)if(e.current.has(t)&&!n)return e}e=e.#t}return null}#b(e){for(let[t,n]of e.current)!this.previous.has(t)&&e.previous.has(t)&&this.previous.set(t,e.previous.get(t)),this.current.set(t,n);for(let[t,n]of e.async_deriveds){let e=this.async_deriveds.get(t);e&&n.promise.then(e.resolve).catch(e.reject)}e.async_deriveds.clear(),this.transfer_effects(e.#u,e.#d);let t=e=>{var n=e.reactions;if(n!==null&&!(e.f&2&&!(e.f&6144)))for(let e of n){var r=e.f;if(r&2)t(e);else{var i=e;r&4194320&&!this.async_deriveds.has(i)&&(this.#d.delete(i),A(i,x),this.schedule(i))}}};for(let e of this.current.keys())t(e);this.oncommit(()=>e.discard()),e.#S(),j=this,this.#_()}#x(e){for(var t=0;t{this.#m=!1,this.linked&&this.flush()}))}transfer_effects(e,t){for(let t of e)this.#u.add(t);for(let e of t)this.#d.add(e);e.clear(),t.clear()}oncommit(e){this.#r.add(e)}ondiscard(e){this.#i.add(e)}settled(){return(this.#s??=y()).promise}static ensure(){if(j===null){let t=j=new e;!Tt&&!wt&&Qe(()=>{t.#e||t.flush()})}return j}apply(){St=null}schedule(e){if(Ct=e,e.b?.is_pending&&e.f&16777228&&!(e.f&32768)){e.b.defer_effect(e);return}this.#c.push(e)}#S(){if(this.linked){var e=this.#t,t=this.#n;e===null||(e.#n=t),t===null?bt=e:t.#t=e,this.linked=!1}}};function jt(e){var t=wt;wt=!0;try{var n;for(e&&(j!==null&&!j.is_fork&&j.flush(),n=e());;){if($e(),j===null)return n;j.flush()}}finally{wt=t}}function Mt(){try{Ve()}catch(e){fn(e,Ct)}}var Nt=null;function Pt(e){var t=e.length;if(t!==0){for(var n=0;n0)){zt.clear();for(let e of Nt){if(e.f&24576)continue;let t=[e],n=e.parent;for(;n!==null;)Nt.has(n)&&(Nt.delete(n),t.push(n)),n=n.parent;for(let e=t.length-1;e>=0;e--){let n=t[e];n.f&24576||or(n)}}Nt.clear()}}Nt=null}}function Ft(e){j.schedule(e)}function It(e,t){if(!(e.f&32&&e.f&1024)){e.f&2048?t.d.push(e):e.f&4096&&t.m.push(e),A(e,b);for(var n=e.first;n!==null;)It(n,t),n=n.next}}function Lt(e){A(e,b);for(var t=e.first;t!==null;)Lt(t),t=t.next}var Rt=new Set,zt=new Map,Bt=!1;function Vt(e,t){return{f:0,v:e,reactions:null,equals:Me,rv:0,wv:0}}function Ht(e,t){let n=Vt(e,t);return Kn(n),n}function M(t,n=!1,r=!0){let i=Vt(t);return n||(i.equals=Pe),e&&r&&D!==null&&D.l!==null&&(D.l.s??=[]).push(i),i}function N(e,t,n=!1){return V!==null&&(!Hn||V.f&131072)&&Ye()&&V.f&4325394&&(Gn===null||!Gn.has(e))&&Ge(),Gt(e,n?Yt(t):t,Dt)}var Ut=null,Wt=0;function Gt(e,t,n=null){if(!e.equals(t)){Bn?zt.set(e,t):zt.has(e)||zt.set(e,e.v);var r=At.ensure();if(r.capture(e,t),e.f&2){let t=e;e.f&2048&>(t),St===null&&tt(t)}e.wv=$n(),Ut=null,Wt=0,Jt(e,x,n),Ut=null,Ye()&&H!==null&&H.f&1024&&!(H.f&96)&&(qn===null?Jn([e]):qn.push(e)),!r.is_fork&&Rt.size>0&&!Bt&&Kt()}return t}function Kt(){Bt=!1;for(let e of Rt){e.f&1024&&A(e,S);let t;try{t=er(e)}catch{t=!0}t&&or(e)}Rt.clear()}function qt(e){N(e,e.v+1)}function Jt(e,t,n){var r=e.reactions;if(r!==null){var i=Ye(),a=r.length;if(Wt+=a,Wt>1e5&&Ut===null&&(Ut=new Set),Ut!==null){if(Ut.has(e))return;Ut.add(e)}for(var o=0;o{if(Zn===c)return e();var t=V,n=Zn;Un(null),Qn(c);var r=e();return Un(t),Qn(n),r};return i&&n.set(`length`,Ht(e.length,s)),new Proxy(e,{defineProperty(e,t,r){(!(`value`in r)||r.configurable===!1||r.enumerable===!1||r.writable===!1)&&Ue();var i=n.get(t);return i===void 0?l(()=>{var e=Ht(r.value,s);return n.set(t,e),e}):N(i,r.value,!0),!0},deleteProperty(e,t){var i=n.get(t);if(i===void 0){if(t in e){let e=l(()=>Ht(r,s));n.set(t,e),qt(o)}}else N(i,r),qt(o);return!0},get(t,i,a){if(i===fe)return e;var o=n.get(i),c=i in t;if(o===void 0&&(!c||u(t,i)?.writable)&&(o=l(()=>Ht(Yt(c?t[i]:r),s)),n.set(i,o)),o!==void 0){var d=G(o);return d===r?void 0:d}return Reflect.get(t,i,a)},getOwnPropertyDescriptor(e,t){this.has?.(e,t);var i=Reflect.getOwnPropertyDescriptor(e,t),a=n.get(t);if(a!==void 0){var o=G(a);if(o===r)return;if(i&&`value`in i)i.value=o;else return{enumerable:!0,configurable:!0,value:o,writable:!0}}return i},has(e,t){if(t===fe)return!0;var i=n.get(t),a=i!==void 0&&i.v!==r||Reflect.has(e,t);return(i!==void 0||H!==null&&(!a||u(e,t)?.writable))&&(i===void 0&&(i=l(()=>Ht(a?Yt(e[t]):r,s)),n.set(t,i)),G(i)===r)?!1:a},set(e,t,a,c){var d=n.get(t),f=t in e;if(i&&t===`length`)for(var p=a;pHt(r,s)),n.set(p+``,m)):N(m,r)}if(d===void 0)(!f||u(e,t)?.writable)&&(d=l(()=>Ht(void 0,s)),N(d,Yt(a)),n.set(t,d));else{f=d.v!==r;var h=l(()=>Yt(a));N(d,h)}var g=Reflect.getOwnPropertyDescriptor(e,t);if(g?.set&&g.set.call(c,a),!f){if(i&&typeof t==`string`){var _=n.get(`length`),v=Number(t);Number.isInteger(v)&&v>=_.v&&N(_,v+1)}qt(o)}return!0},ownKeys(e){G(o);var t=Reflect.ownKeys(e).filter(e=>{var t=n.get(e);return t===void 0||t.v!==r});for(var[i,a]of n)a.v!==r&&!(i in e)&&t.push(i);return t},setPrototypeOf(){We()}})}function Xt(e){try{if(typeof e==`object`&&e&&fe in e)return e[fe]}catch{}return e}function Zt(e,t){return Object.is(Xt(e),Xt(t))}var Qt,$t,en,tn;function nn(){if(Qt===void 0){Qt=window,$t=/Firefox/.test(navigator.userAgent);var e=Element.prototype,t=Node.prototype,n=Text.prototype;en=u(t,`firstChild`).get,tn=u(t,`nextSibling`).get,h(e)&&(e[_e]=void 0,e[ge]=null,e[ve]=void 0,e.__e=void 0),h(n)&&(n[ye]=void 0)}}function P(e=``){return document.createTextNode(e)}function rn(e){return en.call(e)}function an(e){return tn.call(e)}function F(e,t){if(!C)return rn(e);var n=rn(w);if(n===null)n=w.appendChild(P());else if(t&&n.nodeType!==3){var r=P();return n?.before(r),T(r),r}return t&&un(n),T(n),n}function on(e,t=!1){if(!C){var n=rn(e);return n instanceof Comment&&n.data===``?an(n):n}if(t){if(w?.nodeType!==3){var r=P();return w?.before(r),T(r),r}un(w)}return w}function I(e,t=!1){if(!C)return rn(e);var n=F(e,t);return E(e),n}function L(e,t=1,n=!1){let r=C?w:e;for(var i;t--;)i=r,r=an(r);if(!C)return r;if(n){if(r?.nodeType!==3){var a=P();return r===null?i?.after(a):r.before(a),T(a),a}un(r)}return T(r),r}function sn(e){e.textContent=``}function cn(){return!1}function ln(e,t,n){return t==null||t===`http://www.w3.org/1999/xhtml`?n?document.createElement(e,{is:n}):document.createElement(e):n?document.createElementNS(t,e,{is:n}):document.createElementNS(t,e)}function un(e){if(e.nodeValue.length<65536)return;let t=e.nextSibling;for(;t!==null&&t.nodeType===3;)t.remove(),e.nodeValue+=t.nodeValue,t=e.nextSibling}function dn(e){var t=H;if(t===null)return V.f|=de,e;if(!(t.f&32768)&&!(t.f&4))throw e;fn(e,t)}function fn(e,t){if(!(t!==null&&t.f&16384)){for(;t!==null;){if(t.f&128&&!(t.f&33570816)){if(!(t.f&32768))throw e;try{t.b.error(e);return}catch(t){e=t}}t=t.parent}throw e}}function pn(e){H===null&&(V===null&&Be(e),ze()),Bn&&Re(e)}function mn(e,t){var n=t.last;n===null?t.last=t.first=e:(n.next=e,e.prev=n,t.last=e)}function hn(e,t){var n=H;n!==null&&n.f&8192&&(e|=ee);var r={ctx:D,deps:null,nodes:null,f:e|x|512,first:null,fn:t,last:null,next:null,parent:n,b:n&&n.b,prev:null,teardown:null,wv:0,ac:null};j?.register_created_effect(r);var i=r;if(e&4)Et===null?At.ensure().schedule(r):Et.push(r);else if(t!==null){try{or(r)}catch(e){throw B(r),e}i.deps===null&&i.teardown===null&&i.nodes===null&&i.first===i.last&&!(i.f&524288)&&(i=i.first,e&16&&e&65536&&i!==null&&(i.f|=ie))}if(i!==null&&(i.parent=n,n!==null&&mn(i,n),V!==null&&V.f&2&&!(e&64))){var a=V;(a.effects??=[]).push(i)}return r}function gn(){return V!==null&&!Hn}function _n(e){let t=hn(8,null);return A(t,b),t.teardown=e,t}function vn(e){pn(`$effect`);var t=H.f;if(!V&&t&32&&D!==null&&!D.i){var n=D;(n.e??=[]).push(e)}else return yn(e)}function yn(e){return hn(4|se,e)}function bn(e){return pn(`$effect.pre`),hn(8|se,e)}function xn(e){At.ensure();let t=hn(64|oe,e);return(e={})=>new Promise(n=>{e.outro?Nn(t,()=>{B(t),n(void 0)}):(B(t),n(void 0))})}function Sn(e){return hn(4,e)}function Cn(e,t){var n=D,r={effect:null,ran:!1,deps:e};n.l.$.push(r),r.effect=En(()=>{if(e(),!r.ran){r.ran=!0;var n=H;try{Wn(n.parent),K(t)}finally{Wn(n)}}})}function wn(){var e=D;En(()=>{for(var t of e.l.$){t.deps();var n=t.effect;n.f&1024&&n.deps!==null&&A(n,S),er(n)&&or(n),t.ran=!1}})}function Tn(e){return hn(ue|oe,e)}function En(e,t=0){return hn(8|t,e)}function R(e,t=[],n=[],r=[]){st(r,t,n,t=>{hn(8,()=>{e(...t.map(G))})})}function Dn(e,t=0){return hn(16|t,e)}function z(e){return hn(32|oe,e)}function On(e){var t=e.teardown;if(t!==null){let n=Bn,r=V;Vn(!0),Un(null);try{t.call(null)}catch(t){fn(t,e.parent)}finally{Vn(n),Un(r)}}}function kn(e,t=!1){var n=e.first;for(e.first=e.last=null;n!==null;){let e=n.ac;e!==null&&at(()=>{e.abort(xe)});var r=n.next;n.f&64?n.parent=null:B(n,t),n=r}}function An(e){for(var t=e.first;t!==null;){var n=t.next;t.f&32||B(t),t=n}}function B(e,t=!0){var n=!1;(t||e.f&262144)&&e.nodes!==null&&e.nodes.end!==null&&(jn(e.nodes.start,e.nodes.end),n=!0),e.f|=re,kn(e,t&&!n),ar(e,0);var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)e.stop();On(e),e.f^=re,e.f|=te;var i=e.parent;i!==null&&i.first!==null&&Mn(e),e.next=e.prev=e.teardown=e.ctx=e.deps=e.fn=e.nodes=e.ac=e.b=null}function jn(e,t){for(;e!==null;){var n=e===t?null:an(e);e.remove(),e=n}}function Mn(e){var t=e.parent,n=e.prev,r=e.next;n!==null&&(n.next=r),r!==null&&(r.prev=n),t!==null&&(t.first===e&&(t.first=r),t.last===e&&(t.last=n))}function Nn(e,t,n=!0){var r=[];e.f|=256,Pn(e,r,!0);var i=()=>{n&&B(e),t&&t()},a=r.length;if(a>0){var o=()=>--a||i();for(var s of r)s.out(o)}else i()}function Pn(e,t,n){if(!(e.f&8192)){e.f^=ee;var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)(e.is_global||n)&&t.push(e);for(var i=e.first;i!==null;){var a=i.next;if(!(i.f&64)){var o=!!(i.f&65536)||!!(i.f&32)&&!!(e.f&16);Pn(i,t,o?n:!1)}i=a}}}function Fn(e){e.f&=-257,In(e,!0)}function In(e,t){if(!(e.f&256)&&e.f&8192){e.f^=ee,e.f&1024||(A(e,x),At.ensure().schedule(e));for(var n=e.first;n!==null;){var r=n.next,i=!!(n.f&65536)||!!(n.f&32);In(n,i?t:!1),n=r}var a=e.nodes&&e.nodes.t;if(a!==null)for(let e of a)(e.is_global||t)&&e.in()}}function Ln(e,t){if(e.nodes)for(var n=e.nodes.start,r=e.nodes.end;n!==null;){var i=n===r?null:an(n);t.append(n),n=i}}var Rn=null,zn=!1,Bn=!1;function Vn(e){Bn=e}var V=null,Hn=!1;function Un(e){V=e}var H=null;function Wn(e){H=e}var Gn=null;function Kn(e){V!==null&&(V.f&2097152||V.f&2)&&(Gn??=new Set).add(e)}var U=null,W=0,qn=null;function Jn(e){qn=e}var Yn=1,Xn=0,Zn=Xn;function Qn(e){Zn=e}function $n(){return++Yn}function er(e){var t=e.f;if(t&2048)return!0;if(t&4096){for(var n=e.deps,r=n.length,i=0;ie.wv)return!0}t&512&&St===null&&A(e,b)}return!1}function tr(e,t,n=!0){var r=e.reactions;if(r!==null&&!(Gn!==null&&Gn.has(e)))for(var i=0;i{e.ac.abort(xe)}),e.ac=null);try{e.f|=le;var u=e.fn,d=u();e.f|=ne;var f=rr(e);if(Ye()&&qn!==null&&!Hn&&f!==null&&!(e.f&6146))for(var p=0;p0)for(t.length=W+U.length,r=0;r{c.ac.abort(xe),c.ac=null,A(c,x)}),vt(c),ar(c,0)}}function ar(e,t){var n=e.deps;if(n!==null)for(var r=t;rn?.call(this,e))}return e.startsWith(`pointer`)||e.startsWith(`touch`)||e===`wheel`?(i.__removed=!1,Qe(()=>{i.__removed||t.addEventListener(e,i,r)})):t.addEventListener(e,i,r),i}function hr(e,t,n,r,i){var a={capture:r,passive:i},o=mr(e,t,n,a);(t===document.body||t===window||t===document||t instanceof HTMLMediaElement)&&_n(()=>{o.__removed=!0,t.removeEventListener(e,o,a)})}function gr(e,t,n){(t[dr]??={})[e]=n}function _r(e){for(var t=0;t{yr=!1,vr=null}));var o=0,s=vr===e&&e[dr];if(s){var c=i.indexOf(s);if(c!==-1&&(t===document||t===window)){e[dr]=t;return}var u=i.indexOf(t);if(u===-1)return;c<=u&&(o=c)}if(a=i[o]||e.target,a!==t){l(e,`currentTarget`,{configurable:!0,get(){return a||n}});var d=V,f=H;Un(null),Wn(null);try{for(var p,m=[];a!==null&&a!==t;){try{var h=a[dr]?.[r];h!=null&&(!a.disabled||e.target===a)&&h.call(a,e)}catch(e){p?m.push(e):p=e}if(e.cancelBubble)break;o++,a=o{throw e});throw p}}finally{e[dr]=t,delete e.currentTarget,Un(d),Wn(f)}}}var xr=globalThis?.window?.trustedTypes&&globalThis.window.trustedTypes.createPolicy(`svelte-trusted-html`,{createHTML:e=>e});function Sr(e){return xr?.createHTML(e)??e}function Cr(e){var t=ln(`template`);return t.innerHTML=Sr(e.replaceAll(``,``)),t.content}function wr(e,t){var n=H;n.nodes===null&&(n.nodes={start:e,end:t,a:null,t:null})}function J(e,t){var n=!!(t&1),r=!!(t&2),i,a=!e.startsWith(``);return()=>{if(C)return wr(w,null),w;i===void 0&&(i=Cr(a?e:``+e),n||(i=rn(i)));var t=r||$t?document.importNode(i,!0):i.cloneNode(!0);if(n){var o=rn(t),s=t.lastChild;wr(o,s)}else wr(t,t);return t}}function Tr(e=``){if(!C){var t=P(e+``);return wr(t,t),t}var n=w;return n.nodeType===3?un(n):(n.before(n=P()),T(n)),wr(n,n),n}function Er(){if(C)return wr(w,null),w;var e=document.createDocumentFragment(),t=document.createComment(``),n=P();return e.append(t,n),wr(t,n),e}function Y(e,t){if(C){var n=H;(!(n.f&32768)||n.nodes.end===null)&&(n.nodes.end=w),Oe();return}e!==null&&e.before(t)}[...`allowfullscreen.async.autofocus.autoplay.checked.controls.default.disabled.formnovalidate.indeterminate.inert.ismap.loop.multiple.muted.nomodule.novalidate.open.playsinline.readonly.required.reversed.seamless.selected.webkitdirectory.defer.disablepictureinpicture.disableremoteplayback`.split(`.`)];var Dr=[`touchstart`,`touchmove`];function Or(e){return Dr.includes(e)}function kr(e){let t=0,n=Vt(0),r;return()=>{gn()&&(G(n),En(()=>(t===0&&(r=K(()=>e(()=>qt(n)))),t+=1,()=>{Qe(()=>{--t,t===0&&(r?.(),r=void 0,qt(n))})})))}}var Ar=ie|oe;function jr(e,t,n,r){new Mr(e,t,n,r)}var Mr=class{parent;is_pending=!1;transform_error;#e;#t=C?w:null;#n;#r;#i;#a=null;#o=null;#s=null;#c=null;#l=0;#u=0;#d=!1;#f=new Set;#p=new Set;#m=null;#h=kr(()=>(this.#m=Vt(this.#l),()=>{this.#m=null}));constructor(e,t,n,r){this.#e=e,this.#n=t,this.#r=e=>{var t=H;t.b=this,t.f|=128,n(e)},this.parent=H.b,this.transform_error=r??this.parent?.transform_error??(e=>e),this.#i=Dn(()=>{if(C){let e=this.#t;Oe();let t=e.data===`[!`;if(e.data.startsWith(`[?`)){let t=JSON.parse(e.data.slice(2));this.#_(t)}else t?this.#y():this.#g()}else this.#b()},Ar),C&&(this.#e=w)}#g(){try{this.#a=z(()=>this.#r(this.#e))}catch(e){this.error(e)}}#_(e){let t=this.#n.failed,{reset:n,invoke_onerror:r}=this.#v(e);Qe(r),t&&(this.#s=z(()=>{t(this.#e,()=>e,()=>n)}))}#v(e){var t=!1,n=!1;let r=()=>{if(t){Ee();return}t=!0,n&&Ke(),this.#s!==null&&Nn(this.#s,()=>{this.#s=null}),this.#S(()=>{this.#b()})};return{reset:r,invoke_onerror:()=>{try{n=!0,this.#n.onerror?.(e,r),n=!1}catch(e){fn(e,this.#i&&this.#i.parent)}}}}#y(){let e=this.#n.pending;e&&(this.is_pending=!0,this.#o=z(()=>e(this.#e)),Qe(()=>{var e=this.#c=document.createDocumentFragment(),t=P(),n=!1;if(e.append(t),this.#a=this.#S(()=>{try{return z(()=>this.#r(t))}catch(e){try{this.error(e),n=!0}catch(e){fn(e,this.#i.parent)}return null}}),this.#a===null){this.#c=null,n&&this.#x(j);return}this.#u===0&&(this.#e.before(e),this.#c=null,Nn(this.#o,()=>{this.#o=null}),this.#x(j))}))}#b(){try{if(this.is_pending=this.has_pending_snippet(),this.#u=0,this.#l=0,this.#a=z(()=>{this.#r(this.#e)}),this.#u>0){var e=this.#c=document.createDocumentFragment();Ln(this.#a,e);let t=this.#n.pending;this.#o=z(()=>t(this.#e))}else this.#x(j)}catch(e){this.error(e)}}#x(e){this.is_pending=!1,e.transfer_effects(this.#f,this.#p)}defer_effect(e){nt(e,this.#f,this.#p)}is_rendered(){return!this.is_pending&&(!this.parent||this.parent.is_rendered())}has_pending_snippet(){return!!this.#n.pending}#S(e){var t=H,n=V,r=D;Wn(this.#i),Un(this.#i),qe(this.#i.ctx);try{return At.ensure(),e()}finally{Wn(t),Un(n),qe(r)}}#C(e,t){if(!this.has_pending_snippet()){this.parent&&this.parent.#C(e,t);return}this.#u+=e,this.#u===0&&(this.#x(t),this.#o&&Nn(this.#o,()=>{this.#o=null}),this.#c&&=(this.#e.before(this.#c),null))}update_pending_count(e,t){this.#C(e,t),this.#l+=e,!(!this.#m||this.#d)&&(this.#d=!0,Qe(()=>{this.#d=!1,this.#m&&Gt(this.#m,this.#l)}))}get_effect_pending(){return this.#h(),G(this.#m)}error(e){if(!this.#n.onerror&&!this.#n.failed)throw e;j?.is_fork?(this.#a&&j.skip_effect(this.#a),this.#o&&j.skip_effect(this.#o),this.#s&&j.skip_effect(this.#s),j.oncommit(()=>{this.#w(e)})):this.#w(e)}#w(e){this.#a&&=(B(this.#a),null),this.#o&&=(B(this.#o),null),this.#s&&=(B(this.#s),null),C&&(T(this.#t),ke(),T(Ae()));let t=this.#n.failed,n=e=>{let{reset:n,invoke_onerror:r}=this.#v(e);r(),t&&(this.#s=this.#S(()=>{try{return z(()=>{var r=H;r.b=this,r.f|=128,t(this.#e,()=>e,()=>n)})}catch(e){return fn(e,this.#i.parent),null}}))};Qe(()=>{var t;try{t=this.transform_error(e)}catch(e){fn(e,this.#i&&this.#i.parent);return}typeof t==`object`&&t&&typeof t.then==`function`?t.then(n,e=>fn(e,this.#i&&this.#i.parent)):n(t)})}};function X(e,t){var n=t==null?``:typeof t==`object`?`${t}`:t;n!==(e[ye]??=e.nodeValue)&&(e[ye]=n,e.nodeValue=`${n}`)}function Nr(e,t){return Fr(e,t)}var Pr=new Map;function Fr(e,{target:t,anchor:r,props:i={},events:a,context:o,intro:s=!0,transformError:l}){nn();var u=void 0,d=xn(()=>{var s=r??t.appendChild(P());jr(s,{pending:()=>{}},t=>{O({});var r=D;if(o&&(r.c=o),a&&(i.$$events=a),C&&wr(t,null),u=e(t,i)||Je(),C&&(H.nodes.end=w,w===null||w.nodeType!==8||w.data!==`]`))throw we(),n;k()},l);var d=new Set,f=e=>{for(var n=0;n{for(var e of d)for(let r of[t,document]){var n=Pr.get(r),i=n.get(e);--i==0?(r.removeEventListener(e,br),n.delete(e),n.size===0&&Pr.delete(r)):n.set(e,i)}pr.delete(f),s!==r&&s.parentNode?.removeChild(s)}});return Ir.set(u,d),u}var Ir=new WeakMap,Lr=class{anchor;#e=new Map;#t=new Map;#n=new Map;#r=new Set;#i=!0;constructor(e,t=!0){this.anchor=e,this.#i=t}#a=e=>{if(this.#e.has(e)){var t=this.#e.get(e),n=this.#t.get(t);if(n)Fn(n),this.#r.delete(t);else{var r=this.#n.get(t);r&&(Fn(r.effect),this.#t.set(t,r.effect),this.#n.delete(t),r.fragment.lastChild.remove(),this.anchor.before(r.fragment),n=r.effect)}for(let[t,n]of this.#e){if(this.#e.delete(t),t===e)break;let r=this.#n.get(n);r&&(B(r.effect),this.#n.delete(n))}for(let[e,r]of this.#t){if(e===t||this.#r.has(e))continue;let i=()=>{if(Array.from(this.#e.values()).includes(e)){var t=document.createDocumentFragment();Ln(r,t),t.append(P()),this.#n.set(e,{effect:r,fragment:t})}else B(r);this.#r.delete(e),this.#t.delete(e)};this.#i||!n?(this.#r.add(e),Nn(r,i,!1)):i()}}};#o=e=>{this.#e.delete(e);let t=Array.from(this.#e.values());for(let[e,n]of this.#n)t.includes(e)||(B(n.effect),this.#n.delete(e))};ensure(e,t){var n=j,r=cn();if(t&&!this.#t.has(e)&&!this.#n.has(e)){if(r){var i=document.createDocumentFragment(),a=P();i.append(a),this.#n.set(e,{effect:z(()=>t(a)),fragment:i})}else this.#t.set(e,z(()=>t(this.anchor)))}if(this.#e.set(n,e),r){for(let[t,r]of this.#t)t===e?n.unskip_effect(r):n.skip_effect(r);for(let[t,r]of this.#n)t===e?n.unskip_effect(r.effect):n.skip_effect(r.effect);n.oncommit(this.#a),n.ondiscard(this.#o)}else C&&(this.anchor=w),this.#a(n)}};function Rr(t){D===null&&Fe(`onMount`),e&&D.l!==null?zr(D).m.push(t):vn(()=>{let e=K(t);if(typeof e==`function`)return e})}function zr(e){var t=e.l;return t.u??={a:[],b:[],m:[]}}function Z(e,t,n=!1){var r;C&&(r=w,Oe());var i=new Lr(e),a=n?ie:0;function o(e,t){if(C){var n=je(r);if(e!==parseInt(n.substring(1))){var a=Ae();T(a),i.anchor=a,De(!1),i.ensure(e,t),De(!0);return}}i.ensure(e,t)}Dn(()=>{var e=!1;t((t,n=0)=>{e=!0,o(n,t)}),e||o(-1,null)},a)}var Br=Symbol(`NaN`);function Vr(e,t,n){C&&Oe();var r=new Lr(e),i=!Ye();Dn(()=>{var e=t();e!==e&&(e=Br),i&&typeof e==`object`&&e&&(e={}),r.ensure(e,n)})}function Hr(e,t){return t}function Ur(e,t,n){for(var r=[],i=t.length,a,o=t.length,s=0;s{if(a){if(a.pending.delete(n),a.done.add(n),a.pending.size===0){var t=e.outrogroups;Wr(e,c(a.done)),t.delete(a),t.size===0&&(e.outrogroups=null)}}else--o},!1)}if(o===0){var l=r.length===0&&n!==null&&e.pending.size===0;if(l){var u=n,d=u.parentNode;sn(d),d.append(u),e.items.clear()}Wr(e,t,!l)}else a={pending:new Set(t),done:new Set},(e.outrogroups??=new Set).add(a)}function Wr(e,t,n=!0){var r;if(e.pending.size>0){r=new Set;for(let t of e.pending.values())for(let n of t)r.add(e.items.get(n).e)}for(var i=0;i{var e=n();return a(e)?e:e==null?[]:c(e)}),p,m=new Map,h=!0;function g(e){v.effect.f&16384||(v.pending.delete(e),v.fallback=d,Jr(v,p,s,t,r),d!==null&&(p.length===0?d.f&33554432?(d.f^=ce,Xr(d,null,s)):Fn(d):Nn(d,()=>{d=null})))}function _(e){v.pending.delete(e)}var v={effect:Dn(()=>{p=G(f);var e=p.length;let a=!1;C&&je(s)===`[!`!=(e===0)&&(s=Ae(),T(s),De(!1),a=!0);for(var c=new Set,u=j,v=cn(),y=0;yo(s)):(d=z(()=>o(Gr??=P())),d.f|=ce)),e>c.size&&Le(``,``,``),C&&e>0&&T(Ae()),!h){if(m.set(u,c),v){for(let[e,t]of l)c.has(e)||u.skip_effect(t.e);u.oncommit(g),u.ondiscard(_)}else g(u)}a&&De(!0),G(f)}),flags:t,items:l,pending:m,outrogroups:null,fallback:d};h=!1,C&&(s=w)}function qr(e){for(;e!==null&&!(e.f&32);)e=e.next;return e}function Jr(e,t,n,r,i){var a=!!(r&8),o=t.length,s=e.items,l=qr(e.effect.first),u,d=null,f,p=[],m=[],h,g,_,v;if(a)for(v=0;v0){var re=r&4&&o===0?n:null;if(a){for(v=0;v{if(f!==void 0)for(_ of f)_.nodes?.a?.apply()})}function Yr(e,t,n,r,i,a,o,s){var c=o&1?o&16?Vt(n):M(n,!1,!1):null,l=o&2?Vt(i):null;return{v:c,i:l,e:z(()=>(a(t,c??n,l??i,s),()=>{e.delete(r)}))}}function Xr(e,t,n){if(e.nodes)for(var r=e.nodes.start,i=e.nodes.end,a=t&&!(t.f&33554432)?t.nodes.start:n;r!==null;){var o=an(r);if(a.before(r),r===i)return;r=o}}function Zr(e,t,n){t===null?e.effect.first=n:t.next=n,n===null?e.effect.last=t:n.prev=t}function Qr(e,t){let n=null,r=C;var i;if(C){n=w;for(var a=rn(document.head);a!==null&&(a.nodeType!==8||a.data!==e);)a=an(a);if(a===null)De(!1);else{var o=an(a);a.remove(),T(o)}}C||(i=document.head.appendChild(P()));try{Dn(()=>{var e=z(()=>t(i));e.f|=ae,C||(e.nodes===null?e.nodes={start:i,end:i,a:null,t:null}:e.nodes.end=i)})}finally{r&&(De(!0),T(n))}}function $r(e){var t,n,r=``;if(typeof e==`string`||typeof e==`number`)r+=e;else if(typeof e==`object`){if(Array.isArray(e)){var i=e.length;for(t=0;t=0;){var s=o+a;(o===0||ni.includes(r[o-1]))&&(s===r.length||ni.includes(r[s]))?r=(o===0?``:r.substring(0,o))+r.substring(s+1):o=s}}return r===``?null:r}function ii(e,t,n,r,i,a){var o=e[_e];if(C||o!==n||o===void 0){var s=ri(n,r,a);(!C||s!==e.getAttribute(`class`))&&(s==null?e.removeAttribute(`class`):t?e.className=s:e.setAttribute(`class`,s)),e[_e]=n}else if(a&&i!==a)for(var c in a){var l=!!a[c];(i==null||l!==!!i[c])&&e.classList.toggle(c,l)}return a}function ai(e,t){t?e.hasAttribute(`selected`)||e.setAttribute(`selected`,``):e.removeAttribute(`selected`)}function oi(e,t){var n=e.__defaultValue,r=e.multiple,i=r?n??[]:null;if(!r||a(i)){var o=e.selectedIndex,s=t&&r?new Set(e.selectedOptions):null;for(var c of e.options){var l=ui(c);ai(c,r?i.includes(l):Zt(l,n))}if(t){if(s!==null)for(c of e.options){var u=s.has(c);c.selected!==u&&(c.selected=u)}else e.selectedIndex!==o&&(e.selectedIndex=o)}}}function si(e,t,n=!1){if(e.multiple){if(t==null)return;if(!a(t))return Te();for(var r of e.options)r.selected=t.includes(ui(r));return}for(r of e.options)if(Zt(ui(r),t)){r.selected=!0;return}(!n||t!==void 0)&&(e.selectedIndex=-1)}function ci(e){var t=new MutationObserver(t=>{t.every(di)||(`__defaultValue`in e&&oi(e,!1),`__value`in e&&si(e,e.__value))});t.observe(e,{childList:!0,subtree:!0,attributes:!0,attributeFilter:[`value`]}),_n(()=>{t.disconnect()})}function li(e,t,n=t){var r=new WeakSet,i=!0;ot(e,`change`,t=>{var i=t?`[selected]`:`:checked`,a;if(e.multiple)a=[].map.call(e.querySelectorAll(i),ui);else{var o=e.querySelector(i)??e.querySelector(`option:not([disabled])`);a=o&&ui(o)}n(a),e.__value=a,j!==null&&r.add(j)}),Sn(()=>{var a=t();if(e===document.activeElement){var o=j;if(r.has(o))return}if(si(e,a,i),i&&a===void 0){var s=e.querySelector(`:checked`);s!==null&&(a=ui(s),n(a))}e.__value=a,i=!1})}function ui(e){return`__value`in e?e.__value:e.value}function di(e){if(e.target.closest(`selectedcontent`)!==null)return!0;if(e.type===`childList`){var t=[...e.addedNodes,...e.removedNodes];return t.length>0&&t.every(e=>e.nodeName===`SELECTEDCONTENT`)}return!1}var fi=Symbol(`is custom element`),pi=Symbol(`is html`),mi=Se?`link`:`LINK`;function hi(e){if(C){var t=!1,n=()=>{if(!t){if(t=!0,e.hasAttribute(`value`)){var n=e.value;Q(e,`value`,null),e.value=n}if(e.hasAttribute(`checked`)){var r=e.checked;Q(e,`checked`,null),e.checked=r}}};e[be]=n,Qe(n),it()}}function gi(e,t){var n=_i(e);n.checked!==(n.checked=t??void 0)&&(e.checked=t)}function Q(e,t,n,r){var i=_i(e);C&&(i[t]=e.getAttribute(t),t===`src`||t===`srcset`||t===`href`&&e.nodeName===mi)||i[t]!==(i[t]=n)&&(t===`loading`&&(e[he]=n),n==null?e.removeAttribute(t):typeof n!=`string`&&yi(e).has(t)?e[t]=n:e.setAttribute(t,n))}function _i(e){return e[ge]??={[fi]:e.nodeName.includes(`-`),[pi]:e.namespaceURI===i}}var vi=new Map;function yi(e){var t=e.getAttribute(`is`)||e.nodeName,n=vi.get(t);if(n)return n;vi.set(t,n=new Set);for(var r,i=e,a=Element.prototype;a!==i;){for(var o in r=d(i),r)r[o].set&&o!==`innerHTML`&&o!==`textContent`&&o!==`innerText`&&n.add(o);i=m(i)}return n}function bi(e,t,n=t){var r=new WeakSet;ot(e,`input`,async i=>{var a=i?e.defaultValue:e.value;if(a=xi(e)?Si(a):a,n(a),j!==null&&r.add(j),await sr(),a!==(a=t())){var o=e.selectionStart,s=e.selectionEnd,c=e.value.length;if(e.value=a??``,s!==null){var l=e.value.length;o===s&&s===c&&l>c?(e.selectionStart=l,e.selectionEnd=l):(e.selectionStart=o,e.selectionEnd=Math.min(s,l))}}}),(C&&e.defaultValue!==e.value||K(t)==null&&e.value)&&(n(xi(e)?Si(e.value):e.value),j!==null&&r.add(j)),En(()=>{var n=t();if(e===document.activeElement){var i=j;if(r.has(i))return}xi(e)&&n===Si(e.value)||(e.type!==`date`||n||e.value)&&n!==e.value&&(e.value=n??``)})}function xi(e){var t=e.type;return t===`number`||t===`range`}function Si(e){return e===``?null:+e}function Ci(e=!1){let t=D,n=t.l.u;if(!n)return;let r=()=>q(t.s);if(e){let e=0,n={},i=dt(()=>{let r=!1,i=t.s;for(let e in i)i[e]!==n[e]&&(n[e]=i[e],r=!0);return r&&e++,e});r=()=>G(i)}n.b.length&&bn(()=>{wi(t,r),v(n.b)}),vn(()=>{let e=K(()=>n.m.map(_));return()=>{for(let t of e)typeof t==`function`&&t()}}),n.a.length&&vn(()=>{wi(t,r),v(n.a)})}function wi(e,t){if(e.l.s)for(let t of e.l.s)G(t);t()}function Ti(e,t,n){if(e==null)return t(void 0),n&&n(void 0),g;let r=K(()=>e.subscribe(t,n));return r.unsubscribe?()=>r.unsubscribe():r}var Ei=[];function Di(e,t=g){let n=null,r=new Set;function i(t){if(Ne(e,t)&&(e=t,n)){let t=!Ei.length;for(let t of r)t[1](),Ei.push(t,e);if(t){for(let e=0;e{r.delete(c),r.size===0&&n&&(n(),n=null)}}return{set:i,update:a,subscribe:o}}function Oi(e){let t;return Ti(e,e=>t=e)(),t}var ki=!1,Ai=Symbol(`unmounted`);function ji(e,t,n){let r=n[t]??={store:null,source:M(void 0),unsubscribe:g};if(r.store!==e&&!(Ai in n)){if(r.unsubscribe(),r.store=e??null,e==null)r.source.v=void 0,r.unsubscribe=g;else{var i=!0;r.unsubscribe=Ti(e,e=>{i?r.source.v=e:N(r.source,e)}),i=!1}}return e&&Ai in n?Oi(e):G(r.source)}function Mi(){let e={};function t(){_n(()=>{for(var t in e)e[t].unsubscribe();l(e,Ai,{enumerable:!1,value:!0})})}return[e,t]}function Ni(e){var t=ki;try{return ki=!1,[e(),ki]}finally{ki=t}}function $(t,n,r,i){var a=!e||!!(r&2),o=!!(r&8),s=!!(r&16),c=i,l=!0,d=void 0,f=()=>s&&a?(d??=dt(i),G(d)):(l&&(l=!1,c=s?K(i):i),c);let p;if(o){var m=fe in t||me in t;p=u(t,n)?.set??(m&&n in t?e=>t[n]=e:void 0)}var h,g=!1;o?[h,g]=Ni(()=>t[n]):h=t[n],h===void 0&&i!==void 0&&(h=f(),p&&(a&&He(n),p(h)));var _=a?()=>{var e=t[n];return e===void 0?f():(l=!0,e)}:()=>{var e=t[n];return e!==void 0&&(c=void 0),e===void 0?c:e};if(a&&!(r&4))return _;if(p){var v=t.$$legacy;return(function(e,t){return arguments.length>0?((!a||!t||v||g)&&p(t?_():e),e):_()})}var y=!1,b=(r&1?dt:mt)(()=>(y=!1,_()));o&&G(b);var x=H;return(function(e,t){if(arguments.length>0){let n=t?G(b):a&&o?Yt(e):e;return N(b,n),y=!0,c!==void 0&&(c=n),e}return Bn&&y||x.f&16384?b.v:G(b)})}function Pi(e){let t=Di({paired:!!e,controller:!1,busy:!1,error:``}),n=e,r,i={paired:!!e,controller:!1,busy:!1,error:``},a=!1;function o(e){i={...i,...e},t.set(i)}async function s(e=!1){if(!a&&n){a=!0;try{let t=await fetch(`/api/state`,{cache:`no-store`,headers:{"X-Setup-Session-Key":n}});if(!t.ok)throw Error(`The local setup session is unavailable.`);o({view:await t.json(),...e?{}:{error:``}})}catch{o({view:void 0,error:`Connection lost. The CLI may have stopped. Check the terminal before trying again.`})}finally{a=!1}}}async function c(){if(n)try{let e=await fetch(`/api/bootstrap`,{cache:`no-store`,headers:{"X-Setup-Session-Key":n}});if(!e.ok)throw Error(`Could not join this local session.`);let t=await e.json();r=t.capability,o({controller:t.controller,error:``}),await s()}catch{n=void 0,r=void 0,o({view:void 0,paired:!1,controller:!1,error:`Could not connect to the local setup session. Check the terminal and pair again.`})}}async function l(e){if(!(i.busy||i.paired)){o({busy:!0,error:``});try{let t=await fetch(`/api/pair`,{method:`POST`,cache:`no-store`,headers:{"Content-Type":`application/json`},body:JSON.stringify({code:e.trim().toLowerCase()})}),r=await t.json();if(!t.ok)throw Error(String(r.error??`Pairing was rejected.`));if(typeof r.sessionKey!=`string`||!/^[a-f0-9]{64}$/.test(r.sessionKey))throw Error(`Invalid local pairing response.`);n=r.sessionKey,o({paired:!0,error:``}),await c()}catch(e){o({error:e instanceof Error?e.message:`Could not pair this browser.`})}finally{o({busy:!1})}}}async function u(e,t,i=!0){let a=await fetch(e,{method:`POST`,cache:`no-store`,headers:{"Content-Type":`application/json`,"X-Setup-Session-Key":n,...i&&r?{"X-Setup-Capability":r}:{}},body:JSON.stringify(t)}),o=await a.json();if(!a.ok)throw Error(String(o.error??`The request was rejected.`));return o}async function d(e,t){if(!i.busy&&i.controller&&i.view?.promptRevision===e){o({busy:!0,error:``});try{await u(`/api/answer`,{revision:e,value:t}),await s()}catch(e){let t=e instanceof Error?e.message:`Could not submit this answer.`;o({error:t}),/read-only|Control moved/.test(t)?await c():await s(!0)}finally{o({busy:!1})}}}async function f(){if(i.controller&&!i.busy){o({busy:!0,error:``});try{await u(`/api/cancel`,{}),await s()}catch(e){let t=e instanceof Error?e.message:`Cancellation failed.`;o({error:t}),/read-only|Control moved/.test(t)&&await c()}finally{o({busy:!1})}}}async function p(e){if(!(i.busy||!i.paired||i.controller)){o({busy:!0,error:``});try{let t=await u(`/api/takeover`,{code:e.trim().toLowerCase()},!1);r=String(t.capability),o({controller:!0,error:``}),await s()}catch(e){o({error:e instanceof Error?e.message:`Takeover failed.`}),await s(!0)}finally{o({busy:!1})}}}async function m(){try{await u(`/api/close`,{})}catch{}}return{subscribe:t.subscribe,pair:l,connect:c,refresh:s,submit:d,cancel:f,takeOver:p,close:m}}var Fi=J(`
                                                                                                                  • `),Ii=J(``);function Li(e,t){O(t,!1);let n=$(t,`journey`,8),r=[`Repository`,`Setup choices`,`Setup PAT`,`Plan`,`Bot PAT & credentials`,`Apply`];Ci();var i=Ii(),a=L(F(i),4);Kr(a,5,()=>r,Hr,(e,t,r)=>{var i=Fi();let a;var o=F(i),s=I(o,!0),c=I(L(o,2),!0);E(i),R(e=>{Q(i,`aria-current`,(q(n()),K(()=>n()?.position===r+1?`step`:void 0))),a=ii(i,1,``,null,a,{current:n()?.position===r+1,completed:(n()?.position??0)>r+1}),X(s,e),X(c,G(t))},[()=>(q(n()),K(()=>(n()?.position??0)>r+1?`✓`:String(r+1).padStart(2,`0`)))]),Y(e,i)}),E(a),ke(2),E(i),Y(e,i),k()}var Ri=J(`
                                                                                                                    `);function zi(e,t){O(t,!1);let n=M(`system`);Cn(()=>G(n),()=>{document.documentElement.dataset.theme=G(n)}),wn();var r=Ri(),i=F(r);let a;var o=L(i,2);let s;var c=L(o,2);let l;E(r),R(()=>{Q(i,`aria-pressed`,G(n)===`system`),a=ii(i,1,``,null,a,{active:G(n)===`system`}),Q(o,`aria-pressed`,G(n)===`light`),s=ii(o,1,``,null,s,{active:G(n)===`light`}),Q(c,`aria-pressed`,G(n)===`dark`),l=ii(c,1,``,null,l,{active:G(n)===`dark`})}),gr(`click`,i,()=>N(n,`system`)),gr(`click`,o,()=>N(n,`light`)),gr(`click`,c,()=>N(n,`dark`)),Y(e,r),k()}_r([`click`]);var Bi=J(`
                                                                                                                    LOCAL SESSION
                                                                                                                    `);function Vi(e,t){let n=$(t,`repository`,8);var r=Bi(),i=F(r),a=I(L(F(i),2),!0);E(i);var o=L(i,2);zi(L(F(o)),{}),E(o),E(r),R(()=>X(a,n()??`Connecting…`)),Y(e,r)}var Hi=J(`

                                                                                                                    `,1);function Ui(e,t){O(t,!1);let n=M(),r=$(t,`view`,8);Cn(()=>q(r()),()=>{N(n,r()?.outcome===`complete`?`Setup complete.`:r()?.outcome===`dry-run`?`Preview complete.`:r()?.outcome===`cancelled`?`Setup cancelled.`:r()?.outcome?`Setup needs attention.`:r()?.prompt?.title??`Preparing your setup…`)}),wn(),Ci();var i=Hi(),a=on(i),o=L(F(a)),s=I(L(o));E(a);var c=L(a,2),l=I(c,!0),u=I(L(c,2),!0);R((e,t)=>{X(o,` ${e??``} `),X(s,`${t??``} / 06`),X(l,G(n)),X(u,(q(r()),K(()=>r()?.outcome?`The terminal contains the detailed result. Review the next steps below before closing this session.`:`One decision at a time. Your choices shape the permissions, plan and credentials needed for this repository.`)))},[()=>(q(r()),K(()=>r()?.journey?.current?.toUpperCase()??`GETTING READY`)),()=>(q(r()),K(()=>String(r()?.journey?.position??1).padStart(2,`0`)))]),Y(e,i),k()}function Wi(e){try{let t=new URL(e??``);return t.protocol===`https:`&&t.hostname===`github.com`&&(t.pathname===`/settings/personal-access-tokens`||t.pathname.startsWith(`/settings/personal-access-tokens/`))?t.toString():void 0}catch{return}}var Gi=J(``),Ki=J(``);function qi(e,t){let n=$(t,`label`,8),r=$(t,`variant`,8,`primary`),i=$(t,`disabled`,8,!1),a=$(t,`arrow`,8,!1),o=$(t,`onClick`,8);var s=Ki(),c=F(s,!0),l=L(c),u=e=>{Y(e,Gi())};Z(l,e=>{a()&&e(u)}),E(s),R(()=>{ii(s,1,ti(r())),s.disabled=i(),X(c,n())}),gr(`click`,s,function(...e){o()?.apply(this,e)}),Y(e,s)}_r([`click`]);var Ji=J(`Open GitHub link ↗`),Yi=J(`

                                                                                                                    `);function Xi(e,t){O(t,!1);let n=M(),r=$(t,`tone`,8,`info`),i=$(t,`title`,8),a=$(t,`message`,8),o=$(t,`link`,8,void 0),s=$(t,`actionLabel`,8,void 0),c=$(t,`onAction`,8,void 0);Cn(()=>q(o()),()=>{N(n,Wi(o()))}),wn(),Ci();var l=Yi(),u=F(l),d=I(u,!0),f=L(u),p=I(f,!0),m=L(f,2),h=e=>{var t=Ji();R(()=>Q(t,`href`,G(n))),Y(e,t)};Z(m,e=>{G(n)&&e(h)});var g=L(m,2),_=e=>{qi(e,{get label(){return s()},variant:`secondary`,get onClick(){return c()}})};Z(g,e=>{s()&&c()&&e(_)}),E(l),R(()=>{ii(l,1,`banner ${r()??``}`),Q(l,`role`,r()===`error`?`alert`:`status`),X(d,i()),X(p,a())}),Y(e,l),k()}function Zi(e){let t=String(e.question.defaultValue),n=t.split(`,`).map(e=>e.trim()).filter(Boolean);return{value:t,selected:e.question.kind===`multi-select`?n.includes(`All`)&&e.question.choices?.includes(`All`)?[`All`]:(e.question.choices??[]).filter(e=>e!==`All`&&n.includes(e.split(` — `)[0])):e.question.kind===`scope-overrides`?n:[]}}function Qi(e,t){return t===`All`?e.includes(`All`)?[]:[`All`]:e.includes(t)?e.filter(e=>e!==t):[...e.filter(e=>e!==`All`),t]}function $i(e,t,n){return e.question.kind===`scope-overrides`||e.question.kind===`multi-select`?n.length?n.join(`,`):`none`:t}var ea=J(`PERMISSION PREVIEW`),ta=J(`
                                                                                                                    `),na=J(``),ra=J(``),ia=J(``),aa=J(`
                                                                                                                    `),oa=J(``),sa=J(`

                                                                                                                    `,1);function ca(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`controller`,8),i=$(t,`busy`,8),a=$(t,`onSubmit`,8),o=Zi(n()),s=M(o.value),c=M(o.selected);Ci();var l=sa(),u=on(l),d=F(u),f=I(d,!0),p=L(d),m=e=>{Y(e,ea())};Z(p,e=>{q(n()),K(()=>n().phase===`permission-intent`)&&e(m)}),E(u);var h=L(u,2),g=e=>{var t=ta(),i=F(t);let a;var o=L(i);let c;E(t),R(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),Q(i,`aria-pressed`,G(s)===`yes`||G(s)===`true`),i.disabled=!r(),a=ii(i,1,``,null,a,{selected:G(s)===`yes`||G(s)===`true`}),Q(o,`aria-pressed`,G(s)===`no`||G(s)===`false`),o.disabled=!r(),c=ii(o,1,``,null,c,{selected:G(s)===`no`||G(s)===`false`})}),gr(`click`,i,()=>N(s,`yes`)),gr(`click`,o,()=>N(s,`no`)),Y(e,t)},_=e=>{var t=ra();Kr(t,5,()=>(q(n()),K(()=>n().question.choices??[])),Hr,(e,t)=>{var n=na(),r=I(n,!0),i={};R(()=>{X(r,G(t)),i!==(i=G(t))&&(n.value=(n.__value=i)??``)}),Y(e,n)}),E(t),ci(t),R(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),t.disabled=!r()}),li(t,()=>G(s),e=>N(s,e)),Y(e,t)},v=e=>{var t=aa();Kr(t,5,()=>(q(n()),K(()=>n().question.kind===`multi-select`?n().question.choices??[]:n().question.allowedNames??[])),Hr,(e,t)=>{var n=ia(),i=F(n);hi(i);var a=I(L(i),!0);E(n),R(e=>{gi(i,e),i.disabled=!r(),X(a,G(t))},[()=>(G(c),G(t),K(()=>G(c).includes(G(t))))]),gr(`change`,i,()=>N(c,Qi(G(c),G(t)))),Y(e,n)}),E(t),R(()=>Q(t,`aria-label`,(q(n()),K(()=>n().question.label)))),Y(e,t)},y=e=>{var t=oa();hi(t),R(()=>{Q(t,`aria-label`,(q(n()),K(()=>n().question.label))),Q(t,`type`,(q(n()),K(()=>n().question.kind===`number`?`number`:`text`))),Q(t,`min`,(q(n()),K(()=>n().question.kind===`number`?0:void 0))),t.disabled=!r()}),bi(t,()=>G(s),e=>N(s,e)),Y(e,t)};Z(h,e=>{q(n()),K(()=>n().question.kind===`boolean`)?e(g):(q(n()),K(()=>n().question.kind===`choice`)?e(_,1):(q(n()),K(()=>n().question.kind===`multi-select`||n().question.kind===`scope-overrides`)?e(v,2):e(y,-1)))});var b=L(h,2),x=I(b),S=L(b,2);{let e=mt(()=>!r()||i());qi(S,{label:`Continue`,arrow:!0,onClick:()=>a()($i(n(),G(s),G(c))),get disabled(){return G(e)}})}R((e,t)=>{X(f,e),X(x,`Suggested answer: ${t??``} · You can review choices again before creating your setup PAT.`)},[()=>(q(n()),K(()=>n().question.label.replace(` (Space toggles, Enter confirms)`,``))),()=>(q(n()),K(()=>String(n().question.defaultValue)||`none`))]),Y(e,l),k()}_r([`click`,`change`]);var la=J(``),ua=J(`
                                                                                                                    `);function da(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`controller`,8),i=$(t,`busy`,8),a=$(t,`onSubmit`,8);Ci();var o=ua();Kr(o,5,()=>(q(n()),K(()=>n().choices)),Hr,(e,t)=>{var n=la(),o=I(F(n),!0);ke(),E(n),R(()=>{n.disabled=!r()||i(),X(o,G(t))}),gr(`click`,n,()=>a()(G(t))),Y(e,n)}),E(o),Y(e,o),k()}_r([`click`]);var fa=J(`Open the official GitHub PAT form

                                                                                                                    Check the signed-in account, select Only select repositories, then select this repository. GitHub handles 2FA and creates the PAT.

                                                                                                                    `,1),pa=J(`

                                                                                                                    Sent only to this local process. It will not be shown again or saved in browser storage.

                                                                                                                    `),ma=J(` `,1);function ha(e,t){O(t,!1);let n=M(),r=$(t,`prompt`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8),s=M(``);function c(){let e=G(s);r().kind===`secret`&&N(s,``),o()(e)}Cn(()=>q(r()),()=>{N(n,Wi(r().link))}),wn(),Ci();var l=ma(),u=on(l),d=e=>{var t=fa(),r=on(t);ke(),R(()=>Q(r,`href`,G(n))),Y(e,t)};Z(u,e=>{G(n)&&e(d)});var f=L(u,2),p=I(f,!0),m=L(f,2);hi(m);var h=L(m,2),g=e=>{Y(e,pa())};Z(h,e=>{q(r()),K(()=>r().kind===`secret`)&&e(g)});var _=L(h,2);{let e=mt(()=>(q(i()),q(a()),q(r()),G(s),K(()=>!i()||a()||!r().optional&&!G(s).trim())));qi(_,{label:`Continue`,arrow:!0,onClick:c,get disabled(){return G(e)}})}R(()=>{X(p,(q(r()),K(()=>r().kind===`secret`?`Paste the value here`:`Your answer`))),Q(m,`type`,(q(r()),K(()=>r().kind===`secret`?`password`:`text`))),m.disabled=!i(),Q(m,`placeholder`,(q(r()),K(()=>r().kind===`secret`?`Hidden after submission`:`Type your answer`)))}),bi(m,()=>G(s),e=>N(s,e)),Y(e,l),k()}var ga=J(`
                                                                                                                  • `),_a=J(`

                                                                                                                      `),va=J(`

                                                                                                                      Before you continue

                                                                                                                        `),ya=J(`

                                                                                                                        Review exactly what this run may change. The bot PAT and any additional credentials are collected next.

                                                                                                                        `,1);function ba(e,t){O(t,!1);let n=M(),r=$(t,`prompt`,8),i=$(t,`controller`,8),a=$(t,`busy`,8),o=$(t,`onSubmit`,8);Cn(()=>q(r()),()=>{N(n,[{title:`Files`,items:r().plan.files},{title:`Workflows`,items:r().plan.workflows},{title:`Variables`,items:r().plan.variables},{title:`Secret names`,items:r().plan.secrets}])}),wn(),Ci();var s=ya(),c=L(on(s),2);Kr(c,5,()=>G(n),Hr,(e,t)=>{var n=_a(),r=F(n),i=F(r),a=I(L(i),!0);E(r);var o=L(r);Kr(o,5,()=>(G(t),K(()=>G(t).items)),Hr,(e,t)=>{var n=ga(),r=I(n,!0);R(()=>X(r,G(t))),Y(e,n)}),E(o),E(n),R(()=>{X(i,`${G(t),K(()=>G(t).title)??``} `),X(a,(G(t),K(()=>G(t).items.length)))}),Y(e,n)}),E(c);var l=L(c,2),u=e=>{var t=va(),n=L(F(t));Kr(n,5,()=>(q(r()),K(()=>r().plan.warnings)),Hr,(e,t)=>{var n=ga(),r=I(n,!0);R(()=>X(r,G(t))),Y(e,n)}),E(n),E(t),Y(e,t)};Z(l,e=>{q(r()),K(()=>r().plan.warnings.length)&&e(u)});var d=L(l,2),f=F(d);{let e=mt(()=>!i()||a());qi(f,{label:`Stop here`,variant:`secondary`,onClick:()=>o()(`decline`),get disabled(){return G(e)}})}var p=L(f);{let e=mt(()=>!i()||a());qi(p,{label:`Approve this plan`,arrow:!0,onClick:()=>o()(`approve`),get disabled(){return G(e)}})}E(d),Y(e,s),k()}var xa=J(`

                                                                                                                        `),Sa=J(`
                                                                                                                        CURRENT DECISION
                                                                                                                        `);function Ca(e,t){O(t,!1);let n=$(t,`prompt`,8),r=$(t,`revision`,8),i=$(t,`promptRevision`,8),a=$(t,`controller`,8),o=$(t,`busy`,8),s=$(t,`onSubmit`,8);Ci();var c=Sa(),l=F(c),u=I(L(F(l)));E(l);var d=L(l,2),f=e=>{var t=xa(),r=I(t,!0);R(()=>X(r,(q(n()),K(()=>n().description)))),Y(e,t)};Z(d,e=>{q(n()),K(()=>`description`in n()&&n().description)&&e(f)}),Vr(L(d,2),i,e=>{var t=Er(),r=on(t),i=e=>{ca(e,{get prompt(){return n()},get controller(){return a()},get busy(){return o()},get onSubmit(){return s()}})},c=e=>{da(e,{get prompt(){return n()},get controller(){return a()},get busy(){return o()},get onSubmit(){return s()}})},l=e=>{ha(e,{get prompt(){return n()},get controller(){return a()},get busy(){return o()},get onSubmit(){return s()}})},u=e=>{ba(e,{get prompt(){return n()},get controller(){return a()},get busy(){return o()},get onSubmit(){return s()}})};Z(r,e=>{q(n()),K(()=>n().kind===`question`)?e(i):(q(n()),K(()=>n().kind===`choice`||n().kind===`confirm`)?e(c,1):(q(n()),K(()=>n().kind===`text`||n().kind===`secret`)?e(l,2):(q(n()),K(()=>n().kind===`plan`)&&e(u,3))))}),Y(e,t)}),E(c),R(()=>X(u,`SESSION ${r()??``}`)),Y(e,c),k()}var wa=J(` `),Ta=J(`
                                                                                                                      • `),Ea=J(`

                                                                                                                          Conditional grants depend on the choices and GitHub facts. A final audit runs before setup changes.
                                                                                                                          `),Da=J(`

                                                                                                                          Permissions follow your choices

                                                                                                                          We’ll show the exact grants before you create each PAT. Nothing is created just by opening this page.

                                                                                                                          `),Oa=J(``);function ka(e,t){O(t,!1);let n=$(t,`view`,8);Ci();var r=Oa(),i=F(r),a=I(L(F(i),3),!0);E(i);var o=L(i,2),s=e=>{var t=Ea(),r=L(F(t)),i=I(r),a=L(r),o=I(a,!0),s=L(a,2);Kr(s,5,()=>(q(n()),K(()=>n().permissions.report?.checks??n().permissions.requirements??[])),Hr,(e,t)=>{var n=Ta(),r=F(n),i=F(r),a=L(i),o=I(a),s=I(L(a),!0);E(r);var c=L(r),l=F(c,!0),u=L(l),d=e=>{var n=wa(),r=I(n,!0);R(()=>X(r,(G(t),K(()=>G(t).status)))),Y(e,n)};Z(u,e=>{`status`in G(t)&&e(d)}),E(c),E(n),R(()=>{X(i,`${G(t),K(()=>G(t).permission)??``} `),X(o,`${G(t),K(()=>G(t).scope)??``} · ${G(t),K(()=>G(t).applicability)??``}`),X(s,(G(t),K(()=>G(t).reason))),X(l,(G(t),K(()=>G(t).level)))}),Y(e,n)}),E(s),ke(2),E(t),R(()=>{X(i,`${q(n()),K(()=>n().permissions.role===`setup`?`Setup PAT`:`Bot PAT`)??``} access`),X(o,(q(n()),K(()=>n().permissions.report?`Read-only access check`:`Provisional least-privilege grants`)))}),Y(e,t)},c=e=>{Y(e,Da())};Z(o,e=>{q(n()),K(()=>n().permissions)?e(s):e(c,-1)}),E(r),R(()=>X(a,(q(n()),K(()=>n().repository)))),Y(e,r),k()}var Aa=J(`

                                                                                                                          `);function ja(e,t){O(t,!1);let n=M(),r=M(),i=$(t,`outcome`,8),a=$(t,`controller`,8),o=$(t,`onClose`,8);Cn(()=>q(i()),()=>{N(n,i()===`complete`?`Your configuration was applied`:i()===`dry-run`?`No changes were made`:i()===`cancelled`||i()===`blocked`?`No setup changes started`:`Check partial changes before retrying`)}),Cn(()=>q(i()),()=>{N(r,i()===`complete`?`The temporary setup PAT is not revoked automatically. Delete it in GitHub after confirming your setup. Keep the bot PAT until you rotate the installed Secret.`:i()===`partial`?`A bot Secret write or another change may have succeeded. Inspect GitHub and run copilot doctor before replacing or deleting its PAT.`:`Copilot did not begin applying setup changes. PATs you created in GitHub still exist until you delete them there.`)}),wn();var s=Aa(),c=F(s),l=I(c,!0),u=L(c),d=I(u,!0),f=L(u),p=I(f,!0),m=L(f,2),h=e=>{qi(e,{label:`Close local session`,get onClick(){return o()}})};Z(m,e=>{a()&&e(h)}),E(s),R(()=>{X(l,i()===`complete`?`✓`:`!`),X(d,G(n)),X(p,G(r))}),Y(e,s),k()}var Ma=J(`

                                                                                                                          Working on the next step

                                                                                                                          The local process is checking your answers and preparing the next decision. Keep this page open.

                                                                                                                          `);function Na(e){Y(e,Ma())}var Pa=J(`Find the 16-character pairing code in the terminal that started copilot setup --web. Enter it here to view or control this setup. The code is never placed in the browser URL or stored after this page closes.`,1),Fa=J(`
                                                                                                                          PRIVATE LOCAL SESSION

                                                                                                                          Keep the code private. After refreshing this page, enter it again to reconnect.

                                                                                                                          `);function Ia(e,t){O(t,!1);let n=$(t,`busy`,8),r=$(t,`onPair`,8),i=$(t,`mode`,8,`pair`),a=M(``);function o(){let e=G(a);N(a,``),r()(e)}Ci();var s=Fa(),c=L(F(s),2),l=I(F(c),!0);E(c);var u=L(c,2),d=F(u),f=e=>{var t=Pa();ke(2),Y(e,t)},p=e=>{Y(e,Tr(`Re-enter the pairing code from the launching terminal to take control. The previous tab will become read-only.`))};Z(d,e=>{i()===`pair`?e(f):e(p,-1)}),E(u);var m=L(u,2),h=L(F(m),2);hi(h);var g=L(h,4);{let e=mt(()=>i()===`pair`?`Connect to local setup`:`Take control in this tab`),t=mt(()=>(q(n()),G(a),K(()=>n()||G(a).trim().length!==16)));qi(g,{get label(){return G(e)},arrow:!0,onClick:o,get disabled(){return G(t)}})}E(m),E(s),R(()=>{Q(s,`aria-label`,i()===`pair`?`Pair this browser with the local setup session`:`Take control of this local setup session`),X(l,i()===`pair`?`Pair this browser`:`Take control in this tab`),h.disabled=n()}),hr(`submit`,m,e=>{e.preventDefault(),o()}),bi(h,()=>G(a),e=>N(a,e)),Y(e,s),k()}var La=J(``),Ra=J(`
                                                                                                                          `),za=J(` `,1),Ba=J(``),Va=J(`
                                                                                                                          `,1),Ha=J(`
                                                                                                                          LOCALHOST ONLY NO CLOUD SETUP ACCOUNT GITHUB OWNS PAT ISSUANCE
                                                                                                                          `);function Ua(e,t){O(t,!1);let n=()=>ji(a,`$session`,r),[r,i]=Mi(),a=Pi();Rr(()=>{let e=window.setInterval(()=>{n().paired&&!n().view?.outcome&&a.refresh()},900);return()=>window.clearInterval(e)});async function o(){window.confirm(`Cancel this local setup session? PATs already created in GitHub will still exist.`)&&await a.cancel()}async function s(e){let t=n().view?.promptRevision;t!==void 0&&await a.submit(t,e)}Ci();var c=Ha();Qr(`16t12jp`,e=>{Y(e,La())});var l=F(c);{let e=mt(()=>n().view?.journey);Li(l,{get journey(){return G(e)}})}var u=L(l,2),d=F(u);{let e=mt(()=>n().view?.repository);Vi(d,{get repository(){return G(e)}})}var f=L(d,2),p=F(f),m=e=>{Ui(e,{get view(){return n().view}})};Z(p,e=>{n().paired&&e(m)});var h=L(p,2),g=e=>{var t=Ra(),r=L(F(t));E(t),R(()=>X(r,` Reviewing saved choices — pass ${n().view.journey.choiceReviewPass??``}. This is the same setup run, not a restart.`)),Y(e,t)};Z(h,e=>{n().view?.journey?.choiceReviewPass&&n().view.journey.choiceReviewPass>1&&!n().view.outcome&&e(g)});var _=L(h,2),v=e=>{var t=za(),r=on(t);Xi(r,{tone:`warning`,title:`Read-only tab`,message:`Another tab controls this session. Enter the pairing code from the launching terminal to take over.`}),Ia(L(r,2),{mode:`takeover`,get busy(){return n().busy},get onPair(){return a.takeOver}}),Y(e,t)};Z(_,e=>{!n().controller&&n().view&&e(v)});var y=L(_,2),b=e=>{Xi(e,{tone:`error`,title:`Needs attention`,get message(){return n().error}})};Z(y,e=>{n().error&&e(b)});var x=L(y,2),S=e=>{{let t=mt(()=>n().view.message.tone===`success`?`Checked`:n().view.message.tone===`warning`?`Please note`:n().view.message.tone===`error`?`Needs attention`:`Progress update`);Xi(e,{get tone(){return n().view.message.tone},get title(){return G(t)},get message(){return n().view.message.text},get link(){return n().view.message.link}})}};Z(x,e=>{n().view?.message&&e(S)});var ee=L(x,2),te=e=>{Ia(e,{get busy(){return n().busy},get onPair(){return a.pair}})},ne=e=>{ja(e,{get outcome(){return n().view.outcome},get controller(){return n().controller},get onClose(){return a.close}})},re=e=>{var t=Va(),r=on(t),i=F(r);Ca(i,{get prompt(){return n().view.prompt},get revision(){return n().view.revision},get promptRevision(){return n().view.promptRevision},get controller(){return n().controller},get busy(){return n().busy},onSubmit:s}),ka(L(i,2),{get view(){return n().view}}),E(r);var a=L(r,2),c=e=>{var t=Ba();R(()=>t.disabled=n().busy),gr(`click`,t,o),Y(e,t)};Z(a,e=>{n().controller&&n().view.journey?.current!==`Apply`&&e(c)}),Y(e,t)},ie=e=>{Na(e,{})};Z(ee,e=>{n().paired?n().view?.outcome?e(ne,1):n().view?.prompt?e(re,2):e(ie,-1):e(te)}),ke(2),E(f),E(u),E(c),Y(e,c),k(),i()}_r([`click`]),Nr(Ua,{target:document.getElementById(`app`)}); \ No newline at end of file diff --git a/build/web/assets/index-lEwj5VdR.css b/build/web/assets/index-lEwj5VdR.css deleted file mode 100644 index 38607f317..000000000 --- a/build/web/assets/index-lEwj5VdR.css +++ /dev/null @@ -1 +0,0 @@ -:root{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light dark;--page:#f6f8f7;--side:#102423;--side-line:#31504b;--side-text:#e8f5f0;--surface:#fff;--surface-soft:#f3f7f5;--line:#d7e3df;--control-line:#748b82;--text:#18312c;--muted:#58736b;--accent:#176e5e;--accent-strong:#075743;--accent-tint:#dff4e9;--focus:#966000;--warn:#76510d;--warn-bg:#fff6df;--error:#a73434;--error-bg:#fff0ec;--shadow:0 18px 50px #1e403414;font-family:Inter,ui-sans-serif,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}@media (prefers-color-scheme:dark){:root{--lightningcss-light: ;--lightningcss-dark:initial}:root:not([data-theme=light]){--page:#0d1716;--side:#10201d;--side-line:#294a41;--side-text:#eaf7f1;--surface:#182722;--surface-soft:#20342d;--line:#355247;--control-line:#688f7f;--text:#eaf5ee;--muted:#adccbc;--accent:#7ed6ac;--accent-strong:#a4edc2;--accent-tint:#234b38;--focus:#ffca6a;--warn:#ffdd8a;--warn-bg:#463b21;--error:#ffc0b7;--error-bg:#4a2b2a;--shadow:0 18px 50px #00000024}}:root[data-theme=dark]{--page:#0d1716;--side:#10201d;--side-line:#294a41;--side-text:#eaf7f1;--surface:#182722;--surface-soft:#20342d;--line:#355247;--control-line:#688f7f;--text:#eaf5ee;--muted:#adccbc;--accent:#7ed6ac;--accent-strong:#a4edc2;--accent-tint:#234b38;--focus:#ffca6a;--warn:#ffdd8a;--warn-bg:#463b21;--error:#ffc0b7;--error-bg:#4a2b2a;--shadow:0 18px 50px #00000024}:root[data-theme=light]{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light}:root[data-theme=dark]{--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}*{box-sizing:border-box}body{background:var(--page);color:var(--text);margin:0}button,input,select{font:inherit}button{cursor:pointer}button:disabled{cursor:not-allowed;opacity:.5}:focus-visible{outline:3px solid var(--focus);outline-offset:3px}a{color:var(--accent-strong);text-underline-offset:3px}.card,.context-card{background:var(--surface);border:1px solid var(--line);box-shadow:var(--shadow);border-radius:15px}.primary{border:1px solid var(--accent-strong);background:var(--accent-strong);color:var(--side);border-radius:8px;min-height:43px;padding:12px 18px;font-size:12px;font-weight:800}:root[data-theme=light] .primary,:root:not([data-theme=dark]) .primary{color:#fff}@media (prefers-color-scheme:dark){:root:not([data-theme=light]) .primary{color:#0d2419}}.primary span{margin-left:18px}.primary:hover:not(:disabled){filter:brightness(1.1)}.secondary{border:1px solid var(--control-line);color:var(--text);background:var(--surface-soft);border-radius:8px;padding:11px 16px;font-size:12px;font-weight:700}@media (prefers-reduced-motion:reduce){*,:before,:after{scroll-behavior:auto!important;transition-duration:.01ms!important;animation-duration:.01ms!important}}.shell{grid-template-columns:minmax(250px,288px) minmax(0,1fr);min-height:100vh;display:grid}.sidebar{background:var(--side);color:var(--side-text);flex-direction:column;height:100vh;padding:34px 28px;display:flex;position:sticky;top:0}.brand{letter-spacing:-.035em;align-items:center;gap:13px;display:flex}.brand-mark{color:#0c3021;background:#75d9a0;border-radius:11px;place-items:center;width:37px;height:37px;font-size:26px;line-height:1;display:grid}.brand strong{font-size:23px;line-height:1;display:block}.brand small{letter-spacing:.23em;color:#aac8bd;margin-top:5px;font-size:9px;font-weight:800;display:block}.rail-caption{color:#9dbab0;letter-spacing:.18em;margin:78px 0 22px 7px;font-size:10px;font-weight:800}.steps{margin:0;padding:0;list-style:none;position:relative}.steps:before{content:"";background:var(--side-line);width:1px;position:absolute;top:22px;bottom:22px;left:19px}.steps li{color:#a6c2b7;border-radius:10px;align-items:center;gap:16px;min-height:55px;padding:8px 12px 8px 1px;font-size:13px;font-weight:600;display:flex;position:relative}.steps li.current{color:#fff;background:#25443b}.steps li.completed{color:#dbf2e4}.step-index{border:1px solid var(--side-line);background:var(--side);letter-spacing:.04em;border-radius:50%;flex:0 0 37px;place-items:center;height:37px;font-size:11px;font-weight:800;display:grid}.steps .current .step-index{color:#102b1d;background:#80dba8;border-color:#80dba8}.steps .completed .step-index{color:#b9f8c9;background:#204c37;border-color:#45966b;font-size:15px}.sidebar-note{border:1px solid var(--side-line);background:#ffffff09;border-radius:13px;gap:13px;margin-top:auto;padding:19px 16px;display:flex}.sidebar-note>span{color:#8fe1ae;font-size:20px}.sidebar-note strong{font-size:12px}.sidebar-note p{color:#afcabe;margin:6px 0 0;font-size:11px;line-height:1.6}.main{min-width:0}.topbar{border-bottom:1px solid var(--line);background:var(--surface);justify-content:space-between;align-items:center;gap:16px;height:80px;padding:0 clamp(24px,4vw,70px);display:flex}.breadcrumb{align-items:center;gap:12px;min-width:0;font-size:12px;display:flex}.breadcrumb span:first-child{color:var(--muted);letter-spacing:.14em;font-size:10px;font-weight:800}.breadcrumb span:nth-child(2){color:var(--muted)}.breadcrumb strong{white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.top-actions{flex-shrink:0;align-items:center;gap:18px;display:flex}.local-pill{color:var(--accent-strong);background:var(--accent-tint);letter-spacing:.08em;white-space:nowrap;border-radius:6px;padding:8px 11px;font-size:10px;font-weight:800}.pulse-dot{background:currentColor;border-radius:50%;width:6px;height:6px;margin-right:5px;display:inline-block}.theme-switch{border:1px solid var(--line);background:var(--surface-soft);border-radius:8px;padding:3px;display:flex}.theme-switch button{color:var(--muted);background:0 0;border:0;border-radius:5px;min-width:31px;height:27px;font-size:11px}.theme-switch button.active{background:var(--surface);color:var(--text);font-weight:800;box-shadow:0 1px 4px #0000001f}.content{max-width:1320px;margin:0 auto;padding:52px clamp(24px,4vw,70px) 35px}.eyebrow{color:var(--accent-strong);letter-spacing:.18em;align-items:center;gap:10px;font-size:10px;font-weight:900;display:flex}.eyebrow-line{background:var(--accent);width:21px;height:2px}.eyebrow-count{color:var(--muted);letter-spacing:.09em;margin-left:8px}h1{letter-spacing:-.045em;max-width:860px;margin:15px 0 13px;font-size:clamp(30px,3vw,45px);line-height:1.15}.lede{color:var(--muted);max-width:700px;margin:0 0 30px;font-size:14px;line-height:1.65}.workspace-grid{grid-template-columns:minmax(0,1.65fr) minmax(230px,.8fr);align-items:start;gap:19px;display:grid}.context-column{gap:17px;display:grid}.context-card{box-shadow:none;padding:25px}.context-icon{background:var(--accent-tint);width:32px;height:32px;color:var(--accent-strong);border-radius:8px;place-items:center;font-size:19px;display:grid}.context-card h2{letter-spacing:-.015em;margin:17px 0 7px;font-size:14px}.context-card p,.context-card>small{color:var(--muted);margin:0 0 12px;font-size:12px;line-height:1.65;display:block}.context-card code{background:var(--surface-soft);overflow-wrap:anywhere;border-radius:6px;padding:10px;font-size:11px;display:block}.permissions ul{max-height:270px;margin:8px 0 13px;padding:0;list-style:none;overflow:auto}.permissions li{border-bottom:1px solid var(--line);justify-content:space-between;gap:10px;padding:9px 0;font-size:11px;display:flex}.permissions li small{color:var(--muted);margin-top:3px;display:block}.permissions li strong{color:var(--accent-strong);text-transform:uppercase;font-size:9px}footer{color:var(--muted);opacity:.85;letter-spacing:.11em;margin-top:40px;font-size:9px;font-weight:700}footer span{margin:0 8px}.decision-card{min-height:360px;padding:clamp(25px,3vw,40px)}.card-header{justify-content:space-between;align-items:center;gap:12px;margin-bottom:29px;display:flex}.card-kicker{color:var(--accent-strong);letter-spacing:.17em;font-size:10px;font-weight:900}.revision{color:var(--muted);letter-spacing:.08em;font-size:10px}.description{white-space:pre-line;color:var(--muted);margin-top:0;font-size:13px;line-height:1.65}.question-heading{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:10px;margin-bottom:15px;display:flex}.question-heading h2,.decision-card>label{margin:0 0 10px;font-size:15px;font-weight:700;line-height:1.4;display:block}.phase-tag{color:var(--accent-strong);background:var(--accent-tint);letter-spacing:.07em;border-radius:5px;padding:6px 8px;font-size:9px;font-weight:900}input[type=text],input[type=password],input[type=number],select{border:1px solid var(--control-line);background:var(--surface-soft);width:100%;min-height:46px;color:var(--text);border-radius:8px;padding:10px 13px}input[type=checkbox]{accent-color:var(--accent);width:17px;height:17px}.field-help{color:var(--muted);margin:14px 0 24px;font-size:12px;line-height:1.6}.segmented{gap:9px;display:flex}.segmented button{border:1px solid var(--control-line);color:var(--text);background:var(--surface-soft);border-radius:8px;flex:1;padding:13px;font-weight:700}.segmented button.selected{border-color:var(--accent);background:var(--accent-tint);color:var(--accent-strong)}.check-grid{gap:7px;max-height:280px;display:grid;overflow-y:auto}.check-option{background:var(--surface-soft);border:1px solid var(--control-line);border-radius:7px;align-items:center;gap:10px;padding:10px 13px;font-size:12px;display:flex}.choice-list{gap:9px;display:grid}.choice-card{text-align:left;background:var(--surface-soft);width:100%;min-height:52px;color:var(--text);border:1px solid var(--control-line);border-radius:8px;justify-content:space-between;align-items:center;padding:13px 15px;font-size:13px;font-weight:650;display:flex}.choice-card:hover:not(:disabled){border-color:var(--accent);background:var(--accent-tint)}.github-link{background:var(--accent-tint);border:1px solid var(--accent);border-radius:8px;margin:0 0 12px;padding:14px;font-size:13px;font-weight:800;text-decoration:none;display:block}.github-link span{float:right}.plan-sections{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;display:grid}.plan-sections>div,.plan-warnings{background:var(--surface-soft);border:1px solid var(--line);border-radius:8px;padding:13px}.plan-sections h3,.plan-warnings h3{justify-content:space-between;margin:0 0 8px;font-size:12px;display:flex}.plan-sections h3 span{color:var(--accent-strong)}.plan-sections ul,.plan-warnings ul{overflow-wrap:anywhere;max-height:120px;margin:0;padding-left:18px;font-size:11px;line-height:1.7;overflow:auto}.plan-warnings{color:var(--warn);background:var(--warn-bg);margin-top:10px}.button-row{justify-content:space-between;gap:10px;margin-top:18px;display:flex}.review-pass,.banner{white-space:pre-line;border-radius:8px;margin:0 0 20px;padding:14px 18px;font-size:12px;line-height:1.5}.review-pass{color:var(--accent-strong);background:var(--accent-tint);border:1px solid var(--accent)}.review-pass span{margin-right:8px;font-weight:800}.banner{background:var(--surface-soft);border:1px solid var(--line)}.banner p{margin:5px 0 0}.banner.warning{color:var(--warn);background:var(--warn-bg);border-color:var(--warn)}.banner.error{color:var(--error);background:var(--error-bg);border-color:var(--error)}.banner.success{color:var(--accent-strong);background:var(--accent-tint);border-color:var(--accent)}.banner button{margin-top:12px}.cancel-link{color:var(--muted);background:0 0;border:0;margin-top:18px;padding:5px 0;font-size:12px;text-decoration:underline}.result-card,.waiting-card{max-width:750px;padding:36px}.result-icon{background:var(--accent-tint);width:43px;height:43px;color:var(--accent-strong);border-radius:50%;place-items:center;font-size:22px;display:grid}.result-card h2,.waiting-card h2{margin:18px 0 10px;font-size:21px}.result-card p,.waiting-card p{color:var(--muted);font-size:13px;line-height:1.6}.result-links{flex-wrap:wrap;align-items:center;gap:20px;margin:22px 0;font-size:12px;display:flex}.result-links code{background:var(--surface-soft);border-radius:5px;padding:8px}.spinner{border:3px solid var(--line);border-top-color:var(--accent);border-radius:50%;width:25px;height:25px;animation:1s linear infinite spin}@keyframes spin{to{transform:rotate(360deg)}}@media (width<=1100px){.workspace-grid{grid-template-columns:1fr}.context-column{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (width<=780px){.shell{display:block}.sidebar{height:auto;padding:16px 20px;position:static}.rail-caption,.sidebar-note{display:none}.steps{gap:4px;margin-top:18px;display:flex;overflow-x:auto}.steps:before{display:none}.steps li{flex:none;gap:6px;min-height:37px;padding:4px 7px;font-size:11px}.step-index{flex-basis:26px;width:26px;height:26px}.topbar{flex-wrap:wrap;height:auto;min-height:65px;padding:12px 20px}.content{padding:28px 20px}}@media (width<=540px){.context-column,.plan-sections{grid-template-columns:1fr}.top-actions{justify-content:space-between;width:100%}.decision-card{padding:22px}.breadcrumb{max-width:100%}h1{font-size:29px}} diff --git a/build/web/index.html b/build/web/index.html index f5ae56b62..bf7735420 100644 --- a/build/web/index.html +++ b/build/web/index.html @@ -5,8 +5,8 @@ Copilot · Setup studio - - + +
                                                                                                                          diff --git a/docs/authentication.mdx b/docs/authentication.mdx index 8fbfdfc11..86d7e1372 100644 --- a/docs/authentication.mdx +++ b/docs/authentication.mdx @@ -50,6 +50,19 @@ additional grants, setup stops before applying it and prints a corrected link. Update the PAT in GitHub or create a replacement, then rerun setup. Guided setup shows the account returned by GitHub and asks you to confirm it. +For Projects, this early choice is only **whether you want the integration**. +Listing private organization Projects requires authorization, so the assistant +cannot reliably ask you to select Project numbers before the setup PAT exists. +If you opt in, the guided setup PAT requests organization **Projects: read**. +After you enter that PAT, the assistant lists the accessible Projects and lets +you choose their numeric Project numbers and, when available, their Status +options. You can enter a Project URL or number manually if discovery is +unavailable, and must verify that the bot account can access each chosen +Project. The separate bot PAT needs **Projects: read and write** to add and +update items when the workflows run. Fine-grained PATs cannot use the same +personal-Projects listing endpoint as organization Projects; personal-owner +setups use the documented manual path rather than pretending to discover them. + If you choose **Review all setup choices again** at the permission preview, the terminal clearly marks a second pass over your saved answers. Press Enter to keep an answer, or change it; afterward you return to the same PAT review @@ -74,12 +87,13 @@ before it expires. GitHub's documentation is inconsistent: its [PAT limitations list](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#fine-grained-personal-access-tokens-limitations) calls out the Checks API, while the [check-runs endpoint reference](https://docs.github.com/en/rest/checks/runs#list-check-runs-for-a-git-reference) -lists fine-grained PATs with `Checks` read. The documented [PAT form -parameters](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#repository-permissions) -do not include `Checks`, so setup cannot prefill a guided bot link for guarded -approval. If your manual fine-grained PAT form offers `Checks` read, select it; -otherwise use a compatible credential. In either case, confirm access with the -permission audit. Existing command-line token flags also remain, but putting +lists fine-grained PATs with `Checks: read`. The published [PAT URL-permission +table](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#repository-permissions) +does not list `checks`, although the form may currently accept `checks=read`. +The guided setup and bot links include it when required, but you must inspect +the final GitHub form and permission audit. If the form ignores that field, +select `Checks: read` manually or use a compatible credential. Setup never +assumes that a prefilled URL proves API access. Existing command-line token flags also remain, but putting a PAT in a command can expose it in shell history or process listings. ## Permission tables in `copilot setup` @@ -192,7 +206,7 @@ cannot obtain an authoritative remote snapshot or required selected inventory access, it stops before Secrets, Variables, labels, issue types, and tag writes with bounded recovery guidance, including for a repository-scope default. -GitHub does not reveal Secret values through its API. Copilot validates new credentials with provider metadata requests and validates existing remote Secrets through the read-only `copilot_credential_health.yml` workflow. Setup proves the exact file on the selected main ref and dispatches that path only when the workflow is also registered or installed on GitHub's default branch; doctor remains query-only and expects the normally indexed installed workflow. The health workflow reports each requested credential independently, but that bounded reachability result is not a permission audit. If `PAT` already exists, interactive setup asks you to re-enter it and runs the complete workflow-PAT permission matrix before provisioning; unattended setup must supply `PAT` again or stops before mutation. Temporary workflow bootstrap is available only during setup. A preauthenticated Codex session is runner state, not a Secret: it is accepted only when the runtime preflight can execute `codex login status` successfully. +GitHub does not reveal Secret values through its API. Copilot validates new credentials with provider metadata requests and may validate existing remote Secrets by dispatching the installed `copilot_credential_health.yml` workflow. Setup proves the exact file on the selected main ref and dispatches that path only when the workflow is also registered or installed on GitHub's default branch. Plain `copilot doctor` may create an Actions run; `copilot doctor --read-only` never dispatches it and leaves Secret values unverified. The health workflow reports each requested credential independently, but that bounded reachability result is not a permission audit. If `PAT` already exists, interactive setup asks you to re-enter it and runs the complete workflow-PAT permission matrix before provisioning; unattended setup must supply `PAT` again or stops before mutation. Temporary workflow bootstrap is available only during setup. A preauthenticated Codex session is runner state, not a Secret: it is accepted only when the runtime preflight can execute `codex login status` successfully. Terminal setup may attempt a temporary credential-health workflow creation before final Apply when checking existing Secrets. That request can create Git commits @@ -250,7 +264,7 @@ For comment-driven assistance, read-only commands are available to anyone who ca - The person running setup needs a separate fine-grained PAT. Give it only the permissions required by the selected setup features: repository Metadata read and Contents read for inspecting repository files and installed workflows; Administration read when release/hotfix setup or doctor must inspect classic branch protection; Issues write for labels; Variables write for Repository Variables; Secrets read/write when provisioning Secrets; Actions read/write when checking or dispatching credential health; and organization Issue Types or Projects permissions only when those integrations are selected. There is no separate Workflows read permission for inspection. If setup will use organization-level Actions Secrets or Variables, the token also needs the corresponding organization Actions Secrets/Variables read and write permissions. Organization scope is valid only for repositories owned by an organization; setup detects personal repositories and stops before attempting organization writes. For existing Secrets, an installed `copilot_credential_health.yml` requires Actions write for dispatch but does not require Contents or Workflows write. Workflows write and Contents write appear only when the workflow is independently confirmed missing and temporary bootstrap is required; unavailable or unknown workflow state never authorizes temporary creation. Contents write can also be required for an initial tag or another explicitly selected repository mutation. + The person running setup needs a separate fine-grained PAT. Give it only the permissions required by the selected setup features: repository Metadata read and Contents read for inspecting repository files and installed workflows; Administration read when release/hotfix setup or doctor must inspect classic branch protection; Issues write for labels; Variables write for Repository Variables; Secrets read/write when provisioning Secrets; Actions read/write when checking or dispatching credential health; and organization Issue Types or Projects permissions only when those integrations are selected. Setup uses organization Projects **read**, not write, to list Projects after the PAT is entered. There is no separate Workflows read permission for inspection. If setup will use organization-level Actions Secrets or Variables, the token also needs the corresponding organization Actions Secrets/Variables read and write permissions. Organization scope is valid only for repositories owned by an organization; setup detects personal repositories and stops before attempting organization writes. For existing Secrets, an installed `copilot_credential_health.yml` requires Actions write for dispatch but does not require Contents or Workflows write. Workflows write and Contents write appear only when the workflow is independently confirmed missing and temporary bootstrap is required; unavailable or unknown workflow state never authorizes temporary creation. Contents write can also be required for an initial tag or another explicitly selected repository mutation. Enter it in the hidden prompt, or use `--token`/`PERSONAL_ACCESS_TOKEN` for automation. It remains in memory for the command and is not written to `.env`, a config file, or the `PAT` Secret. diff --git a/docs/configuration-checklist.mdx b/docs/configuration-checklist.mdx index 23df65da8..fce6dd71b 100644 --- a/docs/configuration-checklist.mdx +++ b/docs/configuration-checklist.mdx @@ -60,7 +60,7 @@ If guarded PR approval is selected, confirm the exact test/coverage producer tup - [ ] Setup PAT and workflow PAT permission reports contain only stable permission IDs, target scopes, access levels, semantic statuses, and bounded reasons; tokens and raw provider responses are absent. - [ ] `copilot doctor` reports stable check IDs with `pass`, `warn`, `fail`, or dependency-blocked `skipped` and exits non-zero only for `fail`. - [ ] Invalid setup-PAT diagnosis still returns independent local checks; remote dependants identify their blocking check. -- [ ] Doctor is wired only to query ports. Temporary credential-health workflow bootstrap remains setup-only. +- [ ] `copilot doctor --read-only` uses metadata/resource queries only and marks Secret values unverified; plain doctor may dispatch the installed credential-health workflow but never bootstraps one. Temporary workflow bootstrap remains setup-only. - [ ] No legacy setup prompt adapter, compatibility result, dual reader/writer, state alias, or transitional flag is present. ## Release and hotfix orchestration diff --git a/docs/how-to-use.mdx b/docs/how-to-use.mdx index a7d3f4614..54fe719f3 100644 --- a/docs/how-to-use.mdx +++ b/docs/how-to-use.mdx @@ -58,6 +58,31 @@ The page shows the same six setup stages, keeps the review pass visibly part of the current run, presents permissions and the final plan, and asks for a separate final **Apply setup** approval. Use the System/Light/Dark control in the top bar to choose a readable palette; the choice lasts only in that tab. +After a successful Apply, **Verify installed setup (read-only)** performs a +separate metadata inspection in the same local session and shows only summary +counts. It does not dispatch an Action or read Secret values; if you have +already deleted the setup PAT in GitHub, run `copilot doctor --read-only` +with a valid setup PAT instead. A failed or partial Apply does +not offer this button: inspect the itemized result and use the CLI command +after resolving the uncertain resources. +The language selector starts in English and offers Spanish, French, and +Portuguese. Switching language does not change the repository locale or reset +your answers. Question headings, explanations, options, permission details, +plan warnings, local errors, and credential status summaries are translated. +A persistent review notice remains in non-English modes while rare dynamic +GitHub/provider diagnostics and the end-to-end language review are completed. +GitHub's own PAT form uses the language +configured on GitHub, independently of this page. +After confirming the checkout, choose **Basic guided setup** (recommended) or +**Customize every setting**. Basic retains explicit permission, security, +branch-role, Project, approval, and storage decisions; only selected advanced +agent, branch-prefix, and Bugbot values stay at their defaults. Explicit +non-default configuration is still asked. Each section shows question-level +progress and has a Previous question control; the final plan summarizes key +decisions and names sections whose advanced defaults were retained. Use its +Change controls to reopen any section and recheck the plan and setup PAT +permissions before approving. A return to a previous section is the same run, +not a new setup attempt. No GitHub change starts by reviewing or editing. PAT values are submitted to the local process through masked fields and are not restored after refreshing the page. A new setup question starts with its own suggested answer; status updates do not erase an answer you are typing. @@ -67,6 +92,22 @@ pairing code authorizes this takeover, so share it only with someone you trust to control the setup. The terminal remains the default with `copilot setup`. +In the terminal wizard, each question shows a short explanation, the suggested +answer, and a related documentation URL. Type `?` at a text, number, or +single-choice prompt to open detailed **what/when/where/how/why/example/effect/ +verification** guidance without recording an answer. At a multi-selection +prompt, press `?` to see the same guidance without losing the current selection; +press `B` to revisit the previous question, and press Enter only when you are +ready to confirm it. At text, number and single-choice prompts, enter `:back` +to return without clearing other answers. At the final plan, enter `:edit` +and select a section to correct it before approving. Documentation opens only if +you choose to follow its URL; setup does not automatically browse to those +pages. The setup/bot PAT method, repository-owner and bot-login prompts also +accept `?` for English help without making a choice. The setup PAT prompt prints +both the authentication guide and GitHub PAT settings for manual cleanup. At +the final Apply confirmation, `?` explains which resources may change and how +to inspect a partial failure; it never approves the plan. + The web mode still opens GitHub's official form in a separate tab for each PAT; it cannot pick your GitHub account, complete 2FA, select an individual repository, generate a PAT, or revoke it. Confirm the account and repository @@ -91,6 +132,33 @@ In the browser questionnaire, an issue-workflow choice defaulted to **All** stays selected if you continue unchanged; clearing that selection submits **none** explicitly. +If PR approval is enabled, the web assistant reads recent completed PR CI +runs and proposes the exact check name, GitHub App ID, and producing workflow +when the setup PAT has `Actions: read` and `Checks: read`. Open the linked run +and verify the job and its mandatory coverage step before confirming the +producer attestation. A green check is not evidence of a coverage threshold. +If no reliable candidates can be read, enter the exact tuple manually; the +assistant does not guess an App ID. The coverage check selector only offers +checks you selected as trusted producers. `check` mode trusts a CI job that +fails below your coverage budget; `numeric` mode additionally needs the +`copilot-diff-coverage-v1` reporter artifact and explicit verification. +When an active repository-owned ruleset for the configured development branch identifies an +exact check/App pair as required, the assistant links that ruleset. Otherwise +it says **not checked**; this does not mean the check is optional, because +legacy branch protection, inherited organization rulesets, or inaccessible rules may also apply. The ruleset +read uses repository Metadata access and does not add a broader PAT grant. + +If the assistant stops, the result page distinguishes cancellation, expired +session, permission or storage blockers, and a potentially partial Apply. +It shows the stopped stage and a next action. After Apply it lists setup files, +Secrets, labels, issue types, Variables, and the initial tag as completed, +skipped, not started, or needing inspection, without +revealing PATs or provider error payloads. A diagnostic reference lets you +find more detail in the terminal. A "no setup changes started" +message does not mean a PAT created separately on GitHub was deleted. If +the browser reports an unknown cause, inspect the terminal's final error +before retrying. + Interactive `copilot setup` offers a guided GitHub link or manual entry for each PAT. The first link prepares a short-lived **setup PAT** for the person configuring the repository. Before showing it, guided setup asks only the local @@ -99,6 +167,21 @@ what remains unknown until GitHub is inspected. The later questionnaire reuses those answers. The second link, after the setup plan, prepares the **workflow PAT** for the bot account. +Projects are a two-stage choice: before creating the setup PAT, answer only +whether you intend to use Projects so the link can include **Projects: read**. +After entering the PAT, select accessible organization Projects from the +browser checkboxes or terminal multi-select. The displayed number is the +numeric Project number used by `project-ids`, not a GraphQL `PVT_` node ID. +If GitHub cannot return a verifiable list, the assistant explains why and +offers manual entry of a Project URL or number; it does not silently treat an +empty list as proof that no Projects exist. Configure the exact **Status field +options** (not visual board columns) used for newly created and in-progress +items. Multiple selected Projects currently must share the selected Status +values; if they use different Status vocabularies, choose compatible Projects +or configure them separately. The assistant does not claim that a single value +works in every Project when discovery shows otherwise. The bot PAT needs +Project write access later, when the workflow runs. + The interactive terminal also shows your current phase: Repository → Setup choices → Setup PAT → Plan → Bot PAT & credentials → Apply. These are milestones, not a percentage or a fixed number of questions. Before a remote mutation @@ -211,7 +294,7 @@ The complete command reference, including every supported option, is in [Workflo # or: copilot setup --token your_setup_pat ``` - The wizard shows a reviewable plan and asks for confirmation. You may revise the pre-PAT intent before opening GitHub; after entering the PAT, the remaining questionnaire is forward-only. Cancel and rerun to revise an earlier stage. `Ctrl-C` or end-of-input exits 130 with no writes before application, while declining the final plan exits 0 with no writes. Use `copilot setup --dry-run` to inspect the plan without a token or changes. + The wizard shows a reviewable plan and asks for confirmation. During either questionnaire, use `:back` in the terminal or **Previous question** in the browser to correct an answer; the final plan also offers **Change section** (terminal: `:edit`) without discarding other answers. Revisions recompute dependent questions and PAT grants, and a newly required grant must pass another audit before Apply. The web plan lists agent/model routes, issue workflows, exact trusted check/App/workflow identities, coverage evidence, Project Status transitions, scopes, files, Variables, and Secret names. Declining the plan makes no setup changes. The web assistant does not start writes before final Apply, but terminal credential-health verification can attempt a temporary workflow before then; heed any partial-change warning. `Ctrl-C` or end-of-input exits 130, while declining the final plan exits 0. Use `copilot setup --dry-run` to inspect the plan without a token or changes. In automation, `--non-interactive` creates no terminal. `--yes` approves only the final plan: it does not supply a missing setup PAT, workflow credential, provider credential, target, organization prerequisite, or permission acknowledgement. If every required read is verified or positively operationally usable but safe probes cannot prove required writes, inspect the PAT settings first and pass the separate `--confirm-unverifiable-write-permissions` flag. It never bypasses missing or unusable unverifiable read access. @@ -245,7 +328,7 @@ The complete command reference, including every supported option, is in [Workflo **Optional but keep coherence:** - **Labels**: If you change any label input (e.g. `feature-label`, `bugfix-label`, `deploy-label`), use the **same** label names in your issue templates (`labels:` in each `.yml`) and when labeling issues manually. Otherwise the action will not recognize the type or flow. - **Branch name prefixes**: The action uses inputs like `feature-tree`, `bugfix-tree`, `release-tree`, `hotfix-tree` (defaults: `feature`, `bugfix`, `release`, `hotfix`) to create branch names. If you change them, branch names will follow the new prefixes; keep templates and docs in sync. - - **Project columns**: Default column names are "Todo" and "In Progress". If you rename columns in GitHub Projects, set the corresponding action inputs (`project-column-issue-created`, `project-column-issue-in-progress`, etc.) so the action moves issues/PRs to the correct columns. + - **Project Status options**: Default values are "Todo" and "In Progress". If you rename the options in the Project's **Status** single-select field, set the corresponding action inputs (`project-column-issue-created`, `project-column-issue-in-progress`, etc.) to the exact option names. These legacy input names say `column`, but ProjectV2 updates the Status field, not a visual board column. **Bugbot autofix (issue/PR comments):** Workflows that run on `issue_comment` or `pull_request_review_comment` (so users can ask the bot to fix reported findings) must grant **`contents: write`** so the action can commit and push. On **issue_comment**, the action resolves the branch from an open PR that references the issue and checks out that branch before applying fixes and pushing. See [Bugbot autofix](/bugbot/autofix) and [Troubleshooting](/security-operations/operations/troubleshooting). @@ -455,7 +538,13 @@ installation itself validates the pinned provisioning inputs. The `copilot_crede read-only with respect to repository configuration: it executes provider-specific health checks and reports only whether each requested credential is usable. GitHub does not expose Secret values through its API, so `copilot doctor` dispatches this -workflow when it is available on the repository's default branch. +workflow when it is available on the repository's default branch. For a +non-mutating inspection after a partial setup, use `copilot doctor --read-only`: +it checks installed files, metadata, names, Variables and permissions but does +**not** dispatch the credential-health Action. Secret values are therefore +reported as unverified rather than healthy. The browser assistant recommends +this mode; running plain `copilot doctor` is a separate, explicit choice when +you want the workflow-based credential check. When Repository Variables are enabled, setup creates the common `AGENT_*` contract, the provider/model/effort variables for each configured `FINDINGS_*`, `FIXER_*`, @@ -536,7 +625,7 @@ After the tutorial and file customization, you can: - Set **repository or organization variables** for the agent CLI contract (`AGENT_PROVIDER`, `AGENT_MODEL_PROVIDER`, `AGENT_MODEL`, `AGENT_EFFORT`, `AGENT_ALLOWED_MODEL_PROVIDERS`, and `AGENT_ALLOWED_MODELS`) and, when needed, independent task overrides for `FINDINGS_*`, `REVIEWER_*`, `PLANNER_*`, `FIXER_*`, and `TESTER_*`. The supplied Copilot workflow templates forward these values to the action. Keep `CURSOR_API_KEY` available only when one of the configured task providers is Cursor. - Enable the `inactiveIssueClosure` setup feature when you want the scheduled cleanup, and adjust `INACTIVITY_THRESHOLD_HOURS` (1–8760 hours) to match your retention policy. This feature is disabled by default because it closes GitHub issues. -- Adjust **project column names** and **branch names** via action inputs so the action moves issues/PRs to the right columns and uses your branch naming. +- Adjust **Project Status option names** and **branch names** via action inputs so the action sets the intended Status and uses your branch naming. - Customize **issue templates** (copy, add fields, change labels) while keeping label and workflow names consistent as above. - Add or modify **release/hotfix** workflow steps (e.g. build, deploy) while keeping the workflow **filenames** and the action inputs `release-workflow` and `hotfix-workflow` in sync. diff --git a/docs/issues/assignees-and-projects.mdx b/docs/issues/assignees-and-projects.mdx index 7daf26bb5..230d5a664 100644 --- a/docs/issues/assignees-and-projects.mdx +++ b/docs/issues/assignees-and-projects.mdx @@ -41,8 +41,31 @@ Linking issues to **GitHub Project** boards requires a **Personal Access Token ( ### project-ids -- **Format:** Comma-separated list of **project IDs** (numeric). You find the project ID in the project URL or via the API; it is **not** the project name. -- **Effect:** When the action runs (e.g. on issue opened or edited), it **links the issue** to each of these projects and can **move the issue** to a column (e.g. "Todo", "In Progress") based on **`project-column-issue-created`** and **`project-column-issue-in-progress`** (see [Configuration](/configuration)). +- **Format:** Comma-separated list of numeric **Project numbers**. For example, `https://github.com/orgs/ACME/projects/12` has Project number `12`. This is not the Project title or its GraphQL `PVT_` node ID. +- **Effect:** When the action runs (e.g. on issue opened or edited), it **links the issue** to each of these projects and can set its **Status** field (e.g. "Todo", "In Progress") based on **`project-column-issue-created`** and **`project-column-issue-in-progress`** (see [Configuration](/configuration)). Despite their legacy `column` names, these inputs name Status single-select options, not visual board columns. + +During guided setup, first choose whether Projects integration is needed. That +lets the setup PAT request organization **Projects: read**; the assistant can +then list accessible Projects for selection. If discovery is unavailable, enter +the Project URL or number manually and confirm the account and Status options +in GitHub. The workflow's bot PAT separately needs Projects **read and write**. +Use **Retry GitHub discovery** (or `r` in the CLI) to query again without +restarting setup; this is read-only and does not ask for another PAT. A list +with no entries means no *accessible* Projects were returned by the bounded +query, not that the organization definitely has none. Check the setup PAT's +organization `Projects: read` grant and the Project owner, then use a verified +number or URL if the Project still does not appear. Personal-owner Projects +cannot be listed through this fine-grained-PAT endpoint, so enter their URL +number manually. See [GitHub's Projects API](https://docs.github.com/en/rest/projects/projects). + +All selected Projects must contain each Status value you configure for issue +creation, PR creation and the two in-progress transitions. The wizard proposes +common options when GitHub lets it inspect them. It cannot map different +Status vocabularies per Project; choose compatible Projects or configure them +separately. For manually entered Projects, check each option in GitHub before +you apply the plan. The interactive wizard asks you to confirm all four exact +values after inspecting each Project; accepting a suggestion is not +verification. See [GitHub Project fields](https://docs.github.com/en/rest/projects/fields). The column update uses the exact ProjectV2 item returned by GitHub when the issue is added. It does not wait and re-list the board, so normal Project diff --git a/docs/pull-requests/guarded-approval.mdx b/docs/pull-requests/guarded-approval.mdx index 7bbc2079b..6653f7031 100644 --- a/docs/pull-requests/guarded-approval.mdx +++ b/docs/pull-requests/guarded-approval.mdx @@ -19,6 +19,24 @@ New `copilot setup` runs offer `recommend` by default: Copilot assesses evidence Use `copilot setup` interactively to select an exact CI check, its GitHub App ID, and the workflow that produces it. The coverage check must enforce your repository's coverage budget; a successful advisory upload does not prove a percentage. Setup displays the selected names, target branches, Bugbot prerequisites, and branch-rule readiness before confirmation. The generated `copilot_pull_request_approval.yml` must reach the repository default branch before its `workflow_run` wakeups operate. +With `copilot setup --web`, the setup PAT can request conditional `Actions: +read` and `Checks: read` grants so the assistant can offer candidates from +recent completed pull-request runs. Each candidate includes an exact job, +source App ID, workflow name, observed result, and link to its run. Inspect +the job's workflow and coverage-enforcing step yourself before attesting; +Copilot never infers a coverage budget from a green check or job name. If +GitHub cannot provide candidates, use the manual exact tuple entry. +The coverage check dropdown is limited to your chosen trusted checks. +You can retry the read-only GitHub lookup twice without restarting setup or +re-entering the setup PAT. No recent runs, missing permissions and an API +failure are distinct states; none proves that your repository has no CI. +Every suggested producer shows the job, workflow, App ID, result, commit, +observation time and run link. The wizard has **not** checked whether a branch +rule requires that producer; confirm this in GitHub. If two selected producers +share a check name, setup stops before coverage selection because the stored +coverage setting has only one name. Choose one producer or give the CI jobs +distinct names. See [GitHub status checks](https://docs.github.com/en/pull-requests/reference/status-checks). + When testing this repository itself before a new package release, the source repository has a reviewed observer variant that checks out only its trusted default-branch revision and invokes `./`. Local setup preserves that variant instead of replacing it with the consumer template, whose `v3` reference cannot run unreleased changes. Consumer repositories need a published Action version containing this feature before enabling the generated observer. For non-interactive setup, supply the producer explicitly: diff --git a/docs/single-actions/workflow-and-cli.mdx b/docs/single-actions/workflow-and-cli.mdx index 5605c8df1..98cb2ce69 100644 --- a/docs/single-actions/workflow-and-cli.mdx +++ b/docs/single-actions/workflow-and-cli.mdx @@ -302,10 +302,11 @@ Checks: 1 pass, 0 warn, 1 fail, 0 skipped. No repository configuration was changed. ``` -Doctor's composition contains query ports only: it never creates, updates, deletes, or overwrites repository configuration. The credential health query may dispatch the already-installed `copilot_credential_health.yml` workflow, which creates an Actions run but does not modify repository configuration or expose Secret values. Only setup owns the separate temporary health-workflow bootstrap capability. +Doctor never creates, updates, deletes, or overwrites repository configuration. Plain `copilot doctor` may dispatch the already-installed `copilot_credential_health.yml` workflow, creating an Actions run to check otherwise unreadable Secret values. For metadata-only inspection without any Action dispatch, use `--read-only`; Secret values are then reported as unverified. Only setup owns the separate temporary health-workflow bootstrap capability. ```bash copilot doctor +copilot doctor --read-only copilot doctor --token "$SETUP_PAT" copilot doctor --config .copilot-setup.yml --non-interactive ``` diff --git a/scripts/render-web-setup-component.cjs b/scripts/render-web-setup-component.cjs index 20f6da1e2..0507eaf0e 100644 --- a/scripts/render-web-setup-component.cjs +++ b/scripts/render-web-setup-component.cjs @@ -2,7 +2,7 @@ const { resolve } = require('node:path'); /** Test-only semantic render of the configured Svelte/Vite component tree. */ async function main() { - const [name, encodedProps] = process.argv.slice(2); + const [name, encodedProps, locale = 'en'] = process.argv.slice(2); if (!/^[A-Z][A-Za-z]+$/.test(name) || !encodedProps) { throw new Error('A named component and props are required.'); } @@ -12,11 +12,15 @@ async function main() { server: { middlewareMode: true }, appType: 'custom', logLevel: 'silent', }); try { + const catalog = await server.ssrLoadModule('/src/i18n/catalog.ts'); + if (!catalog.setupLocales.includes(locale)) throw new Error('Unsupported test locale.'); + const { setupLocale } = await server.ssrLoadModule('/src/i18n/localeStore.ts'); + setupLocale.set(locale); const component = await server.ssrLoadModule(`/src/components/${name}.svelte`); // Use the same Svelte SSR runtime as the Vite-transformed component. const { render } = await server.ssrLoadModule('svelte/server'); const props = JSON.parse(encodedProps); - for (const key of ['onSubmit', 'onClose', 'onAction', 'onPair']) props[key] = async () => undefined; + for (const key of ['onSubmit', 'onRetryDiscovery', 'onClose', 'onAction', 'onPair']) props[key] = async () => undefined; process.stdout.write(render(component.default, { props }).body); } finally { await server.close(); diff --git a/specs/CATALOG.md b/specs/CATALOG.md index f3158357a..7be28b27c 100644 --- a/specs/CATALOG.md +++ b/specs/CATALOG.md @@ -17,7 +17,7 @@ debt or convert unknown historic intent into a design decision. | `execution-lifecycle` | Implemented | Shared GitHub Action lifecycle from event admission through durable user-facing results | [Execution admission, queueing, routing, and result publication](./execution-admission-queue-and-publication.md) + 3 companion | 84 paths · 2026-09-16 | | `architecture-quality-hardening` | Implemented | Close verified concurrency, error-contract, context-coupling, fan-out, setup/doctor, and provider-policy risks in dependency order | [Architecture quality and scalability hardening](./architecture-quality-and-scalability-hardening.md) + 1 companion | 72 paths · 2026-09-16 | | `setup-and-doctor` | Implemented | Plan, validate, provision, and audit a repository installation without exposing credentials | [Setup, configuration, credentials, and doctor](./setup-configuration-credentials-and-doctor.md) + 2 companion | 90 paths · 2026-09-28 | -| `local-web-setup-assistant` | Proposed | Offer a packaged, loopback-only Svelte setup interface over the existing setup engine with role-separated PAT guidance and revision-bound application | [Local web setup assistant](./local-web-setup-assistant.md) | 73 paths · 2026-09-28 | +| `local-web-setup-assistant` | Proposed | Offer a packaged, loopback-only Svelte setup interface over the existing engine with four-language guidance, English CLI help, basic/custom first-run paths, editable question and plan review, source-labelled GitHub facts, assisted CI and Project evidence, role-separated PATs, and truthful itemized outcomes | [Local web setup assistant](./local-web-setup-assistant.md) | 110 paths · 2026-09-29 | | `guided-bot-pat-onboarding` | Proposed | Guide creation of the persistent workflow PAT using GitHub's official form, verify bot identity and grants, and install the approved Actions Secret | [Guided bot PAT onboarding](./guided-bot-pat-onboarding.md) | 25 paths · 2026-09-25 | | `temporary-setup-operator-authorization` | Proposed | Guide creation of the one-run operator PAT through GitHub's official form, verify final setup access, and report user-owned deletion accurately | [Assisted setup PAT creation](./temporary-setup-operator-authorization.md) | 28 paths · 2026-09-25 | | `issue-start-and-sdd-readiness` | Implemented | Start every admitted issue with one explicit signal and publish a validated SDD before eligible Action-managed branch work | [Uniform issue start and pre-branch SDD readiness](./issue-start-and-branch-readiness.md) + 1 companion | 51 paths · 2026-09-17 | @@ -114,13 +114,13 @@ debt or convert unknown historic intent into a design decision. ### `local-web-setup-assistant` — Local web setup assistant - Owner: Copilot maintainers -- Last verified: 2026-09-28 +- Last verified: 2026-09-29 - Specifications: [`specs/local-web-setup-assistant.md`](./local-web-setup-assistant.md) - Workflows: Not applicable for this capability. - Entrypoints: [`src/cli/commands/setup.ts`](../src/cli/commands/setup.ts) · [`package.json`](../package.json) · [`web/src/main.ts`](../web/src/main.ts) -- Core code: [`web/src/App.svelte`](../web/src/App.svelte) · [`web/src/session/setupSession.ts`](../web/src/session/setupSession.ts) · [`web/src/components/ActionButton.svelte`](../web/src/components/ActionButton.svelte) · [`web/src/components/PairingPanel.svelte`](../web/src/components/PairingPanel.svelte) · [`web/src/components/ContextPanel.svelte`](../web/src/components/ContextPanel.svelte) · [`web/src/components/PromptCard.svelte`](../web/src/components/PromptCard.svelte) · [`web/src/components/QuestionPrompt.svelte`](../web/src/components/QuestionPrompt.svelte) · [`web/src/components/StatusBanner.svelte`](../web/src/components/StatusBanner.svelte) · [`web/src/lib/questionAnswer.ts`](../web/src/lib/questionAnswer.ts) · [`web/src/styles/tokens.css`](../web/src/styles/tokens.css) · [`web/src/style.css`](../web/src/style.css) · [`src/application/contracts/web_setup_view.ts`](../src/application/contracts/web_setup_view.ts) · [`src/application/errors/setup_interaction_cancelled_error.ts`](../src/application/errors/setup_interaction_cancelled_error.ts) · [`src/application/policies/merge_setup_overrides_policy.ts`](../src/application/policies/merge_setup_overrides_policy.ts) · [`src/application/policies/setup_remote_facts_policy.ts`](../src/application/policies/setup_remote_facts_policy.ts) · [`src/application/usecases/setup/prepare_setup_pat_intent_use_case.ts`](../src/application/usecases/setup/prepare_setup_pat_intent_use_case.ts) · [`src/application/usecases/setup/audit_configured_setup_pat_use_case.ts`](../src/application/usecases/setup/audit_configured_setup_pat_use_case.ts) · [`src/application/usecases/setup/verify_web_setup_apply_use_case.ts`](../src/application/usecases/setup/verify_web_setup_apply_use_case.ts) · [`src/application/usecases/setup/verify_setup_pat_bootstrap_use_case.ts`](../src/application/usecases/setup/verify_setup_pat_bootstrap_use_case.ts) · [`src/cli_context.ts`](../src/cli_context.ts) · [`src/cli/setup_command_options.ts`](../src/cli/setup_command_options.ts) · [`src/cli/web_setup_bridge.ts`](../src/cli/web_setup_bridge.ts) · [`src/cli/web_setup_adapters.ts`](../src/cli/web_setup_adapters.ts) · [`src/cli/web_setup_server.ts`](../src/cli/web_setup_server.ts) · [`src/cli/setup_apply_snapshot.ts`](../src/cli/setup_apply_snapshot.ts) · [`src/cli/setup_session_guard.ts`](../src/cli/setup_session_guard.ts) · [`src/application/usecases/setup/setup_wizard_use_case.ts`](../src/application/usecases/setup/setup_wizard_use_case.ts) · [`src/application/usecases/setup/setup_journey_use_case.ts`](../src/application/usecases/setup/setup_journey_use_case.ts) · [`src/application/usecases/setup/setup_credentials_use_case.ts`](../src/application/usecases/setup/setup_credentials_use_case.ts) · [`src/application/policies/setup_questionnaire_policy.ts`](../src/application/policies/setup_questionnaire_policy.ts) · [`src/application/policies/setup_token_permission_policy.ts`](../src/application/policies/setup_token_permission_policy.ts) · [`src/application/policies/setup_configuration_plan.ts`](../src/application/policies/setup_configuration_plan.ts) · [`src/application/policies/setup_pat_creation_url_policy.ts`](../src/application/policies/setup_pat_creation_url_policy.ts) · [`src/infrastructure/composition/setup_credentials_composition_root.ts`](../src/infrastructure/composition/setup_credentials_composition_root.ts) · [`scripts/validate-npm-package.cjs`](../scripts/validate-npm-package.cjs) · [`scripts/render-web-setup-component.cjs`](../scripts/render-web-setup-component.cjs) -- Tests: [`src/__tests__/cli.test.ts`](../src/__tests__/cli.test.ts) · [`src/__tests__/cli_context_branch.test.ts`](../src/__tests__/cli_context_branch.test.ts) · [`src/__tests__/cli_context_root.test.ts`](../src/__tests__/cli_context_root.test.ts) · [`src/cli/__tests__/web_setup_bridge.test.ts`](../src/cli/__tests__/web_setup_bridge.test.ts) · [`src/cli/__tests__/web_setup_adapters.test.ts`](../src/cli/__tests__/web_setup_adapters.test.ts) · [`src/cli/__tests__/web_setup_palette.test.ts`](../src/cli/__tests__/web_setup_palette.test.ts) · [`src/cli/__tests__/web_setup_ui_helpers.test.ts`](../src/cli/__tests__/web_setup_ui_helpers.test.ts) · [`src/cli/__tests__/web_setup_browser_session.test.ts`](../src/cli/__tests__/web_setup_browser_session.test.ts) · [`src/cli/__tests__/web_setup_server.test.ts`](../src/cli/__tests__/web_setup_server.test.ts) · [`src/cli/__tests__/web_setup_browser_open.test.ts`](../src/cli/__tests__/web_setup_browser_open.test.ts) · [`src/cli/__tests__/setup_apply_snapshot.test.ts`](../src/cli/__tests__/setup_apply_snapshot.test.ts) · [`src/cli/__tests__/setup_session_guard.test.ts`](../src/cli/__tests__/setup_session_guard.test.ts) · [`src/cli/__tests__/setup_command_options.test.ts`](../src/cli/__tests__/setup_command_options.test.ts) · [`src/cli/__tests__/web_setup_components.test.ts`](../src/cli/__tests__/web_setup_components.test.ts) · [`src/architecture/__tests__/web_setup_boundaries.test.ts`](../src/architecture/__tests__/web_setup_boundaries.test.ts) · [`src/application/usecases/setup/__tests__/prepare_setup_pat_intent_use_case.test.ts`](../src/application/usecases/setup/__tests__/prepare_setup_pat_intent_use_case.test.ts) · [`src/application/usecases/setup/__tests__/audit_configured_setup_pat_use_case.test.ts`](../src/application/usecases/setup/__tests__/audit_configured_setup_pat_use_case.test.ts) · [`src/application/usecases/setup/__tests__/verify_web_setup_apply_use_case.test.ts`](../src/application/usecases/setup/__tests__/verify_web_setup_apply_use_case.test.ts) · [`src/application/usecases/setup/__tests__/verify_setup_pat_bootstrap_use_case.test.ts`](../src/application/usecases/setup/__tests__/verify_setup_pat_bootstrap_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts) · [`src/application/policies/__tests__/setup_questionnaire_policy.test.ts`](../src/application/policies/__tests__/setup_questionnaire_policy.test.ts) · [`src/application/policies/__tests__/setup_token_permission_policy.test.ts`](../src/application/policies/__tests__/setup_token_permission_policy.test.ts) · [`src/architecture/__tests__/setup_doctor_boundaries.test.ts`](../src/architecture/__tests__/setup_doctor_boundaries.test.ts) -- User documentation: [`README.md`](../README.md) · [`docs/how-to-use.mdx`](../docs/how-to-use.mdx) · [`docs/authentication.mdx`](../docs/authentication.mdx) · [`docs/configuration.mdx`](../docs/configuration.mdx) · [`docs/configuration-checklist.mdx`](../docs/configuration-checklist.mdx) · [`docs/development/architecture.mdx`](../docs/development/architecture.mdx) · [`docs/dependency-rules.md`](../docs/dependency-rules.md) · [`docs/security-operations/operations/troubleshooting.mdx`](../docs/security-operations/operations/troubleshooting.mdx) · [`docs/security-operations/operations/provisioning.mdx`](../docs/security-operations/operations/provisioning.mdx) +- Core code: [`web/src/App.svelte`](../web/src/App.svelte) · [`web/src/session/setupSession.ts`](../web/src/session/setupSession.ts) · [`web/src/components/ActionButton.svelte`](../web/src/components/ActionButton.svelte) · [`web/src/components/PairingPanel.svelte`](../web/src/components/PairingPanel.svelte) · [`web/src/components/ContextPanel.svelte`](../web/src/components/ContextPanel.svelte) · [`web/src/components/PromptCard.svelte`](../web/src/components/PromptCard.svelte) · [`web/src/components/ResultPanel.svelte`](../web/src/components/ResultPanel.svelte) · [`web/src/components/PlanPrompt.svelte`](../web/src/components/PlanPrompt.svelte) · [`web/src/components/PlanDecisionSummary.svelte`](../web/src/components/PlanDecisionSummary.svelte) · [`web/src/components/QuestionPrompt.svelte`](../web/src/components/QuestionPrompt.svelte) · [`web/src/components/QuestionGuidance.svelte`](../web/src/components/QuestionGuidance.svelte) · [`web/src/components/DiscoveryNotice.svelte`](../web/src/components/DiscoveryNotice.svelte) · [`web/src/components/ProducerSelector.svelte`](../web/src/components/ProducerSelector.svelte) · [`web/src/components/CoverageCheckEvidence.svelte`](../web/src/components/CoverageCheckEvidence.svelte) · [`web/src/components/ProjectSelector.svelte`](../web/src/components/ProjectSelector.svelte) · [`web/src/i18n/catalog.ts`](../web/src/i18n/catalog.ts) · [`web/src/i18n/permissionTerms.ts`](../web/src/i18n/permissionTerms.ts) · [`web/src/components/StatusBanner.svelte`](../web/src/components/StatusBanner.svelte) · [`web/src/lib/questionAnswer.ts`](../web/src/lib/questionAnswer.ts) · [`web/src/lib/githubLink.ts`](../web/src/lib/githubLink.ts) · [`web/src/lib/focusOnRevision.ts`](../web/src/lib/focusOnRevision.ts) · [`web/src/i18n/featureNames.ts`](../web/src/i18n/featureNames.ts) · [`web/src/i18n/agentRoleNames.ts`](../web/src/i18n/agentRoleNames.ts) · [`web/src/i18n/checkEvidence.ts`](../web/src/i18n/checkEvidence.ts) · [`web/src/styles/controls.css`](../web/src/styles/controls.css) · [`web/src/styles/tokens.css`](../web/src/styles/tokens.css) · [`web/src/style.css`](../web/src/style.css) · [`src/application/contracts/web_setup_view.ts`](../src/application/contracts/web_setup_view.ts) · [`src/application/ports/setup_terminal_ports.ts`](../src/application/ports/setup_terminal_ports.ts) · [`src/application/errors/setup_interaction_cancelled_error.ts`](../src/application/errors/setup_interaction_cancelled_error.ts) · [`src/application/policies/merge_setup_overrides_policy.ts`](../src/application/policies/merge_setup_overrides_policy.ts) · [`src/application/policies/setup_remote_facts_policy.ts`](../src/application/policies/setup_remote_facts_policy.ts) · [`src/application/usecases/setup/prepare_setup_pat_intent_use_case.ts`](../src/application/usecases/setup/prepare_setup_pat_intent_use_case.ts) · [`src/application/usecases/setup/audit_configured_setup_pat_use_case.ts`](../src/application/usecases/setup/audit_configured_setup_pat_use_case.ts) · [`src/application/usecases/setup/verify_web_setup_apply_use_case.ts`](../src/application/usecases/setup/verify_web_setup_apply_use_case.ts) · [`src/application/usecases/setup/verify_setup_pat_bootstrap_use_case.ts`](../src/application/usecases/setup/verify_setup_pat_bootstrap_use_case.ts) · [`src/cli_context.ts`](../src/cli_context.ts) · [`src/cli/setup_command_options.ts`](../src/cli/setup_command_options.ts) · [`src/cli/web_setup_bridge.ts`](../src/cli/web_setup_bridge.ts) · [`src/cli/web_setup_adapters.ts`](../src/cli/web_setup_adapters.ts) · [`src/cli/web_setup_server.ts`](../src/cli/web_setup_server.ts) · [`src/cli/setup_apply_snapshot.ts`](../src/cli/setup_apply_snapshot.ts) · [`src/cli/setup_result_receipt.ts`](../src/cli/setup_result_receipt.ts) · [`src/cli/setup_session_guard.ts`](../src/cli/setup_session_guard.ts) · [`src/application/usecases/setup/setup_wizard_use_case.ts`](../src/application/usecases/setup/setup_wizard_use_case.ts) · [`src/application/usecases/setup/setup_questionnaire_controller.ts`](../src/application/usecases/setup/setup_questionnaire_controller.ts) · [`src/application/usecases/setup/setup_journey_use_case.ts`](../src/application/usecases/setup/setup_journey_use_case.ts) · [`src/application/usecases/setup/setup_credentials_use_case.ts`](../src/application/usecases/setup/setup_credentials_use_case.ts) · [`src/application/policies/setup_questionnaire_policy.ts`](../src/application/policies/setup_questionnaire_policy.ts) · [`src/application/policies/setup_project_selection_policy.ts`](../src/application/policies/setup_project_selection_policy.ts) · [`src/application/ports/setup_project_discovery_port.ts`](../src/application/ports/setup_project_discovery_port.ts) · [`src/application/ports/setup_approval_check_discovery_port.ts`](../src/application/ports/setup_approval_check_discovery_port.ts) · [`src/infrastructure/github_setup_project_discovery_adapter.ts`](../src/infrastructure/github_setup_project_discovery_adapter.ts) · [`src/infrastructure/github_setup_approval_check_discovery_adapter.ts`](../src/infrastructure/github_setup_approval_check_discovery_adapter.ts) · [`src/application/policies/setup_question_documentation_policy.ts`](../src/application/policies/setup_question_documentation_policy.ts) · [`src/application/policies/setup_question_guidance_policy.ts`](../src/application/policies/setup_question_guidance_policy.ts) · [`src/application/policies/setup_question_purpose_policy.ts`](../src/application/policies/setup_question_purpose_policy.ts) · [`src/application/policies/setup_token_permission_policy.ts`](../src/application/policies/setup_token_permission_policy.ts) · [`src/application/policies/setup_configuration_plan.ts`](../src/application/policies/setup_configuration_plan.ts) · [`src/application/policies/setup_pat_creation_url_policy.ts`](../src/application/policies/setup_pat_creation_url_policy.ts) · [`src/infrastructure/composition/setup_credentials_composition_root.ts`](../src/infrastructure/composition/setup_credentials_composition_root.ts) · [`scripts/validate-npm-package.cjs`](../scripts/validate-npm-package.cjs) · [`scripts/render-web-setup-component.cjs`](../scripts/render-web-setup-component.cjs) +- Tests: [`src/__tests__/cli.test.ts`](../src/__tests__/cli.test.ts) · [`src/__tests__/cli_context_branch.test.ts`](../src/__tests__/cli_context_branch.test.ts) · [`src/__tests__/cli_context_root.test.ts`](../src/__tests__/cli_context_root.test.ts) · [`src/cli/__tests__/web_setup_bridge.test.ts`](../src/cli/__tests__/web_setup_bridge.test.ts) · [`src/cli/__tests__/web_setup_adapters.test.ts`](../src/cli/__tests__/web_setup_adapters.test.ts) · [`src/cli/__tests__/web_setup_palette.test.ts`](../src/cli/__tests__/web_setup_palette.test.ts) · [`src/cli/__tests__/web_setup_ui_helpers.test.ts`](../src/cli/__tests__/web_setup_ui_helpers.test.ts) · [`src/cli/__tests__/web_setup_browser_session.test.ts`](../src/cli/__tests__/web_setup_browser_session.test.ts) · [`src/cli/__tests__/web_setup_server.test.ts`](../src/cli/__tests__/web_setup_server.test.ts) · [`src/cli/__tests__/web_setup_browser_open.test.ts`](../src/cli/__tests__/web_setup_browser_open.test.ts) · [`src/cli/__tests__/setup_apply_snapshot.test.ts`](../src/cli/__tests__/setup_apply_snapshot.test.ts) · [`src/cli/__tests__/setup_result_receipt.test.ts`](../src/cli/__tests__/setup_result_receipt.test.ts) · [`src/cli/__tests__/setup_session_guard.test.ts`](../src/cli/__tests__/setup_session_guard.test.ts) · [`src/cli/__tests__/setup_command_options.test.ts`](../src/cli/__tests__/setup_command_options.test.ts) · [`src/cli/__tests__/web_setup_components.test.ts`](../src/cli/__tests__/web_setup_components.test.ts) · [`src/cli/__tests__/web_setup_catalog.test.ts`](../src/cli/__tests__/web_setup_catalog.test.ts) · [`src/architecture/__tests__/web_setup_boundaries.test.ts`](../src/architecture/__tests__/web_setup_boundaries.test.ts) · [`src/application/usecases/setup/__tests__/prepare_setup_pat_intent_use_case.test.ts`](../src/application/usecases/setup/__tests__/prepare_setup_pat_intent_use_case.test.ts) · [`src/application/usecases/setup/__tests__/audit_configured_setup_pat_use_case.test.ts`](../src/application/usecases/setup/__tests__/audit_configured_setup_pat_use_case.test.ts) · [`src/application/usecases/setup/__tests__/verify_web_setup_apply_use_case.test.ts`](../src/application/usecases/setup/__tests__/verify_web_setup_apply_use_case.test.ts) · [`src/application/usecases/setup/__tests__/verify_setup_pat_bootstrap_use_case.test.ts`](../src/application/usecases/setup/__tests__/verify_setup_pat_bootstrap_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_questionnaire_controller.test.ts`](../src/application/usecases/setup/__tests__/setup_questionnaire_controller.test.ts) · [`src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts) · [`src/application/policies/__tests__/setup_questionnaire_policy.test.ts`](../src/application/policies/__tests__/setup_questionnaire_policy.test.ts) · [`src/application/policies/__tests__/setup_project_selection_policy.test.ts`](../src/application/policies/__tests__/setup_project_selection_policy.test.ts) · [`src/infrastructure/__tests__/github_setup_project_discovery_adapter.test.ts`](../src/infrastructure/__tests__/github_setup_project_discovery_adapter.test.ts) · [`src/infrastructure/__tests__/github_setup_approval_check_discovery_adapter.test.ts`](../src/infrastructure/__tests__/github_setup_approval_check_discovery_adapter.test.ts) · [`src/application/policies/__tests__/setup_question_documentation_policy.test.ts`](../src/application/policies/__tests__/setup_question_documentation_policy.test.ts) · [`src/application/policies/__tests__/setup_question_purpose_policy.test.ts`](../src/application/policies/__tests__/setup_question_purpose_policy.test.ts) · [`src/application/policies/__tests__/setup_token_permission_policy.test.ts`](../src/application/policies/__tests__/setup_token_permission_policy.test.ts) · [`src/architecture/__tests__/setup_doctor_boundaries.test.ts`](../src/architecture/__tests__/setup_doctor_boundaries.test.ts) +- User documentation: [`README.md`](../README.md) · [`docs/how-to-use.mdx`](../docs/how-to-use.mdx) · [`docs/authentication.mdx`](../docs/authentication.mdx) · [`docs/issues/assignees-and-projects.mdx`](../docs/issues/assignees-and-projects.mdx) · [`docs/pull-requests/guarded-approval.mdx`](../docs/pull-requests/guarded-approval.mdx) · [`docs/configuration.mdx`](../docs/configuration.mdx) · [`docs/configuration-checklist.mdx`](../docs/configuration-checklist.mdx) · [`docs/development/architecture.mdx`](../docs/development/architecture.mdx) · [`docs/dependency-rules.md`](../docs/dependency-rules.md) · [`docs/security-operations/operations/troubleshooting.mdx`](../docs/security-operations/operations/troubleshooting.mdx) · [`docs/security-operations/operations/provisioning.mdx`](../docs/security-operations/operations/provisioning.mdx) ### `guided-bot-pat-onboarding` — Guided bot PAT onboarding diff --git a/specs/catalog.json b/specs/catalog.json index be156ceb4..191a8aae0 100644 --- a/specs/catalog.json +++ b/specs/catalog.json @@ -755,9 +755,9 @@ "id": "local-web-setup-assistant", "title": "Local web setup assistant", "status": "proposed", - "scope": "Offer a packaged, loopback-only Svelte setup interface over the existing setup engine with role-separated PAT guidance and revision-bound application", + "scope": "Offer a packaged, loopback-only Svelte setup interface over the existing engine with four-language guidance, English CLI help, basic/custom first-run paths, editable question and plan review, source-labelled GitHub facts, assisted CI and Project evidence, role-separated PATs, and truthful itemized outcomes", "owner": "Copilot maintainers", - "lastVerified": "2026-09-28", + "lastVerified": "2026-09-29", "specs": [ "specs/local-web-setup-assistant.md" ], @@ -774,12 +774,29 @@ "web/src/components/PairingPanel.svelte", "web/src/components/ContextPanel.svelte", "web/src/components/PromptCard.svelte", + "web/src/components/ResultPanel.svelte", + "web/src/components/PlanPrompt.svelte", + "web/src/components/PlanDecisionSummary.svelte", "web/src/components/QuestionPrompt.svelte", + "web/src/components/QuestionGuidance.svelte", + "web/src/components/DiscoveryNotice.svelte", + "web/src/components/ProducerSelector.svelte", + "web/src/components/CoverageCheckEvidence.svelte", + "web/src/components/ProjectSelector.svelte", + "web/src/i18n/catalog.ts", + "web/src/i18n/permissionTerms.ts", "web/src/components/StatusBanner.svelte", "web/src/lib/questionAnswer.ts", + "web/src/lib/githubLink.ts", + "web/src/lib/focusOnRevision.ts", + "web/src/i18n/featureNames.ts", + "web/src/i18n/agentRoleNames.ts", + "web/src/i18n/checkEvidence.ts", + "web/src/styles/controls.css", "web/src/styles/tokens.css", "web/src/style.css", "src/application/contracts/web_setup_view.ts", + "src/application/ports/setup_terminal_ports.ts", "src/application/errors/setup_interaction_cancelled_error.ts", "src/application/policies/merge_setup_overrides_policy.ts", "src/application/policies/setup_remote_facts_policy.ts", @@ -793,11 +810,21 @@ "src/cli/web_setup_adapters.ts", "src/cli/web_setup_server.ts", "src/cli/setup_apply_snapshot.ts", + "src/cli/setup_result_receipt.ts", "src/cli/setup_session_guard.ts", "src/application/usecases/setup/setup_wizard_use_case.ts", + "src/application/usecases/setup/setup_questionnaire_controller.ts", "src/application/usecases/setup/setup_journey_use_case.ts", "src/application/usecases/setup/setup_credentials_use_case.ts", "src/application/policies/setup_questionnaire_policy.ts", + "src/application/policies/setup_project_selection_policy.ts", + "src/application/ports/setup_project_discovery_port.ts", + "src/application/ports/setup_approval_check_discovery_port.ts", + "src/infrastructure/github_setup_project_discovery_adapter.ts", + "src/infrastructure/github_setup_approval_check_discovery_adapter.ts", + "src/application/policies/setup_question_documentation_policy.ts", + "src/application/policies/setup_question_guidance_policy.ts", + "src/application/policies/setup_question_purpose_policy.ts", "src/application/policies/setup_token_permission_policy.ts", "src/application/policies/setup_configuration_plan.ts", "src/application/policies/setup_pat_creation_url_policy.ts", @@ -817,18 +844,26 @@ "src/cli/__tests__/web_setup_server.test.ts", "src/cli/__tests__/web_setup_browser_open.test.ts", "src/cli/__tests__/setup_apply_snapshot.test.ts", + "src/cli/__tests__/setup_result_receipt.test.ts", "src/cli/__tests__/setup_session_guard.test.ts", "src/cli/__tests__/setup_command_options.test.ts", "src/cli/__tests__/web_setup_components.test.ts", + "src/cli/__tests__/web_setup_catalog.test.ts", "src/architecture/__tests__/web_setup_boundaries.test.ts", "src/application/usecases/setup/__tests__/prepare_setup_pat_intent_use_case.test.ts", "src/application/usecases/setup/__tests__/audit_configured_setup_pat_use_case.test.ts", "src/application/usecases/setup/__tests__/verify_web_setup_apply_use_case.test.ts", "src/application/usecases/setup/__tests__/verify_setup_pat_bootstrap_use_case.test.ts", "src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts", + "src/application/usecases/setup/__tests__/setup_questionnaire_controller.test.ts", "src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts", "src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts", "src/application/policies/__tests__/setup_questionnaire_policy.test.ts", + "src/application/policies/__tests__/setup_project_selection_policy.test.ts", + "src/infrastructure/__tests__/github_setup_project_discovery_adapter.test.ts", + "src/infrastructure/__tests__/github_setup_approval_check_discovery_adapter.test.ts", + "src/application/policies/__tests__/setup_question_documentation_policy.test.ts", + "src/application/policies/__tests__/setup_question_purpose_policy.test.ts", "src/application/policies/__tests__/setup_token_permission_policy.test.ts", "src/architecture/__tests__/setup_doctor_boundaries.test.ts" ], @@ -836,6 +871,8 @@ "README.md", "docs/how-to-use.mdx", "docs/authentication.mdx", + "docs/issues/assignees-and-projects.mdx", + "docs/pull-requests/guarded-approval.mdx", "docs/configuration.mdx", "docs/configuration-checklist.mdx", "docs/development/architecture.mdx", diff --git a/specs/guarded-pull-request-approval-setup-and-doctor.md b/specs/guarded-pull-request-approval-setup-and-doctor.md index 60afa9223..c36be0f69 100644 --- a/specs/guarded-pull-request-approval-setup-and-doctor.md +++ b/specs/guarded-pull-request-approval-setup-and-doctor.md @@ -102,6 +102,27 @@ Not applicable: prospective capability. The observed setup/doctor baseline is no `S5` Add a conditional `pull-request-approval` questionnaire stage after `bugbot` and before `projects`. Skip it when `features.pullRequests=false`; set policy `off` in that case. The recommended answers are `recommend`, development target, selected routine branch kinds, distinct linked issue required, fixed protected-path exclusions, and no acceptance of dismissed findings. Setup asks for exact producer tuples and an explicit operator attestation of their App identity and coverage-enforcing step; it does not silently infer coverage from a green workflow. In non-interactive mode missing explicit producer data is an error with no writes, not an invented default. The plan states that a freshly copied observer will not run until committed on the default branch. +For the web presentation, replace raw `name|App ID|workflow` entry with +read-only discovery of *observed GitHub Actions job check runs* linked to +workflow run attempts and source App IDs. Offer 1–8 checkboxes with exact +identities, recency, required-by-branch evidence and links. The operator may +enter an exact tuple manually when discovery is absent or fails. The operator +can explicitly re-run the read-only query twice without restarting setup or +re-entering the PAT; no result means no *accessible observed* run, not proof +that CI is absent. A selected coverage check is one of those entries, not a +second free-text name. Because `coverage.checkName` stores only the name, the +trusted producer list must not contain two checks with the same name; setup +rejects this ambiguity and directs the operator to select one or rename the +jobs. The UI shows exact workflow/App identity and the run link, and calls +branch-required status `not checked` unless inspected separately. Neither +an observed green check nor a workflow file containing `coverage` sets +`producerAttested` or `reporterAttested`: the UI must show what the human +still needs to inspect and confirm. The current observer does not consume +legacy commit statuses as test producers; do not propose them as selectable +equivalents. Private-repository discovery may require conditional setup PAT +`Checks: read` and `Actions: read`, disclosed before PAT creation; refusal +keeps the manual path without falsely identifying an App. + The setup plan MUST display, in order: configured mode; PR scope and fixed exclusions; selected test/coverage producer names and source IDs; Bugbot review floor and path exclusions; effective branch-rule/stale-dismissal readiness for each selected target; workflow files and event names; runtime PAT identity/permission status; Secret/Variable names only; and a one-line outcome (`can approve after installation`, `installed but recommendation only`, or `setup blocked`). `--dry-run` performs no writes and needs no token for local-only preview, marking remote facts `unverified` rather than passing them. `--non-interactive --yes` approves only the complete plan and cannot choose an ambiguous producer or credential. ### 6.2 Doctor sequence @@ -235,6 +256,17 @@ AST tests reject provider imports in the policy/doctor layers and mutation-port ### 9.1 Setup plan and doctor hierarchy +The beginner-facing web card explains `recommend`, `guarded`, and `off` in +terms of whether Copilot only advises or can submit a native approval. For +each producer it shows the exact job, workflow, App, and evidence link before +requesting attestation. Coverage mode explains `check` as CI-enforced pass/fail +and `numeric` as the reviewed `copilot-diff-coverage-v1` artifact plus a +threshold. All these instructions are localized in the ten reviewed web setup +catalogs defined by the local-web SDD; stable producer names, IDs, workflow names, and +policy values are never translated. A blocked result must identify the +failed prerequisite and mutation facts in the browser, not only in terminal +output. See [local web setup assistant](./local-web-setup-assistant.md#93-first-time-comprehension-and-progressive-disclosure). + Setup's first view says what will be enabled, which PRs could receive a native review, and which prerequisites remain. It then shows changed files/Variables/Secret names, one confirmation, and technical details. Doctor's first line says whether native approvals can occur now; each check has stable ID, status, safe evidence, one action, and a link. Setup stays in English while creating the repository profile; doctor uses `repository-locale` (reviewed English/Spanish, complete dynamic catalog or atomic English fallback), consistent with [existing setup/doctor behavior](./setup-configuration-credentials-and-doctor.md). Representative plan/doctor content, with examples rather than fixed repository names: diff --git a/specs/local-web-setup-assistant.md b/specs/local-web-setup-assistant.md index 1fca8b4dd..9b4dcda6c 100644 --- a/specs/local-web-setup-assistant.md +++ b/specs/local-web-setup-assistant.md @@ -3,7 +3,7 @@ - Status: Implementation in progress — target contract, not yet release acceptance - Date: 2026-09-28 - Catalog capability ID: `local-web-setup-assistant` -- Last verified: 2026-09-28 (source/build tests; no live GitHub setup or dogfooding) +- Last verified: 2026-09-29 (source/build tests and screenshot review; no live GitHub setup or dogfooding) - Owners: Copilot maintainers; product, security, and accessibility reviewers - Scope: optional, local Svelte-based presentation of the existing repository setup journey, sharing its policy, credential, and application engine with the terminal - Related issues/PRs: [PR #402](https://github.com/vypdev/copilot/pull/402) carries this implementation alongside the earlier guided PAT work; no test issue or Action is created @@ -641,15 +641,484 @@ architecture guide documents these boundaries for future steps. visible external-destination warning. `Referrer-Policy: no-referrer` protects the local origin when a GitHub link opens. A copyable link may be shown when browser pop-ups are blocked; it contains no session or PAT value. -- English is the initial setup locale, matching the terminal; Spanish is - added only with a reviewed complete catalog, not ad hoc strings. Unknown - locale falls back atomically to English. Account/repo names and remote - messages are escaped as text, never injected as HTML or Markdown. +- English is the initial setup locale, matching the terminal. A visible + language selector offers four complete catalogs: English (`en`), Spanish + (`es`), French (`fr`), and Portuguese (`pt`). These languages are chosen for + direct maintainer translation; the earlier ten-language preview is retired + rather than advertised as complete. A locale MUST NOT appear until its + complete catalog and reviewed safety copy pass the release gate. + It changes explanatory UI text immediately without restarting setup, + changing repository/issue locale, modifying answers, or replaying an Apply. + Unsupported locale falls back atomically to English. Account/repo names + and remote messages are escaped as text, never injected as HTML or Markdown. - No issue/PR/check/comment is added by the web surface, so notification budget is zero. Progress updates in the page are coalesced and do not repeatedly steal focus or announce the same state. GitHub-side account switching and 2FA are explained, not reproduced in the local UI. +### 9.3 First-time comprehension and progressive disclosure + +Every active question MUST explain, in the selected language: what this +controls, when it applies, why the suggested answer is safe, a concrete +example, what changes if selected, and how to inspect the result. The primary +card uses one plain-language sentence and one recommendation; a reusable +details panel holds deeper examples, security implications, and a documentation +link. The semantic question ID is the stable key. Explanatory copy is owned by +one reviewed application presentation catalog and projected into both terminal +and web views; the browser MUST NOT own an independent copy of setup rules. +Conditional visibility and validation stay in the existing questionnaire. +Question IDs absent from a locale catalog fail the catalog completeness test. + +The same completeness rule applies to the English-only interactive CLI. Each +CLI question shows its meaning, recommended answer and a descriptive reference +link before accepting input; entering `?` opens its full `what / when / where / +how / why / example / effect / verify` explanation and returns to the **same** +unanswered question without changing the draft. The web shows the same semantic +help in the selected one of four languages, with those headings in progressive +disclosure and a contextual link beside the question. A link may point to a +relevant Copilot configuration guide or official GitHub documentation, not an +unrelated generic home page. The PAT/setup/bot/plan/Apply/blocked stages, +credential prompts, conditional permission rows, dynamic choices, validation, +and cleanup receive the same treatment. First-time comprehension is not +declared complete while any first-party prompt falls back to English in a +non-English web locale. + +The four-language release gate is all-or-nothing for each selectable locale. +It covers every questionnaire ID and every displayed field (`label`, `summary`, +`when`, `where`, `how`, `why`, `example`, `effect`, `verify`), selectable option +labels, permission name/reason/condition/status, credential and confirmation +prompts, plan warnings, progress and validation notices, and every terminal +outcome. Stable wire values, GitHub-owned content, commands, product names, +user-entered text and repository identifiers are not translated. A static +catalog/key audit MUST reject a missing or unchanged English first-party +sentence; render tests MUST traverse representative normal, conditional, +blocked and partial paths in every locale. A persistent "translation preview" +notice is a development warning only and MUST disappear only when these gates +pass. A separate semantic review of safety-critical PAT, Apply and cleanup copy +in each advertised language remains a release gate in addition to machine +checks; direct translation is not its own independent review. + +Translation production is a development-time operation, never a setup-time +dependency. This four-language slice is translated directly by maintainers and +sends no content to a translation service. If future work explicitly +authorizes external assistance, a translation provider may +receive only an allowlisted export of static, publicly visible English UI copy +and non-sensitive semantic context (message ID, UI location, placeholder names, +and terminology guidance). The export MUST exclude PATs, API keys, cookies, +repository/account names, questionnaire answers, GitHub responses, session +state, logs, source code, and any other runtime or user-specific data. The +export is reviewed before transmission; neither the web client, CLI, CI, nor +published package calls a translation service. Returned text is committed as +static catalogs only after placeholder/option-identity checks, a second-pass +semantic review against the English source, and the safety-copy review above. +An unavailable provider or failed review blocks advertising the affected +locale; it never triggers an English-mixed view or sends runtime content as a +fallback. Translation provenance and review status are recorded without +storing provider credentials or submitted runtime data. + +Help links are selected by a closed, versioned registry keyed by semantic +question/prompt IDs. Their HTTPS origin and path are allowlisted; user-supplied +check names, provider messages, repository names and URLs never become a help +destination. Every registered URL/anchor is verified in documentation/link +tests, and the CLI prints the full safe URL. Browser links open separately +with `noopener noreferrer`, a visible external destination, and no PAT/session +parameters. If a destination is unavailable, setup remains usable and the +local explanation is still complete. GitHub's own PAT form and account/2FA +pages are outside Copilot's translation boundary; the wizard explains those +handoffs in the selected language. + +The ordinary path MUST not force a beginner to understand provider executable +paths, raw producer tuples, or rule syntax. Advanced controls remain reachable +and explain why they matter. In particular: + +| Decision | Normal presentation | Expanded explanation and guard | +|---|---|---| +| Agent executable | `Use the standard agent command` (recommended) | `codex`, `opencode`, or `agent` runs on the Action runner, not this browser; a custom absolute path is advanced and bound to the chosen provider. Do not copy one explicit path to a different provider. | +| Provider reasoning | Do not offer a misleading toggle while the current string-only CLI adapter cannot return separate reasoning parts | If a future adapter supports it, disclose actual text/retention behavior; never promise concision or metadata-only output without a bounded contract. | +| Bugbot dry-run | `Publish Bugbot findings` (recommended) versus persistent `Analyze without publishing` | Not the same as `copilot setup --dry-run`; suppresses review publication/SCM effects and is incompatible with approval evidence. | +| Organization Bugbot rules | Optional multiline rule editor, one rule per line | These rules take precedence over repository rules; their storage scope is shown separately. Never call a repository Variable an organization-wide policy. | +| Agent CLI provisioning | `Automatic` (recommended) / `Use installed only` / advanced `Reinstall reviewed version` | Explain runner ownership, pinned Codex/OpenCode installs, Cursor's manual prerequisite, and explicit-path exemption. | + +Examples in the card must be clearly illustrative, not a real detected value. +The reviewer can always see the current stored value, source (default/config/ +answer/remote evidence), and implications in the final plan. The web's existing +one-line input MUST NOT be used for newline-delimited rule text. + +### 9.4 Assisted trusted-CI and coverage selection + +After the setup PAT is audited, a read-only discovery use case SHOULD propose +recent, exact GitHub Actions job checks for the target repository. It joins +observed check run IDs/App IDs to workflow run attempts and job check-run URLs; +it does not infer a source from a name alone. Display each candidate as a +checkbox/card with exact job/check name, workflow name, App name and numeric ID, +observed SHA/date/conclusion, `required by branch` evidence when available, +and an inspectable GitHub run link. Never suggest Copilot's own approval check +or a generic commit status that the current approval observer cannot consume. +Deduplicate only identical exact tuples, paginate/bound reads, and distinguish +`observed`, `no recent PR runs`, `runs without verifiable jobs`, `permission +denied`, and `unavailable`. Do not claim that a workflow is configured but has +not run unless a separate workflow inventory actually proves it. An empty or +failed discovery keeps a validated manual path; no list result is itself an +attestation. The user may explicitly retry the **current** discovery question +at most twice per setup run. A retry is read-only, retains unsent checkbox and +manual-field input, never advances the questionnaire, and cannot apply a stale +response after a new answer, cancellation, or controller takeover. CLI offers +`r` or an equivalent numbered retry option. The current question updates in +place; prior answers and the PAT are not requested again. When retries are +exhausted, explain the manual path rather than presenting a dead button. Do +not offer retry for a personal-owner Projects endpoint that categorically +does not support the fine-grained PAT, or when no discovery adapter was run. + +Selection becomes 1–8 structured identities, not a semicolon-delimited text +field. `coverage.checkName` MUST select one of those checks. Because the +persisted coverage contract stores a check *name*, two trusted producers with +the same name cannot be disambiguated for coverage: reject that selection with +an actionable explanation before the coverage question, rather than +silently displaying two producer cards for one name. An existing ambiguous +configuration may enter interactive setup for repair, but final validation +still forbids applying or installing it. The selector displays the +full producer identity, recent SHA/date/conclusion and inspectable run, even +though it persists the uniquely selected check name. Ask the explicit producer +and coverage-step attestation **after** the final coverage identity and, +where applicable, numeric reporter choices; it must never precede the +choice it claims to attest. If branch-rule evidence +was not fetched, label required-by-branch as **not checked**, never `not +required`. In check mode, +the UI asks which selected check *fails when the coverage budget fails*, shows +the related workflow/job link and an explicit `I verified the enforcing CI +step` action. A green check or filename containing `coverage` is suggestive, +never proof. Numeric mode explains the exact +`copilot-diff-coverage-v1` artifact, reporter, head/base binding, and threshold; +it can show observed artifact evidence but never sets `reporterAttested` +automatically. Recommendation mode may display unresolved prerequisites; +guarded mode fails closed until exact identities and human attestations pass. + +Discovery MUST return a semantic state (`observed`, `no-recent-runs`, +`permission-denied`, `unavailable`) independently of its candidates. The web +and terminal explain which state occurred, the bounded sample (20 recent PR +workflow runs, at most 15 inspected; up to 30 Projects over two pages), and the +next action before asking for a manual tuple. A network/API failure must not +masquerade as an empty repository. The manual path labels check name, numeric +source App ID, and workflow name separately (or gives an equivalent CLI +template), validates the exact tuple, and never treats it as verified. + +Private-repository discovery needs GitHub `Checks: read` and `Actions: read` +from the setup PAT; these conditional read grants MUST be disclosed in the +pre-PAT intent and generated URL when PR approval is enabled, because setup +then inspects producer readiness and offers remote discovery. +If the operator declines extra grants, local workflow inspection may propose +unverified names but cannot invent App IDs or silently elevate the PAT. This +choice is separate from runtime bot-PAT permissions. The GitHub API's check +run and workflow-list endpoints are the provider boundary; the browser never +receives the PAT. See [check runs](https://docs.github.com/en/rest/checks/runs) +and [workflows](https://docs.github.com/en/rest/actions/workflows). + +### 9.4a GitHub Projects without opaque IDs + +The permission-intent pass asks only whether Projects integration is wanted; +it must not ask for numbers before the setup PAT exists. The post-PAT pass +shows a bounded, read-only list of Projects owned by the repository owner, +each with title, owner, number and inspectable GitHub URL. Organization +Projects use GitHub's paginated organization Projects endpoint and require +organization `Projects: read` for discovery. Setup only reads Projects and +stores their selected numbers/Status names in repository configuration, so its +PAT does not need `Projects: write`. The separate runtime bot PAT needs +`Projects: write` when automation later updates Project items. +Personal-owner REST listing does not accept a fine-grained PAT, so the UI +explicitly says discovery is unsupported and offers validated manual entry. +Network failure, permission denial, no accessible Projects, and a genuinely +empty list have different messages and recovery actions. + +Web Projects use checkboxes with descriptive links; CLI uses a numbered +multi-select. The answer serializes **positive Project numbers** from +`/orgs/OWNER/projects/NUMBER` or `/users/OWNER/projects/NUMBER`, not GraphQL +`PVT_…` IDs. Manual fallback accepts a bounded comma-separated list of positive +numbers or exact matching GitHub Project URLs; it rejects duplicates, wrong +owners, GraphQL IDs and malformed URLs at the question and clearly marks +unverified entries. Existing valid numeric configuration remains readable. +No Project is created by selecting it. + +The four legacy “column” settings actually refer to the Projects V2 `Status` +single-select option. The UI calls them **Status values**, explains the four +issue/PR transitions, and proposes choices only when the selected Projects' +Status options can be inspected and have a common intersection. Missing +Status fields, inaccessible fields, incompatible options or manual entries +are explicit validation/recovery states, not silently verified choices. +The existing four shared values cannot map different vocabularies per Project; +the selector must explicitly explain this and block incompatible discovered +Projects before Apply. Per-Project mappings need a separate design. For manual +Projects whose fields cannot be read, require the operator to check the exact +Status option in each Project and answer a separate, non-defaulted attestation +question after the four values. `No` returns to Project selection within the +same run, Enter stays on the question with help, and only an explicit `Yes` +proceeds. This is a run-scoped human assertion, not a fabricated +remote verification or a new persisted Project field. Do not label it verified +by GitHub. A beginner may skip +Projects. `Empty` means the bounded API returned no *accessible* Projects; the +API does not prove there are none, so neither web nor CLI may assert a +genuinely empty organization. Show the applicable GitHub Project link and a +specific recovery action for each discovery state. + +```text +Want Projects? → audit setup PAT → list owner's Projects or explain why + → select by title/URL (or enter numbers manually) + → inspect common Status options → review effects → Apply +``` + +Text equivalent: decide before creating the PAT, choose existing Projects and +Status values after authorization, review the plan, and only then apply. +Add at least 18 distinct cases to the earlier 241-case budget: 5 policy, 3 +use-case, 4 adapter, 4 UI/CLI, and 2 security/integration. Include pagination +limits, personal-owner unsupported, empty/403/5xx, manual normalization and +owner checks, incompatible Status options, four locales, CLI parity, safe +links and no PAT in browser views. User documentation shows a Project URL, +explains number versus GraphQL ID, and says `Status` rather than “column”. +Provider evidence: [GitHub Projects REST](https://docs.github.com/en/rest/projects/projects) +and [Project fields](https://docs.github.com/en/rest/projects/fields). + +Representative recovery copy, with the same meaning in each advertised web +language and English CLI: + +```text +CI suggestion found: Tests · CI · GitHub App 15368 · success · abc1234 · Sep 29 +Required by branch rule: not checked. Open this CI run; confirm that its +coverage step fails the job below your budget. Choose it only after inspection. +Retry GitHub discovery (2 read-only attempts left), or add name/App ID/workflow. + +Projects query returned no accessible entries. That does not prove the owner +has no Projects. Retry, check Projects: read and owner, or enter the positive +number from github.com/orgs/OWNER/projects/12. The four selected Status values +must exist in every chosen Project; different vocabularies cannot be mapped. +``` + +The read-only retry is an application-port operation triggered by the CLI or +revision/capability-protected local browser endpoint. The current-question +projection is pure; only the application use case owns GitHub discovery and +the two-attempt budget. The browser keeps its prompt revision stable while the +question's candidates/status update, preserving unsent choices. A concurrent +answer, cancellation, or controller takeover prevents the old result from +committing to the visible prompt. Discovery refresh never stores a PAT in the +browser, changes the setup plan, or creates test issues/Actions. + +### 9.5 Language and truthful terminal outcomes + +The selector names the four supported languages; English is default +and all four catalogs must be complete for the shipped setup shell, prompt actions, +question labels/help, progress, PAT guidance, plan, validation, and all +result/cleanup states. An unfinished development branch may preview a locale + only with an explicit persistent notice on untranslated technical content, + including outside the questionnaire; this is not +release acceptance and such a preview must not be shipped as a complete +translation. Translation is presentation-only: stable question IDs, +enum values, API wire values, PAT URLs, and policy serialization remain +locale-neutral. A language change preserves the current draft, pending prompt +revision, pairing/controller capabilities, typed-but-unsubmitted non-secret +answer, and focus. Do not translate user-supplied repository/workflow/check +names or provider errors. Selection is tab-memory only, not a repository +setting; reload returns to English. Set the document `lang` and announce the +selected language accessibly. Unknown locale falls back to a +complete English view; a missing key in any advertised locale fails the build. +Dynamic provider/GitHub prose remains marked as external English when no +trusted structured reason exists, never silently machine-translated. +Translation catalogs are static packaged assets with no external fetch. +Keep one module per language and copy area (shell, question guidance, options, +permissions, prompts, progress, errors, and plan warnings); locale-neutral +resolvers compose them without duplicating setup rules. CI compares the exact +key set of every language against English, not just key counts, and checks +interpolation placeholders, non-empty copy, static option coverage, and the +full defined-question inventory. An equal count with a missing and an extra +key MUST fail. Deliberately identical product names and technical identifiers +are documented exceptions to the unchanged-English-copy audit. +The CLI remains English-only, including the complete question/stage/credential +help and links. First-party server-to-browser prose MUST instead carry a +stable semantic message ID plus locale-neutral parameters so the web never +renders an English CLI sentence as product copy. Technical identifiers, +commands and user-supplied names remain verbatim with bidi isolation; values +submitted back to setup remain the original locale-neutral option values. +For errors with no known semantic ID, the page MUST identify the content as +untranslated external/diagnostic text and still display a localized impact and +recovery action. It MUST NOT silently treat an arbitrary English message as a +translated explanation. Prompt choice labels and permission reasons use +stable identifiers; their submitted values and policy inputs remain unchanged. + +Screenshot evidence on 2026-09-29 showed `PLAN 04/06`, `Setup needs attention`, +and `No setup changes started`. That proves the page reported no Apply +mutation, but hid the actual cause and made cancellation, expiry, and a +blocking validation look identical. This is a product defect. The terminal +may have printed the cause; the browser MUST show the same normalized, +redacted reason and next action itself. A terminal outcome view is immutable +and includes: outcome kind, stopped stage, mutation-started fact, safe reason +code/message, completed local/remote effect names where known, next action, +and PAT cleanup guidance. Never infer `nothing changed` merely from a generic +`blocked` label if an earlier side effect is possible. Preserve the final +cause against later cleanup reminders and close events. Example: + +```text +Setup stopped before applying · Plan (4 of 6) +What happened: The selected CI check could not be verified with this PAT. +Already changed: Nothing in your repository or GitHub configuration. +Next: Give the setup PAT Checks: read and retry, or enter an exact check manually. +Your GitHub-created PAT still exists. Delete it in GitHub when finished. +[See technical details] [Close local session] +``` + +Text equivalent: the user knows the verified cause, what did and did not +change, the safe next action, and the separate GitHub PAT cleanup obligation. +An unknown provider failure says `Cause not confirmed` and offers a bounded +diagnostic code, never a false specific explanation. `copilot doctor` is a +follow-up inspection tool, not a substitute for the result on this page. + +### 9.6 First-run completion: orientation, editing, evidence, and handoff + +This is a proposed extension of the current implementation slice. A developer +must be able to complete setup without guessing whether a displayed value was +read from GitHub, inherited from this checkout, or merely supplied as a product +default. The same semantic decisions and recovery contract apply to English CLI +and the four-language browser; presentation controls may differ. + +```text +Confirm repository -> choose basic/custom scope -> permission preview -> setup PAT + -> inspect facts -> answer relevant groups -> review/edit -> bot PAT + -> Apply once -> inspect itemized receipt -> read-only verification/cleanup +``` + +Text equivalent: the operator sees the target, chooses the amount of optional +configuration, authorizes only needed reads/writes, reviews detected facts and +answers, corrects any group without restarting, explicitly approves mutation, +then receives a durable-to-the-live-session receipt and a safe verification +path. No step silently creates a PAT, issue, Action run, or Project item. + +1. **Orientation and progressive disclosure.** Start with a short capability + summary: what Copilot will install, what a basic path includes, and what + custom settings expose. Basic is the recommended presentation preset, not a + separate policy engine; it retains explicit choices for capabilities, + branches, Projects, guarded approval, storage scope, and all grants that can + change mutation or security. Advanced choices may retain documented defaults + only after the operator sees a grouped summary and can expand/edit them. + The UI MUST show current group and question position/remaining count, not + only six broad stages; conditional questions change the denominator + truthfully. CLI uses a textual equivalent. No fixed time estimate is + presented without measured evidence. +2. **Editing and restart safety.** Back/Change never mutates GitHub, never + resurrects a secret input, and preserves unaffected answers. The final + review groups consequences (features, agents, branches, CI approval, + Projects, Secrets/Variables) and links to edit each group. Changing an + answer re-evaluates dependent questions, project/check evidence, PAT grants, + plan, and Apply revision. An increased grant invalidates previous PAT + readiness until re-audited; a reduced grant warns about excess access but + does not silently revoke a GitHub token. A live session can be rejoined; + after process exit the operator restarts and re-enters credentials. No draft + or approval is persisted to disk or browser storage in this release. A + future opt-in resume requires a separate credential/data-safety SDD with + non-secret data only, explicit consent, 0600 permissions, expiry and fresh + PAT/identity/remote audit. No browser localStorage for PATs, pairing + capabilities, or approval state. +3. **Source-labelled suggestions.** A read-only fact port reports actual + default branch, available development branch, observed workflows/checks, + and selected owner/repository with `observed`, `inherited`, `suggested`, or + `unavailable` provenance plus a source link where safe. A configured + `master` default MUST NOT appear as GitHub-observed `main`. Missing or + inaccessible data is not an empty inventory. Before PAT, local Git facts + are labelled local; after PAT, remote facts may supersede suggestions but + never overwrite an explicit answer. The operator confirms branch roles. +4. **Evidence-assisted CI and Projects.** Observed check identity includes + name, App ID, workflow, run/ref and bounded search scope. When branch + protection/rulesets are readable, mark required-check evidence with the + exact source; otherwise say `not checked` and link to the corresponding + GitHub settings page. A green run never establishes coverage enforcement; + the human attestation remains mandatory. Show the workflow file and run + when exact safe links are available. For several Projects whose Status + vocabularies differ, support an explicit per-Project mapping of all four + transitions or explain why that capability is not yet safe; never claim a + shared value works when it does not. The runtime model and PAT audit must + support per-Project mappings before the UI offers them. No broad grant is + added merely to make a suggestion appear. + In the first implementation slice, Basic skips only catalogued advanced + questions whose configuration still equals the product default; it never + suppresses a non-default override. The plan names every group with retained + defaults, exposes an edit control for it, and re-runs permission audit after + an edit. Selecting independent agent models exposes all per-role questions. + The web plan review also lists issue workflows, all agent/model routes, + exact trusted CI producer identities, coverage mode/check, each selected + Project Status transition, storage scopes, and issue-resource handling; + technical values remain unchanged while labels are localized. + An active repository-owned branch ruleset is positive required-check evidence + only when both check context and source App ID match the observed run; + inherited organization rulesets, branch protection, or an unreadable ruleset + remain `not checked`, never `optional`. A numeric + Project selection with incompatible Status options is blocked rather than + silently mapped to the wrong option. Per-Project mappings require a separate + runtime input and are not implied by the shared-Status selector. +5. **PAT handoff and lifecycle.** Every GitHub form handoff displays the + expected operator/bot account, owner, exact repository selection, grants, + expiry, and the step to return to. Distinguish pending organization + approval, wrong account, insufficient scope, expired/revoked PAT, and + provider outage when evidenced. Setup PAT deletion is a user action in + GitHub, never implied by closing the local page. Bot PAT renewal is a + post-setup obligation; its Secret name/scope and renewal date (if known) + appear in the receipt without revealing value. CLI help prioritizes the + hidden prompt; command-line flags that expose a PAT in shell history are + advanced escape hatches with an explicit warning. +6. **Structured results and read-only verification.** The terminal and web + receive one redacted semantic result: stage, cause, completed/skipped/ + failed/potentially-applied resources, scope, mutation-started fact, + diagnostic reference, safe next action, and PAT cleanup. Unknown causes + remain explicitly unknown, not generic success/failure. An interrupted + Apply cannot claim nothing changed. A post-success `doctor` affordance + runs only read-only checks; it is separate from Apply and cannot silently + dispatch credential-health or create test resources. After a confirmed + successful Apply, the controller may run one bounded in-page read-only + inspection with at most one retry; the page exposes only redacted counts, + never raw doctor evidence or provider errors. The browser also gives the + explicit `copilot doctor --read-only` command; that mode must mark Secret + values unverified. Plain `copilot doctor` may dispatch the + installed credential-health workflow and must not be described as read-only. + The local setup workflow emits a versioned, value-free operation receipt + covering files, Secrets, labels, issue types, Variables and the initial tag. + A mutation attempt is `needs-inspection` until a confirmed return; a later + operation is `not-started` after an earlier exception. The CLI-to-browser + mapper accepts only these exact operation IDs, states and scopes and never + serializes raw provider errors or arbitrary result identifiers. +7. **Comprehension and accessibility gate.** Every question needs a concrete + recommendation, source of the expected value, consequence of alternatives, + validation at the field, and targeted documentation. Generic `enter the + exact value` copy alone does not meet this requirement. The most important + consequence remains visible; expanded help adds detail. Errors identify + the field, cause and correction, preserving input. Dynamic progress/errors + use locale keys and accessible status announcements; an unknown provider or + validation message shows a localized, honest fallback with a terminal- + inspection instruction rather than unreviewed English text. Focus returns + to the current heading + or invalid control, and every state is usable by keyboard, screen reader, + at 200% zoom, and in both themes. English, Spanish, French and Portuguese + must pass semantic language review across success, partial, blocked, + validation and PAT cleanup; key parity is necessary but insufficient. + +Example review and receipt (labels localized in web; CLI English): + +```text +Review setup for org/repo · 6 groups checked, no changes applied +Branches: main (GitHub observed), develop (your answer) [Change] +Trusted CI: Tests · App 15368 · CI (observed run; branch rule not checked) [Change] +Projects: Engineering #12; four Status transitions verified [Change] +Setup PAT: required grants re-audited · Bot PAT: still to be provided +[Apply later] [Continue to bot PAT] + +Setup partially applied · 3 of 4 resource groups inspected +Files: applied · Secret PAT (repository): potentially written · Variables: not started +Cause: GitHub rejected the Variable write (403). No automatic replay. +Next: inspect Secret PAT and Variables in GitHub, then run read-only doctor. +Temporary setup PAT still exists in GitHub. [PAT settings] [Technical details] +``` + +The first view is a no-mutation decision point with editable facts and +explicit provenance. The second is an itemized partial result that does not +equate a failed operation with rollback and separates the two PAT lifecycles. +Primary design references: [W3C multi-page forms](https://www.w3.org/WAI/tutorials/forms/multi-page/), +[W3C error notifications](https://www.w3.org/WAI/tutorials/forms/notifications/), +[GOV.UK check answers](https://design-system.service.gov.uk/patterns/check-answers/), +and [GitHub PAT management](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens). + ## 10. Failure, recovery, and cleanup | Condition | Impact and retained facts | Automatic retry | Operator action / cleanup | @@ -768,23 +1237,27 @@ still offers the terminal setup and doctor paths. ## 14. Testing strategy and numeric budget -The floor is **103 distinct cases**, derived from shared-engine parity, +The revised floor is **350 distinct cases** (the previous 274 plus 76 +first-run-completion cases), derived from shared-engine parity, six-stage transitions, two PAT roles, local HTTP abuse, packaged installs, -drift, and partial mutation. Each test/parameterized behavior counts once; +drift, partial mutation, CI discovery/attestation, complete four-language help, +contextual documentation navigation, English CLI parity, bounded Project +discovery, number/URL validation and shared Status options, +and truthful terminal results. Each test/parameterized behavior counts once; existing CLI tests are retained, not re-counted as new web evidence. | Area | Minimum cases | Risk covered | |---|---:|---| -| Pure choices/config/grant/plan projection | 14 | source locks, defaults, conditional questions, two PAT roles, grants, revision invalidation | -| Session/use cases/idempotency/races | 20 | stages, saved-review pass, tab takeover, stale events, single-flight Apply, cancel, idle/crash replay boundaries, replacement-lock race | -| GitHub/workspace/HTTP adapters | 10 | identity, missing/unknown grants, org approval, Secret scope, bounded errors and provider mapping | -| CLI/packaging/workflow contracts | 10 | flag combinations, browser-open fallback, asset manifest, npm pack/global install, unchanged Action/API bundles | -| UI/accessibility/localization/content | 18 | pending/action/blocked/partial/complete, plan diff, narrow/zoom/keyboard/focus/no-color, both palettes/system toggle and contrast, English fallback, escaping | -| Integration/compatibility/recovery | 13 | terminal-web parity, manual/environment/dry-run, drift, partial write, doctor reconciliation, root-versus-subdirectory launch | -| Security/abuse | 18 | Host/Origin/CSRF, terminal pairing and attempt cap, session-key enforcement on every other API route, CORS, replay, path traversal, XSS/CSP, secret leaks, no GET mutation, body/time/connection limits, atomic lock publication | -| **Total** | **103** | No double counting | - -Within the 18 UI cases, cover at least one render/interaction for each prompt +| Pure choices/config/grant/plan projection | 48 | prior rules plus source provenance, optional-step filtering, dependent invalidation and shared-Status compatibility | +| Session/use cases/idempotency/races | 54 | prior stages plus back/edit/review, live reconnect/no-durable-resume boundaries, stale PAT grants, Apply races and immutable receipt | +| GitHub/workspace/HTTP adapters | 40 | bounded discovery, branch/rule facts, provider error mapping, retry auth and 403/5xx differentiation | +| CLI/packaging/workflow contracts | 37 | English progress/help/edit, PAT safety warnings, safe links and bundle isolation | +| UI/accessibility/localization/content | 101 | four-language question/receipt content, progress, edit controls, validation, focus and blocked/partial states | +| Integration/compatibility/recovery | 42 | live reconnect, preserved answers, incompatible-Project blocking and complete beginner replay in CLI/web | +| Security/abuse | 28 | forged links, stale revisions/controller takeover, PAT exclusion, permissions and least-privilege fallback | +| **Total** | **350** | No double counting | + +Within the 101 UI cases, cover at least one render/interaction for each prompt presenter, one revision-change form reset, secret clearing before dispatch, read-only disabling, all outcome variants, and both theme palettes. Static architecture tests additionally reject network/storage/provider calls from @@ -892,6 +1365,53 @@ help for `--web` explains local-only scope and the `--non-interactive` conflict. the UI does not claim to recover its value and follows the current re-entry/preservation policy. Given an environment-supplied setup PAT, exit never claims to have removed it from the parent shell. +17. Given any reachable questionnaire ID, a first-time operator can read + specific what/when/where/how/why/example/effect/verification help and a + directly relevant safe reference link in the CLI (English) and web (all + four locales); no help item silently falls back to a generic section. +18. Given `?` at an interactive CLI question, expanded English help and its + URL appear without recording an answer or advancing the questionnaire; + the same question and default are then presented again. Credential-method, + repository-owner and bot-login prompts offer the same non-advancing help; + the final Apply prompt explains planned writes and partial-failure recovery + before returning to an unanswered approval; a PAT is never echoed while + showing help. +19. Given a web language switch during a question, PAT, plan, validation or + result, all first-party copy and option labels switch together without + changing IDs, option values, typed input, permissions or Apply state. + Technical values retain their original identity in every locale. +20. Given a dynamic GitHub or provider error, the page explains its structured + impact and next action in the selected language; raw external text is + separately labelled, escaped and never used as a documentation link. +21. Given any question, Back or Change returns to the selected earlier answer + with unaffected non-secret answers retained; a changed dependency asks + newly applicable questions and re-audits grants before new Apply approval. +22. Given a long or conditional question group, browser and CLI show the + current group and truthful remaining question count; a basic presentation + path exposes all security-significant choices and an editable advanced + summary without silently enabling a capability. +23. Given an actual `main` default branch but a product `master` fallback, + the suggestion is labelled GitHub-observed `main`; if remote inspection + fails, the product fallback remains explicitly labelled unverified. +24. Given a readable required-check rule, the exact source and matching + producer are visible; given unreadable rules, the UI says `not checked` + and retains manual attestation. Given incompatible Project Status values, + no shared mapping is applied to all Projects without explicit mapping. +25. Given a wrong, pending, expired or insufficient PAT, the handoff names + the evidenced cause, expected account/owner and repair step; GitHub + cleanup and bot renewal remain separate human obligations. +26. Given failure before or during Apply, browser and CLI render the same + redacted, itemized effect states; ambiguous writes are `potentially + applied`, not `rolled back` or `nothing changed`. A read-only doctor action + never dispatches or creates a test resource. +27. Given a browser reconnect to a live process, it reads the current + server-owned state without replaying an answer or restoring a secret. + Given process exit, no durable draft, pairing authority, or approval is + written; restart revalidates PAT, identity, remote facts, and plan. +28. Given a novice keyboard/screen-reader user in any supported web locale, + every high-risk question identifies source, recommendation and consequence, + errors identify the exact field and correction, and progress/result + changes are announced without relying on color or a terminal window. ## 17. Requirements traceability @@ -904,6 +1424,8 @@ help for `--web` explains local-only scope and the `--non-interactive` conflict. | Revision-bound Apply/recovery (§6.1, §6.3, §10) | CLI root precondition + session coordinator + execution boundary | scenarios 1, 8–11; nested-path launch regression | troubleshooting, provisioning | | Browser security/privacy (§4.3, §11) | loopback HTTP/asset adapters + redacted presenter | scenarios 9, 12–13 | authentication, architecture | | Accessible truthful UX (§9) | Svelte presenter + message catalog | scenarios 5–7, 10–11, 13–14 | how-to-use, troubleshooting | +| Complete question help and links (§9.3–9.5) | application semantic help catalog + English CLI renderer + four-language web presenter | scenarios 17–20; exhaustive ID/link/locale gates | how-to-use, configuration, authentication, agents | +| First-run completion (§9.6) | pure questionnaire/evidence policies, application session/edit/receipt use cases, read-only provider ports, CLI/web presenters | scenarios 21–28; 76 added risk-derived cases plus human first-use review | how-to-use, authentication, troubleshooting, configuration | ## 18. Implementation sequence and current evidence @@ -932,15 +1454,15 @@ application coordinator and full UI/accessibility acceptance gates. These facts are **not** release acceptance. The orchestration in `src/cli/commands/setup.ts` still needs extraction into the prescribed -application-level session coordinator; the 103-case budget, full human + application-level session coordinator; the revised 350-case budget, full human cross-platform/accessibility review, exact per-resource progress/partial evidence, and adversarial concurrency/idle/crash suite remain open. The catalog stays `proposed` until the definition of done is evidenced. Existing terminal policy/use cases remain the authority; the current web path does not introduce its own permission catalog. -The latest full local run on 2026-09-28 passed 502 Jest suites / 5,392 tests, -with 95.95% statements, 90.88% branches, 96.53% functions, and 97.27% lines +The latest full local run on 2026-09-29 passed 506 Jest suites / 5,484 tests, +with 95.97% statements, 90.91% branches, 96.55% functions, and 97.27% lines repository-wide. The new setup-PAT intent, bootstrap audit, configured audit, remote-fact comparison, and override merge modules each reached 100% in all four metrics; final web Apply authorization reached 100% lines and 95.83% @@ -951,10 +1473,87 @@ empty issue-workflow selection, drift, cancellation, and package isolation. Typecheck, lint, Svelte diagnostics, full build, catalog, documentation, workflow, npm-package validation, and package smoke checks passed without real PATs or setup dogfooding. Human browser/accessibility and cross-platform -review, the formal 103-case-by-area acceptance mapping, and the complete +review, the formal 350-case-by-area acceptance mapping, and the complete application-level session coordinator remain open release gates. The generated bundle synchronization check runs after the source/build commit is staged. +The 2026-09-29 localization slice originally previewed ten languages, but +product scope was reduced to English, Spanish, French, and Portuguese. The six +discarded locale catalogs and browser-only high-risk-question overrides are +removed. Separate language modules now own the web shell, question labels and +specific purposes, option labels, prompt decisions, permission explanations, +and progress/validation messages. The four-language question-help contract +includes all defined questions and expanded fields. Tests check exact key +parity, placeholders, static option coverage, permission-reason and plan-warning +inventories, and unchanged wire values. Browser-originated session errors now +have per-language catalogs and an explicitly translated unknown-error fallback; +credential checks carry locale-neutral names and statuses for local display. +These checks prove structural completeness, not linguistic quality or complete +UI coverage. The persistent translation preview remains until dynamic provider +text and normal/blocked/partial render review are closed. The source CI discovery +adapter is a bounded suggestion source, not coverage enforcement or an +attestation. + +The subsequent guidance slice gives all 108 defined questions a field-specific +English/Spanish purpose, adds their contextual documentation links, and lets +the terminal open non-advancing `?` help for questionnaire, credential and +final Apply prompts. Inventory and link tests cover those definitions. Until +every web state is localized and reviewed, every non-English locale displays a +persistent translation-preview notice, including on PAT and result screens. +The related documentation link is visible beside each question without opening +the expanded help. This is a development affordance, **not** four-language +release acceptance. Dynamic provider text and independent +language/accessibility review remain open. + +The next 2026-09-29 discovery slice preserves the questionnaire draft across +bounded, read-only retries; distinguishes unavailable CI/Projects data from +no accessible results; shows the source, run, conclusion, and sampling limits +of suggested checks; and validates unique producer names before selecting a +coverage-enforcing check. Project choices are made from accessible numbered +Projects when available, with an owner-checked URL/number fallback. Selected +Projects retain their identity if they disappear from a later bounded listing, +but are visibly unverified. Common `Status` options are checked when readable; +otherwise an explicit, run-scoped human attestation is required before Apply. +Personal-owner Projects do not offer a retry that the fine-grained PAT API +cannot fulfill. The web and CLI share the same application-owned discovery +contract; neither creates a test issue or dispatches a test Action. + +This slice passed 508 Jest suites / 5,591 tests on 2026-09-29. Repository-wide +coverage was 95.8% statements, 90.64% branches, 96.41% functions, and 97.15% +lines; all configured coverage budgets passed. Typecheck, lint, Svelte +diagnostics, isolated web and CLI production builds, workflow/documentation +validation, and specification validation also passed. These automated checks +do not close the 274-case-by-area acceptance mapping, independent linguistic +and accessibility review, real-browser/cross-platform trials, or the +application-level session coordinator. The catalog remains `proposed`. + +The subsequent first-run slice implements Basic/Custom presentation with +explicit permission-affecting decisions, per-question progress and contextual +documentation, source-labelled branch suggestions, revision-bound Back and +section editing, permission re-audit after edits, evidence-labelled check and +Project discovery, a complete grouped plan, and an itemized redacted result. +Web and English CLI use the same questionnaire and policies. The web result can +run a bounded metadata-only doctor after confirmed success; its response +contains counts only. `copilot doctor --read-only` is the matching terminal +path and never dispatches credential-health Actions; plain doctor may dispatch +the already-installed health workflow. The session/error and result copy has +matching English, Spanish, French, and Portuguese catalogs. The latest full +local run passed 509 Jest suites / 5,636 tests with 95.61% statements, 90.4% +branches, 96.32% functions, and 97.05% lines overall; instrumented web +TypeScript reached 100% statements, functions, and lines. Svelte components +are not included in that TypeScript coverage claim. The result/doctor tests +use fake ports and local loopback fixtures; no PAT, repository setup, test +issue, or GitHub Action was created. + +These measurements do **not** close independent linguistic, keyboard/screen- +reader, 200%-zoom, dark/light, real-browser/cross-platform, or full 350-case +acceptance review. A disk-persisted resume remains a separately specified +future capability; this slice supports reconnecting to a live session only. +The CLI orchestration has not yet been extracted into the prescribed +frontend-neutral coordinator. Until these gates are evidenced, the catalog +remains `proposed` and the non-English browser notice remains a translation +preview, not an unconditional release-quality claim. + 1. Review this threat model and UI prototype with product/security/accessibility; freeze semantic transport schemas, redacted views, and error taxonomy. 2. Extract the existing CLI orchestration into a frontend-neutral setup @@ -977,7 +1576,7 @@ bundle synchronization check runs after the source/build commit is staged. application decision engine with enforceable dependency rules. - [ ] Local HTTP, controller, PAT, plan revision, and Apply defenses pass the adversarial/security budget with no secret in browser storage or logs. -- [ ] All 103 distinct new web cases by area pass without real PATs or +- [ ] All 350 distinct setup-assistant cases by area pass without real PATs or dogfooding; the repository and changed-module coverage thresholds already pass for the current implementation slice. - [ ] Global npm-pack install serves complete local assets; Action/API bundles @@ -1006,6 +1605,9 @@ bundle synchronization check runs after the source/build commit is staged. [Svelte overview](https://svelte.dev/docs/svelte/overview), [Vite static build](https://vite.dev/guide/build), [Node HTTP](https://nodejs.org/api/http.html). +- Language-selection decision: this release deliberately supports English, + Spanish, French, and Portuguese, which maintainers can translate and review + directly. It makes no claim about a live ranking of speaker populations. - Security sources: [OWASP CSRF](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html), [CSP](https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html), [HTML5 storage](https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html). diff --git a/specs/setup-configuration-credentials-and-doctor.md b/specs/setup-configuration-credentials-and-doctor.md index b37ebbb31..f3cd1c1ac 100644 --- a/specs/setup-configuration-credentials-and-doctor.md +++ b/specs/setup-configuration-credentials-and-doctor.md @@ -6,7 +6,7 @@ - Catalog capability ID: `setup-and-doctor` - Last verified: 2026-09-28 (automated journey/presentation gates; live GitHub path remains external) - Owners: Copilot maintainers -- Scope: interactive/non-interactive installation planning, file and resource provisioning, credential validation, and read-only diagnosis +- Scope: interactive/non-interactive installation planning, file and resource provisioning, credential validation, and metadata-only diagnosis - Related issues/PRs: merge-queue readiness SDD; architecture quality and scalability hardening SDD - Required review gates: product UX, architecture, testing, documentation, security/operations @@ -17,7 +17,10 @@ `copilot setup` builds and previews a validated installation plan before writing workflows, templates, Variables, Secrets, labels, issue types, projects, or the initial tag. `copilot doctor` inspects the expected contract without changing -repository configuration. The setup PAT is separate from the workflow PAT and +repository configuration, but its normal credential check may dispatch an +installed GitHub Action. `copilot doctor --read-only` skips that dispatch, +reports Secret values as unverified, and performs only read operations. The +setup PAT is separate from the workflow PAT and provider credentials; secret values never enter config files or plan objects. ```text @@ -127,7 +130,7 @@ organization value that GitHub Actions will expose. | Credentials | one ambiguous token | setup/workflow/provider separation | least privilege | | Remote state | overwrite assumptions | inspect + preserve/replace decision | controlled drift | | Readiness | discovered during release | setup and doctor checks | earlier action | -| Diagnosis | mutation required | read-only doctor | safe audit | +| Diagnosis | mutation required | explicit metadata-only `doctor --read-only`; ordinary doctor may dispatch the installed health Action | safe inspection or separately authorized active check | The architecture hardening preserves the product contract while making cancellation, skipped diagnosis, ordering, and read-only authority explicit. @@ -359,7 +362,7 @@ arbitrary warning text into the pure plan builder. | unverifiable credential | feature may be unsafe | name/scope only | yes | run health/manual check | none | | denied changed file | file unchanged | backup status | yes | approve/adapt | remove unused backup manually | | partial GitHub writes | subset installed | resource names/status | yes | rerun preserve-existing | no destructive rollback | -| doctor fail | no mutation | diagnostic report | yes | run setup/fix access | none | +| read-only doctor fail | no mutation or Action dispatch | diagnostic report | yes | fix access and rerun | none | ## 11. Security, permissions, and privacy @@ -430,7 +433,10 @@ widths, canceled prompts, secret masking, and GitHub permission variants. attempt, local and GitHub state are unchanged. With such an attempt, setup reports a partial result and requires branch/history inspection. 6. Given a changed managed file, setup backs up before approved replacement. -7. Given doctor, no mutation port is called and unhealthy state returns non-zero. +7. Given `doctor --read-only`, no mutation or credential-health dispatch port + is called; installed Secret names are inspected but values are unverified, + and unhealthy state returns non-zero. Given ordinary doctor, any installed + credential-health dispatch is an explicit, separately chosen active check. 8. Given merge-queue without proven support, setup/doctor reports fail closed. 9. Given output inspection, no secret value appears. 10. Given no explicit locale, doctor renders one complete English report. @@ -488,7 +494,8 @@ widths, canceled prompts, secret masking, and GitHub permission variants. - [x] Every new option has default, bounds, precedence, persistence, retirement/rejection, and security rules. - [x] The 112-case budget and coverage thresholds pass. -- [x] Setup cancel/retry/partial state and doctor read-only behavior pass. +- [x] Setup cancel/retry/partial state and metadata-only `doctor --read-only` + behavior pass; ordinary doctor dispatch is disclosed separately. - [x] Secrets are absent from plans, config, logs, errors, and backups. - [x] Workflow/assets, documentation, and catalog checks pass. - [ ] Human terminal and permission-path UX evidence is captured. diff --git a/src/__tests__/cli.test.ts b/src/__tests__/cli.test.ts index 8ecf9fdfc..6123bfc11 100644 --- a/src/__tests__/cli.test.ts +++ b/src/__tests__/cli.test.ts @@ -224,6 +224,10 @@ describe('CLI', () => { }); describe('doctor', () => { + it('passes the read-only choice to diagnosis without dispatching health checks from the command', async () => { + await program.parseAsync(['node', 'cli', 'doctor', '--non-interactive', '--read-only', '--token', 'github_pat_doctor_test_token']); + expect(mockDoctorExecute).toHaveBeenCalledWith(expect.objectContaining({ readOnly: true })); + }); it('presents the report with its resolved catalog and returns a failing exit code when unhealthy', async () => { const catalog = { locale: 'es-ES', message: jest.fn() }; const report = { healthy: false, checks: [], totals: { pass: 0, warn: 0, fail: 1, skipped: 0 } }; @@ -501,6 +505,7 @@ describe('CLI', () => { const answerWebPrompt = async (prompt: WebSetupPrompt): Promise => { if (prompt.title === 'Confirm this repository') return 'Yes, this is my repository'; + if (prompt.title === 'Choose setup detail') return 'Basic guided setup'; if (prompt.title === 'How will you provide your setup PAT?') return 'Manual PAT'; if (prompt.title === 'Temporary setup PAT') return 'github_pat_web_setup_test_token'; if (prompt.kind === 'plan') return 'approve'; @@ -1106,7 +1111,7 @@ describe('CLI', () => { const loadConfig = jest.spyOn(configFile, 'loadSetupConfigurationOverrides').mockReturnValue({ createInitialTag: false, features: { issues: false, release: false, hotfix: false }, - projects: { ids: 'PVT_example' }, + projects: { ids: '42' }, }); const input = guidedTerminal(prompt => prompt.includes('repository owner an organization') ? '1' : undefined); const createTerminal = jest.spyOn(terminalDriver, 'createInteractiveTerminalDriver') @@ -1117,7 +1122,7 @@ describe('CLI', () => { '--skip-variables', '--skip-secrets', '--pr-approval-mode', 'off']); expect(input.readText.mock.calls.some(([prompt]) => String(prompt).includes('repository owner an organization'))).toBe(true); expect(mockTokenPermissionInspect.mock.calls[0][0].requirements.filter((item: SetupTokenPermissionRequirement) => item.scope === 'organization')) - .toEqual([expect.objectContaining({ permission: 'Projects', level: 'write' })]); + .toEqual([expect.objectContaining({ permission: 'Projects', level: 'read' })]); expect(input.readSecret).toHaveBeenCalledWith('Setup PAT'); expect(process.exitCode).toBe(1); } finally { loadConfig.mockRestore(); createTerminal.mockRestore(); } @@ -1420,7 +1425,8 @@ describe('CLI', () => { }); try { await program.parseAsync(['node', 'cli', 'setup']); - expect(terminal.readText).toHaveBeenCalledTimes(1); + expect(terminal.readText).toHaveBeenCalledTimes(2); + expect(terminal.readText).toHaveBeenCalledWith(expect.stringContaining('How much configuration detail')); expect(terminal.readSecret).toHaveBeenCalledWith('Setup PAT'); expect(consoleLogSpy.mock.calls.flat().join('\n')).not.toContain('Revoke temporary setup PAT'); expect(runLocalAction).not.toHaveBeenCalled(); diff --git a/src/__tests__/cli_context_branch.test.ts b/src/__tests__/cli_context_branch.test.ts index 0b7991741..50c739a4d 100644 --- a/src/__tests__/cli_context_branch.test.ts +++ b/src/__tests__/cli_context_branch.test.ts @@ -1,7 +1,7 @@ -import { execSync } from 'child_process'; -import { getCurrentAttachedBranch } from '../cli_context'; +import { execFileSync, execSync } from 'child_process'; +import { getCurrentAttachedBranch, hasLocalOrTrackedGitBranch } from '../cli_context'; -jest.mock('child_process', () => ({ execSync: jest.fn() })); +jest.mock('child_process', () => ({ execSync: jest.fn(), execFileSync: jest.fn() })); describe('verified branch for web setup', () => { afterEach(() => jest.clearAllMocks()); @@ -21,4 +21,21 @@ describe('verified branch for web setup', () => { (execSync as jest.Mock).mockImplementation(() => { throw new Error('detached'); }); expect(getCurrentAttachedBranch('/a/checkout')).toBeUndefined(); }); + + test('labels a branch observed in a local or origin-tracking ref only', () => { + (execFileSync as jest.Mock).mockImplementation((_command, args: string[]) => { + if (args[3] === 'refs/heads/develop') throw new Error('missing'); + return Buffer.alloc(0); + }); + expect(hasLocalOrTrackedGitBranch('/a/checkout', 'develop')).toBe(true); + expect(execFileSync).toHaveBeenCalledWith('git', ['show-ref', '--verify', '--quiet', 'refs/remotes/origin/develop'], + { cwd: '/a/checkout', stdio: 'pipe' }); + }); + + test('never treats unsafe input or a missing branch as observed', () => { + expect(hasLocalOrTrackedGitBranch('/a/checkout', 'bad..branch')).toBe(false); + expect(execFileSync).not.toHaveBeenCalled(); + (execFileSync as jest.Mock).mockImplementation(() => { throw new Error('missing'); }); + expect(hasLocalOrTrackedGitBranch('/a/checkout', 'develop')).toBe(false); + }); }); diff --git a/src/application/contracts/web_setup_view.ts b/src/application/contracts/web_setup_view.ts index fddc06dfb..ffca7acd0 100644 --- a/src/application/contracts/web_setup_view.ts +++ b/src/application/contracts/web_setup_view.ts @@ -1,15 +1,82 @@ import type { SetupJourneyView } from '../policies/setup_journey_policy'; -import type { SetupQuestion } from '../../domain/setup_questionnaire'; +import type { SetupQuestion, SetupQuestionnaireProgress } from '../../domain/setup_questionnaire'; +export type { SetupApprovalCheckCandidate, SetupProjectCandidate, SetupDiscoveryStatus } from '../../domain/setup_questionnaire'; +export type { SetupQuestion } from '../../domain/setup_questionnaire'; +export type { SetupFeature } from '../../domain/setup'; import type { SetupTokenPermissionReport, SetupTokenPermissionRequirement, SetupTokenRole } from '../../domain/setup_token_permissions'; +import type { SetupCredentialStatus } from '../../domain/setup'; + +export interface SetupQuestionExplanation { + readonly label: string; + readonly summary: string; + readonly when: string; + readonly where: string; + readonly how: string; + readonly why: string; + readonly example: string; + readonly effect: string; + readonly verify: string; + readonly documentation: { readonly title: string; readonly url: string }; +} + +export type SetupQuestionPresentation = Readonly>; + +export type WebSetupPromptCopyId = + | 'repository.confirm' | 'setup.depth' | 'setup.environmentPat' | 'plan.review' | 'workflow.update' + | 'setupPat.method' | 'setupPat.ownerKind' | 'setupPat.review' | 'setupPat.entry' + | 'setupPat.confirmAccount' | 'setupPat.confirmWrites' | 'botPat.method' | 'botPat.login' + | 'botPat.entry.guided' | 'botPat.entry.manual' | 'credential.apiKey' + | 'credential.existing' | 'apply.confirm'; + +export interface WebSetupPromptCopyRef { + readonly copyId?: WebSetupPromptCopyId; + readonly copyValues?: Readonly>; +} + +export type WebSetupMessageCopyId = + | 'session.controlMoved' | 'session.cancelled' | 'plan.ready' | 'permission.preview' + | 'setupPat.corrected.bootstrap' | 'setupPat.corrected.final' | 'setupPat.cleanup' + | 'botPat.separation' | 'credential.checks' + | 'credential.status.valid' | 'credential.status.invalid' | 'credential.status.missing' + | 'credential.status.unverifiable' | 'credential.status.not_required' + | 'validation.producers' | 'validation.duplicateNames' | 'validation.number' | 'validation.boolean' + | 'validation.choice' | 'validation.projectStatusVerified' | 'validation.projectStatusRedo' + | 'validation.unknownResource' | 'validation.unknownWorkflow' | 'validation.firstQuestion' + | 'validation.duplicateProducer' | 'validation.savedStatus' | 'validation.projectIncompatible' + | 'validation.projectLimit' | 'validation.projectOwnerNeeded' | 'validation.projectOwnerMismatch' + | 'validation.projectUrl' | 'validation.projectNumber' | 'validation.projectNumberRange' + | 'validation.projectDuplicate' | 'validation.unknown'; export type WebSetupPrompt = - | { kind: 'question'; title: string; question: SetupQuestion; phase: string; pass: number } - | { kind: 'choice'; title: string; description?: string; choices: readonly string[]; defaultValue?: string } - | { kind: 'text' | 'secret'; title: string; description?: string; optional?: boolean; link?: string } - | { kind: 'confirm'; title: string; description?: string; choices: readonly string[] } - | { kind: 'plan'; title: string; plan: WebSetupPlan }; + | ({ kind: 'question'; title: string; question: SetupQuestion; presentation?: SetupQuestionPresentation; phase: string; pass: number; + progress?: SetupQuestionnaireProgress; canGoBack?: boolean } & WebSetupPromptCopyRef) + | ({ kind: 'choice'; title: string; description?: string; choices: readonly string[]; defaultValue?: string } & WebSetupPromptCopyRef) + | ({ kind: 'text' | 'secret'; title: string; description?: string; optional?: boolean; link?: string } & WebSetupPromptCopyRef) + | ({ kind: 'confirm'; title: string; description?: string; choices: readonly string[] } & WebSetupPromptCopyRef) + | ({ kind: 'plan'; title: string; plan: WebSetupPlan; editGroups?: readonly SetupQuestion['stateId'][] } & WebSetupPromptCopyRef); export interface WebSetupPlan { + readonly presentationDefaults: readonly { readonly group: string; readonly count: number }[]; + readonly decisions: { + readonly enabledCapabilities: readonly string[]; + readonly agentRouting: readonly { readonly role: string; readonly provider: string; readonly modelProvider: string; readonly model: string }[]; + readonly issueWorkflows: readonly string[]; + readonly productionBranch: string; + readonly developmentBranch: string; + readonly approvalMode: string; + readonly trustedChecks: readonly { readonly name: string; readonly sourceAppId: number; readonly workflowName: string }[]; + readonly producerAttested: boolean; + readonly coverageMode: string; + readonly coverageCheck: string; + readonly coverageMinimum?: number; + readonly coverageArtifactWorkflow?: string; + readonly coverageReporterAttested?: boolean; + readonly projectNumbers: readonly string[]; + readonly projectStatuses: readonly { readonly transition: string; readonly value: string }[]; + readonly variableScope: string; + readonly secretScope: string; + readonly initialTag: boolean; + }; readonly files: readonly string[]; readonly workflows: readonly string[]; readonly variables: readonly string[]; @@ -23,7 +90,18 @@ export interface WebSetupView { readonly repository: string; readonly journey?: SetupJourneyView; readonly prompt?: WebSetupPrompt; - readonly message?: { tone: 'info' | 'success' | 'warning' | 'error'; text: string; link?: string }; + readonly message?: { tone: 'info' | 'success' | 'warning' | 'error'; text: string; link?: string; + copyId?: WebSetupMessageCopyId; copyValues?: Readonly>; + credentialChecks?: readonly { readonly name: string; readonly status: SetupCredentialStatus }[] }; readonly permissions?: { role: SetupTokenRole; requirements?: readonly SetupTokenPermissionRequirement[]; report?: SetupTokenPermissionReport }; readonly outcome?: 'complete' | 'partial' | 'blocked' | 'cancelled' | 'dry-run'; + readonly doctor?: { readonly status: 'running' | 'complete' | 'failed'; readonly healthy?: boolean; + readonly pass?: number; readonly warn?: number; readonly fail?: number; readonly skipped?: number }; + readonly resultDetail?: { + readonly reasonCode: 'permissions' | 'storage' | 'configuration' | 'session-expired' | 'cancelled' | 'provider' | 'rate-limit' | 'unknown'; + readonly stoppedStage: string; + readonly mutationStarted: boolean; + readonly diagnosticRef?: string; + readonly effects?: readonly { readonly id: string; readonly state: 'completed' | 'skipped' | 'needs-inspection' | 'not-started'; readonly scope?: 'local' | 'repository' | 'organization' | 'mixed' }[]; + }; } diff --git a/src/application/policies/__tests__/setup_configuration_policy.test.ts b/src/application/policies/__tests__/setup_configuration_policy.test.ts index 27fe2c190..99ec6054a 100644 --- a/src/application/policies/__tests__/setup_configuration_policy.test.ts +++ b/src/application/policies/__tests__/setup_configuration_policy.test.ts @@ -560,7 +560,7 @@ describe('setup configuration policy', () => { configuration.agents.findings.provider = 'cursor'; expect(buildSetupPlan(configuration).warnings).toEqual(expect.arrayContaining([ - expect.stringContaining('Project IDs'), + expect.stringContaining('Selected Project numbers'), expect.stringContaining('Always-provision mode reinstalls only default Codex/OpenCode runtimes'), expect.stringContaining('no automatic Cursor installer'), expect.stringContaining('Organization-level'), diff --git a/src/application/policies/__tests__/setup_pat_creation_url_policy.test.ts b/src/application/policies/__tests__/setup_pat_creation_url_policy.test.ts index b536538ca..dd16619ce 100644 --- a/src/application/policies/__tests__/setup_pat_creation_url_policy.test.ts +++ b/src/application/policies/__tests__/setup_pat_creation_url_policy.test.ts @@ -60,7 +60,7 @@ describe('buildSetupPatCreationUrl', () => { it('keeps even the largest valid owner, repository, and grant set within a practical terminal URL', () => { const grants = [ - ...['Metadata', 'Contents', 'Secrets', 'Variables', 'Issues', 'Actions', 'Administration', 'Workflows', 'Pull requests'] + ...['Metadata', 'Contents', 'Secrets', 'Variables', 'Issues', 'Actions', 'Checks', 'Administration', 'Workflows', 'Pull requests'] .map(name => permission('workflow', 'repository', name, name === 'Metadata' ? 'read' : 'write')), ...['Secrets', 'Variables', 'Issue Types', 'Projects', 'Members'] .map(name => permission('workflow', 'organization', name, 'write')), @@ -72,11 +72,12 @@ describe('buildSetupPatCreationUrl', () => { expect(url.length).toBeLessThan(2_048); }); - it('rejects unsupported Checks instead of producing an incomplete guarded link', () => { - expect(() => buildSetupPatCreationUrl({ + it('offers Checks read on setup and bot links while still requiring GitHub form and token audit', () => { + const url = new URL(buildSetupPatCreationUrl({ role: 'workflow', owner: 'vypdev', repository: 'copilot', expiresIn: 90, requirements: [permission('workflow', 'repository', 'Checks', 'read')], - })).toThrow(UnsupportedSetupPatLinkError); + })); + expect(url.searchParams.get('checks')).toBe('read'); }); it.each([ diff --git a/src/application/policies/__tests__/setup_pat_intent_policy.test.ts b/src/application/policies/__tests__/setup_pat_intent_policy.test.ts index 6987aadcd..497bb1b3c 100644 --- a/src/application/policies/__tests__/setup_pat_intent_policy.test.ts +++ b/src/application/policies/__tests__/setup_pat_intent_policy.test.ts @@ -14,7 +14,7 @@ describe('setup PAT permission intent', () => { expect(grants(configuration, 'Organization')).toEqual(expect.arrayContaining([ 'repository:Metadata:read', 'repository:Contents:write', 'repository:Secrets:write', 'repository:Variables:write', 'repository:Issues:write', 'repository:Administration:read', - 'organization:Secrets:write', 'organization:Issue Types:write', 'organization:Projects:write', + 'organization:Secrets:write', 'organization:Issue Types:write', 'organization:Projects:read', ])); expect(grants(configuration, 'Organization')).not.toContain('repository:Actions:write'); expect(grants(configuration, 'Organization')).not.toContain('repository:Workflows:write'); @@ -73,7 +73,7 @@ describe('setup PAT permission intent', () => { pullRequestApproval: { mode: 'off' }, projects: { ids: '' }, storage: { secrets: { preserveExisting: false }, variables: { preserveExisting: true } }, }, false, false)).toEqual(expect.arrayContaining([ - 'pullRequestApproval.mode', 'projects.ids', + 'pullRequestApproval.mode', 'projects.enabled', 'projects.ids', 'storage.secrets.preserveExisting', 'storage.variables.preserveExisting', ])); }); @@ -107,7 +107,7 @@ describe('setup PAT permission intent', () => { configuration.projects.ids = 'PVT_example'; expect(grants(configuration, 'Organization').filter(item => item.startsWith('organization:'))) - .toEqual(['organization:Projects:write']); + .toEqual(['organization:Projects:read']); expect(setupPatIntentNeedsOwnerKind(configuration)).toBe(true); expect(setupPatIntentOwnerConflict(configuration, 'User')).toBe(true); expect(setupPatIntentOwnerConflict(configuration, 'Organization')).toBe(false); diff --git a/src/application/policies/__tests__/setup_project_selection_policy.test.ts b/src/application/policies/__tests__/setup_project_selection_policy.test.ts new file mode 100644 index 000000000..df31dcf2c --- /dev/null +++ b/src/application/policies/__tests__/setup_project_selection_policy.test.ts @@ -0,0 +1,37 @@ +import { parseSetupProjectSelection, sharedProjectStatusOptions } from '../setup_project_selection_policy'; + +describe('setup Project selection', () => { + test.each([ + ['', ''], ['none', ''], ['1,2', '1,2'], + ['https://github.com/orgs/acme/projects/5', '5'], + ['3, https://github.com/orgs/ACME/projects/7', '3,7'], + ])('normalizes %s to numeric Project URL numbers', (input, expected) => { + expect(parseSetupProjectSelection(input, 'acme')).toEqual({ value: expected }); + }); + + test.each([ + 'PVT_kwDOExample', '0', '-2', '1,1', '1,01', + 'https://github.com/orgs/other/projects/5', + 'https://github.com/orgs/acme/projects/5?token=secret', + 'https://evil.example/orgs/acme/projects/5', + '2147483648', '1,,2', + ])('rejects invalid or misleading Project selection %s', input => { + expect(parseSetupProjectSelection(input, 'acme')).toHaveProperty('error'); + }); + + test('rejects more than ten Projects', () => { + expect(parseSetupProjectSelection(Array.from({ length: 11 }, (_, index) => String(index + 1)).join(','))).toHaveProperty('error'); + }); + + test('uses only Status options common to all selected Projects', () => { + expect(sharedProjectStatusOptions('2,3', [ + { number: 2, statusOptions: ['Todo', 'In Progress'] }, + { number: 3, statusOptions: ['In Progress', 'Done'] }, + ])).toEqual({ state: 'observed', options: ['In Progress'] }); + expect(sharedProjectStatusOptions('2,3', [ + { number: 2, statusOptions: ['Todo'] }, { number: 3, statusOptions: ['Done'] }, + ])).toEqual({ state: 'incompatible', options: [] }); + expect(sharedProjectStatusOptions('2,4', [{ number: 2, statusOptions: ['Todo'] }])) + .toEqual({ state: 'unavailable', options: [] }); + }); +}); diff --git a/src/application/policies/__tests__/setup_question_documentation_policy.test.ts b/src/application/policies/__tests__/setup_question_documentation_policy.test.ts new file mode 100644 index 000000000..6ef12d6ee --- /dev/null +++ b/src/application/policies/__tests__/setup_question_documentation_policy.test.ts @@ -0,0 +1,49 @@ +import { existsSync } from 'node:fs'; +import { resolve } from 'node:path'; +import type { SetupQuestion } from '../../../domain/setup_questionnaire'; +import { setupQuestionDocumentation } from '../setup_question_documentation_policy'; + +describe('setup question documentation', () => { + test.each([ + ['features.issues', 'capabilities', '/issues/workflow-setup'], + ['features.pullRequests', 'capabilities', '/pull-requests/workflow-setup'], + ['issueWorkflows.enabled', 'capabilities', '/issues/workflow-setup'], + ['repositoryAgentGuidance.enabled', 'capabilities', '/agents/repository-collaboration'], + ['agents.planner.provider', 'agent-runtime', '/agents/runtime-selection'], + ['agents.findings.executable', 'agent-model-defaults', '/agents/cli-configuration'], + ['agents.findings.model', 'agent-model-defaults', '/agents/model-selection'], + ['repository.mainBranch', 'repository', '/configuration'], + ['repository.preBranchSdd', 'repository', '/issues/pre-branch-sdds'], + ['repository.issueManagedBranches', 'repository', '/issues/branch-management'], + ['repository.inactivityThresholdHours', 'repository', '/issues/notifications-and-auto-close'], + ['repository.desiredAssigneesCount', 'repository', '/issues/assignees-and-projects'], + ['repository.releaseReconciliationStrategy', 'deployment', '/issues/deployment-orchestration'], + ['ai.bugbotSeverity', 'bugbot', '/bugbot/configuration'], + ['ai.bugbotFixVerifyCommands', 'bugbot', '/bugbot/verification-commands'], + ['ai.pullRequestDescriptionMode', 'bugbot', '/pull-requests/ai-description'], + ['projects.enabled', 'projects', '/issues/assignees-and-projects'], + ['manageRepositorySecrets', 'provisioning', '/how-to-use'], + ])('%s has a related, locally documented destination', (id, stateId, path) => { + const reference = setupQuestionDocumentation({ id, stateId: stateId as SetupQuestion['stateId'] }); + expect(reference.title.trim()).not.toBe(''); + expect(reference.url).toBe(`https://docs.page/vypdev/copilot${path}`); + expect(existsSync(resolve(__dirname, '../../../../docs', `${path.slice(1)}.mdx`))).toBe(true); + }); + + test('storage questions point to the official GitHub Actions resource guide', () => { + const reference = setupQuestionDocumentation({ id: 'storage.secrets.defaultScope', stateId: 'storage' }); + expect(reference.url).toBe('https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/use-secrets'); + }); + + test('trusted check selection links directly to the official status-check explanation', () => { + expect(setupQuestionDocumentation({ id: 'pullRequestApproval.testChecks', stateId: 'pull-request-approval' }).url) + .toBe('https://docs.github.com/en/pull-requests/reference/status-checks'); + }); + + test('Project selection and Status options link to official GitHub explanations', () => { + expect(setupQuestionDocumentation({ id: 'projects.ids', stateId: 'projects' }).url) + .toBe('https://docs.github.com/en/issues/planning-and-tracking-with-projects/learning-about-projects/about-projects'); + expect(setupQuestionDocumentation({ id: 'projects.issueCreatedColumn', stateId: 'projects' }).url) + .toBe('https://docs.github.com/en/issues/planning-and-tracking-with-projects/understanding-fields/about-single-select-fields'); + }); +}); diff --git a/src/application/policies/__tests__/setup_question_purpose_policy.test.ts b/src/application/policies/__tests__/setup_question_purpose_policy.test.ts new file mode 100644 index 000000000..298ff4b98 --- /dev/null +++ b/src/application/policies/__tests__/setup_question_purpose_policy.test.ts @@ -0,0 +1,93 @@ +import type { SetupQuestion } from '../../../domain/setup_questionnaire'; +import { setupQuestionPurpose, setupQuestionPurposes } from '../setup_question_purpose_policy'; +import { setupQuestionPresentation } from '../setup_question_guidance_policy'; +import { setupQuestionContentInventory } from '../setup_questionnaire_policy'; +import { questionLabelsFrPt } from '../setup_question_labels_fr_pt'; +import { spanishQuestionLabels } from '../setup_question_translations'; +import { purposesFrPt } from '../setup_question_purpose_fr_pt'; + +function question(id: string, stateId: SetupQuestion['stateId'], label = id): SetupQuestion { + return { id, stateId, label, kind: 'text', defaultValue: '' }; +} + +describe('setup question purpose', () => { + test.each([ + ['repository.repositoryLocale', 'repository', 'does not change the setup page language'], + ['repository.reconciliationPullRequestMode', 'deployment', 'merged automatically'], + ['ai.bugbotFixVerifyCommands', 'bugbot', 'must pass'], + ['pullRequestApproval.coverage.reporterAttested', 'pull-request-approval', 'not just its green check'], + ['manageRepositorySecrets', 'provisioning', 'bot PAT'], + ] as const)('%s explains the exact field, not just its section', (id, stateId, expected) => { + const presentation = setupQuestionPresentation(question(id, stateId)); + expect(presentation.en.summary).toContain(expected); + expect(presentation.es.summary).not.toEqual(presentation.en.summary); + expect(presentation.en.documentation.url).toMatch(/^https:\/\//u); + }); + + test('patterned agent and storage questions have specific explanations', () => { + expect(setupQuestionPurpose(question('agents.tester.provider', 'agent-runtime'))?.en).toContain('agent CLI'); + expect(setupQuestionPurpose(question('storage.secrets.organizationVisibility', 'storage'))?.en).toContain('organization Secrets'); + expect(setupQuestionPurpose(question('projects.issueInProgressColumn', 'projects'))?.en).toContain('Status field option'); + }); + + test('unknown questions retain section guidance without inventing semantics', () => { + const presentation = setupQuestionPresentation(question('future.question', 'bugbot')); + expect(presentation.en.summary).toContain('Bugbot'); + expect(setupQuestionPurpose(question('future.question', 'bugbot'))).toBeUndefined(); + }); + + test('every defined question has a complete four-language help contract', () => { + const questions = setupQuestionContentInventory(); + expect(questions.length).toBeGreaterThan(100); + expect(new Set(questions.map(item => item.id)).size).toBe(questions.length); + const detailed = new Set([ + 'agents.findings.executable', 'ai.includeReasoning', 'ai.bugbotDryRun', + 'ai.bugbotOrganizationRules', 'ai.provisioningMode', + 'pullRequestApproval.testChecks', 'pullRequestApproval.producerAttested', + 'pullRequestApproval.coverage.mode', 'pullRequestApproval.coverage.checkName', + ]); + for (const item of questions) { + expect(Boolean(setupQuestionPurpose(item)) || detailed.has(item.id)).toBe(true); + const presentation = setupQuestionPresentation(item); + for (const locale of ['en', 'es', 'fr', 'pt'] as const) { + const content = presentation[locale]; + for (const field of ['label', 'summary', 'when', 'where', 'how', 'why', 'example', 'effect', 'verify'] as const) { + expect(content[field].trim()).not.toBe(''); + if (locale !== 'en') expect(content[field]).not.toEqual(presentation.en[field]); + } + expect(content.documentation.url).toMatch(/^https:\/\/(docs\.page|docs\.github\.com)\//u); + } + } + }); + + test('high-risk choices explain the concrete verification action in every language', () => { + const cases: readonly [string, SetupQuestion['stateId'], string][] = [ + ['pullRequestApproval.coverage.checkName', 'pull-request-approval', 'job'], + ['pullRequestApproval.producerAttested', 'pull-request-approval', 'App'], + ['pullRequestApproval.coverage.artifactWorkflowName', 'pull-request-approval', 'copilot-diff-coverage-v1'], + ['projects.issueCreatedColumn', 'projects', 'Status'], + ]; + for (const [id, stateId, technicalTerm] of cases) { + const help = setupQuestionPresentation(question(id, stateId)); + for (const locale of ['en', 'es', 'fr', 'pt'] as const) { + expect(help[locale].how).toContain(technicalTerm); + expect(help[locale].how.length).toBeGreaterThan(90); + } + } + }); + + test('French and Portuguese label keys match the Spanish source inventory exactly', () => { + for (const locale of ['fr', 'pt'] as const) { + expect(Object.keys(questionLabelsFrPt[locale]).sort()).toEqual(Object.keys(spanishQuestionLabels).sort()); + for (const translated of Object.values(questionLabelsFrPt[locale])) expect(translated.trim()).not.toBe(''); + } + }); + + test('French and Portuguese specific-purpose keys match English and Spanish', () => { + const ids = Object.keys(setupQuestionPurposes).sort(); + for (const locale of ['fr', 'pt'] as const) { + expect(Object.keys(purposesFrPt[locale]).sort()).toEqual(ids); + for (const value of Object.values(purposesFrPt[locale])) expect(value.trim()).not.toBe(''); + } + }); +}); diff --git a/src/application/policies/__tests__/setup_questionnaire_policy.test.ts b/src/application/policies/__tests__/setup_questionnaire_policy.test.ts index 65d995557..1e5f403bd 100644 --- a/src/application/policies/__tests__/setup_questionnaire_policy.test.ts +++ b/src/application/policies/__tests__/setup_questionnaire_policy.test.ts @@ -4,6 +4,11 @@ import { createSetupQuestionnaire, createSetupPermissionIntentQuestionnaire, createSetupReviewState, + refreshSetupQuestionnaireQuestion, + reopenSetupQuestionnaireGroup, + setupBasicSkippedQuestionIds, + setupEditableGroups, + setupQuestionnaireProgress, enterSetupConfirmation, finishSetupQuestionnaire, setupQuestionnaireStateLabel, @@ -12,6 +17,63 @@ import { import type { SetupQuestionnaireContext, SetupQuestionnaireState } from '../../../domain/setup_questionnaire'; describe('setup questionnaire policy', () => { + it('basic presentation omits only advanced defaults, never security or mutation choices', () => { + const skipped = setupBasicSkippedQuestionIds(); + expect(skipped).toContain('agents.findings.executable'); + expect(skipped).toContain('ai.bugbotCommentLimit'); + expect(skipped.length).toBeGreaterThan(20); + for (const required of ['features.issues', 'features.pullRequests', 'projects.enabled', 'projects.ids', + 'repository.mainBranch', 'repository.developmentBranch', 'pullRequestApproval.mode', + 'ai.membersOnly', 'ai.bugbotTelemetry', 'ai.bugbotDryRun', 'createInitialTag', + 'manageRepositoryVariables', 'manageRepositorySecrets', 'storage.secrets.defaultScope', + 'storage.variables.defaultScope']) expect(skipped).not.toContain(required); + const configured = createDefaultSetupConfiguration(); + configured.ai.bugbotCommentLimit = 12; + expect(setupBasicSkippedQuestionIds(configured)).not.toContain('ai.bugbotCommentLimit'); + }); + it('reports truthful conditional progress and returns to a saved answer without resetting later values', () => { + const first = createSetupQuestionnaire(createDefaultSetupConfiguration()); + expect(setupQuestionnaireProgress(first, {})).toMatchObject({ position: 1, groupPosition: 1, group: 'capabilities' }); + const second = transitionSetupQuestionnaire(first, { kind: 'answer', value: 'no' }); + expect(second.question?.id).toBe('features.pullRequests'); + expect(setupQuestionnaireProgress(second, {})?.position).toBe(2); + const back = transitionSetupQuestionnaire(second, { kind: 'back' }); + expect(back.question?.id).toBe(first.question?.id); + expect(back.question?.defaultValue).toBe(false); + expect(back.draft.features.issues).toBe(false); + expect(transitionSetupQuestionnaire(back, { kind: 'answer', value: 'yes' }).draft.features.issues).toBe(true); + expect(transitionSetupQuestionnaire(first, { kind: 'back' }).validation).toContain('first question'); + }); + + it('does not restart when answering a question makes that question disappear', () => { + const initial = createDefaultSetupConfiguration(); + initial.projects.ids = '12'; + const context = { projectsWanted: true }; + const question = advanceTo(createSetupQuestionnaire(initial, context), 'projects.ids', context); + const next = transitionSetupQuestionnaire(question, { kind: 'answer', value: 'none' }, context); + expect(next.question?.id).not.toBe('features.issues'); + expect(next.stateId).not.toBe('capabilities'); + }); + + it('offers plan correction groups and reopens an existing review without clearing its draft', () => { + const draft = createDefaultSetupConfiguration(); + expect(setupEditableGroups(draft)).toContain('repository'); + expect(setupEditableGroups(draft)).toContain('projects'); + const review = createSetupReviewState(draft); + const reopened = reopenSetupQuestionnaireGroup(review, 'repository', {}); + expect(reopened).toMatchObject({ terminal: 'collecting', stateId: 'repository', draft }); + expect(reopened?.question?.id).toBe('repository.mainBranch'); + expect(reopenSetupQuestionnaireGroup(review, 'agent-role-overrides', {})).toBeUndefined(); + }); + it('preserves independent agent overrides on revisit and normalizes them only when explicitly disabled', () => { + const configuration = createDefaultSetupConfiguration(); + configuration.agents.planner.model = 'different-model'; + expect(setupEditableGroups(configuration)).toContain('agent-role-overrides'); + const question = advanceTo(createSetupQuestionnaire(configuration), 'agents.configureIndependently'); + expect(question.question?.defaultValue).toBe(true); + const disabled = transitionSetupQuestionnaire(question, { kind: 'answer', value: 'no' }); + expect(disabled.draft.agents.planner.model).toBe(disabled.draft.agents.findings.model); + }); it('collects only permission-driving questions and reuses their answers in the full wizard', () => { const defaults = createDefaultSetupConfiguration(); const intentContext = { skipQuestionIds: ['createInitialTag', 'manageRepositorySecrets'] }; @@ -65,7 +127,7 @@ describe('setup questionnaire policy', () => { it('enters review immediately when the permission-intent phase has no open questions', () => { const ids = [ 'features.issues', 'features.pullRequests', 'issueWorkflows.enabled', 'pullRequestApproval.mode', - 'projects.ids', 'createInitialTag', 'manageRepositoryVariables', 'manageRepositorySecrets', + 'projects.enabled', 'createInitialTag', 'manageRepositoryVariables', 'manageRepositorySecrets', 'storage.variables.defaultScope', 'storage.variables.preserveExisting', 'storage.secrets.defaultScope', 'storage.secrets.preserveExisting', ]; @@ -127,6 +189,140 @@ describe('setup questionnaire policy', () => { expect(state.draft.pullRequestApproval.producerAttested).toBe(true); }); + it('asks producer attestation only after the exact coverage check has been selected', () => { + const configuration = createDefaultSetupConfiguration(); + configuration.pullRequestApproval = { ...configuration.pullRequestApproval, mode: 'recommend', + testChecks: [{ name: 'Tests', sourceAppId: 12, workflowName: 'CI' }] }; + const check = advanceTo(createSetupQuestionnaire(configuration), 'pullRequestApproval.coverage.checkName'); + expect(check.question?.choices).toEqual(['Tests']); + const next = transitionSetupQuestionnaire(check, { kind: 'answer', value: 'Tests' }); + expect(next.question?.id).toBe('pullRequestApproval.producerAttested'); + }); + + it('offers observed CI producers without treating a green check as attestation', () => { + const configuration = createDefaultSetupConfiguration(); + configuration.pullRequestApproval = { ...configuration.pullRequestApproval, mode: 'recommend' }; + const candidate = { name: 'Tests', sourceAppId: 12, workflowName: 'CI', + runUrl: 'https://github.com/acme/project/actions/runs/42', headSha: 'a'.repeat(40), conclusion: 'success' }; + const context: SetupQuestionnaireContext = { approvalCheckCandidates: [candidate] }; + const first = advanceTo(createSetupQuestionnaire(configuration, context), 'pullRequestApproval.testChecks', context); + expect(first.question).toMatchObject({ kind: 'producer-select', producerCandidates: [candidate] }); + const invalid = transitionSetupQuestionnaire(first, { kind: 'answer', value: '' }, context); + expect(invalid.validation).toContain('Select 1–8 observed checks'); + const selected = transitionSetupQuestionnaire(first, { kind: 'answer', value: 'Tests|12|CI' }, context); + expect(selected.draft.pullRequestApproval.testChecks).toEqual([{ name: 'Tests', sourceAppId: 12, workflowName: 'CI' }]); + expect(selected.draft.pullRequestApproval.producerAttested).toBe(false); + const coverage = advanceTo(selected, 'pullRequestApproval.coverage.checkName', context); + expect(coverage.question?.choices).toEqual(['Tests']); + expect(coverage.question?.producerCandidates).toEqual([candidate]); + }); + + it('rejects ambiguous trusted check names and preserves previous answers on discovery refresh', () => { + const configuration = createDefaultSetupConfiguration(); + configuration.pullRequestApproval = { ...configuration.pullRequestApproval, mode: 'recommend' }; + const context: SetupQuestionnaireContext = { approvalCheckDiscoveryStatus: 'unavailable', + discoveryRetryRemaining: { checks: 2, projects: 0 }, approvalCheckCandidates: [] }; + const state = advanceTo(createSetupQuestionnaire(configuration, context), 'pullRequestApproval.testChecks', context); + const refreshed = refreshSetupQuestionnaireQuestion(state, { ...context, approvalCheckDiscoveryStatus: 'observed', + discoveryRetryRemaining: { checks: 1, projects: 0 }, approvalCheckCandidates: [{ name: 'Tests', sourceAppId: 12, + workflowName: 'CI', runUrl: 'https://github.com/acme/repo/actions/runs/5', headSha: 'a'.repeat(40), conclusion: 'success' }] }); + expect(refreshed.question).toMatchObject({ id: state.question?.id, discoveryStatus: 'observed', discoveryRetryRemaining: 1 }); + expect(refreshed.answeredQuestionIds).toEqual(state.answeredQuestionIds); + expect(refreshed.draft).toEqual(state.draft); + expect(transitionSetupQuestionnaire(refreshed, { kind: 'answer', value: 'Tests|12|CI;Tests|13|Other' }).validation) + .toContain('same check name'); + }); + + it('does not label a required ruleset check as verified after the target development branch changes', () => { + const configuration = createDefaultSetupConfiguration(); + configuration.pullRequestApproval = { ...configuration.pullRequestApproval, mode: 'recommend' }; + configuration.repository.developmentBranch = 'release'; + const context: SetupQuestionnaireContext = { approvalCheckCandidates: [{ name: 'Tests', sourceAppId: 12, + workflowName: 'CI', runUrl: 'https://github.com/acme/repo/actions/runs/5', headSha: 'a'.repeat(40), conclusion: 'success', + requiredByRuleset: { branch: 'develop', sourceUrl: 'https://github.com/acme/repo/rules/3' } }] }; + const state = advanceTo(createSetupQuestionnaire(configuration, context), 'pullRequestApproval.testChecks', context); + expect(state.question?.producerCandidates?.[0].requiredByRuleset).toBeUndefined(); + configuration.repository.developmentBranch = 'develop'; + const matched = advanceTo(createSetupQuestionnaire(configuration, context), 'pullRequestApproval.testChecks', context); + expect(matched.question?.producerCandidates?.[0].requiredByRuleset?.branch).toBe('develop'); + }); + + it('keeps manual check entry when discovery found no trusted producer', () => { + const configuration = createDefaultSetupConfiguration(); + configuration.pullRequestApproval = { ...configuration.pullRequestApproval, mode: 'recommend' }; + const state = advanceTo(createSetupQuestionnaire(configuration, { approvalCheckCandidates: [] }), + 'pullRequestApproval.testChecks', { approvalCheckCandidates: [] }); + expect(state.question?.kind).toBe('producer-select'); + expect(transitionSetupQuestionnaire(state, { kind: 'answer', value: 'Tests|x|CI' }).validation).toContain('Select 1–8'); + }); + + it('asks only Project intent before PAT and selects concrete Projects afterwards', () => { + const defaults = createDefaultSetupConfiguration(); + const intent = advanceTo(createSetupPermissionIntentQuestionnaire(defaults), 'projects.enabled'); + expect(intent.question?.kind).toBe('boolean'); + const wanted = transitionSetupQuestionnaire(intent, { kind: 'answer', value: 'yes' }); + expect(wanted.projectsWanted).toBe(true); + expect(wanted.draft.projects.ids).toBe(''); + const context: SetupQuestionnaireContext = { projectsWanted: true, projectOwner: 'acme', projectDiscovery: { + status: 'observed', candidates: [ + { number: 2, title: 'First', owner: 'acme', url: 'https://github.com/orgs/acme/projects/2', statusOptions: ['Todo', 'In Progress'] }, + { number: 3, title: 'Second', owner: 'acme', url: 'https://github.com/orgs/acme/projects/3', statusOptions: ['In Progress'] }, + ], + } }; + const select = advanceTo(createSetupQuestionnaire(wanted.draft, context), 'projects.ids', context); + expect(select.question).toMatchObject({ kind: 'project-select', discoveryStatus: 'observed' }); + const selected = transitionSetupQuestionnaire(select, { kind: 'answer', value: '2,3' }, context); + expect(selected.draft.projects.ids).toBe('2,3'); + expect(selected.question).toMatchObject({ id: 'projects.issueCreatedColumn', kind: 'choice', choices: ['In Progress'] }); + expect(transitionSetupQuestionnaire(selected, { kind: 'answer', value: '' }, context).validation) + .toContain('saved Status value'); + }); + + it('rejects incompatible Projects and GraphQL IDs before leaving the question', () => { + const context: SetupQuestionnaireContext = { projectOwner: 'acme', projectDiscovery: { status: 'observed', candidates: [ + { number: 2, title: 'First', owner: 'acme', url: 'https://github.com/orgs/acme/projects/2', statusOptions: ['Todo'] }, + { number: 3, title: 'Second', owner: 'acme', url: 'https://github.com/orgs/acme/projects/3', statusOptions: ['Done'] }, + ] } }; + const state = advanceTo(createSetupQuestionnaire(createDefaultSetupConfiguration(), context), 'projects.ids', context); + expect(transitionSetupQuestionnaire(state, { kind: 'answer', value: '2,3' }, context).validation) + .toContain('no common Status option'); + expect(transitionSetupQuestionnaire(state, { kind: 'answer', value: 'PVT_fake' }, context).validation) + .toContain('positive Project number'); + }); + + it('does not offer a futile Project retry when the personal-owner listing is unsupported', () => { + const context: SetupQuestionnaireContext = { projectOwner: 'someone', projectDiscovery: { + status: 'unsupported', candidates: [], + }, discoveryRetryRemaining: { checks: 0, projects: 0 } }; + const selection = advanceTo(createSetupQuestionnaire(createDefaultSetupConfiguration(), context), 'projects.ids', context); + expect(selection.question?.discoveryStatus).toBe('unsupported'); + expect(selection.question?.discoveryRetryRemaining).toBeUndefined(); + }); + + it('requires an explicit human check of all four Status values when Project fields were not observed', () => { + const context: SetupQuestionnaireContext = { projectOwner: 'acme', projectDiscovery: { + status: 'unsupported', candidates: [], + } }; + const selection = advanceTo(createSetupQuestionnaire(createDefaultSetupConfiguration(), context), 'projects.ids', context); + const selected = transitionSetupQuestionnaire(selection, { kind: 'answer', value: '5' }, context); + const attestation = advanceTo(selected, 'projects.statusVerified', context); + expect(attestation.question?.projectStatusValues).toEqual([ + { transition: 'issueCreated', value: 'Todo' }, + { transition: 'pullRequestCreated', value: 'In Progress' }, + { transition: 'issueInProgress', value: 'In Progress' }, + { transition: 'pullRequestInProgress', value: 'In Progress' }, + ]); + const retry = transitionSetupQuestionnaire(attestation, { kind: 'answer', value: 'no' }, context); + expect(retry.question?.id).toBe('projects.ids'); + expect(retry.validation).toContain('not confirmed'); + expect(retry.draft).toEqual(attestation.draft); + expect(retry.answeredQuestionIds).not.toContain('projects.statusVerified'); + expect(transitionSetupQuestionnaire(attestation, { kind: 'answer', value: '' }, context).validation) + .toContain('Open every selected Project'); + expect(transitionSetupQuestionnaire(attestation, { kind: 'answer', value: 'yes' }, context).question?.id) + .toBe('createInitialTag'); + }); + it('asks for numeric threshold, artifact workflow, and reporter attestation only in numeric mode', () => { const configuration = createDefaultSetupConfiguration(); configuration.pullRequestApproval = { ...configuration.pullRequestApproval, mode: 'recommend' }; @@ -315,9 +511,9 @@ function advanceTo( let state = initial; for (let attempts = 0; attempts < 200 && state.terminal === 'collecting'; attempts += 1) { if (state.question?.id === questionId) return state; - state = transitionSetupQuestionnaire(state, { kind: 'answer', value: '' }, context); + state = transitionSetupQuestionnaire(state, { kind: 'answer', value: state.question?.id === 'pullRequestApproval.testChecks' ? 'Tests|12|CI' : '' }, context); } - throw new Error(`Question ${questionId} was not reached.`); + throw new Error(`Question ${questionId} was not reached; stopped at ${state.question?.id}: ${state.validation ?? 'no validation error'}.`); } function deepFreeze(value: T): T { diff --git a/src/application/policies/__tests__/setup_token_permission_policy.test.ts b/src/application/policies/__tests__/setup_token_permission_policy.test.ts index c3cf93cce..261b6c631 100644 --- a/src/application/policies/__tests__/setup_token_permission_policy.test.ts +++ b/src/application/policies/__tests__/setup_token_permission_policy.test.ts @@ -41,10 +41,11 @@ describe('setup token permission policy', () => { const requirements = buildSetupPatPermissionRequirements(); expect(requirements.map(item => `${item.scope}:${item.permission}:${item.level}`)).toEqual([ 'repository:Metadata:read', 'repository:Contents:read', 'repository:Secrets:write', - 'repository:Variables:write', 'repository:Issues:write', 'repository:Actions:write', + 'repository:Variables:write', 'repository:Issues:write', 'repository:Actions:write', 'repository:Actions:read', + 'repository:Checks:read', 'repository:Administration:read', 'repository:Workflows:write', 'organization:Secrets:write', 'organization:Variables:write', - 'organization:Issue Types:write', 'organization:Projects:write', + 'organization:Issue Types:write', 'organization:Projects:read', ]); }); @@ -58,6 +59,8 @@ describe('setup token permission policy', () => { it('keeps feature-dependent setup grants conditional with visible conditions', () => { const administration = buildSetupPatPermissionRequirements().find(item => item.permission === 'Administration'); expect(administration).toMatchObject({ applicability: 'conditional', condition: expect.stringContaining('Release') }); + const approvalRead = buildSetupPatPermissionRequirements().find(item => item.permission === 'Actions' && item.level === 'read'); + expect(approvalRead).toMatchObject({ applicability: 'conditional', condition: 'Pull-request approval enabled' }); }); it('recomputes only repository setup mutations selected by the approved configuration', () => { @@ -87,7 +90,7 @@ describe('setup token permission policy', () => { ...organization, ownerType: 'Unknown', }).map(item => `${item.scope}:${item.permission}:${item.level}`); expect(unknown).toEqual(expect.arrayContaining([ - 'organization:Secrets:write', 'organization:Issue Types:write', 'organization:Projects:write', + 'organization:Secrets:write', 'organization:Issue Types:write', 'organization:Projects:read', ])); const personal = buildConfiguredSetupPatPermissionRequirements(configuration, { ...organization, ownerType: 'User', @@ -148,7 +151,7 @@ describe('setup token permission policy', () => { expect(permissions).toEqual(expect.arrayContaining([ 'repository:Actions:write', 'repository:Workflows:write', - 'organization:Projects:write', + 'organization:Projects:read', ])); }); diff --git a/src/application/policies/setup_configuration_plan.ts b/src/application/policies/setup_configuration_plan.ts index 65ef6060b..4edf73f63 100644 --- a/src/application/policies/setup_configuration_plan.ts +++ b/src/application/policies/setup_configuration_plan.ts @@ -304,7 +304,7 @@ function buildSetupWarnings(configuration: SetupConfiguration): string[] { warnings.push('Inactive issue closure is enabled; waiting issues are closed after the configured inactivity threshold and can be reopened with a new comment.'); } if (configuration.projects.ids.trim()) { - warnings.push('Project IDs must be accessible to the PAT and use the expected project column names.'); + warnings.push('Selected Project numbers must be accessible to the bot PAT, and all four configured Status values must exist in every selected Project.'); } if (setupAgentTasksForFeatures(configuration).some(task => configuration.agents[task].provider === 'cursor')) { warnings.push('Cursor is an experimental runtime in Copilot and requires a compatible preinstalled CLI plus CURSOR_API_KEY; Copilot has no automatic Cursor installer.'); diff --git a/src/application/policies/setup_configuration_validation.ts b/src/application/policies/setup_configuration_validation.ts index 177bafcac..4091b56da 100644 --- a/src/application/policies/setup_configuration_validation.ts +++ b/src/application/policies/setup_configuration_validation.ts @@ -8,9 +8,21 @@ import { canonicalizeLocaleTag } from '../../domain/locale'; import { ISSUE_WORKFLOW_KINDS } from '../../domain/issue_workflow_profile'; import { effectiveIssueWorkflowProfile } from './setup_issue_workflow_policy'; import { validatePullRequestApprovalPolicy } from '../../domain/pull_request_approval_policy'; +import { parseSetupProjectSelection } from './setup_project_selection_policy'; export function validateSetupConfiguration(configuration: SetupConfiguration, options: { allowIncompleteApproval?: boolean } = {}): string[] { const errors: string[] = []; + const projectSelection = parseSetupProjectSelection(configuration.projects.ids); + if ('error' in projectSelection || projectSelection.value !== configuration.projects.ids) { + errors.push('Project IDs must be a comma-separated list of 1–10 distinct positive Project URL numbers; PVT_ node IDs are not accepted.'); + } + if (configuration.projects.ids) { + for (const [name, value] of Object.entries(configuration.projects).filter(([name]) => name.endsWith('Column'))) { + if (typeof value !== 'string' || !value.trim() || value.length > 100 || /[\p{Cc}\p{Cf}]/u.test(value)) { + errors.push(`Project ${name} must name one existing single-line Status option (1–100 characters).`); + } + } + } errors.push(...validatePullRequestApprovalPolicy(configuration.pullRequestApproval, options.allowIncompleteApproval === true)); if (configuration.actionInputs['pr-approval-policy'] !== undefined) { errors.push('pr-approval-policy cannot be overridden through actionInputs.'); diff --git a/src/application/policies/setup_pat_creation_url_policy.ts b/src/application/policies/setup_pat_creation_url_policy.ts index eae809762..36aa70872 100644 --- a/src/application/policies/setup_pat_creation_url_policy.ts +++ b/src/application/policies/setup_pat_creation_url_policy.ts @@ -11,6 +11,7 @@ const QUERY_PERMISSIONS: Readonly): boolean { - return buildSetupPatIntentPermissionRequirements(configuration, 'Organization') +export function setupPatIntentNeedsOwnerKind(configuration: Readonly, projectsWanted = configuration.projects.ids.trim().length > 0): boolean { + return buildSetupPatIntentPermissionRequirements(configuration, 'Organization', projectsWanted) .some(requirement => requirement.scope === 'organization') || (configuration.manageRepositorySecrets && configuration.storage.secrets.preserveExisting) || (configuration.manageRepositoryVariables && configuration.storage.variables.preserveExisting); } -export function setupPatIntentOwnerConflict(configuration: Readonly, ownerKind: 'Organization' | 'User'): boolean { +export function setupPatIntentOwnerConflict(configuration: Readonly, ownerKind: 'Organization' | 'User', projectsWanted = configuration.projects.ids.trim().length > 0): boolean { return ownerKind === 'User' && ( (configuration.manageRepositorySecrets && ( configuration.storage.secrets.defaultScope === 'organization' @@ -42,6 +42,6 @@ export function setupPatIntentOwnerConflict(configuration: Readonly 0 + || projectsWanted ); } diff --git a/src/application/policies/setup_project_selection_policy.ts b/src/application/policies/setup_project_selection_policy.ts new file mode 100644 index 000000000..b4e536f47 --- /dev/null +++ b/src/application/policies/setup_project_selection_policy.ts @@ -0,0 +1,57 @@ +/** Project V2 setup stores the positive number in its GitHub URL, never a GraphQL node ID. */ +import type { SetupConfiguration } from '../../domain/setup'; +import type { SetupDiscoveryResult, SetupProjectCandidate } from '../../domain/setup_questionnaire'; + +export function parseSetupProjectSelection(raw: string, owner?: string): { value: string } | { error: string } { + const input = raw.normalize('NFKC').trim(); + if (!input || input.toLowerCase() === 'none') return { value: '' }; + const parts = input.split(',').map(part => part.trim()); + if (parts.length > 10 || parts.some(part => !part)) return { error: 'Choose at most 10 Projects; separate numbers or URLs with commas.' }; + const numbers: number[] = []; + for (const part of parts) { + let numberText = part; + if (part.startsWith('https://')) { + if (!owner) return { error: 'A Project URL needs a known repository owner; enter its positive number instead.' }; + try { + const url = new URL(part); + const match = url.pathname.match(/^\/(?:orgs|users)\/([^/]+)\/projects\/([1-9]\d*)\/?$/u); + if (url.origin !== 'https://github.com' || url.search || url.hash || url.username || url.password + || !match || decodeURIComponent(match[1]).toLowerCase() !== owner.toLowerCase()) { + return { error: `Use a GitHub Project URL belonging to ${owner}, without query parameters.` }; + } + numberText = match[2]; + } catch { return { error: 'Enter a valid GitHub Project URL or positive Project number.' }; } + } + if (!/^[1-9]\d*$/u.test(numberText)) return { error: 'Enter the positive Project number from its GitHub URL, not a PVT_ GraphQL ID.' }; + const number = Number(numberText); + if (!Number.isSafeInteger(number) || number > 2_147_483_647) return { error: 'Project numbers must be positive integers at most 2147483647.' }; + if (numbers.includes(number)) return { error: `Project ${number} was selected more than once.` }; + numbers.push(number); + } + return { value: numbers.join(',') }; +} + +export function sharedProjectStatusOptions(projectNumbers: string, projects: readonly { number: number; statusOptions?: readonly string[] }[]): + { state: 'observed' | 'unavailable' | 'incompatible'; options: readonly string[] } { + const numbers = projectNumbers.split(',').map(Number).filter(Boolean); + if (!numbers.length) return { state: 'unavailable', options: [] }; + const selected = numbers.map(number => projects.find(project => project.number === number)); + if (selected.some(project => !project?.statusOptions?.length)) return { state: 'unavailable', options: [] }; + const [first, ...rest] = selected as { statusOptions: readonly string[] }[]; + const common = first.statusOptions.filter(option => rest.every(project => project.statusOptions.includes(option))); + return common.length ? { state: 'observed', options: common } : { state: 'incompatible', options: [] }; +} + +/** A discovered mismatch is unsafe even if values arrived through --config rather than the interactive selector. */ +export function validateDiscoveredProjectStatuses( + configuration: Readonly, discovery?: SetupDiscoveryResult, +): readonly string[] { + if (!configuration.projects.ids || !discovery || discovery.status !== 'observed') return []; + const common = sharedProjectStatusOptions(configuration.projects.ids, discovery.candidates); + if (common.state === 'incompatible') return ['Selected Projects have no common Status option. Choose compatible Projects.']; + if (common.state !== 'observed') return []; + const names = [configuration.projects.issueCreatedColumn, configuration.projects.pullRequestCreatedColumn, + configuration.projects.issueInProgressColumn, configuration.projects.pullRequestInProgressColumn]; + return names.filter(name => !common.options.includes(name)).map(name => + `Status value "${name}" is not available in every selected Project.`); +} diff --git a/src/application/policies/setup_question_documentation_policy.ts b/src/application/policies/setup_question_documentation_policy.ts new file mode 100644 index 000000000..710cb660b --- /dev/null +++ b/src/application/policies/setup_question_documentation_policy.ts @@ -0,0 +1,70 @@ +import type { SetupQuestion } from '../../domain/setup_questionnaire'; + +export interface SetupQuestionDocumentation { + readonly title: string; + readonly url: string; +} + +const docs = { + features: { title: 'Copilot features and workflow triggers', url: 'https://docs.page/vypdev/copilot/features' }, + issueWorkflows: { title: 'Copilot issue workflow setup', url: 'https://docs.page/vypdev/copilot/issues/workflow-setup' }, + branchManagement: { title: 'Issue branch management', url: 'https://docs.page/vypdev/copilot/issues/branch-management' }, + preBranchSdd: { title: 'Pre-branch design documents', url: 'https://docs.page/vypdev/copilot/issues/pre-branch-sdds' }, + issueLifecycle: { title: 'Issue notifications and automatic closure', url: 'https://docs.page/vypdev/copilot/issues/notifications-and-auto-close' }, + assignments: { title: 'Assignees and GitHub Projects', url: 'https://docs.page/vypdev/copilot/issues/assignees-and-projects' }, + pullRequestWorkflows: { title: 'Pull-request workflow setup', url: 'https://docs.page/vypdev/copilot/pull-requests/workflow-setup' }, + pullRequestDescription: { title: 'AI pull-request descriptions', url: 'https://docs.page/vypdev/copilot/pull-requests/ai-description' }, + repositoryGuidance: { title: 'Repository guidance for agents', url: 'https://docs.page/vypdev/copilot/agents/repository-collaboration' }, + runtime: { title: 'Agent runtime selection', url: 'https://docs.page/vypdev/copilot/agents/runtime-selection' }, + model: { title: 'Agent model selection', url: 'https://docs.page/vypdev/copilot/agents/model-selection' }, + command: { title: 'Agent CLI configuration', url: 'https://docs.page/vypdev/copilot/agents/cli-configuration' }, + repository: { title: 'Copilot repository configuration', url: 'https://docs.page/vypdev/copilot/configuration' }, + deployment: { title: 'Release and hotfix orchestration', url: 'https://docs.page/vypdev/copilot/issues/deployment-orchestration' }, + bugbot: { title: 'Bugbot configuration', url: 'https://docs.page/vypdev/copilot/bugbot/configuration' }, + bugbotVerification: { title: 'Bugbot autofix verification commands', url: 'https://docs.page/vypdev/copilot/bugbot/verification-commands' }, + approval: { title: 'Guarded pull-request approval', url: 'https://docs.page/vypdev/copilot/pull-requests/guarded-approval' }, + githubStatusChecks: { title: 'GitHub: status checks and required checks', url: 'https://docs.github.com/en/pull-requests/reference/status-checks' }, + projects: { title: 'Assignees and GitHub Projects', url: 'https://docs.page/vypdev/copilot/issues/assignees-and-projects' }, + githubProjects: { title: 'GitHub: About Projects', url: 'https://docs.github.com/en/issues/planning-and-tracking-with-projects/learning-about-projects/about-projects' }, + githubStatus: { title: 'GitHub: About single-select fields', url: 'https://docs.github.com/en/issues/planning-and-tracking-with-projects/understanding-fields/about-single-select-fields' }, + provisioning: { title: 'Copilot setup and provisioning', url: 'https://docs.page/vypdev/copilot/how-to-use' }, + storage: { title: 'GitHub Actions Secrets and Variables', url: 'https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/use-secrets' }, +} as const satisfies Record; + +/** Links are selected from source-controlled constants, never derived from answers or remote text. */ +export function setupQuestionDocumentation(question: Pick): SetupQuestionDocumentation { + const id = question.id; + if (id === 'issueWorkflows.enabled') return docs.issueWorkflows; + if (id === 'features.issues') return docs.issueWorkflows; + if (id === 'features.pullRequests') return docs.pullRequestWorkflows; + if (id === 'repository.issueManagedBranches' || /^repository\.(feature|bugfix|hotfix|release|docs|chore)Tree$/u.test(id)) return docs.branchManagement; + if (id === 'repository.preBranchSdd') return docs.preBranchSdd; + if (id === 'repository.inactivityThresholdHours' || id === 'features.inactiveIssueClosure') return docs.issueLifecycle; + if (id === 'repository.desiredAssigneesCount' || id === 'repository.desiredReviewersCount') return docs.assignments; + if (id === 'ai.pullRequestDescriptionMode') return docs.pullRequestDescription; + if (id === 'ai.bugbotFixVerifyCommands') return docs.bugbotVerification; + if (id === 'projects.ids') return docs.githubProjects; + if (id === 'pullRequestApproval.testChecks') return docs.githubStatusChecks; + if (/^projects\.(issue|pullRequest)(Created|InProgress)Column$/u.test(id) || id === 'projects.statusVerified') return docs.githubStatus; + if (id.startsWith('repositoryAgentGuidance.')) return docs.repositoryGuidance; + if (id.startsWith('agents.')) { + if (id.endsWith('.provider')) return docs.runtime; + if (id.endsWith('.executable')) return docs.command; + return docs.model; + } + if (id === 'ai.provisioningMode') return docs.command; + const byState = { + capabilities: docs.features, + 'agent-runtime': docs.runtime, + 'agent-model-defaults': docs.model, + 'agent-role-overrides': docs.model, + repository: docs.repository, + deployment: docs.deployment, + bugbot: docs.bugbot, + 'pull-request-approval': docs.approval, + projects: docs.projects, + provisioning: docs.provisioning, + storage: docs.storage, + } as const; + return byState[question.stateId as keyof typeof byState] ?? docs.provisioning; +} diff --git a/src/application/policies/setup_question_guidance_fr.ts b/src/application/policies/setup_question_guidance_fr.ts new file mode 100644 index 000000000..c14bc7a6c --- /dev/null +++ b/src/application/policies/setup_question_guidance_fr.ts @@ -0,0 +1,79 @@ +import type { SetupQuestion } from '../../domain/setup_questionnaire'; +import type { SetupQuestionExplanation } from '../contracts/web_setup_view'; +import { translatedQuestionLabel } from './setup_question_labels_fr_pt'; +import { setupQuestionPurposeFrPt } from './setup_question_purpose_fr_pt'; + +type Copy = Pick; + +const where: Readonly> = { + capabilities: 'La configuration écrit les workflows choisis dans ce dépôt et ne demande que les autorisations GitHub nécessaires.', + 'agent-runtime': 'Les workflows GitHub Actions générés lancent cet agent sur leur runner ; rien n’est installé sur cet ordinateur.', + 'agent-model-defaults': 'Le modèle et la commande communs sont enregistrés dans la configuration du dépôt lue par les workflows.', + 'agent-role-overrides': 'Cette exception propre à une tâche est enregistrée dans le dépôt et lue uniquement lorsque cette tâche s’exécute.', + repository: 'Le profil du dépôt et les workflows générés utilisent cette valeur pour les futurs événements de branches, tickets et pull requests.', + deployment: 'Le profil du dépôt commande les futurs workflows de version et de correctif urgent ; répondre ne publie rien.', + bugbot: 'Le workflow généré lit ce réglage dans la configuration du dépôt ou les Variables GitHub Actions sélectionnées.', + 'pull-request-approval': 'L’approbation encadrée utilise les identités exactes des producteurs CI et les preuves des exécutions GitHub.', + projects: 'Les Projects choisis et leurs valeurs du champ Status seront utilisés par l’automatisation future des tickets et pull requests.', + provisioning: 'Après la confirmation finale, la configuration peut créer ou mettre à jour les fichiers et ressources GitHub Actions choisis.', + storage: 'GitHub Actions stocke ces ressources au niveau du dépôt ou de l’organisation ; ce choix change leur visibilité et les autorisations du PAT.', +}; + +const section: Readonly> = { + capabilities: { summary: 'Choisissez les automatisations que Copilot installera.', when: 'Ce choix influence les workflows, les autorisations GitHub et les questions suivantes.', example: 'Désactivez une fonction que vous ne prévoyez pas d’utiliser.', effect: 'Seules les fonctions sélectionnées figureront dans le plan.', verify: 'Examinez le plan avant d’appliquer les changements.' }, + 'agent-runtime': { summary: 'Choisissez l’agent CLI pour cette tâche.', when: 'Il sera utilisé lorsque la fonction sélectionnée s’exécutera dans GitHub Actions.', example: 'Codex est lancé avec la commande codex.', effect: 'L’Action lance le fournisseur choisi, sans solution de remplacement implicite.', verify: 'Vérifiez que le runner dispose du CLI et des identifiants nécessaires.' }, + 'agent-model-defaults': { summary: 'Définissez les modèles utilisés par défaut pour les tâches de l’agent.', when: 'Ils s’appliquent sauf si vous configurez chaque tâche séparément.', example: 'Gardez le modèle proposé si vous n’avez pas de besoin particulier.', effect: 'L’Action transmet ces valeurs au CLI sélectionné.', verify: 'Examinez le plan et les modèles autorisés sur le runner.' }, + 'agent-role-overrides': { summary: 'Personnalisez cette tâche de l’agent.', when: 'Uniquement si vous avez activé la configuration indépendante des tâches.', example: 'Utilisez un modèle différent pour la revue et la planification.', effect: 'Seule cette tâche utilise cette exception.', verify: 'Examinez les valeurs de chaque tâche dans le plan.' }, + repository: { summary: 'Définissez comment Copilot traite votre dépôt.', when: 'Ce réglage agit sur les workflows et futurs événements de tickets ou pull requests.', example: 'Indiquez le véritable nom de votre branche de développement.', effect: 'L’automatisation future suivra les branches et règles choisies.', verify: 'Examinez les fichiers prévus et le profil du dépôt.' }, + deployment: { summary: 'Définissez le comportement des versions et correctifs urgents.', when: 'Ce réglage n’importe que si ces workflows sont activés.', example: 'Gardez la stratégie par défaut sauf si votre organisation des branches diffère.', effect: 'Il modifie la gestion des branches et pull requests de réconciliation.', verify: 'Examinez la partie versions et correctifs du plan.' }, + bugbot: { summary: 'Définissez comment Bugbot analyse et signale les changements.', when: 'Ce réglage sert lorsque les fonctions de revue IA s’exécutent.', example: 'Par défaut, les résultats admissibles sont publiés sans bloquer toutes les pull requests.', effect: 'Il change les futures publications et diagnostics de revue.', verify: 'Examinez les Variables Bugbot du plan et les résultats de revue.' }, + 'pull-request-approval': { summary: 'Choisissez les preuves exigées avant que le bot recommande ou soumette une approbation.', when: 'Ce réglage ne s’applique que si l’automatisation des pull requests est activée.', example: '« Recommend » informe une personne ; « guarded » peut approuver sur GitHub.', effect: 'Une vérification verte affichée ici ne suffit jamais à approuver une pull request.', verify: 'Inspectez les preuves CI, Bugbot et les règles de branche.' }, + projects: { summary: 'Choisissez une valeur Status existante pour une transition de ticket ou PR.', when: 'Seulement si vous intégrez des Projects.', example: 'Todo à la création ; In Progress au début du travail.', effect: 'L’automatisation modifiera le champ Status, pas une colonne visuelle.', verify: 'Vérifiez les options Status de chaque Project choisi.' }, + provisioning: { summary: 'Choisissez les ressources GitHub Actions gérées par la configuration.', when: 'Cela influence les autorisations du PAT et les écritures prévues.', example: 'Gardez les Secrets activés si le PAT du bot doit être installé.', effect: 'Les ressources sélectionnées pourront être créées ou mises à jour après approbation.', verify: 'Examinez les noms exacts des ressources dans le plan.' }, + storage: { summary: 'Choisissez où résident les Variables et Secrets GitHub Actions.', when: 'Ce réglage s’applique quand leur création est activée.', example: 'Le dépôt est le périmètre par défaut le plus simple.', effect: 'Il change la visibilité, les autorisations et l’ordre de priorité.', verify: 'Examinez le périmètre et les avertissements de masquage dans le plan.' }, +}; + +const special: Readonly> = { + 'agents.findings.executable': { summary: 'Choisissez la commande de l’agent sur le runner GitHub Actions, pas sur cet ordinateur.', when: 'Ne la changez que si un agent personnalisé est délibérément installé sur le runner.', example: 'Laissez vide pour codex, opencode ou agent selon le fournisseur.', effect: 'Le chemin personnalisé est utilisé pour les tâches choisies et n’est jamais installé automatiquement.', verify: 'Vérifiez que le runner possède exactement cet exécutable avant d’activer le workflow.' }, + 'ai.includeReasoning': { summary: 'Demandez des explications supplémentaires si la réponse du fournisseur les contient.', when: 'Réservé aux diagnostics avancés ; le parcours CLI actuel ne fournit pas de parties de raisonnement séparées.', example: 'Laissez désactivé pour une configuration normale.', effect: 'Cela peut ajouter du texte du fournisseur, sans garantir des métadonnées brèves.', verify: 'Inspectez une réponse structurée contrôlée ; ne supposez pas que l’option a produit plus de texte.' }, + 'ai.bugbotDryRun': { summary: 'Gardez Bugbot en mode analyse seule pour ses futures exécutions.', when: 'Utile pour une évaluation ; incompatible avec les preuves nécessaires à l’approbation.', example: 'Choisissez Non pour publier les revues normales.', effect: 'Bugbot analyse sans publier de résultat ni modifier le dépôt. Ce n’est pas setup --dry-run.', verify: 'Inspectez le résultat du workflow Bugbot : le mode analyse seule ne publie ni revue ni vérification.' }, + 'ai.bugbotOrganizationRules': { summary: 'Définissez des consignes générales pour Bugbot, une règle par ligne.', when: 'Utile si l’équipe partage des critères de revue dans le dépôt configuré.', example: 'Signaler les changements qui contournent l’isolation des clients.', effect: 'Ces règles précèdent celles du dépôt ; le périmètre de la Variable détermine le stockage.', verify: 'Inspectez la Variable configurée et activez le traçage des sources de règles.' }, + 'ai.provisioningMode': { summary: 'Décidez comment l’Action trouve ou installe l’agent CLI.', when: 'Ce choix s’applique sur le runner au démarrage d’une tâche IA activée.', example: 'Auto réutilise un CLI installé ou installe une version fixée de Codex/OpenCode.', effect: 'Always réinstalle les versions examinées ; Disabled exige un CLI préinstallé. Cursor doit être préinstallé.', verify: 'Inspectez l’étape de préparation et la version du binaire rapportée par le runner.' }, + 'pullRequestApproval.testChecks': { summary: 'Choisissez les jobs CI que le bot peut considérer comme preuve de tests indépendante.', when: 'Obligatoire pour les modes Recommend et Guarded.', example: 'Sélectionnez le job Tests exact, son ID d’App GitHub et son workflow dans une exécution récente.', effect: 'Seules les identités exactes listées satisfont la condition d’approbation.', verify: 'Ouvrez l’exécution liée et vérifiez le job, l’App et le résultat pour le commit courant.' }, + 'pullRequestApproval.producerAttested': { summary: 'Confirmez avoir inspecté le producteur CI exact et son étape obligatoire de couverture.', when: 'Obligatoire avant que le mode Guarded puisse approuver.', example: 'Vérifiez que le job Tests échoue si le seuil de couverture n’est pas atteint.', effect: 'Votre confirmation est enregistrée ; Copilot ne la déduit pas d’une vérification verte.', verify: 'Inspectez le fichier du workflow et une exécution réelle avant de répondre Oui.' }, + 'pullRequestApproval.coverage.mode': { summary: 'Choisissez comment prouver la couverture exigée du code modifié.', when: 'Ce choix s’applique lorsque l’approbation de PR est activée.', example: 'Check : le CI impose le seuil. Numeric : un workflow fiable publie des décomptes limités.', effect: 'Check fait confiance au garde CI ; Numeric lit copilot-diff-coverage-v1 et compare un seuil.', verify: 'Inspectez respectivement la condition d’échec du CI ou l’artefact du rapporteur.' }, + 'pullRequestApproval.coverage.checkName': { summary: 'Sélectionnez la vérification fiable qui échoue sous le seuil de couverture.', when: 'Obligatoire dans les deux modes de preuve.', example: 'Utilisez le même job Tests exact que dans l’étape précédente.', effect: 'Le succès d’une autre vérification ou App ne remplace pas ce garde.', verify: 'Vérifiez que l’étape de couverture est obligatoire, pas seulement informative.' }, + 'projects.enabled': { summary: 'Décidez si les futurs tickets et PR doivent utiliser des Projects GitHub existants.', when: 'Avant de créer le PAT de configuration pour prévoir le droit de lecture des Projects.', example: 'Oui si l’équipe utilise un Project de l’organisation ; Non pour ignorer cette intégration.', effect: 'Oui prévoit Projects: read de l’organisation si nécessaire. Aucun Project n’est modifié maintenant.', verify: 'Vérifiez les droits du PAT ; les Projects précis seront choisis après son autorisation.' }, + 'projects.ids': { summary: 'Choisissez les Projects existants que Copilot pourra actualiser plus tard.', when: 'Après la vérification du PAT ; si la liste est inaccessible, utilisez la saisie manuelle.', example: 'Pour https://github.com/orgs/acme/projects/5, choisissez la carte ou saisissez 5, jamais PVT_…', effect: 'Leurs numéros seront enregistrés ; aucun élément Project n’est modifié maintenant.', verify: 'Ouvrez chaque Project et vérifiez propriétaire et numéro avant de confirmer le plan.' }, +}; + +function howToChoose(question: SetupQuestion): string { + if (question.id === 'pullRequestApproval.coverage.checkName') return 'Choisissez l’une des vérifications fiables ci-dessus. Ouvrez son exécution et son workflow : l’étape de couverture doit faire échouer le job si le seuil n’est pas atteint. Un résultat vert ne suffit pas.'; + if (question.id === 'pullRequestApproval.producerAttested') return 'Répondez Oui uniquement après avoir vérifié chaque nom, ID d’App et workflow choisis, ainsi que l’étape de couverture obligatoire du check retenu. Sinon, répondez Non et restez en mode recommandation.'; + if (question.id === 'pullRequestApproval.coverage.artifactWorkflowName') return 'Saisissez le nom exact d’un workflow fiable choisi qui publie copilot-diff-coverage-v1 pour cette PR et ses commits de base et de tête. Ne devinez pas le nom du workflow.'; + if (question.id === 'projects.statusVerified') return 'Ouvrez chaque Project choisi sur GitHub, inspectez son champ Status et comparez les quatre valeurs exactes ci-dessus. Répondez Oui uniquement si toutes existent dans chaque Project ; Non revient au choix des Projects.'; + if (/^projects\.(issue|pullRequest)(Created|InProgress)Column$/u.test(question.id)) return 'Choisissez une option du champ Status présente dans tous les Projects sélectionnés. Si les options ne sont pas lisibles, ouvrez chaque Project sur GitHub et saisissez la même valeur existante ; des valeurs différentes par Project ne sont pas prises en charge.'; + switch (question.kind) { + case 'boolean': return 'Choisissez Oui pour activer ou Non pour désactiver ; la réponse suggérée apparaît plus bas.'; + case 'producer-select': return 'Inspectez chaque exécution candidate sur GitHub, puis choisissez le job, l’ID d’App et le workflow exacts. Ne saisissez manuellement que si aucun candidat vérifié n’apparaît.'; + case 'project-select': return 'Choisissez par titre et URL. Saisissez le numéro positif ou l’URL GitHub exacte si un Project manque ; les ID PVT_ sont invalides.'; + case 'scope-overrides': return 'Sélectionnez uniquement les noms hérités à remplacer volontairement dans le dépôt. Laissez vide pour conserver les valeurs de l’organisation.'; + case 'multi-select': return 'Cochez les workflows que vous utiliserez. Vous pouvez en choisir plusieurs ; vérifiez leurs autorisations avant de créer un PAT.'; + case 'choice': return 'Choisissez une valeur après avoir lu ses conséquences ; la valeur enregistrée n’est pas traduite.'; + case 'number': return 'Saisissez un entier dans la plage indiquée ; gardez la valeur suggérée en cas de doute.'; + default: return 'Saisissez la valeur exacte utilisée par votre dépôt ou runner ; ne laissez vide que si la question le permet.'; + } +} + +export function frenchQuestionExplanation(question: SetupQuestion, documentation: SetupQuestionExplanation['documentation']): SetupQuestionExplanation { + const copy = special[question.id] ?? section[question.stateId]; + return { + label: translatedQuestionLabel(question, 'fr'), + ...copy, + summary: special[question.id] ? copy.summary : (setupQuestionPurposeFrPt(question, 'fr') ?? copy.summary), + where: where[question.stateId], + how: howToChoose(question), + why: `Cette décision permet d’accorder le plan, les autorisations du PAT et l’automatisation future avant d’appliquer des changements. ${copy.when}`, + documentation: { title: 'Documentation de cette option', url: documentation.url }, + }; +} diff --git a/src/application/policies/setup_question_guidance_policy.ts b/src/application/policies/setup_question_guidance_policy.ts new file mode 100644 index 000000000..50ef22504 --- /dev/null +++ b/src/application/policies/setup_question_guidance_policy.ts @@ -0,0 +1,139 @@ +import type { SetupQuestion } from '../../domain/setup_questionnaire'; +import type { SetupQuestionExplanation, SetupQuestionPresentation } from '../contracts/web_setup_view'; +import { spanishQuestionLabel } from './setup_question_translations'; +import { setupQuestionDocumentation } from './setup_question_documentation_policy'; +import { setupQuestionPurpose } from './setup_question_purpose_policy'; +import { frenchQuestionExplanation } from './setup_question_guidance_fr'; +import { portugueseQuestionExplanation } from './setup_question_guidance_pt'; + +type Copy = Pick; + +const location: Readonly> = { + capabilities: { en: 'Setup writes the selected workflow files into this repository and requests only the GitHub permissions those workflows need.', es: 'Setup escribe los workflows seleccionados en este repositorio y solicita solo los permisos de GitHub necesarios.' }, + 'agent-runtime': { en: 'The generated GitHub Actions workflows invoke this agent on their runner; this does not install an agent on your computer.', es: 'Los workflows de GitHub Actions invocan este agente en su runner; no se instala en tu ordenador.' }, + 'agent-model-defaults': { en: 'The shared model and command defaults are stored in the repository configuration consumed by the generated workflows.', es: 'Los valores comunes de modelo y comando se guardan en la configuración del repositorio que usan los workflows.' }, + 'agent-role-overrides': { en: 'This task-specific override is stored in the repository configuration and read only when that task runs.', es: 'Esta excepción por tarea se guarda en la configuración del repositorio y se lee cuando se ejecuta esa tarea.' }, + repository: { en: 'The repository profile and generated workflows use this value for future branch, issue and pull-request events.', es: 'El perfil del repositorio y los workflows generados usan este valor en futuros eventos de ramas, issues y pull requests.' }, + deployment: { en: 'The repository profile controls later release and hotfix workflows; nothing is released by answering this question.', es: 'El perfil del repositorio controla los futuros workflows de release y hotfix; responder no publica ninguna versión.' }, + bugbot: { en: 'The generated workflow reads this setting from repository configuration or selected GitHub Actions Variables when Bugbot runs.', es: 'El workflow lee este ajuste de la configuración o las Variables de GitHub Actions seleccionadas al ejecutar Bugbot.' }, + 'pull-request-approval': { en: 'The guarded-approval configuration uses exact CI producer identities and evidence from GitHub pull-request runs.', es: 'La aprobación protegida usa identidades exactas de los productores de CI y pruebas de las ejecuciones de PR en GitHub.' }, + projects: { en: 'Future issue and pull-request automation uses the selected GitHub Projects and their Status field values.', es: 'La automatización futura de issues y pull requests usa los GitHub Projects y los valores de su campo Status.' }, + provisioning: { en: 'After the final Apply confirmation, setup may create or update the selected files and GitHub Actions resources.', es: 'Tras confirmar Aplicar, setup podrá crear o actualizar los archivos y recursos de GitHub Actions elegidos.' }, + storage: { en: 'GitHub Actions stores these resources at repository or organization scope; the scope changes visibility and required PAT grants.', es: 'GitHub Actions guarda estos recursos en el repositorio o la organización; el ámbito cambia la visibilidad y los permisos del PAT.' }, +}; + +const special: Readonly> = { + 'agents.findings.executable': { + en: { summary: 'Choose the agent command used on the GitHub Actions runner, not on this computer.', when: 'Only change this for a runner with a deliberately installed custom agent binary.', example: 'Leave empty for codex, opencode, or agent according to the provider.', effect: 'A custom path is shared unless a task has its own override; it is never installed automatically.', verify: 'Check the runner has this exact executable before enabling the workflow.' }, + es: { summary: 'Elige el comando del agente en el runner de GitHub Actions, no en este ordenador.', when: 'Cámbialo solo si el runner tiene instalado expresamente otro binario.', example: 'Déjalo vacío para usar codex, opencode o agent según el proveedor.', effect: 'La ruta personalizada se comparte salvo que una tarea tenga su propia excepción; nunca se instala automáticamente.', verify: 'Comprueba que el runner tiene exactamente ese ejecutable.' }, + }, + 'ai.includeReasoning': { + en: { summary: 'Ask for additional provider reasoning when the agent response exposes it.', when: 'Advanced diagnostics only; the current string-only CLI path does not provide separate reasoning parts.', example: 'Keep this off for normal setup.', effect: 'May add provider-produced explanation text, not guaranteed concise metadata.', verify: 'Inspect a controlled structured response; do not assume this toggle produced extra text.' }, + es: { summary: 'Solicita razonamiento adicional si la respuesta del proveedor lo ofrece.', when: 'Solo para diagnósticos avanzados; el CLI actual devuelve texto sin partes de razonamiento separadas.', example: 'Déjalo desactivado en una configuración normal.', effect: 'Podría añadir texto del proveedor; no garantiza metadatos breves.', verify: 'Comprueba una respuesta estructurada controlada; no presupongas que la opción tuvo efecto.' }, + }, + 'ai.bugbotDryRun': { + en: { summary: 'Keep Bugbot in analysis-only mode for future runs.', when: 'Useful during evaluation; incompatible with PR approval evidence.', example: 'Choose No to publish normal reviews.', effect: 'Bugbot analyzes but does not publish findings or make SCM changes. This is not setup --dry-run.', verify: 'Inspect the Bugbot workflow result; no published review or Check should appear from dry-run.' }, + es: { summary: 'Mantiene Bugbot en modo solo análisis para las futuras ejecuciones.', when: 'Útil durante una evaluación; incompatible con la evidencia de aprobación de PR.', example: 'Elige No para publicar revisiones normalmente.', effect: 'Bugbot analiza pero no publica hallazgos ni modifica el repositorio. No es setup --dry-run.', verify: 'Revisa el resultado de Bugbot; el modo ensayo no publica revisión ni Check.' }, + }, + 'ai.bugbotOrganizationRules': { + en: { summary: 'Set broad Bugbot review instructions, one rule per line.', when: 'Use when your team needs review criteria shared across its configured repository.', example: 'Flag changes that bypass tenant isolation.', effect: 'These rules run before repository rules; the selected Variable scope determines storage, not the title.', verify: 'Inspect the configured Variable and enable rule-source tracing for a review.' }, + es: { summary: 'Define criterios generales de revisión para Bugbot, una regla por línea.', when: 'Úsalo si el equipo necesita criterios comunes en el repositorio configurado.', example: 'Señala cambios que omitan el aislamiento entre clientes.', effect: 'Se aplican antes que las reglas del repositorio; el ámbito de la Variable determina dónde se guardan.', verify: 'Revisa la Variable configurada y activa el rastreo de fuentes de reglas.' }, + }, + 'ai.provisioningMode': { + en: { summary: 'Decide how the Action finds or installs the selected agent CLI.', when: 'Applies on the runner when an enabled AI task starts.', example: 'Auto reuses an installed CLI or installs pinned Codex/OpenCode when missing.', effect: 'Always reinstalls reviewed defaults; Disabled requires a preinstalled CLI. Cursor must be preinstalled.', verify: 'Inspect the runner provisioning step and its reported binary version.' }, + es: { summary: 'Decide cómo encuentra o instala la Action el agente CLI.', when: 'Se aplica en el runner cuando empieza una tarea de IA.', example: 'Auto reutiliza el CLI existente o instala una versión fijada de Codex/OpenCode si falta.', effect: 'Always reinstala versiones fijadas; Disabled exige instalación previa. Cursor siempre se instala aparte.', verify: 'Revisa el paso de preparación y la versión del binario en el runner.' }, + }, + 'pullRequestApproval.testChecks': { + en: { summary: 'Choose CI jobs the approval bot may trust as independent test evidence.', when: 'Required for recommend or guarded approval.', example: 'Select the exact Tests job, its GitHub App ID, and parent workflow from a recent run.', effect: 'Only the listed exact producer identities can satisfy the approval gate.', verify: 'Open the linked workflow run and confirm the job, App, and current-head result.' }, + es: { summary: 'Selecciona los jobs de CI que el bot puede considerar pruebas fiables.', when: 'Obligatorio para las aprobaciones recomendadas o protegidas.', example: 'Elige el job Tests, su ID de GitHub App y el workflow de una ejecución reciente.', effect: 'Solo esas identidades exactas podrán satisfacer la condición de aprobación.', verify: 'Abre la ejecución vinculada y comprueba job, App y resultado para el commit actual.' }, + }, + 'pullRequestApproval.producerAttested': { + en: { summary: 'Confirm that you inspected the exact CI producer and its coverage-enforcing step.', when: 'Required before guarded mode can ever submit an approval.', example: 'Verify the selected Tests job fails when the coverage budget fails.', effect: 'Your assertion is recorded; Copilot does not infer it from a green check.', verify: 'Inspect the workflow file and an actual CI run before selecting Yes.' }, + es: { summary: 'Confirma que comprobaste el productor exacto de CI y su paso obligatorio de cobertura.', when: 'Necesario antes de que el modo protegido pueda aprobar.', example: 'Comprueba que el job Tests falla cuando no se alcanza la cobertura mínima.', effect: 'Se registra tu confirmación; Copilot no la deduce de un check verde.', verify: 'Revisa el workflow y una ejecución real antes de elegir Sí.' }, + }, + 'pullRequestApproval.coverage.mode': { + en: { summary: 'Choose how approval proves the changed-code coverage requirement.', when: 'Applies when PR approval is enabled.', example: 'Check: CI enforces the budget. Numeric: a trusted workflow publishes bounded counts.', effect: 'Check mode trusts a selected CI gate; numeric mode reads copilot-diff-coverage-v1 and compares a threshold.', verify: 'Inspect the CI failure condition or the reporter artifact, respectively.' }, + es: { summary: 'Elige cómo se demuestra la cobertura del código modificado.', when: 'Se aplica si habilitas la aprobación de PR.', example: 'Check: CI exige el mínimo. Numeric: un workflow fiable publica recuentos de líneas.', effect: 'Check confía en una condición de CI; numeric lee copilot-diff-coverage-v1 y compara un umbral.', verify: 'Comprueba la condición de fallo del CI o el artefacto del reporter.' }, + }, + 'pullRequestApproval.coverage.checkName': { + en: { summary: 'Select the trusted check that fails when coverage is below budget.', when: 'Required for both coverage evidence modes.', example: 'Use the same exact Tests check selected in the previous step.', effect: 'A success from another check or App cannot substitute for this gate.', verify: 'Inspect the selected job and confirm its coverage step is mandatory, not advisory.' }, + es: { summary: 'Selecciona el check fiable que falla si no se alcanza la cobertura mínima.', when: 'Obligatorio en ambos modos de evidencia.', example: 'Usa el mismo check Tests elegido en el paso anterior.', effect: 'Un éxito de otro check o App no sustituye esta condición.', verify: 'Comprueba que el paso de cobertura es obligatorio, no solo informativo.' }, + }, + 'projects.enabled': { + en: { summary: 'Decide whether future issue and PR automation should use existing GitHub Projects.', when: 'Ask now, before creating the setup PAT, so its Project read permission can be scoped correctly.', example: 'Choose Yes if your team already tracks work in an organization Project; choose No to skip it.', effect: 'Yes includes organization Projects: read in the setup PAT when applicable. No Project is changed now.', verify: 'Review the PAT permission table; exact Projects are selected after GitHub authorizes the PAT.' }, + es: { summary: 'Decide si la automatización futura de issues y PR usará Projects existentes.', when: 'Se pregunta antes de crear el PAT de configuración para ajustar el permiso de lectura de Projects.', example: 'Elige Sí si tu equipo usa un Project de la organización; No para omitirlo.', effect: 'Sí incluye Projects: read de la organización en el PAT cuando aplica. Ahora no se modifica ningún Project.', verify: 'Revisa los permisos del PAT; elegirás los Projects concretos tras autorizarlo en GitHub.' }, + }, + 'projects.ids': { + en: { summary: 'Choose the existing Projects that Copilot may update in future issue and PR workflows.', when: 'After the setup PAT is checked, GitHub may list accessible organization Projects. Personal Projects or unavailable lists need manual entry.', example: 'For https://github.com/orgs/acme/projects/5, select the project card or enter 5; never enter PVT_…', effect: 'Setup stores Project numbers in repository configuration; it does not create or edit Project items now.', verify: 'Open each linked Project and check its owner and URL number before approving the plan.' }, + es: { summary: 'Elige los Projects existentes que Copilot podrá actualizar en futuros flujos de issues y PR.', when: 'Después de comprobar el PAT, GitHub puede listar Projects accesibles de la organización. Para Projects personales o fallos de consulta, introdúcelos manualmente.', example: 'Para https://github.com/orgs/acme/projects/5, marca la tarjeta o escribe 5; nunca PVT_…', effect: 'Setup guarda números de Project en la configuración; ahora no crea ni edita elementos.', verify: 'Abre cada Project enlazado y comprueba el dueño y número de la URL antes de aprobar el plan.' }, + }, +}; + +const section: Readonly> = { + capabilities: { en: { summary: 'Choose which automation Copilot will install.', when: 'This affects workflows, GitHub permissions, and later questions.', example: 'Disable a feature you do not plan to use.', effect: 'Only selected capabilities are planned.', verify: 'Review the generated setup plan before Apply.' }, es: { summary: 'Elige qué automatizaciones instalará Copilot.', when: 'Afecta a workflows, permisos de GitHub y preguntas posteriores.', example: 'Desactiva una función que no vayas a usar.', effect: 'Solo se planifican las funciones seleccionadas.', verify: 'Revisa el plan antes de aplicar cambios.' } }, + 'agent-runtime': { en: { summary: 'Choose the agent CLI for this task.', when: 'Applies when the selected feature runs in GitHub Actions.', example: 'Codex runs through the codex CLI.', effect: 'The Action invokes the selected provider, never an implicit fallback.', verify: 'Check the runner has the selected CLI and credentials.' }, es: { summary: 'Elige el agente CLI para esta tarea.', when: 'Se aplica al ejecutar la función elegida en GitHub Actions.', example: 'Codex usa el CLI codex.', effect: 'La Action usa ese proveedor, sin sustitución implícita.', verify: 'Comprueba el CLI y las credenciales del runner.' } }, + 'agent-model-defaults': { en: { summary: 'Set the model defaults shared by agent tasks.', when: 'Used unless you configure each task separately.', example: 'Keep the reviewed model by accepting the suggested value.', effect: 'The Action passes these values to the selected CLI.', verify: 'Check the plan and runner model allowlist.' }, es: { summary: 'Define el modelo común para las tareas del agente.', when: 'Se usa salvo que configures cada tarea por separado.', example: 'Acepta el modelo revisado que aparece como sugerencia.', effect: 'La Action pasa estos valores al CLI elegido.', verify: 'Revisa el plan y la lista de modelos permitidos.' } }, + 'agent-role-overrides': { en: { summary: 'Override this one agent task.', when: 'Only when independent task configuration is enabled.', example: 'Use a different model for review than for planning.', effect: 'Only this task uses the override.', verify: 'Inspect the per-task plan values.' }, es: { summary: 'Personaliza esta tarea del agente.', when: 'Solo si activaste la configuración independiente por tarea.', example: 'Usa un modelo distinto para revisión y planificación.', effect: 'Solo esta tarea usa el valor personalizado.', verify: 'Revisa los valores de cada tarea en el plan.' } }, + repository: { en: { summary: 'Set how Copilot treats your repository.', when: 'Applies to generated workflows and future issue/PR events.', example: 'Use your actual development branch name.', effect: 'Future automation follows the chosen branch and workflow rules.', verify: 'Review the planned files and repository profile.' }, es: { summary: 'Define cómo Copilot tratará tu repositorio.', when: 'Se aplica a los workflows y futuros eventos de issues/PR.', example: 'Indica el nombre real de tu rama de desarrollo.', effect: 'La automatización seguirá las ramas y reglas elegidas.', verify: 'Revisa los archivos del plan y el perfil del repositorio.' } }, + deployment: { en: { summary: 'Choose release and hotfix behavior.', when: 'Only matters when those workflows are enabled.', example: 'Keep the default strategy unless your branching policy differs.', effect: 'Changes how release branches and reconciliation PRs are managed.', verify: 'Inspect the release/hotfix section of the plan.' }, es: { summary: 'Define el comportamiento de releases y hotfixes.', when: 'Importa si activaste esos workflows.', example: 'Conserva la estrategia predeterminada salvo que tus ramas funcionen distinto.', effect: 'Cambia la gestión de ramas y PR de reconciliación.', verify: 'Revisa la sección de releases y hotfixes del plan.' } }, + bugbot: { en: { summary: 'Choose how Bugbot analyzes and reports code changes.', when: 'Used when AI review features run.', example: 'The default publishes eligible findings without blocking all PRs.', effect: 'Changes future review publication and diagnostics.', verify: 'Inspect the Bugbot Variables in the plan and later review results.' }, es: { summary: 'Define cómo Bugbot analiza y comunica cambios de código.', when: 'Se usa cuando se ejecutan funciones de revisión con IA.', example: 'Por defecto publica hallazgos aptos sin bloquear todos los PR.', effect: 'Cambia futuras revisiones y diagnósticos.', verify: 'Revisa las Variables de Bugbot en el plan y sus resultados.' } }, + 'pull-request-approval': { en: { summary: 'Choose evidence required before the bot recommends or submits PR approval.', when: 'Only applies if PR automation is enabled.', example: 'Recommend informs a human; guarded may submit a native approval.', effect: 'No PR is approved solely because this page shows green checks.', verify: 'Inspect the trusted CI, Bugbot, and branch-rule evidence.' }, es: { summary: 'Elige las pruebas necesarias para recomendar o aprobar un PR.', when: 'Solo se aplica si activaste la automatización de PR.', example: 'Recommend informa a una persona; guarded puede publicar una aprobación.', effect: 'Ningún PR se aprueba solo porque esta pantalla muestre checks verdes.', verify: 'Revisa CI, Bugbot y las reglas de rama.' } }, + projects: { en: { summary: 'Choose an existing Project Status value for an issue or PR transition.', when: 'Only when Projects integration is selected.', example: 'Todo when an issue is created; In Progress when work starts.', effect: 'Future automation updates the Status field, not a visual board column.', verify: 'Open each selected Project and inspect its Status field options.' }, es: { summary: 'Elige un valor Status existente para una transición de issue o PR.', when: 'Solo si elegiste integrar Projects.', example: 'Todo al crear un issue; In Progress al empezar el trabajo.', effect: 'La automatización futura actualiza el campo Status, no una columna visual.', verify: 'Abre cada Project y revisa las opciones de su campo Status.' } }, + provisioning: { en: { summary: 'Choose which GitHub Actions resources setup manages.', when: 'Affects PAT grants and setup writes.', example: 'Keep Secrets enabled if the bot PAT must be installed.', effect: 'Selected resources may be created or updated after approval.', verify: 'Inspect exact resource names in the plan.' }, es: { summary: 'Elige qué recursos de GitHub Actions gestionará setup.', when: 'Afecta a permisos del PAT y cambios de configuración.', example: 'Mantén Secrets si hay que instalar el PAT del bot.', effect: 'Los recursos seleccionados podrán crearse o actualizarse tras aprobar.', verify: 'Revisa los nombres exactos en el plan.' } }, + storage: { en: { summary: 'Choose where GitHub Actions Variables and Secrets live.', when: 'Applies when provisioning is enabled.', example: 'Repository scope is the simplest default.', effect: 'Affects visibility, permission grants, and precedence.', verify: 'Check the selected scope and shadow warnings in the plan.' }, es: { summary: 'Elige dónde se guardan Variables y Secrets de GitHub Actions.', when: 'Se aplica si activaste su configuración.', example: 'El ámbito de repositorio es el predeterminado más sencillo.', effect: 'Afecta a visibilidad, permisos y precedencia.', verify: 'Revisa el ámbito y los avisos de superposición en el plan.' } }, +}; + +export function setupQuestionPresentation(question: SetupQuestion): SetupQuestionPresentation { + const copy = special[question.id] ?? section[question.stateId]; + const purpose = setupQuestionPurpose(question); + const documentation = setupQuestionDocumentation(question); + const genericHow = question.kind === 'boolean' + ? { en: 'Choose Yes to enable this behavior or No to leave it off; the suggested answer appears below.', es: 'Elige Sí para activarlo o No para dejarlo desactivado; abajo verás la respuesta sugerida.' } + : question.kind === 'producer-select' + ? { en: 'Inspect each candidate run on GitHub, then select its exact job, source App ID and workflow. A listed run is observed, not proof of a required coverage gate; use manual entry for a missing producer.', es: 'Abre cada ejecución candidata en GitHub y comprueba el job, la App y el workflow exactos. Una ejecución listada es observada, no prueba que exija cobertura; usa la entrada manual si falta un productor.' } + : question.kind === 'project-select' + ? { en: 'Select Projects by title and URL. If one is missing, enter its positive URL number or exact GitHub URL; PVT_ IDs are not valid.', es: 'Marca Projects por título y URL. Si falta uno, introduce su número positivo o URL exacta de GitHub; los IDs PVT_ no valen.' } + : question.kind === 'scope-overrides' + ? { en: 'Select only inherited names you deliberately want to replace at repository scope. Leave empty to keep organization values.', es: 'Selecciona solo los nombres heredados que quieras sustituir en el repositorio. Vacío conserva los valores de la organización.' } + : question.kind === 'multi-select' + ? { en: 'Toggle the listed workflows you intend to use. You can select more than one; review their GitHub permissions before creating a PAT.', es: 'Marca los workflows que usarás. Puedes elegir varios; revisa sus permisos de GitHub antes de crear el PAT.' } + : question.kind === 'choice' + ? { en: 'Select one of the listed values after reading its consequence; the stored value is not translated.', es: 'Elige una de las opciones tras revisar sus consecuencias; el valor guardado no se traduce.' } + : question.kind === 'number' + ? { en: 'Enter a whole number within the range described in the question; accept the suggested value when unsure.', es: 'Introduce un número entero dentro del intervalo indicado; acepta el sugerido si tienes dudas.' } + : { en: 'Enter the exact value used by your repository or runner; leave it empty only when the question says empty is allowed.', es: 'Introduce el valor exacto de tu repositorio o runner; déjalo vacío solo si la pregunta lo permite.' }; + const howById: Readonly> = { + 'pullRequestApproval.coverage.checkName': { + en: 'Select one of the trusted checks above. Open its linked run and workflow file; the coverage step must fail this job when the budget fails. A green result alone is not proof.', + es: 'Elige uno de los checks fiables anteriores. Abre su ejecución y workflow; el paso de cobertura debe hacer fallar el job si no se alcanza el mínimo. Un resultado verde no basta.', + }, + 'pullRequestApproval.producerAttested': { + en: 'Answer Yes only after inspecting every selected name, App ID and workflow, plus the coverage-enforcing step of the check you just chose. Otherwise answer No and stay in recommendation mode.', + es: 'Responde Sí solo tras comprobar cada nombre, ID de App y workflow, además del paso obligatorio de cobertura del check elegido. Si no, responde No y mantén el modo recomendación.', + }, + 'pullRequestApproval.coverage.artifactWorkflowName': { + en: 'Enter the exact name of a trusted selected workflow that publishes copilot-diff-coverage-v1 for this PR/head/base. Do not enter an artifact filename or a guessed workflow name.', + es: 'Escribe el nombre exacto de un workflow fiable seleccionado que publique copilot-diff-coverage-v1 para este PR/head/base. No pongas un archivo ni un nombre supuesto.', + }, + 'projects.statusVerified': { + en: 'Open every selected Project in GitHub, inspect its Status field, and compare the exact four values shown above. Choose Yes only when all four exist in every Project; No returns to Project selection.', + es: 'Abre cada Project elegido en GitHub, revisa su campo Status y compara los cuatro valores exactos anteriores. Elige Sí solo si todos existen en cada Project; No vuelve a la selección de Projects.', + }, + }; + const statusHow = /^projects\.(issue|pullRequest)(Created|InProgress)Column$/u.test(question.id) + ? { en: 'Choose an option shown in every selected Project’s Status field. If options cannot be read, open each Project in GitHub and enter the same exact existing option; different names per Project are not supported.', + es: 'Elige una opción del campo Status de todos los Projects seleccionados. Si no se pueden consultar, abre cada Project y escribe el mismo valor existente; no se admiten nombres distintos por Project.' } + : undefined; + const how = howById[question.id] ?? statusHow ?? genericHow; + const where = location[question.stateId]; + return { + en: { label: question.label.replace(' (Space toggles, Enter confirms)', ''), ...copy.en, + summary: special[question.id] ? copy.en.summary : (purpose?.en ?? copy.en.summary), + where: where.en, how: how.en, why: `This choice is requested now so the plan, token permissions and future automation agree. ${copy.en.when}`, documentation }, + es: { label: spanishQuestionLabel(question), ...copy.es, + summary: special[question.id] ? copy.es.summary : (purpose?.es ?? copy.es.summary), + where: where.es, how: how.es, why: `Esta elección permite ajustar el plan, los permisos del PAT y la automatización futura antes de aplicar cambios. ${copy.es.when}`, documentation }, + fr: frenchQuestionExplanation(question, documentation), + pt: portugueseQuestionExplanation(question, documentation), + }; +} diff --git a/src/application/policies/setup_question_guidance_pt.ts b/src/application/policies/setup_question_guidance_pt.ts new file mode 100644 index 000000000..def2f336b --- /dev/null +++ b/src/application/policies/setup_question_guidance_pt.ts @@ -0,0 +1,79 @@ +import type { SetupQuestion } from '../../domain/setup_questionnaire'; +import type { SetupQuestionExplanation } from '../contracts/web_setup_view'; +import { translatedQuestionLabel } from './setup_question_labels_fr_pt'; +import { setupQuestionPurposeFrPt } from './setup_question_purpose_fr_pt'; + +type Copy = Pick; + +const where: Readonly> = { + capabilities: 'A configuração escreve os fluxos escolhidos neste repositório e pede apenas as permissões GitHub necessárias.', + 'agent-runtime': 'Os fluxos GitHub Actions gerados executam este agente no respetivo runner; nada é instalado neste computador.', + 'agent-model-defaults': 'O modelo e o comando comuns são guardados na configuração do repositório usada pelos fluxos gerados.', + 'agent-role-overrides': 'Esta exceção para uma tarefa é guardada no repositório e lida apenas quando essa tarefa é executada.', + repository: 'O perfil do repositório e os fluxos gerados usam este valor em futuros eventos de ramos, questões e pull requests.', + deployment: 'O perfil do repositório controla futuros fluxos de release e hotfix; responder não publica nada.', + bugbot: 'O fluxo gerado lê esta definição da configuração ou das Variables do GitHub Actions selecionadas.', + 'pull-request-approval': 'A aprovação protegida usa identidades exatas dos produtores CI e provas das execuções de pull requests no GitHub.', + projects: 'Os Projects escolhidos e os valores do campo Status serão usados pela futura automatização de questões e pull requests.', + provisioning: 'Após a confirmação final, a configuração pode criar ou atualizar os ficheiros e recursos do GitHub Actions escolhidos.', + storage: 'O GitHub Actions guarda estes recursos no repositório ou na organização; o âmbito altera a visibilidade e as permissões do PAT.', +}; + +const section: Readonly> = { + capabilities: { summary: 'Escolha as automatizações que o Copilot irá instalar.', when: 'Isto afeta os fluxos, as permissões GitHub e as perguntas seguintes.', example: 'Desative uma função que não pretende utilizar.', effect: 'Só as funções selecionadas entram no plano.', verify: 'Reveja o plano antes de aplicar alterações.' }, + 'agent-runtime': { summary: 'Escolha o agente CLI para esta tarefa.', when: 'Aplica-se quando a função selecionada é executada no GitHub Actions.', example: 'O Codex é executado através do comando codex.', effect: 'A Action executa o fornecedor escolhido, sem substituição implícita.', verify: 'Confirme que o runner tem o CLI e as credenciais necessárias.' }, + 'agent-model-defaults': { summary: 'Defina os modelos predefinidos comuns às tarefas do agente.', when: 'Usam-se salvo se configurar cada tarefa separadamente.', example: 'Mantenha o modelo sugerido se não tiver uma necessidade específica.', effect: 'A Action passa estes valores ao CLI escolhido.', verify: 'Reveja o plano e os modelos permitidos no runner.' }, + 'agent-role-overrides': { summary: 'Personalize esta tarefa do agente.', when: 'Apenas se tiver ativado a configuração independente por tarefa.', example: 'Use um modelo diferente para revisão e planeamento.', effect: 'A exceção só se aplica a esta tarefa.', verify: 'Reveja os valores de cada tarefa no plano.' }, + repository: { summary: 'Defina como o Copilot trata o seu repositório.', when: 'Aplica-se aos fluxos gerados e a futuros eventos de questões ou pull requests.', example: 'Indique o nome real do ramo de desenvolvimento.', effect: 'A futura automatização segue os ramos e as regras escolhidos.', verify: 'Reveja os ficheiros planeados e o perfil do repositório.' }, + deployment: { summary: 'Defina o comportamento de releases e hotfixes.', when: 'Só importa se esses fluxos estiverem ativados.', example: 'Mantenha a estratégia predefinida salvo se a política de ramos for diferente.', effect: 'Altera a gestão de ramos e pull requests de reconciliação.', verify: 'Reveja a secção de releases e hotfixes do plano.' }, + bugbot: { summary: 'Defina como o Bugbot analisa e comunica alterações.', when: 'Usa-se quando as funções de revisão por IA são executadas.', example: 'Por predefinição, publica resultados elegíveis sem bloquear todas as pull requests.', effect: 'Altera futuras publicações e diagnósticos de revisão.', verify: 'Reveja as Variables do Bugbot no plano e os resultados posteriores.' }, + 'pull-request-approval': { summary: 'Escolha as provas exigidas antes de o bot recomendar ou submeter uma aprovação.', when: 'Só se aplica se a automatização de pull requests estiver ativa.', example: '«Recommend» informa uma pessoa; «guarded» pode aprovar no GitHub.', effect: 'Uma verificação verde nesta página nunca aprova uma pull request por si só.', verify: 'Inspecione as provas CI, o Bugbot e as regras de ramos.' }, + projects: { summary: 'Escolha um valor Status existente para uma transição de questão ou PR.', when: 'Apenas se integrar Projects.', example: 'Todo na criação; In Progress no início do trabalho.', effect: 'A automatização atualiza o campo Status, não uma coluna visual.', verify: 'Verifique as opções Status de cada Project escolhido.' }, + provisioning: { summary: 'Escolha os recursos do GitHub Actions geridos pela configuração.', when: 'Isto afeta as permissões do PAT e as alterações previstas.', example: 'Mantenha os Secrets ativos se for necessário instalar o PAT do bot.', effect: 'Os recursos selecionados poderão ser criados ou atualizados após aprovação.', verify: 'Reveja os nomes exatos dos recursos no plano.' }, + storage: { summary: 'Escolha onde ficam as Variables e Secrets do GitHub Actions.', when: 'Aplica-se quando a sua criação está ativa.', example: 'O âmbito do repositório é a opção predefinida mais simples.', effect: 'Altera visibilidade, permissões e precedência.', verify: 'Confirme o âmbito e os avisos de sobreposição no plano.' }, +}; + +const special: Readonly> = { + 'agents.findings.executable': { summary: 'Escolha o comando do agente no runner GitHub Actions, não neste computador.', when: 'Altere-o apenas se tiver instalado deliberadamente outro agente no runner.', example: 'Deixe vazio para codex, opencode ou agent, conforme o fornecedor.', effect: 'O caminho personalizado é usado pelas tarefas escolhidas e nunca é instalado automaticamente.', verify: 'Confirme que o runner tem exatamente este executável antes de ativar o fluxo.' }, + 'ai.includeReasoning': { summary: 'Peça explicações adicionais se a resposta do fornecedor as disponibilizar.', when: 'Só para diagnóstico avançado; o percurso CLI atual não fornece partes de raciocínio separadas.', example: 'Mantenha desativado numa configuração normal.', effect: 'Pode acrescentar texto do fornecedor, sem garantir metadados breves.', verify: 'Inspecione uma resposta estruturada controlada; não presuma que a opção produziu texto adicional.' }, + 'ai.bugbotDryRun': { summary: 'Mantenha o Bugbot em modo apenas de análise nas próximas execuções.', when: 'Útil numa avaliação; incompatível com provas de aprovação.', example: 'Escolha Não para publicar revisões normais.', effect: 'O Bugbot analisa sem publicar resultados nem alterar o repositório. Não é setup --dry-run.', verify: 'Inspecione o resultado do fluxo Bugbot: a simulação não publica revisão nem verificação.' }, + 'ai.bugbotOrganizationRules': { summary: 'Defina instruções gerais para o Bugbot, uma regra por linha.', when: 'Use se a equipa precisar de critérios de revisão partilhados no repositório configurado.', example: 'Assinalar alterações que contornem o isolamento entre clientes.', effect: 'Estas regras precedem as do repositório; o âmbito da Variable determina o armazenamento.', verify: 'Inspecione a Variable configurada e ative o rastreio das fontes das regras.' }, + 'ai.provisioningMode': { summary: 'Decida como a Action encontra ou instala o agente CLI.', when: 'Aplica-se no runner quando começa uma tarefa de IA ativa.', example: 'Auto reutiliza um CLI instalado ou instala uma versão fixa de Codex/OpenCode.', effect: 'Always reinstala as versões revistas; Disabled exige um CLI pré-instalado. Cursor tem de estar pré-instalado.', verify: 'Inspecione a etapa de preparação e a versão do binário comunicada pelo runner.' }, + 'pullRequestApproval.testChecks': { summary: 'Escolha os jobs CI que o bot pode aceitar como prova independente de testes.', when: 'Obrigatório para os modos Recommend e Guarded.', example: 'Selecione o job Tests exato, o ID da App GitHub e o workflow de uma execução recente.', effect: 'Só as identidades exatas listadas satisfazem a condição de aprovação.', verify: 'Abra a execução associada e confirme job, App e resultado do commit atual.' }, + 'pullRequestApproval.producerAttested': { summary: 'Confirme que inspecionou o produtor CI exato e a sua etapa obrigatória de cobertura.', when: 'Obrigatório antes de o modo Guarded poder aprovar.', example: 'Confirme que o job Tests falha se o limite de cobertura não for atingido.', effect: 'A sua confirmação fica registada; o Copilot não a deduz de uma verificação verde.', verify: 'Inspecione o ficheiro do workflow e uma execução real antes de escolher Sim.' }, + 'pullRequestApproval.coverage.mode': { summary: 'Escolha como comprovar a cobertura exigida do código alterado.', when: 'Aplica-se quando a aprovação de PR está ativa.', example: 'Check: o CI exige o limite. Numeric: um workflow fiável publica contagens limitadas.', effect: 'Check confia numa condição CI; Numeric lê copilot-diff-coverage-v1 e compara o limite.', verify: 'Inspecione, respetivamente, a condição de falha CI ou o artefacto do relatório.' }, + 'pullRequestApproval.coverage.checkName': { summary: 'Selecione a verificação fiável que falha abaixo do limite de cobertura.', when: 'Obrigatório nos dois modos de prova.', example: 'Use o mesmo job Tests exato da etapa anterior.', effect: 'O sucesso de outra verificação ou App não substitui esta condição.', verify: 'Confirme que a etapa de cobertura é obrigatória e não apenas informativa.' }, + 'projects.enabled': { summary: 'Decida se futuras questões e PR devem usar Projects GitHub existentes.', when: 'Antes de criar o PAT de configuração para prever o acesso de leitura a Projects.', example: 'Sim se a equipa usa um Project da organização; Não para ignorar.', effect: 'Sim inclui Projects: read da organização quando necessário. Nenhum Project é alterado agora.', verify: 'Reveja as permissões do PAT; escolherá os Projects concretos depois de o autorizar.' }, + 'projects.ids': { summary: 'Selecione os Projects existentes que o Copilot poderá atualizar futuramente.', when: 'Após verificar o PAT; se a lista não estiver disponível, introduza os dados manualmente.', example: 'Para https://github.com/orgs/acme/projects/5, marque o cartão ou introduza 5, nunca PVT_…', effect: 'Os números ficam guardados; nenhum item de Project é alterado agora.', verify: 'Abra cada Project e confirme proprietário e número antes de aprovar o plano.' }, +}; + +function howToChoose(question: SetupQuestion): string { + if (question.id === 'pullRequestApproval.coverage.checkName') return 'Escolha uma das verificações fiáveis acima. Abra a execução e o workflow: o passo de cobertura tem de fazer falhar o job quando o limite não é atingido. Um resultado verde não basta.'; + if (question.id === 'pullRequestApproval.producerAttested') return 'Responda Sim apenas depois de verificar cada nome, ID da App e workflow escolhido, bem como o passo obrigatório de cobertura do check selecionado. Caso contrário, responda Não e mantenha o modo de recomendação.'; + if (question.id === 'pullRequestApproval.coverage.artifactWorkflowName') return 'Introduza o nome exato de um workflow fiável selecionado que publique copilot-diff-coverage-v1 para este PR e os seus commits base e head. Não adivinhe o nome do workflow.'; + if (question.id === 'projects.statusVerified') return 'Abra cada Project escolhido no GitHub, inspecione o campo Status e compare os quatro valores exatos acima. Responda Sim apenas se todos existirem em cada Project; Não regressa à seleção de Projects.'; + if (/^projects\.(issue|pullRequest)(Created|InProgress)Column$/u.test(question.id)) return 'Escolha uma opção do campo Status presente em todos os Projects selecionados. Se não conseguir consultar as opções, abra cada Project no GitHub e introduza o mesmo valor existente; valores diferentes por Project não são suportados.'; + switch (question.kind) { + case 'boolean': return 'Escolha Sim para ativar ou Não para desativar; a resposta sugerida aparece abaixo.'; + case 'producer-select': return 'Inspecione cada execução candidata no GitHub e escolha o job, ID da App e workflow exatos. Introduza manualmente apenas se não houver candidato verificado.'; + case 'project-select': return 'Selecione pelo título e URL. Se faltar um Project, introduza o número positivo ou URL exato do GitHub; IDs PVT_ não são válidos.'; + case 'scope-overrides': return 'Selecione apenas os nomes herdados que pretende substituir no repositório. Deixe vazio para conservar os valores da organização.'; + case 'multi-select': return 'Assinale os fluxos que pretende usar. Pode escolher vários; reveja as permissões antes de criar um PAT.'; + case 'choice': return 'Escolha um valor após ler as consequências; o valor guardado não é traduzido.'; + case 'number': return 'Introduza um número inteiro no intervalo indicado; mantenha o valor sugerido se tiver dúvidas.'; + default: return 'Introduza o valor exato usado pelo repositório ou runner; deixe vazio apenas se a pergunta o permitir.'; + } +} + +export function portugueseQuestionExplanation(question: SetupQuestion, documentation: SetupQuestionExplanation['documentation']): SetupQuestionExplanation { + const copy = special[question.id] ?? section[question.stateId]; + return { + label: translatedQuestionLabel(question, 'pt'), + ...copy, + summary: special[question.id] ? copy.summary : (setupQuestionPurposeFrPt(question, 'pt') ?? copy.summary), + where: where[question.stateId], + how: howToChoose(question), + why: `Esta decisão permite alinhar o plano, as permissões do PAT e a futura automatização antes de aplicar alterações. ${copy.when}`, + documentation: { title: 'Documentação desta opção', url: documentation.url }, + }; +} diff --git a/src/application/policies/setup_question_labels/fr.ts b/src/application/policies/setup_question_labels/fr.ts new file mode 100644 index 000000000..8a1bbf6f1 --- /dev/null +++ b/src/application/policies/setup_question_labels/fr.ts @@ -0,0 +1,86 @@ +import type { spanishQuestionLabels } from '../setup_question_translations'; + +/** French presentation labels; semantic question IDs remain unchanged. */ +export const questionLabelsFr: Readonly> = { + 'features.issues': 'Automatiser les tickets : branches, étiquettes, projets et cycle de vie', + 'features.pullRequests': 'Automatiser les pull requests : revue, description et cycle de vie', + 'features.commits': 'Automatiser les commits : progression, taille et analyse Bugbot', + 'features.issueComments': 'Répondre aux commentaires des tickets et permettre les corrections Bugbot', + 'features.pullRequestComments': 'Répondre aux commentaires des pull requests et permettre les corrections Bugbot', + 'features.agentProvisioning': 'Vérifier l’installation des agents CLI dans GitHub Actions', + 'features.credentialHealth': 'Vérifier l’état des identifiants distants', + 'features.inactiveIssueClosure': 'Fermer les tickets inactifs après le délai défini', + 'features.issueTemplates': 'Installer les modèles de ticket', + 'features.pullRequestTemplate': 'Installer le modèle de pull request', + 'issueWorkflows.enabled': 'Types de workflows de ticket à activer', + 'repositoryAgentGuidance.enabled': 'Générer des instructions pour les agents dans le dépôt ?', + 'repositoryAgentGuidance.agentsPointer': 'Comment trouver les instructions depuis AGENTS.md', + 'agents.findings.modelProvider': 'Fournisseur de modèle partagé (sauf réglage propre à une tâche)', + 'agents.findings.model': 'Modèle partagé (sauf réglage propre à une tâche)', + 'agents.findings.effort': 'Effort de raisonnement partagé (les réglages par tâche sont conservés)', + 'agents.findings.executable': 'Exécutable partagé validé (les réglages par tâche sont conservés)', + 'agents.configureIndependently': 'Configurer le modèle et la commande séparément pour chaque tâche ?', + 'repository.mainBranch': 'Branche de production', + 'repository.developmentBranch': 'Branche de développement', + 'repository.featureTree': 'Préfixe des branches de fonctionnalité', + 'repository.bugfixTree': 'Préfixe des branches de correction', + 'repository.hotfixTree': 'Préfixe des branches de correctif urgent', + 'repository.releaseTree': 'Préfixe des branches de version', + 'repository.docsTree': 'Préfixe des branches de documentation', + 'repository.choreTree': 'Préfixe des branches de maintenance', + 'repository.issueManagedBranches': 'L’Action peut-elle créer des branches liées aux tickets ?', + 'repository.preBranchSdd': 'Exiger un SDD avant de créer certaines branches ?', + 'repository.reopenIssueOnPush': 'Rouvrir un ticket fermé quand sa branche reçoit des commits ?', + 'repository.desiredAssigneesCount': 'Nombre souhaité de responsables par ticket', + 'repository.desiredReviewersCount': 'Nombre souhaité de réviseurs par pull request', + 'repository.inactivityThresholdHours': 'Heures d’inactivité avant la fermeture d’un ticket en attente', + 'repository.repositoryLocale': 'Langue des messages du dépôt', + 'repository.issueLocale': 'Langue des tickets (vide : hériter)', + 'repository.pullRequestLocale': 'Langue des pull requests (vide : hériter)', + 'repository.commitPrefixTransforms': 'Transformation des préfixes de commit', + 'repository.releaseReconciliationStrategy': 'Stratégie de réconciliation des versions', + 'repository.hotfixReconciliationStrategy': 'Stratégie de réconciliation des correctifs urgents', + 'repository.reconciliationPullRequestMode': 'Mode des pull requests de réconciliation', + 'repository.reconciliationBackmergeMode': 'Mode de fusion de retour', + 'repository.hotfixActiveReleasePolicy': 'Destination du correctif pendant une version active', + 'repository.reconciliationTree': 'Préfixe des branches de réconciliation', + 'repository.reconciliationCleanup': 'Nettoyage des branches après réconciliation', + 'repository.reconciliationIssueCompletion': 'Sort du ticket après réconciliation', + 'repository.orchestrationPresentationMode': 'Niveau de détail du centre de contrôle des versions', + 'repository.orchestrationDiagrams': 'Afficher des diagrammes accessibles pour les versions ?', + 'repository.orchestrationCommentMode': 'Comment publier les commentaires du cycle de version', + 'ai.pullRequestDescriptionMode': 'Comment mettre à jour la description des pull requests', + 'ai.ignoreFiles': 'Fichiers que l’IA doit ignorer', + 'ai.membersOnly': 'Limiter le traitement par IA aux membres du dépôt ?', + 'ai.includeReasoning': 'Inclure des explications supplémentaires du fournisseur ?', + 'ai.bugbotSeverity': 'Gravité minimale des résultats publiés par Bugbot', + 'ai.bugbotCommentLimit': 'Nombre maximal de commentaires Bugbot par exécution', + 'ai.bugbotFixVerifyCommands': 'Commandes de vérification des corrections Bugbot', + 'ai.bugbotDryRun': 'Analyser avec Bugbot sans publier de changements ?', + 'ai.bugbotEffort': 'Profondeur de l’analyse Bugbot', + 'ai.bugbotReviewDrafts': 'Analyser les pull requests en brouillon ?', + 'ai.bugbotTraceRules': 'Indiquer quelles sources de règles ont été appliquées ?', + 'ai.bugbotSuggestedChanges': 'Publier des suggestions de modification sûres ?', + 'ai.bugbotTelemetry': 'Enregistrer des métriques Bugbot sans contenu ?', + 'ai.bugbotFailOnUnresolved': 'Faire échouer la vérification si des résultats restent ouverts ?', + 'ai.bugbotOrganizationRules': 'Règles Bugbot communes, une par ligne', + 'ai.provisioningMode': 'Comment préparer l’agent CLI sur le runner', + 'pullRequestApproval.mode': 'Que peut faire le bot pour approuver les pull requests ?', + 'pullRequestApproval.testChecks': 'Quelles vérifications CI sont fiables pour approuver ?', + 'pullRequestApproval.producerAttested': 'Avez-vous vérifié le job, l’App et l’étape obligatoire de couverture ?', + 'pullRequestApproval.coverage.mode': 'Comment prouver la couverture requise', + 'pullRequestApproval.coverage.checkName': 'Vérification fiable imposant la couverture', + 'pullRequestApproval.coverage.minDiffPercent': 'Couverture minimale des lignes modifiées (0–100)', + 'pullRequestApproval.coverage.artifactWorkflowName': 'Workflow publiant copilot-diff-coverage-v1', + 'pullRequestApproval.coverage.reporterAttested': 'Avez-vous vérifié l’installation du rapporteur numérique ?', + 'projects.enabled': 'Intégrer des Projects GitHub existants ?', + 'projects.ids': 'Choisir des Projects existants ou saisir leurs numéros d’URL', + 'projects.statusVerified': 'Avez-vous vérifié sur GitHub les quatre valeurs Status exactes de chaque Project choisi ?', + 'projects.issueCreatedColumn': 'Valeur Status des nouveaux tickets', + 'projects.pullRequestCreatedColumn': 'Valeur Status des nouvelles pull requests', + 'projects.issueInProgressColumn': 'Valeur Status des tickets en cours', + 'projects.pullRequestInProgressColumn': 'Valeur Status des pull requests en cours', + createInitialTag: 'Créer v1.0.0 si aucune étiquette de version n’existe ?', + manageRepositoryVariables: 'Créer ou mettre à jour les Variables GitHub Actions ?', + manageRepositorySecrets: 'Valider et configurer les Secrets GitHub Actions ?', +}; diff --git a/src/application/policies/setup_question_labels/pt.ts b/src/application/policies/setup_question_labels/pt.ts new file mode 100644 index 000000000..bd63d7667 --- /dev/null +++ b/src/application/policies/setup_question_labels/pt.ts @@ -0,0 +1,86 @@ +import type { spanishQuestionLabels } from '../setup_question_translations'; + +/** Portuguese presentation labels; semantic question IDs remain unchanged. */ +export const questionLabelsPt: Readonly> = { + 'features.issues': 'Automatizar questões: ramos, etiquetas, projetos e ciclo de vida', + 'features.pullRequests': 'Automatizar pull requests: revisão, descrição e ciclo de vida', + 'features.commits': 'Automatizar commits: progresso, tamanho e análise do Bugbot', + 'features.issueComments': 'Responder a comentários de questões e permitir correções do Bugbot', + 'features.pullRequestComments': 'Responder a comentários de pull requests e permitir correções do Bugbot', + 'features.agentProvisioning': 'Verificar a instalação dos agentes CLI no GitHub Actions', + 'features.credentialHealth': 'Verificar o estado das credenciais remotas', + 'features.inactiveIssueClosure': 'Fechar questões inativas após o prazo definido', + 'features.issueTemplates': 'Instalar modelos de questão', + 'features.pullRequestTemplate': 'Instalar o modelo de pull request', + 'issueWorkflows.enabled': 'Tipos de fluxo de questões a ativar', + 'repositoryAgentGuidance.enabled': 'Gerar instruções para agentes no repositório?', + 'repositoryAgentGuidance.agentsPointer': 'Como encontrar as instruções a partir de AGENTS.md', + 'agents.findings.modelProvider': 'Fornecedor de modelo partilhado (salvo definição própria de uma tarefa)', + 'agents.findings.model': 'Modelo partilhado (salvo definição própria de uma tarefa)', + 'agents.findings.effort': 'Esforço de raciocínio partilhado (as definições por tarefa são preservadas)', + 'agents.findings.executable': 'Executável partilhado validado (as definições por tarefa são preservadas)', + 'agents.configureIndependently': 'Configurar modelo e comando separadamente para cada tarefa?', + 'repository.mainBranch': 'Ramo de produção', + 'repository.developmentBranch': 'Ramo de desenvolvimento', + 'repository.featureTree': 'Prefixo dos ramos de funcionalidade', + 'repository.bugfixTree': 'Prefixo dos ramos de correção', + 'repository.hotfixTree': 'Prefixo dos ramos de hotfix', + 'repository.releaseTree': 'Prefixo dos ramos de release', + 'repository.docsTree': 'Prefixo dos ramos de documentação', + 'repository.choreTree': 'Prefixo dos ramos de manutenção', + 'repository.issueManagedBranches': 'A Action pode criar ramos associados a questões?', + 'repository.preBranchSdd': 'Exigir um SDD antes de criar determinados ramos?', + 'repository.reopenIssueOnPush': 'Reabrir uma questão fechada quando o seu ramo recebe commits?', + 'repository.desiredAssigneesCount': 'Número pretendido de responsáveis por questão', + 'repository.desiredReviewersCount': 'Número pretendido de revisores por pull request', + 'repository.inactivityThresholdHours': 'Horas de inatividade antes de fechar uma questão em espera', + 'repository.repositoryLocale': 'Idioma das mensagens do repositório', + 'repository.issueLocale': 'Idioma das questões (vazio: herdar)', + 'repository.pullRequestLocale': 'Idioma das pull requests (vazio: herdar)', + 'repository.commitPrefixTransforms': 'Transformação dos prefixos dos commits', + 'repository.releaseReconciliationStrategy': 'Estratégia de reconciliação de releases', + 'repository.hotfixReconciliationStrategy': 'Estratégia de reconciliação de hotfixes', + 'repository.reconciliationPullRequestMode': 'Modo das pull requests de reconciliação', + 'repository.reconciliationBackmergeMode': 'Modo da fusão de retorno', + 'repository.hotfixActiveReleasePolicy': 'Destino do hotfix durante uma release ativa', + 'repository.reconciliationTree': 'Prefixo dos ramos de reconciliação', + 'repository.reconciliationCleanup': 'Limpeza de ramos após a reconciliação', + 'repository.reconciliationIssueCompletion': 'O que fazer à questão após a reconciliação', + 'repository.orchestrationPresentationMode': 'Nível de detalhe do centro de controlo de releases', + 'repository.orchestrationDiagrams': 'Mostrar diagramas acessíveis para releases?', + 'repository.orchestrationCommentMode': 'Como publicar os comentários do ciclo de release', + 'ai.pullRequestDescriptionMode': 'Como atualizar a descrição das pull requests', + 'ai.ignoreFiles': 'Ficheiros que a IA deve ignorar', + 'ai.membersOnly': 'Limitar o processamento por IA aos membros do repositório?', + 'ai.includeReasoning': 'Incluir explicações adicionais do fornecedor?', + 'ai.bugbotSeverity': 'Gravidade mínima dos resultados publicados pelo Bugbot', + 'ai.bugbotCommentLimit': 'Número máximo de comentários do Bugbot por execução', + 'ai.bugbotFixVerifyCommands': 'Comandos de verificação das correções do Bugbot', + 'ai.bugbotDryRun': 'Analisar com o Bugbot sem publicar alterações?', + 'ai.bugbotEffort': 'Profundidade da análise do Bugbot', + 'ai.bugbotReviewDrafts': 'Rever pull requests em rascunho?', + 'ai.bugbotTraceRules': 'Indicar que fontes de regras foram aplicadas?', + 'ai.bugbotSuggestedChanges': 'Publicar sugestões de alteração seguras?', + 'ai.bugbotTelemetry': 'Registar métricas do Bugbot sem conteúdo?', + 'ai.bugbotFailOnUnresolved': 'Fazer falhar a verificação se houver resultados por resolver?', + 'ai.bugbotOrganizationRules': 'Regras Bugbot partilhadas, uma por linha', + 'ai.provisioningMode': 'Como preparar o agente CLI no runner', + 'pullRequestApproval.mode': 'O que pode o bot fazer na aprovação de pull requests?', + 'pullRequestApproval.testChecks': 'Que verificações CI são fiáveis para aprovar?', + 'pullRequestApproval.producerAttested': 'Verificou o job, a App e a etapa obrigatória de cobertura?', + 'pullRequestApproval.coverage.mode': 'Como comprovar a cobertura exigida', + 'pullRequestApproval.coverage.checkName': 'Verificação fiável que exige cobertura', + 'pullRequestApproval.coverage.minDiffPercent': 'Cobertura mínima das linhas alteradas (0–100)', + 'pullRequestApproval.coverage.artifactWorkflowName': 'Workflow que publica copilot-diff-coverage-v1', + 'pullRequestApproval.coverage.reporterAttested': 'Verificou a instalação do relatório numérico?', + 'projects.enabled': 'Integrar Projects GitHub existentes?', + 'projects.ids': 'Selecionar Projects existentes ou introduzir os números dos URL', + 'projects.statusVerified': 'Confirmou no GitHub os quatro valores Status exatos de cada Project escolhido?', + 'projects.issueCreatedColumn': 'Valor Status das novas questões', + 'projects.pullRequestCreatedColumn': 'Valor Status das novas pull requests', + 'projects.issueInProgressColumn': 'Valor Status das questões em curso', + 'projects.pullRequestInProgressColumn': 'Valor Status das pull requests em curso', + createInitialTag: 'Criar v1.0.0 se ainda não existir uma etiqueta de versão?', + manageRepositoryVariables: 'Criar ou atualizar as Variables do GitHub Actions?', + manageRepositorySecrets: 'Validar e configurar os Secrets do GitHub Actions?', +}; diff --git a/src/application/policies/setup_question_labels_fr_pt.ts b/src/application/policies/setup_question_labels_fr_pt.ts new file mode 100644 index 000000000..6461aadfc --- /dev/null +++ b/src/application/policies/setup_question_labels_fr_pt.ts @@ -0,0 +1,36 @@ +import type { SetupQuestion } from '../../domain/setup_questionnaire'; +import { spanishQuestionLabel } from './setup_question_translations'; +import { questionLabelsFr } from './setup_question_labels/fr'; +import { questionLabelsPt } from './setup_question_labels/pt'; + +export const questionLabelsFrPt = { fr: questionLabelsFr, pt: questionLabelsPt } as const; + +const roleNames = { + fr: { planner: 'Planification', findings: 'Résultats', reviewer: 'Revue', fixer: 'Correction', tester: 'Tests' }, + pt: { planner: 'Planeamento', findings: 'Resultados', reviewer: 'Revisão', fixer: 'Correção', tester: 'Testes' }, +} as const; + +export function translatedQuestionLabel(question: SetupQuestion, locale: 'en' | 'es' | 'fr' | 'pt'): string { + if (locale === 'en') return question.label.replace(' (Space toggles, Enter confirms)', ''); + if (locale === 'es') return spanishQuestionLabel(question); + const exact = questionLabelsFrPt[locale][question.id]; + if (exact) return exact; + const agent = question.id.match(/^agents\.(planner|findings|reviewer|fixer|tester)\.(provider|modelProvider|model|effort|executable)$/u); + if (agent) { + const fields = { + fr: { provider: 'agent CLI', modelProvider: 'fournisseur du modèle', model: 'modèle', effort: 'effort de raisonnement', executable: 'commande exécutable' }, + pt: { provider: 'agente CLI', modelProvider: 'fornecedor do modelo', model: 'modelo', effort: 'esforço de raciocínio', executable: 'comando executável' }, + } as const; + return `${roleNames[locale][agent[1] as keyof typeof roleNames.fr]} : ${fields[locale][agent[2] as keyof typeof fields.fr]}`; + } + const storage = question.id.match(/^storage\.(variables|secrets)\.(defaultScope|organizationVisibility|preserveExisting|overrides)$/u); + if (storage) { + const resources = { fr: { variables: 'Variables', secrets: 'Secrets' }, pt: { variables: 'Variables', secrets: 'Secrets' } } as const; + const fields = { + fr: { defaultScope: 'périmètre par défaut', organizationVisibility: 'visibilité dans l’organisation', preserveExisting: 'conserver les ressources existantes', overrides: 'exceptions de périmètre' }, + pt: { defaultScope: 'âmbito predefinido', organizationVisibility: 'visibilidade na organização', preserveExisting: 'conservar recursos existentes', overrides: 'exceções de âmbito' }, + } as const; + return `${resources[locale][storage[1] as 'variables' | 'secrets']} : ${fields[locale][storage[2] as keyof typeof fields.fr]}`; + } + return question.label; +} diff --git a/src/application/policies/setup_question_purpose/fr.ts b/src/application/policies/setup_question_purpose/fr.ts new file mode 100644 index 000000000..8e251d23c --- /dev/null +++ b/src/application/policies/setup_question_purpose/fr.ts @@ -0,0 +1,51 @@ +/** French question-specific purposes. */ +export const purposesFr: Readonly> = { + 'issueWorkflows.enabled': 'Choisissez les workflows de ticket que Copilot pourra exécuter ; chacun agit différemment sur les branches, étiquettes et automatisations.', + 'repositoryAgentGuidance.enabled': 'Générez des instructions pour aider les agents IA à travailler en sécurité dans ce projet.', + 'repositoryAgentGuidance.agentsPointer': 'Décidez si le fichier AGENTS.md racine renvoie aux instructions générées, est créé s’il manque, ou reste intact.', + 'agents.configureIndependently': 'Attribuez des fournisseurs et modèles distincts à la planification, aux résultats, à la revue, à la correction et aux tests.', + 'repository.mainBranch': 'Indiquez la branche de production utilisée comme référence par les versions et correctifs urgents.', + 'repository.developmentBranch': 'Indiquez la branche d’intégration habituelle visée par la création de branches et la réconciliation.', + 'repository.issueManagedBranches': 'Autorisez l’Action à créer une branche liée lorsqu’un ticket passe en cours.', + 'repository.preBranchSdd': 'Exigez un document de conception approuvé avant certaines branches de fonctionnalité ou de changement de contrat.', + 'repository.reopenIssueOnPush': 'Rouvrez un ticket terminé quand de nouveaux commits arrivent sur sa branche liée.', + 'repository.desiredAssigneesCount': 'Définissez combien de personnes Copilot affecte à un nouveau ticket ; zéro désactive l’affectation automatique.', + 'repository.desiredReviewersCount': 'Définissez combien de réviseurs Copilot sollicite pour une pull request ; zéro désactive les demandes automatiques.', + 'repository.inactivityThresholdHours': 'Définissez combien de temps un ticket reste sans activité avant que le workflow activé puisse le fermer.', + 'repository.repositoryLocale': 'Choisissez la balise de langue BCP-47 des messages Copilot sur GitHub ; elle ne change pas la langue de cette page.', + 'repository.issueLocale': 'Changez la langue des messages GitHub pour les tickets ; laissez vide pour hériter de la langue du dépôt.', + 'repository.pullRequestLocale': 'Changez la langue des messages GitHub pour les pull requests ; laissez vide pour hériter de la langue du dépôt.', + 'repository.commitPrefixTransforms': 'Définissez les substitutions de préfixes de commit ; laissez vide si vos conventions n’en ont pas besoin.', + 'repository.releaseReconciliationStrategy': 'Choisissez comment les changements d’une version terminée reviennent dans le développement sans perdre leur filiation.', + 'repository.hotfixReconciliationStrategy': 'Choisissez comment un correctif urgent de production est reporté sur les branches en cours.', + 'repository.reconciliationPullRequestMode': 'Choisissez si les pull requests de réconciliation sont créées, fusionnées, mises en file ou laissées à une personne.', + 'repository.reconciliationBackmergeMode': 'Choisissez une fusion de retour directe ou passant par une branche de synchronisation.', + 'repository.hotfixActiveReleasePolicy': 'Choisissez où propager un correctif urgent lorsqu’une branche de version est déjà active.', + 'repository.reconciliationCleanup': 'Choisissez les branches temporaires à supprimer après une réconciliation réussie.', + 'repository.reconciliationIssueCompletion': 'Choisissez si le ticket à l’origine de la réconciliation se ferme ou reste ouvert pour suivi.', + 'repository.orchestrationPresentationMode': 'Choisissez le niveau de progression et de détail affiché dans le centre de contrôle GitHub des versions.', + 'repository.orchestrationDiagrams': 'Incluez des diagrammes Mermaid accessibles dans les informations de version.', + 'repository.orchestrationCommentMode': 'Choisissez si les commentaires de version sont mis à jour ou publiés à chaque étape importante.', + 'ai.pullRequestDescriptionMode': 'Choisissez si l’IA remplace, complète, préserve ou ne modifie jamais les descriptions des pull requests.', + 'ai.ignoreFiles': 'Indiquez les motifs de fichiers à exclure de la revue IA ; ces fichiers restent visibles sur GitHub.', + 'ai.membersOnly': 'N’autorisez le traitement IA que pour les demandes des membres du dépôt, pas pour tous les contributeurs externes.', + 'ai.bugbotSeverity': 'Fixez la gravité minimale publiée par Bugbot ; les résultats moins graves restent non publiés.', + 'ai.bugbotCommentLimit': 'Limitez les commentaires Bugbot par exécution pour ne pas submerger une pull request.', + 'ai.bugbotFixVerifyCommands': 'Indiquez les commandes qui doivent réussir avant qu’une correction automatique Bugbot soit considérée comme vérifiée.', + 'ai.bugbotEffort': 'Choisissez la profondeur des revues Bugbot ; un effort supérieur peut durer et consommer davantage.', + 'ai.bugbotReviewDrafts': 'Décidez si Bugbot analyse les pull requests en brouillon avant qu’elles soient prêtes.', + 'ai.bugbotTraceRules': 'Ajoutez l’origine de chaque règle de revue appliquée dans les résumés Bugbot pour faciliter l’audit.', + 'ai.bugbotSuggestedChanges': 'Autorisez Bugbot à joindre des suggestions de code sûres aux résultats publiés.', + 'ai.bugbotTelemetry': 'Enregistrez des métriques opérationnelles Bugbot sans stocker le contenu du dépôt.', + 'ai.bugbotFailOnUnresolved': 'Faites échouer la vérification Bugbot tant que des résultats exploitables restent ouverts.', + 'pullRequestApproval.mode': 'Choisissez si le bot recommande une approbation, peut approuver GitHub sous garde, ou n’intervient pas.', + 'pullRequestApproval.coverage.minDiffPercent': 'Définissez le pourcentage minimal de lignes modifiées couvertes qu’un rapporteur numérique fiable doit prouver.', + 'pullRequestApproval.coverage.artifactWorkflowName': 'Indiquez le workflow fiable exact qui publie l’artefact copilot-diff-coverage-v1.', + 'pullRequestApproval.coverage.reporterAttested': 'Confirmez avoir inspecté le rapporteur numérique dans ce workflow exact, et non seulement sa vérification verte.', + 'projects.enabled': 'Décidez si Copilot doit ajouter tickets et pull requests à des Projects existants ; le PAT servira ensuite à lister ceux de l’organisation.', + 'projects.ids': 'Choisissez des Projects existants par leur titre ou saisissez le numéro positif de leur URL ; les ID PVT_ ne conviennent pas.', + 'projects.statusVerified': 'Confirmez que les quatre options Status choisies existent dans chaque Project lorsque GitHub n’a pas pu vérifier leurs champs.', + createInitialTag: 'Créez v1.0.0 seulement si le dépôt n’a encore aucune étiquette de version.', + manageRepositoryVariables: 'Autorisez la création ou mise à jour des Variables GitHub Actions nécessaires aux workflows choisis.', + manageRepositorySecrets: 'Autorisez la validation et l’installation des Secrets GitHub Actions requis, dont le PAT du bot si nécessaire.', +}; diff --git a/src/application/policies/setup_question_purpose/pt.ts b/src/application/policies/setup_question_purpose/pt.ts new file mode 100644 index 000000000..a05caaa54 --- /dev/null +++ b/src/application/policies/setup_question_purpose/pt.ts @@ -0,0 +1,51 @@ +/** Portuguese question-specific purposes. */ +export const purposesPt: Readonly> = { + 'issueWorkflows.enabled': 'Escolha os fluxos de questões que o Copilot poderá executar; cada um afeta de forma diferente ramos, etiquetas e automatizações.', + 'repositoryAgentGuidance.enabled': 'Gere instruções para ajudar os agentes de IA a trabalhar com segurança neste projeto.', + 'repositoryAgentGuidance.agentsPointer': 'Decida se o AGENTS.md da raiz aponta para as instruções geradas, é criado se faltar ou permanece intacto.', + 'agents.configureIndependently': 'Defina fornecedores e modelos distintos para planeamento, resultados, revisão, correção e testes.', + 'repository.mainBranch': 'Indique o ramo de produção usado como referência por releases e hotfixes.', + 'repository.developmentBranch': 'Indique o ramo de integração habitual usado na criação de ramos e na reconciliação.', + 'repository.issueManagedBranches': 'Permita que a Action crie um ramo associado quando uma questão passa a estar em curso.', + 'repository.preBranchSdd': 'Exija um documento de desenho aprovado antes de determinados ramos de funcionalidade ou de alteração de contratos.', + 'repository.reopenIssueOnPush': 'Reabra uma questão concluída quando forem enviados novos commits para o ramo associado.', + 'repository.desiredAssigneesCount': 'Defina quantas pessoas o Copilot atribui a uma nova questão; zero desativa a atribuição automática.', + 'repository.desiredReviewersCount': 'Defina quantos revisores o Copilot solicita para uma pull request; zero desativa os pedidos automáticos.', + 'repository.inactivityThresholdHours': 'Defina quanto tempo uma questão fica sem atividade antes de o fluxo ativado a poder fechar.', + 'repository.repositoryLocale': 'Escolha a etiqueta BCP-47 das mensagens do Copilot no GitHub; não altera o idioma desta página.', + 'repository.issueLocale': 'Altere o idioma das mensagens GitHub para questões; deixe vazio para herdar o idioma do repositório.', + 'repository.pullRequestLocale': 'Altere o idioma das mensagens GitHub para pull requests; deixe vazio para herdar o idioma do repositório.', + 'repository.commitPrefixTransforms': 'Defina substituições dos prefixos dos commits; deixe vazio se as suas convenções não precisarem delas.', + 'repository.releaseReconciliationStrategy': 'Escolha como as alterações de uma release concluída regressam ao desenvolvimento sem perder a sua origem.', + 'repository.hotfixReconciliationStrategy': 'Escolha como um hotfix de produção é propagado para os ramos em curso.', + 'repository.reconciliationPullRequestMode': 'Escolha se as pull requests de reconciliação são criadas, integradas, colocadas em fila ou deixadas a uma pessoa.', + 'repository.reconciliationBackmergeMode': 'Escolha uma fusão de retorno direta ou através de um ramo de sincronização.', + 'repository.hotfixActiveReleasePolicy': 'Escolha para onde propagar um hotfix quando já existe um ramo de release ativo.', + 'repository.reconciliationCleanup': 'Escolha que ramos temporários serão eliminados após uma reconciliação bem-sucedida.', + 'repository.reconciliationIssueCompletion': 'Escolha se a questão que iniciou a reconciliação é fechada ou fica aberta para acompanhamento.', + 'repository.orchestrationPresentationMode': 'Escolha o nível de progresso e detalhe apresentado no centro de controlo GitHub das releases.', + 'repository.orchestrationDiagrams': 'Inclua diagramas Mermaid acessíveis na informação sobre releases.', + 'repository.orchestrationCommentMode': 'Escolha se os comentários da release são atualizados ou publicados em cada marco.', + 'ai.pullRequestDescriptionMode': 'Escolha se a IA substitui, acrescenta, preserva ou nunca altera as descrições das pull requests.', + 'ai.ignoreFiles': 'Indique padrões de ficheiros a excluir da revisão por IA; continuam visíveis no GitHub.', + 'ai.membersOnly': 'Permita o processamento por IA apenas para pedidos de membros do repositório, não de quaisquer colaboradores externos.', + 'ai.bugbotSeverity': 'Defina a gravidade mínima publicada pelo Bugbot; resultados menos graves não são publicados.', + 'ai.bugbotCommentLimit': 'Limite os comentários do Bugbot por execução para não sobrecarregar uma pull request.', + 'ai.bugbotFixVerifyCommands': 'Indique os comandos que têm de passar antes de uma correção automática do Bugbot ser considerada verificada.', + 'ai.bugbotEffort': 'Escolha a profundidade das revisões do Bugbot; mais esforço pode demorar e consumir mais recursos.', + 'ai.bugbotReviewDrafts': 'Decida se o Bugbot revê pull requests em rascunho antes de estarem prontas.', + 'ai.bugbotTraceRules': 'Inclua a origem de cada regra de revisão aplicada nos resumos do Bugbot para facilitar auditorias.', + 'ai.bugbotSuggestedChanges': 'Permita ao Bugbot anexar sugestões de código seguras aos resultados publicados.', + 'ai.bugbotTelemetry': 'Registe métricas operacionais do Bugbot sem guardar conteúdo do repositório.', + 'ai.bugbotFailOnUnresolved': 'Faça falhar a verificação do Bugbot enquanto existirem resultados acionáveis por resolver.', + 'pullRequestApproval.mode': 'Escolha se o bot recomenda aprovação, pode aprovar no GitHub sob condições ou não intervém.', + 'pullRequestApproval.coverage.minDiffPercent': 'Defina a percentagem mínima de linhas alteradas cobertas que um relatório numérico fiável tem de provar.', + 'pullRequestApproval.coverage.artifactWorkflowName': 'Indique o workflow fiável exato que publica o artefacto copilot-diff-coverage-v1.', + 'pullRequestApproval.coverage.reporterAttested': 'Confirme que inspecionou o relatório numérico nesse workflow exato, e não apenas uma verificação verde.', + 'projects.enabled': 'Decida se o Copilot deve adicionar questões e pull requests a Projects existentes; o PAT será usado depois para listar os da organização.', + 'projects.ids': 'Selecione Projects existentes pelo título ou introduza o número positivo do URL; IDs PVT_ não são usados.', + 'projects.statusVerified': 'Confirme que as quatro opções Status escolhidas existem em todos os Projects quando o GitHub não conseguiu verificar os campos.', + createInitialTag: 'Crie v1.0.0 apenas se o repositório ainda não tiver uma etiqueta de versão.', + manageRepositoryVariables: 'Permita criar ou atualizar as Variables do GitHub Actions necessárias aos fluxos escolhidos.', + manageRepositorySecrets: 'Permita validar e instalar os Secrets do GitHub Actions necessários, incluindo o PAT do bot quando aplicável.', +}; diff --git a/src/application/policies/setup_question_purpose_fr_pt.ts b/src/application/policies/setup_question_purpose_fr_pt.ts new file mode 100644 index 000000000..8e83d9a07 --- /dev/null +++ b/src/application/policies/setup_question_purpose_fr_pt.ts @@ -0,0 +1,51 @@ +import type { SetupQuestion } from '../../domain/setup_questionnaire'; +import { purposesFr } from './setup_question_purpose/fr'; +import { purposesPt } from './setup_question_purpose/pt'; + +export const purposesFrPt = { fr: purposesFr, pt: purposesPt } as const; + +export function setupQuestionPurposeFrPt(question: SetupQuestion, locale: 'fr' | 'pt'): string | undefined { + const exact = purposesFrPt[locale][question.id]; + if (exact) return exact; + if (question.id.startsWith('features.')) return locale === 'fr' + ? 'Activez ou désactivez cette fonction. Si vous la désactivez, cette configuration n’installera ni son automatisation ni ses autorisations conditionnelles.' + : 'Ative ou desative esta função. Se a desativar, esta configuração não instalará a automatização nem pedirá as permissões condicionais correspondentes.'; + if (/^agents\.[^.]+\.provider$/u.test(question.id)) return locale === 'fr' + ? 'Choisissez l’agent CLI de cette tâche dans GitHub Actions ; ce fournisseur détermine la commande et les identifiants du runner.' + : 'Escolha o agente CLI desta tarefa no GitHub Actions; o fornecedor determina o comando e as credenciais do runner.'; + const setting = question.id.match(/^agents\.[^.]+\.(modelProvider|model|effort|executable)$/u)?.[1]; + if (setting) { + const fields = { + fr: { modelProvider: 'le service fournissant le modèle et ses identifiants', model: 'le nom exact du modèle autorisé par le fournisseur', effort: 'l’effort de raisonnement (ou vide pour la valeur du fournisseur)', executable: 'la commande présente sur le runner GitHub Actions, pas sur cet ordinateur' }, + pt: { modelProvider: 'o serviço que fornece o modelo e as suas credenciais', model: 'o nome exato do modelo permitido pelo fornecedor', effort: 'o esforço de raciocínio (ou vazio para usar a predefinição do fornecedor)', executable: 'o comando disponível no runner GitHub Actions, não neste computador' }, + } as const; + const scope = question.stateId === 'agent-model-defaults' + ? (locale === 'fr' ? 'pour toutes les tâches actives' : 'para todas as tarefas ativas') + : (locale === 'fr' ? 'pour cette tâche' : 'para esta tarefa'); + return `${locale === 'fr' ? 'Définissez' : 'Defina'} ${fields[locale][setting as keyof typeof fields.fr]} ${scope}.`; + } + if (/^repository\.(feature|bugfix|hotfix|release|docs|chore|reconciliation)Tree$/u.test(question.id)) return locale === 'fr' + ? 'Définissez le préfixe des branches créées par Copilot pour ce type de travail ; il doit suivre votre convention de nommage.' + : 'Defina o prefixo dos ramos criados pelo Copilot para este tipo de trabalho; deve seguir as suas regras de nomes.'; + if (/^projects\.(issue|pullRequest)(Created|InProgress)Column$/u.test(question.id)) return locale === 'fr' + ? 'Choisissez la valeur du champ Status appliquée à la création ou au début du travail ; ce n’est pas le nom d’une colonne visuelle.' + : 'Escolha o valor do campo Status aplicado na criação ou no início do trabalho; não é o nome de uma coluna visual.'; + const storage = question.id.match(/^storage\.(variables|secrets)\.(defaultScope|organizationVisibility|preserveExisting|overrides)$/u); + if (storage) { + const resource = storage[1] === 'variables' ? 'Variables' : 'Secrets'; + const field = storage[2]; + if (field === 'defaultScope') return locale === 'fr' + ? `Choisissez si les nouvelles ${resource} sont stockées dans le dépôt ou l’organisation ; ce dernier périmètre peut exiger davantage d’autorisations du PAT.` + : `Escolha se as novas ${resource} ficam no repositório ou na organização; este último âmbito pode exigir mais permissões do PAT.`; + if (field === 'organizationVisibility') return locale === 'fr' + ? `Choisissez les dépôts pouvant utiliser les ${resource} de l’organisation ; « selected » est l’accès le plus restreint.` + : `Escolha os repositórios que podem usar as ${resource} da organização; «selected» é a visibilidade mais restrita.`; + if (field === 'preserveExisting') return locale === 'fr' + ? `Conservez les ${resource} existantes déjà applicables au lieu de les écraser pendant la configuration.` + : `Conserve as ${resource} existentes e aplicáveis em vez de as substituir durante a configuração.`; + return locale === 'fr' + ? `Sélectionnez les ${resource} héritées de l’organisation à définir plutôt dans le dépôt.` + : `Selecione as ${resource} herdadas da organização que pretende definir no repositório.`; + } + return undefined; +} diff --git a/src/application/policies/setup_question_purpose_policy.ts b/src/application/policies/setup_question_purpose_policy.ts new file mode 100644 index 000000000..f7fcf9ecc --- /dev/null +++ b/src/application/policies/setup_question_purpose_policy.ts @@ -0,0 +1,104 @@ +import type { SetupQuestion } from '../../domain/setup_questionnaire'; + +type Purpose = Readonly<{ en: string; es: string }>; + +/** Field-specific meaning for choices whose label alone is easy to misinterpret. */ +export const setupQuestionPurposes: Readonly> = { + 'issueWorkflows.enabled': { en: 'Choose the issue workflows Copilot may run; each type has different branch, label and automation effects.', es: 'Elige los flujos de issues que podrá ejecutar Copilot; cada tipo tiene efectos distintos sobre ramas, etiquetas y automatización.' }, + 'repositoryAgentGuidance.enabled': { en: 'Generate repository instructions that tell AI agents how to work safely in this project.', es: 'Genera instrucciones para que los agentes de IA trabajen con seguridad en este proyecto.' }, + 'repositoryAgentGuidance.agentsPointer': { en: 'Choose whether the root AGENTS.md points to generated instructions, is created if missing, or stays untouched.', es: 'Elige si el AGENTS.md raíz apunta a las instrucciones generadas, se crea si falta o permanece intacto.' }, + 'agents.configureIndependently': { en: 'Give planning, findings, review, fixing and testing separate provider and model settings instead of shared defaults.', es: 'Da a planificación, hallazgos, revisión, corrección y pruebas ajustes distintos de proveedor y modelo.' }, + 'repository.mainBranch': { en: 'Name the production branch; release and hotfix automation use it as their production reference.', es: 'Indica la rama de producción; las automatizaciones de release y hotfix la usan como referencia.' }, + 'repository.developmentBranch': { en: 'Name the normal integration branch; branch creation and reconciliation target it.', es: 'Indica la rama de integración habitual; la creación de ramas y la reconciliación la usan.' }, + 'repository.issueManagedBranches': { en: 'Allow the Action to create a linked branch when an issue enters an in-progress state.', es: 'Permite a la Action crear una rama vinculada cuando un issue pasa a «en curso».' }, + 'repository.preBranchSdd': { en: 'Require an approved design document before feature or contract-changing branches are created.', es: 'Exige un diseño aprobado antes de crear ramas de funcionalidad o cambios de contrato.' }, + 'repository.reopenIssueOnPush': { en: 'Reopen a completed issue when someone pushes more work to its linked branch.', es: 'Reabre un issue completado si alguien añade cambios a su rama vinculada.' }, + 'repository.desiredAssigneesCount': { en: 'Set how many people Copilot assigns to a new issue; zero disables automatic assignment.', es: 'Define cuántas personas asigna Copilot a un issue nuevo; cero desactiva la asignación automática.' }, + 'repository.desiredReviewersCount': { en: 'Set how many reviewers Copilot requests for a pull request; zero disables automatic requests.', es: 'Define cuántos revisores solicita Copilot para un pull request; cero desactiva la solicitud automática.' }, + 'repository.inactivityThresholdHours': { en: 'Set how long an issue waits without activity before the enabled inactivity workflow may close it.', es: 'Define cuánto tiempo espera sin actividad un issue antes de que el flujo habilitado pueda cerrarlo.' }, + 'repository.repositoryLocale': { en: 'Choose the BCP-47 language tag for Copilot messages on GitHub; this does not change the setup page language.', es: 'Elige la etiqueta BCP-47 de los mensajes de Copilot en GitHub; no cambia el idioma de esta página.' }, + 'repository.issueLocale': { en: 'Override the GitHub message language for issues; leave empty to inherit the repository language.', es: 'Cambia el idioma de los mensajes de issues; vacío hereda el idioma del repositorio.' }, + 'repository.pullRequestLocale': { en: 'Override the GitHub message language for pull requests; leave empty to inherit the repository language.', es: 'Cambia el idioma de los mensajes de pull requests; vacío hereda el idioma del repositorio.' }, + 'repository.commitPrefixTransforms': { en: 'Define commit-prefix rewrites used by commit automation; leave empty if your conventions need no mapping.', es: 'Define sustituciones de prefijos de commits; déjalo vacío si tus convenciones no necesitan cambios.' }, + 'repository.releaseReconciliationStrategy': { en: 'Choose how completed release changes return to development without losing production lineage.', es: 'Elige cómo vuelven los cambios de una release a desarrollo sin perder su relación con producción.' }, + 'repository.hotfixReconciliationStrategy': { en: 'Choose how an emergency production fix is carried back to ongoing branches.', es: 'Elige cómo se incorpora un arreglo urgente de producción a las demás ramas activas.' }, + 'repository.reconciliationPullRequestMode': { en: 'Choose whether reconciliation PRs are created, merged automatically, queued, or left for a human.', es: 'Elige si los PR de reconciliación se crean, fusionan automáticamente, encolan o quedan para una persona.' }, + 'repository.reconciliationBackmergeMode': { en: 'Choose whether the return merge is direct or goes through a synchronization branch.', es: 'Elige si la integración de vuelta es directa o pasa por una rama de sincronización.' }, + 'repository.hotfixActiveReleasePolicy': { en: 'Choose where a hotfix propagates when a release branch is already active.', es: 'Elige a dónde se propaga un hotfix si ya hay una rama de release activa.' }, + 'repository.reconciliationCleanup': { en: 'Choose which temporary branches are deleted after successful reconciliation.', es: 'Elige qué ramas temporales se eliminan tras una reconciliación correcta.' }, + 'repository.reconciliationIssueCompletion': { en: 'Choose whether the issue that launched reconciliation closes or stays open for follow-up.', es: 'Elige si el issue que inició la reconciliación se cierra o sigue abierto.' }, + 'repository.orchestrationPresentationMode': { en: 'Choose how much release progress and detail appears in the GitHub control-center view.', es: 'Elige cuánto progreso y detalle muestra el centro de control de releases en GitHub.' }, + 'repository.orchestrationDiagrams': { en: 'Include accessible Mermaid diagrams in release status information.', es: 'Incluye diagramas Mermaid accesibles en la información de releases.' }, + 'repository.orchestrationCommentMode': { en: 'Choose whether release lifecycle comments update in place or are posted at milestones.', es: 'Elige si los comentarios de la release se actualizan o se publican en cada hito.' }, + 'ai.pullRequestDescriptionMode': { en: 'Choose whether AI replaces, appends to, preserves, or never edits pull-request descriptions.', es: 'Elige si la IA sustituye, amplía, conserva o nunca modifica las descripciones de pull requests.' }, + 'ai.ignoreFiles': { en: 'List file patterns the AI review should skip; this does not hide those files on GitHub.', es: 'Indica patrones de archivos que la revisión con IA debe omitir; no los oculta en GitHub.' }, + 'ai.membersOnly': { en: 'Allow AI processing only for requests from repository members, not arbitrary external contributors.', es: 'Permite el procesamiento con IA solo para miembros del repositorio, no para colaboradores externos.' }, + 'ai.bugbotSeverity': { en: 'Set the lowest severity Bugbot publishes; lower-severity findings remain unpublished.', es: 'Define la gravedad mínima que publica Bugbot; los hallazgos menores no se publican.' }, + 'ai.bugbotCommentLimit': { en: 'Cap the number of Bugbot review comments in one run to avoid overwhelming a pull request.', es: 'Limita los comentarios de Bugbot por ejecución para no saturar un pull request.' }, + 'ai.bugbotFixVerifyCommands': { en: 'Specify commands that must pass before Bugbot considers an automatic fix verified.', es: 'Indica los comandos que deben pasar antes de considerar verificada una corrección de Bugbot.' }, + 'ai.bugbotEffort': { en: 'Choose the depth of Bugbot reviews; higher effort can take longer and use more model capacity.', es: 'Elige la profundidad de las revisiones de Bugbot; más esfuerzo puede tardar y consumir más.' }, + 'ai.bugbotReviewDrafts': { en: 'Decide whether Bugbot reviews draft pull requests before they are marked ready.', es: 'Decide si Bugbot revisa pull requests en borrador antes de que estén listos.' }, + 'ai.bugbotTraceRules': { en: 'Include the source of each applied review rule in Bugbot summaries for auditability.', es: 'Incluye la procedencia de las reglas aplicadas en los resúmenes de Bugbot para facilitar auditorías.' }, + 'ai.bugbotSuggestedChanges': { en: 'Allow Bugbot to attach safe inline code suggestions to published findings.', es: 'Permite a Bugbot adjuntar sugerencias de código seguras a los hallazgos publicados.' }, + 'ai.bugbotTelemetry': { en: 'Record operational Bugbot metrics without recording repository content.', es: 'Registra métricas operativas de Bugbot sin guardar contenido del repositorio.' }, + 'ai.bugbotFailOnUnresolved': { en: 'Make the Bugbot workflow check fail while actionable findings remain unresolved.', es: 'Hace fallar el check de Bugbot mientras queden hallazgos accionables sin resolver.' }, + 'pullRequestApproval.mode': { en: 'Choose whether the bot recommends approval, may submit a guarded GitHub approval, or does neither.', es: 'Elige si el bot recomienda aprobar, puede publicar una aprobación protegida o no interviene.' }, + 'pullRequestApproval.coverage.minDiffPercent': { en: 'Set the minimum percentage of changed lines that a trusted numeric reporter must prove are covered.', es: 'Define el porcentaje mínimo de líneas modificadas cubiertas que debe acreditar un reporter numérico fiable.' }, + 'pullRequestApproval.coverage.artifactWorkflowName': { en: 'Name the exact trusted workflow that publishes the copilot-diff-coverage-v1 artifact.', es: 'Indica el workflow fiable exacto que publica el artefacto copilot-diff-coverage-v1.' }, + 'pullRequestApproval.coverage.reporterAttested': { en: 'Confirm you inspected the numeric coverage reporter in that exact workflow, not just its green check.', es: 'Confirma que revisaste el reporter numérico en ese workflow exacto, no solo su check verde.' }, + 'projects.enabled': { en: 'Decide whether Copilot should add issues and pull requests to existing GitHub Projects; the setup PAT is needed to list private organization Projects later.', es: 'Decide si Copilot debe añadir issues y pull requests a Projects existentes; el PAT de setup hará falta después para consultar Projects privados de la organización.' }, + 'projects.ids': { en: 'Choose existing Projects by title after PAT verification, or enter the positive number in each Project URL; PVT_ node IDs are not used.', es: 'Elige Projects existentes por título tras verificar el PAT o introduce el número positivo de cada URL; no se usan IDs de nodo PVT_.' }, + 'projects.statusVerified': { en: 'Confirm that all four chosen Status options actually exist in every selected Project when GitHub could not verify their fields.', es: 'Confirma que las cuatro opciones Status existen en todos los Projects elegidos cuando GitHub no pudo comprobar sus campos.' }, + createInitialTag: { en: 'Create v1.0.0 only if this repository has no version tag yet.', es: 'Crea v1.0.0 solo si este repositorio todavía no tiene un tag de versión.' }, + manageRepositoryVariables: { en: 'Allow setup to create or update GitHub Actions Variables required by selected workflows.', es: 'Permite a setup crear o actualizar Variables de GitHub Actions necesarias para los workflows elegidos.' }, + manageRepositorySecrets: { en: 'Allow setup to validate and install required GitHub Actions Secrets, including the bot PAT when needed.', es: 'Permite a setup validar e instalar Secrets de GitHub Actions, incluido el PAT del bot cuando haga falta.' }, +}; + +export function setupQuestionPurpose(question: SetupQuestion): Purpose | undefined { + const exact = setupQuestionPurposes[question.id]; + if (exact) return exact; + if (question.id.startsWith('features.')) return { + en: `Enable or disable ${question.label.toLowerCase()}. Disabling it removes its automation and conditional permission needs from this setup.`, + es: 'Activa o desactiva esta función. Si la desactivas, setup no instalará su automatización ni solicitará sus permisos condicionales.', + }; + if (/^agents\.[^.]+\.provider$/u.test(question.id)) return { + en: 'Choose the agent CLI for this task in GitHub Actions; the provider determines the runner command and credentials.', + es: 'Elige el agente CLI de esta tarea en GitHub Actions; determina el comando y las credenciales del runner.', + }; + const agentSetting = question.id.match(/^agents\.[^.]+\.(modelProvider|model|effort|executable)$/u)?.[1]; + if (agentSetting) { + const shared = question.stateId === 'agent-model-defaults'; + const scope = shared ? { en: 'enabled agent tasks without a per-role override', es: 'las tareas activas del agente sin una excepción propia' } + : { en: 'this agent task', es: 'esta tarea del agente' }; + const setting: Record = { + modelProvider: { en: 'the service that supplies the model and its credentials', es: 'el servicio que proporciona el modelo y sus credenciales' }, + model: { en: 'the exact model name allowed by the selected provider', es: 'el nombre exacto del modelo permitido por el proveedor elegido' }, + effort: { en: 'the reasoning-effort level, or leave empty for the provider default', es: 'el nivel de razonamiento, o vacío para usar el valor del proveedor' }, + executable: { en: 'the executable available on the GitHub Actions runner, not this computer', es: 'el ejecutable disponible en el runner de GitHub Actions, no en este ordenador' }, + }; + return { + en: `Set ${setting[agentSetting].en} for ${scope.en}.`, + es: `Define ${setting[agentSetting].es} para ${scope.es}.`, + }; + } + if (/^repository\.(feature|bugfix|hotfix|release|docs|chore|reconciliation)Tree$/u.test(question.id)) return { + en: 'Set the prefix of branches Copilot creates for this work type; it must match your naming policy.', + es: 'Define el prefijo de las ramas que Copilot crea para este tipo de trabajo; debe seguir tus reglas de nombres.', + }; + if (/^projects\.(issue|pullRequest)(Created|InProgress)Column$/u.test(question.id)) return { + en: 'Choose the existing Status field option applied when this issue or pull request is created or enters progress; it is not a board-view column name.', + es: 'Elige la opción existente del campo Status al crear este issue o pull request o pasarlo a «en curso»; no es el nombre de una columna visual.', + }; + const storageSetting = question.id.match(/^storage\.(variables|secrets)\.(defaultScope|organizationVisibility|preserveExisting|overrides)$/u); + if (storageSetting) { + const resource = storageSetting[1] === 'variables' ? 'Variables' : 'Secrets'; + const setting: Record = { + defaultScope: { en: `Choose whether new ${resource} live in the repository or organization; organization storage can need extra PAT grants.`, es: `Elige si los ${resource} nuevos se guardan en el repositorio o la organización; este último ámbito puede exigir más permisos del PAT.` }, + organizationVisibility: { en: `Choose which repositories can use organization ${resource}; selected is the narrowest visibility.`, es: `Elige qué repositorios pueden usar los ${resource} de la organización; «selected» es la visibilidad más restringida.` }, + preserveExisting: { en: `Keep effective existing ${resource} instead of overwriting them during setup.`, es: `Conserva los ${resource} existentes que ya se aplican, en lugar de sobrescribirlos durante setup.` }, + overrides: { en: `Select inherited organization ${resource} that should instead be set at repository scope.`, es: `Selecciona los ${resource} heredados de la organización que quieras definir en el repositorio.` }, + }; + return setting[storageSetting[2]]; + } + return undefined; +} diff --git a/src/application/policies/setup_question_translations.ts b/src/application/policies/setup_question_translations.ts new file mode 100644 index 000000000..4d5526fe7 --- /dev/null +++ b/src/application/policies/setup_question_translations.ts @@ -0,0 +1,105 @@ +import type { SetupQuestion } from '../../domain/setup_questionnaire'; + +export const spanishQuestionLabels: Readonly> = { + 'features.issues': 'Automatizar issues: ramas, etiquetas, proyectos y ciclo de vida', + 'features.pullRequests': 'Automatizar pull requests: revisión, descripción y ciclo de vida', + 'features.commits': 'Automatizar commits: progreso, tamaño y análisis de Bugbot', + 'features.issueComments': 'Responder a comentarios de issues y permitir correcciones de Bugbot', + 'features.pullRequestComments': 'Responder a comentarios de pull requests y permitir correcciones de Bugbot', + 'features.agentProvisioning': 'Comprobar la instalación de agentes CLI en GitHub Actions', + 'features.credentialHealth': 'Comprobar el estado de las credenciales remotas', + 'features.inactiveIssueClosure': 'Cerrar issues sin actividad tras el plazo configurado', + 'features.issueTemplates': 'Instalar plantillas de issues', + 'features.pullRequestTemplate': 'Instalar plantilla de pull request', + 'issueWorkflows.enabled': 'Tipos de flujo de issues que quieres activar', + 'repositoryAgentGuidance.enabled': '¿Generar instrucciones para agentes en el repositorio?', + 'repositoryAgentGuidance.agentsPointer': 'Cómo descubrir las instrucciones desde AGENTS.md', + 'agents.findings.modelProvider': 'Proveedor de modelo compartido (salvo configuración propia de una tarea)', + 'agents.findings.model': 'Modelo compartido (salvo configuración propia de una tarea)', + 'agents.findings.effort': 'Esfuerzo de razonamiento compartido (los ajustes por tarea se conservan)', + 'agents.findings.executable': 'Ejecutable compartido validado (los ajustes por tarea se conservan)', + 'agents.configureIndependently': '¿Configurar modelo y comando por tarea?', + 'repository.mainBranch': 'Rama de producción', + 'repository.developmentBranch': 'Rama de desarrollo', + 'repository.featureTree': 'Prefijo de ramas de funcionalidad', + 'repository.bugfixTree': 'Prefijo de ramas de corrección', + 'repository.hotfixTree': 'Prefijo de ramas de hotfix', + 'repository.releaseTree': 'Prefijo de ramas de release', + 'repository.docsTree': 'Prefijo de ramas de documentación', + 'repository.choreTree': 'Prefijo de ramas de mantenimiento', + 'repository.issueManagedBranches': '¿Puede la Action crear ramas vinculadas a issues?', + 'repository.preBranchSdd': '¿Exigir un SDD antes de crear ciertas ramas?', + 'repository.reopenIssueOnPush': '¿Reabrir issues cerrados al actualizar su rama?', + 'repository.desiredAssigneesCount': 'Número deseado de personas asignadas a issues', + 'repository.desiredReviewersCount': 'Número deseado de revisores de pull requests', + 'repository.inactivityThresholdHours': 'Horas sin actividad antes de cerrar un issue en espera', + 'repository.repositoryLocale': 'Idioma de los mensajes del repositorio', + 'repository.issueLocale': 'Idioma de los issues (vacío: heredar)', + 'repository.pullRequestLocale': 'Idioma de los pull requests (vacío: heredar)', + 'repository.commitPrefixTransforms': 'Transformación de prefijos de commits', + 'repository.releaseReconciliationStrategy': 'Estrategia para reconciliar releases', + 'repository.hotfixReconciliationStrategy': 'Estrategia para reconciliar hotfixes', + 'repository.reconciliationPullRequestMode': 'Modo de pull requests de reconciliación', + 'repository.reconciliationBackmergeMode': 'Modo de integración de vuelta', + 'repository.hotfixActiveReleasePolicy': 'Destino del hotfix durante una release activa', + 'repository.reconciliationTree': 'Prefijo de ramas de reconciliación', + 'repository.reconciliationCleanup': 'Limpieza de ramas tras reconciliar', + 'repository.reconciliationIssueCompletion': 'Qué hacer con el issue al terminar', + 'repository.orchestrationPresentationMode': 'Nivel de detalle del centro de control de releases', + 'repository.orchestrationDiagrams': '¿Mostrar diagramas accesibles de releases?', + 'repository.orchestrationCommentMode': 'Cómo publicar comentarios del ciclo de release', + 'ai.pullRequestDescriptionMode': 'Cómo actualizar la descripción de los pull requests', + 'ai.ignoreFiles': 'Archivos que la IA debe ignorar', + 'ai.membersOnly': '¿Limitar el procesamiento de IA a miembros del repositorio?', + 'ai.includeReasoning': '¿Incluir el razonamiento adicional del proveedor?', + 'ai.bugbotSeverity': 'Gravedad mínima para publicar hallazgos de Bugbot', + 'ai.bugbotCommentLimit': 'Máximo de comentarios de Bugbot por ejecución', + 'ai.bugbotFixVerifyCommands': 'Comandos para verificar correcciones de Bugbot', + 'ai.bugbotDryRun': '¿Analizar sin publicar cambios de Bugbot?', + 'ai.bugbotEffort': 'Profundidad del análisis de Bugbot', + 'ai.bugbotReviewDrafts': '¿Revisar pull requests en borrador?', + 'ai.bugbotTraceRules': '¿Indicar qué fuentes de reglas se aplicaron?', + 'ai.bugbotSuggestedChanges': '¿Publicar sugerencias de cambio seguras?', + 'ai.bugbotTelemetry': '¿Registrar métricas de Bugbot sin contenido?', + 'ai.bugbotFailOnUnresolved': '¿Bloquear el check si quedan hallazgos sin resolver?', + 'ai.bugbotOrganizationRules': 'Reglas generales de Bugbot, una por línea', + 'ai.provisioningMode': 'Cómo preparar el agente CLI en el runner', + 'pullRequestApproval.mode': '¿Qué puede hacer el bot con las aprobaciones de PR?', + 'pullRequestApproval.testChecks': '¿Qué checks de CI son fiables para aprobar PRs?', + 'pullRequestApproval.producerAttested': '¿Has comprobado el job, la App y el paso obligatorio de cobertura?', + 'pullRequestApproval.coverage.mode': 'Cómo demostrar que se cumple la cobertura', + 'pullRequestApproval.coverage.checkName': 'Check fiable que exige la cobertura', + 'pullRequestApproval.coverage.minDiffPercent': 'Cobertura mínima de líneas modificadas (0–100)', + 'pullRequestApproval.coverage.artifactWorkflowName': 'Workflow que publica copilot-diff-coverage-v1', + 'pullRequestApproval.coverage.reporterAttested': '¿Has comprobado que el reporter numérico está instalado?', + 'projects.enabled': '¿Quieres integrar Projects de GitHub?', + 'projects.ids': 'Selecciona Projects existentes o indica los números de sus URL', + 'projects.statusVerified': '¿Has comprobado en GitHub los cuatro valores Status exactos de cada Project elegido?', + 'projects.issueCreatedColumn': 'Estado Status de nuevos issues', + 'projects.pullRequestCreatedColumn': 'Estado Status de nuevos pull requests', + 'projects.issueInProgressColumn': 'Estado Status de issues en curso', + 'projects.pullRequestInProgressColumn': 'Estado Status de pull requests en curso', + createInitialTag: '¿Crear v1.0.0 si todavía no existe ningún tag?', + manageRepositoryVariables: '¿Crear o actualizar Variables de GitHub Actions?', + manageRepositorySecrets: '¿Validar y configurar Secrets de GitHub Actions?', +}; + +const roleNames: Readonly> = { + planner: 'Planificación', findings: 'Hallazgos', reviewer: 'Revisión', fixer: 'Corrección', tester: 'Pruebas', +}; + +export function spanishQuestionLabel(question: SetupQuestion): string { + if (spanishQuestionLabels[question.id]) return spanishQuestionLabels[question.id]; + const agent = question.id.match(/^agents\.(planner|findings|reviewer|fixer|tester)\.(provider|modelProvider|model|effort|executable)$/u); + if (agent) { + const field: Record = { provider: 'agente CLI', modelProvider: 'proveedor del modelo', model: 'modelo', effort: 'esfuerzo', executable: 'comando ejecutable' }; + return `${roleNames[agent[1]]}: ${field[agent[2]]}`; + } + const storage = question.id.match(/^storage\.(variables|secrets)\.(defaultScope|organizationVisibility|preserveExisting|overrides)$/u); + if (storage) { + const resource = storage[1] === 'variables' ? 'Variables' : 'Secrets'; + const field: Record = { defaultScope: 'ámbito predeterminado', organizationVisibility: 'visibilidad en la organización', preserveExisting: 'conservar los existentes', overrides: 'excepciones de ámbito' }; + return `${resource}: ${field[storage[2]]}`; + } + return question.label; +} diff --git a/src/application/policies/setup_questionnaire_policy.ts b/src/application/policies/setup_questionnaire_policy.ts index cd5057fc6..e0d5d7db7 100644 --- a/src/application/policies/setup_questionnaire_policy.ts +++ b/src/application/policies/setup_questionnaire_policy.ts @@ -6,16 +6,18 @@ import type { SetupQuestionnaireEvent, SetupQuestionnaireState, SetupQuestionnaireStateId, + SetupQuestionnaireProgress, } from '../../domain/setup_questionnaire'; import { cloneSetupConfiguration } from './setup_configuration_clone_policy'; -import { SETUP_AGENT_TASKS, SETUP_FEATURE_DESCRIPTIONS } from './setup_configuration_defaults'; +import { createDefaultSetupConfiguration, SETUP_AGENT_TASKS, SETUP_FEATURE_DESCRIPTIONS } from './setup_configuration_defaults'; import { ISSUE_WORKFLOW_KINDS, ISSUE_WORKFLOW_CATALOG, createIssueWorkflowProfile, type IssueWorkflowKind } from '../../domain/issue_workflow_profile'; +import { parseSetupProjectSelection, sharedProjectStatusOptions } from './setup_project_selection_policy'; const AGENT_PROVIDERS = ['codex', 'opencode', 'cursor'] as const; const MODEL_PROVIDERS = ['openai', 'anthropic', 'google', 'openrouter', 'opencode', 'local'] as const; const PERMISSION_INTENT_QUESTION_IDS = new Set([ 'features.issues', 'features.pullRequests', 'issueWorkflows.enabled', - 'pullRequestApproval.mode', 'projects.ids', 'createInitialTag', + 'pullRequestApproval.mode', 'projects.enabled', 'createInitialTag', 'manageRepositoryVariables', 'manageRepositorySecrets', 'storage.variables.defaultScope', 'storage.variables.preserveExisting', 'storage.secrets.defaultScope', 'storage.secrets.preserveExisting', @@ -28,7 +30,7 @@ interface QuestionDefinition { readonly kind: SetupQuestion['kind']; readonly choices?: readonly string[]; readonly applies?: (draft: SetupConfiguration, independently: boolean, context: SetupQuestionnaireContext) => boolean; - readonly read?: (draft: SetupConfiguration) => string | number | boolean; + readonly read?: (draft: SetupConfiguration, context: SetupQuestionnaireContext) => string | number | boolean; } export function createSetupQuestionnaire( @@ -36,10 +38,17 @@ export function createSetupQuestionnaire( context: SetupQuestionnaireContext = {}, ): SetupQuestionnaireState { const draft = cloneSetupConfiguration(configuration); - const question = questions(draft, false, context, 'full')[0]; + const independently = hasIndependentAgentSettings(draft); + const question = questions(draft, independently, context, 'full')[0]; return question - ? { stateId: question.stateId, draft, question, terminal: 'collecting', configureIndependently: false, phase: 'full' } - : { stateId: 'review', draft, terminal: 'review', configureIndependently: false, phase: 'full' }; + ? { stateId: question.stateId, draft, question, terminal: 'collecting', configureIndependently: independently, phase: 'full' } + : { stateId: 'review', draft, terminal: 'review', configureIndependently: independently, phase: 'full' }; +} + +function hasIndependentAgentSettings(draft: SetupConfiguration): boolean { + const shared = draft.agents.findings; + return SETUP_AGENT_TASKS.filter(task => task !== 'findings').some(task => + (['modelProvider', 'model', 'effort', 'executable'] as const).some(field => draft.agents[task][field] !== shared[field])); } export function createSetupPermissionIntentQuestionnaire( @@ -47,10 +56,11 @@ export function createSetupPermissionIntentQuestionnaire( context: SetupQuestionnaireContext = {}, ): SetupQuestionnaireState { const draft = cloneSetupConfiguration(configuration); + const projectsWanted = context.projectsWanted ?? Boolean(draft.projects.ids.trim()); const question = questions(draft, false, context, 'permission-intent')[0]; return question - ? { stateId: question.stateId, draft, question, terminal: 'collecting', configureIndependently: false, phase: 'permission-intent', answeredQuestionIds: [] } - : { stateId: 'review', draft, terminal: 'review', configureIndependently: false, phase: 'permission-intent', answeredQuestionIds: [] }; + ? { stateId: question.stateId, draft, question, terminal: 'collecting', configureIndependently: false, phase: 'permission-intent', answeredQuestionIds: [], projectsWanted } + : { stateId: 'review', draft, terminal: 'review', configureIndependently: false, phase: 'permission-intent', answeredQuestionIds: [], projectsWanted }; } export function createSetupReviewState(configuration: SetupConfiguration): SetupQuestionnaireState { @@ -62,6 +72,84 @@ export function createSetupReviewState(configuration: SetupConfiguration): Setup }; } +/** Re-project the current question after a read-only discovery without replaying answers. */ +export function refreshSetupQuestionnaireQuestion( + state: SetupQuestionnaireState, + context: SetupQuestionnaireContext, +): SetupQuestionnaireState { + if (state.terminal !== 'collecting' || !state.question) return state; + const question = questions(state.draft, state.configureIndependently, context, state.phase ?? 'full') + .find(candidate => candidate.id === state.question?.id); + return question ? { ...state, question, validation: undefined } : state; +} + +/** The denominator follows the currently applicable, unskipped questions. */ +export function setupQuestionnaireProgress( + state: SetupQuestionnaireState, + context: SetupQuestionnaireContext, +): SetupQuestionnaireProgress | undefined { + if (state.terminal !== 'collecting' || !state.question) return undefined; + const visible = questions(state.draft, state.configureIndependently, context, state.phase ?? 'full'); + const index = visible.findIndex(item => item.id === state.question?.id); + if (index < 0) return undefined; + const group = state.question.stateId; + const groupQuestions = visible.filter(item => item.stateId === group); + return { position: index + 1, total: visible.length, groupPosition: groupQuestions.findIndex(item => item.id === state.question?.id) + 1, + groupTotal: groupQuestions.length, group }; +} + +/** Reopen an already answered group for final-plan correction without clearing unrelated values. */ +export function reopenSetupQuestionnaireGroup( + state: SetupQuestionnaireState, + group: SetupQuestion['stateId'], + context: SetupQuestionnaireContext, +): SetupQuestionnaireState | undefined { + if (state.terminal !== 'review') return undefined; + const first = questions(state.draft, state.configureIndependently, context, 'full').find(item => item.stateId === group); + return first ? { ...state, stateId: first.stateId, terminal: 'collecting', question: first, validation: undefined, + phase: 'full', answeredQuestionIds: [] } : undefined; +} + +export function setupQuestionIdsForGroup(group: SetupQuestion['stateId']): readonly string[] { + return definitions().filter(item => item.stateId === group).map(item => item.id); +} + +/** Basic changes presentation only: security- and permission-driving decisions stay visible. */ +export function setupBasicSkippedQuestionIds(configuration?: SetupConfiguration): readonly string[] { + const defaults = configuration ? createDefaultSetupConfiguration() : undefined; + const advancedRepository = new Set([ + 'featureTree', 'bugfixTree', 'hotfixTree', 'releaseTree', 'docsTree', 'choreTree', + 'reconciliationTree', 'reopenIssueOnPush', 'inactivityThresholdHours', + 'issueLocale', 'pullRequestLocale', 'commitPrefixTransforms', + ]); + const advancedBugbot = new Set([ + 'pullRequestDescriptionMode', 'ignoreFiles', 'includeReasoning', 'bugbotCommentLimit', + 'bugbotFixVerifyCommands', 'bugbotEffort', 'bugbotReviewDrafts', 'bugbotTraceRules', + 'bugbotSuggestedChanges', 'bugbotOrganizationRules', + ]); + return definitions().filter(definition => + definition.id === 'agents.findings.effort' + || definition.id === 'agents.findings.executable' + || (definition.id.startsWith('agents.') && definition.id.endsWith('.provider') && definition.id !== 'agents.findings.provider') + || (definition.id.startsWith('repository.') && advancedRepository.has(definition.id.slice('repository.'.length))) + || (definition.id.startsWith('ai.') && advancedBugbot.has(definition.id.slice('ai.'.length))) + ).filter(definition => !configuration || JSON.stringify(valueAtPath(configuration, definition.id)) + === JSON.stringify(valueAtPath(defaults!, definition.id)) + ).map(definition => definition.id); +} + +function valueAtPath(value: unknown, path: string): unknown { + return path.split('.').reduce((current, key) => current && typeof current === 'object' + ? (current as Record)[key] : undefined, value); +} + +export function setupEditableGroups(configuration: SetupConfiguration): readonly SetupQuestion['stateId'][] { + // Projects can be enabled at review even if the operator declined it before + // the PAT handoff. The re-run audits any newly required grant before Apply. + const visible = questions(configuration, hasIndependentAgentSettings(configuration), { projectsWanted: true }, 'full'); + return [...new Set(visible.map(item => item.stateId))]; +} + export function transitionSetupQuestionnaire( state: SetupQuestionnaireState, event: SetupQuestionnaireEvent, @@ -76,8 +164,24 @@ export function transitionSetupQuestionnaire( configureIndependently: state.configureIndependently, phase: state.phase, answeredQuestionIds: state.answeredQuestionIds, + projectsWanted: state.projectsWanted, }; } + if (event.kind === 'back') { + const visible = questions(state.draft, state.configureIndependently, context, state.phase ?? 'full'); + const index = visible.findIndex(item => item.id === state.question?.id); + if (index <= 0) return { ...state, validation: 'This is the first question in this pass. Review it or cancel setup.' }; + const previous = visible[index - 1]; + return { ...state, stateId: previous.stateId, question: previous, validation: undefined, + answeredQuestionIds: state.answeredQuestionIds?.filter(id => visible.findIndex(item => item.id === id) < index - 1) }; + } + if (state.question.id === 'projects.statusVerified' && ['n', 'no', 'false', '0'].includes(event.value.normalize('NFKC').trim().toLowerCase())) { + const selection = questions(state.draft, state.configureIndependently, context, state.phase ?? 'full') + .find(question => question.id === 'projects.ids'); + if (selection) return { ...state, question: selection, stateId: 'projects', + validation: 'Status values were not confirmed. Choose compatible Projects, then review their Status options again.', + answeredQuestionIds: state.answeredQuestionIds?.filter(id => id !== 'projects.ids' && !id.startsWith('projects.')) }; + } const parsed = parseAnswer(state.question, event.value); if ('error' in parsed) { return { @@ -89,11 +193,16 @@ export function transitionSetupQuestionnaire( const configureIndependently = state.question.id === 'agents.configureIndependently' ? Boolean(parsed.value) : state.configureIndependently; - const draft = applyAnswer(state.draft, state.question, parsed.value); + const draft = applyAnswer(state.draft, state.question, parsed.value, state.configureIndependently); + const projectsWanted = state.question.id === 'projects.enabled' ? Boolean(parsed.value) : state.projectsWanted; const answeredQuestionIds = [...(state.answeredQuestionIds ?? []), state.question.id]; const nextQuestions = questions(draft, configureIndependently, context, state.phase ?? 'full'); - const nextIndex = nextQuestions.findIndex((question) => question.id === state.question?.id); - const next = nextQuestions[nextIndex + 1]; + // A just-answered question may become inapplicable (for example, clearing + // Projects removes its dependent fields). Advance by canonical definition + // order; indexing the new visible list at -1 would restart the wizard. + const definitionOrder = definitions().map(definition => definition.id); + const currentOrder = definitionOrder.indexOf(state.question.id); + const next = nextQuestions.find(question => definitionOrder.indexOf(question.id) > currentOrder); return next ? { stateId: next.stateId, @@ -103,8 +212,9 @@ export function transitionSetupQuestionnaire( configureIndependently, phase: state.phase, answeredQuestionIds, + projectsWanted, } - : { stateId: 'review', draft, terminal: 'review', configureIndependently, phase: state.phase, answeredQuestionIds }; + : { stateId: 'review', draft, terminal: 'review', configureIndependently, phase: state.phase, answeredQuestionIds, projectsWanted }; } export function enterSetupConfirmation(state: SetupQuestionnaireState): SetupQuestionnaireState { @@ -152,7 +262,7 @@ function questions( phase: 'full' | 'permission-intent', ): SetupQuestion[] { return definitions().filter((definition) => - (phase === 'full' || PERMISSION_INTENT_QUESTION_IDS.has(definition.id)) + (phase === 'full' ? definition.id !== 'projects.enabled' : PERMISSION_INTENT_QUESTION_IDS.has(definition.id)) && !context.skipQuestionIds?.includes(definition.id) && (definition.applies?.(draft, independently, context) ?? true)) .map((definition) => toQuestion(definition, draft, context)); @@ -191,20 +301,30 @@ function definitions(): readonly QuestionDefinition[] { ...SETUP_AGENT_TASKS.map((task): QuestionDefinition => ({ stateId: 'agent-runtime', id: `agents.${task}.provider`, label: `${formatTask(task)} runtime`, kind: 'choice', choices: AGENT_PROVIDERS, })), - { stateId: 'agent-model-defaults', id: 'agents.findings.modelProvider', label: 'Model provider for all tasks', kind: 'choice', choices: MODEL_PROVIDERS }, - { stateId: 'agent-model-defaults', id: 'agents.findings.model', label: 'Model name for all tasks', kind: 'text' }, - { stateId: 'agent-model-defaults', id: 'agents.findings.effort', label: 'Reasoning effort for all tasks (empty uses provider default)', kind: 'text' }, - { stateId: 'agent-model-defaults', id: 'agents.findings.executable', label: 'Validated executable for all tasks (empty uses the manifest basename)', kind: 'text' }, - { stateId: 'agent-model-defaults', id: 'agents.configureIndependently', label: 'Configure model provider, model, effort, and executable independently for every task?', kind: 'boolean', read: () => false }, + { stateId: 'agent-model-defaults', id: 'agents.findings.modelProvider', label: 'Shared model provider (unless a role has its own setting)', kind: 'choice', choices: MODEL_PROVIDERS }, + { stateId: 'agent-model-defaults', id: 'agents.findings.model', label: 'Shared model name (unless a role has its own setting)', kind: 'text' }, + { stateId: 'agent-model-defaults', id: 'agents.findings.effort', label: 'Shared reasoning effort (empty uses provider default; per-role overrides stay separate)', kind: 'text' }, + { stateId: 'agent-model-defaults', id: 'agents.findings.executable', label: 'Shared validated executable (empty uses manifest basename; per-role overrides stay separate)', kind: 'text' }, + { stateId: 'agent-model-defaults', id: 'agents.configureIndependently', label: 'Configure model provider, model, effort, and executable independently for every task?', kind: 'boolean', read: draft => hasIndependentAgentSettings(draft) }, ...SETUP_AGENT_TASKS.filter((task) => task !== 'findings').flatMap((task) => agentOverrideQuestions(task)), ...repositoryQuestions(), ...deploymentQuestions(), ...bugbotQuestions(), ...approvalQuestions(), - { stateId: 'projects', id: 'projects.ids', label: 'GitHub Project IDs (comma-separated, empty skips integration)', kind: 'text' }, + { stateId: 'projects', id: 'projects.enabled', label: 'Integrate existing GitHub Projects with issue and pull-request automation?', kind: 'boolean', + read: (draft, context) => context.projectsWanted ?? Boolean(draft.projects.ids.trim()), + applies: draft => draft.features.issues !== false || draft.features.pullRequests !== false }, + { stateId: 'projects', id: 'projects.ids', label: 'Select existing GitHub Projects (or enter Project numbers from their URLs)', kind: 'text', + applies: (draft, _independent, context) => (draft.features.issues !== false || draft.features.pullRequests !== false) && context.projectsWanted !== false }, ...['issueCreatedColumn', 'pullRequestCreatedColumn', 'issueInProgressColumn', 'pullRequestInProgressColumn'].map((field): QuestionDefinition => ({ stateId: 'projects', id: `projects.${field}`, label: projectLabel(field), kind: 'text', applies: (config) => Boolean(config.projects.ids.trim()), })), + { stateId: 'projects', id: 'projects.statusVerified', + label: 'Have you checked every selected Project in GitHub and confirmed all four exact Status values?', + kind: 'boolean', read: () => false, + applies: (draft, _independently, context) => Boolean(draft.projects.ids.trim()) + && sharedProjectStatusOptions(draft.projects.ids, context.projectDiscovery?.candidates ?? []).state === 'unavailable', + }, { stateId: 'provisioning', id: 'createInitialTag', label: 'Create v1.0.0 when no version tag exists?', kind: 'boolean' }, { stateId: 'provisioning', id: 'manageRepositoryVariables', label: 'Create/update GitHub Actions Variables?', kind: 'boolean' }, { stateId: 'provisioning', id: 'manageRepositorySecrets', label: 'Validate and provision required GitHub Actions Secrets?', kind: 'boolean' }, @@ -213,6 +333,13 @@ function definitions(): readonly QuestionDefinition[] { ]; } +/** Stable content inventory for documentation and localization audits; never answers questions. */ +export function setupQuestionContentInventory(): readonly SetupQuestion[] { + return definitions().map(({ stateId, id, label, kind, choices }) => ({ + stateId, id, label, kind, choices, defaultValue: '', + })); +} + function agentOverrideQuestions(task: AgentTask): QuestionDefinition[] { const applies = (_draft: SetupConfiguration, independently: boolean) => independently; return [ @@ -298,12 +425,6 @@ function approvalQuestions(): QuestionDefinition[] { read: draft => draft.pullRequestApproval.testChecks.map(check => `${check.name}|${check.sourceAppId}|${check.workflowName}`).join(';'), applies: draft => draft.features.pullRequests !== false && draft.pullRequestApproval.mode !== 'off', }, - { - stateId: 'pull-request-approval', id: 'pullRequestApproval.producerAttested', - label: 'Have you verified each exact check, source App ID, workflow, and coverage-enforcing CI step?', - kind: 'boolean', - applies: draft => draft.features.pullRequests !== false && draft.pullRequestApproval.mode !== 'off', - }, { stateId: 'pull-request-approval', id: 'pullRequestApproval.coverage.mode', label: 'Coverage evidence mode', kind: 'choice', choices: ['check', 'numeric'], @@ -312,7 +433,7 @@ function approvalQuestions(): QuestionDefinition[] { { stateId: 'pull-request-approval', id: 'pullRequestApproval.coverage.checkName', label: 'Exact trusted check that enforces the coverage budget (no inferred percentage)', - kind: 'text', + kind: 'choice', applies: draft => draft.features.pullRequests !== false && draft.pullRequestApproval.mode !== 'off', }, { @@ -339,6 +460,12 @@ function approvalQuestions(): QuestionDefinition[] { applies: draft => draft.features.pullRequests !== false && draft.pullRequestApproval.mode !== 'off' && draft.pullRequestApproval.coverage.mode === 'numeric', }, + { + stateId: 'pull-request-approval', id: 'pullRequestApproval.producerAttested', + label: 'Have you verified each exact check, source App ID, workflow, and coverage-enforcing CI step?', + kind: 'boolean', + applies: draft => draft.features.pullRequests !== false && draft.pullRequestApproval.mode !== 'off', + }, ]; } @@ -369,17 +496,56 @@ function choice(field: string, label: string, choices: readonly string[]): Quest } function toQuestion(definition: QuestionDefinition, draft: SetupConfiguration, context: SetupQuestionnaireContext): SetupQuestion { + const branchScopedCandidates = context.approvalCheckCandidates?.map(candidate => + candidate.requiredByRuleset?.branch !== draft.repository.developmentBranch + ? { ...candidate, requiredByRuleset: undefined } : candidate); + const producerCandidates = definition.id === 'pullRequestApproval.testChecks' ? branchScopedCandidates + : definition.id === 'pullRequestApproval.coverage.checkName' ? branchScopedCandidates?.filter(candidate => + draft.pullRequestApproval.testChecks.some(check => check.name === candidate.name + && check.sourceAppId === candidate.sourceAppId && check.workflowName === candidate.workflowName)) : undefined; + const coverageChoices = definition.id === 'pullRequestApproval.coverage.checkName' + ? [...new Set(draft.pullRequestApproval.testChecks.map(check => check.name))] : undefined; const allowedNames = definition.kind === 'scope-overrides' ? inheritedNames(definition.id.includes('.variables.') ? 'variables' : 'secrets', draft, context) : undefined; + const projectQuestion = definition.id === 'projects.ids'; + const statusQuestion = /^projects\.(issue|pullRequest)(Created|InProgress)Column$/u.test(definition.id); + const projectCandidates = context.projectDiscovery?.candidates ?? []; + const projectStatus = statusQuestion + ? sharedProjectStatusOptions(draft.projects.ids, projectCandidates) : undefined; return { stateId: definition.stateId, id: definition.id, label: definition.label, - kind: definition.kind, - defaultValue: definition.read?.(draft) ?? readPath(draft, definition.id, allowedNames), - ...(definition.choices ? { choices: definition.choices } : {}), + kind: definition.id === 'pullRequestApproval.testChecks' ? 'producer-select' + : projectQuestion ? 'project-select' + : statusQuestion && projectStatus?.state === 'observed' ? 'choice' : definition.kind, + defaultValue: definition.read?.(draft, context) ?? readPath(draft, definition.id, allowedNames), + ...(coverageChoices ? { choices: coverageChoices } : projectStatus?.state === 'observed' + ? { choices: projectStatus.options } : definition.choices ? { choices: definition.choices } : {}), ...(allowedNames ? { allowedNames } : {}), + ...(producerCandidates?.length ? { producerCandidates } : {}), + ...(definition.id === 'pullRequestApproval.coverage.checkName' + ? { trustedProducers: draft.pullRequestApproval.testChecks } : {}), + ...(definition.id === 'pullRequestApproval.testChecks' && context.approvalCheckDiscoveryStatus + ? { discoveryStatus: context.approvalCheckDiscoveryStatus, discoveryTruncated: context.approvalCheckDiscoveryTruncated, + discoveryRetryRemaining: context.discoveryRetryRemaining?.checks ?? 0 } : {}), + ...(projectQuestion ? { discoveryStatus: context.projectDiscovery?.status ?? 'unavailable', + discoveryTruncated: context.projectDiscovery?.truncated, + ...(context.projectDiscovery && context.projectDiscovery.status !== 'unsupported' && context.discoveryRetryRemaining + ? { discoveryRetryRemaining: context.discoveryRetryRemaining.projects } : {}), + projectCandidates, projectOwner: context.projectOwner } : {}), + ...(statusQuestion && projectStatus ? { statusOptionState: projectStatus.state } : {}), + ...(definition.id === 'projects.statusVerified' ? { projectStatusValues: [ + { transition: 'issueCreated' as const, value: draft.projects.issueCreatedColumn }, + { transition: 'pullRequestCreated' as const, value: draft.projects.pullRequestCreatedColumn }, + { transition: 'issueInProgress' as const, value: draft.projects.issueInProgressColumn }, + { transition: 'pullRequestInProgress' as const, value: draft.projects.pullRequestInProgressColumn }, + ] } : {}), + ...(definition.id === 'repository.mainBranch' && context.branchSources + ? { suggestionSource: context.branchSources.main } : {}), + ...(definition.id === 'repository.developmentBranch' && context.branchSources + ? { suggestionSource: context.branchSources.development } : {}), }; } @@ -399,6 +565,34 @@ function readPath( function parseAnswer(question: SetupQuestion, raw: string): { value: string | number | boolean | Record } | { error: string } { const input = raw.normalize('NFKC').trim(); + if (question.id === 'projects.statusVerified') return ['y', 'yes', 'true', '1'].includes(input.toLowerCase()) + ? { value: true } : { error: 'Open every selected Project in GitHub and confirm that all four exact Status values exist. Answer Yes after checking, or No to choose Projects again.' }; + if (question.id === 'projects.ids') { + const parsed = parseSetupProjectSelection(input || String(question.defaultValue), question.projectOwner); + if ('error' in parsed) return parsed; + const status = sharedProjectStatusOptions(parsed.value, question.projectCandidates ?? []); + if (status.state === 'incompatible') return { error: 'Selected Projects have no common Status option. Choose compatible Projects or configure them separately.' }; + return parsed; + } + if (question.id === 'pullRequestApproval.testChecks') { + const entries = (input || String(question.defaultValue)).split(';').map(item => item.trim()).filter(Boolean) + .flatMap(item => item.split(',').map(value => value.trim()).filter(Boolean)) + .map(item => { + const index = Number(item) - 1; + const candidate = Number.isSafeInteger(index) && /^[1-9]\d*$/u.test(item) ? question.producerCandidates?.[index] : undefined; + return candidate ? `${candidate.name}|${candidate.sourceAppId}|${candidate.workflowName}` : item; + }); + if (entries.length < 1 || entries.length > 8 || entries.some(entry => !/^[^|;\r\n]{1,100}\|[1-9][0-9]*\|[^|;\r\n]{1,100}$/u.test(entry))) { + return { error: 'Select 1–8 observed checks or enter exact name|App ID|workflow tuples.' }; + } + if (new Set(entries).size !== entries.length) return { error: 'A trusted check was selected more than once.' }; + const names = entries.map(entry => entry.split('|', 1)[0]); + if (new Set(names).size !== names.length) return { error: 'Two trusted producers use the same check name. Coverage stores only one name; choose one producer or rename the CI jobs before continuing.' }; + return { value: entries.join(';') }; + } + if (!input && question.statusOptionState === 'observed' && !question.choices?.includes(String(question.defaultValue))) { + return { error: 'The saved Status value is not available in every selected Project. Choose a listed Status option.' }; + } if (!input && question.kind !== 'scope-overrides') return { value: question.defaultValue }; if (question.kind === 'text') return { value: input }; if (question.kind === 'number') { @@ -438,9 +632,22 @@ function applyAnswer( configuration: SetupConfiguration, question: SetupQuestion, value: string | number | boolean | Record, + independently: boolean, ): SetupConfiguration { const draft = cloneSetupConfiguration(configuration); - if (question.id === 'agents.configureIndependently') return draft; + if (question.id === 'agents.configureIndependently') { + if (!value) for (const task of SETUP_AGENT_TASKS.filter(task => task !== 'findings')) { + draft.agents[task] = { ...draft.agents[task], modelProvider: draft.agents.findings.modelProvider, + model: draft.agents.findings.model, effort: draft.agents.findings.effort, + executable: draft.agents.findings.executable }; + } + return draft; + } + if (question.id === 'projects.enabled') { + if (!value) draft.projects.ids = ''; + return draft; + } + if (question.id === 'projects.statusVerified') return draft; if (question.id === 'features.issues' && value === false) { draft.features.issues = false; draft.features.release = false; @@ -488,7 +695,9 @@ function applyAnswer( } if (['agents.findings.modelProvider', 'agents.findings.model', 'agents.findings.effort', 'agents.findings.executable'].includes(question.id)) { const field = question.id.split('.')[2] as 'modelProvider' | 'model' | 'effort' | 'executable'; - for (const task of SETUP_AGENT_TASKS) draft.agents[task] = { ...draft.agents[task], [field]: value as string }; + for (const task of independently ? (['findings'] as const) : SETUP_AGENT_TASKS) { + draft.agents[task] = { ...draft.agents[task], [field]: value as string }; + } return draft; } const parts = question.id.split('.'); diff --git a/src/application/policies/setup_token_permission_policy.ts b/src/application/policies/setup_token_permission_policy.ts index 5d083d61c..3c22e732c 100644 --- a/src/application/policies/setup_token_permission_policy.ts +++ b/src/application/policies/setup_token_permission_policy.ts @@ -39,20 +39,24 @@ const requirement = (input: PermissionInput): SetupTokenPermissionRequirement => * interactive configuration does not exist before the setup PAT prompt. */ export function buildSetupPatPermissionRequirements(): SetupTokenPermissionRequirement[] { - return normalizePermissionRequirements([ + // Keep conditional read and write paths separate here: collapsing Actions + // into one write row would hide the approval-only read requirement. + return [ requirement({ role: 'setup', scope: 'repository', permission: 'Metadata', level: 'read', reason: 'Resolve repository identity and visibility.', probe: 'metadata' }), requirement({ role: 'setup', scope: 'repository', permission: 'Contents', level: 'read', reason: 'Inspect installed workflows and repository files.', probe: 'contents' }), requirement({ role: 'setup', scope: 'repository', permission: 'Secrets', level: 'write', applicability: 'conditional', condition: 'Secret provisioning enabled', reason: 'Inspect and provision selected GitHub Actions Secrets.', probe: 'secrets' }), requirement({ role: 'setup', scope: 'repository', permission: 'Variables', level: 'write', applicability: 'conditional', condition: 'Variable provisioning enabled', reason: 'Inspect and provision selected GitHub Actions Variables.', probe: 'variables' }), requirement({ role: 'setup', scope: 'repository', permission: 'Issues', level: 'write', applicability: 'conditional', condition: 'Issue workflows enabled', reason: 'Provision labels and issue resources.', probe: 'issues' }), requirement({ role: 'setup', scope: 'repository', permission: 'Actions', level: 'write', applicability: 'conditional', condition: 'Credential health enabled', reason: 'Inspect and dispatch credential-health workflows.', probe: 'actions' }), + requirement({ role: 'setup', scope: 'repository', permission: 'Actions', level: 'read', applicability: 'conditional', condition: 'Pull-request approval enabled', reason: 'Inspect CI workflow runs and jobs for exact producer identities.', probe: 'actions' }), + requirement({ role: 'setup', scope: 'repository', permission: 'Checks', level: 'read', applicability: 'conditional', condition: 'Pull-request approval enabled', reason: 'Discover exact CI check and producer identities.', probe: 'checks' }), requirement({ role: 'setup', scope: 'repository', permission: 'Administration', level: 'read', applicability: 'conditional', condition: 'Release, hotfix, or guarded approval enabled', reason: 'Inspect branch protection and rulesets.', probe: 'administration' }), requirement({ role: 'setup', scope: 'repository', permission: 'Workflows', level: 'write', applicability: 'conditional', condition: 'Temporary health workflow required', reason: 'Bootstrap a missing credential-health workflow.', probe: 'workflows' }), requirement({ role: 'setup', scope: 'organization', permission: 'Secrets', level: 'write', applicability: 'conditional', condition: 'Organization Secret storage selected', reason: 'Inspect and provision organization Actions Secrets.', probe: 'secrets' }), requirement({ role: 'setup', scope: 'organization', permission: 'Variables', level: 'write', applicability: 'conditional', condition: 'Organization Variable storage selected', reason: 'Inspect and provision organization Actions Variables.', probe: 'variables' }), requirement({ role: 'setup', scope: 'organization', permission: 'Issue Types', level: 'write', applicability: 'conditional', condition: 'Issue type automation enabled', reason: 'Provision and assign configured issue types.', probe: 'issue-types' }), - requirement({ role: 'setup', scope: 'organization', permission: 'Projects', level: 'write', applicability: 'conditional', condition: 'Organization Projects selected', reason: 'Inspect and configure selected Projects.', probe: 'projects' }), - ]); + requirement({ role: 'setup', scope: 'organization', permission: 'Projects', level: 'read', applicability: 'conditional', condition: 'Organization Projects selected', reason: 'Inspect selected Projects and their Status options; setup does not edit Project items.', probe: 'projects' }), + ]; } /** @@ -74,8 +78,9 @@ export function buildConfiguredSetupPatPermissionRequirements( export function buildSetupPatIntentPermissionRequirements( configuration: Readonly, ownerKind: 'Organization' | 'User', + projectsWanted = configuration.projects.ids.trim().length > 0, ): SetupTokenPermissionRequirement[] { - return buildSetupPatRequirements(configuration, ownerKind === 'Organization'); + return buildSetupPatRequirements(configuration, ownerKind === 'Organization', undefined, projectsWanted); } export function buildSetupPatIntentUncertainty(configuration: Readonly, ownerKind: 'Organization' | 'User'): string[] { @@ -111,6 +116,7 @@ function buildSetupPatRequirements( configuration: Readonly, organization: boolean, remote?: Readonly, + projectsWanted = configuration.projects.ids.trim().length > 0, ): SetupTokenPermissionRequirement[] { const repositorySecretNames = buildSetupCredentialRequirements(configuration) .map(credential => credential.name); @@ -128,6 +134,7 @@ function buildSetupPatRequirements( || configuration.features.hotfix || enabledIssueWorkflowKinds.some(kind => kind === 'release' || kind === 'hotfix'); const guardedApproval = configuration.pullRequestApproval.mode === 'guarded'; + const approvalEnabled = configuration.pullRequestApproval.mode !== 'off'; const hasExistingCredential = repositorySecretNames.some(name => remote?.repositorySecrets.includes(name) || remote?.organizationSecrets.includes(name), ); @@ -158,6 +165,10 @@ function buildSetupPatRequirements( role: 'setup', scope: 'repository', permission: 'Actions', level: 'write', reason: 'Dispatch credential-health checks for existing Secrets.', probe: 'actions', })] : []), + ...(approvalEnabled ? [ + requirement({ role: 'setup', scope: 'repository', permission: 'Actions', level: 'read', reason: 'Inspect CI workflow runs and jobs for approval evidence.', probe: 'actions' }), + requirement({ role: 'setup', scope: 'repository', permission: 'Checks', level: 'read', reason: 'Discover exact CI check and producer identities.', probe: 'checks' }), + ] : []), ...(needsCredentialHealthBootstrap ? [ requirement({ role: 'setup', scope: 'repository', permission: 'Contents', level: 'write', reason: 'Temporarily install credential health when its workflow is not confirmed installed.', probe: 'contents' }), requirement({ role: 'setup', scope: 'repository', permission: 'Workflows', level: 'write', reason: 'Temporarily install credential health when its workflow is not confirmed installed.', probe: 'workflows' }), @@ -178,9 +189,9 @@ function buildSetupPatRequirements( role: 'setup', scope: 'organization', permission: 'Issue Types', level: 'write', reason: 'Provision native issue types for the selected workflows.', probe: 'issue-types', })] : []), - ...(organization && configuration.projects.ids.trim().length > 0 ? [requirement({ - role: 'setup', scope: 'organization', permission: 'Projects', level: 'write', - reason: 'Inspect and configure the selected organization Projects.', probe: 'projects', + ...(organization && projectsWanted ? [requirement({ + role: 'setup', scope: 'organization', permission: 'Projects', level: 'read', + reason: 'Inspect selected Projects and their Status options; setup does not edit Project items.', probe: 'projects', })] : []), ]); } diff --git a/src/application/ports/setup_approval_check_discovery_port.ts b/src/application/ports/setup_approval_check_discovery_port.ts new file mode 100644 index 000000000..ef61a3f70 --- /dev/null +++ b/src/application/ports/setup_approval_check_discovery_port.ts @@ -0,0 +1,5 @@ +import type { SetupApprovalCheckCandidate, SetupDiscoveryResult } from '../../domain/setup_questionnaire'; + +export interface SetupApprovalCheckDiscoveryPort { + discover(owner: string, repository: string, token: string, targetBranch?: string): Promise>; +} diff --git a/src/application/ports/setup_project_discovery_port.ts b/src/application/ports/setup_project_discovery_port.ts new file mode 100644 index 000000000..3832a57c5 --- /dev/null +++ b/src/application/ports/setup_project_discovery_port.ts @@ -0,0 +1,6 @@ +import type { SetupDiscoveryResult, SetupProjectCandidate } from '../../domain/setup_questionnaire'; + +/** Read-only GitHub Project inventory for setup; the PAT never crosses into a browser view. */ +export interface SetupProjectDiscoveryPort { + discover(owner: string, ownerType: 'Organization' | 'User' | 'Unknown', token: string): Promise>; +} diff --git a/src/application/ports/setup_terminal_ports.ts b/src/application/ports/setup_terminal_ports.ts index f4dbe4265..e35b60551 100644 --- a/src/application/ports/setup_terminal_ports.ts +++ b/src/application/ports/setup_terminal_ports.ts @@ -4,6 +4,7 @@ import type { SetupQuestionnaireContext, SetupQuestionnaireState, SetupQuestionnaireStateId, + SetupQuestionnaireProgress, } from '../../domain/setup_questionnaire'; export type TerminalReadResult = @@ -16,7 +17,7 @@ export interface TerminalDriver { isInteractive(): boolean; readText(prompt: string): Promise; /** Optional raw-mode selector. Drivers without it fall back to text parsing. */ - readMultiSelect?(prompt: string, choices: readonly string[], selected: readonly string[]): Promise; + readMultiSelect?(prompt: string, choices: readonly string[], selected: readonly string[], helpText?: string): Promise; readSecret(prompt: string): Promise; close(): void; } @@ -24,7 +25,9 @@ export interface TerminalDriver { export interface SetupQuestionRenderer { showIntroduction(): void; showState(stateId: SetupQuestionnaireStateId): void; - renderPrompt(question: SetupQuestion): string; + renderPrompt(question: SetupQuestion, progress?: SetupQuestionnaireProgress): string; + renderHelp(question: SetupQuestion): string; + showHelp(question: SetupQuestion): void; showValidation(message: string): void; showCancelled(): void; } @@ -33,9 +36,15 @@ export interface SetupConfigurationCollectorPort { collect( initial: SetupQuestionnaireState, context: SetupQuestionnaireContext, + discoveryRefresh?: SetupDiscoveryRefreshPort, ): Promise; } +/** Read-only, explicitly requested discovery; undefined means the retry budget is exhausted. */ +export interface SetupDiscoveryRefreshPort { + refresh(kind: 'checks' | 'projects'): Promise; +} + export interface SetupPlanPresenterPort { present(plan: SetupPlan): void; } @@ -45,5 +54,6 @@ export interface SetupPlanConfirmationPort { | { readonly kind: 'approved' } | { readonly kind: 'declined' } | { readonly kind: 'cancelled' } + | { readonly kind: 'revise'; readonly group: SetupQuestion['stateId'] } >; } diff --git a/src/application/usecases/actions/__tests__/initial_setup_use_case.test.ts b/src/application/usecases/actions/__tests__/initial_setup_use_case.test.ts index 5e7db0bd8..c60bbee00 100644 --- a/src/application/usecases/actions/__tests__/initial_setup_use_case.test.ts +++ b/src/application/usecases/actions/__tests__/initial_setup_use_case.test.ts @@ -1,5 +1,5 @@ import { InitialSetupUseCase } from '../initial_setup_use_case'; -import { Result } from '../../../../data/model/result'; +import { Result, getResultPayload } from '../../../../data/model/result'; import type { Execution } from '../../../../data/model/execution'; import { createDefaultSetupConfiguration } from '../../../policies/setup_configuration_policy'; import { projectInitialSetupContext } from '../../push_single_action_contexts'; @@ -140,6 +140,8 @@ describe('InitialSetupUseCase', () => { ); expect(mockSetupHasValidToken).toHaveBeenCalledTimes(1); expect(mockSetupPrepare).not.toHaveBeenCalled(); + expect(getResultPayload(getResultPayload(results[0].payload)?.setupReceipt)?.effects) + .toEqual(expect.arrayContaining([{ id: 'files', state: 'not-started', scope: 'local' }])); } finally { mockSetupHasValidToken.mockReturnValue(true); } @@ -170,6 +172,26 @@ describe('InitialSetupUseCase', () => { param.labels, ); expect(results[0].steps?.some((s) => s.includes('Issue types'))).toBe(true); + expect(getResultPayload(getResultPayload(results[0].payload)?.setupReceipt)?.effects).toEqual([ + { id: 'files', state: 'completed', scope: 'local' }, + { id: 'secrets', state: 'skipped', scope: 'repository' }, + { id: 'labels', state: 'skipped', scope: 'repository' }, + { id: 'issue-types', state: 'skipped', scope: 'repository' }, + { id: 'variables', state: 'skipped', scope: 'repository' }, + { id: 'initial-tag', state: 'skipped', scope: 'repository' }, + ]); + }); + + it('marks a failed local write as needing inspection and later resources as not started', async () => { + mockSetupPrepare.mockImplementationOnce(() => { throw new Error('write may have happened'); }); + const results = await useCase.invoke(baseParam()); + const effects = getResultPayload(getResultPayload(results[0].payload)?.setupReceipt)?.effects; + expect(results[0].success).toBe(false); + expect(effects).toEqual(expect.arrayContaining([ + { id: 'files', state: 'needs-inspection', scope: 'local' }, + { id: 'secrets', state: 'not-started', scope: 'repository' }, + { id: 'labels', state: 'not-started', scope: 'repository' }, + ])); }); it('creates default tag v1.0.0 when no version tags exist', async () => { @@ -197,6 +219,9 @@ describe('InitialSetupUseCase', () => { expect.arrayContaining([{ name: 'AGENT_PROVIDER', value: 'codex' }]), ); expect(results[0].steps).toContain('⏭️ Initial version tag creation disabled by setup configuration.'); + expect(getResultPayload(getResultPayload(results[0].payload)?.setupReceipt)?.effects) + .toEqual(expect.arrayContaining([{ id: 'initial-tag', state: 'skipped', scope: 'repository' }, + { id: 'variables', state: 'completed', scope: 'repository' }])); }); it('provisions Variables at organization scope when the configuration selects it', async () => { diff --git a/src/application/usecases/actions/initial_setup_workflow.ts b/src/application/usecases/actions/initial_setup_workflow.ts index 3e3d8dee4..7bfa3ff3b 100644 --- a/src/application/usecases/actions/initial_setup_workflow.ts +++ b/src/application/usecases/actions/initial_setup_workflow.ts @@ -11,7 +11,7 @@ import type { BoundSetupWorkspacePort } from '../../ports/setup_workspace_ports' import { DEFAULT_INITIAL_TAG } from '../../../data/model/version_policy'; import { logDebugInfo, logError, logInfo } from '../../ports/logging_ports'; import { getTaskEmoji } from '../../../utils/task_emoji'; -import type { SetupConfiguration } from '../../../domain/setup'; +import type { SetupConfiguration, SetupOperationEffect } from '../../../domain/setup'; import type { SetupResourceProvisioningDependencies } from './setup_resource_provisioning'; import type { InitialSetupContext } from '../push_single_action_contexts'; import { @@ -52,19 +52,33 @@ export async function runInitialSetupWorkflow( logInfo(`${getTaskEmoji(TASK_ID)} Executing ${TASK_ID}.`); const steps: string[] = []; const errors: ApplicationError[] = []; + const configuration = request.setupConfiguration; + const effects: SetupOperationEffect[] = [ + { id: 'files', state: 'not-started', scope: 'local' }, + { id: 'secrets', state: 'not-started', scope: resourceScope(configuration, 'secrets') }, + { id: 'labels', state: 'not-started', scope: 'repository' }, + { id: 'issue-types', state: 'not-started', scope: 'repository' }, + { id: 'variables', state: 'not-started', scope: resourceScope(configuration, 'variables') }, + { id: 'initial-tag', state: 'not-started', scope: 'repository' }, + ]; + const mark = (id: SetupOperationEffect['id'], state: SetupOperationEffect['state']) => { + const index = effects.findIndex(effect => effect.id === id); + effects[index] = { ...effects[index], state }; + }; + const receipt = () => buildResult(errors, steps, effects); try { const setupConfiguration = request.setupConfiguration; if (!dependencies.setupWorkspacePort.hasValidToken()) { logInfo(' 🛑 Setup requires the setup PAT provided for this command with a valid token.'); errors.push(new ApplicationError('authorization.credential-invalid', 'A valid setup PAT must be provided to run setup. It is separate from the workflow PAT Secret.')); - return [buildResult(errors, steps)]; + return [receipt()]; } logInfo('🔐 Checking GitHub access...'); const githubAccess = await verifyGitHubAccess(request, dependencies.authenticatedUserPort); if (!githubAccess.success) { errors.push(...githubAccess.errors); - return [buildResult(errors, steps)]; + return [receipt()]; } steps.push(`✅ GitHub access verified: ${githubAccess.user}`); @@ -81,7 +95,7 @@ export async function runInitialSetupWorkflow( if (remoteConfigurationErrors.length === 0) { errors.push(new ApplicationError('provider.unavailable', 'Could not inspect existing GitHub Actions resource scopes. Restore inventory access and rerun setup.')); } - return [buildResult(errors, steps)]; + return [receipt()]; } const inventoryErrors = [ ...validateSetupStorageAgainstRemote(setupConfiguration, remoteConfiguration), @@ -92,7 +106,7 @@ export async function runInitialSetupWorkflow( ]; if (inventoryErrors.length > 0) { errors.push(...fromMessages(inventoryErrors, 'provider.unavailable')); - return [buildResult(errors, steps)]; + return [receipt()]; } } @@ -105,15 +119,24 @@ export async function runInitialSetupWorkflow( approvedWorkflowFiles: request.workflowUpdates, } : {}), }; + mark('files', 'needs-inspection'); const filesResult = dependencies.setupWorkspacePort.prepare(workspaceSelection); + mark('files', filesResult.copied > 0 ? 'completed' : 'skipped'); steps.push(`✅ Setup files: ${filesResult.copied} copied, ${filesResult.skipped} already existed`); + const secretValues = Number(Boolean(request.setupCredentials?.workflowPat)) + (request.setupCredentials?.apiKeys.length ?? 0); + if (setupConfiguration?.manageRepositorySecrets && secretValues > 0) mark('secrets', 'needs-inspection'); const secrets = await ensureRepositorySecrets(request, dependencies, setupConfiguration, remoteConfiguration); + mark('secrets', secrets.errors.length ? 'needs-inspection' + : setupConfiguration?.manageRepositorySecrets && secretValues > 0 ? 'completed' : 'skipped'); if (secrets.step) steps.push(secrets.step); if (secrets.errors.length > 0) errors.push(...fromMessages(secrets.errors, 'authorization.credential-invalid')); logInfo('🏷️ Checking configured and progress labels...'); + mark('labels', 'needs-inspection'); const labels = await ensureInitialLabels(request, dependencies.initialLabelProvisioningPort, setupConfiguration); + mark('labels', !labels.completed || labels.configured.errors.length || labels.progress.errors.length + ? 'needs-inspection' : labels.configured.created + labels.progress.created > 0 ? 'completed' : 'skipped'); if (!labels.completed) { errors.push(labels.error); } else { @@ -122,26 +145,34 @@ export async function runInitialSetupWorkflow( } logInfo('📋 Checking issue types...'); + mark('issue-types', 'needs-inspection'); const issueTypes = await ensureIssueTypes(request, dependencies.issueTypeProvisioningPort, setupConfiguration); + mark('issue-types', !issueTypes.success ? 'needs-inspection' : issueTypes.created > 0 ? 'completed' : 'skipped'); if (!issueTypes.success) { errors.push(...fromMessages(issueTypes.errors, 'provider.unavailable')); } else { steps.push(`✅ Issue types checked: ${issueTypes.created} created, ${issueTypes.existing} already existed`); } + if (setupConfiguration?.manageRepositoryVariables) mark('variables', 'needs-inspection'); const variables = await ensureRepositoryVariables(request, dependencies, setupConfiguration, remoteConfiguration); + mark('variables', variables.errors.length ? 'needs-inspection' + : setupConfiguration?.manageRepositoryVariables ? 'completed' : 'skipped'); if (variables.step) steps.push(variables.step); if (variables.errors.length > 0) errors.push(...fromMessages(variables.errors, 'provider.unavailable')); + if (setupConfiguration?.createInitialTag !== false) mark('initial-tag', 'needs-inspection'); const defaultVersion = await ensureDefaultVersion(request, dependencies, setupConfiguration); + mark('initial-tag', defaultVersion.error ? 'needs-inspection' + : defaultVersion.step?.includes('created on branch') ? 'completed' : 'skipped'); if (defaultVersion.step) steps.push(defaultVersion.step); if (defaultVersion.error) errors.push(defaultVersion.error); - return [buildResult(errors, steps)]; + return [receipt()]; } catch (error) { const semanticError = toApplicationError(error, 'workflow.failed', 'Error running initial setup.'); logError(semanticError); errors.push(semanticError); - return [buildResult(errors, steps)]; + return [receipt()]; } } @@ -249,16 +280,23 @@ function appendLabelSummary( } } -function buildResult(errors: ApplicationError[], steps: string[]): Result { +function buildResult(errors: ApplicationError[], steps: string[], effects: readonly SetupOperationEffect[]): Result { return new Result({ id: TASK_ID, success: errors.length === 0, executed: true, steps, + payload: { setupReceipt: { version: 1, effects: effects.map(effect => ({ ...effect })) } }, errors: errors.length > 0 ? errors : undefined, }); } +function resourceScope(configuration: SetupConfiguration | undefined, kind: 'secrets' | 'variables'): SetupOperationEffect['scope'] { + const policy = configuration?.storage[kind]; + if (!policy) return 'repository'; + return Object.values(policy.overrides).some(scope => scope !== policy.defaultScope) ? 'mixed' : policy.defaultScope; +} + function fromMessages(messages: readonly string[], code: ApplicationErrorCode): ApplicationError[] { return messages.map(message => new ApplicationError(code, message)); } diff --git a/src/application/usecases/setup/__tests__/doctor_use_case.test.ts b/src/application/usecases/setup/__tests__/doctor_use_case.test.ts index 64a5f658f..85d5add2f 100644 --- a/src/application/usecases/setup/__tests__/doctor_use_case.test.ts +++ b/src/application/usecases/setup/__tests__/doctor_use_case.test.ts @@ -86,6 +86,17 @@ describe('SetupDoctorUseCase', () => { expect(report.totals.fail).toBe(0); }); + it('read-only mode never dispatches credential-health Actions and marks Secret values unverified', async () => { + const configuration = createDefaultSetupConfiguration(); + const deps = dependencies(configuration); + const { report } = await new SetupDoctorUseCase(deps).execute({ ...request(configuration), readOnly: true }); + expect(deps.remoteHealth.validateExisting).not.toHaveBeenCalled(); + expect(deps.remoteConfiguration.inspect).toHaveBeenCalledTimes(1); + expect(report.checks).toEqual(expect.arrayContaining([ + expect.objectContaining({ id: 'credential.pat', status: 'warn', evidence: expect.objectContaining({ present: true }) }), + ])); + }); + it('uses repository locale for the whole report and reuses its catalog in merge readiness', async () => { const configuration = createDefaultSetupConfiguration(); configuration.repository.repositoryLocale = 'es-ES'; diff --git a/src/application/usecases/setup/__tests__/prepare_setup_pat_intent_use_case.test.ts b/src/application/usecases/setup/__tests__/prepare_setup_pat_intent_use_case.test.ts index 82b7d52c7..e56d3426b 100644 --- a/src/application/usecases/setup/__tests__/prepare_setup_pat_intent_use_case.test.ts +++ b/src/application/usecases/setup/__tests__/prepare_setup_pat_intent_use_case.test.ts @@ -47,6 +47,20 @@ describe('PrepareSetupPatIntentUseCase', () => { expect(ports.showDetails).toHaveBeenCalledWith(expect.arrayContaining([expect.objectContaining({ role: 'setup' })])); }); + test('previews Projects read from intent before any Project number can be discovered', async () => { + const { ports, useCase } = harness(); + jest.spyOn(ports, 'collect').mockImplementation(async initial => ({ + ...initial, terminal: 'review', question: undefined, projectsWanted: true, + draft: { ...initial.draft, projects: { ...initial.draft.projects, ids: '' } }, + })); + const result = await useCase.execute(request); + expect(result.kind).toBe('guided'); + if (result.kind !== 'guided') return; + expect(result.url).toContain('organization_projects=read'); + expect(result.permissionIntent.projectsWanted).toBe(true); + expect(result.permissionIntent.draft.projects.ids).toBe(''); + }); + test('revisiting choices re-collects with the next pass and retains one session', async () => { const { ports, useCase } = harness(); jest.spyOn(ports, 'review').mockResolvedValueOnce('revise').mockResolvedValueOnce('continue'); diff --git a/src/application/usecases/setup/__tests__/setup_questionnaire_controller.test.ts b/src/application/usecases/setup/__tests__/setup_questionnaire_controller.test.ts index 25ff9fb3c..dad97db85 100644 --- a/src/application/usecases/setup/__tests__/setup_questionnaire_controller.test.ts +++ b/src/application/usecases/setup/__tests__/setup_questionnaire_controller.test.ts @@ -1,5 +1,5 @@ import { SetupQuestionnaireController } from '../setup_questionnaire_controller'; -import { createSetupQuestionnaire } from '../../../policies/setup_questionnaire_policy'; +import { createSetupQuestionnaire, setupQuestionContentInventory } from '../../../policies/setup_questionnaire_policy'; import { createDefaultSetupConfiguration } from '../../../policies/setup_configuration_policy'; import type { SetupQuestionRenderer, TerminalDriver, TerminalReadResult } from '../../../ports/setup_terminal_ports'; @@ -8,6 +8,8 @@ function renderer(): jest.Mocked { showIntroduction: jest.fn(), showState: jest.fn(), renderPrompt: jest.fn((question) => `${question.id}: `), + renderHelp: jest.fn((question) => `Help for ${question.id}`), + showHelp: jest.fn(), showValidation: jest.fn(), showCancelled: jest.fn(), }; @@ -58,6 +60,17 @@ describe('SetupQuestionnaireController', () => { expect(input.readText.mock.calls[0][0]).toBe(input.readText.mock.calls[1][0]); }); + it('shows question help and repeats the same unanswered question without mutating the draft', async () => { + const output = renderer(); + const input = terminal([{ kind: 'value', value: '?' }, { kind: 'value', value: '' }]); + const initial = createSetupQuestionnaire(createDefaultSetupConfiguration()); + const result = await new SetupQuestionnaireController(input, output).collect(initial, {}); + expect(result.terminal).toBe('review'); + expect(output.showHelp).toHaveBeenCalledWith(initial.question); + expect(input.readText.mock.calls[0][0]).toBe(input.readText.mock.calls[1][0]); + expect(initial.answeredQuestionIds).toBeUndefined(); + }); + it.each([ ['Ctrl-C', { kind: 'cancel' } as const], ['EOF', { kind: 'end-of-input' } as const], @@ -83,4 +96,25 @@ describe('SetupQuestionnaireController', () => { )).rejects.toThrow('requires an interactive terminal'); expect(input.readText).not.toHaveBeenCalled(); }); + + it('retries Project discovery in the CLI without advancing or replaying prior questions', async () => { + const context = { skipQuestionIds: setupQuestionContentInventory().map(item => item.id).filter(id => id !== 'projects.ids'), + projectOwner: 'owner', projectDiscovery: { status: 'unavailable' as const, candidates: [] }, + discoveryRetryRemaining: { checks: 0, projects: 1 } }; + const initial = createSetupQuestionnaire(createDefaultSetupConfiguration(), context); + expect(initial.question?.id).toBe('projects.ids'); + const input = { ...terminal([]), readMultiSelect: jest.fn() + .mockResolvedValueOnce({ kind: 'value', value: 'retry' }) + .mockResolvedValueOnce({ kind: 'value', value: 'none' }) }; + const refresh = jest.fn(async () => ({ ...context, + projectDiscovery: { status: 'observed' as const, candidates: [{ number: 5, title: 'Roadmap', owner: 'owner', + url: 'https://github.com/orgs/owner/projects/5' }] }, + discoveryRetryRemaining: { checks: 0, projects: 0 } })); + const result = await new SetupQuestionnaireController(input, renderer()).collect(initial, context, { refresh }); + expect(result.terminal).toBe('review'); + expect(result.draft.projects.ids).toBe(''); + expect(refresh).toHaveBeenCalledWith('projects'); + expect(input.readMultiSelect).toHaveBeenCalledTimes(2); + expect(input.readMultiSelect.mock.calls[1][1]).toEqual(expect.arrayContaining([expect.stringContaining('Roadmap')])); + }); }); diff --git a/src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts b/src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts index b71b30f79..277127fb0 100644 --- a/src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts +++ b/src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts @@ -4,7 +4,8 @@ import { buildSetupRepositoryVariables, createDefaultSetupConfiguration, } from '../../../policies/setup_configuration_policy'; -import { createSetupReviewState } from '../../../policies/setup_questionnaire_policy'; +import { createSetupReviewState, setupQuestionContentInventory } from '../../../policies/setup_questionnaire_policy'; +import type { SetupQuestionnaireContext, SetupQuestionnaireState } from '../../../../domain/setup_questionnaire'; const remote = { ownerType: 'Organization' as const, @@ -31,6 +32,45 @@ function dependencies(overrides: Record = {}) { } describe('SetupWizardUseCase', () => { + it('suggests an observed GitHub default branch without overriding an explicit configured branch', async () => { + const collect = jest.fn(async (state: SetupQuestionnaireState, _context: SetupQuestionnaireContext) => createSetupReviewState(state.draft)); + const deps = dependencies({ collector: { collect }, remoteConfiguration: { inspect: jest.fn().mockResolvedValue({ ...remote, defaultBranch: 'main' }) } }); + const request = { mode: 'interactive' as const, remoteTarget: { owner: 'acme', repository: 'repo', token: 'setup-token' }, + overrides: { pullRequestApproval: { mode: 'off' as const } } }; + await new SetupWizardUseCase(deps).execute(request); + expect(collect.mock.calls[0][0].question?.id).toBe('features.issues'); + expect(collect.mock.calls[0][0].draft.repository.mainBranch).toBe('main'); + expect(collect.mock.calls[0][1].branchSources?.main).toBe('github'); + expect(collect.mock.calls[0][1].branchSources?.development).toBe('default'); + collect.mockClear(); + await new SetupWizardUseCase(deps).execute({ ...request, developmentBranchObservedLocally: true }); + expect(collect.mock.calls[0][1].branchSources?.development).toBe('local'); + collect.mockClear(); + await new SetupWizardUseCase(deps).execute({ ...request, overrides: { ...request.overrides, repository: { mainBranch: 'production' } } }); + expect(collect.mock.calls[0][0].draft.repository.mainBranch).toBe('production'); + expect(collect.mock.calls[0][1].branchSources?.main).toBe('configuration'); + }); + it('re-enters a chosen plan section, retains other answers and rebuilds the plan before approval', async () => { + let pass = 0; + const collect = jest.fn(async (state, _context) => { + pass += 1; + return pass === 1 + ? { ...createSetupReviewState(state.draft), answeredQuestionIds: setupQuestionContentInventory().map(item => item.id) } + : createSetupReviewState({ ...state.draft, repository: { ...state.draft.repository, mainBranch: 'main' } }); + }); + const confirmation = { confirm: jest.fn().mockResolvedValueOnce({ kind: 'revise', group: 'repository' }) + .mockResolvedValueOnce({ kind: 'approved' }) }; + const deps = dependencies({ collector: { collect }, confirmation }); + const result = await new SetupWizardUseCase(deps).execute({ mode: 'interactive', + overrides: { pullRequestApproval: { mode: 'off' } } }); + expect(result.status).toBe('completed'); + if (result.status === 'completed') expect(result.configuration.repository.mainBranch).toBe('main'); + expect(collect).toHaveBeenCalledTimes(2); + expect(collect.mock.calls[1][0].question?.id).toBe('repository.mainBranch'); + expect(collect.mock.calls[1][1].skipQuestionIds).toContain('features.issues'); + expect(confirmation.confirm).toHaveBeenCalledTimes(2); + expect(deps.planPresenter.present).toHaveBeenCalledTimes(2); + }); it('starts the main questionnaire from reviewed permission intent and skips its answered questions', async () => { const draft = buildInitialSetupConfiguration({ mode: 'interactive', overrides: { pullRequestApproval: { mode: 'off' } } }); draft.createInitialTag = false; @@ -49,6 +89,19 @@ describe('SetupWizardUseCase', () => { expect(collect.mock.calls[0][1].skipQuestionIds).toEqual(['createInitialTag', 'manageRepositorySecrets', 'features.issues']); }); + it('lets interactive setup repair legacy duplicate check names before final validation', async () => { + const producer = { name: 'Tests', sourceAppId: 12, workflowName: 'CI' }; + const collect = jest.fn(async state => createSetupReviewState({ ...state.draft, + pullRequestApproval: { ...state.draft.pullRequestApproval, testChecks: [producer] }, + })); + const result = await new SetupWizardUseCase(dependencies({ collector: { collect } })).execute({ + mode: 'interactive', overrides: { pullRequestApproval: { mode: 'recommend', coverage: { mode: 'check', checkName: 'Tests' }, + testChecks: [producer, { name: 'Tests', sourceAppId: 13, workflowName: 'Other CI' }] } }, + }); + expect(collect).toHaveBeenCalledTimes(1); + expect(result.status).toBe('completed'); + }); + it('requires an explicit exact CI producer in non-interactive guarded setup', async () => { await expect(new SetupWizardUseCase(dependencies()).execute({ mode: 'non-interactive' })) .rejects.toThrow('guarded/recommend mode requires 1–8 exact test checks'); @@ -194,7 +247,66 @@ describe('SetupWizardUseCase', () => { remote, variableNames: buildSetupRepositoryVariables(createDefaultSetupConfiguration()).map((item) => item.name), secretNames: buildSetupCredentialRequirements(createDefaultSetupConfiguration()).map((item) => item.name), - })); + }), expect.objectContaining({ refresh: expect.any(Function) })); + }); + + it('discovers Projects with the entered setup PAT only after intent and passes the inventory to the questionnaire', async () => { + const events: string[] = []; + const draft = buildInitialSetupConfiguration({ mode: 'interactive', overrides: { pullRequestApproval: { mode: 'off' } } }); + const inspect = jest.fn(async () => { events.push('inspect'); return remote; }); + const discover = jest.fn(async () => { + events.push('projects'); + return { status: 'observed' as const, candidates: [{ number: 12, title: 'Roadmap', owner: 'owner', + url: 'https://github.com/orgs/owner/projects/12', statusOptions: ['Todo', 'In Progress'] }] }; + }); + const collect = jest.fn(async (state) => { events.push('collect'); return createSetupReviewState(state.draft); }); + await new SetupWizardUseCase(dependencies({ + collector: { collect }, remoteConfiguration: { inspect }, projectDiscovery: { discover }, + })).execute({ mode: 'interactive', overrides: { pullRequestApproval: { mode: 'off' } }, + permissionIntent: { draft, answeredQuestionIds: ['projects.enabled'], projectsWanted: true }, + remoteTarget: { owner: 'owner', repository: 'repo', token: 'setup-token' } }); + expect(events).toEqual(['inspect', 'projects', 'collect']); + expect(discover).toHaveBeenCalledWith('owner', 'Organization', 'setup-token'); + expect(collect).toHaveBeenCalledWith(expect.anything(), expect.objectContaining({ + projectsWanted: true, projectDiscovery: expect.objectContaining({ status: 'observed' }), + }), expect.objectContaining({ refresh: expect.any(Function) })); + }); + + it('discovers Projects when the operator enables them while revising a plan that originally skipped them', async () => { + const draft = buildInitialSetupConfiguration({ mode: 'interactive', overrides: { pullRequestApproval: { mode: 'off' } } }); + const discover = jest.fn().mockResolvedValue({ status: 'empty', candidates: [] }); + const collect = jest.fn(async state => createSetupReviewState(state.draft)); + await new SetupWizardUseCase(dependencies({ collector: { collect }, + remoteConfiguration: { inspect: jest.fn().mockResolvedValue(remote) }, + projectDiscovery: { discover } })).execute({ mode: 'interactive', overrides: { pullRequestApproval: { mode: 'off' } }, + permissionIntent: { draft, answeredQuestionIds: ['projects.enabled'], projectsWanted: false }, + revision: { group: 'projects', answeredQuestionIds: ['projects.enabled'] }, + remoteTarget: { owner: 'owner', repository: 'repo', token: 'setup-token' } }); + expect(discover).toHaveBeenCalledWith('owner', 'Organization', 'setup-token'); + expect(collect).toHaveBeenCalledWith(expect.anything(), expect.objectContaining({ + projectsWanted: true, projectDiscovery: expect.objectContaining({ status: 'empty' }), + }), expect.anything()); + }); + + it('bounds explicit Project discovery retries and never requests another PAT', async () => { + const draft = buildInitialSetupConfiguration({ mode: 'interactive', overrides: { pullRequestApproval: { mode: 'off' } } }); + const discover = jest.fn().mockResolvedValueOnce({ status: 'unavailable', candidates: [] }) + .mockResolvedValueOnce({ status: 'observed', candidates: [{ number: 4, owner: 'owner', title: 'Roadmap', + url: 'https://github.com/orgs/owner/projects/4', statusOptions: ['Todo', 'In Progress'] }] }) + .mockResolvedValueOnce({ status: 'empty', candidates: [] }); + const collect = jest.fn(async (state, context, refresh) => { + expect(context.discoveryRetryRemaining.projects).toBe(2); + expect((await refresh.refresh('projects'))?.projectDiscovery?.status).toBe('observed'); + expect((await refresh.refresh('projects'))?.projectDiscovery?.status).toBe('empty'); + expect(await refresh.refresh('projects')).toBeUndefined(); + return createSetupReviewState(state.draft); + }); + await new SetupWizardUseCase(dependencies({ collector: { collect }, remoteConfiguration: { inspect: jest.fn().mockResolvedValue(remote) }, + projectDiscovery: { discover } })).execute({ mode: 'interactive', overrides: { pullRequestApproval: { mode: 'off' } }, + permissionIntent: { draft, answeredQuestionIds: ['projects.enabled'], projectsWanted: true }, + remoteTarget: { owner: 'owner', repository: 'repo', token: 'setup-token' } }); + expect(discover).toHaveBeenCalledTimes(3); + expect(discover).toHaveBeenNthCalledWith(3, 'owner', 'Organization', 'setup-token'); }); it('adds live merge-queue readiness to the setup plan', async () => { @@ -380,7 +492,7 @@ describe('SetupWizardUseCase', () => { }); expect(collect).toHaveBeenCalledWith(expect.anything(), expect.objectContaining({ remote: expect.objectContaining({ repositoryVariablesAccess: 'unavailable' }), - })); + }), expect.objectContaining({ refresh: expect.any(Function) })); expect(deps.finalPermissionAudit.audit).toHaveBeenCalledTimes(1); expect(deps.planPresenter.present).not.toHaveBeenCalled(); expect(deps.confirmation.confirm).not.toHaveBeenCalled(); diff --git a/src/application/usecases/setup/doctor_use_case.ts b/src/application/usecases/setup/doctor_use_case.ts index a85bd6ace..5558731ba 100644 --- a/src/application/usecases/setup/doctor_use_case.ts +++ b/src/application/usecases/setup/doctor_use_case.ts @@ -46,6 +46,8 @@ export interface DoctorRequest { repository: string; setupToken: string; configuration: SetupConfiguration; + /** Inspect installed resources without dispatching credential-health Actions. */ + readOnly?: boolean; } export interface SetupDoctorDependencies { @@ -257,7 +259,7 @@ export class SetupDoctorUseCase { const remoteSecrets = new Set([...remote.repositorySecrets, ...remote.organizationSecrets]); const present = requirements.filter((requirement) => remoteSecrets.has(requirement.name)); let health: readonly SetupCredentialCheck[] | undefined; - if (present.length > 0) { + if (present.length > 0 && !request.readOnly) { try { health = await this.dependencies.remoteHealth.validateExisting( request.owner, diff --git a/src/application/usecases/setup/prepare_setup_pat_intent_use_case.ts b/src/application/usecases/setup/prepare_setup_pat_intent_use_case.ts index f4dc460ac..23eb76b36 100644 --- a/src/application/usecases/setup/prepare_setup_pat_intent_use_case.ts +++ b/src/application/usecases/setup/prepare_setup_pat_intent_use_case.ts @@ -26,6 +26,7 @@ export interface SetupPatIntentPreview { readonly ownerConflict: boolean; readonly errors: readonly string[]; readonly pass: number; + readonly projectsWanted: boolean; } export interface PrepareSetupPatIntentPorts { @@ -46,7 +47,7 @@ export type PrepareSetupPatIntentResult = readonly url: string; readonly requirements: readonly SetupTokenPermissionRequirement[]; readonly ownerKind: 'Organization' | 'User'; - readonly permissionIntent: { readonly draft: SetupConfiguration; readonly answeredQuestionIds: readonly string[] }; + readonly permissionIntent: { readonly draft: SetupConfiguration; readonly answeredQuestionIds: readonly string[]; readonly projectsWanted: boolean }; }; /** Frontend-neutral preparation; GitHub still issues the PAT in its own UI. */ @@ -63,23 +64,25 @@ export class PrepareSetupPatIntentUseCase { skipRepositorySecrets: request.skipRepositorySecrets, }); let pass = 1; + let projectsWanted = Boolean(draft.projects.ids.trim()); while (true) { - const context = { skipQuestionIds: fixedQuestionIds }; + const context = { skipQuestionIds: fixedQuestionIds, projectsWanted }; const intent = await this.ports.collect(createSetupPermissionIntentQuestionnaire(draft, context), context, pass); if (intent.terminal === 'cancelled') throw new SetupInteractionCancelledError(); draft = intent.draft; - const ownerKind = setupPatIntentNeedsOwnerKind(draft) ? await this.ports.chooseOwnerKind() : 'User'; + projectsWanted = Boolean(draft.projects.ids.trim()) || (intent.projectsWanted ?? projectsWanted); + const ownerKind = setupPatIntentNeedsOwnerKind(draft, projectsWanted) ? await this.ports.chooseOwnerKind() : 'User'; if (ownerKind === 'unknown') { this.ports.onManual('owner-unknown'); return { kind: 'manual' }; } - const ownerConflict = setupPatIntentOwnerConflict(draft, ownerKind); + const ownerConflict = setupPatIntentOwnerConflict(draft, ownerKind, projectsWanted); const errors = validateSetupConfiguration(draft, { allowIncompleteApproval: true }); - const requirements = buildSetupPatIntentPermissionRequirements(draft, ownerKind); + const requirements = buildSetupPatIntentPermissionRequirements(draft, ownerKind, projectsWanted); this.ports.advanceToSetupPat(); this.ports.showPreview({ draft, requirements, uncertain: buildSetupPatIntentUncertainty(draft, ownerKind), - ownerConflict, errors, pass, + ownerConflict, errors, pass, projectsWanted, }); let decision: Awaited>; @@ -109,6 +112,7 @@ export class PrepareSetupPatIntentUseCase { ownerKind, permissionIntent: { draft, + projectsWanted, answeredQuestionIds: [...new Set([...fixedQuestionIds, ...(intent.answeredQuestionIds ?? [])])], }, }; diff --git a/src/application/usecases/setup/setup_questionnaire_controller.ts b/src/application/usecases/setup/setup_questionnaire_controller.ts index 9a39852de..629c9c564 100644 --- a/src/application/usecases/setup/setup_questionnaire_controller.ts +++ b/src/application/usecases/setup/setup_questionnaire_controller.ts @@ -2,6 +2,7 @@ import type { SetupConfigurationCollectorPort, SetupQuestionRenderer, TerminalDriver, + SetupDiscoveryRefreshPort, } from '../../ports/setup_terminal_ports'; import type { SetupQuestionnaireContext, @@ -9,7 +10,7 @@ import type { SetupQuestionnaireState, SetupQuestionnaireStateId, } from '../../../domain/setup_questionnaire'; -import { transitionSetupQuestionnaire } from '../../policies/setup_questionnaire_policy'; +import { refreshSetupQuestionnaireQuestion, setupQuestionnaireProgress, transitionSetupQuestionnaire } from '../../policies/setup_questionnaire_policy'; import { ApplicationError } from '../../errors/application_error'; export class SetupQuestionnaireController implements SetupConfigurationCollectorPort { @@ -21,6 +22,7 @@ export class SetupQuestionnaireController implements SetupConfigurationCollector async collect( initial: SetupQuestionnaireState, context: SetupQuestionnaireContext, + discoveryRefresh?: SetupDiscoveryRefreshPort, ): Promise { if (!this.terminal.isInteractive()) { throw new ApplicationError( @@ -30,6 +32,8 @@ export class SetupQuestionnaireController implements SetupConfigurationCollector } this.renderer.showIntroduction(); let state = initial; + let currentContext = context; + let pendingProjectSelection: readonly string[] | undefined; let visibleState: SetupQuestionnaireStateId | undefined; while (state.terminal === 'collecting' && state.question) { if (visibleState !== state.stateId) { @@ -37,14 +41,52 @@ export class SetupQuestionnaireController implements SetupConfigurationCollector visibleState = state.stateId; } if (state.validation) this.renderer.showValidation(state.validation); - const input = state.question.kind === 'multi-select' && this.terminal.readMultiSelect + let input = (state.question.kind === 'multi-select' || state.question.kind === 'project-select') && this.terminal.readMultiSelect ? await this.terminal.readMultiSelect( - this.renderer.renderPrompt(state.question), - state.question.choices ?? [], - parseSelectedDefaults(state.question.defaultValue), + this.renderer.renderPrompt(state.question, setupQuestionnaireProgress(state, currentContext)), + state.question.kind === 'project-select' + ? [...(state.question.projectCandidates ?? []).map(candidate => `${candidate.number} — ${candidate.title} (${candidate.url})`), 'manual — Enter Project number or URL', + ...(state.question.discoveryRetryRemaining ? ['retry — Retry GitHub Project discovery'] : [])] + : state.question.choices ?? [], + state.question.kind === 'project-select' && pendingProjectSelection + ? pendingProjectSelection : parseSelectedDefaults(state.question.defaultValue), + this.renderer.renderHelp(state.question), ) - : await this.terminal.readText(this.renderer.renderPrompt(state.question)); - state = transitionSetupQuestionnaire(state, toEvent(input), context); + : await this.terminal.readText(this.renderer.renderPrompt(state.question, setupQuestionnaireProgress(state, currentContext))); + if (state.question.kind === 'project-select' && input.kind === 'value' && input.value.split(',').includes('manual') + && !input.value.split(',').includes('retry')) { + const manual = await this.terminal.readText('Enter additional Project numbers or GitHub URLs, comma-separated (empty adds none): '); + input = manual.kind === 'value' + ? { kind: 'value', value: [input.value.replace(/(?:^|,)manual(?:,|$)/gu, ',').replace(/^,|,$/gu, ''), manual.value] + .filter(value => value && value !== 'none').join(',') || 'none' } : manual; + } + if (input.kind === 'value' && input.value.trim() === '?') { + this.renderer.showHelp(state.question); + continue; + } + if (input.kind === 'value' && input.value.trim().toLowerCase() === ':back') { + pendingProjectSelection = undefined; + state = transitionSetupQuestionnaire(state, { kind: 'back' }, currentContext); + continue; + } + const kind = state.question.id === 'projects.ids' ? 'projects' + : state.question.id === 'pullRequestApproval.testChecks' ? 'checks' : undefined; + if (kind && input.kind === 'value' && (input.value.trim().toLowerCase() === 'r' + || input.value.split(',').includes('retry'))) { + if (kind === 'projects') pendingProjectSelection = input.value.split(',').filter(value => value !== 'retry'); + if (!state.question.discoveryRetryRemaining || !discoveryRefresh) { + this.renderer.showValidation('No discovery retries remain. Use the manual option or continue.'); + continue; + } + const refreshed = await discoveryRefresh.refresh(kind); + if (refreshed) { + currentContext = refreshed; + state = refreshSetupQuestionnaireQuestion(state, currentContext); + } + continue; + } + pendingProjectSelection = undefined; + state = transitionSetupQuestionnaire(state, toEvent(input), currentContext); } if (state.terminal === 'cancelled') this.renderer.showCancelled(); return state; diff --git a/src/application/usecases/setup/setup_wizard_use_case.ts b/src/application/usecases/setup/setup_wizard_use_case.ts index f87e412c1..55f208e31 100644 --- a/src/application/usecases/setup/setup_wizard_use_case.ts +++ b/src/application/usecases/setup/setup_wizard_use_case.ts @@ -10,7 +10,7 @@ import type { } from '../../ports/setup_wizard_ports'; import { ApplicationError } from '../../errors/application_error'; import type { SetupConfiguration, SetupPlan, SetupRemoteConfiguration } from '../../../domain/setup'; -import type { SetupQuestionnaireContext } from '../../../domain/setup_questionnaire'; +import type { SetupQuestion, SetupQuestionnaireContext } from '../../../domain/setup_questionnaire'; import { buildSetupCredentialRequirements, buildSetupRepositoryVariables, @@ -28,11 +28,17 @@ import { createSetupReviewState, enterSetupConfirmation, finishSetupQuestionnaire, + setupQuestionIdsForGroup, + setupBasicSkippedQuestionIds, + setupQuestionContentInventory, } from '../../policies/setup_questionnaire_policy'; import { cloneSetupConfiguration } from '../../policies/setup_configuration_clone_policy'; import { resolveStaticSetupDoctorCatalog } from '../../policies/setup_doctor_message_catalog'; import { DEFAULT_PULL_REQUEST_APPROVAL_POLICY } from '../../../domain/pull_request_approval_policy'; import type { SetupApprovalReadinessPort } from '../../ports/setup_approval_readiness_port'; +import type { SetupApprovalCheckDiscoveryPort } from '../../ports/setup_approval_check_discovery_port'; +import type { SetupProjectDiscoveryPort } from '../../ports/setup_project_discovery_port'; +import { validateDiscoveredProjectStatuses } from '../../policies/setup_project_selection_policy'; import type { DoctorCheck } from '../../../domain/setup'; export interface SetupWizardRequest { @@ -41,12 +47,19 @@ export interface SetupWizardRequest { skipRepositoryVariables?: boolean; skipRepositorySecrets?: boolean; previewOnly?: boolean; + presentationMode?: 'basic' | 'custom'; + developmentBranchObservedLocally?: boolean; + /** Internal stable presentation snapshot across plan revisions. */ + basicSkippedQuestionIds?: readonly string[]; + reviewedGroups?: readonly SetupQuestion['stateId'][]; remoteTarget?: { owner: string; repository: string; token: string; }; - permissionIntent?: { draft: SetupConfiguration; answeredQuestionIds: readonly string[] }; + permissionIntent?: { draft: SetupConfiguration; answeredQuestionIds: readonly string[]; projectsWanted?: boolean }; + /** Internal same-run plan correction. No credential or approval is persisted here. */ + revision?: { group: SetupQuestion['stateId']; answeredQuestionIds: readonly string[] }; } export type SetupWizardResult = @@ -88,6 +101,8 @@ export interface SetupWizardDependencies { remoteConfiguration?: SetupRemoteConfigurationReadPort; mergeQueueReadiness?: SetupMergeQueueReadinessPort; approvalReadiness?: SetupApprovalReadinessPort; + approvalCheckDiscovery?: SetupApprovalCheckDiscoveryPort; + projectDiscovery?: SetupProjectDiscoveryPort; } export class SetupWizardUseCase { @@ -97,6 +112,8 @@ export class SetupWizardUseCase { const defaults = buildInitialSetupConfiguration(request); const effectiveOverrides = request.overrides; const initial = request.permissionIntent ? cloneSetupConfiguration(request.permissionIntent.draft) : defaults; + const basicSkippedQuestionIds = request.presentationMode === 'basic' + ? request.basicSkippedQuestionIds ?? setupBasicSkippedQuestionIds(initial) : []; let remoteConfiguration: SetupRemoteConfiguration | undefined; if (request.remoteTarget) { try { @@ -109,6 +126,10 @@ export class SetupWizardUseCase { remoteConfiguration = unavailableRemoteConfiguration(); } } + const explicitMainBranch = request.overrides?.repository?.mainBranch !== undefined; + if (!explicitMainBranch && remoteConfiguration?.defaultBranch) { + initial.repository.mainBranch = remoteConfiguration.defaultBranch; + } const defaultValidationErrors = validateSetupConfiguration(initial, { allowIncompleteApproval: true }); if (defaultValidationErrors.length > 0) { throw new ApplicationError( @@ -116,14 +137,69 @@ export class SetupWizardUseCase { `Invalid setup configuration:\n${defaultValidationErrors.map((error) => `- ${error}`).join('\n')}`, ); } - const context: SetupQuestionnaireContext = { + let approvalDiscovery = request.mode === 'interactive' && initial.pullRequestApproval.mode !== 'off' + && request.remoteTarget && this.dependencies.approvalCheckDiscovery + ? await this.dependencies.approvalCheckDiscovery.discover( + request.remoteTarget.owner, request.remoteTarget.repository, request.remoteTarget.token, initial.repository.developmentBranch, + ).catch(() => ({ status: 'unavailable' as const, candidates: [], truncated: false })) : undefined; + let projectDiscovery = request.mode === 'interactive' + && (request.permissionIntent?.projectsWanted !== false || request.revision?.group === 'projects') + && request.remoteTarget && this.dependencies.projectDiscovery + ? await this.dependencies.projectDiscovery.discover( + request.remoteTarget.owner, remoteConfiguration?.ownerType ?? 'Unknown', request.remoteTarget.token, + ).catch(() => ({ status: 'unavailable' as const, candidates: [] })) : undefined; + let context: SetupQuestionnaireContext = { ...(remoteConfiguration ? { remote: remoteConfiguration } : {}), + branchSources: { main: explicitMainBranch ? 'configuration' : remoteConfiguration?.defaultBranch ? 'github' : 'default', + development: request.overrides?.repository?.developmentBranch !== undefined ? 'configuration' + : request.developmentBranchObservedLocally ? 'local' : 'default' }, variableNames: buildSetupRepositoryVariables(initial).map((variable) => variable.name), secretNames: buildSetupCredentialRequirements(initial).map((requirement) => requirement.name), - ...(request.permissionIntent ? { skipQuestionIds: request.permissionIntent.answeredQuestionIds } : {}), + ...(request.revision ? { skipQuestionIds: [...new Set([ + ...request.revision.answeredQuestionIds, + ...basicSkippedQuestionIds, + ])].filter(id => !setupQuestionIdsForGroup(request.revision!.group).includes(id)), + projectsWanted: request.revision.group === 'projects' || Boolean(initial.projects.ids.trim()) } : {}), + ...(!request.revision ? { skipQuestionIds: [...new Set([ + ...(request.permissionIntent?.answeredQuestionIds ?? []), + ...(request.permissionIntent?.projectsWanted === false ? ['projects.ids'] : []), + ...basicSkippedQuestionIds, + ])], ...(request.permissionIntent ? { projectsWanted: request.permissionIntent.projectsWanted } : {}) } : {}), + ...(approvalDiscovery ? { approvalCheckCandidates: approvalDiscovery.candidates, + approvalCheckDiscoveryStatus: approvalDiscovery.status, + approvalCheckDiscoveryTruncated: approvalDiscovery.truncated } : {}), + ...(projectDiscovery ? { projectDiscovery } : {}), + ...(request.remoteTarget ? { projectOwner: request.remoteTarget.owner } : {}), + discoveryRetryRemaining: { checks: approvalDiscovery ? 2 : 0, + projects: projectDiscovery && projectDiscovery.status !== 'unsupported' ? 2 : 0 }, + }; + const discoveryRefresh = { + refresh: async (kind: 'checks' | 'projects'): Promise => { + const target = request.remoteTarget; + const remaining = context.discoveryRetryRemaining?.[kind] ?? 0; + if (!target || remaining <= 0) return undefined; + if (kind === 'checks') { + if (!this.dependencies.approvalCheckDiscovery) return undefined; + approvalDiscovery = await this.dependencies.approvalCheckDiscovery.discover( + target.owner, target.repository, target.token, initial.repository.developmentBranch, + ).catch(() => ({ status: 'unavailable' as const, candidates: [], truncated: false })); + context = { ...context, approvalCheckCandidates: approvalDiscovery.candidates, + approvalCheckDiscoveryStatus: approvalDiscovery.status, + approvalCheckDiscoveryTruncated: approvalDiscovery.truncated, + discoveryRetryRemaining: { ...context.discoveryRetryRemaining!, checks: remaining - 1 } }; + } else { + if (!this.dependencies.projectDiscovery) return undefined; + projectDiscovery = await this.dependencies.projectDiscovery.discover( + target.owner, remoteConfiguration?.ownerType ?? 'Unknown', target.token, + ).catch(() => ({ status: 'unavailable' as const, candidates: [] })); + context = { ...context, projectDiscovery, + discoveryRetryRemaining: { ...context.discoveryRetryRemaining!, projects: remaining - 1 } }; + } + return context; + }, }; const questionnaire = request.mode === 'interactive' - ? await this.collectInteractive(initial, context) + ? await this.collectInteractive(initial, context, discoveryRefresh) : createSetupReviewState(initial); if (questionnaire.terminal === 'cancelled') { return { @@ -141,7 +217,10 @@ export class SetupWizardUseCase { // default must not outlive an explicit decision to disable PR automation. collectedConfiguration.pullRequestApproval = { ...collectedConfiguration.pullRequestApproval, mode: 'off' }; } - const validationErrors = validateSetupConfiguration(collectedConfiguration, { allowIncompleteApproval: request.previewOnly === true }); + const validationErrors = [ + ...validateSetupConfiguration(collectedConfiguration, { allowIncompleteApproval: request.previewOnly === true }), + ...validateDiscoveredProjectStatuses(collectedConfiguration, projectDiscovery), + ]; if (validationErrors.length > 0) { throw new ApplicationError( 'configuration.invalid', @@ -247,9 +326,28 @@ export class SetupWizardUseCase { } } const plan = buildSetupPlan(configuration, readiness, approvalReadiness); + if (basicSkippedQuestionIds.length) { + const byGroup = new Map(); + for (const item of setupQuestionContentInventory()) { + if (basicSkippedQuestionIds.includes(item.id) && !request.reviewedGroups?.includes(item.stateId) + && request.revision?.group !== item.stateId) byGroup.set(item.stateId, (byGroup.get(item.stateId) ?? 0) + 1); + } + plan.presentationDefaults = [...byGroup].map(([group, count]) => ({ group, count })); + } this.dependencies.planPresenter.present(plan); const confirmation = enterSetupConfirmation(questionnaire); const decision = await this.dependencies.confirmation.confirm(plan); + if (decision.kind === 'revise') { + if (request.mode !== 'interactive') throw new ApplicationError('configuration.invalid', 'Plan editing requires interactive setup.'); + const answeredQuestionIds = [...new Set([ + ...(request.revision?.answeredQuestionIds ?? []), + ...(request.permissionIntent?.answeredQuestionIds ?? []), + ...(questionnaire.answeredQuestionIds ?? []), + ])]; + return this.execute({ ...request, overrides: cloneSetupConfiguration(configuration), permissionIntent: undefined, + basicSkippedQuestionIds, reviewedGroups: [...new Set([...(request.reviewedGroups ?? []), decision.group])], + revision: { group: decision.group, answeredQuestionIds } }); + } const completed = finishSetupQuestionnaire(confirmation, decision.kind === 'approved'); if (completed.terminal === 'cancelled') { return { @@ -271,11 +369,12 @@ export class SetupWizardUseCase { private collectInteractive( defaults: SetupConfiguration, context: Parameters['collect']>[1], + discoveryRefresh?: Parameters['collect']>[2], ) { if (!this.dependencies.collector) { throw new ApplicationError('configuration.invalid', 'Interactive setup requires a questionnaire collector.'); } - return this.dependencies.collector.collect(createSetupQuestionnaire(defaults, context), context); + return this.dependencies.collector.collect(createSetupQuestionnaire(defaults, context), context, discoveryRefresh); } } diff --git a/src/cli/__tests__/setup_presenters.test.ts b/src/cli/__tests__/setup_presenters.test.ts index 7c0236308..d4cf3afda 100644 --- a/src/cli/__tests__/setup_presenters.test.ts +++ b/src/cli/__tests__/setup_presenters.test.ts @@ -147,6 +147,14 @@ describe('setup presenters and prompt-specific adapters', () => { kind: 'boolean', defaultValue: true, })).toContain('[Y]'); + const help = renderer.renderHelp({ + stateId: 'agent-model-defaults', id: 'agents.findings.executable', + label: 'Validated executable for all tasks', kind: 'text', defaultValue: '', + }); + for (const heading of ['What:', 'When:', 'Where:', 'How:', 'Why:', 'Example:', 'Effect:', 'Verify:', 'Read more']) { + expect(help).toContain(heading); + } + expect(help).toContain('https://docs.page/vypdev/copilot/agents/cli-configuration'); const log = jest.spyOn(console, 'log').mockImplementation(); renderer.showIntroduction(); renderer.showState('capabilities'); @@ -172,6 +180,20 @@ describe('setup presenters and prompt-specific adapters', () => { await expect(new DryRunSetupPlanConfirmation().confirm(plan)).resolves.toEqual({ kind: 'approved' }); }); + it('explains final Apply on ? and re-asks without approving it', async () => { + const log = jest.spyOn(console, 'log').mockImplementation(); + try { + const input = terminal([{ kind: 'value', value: '?' }, { kind: 'value', value: 'no' }]); + await expect(new SetupPlanConfirmationAdapter(input, false).confirm(buildSetupPlan(createDefaultSetupConfiguration()))) + .resolves.toEqual({ kind: 'declined' }); + expect(input.readText).toHaveBeenCalledTimes(2); + const output = log.mock.calls.flat().join('\n'); + expect(output).toContain('This is the final approval'); + expect(output).toContain('PATs created on GitHub are not deleted automatically'); + expect(output).toContain('https://docs.page/vypdev/copilot/how-to-use'); + } finally { log.mockRestore(); } + }); + it('keeps non-interactive credential values separate from questionnaire and plan state', async () => { const adapter = new SetupCredentialPromptAdapter(undefined, { PAT: 'workflow-token' }); const requirement = { name: 'PAT', kind: 'workflowPat' as const, description: 'Runtime token' }; @@ -296,6 +318,34 @@ describe('setup presenters and prompt-specific adapters', () => { } finally { log.mockRestore(); } }); + it('opens credential choice help with ? and then asks the same unanswered question', async () => { + const log = jest.spyOn(console, 'log').mockImplementation(); + try { + const input = terminal([{ kind: 'value', value: '?' }, { kind: 'value', value: '2' }]); + await expect(new SetupCredentialPromptAdapter(input, {}).chooseSetupOwnerKind()).resolves.toBe('User'); + expect(input.readText).toHaveBeenCalledTimes(2); + expect(String(input.readText.mock.calls[0][0])).toContain('Type ? for more detail'); + expect(log.mock.calls.flat().join('\n')).toContain('owner/repository'); + expect(log.mock.calls.flat().join('\n')).toContain('https://docs.page/vypdev/copilot/authentication'); + } finally { log.mockRestore(); } + }); + + it('explains a bot login on ? without treating it as an account', async () => { + const log = jest.spyOn(console, 'log').mockImplementation(); + try { + const input = terminal([ + { kind: 'value', value: '1' }, { kind: 'value', value: '?' }, + { kind: 'value', value: 'vypbot' }, { kind: 'value', value: 'bot-token' }, + ]); + const adapter = new SetupCredentialPromptAdapter(input, {}); + adapter.configureWorkflowPatGuide('https://github.com/settings/personal-access-tokens/new', async login => ({ login, id: 123 })); + await expect(adapter.requestWorkflowPat({ name: 'PAT', kind: 'workflowPat', description: 'runtime' })) + .resolves.toEqual({ name: 'PAT', value: 'bot-token' }); + expect(log.mock.calls.flat().join('\n')).toContain('numeric account ID'); + expect(input.readText).toHaveBeenCalledTimes(3); + } finally { log.mockRestore(); } + }); + it('reviews intent explicitly, supports revision, and can fall back to manual input', async () => { const log = jest.spyOn(console, 'log').mockImplementation(); try { diff --git a/src/cli/__tests__/setup_result_receipt.test.ts b/src/cli/__tests__/setup_result_receipt.test.ts new file mode 100644 index 000000000..bd8358bf8 --- /dev/null +++ b/src/cli/__tests__/setup_result_receipt.test.ts @@ -0,0 +1,62 @@ +import { ApplicationError } from '../../data/model/application_error'; +import { Result } from '../../data/model/result'; +import { setupActionResultFailure, setupResultEffects, setupResultReason } from '../setup_result_receipt'; + +const reference = '12345678-1234-4123-8123-123456789abc'; + +describe('setup result receipt', () => { + test('reads only the versioned, complete, value-free resource receipt', () => { + const effects = [ + { id: 'files', state: 'completed', scope: 'local' }, + { id: 'secrets', state: 'needs-inspection', scope: 'organization' }, + { id: 'labels', state: 'skipped', scope: 'repository' }, + { id: 'issue-types', state: 'completed', scope: 'repository' }, + { id: 'variables', state: 'not-started', scope: 'mixed' }, + { id: 'initial-tag', state: 'not-started', scope: 'repository' }, + ]; + const result = new Result({ id: 'InitialSetupUseCase', success: false, executed: true, + payload: { setupReceipt: { version: 1, effects, secretValue: 'must-never-appear' } } }); + expect(setupResultEffects([result])).toEqual(effects); + expect(JSON.stringify(setupResultEffects([result]))).not.toContain('must-never-appear'); + const poisoned = new Result({ id: 'github_pat_fake', success: false, executed: true, + payload: { setupReceipt: { version: 1, effects: effects.map(item => item.id === 'files' ? { ...item, id: 'github_pat_fake' } : item) } } }); + expect(setupResultEffects([poisoned])).toEqual([{ id: 'step-1', state: 'needs-inspection' }]); + }); + test.each([ + ['authorization.denied', 'permissions'], + ['configuration.invalid', 'configuration'], + ['provider.rate-limited', 'rate-limit'], + ['provider.unavailable', 'provider'], + ['workflow.failed', 'unknown'], + ] as const)('maps %s to a redacted %s reason', (code, reason) => { + expect(setupResultReason(code)).toBe(reason); + }); + + test('does not serialize provider error messages or unsafe action identifiers', () => { + const error = new ApplicationError('provider.unavailable', 'private diagnostic', { correlationId: reference }); + const results = [ + new Result({ id: 'files', success: true, executed: true }), + new Result({ id: 'secret\nPAT', success: false, executed: true, errors: [error] }), + new Result({ id: 'variables', success: true, executed: false }), + ]; + expect(setupResultEffects(results)).toEqual([ + { id: 'files', state: 'completed' }, + { id: 'step-2', state: 'needs-inspection' }, + { id: 'variables', state: 'skipped' }, + ]); + expect(setupActionResultFailure(results)).toEqual({ reasonCode: 'provider', diagnosticRef: reference }); + expect(JSON.stringify(setupResultEffects(results))).not.toContain('private diagnostic'); + }); + + test('uses unknown reason for failed result without structured error', () => { + expect(setupActionResultFailure([new Result({ id: 'files', success: false })])).toEqual({ reasonCode: 'unknown' }); + expect(setupActionResultFailure([new Result({ id: 'files', success: true })])).toBeUndefined(); + }); + + test('does not throw or leak text if a legacy action returns an unstructured error', () => { + const result = { id: 'InitialSetupUseCase', success: false, executed: true, + errors: ['private provider diagnostic'] } as unknown as Result; + expect(setupActionResultFailure([result])).toEqual({ reasonCode: 'unknown' }); + expect(setupResultEffects([result])).toEqual([{ id: 'setup-workflow', state: 'needs-inspection' }]); + }); +}); diff --git a/src/cli/__tests__/setup_terminal_driver.test.ts b/src/cli/__tests__/setup_terminal_driver.test.ts index 57c461d79..0e7d8f11e 100644 --- a/src/cli/__tests__/setup_terminal_driver.test.ts +++ b/src/cli/__tests__/setup_terminal_driver.test.ts @@ -145,6 +145,15 @@ describe('NodeTerminalDriver', () => { expect(mockStdin.setRawMode).toHaveBeenLastCalledWith(false); }); + it('shows raw-mode help without losing the current multi-selection', async () => { + const pending = new NodeTerminalDriver().readMultiSelect( + 'Issue workflows', ['All', 'feature — Feature', 'help — Help'], [], 'What: choose issue workflows', + ); + mockInputHandlers.get('data')?.(Buffer.from('\u001b[B ?\n')); + await expect(pending).resolves.toEqual({ kind: 'value', value: 'feature' }); + expect(mockStdout.write).toHaveBeenCalledWith(expect.stringContaining('What: choose issue workflows')); + }); + it.each([ ['data', '\u0003', 'cancel'], ['data', '\u0004', 'end-of-input'], diff --git a/src/cli/__tests__/web_setup_adapters.test.ts b/src/cli/__tests__/web_setup_adapters.test.ts index 651ef643b..14edfe3f0 100644 --- a/src/cli/__tests__/web_setup_adapters.test.ts +++ b/src/cli/__tests__/web_setup_adapters.test.ts @@ -48,7 +48,7 @@ describe('semantic web setup adapters', () => { const ask = jest.spyOn(bridge, 'ask').mockResolvedValueOnce(undefined); const initial = createSetupPermissionIntentQuestionnaire(buildInitialSetupConfiguration({ mode: 'interactive' })); const result = await new WebSetupQuestionnaireCollector(bridge).collect({ ...initial, phase: undefined }, {}); - expect(ask).toHaveBeenCalledWith(expect.objectContaining({ phase: 'full', pass: 1 })); + expect(ask).toHaveBeenCalledWith(expect.objectContaining({ phase: 'full', pass: 1 }), undefined, expect.any(Function)); expect(result.terminal).toBe('cancelled'); }); diff --git a/src/cli/__tests__/web_setup_bridge.test.ts b/src/cli/__tests__/web_setup_bridge.test.ts index 9f3517de4..71284616e 100644 --- a/src/cli/__tests__/web_setup_bridge.test.ts +++ b/src/cli/__tests__/web_setup_bridge.test.ts @@ -1,6 +1,56 @@ import { WebSetupBridge } from '../web_setup_bridge'; describe('WebSetupBridge', () => { + test('read-only verification runs only after a completed setup and publishes counts without diagnostic values', async () => { + const bridge = new WebSetupBridge('owner/repo'); + const run = jest.fn().mockResolvedValue({ healthy: false, pass: 3, warn: 1, fail: 0, skipped: 2, + token: 'must-not-appear' }); + bridge.configureReadOnlyDoctor(run); + expect(await bridge.runReadOnlyDoctor()).toBe('unavailable'); + bridge.finish('complete', 'Done'); + expect(await bridge.runReadOnlyDoctor()).toBe('complete'); + expect(await bridge.runReadOnlyDoctor()).toBe('complete'); + expect(run).toHaveBeenCalledTimes(1); + expect(bridge.snapshot().doctor).toEqual({ status: 'complete', healthy: false, pass: 3, warn: 1, fail: 0, skipped: 2 }); + expect(JSON.stringify(bridge.snapshot())).not.toContain('must-not-appear'); + }); + test('read-only verification serializes no provider error and allows only one bounded retry', async () => { + const bridge = new WebSetupBridge('owner/repo'); + const run = jest.fn().mockRejectedValue(new Error('private GitHub diagnostic')); + bridge.configureReadOnlyDoctor(run); + bridge.finish('complete', 'Done'); + expect(await bridge.runReadOnlyDoctor()).toBe('failed'); + expect(await bridge.runReadOnlyDoctor()).toBe('failed'); + expect(await bridge.runReadOnlyDoctor()).toBe('unavailable'); + expect(run).toHaveBeenCalledTimes(2); + expect(bridge.snapshot().doctor).toEqual({ status: 'failed' }); + expect(JSON.stringify(bridge.snapshot())).not.toContain('private GitHub diagnostic'); + }); + test('keeps a redacted operation receipt and diagnostic reference together', () => { + const bridge = new WebSetupBridge('owner/repo'); + bridge.setJourney({ repository: 'owner/repo', position: 6, total: 6, current: 'Apply', complete: [], pending: [], mutationStarted: true, choiceReviewPass: 1 }); + bridge.effects([{ id: 'files', state: 'completed' }, { id: 'secret', state: 'needs-inspection' }]); + bridge.resultReason('provider', '12345678-1234-4123-8123-123456789abc'); + bridge.finish('partial', 'Inspect changes'); + expect(bridge.snapshot().resultDetail).toEqual(expect.objectContaining({ reasonCode: 'provider', diagnosticRef: '12345678-1234-4123-8123-123456789abc', effects: [ + { id: 'files', state: 'completed' }, { id: 'secret', state: 'needs-inspection' }, + ] })); + }); + test('back navigation rotates the question revision without resolving or echoing a draft answer', async () => { + const bridge = new WebSetupBridge('owner/repo'); + const commit = jest.fn(); + const pending = bridge.ask({ kind: 'text', title: 'Second' }, undefined, + () => ({ prompt: { kind: 'text', title: 'First' }, commit })); + const oldRevision = bridge.snapshot().promptRevision!; + expect(bridge.back(oldRevision)).toBe('updated'); + expect(commit).toHaveBeenCalledTimes(1); + const newRevision = bridge.snapshot().promptRevision!; + expect(newRevision).toBeGreaterThan(oldRevision); + expect(bridge.answer(oldRevision, 'stale')).toBe(false); + expect(bridge.answer(newRevision, 'fresh')).toBe(true); + expect(await pending).toBe('fresh'); + expect(JSON.stringify(bridge.snapshot())).not.toContain('fresh'); + }); test('publishes semantic prompts with one-use revisions and never echoes an answer', async () => { const bridge = new WebSetupBridge('owner/repo'); const pending = bridge.ask({ kind: 'secret', title: 'Setup PAT' }); @@ -32,6 +82,7 @@ describe('WebSetupBridge', () => { bridge.cancel(); expect(await pending).toBeUndefined(); expect(bridge.snapshot().outcome).toBe('cancelled'); + expect(bridge.snapshot().resultDetail).toEqual(expect.objectContaining({ reasonCode: 'cancelled', mutationStarted: false })); await expect(bridge.ask({ kind: 'text', title: 'Again' })).rejects.toThrow('ended'); }); @@ -79,6 +130,55 @@ describe('WebSetupBridge', () => { expect(await pending).toBe('yes'); }); + test('explicit discovery retry updates the pending prompt in place without consuming its answer', async () => { + const bridge = new WebSetupBridge('owner/repo'); + const pending = bridge.ask({ kind: 'text', title: 'Discovery' }, async () => ({ + prompt: { kind: 'text', title: 'Updated discovery' }, commit: jest.fn(), + })); + const revision = bridge.snapshot().promptRevision!; + expect(await bridge.retryDiscovery(revision)).toBe('updated'); + expect(bridge.snapshot().promptRevision).toBe(revision); + expect(bridge.snapshot().prompt).toMatchObject({ title: 'Updated discovery' }); + expect(bridge.answer(revision, 'kept choice')).toBe(true); + expect(await pending).toBe('kept choice'); + expect(await bridge.retryDiscovery(revision)).toBe('stale'); + }); + + test('a retry cannot commit after cancellation or a controller takeover', async () => { + const bridge = new WebSetupBridge('owner/repo'); + const first = bridge.bootstrap(); + let finish!: (value: { prompt: { kind: 'text'; title: string }; commit: () => void }) => void; + const commit = jest.fn(); + const pending = bridge.ask({ kind: 'text', title: 'Original' }, () => new Promise(resolve => { finish = resolve; })); + const revision = bridge.snapshot().promptRevision!; + const retry = bridge.retryDiscovery(revision); + expect(bridge.answer(revision, 'racing answer')).toBe(false); + bridge.takeOver(); + expect(bridge.isController(first.capability!)).toBe(false); + finish({ prompt: { kind: 'text', title: 'Stale' }, commit }); + expect(await retry).toBe('stale'); + expect(commit).not.toHaveBeenCalled(); + expect(bridge.snapshot().prompt).toMatchObject({ title: 'Original' }); + bridge.cancel(); + expect(await pending).toBeUndefined(); + }); + + test('cancelling while discovery is in flight discards its result and leaves no pending answer', async () => { + const bridge = new WebSetupBridge('owner/repo'); + let finish!: (value: { prompt: { kind: 'text'; title: string }; commit: () => void }) => void; + const commit = jest.fn(); + const pending = bridge.ask({ kind: 'text', title: 'Original' }, () => new Promise(resolve => { finish = resolve; })); + const revision = bridge.snapshot().promptRevision!; + const retry = bridge.retryDiscovery(revision); + await expect(bridge.retryDiscovery(revision)).resolves.toBe('unavailable'); + expect(bridge.cancel()).toBe(true); + finish({ prompt: { kind: 'text', title: 'Late' }, commit }); + expect(await retry).toBe('stale'); + expect(commit).not.toHaveBeenCalled(); + expect(await pending).toBeUndefined(); + expect(bridge.snapshot().prompt).toBeUndefined(); + }); + test('rejects a value outside the visible choice without consuming the prompt', async () => { const bridge = new WebSetupBridge('owner/repo'); const pending = bridge.ask({ kind: 'confirm', title: 'Apply?', choices: ['Apply setup', 'Stop'] }); @@ -107,6 +207,15 @@ describe('WebSetupBridge', () => { expect(bridge.snapshot().outcome).toBe('blocked'); }); + test('blocked result retains a specific safe reason and stage, without raw provider text', () => { + const bridge = new WebSetupBridge('owner/repo'); + bridge.setJourney({ repository: 'owner/repo', position: 4, total: 6, current: 'Plan', complete: [], pending: [], mutationStarted: false, choiceReviewPass: 1 }); + bridge.resultReason('permissions'); + bridge.finish('blocked', 'Full terminal details'); + expect(bridge.snapshot().resultDetail).toEqual({ reasonCode: 'permissions', stoppedStage: 'Plan', mutationStarted: false }); + expect(JSON.stringify(bridge.snapshot().resultDetail)).not.toContain('Full terminal details'); + }); + test('requirement/report projection exposes role and status only', () => { const bridge = new WebSetupBridge('owner/repo'); bridge.requirements('setup', []); diff --git a/src/cli/__tests__/web_setup_browser_session.test.ts b/src/cli/__tests__/web_setup_browser_session.test.ts index 346a2c848..4436dbd7f 100644 --- a/src/cli/__tests__/web_setup_browser_session.test.ts +++ b/src/cli/__tests__/web_setup_browser_session.test.ts @@ -230,6 +230,81 @@ describe('browser session transport', () => { expect(JSON.parse(latest).busy).toBe(false); }); + test('a discovery retry sends only the revision and controller capability, then reads updated state', async () => { + const requests: Array<{ path: string; options?: RequestInit }> = []; + globalThis.fetch = jest.fn(async (path: string, options?: RequestInit) => { + requests.push({ path, options }); + if (path === '/api/bootstrap') return response({ controller: true, capability: 'controller' }); + if (path === '/api/state') return response(view); + if (path === '/api/retry-discovery') return response({ updated: true }); + throw new Error('Unexpected route'); + }) as typeof fetch; + const session = createSetupSession(TEST_SESSION_KEY); + await session.connect(); + await session.retryDiscovery(7); + expect(requests.map(request => request.path)).toEqual(['/api/bootstrap', '/api/state', '/api/retry-discovery', '/api/state']); + expect(JSON.parse(String(requests[2].options?.body))).toEqual({ revision: 7 }); + expect(requests[2].options?.headers).toEqual(expect.objectContaining({ 'X-Setup-Capability': 'controller' })); + await session.retryDiscovery(8); + expect(requests).toHaveLength(4); + }); + + test('a rejected discovery retry retains the current question and explains the error', async () => { + const requests: string[] = []; + globalThis.fetch = jest.fn(async (path: string) => { + requests.push(path); + if (path === '/api/bootstrap') return response({ controller: true, capability: 'controller' }); + if (path === '/api/state') return response(view); + if (path === '/api/retry-discovery') return response({ error: 'Discovery cannot be retried here. Use the manual option.' }, 409); + throw new Error('Unexpected route'); + }) as typeof fetch; + const session = createSetupSession(TEST_SESSION_KEY); + let latest = {} as { error: string; view?: WebSetupView; busy: boolean }; + session.subscribe(state => { latest = state; }); + await session.connect(); + await session.retryDiscovery(7); + expect(requests).toEqual(['/api/bootstrap', '/api/state', '/api/retry-discovery', '/api/state']); + expect(latest.error).toContain('manual option'); + expect(latest.view?.promptRevision).toBe(7); + expect(latest.busy).toBe(false); + }); + + test('controller transfer during a discovery retry reconnects read-only and never replays it', async () => { + let bootstraps = 0; + const requests: string[] = []; + globalThis.fetch = jest.fn(async (path: string) => { + requests.push(path); + if (path === '/api/bootstrap') return response({ controller: ++bootstraps === 1, capability: 'controller' }); + if (path === '/api/state') return response(view); + if (path === '/api/retry-discovery') return response({ error: 'Control moved to another tab.' }, 403); + throw new Error('Unexpected route'); + }) as typeof fetch; + const session = createSetupSession(TEST_SESSION_KEY); + let latest = {} as { controller: boolean; busy: boolean }; + session.subscribe(state => { latest = state; }); + await session.connect(); + await session.retryDiscovery(7); + expect(requests).toEqual(['/api/bootstrap', '/api/state', '/api/retry-discovery', '/api/bootstrap', '/api/state']); + expect(latest.controller).toBe(false); + expect(latest.busy).toBe(false); + }); + + test('a transport exception during discovery retry produces a bounded local error', async () => { + globalThis.fetch = jest.fn(async (path: string) => { + if (path === '/api/bootstrap') return response({ controller: true, capability: 'controller' }); + if (path === '/api/state') return response(view); + if (path === '/api/retry-discovery') throw 'transport unavailable'; + throw new Error('Unexpected route'); + }) as typeof fetch; + const session = createSetupSession(TEST_SESSION_KEY); + let latest = {} as { error: string; busy: boolean }; + session.subscribe(state => { latest = state; }); + await session.connect(); + await session.retryDiscovery(7); + expect(latest.error).toBe('Could not retry discovery.'); + expect(latest.busy).toBe(false); + }); + test('control transfer reconnects as read-only and does not replay an answer', async () => { let bootstraps = 0; const requests: string[] = []; @@ -470,4 +545,90 @@ describe('browser session transport', () => { globalThis.fetch = jest.fn(async () => { throw new Error('CLI stopped'); }) as typeof fetch; await expect(createSetupSession(TEST_SESSION_KEY).close()).resolves.toBeUndefined(); }); + + test('read-only doctor is available only after success to the controller, refreshes redacted results, and cannot run twice concurrently', async () => { + const complete: WebSetupView = { ...view, outcome: 'complete' }; + let releaseDoctor!: (value: Response) => void; + const pendingDoctor = new Promise(resolve => { releaseDoctor = resolve; }); + let currentView = complete; + const requests: Array<{ path: string; options?: RequestInit }> = []; + globalThis.fetch = jest.fn(async (path: string, options?: RequestInit) => { + requests.push({ path, options }); + if (path === '/api/bootstrap') return response({ controller: true, capability: 'doctor-capability' }); + if (path === '/api/state') return response(currentView); + if (path === '/api/doctor') return pendingDoctor; + throw new Error('Unexpected route'); + }) as typeof fetch; + const session = createSetupSession(TEST_SESSION_KEY); + let latest = {} as { busy: boolean; error: string; view?: WebSetupView }; + session.subscribe(next => { latest = next; }); + await session.runDoctor(); + expect(requests).toHaveLength(0); + await session.connect(); + const first = session.runDoctor(); + await session.runDoctor(); + expect(requests.filter(request => request.path === '/api/doctor')).toHaveLength(1); + expect(latest.view?.doctor?.status).toBe('running'); + expect(requests.find(request => request.path === '/api/doctor')?.options?.headers) + .toEqual(expect.objectContaining({ 'X-Setup-Capability': 'doctor-capability' })); + currentView = { ...complete, doctor: { status: 'complete', healthy: true, pass: 3, warn: 1, fail: 0, skipped: 2 } }; + releaseDoctor(response({ ok: true })); + await first; + expect(latest).toMatchObject({ busy: false, error: '', view: { doctor: { status: 'complete', pass: 3 } } }); + }); + + test('doctor failure preserves success, reports the error, and refreshes the failed status', async () => { + const complete: WebSetupView = { ...view, outcome: 'complete' }; + let currentView = complete; + globalThis.fetch = jest.fn(async (path: string) => { + if (path === '/api/bootstrap') return response({ controller: true, capability: 'doctor-capability' }); + if (path === '/api/state') return response(currentView); + if (path === '/api/doctor') { + currentView = { ...complete, doctor: { status: 'failed' } }; + return response({ error: 'Read-only diagnosis is unavailable.' }, 503); + } + throw new Error('Unexpected route'); + }) as typeof fetch; + const session = createSetupSession(TEST_SESSION_KEY); + let latest = {} as { busy: boolean; error: string; view?: WebSetupView }; + session.subscribe(next => { latest = next; }); + await session.connect(); + await session.runDoctor(); + expect(latest).toMatchObject({ busy: false, error: 'Read-only diagnosis is unavailable.', view: { outcome: 'complete', doctor: { status: 'failed' } } }); + }); + + test('Back is revision-bound, preserves the server-owned question, and handles stale control safely', async () => { + let controller = true; + let failBack = false; + const previous: WebSetupView = { ...view, promptRevision: 8, prompt: { kind: 'question', title: 'Production branch', phase: 'plan', pass: 1, question: { + stateId: 'repository', id: 'repository.mainBranch', label: 'Production branch', kind: 'text', defaultValue: 'main', + } } }; + let currentView = view; + const paths: string[] = []; + globalThis.fetch = jest.fn(async (path: string) => { + paths.push(path); + if (path === '/api/bootstrap') return response({ controller, ...(controller ? { capability: 'controller-capability' } : {}) }); + if (path === '/api/state') return response(currentView); + if (path === '/api/back') { + if (failBack) { controller = false; return response({ error: 'Control moved to another tab.' }, 403); } + currentView = previous; + return response({ accepted: true }); + } + throw new Error('Unexpected route'); + }) as typeof fetch; + const session = createSetupSession(TEST_SESSION_KEY); + let latest = {} as { controller: boolean; busy: boolean; view?: WebSetupView }; + session.subscribe(next => { latest = next; }); + await session.back(7); + expect(paths).toHaveLength(0); + await session.connect(); + await session.back(6); + expect(paths).not.toContain('/api/back'); + await session.back(7); + expect(latest).toMatchObject({ busy: false, view: { promptRevision: 8 } }); + failBack = true; + await session.back(8); + expect(latest.controller).toBe(false); + expect(paths.filter(path => path === '/api/back')).toHaveLength(2); + }); }); diff --git a/src/cli/__tests__/web_setup_catalog.test.ts b/src/cli/__tests__/web_setup_catalog.test.ts new file mode 100644 index 000000000..822723741 --- /dev/null +++ b/src/cli/__tests__/web_setup_catalog.test.ts @@ -0,0 +1,270 @@ +import { en, es, setupCatalogs, setupLocales, stageLabel, tr } from '../../../web/src/i18n/catalog'; +import { permissionName, permissionStatus, permissionTerm, permissionTermCatalogs } from '../../../web/src/i18n/permissionTerms'; +import { isQuestionOptionLocalized, optionCatalogs, questionOptionLabel } from '../../../web/src/i18n/questionOptions'; +import { setupQuestionContentInventory } from '../../application/policies/setup_questionnaire_policy'; +import { permissionCopy, permissionCopyCatalogs, isKnownPermissionCopy } from '../../../web/src/i18n/permissionCopy'; +import { permissionTexts } from '../../../web/src/i18n/permissions/en'; +import * as ts from 'typescript'; +import { readFileSync } from 'node:fs'; +import { resolve } from 'node:path'; +import { promptCopyCatalogs, localizedPromptChoice, localizedPromptCopy } from '../../../web/src/i18n/promptCopy'; +import { messageCopyCatalogs, localizedMessage } from '../../../web/src/i18n/messageCopy'; +import { localizedSessionError, sessionErrorCatalogs } from '../../../web/src/i18n/sessionErrors'; +import { localizedPlanWarning, planWarningCatalogs } from '../../../web/src/i18n/planWarningCopy'; +import { agentRoleName, agentRoleNames } from '../../../web/src/i18n/agentRoleNames'; +import { validationCopy } from '../web_setup_adapters'; + +describe('web setup localization catalog', () => { + test('English is the default and only the four selected locales are advertised', () => { + expect(setupLocales).toEqual(['en', 'es', 'fr', 'pt']); + expect(es).toBe(setupCatalogs.es); + const keys = Object.keys(en).sort(); + for (const locale of setupLocales) { + expect(Object.keys(setupCatalogs[locale]).sort()).toEqual(keys); + for (const key of keys) { + const value = setupCatalogs[locale][key as keyof typeof en]; + expect(value.trim()).not.toBe(''); + expect([...value.matchAll(/\{([a-zA-Z]\w*)\}/gu)].map(match => match[1]).sort()) + .toEqual([...en[key as keyof typeof en].matchAll(/\{([a-zA-Z]\w*)\}/gu)].map(match => match[1]).sort()); + } + } + }); + + test('unsupported runtime locale falls back to complete English copy', () => { + expect(tr('blockedTitle', 'invalid' as typeof setupLocales[number])).toBe(en.blockedTitle); + }); + + test('interpolation and stage names follow the selected locale', () => { + expect(tr('reviewPass', 'es', { pass: '2' })).toContain('pasada 2'); + expect(tr('reviewPass', 'en')).not.toContain('{pass}'); + expect(stageLabel('Setup PAT', 'fr')).toBe(tr('setupPat', 'fr')); + expect(stageLabel('unrecognized', 'en')).toBe(tr('gettingReady', 'en')); + }); + + test('public permission and prompt copy translate known text and omit missing placeholders safely', () => { + const known = 'Inspect selected Projects and their Status options; setup does not edit Project items.'; + expect(permissionCopy('es', known)).toContain('Status'); + expect(permissionCopy('en', known)).toBe(known); + expect(permissionCopy('fr', 'Provider-generated detail')).toBe(tr('permissionUnknown', 'fr')); + expect(localizedPromptCopy({ kind: 'choice', title: 'raw', choices: [], copyId: 'setupPat.confirmAccount', + copyValues: { account: 'bot' } }, 'es')?.title).toContain('bot'); + expect(localizedPromptCopy({ kind: 'choice', title: 'raw', choices: [], copyId: 'setupPat.confirmAccount' }, 'en')?.title) + .not.toContain('{account}'); + expect(localizedPromptCopy({ kind: 'choice', title: 'raw', choices: [] }, 'es')).toBeUndefined(); + expect(localizedPromptCopy({ kind: 'choice', title: 'raw', choices: [], copyId: 'credential.existing', + copyValues: { status: 'valid' } }, 'fr')?.title).toContain('Valide'); + expect(localizedPromptCopy({ kind: 'choice', title: 'raw', choices: [], copyId: 'credential.existing', + copyValues: { status: 'provider-specific' } }, 'es')?.title).toContain('provider-specific'); + expect(localizedPromptChoice({ kind: 'choice', title: 'raw', choices: ['Fallback'], copyId: 'credential.apiKey' }, 'es', 0)) + .toBe('Fallback'); + }); + + test('every known questionnaire validation has localized copy and unknown text cannot leak English into other locales', () => { + const messages = [ + 'This is the first question in this pass. Review it or cancel setup.', + 'A trusted check was selected more than once.', + 'The saved Status value is not available in every selected Project. Choose a listed Status option.', + 'Selected Projects have no common Status option. Choose compatible Projects or configure them separately.', + 'Choose at most 10 Projects; separate numbers or URLs with commas.', + 'A Project URL needs a known repository owner; enter its positive number instead.', + 'Use a GitHub Project URL belonging to acme, without query parameters.', + 'Enter a valid GitHub Project URL or positive Project number.', + 'Enter the positive Project number from its GitHub URL, not a PVT_ GraphQL ID.', + 'Project numbers must be positive integers at most 2147483647.', + 'Project 7 was selected more than once.', + ]; + for (const message of messages) { + const copy = validationCopy(message); + expect(copy).toBeDefined(); + for (const locale of ['es', 'fr', 'pt'] as const) { + expect(localizedMessage({ tone: 'warning', text: message, copyId: copy!.id, copyValues: copy!.values }, locale)) + .not.toBe(message); + } + } + expect(localizedMessage({ tone: 'warning', text: 'unexpected English diagnostic' }, 'es')).toBe(tr('unknownLocalError', 'es')); + }); + + test('every non-English preview explicitly discloses untranslated setup text', () => { + for (const locale of setupLocales.filter(item => item !== 'en')) { + expect(tr('translationPreviewTitle', locale).trim()).not.toBe(''); + expect(tr('translationPreviewBody', locale)).not.toEqual(en.translationPreviewBody); + } + }); + + test('permission metadata uses translated labels without changing permission identities', () => { + for (const section of ['names', 'terms'] as const) { + const keys = Object.keys(permissionTermCatalogs[section].en).sort(); + for (const locale of setupLocales) expect(Object.keys(permissionTermCatalogs[section][locale]).sort()).toEqual(keys); + } + for (const locale of setupLocales) { + for (const term of ['repository', 'organization', 'read', 'write', 'required', 'conditional', 'verified', 'missing', 'unverifiable'] as const) { + expect(permissionTerm(locale, term).trim()).not.toBe(''); + } + } + expect(permissionTerm('es', 'write')).toBe('Escritura'); + expect(permissionTerm('fr', 'unverifiable')).toBe('Non vérifiable'); + expect(permissionStatus('es', 'verified')).toBe('Verificado'); + expect(permissionStatus('es', 'unknown-status')).toBeUndefined(); + for (const locale of setupLocales) { + for (const name of ['Metadata', 'Contents', 'Secrets', 'Variables', 'Issues', 'Actions', 'Checks', 'Administration', 'Workflows', 'Issue Types', 'Projects', 'Pull requests', 'Members']) { + expect(permissionName(locale, name).trim()).not.toBe(''); + } + } + expect(permissionName('es', 'Checks')).toBe('Comprobaciones'); + expect(permissionName('fr', 'Workflows')).toBe('Flux de travail'); + expect(permissionName('es', 'Unknown')).toBe('Unknown'); + }); + + test('static question options have the same keys in every translated catalog', () => { + const keys = Object.keys(optionCatalogs.es).sort(); + for (const locale of ['fr', 'pt'] as const) { + expect(Object.keys(optionCatalogs[locale]).sort()).toEqual(keys); + } + for (const question of setupQuestionContentInventory()) { + for (const choice of question.choices ?? []) { + for (const locale of ['es', 'fr', 'pt'] as const) { + expect(isQuestionOptionLocalized(question.id, choice, locale)).toBe(true); + } + } + } + expect(questionOptionLabel('issueWorkflows.enabled', 'feature — Feature', 'fr')).toBe('feature — Fonctionnalité'); + expect(questionOptionLabel('repository.reconciliationCleanup', 'all', 'pt')).toBe('Todos os ramos temporários'); + expect(questionOptionLabel('agents.findings.provider', 'codex', 'es')).toBe('codex'); + expect(isQuestionOptionLocalized('pullRequestApproval.coverage.checkName', 'Coverage from GitHub', 'fr')).toBe(true); + }); + + test('plan review names every agent role in each supported language', () => { + for (const locale of setupLocales) { + expect(Object.keys(agentRoleNames[locale]).sort()).toEqual(Object.keys(agentRoleNames.en).sort()); + for (const role of ['planner', 'findings', 'reviewer', 'fixer', 'tester']) { + expect(agentRoleName(role, locale).trim()).not.toBe(''); + } + } + expect(agentRoleName('planner', 'es')).toBe('Planificador'); + expect(agentRoleName('findings', 'fr')).toBe('Analyste des problèmes'); + }); + + test('every literal permission reason and condition has exactly one translated key', () => { + const path = resolve(__dirname, '../../application/policies/setup_token_permission_policy.ts'); + const file = ts.createSourceFile(path, readFileSync(path, 'utf8'), ts.ScriptTarget.Latest, true); + const emitted = new Set(); + const collect = (node: ts.Node): void => { + if (ts.isStringLiteral(node)) emitted.add(node.text); + else ts.forEachChild(node, collect); + }; + const visit = (node: ts.Node): void => { + if (ts.isPropertyAssignment(node) && ['reason', 'condition'].includes(node.name.getText(file))) collect(node.initializer); + ts.forEachChild(node, visit); + }; + visit(file); + expect([...permissionTexts].sort()).toEqual([...emitted].sort()); + for (const locale of ['es', 'fr', 'pt'] as const) { + expect(Object.keys(permissionCopyCatalogs[locale]).sort()).toEqual([...permissionTexts].sort()); + for (const text of permissionTexts) { + expect(isKnownPermissionCopy(text)).toBe(true); + expect(permissionCopyCatalogs[locale][text].trim()).not.toBe(''); + expect(permissionCopyCatalogs[locale][text]).not.toBe(text); + } + } + }); + + test('every prompt has the same translated keys, placeholders and choice count', () => { + const ids = Object.keys(promptCopyCatalogs.en).sort(); + for (const locale of setupLocales) { + expect(Object.keys(promptCopyCatalogs[locale]).sort()).toEqual(ids); + for (const id of ids) { + const source = promptCopyCatalogs.en[id as keyof typeof promptCopyCatalogs.en]; + const copy = promptCopyCatalogs[locale][id as keyof typeof promptCopyCatalogs.en]; + expect(copy.title.trim()).not.toBe(''); + expect(copy.description.trim()).not.toBe(''); + for (const field of ['title', 'description'] as const) { + const placeholders = (value: string) => [...value.matchAll(/\{([a-zA-Z]\w*)\}/gu)].map(match => match[1]).sort(); + expect(placeholders(copy[field])).toEqual(placeholders(source[field])); + if (locale !== 'en') expect(copy[field]).not.toBe(source[field]); + } + expect(copy.choices?.length ?? 0).toBe(source.choices?.length ?? 0); + } + } + const prompt = { kind: 'choice' as const, title: 'How will you provide your setup PAT?', choices: ['Guided GitHub link', 'Manual PAT'], copyId: 'setupPat.method' as const }; + expect(localizedPromptChoice(prompt, 'fr', 0)).toBe('Lien GitHub guidé'); + expect(prompt.choices[0]).toBe('Guided GitHub link'); + }); + + test('progress and validation messages have identical semantic keys and placeholders', () => { + const ids = Object.keys(messageCopyCatalogs.en).sort(); + const placeholders = (value: string) => [...value.matchAll(/\{([a-zA-Z]\w*)\}/gu)].map(match => match[1]).sort(); + for (const locale of setupLocales) { + expect(Object.keys(messageCopyCatalogs[locale]).sort()).toEqual(ids); + for (const id of ids) { + const source = messageCopyCatalogs.en[id as keyof typeof messageCopyCatalogs.en]; + const translated = messageCopyCatalogs[locale][id as keyof typeof messageCopyCatalogs.en]; + expect(translated.trim()).not.toBe(''); + expect(placeholders(translated)).toEqual(placeholders(source)); + if (locale !== 'en') expect(translated).not.toBe(source); + } + } + expect(localizedMessage({ tone: 'info', text: 'Plan ready', copyId: 'plan.ready', copyValues: { files: '2', variables: '1', secrets: '3' } }, 'es')) + .toContain('2 archivos, 1 Variables y 3 nombres'); + const preview = localizedMessage({ tone: 'info', text: 'raw English', copyId: 'permission.preview', copyValues: { + issues: 'feature|bugfix', approval: 'guarded', secrets: 'repository', variables: 'off', projects: 'none', + } }, 'es'); + expect(preview).toContain('Funcionalidad, Corrección'); + expect(preview).toContain('Aprobar solo con garantías'); + expect(preview).not.toContain('raw English'); + expect(localizedMessage({ tone: 'warning', text: 'raw English', copyId: 'credential.checks', + copyValues: { count: '2', names: 'BOT_PAT, API_KEY' }, credentialChecks: [ + { name: 'BOT_PAT', status: 'valid' }, { name: 'API_KEY', status: 'unverifiable' }, + ] }, 'fr')).toContain('API_KEY: Invérifiable sans nouvelle valeur'); + }); + + test('local session errors have exact key parity and a translated unknown-error fallback', () => { + const keys = Object.keys(sessionErrorCatalogs.en).sort(); + for (const locale of setupLocales) { + expect(Object.keys(sessionErrorCatalogs[locale]).sort()).toEqual(keys); + for (const key of keys) { + const value = sessionErrorCatalogs[locale][key as keyof typeof sessionErrorCatalogs.en]; + expect(value.trim()).not.toBe(''); + if (locale !== 'en') expect(value).not.toBe(sessionErrorCatalogs.en[key as keyof typeof sessionErrorCatalogs.en]); + } + expect(localizedSessionError('unrecognized server detail', locale)).toBe(tr('unknownLocalError', locale)); + } + expect(localizedSessionError('Incorrect pairing code. Check the terminal.', 'es').toLowerCase()).toContain('código'); + }); + + test('every static HTTP error and final doctor/back failure has reviewed four-language copy', () => { + const server = readFileSync(resolve(__dirname, '../web_setup_server.ts'), 'utf8'); + const errors = new Set([...server.matchAll(/error:\s*'([^']+)'/gu)].map(match => match[1])); + for (const extra of ['No earlier question is available here.', 'Read-only verification failed. Check the terminal.', + 'Read-only verification is unavailable or already running.', 'Could not retry discovery.', + 'Could not return to the previous question.', 'Read-only verification failed.']) errors.add(extra); + for (const error of errors) { + expect(Object.prototype.hasOwnProperty.call(sessionErrorCatalogs.en, error)).toBe(true); + for (const locale of ['es', 'fr', 'pt'] as const) { + expect(localizedSessionError(error, locale)).not.toBe(tr('unknownLocalError', locale)); + } + } + }); + + test('all current plan warnings have exactly one translation per language', () => { + const path = resolve(__dirname, '../../application/policies/setup_configuration_plan.ts'); + const file = ts.createSourceFile(path, readFileSync(path, 'utf8'), ts.ScriptTarget.Latest, true); + const emitted = new Set(); + const visit = (node: ts.Node): void => { + if (ts.isCallExpression(node) && node.expression.getText(file) === 'warnings.push' && ts.isStringLiteral(node.arguments[0])) { + emitted.add(node.arguments[0].text); + } + ts.forEachChild(node, visit); + }; + visit(file); + expect(Object.keys(planWarningCatalogs.en).sort()).toEqual([...emitted].sort()); + for (const locale of setupLocales) { + expect(Object.keys(planWarningCatalogs[locale]).sort()).toEqual([...emitted].sort()); + for (const warning of emitted) { + const translated = localizedPlanWarning(warning, locale); + expect(translated.trim()).not.toBe(''); + if (locale !== 'en') expect(translated).not.toBe(warning); + } + } + expect(localizedPlanWarning('Unreviewed English warning', 'es')).toBe(tr('planUnknownWarning', 'es')); + }); +}); diff --git a/src/cli/__tests__/web_setup_components.test.ts b/src/cli/__tests__/web_setup_components.test.ts index ceea53876..c9e1828a2 100644 --- a/src/cli/__tests__/web_setup_components.test.ts +++ b/src/cli/__tests__/web_setup_components.test.ts @@ -1,9 +1,11 @@ import { execFileSync } from 'node:child_process'; import { resolve } from 'node:path'; +import { setupQuestionPresentation } from '../../application/policies/setup_question_guidance_policy'; +import type { SetupQuestion } from '../../domain/setup_questionnaire'; -function markup(name: string, props: Record): string { +function markup(name: string, props: Record, locale = 'en'): string { return execFileSync(process.execPath, [ - resolve(__dirname, '../../../scripts/render-web-setup-component.cjs'), name, JSON.stringify(props), + resolve(__dirname, '../../../scripts/render-web-setup-component.cjs'), name, JSON.stringify(props), locale, ], { encoding: 'utf8' }); } @@ -18,6 +20,23 @@ describe('web setup component semantics', () => { expect(html).toContain('After refreshing'); expect(html).not.toContain('setup-key'); }); + test.each([ + ['en', 'Pair this browser'], ['es', 'Vincula este navegador'], + ['fr', 'Associer ce navigateur'], ['pt', 'Emparelhar este navegador'], + ])('%s has a localized pairing screen', (locale, label) => { + const html = markup('PairingPanel', { busy: false }, locale); + expect(html).toContain(label); + expect(html).toContain('copilot setup --web'); + }); + test('language selector lists exactly the four supported languages, with English first', () => { + const html = markup('LanguageSwitch', {}); + for (const code of ['en', 'es', 'fr', 'pt']) { + expect(html).toContain(`value="${code}"`); + } + expect(html.indexOf('value="en"')).toBeLessThan(html.indexOf('value="es"')); + expect(html).not.toContain('value="ar"'); + expect(html).toContain('aria-live="polite"'); + }); test.each([ ['complete', 'Your configuration was applied', 'not revoked automatically'], ['dry-run', 'No changes were made', 'did not begin applying'], @@ -31,6 +50,154 @@ describe('web setup component semantics', () => { expect(html).toContain('Close local session'); expect(html).toContain('copilot doctor'); }); + test('completed Spanish result offers safe in-page verification and explains unverified Secret values', () => { + const before = markup('ResultPanel', { outcome: 'complete', controller: true, onClose: noOp }, 'es'); + expect(before).toContain('Comprobar instalación (solo lectura)'); + const html = markup('ResultPanel', { outcome: 'complete', controller: true, onClose: noOp, + doctor: { status: 'complete', healthy: false, pass: 4, warn: 1, fail: 0, skipped: 2 } }, 'es'); + expect(html).toContain('4 correctas'); + expect(html).toContain('Este modo no puede verificar los valores de Secrets.'); + expect(html).toContain('copilot doctor --read-only'); + expect(html).not.toContain('private GitHub diagnostic'); + }); + + test('blocked page identifies the cause and next action in the chosen language', () => { + const detail = { reasonCode: 'permissions', stoppedStage: 'Plan', mutationStarted: false }; + const html = markup('ResultPanel', { outcome: 'blocked', detail, controller: true }, 'es'); + expect(html).toContain('Faltan permisos del PAT'); + expect(html).toContain('Plan'); + expect(html).toContain('Comprueba los permisos mostrados'); + expect(html).toContain('No se iniciaron cambios'); + }); + + test('partial result shows structured cause, safe effects, and diagnostic reference', () => { + const html = markup('ResultPanel', { outcome: 'partial', controller: true, onClose: noOp, + detail: { reasonCode: 'provider', stoppedStage: 'Apply', mutationStarted: true, + diagnosticRef: '12345678-1234-4123-8123-123456789abc', + effects: [{ id: 'files', state: 'completed' }, { id: 'secret', state: 'needs-inspection' }] } }); + expect(html).toContain('GitHub or another provider did not complete'); + expect(html).toContain('Reported completed'); + expect(html).toContain('Outcome needs inspection'); + expect(html).toContain('12345678-1234-4123-8123-123456789abc'); + }); + + test('French technical question guidance is complete, not a mixed-language preview', () => { + const question: SetupQuestion = { stateId: 'pull-request-approval', id: 'pullRequestApproval.testChecks', + label: 'Trusted checks', kind: 'text', defaultValue: '' }; + const html = markup('QuestionPrompt', { prompt: { + kind: 'question', title: 'Approval', phase: 'full', pass: 1, + question, presentation: setupQuestionPresentation(question), + }, controller: true, busy: false }, 'fr'); + expect(html).toContain('Quelles vérifications CI sont fiables pour approuver ?'); + expect(html).toContain('jobs CI'); + expect(html).not.toContain('Detailed guidance below is currently available in English'); + }); + + test('question details explain where, how and why, with a safe contextual link', () => { + const html = markup('QuestionPrompt', { prompt: { + kind: 'question', title: 'Repository', phase: 'full', pass: 1, + question: { stateId: 'repository', id: 'repository.mainBranch', label: 'Production branch', kind: 'text', defaultValue: 'main' }, + presentation: { + en: { label: 'Production branch', summary: 'Choose the production branch.', when: 'Before release.', + where: 'Repository settings.', how: 'Enter its exact name.', why: 'Releases target this branch.', + example: 'main', effect: 'Changes release target.', verify: 'Review plan.', + documentation: { title: 'Copilot configuration', url: 'https://docs.page/vypdev/copilot/configuration' } }, + es: { label: 'Rama de producción', summary: 'Elige la rama de producción.', when: 'Antes de publicar.', + where: 'Configuración del repositorio.', how: 'Escribe el nombre exacto.', why: 'La publicación usa esta rama.', + example: 'main', effect: 'Cambia el destino.', verify: 'Revisa el plan.', + documentation: { title: 'Configuración de Copilot', url: 'https://docs.page/vypdev/copilot/configuration' } }, + }, + }, controller: true, busy: false }, 'es'); + expect(html).toContain('Dónde se configura'); + expect(html).toContain('Cómo elegir'); + expect(html).toContain('Por qué importa'); + expect(html).toContain('https://docs.page/vypdev/copilot/configuration'); + expect(html).toContain('rel="noopener noreferrer"'); + expect(html.indexOf('question-help-link')).toBeLessThan(html.indexOf(' { + const html = markup('QuestionPrompt', { prompt: { + kind: 'question', title: 'Projects', phase: 'full', pass: 1, + question: { stateId: 'projects', id: 'projects.ids', label: 'Projects', kind: 'project-select', defaultValue: '', + discoveryStatus: 'empty', discoveryRetryRemaining: 2, projectCandidates: [], projectOwner: 'acme' }, + }, controller: true, busy: false }, locale); + expect(html).toContain(retryLabel); + expect(html).toContain('2'); + expect(html).toContain('discovery-actions'); + }); + + test('discovery scope is specific and unsupported personal Projects do not offer retry', () => { + const checkNotice = markup('DiscoveryNotice', { kind: 'checks', status: 'observed' }, 'en'); + expect(checkNotice).toContain('20 recent pull-request workflow runs'); + expect(checkNotice).toContain('15 runs'); + const projectNotice = markup('DiscoveryNotice', { kind: 'projects', status: 'observed' }, 'en'); + expect(projectNotice).toContain('30 accessible organization Projects'); + const unsupported = markup('QuestionPrompt', { prompt: { kind: 'question', title: 'Projects', phase: 'full', pass: 1, + question: { stateId: 'projects', id: 'projects.ids', label: 'Projects', kind: 'project-select', defaultValue: '', + discoveryStatus: 'unsupported', projectCandidates: [], projectOwner: 'owner' } }, controller: true, busy: false }, 'en'); + expect(unsupported).not.toContain('Retry GitHub discovery'); + expect(unsupported).toContain('fine-grained PAT'); + }); + + test('coverage selector shows the selected producer identity and the observation limitation', () => { + const html = markup('QuestionPrompt', { prompt: { + kind: 'question', title: 'Coverage', phase: 'full', pass: 1, + question: { stateId: 'pull-request-approval', id: 'pullRequestApproval.coverage.checkName', + label: 'Coverage check', kind: 'choice', defaultValue: 'Tests', choices: ['Tests'], + trustedProducers: [{ name: 'Tests', workflowName: 'CI', sourceAppId: 12 }], + producerCandidates: [{ name: 'Tests', workflowName: 'CI', sourceAppId: 12, + runUrl: 'https://github.com/acme/repo/actions/runs/7', headSha: 'a'.repeat(40), + conclusion: 'success', observedAt: '2026-09-29T10:00:00Z' }] }, + }, controller: true, busy: false }, 'en'); + expect(html).toContain('Tests — CI · App 12'); + expect(html).toContain('2026-09-29T10:00:00Z'); + expect(html).toContain('Required by branch rule: not checked'); + }); + + test('a Project retained across discovery refresh remains visible as unverified and removable', () => { + const html = markup('ProjectSelector', { candidates: [], selected: ['12'], value: '', controller: true }, 'en'); + expect(html).toContain('#12'); + expect(html).toContain('no longer appear in this GitHub result'); + expect(html).toContain('Remove selection'); + }); + + test('manual Project Status attestation displays every transition and exact value', () => { + const question: SetupQuestion = { stateId: 'projects', id: 'projects.statusVerified', label: 'Verify Status', + kind: 'boolean', defaultValue: false, projectStatusValues: [ + { transition: 'issueCreated', value: 'Todo' }, + { transition: 'pullRequestCreated', value: 'Doing' }, + { transition: 'issueInProgress', value: 'Started' }, + { transition: 'pullRequestInProgress', value: 'Active' }, + ] }; + const html = markup('QuestionPrompt', { prompt: { kind: 'question', title: 'Projects', phase: 'full', pass: 1, + question, presentation: setupQuestionPresentation(question) }, controller: true, busy: false }, 'es'); + for (const value of ['Todo', 'Doing', 'Started', 'Active']) expect(html).toContain(value); + expect(html).toContain('Issue nuevo'); + expect(html).toContain('Pull request en curso'); + }); + + test('boolean recommendations use the selected language rather than raw true or false', () => { + const html = markup('QuestionPrompt', { prompt: { + kind: 'question', title: 'Features', phase: 'full', pass: 1, + question: { stateId: 'capabilities', id: 'features.issues', label: 'Issue automation?', kind: 'boolean', defaultValue: false }, + }, controller: true, busy: false }, 'es'); + expect(html).toContain('Respuesta sugerida: No'); + expect(html).not.toContain('Respuesta sugerida: false'); + }); + + test('choice recommendations translate their display label without changing the option value', () => { + const html = markup('QuestionPrompt', { prompt: { + kind: 'question', title: 'Provisioning', phase: 'full', pass: 1, + question: { stateId: 'provisioning', id: 'ai.provisioningMode', label: 'Provisioning mode', + kind: 'choice', defaultValue: 'always', choices: ['auto', 'always', 'disabled'] }, + }, controller: true, busy: false }, 'es'); + expect(html).toContain('Respuesta sugerida: Reinstalar siempre'); + expect(html).toContain('value="always"'); + }); test('read-only result cannot show its close control', () => { expect(markup('ResultPanel', { outcome: 'complete', controller: false, onClose: noOp })).not.toContain('Close local session'); @@ -46,6 +213,19 @@ describe('web setup component semantics', () => { expect(html.match(/disabled/g)?.length).toBeGreaterThanOrEqual(2); }); + test('non-question decisions show their purpose before the choices', () => { + const html = markup('PromptCard', { + prompt: { kind: 'choice', title: 'How will you provide your setup PAT?', + description: 'Choose the account that will configure this repository.', + choices: ['Guided GitHub link', 'Manual PAT'] }, + revision: 1, promptRevision: 1, controller: true, busy: false, + }); + expect(html).toContain('

                                                                                                                          How will you provide your setup PAT?

                                                                                                                          '); + expect(html.indexOf('How will you provide your setup PAT?')) + .toBeLessThan(html.indexOf('Guided GitHub link')); + expect(html).toContain('Choose the account that will configure this repository.'); + }); + test('secret presenter uses a password input and only an allowlisted GitHub URL', () => { const html = markup('CredentialPrompt', { prompt: { kind: 'secret', title: 'Setup PAT', link: 'https://github.com/settings/personal-access-tokens/new?name=Test' }, @@ -69,13 +249,35 @@ describe('web setup component semantics', () => { test('review plan shows only selected resource names and requires an explicit control', () => { const html = markup('PlanPrompt', { prompt: { kind: 'plan', title: 'Review', plan: { + presentationDefaults: [], + decisions: { enabledCapabilities: ['issues'], agentRouting: [{ role: 'planner', provider: 'codex', modelProvider: 'openai', model: 'o3' }], + issueWorkflows: ['bugfix'], productionBranch: 'main', developmentBranch: 'develop', + approvalMode: 'recommend', trustedChecks: [{ name: 'Tests', sourceAppId: 15368, workflowName: 'CI' }], producerAttested: true, + coverageMode: 'check', coverageCheck: 'Tests', projectNumbers: ['12'], + projectStatuses: [{ transition: 'issueCreated', value: 'Todo' }], + variableScope: 'repository', secretScope: 'repository', initialTag: false }, files: ['AGENTS.md'], workflows: ['copilot.yml'], variables: ['MAIN_BRANCH'], secrets: ['PAT'], warnings: [], } }, controller: true, busy: false, onSubmit: noOp, }); for (const item of ['AGENTS.md', 'copilot.yml', 'MAIN_BRANCH', 'PAT']) expect(html).toContain(item); + expect(html).toContain('main'); + for (const item of ['Planner', 'o3', 'Tests', '15368', 'Todo', '#12']) expect(html).toContain(item); expect(html).toContain('Approve'); }); + test('numeric coverage plan review includes the exact threshold, reporter and operator attestation in Spanish', () => { + const html = markup('PlanDecisionSummary', { decisions: { + enabledCapabilities: ['pullRequests'], issueWorkflows: [], agentRouting: [], + productionBranch: 'main', developmentBranch: 'develop', approvalMode: 'guarded', + trustedChecks: [{ name: 'Coverage', sourceAppId: 15368, workflowName: 'CI' }], producerAttested: true, + coverageMode: 'numeric', coverageCheck: 'Coverage', coverageMinimum: 87, + coverageArtifactWorkflow: 'CI', coverageReporterAttested: true, + projectNumbers: [], projectStatuses: [], variableScope: 'repository', secretScope: 'repository', initialTag: false, + } }, 'es'); + for (const value of ['Cobertura mínima de líneas modificadas', '87%', 'Workflow que publica el artefacto', + 'Generador de cobertura comprobado por ti', 'Coverage', '15368']) expect(html).toContain(value); + }); + test('status banner displays an error without turning arbitrary links into actions', () => { const html = markup('StatusBanner', { tone: 'error', title: 'Needs attention', message: 'No permission', link: 'javascript:alert(1)', @@ -98,6 +300,9 @@ describe('web setup component semantics', () => { expect(html).toContain('Bot PAT'); expect(html).toContain('Contents'); expect(html).toContain('owner/repo'); + const translated = markup('ContextPanel', { view: base }, 'es'); + expect(translated).toContain('Contenido'); + expect(translated).toContain('GitHub · Contents'); }); test('permission evidence labels missing grants instead of implying access', () => { @@ -106,8 +311,8 @@ describe('web setup component semantics', () => { applicability: 'required', reason: 'Provision Actions Secret', status: 'missing' }] }, } } }); expect(html).toContain('Setup PAT'); - expect(html).toContain('required'); - expect(html).toContain('missing'); + expect(html).toContain('Required'); + expect(html).toContain('Missing'); expect(html).toContain('Provision Actions Secret'); }); @@ -137,6 +342,74 @@ describe('web setup component semantics', () => { expect(html).toContain('Continue'); expect(html).not.toMatch(/]*disabled[^>]*>Continue/); }); + test('coverage choice links back to the selected exact CI producer', () => { + const html = markup('QuestionPrompt', { prompt: { + kind: 'question', title: 'Coverage', phase: 'full', pass: 1, + question: { stateId: 'pull-request-approval', id: 'pullRequestApproval.coverage.checkName', + label: 'Coverage check', kind: 'choice', defaultValue: 'Tests', choices: ['Tests'], + producerCandidates: [{ name: 'Tests', sourceAppId: 12, workflowName: 'CI', + runUrl: 'https://github.com/acme/repo/actions/runs/42', headSha: 'a'.repeat(40), conclusion: 'success' }] }, + }, controller: true, busy: false }); + expect(html).toContain('GitHub App 12'); + expect(html).toContain('https://github.com/acme/repo/actions/runs/42'); + }); + test('suggested check card shows observed App, revision, date, and safe run link', () => { + const html = markup('QuestionPrompt', { prompt: { + kind: 'question', title: 'Trusted checks', phase: 'full', pass: 1, + question: { stateId: 'pull-request-approval', id: 'pullRequestApproval.testChecks', + label: 'Trusted checks', kind: 'producer-select', defaultValue: '', + producerCandidates: [{ name: 'Tests', sourceAppId: 12, sourceAppName: 'GitHub Actions', workflowName: 'CI', + runUrl: 'https://github.com/acme/repo/actions/runs/42', headSha: 'a'.repeat(40), conclusion: 'success', + observedAt: '2026-09-29T00:00:00Z' }] }, + }, controller: true, busy: false }); + expect(html).toContain('GitHub Actions 12'); + expect(html).toContain('aaaaaaa'); + expect(html).toContain('https://github.com/acme/repo/actions/runs/42'); + expect(html).toContain('Check/job name'); + expect(html).toContain('Source GitHub App ID'); + expect(html).toContain('Workflow name'); + }); + + test.each([ + ['en', 'GitHub did not allow Project discovery'], + ['es', 'GitHub no permitió consultar Projects'], + ['fr', 'GitHub a refusé la découverte des Projects'], + ['pt', 'O GitHub recusou a consulta de Projects'], + ])('%s Project selector explains permission denial and manual fallback', (locale, copy) => { + const html = markup('QuestionPrompt', { prompt: { + kind: 'question', title: 'Projects', phase: 'full', pass: 1, + question: { stateId: 'projects', id: 'projects.ids', label: 'Projects', kind: 'project-select', defaultValue: '', + discoveryStatus: 'permission-denied', projectCandidates: [], projectOwner: 'acme' }, + }, controller: true, busy: false }, locale); + expect(html).toContain(copy); + expect(html).toContain('PVT_'); + expect(html).toContain('manual-project'); + }); + + test('Project cards show title, number and only safe official links', () => { + const html = markup('ProjectSelector', { candidates: [ + { title: 'Roadmap', number: 5, owner: 'acme', url: 'https://github.com/orgs/acme/projects/5', statusOptions: ['Todo'] }, + { title: '', number: 7, owner: 'acme', url: 'https://evil.example/phish' }, + ], selected: ['5'], value: '', controller: true }); + expect(html).toContain('Roadmap'); + expect(html).toContain('#5'); + expect(html).toContain('https://github.com/orgs/acme/projects/5'); + expect(html).not.toContain('https://evil.example/phish'); + expect(html).toContain('<unsafe>'); + expect(html).toContain('checked'); + }); + + test('Status choice is explicit when Project options are verified', () => { + const statusQuestion: SetupQuestion = { stateId: 'projects', id: 'projects.issueCreatedColumn', label: 'Status for new issues', + kind: 'choice', defaultValue: 'Todo', choices: ['Todo', 'In Progress'], statusOptionState: 'observed' }; + const html = markup('QuestionPrompt', { prompt: { + kind: 'question', title: 'Projects', phase: 'full', pass: 1, + question: statusQuestion, presentation: setupQuestionPresentation(statusQuestion), + }, controller: true, busy: false }, 'es'); + expect(html).toContain('Estado Status de nuevos issues'); + expect(html).toContain('value="Todo"'); + expect(html).toContain('value="In Progress"'); + }); test('successive question revisions render their own default values', () => { const question = (id: string, defaultValue: string) => ({ diff --git a/src/cli/__tests__/web_setup_server.test.ts b/src/cli/__tests__/web_setup_server.test.ts index ad91037ed..5fee80180 100644 --- a/src/cli/__tests__/web_setup_server.test.ts +++ b/src/cli/__tests__/web_setup_server.test.ts @@ -221,6 +221,25 @@ describe('local web setup server', () => { expect(await (await fetch(`${server.url}api/state`)).text()).not.toContain('ghp_private'); }); + test('allows only the paired controller to explicitly retry the current discovery revision', async () => { + const boot = await (await fetch(`${server.url}api/bootstrap`)).json() as { capability: string }; + const commit = jest.fn(); + const pending = bridge.ask({ kind: 'text', title: 'Checks' }, async () => ({ + prompt: { kind: 'text', title: 'Checks refreshed' }, commit, + })); + const revision = bridge.snapshot().promptRevision!; + expect((await jsonPost(server.url, 'api/retry-discovery', { revision }, { 'X-Setup-Capability': 'wrong' })).status).toBe(403); + expect((await jsonPost(server.url, 'api/retry-discovery', { revision: 'bad' }, { 'X-Setup-Capability': boot.capability })).status).toBe(400); + expect((await jsonPost(server.url, 'api/retry-discovery', { revision: revision + 1 }, { 'X-Setup-Capability': boot.capability })).status).toBe(409); + const response = await jsonPost(server.url, 'api/retry-discovery', { revision }, { 'X-Setup-Capability': boot.capability }); + expect(response.status).toBe(200); + expect(commit).toHaveBeenCalledTimes(1); + expect(bridge.snapshot().promptRevision).toBe(revision); + expect(bridge.snapshot().prompt?.title).toBe('Checks refreshed'); + expect((await jsonPost(server.url, 'api/answer', { revision, value: 'selected' }, { 'X-Setup-Capability': boot.capability })).status).toBe(200); + expect(await pending).toBe('selected'); + }); + test('rejects oversized answers, wrong method, and unsupported content type', async () => { const boot = await (await fetch(`${server.url}api/bootstrap`)).json() as { capability: string }; const pending = bridge.ask({ kind: 'text', title: 'Answer' }); @@ -288,6 +307,22 @@ describe('local web setup server', () => { await server.closed; }); + test('post-success read-only verification requires the controller and returns only redacted counts', async () => { + const { capability } = await (await fetch(`${server.url}api/bootstrap`)).json() as { capability: string }; + const run = jest.fn().mockResolvedValue({ healthy: false, pass: 4, warn: 1, fail: 0, skipped: 2, + secret: 'must-not-appear' }); + bridge.configureReadOnlyDoctor(run); + expect((await jsonPost(server.url, 'api/doctor', {}, { 'X-Setup-Capability': capability })).status).toBe(409); + bridge.finish('complete', 'done'); + expect((await jsonPost(server.url, 'api/doctor', {}, { 'X-Setup-Capability': 'wrong' })).status).toBe(403); + expect((await jsonPost(server.url, 'api/doctor', {}, { 'X-Setup-Capability': capability })).status).toBe(200); + const state = await (await fetch(`${server.url}api/state`)).text(); + expect(state).toContain('"warn":1'); + expect(state).not.toContain('must-not-appear'); + expect((await jsonPost(server.url, 'api/doctor', {}, { 'X-Setup-Capability': capability })).status).toBe(200); + expect(run).toHaveBeenCalledTimes(1); + }); + test.each([ [{ 'X-Forwarded-Host': 'evil.example' }, 403], [{ 'X-Forwarded-Proto': 'https' }, 403], @@ -362,6 +397,7 @@ describe('local web setup server', () => { expect((await wrongType('api/cancel')).status).toBe(415); bridge.finish('complete', 'done'); expect((await wrongType('api/close')).status).toBe(415); + expect((await wrongType('api/doctor')).status).toBe(415); }); test('mutating requests without a controller capability do nothing', async () => { @@ -370,6 +406,7 @@ describe('local web setup server', () => { const revision = bridge.snapshot().promptRevision; expect((await jsonPost(server.url, 'api/answer', { revision, value: 'ignored' })).status).toBe(403); expect((await jsonPost(server.url, 'api/cancel', {})).status).toBe(403); + expect((await jsonPost(server.url, 'api/doctor', {})).status).toBe(403); expect((await jsonPost(server.url, 'api/takeover', { code: 42 })).status).toBe(403); expect((await fetch(`${server.url}api/answer`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ revision, value: 'ignored' }) })).status).toBe(403); expect(origin).toContain('127.0.0.1'); diff --git a/src/cli/__tests__/web_setup_ui_helpers.test.ts b/src/cli/__tests__/web_setup_ui_helpers.test.ts index d455981b1..270c589c0 100644 --- a/src/cli/__tests__/web_setup_ui_helpers.test.ts +++ b/src/cli/__tests__/web_setup_ui_helpers.test.ts @@ -1,5 +1,9 @@ import type { WebSetupPrompt } from '../../application/contracts/web_setup_view'; -import { safeGithubLink } from '../../../web/src/lib/githubLink'; +import { safeGithubLink, safeGithubRunLink, safeGithubProjectLink, safeGithubRulesetLink } from '../../../web/src/lib/githubLink'; +import { checkConclusionLabel } from '../../../web/src/i18n/checkEvidence'; +import { featureName } from '../../../web/src/i18n/featureNames'; +import { focusOnRevision } from '../../../web/src/lib/focusOnRevision'; +import { safeHelpLink } from '../../../web/src/lib/helpLink'; import { initialQuestionAnswer, submittedQuestionAnswer, toggleSelection } from '../../../web/src/lib/questionAnswer'; function question(kind: Extract['question']['kind'], defaultValue: string): Extract { @@ -10,6 +14,38 @@ function question(kind: Extract['question' } describe('web setup presentation helpers', () => { + test('focus follows a new prompt revision but not background status polls', async () => { + const focus = jest.fn(); + const action = focusOnRevision({ isConnected: true, focus }, 1); + await Promise.resolve(); + expect(focus).toHaveBeenCalledTimes(1); + action.update(1); + await Promise.resolve(); + expect(focus).toHaveBeenCalledTimes(1); + action.update(2); + await Promise.resolve(); + expect(focus).toHaveBeenCalledTimes(2); + action.update(3); + action.destroy(); + await Promise.resolve(); + expect(focus).toHaveBeenCalledTimes(2); + }); + test('localizes observed check outcomes and feature names without inventing unknown outcomes', () => { + expect(checkConclusionLabel('success', 'es')).toBe('Correcto'); + expect(checkConclusionLabel('future-state', 'fr')).toBe('Résultat inconnu'); + expect(featureName('credentialHealth', 'pt')).toBe('Estado das credenciais'); + expect(featureName('future-capability', 'en')).toBe('future-capability'); + }); + + test.each([ + ['https://github.com/acme/repo/rules/7', true], + ['https://github.com/acme/repo/rules/7?token=x', false], + ['https://evil.example/acme/repo/rules/7', false], + ['https://github.com/acme/repo/rules/0', false], + ['not-a-url', false], + ])('ruleset link allowlist %s: %s', (link, allowed) => { + expect(Boolean(safeGithubRulesetLink(link))).toBe(allowed); + }); test('preselects matching multi-select defaults without selecting All', () => { expect(initialQuestionAnswer(question('multi-select', 'One,Two'))).toEqual({ value: 'One,Two', selected: ['One — details', 'Two — details'], @@ -46,6 +82,38 @@ describe('web setup presentation helpers', () => { expect(submittedQuestionAnswer(prompt, '', ['one'])).toBe('one'); }); + test('observed producer defaults select exact identities and can add a manual tuple', () => { + const prompt = { ...question('producer-select', 'Tests|12|CI'), question: { + ...question('producer-select', 'Tests|12|CI').question, + producerCandidates: [{ name: 'Tests', sourceAppId: 12, workflowName: 'CI', + runUrl: 'https://github.com/acme/repo/actions/runs/1', headSha: 'a'.repeat(40), conclusion: 'success' }], + } }; + expect(initialQuestionAnswer(prompt)).toEqual({ value: '', selected: ['Tests|12|CI'] }); + expect(submittedQuestionAnswer(prompt, 'Lint|12|CI; ', ['Tests|12|CI'])).toBe('Tests|12|CI;Lint|12|CI'); + const unmatched = { ...prompt, question: { ...prompt.question, defaultValue: 'Other|13|CI' } }; + expect(initialQuestionAnswer(unmatched)).toEqual({ value: '', selected: ['Other|13|CI'] }); + }); + + test('Project defaults distinguish discovered checkboxes from manually entered numbers', () => { + const prompt = { ...question('project-select', '2,9'), question: { + ...question('project-select', '2,9').question, id: 'projects.ids', + projectCandidates: [{ number: 2, title: 'Roadmap', owner: 'acme', url: 'https://github.com/orgs/acme/projects/2' }], + } }; + expect(initialQuestionAnswer(prompt)).toEqual({ selected: ['2'], value: '9' }); + expect(submittedQuestionAnswer(prompt, '9', ['2'])).toBe('2,9'); + expect(submittedQuestionAnswer(prompt, '', [])).toBe('none'); + }); + + test.each([ + ['https://github.com/orgs/acme/projects/2', true], + ['https://github.com/users/acme/projects/2', true], + ['https://github.com/orgs/acme/projects/2?token=x', false], + ['https://evil.example/orgs/acme/projects/2', false], + ['not-a-url', false], + ])('safe Project detail link %s: %s', (link, allowed) => { + expect(Boolean(safeGithubProjectLink(link))).toBe(allowed); + }); + test('All is mutually exclusive with individual choices', () => { expect(toggleSelection(['one'], 'All')).toEqual(['All']); expect(toggleSelection(['All'], 'one')).toEqual(['one']); @@ -71,4 +139,36 @@ describe('web setup presentation helpers', () => { ])('allowlisted GitHub link %s: %s', (link, allowed) => { expect(Boolean(safeGithubLink(link))).toBe(allowed); }); + + test.each([ + ['https://github.com/acme/repo/actions/runs/42', true], + ['https://github.com/acme/repo/actions/runs/42?x=1', false], + ['https://github.com/acme/repo/actions/runs/42#secret', false], + ['https://evil.example/acme/repo/actions/runs/42', false], + ['https://github.com/acme/repo/settings/secrets', false], + ['https://github.com@evil.example/acme/repo/actions/runs/42', false], + ['javascript:alert(1)', false], + ['not-a-url', false], + ])('CI run link allowlist %s: %s', (link, allowed) => { + expect(Boolean(safeGithubRunLink(link))).toBe(allowed); + }); + + test.each([ + ['https://docs.page/vypdev/copilot/agents/model-selection', true], + ['https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/use-secrets', true], + ['https://docs.page/vypdev/copilot/pull-requests/guarded-approval#coverage', true], + ['https://docs.page/vypdev/copilot/agents/model-selection?token=secret', false], + ['https://docs.page.evil.example/vypdev/copilot/agents', false], + ['https://docs.page@evil.example/vypdev/copilot/agents', false], + ['http://docs.page/vypdev/copilot/agents', false], + ['https://docs.github.com/other/guide', false], + ['javascript:alert(1)', false], + ['not-a-url', false], + ])('documentation link allowlist %s: %s', (link, allowed) => { + expect(Boolean(safeHelpLink(link))).toBe(allowed); + }); + + test('absent documentation link remains absent', () => { + expect(safeHelpLink(undefined)).toBeUndefined(); + }); }); diff --git a/src/cli/commands/doctor.ts b/src/cli/commands/doctor.ts index b6e0a7933..40a7bbd1a 100644 --- a/src/cli/commands/doctor.ts +++ b/src/cli/commands/doctor.ts @@ -13,9 +13,10 @@ import { SetupCredentialPromptAdapter, SetupTerminalCancelledError } from '../se export function registerDoctorCommand(program: Command): void { program .command('doctor') - .description('Verify Copilot workflows, Variables, Secrets, and setup PAT without changing repository configuration') + .description('Verify Copilot resources; use --read-only to avoid dispatching credential-health Actions') .option('-t, --token ', 'Setup PAT (or PERSONAL_ACCESS_TOKEN from the environment)') .option('--config ', 'YAML or JSON setup configuration used as the expected contract') + .option('--read-only', 'Inspect metadata and installed resources without dispatching credential-health Actions', false) .option('--non-interactive', 'Do not prompt; use --token or PERSONAL_ACCESS_TOKEN', false) .action(async options => { const terminal = options.nonInteractive ? undefined : createInteractiveTerminalDriver(); @@ -39,6 +40,7 @@ export function registerDoctorCommand(program: Command): void { repository: gitInfo.repo, setupToken: token, configuration: expected, + readOnly: Boolean(options.readOnly), }); new SetupDoctorPresenter(diagnosis.catalog).present(diagnosis.report); if (!diagnosis.report.healthy) process.exitCode = 1; diff --git a/src/cli/commands/setup.ts b/src/cli/commands/setup.ts index d25c28a8a..5d57fc8b2 100644 --- a/src/cli/commands/setup.ts +++ b/src/cli/commands/setup.ts @@ -3,7 +3,7 @@ import { runLocalAction } from '../../actions/local_action'; import { TITLE } from '../../application/contracts/product_identity'; import { getSetupToken } from '../../utils/setup_files'; import { logError, logInfo } from '../../utils/logger'; -import { getCurrentAttachedBranch, getCurrentHeadSha, getGitInfo, getGitRepositoryRoot, isGitRepositoryRoot, isInsideGitRepo } from '../../cli_context'; +import { getCurrentAttachedBranch, getCurrentHeadSha, getGitInfo, getGitRepositoryRoot, hasLocalOrTrackedGitBranch, isGitRepositoryRoot, isInsideGitRepo } from '../../cli_context'; import { buildSetupParams } from './setup_policy'; import { collectApprovalCheck, collectScope, collectSecret, loadSetupOverrides } from '../setup_command_options'; import { SetupQuestionnaireController, SetupWizardUseCase } from '../../application/usecases/setup'; @@ -17,9 +17,11 @@ import { buildWorkflowPatPermissionRequirements, } from '../../application/policies/setup_token_permission_policy'; import { createSetupCredentialsUseCase, createSetupRemoteConfigurationReadPort } from '../../infrastructure/composition/setup_credentials_composition_root'; -import { createSetupMergeQueueReadinessUseCase } from '../../infrastructure/composition/setup_doctor_composition_root'; +import { createSetupDoctorUseCase, createSetupMergeQueueReadinessUseCase } from '../../infrastructure/composition/setup_doctor_composition_root'; import { SetupDoctorWorkspaceQueryAdapter } from '../../infrastructure/setup_workspace_adapter'; import { GithubSetupApprovalReadinessAdapter } from '../../infrastructure/setup_approval_readiness_adapter'; +import { GithubSetupApprovalCheckDiscoveryAdapter } from '../../infrastructure/github_setup_approval_check_discovery_adapter'; +import { GithubSetupProjectDiscoveryAdapter } from '../../infrastructure/github_setup_project_discovery_adapter'; import type { SetupConfiguration } from '../../domain/setup'; import { ApplicationError, toApplicationError } from '../../application/errors/application_error'; import { createInteractiveTerminalDriver } from '../setup_terminal_driver'; @@ -46,6 +48,7 @@ import { WebSetupPlanConfirmation, WebSetupPlanPresenter, WebSetupQuestionnaireCollector, WebSetupWorkflowUpdatePrompt, } from '../web_setup_adapters'; +import { setupActionResultFailure, setupResultEffects, setupResultReason } from '../setup_result_receipt'; export function registerSetupCommand(program: Command): void { program @@ -138,7 +141,7 @@ export function registerSetupCommand(program: Command): void { journey = new SetupJourneyUseCase(`${gitInfo.owner}/${gitInfo.repo}`, webBridge ? new WebSetupJourneyPresenter(webBridge) : new ConsoleSetupJourneyPresenter()); if (webBridge) { - const target = await webBridge.ask({ kind: 'confirm', title: 'Confirm this repository', + const target = await webBridge.ask({ kind: 'confirm', title: 'Confirm this repository', copyId: 'repository.confirm', copyValues: { repository: `${gitInfo.owner}/${gitInfo.repo}`, branch: initialBranch ?? '' }, description: `This local checkout resolves to ${gitInfo.owner}/${gitInfo.repo} on branch ${initialBranch}. Confirm the target before configuring PAT access or files.`, choices: ['Yes, this is my repository', 'Stop and choose another checkout'] }); if (target === undefined) throw new SetupTerminalCancelledError(); @@ -147,10 +150,21 @@ export function registerSetupCommand(program: Command): void { journey.advance('choices'); } const overrides = loadSetupOverrides(options); + let presentationMode: 'basic' | 'custom' = 'custom'; + if (!options.nonInteractive && !options.dryRun) { + if (webBridge) { + const depth = await webBridge.ask({ kind: 'choice', title: 'Choose setup detail', copyId: 'setup.depth', + choices: ['Basic guided setup', 'Customize every setting'], defaultValue: 'Basic guided setup' }); + if (depth === undefined) throw new SetupTerminalCancelledError(); + presentationMode = depth === 'Basic guided setup' ? 'basic' : 'custom'; + } else if (credentialPrompt instanceof SetupCredentialPromptAdapter) { + presentationMode = await credentialPrompt.chooseSetupPresentationMode(); + } + } let setupPatPermissions = buildSetupPatPermissionRequirements(); let token = getSetupToken(cwd, options.token); if (webBridge && token) { - const choice = await webBridge.ask({ kind: 'choice', title: 'An environment setup PAT is available', + const choice = await webBridge.ask({ kind: 'choice', title: 'An environment setup PAT is available', copyId: 'setup.environmentPat', description: 'Its value stays in the CLI process and is never sent to this page. Exiting Copilot cannot unset your parent shell variable.', choices: ['Use the environment PAT', 'Create or enter a different PAT'] }); if (choice === undefined) throw new SetupTerminalCancelledError(); @@ -159,7 +173,7 @@ export function registerSetupCommand(program: Command): void { if (token || options.nonInteractive) permissionPresenter.showDetailedRequirements('setup', setupPatPermissions); else permissionPresenter.showRequirements('setup', setupPatPermissions); let setupPatAccount: string | undefined; - let permissionIntent: { draft: SetupConfiguration; answeredQuestionIds: readonly string[] } | undefined; + let permissionIntent: { draft: SetupConfiguration; answeredQuestionIds: readonly string[]; projectsWanted: boolean } | undefined; let assertedOwnerKind: 'Organization' | 'User' | undefined; if (!token && !options.nonInteractive && !options.dryRun) { if (await credentialPrompt.chooseSetupPatMethod() === 'guided') { @@ -170,14 +184,20 @@ export function registerSetupCommand(program: Command): void { .collect(initial, context), chooseOwnerKind: () => credentialPrompt.chooseSetupOwnerKind(), review: () => credentialPrompt.reviewSetupPatIntent(), - showPreview: ({ draft, requirements, uncertain, ownerConflict, errors, pass }) => { + showPreview: ({ draft, requirements, uncertain, ownerConflict, errors, pass, projectsWanted }) => { if (ownerConflict) logInfo('This plan selects organization storage or Projects, but the owner was declared a personal account. Revise the choices or use the manual PAT path.'); if (errors.length) logInfo(`The selected local configuration needs correction before a guided link can be generated:\n${errors.map(item => ` - ${item}`).join('\n')}`); if (pass > 1) logInfo('Choice review complete. Returning to setup PAT permission review.'); logInfo('Permission intent:'); logInfo(` Initial tag: ${draft.createInitialTag ? 'yes' : 'no'}; issue workflows: ${draft.features.issues ? draft.issueWorkflows.enabled.join(', ') || 'none' : 'disabled'}; PR approval: ${draft.pullRequestApproval.mode}`); - logInfo(` Secrets: ${draft.manageRepositorySecrets ? draft.storage.secrets.defaultScope : 'off'}; Variables: ${draft.manageRepositoryVariables ? draft.storage.variables.defaultScope : 'off'}; Projects: ${draft.projects.ids.trim() || 'none'}`); - webBridge?.message(`Permission preview: issue workflows ${draft.features.issues ? draft.issueWorkflows.enabled.join(', ') || 'none' : 'disabled'}; PR approval ${draft.pullRequestApproval.mode}; Secrets ${draft.manageRepositorySecrets ? draft.storage.secrets.defaultScope : 'off'}; Variables ${draft.manageRepositoryVariables ? draft.storage.variables.defaultScope : 'off'}; Projects ${draft.projects.ids.trim() || 'none'}.`, 'info'); + logInfo(` Secrets: ${draft.manageRepositorySecrets ? draft.storage.secrets.defaultScope : 'off'}; Variables: ${draft.manageRepositoryVariables ? draft.storage.variables.defaultScope : 'off'}; Projects: ${projectsWanted ? 'yes (choose exact Projects after PAT)' : 'none'}`); + webBridge?.message(`Permission preview: issue workflows ${draft.features.issues ? draft.issueWorkflows.enabled.join(', ') || 'none' : 'disabled'}; PR approval ${draft.pullRequestApproval.mode}; Secrets ${draft.manageRepositorySecrets ? draft.storage.secrets.defaultScope : 'off'}; Variables ${draft.manageRepositoryVariables ? draft.storage.variables.defaultScope : 'off'}; Projects ${projectsWanted ? 'yes (choose after PAT)' : 'none'}.`, 'info', undefined, 'permission.preview', { + issues: draft.features.issues ? draft.issueWorkflows.enabled.join('|') || 'none' : 'disabled', + approval: draft.pullRequestApproval.mode, + secrets: draft.manageRepositorySecrets ? draft.storage.secrets.defaultScope : 'off', + variables: draft.manageRepositoryVariables ? draft.storage.variables.defaultScope : 'off', + projects: projectsWanted ? 'yes' : 'none', + }); permissionPresenter.showRequirements('setup', requirements); if (uncertain.length) logInfo(`May need after GitHub inspection:\n${uncertain.map(item => ` - ${item}`).join('\n')}`); }, @@ -257,6 +277,8 @@ export function registerSetupCommand(program: Command): void { remoteConfiguration: remoteConfigurationReader, mergeQueueReadiness: createSetupMergeQueueReadinessUseCase(), approvalReadiness: new GithubSetupApprovalReadinessAdapter(), + approvalCheckDiscovery: new GithubSetupApprovalCheckDiscoveryAdapter(), + projectDiscovery: new GithubSetupProjectDiscoveryAdapter(), }); const result = await wizard.execute({ mode: options.nonInteractive ? 'non-interactive' : 'interactive', @@ -265,6 +287,8 @@ export function registerSetupCommand(program: Command): void { skipRepositoryVariables: Boolean(options.skipVariables), skipRepositorySecrets: Boolean(options.skipSecrets), previewOnly: Boolean(options.dryRun), + presentationMode, + developmentBranchObservedLocally: hasLocalOrTrackedGitBranch(cwd, overrides.repository?.developmentBranch ?? 'develop'), ...(token ? { remoteTarget: { owner: gitInfo.owner, repository: gitInfo.repo, token } } : {}), }); if (result.status === 'cancelled') { @@ -277,6 +301,7 @@ export function registerSetupCommand(program: Command): void { } if (result.status === 'blocked') { journey?.finish('blocked'); + webBridge?.resultReason(result.reason === 'setup-permissions-unavailable' ? 'permissions' : 'storage'); logError(new ApplicationError( result.reason === 'setup-permissions-unavailable' ? 'authorization.credential-invalid' : 'provider.unavailable', `${result.reason === 'setup-permissions-unavailable' @@ -349,7 +374,7 @@ export function registerSetupCommand(program: Command): void { } const authorization = await new VerifyWebSetupApplyUseCase({ confirm: async () => { - const answer = await webBridge.ask({ kind: 'confirm', title: 'Apply this setup now?', + const answer = await webBridge.ask({ kind: 'confirm', title: 'Apply this setup now?', copyId: 'apply.confirm', description: 'This is the final approval. Local files and selected GitHub resources may change. A partial result may require inspection before retrying.', choices: ['Apply setup', 'Stop without applying'] }); return answer === undefined ? undefined : answer === 'Apply setup' ? 'apply' : 'stop'; @@ -390,15 +415,30 @@ export function registerSetupCommand(program: Command): void { journey?.markMutationStarted(); setupApplyStarted = true; const actionResults = await runLocalAction(params); + webBridge?.effects(setupResultEffects(actionResults)); if (actionResults.some(actionResult => !actionResult.success || actionResult.errors.length > 0)) { + const failure = setupActionResultFailure(actionResults); + if (failure) webBridge?.resultReason(failure.reasonCode, failure.diagnosticRef); journey?.finish('partial'); logInfo('Setup reported failures or partial completion. If a bot PAT was supplied, its Secret may already have been written; inspect the result and GitHub Secret name/scope before retrying or revoking it.'); process.exitCode = 1; } else { + if (webBridge && token) { + const doctorToken = token; + webBridge.configureReadOnlyDoctor(async () => { + const diagnosis = await createSetupDoctorUseCase().execute({ owner: gitInfo.owner, + repository: gitInfo.repo, setupToken: doctorToken, configuration, readOnly: true }); + return { healthy: diagnosis.report.healthy, ...diagnosis.report.totals }; + }); + } journey?.finish('complete'); } } catch (error) { journey?.finish(setupMutationStarted ? 'partial' : error instanceof SetupTerminalCancelledError ? 'cancelled' : 'blocked'); + const normalizedError = error instanceof SetupTerminalCancelledError ? undefined + : toApplicationError(error, 'workflow.failed', 'Setup failed.'); + webBridge?.resultReason(error instanceof SetupTerminalCancelledError ? 'cancelled' + : setupResultReason(normalizedError!.code), normalizedError?.correlationId); if (setupMutationStarted && !setupApplyStarted) { logInfo('A temporary credential-health workflow create was attempted before Apply. Inspect the selected branch and GitHub workflow history before retrying; a failed request may still have reached GitHub.'); } @@ -424,7 +464,7 @@ export function registerSetupCommand(program: Command): void { webBridge.finish(outcome, outcome === 'complete' ? 'Setup completed. Delete the temporary setup PAT in GitHub; keep the bot PAT while its Secret is in use.' : outcome === 'dry-run' ? 'Dry run complete. No files or GitHub resources changed.' - : outcome === 'partial' ? 'Setup may be partial. Inspect GitHub resources and run copilot doctor before retrying.' + : outcome === 'partial' ? 'Setup may be partial. Inspect GitHub resources and run copilot doctor --read-only before retrying.' : 'No further setup changes will be applied. Any PAT already created in GitHub still exists until you delete it there.'); logInfo('The local browser page shows the result. Choose “Close local session” there, or stop this command with Ctrl+C.'); await webServer.closed; diff --git a/src/cli/setup_confirmation_adapter.ts b/src/cli/setup_confirmation_adapter.ts index 4682085dd..547b0ee38 100644 --- a/src/cli/setup_confirmation_adapter.ts +++ b/src/cli/setup_confirmation_adapter.ts @@ -3,7 +3,9 @@ import type { TerminalDriver, } from '../application/ports/setup_terminal_ports'; import type { SetupPlan } from '../domain/setup'; -import { color } from './setup_prompt_rendering'; +import { color, renderBox } from './setup_prompt_rendering'; +import { setupEditableGroups, setupQuestionnaireStateLabel } from '../application/policies/setup_questionnaire_policy'; +import type { SetupQuestion } from '../domain/setup_questionnaire'; export class SetupPlanConfirmationAdapter implements SetupPlanConfirmationPort { constructor( @@ -12,17 +14,39 @@ export class SetupPlanConfirmationAdapter implements SetupPlanConfirmationPort { ) {} async confirm(plan: SetupPlan): Promise< - { kind: 'approved' } | { kind: 'declined' } | { kind: 'cancelled' } + { kind: 'approved' } | { kind: 'declined' } | { kind: 'cancelled' } | { kind: 'revise'; group: SetupQuestion['stateId'] } > { if (this.assumeYes) return { kind: 'approved' }; if (!this.terminal) return { kind: 'declined' }; - const target = plan.configuration.manageRepositoryVariables - ? 'the repository and GitHub Variables' - : 'the repository'; + const target = plan.configuration.manageRepositoryVariables || plan.configuration.manageRepositorySecrets + ? 'repository files and selected GitHub Actions resources' + : 'repository files'; + const groups = setupEditableGroups(plan.configuration); while (true) { - const result = await this.terminal.readText(`Apply this setup plan to ${target}? ${color('[N]', 90)}: `); + const result = await this.terminal.readText(`Apply this setup plan to ${target}? Type ? for details or :edit to change an answer. ${color('[N]', 90)}: `); if (result.kind !== 'value') return { kind: 'cancelled' }; const value = result.value.normalize('NFKC').trim().toLowerCase(); + if (value === '?') { + console.log(renderBox([ + `This is the final approval. The plan lists ${plan.selectedFiles.length} file(s), ${plan.variables.length} Variable(s), and ${plan.requiredSecrets.length} Secret name(s).`, + 'Yes starts the listed local and GitHub setup writes. No leaves the plan unapplied.', + 'A failure after writes begin may leave partial changes; inspect the result and run copilot doctor --read-only before retrying.', + 'PATs created on GitHub are not deleted automatically if you decline or cancel.', + 'Read more: https://docs.page/vypdev/copilot/how-to-use', + ].join('\n'), 'Before applying setup')); + continue; + } + if (value === ':edit') { + console.log(groups.map((group, index) => ` ${index + 1}) ${setupQuestionnaireStateLabel(group)}`).join('\n')); + const selected = await this.terminal.readText('Choose a section number (empty returns to the plan): '); + if (selected.kind !== 'value') return { kind: 'cancelled' }; + const index = Number(selected.value.trim()) - 1; + if (/^[1-9]\d*$/u.test(selected.value.trim()) && Number.isSafeInteger(index) && groups[index]) { + return { kind: 'revise', group: groups[index] }; + } + if (selected.value.trim()) console.log(color('Choose one of the listed section numbers.', 33)); + continue; + } if (!value || ['n', 'no', 'false', '0'].includes(value)) return { kind: 'declined' }; if (['y', 'yes', 'true', '1'].includes(value)) return { kind: 'approved' }; console.log(color('Enter yes or no.', 33)); diff --git a/src/cli/setup_credential_prompt_adapter.ts b/src/cli/setup_credential_prompt_adapter.ts index c09c43872..953e9cfbd 100644 --- a/src/cli/setup_credential_prompt_adapter.ts +++ b/src/cli/setup_credential_prompt_adapter.ts @@ -16,6 +16,9 @@ import { SetupInteractionCancelledError } from '../application/errors/setup_inte /** @deprecated Use the presentation-neutral cancellation signal in new adapters. */ export const SetupTerminalCancelledError = SetupInteractionCancelledError; +const AUTHENTICATION_GUIDE = 'https://docs.page/vypdev/copilot/authentication'; +const GITHUB_PAT_SETTINGS = 'https://github.com/settings/personal-access-tokens'; + export class SetupCredentialPromptAdapter implements SetupCredentialPromptPort { private setupPatGuide?: string; private workflowPatGuide?: string; @@ -33,16 +36,25 @@ export class SetupCredentialPromptAdapter implements SetupCredentialPromptPort { configureSetupPatGuide(url: string): void { this.setupPatGuide = url; } get usedGuidedSetupPat(): boolean { return this.guidedSetup; } + async chooseSetupPresentationMode(): Promise<'basic' | 'custom'> { + if (!this.terminal) return 'custom'; + const choice = await this.readChoice('How much configuration detail would you like to review now?', + ['Basic guided setup', 'Customize every setting'], 'Basic guided setup', + 'Basic keeps every permission, security, branch-role, Projects, approval, and storage decision visible. It uses existing defaults for selected advanced agent, branch-prefix, and Bugbot settings. The final plan shows their consequences and lets you edit any section before Apply. Customize asks every applicable question. Neither path changes GitHub before your final approval.'); + return choice === 'Basic guided setup' ? 'basic' : 'custom'; + } async chooseSetupPatMethod(): Promise<'guided' | 'manual'> { if (!this.terminal) return 'manual'; this.setupMethodChosen = true; - this.guidedSetup = (await this.readChoice('How would you like to provide the setup PAT?', ['guided link', 'manual PAT'], 'guided link')) === 'guided link'; + this.guidedSetup = (await this.readChoice('How would you like to provide the setup PAT?', ['guided link', 'manual PAT'], 'guided link', + `Guided opens GitHub's official fine-grained PAT form with proposed grants. Manual means you create the PAT yourself and enter it here. In either case GitHub handles account sign-in and 2FA; Copilot never revokes the token automatically.\nRead more: ${AUTHENTICATION_GUIDE}`)) === 'guided link'; return this.guidedSetup ? 'guided' : 'manual'; } useManualSetupPat(): void { this.guidedSetup = false; this.setupPatGuide = undefined; this.setupMethodChosen = true; } async chooseSetupOwnerKind(): Promise<'Organization' | 'User' | 'unknown'> { if (!this.terminal) return 'unknown'; - const choice = await this.readChoice('Is the GitHub repository owner an organization or a personal account?', ['organization', 'personal account', 'not sure']); + const choice = await this.readChoice('Is the GitHub repository owner an organization or a personal account?', ['organization', 'personal account', 'not sure'], undefined, + `The owner is the name before / in owner/repository. Organization-owned repositories can require organization-level grants or SSO approval; a personal account cannot. Check the repository header on GitHub if unsure.\nRead more: ${AUTHENTICATION_GUIDE}`); return choice === 'organization' ? 'Organization' : choice === 'personal account' ? 'User' : 'unknown'; } async reviewSetupPatIntent(): Promise<'continue' | 'revise' | 'manual' | 'details'> { @@ -50,6 +62,8 @@ export class SetupCredentialPromptAdapter implements SetupCredentialPromptPort { const choice = await this.readChoice( 'Review these intended grants before opening GitHub. What would you like to do?', ['continue to GitHub', 'review all setup choices again', 'view full permission table', 'enter a PAT manually'], + undefined, + `These grants are provisional: your choices and GitHub visibility determine the final least-privilege PAT permissions. Reviewing choices does not restart this setup run or apply changes.\nRead more: ${AUTHENTICATION_GUIDE}`, ); if (choice === 'review all setup choices again') return 'revise'; if (choice === 'view full permission table') return 'details'; @@ -67,7 +81,8 @@ export class SetupCredentialPromptAdapter implements SetupCredentialPromptPort { if (!this.guidedSetup || !this.terminal) return true; if (!account || !/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/.test(account)) return false; console.log(`GitHub authenticated the setup PAT as @${account}.`); - return (await this.readChoice('Is this the account you intended to configure with?', ['yes', 'no'], 'yes')) === 'yes'; + return (await this.readChoice('Is this the account you intended to configure with?', ['yes', 'no'], 'yes', + `Use the operator account that is authorized to configure this repository and organization. A different account's PAT may have different access even if the form looked correct. Select no to stop safely.\nRead more: ${AUTHENTICATION_GUIDE}`)) === 'yes'; } showSetupPatCleanupReminder(): void { @@ -96,7 +111,8 @@ export class SetupCredentialPromptAdapter implements SetupCredentialPromptPort { async requestSetupPat(): Promise { if (!this.terminal) return undefined; if (this.setupPatGuide && !this.setupMethodChosen) { - this.guidedSetup = (await this.readChoice('How would you like to provide the setup PAT?', ['guided link', 'manual PAT'], 'guided link')) === 'guided link'; + this.guidedSetup = (await this.readChoice('How would you like to provide the setup PAT?', ['guided link', 'manual PAT'], 'guided link', + `Guided opens GitHub's official form; manual uses a PAT you made yourself. Both are entered only into this local command.\nRead more: ${AUTHENTICATION_GUIDE}`)) === 'guided link'; if (this.guidedSetup) { console.log(renderBox( 'Provisional link: Open this GitHub link in your browser, sign in as the account configuring this repository, complete any 2FA or SSO, and review the prefilled fine-grained permissions. GitHub owns token creation; Copilot never handles your web session. Change All repositories to Only select repositories and select ONLY this repository. Remote inspection may require a corrected token later.', @@ -119,6 +135,7 @@ export class SetupCredentialPromptAdapter implements SetupCredentialPromptPort { 'Setup PAT', 33, )); + console.log(`PAT creation and cleanup: ${AUTHENTICATION_GUIDE}\nGitHub PAT settings: ${GITHUB_PAT_SETTINGS}`); return this.readSecret('Setup PAT'); } @@ -160,6 +177,7 @@ export class SetupCredentialPromptAdapter implements SetupCredentialPromptPort { 33, )); console.log(`Credential options: ${requirements.map((requirement) => requirement.name).join(', ')}`); + console.log(`Why these credentials are separate: ${AUTHENTICATION_GUIDE}`); } async requestWorkflowPat( @@ -169,7 +187,8 @@ export class SetupCredentialPromptAdapter implements SetupCredentialPromptPort { if (this.terminal && !this.credentialValues[requirement.name]?.trim() && this.workflowPatGuide) { let choice: string; do { - choice = await this.readChoice('How would you like to provide the bot workflow PAT?', ['guided link', 'manual PAT', 'view full permission table'], 'guided link'); + choice = await this.readChoice('How would you like to provide the bot workflow PAT?', ['guided link', 'manual PAT', 'view full permission table'], 'guided link', + `Use a PAT from the dedicated bot account, not the operator setup PAT. Guided opens GitHub's form; manual keeps the permission table visible. The bot PAT is installed as an Actions Secret only after Apply.\nRead more: ${AUTHENTICATION_GUIDE}`); if (choice === 'view full permission table' && this.workflowPatRequirements) { console.log(renderSetupTokenPermissionRequirements('workflow', this.workflowPatRequirements)); } @@ -195,9 +214,13 @@ export class SetupCredentialPromptAdapter implements SetupCredentialPromptPort { private async readBotLogin(): Promise { while (true) { - const result = await this.terminal!.readText('Expected GitHub bot login (without @): '); + const result = await this.terminal!.readText('Expected GitHub bot login (without @; type ? for help): '); if (result.kind !== 'value') throw new SetupTerminalCancelledError(); const login = result.value.trim(); + if (login === '?') { + console.log(renderBox(`Enter the exact GitHub username of the separate bot account, without @. Copilot resolves its numeric account ID and compares it with the PAT before any Secret is written. It does not sign in as the bot or store its web credentials.\nRead more: ${AUTHENTICATION_GUIDE}`, 'About the bot account')); + continue; + } if (/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/.test(login)) return login; console.log(color('Enter a valid GitHub account login.', 33)); } @@ -221,6 +244,7 @@ export class SetupCredentialPromptAdapter implements SetupCredentialPromptPort { `How should Copilot handle the existing ${requirement.name}?`, ['keep', 'replace', 'skip'], check.status === 'valid' ? 'keep' : 'replace', + `Keep retains the existing Secret; GitHub does not reveal its value for inspection. Replace asks for a new credential and may update the Secret after Apply. Skip leaves this optional credential unconfigured. Check the plan before approving writes.\nRead more: ${AUTHENTICATION_GUIDE}`, ) as Promise; } @@ -256,6 +280,7 @@ export class SetupCredentialPromptAdapter implements SetupCredentialPromptPort { label: string, choices: readonly string[], defaultValue?: string, + help?: string, ): Promise { while (true) { const lines = choices.map((choice, index) => @@ -263,9 +288,14 @@ export class SetupCredentialPromptAdapter implements SetupCredentialPromptPort { const result = await this.terminal!.readText([ label, ...lines, + ...(help ? ['Type ? for more detail without selecting an answer.'] : []), `Select 1-${choices.length}${defaultValue ? ` ${color(`[${choices.indexOf(defaultValue) + 1}]`, 90)}` : ''}: `, ].join('\n')); if (result.kind !== 'value') throw new SetupTerminalCancelledError(); + if (result.value.trim() === '?' && help) { + console.log(renderBox(help, 'About this credential choice')); + continue; + } if (!result.value.trim() && defaultValue) return defaultValue; const index = Number(result.value) - 1; if (Number.isInteger(index) && choices[index]) return choices[index]; diff --git a/src/cli/setup_plan_presenter.ts b/src/cli/setup_plan_presenter.ts index 2e1a6af39..865e5a03e 100644 --- a/src/cli/setup_plan_presenter.ts +++ b/src/cli/setup_plan_presenter.ts @@ -36,6 +36,8 @@ export function renderSetupPlan(plan: SetupPlan): string { ` Outcome: ${approval.mode === 'off' ? 'disabled' : approval.mode === 'recommend' ? 'recommendation only' : 'eligible PRs may be approved after default-branch installation and live evidence'}`, ' Native approval still requires readable stale-dismissal rules and a distinct runtime PAT bot.', '', color('Repository changes', 36), + ` Production/development branches: ${plan.configuration.repository.mainBranch} / ${plan.configuration.repository.developmentBranch}`, + ` Projects: ${plan.configuration.projects.ids || '(none)'}`, ` Files selected: ${plan.selectedFiles.length}`, ` Variables to upsert: ${plan.configuration.manageRepositoryVariables ? plan.variables.length : 0}`, ` Secret options to validate/provision: ${plan.configuration.manageRepositorySecrets ? plan.credentialRequirements.length : 0}`, @@ -43,6 +45,8 @@ export function renderSetupPlan(plan: SetupPlan): string { ` Secret storage: ${storageLabel(plan.configuration.storage.secrets)}`, ' Labels and issue types: always checked by Copilot setup', ` Initial tag: ${plan.configuration.createInitialTag ? 'v1.0.0 when no version tag exists' : 'disabled'}`, '', + ...(plan.presentationDefaults?.length ? [color('Advanced defaults retained in basic setup', 36), + ...plan.presentationDefaults.map(item => ` ${item.group}: ${item.count} settings not asked; use :edit at plan confirmation to review or change.`), ''] : []), ...(plan.mergeQueueReadiness.length > 0 ? [ color('Merge queue readiness', 36), ...plan.mergeQueueReadiness.map((check) => ` ${doctorIcon(check.status)} ${check.id}: ${check.summary}`), diff --git a/src/cli/setup_question_renderer.ts b/src/cli/setup_question_renderer.ts index ba2a01f60..5e8ba222a 100644 --- a/src/cli/setup_question_renderer.ts +++ b/src/cli/setup_question_renderer.ts @@ -1,7 +1,8 @@ import type { SetupQuestionRenderer } from '../application/ports/setup_terminal_ports'; -import type { SetupQuestion } from '../domain/setup_questionnaire'; +import type { SetupQuestion, SetupQuestionnaireProgress } from '../domain/setup_questionnaire'; import { color, renderBox } from './setup_prompt_rendering'; import { setupQuestionnaireStateLabel } from '../application/policies/setup_questionnaire_policy'; +import { setupQuestionPresentation } from '../application/policies/setup_question_guidance_policy'; export class ConsoleSetupQuestionRenderer implements SetupQuestionRenderer { constructor( @@ -39,28 +40,67 @@ export class ConsoleSetupQuestionRenderer implements SetupQuestionRenderer { console.log(color(`\n${setupQuestionnaireStateLabel(stateId)}\n`, 36)); } - renderPrompt(question: SetupQuestion): string { + renderPrompt(question: SetupQuestion, progress?: SetupQuestionnaireProgress): string { + const help = setupQuestionPresentation(question).en; + const step = progress ? `${setupQuestionnaireStateLabel(progress.group)} — question ${progress.groupPosition} of ${progress.groupTotal} (overall ${progress.position} of ${progress.total}).\n` : ''; + const source = question.suggestionSource === 'github' ? ' (observed from authenticated GitHub repository metadata)' + : question.suggestionSource === 'local' ? ' (observed in this local checkout; confirm it exists on GitHub)' + : question.suggestionSource === 'configuration' ? ' (provided by your configuration)' + : question.suggestionSource === 'default' ? ' (product default; not verified against GitHub)' : ''; + const heading = `${step}${question.label}\n ${help.summary}\n Suggested: ${formatDefault(question.defaultValue)}${source}. ${help.documentation.title}: ${help.documentation.url}\n Type ? for detailed help; type :back to return to the previous question without clearing saved answers.${discoveryNote(question)}`; + const reviewedStatuses = question.projectStatusValues?.map(item => ` ${item.transition}: ${item.value}`).join('\n'); const fallback = formatDefault(question.defaultValue); if (question.kind === 'choice') { const choices = question.choices ?? []; const lines = choices.map((choice, index) => - ` ${index + 1}) ${choice}${choice === question.defaultValue ? color(' (default)', 90) : ''}`); - return [question.label, ...lines, `Select 1-${choices.length} ${color(`[${choices.indexOf(String(question.defaultValue)) + 1}]`, 90)}: `].join('\n'); + ` ${index + 1}) ${choice}${question.id === 'pullRequestApproval.coverage.checkName' + ? (() => { const producer = question.trustedProducers?.find(item => item.name === choice); + return producer ? ` — ${producer.workflowName} · App ${producer.sourceAppId}` : ''; })() : ''}${choice === question.defaultValue ? color(' (default)', 90) : ''}`); + return [heading, ...lines, `Select 1-${choices.length} ${color(`[${choices.indexOf(String(question.defaultValue)) + 1}]`, 90)}: `].join('\n'); } if (question.kind === 'multi-select') { - const selected = new Set(formatDefault(question.defaultValue).split(',').map(item => item.trim()).filter(Boolean)); - const choices = question.choices ?? []; - const lines = choices.map((choice, index) => { - const workflowId = choice === 'All' ? 'all' : choice.split(' — ')[0]; - const checked = selected.has('all') || selected.has(workflowId) ? '●' : '○'; - return ` ${checked} ${index === 0 ? 'All' : choice}`; - }); - return [question.label, ...lines, 'Use ↑/↓ and Space to toggle; Enter to confirm.'].join('\n'); + return [heading, 'Use ↑/↓ and Space to toggle; Enter to confirm. Press ? for help or B for the previous question.'].join('\n'); + } + if (question.kind === 'producer-select') { + const choices = question.producerCandidates ?? []; + const lines = choices.map((candidate, index) => + ` ${index + 1}) ${candidate.name} · App ${candidate.sourceAppId} · ${candidate.workflowName} · ${candidate.conclusion} · ${candidate.headSha.slice(0, 7)} · ${candidate.observedAt ?? 'date unavailable'}\n ${candidate.runUrl}\n ${candidate.requiredByRuleset ? `Required on ${candidate.requiredByRuleset.branch} by active ruleset: ${candidate.requiredByRuleset.sourceUrl}` : 'Required by branch rule: not checked'}`); + return [heading, ...lines, 'Enter check numbers separated by commas (for example 1,2), or exact name|App ID|workflow tuples separated by semicolons.', + 'A listed ruleset proves only the exact required check/App pair on that target branch. "Not checked" is not evidence that the check is optional; inspect branch protection too.', + 'A suggested check is not proof of coverage. Inspect its workflow and required step before attesting.', + ` ${color(`[${fallback}]`, 90)}: `].join('\n'); + } + if (question.kind === 'project-select') { + return [heading, + 'Use ↑/↓ and Space to choose Projects; B returns to the previous question. Their numbers come from the GitHub URL, not PVT_ node IDs.', + 'Select "Manual entry" if a Project is missing. Select "Retry" to query GitHub again without restarting setup.', + 'All selected Projects must share each chosen Status value; this setup cannot map different values per Project.'].join('\n'); } if (question.kind === 'scope-overrides' && question.allowedNames?.length) { - return `${question.label}\n Available: ${question.allowedNames.join(', ')}; enter "none" to inherit all\n ${color(`[${fallback}]`, 90)}: `; + return `${heading}\n Available: ${question.allowedNames.join(', ')}; enter "none" to inherit all\n ${color(`[${fallback}]`, 90)}: `; } - return `${question.label} ${color(`[${fallback}]`, 90)}: `; + return `${heading}${reviewedStatuses ? `\n Verify these exact Status values in every selected Project:\n${reviewedStatuses}` : ''}${question.statusOptionState === 'unavailable' + ? '\n Status options could not be verified for every Project. Check the exact existing value in every selected Project before continuing.' : ''}${question.statusOptionState === 'incompatible' + ? '\n Selected Projects have no common Status values. Return to Project selection and choose compatible Projects.' : ''}\n ${color(`[${fallback}]`, 90)}: `; + } + + renderHelp(question: SetupQuestion): string { + const help = setupQuestionPresentation(question).en; + return [ + `What: ${help.summary}`, + `When: ${help.when}`, + `Where: ${help.where}`, + `How: ${help.how}`, + `Why: ${help.why}`, + `Example: ${help.example}`, + `Effect: ${help.effect}`, + `Verify: ${help.verify}`, + `Read more — ${help.documentation.title}: ${help.documentation.url}`, + ].join('\n'); + } + + showHelp(question: SetupQuestion): void { + console.log(renderBox(this.renderHelp(question), 'About this setup choice')); } showValidation(message: string): void { @@ -76,3 +116,29 @@ function formatDefault(value: string | number | boolean): string { if (typeof value === 'boolean') return value ? 'Y' : 'N'; return String(value) || 'none'; } + +function discoveryNote(question: SetupQuestion): string { + const status = question.discoveryStatus; + if (!status) return ''; + const check: Record = { + observed: 'Recent CI jobs were found. Inspect the linked runs before trusting a producer.', + 'no-recent-runs': 'No recent PR CI runs were found. Run normal CI or enter an exact producer manually.', + 'no-verifiable-checks': 'Recent runs exist, but exact job/App identity could not be verified. Use manual entry after inspecting GitHub.', + 'permission-denied': 'GitHub denied CI discovery. Give the setup PAT Actions: read and Checks: read, or enter a verified producer manually.', + unavailable: 'CI discovery failed; this does not mean there are no checks. Retry or use verified manual entry.', + }; + const project: Record = { + observed: 'Existing organization Projects are listed below. Inspect each GitHub URL before selecting it.', + empty: 'The bounded GitHub query returned no accessible Projects; this does not prove none exist. Check organization access or enter a verified number manually.', + 'permission-denied': 'GitHub denied Project discovery. Check organization Projects: read on the setup PAT, or enter numbers manually.', + unavailable: 'Project discovery failed; this does not mean no Projects exist. Use a verified number or retry.', + unsupported: 'Fine-grained PATs cannot list personal Projects through this GitHub API. Use the number in an existing Project URL.', + }; + const note = question.id === 'projects.ids' ? project[status] : check[status]; + const sample = status === 'observed' || status === 'empty' || status === 'no-recent-runs' || status === 'no-verifiable-checks' + ? question.id === 'projects.ids' + ? '\n Search scope: at most 30 accessible organization Projects from two pages; up to 100 fields per Project.' + : '\n Search scope: up to 20 recent PR workflow runs; at most 15 runs and 100 checks per commit are inspected.' + : ''; + return note ? `\n ${note}${sample}${question.discoveryTruncated ? '\n Only a bounded sample was inspected; use manual entry for missing items.' : ''}${question.discoveryRetryRemaining ? `\n Type r to retry GitHub discovery (${question.discoveryRetryRemaining} read-only attempts left).` : ''}` : ''; +} diff --git a/src/cli/setup_result_receipt.ts b/src/cli/setup_result_receipt.ts new file mode 100644 index 000000000..a92c3689b --- /dev/null +++ b/src/cli/setup_result_receipt.ts @@ -0,0 +1,55 @@ +import { getResultPayload, type Result } from '../data/model/result'; +import type { ApplicationErrorCode } from '../application/errors/application_error'; +import type { WebSetupView } from '../application/contracts/web_setup_view'; + +export function setupResultReason(code?: ApplicationErrorCode): NonNullable['reasonCode'] { + if (!code) return 'unknown'; + if (code.startsWith('authorization.')) return 'permissions'; + if (code.startsWith('configuration.') || code === 'validation.invalid-input') return 'configuration'; + if (code === 'provider.rate-limited') return 'rate-limit'; + if (code.startsWith('provider.') || code === 'timeout') return 'provider'; + return 'unknown'; +} + +/** Provider errors are never serialized; failed steps remain potentially applied. */ +export function setupResultEffects(results: readonly Result[]): NonNullable['effects']> { + for (const result of results) { + const receipt = getResultPayload(getResultPayload(result.payload)?.setupReceipt); + if (receipt?.version !== 1 || !Array.isArray(receipt.effects)) continue; + const parsed = receipt.effects.map(parseEffect); + if (parsed.length === EFFECT_IDS.length && parsed.every(Boolean) + && EFFECT_IDS.every(id => parsed.some(effect => effect?.id === id))) return parsed as NonNullable['effects']>; + } + return results.map((result, index) => ({ + id: safeEffectId(result.id, index), + state: !result.success || result.errors.length > 0 ? 'needs-inspection' + : result.executed ? 'completed' : 'skipped', + })); +} + +const EFFECT_IDS = ['files', 'secrets', 'labels', 'issue-types', 'variables', 'initial-tag'] as const; +const EFFECT_STATES = ['completed', 'skipped', 'needs-inspection', 'not-started'] as const; +const EFFECT_SCOPES = ['local', 'repository', 'organization', 'mixed'] as const; + +function parseEffect(value: unknown): { id: typeof EFFECT_IDS[number]; state: typeof EFFECT_STATES[number]; scope: typeof EFFECT_SCOPES[number] } | undefined { + const effect = getResultPayload(value); + if (!effect || !EFFECT_IDS.includes(effect.id as typeof EFFECT_IDS[number]) + || !EFFECT_STATES.includes(effect.state as typeof EFFECT_STATES[number]) + || !EFFECT_SCOPES.includes(effect.scope as typeof EFFECT_SCOPES[number])) return undefined; + return { id: effect.id as typeof EFFECT_IDS[number], state: effect.state as typeof EFFECT_STATES[number], scope: effect.scope as typeof EFFECT_SCOPES[number] }; +} + +export function setupActionResultFailure(results: readonly Result[]): { + reasonCode: NonNullable['reasonCode']; diagnosticRef?: string; +} | undefined { + const first = results.flatMap(result => result.errors)[0]; + if (!first) return results.some(result => !result.success) ? { reasonCode: 'unknown' } : undefined; + if (typeof first !== 'object') return { reasonCode: 'unknown' }; + return { reasonCode: setupResultReason(first.code), + ...(first.correlationId ? { diagnosticRef: first.correlationId } : {}) }; +} + +function safeEffectId(value: string, index: number): string { + return EFFECT_IDS.includes(value as typeof EFFECT_IDS[number]) ? value + : value === 'InitialSetupUseCase' ? 'setup-workflow' : `step-${index + 1}`; +} diff --git a/src/cli/setup_terminal_driver.ts b/src/cli/setup_terminal_driver.ts index e75ff0dc5..f693407e8 100644 --- a/src/cli/setup_terminal_driver.ts +++ b/src/cli/setup_terminal_driver.ts @@ -94,6 +94,7 @@ export class NodeTerminalDriver implements TerminalDriver { prompt: string, choices: readonly string[], selected: readonly string[], + helpText?: string, ): Promise { if (this.closed) return { kind: 'end-of-input' }; const input = stdin as typeof stdin & { setRawMode?: (mode: boolean) => void }; @@ -136,6 +137,13 @@ export class NodeTerminalDriver implements TerminalDriver { if (data.startsWith('\u001b[B', offset)) { index = Math.min(choices.length - 1, index + 1); offset += 2; render(); continue; } if (character === '\u0003') { finish({ kind: 'cancel' }); return; } if (character === '\u0004') { finish({ kind: 'end-of-input' }); return; } + if (character === 'b' || character === 'B') { finish({ kind: 'value', value: ':back' }); return; } + if (character === '?' && helpText) { + stdout.write(`\n${helpText}\n\n`); + rendered = false; + render(); + continue; + } if (character === ' ') { const id = choices[index] === 'All' ? 'all' : choices[index].split(' — ')[0]; if (id === 'all') value = value.size === choices.length - 1 ? new Set() : new Set(choices.slice(1).map(choice => choice.split(' — ')[0])); diff --git a/src/cli/web_setup_adapters.ts b/src/cli/web_setup_adapters.ts index 3b0645d39..9313c8074 100644 --- a/src/cli/web_setup_adapters.ts +++ b/src/cli/web_setup_adapters.ts @@ -1,27 +1,57 @@ -import type { SetupConfigurationCollectorPort, SetupPlanConfirmationPort, SetupPlanPresenterPort } from '../application/ports/setup_terminal_ports'; +import type { SetupConfigurationCollectorPort, SetupDiscoveryRefreshPort, SetupPlanConfirmationPort, SetupPlanPresenterPort } from '../application/ports/setup_terminal_ports'; import type { SetupCredentialPromptPort, SetupWorkflowUpdatePromptPort } from '../application/ports/setup_wizard_ports'; import type { SetupTokenPermissionPresenterPort } from '../application/ports/setup_token_permission_ports'; import type { SetupJourneyPresenterPort } from '../application/usecases/setup/setup_journey_use_case'; import type { SetupGithubIdentity } from '../application/ports/setup_pat_identity_ports'; import type { SetupCredentialCheck, SetupCredentialDecision, SetupCredentialRequirement, SetupCredentialValue, SetupPlan, SetupWorkflowComparison } from '../domain/setup'; import type { SetupTokenPermissionReport, SetupTokenPermissionRequirement, SetupTokenRole } from '../domain/setup_token_permissions'; -import type { SetupQuestionnaireContext, SetupQuestionnaireState } from '../domain/setup_questionnaire'; -import { setupQuestionnaireStateLabel, transitionSetupQuestionnaire } from '../application/policies/setup_questionnaire_policy'; +import type { SetupQuestion, SetupQuestionnaireContext, SetupQuestionnaireState } from '../domain/setup_questionnaire'; +import { refreshSetupQuestionnaireQuestion, setupEditableGroups, setupQuestionnaireProgress, setupQuestionnaireStateLabel, transitionSetupQuestionnaire } from '../application/policies/setup_questionnaire_policy'; +import { setupQuestionPresentation } from '../application/policies/setup_question_guidance_policy'; import { SetupInteractionCancelledError } from '../application/errors/setup_interaction_cancelled_error'; import { WebSetupBridge, toWebSetupPlan } from './web_setup_bridge'; +import type { WebSetupPromptCopyId } from '../application/contracts/web_setup_view'; +import type { WebSetupMessageCopyId } from '../application/contracts/web_setup_view'; export class WebSetupQuestionnaireCollector implements SetupConfigurationCollectorPort { constructor(private readonly bridge: WebSetupBridge, private readonly pass = 1) {} - async collect(initial: SetupQuestionnaireState, context: SetupQuestionnaireContext): Promise { + async collect(initial: SetupQuestionnaireState, context: SetupQuestionnaireContext, discoveryRefresh?: SetupDiscoveryRefreshPort): Promise { let state = initial; + let currentContext = context; while (state.terminal === 'collecting' && state.question) { - if (state.validation) this.bridge.message(state.validation, 'warning'); + if (state.validation) { + const copy = validationCopy(state.validation) ?? { id: 'validation.unknown' as const }; + this.bridge.message(state.validation, 'warning', undefined, copy.id, copy.values); + } const value = await this.bridge.ask({ kind: 'question', title: setupQuestionnaireStateLabel(state.stateId), - question: state.question, phase: state.phase ?? 'full', pass: this.pass, + question: state.question, presentation: setupQuestionPresentation(state.question), phase: state.phase ?? 'full', pass: this.pass, + progress: setupQuestionnaireProgress(state, currentContext), canGoBack: (setupQuestionnaireProgress(state, currentContext)?.position ?? 1) > 1, + }, discoveryRefresh && state.question.discoveryRetryRemaining ? async () => { + const kind = state.question?.id === 'projects.ids' ? 'projects' + : state.question?.id === 'pullRequestApproval.testChecks' ? 'checks' : undefined; + if (!kind) return undefined; + const refreshed = await discoveryRefresh.refresh(kind); + if (!refreshed) return undefined; + const refreshedState = refreshSetupQuestionnaireQuestion(state, refreshed); + return refreshedState.question ? { prompt: { kind: 'question' as const, + title: setupQuestionnaireStateLabel(refreshedState.stateId), + question: refreshedState.question, presentation: setupQuestionPresentation(refreshedState.question), + phase: refreshedState.phase ?? 'full' as const, pass: this.pass, + progress: setupQuestionnaireProgress(refreshedState, refreshed), + canGoBack: (setupQuestionnaireProgress(refreshedState, refreshed)?.position ?? 1) > 1 }, + commit: () => { currentContext = refreshed; state = refreshedState; } } : undefined; + } : undefined, () => { + const previous = transitionSetupQuestionnaire(state, { kind: 'back' }, currentContext); + if (previous.question?.id === state.question?.id || !previous.question) return undefined; + return { prompt: { kind: 'question' as const, title: setupQuestionnaireStateLabel(previous.stateId), + question: previous.question, presentation: setupQuestionPresentation(previous.question), + phase: previous.phase ?? 'full', pass: this.pass, progress: setupQuestionnaireProgress(previous, currentContext), + canGoBack: (setupQuestionnaireProgress(previous, currentContext)?.position ?? 1) > 1 }, + commit: () => { state = previous; } }; }); - state = transitionSetupQuestionnaire(state, value === undefined ? { kind: 'cancel' } : { kind: 'answer', value }, context); + state = transitionSetupQuestionnaire(state, value === undefined ? { kind: 'cancel' } : { kind: 'answer', value }, currentContext); } return state; } @@ -30,14 +60,20 @@ export class WebSetupQuestionnaireCollector implements SetupConfigurationCollect export class WebSetupPlanPresenter implements SetupPlanPresenterPort { constructor(private readonly bridge: WebSetupBridge) {} present(plan: SetupPlan): void { - this.bridge.message(`Plan ready: ${plan.selectedFiles.length} files, ${plan.variables.length} Variables and ${plan.requiredSecrets.length} Secret names. Review it before continuing.`); + this.bridge.message(`Plan ready: ${plan.selectedFiles.length} files, ${plan.variables.length} Variables and ${plan.requiredSecrets.length} Secret names. Review it before continuing.`, 'info', undefined, 'plan.ready', { files: String(plan.selectedFiles.length), variables: String(plan.variables.length), secrets: String(plan.requiredSecrets.length) }); } } export class WebSetupPlanConfirmation implements SetupPlanConfirmationPort { constructor(private readonly bridge: WebSetupBridge) {} - async confirm(plan: SetupPlan): Promise<{ kind: 'approved' | 'declined' | 'cancelled' }> { - const response = await this.bridge.ask({ kind: 'plan', title: 'Review your setup plan', plan: toWebSetupPlan(plan) }); + async confirm(plan: SetupPlan): Promise<{ kind: 'approved' | 'declined' | 'cancelled' } | { kind: 'revise'; group: SetupQuestion['stateId'] }> { + const groups = setupEditableGroups(plan.configuration); + const response = await this.bridge.ask({ kind: 'plan', title: 'Review your setup plan', copyId: 'plan.review', plan: toWebSetupPlan(plan), editGroups: groups }); + if (response?.startsWith('revise:')) { + const group = response.slice('revise:'.length) as SetupQuestion['stateId']; + if (groups.includes(group)) return { kind: 'revise', group }; + throw new Error('Invalid setup section.'); + } return { kind: response === undefined ? 'cancelled' : response === 'approve' ? 'approved' : 'declined' }; } } @@ -52,6 +88,7 @@ export class WebSetupWorkflowUpdatePrompt implements SetupWorkflowUpdatePromptPo kind: 'confirm', title: 'Update existing workflows?', description: changed.map(item => `${item.destination} (${item.status})`).join('\n'), choices: ['Keep existing', 'Update setup-managed workflows'], + copyId: 'workflow.update', copyValues: { files: changed.map(item => item.destination).join(', ') }, }); if (answer === undefined) throw new SetupInteractionCancelledError(); return answer === 'Update setup-managed workflows'; @@ -84,52 +121,52 @@ export class WebSetupCredentialPrompt implements SetupCredentialPromptPort { configureSetupPatGuide(url: string): void { this.setupGuide = url; } useManualSetupPat(): void { this.guidedSetup = false; this.setupGuide = undefined; } async chooseSetupPatMethod(): Promise<'guided' | 'manual'> { - this.guidedSetup = await this.choice('How will you provide your setup PAT?', ['Guided GitHub link', 'Manual PAT']) === 'Guided GitHub link'; + this.guidedSetup = await this.choice('How will you provide your setup PAT?', ['Guided GitHub link', 'Manual PAT'], undefined, 'setupPat.method') === 'Guided GitHub link'; return this.guidedSetup ? 'guided' : 'manual'; } async chooseSetupOwnerKind(): Promise<'Organization' | 'User' | 'unknown'> { - const answer = await this.choice('What kind of GitHub account owns this repository?', ['Organization', 'Personal account', 'Not sure']); + const answer = await this.choice('What kind of GitHub account owns this repository?', ['Organization', 'Personal account', 'Not sure'], undefined, 'setupPat.ownerKind'); return answer === 'Organization' ? 'Organization' : answer === 'Personal account' ? 'User' : 'unknown'; } async reviewSetupPatIntent(): Promise<'continue' | 'revise' | 'manual' | 'details'> { - const answer = await this.choice('Review these provisional setup PAT grants', ['Continue to GitHub', 'Review setup choices again', 'View full permission table', 'Enter a PAT manually']); + const answer = await this.choice('Review these provisional setup PAT grants', ['Continue to GitHub', 'Review setup choices again', 'View full permission table', 'Enter a PAT manually'], undefined, 'setupPat.review'); return answer === 'Review setup choices again' ? 'revise' : answer === 'View full permission table' ? 'details' : answer === 'Enter a PAT manually' ? 'manual' : 'continue'; } async requestSetupPat(): Promise { return this.secret('Temporary setup PAT', 'Use the operator account in GitHub. Complete 2FA there, switch to Only select repositories, select this repository, and copy the generated token here. This token is for this run only; delete it in GitHub afterwards.', - this.guidedSetup ? this.setupGuide : undefined); + this.guidedSetup ? this.setupGuide : undefined, false, 'setupPat.entry'); } async confirmGuidedSetupAccount(account?: string): Promise { if (!this.guidedSetup) return true; if (!account) return false; - return await this.choice(`GitHub authenticated the setup PAT as @${account}. Is that the intended operator account?`, ['Yes, continue', 'No, stop']) === 'Yes, continue'; + return await this.choice(`GitHub authenticated the setup PAT as @${account}. Is that the intended operator account?`, ['Yes, continue', 'No, stop'], undefined, 'setupPat.confirmAccount', { account }) === 'Yes, continue'; } showUpdatedSetupPatLink(url: string, stage: 'bootstrap' | 'final', delta?: readonly string[]): void { - this.bridge.message(`Setup PAT ${stage === 'final' ? 'permissions changed' : 'access failed'}. No setup mutation started. ${delta?.join(', ') ?? ''} Create a corrected PAT using the updated GitHub link.`, 'warning', url); + this.bridge.message(`Setup PAT ${stage === 'final' ? 'permissions changed' : 'access failed'}. No setup mutation started. ${delta?.join(', ') ?? ''} Create a corrected PAT using the updated GitHub link.`, 'warning', url, stage === 'final' ? 'setupPat.corrected.final' : 'setupPat.corrected.bootstrap', { grants: delta?.join(', ') ?? '' }); } showSetupPatCleanupReminder(): void { - if (this.guidedSetup) this.bridge.message('Delete the temporary setup PAT in GitHub Settings after this run. Closing Copilot does not revoke it.', 'warning', 'https://github.com/settings/personal-access-tokens'); + if (this.guidedSetup) this.bridge.message('Delete the temporary setup PAT in GitHub Settings after this run. Closing Copilot does not revoke it.', 'warning', 'https://github.com/settings/personal-access-tokens', 'setupPat.cleanup'); } async confirmUnverifiableTokenPermissions(report: SetupTokenPermissionReport): Promise { const writes = report.checks.filter(item => item.applicability === 'required' && item.level === 'write' && item.status === 'unverifiable'); if (!report.confirmationRequired || writes.length === 0) return false; - return await this.choice('GitHub cannot safely prove these write grants without a mutation. Confirm they are configured exactly as shown.', ['No, stop', 'Yes, I checked them']) === 'Yes, I checked them'; + return await this.choice('GitHub cannot safely prove these write grants without a mutation. Confirm they are configured exactly as shown.', ['No, stop', 'Yes, I checked them'], undefined, 'setupPat.confirmWrites') === 'Yes, I checked them'; } configureWorkflowPatGuide(url: string, resolveIdentity: (login: string) => Promise, requirements?: readonly SetupTokenPermissionRequirement[]): void { this.workflowGuide = url; this.resolveBot = resolveIdentity; this.workflowRequirements = requirements; } explainCredentialSeparation(requirements: readonly SetupCredentialRequirement[]): void { - this.bridge.message(`The bot PAT is separate from your setup PAT. Runtime credentials (${requirements.map(item => item.name).join(', ')}) become GitHub Actions Secrets; existing Secret values cannot be read back. This browser flow will not dispatch or install a credential-health workflow before Apply. Re-enter an existing bot PAT so its grants can be audited.`, 'info'); + this.bridge.message(`The bot PAT is separate from your setup PAT. Runtime credentials (${requirements.map(item => item.name).join(', ')}) become GitHub Actions Secrets; existing Secret values cannot be read back. This browser flow will not dispatch or install a credential-health workflow before Apply. Re-enter an existing bot PAT so its grants can be audited.`, 'info', undefined, 'botPat.separation', { names: requirements.map(item => item.name).join(', ') }); } async requestWorkflowPat(requirement: SetupCredentialRequirement, current?: SetupCredentialCheck): Promise { let guide: string | undefined; let botInfo = ''; if (this.workflowGuide) { - const method = await this.choice('How will you provide the bot PAT?', ['Guided GitHub link', 'Manual PAT']); + const method = await this.choice('How will you provide the bot PAT?', ['Guided GitHub link', 'Manual PAT'], undefined, 'botPat.method'); if (method === 'Guided GitHub link') { - const login = await this.text('Expected GitHub bot login', 'Enter the bot account login, without @. We will verify its numeric account ID against the token.'); + const login = await this.text('Expected GitHub bot login', 'Enter the bot account login, without @. We will verify its numeric account ID against the token.', 'botPat.login'); if (!login || !/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/.test(login)) throw new Error('Enter a valid GitHub bot login.'); this.botIdentity = await this.resolveBot!(login); guide = this.workflowGuide; @@ -138,34 +175,65 @@ export class WebSetupCredentialPrompt implements SetupCredentialPromptPort { } const value = await this.secret(`${requirement.name} — bot account PAT`, `${botInfo}Use the bot account, select only the intended repository and review all grants. Suggested expiry is 90 days. ${current ? `Existing Secret: ${current.status}; its value cannot be read back.` : ''}`, - guide); + guide, false, guide ? 'botPat.entry.guided' : 'botPat.entry.manual', { name: requirement.name, account: this.botIdentity?.login ?? '', accountId: String(this.botIdentity?.id ?? ''), existing: current?.status ?? '' }); return value ? { name: requirement.name, value } : undefined; } async requestApiKey(requirement: SetupCredentialRequirement, current?: SetupCredentialCheck): Promise { - const value = await this.secret(`${requirement.name} — ${requirement.provider ?? 'provider'} API key`, current?.message, undefined, Boolean(requirement.alternativeGroups?.length)); + const value = await this.secret(`${requirement.name} — ${requirement.provider ?? 'provider'} API key`, current?.message, undefined, Boolean(requirement.alternativeGroups?.length), 'credential.apiKey', { name: requirement.name, provider: requirement.provider ?? 'provider' }); return value ? { name: requirement.name, value } : undefined; } async chooseExistingCredential(requirement: SetupCredentialRequirement, check: SetupCredentialCheck): Promise { - const answer = await this.choice(`Existing ${requirement.name}: ${check.status}`, ['keep', 'replace', 'skip'], check.message); + const answer = await this.choice(`Existing ${requirement.name}: ${check.status}`, ['keep', 'replace', 'skip'], check.message, 'credential.existing', { name: requirement.name, status: check.status }); return answer as SetupCredentialDecision; } showCredentialChecks(checks: readonly SetupCredentialCheck[]): void { - this.bridge.message(checks.map(item => `${item.name}: ${item.status} — ${item.message}`).join('\n'), checks.some(item => item.status === 'invalid') ? 'warning' : 'success'); + this.bridge.message(checks.map(item => `${item.name}: ${item.status} — ${item.message}`).join('\n'), checks.some(item => item.status === 'invalid') ? 'warning' : 'success', undefined, 'credential.checks', { names: checks.map(item => item.name).join(', '), count: String(checks.length) }, checks.map(item => ({ name: item.name, status: item.status }))); } - private async choice(title: string, choices: readonly string[], description?: string): Promise { - const answer = await this.bridge.ask({ kind: 'choice', title, choices, description }); + private async choice(title: string, choices: readonly string[], description?: string, copyId?: WebSetupPromptCopyId, copyValues?: Readonly>): Promise { + const answer = await this.bridge.ask({ kind: 'choice', title, choices, description, copyId, copyValues }); if (answer === undefined) throw new SetupInteractionCancelledError(); if (!choices.includes(answer)) throw new Error('Invalid setup choice.'); return answer; } - private async text(title: string, description?: string): Promise { - const answer = await this.bridge.ask({ kind: 'text', title, description }); + private async text(title: string, description?: string, copyId?: WebSetupPromptCopyId): Promise { + const answer = await this.bridge.ask({ kind: 'text', title, description, copyId }); if (answer === undefined) throw new SetupInteractionCancelledError(); return answer.trim(); } - private async secret(title: string, description?: string, link?: string, optional = false): Promise { - const answer = await this.bridge.ask({ kind: 'secret', title, description, optional, link }); + private async secret(title: string, description?: string, link?: string, optional = false, copyId?: WebSetupPromptCopyId, copyValues?: Readonly>): Promise { + const answer = await this.bridge.ask({ kind: 'secret', title, description, optional, link, copyId, copyValues }); if (answer === undefined) throw new SetupInteractionCancelledError(); return answer.trim(); } } + +export function validationCopy(message: string): { id: WebSetupMessageCopyId; values?: Readonly> } | undefined { + const fixed: Readonly> = { + 'Select 1–8 observed checks or enter exact name|App ID|workflow tuples.': 'validation.producers', + 'Two trusted producers use the same check name. Coverage stores only one name; choose one producer or rename the CI jobs before continuing.': 'validation.duplicateNames', + 'Open every selected Project in GitHub and confirm that all four exact Status values exist. Answer Yes after checking, or No to choose Projects again.': 'validation.projectStatusVerified', + 'Status values were not confirmed. Choose compatible Projects, then review their Status options again.': 'validation.projectStatusRedo', + 'Enter a non-negative whole number.': 'validation.number', + 'Enter yes or no.': 'validation.boolean', + 'Select one of the listed options.': 'validation.choice', + 'This is the first question in this pass. Review it or cancel setup.': 'validation.firstQuestion', + 'A trusted check was selected more than once.': 'validation.duplicateProducer', + 'The saved Status value is not available in every selected Project. Choose a listed Status option.': 'validation.savedStatus', + 'Selected Projects have no common Status option. Choose compatible Projects or configure them separately.': 'validation.projectIncompatible', + 'Choose at most 10 Projects; separate numbers or URLs with commas.': 'validation.projectLimit', + 'A Project URL needs a known repository owner; enter its positive number instead.': 'validation.projectOwnerNeeded', + 'Enter a valid GitHub Project URL or positive Project number.': 'validation.projectUrl', + 'Enter the positive Project number from its GitHub URL, not a PVT_ GraphQL ID.': 'validation.projectNumber', + 'Project numbers must be positive integers at most 2147483647.': 'validation.projectNumberRange', + }; + if (fixed[message]) return { id: fixed[message] }; + const inherited = message.match(/^Unknown inherited resource name\(s\): (.+)\.$/u); + if (inherited) return { id: 'validation.unknownResource', values: { names: inherited[1] } }; + const workflows = message.match(/^Unknown issue workflow\(s\): (.+)\.$/u); + if (workflows) return { id: 'validation.unknownWorkflow', values: { names: workflows[1] } }; + const owner = message.match(/^Use a GitHub Project URL belonging to ([^,]+), without query parameters\.$/u); + if (owner) return { id: 'validation.projectOwnerMismatch', values: { owner: owner[1] } }; + const duplicate = message.match(/^Project ([1-9]\d*) was selected more than once\.$/u); + if (duplicate) return { id: 'validation.projectDuplicate', values: { number: duplicate[1] } }; + return undefined; +} diff --git a/src/cli/web_setup_bridge.ts b/src/cli/web_setup_bridge.ts index a0d577dac..33aec1212 100644 --- a/src/cli/web_setup_bridge.ts +++ b/src/cli/web_setup_bridge.ts @@ -3,15 +3,20 @@ import type { SetupJourneyView } from '../application/policies/setup_journey_pol import type { SetupPlan } from '../domain/setup'; import type { SetupTokenPermissionReport, SetupTokenPermissionRequirement, SetupTokenRole } from '../domain/setup_token_permissions'; import type { WebSetupPlan, WebSetupPrompt, WebSetupView } from '../application/contracts/web_setup_view'; +import type { WebSetupMessageCopyId } from '../application/contracts/web_setup_view'; /** A one-run, in-memory handoff. Values submitted by the browser are never part of a view. */ export class WebSetupBridge { private revision = 0; private view: WebSetupView; - private pending?: { revision: number; resolve: (value: string | undefined) => void }; + private pending?: { revision: number; resolve: (value: string | undefined) => void; + refresh?: () => Promise<{ prompt: WebSetupPrompt; commit: () => void } | undefined>; + navigateBack?: () => { prompt: WebSetupPrompt; commit: () => void } | undefined; refreshing?: boolean }; private subscribers = new Set<(view: WebSetupView) => void>(); private controller?: string; private lastAnsweredRevision?: number; + private readOnlyDoctor?: () => Promise<{ healthy: boolean; pass: number; warn: number; fail: number; skipped: number }>; + private doctorAttempts = 0; constructor(repository: string) { this.view = { revision: 0, repository }; @@ -37,7 +42,7 @@ export class WebSetupBridge { takeOver(): string { this.controller = randomBytes(32).toString('hex'); - this.publish({ message: { tone: 'info', text: 'Control moved to this tab. The previous tab is now read-only.' } }); + this.publish({ message: { tone: 'info', text: 'Control moved to this tab. The previous tab is now read-only.', copyId: 'session.controlMoved' } }); return this.controller; } @@ -45,17 +50,51 @@ export class WebSetupBridge { return Boolean(this.controller && sameCapability(capability, this.controller)); } - async ask(prompt: WebSetupPrompt): Promise { + async ask(prompt: WebSetupPrompt, refresh?: () => Promise<{ prompt: WebSetupPrompt; commit: () => void } | undefined>, + navigateBack?: () => { prompt: WebSetupPrompt; commit: () => void } | undefined): Promise { if (this.pending || this.view.outcome) throw new Error('A setup decision is already pending or the session has ended.'); const revision = this.revision + 1; this.publish({ prompt, promptRevision: revision }); - return new Promise(resolve => { this.pending = { revision, resolve }; }); + return new Promise(resolve => { this.pending = { revision, resolve, refresh, navigateBack }; }); + } + + back(revision: number): 'updated' | 'stale' | 'unavailable' { + const pending = this.pending; + if (!pending || pending.revision !== revision || this.view.outcome) return 'stale'; + if (!pending.navigateBack || pending.refreshing) return 'unavailable'; + const result = pending.navigateBack(); + if (!result) return 'unavailable'; + result.commit(); + pending.revision = this.revision + 1; + this.publish({ prompt: result.prompt, promptRevision: pending.revision, message: undefined }); + return 'updated'; + } + + async retryDiscovery(revision: number): Promise<'updated' | 'stale' | 'unavailable'> { + const pending = this.pending; + if (!pending || pending.revision !== revision || this.view.outcome) return 'stale'; + if (!pending.refresh || pending.refreshing) return 'unavailable'; + const controller = this.controller; + pending.refreshing = true; + try { + const result = await pending.refresh(); + if (this.pending !== pending || this.view.outcome || controller !== this.controller) return 'stale'; + if (!result) return 'unavailable'; + result.commit(); + // Keep promptRevision stable so the browser retains unsent manual and checkbox input. + this.publish({ prompt: result.prompt }); + return 'updated'; + } finally { + pending.refreshing = false; + } } answer(revision: number, value: string): boolean { - if (!this.pending || this.pending.revision !== revision || this.view.outcome) return false; + if (!this.pending || this.pending.revision !== revision || this.pending.refreshing || this.view.outcome) return false; const prompt = this.view.prompt; if (prompt && (prompt.kind === 'choice' || prompt.kind === 'confirm') && !prompt.choices.includes(value)) return false; + if (prompt?.kind === 'plan' && value !== 'approve' && value !== 'decline' + && !prompt.editGroups?.some(group => value === `revise:${group}`)) return false; const pending = this.pending; this.pending = undefined; this.lastAnsweredRevision = revision; @@ -66,18 +105,44 @@ export class WebSetupBridge { wasAnswered(revision: number): boolean { return this.lastAnsweredRevision === revision; } + configureReadOnlyDoctor(run: () => Promise<{ healthy: boolean; pass: number; warn: number; fail: number; skipped: number }>): void { + this.readOnlyDoctor = run; + } + + async runReadOnlyDoctor(): Promise<'complete' | 'failed' | 'unavailable' | 'busy'> { + if (this.view.outcome !== 'complete' || !this.readOnlyDoctor) return 'unavailable'; + if (this.view.doctor?.status === 'running') return 'busy'; + if (this.view.doctor?.status === 'complete') return 'complete'; + if (this.doctorAttempts >= 2) return 'unavailable'; + this.doctorAttempts += 1; + this.publish({ doctor: { status: 'running' } }); + try { + const summary = await this.readOnlyDoctor(); + const counts = [summary.pass, summary.warn, summary.fail, summary.skipped]; + if (counts.some(value => !Number.isSafeInteger(value) || value < 0)) throw new Error('Invalid doctor summary.'); + this.publish({ doctor: { status: 'complete', healthy: summary.healthy === true, + pass: summary.pass, warn: summary.warn, fail: summary.fail, skipped: summary.skipped } }); + return 'complete'; + } catch { + this.publish({ doctor: { status: 'failed' } }); + return 'failed'; + } + } + cancel(): boolean { if (this.view.journey?.mutationStarted || this.view.outcome) return false; const pending = this.pending; this.pending = undefined; - this.publish({ prompt: undefined, promptRevision: undefined, outcome: 'cancelled', message: { tone: 'warning', text: 'Setup cancelled before applying further changes. Any PAT created at GitHub still exists until you delete it there.' } }); + this.publish({ prompt: undefined, promptRevision: undefined, outcome: 'cancelled', resultDetail: { + reasonCode: 'cancelled', stoppedStage: this.view.journey?.current ?? 'Preparation', mutationStarted: false, + }, message: { tone: 'warning', text: 'Setup cancelled before applying further changes. Any PAT created at GitHub still exists until you delete it there.', copyId: 'session.cancelled' } }); pending?.resolve(undefined); return true; } setJourney(journey: SetupJourneyView): void { this.publish({ journey }); } - message(text: string, tone: 'info' | 'success' | 'warning' | 'error' = 'info', link?: string): void { - this.publish({ message: { tone, text, ...(link ? { link } : {}) } }); + message(text: string, tone: 'info' | 'success' | 'warning' | 'error' = 'info', link?: string, copyId?: WebSetupMessageCopyId, copyValues?: Readonly>, credentialChecks?: NonNullable['credentialChecks']): void { + this.publish({ message: { tone, text, ...(link ? { link } : {}), copyId, copyValues, credentialChecks } }); } requirements(role: SetupTokenRole, requirements: readonly SetupTokenPermissionRequirement[]): void { this.publish({ permissions: { role, requirements, report: undefined } }); @@ -85,11 +150,34 @@ export class WebSetupBridge { report(report: SetupTokenPermissionReport): void { this.publish({ permissions: { role: report.role, requirements: this.view.permissions?.requirements, report } }); } + resultReason(reasonCode: NonNullable['reasonCode'], diagnosticRef?: string): void { + if (this.view.outcome) return; + this.publish({ resultDetail: { + reasonCode, + stoppedStage: this.view.journey?.current ?? 'Preparation', + mutationStarted: this.view.journey?.mutationStarted === true, + ...(this.view.resultDetail?.effects ? { effects: this.view.resultDetail.effects } : {}), + ...(diagnosticRef && /^[0-9a-f-]{36}$/u.test(diagnosticRef) ? { diagnosticRef } : {}), + } }); + } + effects(effects: NonNullable['effects']): void { + if (this.view.outcome) return; + this.publish({ resultDetail: { reasonCode: this.view.resultDetail?.reasonCode ?? 'unknown', + stoppedStage: this.view.journey?.current ?? 'Preparation', + mutationStarted: this.view.journey?.mutationStarted === true, effects, + ...(this.view.resultDetail?.diagnosticRef ? { diagnosticRef: this.view.resultDetail.diagnosticRef } : {}) } }); + } finish(outcome: NonNullable, text: string): void { if (this.view.outcome) return; this.pending?.resolve(undefined); this.pending = undefined; - this.publish({ prompt: undefined, promptRevision: undefined, outcome, message: { tone: outcome === 'complete' || outcome === 'dry-run' ? 'success' : 'warning', text } }); + this.publish({ prompt: undefined, promptRevision: undefined, outcome, message: { tone: outcome === 'complete' || outcome === 'dry-run' ? 'success' : 'warning', text }, + ...(this.view.resultDetail ? {} : { resultDetail: { + reasonCode: outcome === 'cancelled' ? 'cancelled' : outcome === 'blocked' ? 'unknown' : 'unknown', + stoppedStage: this.view.journey?.current ?? 'Preparation', + mutationStarted: this.view.journey?.mutationStarted === true, + } }), + }); } private publish(change: Partial): void { @@ -109,6 +197,36 @@ function sameCapability(provided: string, expected: string): boolean { export function toWebSetupPlan(plan: SetupPlan): WebSetupPlan { return { + presentationDefaults: plan.presentationDefaults ?? [], + decisions: { + enabledCapabilities: Object.entries(plan.configuration.features).filter(([, enabled]) => enabled).map(([name]) => name), + agentRouting: Object.entries(plan.configuration.agents).map(([role, agent]) => ({ role, + provider: agent.provider, modelProvider: agent.modelProvider, model: agent.model })), + issueWorkflows: plan.configuration.features.issues ? plan.configuration.issueWorkflows.enabled : [], + productionBranch: plan.configuration.repository.mainBranch, + developmentBranch: plan.configuration.repository.developmentBranch, + approvalMode: plan.configuration.pullRequestApproval.mode, + trustedChecks: plan.configuration.pullRequestApproval.testChecks.map(check => ({ name: check.name, + sourceAppId: check.sourceAppId, workflowName: check.workflowName })), + producerAttested: plan.configuration.pullRequestApproval.producerAttested, + coverageMode: plan.configuration.pullRequestApproval.coverage.mode, + coverageCheck: plan.configuration.pullRequestApproval.coverage.checkName, + ...(plan.configuration.pullRequestApproval.coverage.mode === 'numeric' ? { + coverageMinimum: plan.configuration.pullRequestApproval.coverage.minDiffPercent, + coverageArtifactWorkflow: plan.configuration.pullRequestApproval.coverage.artifactWorkflowName, + coverageReporterAttested: plan.configuration.pullRequestApproval.coverage.reporterAttested, + } : {}), + projectNumbers: plan.configuration.projects.ids.split(',').filter(Boolean), + projectStatuses: [ + { transition: 'issueCreated', value: plan.configuration.projects.issueCreatedColumn }, + { transition: 'pullRequestCreated', value: plan.configuration.projects.pullRequestCreatedColumn }, + { transition: 'issueInProgress', value: plan.configuration.projects.issueInProgressColumn }, + { transition: 'pullRequestInProgress', value: plan.configuration.projects.pullRequestInProgressColumn }, + ], + variableScope: plan.configuration.manageRepositoryVariables ? plan.configuration.storage.variables.defaultScope : 'disabled', + secretScope: plan.configuration.manageRepositorySecrets ? plan.configuration.storage.secrets.defaultScope : 'disabled', + initialTag: plan.configuration.createInitialTag, + }, files: plan.selectedFiles, workflows: plan.workflowFiles, variables: plan.variables.map(variable => variable.name), diff --git a/src/cli/web_setup_server.ts b/src/cli/web_setup_server.ts index 147d65c89..64c3c4110 100644 --- a/src/cli/web_setup_server.ts +++ b/src/cli/web_setup_server.ts @@ -43,6 +43,7 @@ export async function startWebSetupServer(bridge: WebSetupBridge, assets = join( if (idleTimer) clearTimeout(idleTimer); idleTimer = setTimeout(() => { if (!bridge.snapshot().journey?.mutationStarted && !bridge.snapshot().outcome) { + bridge.resultReason('session-expired'); bridge.finish('blocked', 'This local setup session expired after 30 minutes without a decision. Start a new setup run; GitHub PATs are not revoked automatically.'); } }, 30 * 60 * 1000); @@ -124,6 +125,52 @@ export async function startWebSetupServer(bridge: WebSetupBridge, assets = join( : { error: 'This question changed. Refresh the current state.' }); return; } + if (request.method === 'POST' && request.url === '/api/retry-discovery') { + const capability = String(request.headers['x-setup-capability'] ?? ''); + if (!bridge.isController(capability)) { respond(response, 403, { error: 'This tab is read-only.' }); return; } + if (request.headers['content-type'] !== 'application/json') { respond(response, 415, { error: 'JSON required.' }); return; } + const body = await readJson(request); + if (!Number.isSafeInteger(body.revision) || (body.revision as number) <= 0) { + respond(response, 400, { error: 'Invalid question revision.' }); return; + } + if (!bridge.isController(capability)) { respond(response, 403, { error: 'Control moved to another tab.' }); return; } + const result = await bridge.retryDiscovery(body.revision as number); + if (!bridge.isController(capability)) { respond(response, 403, { error: 'Control moved to another tab.' }); return; } + if (result === 'updated') armIdle(); + respond(response, result === 'updated' ? 200 : 409, result === 'updated' + ? { updated: true } : { error: result === 'stale' ? 'This question changed. Refresh the current state.' + : 'Discovery cannot be retried here. Use the manual option.' }); + return; + } + if (request.method === 'POST' && request.url === '/api/back') { + const capability = String(request.headers['x-setup-capability'] ?? ''); + if (!bridge.isController(capability)) { respond(response, 403, { error: 'This tab is read-only.' }); return; } + if (request.headers['content-type'] !== 'application/json') { respond(response, 415, { error: 'JSON required.' }); return; } + const body = await readJson(request); + if (!Number.isSafeInteger(body.revision) || (body.revision as number) <= 0) { + respond(response, 400, { error: 'Invalid question revision.' }); return; + } + if (!bridge.isController(capability)) { respond(response, 403, { error: 'Control moved to another tab.' }); return; } + const result = bridge.back(body.revision as number); + if (result === 'updated') armIdle(); + respond(response, result === 'updated' ? 200 : 409, result === 'updated' + ? { updated: true } : { error: result === 'stale' ? 'This question changed. Refresh the current state.' + : 'No earlier question is available here.' }); + return; + } + if (request.method === 'POST' && request.url === '/api/doctor') { + const capability = String(request.headers['x-setup-capability'] ?? ''); + if (!bridge.isController(capability)) { respond(response, 403, { error: 'This tab is read-only.' }); return; } + if (request.headers['content-type'] !== 'application/json') { respond(response, 415, { error: 'JSON required.' }); return; } + await readJson(request); + if (!bridge.isController(capability)) { respond(response, 403, { error: 'Control moved to another tab.' }); return; } + const result = await bridge.runReadOnlyDoctor(); + if (!bridge.isController(capability)) { respond(response, 403, { error: 'Control moved to another tab.' }); return; } + respond(response, result === 'complete' ? 200 : 409, result === 'complete' + ? { checked: true } : { error: result === 'failed' ? 'Read-only verification failed. Check the terminal.' + : 'Read-only verification is unavailable or already running.' }); + return; + } if (request.method === 'POST' && request.url === '/api/cancel') { if (!bridge.isController(String(request.headers['x-setup-capability'] ?? ''))) { respond(response, 403, { error: 'This tab is read-only.' }); return; } if (request.headers['content-type'] !== 'application/json') { respond(response, 415, { error: 'JSON required.' }); return; } @@ -197,6 +244,7 @@ export async function startWebSetupServer(bridge: WebSetupBridge, assets = join( armIdle(); const hardTimer = setTimeout(() => { if (!bridge.snapshot().journey?.mutationStarted && !bridge.snapshot().outcome) { + bridge.resultReason('session-expired'); bridge.finish('blocked', 'This local setup session reached its four-hour limit. Start a new run; no prior approval can be replayed.'); } }, 4 * 60 * 60 * 1000); diff --git a/src/cli_context.ts b/src/cli_context.ts index a4132e7bf..b7ed62a3f 100644 --- a/src/cli_context.ts +++ b/src/cli_context.ts @@ -1,4 +1,4 @@ -import { execSync } from 'child_process'; +import { execFileSync, execSync } from 'child_process'; import { realpathSync } from 'node:fs'; import { ERRORS } from './cli/cli_errors'; import { canonicalGitObjectId } from './domain/git_object_id'; @@ -40,6 +40,18 @@ export function getCurrentAttachedBranch(cwd: string): string | undefined { } } +/** Positive local evidence only; a missing ref says nothing about remote branches. */ +export function hasLocalOrTrackedGitBranch(cwd: string, branch: string): boolean { + if (!/^[A-Za-z0-9][A-Za-z0-9._/-]{0,99}$/u.test(branch) || branch.includes('..') || branch.endsWith('.lock')) return false; + for (const ref of [`refs/heads/${branch}`, `refs/remotes/origin/${branch}`]) { + try { + execFileSync('git', ['show-ref', '--verify', '--quiet', ref], { cwd, stdio: 'pipe' }); + return true; + } catch { /* Try the other explicit ref. */ } + } + return false; +} + /** Returns the canonical object ID for the workspace revision being analyzed. */ export function getCurrentHeadSha(): string | undefined { try { diff --git a/src/data/repository/repository_variables_repository.ts b/src/data/repository/repository_variables_repository.ts index d66ee73fe..e02a6b55a 100644 --- a/src/data/repository/repository_variables_repository.ts +++ b/src/data/repository/repository_variables_repository.ts @@ -16,6 +16,7 @@ import type { } from '../../infrastructure/github/ports/github_repository_variables_protocol'; import nacl from 'tweetnacl'; import { createHash } from 'node:crypto'; +import { isSafeBranchTree } from '../../domain/deployment_configuration'; class GithubActionsResourceTransport { constructor(private readonly githubClient: GithubClientPort) {} @@ -52,6 +53,8 @@ class GithubActionsResourceTransport { const credentialHealthWorkflow = await this.inspectDefaultCredentialHealthWorkflow(client, owner, repository); return { ownerType, + ...(typeof metadata.default_branch === 'string' && isSafeBranchTree(metadata.default_branch) + ? { defaultBranch: metadata.default_branch } : {}), repositoryId: metadata.id, repositoryVisibility, repositorySecrets: repositorySecretsResult.resources, diff --git a/src/domain/__tests__/pull_request_approval.test.ts b/src/domain/__tests__/pull_request_approval.test.ts index c60a47d18..e40000e2b 100644 --- a/src/domain/__tests__/pull_request_approval.test.ts +++ b/src/domain/__tests__/pull_request_approval.test.ts @@ -38,6 +38,13 @@ describe('guarded PR approval policy', () => { expect(validatePullRequestApprovalPolicy(policy)).toEqual([]); expect(parsePullRequestApprovalPolicy(JSON.stringify(policy))).toEqual(policy); }); + it('allows an ambiguous legacy producer list only for interactive repair, never for final policy use', () => { + const ambiguous = { ...policy, testChecks: [...policy.testChecks, + { name: 'CI Check', sourceAppId: 123, workflowName: 'Another CI' }] }; + expect(validatePullRequestApprovalPolicy(ambiguous, true)).toEqual([]); + expect(validatePullRequestApprovalPolicy(ambiguous)).toContain('Trusted check names must be unique because coverage stores only a check name.'); + expect(() => parsePullRequestApprovalPolicy(JSON.stringify(ambiguous))).toThrow('unique'); + }); it.each([ [{ ...policy, version: 2 }, 'version'], [{ ...policy, targetRoles: ['development', 'development'] }, 'targetRoles'], diff --git a/src/domain/pull_request_approval_policy.ts b/src/domain/pull_request_approval_policy.ts index 9fac5318b..40630818e 100644 --- a/src/domain/pull_request_approval_policy.ts +++ b/src/domain/pull_request_approval_policy.ts @@ -99,6 +99,7 @@ export function validatePullRequestApprovalPolicy(value: unknown, allowIncomplet errors.push('guarded/recommend mode requires 1–8 exact test checks.'); } else { const identities = new Set(); + const names = new Set(); for (const item of value.testChecks) { if (!isRecord(item)) { errors.push('Each test check must be an object.'); continue; } unknownKeys(item, PRODUCER_KEYS, 'test check', errors); @@ -108,6 +109,8 @@ export function validatePullRequestApprovalPolicy(value: unknown, allowIncomplet const identity = `${item.name}:${item.sourceAppId}:${item.workflowName}`; if (identities.has(identity)) errors.push('Test checks cannot contain duplicate producer identities.'); identities.add(identity); + if (value.mode !== 'off' && !allowIncomplete && names.has(String(item.name))) errors.push('Trusted check names must be unique because coverage stores only a check name.'); + names.add(String(item.name)); } } if (typeof value.producerAttested !== 'boolean') errors.push('producerAttested must be boolean.'); diff --git a/src/domain/setup.ts b/src/domain/setup.ts index 1f9f73671..a2df26621 100644 --- a/src/domain/setup.ts +++ b/src/domain/setup.ts @@ -197,6 +197,8 @@ export type SetupCredentialHealthWorkflowState = 'installed' | 'missing' | 'unav export interface SetupRemoteConfiguration { ownerType: SetupOwnerType; + /** Read from authenticated GitHub metadata, not inferred from local branch names. */ + defaultBranch?: string; repositoryId?: number; repositoryVisibility: SetupRepositoryVisibility; repositorySecrets: readonly string[]; @@ -241,6 +243,8 @@ export interface SetupVariable { } export interface SetupPlan { + /** Informational only: advanced defaults not asked in basic presentation. */ + presentationDefaults?: readonly { group: string; count: number }[]; configuration: SetupConfiguration; workflowFiles: string[]; issueTemplateFiles: string[]; @@ -252,3 +256,10 @@ export interface SetupPlan { approvalReadiness: DoctorCheck[]; warnings: string[]; } + +/** Structured, value-free receipt for the local setup workflow. */ +export interface SetupOperationEffect { + readonly id: 'files' | 'secrets' | 'labels' | 'issue-types' | 'variables' | 'initial-tag'; + readonly state: 'completed' | 'skipped' | 'needs-inspection' | 'not-started'; + readonly scope: 'local' | 'repository' | 'organization' | 'mixed'; +} diff --git a/src/domain/setup_questionnaire.ts b/src/domain/setup_questionnaire.ts index 100617420..d37b372f8 100644 --- a/src/domain/setup_questionnaire.ts +++ b/src/domain/setup_questionnaire.ts @@ -1,4 +1,5 @@ import type { SetupConfiguration, SetupRemoteConfiguration } from './setup'; +import type { PullRequestApprovalProducer } from './pull_request_approval_policy'; export const SETUP_QUESTIONNAIRE_STATE_ORDER = [ 'capabilities', @@ -19,7 +20,36 @@ export const SETUP_QUESTIONNAIRE_STATE_ORDER = [ ] as const; export type SetupQuestionnaireStateId = (typeof SETUP_QUESTIONNAIRE_STATE_ORDER)[number]; -export type SetupQuestionKind = 'boolean' | 'number' | 'text' | 'choice' | 'multi-select' | 'scope-overrides'; +export type SetupQuestionKind = 'boolean' | 'number' | 'text' | 'choice' | 'multi-select' | 'scope-overrides' | 'producer-select' | 'project-select'; + +export type SetupDiscoveryStatus = 'observed' | 'no-recent-runs' | 'no-verifiable-checks' | 'empty' | 'permission-denied' | 'unavailable' | 'unsupported'; + +export interface SetupProjectCandidate { + readonly number: number; + readonly title: string; + readonly owner: string; + readonly url: string; + readonly statusOptions?: readonly string[]; +} + +export interface SetupDiscoveryResult { + readonly status: SetupDiscoveryStatus; + readonly candidates: readonly T[]; + readonly truncated?: boolean; +} + +export interface SetupApprovalCheckCandidate { + readonly name: string; + readonly sourceAppId: number; + readonly sourceAppName?: string; + readonly workflowName: string; + readonly runUrl: string; + readonly headSha: string; + readonly conclusion: string; + readonly observedAt?: string; + /** Exact App-bound status check in an active ruleset for this branch; absence means unverified, not optional. */ + readonly requiredByRuleset?: { readonly branch: string; readonly sourceUrl: string }; +} export interface SetupQuestion { readonly stateId: Exclude; @@ -29,6 +59,16 @@ export interface SetupQuestion { readonly defaultValue: string | number | boolean; readonly choices?: readonly string[]; readonly allowedNames?: readonly string[]; + readonly producerCandidates?: readonly SetupApprovalCheckCandidate[]; + readonly trustedProducers?: readonly PullRequestApprovalProducer[]; + readonly discoveryStatus?: SetupDiscoveryStatus; + readonly discoveryTruncated?: boolean; + readonly discoveryRetryRemaining?: number; + readonly projectCandidates?: readonly SetupProjectCandidate[]; + readonly projectOwner?: string; + readonly statusOptionState?: 'observed' | 'unavailable' | 'incompatible'; + readonly projectStatusValues?: readonly { readonly transition: 'issueCreated' | 'pullRequestCreated' | 'issueInProgress' | 'pullRequestInProgress'; readonly value: string }[]; + readonly suggestionSource?: 'github' | 'local' | 'configuration' | 'default'; } export interface SetupQuestionnaireState { @@ -40,10 +80,20 @@ export interface SetupQuestionnaireState { readonly configureIndependently: boolean; readonly phase?: 'full' | 'permission-intent'; readonly answeredQuestionIds?: readonly string[]; + readonly projectsWanted?: boolean; +} + +export interface SetupQuestionnaireProgress { + readonly position: number; + readonly total: number; + readonly groupPosition: number; + readonly groupTotal: number; + readonly group: SetupQuestion['stateId']; } export type SetupQuestionnaireEvent = | { readonly kind: 'answer'; readonly value: string } + | { readonly kind: 'back' } | { readonly kind: 'cancel' } | { readonly kind: 'end-of-input' }; @@ -52,4 +102,12 @@ export interface SetupQuestionnaireContext { readonly variableNames?: readonly string[]; readonly secretNames?: readonly string[]; readonly skipQuestionIds?: readonly string[]; + readonly approvalCheckCandidates?: readonly SetupApprovalCheckCandidate[]; + readonly approvalCheckDiscoveryStatus?: SetupDiscoveryStatus; + readonly approvalCheckDiscoveryTruncated?: boolean; + readonly projectDiscovery?: SetupDiscoveryResult; + readonly projectOwner?: string; + readonly projectsWanted?: boolean; + readonly discoveryRetryRemaining?: Readonly<{ checks: number; projects: number }>; + readonly branchSources?: Readonly<{ main: 'github' | 'configuration' | 'default'; development: 'local' | 'configuration' | 'default' }>; } diff --git a/src/infrastructure/__tests__/github_setup_approval_check_discovery_adapter.test.ts b/src/infrastructure/__tests__/github_setup_approval_check_discovery_adapter.test.ts new file mode 100644 index 000000000..8795864a3 --- /dev/null +++ b/src/infrastructure/__tests__/github_setup_approval_check_discovery_adapter.test.ts @@ -0,0 +1,85 @@ +import * as github from '@actions/github'; +import { GithubSetupApprovalCheckDiscoveryAdapter } from '../github_setup_approval_check_discovery_adapter'; + +jest.mock('@actions/github', () => ({ getOctokit: jest.fn() })); + +const sha = 'a'.repeat(40); +const owner = 'acme'; +const repository = 'project'; + +function arrange(runs: unknown[], checks: unknown[], jobs: unknown[]): { listWorkflowRunsForRepo: jest.Mock; listForRef: jest.Mock; listJobsForWorkflowRunAttempt: jest.Mock; request: jest.Mock } { + const listWorkflowRunsForRepo = jest.fn().mockResolvedValue({ data: { workflow_runs: runs } }); + const listForRef = jest.fn().mockResolvedValue({ data: { check_runs: checks } }); + const listJobsForWorkflowRunAttempt = jest.fn().mockResolvedValue({ data: { jobs } }); + const request = jest.fn().mockResolvedValue({ data: [] }); + (github.getOctokit as jest.Mock).mockReturnValue({ request, rest: { + actions: { listWorkflowRunsForRepo, listJobsForWorkflowRunAttempt }, checks: { listForRef }, + } }); + return { listWorkflowRunsForRepo, listForRef, listJobsForWorkflowRunAttempt, request }; +} + +describe('GitHub setup approval check discovery', () => { + beforeEach(() => jest.clearAllMocks()); + + test('marks only an exact check/App pair required by an active branch ruleset', async () => { + const calls = arrange( + [{ id: 42, name: 'CI', head_sha: sha, run_attempt: 1, status: 'completed' }], + [{ id: 90, name: 'Tests', app: { id: 12 }, head_sha: sha, conclusion: 'success' }, + { id: 91, name: 'Other', app: { id: 99 }, head_sha: sha, conclusion: 'success' }], + [{ name: 'Tests', check_run_url: 'https://api.github.com/repos/acme/project/check-runs/90' }, + { name: 'Other', check_run_url: 'https://api.github.com/repos/acme/project/check-runs/91' }], + ); + calls.request.mockResolvedValueOnce({ data: [{ type: 'required_status_checks', ruleset_id: 7, + ruleset_source_type: 'Repository', ruleset_source: 'acme/project', + parameters: { required_status_checks: [{ context: 'Tests', integration_id: 12 }, { context: 'Other', integration_id: 12 }] } }] }); + const result = await new GithubSetupApprovalCheckDiscoveryAdapter().discover(owner, repository, 'secret', 'develop'); + expect(calls.request).toHaveBeenCalledWith('GET /repos/{owner}/{repo}/rules/branches/{branch}', + expect.objectContaining({ branch: 'develop', per_page: 100 })); + expect(result.candidates[0].requiredByRuleset).toEqual({ branch: 'develop', sourceUrl: 'https://github.com/acme/project/rules/7' }); + expect(result.candidates[1].requiredByRuleset).toBeUndefined(); + }); + + test('suggests only exact jobs joined to a completed PR workflow and Check Run App ID', async () => { + const calls = arrange( + [{ id: 42, name: 'CI', head_sha: sha, run_attempt: 2, status: 'completed', conclusion: 'success', created_at: '2026-09-29T00:00:00Z' }], + [{ id: 90, name: 'Tests', app: { id: 12, name: 'GitHub Actions' }, head_sha: sha, conclusion: 'success' }, + { id: 91, name: 'Foreign', app: { id: 34 }, head_sha: sha, conclusion: 'success' }], + [{ name: 'Tests', check_run_url: 'https://api.github.com/repos/acme/project/check-runs/90' }], + ); + const result = await new GithubSetupApprovalCheckDiscoveryAdapter().discover(owner, repository, 'secret'); + expect(result).toEqual({ status: 'observed', candidates: [{ name: 'Tests', sourceAppId: 12, sourceAppName: 'GitHub Actions', workflowName: 'CI', + runUrl: 'https://github.com/acme/project/actions/runs/42', headSha: sha, conclusion: 'success', observedAt: '2026-09-29T00:00:00Z' }] }); + expect(calls.listWorkflowRunsForRepo).toHaveBeenCalledWith(expect.objectContaining({ owner, repo: repository, event: 'pull_request', per_page: 20 })); + expect(calls.listForRef).toHaveBeenCalledWith(expect.objectContaining({ ref: sha, filter: 'all' })); + expect(calls.listJobsForWorkflowRunAttempt).toHaveBeenCalledWith(expect.objectContaining({ run_id: 42, attempt_number: 2 })); + }); + + test('does not invent identities from a job name, skipped workflow or unsafe producer', async () => { + arrange( + [{ id: 42, name: 'CI', head_sha: sha, run_attempt: 1, status: 'completed' }, + { id: 43, name: 'Copilot - Approval', head_sha: sha, run_attempt: 1, status: 'completed' }, + { id: 44, name: 'Pending', head_sha: sha, run_attempt: 1, status: 'in_progress' }], + [{ id: 90, name: 'Tests|fake', app: { id: 12 }, head_sha: sha, conclusion: 'success' }, + { id: 91, name: 'Tests', app: null, head_sha: sha, conclusion: 'success' }, + { id: 92, name: 'Tests', app: { id: 12 }, head_sha: 'b'.repeat(40), conclusion: 'success' }, + { id: 93, name: 'Tests\u202eevil', app: { id: 12 }, head_sha: sha, conclusion: 'success' }], + [{ name: 'Tests', check_run_url: 'https://api.github.com/repos/acme/project/check-runs/90' }, + { name: 'Tests', check_run_url: 'https://api.github.com/repos/acme/project/check-runs/91' }, + { name: 'Tests', check_run_url: 'https://api.github.com/repos/acme/project/check-runs/92' }, + { name: 'Tests', check_run_url: 'https://api.github.com/repos/acme/project/check-runs/93' }], + ); + expect(await new GithubSetupApprovalCheckDiscoveryAdapter().discover(owner, repository, 'secret')).toEqual({ status: 'no-verifiable-checks', candidates: [] }); + }); + + test('distinguishes no recent PR runs from missing permission and provider outage', async () => { + const empty = arrange([], [], []); + expect(await new GithubSetupApprovalCheckDiscoveryAdapter().discover(owner, repository, 'secret')) + .toEqual({ status: 'no-recent-runs', candidates: [] }); + empty.listWorkflowRunsForRepo.mockRejectedValueOnce(Object.assign(new Error('denied'), { status: 403 })); + expect(await new GithubSetupApprovalCheckDiscoveryAdapter().discover(owner, repository, 'secret')) + .toEqual({ status: 'permission-denied', candidates: [] }); + empty.listWorkflowRunsForRepo.mockRejectedValueOnce(new Error('offline')); + expect(await new GithubSetupApprovalCheckDiscoveryAdapter().discover(owner, repository, 'secret')) + .toEqual({ status: 'unavailable', candidates: [] }); + }); +}); diff --git a/src/infrastructure/__tests__/github_setup_project_discovery_adapter.test.ts b/src/infrastructure/__tests__/github_setup_project_discovery_adapter.test.ts new file mode 100644 index 000000000..ee366f48e --- /dev/null +++ b/src/infrastructure/__tests__/github_setup_project_discovery_adapter.test.ts @@ -0,0 +1,63 @@ +import * as github from '@actions/github'; +import { GithubSetupProjectDiscoveryAdapter } from '../github_setup_project_discovery_adapter'; + +jest.mock('@actions/github', () => ({ getOctokit: jest.fn() })); + +const adapter = new GithubSetupProjectDiscoveryAdapter(); +const owner = 'acme'; + +function arrange(request: jest.Mock): void { + (github.getOctokit as jest.Mock).mockReturnValue({ request }); +} + +describe('GitHub setup Project discovery', () => { + beforeEach(() => jest.clearAllMocks()); + + test('lists existing organization Projects and reads their Status options without writes', async () => { + const request = jest.fn().mockImplementation(async (route: string) => route.endsWith('/fields') + ? { data: [{ name: 'Status', data_type: 'single_select', options: [{ name: { raw: 'Todo' } }, { name: { raw: 'In Progress' } }] }], headers: {} } + : { data: [{ number: 5, title: 'Roadmap', state: 'open' }], headers: {} }); + arrange(request); + expect(await adapter.discover(owner, 'Organization', 'secret')).toEqual({ status: 'observed', candidates: [{ + number: 5, title: 'Roadmap', owner, url: 'https://github.com/orgs/acme/projects/5', statusOptions: ['Todo', 'In Progress'], + }] }); + expect(request).toHaveBeenCalledWith('GET /orgs/{org}/projectsV2', { org: owner, per_page: 50 }); + expect(request).toHaveBeenCalledWith('GET /orgs/{org}/projectsV2/{project_number}/fields', { org: owner, project_number: 5, per_page: 100 }); + expect(request.mock.calls.every(([route]) => route.startsWith('GET '))).toBe(true); + }); + + test('uses bounded cursor pagination and marks inaccessible Status options as unverified', async () => { + const request = jest.fn().mockResolvedValueOnce({ data: [{ number: 2, title: 'First' }], + headers: { link: '; rel="next"' } }) + .mockResolvedValueOnce({ data: [{ number: 3, title: 'Second' }], headers: {} }) + .mockResolvedValueOnce({ data: [], headers: {} }) + .mockRejectedValueOnce(Object.assign(new Error('forbidden'), { status: 403 })); + arrange(request); + const result = await adapter.discover(owner, 'Organization', 'secret'); + expect(result).toMatchObject({ status: 'observed', candidates: [ + { number: 2, title: 'First' }, { number: 3, title: 'Second' }, + ] }); + expect(result.candidates.every(candidate => candidate.statusOptions === undefined)).toBe(true); + expect(request).toHaveBeenNthCalledWith(2, 'GET /orgs/{org}/projectsV2', { org: owner, per_page: 50, after: 'cursor2' }); + }); + + test('distinguishes personal-owner unsupported, empty, denied and provider unavailable', async () => { + expect(await adapter.discover(owner, 'User', 'secret')).toEqual({ status: 'unsupported', candidates: [] }); + expect(github.getOctokit).not.toHaveBeenCalled(); + arrange(jest.fn().mockResolvedValue({ data: [], headers: {} })); + expect(await adapter.discover(owner, 'Organization', 'secret')).toEqual({ status: 'empty', candidates: [] }); + arrange(jest.fn().mockRejectedValue(Object.assign(new Error('denied'), { status: 403 }))); + expect(await adapter.discover(owner, 'Organization', 'secret')).toEqual({ status: 'permission-denied', candidates: [] }); + arrange(jest.fn().mockRejectedValue(new Error('offline'))); + expect(await adapter.discover(owner, 'Organization', 'secret')).toEqual({ status: 'unavailable', candidates: [] }); + }); + + test('ignores closed, malformed and unsafe Project rows', async () => { + arrange(jest.fn().mockResolvedValue({ data: [ + { number: 1, title: 'Closed', state: 'closed' }, { number: 0, title: 'Zero' }, + { number: 2, title: ' - - - + async function retryDiscovery(): Promise { + const revision = $session.view?.promptRevision; + if (revision !== undefined) await session.retryDiscovery(revision); + } + async function back(): Promise { + const revision = $session.view?.promptRevision; + if (revision !== undefined) await session.back(revision); + } +
                                                                                                                          + {#if $setupLocale !== 'en'} + + {/if} {#if $session.paired}{/if} {#if $session.view?.journey?.choiceReviewPass && $session.view.journey.choiceReviewPass > 1 && !$session.view.outcome} -
                                                                                                                          Reviewing saved choices — pass {$session.view.journey.choiceReviewPass}. This is the same setup run, not a restart.
                                                                                                                          +
                                                                                                                          {tr('reviewPass', $setupLocale, { pass: String($session.view.journey.choiceReviewPass) })}
                                                                                                                          {/if} {#if !$session.controller && $session.view} - + {/if} - {#if $session.error}{/if} - {#if $session.view?.message} - + {#if $session.error}{/if} + {#if $session.view?.message && !$session.view.outcome} + {/if} {#if !$session.paired} {:else if $session.view?.outcome} - + {:else if $session.view?.prompt}
                                                                                                                          - +
                                                                                                                          - {#if $session.controller && $session.view.journey?.current !== 'Apply'}{/if} + {#if $session.controller && $session.view.journey?.current !== 'Apply'}{/if} {:else} {/if} -
                                                                                                                          LOCALHOST ONLY NO CLOUD SETUP ACCOUNT GITHUB OWNS PAT ISSUANCE
                                                                                                                          +
                                                                                                                          {tr('footerLocal', $setupLocale)} {tr('footerCloud', $setupLocale)} {tr('footerGithub', $setupLocale)}
                                                                                                                          diff --git a/web/src/components/ChoicePrompt.svelte b/web/src/components/ChoicePrompt.svelte index 8da064b38..66dd91ca9 100644 --- a/web/src/components/ChoicePrompt.svelte +++ b/web/src/components/ChoicePrompt.svelte @@ -1,9 +1,11 @@ -
                                                                                                                          {#each prompt.choices as option}{/each}
                                                                                                                          +
                                                                                                                          {#each prompt.choices as option, index}{/each}
                                                                                                                          diff --git a/web/src/components/ContextPanel.svelte b/web/src/components/ContextPanel.svelte index 8e7c5047e..e91d40447 100644 --- a/web/src/components/ContextPanel.svelte +++ b/web/src/components/ContextPanel.svelte @@ -1,16 +1,20 @@ -