From 98262d8fb29c05b202b229dd4479ae7ffe038da4 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Wed, 1 Oct 2025 13:38:25 +0100 Subject: [PATCH 001/298] ci: allow dispatching workflow triage via label (#20042) Allows creating a task for an issue if a label 'traiage' is set. Requires membership of the `coder` org to run. Manual workflow_dispatch: https://github.com/coder/coder/actions/runs/18158719999/job/51684512634 --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .github/workflows/traiage.yaml | 85 ++++++++++++++++++++++++++++------ 1 file changed, 72 insertions(+), 13 deletions(-) diff --git a/.github/workflows/traiage.yaml b/.github/workflows/traiage.yaml index 615c5bec19e7f..dc45f242dbb87 100644 --- a/.github/workflows/traiage.yaml +++ b/.github/workflows/traiage.yaml @@ -1,6 +1,9 @@ name: AI Triage Automation on: + issues: + types: + - labeled workflow_dispatch: inputs: issue_url: @@ -32,6 +35,7 @@ jobs: traiage: name: Triage GitHub Issue with Claude Code runs-on: ubuntu-latest + if: github.event.label.name == 'traiage' || github.event_name == 'workflow_dispatch' timeout-minutes: 30 env: CODER_URL: ${{ secrets.TRAIAGE_CODER_URL }} @@ -43,17 +47,58 @@ jobs: actions: write steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - persist-credentials: false - fetch-depth: 0 + - name: Get GitHub user ID + id: github-user-id + if: always() + env: + GITHUB_ACTOR: ${{ github.actor }} + GITHUB_EVENT_NAME: ${{ github.event_name }} + GITHUB_EVENT_USER_ID: ${{ github.event.sender.id }} + GITHUB_EVENT_USER_LOGIN: ${{ github.event.sender.login }} + GH_TOKEN: ${{ github.token }} + run: | + # For workflow_dispatch, use the actor who triggered it + # For issues events, use the issue author + if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then + if ! GITHUB_USER_ID=$(gh api "users/${GITHUB_ACTOR}" --jq '.id'); then + echo "::error::Failed to get GitHub user ID for actor ${GITHUB_ACTOR}" + exit 1 + fi + echo "Using workflow_dispatch actor: ${GITHUB_ACTOR} (ID: ${GITHUB_USER_ID})" + echo "github_user_id=${GITHUB_USER_ID}" >> "${GITHUB_OUTPUT}" + echo "github_username=${GITHUB_ACTOR}" >> "${GITHUB_OUTPUT}" + exit 0 + elif [[ "${GITHUB_EVENT_NAME}" == "issues" ]]; then + GITHUB_USER_ID=${GITHUB_EVENT_USER_ID} + echo "Using issue author: ${GITHUB_EVENT_USER_LOGIN} (ID: ${GITHUB_USER_ID})" + echo "github_user_id=${GITHUB_USER_ID}" >> "${GITHUB_OUTPUT}" + echo "github_username=${GITHUB_EVENT_USER_LOGIN}" >> "${GITHUB_OUTPUT}" + exit 0 + else + echo "::error::Unsupported event type: ${GITHUB_EVENT_NAME}" + exit 1 + fi + + - name: Verify organization membership + env: + GITHUB_ORG: ${{ github.repository_owner }} + GH_TOKEN: ${{ github.token }} + GITHUB_USERNAME: ${{ steps.github-user-id.outputs.github_username }} + GITHUB_USER_ID: ${{ steps.github-user-id.outputs.github_user_id }} + run: | + # Check if the actor is a member of the organization + if ! gh api "orgs/${GITHUB_ORG}/members/${GITHUB_USERNAME}" --silent 2>/dev/null; then + echo "::error title=Access Denied::User ${GITHUB_USERNAME} is not a member of the ${GITHUB_ORG} organization" + echo "::error::You must be a member of the ${GITHUB_ORG} GitHub organization to run this workflow." + exit 1 + fi + echo "::notice::User ${GITHUB_USERNAME} verified as member of ${GITHUB_ORG} organization" - name: Extract context key from issue id: extract-context env: ISSUE_URL: ${{ inputs.issue_url }} - GITHUB_TOKEN: ${{ github.token }} + GH_TOKEN: ${{ github.token }} run: | issue_number="$(gh issue view "${ISSUE_URL}" --json number --jq '.number')" context_key="gh-${issue_number}" @@ -82,11 +127,9 @@ jobs: id: get-coder-username env: CODER_SESSION_TOKEN: ${{ secrets.TRAIAGE_CODER_SESSION_TOKEN }} - GITHUB_USER_ID: ${{ - (github.event_name == 'workflow_dispatch' && github.actor_id) - }} + GH_TOKEN: ${{ github.token }} + GITHUB_USER_ID: ${{ steps.github-user-id.outputs.github_user_id }} run: | - [[ -z "${GITHUB_USER_ID}" || "${GITHUB_USER_ID}" == "null" ]] && echo "No GitHub actor ID found" && exit 1 user_json=$( coder users list --github-user-id="${GITHUB_USER_ID}" --output=json ) @@ -94,13 +137,20 @@ jobs: [[ -z "${coder_username}" || "${coder_username}" == "null" ]] && echo "No Coder user with GitHub user ID ${GITHUB_USER_ID} found" && exit 1 echo "coder_username=${coder_username}" >> "${GITHUB_OUTPUT}" + - name: Checkout repository + uses: actions/checkout@v4 + with: + persist-credentials: false + fetch-depth: 0 + # TODO(Cian): this is a good use-case for 'recipes' - name: Create Coder task id: create-task env: CODER_USERNAME: ${{ steps.get-coder-username.outputs.coder_username }} CONTEXT_KEY: ${{ steps.extract-context.outputs.context_key }} - GITHUB_TOKEN: ${{ github.token }} + GH_TOKEN: ${{ github.token }} + GITHUB_REPOSITORY: ${{ github.repository }} ISSUE_URL: ${{ inputs.issue_url }} PREFIX: ${{ inputs.prefix }} RUN_ID: ${{ github.run_id }} @@ -125,7 +175,11 @@ jobs: export TASK_NAME="${PREFIX}-${CONTEXT_KEY}-${RUN_ID}" echo "Creating task: $TASK_NAME" ./scripts/traiage.sh create - coder exp task status "${CODER_USERNAME}/$TASK_NAME" --watch + if [[ "${ISSUE_URL}" == "https://github.com/${GITHUB_REPOSITORY}"* ]]; then + gh issue comment "${ISSUE_URL}" --body "Task created: ${TASK_NAME}" --create-if-none --edit-last + else + echo "Skipping comment on other repo." + fi echo "TASK_NAME=${CODER_USERNAME}/${TASK_NAME}" >> "${GITHUB_OUTPUT}" echo "TASK_NAME=${CODER_USERNAME}/${TASK_NAME}" >> "${GITHUB_ENV}" @@ -134,7 +188,11 @@ jobs: if: inputs.cleanup env: BUCKET_PREFIX: "gs://coder-traiage-outputs/traiage" + CODER_USERNAME: ${{ steps.get-coder-username.outputs.coder_username }} + TASK_NAME: ${{ steps.create-task.outputs.TASK_NAME }} run: | + echo "Waiting for task to complete..." + coder exp task status "${CODER_USERNAME}/$TASK_NAME" --watch echo "Creating archive for workspace: $TASK_NAME" ./scripts/traiage.sh archive echo "archive_url=${BUCKET_PREFIX%%/}/$TASK_NAME.tar.gz" >> "${GITHUB_OUTPUT}" @@ -145,8 +203,9 @@ jobs: env: ARCHIVE_URL: ${{ steps.create-archive.outputs.archive_url }} BUCKET_PREFIX: "gs://coder-traiage-outputs/traiage" + CODER_USERNAME: ${{ steps.get-coder-username.outputs.coder_username }} CONTEXT_KEY: ${{ steps.extract-context.outputs.context_key }} - GITHUB_TOKEN: ${{ github.token }} + GH_TOKEN: ${{ github.token }} GITHUB_REPOSITORY: ${{ github.repository }} ISSUE_URL: ${{ inputs.issue_url }} TASK_NAME: ${{ steps.create-task.outputs.TASK_NAME }} From 257fb768828e991df3715da69f6a67d10256ee3b Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Wed, 1 Oct 2025 15:50:40 +0100 Subject: [PATCH 002/298] ci: automatically determine issue URL when invoked via issue label assignment (#20089) Silly me forgot that `inputs.*` will likely be empty when invoked outside of `workflow_dispatch`. Sample run: https://github.com/coder/coder/actions/runs/18165531528/job/51706661391 --- .github/workflows/traiage.yaml | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/.github/workflows/traiage.yaml b/.github/workflows/traiage.yaml index dc45f242dbb87..24192fc2d1812 100644 --- a/.github/workflows/traiage.yaml +++ b/.github/workflows/traiage.yaml @@ -94,10 +94,28 @@ jobs: fi echo "::notice::User ${GITHUB_USERNAME} verified as member of ${GITHUB_ORG} organization" + - name: Determine issue URL + id: determine-issue-url + env: + INPUTS_ISSUE_URL: ${{ inputs.issue_url }} + GITHUB_EVENT_ISSUE_HTML_URL: ${{ github.event.issue.html_url }} + GITHUB_EVENT_NAME: ${{ github.event_name }} + run: | + if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then + echo "issue_url=${INPUTS_ISSUE_URL}" >> "${GITHUB_OUTPUT}" + exit 0 + elif [[ "${GITHUB_EVENT_NAME}" == "issues" ]]; then + echo "issue_url=${GITHUB_EVENT_ISSUE_HTML_URL}" >> "${GITHUB_OUTPUT}" + exit 0 + else + echo "::error::Unsupported event type: ${GITHUB_EVENT_NAME}" + exit 1 + fi + - name: Extract context key from issue id: extract-context env: - ISSUE_URL: ${{ inputs.issue_url }} + ISSUE_URL: ${{ steps.determine-issue-url.outputs.issue_url }} GH_TOKEN: ${{ github.token }} run: | issue_number="$(gh issue view "${ISSUE_URL}" --json number --jq '.number')" @@ -151,7 +169,7 @@ jobs: CONTEXT_KEY: ${{ steps.extract-context.outputs.context_key }} GH_TOKEN: ${{ github.token }} GITHUB_REPOSITORY: ${{ github.repository }} - ISSUE_URL: ${{ inputs.issue_url }} + ISSUE_URL: ${{ steps.determine-issue-url.outputs.issue_url }} PREFIX: ${{ inputs.prefix }} RUN_ID: ${{ github.run_id }} TEMPLATE_PARAMETERS: ${{ secrets.TRAIAGE_TEMPLATE_PARAMETERS }} @@ -207,7 +225,7 @@ jobs: CONTEXT_KEY: ${{ steps.extract-context.outputs.context_key }} GH_TOKEN: ${{ github.token }} GITHUB_REPOSITORY: ${{ github.repository }} - ISSUE_URL: ${{ inputs.issue_url }} + ISSUE_URL: ${{ steps.determine-issue-url.outputs.issue_url }} TASK_NAME: ${{ steps.create-task.outputs.TASK_NAME }} run: | SUMMARY_FILE=$(mktemp) From 3a56ea56a7f5357902ca4ed5c1aff54bf40b899f Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Wed, 1 Oct 2025 13:23:51 -0500 Subject: [PATCH 003/298] test: fix rbac benchmark to test performance instead of cache (#20097) The benchmark should be testing the performance of `authorize`, not a cache lookup --- coderd/rbac/authz_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coderd/rbac/authz_test.go b/coderd/rbac/authz_test.go index cd2bbb808add9..25955131242a8 100644 --- a/coderd/rbac/authz_test.go +++ b/coderd/rbac/authz_test.go @@ -187,7 +187,7 @@ func BenchmarkRBACAuthorizeGroups(b *testing.B) { uuid.MustParse("0632b012-49e0-4d70-a5b3-f4398f1dcd52"), uuid.MustParse("70dbaa7a-ea9c-4f68-a781-97b08af8461d"), ) - authorizer := rbac.NewStrictCachingAuthorizer(prometheus.NewRegistry()) + authorizer := rbac.NewAuthorizer(prometheus.NewRegistry()) // Same benchmark cases, but this time groups will be used to match. // Some '*' permissions will still match, but using a fake action reduces From 0fbe21e574dc27b5f9cc2bb50aec29c84c40926c Mon Sep 17 00:00:00 2001 From: Bruno Quaresma Date: Wed, 1 Oct 2025 15:24:20 -0300 Subject: [PATCH 004/298] chore: downgrade msw and @radix-ui/dialog (#20098) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The upgrade caused the following error: ``` node: ../deps/uv/src/unix/stream.c:456: uv__stream_destroy: Assertion `!uv__io_active(&stream->io_watcher, POLLIN | POLLOUT)' failed. ``` After downgrading `msw`, a new error appeared only in `WorkspacesPage.test.tsx`: ``` <--- Last few GCs ---> [2799:0x292c2000] 16790 ms: Scavenge 336.1 (443.3) -> 322.8 (443.3) MB, pooled: 32 MB, 6.45 / 0.00 ms (average mu = 0.997, current mu = 0.996) allocation failure; [2799:0x292c2000] 16883 ms: Scavenge 336.7 (443.3) -> 326.8 (443.3) MB, pooled: 32 MB, 8.29 / 0.00 ms (average mu = 0.997, current mu = 0.996) allocation failure; [2799:0x292c2000] 16989 ms: Scavenge 339.6 (443.3) -> 329.1 (443.3) MB, pooled: 32 MB, 9.87 / 0.00 ms (average mu = 0.997, current mu = 0.996) allocation failure; ``` After some debugging, I traced it to `@radix-ui/dialog`. I didn’t find any open issues about memory leaks there, so my guess is it’s just using more memory than our default allocation. Jest has an option to increase the memory limit, but we should be fine for now. Related issue: [https://github.com/mswjs/msw/issues/2537](https://github.com/mswjs/msw/issues/2537) --- site/package.json | 4 +- site/pnpm-lock.yaml | 308 ++++++++++++++++++++------------------------ 2 files changed, 139 insertions(+), 173 deletions(-) diff --git a/site/package.json b/site/package.json index 7a52d5545eb8d..11ed58a05838a 100644 --- a/site/package.json +++ b/site/package.json @@ -55,7 +55,7 @@ "@radix-ui/react-avatar": "1.1.2", "@radix-ui/react-checkbox": "1.1.4", "@radix-ui/react-collapsible": "1.1.2", - "@radix-ui/react-dialog": "1.1.15", + "@radix-ui/react-dialog": "1.1.4", "@radix-ui/react-dropdown-menu": "2.1.4", "@radix-ui/react-label": "2.1.0", "@radix-ui/react-popover": "1.1.5", @@ -169,7 +169,7 @@ "jest-websocket-mock": "2.5.0", "jest_workaround": "0.1.14", "knip": "5.64.1", - "msw": "2.11.3", + "msw": "2.4.8", "postcss": "8.5.1", "protobufjs": "7.4.0", "rollup-plugin-visualizer": "5.14.0", diff --git a/site/pnpm-lock.yaml b/site/pnpm-lock.yaml index c77df41bc3ede..676011c935c76 100644 --- a/site/pnpm-lock.yaml +++ b/site/pnpm-lock.yaml @@ -80,8 +80,8 @@ importers: specifier: 1.1.2 version: 1.1.2(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@radix-ui/react-dialog': - specifier: 1.1.15 - version: 1.1.15(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + specifier: 1.1.4 + version: 1.1.4(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@radix-ui/react-dropdown-menu': specifier: 2.1.4 version: 2.1.4(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) @@ -283,7 +283,7 @@ importers: version: 2.2.0 '@chromatic-com/storybook': specifier: 4.1.0 - version: 4.1.0(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.11.3(@types/node@20.17.16)(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))) + version: 4.1.0(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))) '@octokit/types': specifier: 12.3.0 version: 12.3.0 @@ -292,16 +292,16 @@ importers: version: 1.55.1 '@storybook/addon-docs': specifier: 9.1.2 - version: 9.1.2(@types/react@19.1.13)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.11.3(@types/node@20.17.16)(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))) + version: 9.1.2(@types/react@19.1.13)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))) '@storybook/addon-links': specifier: 9.1.2 - version: 9.1.2(react@19.1.1)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.11.3(@types/node@20.17.16)(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))) + version: 9.1.2(react@19.1.1)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))) '@storybook/addon-themes': specifier: 9.1.2 - version: 9.1.2(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.11.3(@types/node@20.17.16)(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))) + version: 9.1.2(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))) '@storybook/react-vite': specifier: 9.1.2 - version: 9.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(rollup@4.52.3)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.11.3(@types/node@20.17.16)(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)))(typescript@5.6.3)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) + version: 9.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(rollup@4.52.3)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)))(typescript@5.6.3)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) '@swc/core': specifier: 1.3.38 version: 1.3.38 @@ -417,8 +417,8 @@ importers: specifier: 5.64.1 version: 5.64.1(@types/node@20.17.16)(typescript@5.6.3) msw: - specifier: 2.11.3 - version: 2.11.3(@types/node@20.17.16)(typescript@5.6.3) + specifier: 2.4.8 + version: 2.4.8(typescript@5.6.3) postcss: specifier: 8.5.1 version: 8.5.1 @@ -436,10 +436,10 @@ importers: version: 1.17.0 storybook: specifier: 9.1.2 - version: 9.1.2(@testing-library/dom@10.4.0)(msw@2.11.3(@types/node@20.17.16)(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) + version: 9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) storybook-addon-remix-react-router: specifier: 5.0.0 - version: 5.0.0(react-dom@19.1.1(react@19.1.1))(react-router@7.8.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.11.3(@types/node@20.17.16)(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))) + version: 5.0.0(react-dom@19.1.1(react@19.1.1))(react-router@7.8.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))) tailwindcss: specifier: 3.4.17 version: 3.4.17(ts-node@10.9.2(@swc/core@1.3.38)(@types/node@20.17.16)(typescript@5.6.3)) @@ -717,6 +717,9 @@ packages: '@bundled-es-modules/statuses@1.0.1': resolution: {integrity: sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==, tarball: https://registry.npmjs.org/@bundled-es-modules/statuses/-/statuses-1.0.1.tgz} + '@bundled-es-modules/tough-cookie@0.1.6': + resolution: {integrity: sha512-dvMHbL464C0zI+Yqxbz6kZ5TOEp7GLW+pry/RWndAR8MJQAXZ2rPmIs8tziTZjeIyhSNZgZbCePtfSbdWqStJw==, tarball: https://registry.npmjs.org/@bundled-es-modules/tough-cookie/-/tough-cookie-0.1.6.tgz} + '@chromatic-com/storybook@4.1.0': resolution: {integrity: sha512-B9XesFX5lQUdP81/QBTtkiYOFqEsJwQpzkZlcYPm2n/L1S/8ZabSPbz6NoY8hOJTXWZ2p7grygUlxyGy+gAvfQ==, tarball: https://registry.npmjs.org/@chromatic-com/storybook/-/storybook-4.1.0.tgz} engines: {node: '>=20.0.0', yarn: '>=1.22.18'} @@ -1189,40 +1192,25 @@ packages: peerDependencies: react: '*' - '@inquirer/ansi@1.0.0': - resolution: {integrity: sha512-JWaTfCxI1eTmJ1BIv86vUfjVatOdxwD0DAVKYevY8SazeUUZtW+tNbsdejVO1GYE0GXJW1N1ahmiC3TFd+7wZA==, tarball: https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.0.tgz} + '@inquirer/confirm@3.2.0': + resolution: {integrity: sha512-oOIwPs0Dvq5220Z8lGL/6LHRTEr9TgLHmiI99Rj1PJ1p1czTys+olrgBqZk4E2qC0YTzeHprxSQmoHioVdJ7Lw==, tarball: https://registry.npmjs.org/@inquirer/confirm/-/confirm-3.2.0.tgz} engines: {node: '>=18'} - '@inquirer/confirm@5.1.18': - resolution: {integrity: sha512-MilmWOzHa3Ks11tzvuAmFoAd/wRuaP3SwlT1IZhyMke31FKLxPiuDWcGXhU+PKveNOpAc4axzAgrgxuIJJRmLw==, tarball: https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.18.tgz} + '@inquirer/core@9.2.1': + resolution: {integrity: sha512-F2VBt7W/mwqEU4bL0RnHNZmC/OxzNx9cOYxHqnXX3MP6ruYvZUZAW9imgN9+h/uBT/oP8Gh888J2OZSbjSeWcg==, tarball: https://registry.npmjs.org/@inquirer/core/-/core-9.2.1.tgz} engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - - '@inquirer/core@10.2.2': - resolution: {integrity: sha512-yXq/4QUnk4sHMtmbd7irwiepjB8jXU0kkFRL4nr/aDBA2mDz13cMakEWdDwX3eSCTkk03kwcndD1zfRAIlELxA==, tarball: https://registry.npmjs.org/@inquirer/core/-/core-10.2.2.tgz} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true '@inquirer/figures@1.0.13': resolution: {integrity: sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw==, tarball: https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.13.tgz} engines: {node: '>=18'} - '@inquirer/type@3.0.8': - resolution: {integrity: sha512-lg9Whz8onIHRthWaN1Q9EGLa/0LFJjyM8mEUbL1eTi6yMGvBf8gvyDLtxSXztQsxMvhxxNpJYrwa1YHdq+w4Jw==, tarball: https://registry.npmjs.org/@inquirer/type/-/type-3.0.8.tgz} + '@inquirer/type@1.5.5': + resolution: {integrity: sha512-MzICLu4yS7V8AA61sANROZ9vT1H3ooca5dSmI1FjZkzq7o/koMsRfQSzRtFo+F3Ao4Sf1C0bpLKejpKB/+j6MA==, tarball: https://registry.npmjs.org/@inquirer/type/-/type-1.5.5.tgz} + engines: {node: '>=18'} + + '@inquirer/type@2.0.0': + resolution: {integrity: sha512-XvJRx+2KR3YXyYtPUUy+qd9i7p+GO9Ko6VIIpWlBrpWwXDv8WLFeHTxz35CfQFUiBMLXlGHhGzys7lqit9gWag==, tarball: https://registry.npmjs.org/@inquirer/type/-/type-2.0.0.tgz} engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==, tarball: https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz} @@ -1391,8 +1379,8 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - '@mswjs/interceptors@0.39.7': - resolution: {integrity: sha512-sURvQbbKsq5f8INV54YJgJEdk8oxBanqkTiXXd33rKmofFCwZLhLRszPduMZ9TA9b8/1CHc/IJmOlBHJk2Q5AQ==, tarball: https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.39.7.tgz} + '@mswjs/interceptors@0.35.9': + resolution: {integrity: sha512-SSnyl/4ni/2ViHKkiZb8eajA/eN1DNFaHjhGiLUdZvDz6PKF4COSf/17xqSz64nOo2Ia29SA6B2KNCsyCbVmaQ==, tarball: https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.35.9.tgz} engines: {node: '>=18'} '@mui/core-downloads-tracker@5.18.0': @@ -1839,8 +1827,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-dialog@1.1.15': - resolution: {integrity: sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==, tarball: https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz} + '@radix-ui/react-dialog@1.1.4': + resolution: {integrity: sha512-Ur7EV1IwQGCyaAuyDRiOLA5JIUZxELJljF+MbM/2NC0BYwfuRrbpS30BiQBJrVruscgUkieKkqXYDOoByaxIoA==, tarball: https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.4.tgz} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -2088,19 +2076,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-presence@1.1.5': - resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==, tarball: https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-primitive@2.0.0': resolution: {integrity: sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==, tarball: https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.0.tgz} peerDependencies: @@ -2970,12 +2945,18 @@ packages: '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==, tarball: https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz} + '@types/mute-stream@0.0.4': + resolution: {integrity: sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==, tarball: https://registry.npmjs.org/@types/mute-stream/-/mute-stream-0.0.4.tgz} + '@types/node@18.19.129': resolution: {integrity: sha512-hrmi5jWt2w60ayox3iIXwpMEnfUvOLJCRtrOPbHtH15nTjvO7uhnelvrdAs0dO0/zl5DZ3ZbahiaXEVb54ca/A==, tarball: https://registry.npmjs.org/@types/node/-/node-18.19.129.tgz} '@types/node@20.17.16': resolution: {integrity: sha512-vOTpLduLkZXePLxHiHsBLp98mHGnl8RptV4YAO3HfKO5UHjDvySGbxKtpYfy8Sx5+WKcgc45qNreJJRVM3L6mw==, tarball: https://registry.npmjs.org/@types/node/-/node-20.17.16.tgz} + '@types/node@22.18.8': + resolution: {integrity: sha512-pAZSHMiagDR7cARo/cch1f3rXy0AEXwsVsVH09FcyeJVAzCnGgmYis7P3JidtTUjyadhTeSo8TgRPswstghDaw==, tarball: https://registry.npmjs.org/@types/node/-/node-22.18.8.tgz} + '@types/parse-json@4.0.2': resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==, tarball: https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz} @@ -3053,6 +3034,9 @@ packages: '@types/tough-cookie@4.0.2': resolution: {integrity: sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==, tarball: https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.2.tgz} + '@types/tough-cookie@4.0.5': + resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==, tarball: https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz} + '@types/ua-parser-js@0.7.36': resolution: {integrity: sha512-N1rW+njavs70y2cApeIw1vLMYXRwfBy+7trgavGuuTfOd7j1Yh7QTRc/yqsPl6ncokt72ZXuxEU0PiCp9bSwNQ==, tarball: https://registry.npmjs.org/@types/ua-parser-js/-/ua-parser-js-0.7.36.tgz} @@ -3068,6 +3052,9 @@ packages: '@types/uuid@9.0.2': resolution: {integrity: sha512-kNnC1GFBLuhImSnV7w4njQkUiJi0ZXUycu1rUaouPqiKlXkh77JKgdRnTAp1x5eBwcIwbtI+3otwzuIDEuDoxQ==, tarball: https://registry.npmjs.org/@types/uuid/-/uuid-9.0.2.tgz} + '@types/wrap-ansi@3.0.0': + resolution: {integrity: sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==, tarball: https://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz} + '@types/yargs-parser@21.0.2': resolution: {integrity: sha512-5qcvofLPbfjmBfKaLfj/+f+Sbd6pN4zl7w7VSVI5uz7m9QZTuB2aZAa2uo1wHFBNN2x6g/SoTkXmd8mQnQF2Cw==, tarball: https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.2.tgz} @@ -5224,8 +5211,8 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==, tarball: https://registry.npmjs.org/ms/-/ms-2.1.3.tgz} - msw@2.11.3: - resolution: {integrity: sha512-878imp8jxIpfzuzxYfX0qqTq1IFQz/1/RBHs/PyirSjzi+xKM/RRfIpIqHSCWjH0GxidrjhgiiXC+DWXNDvT9w==, tarball: https://registry.npmjs.org/msw/-/msw-2.11.3.tgz} + msw@2.4.8: + resolution: {integrity: sha512-a+FUW1m5yT8cV9GBy0L/cbNg0EA4//SKEzgu3qFrpITrWYeZmqfo7dqtM74T2lAl69jjUjjCaEhZKaxG2Ns8DA==, tarball: https://registry.npmjs.org/msw/-/msw-2.4.8.tgz} engines: {node: '>=18'} hasBin: true peerDependencies: @@ -5234,9 +5221,9 @@ packages: typescript: optional: true - mute-stream@2.0.0: - resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==, tarball: https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz} - engines: {node: ^18.17.0 || >=20.5.0} + mute-stream@1.0.0: + resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==, tarball: https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==, tarball: https://registry.npmjs.org/mz/-/mz-2.7.0.tgz} @@ -5876,9 +5863,6 @@ packages: resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==, tarball: https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz} engines: {node: '>=8'} - rettime@0.7.0: - resolution: {integrity: sha512-LPRKoHnLKd/r3dVxcwO7vhCW+orkOGj9ViueosEBK6ie89CijnfRlhaDhHq/3Hxu4CkWQtxwlBG0mzTQY6uQjw==, tarball: https://registry.npmjs.org/rettime/-/rettime-0.7.0.tgz} - reusify@1.1.0: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==, tarball: https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -6220,13 +6204,6 @@ packages: resolution: {integrity: sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==, tarball: https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.3.tgz} engines: {node: '>=14.0.0'} - tldts-core@7.0.16: - resolution: {integrity: sha512-XHhPmHxphLi+LGbH0G/O7dmUH9V65OY20R7vH8gETHsp5AZCjBk9l8sqmRKLaGOxnETU7XNSDUPtewAy/K6jbA==, tarball: https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.16.tgz} - - tldts@7.0.16: - resolution: {integrity: sha512-5bdPHSwbKTeHmXrgecID4Ljff8rQjv7g8zKQPkCozRo2HWWni+p310FSn5ImI+9kWw9kK4lzOB5q/a6iv0IJsw==, tarball: https://registry.npmjs.org/tldts/-/tldts-7.0.16.tgz} - hasBin: true - tmpl@1.0.5: resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==, tarball: https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz} @@ -6245,10 +6222,6 @@ packages: resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==, tarball: https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz} engines: {node: '>=6'} - tough-cookie@6.0.0: - resolution: {integrity: sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==, tarball: https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.0.tgz} - engines: {node: '>=16'} - tr46@3.0.0: resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==, tarball: https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz} engines: {node: '>=12'} @@ -6355,6 +6328,9 @@ packages: undici-types@6.19.8: resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==, tarball: https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz} + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==, tarball: https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz} + undici@6.21.3: resolution: {integrity: sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==, tarball: https://registry.npmjs.org/undici/-/undici-6.21.3.tgz} engines: {node: '>=18.17'} @@ -6407,9 +6383,6 @@ packages: unplugin@1.5.0: resolution: {integrity: sha512-9ZdRwbh/4gcm1JTOkp9lAkIDrtOyOxgHmY7cjuwI8L/2RTikMcVG25GsZwNAgRuap3iDw2jeq7eoqtAsz5rW3A==, tarball: https://registry.npmjs.org/unplugin/-/unplugin-1.5.0.tgz} - until-async@3.0.2: - resolution: {integrity: sha512-IiSk4HlzAMqTUseHHe3VhIGyuFmN90zMTpD3Z3y8jeQbzLIq500MVM7Jq2vUAnTKAFPJrqwkzr6PoTcPhGcOiw==, tarball: https://registry.npmjs.org/until-async/-/until-async-3.0.2.tgz} - update-browserslist-db@1.1.1: resolution: {integrity: sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==, tarball: https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz} hasBin: true @@ -7021,13 +6994,18 @@ snapshots: dependencies: statuses: 2.0.2 - '@chromatic-com/storybook@4.1.0(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.11.3(@types/node@20.17.16)(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)))': + '@bundled-es-modules/tough-cookie@0.1.6': + dependencies: + '@types/tough-cookie': 4.0.5 + tough-cookie: 4.1.4 + + '@chromatic-com/storybook@4.1.0(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)))': dependencies: '@neoconfetti/react': 1.0.0 chromatic: 12.2.0 filesize: 10.1.2 jsonfile: 6.1.0 - storybook: 9.1.2(@testing-library/dom@10.4.0)(msw@2.11.3(@types/node@20.17.16)(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) + storybook: 9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) strip-ansi: 7.1.0 transitivePeerDependencies: - '@chromatic-com/cypress' @@ -7397,33 +7375,35 @@ snapshots: dependencies: react: 19.1.1 - '@inquirer/ansi@1.0.0': {} - - '@inquirer/confirm@5.1.18(@types/node@20.17.16)': + '@inquirer/confirm@3.2.0': dependencies: - '@inquirer/core': 10.2.2(@types/node@20.17.16) - '@inquirer/type': 3.0.8(@types/node@20.17.16) - optionalDependencies: - '@types/node': 20.17.16 + '@inquirer/core': 9.2.1 + '@inquirer/type': 1.5.5 - '@inquirer/core@10.2.2(@types/node@20.17.16)': + '@inquirer/core@9.2.1': dependencies: - '@inquirer/ansi': 1.0.0 '@inquirer/figures': 1.0.13 - '@inquirer/type': 3.0.8(@types/node@20.17.16) + '@inquirer/type': 2.0.0 + '@types/mute-stream': 0.0.4 + '@types/node': 22.18.8 + '@types/wrap-ansi': 3.0.0 + ansi-escapes: 4.3.2 cli-width: 4.1.0 - mute-stream: 2.0.0 + mute-stream: 1.0.0 signal-exit: 4.1.0 + strip-ansi: 6.0.1 wrap-ansi: 6.2.0 yoctocolors-cjs: 2.1.3 - optionalDependencies: - '@types/node': 20.17.16 '@inquirer/figures@1.0.13': {} - '@inquirer/type@3.0.8(@types/node@20.17.16)': - optionalDependencies: - '@types/node': 20.17.16 + '@inquirer/type@1.5.5': + dependencies: + mute-stream: 1.0.0 + + '@inquirer/type@2.0.0': + dependencies: + mute-stream: 1.0.0 '@isaacs/cliui@8.0.2': dependencies: @@ -7715,7 +7695,7 @@ snapshots: react: 19.1.1 react-dom: 19.1.1(react@19.1.1) - '@mswjs/interceptors@0.39.7': + '@mswjs/interceptors@0.35.9': dependencies: '@open-draft/deferred-promise': 2.2.0 '@open-draft/logger': 0.3.0 @@ -8100,20 +8080,20 @@ snapshots: optionalDependencies: '@types/react': 19.1.13 - '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@radix-ui/react-dialog@1.1.4(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-id': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-slot': 1.2.3(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-context': 1.1.1(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-dismissable-layer': 1.1.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-focus-guards': 1.1.1(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-focus-scope': 1.1.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-id': 1.1.0(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-portal': 1.1.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-slot': 1.1.1(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.1.13)(react@19.1.1) aria-hidden: 1.2.6 react: 19.1.1 react-dom: 19.1.1(react@19.1.1) @@ -8360,16 +8340,6 @@ snapshots: '@types/react': 19.1.13 '@types/react-dom': 19.1.9(@types/react@19.1.13) - '@radix-ui/react-presence@1.1.5(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.13)(react@19.1.1) - react: 19.1.1 - react-dom: 19.1.1(react@19.1.1) - optionalDependencies: - '@types/react': 19.1.13 - '@types/react-dom': 19.1.9(@types/react@19.1.13) - '@radix-ui/react-primitive@2.0.0(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: '@radix-ui/react-slot': 1.1.0(@types/react@19.1.13)(react@19.1.1) @@ -8803,41 +8773,41 @@ snapshots: dependencies: '@sinonjs/commons': 3.0.0 - '@storybook/addon-docs@9.1.2(@types/react@19.1.13)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.11.3(@types/node@20.17.16)(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)))': + '@storybook/addon-docs@9.1.2(@types/react@19.1.13)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)))': dependencies: '@mdx-js/react': 3.0.1(@types/react@19.1.13)(react@19.1.1) - '@storybook/csf-plugin': 9.1.2(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.11.3(@types/node@20.17.16)(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))) + '@storybook/csf-plugin': 9.1.2(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))) '@storybook/icons': 1.4.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@storybook/react-dom-shim': 9.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.11.3(@types/node@20.17.16)(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))) + '@storybook/react-dom-shim': 9.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) - storybook: 9.1.2(@testing-library/dom@10.4.0)(msw@2.11.3(@types/node@20.17.16)(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) + storybook: 9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) ts-dedent: 2.2.0 transitivePeerDependencies: - '@types/react' - '@storybook/addon-links@9.1.2(react@19.1.1)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.11.3(@types/node@20.17.16)(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)))': + '@storybook/addon-links@9.1.2(react@19.1.1)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)))': dependencies: '@storybook/global': 5.0.0 - storybook: 9.1.2(@testing-library/dom@10.4.0)(msw@2.11.3(@types/node@20.17.16)(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) + storybook: 9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) optionalDependencies: react: 19.1.1 - '@storybook/addon-themes@9.1.2(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.11.3(@types/node@20.17.16)(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)))': + '@storybook/addon-themes@9.1.2(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)))': dependencies: - storybook: 9.1.2(@testing-library/dom@10.4.0)(msw@2.11.3(@types/node@20.17.16)(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) + storybook: 9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) ts-dedent: 2.2.0 - '@storybook/builder-vite@9.1.2(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.11.3(@types/node@20.17.16)(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)))(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))': + '@storybook/builder-vite@9.1.2(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)))(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))': dependencies: - '@storybook/csf-plugin': 9.1.2(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.11.3(@types/node@20.17.16)(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))) - storybook: 9.1.2(@testing-library/dom@10.4.0)(msw@2.11.3(@types/node@20.17.16)(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) + '@storybook/csf-plugin': 9.1.2(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))) + storybook: 9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) ts-dedent: 2.2.0 vite: 7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0) - '@storybook/csf-plugin@9.1.2(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.11.3(@types/node@20.17.16)(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)))': + '@storybook/csf-plugin@9.1.2(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)))': dependencies: - storybook: 9.1.2(@testing-library/dom@10.4.0)(msw@2.11.3(@types/node@20.17.16)(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) + storybook: 9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) unplugin: 1.5.0 '@storybook/global@5.0.0': {} @@ -8847,25 +8817,25 @@ snapshots: react: 19.1.1 react-dom: 19.1.1(react@19.1.1) - '@storybook/react-dom-shim@9.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.11.3(@types/node@20.17.16)(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)))': + '@storybook/react-dom-shim@9.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)))': dependencies: react: 19.1.1 react-dom: 19.1.1(react@19.1.1) - storybook: 9.1.2(@testing-library/dom@10.4.0)(msw@2.11.3(@types/node@20.17.16)(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) + storybook: 9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) - '@storybook/react-vite@9.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(rollup@4.52.3)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.11.3(@types/node@20.17.16)(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)))(typescript@5.6.3)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))': + '@storybook/react-vite@9.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(rollup@4.52.3)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)))(typescript@5.6.3)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))': dependencies: '@joshwooding/vite-plugin-react-docgen-typescript': 0.6.1(typescript@5.6.3)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) '@rollup/pluginutils': 5.0.5(rollup@4.52.3) - '@storybook/builder-vite': 9.1.2(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.11.3(@types/node@20.17.16)(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)))(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) - '@storybook/react': 9.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.11.3(@types/node@20.17.16)(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)))(typescript@5.6.3) + '@storybook/builder-vite': 9.1.2(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)))(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) + '@storybook/react': 9.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)))(typescript@5.6.3) find-up: 7.0.0 magic-string: 0.30.17 react: 19.1.1 react-docgen: 8.0.0 react-dom: 19.1.1(react@19.1.1) resolve: 1.22.10 - storybook: 9.1.2(@testing-library/dom@10.4.0)(msw@2.11.3(@types/node@20.17.16)(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) + storybook: 9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) tsconfig-paths: 4.2.0 vite: 7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0) transitivePeerDependencies: @@ -8873,13 +8843,13 @@ snapshots: - supports-color - typescript - '@storybook/react@9.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.11.3(@types/node@20.17.16)(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)))(typescript@5.6.3)': + '@storybook/react@9.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)))(typescript@5.6.3)': dependencies: '@storybook/global': 5.0.0 - '@storybook/react-dom-shim': 9.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.11.3(@types/node@20.17.16)(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))) + '@storybook/react-dom-shim': 9.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) - storybook: 9.1.2(@testing-library/dom@10.4.0)(msw@2.11.3(@types/node@20.17.16)(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) + storybook: 9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) optionalDependencies: typescript: 5.6.3 @@ -9199,6 +9169,10 @@ snapshots: '@types/ms@2.1.0': {} + '@types/mute-stream@0.0.4': + dependencies: + '@types/node': 20.17.16 + '@types/node@18.19.129': dependencies: undici-types: 5.26.5 @@ -9207,6 +9181,10 @@ snapshots: dependencies: undici-types: 6.19.8 + '@types/node@22.18.8': + dependencies: + undici-types: 6.21.0 + '@types/parse-json@4.0.2': {} '@types/prop-types@15.7.15': {} @@ -9284,6 +9262,8 @@ snapshots: '@types/tough-cookie@4.0.2': {} + '@types/tough-cookie@4.0.5': {} + '@types/ua-parser-js@0.7.36': {} '@types/unist@2.0.11': {} @@ -9294,6 +9274,8 @@ snapshots: '@types/uuid@9.0.2': {} + '@types/wrap-ansi@3.0.0': {} + '@types/yargs-parser@21.0.2': {} '@types/yargs-parser@21.0.3': {} @@ -9328,13 +9310,13 @@ snapshots: chai: 5.2.1 tinyrainbow: 2.0.0 - '@vitest/mocker@3.2.4(msw@2.11.3(@types/node@20.17.16)(typescript@5.6.3))(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))': + '@vitest/mocker@3.2.4(msw@2.4.8(typescript@5.6.3))(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - msw: 2.11.3(@types/node@20.17.16)(typescript@5.6.3) + msw: 2.4.8(typescript@5.6.3) vite: 7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0) '@vitest/pretty-format@3.2.4': @@ -9787,7 +9769,7 @@ snapshots: cmdk@1.0.4(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: - '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-dialog': 1.1.4(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@radix-ui/react-id': 1.1.0(@types/react@19.1.13)(react@19.1.1) '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) react: 19.1.1 @@ -12078,33 +12060,29 @@ snapshots: ms@2.1.3: {} - msw@2.11.3(@types/node@20.17.16)(typescript@5.6.3): + msw@2.4.8(typescript@5.6.3): dependencies: '@bundled-es-modules/cookie': 2.0.1 '@bundled-es-modules/statuses': 1.0.1 - '@inquirer/confirm': 5.1.18(@types/node@20.17.16) - '@mswjs/interceptors': 0.39.7 - '@open-draft/deferred-promise': 2.2.0 + '@bundled-es-modules/tough-cookie': 0.1.6 + '@inquirer/confirm': 3.2.0 + '@mswjs/interceptors': 0.35.9 + '@open-draft/until': 2.1.0 '@types/cookie': 0.6.0 '@types/statuses': 2.0.6 + chalk: 4.1.2 graphql: 16.11.0 headers-polyfill: 4.0.3 is-node-process: 1.2.0 outvariant: 1.4.3 path-to-regexp: 6.3.0 - picocolors: 1.1.1 - rettime: 0.7.0 strict-event-emitter: 0.5.1 - tough-cookie: 6.0.0 type-fest: 4.41.0 - until-async: 3.0.2 yargs: 17.7.2 optionalDependencies: typescript: 5.6.3 - transitivePeerDependencies: - - '@types/node' - mute-stream@2.0.0: {} + mute-stream@1.0.0: {} mz@2.7.0: dependencies: @@ -12812,8 +12790,6 @@ snapshots: onetime: 5.1.2 signal-exit: 3.0.7 - rettime@0.7.0: {} - reusify@1.1.0: {} rimraf@3.0.2: @@ -13015,24 +12991,24 @@ snapshots: dependencies: internal-slot: 1.0.6 - storybook-addon-remix-react-router@5.0.0(react-dom@19.1.1(react@19.1.1))(react-router@7.8.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.11.3(@types/node@20.17.16)(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))): + storybook-addon-remix-react-router@5.0.0(react-dom@19.1.1(react@19.1.1))(react-router@7.8.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))): dependencies: '@mjackson/form-data-parser': 0.4.0 compare-versions: 6.1.0 react-inspector: 6.0.2(react@19.1.1) react-router: 7.8.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - storybook: 9.1.2(@testing-library/dom@10.4.0)(msw@2.11.3(@types/node@20.17.16)(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) + storybook: 9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) optionalDependencies: react: 19.1.1 react-dom: 19.1.1(react@19.1.1) - storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.11.3(@types/node@20.17.16)(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)): + storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)): dependencies: '@storybook/global': 5.0.0 '@testing-library/jest-dom': 6.6.3 '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.0) '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(msw@2.11.3(@types/node@20.17.16)(typescript@5.6.3))(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) + '@vitest/mocker': 3.2.4(msw@2.4.8(typescript@5.6.3))(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) '@vitest/spy': 3.2.4 better-opn: 3.0.2 esbuild: 0.25.3 @@ -13211,12 +13187,6 @@ snapshots: tinyspy@4.0.3: {} - tldts-core@7.0.16: {} - - tldts@7.0.16: - dependencies: - tldts-core: 7.0.16 - tmpl@1.0.5: {} to-regex-range@5.0.1: @@ -13234,10 +13204,6 @@ snapshots: universalify: 0.2.0 url-parse: 1.5.10 - tough-cookie@6.0.0: - dependencies: - tldts: 7.0.16 - tr46@3.0.0: dependencies: punycode: 2.3.1 @@ -13334,6 +13300,8 @@ snapshots: undici-types@6.19.8: {} + undici-types@6.21.0: {} + undici@6.21.3: {} unicorn-magic@0.1.0: {} @@ -13398,8 +13366,6 @@ snapshots: webpack-sources: 3.2.3 webpack-virtual-modules: 0.5.0 - until-async@3.0.2: {} - update-browserslist-db@1.1.1(browserslist@4.24.2): dependencies: browserslist: 4.24.2 From f23a6a1140157b20c3b4ba1198e01c8d67c8f7f6 Mon Sep 17 00:00:00 2001 From: Bruno Quaresma Date: Wed, 1 Oct 2025 15:37:11 -0300 Subject: [PATCH 005/298] feat: add remove task button into the tasks list (#20036) **Demo:** https://github.com/user-attachments/assets/eca91a46-41fb-412c-b476-0cf91c0b69b8 Closes https://github.com/coder/coder/issues/19525 --- .../TaskDeleteDialog.stories.tsx | 50 +++++++++++++ .../TaskDeleteDialog/TaskDeleteDialog.tsx | 58 ++++++++++++++ .../TasksSidebar/TasksSidebar.stories.tsx | 52 +------------ .../tasks/TasksSidebar/TasksSidebar.tsx | 46 +++--------- .../src/pages/TasksPage/TasksPage.stories.tsx | 14 ++++ site/src/pages/TasksPage/TasksTable.tsx | 75 ++++++++++++++----- 6 files changed, 189 insertions(+), 106 deletions(-) create mode 100644 site/src/modules/tasks/TaskDeleteDialog/TaskDeleteDialog.stories.tsx create mode 100644 site/src/modules/tasks/TaskDeleteDialog/TaskDeleteDialog.tsx diff --git a/site/src/modules/tasks/TaskDeleteDialog/TaskDeleteDialog.stories.tsx b/site/src/modules/tasks/TaskDeleteDialog/TaskDeleteDialog.stories.tsx new file mode 100644 index 0000000000000..e595c26a7884e --- /dev/null +++ b/site/src/modules/tasks/TaskDeleteDialog/TaskDeleteDialog.stories.tsx @@ -0,0 +1,50 @@ +import { MockTasks, MockWorkspace } from "testHelpers/entities"; +import { withGlobalSnackbar } from "testHelpers/storybook"; +import type { Meta, StoryObj } from "@storybook/react-vite"; +import { API } from "api/api"; +import { expect, spyOn, userEvent, waitFor, within } from "storybook/test"; +import { TaskDeleteDialog } from "./TaskDeleteDialog"; + +const meta: Meta = { + title: "modules/tasks/TaskDeleteDialog", + component: TaskDeleteDialog, + decorators: [withGlobalSnackbar], +}; + +export default meta; +type Story = StoryObj; + +export const DeleteTaskSuccess: Story = { + decorators: [withGlobalSnackbar], + args: { + open: true, + task: { prompt: "My Task", workspace: MockWorkspace }, + onClose: () => {}, + }, + parameters: { + chromatic: { + disableSnapshot: false, + }, + }, + beforeEach: () => { + spyOn(API.experimental, "deleteTask").mockResolvedValue(); + }, + play: async ({ canvasElement, step }) => { + const body = within(canvasElement.ownerDocument.body); + + await step("Confirm delete", async () => { + const confirmButton = await body.findByRole("button", { + name: /delete/i, + }); + await userEvent.click(confirmButton); + await step("Confirm delete", async () => { + await waitFor(() => { + expect(API.experimental.deleteTask).toHaveBeenCalledWith( + MockTasks[0].workspace.owner_name, + MockTasks[0].workspace.id, + ); + }); + }); + }); + }, +}; diff --git a/site/src/modules/tasks/TaskDeleteDialog/TaskDeleteDialog.tsx b/site/src/modules/tasks/TaskDeleteDialog/TaskDeleteDialog.tsx new file mode 100644 index 0000000000000..b5bac134a66e4 --- /dev/null +++ b/site/src/modules/tasks/TaskDeleteDialog/TaskDeleteDialog.tsx @@ -0,0 +1,58 @@ +import { API } from "api/api"; +import { getErrorDetail, getErrorMessage } from "api/errors"; +import { ConfirmDialog } from "components/Dialogs/ConfirmDialog/ConfirmDialog"; +import { displayError, displaySuccess } from "components/GlobalSnackbar/utils"; +import type { FC } from "react"; +import { QueryClient, useMutation } from "react-query"; +import type { Task } from "../tasks"; + +type TaskDeleteDialogProps = { + open: boolean; + task: Task; + onClose: () => void; + onSuccess?: () => void; +}; + +export const TaskDeleteDialog: FC = ({ + task, + onSuccess, + ...props +}) => { + const queryClient = new QueryClient(); + const deleteTaskMutation = useMutation({ + mutationFn: () => + API.experimental.deleteTask(task.workspace.owner_name, task.workspace.id), + onSuccess: async () => { + await queryClient.invalidateQueries({ queryKey: ["tasks"] }); + }, + }); + + return ( + { + try { + await deleteTaskMutation.mutateAsync(); + displaySuccess("Task deleted successfully"); + onSuccess?.(); + } catch (error) { + displayError( + getErrorMessage(error, "Failed to delete task"), + getErrorDetail(error), + ); + } finally { + props.onClose(); + } + }} + description={ +

+ This action is irreversible and removes all workspace resources and + data. +

+ } + /> + ); +}; diff --git a/site/src/modules/tasks/TasksSidebar/TasksSidebar.stories.tsx b/site/src/modules/tasks/TasksSidebar/TasksSidebar.stories.tsx index 333b3db13cbf4..11fb2c54bbbb1 100644 --- a/site/src/modules/tasks/TasksSidebar/TasksSidebar.stories.tsx +++ b/site/src/modules/tasks/TasksSidebar/TasksSidebar.stories.tsx @@ -1,9 +1,9 @@ import { MockTasks, MockUserOwner, mockApiError } from "testHelpers/entities"; -import { withAuthProvider, withGlobalSnackbar } from "testHelpers/storybook"; +import { withAuthProvider } from "testHelpers/storybook"; import type { Meta, StoryObj } from "@storybook/react-vite"; import { API } from "api/api"; import { MockUsers } from "pages/UsersPage/storybookData/users"; -import { expect, spyOn, userEvent, waitFor, within } from "storybook/test"; +import { spyOn, userEvent, within } from "storybook/test"; import { reactRouterParameters } from "storybook-addon-remix-react-router"; import { TasksSidebar } from "./TasksSidebar"; @@ -93,7 +93,7 @@ export const OpenOptionsMenu: Story = { }, }; -export const DeleteTaskDialog: Story = { +export const OpenDeleteDialog: Story = { beforeEach: () => { spyOn(API.experimental, "getTasks").mockResolvedValue(MockTasks); }, @@ -114,49 +114,3 @@ export const DeleteTaskDialog: Story = { }); }, }; - -export const DeleteTaskSuccess: Story = { - decorators: [withGlobalSnackbar], - parameters: { - chromatic: { - disableSnapshot: false, - }, - }, - beforeEach: () => { - spyOn(API.experimental, "getTasks").mockResolvedValue(MockTasks); - spyOn(API.experimental, "deleteTask").mockResolvedValue(); - }, - play: async ({ canvasElement, step }) => { - const body = within(canvasElement.ownerDocument.body); - const canvas = within(canvasElement); - - await step("Open menu", async () => { - const optionButtons = await canvas.findAllByRole("button", { - name: /task options/i, - }); - await userEvent.click(optionButtons[0]); - }); - - await step("Open delete dialog", async () => { - const deleteButton = await body.findByRole("menuitem", { - name: /delete/i, - }); - await userEvent.click(deleteButton); - }); - - await step("Confirm delete", async () => { - const confirmButton = await body.findByRole("button", { - name: /delete/i, - }); - await userEvent.click(confirmButton); - await step("Confirm delete", async () => { - await waitFor(() => { - expect(API.experimental.deleteTask).toHaveBeenCalledWith( - MockTasks[0].workspace.owner_name, - MockTasks[0].workspace.id, - ); - }); - }); - }); - }, -}; diff --git a/site/src/modules/tasks/TasksSidebar/TasksSidebar.tsx b/site/src/modules/tasks/TasksSidebar/TasksSidebar.tsx index 0c440e6432aa5..b63366e1b98cc 100644 --- a/site/src/modules/tasks/TasksSidebar/TasksSidebar.tsx +++ b/site/src/modules/tasks/TasksSidebar/TasksSidebar.tsx @@ -1,8 +1,7 @@ import { API } from "api/api"; -import { getErrorDetail, getErrorMessage } from "api/errors"; +import { getErrorMessage } from "api/errors"; import { cva } from "class-variance-authority"; import { Button } from "components/Button/Button"; -import { ConfirmDialog } from "components/Dialogs/ConfirmDialog/ConfirmDialog"; import { DropdownMenu, DropdownMenuContent, @@ -10,7 +9,6 @@ import { DropdownMenuItem, DropdownMenuTrigger, } from "components/DropdownMenu/DropdownMenu"; -import { displayError, displaySuccess } from "components/GlobalSnackbar/utils"; import { CoderIcon } from "components/Icons/CoderIcon"; import { ScrollArea } from "components/ScrollArea/ScrollArea"; import { Skeleton } from "components/Skeleton/Skeleton"; @@ -25,9 +23,10 @@ import { useSearchParamsKey } from "hooks/useSearchParamsKey"; import { EditIcon, EllipsisIcon, PanelLeftIcon, TrashIcon } from "lucide-react"; import type { Task } from "modules/tasks/tasks"; import { type FC, useState } from "react"; -import { QueryClient, useMutation, useQuery } from "react-query"; +import { useQuery } from "react-query"; import { Link as RouterLink, useNavigate, useParams } from "react-router"; import { cn } from "utils/cn"; +import { TaskDeleteDialog } from "../TaskDeleteDialog/TaskDeleteDialog"; import { UserCombobox } from "./UserCombobox"; export const TasksSidebar: FC = () => { @@ -180,14 +179,6 @@ const TaskSidebarMenuItem: FC = ({ task }) => { const isActive = task.workspace.name === workspace; const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false); const navigate = useNavigate(); - const queryClient = new QueryClient(); - const deleteTaskMutation = useMutation({ - mutationFn: () => - API.experimental.deleteTask(task.workspace.owner_name, task.workspace.id), - onSuccess: async () => { - await queryClient.invalidateQueries({ queryKey: ["tasks"] }); - }, - }); return ( <> @@ -249,36 +240,17 @@ const TaskSidebarMenuItem: FC = ({ task }) => { - { + task={task} + onClose={() => { setIsDeleteDialogOpen(false); }} - onConfirm={async () => { - try { - await deleteTaskMutation.mutateAsync(); - displaySuccess("Task deleted successfully"); - if (isActive) { - navigate("/tasks"); - } - } catch (error) { - displayError( - getErrorMessage(error, "Failed to delete task"), - getErrorDetail(error), - ); - } finally { - setIsDeleteDialogOpen(false); + onSuccess={() => { + if (isActive) { + navigate("/tasks"); } }} - description={ -

- This action is irreversible and removes all workspace resources and - data. -

- } /> ); diff --git a/site/src/pages/TasksPage/TasksPage.stories.tsx b/site/src/pages/TasksPage/TasksPage.stories.tsx index b0510bb6f4cab..7f4818bf00f4f 100644 --- a/site/src/pages/TasksPage/TasksPage.stories.tsx +++ b/site/src/pages/TasksPage/TasksPage.stories.tsx @@ -424,3 +424,17 @@ export const NonAdmin: Story = { }); }, }; + +export const OpenDeleteDialog: Story = { + beforeEach: () => { + spyOn(API, "getTemplates").mockResolvedValue([MockTemplate]); + spyOn(API.experimental, "getTasks").mockResolvedValue(MockTasks); + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const deleteButtons = await canvas.findAllByRole("button", { + name: /delete task/i, + }); + await userEvent.click(deleteButtons[0]); + }, +}; diff --git a/site/src/pages/TasksPage/TasksTable.tsx b/site/src/pages/TasksPage/TasksTable.tsx index 883f3dd84cb5e..c1cddb75a8e8f 100644 --- a/site/src/pages/TasksPage/TasksTable.tsx +++ b/site/src/pages/TasksPage/TasksTable.tsx @@ -16,10 +16,17 @@ import { TableLoaderSkeleton, TableRowSkeleton, } from "components/TableLoader/TableLoader"; -import { RotateCcwIcon } from "lucide-react"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "components/Tooltip/Tooltip"; +import { RotateCcwIcon, TrashIcon } from "lucide-react"; +import { TaskDeleteDialog } from "modules/tasks/TaskDeleteDialog/TaskDeleteDialog"; import type { Task } from "modules/tasks/tasks"; import { WorkspaceAppStatus } from "modules/workspaces/WorkspaceAppStatus/WorkspaceAppStatus"; -import type { FC, ReactNode } from "react"; +import { type FC, type ReactNode, useState } from "react"; import { Link as RouterLink } from "react-router"; import { relativeTime } from "utils/time"; @@ -39,7 +46,7 @@ export const TasksTable: FC = ({ tasks, error, onRetry }) => { } else if (tasks.length === 0) { body = ; } else { - body = ; + body = tasks.map((task) => ); } return ( @@ -49,6 +56,7 @@ export const TasksTable: FC = ({ tasks, error, onRetry }) => { Task Status Created by + {body} @@ -103,24 +111,25 @@ const TasksEmpty: FC = () => { ); }; -type TasksProps = { tasks: Task[] }; +type TaskRowProps = { task: Task }; -const Tasks: FC = ({ tasks }) => { - return tasks.map(({ workspace, prompt }) => { - const templateDisplayName = - workspace.template_display_name ?? workspace.template_name; +const TaskRow: FC = ({ task }) => { + const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false); + const templateDisplayName = + task.workspace.template_display_name ?? task.workspace.template_name; - return ( - + return ( + <> + - {prompt} + {task.prompt} Access task @@ -132,7 +141,7 @@ const Tasks: FC = ({ tasks }) => { } @@ -140,24 +149,50 @@ const Tasks: FC = ({ tasks }) => { - {relativeTime(new Date(workspace.created_at))} + {relativeTime(new Date(task.workspace.created_at))} } - src={workspace.owner_avatar_url} + src={task.workspace.owner_avatar_url} /> + + + + + + + Delete task + + + - ); - }); + + { + setIsDeleteDialogOpen(false); + }} + /> + + ); }; const TasksSkeleton: FC = () => { From 718f712c1818dc86646665c7d4b0e1d446d94264 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 18:38:25 +0000 Subject: [PATCH 006/298] chore: bump the react group across 1 directory with 2 updates (#20102) Bumps the react group with 2 updates in the /site directory: [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) and [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom). Updates `@types/react` from 19.1.13 to 19.1.17
Commits

Updates `@types/react-dom` from 19.1.9 to 19.1.11
Commits

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- site/package.json | 4 +- site/pnpm-lock.yaml | 1072 ++++++++++++++++++++++--------------------- 2 files changed, 540 insertions(+), 536 deletions(-) diff --git a/site/package.json b/site/package.json index 11ed58a05838a..14e3365fc6d54 100644 --- a/site/package.json +++ b/site/package.json @@ -145,10 +145,10 @@ "@types/jest": "29.5.14", "@types/lodash": "4.17.15", "@types/node": "20.17.16", - "@types/react": "19.1.13", + "@types/react": "19.1.17", "@types/react-color": "3.0.13", "@types/react-date-range": "1.4.4", - "@types/react-dom": "19.1.9", + "@types/react-dom": "19.1.11", "@types/react-syntax-highlighter": "15.5.13", "@types/react-virtualized-auto-sizer": "1.0.4", "@types/react-window": "1.8.8", diff --git a/site/pnpm-lock.yaml b/site/pnpm-lock.yaml index 676011c935c76..f36ffa639e6d9 100644 --- a/site/pnpm-lock.yaml +++ b/site/pnpm-lock.yaml @@ -33,10 +33,10 @@ importers: version: 11.13.5 '@emotion/react': specifier: 11.14.0 - version: 11.14.0(@types/react@19.1.13)(react@19.1.1) + version: 11.14.0(@types/react@19.1.17)(react@19.1.1) '@emotion/styled': specifier: 11.14.1 - version: 11.14.1(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react@19.1.1) + version: 11.14.1(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react@19.1.1) '@fontsource-variable/inter': specifier: 5.1.1 version: 5.1.1 @@ -57,64 +57,64 @@ importers: version: 4.7.0(monaco-editor@0.52.2)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@mui/icons-material': specifier: 5.18.0 - version: 5.18.0(@mui/material@5.18.0(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@types/react@19.1.13)(react@19.1.1) + version: 5.18.0(@mui/material@5.18.0(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@types/react@19.1.17)(react@19.1.1) '@mui/material': specifier: 5.18.0 - version: 5.18.0(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + version: 5.18.0(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@mui/system': specifier: 5.18.0 - version: 5.18.0(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react@19.1.1) + version: 5.18.0(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react@19.1.1) '@mui/utils': specifier: 5.17.1 - version: 5.17.1(@types/react@19.1.13)(react@19.1.1) + version: 5.17.1(@types/react@19.1.17)(react@19.1.1) '@mui/x-tree-view': specifier: 7.29.10 - version: 7.29.10(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react@19.1.1))(@mui/material@5.18.0(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@mui/system@5.18.0(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + version: 7.29.10(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react@19.1.1))(@mui/material@5.18.0(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@mui/system@5.18.0(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@radix-ui/react-avatar': specifier: 1.1.2 - version: 1.1.2(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + version: 1.1.2(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@radix-ui/react-checkbox': specifier: 1.1.4 - version: 1.1.4(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + version: 1.1.4(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@radix-ui/react-collapsible': specifier: 1.1.2 - version: 1.1.2(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + version: 1.1.2(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@radix-ui/react-dialog': specifier: 1.1.4 - version: 1.1.4(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + version: 1.1.4(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@radix-ui/react-dropdown-menu': specifier: 2.1.4 - version: 2.1.4(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + version: 2.1.4(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@radix-ui/react-label': specifier: 2.1.0 - version: 2.1.0(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + version: 2.1.0(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@radix-ui/react-popover': specifier: 1.1.5 - version: 1.1.5(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + version: 1.1.5(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@radix-ui/react-radio-group': specifier: 1.2.3 - version: 1.2.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + version: 1.2.3(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@radix-ui/react-scroll-area': specifier: 1.2.3 - version: 1.2.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + version: 1.2.3(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@radix-ui/react-select': specifier: 2.2.6 - version: 2.2.6(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + version: 2.2.6(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@radix-ui/react-separator': specifier: 1.1.7 - version: 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + version: 1.1.7(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@radix-ui/react-slider': specifier: 1.2.2 - version: 1.2.2(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + version: 1.2.2(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@radix-ui/react-slot': specifier: 1.2.3 - version: 1.2.3(@types/react@19.1.13)(react@19.1.1) + version: 1.2.3(@types/react@19.1.17)(react@19.1.1) '@radix-ui/react-switch': specifier: 1.1.1 - version: 1.1.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + version: 1.1.1(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@radix-ui/react-tooltip': specifier: 1.1.7 - version: 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + version: 1.1.7(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@tanstack/react-query-devtools': specifier: 5.77.0 version: 5.77.0(@tanstack/react-query@5.77.0(react@19.1.1))(react@19.1.1) @@ -153,7 +153,7 @@ importers: version: 2.1.1 cmdk: specifier: 1.0.4 - version: 1.0.4(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + version: 1.0.4(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) color-convert: specifier: 2.0.1 version: 2.0.1 @@ -213,7 +213,7 @@ importers: version: 19.1.1(react@19.1.1) react-markdown: specifier: 9.1.0 - version: 9.1.0(@types/react@19.1.13)(react@19.1.1) + version: 9.1.0(@types/react@19.1.17)(react@19.1.1) react-query: specifier: npm:@tanstack/react-query@5.77.0 version: '@tanstack/react-query@5.77.0(react@19.1.1)' @@ -228,7 +228,7 @@ importers: version: 15.6.1(react@19.1.1) react-textarea-autosize: specifier: 8.5.9 - version: 8.5.9(@types/react@19.1.13)(react@19.1.1) + version: 8.5.9(@types/react@19.1.17)(react@19.1.1) react-virtualized-auto-sizer: specifier: 1.0.24 version: 1.0.24(react-dom@19.1.1(react@19.1.1))(react@19.1.1) @@ -292,7 +292,7 @@ importers: version: 1.55.1 '@storybook/addon-docs': specifier: 9.1.2 - version: 9.1.2(@types/react@19.1.13)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))) + version: 9.1.2(@types/react@19.1.17)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))) '@storybook/addon-links': specifier: 9.1.2 version: 9.1.2(react@19.1.1)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))) @@ -316,7 +316,7 @@ importers: version: 6.6.3 '@testing-library/react': specifier: 14.3.1 - version: 14.3.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + version: 14.3.1(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@testing-library/user-event': specifier: 14.6.1 version: 14.6.1(@testing-library/dom@10.4.0) @@ -345,17 +345,17 @@ importers: specifier: 20.17.16 version: 20.17.16 '@types/react': - specifier: 19.1.13 - version: 19.1.13 + specifier: 19.1.17 + version: 19.1.17 '@types/react-color': specifier: 3.0.13 - version: 3.0.13(@types/react@19.1.13) + version: 3.0.13(@types/react@19.1.17) '@types/react-date-range': specifier: 1.4.4 version: 1.4.4 '@types/react-dom': - specifier: 19.1.9 - version: 19.1.9(@types/react@19.1.13) + specifier: 19.1.11 + version: 19.1.11(@types/react@19.1.17) '@types/react-syntax-highlighter': specifier: 15.5.13 version: 15.5.13 @@ -2977,11 +2977,13 @@ packages: '@types/react-date-range@1.4.4': resolution: {integrity: sha512-9Y9NyNgaCsEVN/+O4HKuxzPbVjRVBGdOKRxMDcsTRWVG62lpYgnxefNckTXDWup8FvczoqPW0+ESZR6R1yymDg==, tarball: https://registry.npmjs.org/@types/react-date-range/-/react-date-range-1.4.4.tgz} - '@types/react-dom@18.3.1': - resolution: {integrity: sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==, tarball: https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz} + '@types/react-dom@18.3.7': + resolution: {integrity: sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==, tarball: https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz} + peerDependencies: + '@types/react': ^18.0.0 - '@types/react-dom@19.1.9': - resolution: {integrity: sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==, tarball: https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.9.tgz} + '@types/react-dom@19.1.11': + resolution: {integrity: sha512-3BKc/yGdNTYQVVw4idqHtSOcFsgGuBbMveKCOgF8wQ5QtrYOc3jDIlzg3jef04zcXFIHLelyGlj0T+BJ8+KN+w==, tarball: https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.11.tgz} peerDependencies: '@types/react': ^19.0.0 @@ -2999,8 +3001,8 @@ packages: '@types/react-window@1.8.8': resolution: {integrity: sha512-8Ls660bHR1AUA2kuRvVG9D/4XpRC6wjAaPT9dil7Ckc76eP9TKWZwwmgfq8Q1LANX3QNDnoU4Zp48A3w+zK69Q==, tarball: https://registry.npmjs.org/@types/react-window/-/react-window-1.8.8.tgz} - '@types/react@19.1.13': - resolution: {integrity: sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ==, tarball: https://registry.npmjs.org/@types/react/-/react-19.1.13.tgz} + '@types/react@19.1.17': + resolution: {integrity: sha512-Qec1E3mhALmaspIrhWt9jkQMNdw6bReVu64mjvhbhq2NFPftLPVr+l1SZgmw/66WwBNpDh7ao5AT6gF5v41PFA==, tarball: https://registry.npmjs.org/@types/react/-/react-19.1.17.tgz} '@types/reactcss@1.2.13': resolution: {integrity: sha512-gi3S+aUi6kpkF5vdhUsnkwbiSEIU/BEJyD7kBy2SudWBUuKmJk8AQKE0OVcQQeEy40Azh0lV6uynxlikYIJuwg==, tarball: https://registry.npmjs.org/@types/reactcss/-/reactcss-1.2.13.tgz} @@ -7081,7 +7083,7 @@ snapshots: '@emotion/memoize@0.9.0': {} - '@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1)': + '@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1)': dependencies: '@babel/runtime': 7.26.10 '@emotion/babel-plugin': 11.13.5 @@ -7093,7 +7095,7 @@ snapshots: hoist-non-react-statics: 3.3.2 react: 19.1.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 transitivePeerDependencies: - supports-color @@ -7107,18 +7109,18 @@ snapshots: '@emotion/sheet@1.4.0': {} - '@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react@19.1.1)': + '@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react@19.1.1)': dependencies: '@babel/runtime': 7.26.10 '@emotion/babel-plugin': 11.13.5 '@emotion/is-prop-valid': 1.4.0 - '@emotion/react': 11.14.0(@types/react@19.1.13)(react@19.1.1) + '@emotion/react': 11.14.0(@types/react@19.1.17)(react@19.1.1) '@emotion/serialize': 1.3.3 '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.1.1) '@emotion/utils': 1.4.2 react: 19.1.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 transitivePeerDependencies: - supports-color @@ -7668,10 +7670,10 @@ snapshots: '@leeoniya/ufuzzy@1.0.10': {} - '@mdx-js/react@3.0.1(@types/react@19.1.13)(react@19.1.1)': + '@mdx-js/react@3.0.1(@types/react@19.1.17)(react@19.1.1)': dependencies: '@types/mdx': 2.0.9 - '@types/react': 19.1.13 + '@types/react': 19.1.17 react: 19.1.1 '@mjackson/form-data-parser@0.4.0': @@ -7706,23 +7708,23 @@ snapshots: '@mui/core-downloads-tracker@5.18.0': {} - '@mui/icons-material@5.18.0(@mui/material@5.18.0(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@types/react@19.1.13)(react@19.1.1)': + '@mui/icons-material@5.18.0(@mui/material@5.18.0(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@types/react@19.1.17)(react@19.1.1)': dependencies: '@babel/runtime': 7.26.10 - '@mui/material': 5.18.0(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@mui/material': 5.18.0(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) react: 19.1.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - '@mui/material@5.18.0(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@mui/material@5.18.0(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: '@babel/runtime': 7.26.10 '@mui/core-downloads-tracker': 5.18.0 - '@mui/system': 5.18.0(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react@19.1.1) - '@mui/types': 7.2.24(@types/react@19.1.13) - '@mui/utils': 5.17.1(@types/react@19.1.13)(react@19.1.1) + '@mui/system': 5.18.0(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react@19.1.1) + '@mui/types': 7.2.24(@types/react@19.1.17) + '@mui/utils': 5.17.1(@types/react@19.1.17)(react@19.1.1) '@popperjs/core': 2.11.8 - '@types/react-transition-group': 4.4.12(@types/react@19.1.13) + '@types/react-transition-group': 4.4.12(@types/react@19.1.17) clsx: 2.1.1 csstype: 3.1.3 prop-types: 15.8.1 @@ -7731,20 +7733,20 @@ snapshots: react-is: 19.1.1 react-transition-group: 4.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1) optionalDependencies: - '@emotion/react': 11.14.0(@types/react@19.1.13)(react@19.1.1) - '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react@19.1.1) - '@types/react': 19.1.13 + '@emotion/react': 11.14.0(@types/react@19.1.17)(react@19.1.1) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react@19.1.1) + '@types/react': 19.1.17 - '@mui/private-theming@5.17.1(@types/react@19.1.13)(react@19.1.1)': + '@mui/private-theming@5.17.1(@types/react@19.1.17)(react@19.1.1)': dependencies: '@babel/runtime': 7.26.10 - '@mui/utils': 5.17.1(@types/react@19.1.13)(react@19.1.1) + '@mui/utils': 5.17.1(@types/react@19.1.17)(react@19.1.1) prop-types: 15.8.1 react: 19.1.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - '@mui/styled-engine@5.18.0(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react@19.1.1))(react@19.1.1)': + '@mui/styled-engine@5.18.0(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react@19.1.1))(react@19.1.1)': dependencies: '@babel/runtime': 7.26.10 '@emotion/cache': 11.14.0 @@ -7753,65 +7755,65 @@ snapshots: prop-types: 15.8.1 react: 19.1.1 optionalDependencies: - '@emotion/react': 11.14.0(@types/react@19.1.13)(react@19.1.1) - '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react@19.1.1) + '@emotion/react': 11.14.0(@types/react@19.1.17)(react@19.1.1) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react@19.1.1) - '@mui/system@5.18.0(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react@19.1.1)': + '@mui/system@5.18.0(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react@19.1.1)': dependencies: '@babel/runtime': 7.26.10 - '@mui/private-theming': 5.17.1(@types/react@19.1.13)(react@19.1.1) - '@mui/styled-engine': 5.18.0(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react@19.1.1))(react@19.1.1) - '@mui/types': 7.2.24(@types/react@19.1.13) - '@mui/utils': 5.17.1(@types/react@19.1.13)(react@19.1.1) + '@mui/private-theming': 5.17.1(@types/react@19.1.17)(react@19.1.1) + '@mui/styled-engine': 5.18.0(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react@19.1.1))(react@19.1.1) + '@mui/types': 7.2.24(@types/react@19.1.17) + '@mui/utils': 5.17.1(@types/react@19.1.17)(react@19.1.1) clsx: 2.1.1 csstype: 3.1.3 prop-types: 15.8.1 react: 19.1.1 optionalDependencies: - '@emotion/react': 11.14.0(@types/react@19.1.13)(react@19.1.1) - '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react@19.1.1) - '@types/react': 19.1.13 + '@emotion/react': 11.14.0(@types/react@19.1.17)(react@19.1.1) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react@19.1.1) + '@types/react': 19.1.17 - '@mui/types@7.2.24(@types/react@19.1.13)': + '@mui/types@7.2.24(@types/react@19.1.17)': optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - '@mui/utils@5.17.1(@types/react@19.1.13)(react@19.1.1)': + '@mui/utils@5.17.1(@types/react@19.1.17)(react@19.1.1)': dependencies: '@babel/runtime': 7.26.10 - '@mui/types': 7.2.24(@types/react@19.1.13) + '@mui/types': 7.2.24(@types/react@19.1.17) '@types/prop-types': 15.7.15 clsx: 2.1.1 prop-types: 15.8.1 react: 19.1.1 react-is: 19.1.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - '@mui/x-internals@7.29.0(@types/react@19.1.13)(react@19.1.1)': + '@mui/x-internals@7.29.0(@types/react@19.1.17)(react@19.1.1)': dependencies: '@babel/runtime': 7.26.10 - '@mui/utils': 5.17.1(@types/react@19.1.13)(react@19.1.1) + '@mui/utils': 5.17.1(@types/react@19.1.17)(react@19.1.1) react: 19.1.1 transitivePeerDependencies: - '@types/react' - '@mui/x-tree-view@7.29.10(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react@19.1.1))(@mui/material@5.18.0(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@mui/system@5.18.0(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@mui/x-tree-view@7.29.10(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react@19.1.1))(@mui/material@5.18.0(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@mui/system@5.18.0(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: '@babel/runtime': 7.26.10 - '@mui/material': 5.18.0(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@mui/system': 5.18.0(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react@19.1.1) - '@mui/utils': 5.17.1(@types/react@19.1.13)(react@19.1.1) - '@mui/x-internals': 7.29.0(@types/react@19.1.13)(react@19.1.1) - '@types/react-transition-group': 4.4.12(@types/react@19.1.13) + '@mui/material': 5.18.0(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@mui/system': 5.18.0(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react@19.1.1) + '@mui/utils': 5.17.1(@types/react@19.1.17)(react@19.1.1) + '@mui/x-internals': 7.29.0(@types/react@19.1.17)(react@19.1.1) + '@types/react-transition-group': 4.4.12(@types/react@19.1.17) clsx: 2.1.1 prop-types: 15.8.1 react: 19.1.1 react-dom: 19.1.1(react@19.1.1) react-transition-group: 4.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1) optionalDependencies: - '@emotion/react': 11.14.0(@types/react@19.1.13)(react@19.1.1) - '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react@19.1.1) + '@emotion/react': 11.14.0(@types/react@19.1.17)(react@19.1.1) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react@19.1.1) transitivePeerDependencies: - '@types/react' @@ -7952,736 +7954,736 @@ snapshots: '@radix-ui/primitive@1.1.3': {} - '@radix-ui/react-arrow@1.1.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@radix-ui/react-arrow@1.1.1(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 - '@types/react-dom': 19.1.9(@types/react@19.1.13) + '@types/react': 19.1.17 + '@types/react-dom': 19.1.11(@types/react@19.1.17) - '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 - '@types/react-dom': 19.1.9(@types/react@19.1.13) + '@types/react': 19.1.17 + '@types/react-dom': 19.1.11(@types/react@19.1.17) - '@radix-ui/react-avatar@1.1.2(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@radix-ui/react-avatar@1.1.2(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: - '@radix-ui/react-context': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-context': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.1.17)(react@19.1.1) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 - '@types/react-dom': 19.1.9(@types/react@19.1.13) + '@types/react': 19.1.17 + '@types/react-dom': 19.1.11(@types/react@19.1.17) - '@radix-ui/react-checkbox@1.1.4(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@radix-ui/react-checkbox@1.1.4(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-context': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-use-previous': 1.1.0(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-use-size': 1.1.0(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-context': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-use-previous': 1.1.0(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-use-size': 1.1.0(@types/react@19.1.17)(react@19.1.1) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 - '@types/react-dom': 19.1.9(@types/react@19.1.13) + '@types/react': 19.1.17 + '@types/react-dom': 19.1.11(@types/react@19.1.17) - '@radix-ui/react-collapsible@1.1.2(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@radix-ui/react-collapsible@1.1.2(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-context': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-id': 1.1.0(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-context': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-id': 1.1.0(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.1.17)(react@19.1.1) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 - '@types/react-dom': 19.1.9(@types/react@19.1.13) + '@types/react': 19.1.17 + '@types/react-dom': 19.1.11(@types/react@19.1.17) - '@radix-ui/react-collection@1.1.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@radix-ui/react-collection@1.1.1(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-context': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-slot': 1.1.1(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-context': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-slot': 1.1.1(@types/react@19.1.17)(react@19.1.1) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 - '@types/react-dom': 19.1.9(@types/react@19.1.13) + '@types/react': 19.1.17 + '@types/react-dom': 19.1.11(@types/react@19.1.17) - '@radix-ui/react-collection@1.1.2(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@radix-ui/react-collection@1.1.2(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-context': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-slot': 1.1.2(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-context': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-slot': 1.1.2(@types/react@19.1.17)(react@19.1.1) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 - '@types/react-dom': 19.1.9(@types/react@19.1.13) + '@types/react': 19.1.17 + '@types/react-dom': 19.1.11(@types/react@19.1.17) - '@radix-ui/react-collection@1.1.7(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@radix-ui/react-collection@1.1.7(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-slot': 1.2.3(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.17)(react@19.1.1) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 - '@types/react-dom': 19.1.9(@types/react@19.1.13) + '@types/react': 19.1.17 + '@types/react-dom': 19.1.11(@types/react@19.1.17) - '@radix-ui/react-compose-refs@1.1.0(@types/react@19.1.13)(react@19.1.1)': + '@radix-ui/react-compose-refs@1.1.0(@types/react@19.1.17)(react@19.1.1)': dependencies: react: 19.1.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - '@radix-ui/react-compose-refs@1.1.1(@types/react@19.1.13)(react@19.1.1)': + '@radix-ui/react-compose-refs@1.1.1(@types/react@19.1.17)(react@19.1.1)': dependencies: react: 19.1.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - '@radix-ui/react-compose-refs@1.1.2(@types/react@19.1.13)(react@19.1.1)': + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.1.17)(react@19.1.1)': dependencies: react: 19.1.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - '@radix-ui/react-context@1.1.1(@types/react@19.1.13)(react@19.1.1)': + '@radix-ui/react-context@1.1.1(@types/react@19.1.17)(react@19.1.1)': dependencies: react: 19.1.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - '@radix-ui/react-context@1.1.2(@types/react@19.1.13)(react@19.1.1)': + '@radix-ui/react-context@1.1.2(@types/react@19.1.17)(react@19.1.1)': dependencies: react: 19.1.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - '@radix-ui/react-dialog@1.1.4(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@radix-ui/react-dialog@1.1.4(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-context': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-dismissable-layer': 1.1.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-focus-guards': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-focus-scope': 1.1.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-id': 1.1.0(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-portal': 1.1.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-slot': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-context': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-dismissable-layer': 1.1.3(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-focus-guards': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-focus-scope': 1.1.1(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-id': 1.1.0(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-portal': 1.1.3(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-slot': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.1.17)(react@19.1.1) aria-hidden: 1.2.6 react: 19.1.1 react-dom: 19.1.1(react@19.1.1) - react-remove-scroll: 2.7.1(@types/react@19.1.13)(react@19.1.1) + react-remove-scroll: 2.7.1(@types/react@19.1.17)(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 - '@types/react-dom': 19.1.9(@types/react@19.1.13) + '@types/react': 19.1.17 + '@types/react-dom': 19.1.11(@types/react@19.1.17) - '@radix-ui/react-direction@1.1.0(@types/react@19.1.13)(react@19.1.1)': + '@radix-ui/react-direction@1.1.0(@types/react@19.1.17)(react@19.1.1)': dependencies: react: 19.1.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - '@radix-ui/react-direction@1.1.1(@types/react@19.1.13)(react@19.1.1)': + '@radix-ui/react-direction@1.1.1(@types/react@19.1.17)(react@19.1.1)': dependencies: react: 19.1.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.1.17)(react@19.1.1) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 - '@types/react-dom': 19.1.9(@types/react@19.1.13) + '@types/react': 19.1.17 + '@types/react-dom': 19.1.11(@types/react@19.1.17) - '@radix-ui/react-dismissable-layer@1.1.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@radix-ui/react-dismissable-layer@1.1.3(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-use-escape-keydown': 1.1.0(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-use-escape-keydown': 1.1.0(@types/react@19.1.17)(react@19.1.1) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 - '@types/react-dom': 19.1.9(@types/react@19.1.13) + '@types/react': 19.1.17 + '@types/react-dom': 19.1.11(@types/react@19.1.17) - '@radix-ui/react-dismissable-layer@1.1.4(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@radix-ui/react-dismissable-layer@1.1.4(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-use-escape-keydown': 1.1.0(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-use-escape-keydown': 1.1.0(@types/react@19.1.17)(react@19.1.1) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 - '@types/react-dom': 19.1.9(@types/react@19.1.13) + '@types/react': 19.1.17 + '@types/react-dom': 19.1.11(@types/react@19.1.17) - '@radix-ui/react-dropdown-menu@2.1.4(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@radix-ui/react-dropdown-menu@2.1.4(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-context': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-id': 1.1.0(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-menu': 2.1.4(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-context': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-id': 1.1.0(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-menu': 2.1.4(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.1.17)(react@19.1.1) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 - '@types/react-dom': 19.1.9(@types/react@19.1.13) + '@types/react': 19.1.17 + '@types/react-dom': 19.1.11(@types/react@19.1.17) - '@radix-ui/react-focus-guards@1.1.1(@types/react@19.1.13)(react@19.1.1)': + '@radix-ui/react-focus-guards@1.1.1(@types/react@19.1.17)(react@19.1.1)': dependencies: react: 19.1.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - '@radix-ui/react-focus-guards@1.1.3(@types/react@19.1.13)(react@19.1.1)': + '@radix-ui/react-focus-guards@1.1.3(@types/react@19.1.17)(react@19.1.1)': dependencies: react: 19.1.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - '@radix-ui/react-focus-scope@1.1.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@radix-ui/react-focus-scope@1.1.1(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.1.17)(react@19.1.1) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 - '@types/react-dom': 19.1.9(@types/react@19.1.13) + '@types/react': 19.1.17 + '@types/react-dom': 19.1.11(@types/react@19.1.17) - '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.17)(react@19.1.1) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 - '@types/react-dom': 19.1.9(@types/react@19.1.13) + '@types/react': 19.1.17 + '@types/react-dom': 19.1.11(@types/react@19.1.17) - '@radix-ui/react-id@1.1.0(@types/react@19.1.13)(react@19.1.1)': + '@radix-ui/react-id@1.1.0(@types/react@19.1.17)(react@19.1.1)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.1.17)(react@19.1.1) react: 19.1.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - '@radix-ui/react-id@1.1.1(@types/react@19.1.13)(react@19.1.1)': + '@radix-ui/react-id@1.1.1(@types/react@19.1.17)(react@19.1.1)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.17)(react@19.1.1) react: 19.1.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - '@radix-ui/react-label@2.1.0(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@radix-ui/react-label@2.1.0(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: - '@radix-ui/react-primitive': 2.0.0(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 - '@types/react-dom': 19.1.9(@types/react@19.1.13) + '@types/react': 19.1.17 + '@types/react-dom': 19.1.11(@types/react@19.1.17) - '@radix-ui/react-menu@2.1.4(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@radix-ui/react-menu@2.1.4(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-collection': 1.1.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-context': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-direction': 1.1.0(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-dismissable-layer': 1.1.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-focus-guards': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-focus-scope': 1.1.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-id': 1.1.0(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-popper': 1.2.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-portal': 1.1.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-roving-focus': 1.1.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-slot': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-collection': 1.1.1(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-context': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-direction': 1.1.0(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-dismissable-layer': 1.1.3(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-focus-guards': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-focus-scope': 1.1.1(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-id': 1.1.0(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-popper': 1.2.1(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-portal': 1.1.3(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-roving-focus': 1.1.1(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-slot': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.1.17)(react@19.1.1) aria-hidden: 1.2.6 react: 19.1.1 react-dom: 19.1.1(react@19.1.1) - react-remove-scroll: 2.7.1(@types/react@19.1.13)(react@19.1.1) + react-remove-scroll: 2.7.1(@types/react@19.1.17)(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 - '@types/react-dom': 19.1.9(@types/react@19.1.13) + '@types/react': 19.1.17 + '@types/react-dom': 19.1.11(@types/react@19.1.17) - '@radix-ui/react-popover@1.1.5(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@radix-ui/react-popover@1.1.5(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-context': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-dismissable-layer': 1.1.4(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-focus-guards': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-focus-scope': 1.1.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-id': 1.1.0(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-popper': 1.2.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-portal': 1.1.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-slot': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-context': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-dismissable-layer': 1.1.4(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-focus-guards': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-focus-scope': 1.1.1(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-id': 1.1.0(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-popper': 1.2.1(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-portal': 1.1.3(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-slot': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.1.17)(react@19.1.1) aria-hidden: 1.2.4 react: 19.1.1 react-dom: 19.1.1(react@19.1.1) - react-remove-scroll: 2.6.3(@types/react@19.1.13)(react@19.1.1) + react-remove-scroll: 2.6.3(@types/react@19.1.17)(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 - '@types/react-dom': 19.1.9(@types/react@19.1.13) + '@types/react': 19.1.17 + '@types/react-dom': 19.1.11(@types/react@19.1.17) - '@radix-ui/react-popper@1.2.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@radix-ui/react-popper@1.2.1(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: '@floating-ui/react-dom': 2.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-arrow': 1.1.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-context': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-use-rect': 1.1.0(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-use-size': 1.1.0(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-arrow': 1.1.1(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-context': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-use-rect': 1.1.0(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-use-size': 1.1.0(@types/react@19.1.17)(react@19.1.1) '@radix-ui/rect': 1.1.0 react: 19.1.1 react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 - '@types/react-dom': 19.1.9(@types/react@19.1.13) + '@types/react': 19.1.17 + '@types/react-dom': 19.1.11(@types/react@19.1.17) - '@radix-ui/react-popper@1.2.8(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@radix-ui/react-popper@1.2.8(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: '@floating-ui/react-dom': 2.1.6(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-use-rect': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-use-rect': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.17)(react@19.1.1) '@radix-ui/rect': 1.1.1 react: 19.1.1 react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 - '@types/react-dom': 19.1.9(@types/react@19.1.13) + '@types/react': 19.1.17 + '@types/react-dom': 19.1.11(@types/react@19.1.17) - '@radix-ui/react-portal@1.1.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@radix-ui/react-portal@1.1.3(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.1.17)(react@19.1.1) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 - '@types/react-dom': 19.1.9(@types/react@19.1.13) + '@types/react': 19.1.17 + '@types/react-dom': 19.1.11(@types/react@19.1.17) - '@radix-ui/react-portal@1.1.9(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@radix-ui/react-portal@1.1.9(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.17)(react@19.1.1) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 - '@types/react-dom': 19.1.9(@types/react@19.1.13) + '@types/react': 19.1.17 + '@types/react-dom': 19.1.11(@types/react@19.1.17) - '@radix-ui/react-presence@1.1.2(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@radix-ui/react-presence@1.1.2(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.1.17)(react@19.1.1) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 - '@types/react-dom': 19.1.9(@types/react@19.1.13) + '@types/react': 19.1.17 + '@types/react-dom': 19.1.11(@types/react@19.1.17) - '@radix-ui/react-primitive@2.0.0(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@radix-ui/react-primitive@2.0.0(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: - '@radix-ui/react-slot': 1.1.0(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-slot': 1.1.0(@types/react@19.1.17)(react@19.1.1) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 - '@types/react-dom': 19.1.9(@types/react@19.1.13) + '@types/react': 19.1.17 + '@types/react-dom': 19.1.11(@types/react@19.1.17) - '@radix-ui/react-primitive@2.0.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@radix-ui/react-primitive@2.0.1(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: - '@radix-ui/react-slot': 1.1.1(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-slot': 1.1.1(@types/react@19.1.17)(react@19.1.1) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 - '@types/react-dom': 19.1.9(@types/react@19.1.13) + '@types/react': 19.1.17 + '@types/react-dom': 19.1.11(@types/react@19.1.17) - '@radix-ui/react-primitive@2.0.2(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@radix-ui/react-primitive@2.0.2(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: - '@radix-ui/react-slot': 1.1.2(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-slot': 1.1.2(@types/react@19.1.17)(react@19.1.1) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 - '@types/react-dom': 19.1.9(@types/react@19.1.13) + '@types/react': 19.1.17 + '@types/react-dom': 19.1.11(@types/react@19.1.17) - '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: - '@radix-ui/react-slot': 1.2.3(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.17)(react@19.1.1) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 - '@types/react-dom': 19.1.9(@types/react@19.1.13) + '@types/react': 19.1.17 + '@types/react-dom': 19.1.11(@types/react@19.1.17) - '@radix-ui/react-radio-group@1.2.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@radix-ui/react-radio-group@1.2.3(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-context': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-direction': 1.1.0(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-roving-focus': 1.1.2(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-use-previous': 1.1.0(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-use-size': 1.1.0(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-context': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-direction': 1.1.0(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-roving-focus': 1.1.2(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-use-previous': 1.1.0(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-use-size': 1.1.0(@types/react@19.1.17)(react@19.1.1) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 - '@types/react-dom': 19.1.9(@types/react@19.1.13) + '@types/react': 19.1.17 + '@types/react-dom': 19.1.11(@types/react@19.1.17) - '@radix-ui/react-roving-focus@1.1.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@radix-ui/react-roving-focus@1.1.1(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-collection': 1.1.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-context': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-direction': 1.1.0(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-id': 1.1.0(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-collection': 1.1.1(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-context': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-direction': 1.1.0(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-id': 1.1.0(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.1.17)(react@19.1.1) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 - '@types/react-dom': 19.1.9(@types/react@19.1.13) + '@types/react': 19.1.17 + '@types/react-dom': 19.1.11(@types/react@19.1.17) - '@radix-ui/react-roving-focus@1.1.2(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@radix-ui/react-roving-focus@1.1.2(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-collection': 1.1.2(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-context': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-direction': 1.1.0(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-id': 1.1.0(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-collection': 1.1.2(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-context': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-direction': 1.1.0(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-id': 1.1.0(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.1.17)(react@19.1.1) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 - '@types/react-dom': 19.1.9(@types/react@19.1.13) + '@types/react': 19.1.17 + '@types/react-dom': 19.1.11(@types/react@19.1.17) - '@radix-ui/react-scroll-area@1.2.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@radix-ui/react-scroll-area@1.2.3(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: '@radix-ui/number': 1.1.0 '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-context': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-direction': 1.1.0(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-context': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-direction': 1.1.0(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.1.17)(react@19.1.1) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 - '@types/react-dom': 19.1.9(@types/react@19.1.13) + '@types/react': 19.1.17 + '@types/react-dom': 19.1.11(@types/react@19.1.17) - '@radix-ui/react-select@2.2.6(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@radix-ui/react-select@2.2.6(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: '@radix-ui/number': 1.1.1 '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-direction': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-id': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-slot': 1.2.3(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) aria-hidden: 1.2.6 react: 19.1.1 react-dom: 19.1.1(react@19.1.1) - react-remove-scroll: 2.7.1(@types/react@19.1.13)(react@19.1.1) + react-remove-scroll: 2.7.1(@types/react@19.1.17)(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 - '@types/react-dom': 19.1.9(@types/react@19.1.13) + '@types/react': 19.1.17 + '@types/react-dom': 19.1.11(@types/react@19.1.17) - '@radix-ui/react-separator@1.1.7(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@radix-ui/react-separator@1.1.7(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 - '@types/react-dom': 19.1.9(@types/react@19.1.13) + '@types/react': 19.1.17 + '@types/react-dom': 19.1.11(@types/react@19.1.17) - '@radix-ui/react-slider@1.2.2(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@radix-ui/react-slider@1.2.2(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: '@radix-ui/number': 1.1.0 '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-collection': 1.1.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-context': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-direction': 1.1.0(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-use-previous': 1.1.0(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-use-size': 1.1.0(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-collection': 1.1.1(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-context': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-direction': 1.1.0(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-use-previous': 1.1.0(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-use-size': 1.1.0(@types/react@19.1.17)(react@19.1.1) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 - '@types/react-dom': 19.1.9(@types/react@19.1.13) + '@types/react': 19.1.17 + '@types/react-dom': 19.1.11(@types/react@19.1.17) - '@radix-ui/react-slot@1.1.0(@types/react@19.1.13)(react@19.1.1)': + '@radix-ui/react-slot@1.1.0(@types/react@19.1.17)(react@19.1.1)': dependencies: - '@radix-ui/react-compose-refs': 1.1.0(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-compose-refs': 1.1.0(@types/react@19.1.17)(react@19.1.1) react: 19.1.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - '@radix-ui/react-slot@1.1.1(@types/react@19.1.13)(react@19.1.1)': + '@radix-ui/react-slot@1.1.1(@types/react@19.1.17)(react@19.1.1)': dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.17)(react@19.1.1) react: 19.1.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - '@radix-ui/react-slot@1.1.2(@types/react@19.1.13)(react@19.1.1)': + '@radix-ui/react-slot@1.1.2(@types/react@19.1.17)(react@19.1.1)': dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.17)(react@19.1.1) react: 19.1.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - '@radix-ui/react-slot@1.2.3(@types/react@19.1.13)(react@19.1.1)': + '@radix-ui/react-slot@1.2.3(@types/react@19.1.17)(react@19.1.1)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.17)(react@19.1.1) react: 19.1.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - '@radix-ui/react-switch@1.1.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@radix-ui/react-switch@1.1.1(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: '@radix-ui/primitive': 1.1.0 - '@radix-ui/react-compose-refs': 1.1.0(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-context': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-primitive': 2.0.0(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-use-previous': 1.1.0(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-use-size': 1.1.0(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-compose-refs': 1.1.0(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-context': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-use-previous': 1.1.0(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-use-size': 1.1.0(@types/react@19.1.17)(react@19.1.1) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 - '@types/react-dom': 19.1.9(@types/react@19.1.13) + '@types/react': 19.1.17 + '@types/react-dom': 19.1.11(@types/react@19.1.17) - '@radix-ui/react-tooltip@1.1.7(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@radix-ui/react-tooltip@1.1.7(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-context': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-dismissable-layer': 1.1.4(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-id': 1.1.0(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-popper': 1.2.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-portal': 1.1.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-slot': 1.1.1(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-visually-hidden': 1.1.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-context': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-dismissable-layer': 1.1.4(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-id': 1.1.0(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-popper': 1.2.1(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-portal': 1.1.3(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-slot': 1.1.1(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-visually-hidden': 1.1.1(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 - '@types/react-dom': 19.1.9(@types/react@19.1.13) + '@types/react': 19.1.17 + '@types/react-dom': 19.1.11(@types/react@19.1.17) - '@radix-ui/react-use-callback-ref@1.1.0(@types/react@19.1.13)(react@19.1.1)': + '@radix-ui/react-use-callback-ref@1.1.0(@types/react@19.1.17)(react@19.1.1)': dependencies: react: 19.1.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.1.13)(react@19.1.1)': + '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.1.17)(react@19.1.1)': dependencies: react: 19.1.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - '@radix-ui/react-use-controllable-state@1.1.0(@types/react@19.1.13)(react@19.1.1)': + '@radix-ui/react-use-controllable-state@1.1.0(@types/react@19.1.17)(react@19.1.1)': dependencies: - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.1.17)(react@19.1.1) react: 19.1.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.1.13)(react@19.1.1)': + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.1.17)(react@19.1.1)': dependencies: - '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.17)(react@19.1.1) react: 19.1.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.1.13)(react@19.1.1)': + '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.1.17)(react@19.1.1)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.17)(react@19.1.1) react: 19.1.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - '@radix-ui/react-use-escape-keydown@1.1.0(@types/react@19.1.13)(react@19.1.1)': + '@radix-ui/react-use-escape-keydown@1.1.0(@types/react@19.1.17)(react@19.1.1)': dependencies: - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.1.17)(react@19.1.1) react: 19.1.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.1.13)(react@19.1.1)': + '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.1.17)(react@19.1.1)': dependencies: - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.17)(react@19.1.1) react: 19.1.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - '@radix-ui/react-use-layout-effect@1.1.0(@types/react@19.1.13)(react@19.1.1)': + '@radix-ui/react-use-layout-effect@1.1.0(@types/react@19.1.17)(react@19.1.1)': dependencies: react: 19.1.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.1.13)(react@19.1.1)': + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.1.17)(react@19.1.1)': dependencies: react: 19.1.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - '@radix-ui/react-use-previous@1.1.0(@types/react@19.1.13)(react@19.1.1)': + '@radix-ui/react-use-previous@1.1.0(@types/react@19.1.17)(react@19.1.1)': dependencies: react: 19.1.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - '@radix-ui/react-use-previous@1.1.1(@types/react@19.1.13)(react@19.1.1)': + '@radix-ui/react-use-previous@1.1.1(@types/react@19.1.17)(react@19.1.1)': dependencies: react: 19.1.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - '@radix-ui/react-use-rect@1.1.0(@types/react@19.1.13)(react@19.1.1)': + '@radix-ui/react-use-rect@1.1.0(@types/react@19.1.17)(react@19.1.1)': dependencies: '@radix-ui/rect': 1.1.0 react: 19.1.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - '@radix-ui/react-use-rect@1.1.1(@types/react@19.1.13)(react@19.1.1)': + '@radix-ui/react-use-rect@1.1.1(@types/react@19.1.17)(react@19.1.1)': dependencies: '@radix-ui/rect': 1.1.1 react: 19.1.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - '@radix-ui/react-use-size@1.1.0(@types/react@19.1.13)(react@19.1.1)': + '@radix-ui/react-use-size@1.1.0(@types/react@19.1.17)(react@19.1.1)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.1.17)(react@19.1.1) react: 19.1.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - '@radix-ui/react-use-size@1.1.1(@types/react@19.1.13)(react@19.1.1)': + '@radix-ui/react-use-size@1.1.1(@types/react@19.1.17)(react@19.1.1)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.13)(react@19.1.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.17)(react@19.1.1) react: 19.1.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - '@radix-ui/react-visually-hidden@1.1.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@radix-ui/react-visually-hidden@1.1.1(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 - '@types/react-dom': 19.1.9(@types/react@19.1.13) + '@types/react': 19.1.17 + '@types/react-dom': 19.1.11(@types/react@19.1.17) - '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 - '@types/react-dom': 19.1.9(@types/react@19.1.13) + '@types/react': 19.1.17 + '@types/react-dom': 19.1.11(@types/react@19.1.17) '@radix-ui/rect@1.1.0': {} @@ -8773,9 +8775,9 @@ snapshots: dependencies: '@sinonjs/commons': 3.0.0 - '@storybook/addon-docs@9.1.2(@types/react@19.1.13)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)))': + '@storybook/addon-docs@9.1.2(@types/react@19.1.17)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)))': dependencies: - '@mdx-js/react': 3.0.1(@types/react@19.1.13)(react@19.1.1) + '@mdx-js/react': 3.0.1(@types/react@19.1.17)(react@19.1.1) '@storybook/csf-plugin': 9.1.2(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))) '@storybook/icons': 1.4.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@storybook/react-dom-shim': 9.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))) @@ -8960,13 +8962,15 @@ snapshots: lodash: 4.17.21 redent: 3.0.0 - '@testing-library/react@14.3.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@testing-library/react@14.3.1(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: '@babel/runtime': 7.26.10 '@testing-library/dom': 9.3.3 - '@types/react-dom': 18.3.1 + '@types/react-dom': 18.3.7(@types/react@19.1.17) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) + transitivePeerDependencies: + - '@types/react' '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.0)': dependencies: @@ -9113,7 +9117,7 @@ snapshots: '@types/hoist-non-react-statics@3.3.5': dependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 hoist-non-react-statics: 3.3.2 '@types/http-errors@2.0.1': {} @@ -9193,47 +9197,47 @@ snapshots: '@types/range-parser@1.2.4': {} - '@types/react-color@3.0.13(@types/react@19.1.13)': + '@types/react-color@3.0.13(@types/react@19.1.17)': dependencies: - '@types/react': 19.1.13 - '@types/reactcss': 1.2.13(@types/react@19.1.13) + '@types/react': 19.1.17 + '@types/reactcss': 1.2.13(@types/react@19.1.17) '@types/react-date-range@1.4.4': dependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 date-fns: 2.30.0 - '@types/react-dom@18.3.1': + '@types/react-dom@18.3.7(@types/react@19.1.17)': dependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - '@types/react-dom@19.1.9(@types/react@19.1.13)': + '@types/react-dom@19.1.11(@types/react@19.1.17)': dependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 '@types/react-syntax-highlighter@15.5.13': dependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - '@types/react-transition-group@4.4.12(@types/react@19.1.13)': + '@types/react-transition-group@4.4.12(@types/react@19.1.17)': dependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 '@types/react-virtualized-auto-sizer@1.0.4': dependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 '@types/react-window@1.8.8': dependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - '@types/react@19.1.13': + '@types/react@19.1.17': dependencies: csstype: 3.1.3 - '@types/reactcss@1.2.13(@types/react@19.1.13)': + '@types/reactcss@1.2.13(@types/react@19.1.17)': dependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 '@types/resolve@1.20.4': {} @@ -9767,11 +9771,11 @@ snapshots: clsx@2.1.1: {} - cmdk@1.0.4(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + cmdk@1.0.4(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: - '@radix-ui/react-dialog': 1.1.4(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-id': 1.1.0(@types/react@19.1.13)(react@19.1.1) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-dialog': 1.1.4(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-id': 1.1.0(@types/react@19.1.17)(react@19.1.1) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.1.11(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) use-sync-external-store: 1.4.0(react@19.1.1) @@ -12533,11 +12537,11 @@ snapshots: prop-types: 15.8.1 react: 19.1.1 - react-markdown@9.1.0(@types/react@19.1.13)(react@19.1.1): + react-markdown@9.1.0(@types/react@19.1.17)(react@19.1.1): dependencies: '@types/hast': 3.0.4 '@types/mdast': 4.0.4 - '@types/react': 19.1.13 + '@types/react': 19.1.17 devlop: 1.1.0 hast-util-to-jsx-runtime: 2.3.6 html-url-attributes: 3.0.1 @@ -12553,35 +12557,35 @@ snapshots: react-refresh@0.17.0: {} - react-remove-scroll-bar@2.3.8(@types/react@19.1.13)(react@19.1.1): + react-remove-scroll-bar@2.3.8(@types/react@19.1.17)(react@19.1.1): dependencies: react: 19.1.1 - react-style-singleton: 2.2.3(@types/react@19.1.13)(react@19.1.1) + react-style-singleton: 2.2.3(@types/react@19.1.17)(react@19.1.1) tslib: 2.8.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - react-remove-scroll@2.6.3(@types/react@19.1.13)(react@19.1.1): + react-remove-scroll@2.6.3(@types/react@19.1.17)(react@19.1.1): dependencies: react: 19.1.1 - react-remove-scroll-bar: 2.3.8(@types/react@19.1.13)(react@19.1.1) - react-style-singleton: 2.2.3(@types/react@19.1.13)(react@19.1.1) + react-remove-scroll-bar: 2.3.8(@types/react@19.1.17)(react@19.1.1) + react-style-singleton: 2.2.3(@types/react@19.1.17)(react@19.1.1) tslib: 2.8.1 - use-callback-ref: 1.3.3(@types/react@19.1.13)(react@19.1.1) - use-sidecar: 1.1.3(@types/react@19.1.13)(react@19.1.1) + use-callback-ref: 1.3.3(@types/react@19.1.17)(react@19.1.1) + use-sidecar: 1.1.3(@types/react@19.1.17)(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - react-remove-scroll@2.7.1(@types/react@19.1.13)(react@19.1.1): + react-remove-scroll@2.7.1(@types/react@19.1.17)(react@19.1.1): dependencies: react: 19.1.1 - react-remove-scroll-bar: 2.3.8(@types/react@19.1.13)(react@19.1.1) - react-style-singleton: 2.2.3(@types/react@19.1.13)(react@19.1.1) + react-remove-scroll-bar: 2.3.8(@types/react@19.1.17)(react@19.1.1) + react-style-singleton: 2.2.3(@types/react@19.1.17)(react@19.1.1) tslib: 2.8.1 - use-callback-ref: 1.3.3(@types/react@19.1.13)(react@19.1.1) - use-sidecar: 1.1.3(@types/react@19.1.13)(react@19.1.1) + use-callback-ref: 1.3.3(@types/react@19.1.17)(react@19.1.1) + use-sidecar: 1.1.3(@types/react@19.1.17)(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 react-resizable-panels@3.0.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: @@ -12604,13 +12608,13 @@ snapshots: react-dom: 19.1.1(react@19.1.1) react-transition-group: 4.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - react-style-singleton@2.2.3(@types/react@19.1.13)(react@19.1.1): + react-style-singleton@2.2.3(@types/react@19.1.17)(react@19.1.1): dependencies: get-nonce: 1.0.1 react: 19.1.1 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 react-syntax-highlighter@15.6.1(react@19.1.1): dependencies: @@ -12622,12 +12626,12 @@ snapshots: react: 19.1.1 refractor: 3.6.0 - react-textarea-autosize@8.5.9(@types/react@19.1.13)(react@19.1.1): + react-textarea-autosize@8.5.9(@types/react@19.1.17)(react@19.1.1): dependencies: '@babel/runtime': 7.26.10 react: 19.1.1 - use-composed-ref: 1.4.0(@types/react@19.1.13)(react@19.1.1) - use-latest: 1.3.0(@types/react@19.1.13)(react@19.1.1) + use-composed-ref: 1.4.0(@types/react@19.1.17)(react@19.1.1) + use-latest: 1.3.0(@types/react@19.1.17)(react@19.1.1) transitivePeerDependencies: - '@types/react' @@ -13388,39 +13392,39 @@ snapshots: querystringify: 2.2.0 requires-port: 1.0.0 - use-callback-ref@1.3.3(@types/react@19.1.13)(react@19.1.1): + use-callback-ref@1.3.3(@types/react@19.1.17)(react@19.1.1): dependencies: react: 19.1.1 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - use-composed-ref@1.4.0(@types/react@19.1.13)(react@19.1.1): + use-composed-ref@1.4.0(@types/react@19.1.17)(react@19.1.1): dependencies: react: 19.1.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - use-isomorphic-layout-effect@1.2.1(@types/react@19.1.13)(react@19.1.1): + use-isomorphic-layout-effect@1.2.1(@types/react@19.1.17)(react@19.1.1): dependencies: react: 19.1.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - use-latest@1.3.0(@types/react@19.1.13)(react@19.1.1): + use-latest@1.3.0(@types/react@19.1.17)(react@19.1.1): dependencies: react: 19.1.1 - use-isomorphic-layout-effect: 1.2.1(@types/react@19.1.13)(react@19.1.1) + use-isomorphic-layout-effect: 1.2.1(@types/react@19.1.17)(react@19.1.1) optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 - use-sidecar@1.1.3(@types/react@19.1.13)(react@19.1.1): + use-sidecar@1.1.3(@types/react@19.1.17)(react@19.1.1): dependencies: detect-node-es: 1.1.0 react: 19.1.1 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.1.17 use-sync-external-store@1.4.0(react@19.1.1): dependencies: From 6b61c8a32a8778bf2e5a65161ee2362d8aae0183 Mon Sep 17 00:00:00 2001 From: Bruno Quaresma Date: Wed, 1 Oct 2025 15:49:52 -0300 Subject: [PATCH 007/298] feat: add workspace status on tasks (#20037) Screenshot 2025-09-30 at 11 29 31 Closes https://github.com/coder/coder/issues/19988 --- .../WorkspaceStatus.stories.tsx | 22 +++++++++++++ .../WorkspaceStatus/WorkspaceStatus.tsx | 27 +++++++++++++++ site/src/pages/TasksPage/TasksTable.tsx | 10 +++++- .../pages/WorkspacesPage/WorkspacesTable.tsx | 33 +++---------------- 4 files changed, 63 insertions(+), 29 deletions(-) create mode 100644 site/src/modules/workspaces/WorkspaceStatus/WorkspaceStatus.stories.tsx create mode 100644 site/src/modules/workspaces/WorkspaceStatus/WorkspaceStatus.tsx diff --git a/site/src/modules/workspaces/WorkspaceStatus/WorkspaceStatus.stories.tsx b/site/src/modules/workspaces/WorkspaceStatus/WorkspaceStatus.stories.tsx new file mode 100644 index 0000000000000..60313bf116efd --- /dev/null +++ b/site/src/modules/workspaces/WorkspaceStatus/WorkspaceStatus.stories.tsx @@ -0,0 +1,22 @@ +import { MockDormantWorkspace, MockWorkspace } from "testHelpers/entities"; +import type { Meta, StoryObj } from "@storybook/react-vite"; +import { WorkspaceStatus } from "./WorkspaceStatus"; + +const meta: Meta = { + title: "modules/workspaces/WorkspaceStatus", + component: WorkspaceStatus, + args: { + workspace: MockWorkspace, + }, +}; + +export default meta; +type Story = StoryObj; + +export const Running: Story = {}; + +export const Dormant: Story = { + args: { + workspace: MockDormantWorkspace, + }, +}; diff --git a/site/src/modules/workspaces/WorkspaceStatus/WorkspaceStatus.tsx b/site/src/modules/workspaces/WorkspaceStatus/WorkspaceStatus.tsx new file mode 100644 index 0000000000000..7888b1c77ef3e --- /dev/null +++ b/site/src/modules/workspaces/WorkspaceStatus/WorkspaceStatus.tsx @@ -0,0 +1,27 @@ +import type { Workspace } from "api/typesGenerated"; +import type { FC } from "react"; +import { lastUsedMessage } from "utils/workspace"; +import { WorkspaceDormantBadge } from "../WorkspaceDormantBadge/WorkspaceDormantBadge"; +import { WorkspaceStatusIndicator } from "../WorkspaceStatusIndicator/WorkspaceStatusIndicator"; + +type WorkspaceStatusProps = { + workspace: Workspace; +}; + +export const WorkspaceStatus: FC = ({ workspace }) => { + return ( +
+ + {workspace.dormant_at && ( + + )} + + +
+ ); +}; diff --git a/site/src/pages/TasksPage/TasksTable.tsx b/site/src/pages/TasksPage/TasksTable.tsx index c1cddb75a8e8f..c9ff75bb36ddc 100644 --- a/site/src/pages/TasksPage/TasksTable.tsx +++ b/site/src/pages/TasksPage/TasksTable.tsx @@ -26,6 +26,7 @@ import { RotateCcwIcon, TrashIcon } from "lucide-react"; import { TaskDeleteDialog } from "modules/tasks/TaskDeleteDialog/TaskDeleteDialog"; import type { Task } from "modules/tasks/tasks"; import { WorkspaceAppStatus } from "modules/workspaces/WorkspaceAppStatus/WorkspaceAppStatus"; +import { WorkspaceStatus } from "modules/workspaces/WorkspaceStatus/WorkspaceStatus"; import { type FC, type ReactNode, useState } from "react"; import { Link as RouterLink } from "react-router"; import { relativeTime } from "utils/time"; @@ -54,7 +55,8 @@ export const TasksTable: FC = ({ tasks, error, onRetry }) => { Task - Status + Agent status + Workspace status Created by @@ -153,6 +155,9 @@ const TaskRow: FC = ({ task }) => { status={task.workspace.latest_app_status} /> + + + { + + + diff --git a/site/src/pages/WorkspacesPage/WorkspacesTable.tsx b/site/src/pages/WorkspacesPage/WorkspacesTable.tsx index a6ba1e4a43dad..7ab941821d7b1 100644 --- a/site/src/pages/WorkspacesPage/WorkspacesTable.tsx +++ b/site/src/pages/WorkspacesPage/WorkspacesTable.tsx @@ -65,10 +65,9 @@ import { useAppLink } from "modules/apps/useAppLink"; import { useDashboard } from "modules/dashboard/useDashboard"; import { abilitiesByWorkspaceStatus } from "modules/workspaces/actions"; import { WorkspaceBuildCancelDialog } from "modules/workspaces/WorkspaceBuildCancelDialog/WorkspaceBuildCancelDialog"; -import { WorkspaceDormantBadge } from "modules/workspaces/WorkspaceDormantBadge/WorkspaceDormantBadge"; import { WorkspaceMoreActions } from "modules/workspaces/WorkspaceMoreActions/WorkspaceMoreActions"; import { WorkspaceOutdatedTooltip } from "modules/workspaces/WorkspaceOutdatedTooltip/WorkspaceOutdatedTooltip"; -import { WorkspaceStatusIndicator } from "modules/workspaces/WorkspaceStatusIndicator/WorkspaceStatusIndicator"; +import { WorkspaceStatus } from "modules/workspaces/WorkspaceStatus/WorkspaceStatus"; import { useWorkspaceUpdate, WorkspaceUpdateDialogs, @@ -83,10 +82,7 @@ import { import { useMutation, useQuery, useQueryClient } from "react-query"; import { useNavigate } from "react-router"; import { cn } from "utils/cn"; -import { - getDisplayWorkspaceTemplateName, - lastUsedMessage, -} from "utils/workspace"; +import { getDisplayWorkspaceTemplateName } from "utils/workspace"; import { WorkspacesEmpty } from "./WorkspacesEmpty"; interface WorkspacesTableProps { @@ -256,7 +252,9 @@ export const WorkspacesTable: FC = ({ /> - + + + { return ["deleting", "pending"].includes(workspace.latest_build.status); }; -type WorkspaceStatusCellProps = { - workspace: Workspace; -}; - -const WorkspaceStatusCell: FC = ({ workspace }) => { - return ( - -
- - {workspace.dormant_at && ( - - )} - - - {lastUsedMessage(workspace.last_used_at)} - -
-
- ); -}; - type WorkspaceActionsCellProps = { workspace: Workspace; onActionSuccess: () => Promise; From b3f1492f146c4f487034fba088b37ff848b76fda Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Wed, 1 Oct 2025 19:53:58 +0100 Subject: [PATCH 008/298] ci(.github/workflows/traiage.yaml): set default inputs on trigger by label assignment (#20100) When not triggering via `workflow_dispatch`, it looks like the default values are simply empty. This PR creates an intermediate step to conditionally set defaults based on `github.event_name`. I'm also adding a commented-out step for installing `gh` that's required for local testing via `nektos/act`. It's not required in a 'real' runner. --- .github/workflows/traiage.yaml | 93 +++++++++++++++++++++------------- 1 file changed, 58 insertions(+), 35 deletions(-) diff --git a/.github/workflows/traiage.yaml b/.github/workflows/traiage.yaml index 24192fc2d1812..09881ff2ee46a 100644 --- a/.github/workflows/traiage.yaml +++ b/.github/workflows/traiage.yaml @@ -40,25 +40,57 @@ jobs: env: CODER_URL: ${{ secrets.TRAIAGE_CODER_URL }} CODER_SESSION_TOKEN: ${{ secrets.TRAIAGE_CODER_SESSION_TOKEN }} - TEMPLATE_NAME: ${{ inputs.template_name }} permissions: contents: read issues: write actions: write steps: - - name: Get GitHub user ID - id: github-user-id + # This is only required for testing locally using nektos/act, so leaving commented out. + # An alternative is to use a larger or custom image. + # - name: Install Github CLI + # id: install-gh + # run: | + # (type -p wget >/dev/null || (sudo apt update && sudo apt install wget -y)) \ + # && sudo mkdir -p -m 755 /etc/apt/keyrings \ + # && out=$(mktemp) && wget -nv -O$out https://cli.github.com/packages/githubcli-archive-keyring.gpg \ + # && cat $out | sudo tee /etc/apt/keyrings/githubcli-archive-keyring.gpg > /dev/null \ + # && sudo chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg \ + # && sudo mkdir -p -m 755 /etc/apt/sources.list.d \ + # && echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null \ + # && sudo apt update \ + # && sudo apt install gh -y + + - name: Determine Inputs + id: determine-inputs if: always() env: GITHUB_ACTOR: ${{ github.actor }} + GITHUB_EVENT_ISSUE_HTML_URL: ${{ github.event.issue.html_url }} GITHUB_EVENT_NAME: ${{ github.event_name }} GITHUB_EVENT_USER_ID: ${{ github.event.sender.id }} GITHUB_EVENT_USER_LOGIN: ${{ github.event.sender.login }} + INPUTS_CLEANUP: ${{ inputs.cleanup || false }} + INPUTS_ISSUE_URL: ${{ inputs.issue_url }} + INPUTS_TEMPLATE_NAME: ${{ inputs.template_name || 'traiage' }} + INPUTS_TEMPLATE_PRESET: ${{ inputs.template_preset || 'Default'}} + INPUTS_PREFIX: ${{ inputs.prefix || 'traiage' }} GH_TOKEN: ${{ github.token }} run: | + echo "Using template name: ${INPUTS_TEMPLATE_NAME}" + echo "template_name=${INPUTS_TEMPLATE_NAME}" >> "${GITHUB_OUTPUT}" + + echo "Using template preset: ${INPUTS_TEMPLATE_PRESET}" + echo "template_preset=${INPUTS_TEMPLATE_PRESET}" >> "${GITHUB_OUTPUT}" + + echo "Using prefix: ${INPUTS_PREFIX}" + echo "prefix=${INPUTS_PREFIX}" >> "${GITHUB_OUTPUT}" + + echo "Using cleanup: ${INPUTS_CLEANUP}" + echo "cleanup=${INPUTS_CLEANUP}" >> "${GITHUB_OUTPUT}" + # For workflow_dispatch, use the actor who triggered it - # For issues events, use the issue author + # For issues events, use the issue author. if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then if ! GITHUB_USER_ID=$(gh api "users/${GITHUB_ACTOR}" --jq '.id'); then echo "::error::Failed to get GitHub user ID for actor ${GITHUB_ACTOR}" @@ -67,12 +99,20 @@ jobs: echo "Using workflow_dispatch actor: ${GITHUB_ACTOR} (ID: ${GITHUB_USER_ID})" echo "github_user_id=${GITHUB_USER_ID}" >> "${GITHUB_OUTPUT}" echo "github_username=${GITHUB_ACTOR}" >> "${GITHUB_OUTPUT}" + + echo "Using issue URL: ${INPUTS_ISSUE_URL}" + echo "issue_url=${INPUTS_ISSUE_URL}" >> "${GITHUB_OUTPUT}" + exit 0 elif [[ "${GITHUB_EVENT_NAME}" == "issues" ]]; then GITHUB_USER_ID=${GITHUB_EVENT_USER_ID} echo "Using issue author: ${GITHUB_EVENT_USER_LOGIN} (ID: ${GITHUB_USER_ID})" echo "github_user_id=${GITHUB_USER_ID}" >> "${GITHUB_OUTPUT}" echo "github_username=${GITHUB_EVENT_USER_LOGIN}" >> "${GITHUB_OUTPUT}" + + echo "Using issue URL: ${GITHUB_EVENT_ISSUE_HTML_URL}" + echo "issue_url=${GITHUB_EVENT_ISSUE_HTML_URL}" >> "${GITHUB_OUTPUT}" + exit 0 else echo "::error::Unsupported event type: ${GITHUB_EVENT_NAME}" @@ -83,8 +123,8 @@ jobs: env: GITHUB_ORG: ${{ github.repository_owner }} GH_TOKEN: ${{ github.token }} - GITHUB_USERNAME: ${{ steps.github-user-id.outputs.github_username }} - GITHUB_USER_ID: ${{ steps.github-user-id.outputs.github_user_id }} + GITHUB_USERNAME: ${{ steps.determine-inputs.outputs.github_username }} + GITHUB_USER_ID: ${{ steps.determine-inputs.outputs.github_user_id }} run: | # Check if the actor is a member of the organization if ! gh api "orgs/${GITHUB_ORG}/members/${GITHUB_USERNAME}" --silent 2>/dev/null; then @@ -94,28 +134,10 @@ jobs: fi echo "::notice::User ${GITHUB_USERNAME} verified as member of ${GITHUB_ORG} organization" - - name: Determine issue URL - id: determine-issue-url - env: - INPUTS_ISSUE_URL: ${{ inputs.issue_url }} - GITHUB_EVENT_ISSUE_HTML_URL: ${{ github.event.issue.html_url }} - GITHUB_EVENT_NAME: ${{ github.event_name }} - run: | - if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then - echo "issue_url=${INPUTS_ISSUE_URL}" >> "${GITHUB_OUTPUT}" - exit 0 - elif [[ "${GITHUB_EVENT_NAME}" == "issues" ]]; then - echo "issue_url=${GITHUB_EVENT_ISSUE_HTML_URL}" >> "${GITHUB_OUTPUT}" - exit 0 - else - echo "::error::Unsupported event type: ${GITHUB_EVENT_NAME}" - exit 1 - fi - - name: Extract context key from issue id: extract-context env: - ISSUE_URL: ${{ steps.determine-issue-url.outputs.issue_url }} + ISSUE_URL: ${{ steps.determine-inputs.outputs.issue_url }} GH_TOKEN: ${{ github.token }} run: | issue_number="$(gh issue view "${ISSUE_URL}" --json number --jq '.number')" @@ -146,7 +168,7 @@ jobs: env: CODER_SESSION_TOKEN: ${{ secrets.TRAIAGE_CODER_SESSION_TOKEN }} GH_TOKEN: ${{ github.token }} - GITHUB_USER_ID: ${{ steps.github-user-id.outputs.github_user_id }} + GITHUB_USER_ID: ${{ steps.determine-inputs.outputs.github_user_id }} run: | user_json=$( coder users list --github-user-id="${GITHUB_USER_ID}" --output=json @@ -169,11 +191,12 @@ jobs: CONTEXT_KEY: ${{ steps.extract-context.outputs.context_key }} GH_TOKEN: ${{ github.token }} GITHUB_REPOSITORY: ${{ github.repository }} - ISSUE_URL: ${{ steps.determine-issue-url.outputs.issue_url }} - PREFIX: ${{ inputs.prefix }} + ISSUE_URL: ${{ steps.determine-inputs.outputs.issue_url }} + PREFIX: ${{ steps.determine-inputs.outputs.prefix }} RUN_ID: ${{ github.run_id }} + TEMPLATE_NAME: ${{ steps.determine-inputs.outputs.template_name }} TEMPLATE_PARAMETERS: ${{ secrets.TRAIAGE_TEMPLATE_PARAMETERS }} - TEMPLATE_PRESET: ${{ inputs.template_preset }} + TEMPLATE_PRESET: ${{ steps.determine-inputs.outputs.template_preset }} run: | # Fetch issue description using `gh` CLI issue_description=$(gh issue view "${ISSUE_URL}") @@ -203,21 +226,21 @@ jobs: - name: Create and upload archive id: create-archive - if: inputs.cleanup + if: steps.determine-inputs.outputs.cleanup == 'true' env: BUCKET_PREFIX: "gs://coder-traiage-outputs/traiage" CODER_USERNAME: ${{ steps.get-coder-username.outputs.coder_username }} TASK_NAME: ${{ steps.create-task.outputs.TASK_NAME }} run: | echo "Waiting for task to complete..." - coder exp task status "${CODER_USERNAME}/$TASK_NAME" --watch - echo "Creating archive for workspace: $TASK_NAME" + coder exp task status "${TASK_NAME}" --watch + echo "Creating archive for workspace: ${TASK_NAME}" ./scripts/traiage.sh archive echo "archive_url=${BUCKET_PREFIX%%/}/$TASK_NAME.tar.gz" >> "${GITHUB_OUTPUT}" - name: Generate a summary of the changes and post a comment on GitHub. id: generate-summary - if: inputs.cleanup + if: steps.determine-inputs.outputs.cleanup == 'true' env: ARCHIVE_URL: ${{ steps.create-archive.outputs.archive_url }} BUCKET_PREFIX: "gs://coder-traiage-outputs/traiage" @@ -225,7 +248,7 @@ jobs: CONTEXT_KEY: ${{ steps.extract-context.outputs.context_key }} GH_TOKEN: ${{ github.token }} GITHUB_REPOSITORY: ${{ github.repository }} - ISSUE_URL: ${{ steps.determine-issue-url.outputs.issue_url }} + ISSUE_URL: ${{ steps.determine-inputs.outputs.issue_url }} TASK_NAME: ${{ steps.create-task.outputs.TASK_NAME }} run: | SUMMARY_FILE=$(mktemp) @@ -256,7 +279,7 @@ jobs: cat "${SUMMARY_FILE}" >> "${GITHUB_STEP_SUMMARY}" - name: Cleanup task - if: inputs.cleanup && steps.create-task.outputs.TASK_NAME != '' && steps.create-archive.outputs.archive_url != '' + if: steps.determine-inputs.outputs.cleanup == 'true' && steps.create-task.outputs.TASK_NAME != '' && steps.create-archive.outputs.archive_url != '' run: | echo "Cleaning up task: $TASK_NAME" ./scripts/traiage.sh delete || true From 3afab824f5ce227543788ad65474fb39c63b740a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=82=B1=E3=82=A4=E3=83=A9?= Date: Wed, 1 Oct 2025 13:26:49 -0600 Subject: [PATCH 009/298] fix: revert playwright update and update dependabot config (#20103) --- .github/dependabot.yaml | 4 ++++ site/package.json | 2 +- site/pnpm-lock.yaml | 26 +++++++++++++------------- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml index 67d1f1342dcaf..fbf713d16b5bd 100644 --- a/.github/dependabot.yaml +++ b/.github/dependabot.yaml @@ -80,6 +80,9 @@ updates: mui: patterns: - "@mui*" + radix: + patterns: + - "@radix-ui/*" react: patterns: - "react" @@ -104,6 +107,7 @@ updates: - dependency-name: "*" update-types: - version-update:semver-major + - dependency-name: "@playwright/test" open-pull-requests-limit: 15 - package-ecosystem: "terraform" diff --git a/site/package.json b/site/package.json index 14e3365fc6d54..d18b8f1d5a0d4 100644 --- a/site/package.json +++ b/site/package.json @@ -126,7 +126,7 @@ "@biomejs/biome": "2.2.0", "@chromatic-com/storybook": "4.1.0", "@octokit/types": "12.3.0", - "@playwright/test": "1.55.1", + "@playwright/test": "1.50.1", "@storybook/addon-docs": "9.1.2", "@storybook/addon-links": "9.1.2", "@storybook/addon-themes": "9.1.2", diff --git a/site/pnpm-lock.yaml b/site/pnpm-lock.yaml index f36ffa639e6d9..e49af3a101ba3 100644 --- a/site/pnpm-lock.yaml +++ b/site/pnpm-lock.yaml @@ -288,8 +288,8 @@ importers: specifier: 12.3.0 version: 12.3.0 '@playwright/test': - specifier: 1.55.1 - version: 1.55.1 + specifier: 1.50.1 + version: 1.50.1 '@storybook/addon-docs': specifier: 9.1.2 version: 9.1.2(@types/react@19.1.17)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))) @@ -1625,8 +1625,8 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==, tarball: https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz} engines: {node: '>=14'} - '@playwright/test@1.55.1': - resolution: {integrity: sha512-IVAh/nOJaw6W9g+RJVlIQJ6gSiER+ae6mKQ5CX1bERzQgbC1VSeBlwdvczT7pxb0GWiyrxH4TGKbMfDb4Sq/ig==, tarball: https://registry.npmjs.org/@playwright/test/-/test-1.55.1.tgz} + '@playwright/test@1.50.1': + resolution: {integrity: sha512-Jii3aBg+CEDpgnuDxEp/h7BimHcUTDlpEtce89xEumlJ5ef2hqepZ+PWp1DDpYC/VO9fmWVI1IlEaoI5fK9FXQ==, tarball: https://registry.npmjs.org/@playwright/test/-/test-1.50.1.tgz} engines: {node: '>=18'} hasBin: true @@ -5459,13 +5459,13 @@ packages: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==, tarball: https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz} engines: {node: '>=8'} - playwright-core@1.55.1: - resolution: {integrity: sha512-Z6Mh9mkwX+zxSlHqdr5AOcJnfp+xUWLCt9uKV18fhzA8eyxUd8NUWzAjxUh55RZKSYwDGX0cfaySdhZJGMoJ+w==, tarball: https://registry.npmjs.org/playwright-core/-/playwright-core-1.55.1.tgz} + playwright-core@1.50.1: + resolution: {integrity: sha512-ra9fsNWayuYumt+NiM069M6OkcRb1FZSK8bgi66AtpFoWkg2+y0bJSNmkFrWhMbEBbVKC/EruAHH3g0zmtwGmQ==, tarball: https://registry.npmjs.org/playwright-core/-/playwright-core-1.50.1.tgz} engines: {node: '>=18'} hasBin: true - playwright@1.55.1: - resolution: {integrity: sha512-cJW4Xd/G3v5ovXtJJ52MAOclqeac9S/aGGgRzLabuF8TnIb6xHvMzKIa6JmrRzUkeXJgfL1MhukP0NK6l39h3A==, tarball: https://registry.npmjs.org/playwright/-/playwright-1.55.1.tgz} + playwright@1.50.1: + resolution: {integrity: sha512-G8rwsOQJ63XG6BbKj2w5rHeavFjy5zynBA9zsJMMtBoe/Uf757oG12NXz6e6OirF7RCrTVAKFXbLmn1RbL7Qaw==, tarball: https://registry.npmjs.org/playwright/-/playwright-1.50.1.tgz} engines: {node: '>=18'} hasBin: true @@ -7915,9 +7915,9 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@playwright/test@1.55.1': + '@playwright/test@1.50.1': dependencies: - playwright: 1.55.1 + playwright: 1.50.1 '@popperjs/core@2.11.8': {} @@ -12322,11 +12322,11 @@ snapshots: dependencies: find-up: 4.1.0 - playwright-core@1.55.1: {} + playwright-core@1.50.1: {} - playwright@1.55.1: + playwright@1.50.1: dependencies: - playwright-core: 1.55.1 + playwright-core: 1.50.1 optionalDependencies: fsevents: 2.3.2 From 7c3e24f3be4519f6a33fa10ac61ee7576722e693 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 19:33:42 +0000 Subject: [PATCH 010/298] chore: bump ubuntu from `0e5e4a5` to `4e0171b` in /dogfood/coder (#20105) Bumps ubuntu from `0e5e4a5` to `4e0171b`. [![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=ubuntu&package-manager=docker&previous-version=jammy&new-version=jammy)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- dogfood/coder/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dogfood/coder/Dockerfile b/dogfood/coder/Dockerfile index 90bdcb71aaacb..f64c47819d799 100644 --- a/dogfood/coder/Dockerfile +++ b/dogfood/coder/Dockerfile @@ -8,7 +8,7 @@ RUN sed -i 's|http://deb.debian.org/debian|http://mirrors.edge.kernel.org/debian RUN apt-get update && apt-get install -y libssl-dev openssl pkg-config build-essential RUN cargo install jj-cli typos-cli watchexec-cli -FROM ubuntu:jammy@sha256:0e5e4a57c2499249aafc3b40fcd541e9a456aab7296681a3994d631587203f97 AS go +FROM ubuntu:jammy@sha256:4e0171b9275e12d375863f2b3ae9ce00a4c53ddda176bd55868df97ac6f21a6e AS go # Install Go manually, so that we can control the version ARG GO_VERSION=1.24.6 @@ -97,7 +97,7 @@ RUN curl -L -o protoc.zip https://github.com/protocolbuffers/protobuf/releases/d unzip protoc.zip && \ rm protoc.zip -FROM ubuntu:jammy@sha256:0e5e4a57c2499249aafc3b40fcd541e9a456aab7296681a3994d631587203f97 +FROM ubuntu:jammy@sha256:4e0171b9275e12d375863f2b3ae9ce00a4c53ddda176bd55868df97ac6f21a6e SHELL ["/bin/bash", "-c"] From b05033897aa32ee2620adc7b940a28f56d5724e6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 19:33:54 +0000 Subject: [PATCH 011/298] chore: bump rust from `3f391b0` to `1219c0b` in /dogfood/coder (#20108) Bumps rust from `3f391b0` to `1219c0b`. [![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=rust&package-manager=docker&previous-version=slim&new-version=slim)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- dogfood/coder/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dogfood/coder/Dockerfile b/dogfood/coder/Dockerfile index f64c47819d799..7f1eda55a6c58 100644 --- a/dogfood/coder/Dockerfile +++ b/dogfood/coder/Dockerfile @@ -1,5 +1,5 @@ # 1.86.0 -FROM rust:slim@sha256:3f391b0678a6e0c88fd26f13e399c9c515ac47354e3cadfee7daee3b21651a4f AS rust-utils +FROM rust:slim@sha256:1219c0b5980e8310eb8e08a7fec9e2159ffc9f166cc9999ddb13604c43cd0eb6 AS rust-utils # Install rust helper programs ENV CARGO_INSTALL_ROOT=/tmp/ # Use more reliable mirrors for Debian packages From 3eb6e1cc9771e7fea2313b2d4c78cd4730148d0b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 19:34:11 +0000 Subject: [PATCH 012/298] chore: bump coder/claude-code/coder from 3.0.0 to 3.0.1 in /dogfood/coder (#20104) [![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=coder/claude-code/coder&package-manager=terraform&previous-version=3.0.0&new-version=3.0.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- dogfood/coder/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dogfood/coder/main.tf b/dogfood/coder/main.tf index 6762f2bcbee27..614b25cdb9ec8 100644 --- a/dogfood/coder/main.tf +++ b/dogfood/coder/main.tf @@ -866,7 +866,7 @@ locals { module "claude-code" { count = local.has_ai_prompt ? data.coder_workspace.me.start_count : 0 source = "dev.registry.coder.com/coder/claude-code/coder" - version = "3.0.0" + version = "3.0.1" agent_id = coder_agent.dev.id workdir = local.repo_dir claude_code_version = "latest" From c6c9fa1e3972b8ea19fa73c9179137ff47b3cafd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 19:34:28 +0000 Subject: [PATCH 013/298] chore: bump alpine from 3.21.3 to 3.22.1 in /scripts (#20107) Bumps alpine from 3.21.3 to 3.22.1. [![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=alpine&package-manager=docker&previous-version=3.21.3&new-version=3.22.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- scripts/Dockerfile.base | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/Dockerfile.base b/scripts/Dockerfile.base index 53c999301e410..e13b5ffa666e0 100644 --- a/scripts/Dockerfile.base +++ b/scripts/Dockerfile.base @@ -1,7 +1,7 @@ # This is the base image used for Coder images. It's a multi-arch image that is # built in depot.dev for all supported architectures. Since it's built on real # hardware and not cross-compiled, it can have "RUN" commands. -FROM alpine:3.21.3@sha256:a8560b36e8b8210634f77d9f7f9efd7ffa463e380b75e2e74aff4511df3ef88c +FROM alpine:3.22.1@sha256:4bcff63911fcb4448bd4fdacec207030997caf25e9bea4045fa6c8c44de311d1 # We use a single RUN command to reduce the number of layers in the image. # NOTE: Keep the Terraform version in sync with minTerraformVersion and From 7328fa5c0866ca46587b5e39a50ea9a241330a03 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 19:37:23 +0000 Subject: [PATCH 014/298] chore: bump typescript from 5.7.3 to 5.9.3 in /offlinedocs (#20112) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [typescript](https://github.com/microsoft/TypeScript) from 5.7.3 to 5.9.3.
Release notes

Sourced from typescript's releases.

TypeScript 5.9.3

Note: this tag was recreated to point at the correct commit. The npm package contained the correct content.

For release notes, check out the release announcement

Downloads are available on:

TypeScript 5.9

For release notes, check out the release announcement

Downloads are available on:

TypeScript 5.9 RC

For release notes, check out the release announcement

Downloads are available on:

TypeScript 5.9 Beta

For release notes, check out the release announcement.

Downloads are available on:

TypeScript 5.8.3

For release notes, check out the release announcement.

Downloads are available on:

... (truncated)

Commits
  • c63de15 Bump version to 5.9.3 and LKG
  • 8428ca4 🤖 Pick PR #62438 (Fix incorrectly ignored dts file fr...) into release-5.9 (#...
  • a131cac 🤖 Pick PR #62351 (Add missing Float16Array constructo...) into release-5.9 (#...
  • 0424333 🤖 Pick PR #62423 (Revert PR 61928) into release-5.9 (#62425)
  • bdb641a 🤖 Pick PR #62311 (Fix parenthesizer rules for manuall...) into release-5.9 (#...
  • 0d9b9b9 🤖 Pick PR #61978 (Restructure CI to prepare for requi...) into release-5.9 (#...
  • 2dce0c5 Intentionally regress one buggy declaration output to an older version (#62163)
  • 5be3346 Bump version to 5.9.2 and LKG
  • ad825f2 Bump version to 5.9.1-rc and LKG
  • 463a5bf Update LKG
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=typescript&package-manager=npm_and_yarn&previous-version=5.7.3&new-version=5.9.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- offlinedocs/package.json | 2 +- offlinedocs/pnpm-lock.yaml | 92 +++++++++++++++++++------------------- 2 files changed, 47 insertions(+), 47 deletions(-) diff --git a/offlinedocs/package.json b/offlinedocs/package.json index 15c5ce793b135..26073286ddb65 100644 --- a/offlinedocs/package.json +++ b/offlinedocs/package.json @@ -38,7 +38,7 @@ "eslint": "8.57.1", "eslint-config-next": "14.2.33", "prettier": "3.6.2", - "typescript": "5.7.3" + "typescript": "5.9.3" }, "engines": { "npm": ">=9.0.0 <10.0.0", diff --git a/offlinedocs/pnpm-lock.yaml b/offlinedocs/pnpm-lock.yaml index 368b8515c6b54..7c4466814364c 100644 --- a/offlinedocs/pnpm-lock.yaml +++ b/offlinedocs/pnpm-lock.yaml @@ -78,13 +78,13 @@ importers: version: 8.57.1 eslint-config-next: specifier: 14.2.33 - version: 14.2.33(eslint@8.57.1)(typescript@5.7.3) + version: 14.2.33(eslint@8.57.1)(typescript@5.9.3) prettier: specifier: 3.6.2 version: 3.6.2 typescript: - specifier: 5.7.3 - version: 5.7.3 + specifier: 5.9.3 + version: 5.9.3 packages: @@ -2465,8 +2465,8 @@ packages: resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} engines: {node: '>= 0.4'} - typescript@5.7.3: - resolution: {integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==} + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} hasBin: true @@ -3101,41 +3101,41 @@ snapshots: '@types/unist@3.0.3': {} - '@typescript-eslint/eslint-plugin@8.45.0(@typescript-eslint/parser@8.45.0(eslint@8.57.1)(typescript@5.7.3))(eslint@8.57.1)(typescript@5.7.3)': + '@typescript-eslint/eslint-plugin@8.45.0(@typescript-eslint/parser@8.45.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.45.0(eslint@8.57.1)(typescript@5.7.3) + '@typescript-eslint/parser': 8.45.0(eslint@8.57.1)(typescript@5.9.3) '@typescript-eslint/scope-manager': 8.45.0 - '@typescript-eslint/type-utils': 8.45.0(eslint@8.57.1)(typescript@5.7.3) - '@typescript-eslint/utils': 8.45.0(eslint@8.57.1)(typescript@5.7.3) + '@typescript-eslint/type-utils': 8.45.0(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/utils': 8.45.0(eslint@8.57.1)(typescript@5.9.3) '@typescript-eslint/visitor-keys': 8.45.0 eslint: 8.57.1 graphemer: 1.4.0 ignore: 7.0.5 natural-compare: 1.4.0 - ts-api-utils: 2.1.0(typescript@5.7.3) - typescript: 5.7.3 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.45.0(eslint@8.57.1)(typescript@5.7.3)': + '@typescript-eslint/parser@8.45.0(eslint@8.57.1)(typescript@5.9.3)': dependencies: '@typescript-eslint/scope-manager': 8.45.0 '@typescript-eslint/types': 8.45.0 - '@typescript-eslint/typescript-estree': 8.45.0(typescript@5.7.3) + '@typescript-eslint/typescript-estree': 8.45.0(typescript@5.9.3) '@typescript-eslint/visitor-keys': 8.45.0 debug: 4.4.3 eslint: 8.57.1 - typescript: 5.7.3 + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.45.0(typescript@5.7.3)': + '@typescript-eslint/project-service@8.45.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.45.0(typescript@5.7.3) + '@typescript-eslint/tsconfig-utils': 8.45.0(typescript@5.9.3) '@typescript-eslint/types': 8.45.0 debug: 4.4.3 - typescript: 5.7.3 + typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -3144,28 +3144,28 @@ snapshots: '@typescript-eslint/types': 8.45.0 '@typescript-eslint/visitor-keys': 8.45.0 - '@typescript-eslint/tsconfig-utils@8.45.0(typescript@5.7.3)': + '@typescript-eslint/tsconfig-utils@8.45.0(typescript@5.9.3)': dependencies: - typescript: 5.7.3 + typescript: 5.9.3 - '@typescript-eslint/type-utils@8.45.0(eslint@8.57.1)(typescript@5.7.3)': + '@typescript-eslint/type-utils@8.45.0(eslint@8.57.1)(typescript@5.9.3)': dependencies: '@typescript-eslint/types': 8.45.0 - '@typescript-eslint/typescript-estree': 8.45.0(typescript@5.7.3) - '@typescript-eslint/utils': 8.45.0(eslint@8.57.1)(typescript@5.7.3) + '@typescript-eslint/typescript-estree': 8.45.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.45.0(eslint@8.57.1)(typescript@5.9.3) debug: 4.4.3 eslint: 8.57.1 - ts-api-utils: 2.1.0(typescript@5.7.3) - typescript: 5.7.3 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 transitivePeerDependencies: - supports-color '@typescript-eslint/types@8.45.0': {} - '@typescript-eslint/typescript-estree@8.45.0(typescript@5.7.3)': + '@typescript-eslint/typescript-estree@8.45.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.45.0(typescript@5.7.3) - '@typescript-eslint/tsconfig-utils': 8.45.0(typescript@5.7.3) + '@typescript-eslint/project-service': 8.45.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.45.0(typescript@5.9.3) '@typescript-eslint/types': 8.45.0 '@typescript-eslint/visitor-keys': 8.45.0 debug: 4.4.3 @@ -3173,19 +3173,19 @@ snapshots: is-glob: 4.0.3 minimatch: 9.0.5 semver: 7.7.2 - ts-api-utils: 2.1.0(typescript@5.7.3) - typescript: 5.7.3 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.45.0(eslint@8.57.1)(typescript@5.7.3)': + '@typescript-eslint/utils@8.45.0(eslint@8.57.1)(typescript@5.9.3)': dependencies: '@eslint-community/eslint-utils': 4.9.0(eslint@8.57.1) '@typescript-eslint/scope-manager': 8.45.0 '@typescript-eslint/types': 8.45.0 - '@typescript-eslint/typescript-estree': 8.45.0(typescript@5.7.3) + '@typescript-eslint/typescript-estree': 8.45.0(typescript@5.9.3) eslint: 8.57.1 - typescript: 5.7.3 + typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -3732,21 +3732,21 @@ snapshots: escape-string-regexp@5.0.0: {} - eslint-config-next@14.2.33(eslint@8.57.1)(typescript@5.7.3): + eslint-config-next@14.2.33(eslint@8.57.1)(typescript@5.9.3): dependencies: '@next/eslint-plugin-next': 14.2.33 '@rushstack/eslint-patch': 1.12.0 - '@typescript-eslint/eslint-plugin': 8.45.0(@typescript-eslint/parser@8.45.0(eslint@8.57.1)(typescript@5.7.3))(eslint@8.57.1)(typescript@5.7.3) - '@typescript-eslint/parser': 8.45.0(eslint@8.57.1)(typescript@5.7.3) + '@typescript-eslint/eslint-plugin': 8.45.0(@typescript-eslint/parser@8.45.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/parser': 8.45.0(eslint@8.57.1)(typescript@5.9.3) eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@8.57.1) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.45.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.45.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.1) eslint-plugin-react: 7.37.5(eslint@8.57.1) eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@8.57.1) optionalDependencies: - typescript: 5.7.3 + typescript: 5.9.3 transitivePeerDependencies: - eslint-import-resolver-webpack - eslint-plugin-import-x @@ -3771,22 +3771,22 @@ snapshots: tinyglobby: 0.2.15 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.45.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.45.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.45.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.45.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.45.0(eslint@8.57.1)(typescript@5.7.3) + '@typescript-eslint/parser': 8.45.0(eslint@8.57.1)(typescript@5.9.3) eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@8.57.1) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.45.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.45.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -3797,7 +3797,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.45.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.45.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -3809,7 +3809,7 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.45.0(eslint@8.57.1)(typescript@5.7.3) + '@typescript-eslint/parser': 8.45.0(eslint@8.57.1)(typescript@5.9.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -5506,9 +5506,9 @@ snapshots: trough@2.2.0: {} - ts-api-utils@2.1.0(typescript@5.7.3): + ts-api-utils@2.1.0(typescript@5.9.3): dependencies: - typescript: 5.7.3 + typescript: 5.9.3 tsconfig-paths@3.15.0: dependencies: @@ -5562,7 +5562,7 @@ snapshots: possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 - typescript@5.7.3: {} + typescript@5.9.3: {} unbox-primitive@1.1.0: dependencies: From f7388f3dfc5d346831dde385a14b3e0351dbf11e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 19:41:20 +0000 Subject: [PATCH 015/298] chore: bump github.com/anthropics/anthropic-sdk-go from 1.12.0 to 1.13.0 (#20109) Bumps [github.com/anthropics/anthropic-sdk-go](https://github.com/anthropics/anthropic-sdk-go) from 1.12.0 to 1.13.0.
Release notes

Sourced from github.com/anthropics/anthropic-sdk-go's releases.

v1.13.0

1.13.0 (2025-09-29)

Full Changelog: v1.12.0...v1.13.0

Features

  • api: adds support for Claude Sonnet 4.5 and context management features (3d5d51a)

Bug Fixes

  • bugfix for setting JSON keys with special characters (c868b92)
  • internal: unmarshal correctly when there are multiple discriminators (ecc3ce3)
  • use slices.Concat instead of sometimes modifying r.Options (88e7186)

Chores

  • bump minimum go version to 1.22 (87af8f3)
  • do not install brew dependencies in ./scripts/bootstrap by default (c689348)
  • internal: fix tests (bfc6eaf)
  • update more docs for 1.22 (d67c50d)
Changelog

Sourced from github.com/anthropics/anthropic-sdk-go's changelog.

1.13.0 (2025-09-29)

Full Changelog: v1.12.0...v1.13.0

Features

  • api: adds support for Claude Sonnet 4.5 and context management features (3d5d51a)

Bug Fixes

  • bugfix for setting JSON keys with special characters (c868b92)
  • internal: unmarshal correctly when there are multiple discriminators (ecc3ce3)
  • use slices.Concat instead of sometimes modifying r.Options (88e7186)

Chores

  • bump minimum go version to 1.22 (87af8f3)
  • do not install brew dependencies in ./scripts/bootstrap by default (c689348)
  • internal: fix tests (bfc6eaf)
  • update more docs for 1.22 (d67c50d)
Commits
  • e8befdc release: 1.13.0
  • 1de6d47 feat(api): adds support for Claude Sonnet 4.5 and context management features
  • a9aab31 fix: bugfix for setting JSON keys with special characters
  • 41a7454 codegen metadata
  • 31633bd chore: do not install brew dependencies in ./scripts/bootstrap by default
  • 2deaed6 fix: use slices.Concat instead of sometimes modifying r.Options
  • 9f35b68 chore: update more docs for 1.22
  • 287a399 chore: bump minimum go version to 1.22
  • aa4540a chore(internal): fix tests
  • 73e5532 codegen metadata
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/anthropics/anthropic-sdk-go&package-manager=go_modules&previous-version=1.12.0&new-version=1.13.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index b0c3360913ceb..581f1beaeecff 100644 --- a/go.mod +++ b/go.mod @@ -475,7 +475,7 @@ require ( ) require ( - github.com/anthropics/anthropic-sdk-go v1.12.0 + github.com/anthropics/anthropic-sdk-go v1.13.0 github.com/brianvoe/gofakeit/v7 v7.7.1 github.com/coder/agentapi-sdk-go v0.0.0-20250505131810-560d1d88d225 github.com/coder/aibridge v0.1.3 diff --git a/go.sum b/go.sum index 2842cbcc7baa2..c1151e853041d 100644 --- a/go.sum +++ b/go.sum @@ -722,8 +722,8 @@ github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwTo github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= -github.com/anthropics/anthropic-sdk-go v1.12.0 h1:xPqlGnq7rWrTiHazIvCiumA0u7mGQnwDQtvA1M82h9U= -github.com/anthropics/anthropic-sdk-go v1.12.0/go.mod h1:WTz31rIUHUHqai2UslPpw5CwXrQP3geYBioRV4WOLvE= +github.com/anthropics/anthropic-sdk-go v1.13.0 h1:Bhbe8sRoDPtipttg8bQYrMCKe2b79+q6rFW1vOKEUKI= +github.com/anthropics/anthropic-sdk-go v1.13.0/go.mod h1:WTz31rIUHUHqai2UslPpw5CwXrQP3geYBioRV4WOLvE= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0= github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4xei5aX110hRiI= From 0765970d4905e4a7b6b3b9a39a4efc1cfe28d11c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 19:42:04 +0000 Subject: [PATCH 016/298] chore: bump jest-fixed-jsdom from 0.0.9 to 0.0.10 in /site (#20114) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [jest-fixed-jsdom](https://github.com/mswjs/jest-fixed-jsdom) from 0.0.9 to 0.0.10.
Release notes

Sourced from jest-fixed-jsdom's releases.

v0.0.10 (2025-08-30)

Bug Fixes

  • using node's global AbortController and AbortSignal (#35) (1e63cde866d5575f42ec5fc4520ebb9c487101e2) @​stevematney
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=jest-fixed-jsdom&package-manager=npm_and_yarn&previous-version=0.0.9&new-version=0.0.10)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- site/package.json | 2 +- site/pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/site/package.json b/site/package.json index d18b8f1d5a0d4..eb3ff1a828ee4 100644 --- a/site/package.json +++ b/site/package.json @@ -164,7 +164,7 @@ "jest": "29.7.0", "jest-canvas-mock": "2.5.2", "jest-environment-jsdom": "29.5.0", - "jest-fixed-jsdom": "0.0.9", + "jest-fixed-jsdom": "0.0.10", "jest-location-mock": "2.0.0", "jest-websocket-mock": "2.5.0", "jest_workaround": "0.1.14", diff --git a/site/pnpm-lock.yaml b/site/pnpm-lock.yaml index e49af3a101ba3..85ab5b0469cd2 100644 --- a/site/pnpm-lock.yaml +++ b/site/pnpm-lock.yaml @@ -402,8 +402,8 @@ importers: specifier: 29.5.0 version: 29.5.0 jest-fixed-jsdom: - specifier: 0.0.9 - version: 0.0.9(jest-environment-jsdom@29.5.0) + specifier: 0.0.10 + version: 0.0.10(jest-environment-jsdom@29.5.0) jest-location-mock: specifier: 2.0.0 version: 2.0.0 @@ -4643,8 +4643,8 @@ packages: resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==, tarball: https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-fixed-jsdom@0.0.9: - resolution: {integrity: sha512-KPfqh2+sn5q2B+7LZktwDcwhCpOpUSue8a1I+BcixWLOQoEVyAjAGfH+IYZGoxZsziNojoHGRTC8xRbB1wDD4g==, tarball: https://registry.npmjs.org/jest-fixed-jsdom/-/jest-fixed-jsdom-0.0.9.tgz} + jest-fixed-jsdom@0.0.10: + resolution: {integrity: sha512-WaEVX+FripJh+Hn/7dysIgqP66h0KT1NNC22NGmNYANExtCoYNk1q2yjwwcdSboBMkkhn0NtmvKad/cmisnCLg==, tarball: https://registry.npmjs.org/jest-fixed-jsdom/-/jest-fixed-jsdom-0.0.10.tgz} engines: {node: '>=18.0.0'} peerDependencies: jest-environment-jsdom: '>=28.0.0' @@ -11066,7 +11066,7 @@ snapshots: jest-mock: 29.7.0 jest-util: 29.7.0 - jest-fixed-jsdom@0.0.9(jest-environment-jsdom@29.5.0): + jest-fixed-jsdom@0.0.10(jest-environment-jsdom@29.5.0): dependencies: jest-environment-jsdom: 29.5.0 From f68495aa6eca9282a83e83c6bcb63d03def3395f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 19:42:17 +0000 Subject: [PATCH 017/298] chore: bump ua-parser-js from 1.0.40 to 1.0.41 in /site (#20116) Bumps [ua-parser-js](https://github.com/faisalman/ua-parser-js) from 1.0.40 to 1.0.41.
Release notes

Sourced from ua-parser-js's releases.

v1.0.41

Version 0.7.41 / 1.0.41

  • Add new browser: Daum, Ladybird
  • Add new device vendor: HMD
  • Add new engine: LibWeb
  • Add new os: Windows IoT, Ubuntu Touch
  • Improve cpu detection: ARM, x86
  • Improve device vendor detection: Apple, Archos, Generic, Google, Honor, Huawei, Infinix, Nvidia, Lenovo, Nokia, OnePlus, Xiaomi
  • Improve device type detection: smarttv, wearables
  • Improve os detection: Linux, Symbian

Full Changelog: https://github.com/faisalman/ua-parser-js/compare/1.0.40...1.0.41

Changelog

Sourced from ua-parser-js's changelog.

Version 0.7.41 / 1.0.41

  • Add new browser: Daum, Ladybird
  • Add new device vendor: HMD
  • Add new engine: LibWeb
  • Add new os: Windows IoT, Ubuntu Touch
  • Improve cpu detection: ARM, x86
  • Improve device vendor detection: Apple, Archos, Generic, Google, Honor, Huawei, Infinix, Nvidia, Lenovo, Nokia, OnePlus, Xiaomi
  • Improve device type detection: smarttv, wearables
  • Improve os detection: Linux, Symbian
Commits
  • 90017c9 Bump version 1.0.41 (mirror of 0.7.41)
  • af825ff Bump version 0.7.41
  • 5925954 Backport - Improve detection for Nokia device & Symbian OS
  • fc668ef Backport - Improve device detection for Generic device: capture its device mo...
  • 0543fb2 Backport - Improve CPU detection: ARM
  • 98f1c00 Backport - Improve device detection for unidentified SmartTV vendors
  • d66c971 Backport - Improve detection for Nvidia devices
  • cbe6038 Backport - Add Daum app user agent (#773)
  • e665bd5 Backport - Add new OS: Ubuntu Touch
  • 20c3040 Backport - Add new device: Apple HomePod
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=ua-parser-js&package-manager=npm_and_yarn&previous-version=1.0.40&new-version=1.0.41)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- site/package.json | 2 +- site/pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/site/package.json b/site/package.json index eb3ff1a828ee4..d44089b0624d6 100644 --- a/site/package.json +++ b/site/package.json @@ -114,7 +114,7 @@ "tailwind-merge": "2.6.0", "tailwindcss-animate": "1.0.7", "tzdata": "1.0.46", - "ua-parser-js": "1.0.40", + "ua-parser-js": "1.0.41", "ufuzzy": "npm:@leeoniya/ufuzzy@1.0.10", "undici": "6.21.3", "unique-names-generator": "4.7.1", diff --git a/site/pnpm-lock.yaml b/site/pnpm-lock.yaml index 85ab5b0469cd2..6d8a1395f0962 100644 --- a/site/pnpm-lock.yaml +++ b/site/pnpm-lock.yaml @@ -257,8 +257,8 @@ importers: specifier: 1.0.46 version: 1.0.46 ua-parser-js: - specifier: 1.0.40 - version: 1.0.40 + specifier: 1.0.41 + version: 1.0.41 ufuzzy: specifier: npm:@leeoniya/ufuzzy@1.0.10 version: '@leeoniya/ufuzzy@1.0.10' @@ -6320,8 +6320,8 @@ packages: tzdata@1.0.46: resolution: {integrity: sha512-zJ4Jv3KCgN3dFeSADpIfHKt9bdIY7TjK3ELaij6oFvyyQBuIZ9LwMlR51vJvMQvRWQ9cS2v92xeZ0sQW4hXCWA==, tarball: https://registry.npmjs.org/tzdata/-/tzdata-1.0.46.tgz} - ua-parser-js@1.0.40: - resolution: {integrity: sha512-z6PJ8Lml+v3ichVojCiB8toQJBuwR42ySM4ezjXIqXK3M0HczmKQ3LF4rhU55PfD99KEEXQG6yb7iOMyvYuHew==, tarball: https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.40.tgz} + ua-parser-js@1.0.41: + resolution: {integrity: sha512-LbBDqdIC5s8iROCUjMbW1f5dJQTEFB1+KO9ogbvlb3nm9n4YHa5p4KTvFPWvh2Hs8gZMBuiB1/8+pdfe/tDPug==, tarball: https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.41.tgz} hasBin: true undici-types@5.26.5: @@ -13298,7 +13298,7 @@ snapshots: tzdata@1.0.46: {} - ua-parser-js@1.0.40: {} + ua-parser-js@1.0.41: {} undici-types@5.26.5: {} From a191ff9aa29e2a36d0eb8a920432a925433986fa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 19:42:26 +0000 Subject: [PATCH 018/298] chore: bump dayjs from 1.11.13 to 1.11.18 in /site (#20115) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [dayjs](https://github.com/iamkun/dayjs) from 1.11.13 to 1.11.18.
Release notes

Sourced from dayjs's releases.

v1.11.18

1.11.18 (2025-08-30)

Bug Fixes

  • error semantic-release dependency (8cfb313)

v1.11.17

1.11.17 (2025-08-29)

Bug Fixes

  • [en-AU] locale use the same ordinal as moment (#2878) (1b95ecd)

v1.11.16

1.11.16 (2025-08-29)

Bug Fixes

  • test release workflow (no code changes) (c38c428)

v1.11.15

1.11.15 (2025-08-28)

Bug Fixes

  • Fix misspellings in Irish or Irish Gaelic [ga] (#2861) (9c14a42)

v1.11.14

1.11.14 (2025-08-27)

Bug Fixes

  • .utcOffset(0, true) result and its clone are different bug (#2505) (fefdcd4)
Changelog

Sourced from dayjs's changelog.

1.11.18 (2025-08-30)

Bug Fixes

  • error semantic-release dependency (8cfb313)

1.11.17 (2025-08-29)

Bug Fixes

  • [en-AU] locale use the same ordinal as moment (#2878) (1b95ecd)

1.11.16 (2025-08-29)

Bug Fixes

  • test release workflow (no code changes) (c38c428)

1.11.15 (2025-08-28)

Bug Fixes

  • Fix misspellings in Irish or Irish Gaelic [ga] (#2861) (9c14a42)

1.11.14 (2025-08-27)

Bug Fixes

  • .utcOffset(0, true) result and its clone are different bug (#2505) (fefdcd4)
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=dayjs&package-manager=npm_and_yarn&previous-version=1.11.13&new-version=1.11.18)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- site/package.json | 2 +- site/pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/site/package.json b/site/package.json index d44089b0624d6..3a974c5b0348e 100644 --- a/site/package.json +++ b/site/package.json @@ -83,7 +83,7 @@ "color-convert": "2.0.1", "cron-parser": "4.9.0", "cronstrue": "2.50.0", - "dayjs": "1.11.13", + "dayjs": "1.11.18", "emoji-mart": "5.6.0", "file-saver": "2.0.5", "formik": "2.4.6", diff --git a/site/pnpm-lock.yaml b/site/pnpm-lock.yaml index 6d8a1395f0962..6fdcf50cfd5b3 100644 --- a/site/pnpm-lock.yaml +++ b/site/pnpm-lock.yaml @@ -164,8 +164,8 @@ importers: specifier: 2.50.0 version: 2.50.0 dayjs: - specifier: 1.11.13 - version: 1.11.13 + specifier: 1.11.18 + version: 1.11.18 emoji-mart: specifier: 5.6.0 version: 5.6.0 @@ -3707,8 +3707,8 @@ packages: resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==, tarball: https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz} engines: {node: '>=0.11'} - dayjs@1.11.13: - resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==, tarball: https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz} + dayjs@1.11.18: + resolution: {integrity: sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==, tarball: https://registry.npmjs.org/dayjs/-/dayjs-1.11.18.tgz} debug@2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==, tarball: https://registry.npmjs.org/debug/-/debug-2.6.9.tgz} @@ -9935,7 +9935,7 @@ snapshots: dependencies: '@babel/runtime': 7.26.10 - dayjs@1.11.13: {} + dayjs@1.11.18: {} debug@2.6.9: dependencies: From 30f81edbceabe11261989fae6509ff76c245f95d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 19:42:34 +0000 Subject: [PATCH 019/298] chore: bump google.golang.org/api from 0.250.0 to 0.251.0 (#20110) Bumps [google.golang.org/api](https://github.com/googleapis/google-api-go-client) from 0.250.0 to 0.251.0.
Release notes

Sourced from google.golang.org/api's releases.

v0.251.0

0.251.0 (2025-09-30)

Features

Changelog

Sourced from google.golang.org/api's changelog.

0.251.0 (2025-09-30)

Features

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=google.golang.org/api&package-manager=go_modules&previous-version=0.250.0&new-version=0.251.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 581f1beaeecff..34967b03a7060 100644 --- a/go.mod +++ b/go.mod @@ -74,7 +74,7 @@ replace github.com/spf13/afero => github.com/aslilac/afero v0.0.0-20250403163713 require ( cdr.dev/slog v1.6.2-0.20250703074222-9df5e0a6c145 - cloud.google.com/go/compute/metadata v0.8.4 + cloud.google.com/go/compute/metadata v0.9.0 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d github.com/adrg/xdg v0.5.0 github.com/ammario/tlru v0.4.0 @@ -206,7 +206,7 @@ require ( golang.org/x/text v0.29.0 golang.org/x/tools v0.37.0 golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da - google.golang.org/api v0.250.0 + google.golang.org/api v0.251.0 google.golang.org/grpc v1.75.1 google.golang.org/protobuf v1.36.9 gopkg.in/DataDog/dd-trace-go.v1 v1.74.0 @@ -454,7 +454,7 @@ require ( google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto v0.0.0-20250715232539-7130f93afb79 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250715232539-7130f93afb79 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250929231259-57b25ae835d4 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect howett.net/plist v1.0.0 // indirect diff --git a/go.sum b/go.sum index c1151e853041d..ecc9506cbe700 100644 --- a/go.sum +++ b/go.sum @@ -184,8 +184,8 @@ cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZ cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= -cloud.google.com/go/compute/metadata v0.8.4 h1:oXMa1VMQBVCyewMIOm3WQsnVd9FbKBtm8reqWRaXnHQ= -cloud.google.com/go/compute/metadata v0.8.4/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= +cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= +cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY= cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w= @@ -2534,8 +2534,8 @@ google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/ google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0= google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg= -google.golang.org/api v0.250.0 h1:qvkwrf/raASj82UegU2RSDGWi/89WkLckn4LuO4lVXM= -google.golang.org/api v0.250.0/go.mod h1:Y9Uup8bDLJJtMzJyQnu+rLRJLA0wn+wTtc6vTlOvfXo= +google.golang.org/api v0.251.0 h1:6lea5nHRT8RUmpy9kkC2PJYnhnDAB13LqrLSVQlMIE8= +google.golang.org/api v0.251.0/go.mod h1:Rwy0lPf/TD7+T2VhYcffCHhyyInyuxGjICxdfLqT7KI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -2680,8 +2680,8 @@ google.golang.org/genproto v0.0.0-20250715232539-7130f93afb79 h1:Nt6z9UHqSlIdIGJ google.golang.org/genproto v0.0.0-20250715232539-7130f93afb79/go.mod h1:kTmlBHMPqR5uCZPBvwa2B18mvubkjyY3CRLI0c6fj0s= google.golang.org/genproto/googleapis/api v0.0.0-20250715232539-7130f93afb79 h1:iOye66xuaAK0WnkPuhQPUFy8eJcmwUXqGGP3om6IxX8= google.golang.org/genproto/googleapis/api v0.0.0-20250715232539-7130f93afb79/go.mod h1:HKJDgKsFUnv5VAGeQjz8kxcgDP0HoE0iZNp0OdZNlhE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 h1:/OQuEa4YWtDt7uQWHd3q3sUMb+QOLQUg1xa8CEsRv5w= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250929231259-57b25ae835d4 h1:i8QOKZfYg6AbGVZzUAY3LrNWCKF8O6zFisU9Wl9RER4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250929231259-57b25ae835d4/go.mod h1:HSkG/KdJWusxU1F6CNrwNDjBMgisKxGnc5dAZfT0mjQ= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= From 29aa1cc57254adf3afb2a77fc8ed2ee7076ff6fd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 19:42:53 +0000 Subject: [PATCH 020/298] chore: bump tailwindcss from 3.4.17 to 3.4.18 in /site (#20117) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [//]: # (dependabot-start) ⚠️ **Dependabot is rebasing this PR** ⚠️ Rebasing might not happen immediately, so don't worry if this takes some time. Note: if you make any changes to this PR yourself, they will take precedence over the rebase. --- [//]: # (dependabot-end) Bumps [tailwindcss](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/tailwindcss) from 3.4.17 to 3.4.18.
Release notes

Sourced from tailwindcss's releases.

v3.4.18

Fixed

  • Improve support for raw supports-[…] queries in arbitrary values (#13605)
  • Fix require.cache error when loaded through a TypeScript file in Node 22.18+ (#18665)
  • Support import.meta.resolve(…) in configs for new enough Node.js versions (#18938)
  • Allow using newer versions of postcss-load-config for better ESM and TypeScript PostCSS config support with the CLI (#18938)
  • Remove irrelevant utility rules when matching important classes (#19030)
Changelog

Sourced from tailwindcss's changelog.

[3.4.18] - 2024-10-01

Fixed

  • Improve support for raw supports-[…] queries in arbitrary values (#13605)
  • Fix require.cache error when loaded through a TypeScript file in Node 22.18+ (#18665)
  • Support import.meta.resolve(…) in configs for new enough Node.js versions (#18938)
  • Allow using newer versions of postcss-load-config for better ESM and TypeScript PostCSS config support with the CLI (#18938)
  • Remove irrelevant utility rules when matching important classes (#19030)
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=tailwindcss&package-manager=npm_and_yarn&previous-version=3.4.17&new-version=3.4.18)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- site/package.json | 2 +- site/pnpm-lock.yaml | 192 +++++++++++++++++++++----------------------- 2 files changed, 93 insertions(+), 101 deletions(-) diff --git a/site/package.json b/site/package.json index 3a974c5b0348e..e90e860dd6a17 100644 --- a/site/package.json +++ b/site/package.json @@ -177,7 +177,7 @@ "ssh2": "1.17.0", "storybook": "9.1.2", "storybook-addon-remix-react-router": "5.0.0", - "tailwindcss": "3.4.17", + "tailwindcss": "3.4.18", "ts-proto": "1.164.0", "typescript": "5.6.3", "vite": "7.1.7", diff --git a/site/pnpm-lock.yaml b/site/pnpm-lock.yaml index 6fdcf50cfd5b3..e2fdf68962550 100644 --- a/site/pnpm-lock.yaml +++ b/site/pnpm-lock.yaml @@ -252,7 +252,7 @@ importers: version: 2.6.0 tailwindcss-animate: specifier: 1.0.7 - version: 1.0.7(tailwindcss@3.4.17(ts-node@10.9.2(@swc/core@1.3.38)(@types/node@20.17.16)(typescript@5.6.3))) + version: 1.0.7(tailwindcss@3.4.18(yaml@2.7.0)) tzdata: specifier: 1.0.46 version: 1.0.46 @@ -283,7 +283,7 @@ importers: version: 2.2.0 '@chromatic-com/storybook': specifier: 4.1.0 - version: 4.1.0(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))) + version: 4.1.0(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0))) '@octokit/types': specifier: 12.3.0 version: 12.3.0 @@ -292,16 +292,16 @@ importers: version: 1.50.1 '@storybook/addon-docs': specifier: 9.1.2 - version: 9.1.2(@types/react@19.1.17)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))) + version: 9.1.2(@types/react@19.1.17)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0))) '@storybook/addon-links': specifier: 9.1.2 - version: 9.1.2(react@19.1.1)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))) + version: 9.1.2(react@19.1.1)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0))) '@storybook/addon-themes': specifier: 9.1.2 - version: 9.1.2(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))) + version: 9.1.2(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0))) '@storybook/react-vite': specifier: 9.1.2 - version: 9.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(rollup@4.52.3)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)))(typescript@5.6.3)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) + version: 9.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(rollup@4.52.3)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0)))(typescript@5.6.3)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0)) '@swc/core': specifier: 1.3.38 version: 1.3.38 @@ -310,7 +310,7 @@ importers: version: 0.2.37(@swc/core@1.3.38) '@tailwindcss/typography': specifier: 0.5.16 - version: 0.5.16(tailwindcss@3.4.17(ts-node@10.9.2(@swc/core@1.3.38)(@types/node@20.17.16)(typescript@5.6.3))) + version: 0.5.16(tailwindcss@3.4.18(yaml@2.7.0)) '@testing-library/jest-dom': specifier: 6.6.3 version: 6.6.3 @@ -379,7 +379,7 @@ importers: version: 9.0.2 '@vitejs/plugin-react': specifier: 5.0.4 - version: 5.0.4(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) + version: 5.0.4(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0)) autoprefixer: specifier: 10.4.20 version: 10.4.20(postcss@8.5.1) @@ -436,13 +436,13 @@ importers: version: 1.17.0 storybook: specifier: 9.1.2 - version: 9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) + version: 9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0)) storybook-addon-remix-react-router: specifier: 5.0.0 - version: 5.0.0(react-dom@19.1.1(react@19.1.1))(react-router@7.8.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))) + version: 5.0.0(react-dom@19.1.1(react@19.1.1))(react-router@7.8.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0))) tailwindcss: - specifier: 3.4.17 - version: 3.4.17(ts-node@10.9.2(@swc/core@1.3.38)(@types/node@20.17.16)(typescript@5.6.3)) + specifier: 3.4.18 + version: 3.4.18(yaml@2.7.0) ts-proto: specifier: 1.164.0 version: 1.164.0 @@ -451,10 +451,10 @@ importers: version: 5.6.3 vite: specifier: 7.1.7 - version: 7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0) + version: 7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0) vite-plugin-checker: specifier: 0.11.0 - version: 0.11.0(@biomejs/biome@2.2.0)(eslint@8.52.0)(optionator@0.9.3)(typescript@5.6.3)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) + version: 0.11.0(@biomejs/biome@2.2.0)(eslint@8.52.0)(optionator@0.9.3)(typescript@5.6.3)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0)) packages: @@ -1321,10 +1321,6 @@ packages: '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==, tarball: https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz} - '@jridgewell/gen-mapping@0.3.8': - resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==, tarball: https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz} - engines: {node: '>=6.0.0'} - '@jridgewell/remapping@2.3.5': resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==, tarball: https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz} @@ -1332,10 +1328,6 @@ packages: resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==, tarball: https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz} engines: {node: '>=6.0.0'} - '@jridgewell/set-array@1.2.1': - resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==, tarball: https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz} - engines: {node: '>=6.0.0'} - '@jridgewell/sourcemap-codec@1.5.0': resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==, tarball: https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz} @@ -5451,8 +5443,8 @@ packages: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==, tarball: https://registry.npmjs.org/pify/-/pify-2.3.0.tgz} engines: {node: '>=0.10.0'} - pirates@4.0.6: - resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==, tarball: https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz} + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==, tarball: https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz} engines: {node: '>= 6'} pkg-dir@4.2.0: @@ -5479,22 +5471,28 @@ packages: peerDependencies: postcss: ^8.0.0 - postcss-js@4.0.1: - resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==, tarball: https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz} + postcss-js@4.1.0: + resolution: {integrity: sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==, tarball: https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz} engines: {node: ^12 || ^14 || >= 16} peerDependencies: postcss: ^8.4.21 - postcss-load-config@4.0.2: - resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==, tarball: https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz} - engines: {node: '>= 14'} + postcss-load-config@6.0.1: + resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==, tarball: https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz} + engines: {node: '>= 18'} peerDependencies: + jiti: '>=1.21.0' postcss: '>=8.0.9' - ts-node: '>=9.0.0' + tsx: ^4.8.1 + yaml: ^2.4.2 peerDependenciesMeta: + jiti: + optional: true postcss: optional: true - ts-node: + tsx: + optional: true + yaml: optional: true postcss-nested@6.2.0: @@ -6163,8 +6161,8 @@ packages: peerDependencies: tailwindcss: '>=3.0.0 || insiders' - tailwindcss@3.4.17: - resolution: {integrity: sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==, tarball: https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz} + tailwindcss@3.4.18: + resolution: {integrity: sha512-6A2rnmW5xZMdw11LYjhcI5846rt9pbLSabY5XPxo+XWdxwZaFEn47Go4NzFiHu9sNNmr/kXivP1vStfvMaK1GQ==, tarball: https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.18.tgz} engines: {node: '>=14.0.0'} hasBin: true @@ -7001,13 +6999,13 @@ snapshots: '@types/tough-cookie': 4.0.5 tough-cookie: 4.1.4 - '@chromatic-com/storybook@4.1.0(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)))': + '@chromatic-com/storybook@4.1.0(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0)))': dependencies: '@neoconfetti/react': 1.0.0 chromatic: 12.2.0 filesize: 10.1.2 jsonfile: 6.1.0 - storybook: 9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) + storybook: 9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0)) strip-ansi: 7.1.0 transitivePeerDependencies: - '@chromatic-com/cypress' @@ -7595,7 +7593,7 @@ snapshots: jest-regex-util: 29.6.3 jest-util: 29.7.0 micromatch: 4.0.8 - pirates: 4.0.6 + pirates: 4.0.7 slash: 3.0.0 write-file-atomic: 4.0.2 transitivePeerDependencies: @@ -7619,12 +7617,12 @@ snapshots: '@types/yargs': 17.0.33 chalk: 4.1.2 - '@joshwooding/vite-plugin-react-docgen-typescript@0.6.1(typescript@5.6.3)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))': + '@joshwooding/vite-plugin-react-docgen-typescript@0.6.1(typescript@5.6.3)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0))': dependencies: glob: 10.4.5 magic-string: 0.30.17 react-docgen-typescript: 2.2.2(typescript@5.6.3) - vite: 7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0) + vite: 7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0) optionalDependencies: typescript: 5.6.3 @@ -7633,12 +7631,6 @@ snapshots: '@jridgewell/sourcemap-codec': 1.5.5 '@jridgewell/trace-mapping': 0.3.31 - '@jridgewell/gen-mapping@0.3.8': - dependencies: - '@jridgewell/set-array': 1.2.1 - '@jridgewell/sourcemap-codec': 1.5.0 - '@jridgewell/trace-mapping': 0.3.25 - '@jridgewell/remapping@2.3.5': dependencies: '@jridgewell/gen-mapping': 0.3.13 @@ -7646,8 +7638,6 @@ snapshots: '@jridgewell/resolve-uri@3.1.2': {} - '@jridgewell/set-array@1.2.1': {} - '@jridgewell/sourcemap-codec@1.5.0': {} '@jridgewell/sourcemap-codec@1.5.5': {} @@ -7655,7 +7645,7 @@ snapshots: '@jridgewell/trace-mapping@0.3.25': dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/sourcemap-codec': 1.5.5 '@jridgewell/trace-mapping@0.3.31': dependencies: @@ -8775,41 +8765,41 @@ snapshots: dependencies: '@sinonjs/commons': 3.0.0 - '@storybook/addon-docs@9.1.2(@types/react@19.1.17)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)))': + '@storybook/addon-docs@9.1.2(@types/react@19.1.17)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0)))': dependencies: '@mdx-js/react': 3.0.1(@types/react@19.1.17)(react@19.1.1) - '@storybook/csf-plugin': 9.1.2(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))) + '@storybook/csf-plugin': 9.1.2(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0))) '@storybook/icons': 1.4.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@storybook/react-dom-shim': 9.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))) + '@storybook/react-dom-shim': 9.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0))) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) - storybook: 9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) + storybook: 9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0)) ts-dedent: 2.2.0 transitivePeerDependencies: - '@types/react' - '@storybook/addon-links@9.1.2(react@19.1.1)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)))': + '@storybook/addon-links@9.1.2(react@19.1.1)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0)))': dependencies: '@storybook/global': 5.0.0 - storybook: 9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) + storybook: 9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0)) optionalDependencies: react: 19.1.1 - '@storybook/addon-themes@9.1.2(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)))': + '@storybook/addon-themes@9.1.2(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0)))': dependencies: - storybook: 9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) + storybook: 9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0)) ts-dedent: 2.2.0 - '@storybook/builder-vite@9.1.2(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)))(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))': + '@storybook/builder-vite@9.1.2(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0)))(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0))': dependencies: - '@storybook/csf-plugin': 9.1.2(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))) - storybook: 9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) + '@storybook/csf-plugin': 9.1.2(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0))) + storybook: 9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0)) ts-dedent: 2.2.0 - vite: 7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0) + vite: 7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0) - '@storybook/csf-plugin@9.1.2(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)))': + '@storybook/csf-plugin@9.1.2(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0)))': dependencies: - storybook: 9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) + storybook: 9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0)) unplugin: 1.5.0 '@storybook/global@5.0.0': {} @@ -8819,39 +8809,39 @@ snapshots: react: 19.1.1 react-dom: 19.1.1(react@19.1.1) - '@storybook/react-dom-shim@9.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)))': + '@storybook/react-dom-shim@9.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0)))': dependencies: react: 19.1.1 react-dom: 19.1.1(react@19.1.1) - storybook: 9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) + storybook: 9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0)) - '@storybook/react-vite@9.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(rollup@4.52.3)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)))(typescript@5.6.3)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))': + '@storybook/react-vite@9.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(rollup@4.52.3)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0)))(typescript@5.6.3)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0))': dependencies: - '@joshwooding/vite-plugin-react-docgen-typescript': 0.6.1(typescript@5.6.3)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) + '@joshwooding/vite-plugin-react-docgen-typescript': 0.6.1(typescript@5.6.3)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0)) '@rollup/pluginutils': 5.0.5(rollup@4.52.3) - '@storybook/builder-vite': 9.1.2(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)))(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) - '@storybook/react': 9.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)))(typescript@5.6.3) + '@storybook/builder-vite': 9.1.2(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0)))(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0)) + '@storybook/react': 9.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0)))(typescript@5.6.3) find-up: 7.0.0 magic-string: 0.30.17 react: 19.1.1 react-docgen: 8.0.0 react-dom: 19.1.1(react@19.1.1) resolve: 1.22.10 - storybook: 9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) + storybook: 9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0)) tsconfig-paths: 4.2.0 - vite: 7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0) + vite: 7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0) transitivePeerDependencies: - rollup - supports-color - typescript - '@storybook/react@9.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)))(typescript@5.6.3)': + '@storybook/react@9.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0)))(typescript@5.6.3)': dependencies: '@storybook/global': 5.0.0 - '@storybook/react-dom-shim': 9.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))) + '@storybook/react-dom-shim': 9.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0))) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) - storybook: 9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) + storybook: 9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0)) optionalDependencies: typescript: 5.6.3 @@ -8907,13 +8897,13 @@ snapshots: '@swc/counter': 0.1.3 jsonc-parser: 3.2.0 - '@tailwindcss/typography@0.5.16(tailwindcss@3.4.17(ts-node@10.9.2(@swc/core@1.3.38)(@types/node@20.17.16)(typescript@5.6.3)))': + '@tailwindcss/typography@0.5.16(tailwindcss@3.4.18(yaml@2.7.0))': dependencies: lodash.castarray: 4.4.0 lodash.isplainobject: 4.0.6 lodash.merge: 4.6.2 postcss-selector-parser: 6.0.10 - tailwindcss: 3.4.17(ts-node@10.9.2(@swc/core@1.3.38)(@types/node@20.17.16)(typescript@5.6.3)) + tailwindcss: 3.4.18(yaml@2.7.0) '@tanstack/query-core@5.77.0': {} @@ -9294,7 +9284,7 @@ snapshots: '@ungap/structured-clone@1.3.0': {} - '@vitejs/plugin-react@5.0.4(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))': + '@vitejs/plugin-react@5.0.4(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0))': dependencies: '@babel/core': 7.28.4 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.4) @@ -9302,7 +9292,7 @@ snapshots: '@rolldown/pluginutils': 1.0.0-beta.38 '@types/babel__core': 7.20.5 react-refresh: 0.17.0 - vite: 7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0) + vite: 7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0) transitivePeerDependencies: - supports-color @@ -9314,14 +9304,14 @@ snapshots: chai: 5.2.1 tinyrainbow: 2.0.0 - '@vitest/mocker@3.2.4(msw@2.4.8(typescript@5.6.3))(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))': + '@vitest/mocker@3.2.4(msw@2.4.8(typescript@5.6.3))(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: msw: 2.4.8(typescript@5.6.3) - vite: 7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0) + vite: 7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0) '@vitest/pretty-format@3.2.4': dependencies: @@ -12316,7 +12306,7 @@ snapshots: pify@2.3.0: {} - pirates@4.0.6: {} + pirates@4.0.7: {} pkg-dir@4.2.0: dependencies: @@ -12339,18 +12329,18 @@ snapshots: read-cache: 1.0.0 resolve: 1.22.10 - postcss-js@4.0.1(postcss@8.5.1): + postcss-js@4.1.0(postcss@8.5.1): dependencies: camelcase-css: 2.0.1 postcss: 8.5.1 - postcss-load-config@4.0.2(postcss@8.5.1)(ts-node@10.9.2(@swc/core@1.3.38)(@types/node@20.17.16)(typescript@5.6.3)): + postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.1)(yaml@2.7.0): dependencies: lilconfig: 3.1.3 - yaml: 2.7.0 optionalDependencies: + jiti: 1.21.7 postcss: 8.5.1 - ts-node: 10.9.2(@swc/core@1.3.38)(@types/node@20.17.16)(typescript@5.6.3) + yaml: 2.7.0 postcss-nested@6.2.0(postcss@8.5.1): dependencies: @@ -12995,24 +12985,24 @@ snapshots: dependencies: internal-slot: 1.0.6 - storybook-addon-remix-react-router@5.0.0(react-dom@19.1.1(react@19.1.1))(react-router@7.8.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0))): + storybook-addon-remix-react-router@5.0.0(react-dom@19.1.1(react@19.1.1))(react-router@7.8.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1)(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0))): dependencies: '@mjackson/form-data-parser': 0.4.0 compare-versions: 6.1.0 react-inspector: 6.0.2(react@19.1.1) react-router: 7.8.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - storybook: 9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) + storybook: 9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0)) optionalDependencies: react: 19.1.1 react-dom: 19.1.1(react@19.1.1) - storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)): + storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0)): dependencies: '@storybook/global': 5.0.0 '@testing-library/jest-dom': 6.6.3 '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.0) '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(msw@2.4.8(typescript@5.6.3))(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)) + '@vitest/mocker': 3.2.4(msw@2.4.8(typescript@5.6.3))(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0)) '@vitest/spy': 3.2.4 better-opn: 3.0.2 esbuild: 0.25.3 @@ -13104,12 +13094,12 @@ snapshots: sucrase@3.35.0: dependencies: - '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/gen-mapping': 0.3.13 commander: 4.1.1 glob: 10.4.5 lines-and-columns: 1.2.4 mz: 2.7.0 - pirates: 4.0.6 + pirates: 4.0.7 ts-interface-checker: 0.1.13 supports-color@7.2.0: @@ -13126,11 +13116,11 @@ snapshots: tailwind-merge@2.6.0: {} - tailwindcss-animate@1.0.7(tailwindcss@3.4.17(ts-node@10.9.2(@swc/core@1.3.38)(@types/node@20.17.16)(typescript@5.6.3))): + tailwindcss-animate@1.0.7(tailwindcss@3.4.18(yaml@2.7.0)): dependencies: - tailwindcss: 3.4.17(ts-node@10.9.2(@swc/core@1.3.38)(@types/node@20.17.16)(typescript@5.6.3)) + tailwindcss: 3.4.18(yaml@2.7.0) - tailwindcss@3.4.17(ts-node@10.9.2(@swc/core@1.3.38)(@types/node@20.17.16)(typescript@5.6.3)): + tailwindcss@3.4.18(yaml@2.7.0): dependencies: '@alloc/quick-lru': 5.2.0 arg: 5.0.2 @@ -13148,14 +13138,15 @@ snapshots: picocolors: 1.1.1 postcss: 8.5.1 postcss-import: 15.1.0(postcss@8.5.1) - postcss-js: 4.0.1(postcss@8.5.1) - postcss-load-config: 4.0.2(postcss@8.5.1)(ts-node@10.9.2(@swc/core@1.3.38)(@types/node@20.17.16)(typescript@5.6.3)) + postcss-js: 4.1.0(postcss@8.5.1) + postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.1)(yaml@2.7.0) postcss-nested: 6.2.0(postcss@8.5.1) postcss-selector-parser: 6.1.2 resolve: 1.22.10 sucrase: 3.35.0 transitivePeerDependencies: - - ts-node + - tsx + - yaml test-exclude@6.0.0: dependencies: @@ -13474,7 +13465,7 @@ snapshots: d3-time: 3.1.0 d3-timer: 3.0.1 - vite-plugin-checker@0.11.0(@biomejs/biome@2.2.0)(eslint@8.52.0)(optionator@0.9.3)(typescript@5.6.3)(vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0)): + vite-plugin-checker@0.11.0(@biomejs/biome@2.2.0)(eslint@8.52.0)(optionator@0.9.3)(typescript@5.6.3)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0)): dependencies: '@babel/code-frame': 7.27.1 chokidar: 4.0.3 @@ -13483,7 +13474,7 @@ snapshots: picomatch: 4.0.3 tiny-invariant: 1.3.3 tinyglobby: 0.2.15 - vite: 7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0) + vite: 7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0) vscode-uri: 3.1.0 optionalDependencies: '@biomejs/biome': 2.2.0 @@ -13491,7 +13482,7 @@ snapshots: optionator: 0.9.3 typescript: 5.6.3 - vite@7.1.7(@types/node@20.17.16)(jiti@2.6.1)(yaml@2.7.0): + vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0): dependencies: esbuild: 0.25.10 fdir: 6.5.0(picomatch@4.0.3) @@ -13502,7 +13493,7 @@ snapshots: optionalDependencies: '@types/node': 20.17.16 fsevents: 2.3.3 - jiti: 2.6.1 + jiti: 1.21.7 yaml: 2.7.0 vscode-uri@3.1.0: {} @@ -13609,7 +13600,8 @@ snapshots: yaml@1.10.2: {} - yaml@2.7.0: {} + yaml@2.7.0: + optional: true yargs-parser@21.1.1: {} From 4cabb9528ecd5ef5493b7f8b53185cb79afc18fe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 19:46:34 +0000 Subject: [PATCH 021/298] chore: bump @types/lodash from 4.17.15 to 4.17.20 in /site (#20122) Bumps [@types/lodash](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/lodash) from 4.17.15 to 4.17.20.
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@types/lodash&package-manager=npm_and_yarn&previous-version=4.17.15&new-version=4.17.20)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- site/package.json | 2 +- site/pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/site/package.json b/site/package.json index e90e860dd6a17..bb2257d277c85 100644 --- a/site/package.json +++ b/site/package.json @@ -143,7 +143,7 @@ "@types/file-saver": "2.0.7", "@types/humanize-duration": "3.27.4", "@types/jest": "29.5.14", - "@types/lodash": "4.17.15", + "@types/lodash": "4.17.20", "@types/node": "20.17.16", "@types/react": "19.1.17", "@types/react-color": "3.0.13", diff --git a/site/pnpm-lock.yaml b/site/pnpm-lock.yaml index e2fdf68962550..20a93530895b6 100644 --- a/site/pnpm-lock.yaml +++ b/site/pnpm-lock.yaml @@ -339,8 +339,8 @@ importers: specifier: 29.5.14 version: 29.5.14 '@types/lodash': - specifier: 4.17.15 - version: 4.17.15 + specifier: 4.17.20 + version: 4.17.20 '@types/node': specifier: 20.17.16 version: 20.17.16 @@ -2916,8 +2916,8 @@ packages: '@types/jsdom@20.0.1': resolution: {integrity: sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==, tarball: https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz} - '@types/lodash@4.17.15': - resolution: {integrity: sha512-w/P33JFeySuhN6JLkysYUK2gEmy9kHHFN7E8ro0tkfmlDOgxBDzWEZ/J8cWA+fHqFevpswDTFZnDx+R9lbL6xw==, tarball: https://registry.npmjs.org/@types/lodash/-/lodash-4.17.15.tgz} + '@types/lodash@4.17.20': + resolution: {integrity: sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==, tarball: https://registry.npmjs.org/@types/lodash/-/lodash-4.17.20.tgz} '@types/mdast@4.0.3': resolution: {integrity: sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==, tarball: https://registry.npmjs.org/@types/mdast/-/mdast-4.0.3.tgz} @@ -9145,7 +9145,7 @@ snapshots: '@types/tough-cookie': 4.0.2 parse5: 7.1.2 - '@types/lodash@4.17.15': {} + '@types/lodash@4.17.20': {} '@types/mdast@4.0.3': dependencies: From cf8d7665e3feeae41082d7004466be182692ae35 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 19:50:53 +0000 Subject: [PATCH 022/298] chore: bump chroma-js from 2.4.2 to 2.6.0 in /site (#20123) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [chroma-js](https://github.com/gka/chroma.js) from 2.4.2 to 2.6.0.
Release notes

Sourced from chroma-js's releases.

v2.6.0

What's Changed

Full Changelog: https://github.com/gka/chroma.js/compare/v2.5.0...v2.6.0

v2.5.0

What's Changed

New Contributors

Full Changelog: https://github.com/gka/chroma.js/compare/v2.3.0...v2.5.0

Changelog

Sourced from chroma-js's changelog.

2.6.0

2.5.0

  • refactored code base to ES6 modules

2.4.0

  • add support for Oklab and Oklch color spaces

2.3.0

  • use binom of degree n in chroma.bezier

2.2.0

  • use Delta e2000 for chroma.deltaE #269

2.0.3

  • hsl2rgb will, like other x2rgb conversions now set the default alpha to 1

2.0.2

  • use a more mangle-safe check for Color class constructor to fix issues with uglifyjs and terser

2.0.1

  • added chroma.valid() for checking if a color can be parsed by chroma.js

2.0.0

  • chroma.js has been ported from CoffeeScript to ES6! This means you can now import parts of chroma in your projects!
  • changed HCG input space from [0..360,0..100,0..100] to [0..360,0..1,0..1] (to be in line with HSL)
  • added new object unpacking (e.g. hsl2rgb({h,s,l}))
  • changed default interpolation to lrgb in mix/interpolate and average.
  • if colors can't be parsed correctly, chroma will now throw Errors instead of silently failing with console.errors

1.4.1

  • chroma.scale() now interprets null as NaN and returns the fallback color. Before it had interpreted null as 0
  • added scale.nodata() to allow customizing the previously hard-coded fallback (aka "no data") color #cccccc

1.4.0

  • color.hex() now automatically sets the mode to 'rgba' if the colors alpha channel is < 1. so chroma('rgba(255,0,0,.5)').hex() will now return "#ff000080" instead of "#ff0000". if this is not what you want, you must explicitly set the mode to rgb using .hex("rgb").
  • bugfix in chroma.average in LRGB mode (#187)
  • chroma.scale now also works with just one color (#180)

1.3.5

  • added LRGB interpolation

1.3.4

  • passing null as mode in scale.colors will return chroma objects

1.3.3

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=chroma-js&package-manager=npm_and_yarn&previous-version=2.4.2&new-version=2.6.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- site/package.json | 2 +- site/pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/site/package.json b/site/package.json index bb2257d277c85..71b790967dcfe 100644 --- a/site/package.json +++ b/site/package.json @@ -76,7 +76,7 @@ "@xterm/xterm": "5.5.0", "ansi-to-html": "0.7.2", "axios": "1.12.0", - "chroma-js": "2.4.2", + "chroma-js": "2.6.0", "class-variance-authority": "0.7.1", "clsx": "2.1.1", "cmdk": "1.0.4", diff --git a/site/pnpm-lock.yaml b/site/pnpm-lock.yaml index 20a93530895b6..1b81ecdeb3be7 100644 --- a/site/pnpm-lock.yaml +++ b/site/pnpm-lock.yaml @@ -143,8 +143,8 @@ importers: specifier: 1.12.0 version: 1.12.0 chroma-js: - specifier: 2.4.2 - version: 2.4.2 + specifier: 2.6.0 + version: 2.6.0 class-variance-authority: specifier: 0.7.1 version: 0.7.1 @@ -3459,8 +3459,8 @@ packages: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==, tarball: https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz} engines: {node: '>= 14.16.0'} - chroma-js@2.4.2: - resolution: {integrity: sha512-U9eDw6+wt7V8z5NncY2jJfZa+hUH8XEj8FQHgFJTrUFnJfXYf4Ml4adI2vXZOjqRDpFWtYVWypDfZwnJ+HIR4A==, tarball: https://registry.npmjs.org/chroma-js/-/chroma-js-2.4.2.tgz} + chroma-js@2.6.0: + resolution: {integrity: sha512-BLHvCB9s8Z1EV4ethr6xnkl/P2YRFOGqfgvuMG/MyCbZPrTA+NeiByY6XvgF0zP4/2deU2CXnWyMa3zu1LqQ3A==, tarball: https://registry.npmjs.org/chroma-js/-/chroma-js-2.6.0.tgz} chromatic@11.29.0: resolution: {integrity: sha512-yisBlntp9hHVj19lIQdpTlcYIXuU9H/DbFuu6tyWHmj6hWT2EtukCCcxYXL78XdQt1vm2GfIrtgtKpj/Rzmo4A==, tarball: https://registry.npmjs.org/chromatic/-/chromatic-11.29.0.tgz} @@ -9727,7 +9727,7 @@ snapshots: dependencies: readdirp: 4.1.2 - chroma-js@2.4.2: {} + chroma-js@2.6.0: {} chromatic@11.29.0: {} From b92a0f428c7574781e8c90f658b1dcc6d7728f05 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 19:51:24 +0000 Subject: [PATCH 023/298] chore: bump autoprefixer from 10.4.20 to 10.4.21 in /site (#20124) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [autoprefixer](https://github.com/postcss/autoprefixer) from 10.4.20 to 10.4.21.
Release notes

Sourced from autoprefixer's releases.

10.4.21

Changelog

Sourced from autoprefixer's changelog.

10.4.21

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=autoprefixer&package-manager=npm_and_yarn&previous-version=10.4.20&new-version=10.4.21)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- site/package.json | 2 +- site/pnpm-lock.yaml | 75 +++++++++++---------------------------------- 2 files changed, 19 insertions(+), 58 deletions(-) diff --git a/site/package.json b/site/package.json index 71b790967dcfe..bba60a5ada644 100644 --- a/site/package.json +++ b/site/package.json @@ -157,7 +157,7 @@ "@types/ua-parser-js": "0.7.36", "@types/uuid": "9.0.2", "@vitejs/plugin-react": "5.0.4", - "autoprefixer": "10.4.20", + "autoprefixer": "10.4.21", "chromatic": "11.29.0", "dpdm": "3.14.0", "express": "4.21.2", diff --git a/site/pnpm-lock.yaml b/site/pnpm-lock.yaml index 1b81ecdeb3be7..3a9307d25701b 100644 --- a/site/pnpm-lock.yaml +++ b/site/pnpm-lock.yaml @@ -381,8 +381,8 @@ importers: specifier: 5.0.4 version: 5.0.4(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0)) autoprefixer: - specifier: 10.4.20 - version: 10.4.20(postcss@8.5.1) + specifier: 10.4.21 + version: 10.4.21(postcss@8.5.1) chromatic: specifier: 11.29.0 version: 11.29.0 @@ -3257,8 +3257,8 @@ packages: asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==, tarball: https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz} - autoprefixer@10.4.20: - resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==, tarball: https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz} + autoprefixer@10.4.21: + resolution: {integrity: sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==, tarball: https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz} engines: {node: ^10 || ^12 || >=14} hasBin: true peerDependencies: @@ -3309,8 +3309,8 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==, tarball: https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz} - baseline-browser-mapping@2.8.9: - resolution: {integrity: sha512-hY/u2lxLrbecMEWSB0IpGzGyDyeoMFQhCvZd2jGFSE5I17Fh01sYUBPCJtkWERw7zrac9+cIghxm/ytJa2X8iA==, tarball: https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.9.tgz} + baseline-browser-mapping@2.8.10: + resolution: {integrity: sha512-uLfgBi+7IBNay8ECBO2mVMGZAc1VgZWEChxm4lv+TobGdG82LnXMjuNGo/BSSZZL4UmkWhxEHP2f5ziLNwGWMA==, tarball: https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.10.tgz} hasBin: true bcrypt-pbkdf@1.0.2: @@ -3338,13 +3338,8 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==, tarball: https://registry.npmjs.org/braces/-/braces-3.0.3.tgz} engines: {node: '>=8'} - browserslist@4.24.2: - resolution: {integrity: sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==, tarball: https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - - browserslist@4.26.2: - resolution: {integrity: sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A==, tarball: https://registry.npmjs.org/browserslist/-/browserslist-4.26.2.tgz} + browserslist@4.26.3: + resolution: {integrity: sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w==, tarball: https://registry.npmjs.org/browserslist/-/browserslist-4.26.3.tgz} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -3397,9 +3392,6 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==, tarball: https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz} engines: {node: '>=10'} - caniuse-lite@1.0.30001717: - resolution: {integrity: sha512-auPpttCq6BDEG8ZAuHJIplGw6GODhjw+/11e7IjpnYCxZcW/ONgPs0KVBJ0d1bY3e2+7PRe5RCLyP+PfwVgkYw==, tarball: https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001717.tgz} - caniuse-lite@1.0.30001746: resolution: {integrity: sha512-eA7Ys/DGw+pnkWWSE/id29f2IcPHVoE8wxtvE5JdvD2V28VTDPy1yEeo11Guz0sJ4ZeGRcm3uaTcAqK1LXaphA==, tarball: https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001746.tgz} @@ -3868,9 +3860,6 @@ packages: electron-to-chromium@1.5.228: resolution: {integrity: sha512-nxkiyuqAn4MJ1QbobwqJILiDtu/jk14hEAWaMiJmNPh1Z+jqoFlBFZjdXwLWGeVSeu9hGLg6+2G9yJaW8rBIFA==, tarball: https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.228.tgz} - electron-to-chromium@1.5.50: - resolution: {integrity: sha512-eMVObiUQ2LdgeO1F/ySTXsvqvxb6ZH2zPGaMYsWzRDdOddUa77tdmI0ltg+L16UpbWdhPmuF3wIQYyQq65WfZw==, tarball: https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.50.tgz} - emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==, tarball: https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz} engines: {node: '>=12'} @@ -5250,9 +5239,6 @@ packages: node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==, tarball: https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz} - node-releases@2.0.18: - resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==, tarball: https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz} - node-releases@2.0.21: resolution: {integrity: sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==, tarball: https://registry.npmjs.org/node-releases/-/node-releases-2.0.21.tgz} @@ -6383,12 +6369,6 @@ packages: unplugin@1.5.0: resolution: {integrity: sha512-9ZdRwbh/4gcm1JTOkp9lAkIDrtOyOxgHmY7cjuwI8L/2RTikMcVG25GsZwNAgRuap3iDw2jeq7eoqtAsz5rW3A==, tarball: https://registry.npmjs.org/unplugin/-/unplugin-1.5.0.tgz} - update-browserslist-db@1.1.1: - resolution: {integrity: sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==, tarball: https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - update-browserslist-db@1.1.3: resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==, tarball: https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz} hasBin: true @@ -6771,7 +6751,7 @@ snapshots: dependencies: '@babel/compat-data': 7.28.4 '@babel/helper-validator-option': 7.27.1 - browserslist: 4.26.2 + browserslist: 4.26.3 lru-cache: 5.1.1 semver: 7.7.2 @@ -9470,10 +9450,10 @@ snapshots: asynckit@0.4.0: {} - autoprefixer@10.4.20(postcss@8.5.1): + autoprefixer@10.4.21(postcss@8.5.1): dependencies: - browserslist: 4.24.2 - caniuse-lite: 1.0.30001717 + browserslist: 4.26.3 + caniuse-lite: 1.0.30001746 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.1.1 @@ -9559,7 +9539,7 @@ snapshots: base64-js@1.5.1: {} - baseline-browser-mapping@2.8.9: {} + baseline-browser-mapping@2.8.10: {} bcrypt-pbkdf@1.0.2: dependencies: @@ -9603,20 +9583,13 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist@4.24.2: - dependencies: - caniuse-lite: 1.0.30001717 - electron-to-chromium: 1.5.50 - node-releases: 2.0.18 - update-browserslist-db: 1.1.1(browserslist@4.24.2) - - browserslist@4.26.2: + browserslist@4.26.3: dependencies: - baseline-browser-mapping: 2.8.9 + baseline-browser-mapping: 2.8.10 caniuse-lite: 1.0.30001746 electron-to-chromium: 1.5.228 node-releases: 2.0.21 - update-browserslist-db: 1.1.3(browserslist@4.26.2) + update-browserslist-db: 1.1.3(browserslist@4.26.3) bser@2.1.1: dependencies: @@ -9667,8 +9640,6 @@ snapshots: camelcase@6.3.0: {} - caniuse-lite@1.0.30001717: {} - caniuse-lite@1.0.30001746: {} case-anything@2.1.13: {} @@ -10079,8 +10050,6 @@ snapshots: electron-to-chromium@1.5.228: {} - electron-to-chromium@1.5.50: {} - emittery@0.13.1: {} emoji-mart@5.6.0: {} @@ -12099,8 +12068,6 @@ snapshots: node-int64@0.4.0: {} - node-releases@2.0.18: {} - node-releases@2.0.21: {} normalize-path@3.0.0: {} @@ -13361,15 +13328,9 @@ snapshots: webpack-sources: 3.2.3 webpack-virtual-modules: 0.5.0 - update-browserslist-db@1.1.1(browserslist@4.24.2): - dependencies: - browserslist: 4.24.2 - escalade: 3.2.0 - picocolors: 1.1.1 - - update-browserslist-db@1.1.3(browserslist@4.26.2): + update-browserslist-db@1.1.3(browserslist@4.26.3): dependencies: - browserslist: 4.26.2 + browserslist: 4.26.3 escalade: 3.2.0 picocolors: 1.1.1 From e89c7cdcef17e1437e96b5c11b67e04177301b21 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 19:51:43 +0000 Subject: [PATCH 024/298] chore: bump @fontsource/ibm-plex-mono from 5.1.1 to 5.2.7 in /site (#20127) Bumps [@fontsource/ibm-plex-mono](https://github.com/fontsource/font-files/tree/HEAD/fonts/google/ibm-plex-mono) from 5.1.1 to 5.2.7.
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@fontsource/ibm-plex-mono&package-manager=npm_and_yarn&previous-version=5.1.1&new-version=5.2.7)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- site/package.json | 2 +- site/pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/site/package.json b/site/package.json index bba60a5ada644..cfa5718b11637 100644 --- a/site/package.json +++ b/site/package.json @@ -43,7 +43,7 @@ "@emotion/styled": "11.14.1", "@fontsource-variable/inter": "5.1.1", "@fontsource/fira-code": "5.2.7", - "@fontsource/ibm-plex-mono": "5.1.1", + "@fontsource/ibm-plex-mono": "5.2.7", "@fontsource/jetbrains-mono": "5.2.5", "@fontsource/source-code-pro": "5.2.5", "@monaco-editor/react": "4.7.0", diff --git a/site/pnpm-lock.yaml b/site/pnpm-lock.yaml index 3a9307d25701b..acc08688be6eb 100644 --- a/site/pnpm-lock.yaml +++ b/site/pnpm-lock.yaml @@ -44,8 +44,8 @@ importers: specifier: 5.2.7 version: 5.2.7 '@fontsource/ibm-plex-mono': - specifier: 5.1.1 - version: 5.1.1 + specifier: 5.2.7 + version: 5.2.7 '@fontsource/jetbrains-mono': specifier: 5.2.5 version: 5.2.5 @@ -1165,8 +1165,8 @@ packages: '@fontsource/fira-code@5.2.7': resolution: {integrity: sha512-tnB9NNund9TwIym8/7DMJe573nlPEQb+fKUV5GL8TBYXjIhDvL0D7mgmNVNQUPhXp+R7RylQeiBdkA4EbOHPGQ==, tarball: https://registry.npmjs.org/@fontsource/fira-code/-/fira-code-5.2.7.tgz} - '@fontsource/ibm-plex-mono@5.1.1': - resolution: {integrity: sha512-1aayqPe/ZkD3MlvqpmOHecfA3f2B8g+fAEkgvcCd3lkPP0pS1T0xG5Zmn2EsJQqr1JURtugPUH+5NqvKyfFZMQ==, tarball: https://registry.npmjs.org/@fontsource/ibm-plex-mono/-/ibm-plex-mono-5.1.1.tgz} + '@fontsource/ibm-plex-mono@5.2.7': + resolution: {integrity: sha512-MKAb8qV+CaiMQn2B0dIi1OV3565NYzp3WN5b4oT6LTkk+F0jR6j0ZN+5BKJiIhffDC3rtBULsYZE65+0018z9w==, tarball: https://registry.npmjs.org/@fontsource/ibm-plex-mono/-/ibm-plex-mono-5.2.7.tgz} '@fontsource/jetbrains-mono@5.2.5': resolution: {integrity: sha512-TPZ9b/uq38RMdrlZZkl0RwN8Ju9JxuqMETrw76pUQFbGtE1QbwQaNsLlnUrACNNBNbd0NZRXiJJSkC8ajPgbew==, tarball: https://registry.npmjs.org/@fontsource/jetbrains-mono/-/jetbrains-mono-5.2.5.tgz} @@ -7330,7 +7330,7 @@ snapshots: '@fontsource/fira-code@5.2.7': {} - '@fontsource/ibm-plex-mono@5.1.1': {} + '@fontsource/ibm-plex-mono@5.2.7': {} '@fontsource/jetbrains-mono@5.2.5': {} From b68c648476f623ef5935865a0bcb1067a782239a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 19:51:57 +0000 Subject: [PATCH 025/298] chore: bump react-resizable-panels from 3.0.3 to 3.0.6 in /site (#20125) Bumps [react-resizable-panels](https://github.com/bvaughn/react-resizable-panels) from 3.0.3 to 3.0.6.
Release notes

Sourced from react-resizable-panels's releases.

3.0.6

  • #517: Fixed Firefox bug that caused resizing to be interrupted unexpected

3.0.5

  • #512: Fixed size precision regression from 2.0.17

3.0.4

  • #503: Support custom cursors
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=react-resizable-panels&package-manager=npm_and_yarn&previous-version=3.0.3&new-version=3.0.6)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- site/package.json | 2 +- site/pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/site/package.json b/site/package.json index cfa5718b11637..d3c30a7a4dce7 100644 --- a/site/package.json +++ b/site/package.json @@ -101,7 +101,7 @@ "react-dom": "19.1.1", "react-markdown": "9.1.0", "react-query": "npm:@tanstack/react-query@5.77.0", - "react-resizable-panels": "3.0.3", + "react-resizable-panels": "3.0.6", "react-router": "7.8.0", "react-syntax-highlighter": "15.6.1", "react-textarea-autosize": "8.5.9", diff --git a/site/pnpm-lock.yaml b/site/pnpm-lock.yaml index acc08688be6eb..7d88aef2ab9b7 100644 --- a/site/pnpm-lock.yaml +++ b/site/pnpm-lock.yaml @@ -218,8 +218,8 @@ importers: specifier: npm:@tanstack/react-query@5.77.0 version: '@tanstack/react-query@5.77.0(react@19.1.1)' react-resizable-panels: - specifier: 3.0.3 - version: 3.0.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + specifier: 3.0.6 + version: 3.0.6(react-dom@19.1.1(react@19.1.1))(react@19.1.1) react-router: specifier: 7.8.0 version: 7.8.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) @@ -5685,8 +5685,8 @@ packages: '@types/react': optional: true - react-resizable-panels@3.0.3: - resolution: {integrity: sha512-7HA8THVBHTzhDK4ON0tvlGXyMAJN1zBeRpuyyremSikgYh2ku6ltD7tsGQOcXx4NKPrZtYCm/5CBr+dkruTGQw==, tarball: https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-3.0.3.tgz} + react-resizable-panels@3.0.6: + resolution: {integrity: sha512-b3qKHQ3MLqOgSS+FRYKapNkJZf5EQzuf6+RLiq1/IlTHw99YrZ2NJZLk4hQIzTnnIkRg2LUqyVinu6YWWpUYew==, tarball: https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-3.0.6.tgz} peerDependencies: react: ^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc react-dom: ^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc @@ -12544,7 +12544,7 @@ snapshots: optionalDependencies: '@types/react': 19.1.17 - react-resizable-panels@3.0.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + react-resizable-panels@3.0.6(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: react: 19.1.1 react-dom: 19.1.1(react@19.1.1) From f05ccfe525763e8abbd3baf64375c6cc60cd9b56 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 19:57:46 +0000 Subject: [PATCH 026/298] chore: bump postcss from 8.5.1 to 8.5.6 in /site (#20113) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [postcss](https://github.com/postcss/postcss) from 8.5.1 to 8.5.6.
Release notes

Sourced from postcss's releases.

8.5.6

  • Fixed ContainerWithChildren type discriminating (by @​Goodwine).

8.5.5

  • Fixed package.jsonexports compatibility with some tools (by @​JounQin).

8.5.4

8.5.3

8.5.2

Changelog

Sourced from postcss's changelog.

8.5.6

  • Fixed ContainerWithChildren type discriminating (by @​Goodwine).

8.5.5

  • Fixed package.jsonexports compatibility with some tools (by @​JounQin).

8.5.4

8.5.3

8.5.2

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=postcss&package-manager=npm_and_yarn&previous-version=8.5.1&new-version=8.5.6)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- site/package.json | 2 +- site/pnpm-lock.yaml | 53 +++++++++++++++------------------------------ 2 files changed, 19 insertions(+), 36 deletions(-) diff --git a/site/package.json b/site/package.json index d3c30a7a4dce7..500bc7d41d539 100644 --- a/site/package.json +++ b/site/package.json @@ -170,7 +170,7 @@ "jest_workaround": "0.1.14", "knip": "5.64.1", "msw": "2.4.8", - "postcss": "8.5.1", + "postcss": "8.5.6", "protobufjs": "7.4.0", "rollup-plugin-visualizer": "5.14.0", "rxjs": "7.8.1", diff --git a/site/pnpm-lock.yaml b/site/pnpm-lock.yaml index 7d88aef2ab9b7..072598486cbab 100644 --- a/site/pnpm-lock.yaml +++ b/site/pnpm-lock.yaml @@ -382,7 +382,7 @@ importers: version: 5.0.4(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0)) autoprefixer: specifier: 10.4.21 - version: 10.4.21(postcss@8.5.1) + version: 10.4.21(postcss@8.5.6) chromatic: specifier: 11.29.0 version: 11.29.0 @@ -420,8 +420,8 @@ importers: specifier: 2.4.8 version: 2.4.8(typescript@5.6.3) postcss: - specifier: 8.5.1 - version: 8.5.1 + specifier: 8.5.6 + version: 8.5.6 protobufjs: specifier: 7.4.0 version: 7.4.0 @@ -5219,11 +5219,6 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - nanoid@3.3.8: - resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==, tarball: https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - napi-postinstall@0.3.3: resolution: {integrity: sha512-uTp172LLXSxuSYHv/kou+f6KW3SMppU9ivthaVTXian9sOt3XM/zHYHpRZiLgQoxeWfYUnslNWQHF1+G71xcow==, tarball: https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.3.tgz} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} @@ -5498,10 +5493,6 @@ packages: postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==, tarball: https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz} - postcss@8.5.1: - resolution: {integrity: sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==, tarball: https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz} - engines: {node: ^10 || ^12 || >=14} - postcss@8.5.6: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==, tarball: https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz} engines: {node: ^10 || ^12 || >=14} @@ -9450,14 +9441,14 @@ snapshots: asynckit@0.4.0: {} - autoprefixer@10.4.21(postcss@8.5.1): + autoprefixer@10.4.21(postcss@8.5.6): dependencies: browserslist: 4.26.3 caniuse-lite: 1.0.30001746 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.1.1 - postcss: 8.5.1 + postcss: 8.5.6 postcss-value-parser: 4.2.0 available-typed-arrays@1.0.7: @@ -12058,8 +12049,6 @@ snapshots: nanoid@3.3.11: {} - nanoid@3.3.8: {} - napi-postinstall@0.3.3: {} natural-compare@1.4.0: {} @@ -12289,29 +12278,29 @@ snapshots: possible-typed-array-names@1.0.0: {} - postcss-import@15.1.0(postcss@8.5.1): + postcss-import@15.1.0(postcss@8.5.6): dependencies: - postcss: 8.5.1 + postcss: 8.5.6 postcss-value-parser: 4.2.0 read-cache: 1.0.0 resolve: 1.22.10 - postcss-js@4.1.0(postcss@8.5.1): + postcss-js@4.1.0(postcss@8.5.6): dependencies: camelcase-css: 2.0.1 - postcss: 8.5.1 + postcss: 8.5.6 - postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.1)(yaml@2.7.0): + postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.6)(yaml@2.7.0): dependencies: lilconfig: 3.1.3 optionalDependencies: jiti: 1.21.7 - postcss: 8.5.1 + postcss: 8.5.6 yaml: 2.7.0 - postcss-nested@6.2.0(postcss@8.5.1): + postcss-nested@6.2.0(postcss@8.5.6): dependencies: - postcss: 8.5.1 + postcss: 8.5.6 postcss-selector-parser: 6.1.2 postcss-selector-parser@6.0.10: @@ -12326,12 +12315,6 @@ snapshots: postcss-value-parser@4.2.0: {} - postcss@8.5.1: - dependencies: - nanoid: 3.3.8 - picocolors: 1.1.1 - source-map-js: 1.2.1 - postcss@8.5.6: dependencies: nanoid: 3.3.11 @@ -13103,11 +13086,11 @@ snapshots: normalize-path: 3.0.0 object-hash: 3.0.0 picocolors: 1.1.1 - postcss: 8.5.1 - postcss-import: 15.1.0(postcss@8.5.1) - postcss-js: 4.1.0(postcss@8.5.1) - postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.1)(yaml@2.7.0) - postcss-nested: 6.2.0(postcss@8.5.1) + postcss: 8.5.6 + postcss-import: 15.1.0(postcss@8.5.6) + postcss-js: 4.1.0(postcss@8.5.6) + postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.6)(yaml@2.7.0) + postcss-nested: 6.2.0(postcss@8.5.6) postcss-selector-parser: 6.1.2 resolve: 1.22.10 sucrase: 3.35.0 From 0993dcfef7e80d442aa2f76aaedaf97187e5e594 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 19:57:55 +0000 Subject: [PATCH 027/298] chore: bump monaco-editor from 0.52.2 to 0.53.0 in /site (#20120) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [monaco-editor](https://github.com/microsoft/monaco-editor) from 0.52.2 to 0.53.0.
Release notes

Sourced from monaco-editor's releases.

v0.53.0

Changes:

  • #4980: sets node version
  • #4979: v0.53.0
  • #4978: updates changelog
  • #4975: Fixes worker sandbox problems
  • #4969: Implements language selection
  • #4967: adds amd and firefox/webkit to smoke tests
  • #4966: Fixes AMD web worker loading
  • #4965: Updates monaco-editor-core dependency & fixes basic-languages amd file
  • #4964: Run npm run playwright-install
  • #4963: Hediet/b/successful mosquito
  • #4962: Fixes node version
  • #4950: ESM Progress
  • #4913: Bump webpack-dev-server from 4.10.0 to 5.2.1 in /samples
  • #4915: Add snowflake sql keywords
  • #4895: Fixes microsoft/monaco-editor#4799
  • #4742: Update webpack plugin to support module workers
  • #4824: Fix CI and website workflows
  • #4747: Engineering - add dependsOn field
  • #4734: Bump express from 4.19.2 to 4.21.1 in /website
  • #4616: Bump requirejs from 2.3.6 to 2.3.7
  • #4668: Bump micromatch from 4.0.5 to 4.0.8 in /website
  • #4726: Bump http-proxy-middleware from 2.0.6 to 2.0.7 in /website
  • #4595: Bump ws from 8.8.1 to 8.18.0 in /samples
  • #4697: Bump body-parser and express in /samples
  • #4696: Bump rollup from 2.79.1 to 2.79.2

This list of changes was auto generated.

v0.53.0-rc2

No release notes provided.

v0.53.0-dev-20250908

No release notes provided.

v0.53.0-dev-20250907

No release notes provided.

v0.53.0-dev-20250906

Changes:

  • #4978: updates changelog

... (truncated)

Changelog

Sourced from monaco-editor's changelog.

[0.53.0]

  • :warning: This release deprecates the AMD build and ships with significant changes of the AMD build. The AMD build will still be shipped for a while, but we don't offer support for it anymore. Please migrate to the ESM build.

New Features

  • Next Edit Suggestion support.
  • Scroll On Middle Click
  • Edit Context Support

Breaking Changes

  • Internal AMD modules are no longer accessible. Accessing internal AMD modules was never supported. While this is still possible in the ESM build, we don't encourage this usage pattern.
  • The browser-script-editor scenario for unbundled synchronous script import and editor creation no longer works. Instead, a the ESM build should be used with a bundler, such as vite or webpack.
  • Custom AMD workers don't work anymore out of the box.

[0.52.0]

  • Comment added inside of IModelContentChangedEvent

[0.51.0]

  • New fields IEditorOptions.placeholder and IEditorOptions.compactMode
  • New fields IGotoLocationOptions.multipleTests and IGotoLocationOptions.alternativeTestsCommand
  • New field IInlineEditOptions.backgroundColoring
  • New experimental field IEditorOptions.experimental.useTrueInlineView
  • New options CommentThreadRevealOptions for comments

Contributions to monaco-editor:

[0.50.0]

  • New field IEditorMinimapOptions.sectionHeaderLetterSpacing
  • New field IOverlayWidgetPosition.stackOridinal
  • New field EmitOutput.diagnostics
  • New event IOverlayWidget.onDidLayout
  • New events ICodeEditor.onBeginUpdate and ICodeEditor.onEndUpdate
  • HoverVerbosityRequest.action -> HoverVerbosityRequest.verbosityDelta
  • MultiDocumentHighlightProvider.selector changed from LanguageFilter to LanguageSelector
  • New optional parameters in getEmitOutput: emitOnlyDtsFiles and forceDtsEmit

Contributions to monaco-editor:

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=monaco-editor&package-manager=npm_and_yarn&previous-version=0.52.2&new-version=0.53.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- site/package.json | 2 +- site/pnpm-lock.yaml | 23 +++++++++++++++-------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/site/package.json b/site/package.json index 500bc7d41d539..660227b1373a1 100644 --- a/site/package.json +++ b/site/package.json @@ -92,7 +92,7 @@ "jszip": "3.10.1", "lodash": "4.17.21", "lucide-react": "0.474.0", - "monaco-editor": "0.52.2", + "monaco-editor": "0.53.0", "pretty-bytes": "6.1.1", "react": "19.1.1", "react-color": "2.19.3", diff --git a/site/pnpm-lock.yaml b/site/pnpm-lock.yaml index 072598486cbab..068bc1959648e 100644 --- a/site/pnpm-lock.yaml +++ b/site/pnpm-lock.yaml @@ -54,7 +54,7 @@ importers: version: 5.2.5 '@monaco-editor/react': specifier: 4.7.0 - version: 4.7.0(monaco-editor@0.52.2)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + version: 4.7.0(monaco-editor@0.53.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@mui/icons-material': specifier: 5.18.0 version: 5.18.0(@mui/material@5.18.0(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react@19.1.1))(@types/react@19.1.17)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@types/react@19.1.17)(react@19.1.1) @@ -191,8 +191,8 @@ importers: specifier: 0.474.0 version: 0.474.0(react@19.1.1) monaco-editor: - specifier: 0.52.2 - version: 0.52.2 + specifier: 0.53.0 + version: 0.53.0 pretty-bytes: specifier: 6.1.1 version: 6.1.1 @@ -3031,6 +3031,9 @@ packages: '@types/tough-cookie@4.0.5': resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==, tarball: https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz} + '@types/trusted-types@1.0.6': + resolution: {integrity: sha512-230RC8sFeHoT6sSUlRO6a8cAnclO06eeiq1QDfiv2FGCLWFvvERWgwIQD4FWqD9A69BN7Lzee4OXwoMVnnsWDw==, tarball: https://registry.npmjs.org/@types/trusted-types/-/trusted-types-1.0.6.tgz} + '@types/ua-parser-js@0.7.36': resolution: {integrity: sha512-N1rW+njavs70y2cApeIw1vLMYXRwfBy+7trgavGuuTfOd7j1Yh7QTRc/yqsPl6ncokt72ZXuxEU0PiCp9bSwNQ==, tarball: https://registry.npmjs.org/@types/ua-parser-js/-/ua-parser-js-0.7.36.tgz} @@ -5182,8 +5185,8 @@ packages: resolution: {integrity: sha512-qxBgB7Qa2sEQgHFjj0dSigq7fX4k6Saisd5Nelwp2q8mlbAFh5dHV9JTTlF8viYJLSSWgMCZFUom8PJcMNBoJw==, tarball: https://registry.npmjs.org/mock-socket/-/mock-socket-9.3.1.tgz} engines: {node: '>= 8'} - monaco-editor@0.52.2: - resolution: {integrity: sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ==, tarball: https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.52.2.tgz} + monaco-editor@0.53.0: + resolution: {integrity: sha512-0WNThgC6CMWNXXBxTbaYYcunj08iB5rnx4/G56UOPeL9UVIUGGHA1GR0EWIh9Ebabj7NpCRawQ5b0hfN1jQmYQ==, tarball: https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.53.0.tgz} moo-color@1.0.3: resolution: {integrity: sha512-i/+ZKXMDf6aqYtBhuOcej71YSlbjT3wCO/4H1j8rPvxDJEifdwgg5MaFyu6iYAT8GBZJg2z0dkgK4YMzvURALQ==, tarball: https://registry.npmjs.org/moo-color/-/moo-color-1.0.3.tgz} @@ -7651,10 +7654,10 @@ snapshots: dependencies: state-local: 1.0.7 - '@monaco-editor/react@4.7.0(monaco-editor@0.52.2)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@monaco-editor/react@4.7.0(monaco-editor@0.53.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: '@monaco-editor/loader': 1.5.0 - monaco-editor: 0.52.2 + monaco-editor: 0.53.0 react: 19.1.1 react-dom: 19.1.1(react@19.1.1) @@ -9229,6 +9232,8 @@ snapshots: '@types/tough-cookie@4.0.5': {} + '@types/trusted-types@1.0.6': {} + '@types/ua-parser-js@0.7.36': {} '@types/unist@2.0.11': {} @@ -12004,7 +12009,9 @@ snapshots: mock-socket@9.3.1: {} - monaco-editor@0.52.2: {} + monaco-editor@0.53.0: + dependencies: + '@types/trusted-types': 1.0.6 moo-color@1.0.3: dependencies: From ebcfae27a2b6cca88a10d37a21f0e1b5b8f90b78 Mon Sep 17 00:00:00 2001 From: Asher Date: Wed, 1 Oct 2025 13:39:45 -0800 Subject: [PATCH 028/298] feat: add task create, list, status, and delete MCP tools (#19901) --- coderd/database/dbfake/dbfake.go | 27 +++ codersdk/toolsdk/toolsdk.go | 253 +++++++++++++++++++++- codersdk/toolsdk/toolsdk_test.go | 356 +++++++++++++++++++++++++++++++ 3 files changed, 633 insertions(+), 3 deletions(-) diff --git a/coderd/database/dbfake/dbfake.go b/coderd/database/dbfake/dbfake.go index 6d99005fb3334..a4742a09fff6c 100644 --- a/coderd/database/dbfake/dbfake.go +++ b/coderd/database/dbfake/dbfake.go @@ -24,6 +24,7 @@ import ( "github.com/coder/coder/v2/coderd/rbac" "github.com/coder/coder/v2/coderd/telemetry" "github.com/coder/coder/v2/coderd/wspubsub" + "github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/provisionersdk" sdkproto "github.com/coder/coder/v2/provisionersdk/proto" ) @@ -55,6 +56,7 @@ type WorkspaceBuildBuilder struct { params []database.WorkspaceBuildParameter agentToken string dispo workspaceBuildDisposition + taskAppID uuid.UUID } type workspaceBuildDisposition struct { @@ -117,6 +119,23 @@ func (b WorkspaceBuildBuilder) WithAgent(mutations ...func([]*sdkproto.Agent) [] return b } +func (b WorkspaceBuildBuilder) WithTask() WorkspaceBuildBuilder { + //nolint: revive // returns modified struct + b.taskAppID = uuid.New() + return b.Params(database.WorkspaceBuildParameter{ + Name: codersdk.AITaskPromptParameterName, + Value: "list me", + }).WithAgent(func(a []*sdkproto.Agent) []*sdkproto.Agent { + a[0].Apps = []*sdkproto.App{ + { + Id: b.taskAppID.String(), + Slug: "vcode", + }, + } + return a + }) +} + func (b WorkspaceBuildBuilder) Starting() WorkspaceBuildBuilder { //nolint: revive // returns modified struct b.dispo.starting = true @@ -134,6 +153,14 @@ func (b WorkspaceBuildBuilder) Do() WorkspaceResponse { b.seed.ID = uuid.New() b.seed.JobID = jobID + if b.taskAppID != uuid.Nil { + b.seed.HasAITask = sql.NullBool{ + Bool: true, + Valid: true, + } + b.seed.AITaskSidebarAppID = uuid.NullUUID{UUID: b.taskAppID, Valid: true} + } + resp := WorkspaceResponse{ AgentToken: b.agentToken, } diff --git a/codersdk/toolsdk/toolsdk.go b/codersdk/toolsdk/toolsdk.go index acc7a9b10292a..63e17c2f039cd 100644 --- a/codersdk/toolsdk/toolsdk.go +++ b/codersdk/toolsdk/toolsdk.go @@ -50,6 +50,10 @@ const ( ToolNameWorkspaceEditFile = "coder_workspace_edit_file" ToolNameWorkspaceEditFiles = "coder_workspace_edit_files" ToolNameWorkspacePortForward = "coder_workspace_port_forward" + ToolNameCreateTask = "coder_create_task" + ToolNameDeleteTask = "coder_delete_task" + ToolNameListTasks = "coder_list_tasks" + ToolNameGetTaskStatus = "coder_get_task_status" ) func NewDeps(client *codersdk.Client, opts ...func(*Deps)) (Deps, error) { @@ -223,6 +227,10 @@ var All = []GenericTool{ WorkspaceEditFile.Generic(), WorkspaceEditFiles.Generic(), WorkspacePortForward.Generic(), + CreateTask.Generic(), + DeleteTask.Generic(), + ListTasks.Generic(), + GetTaskStatus.Generic(), } type ReportTaskArgs struct { @@ -344,7 +352,7 @@ is provisioned correctly and the agent can connect to the control plane. Properties: map[string]any{ "user": map[string]any{ "type": "string", - "description": "Username or ID of the user to create the workspace for. Use the `me` keyword to create a workspace for the authenticated user.", + "description": userDescription("create a workspace"), }, "template_version_id": map[string]any{ "type": "string", @@ -1393,8 +1401,6 @@ type WorkspaceLSResponse struct { Contents []WorkspaceLSFile `json:"contents"` } -const workspaceDescription = "The workspace name in the format [owner/]workspace[.agent]. If an owner is not specified, the authenticated user is used." - var WorkspaceLS = Tool[WorkspaceLSArgs, WorkspaceLSResponse]{ Tool: aisdk.Tool{ Name: ToolNameWorkspaceLS, @@ -1750,6 +1756,237 @@ var WorkspacePortForward = Tool[WorkspacePortForwardArgs, WorkspacePortForwardRe }, } +type CreateTaskArgs struct { + Input string `json:"input"` + TemplateVersionID string `json:"template_version_id"` + TemplateVersionPresetID string `json:"template_version_preset_id"` + User string `json:"user"` +} + +var CreateTask = Tool[CreateTaskArgs, codersdk.Task]{ + Tool: aisdk.Tool{ + Name: ToolNameCreateTask, + Description: `Create a task.`, + Schema: aisdk.Schema{ + Properties: map[string]any{ + "input": map[string]any{ + "type": "string", + "description": "Input/prompt for the task.", + }, + "template_version_id": map[string]any{ + "type": "string", + "description": "ID of the template version to create the task from.", + }, + "template_version_preset_id": map[string]any{ + "type": "string", + "description": "Optional ID of the template version preset to create the task from.", + }, + "user": map[string]any{ + "type": "string", + "description": userDescription("create a task"), + }, + }, + Required: []string{"input", "template_version_id"}, + }, + }, + UserClientOptional: true, + Handler: func(ctx context.Context, deps Deps, args CreateTaskArgs) (codersdk.Task, error) { + if args.Input == "" { + return codersdk.Task{}, xerrors.New("input is required") + } + + tvID, err := uuid.Parse(args.TemplateVersionID) + if err != nil { + return codersdk.Task{}, xerrors.New("template_version_id must be a valid UUID") + } + + var tvPresetID uuid.UUID + if args.TemplateVersionPresetID != "" { + tvPresetID, err = uuid.Parse(args.TemplateVersionPresetID) + if err != nil { + return codersdk.Task{}, xerrors.New("template_version_preset_id must be a valid UUID") + } + } + + if args.User == "" { + args.User = codersdk.Me + } + + expClient := codersdk.NewExperimentalClient(deps.coderClient) + task, err := expClient.CreateTask(ctx, args.User, codersdk.CreateTaskRequest{ + Input: args.Input, + TemplateVersionID: tvID, + TemplateVersionPresetID: tvPresetID, + }) + if err != nil { + return codersdk.Task{}, xerrors.Errorf("create task: %w", err) + } + + return task, nil + }, +} + +type DeleteTaskArgs struct { + TaskID string `json:"task_id"` +} + +var DeleteTask = Tool[DeleteTaskArgs, codersdk.Response]{ + Tool: aisdk.Tool{ + Name: ToolNameDeleteTask, + Description: `Delete a task.`, + Schema: aisdk.Schema{ + Properties: map[string]any{ + "task_id": map[string]any{ + "type": "string", + "description": taskIDDescription("delete"), + }, + }, + Required: []string{"task_id"}, + }, + }, + UserClientOptional: true, + Handler: func(ctx context.Context, deps Deps, args DeleteTaskArgs) (codersdk.Response, error) { + if args.TaskID == "" { + return codersdk.Response{}, xerrors.New("task_id is required") + } + + expClient := codersdk.NewExperimentalClient(deps.coderClient) + + var owner string + id, err := uuid.Parse(args.TaskID) + if err == nil { + task, err := expClient.TaskByID(ctx, id) + if err != nil { + return codersdk.Response{}, xerrors.Errorf("get task %q: %w", args.TaskID, err) + } + owner = task.OwnerName + } else { + ws, err := normalizedNamedWorkspace(ctx, deps.coderClient, args.TaskID) + if err != nil { + return codersdk.Response{}, xerrors.Errorf("get task workspace %q: %w", args.TaskID, err) + } + owner = ws.OwnerName + id = ws.ID + } + + err = expClient.DeleteTask(ctx, owner, id) + if err != nil { + return codersdk.Response{}, xerrors.Errorf("delete task: %w", err) + } + + return codersdk.Response{ + Message: "Task deleted successfully", + }, nil + }, +} + +type ListTasksArgs struct { + Status string `json:"status"` + User string `json:"user"` +} + +type ListTasksResponse struct { + Tasks []codersdk.Task `json:"tasks"` +} + +var ListTasks = Tool[ListTasksArgs, ListTasksResponse]{ + Tool: aisdk.Tool{ + Name: ToolNameListTasks, + Description: `List tasks.`, + Schema: aisdk.Schema{ + Properties: map[string]any{ + "status": map[string]any{ + "type": "string", + "description": "Optional filter by task status.", + }, + "user": map[string]any{ + "type": "string", + "description": userDescription("list tasks"), + }, + }, + Required: []string{}, + }, + }, + UserClientOptional: true, + Handler: func(ctx context.Context, deps Deps, args ListTasksArgs) (ListTasksResponse, error) { + if args.User == "" { + args.User = codersdk.Me + } + + expClient := codersdk.NewExperimentalClient(deps.coderClient) + tasks, err := expClient.Tasks(ctx, &codersdk.TasksFilter{ + Owner: args.User, + Status: args.Status, + }) + if err != nil { + return ListTasksResponse{}, xerrors.Errorf("list tasks: %w", err) + } + + return ListTasksResponse{ + Tasks: tasks, + }, nil + }, +} + +type GetTaskStatusArgs struct { + TaskID string `json:"task_id"` +} + +type GetTaskStatusResponse struct { + Status codersdk.WorkspaceStatus `json:"status"` + State *codersdk.TaskStateEntry `json:"state"` +} + +var GetTaskStatus = Tool[GetTaskStatusArgs, GetTaskStatusResponse]{ + Tool: aisdk.Tool{ + Name: ToolNameGetTaskStatus, + Description: `Get the status of a task.`, + Schema: aisdk.Schema{ + Properties: map[string]any{ + "task_id": map[string]any{ + "type": "string", + "description": taskIDDescription("get"), + }, + }, + Required: []string{"task_id"}, + }, + }, + UserClientOptional: true, + Handler: func(ctx context.Context, deps Deps, args GetTaskStatusArgs) (GetTaskStatusResponse, error) { + if args.TaskID == "" { + return GetTaskStatusResponse{}, xerrors.New("task_id is required") + } + + expClient := codersdk.NewExperimentalClient(deps.coderClient) + + id, err := uuid.Parse(args.TaskID) + if err != nil { + ws, err := normalizedNamedWorkspace(ctx, deps.coderClient, args.TaskID) + if err != nil { + return GetTaskStatusResponse{}, xerrors.Errorf("get task workspace %q: %w", args.TaskID, err) + } + id = ws.ID + } + + task, err := expClient.TaskByID(ctx, id) + if err != nil { + return GetTaskStatusResponse{}, xerrors.Errorf("get task %q: %w", args.TaskID, err) + } + + return GetTaskStatusResponse{ + Status: task.Status, + State: task.CurrentState, + }, nil + }, +} + +// normalizedNamedWorkspace normalizes the workspace name before getting the +// workspace by name. +func normalizedNamedWorkspace(ctx context.Context, client *codersdk.Client, name string) (codersdk.Workspace, error) { + // Maybe namedWorkspace should itself call NormalizeWorkspaceInput? + return namedWorkspace(ctx, client, NormalizeWorkspaceInput(name)) +} + // NormalizeWorkspaceInput converts workspace name input to standard format. // Handles the following input formats: // - workspace → workspace @@ -1810,3 +2047,13 @@ func newAgentConn(ctx context.Context, client *codersdk.Client, workspace string } return conn, nil } + +const workspaceDescription = "The workspace name in the format [owner/]workspace[.agent]. If an owner is not specified, the authenticated user is used." + +func taskIDDescription(action string) string { + return fmt.Sprintf("ID or workspace identifier in the format [owner/]workspace[.agent] for the task to %s. If an owner is not specified, the authenticated user is used.", action) +} + +func userDescription(action string) string { + return fmt.Sprintf("Username or ID of the user for which to %s. Omit or use the `me` keyword to %s for the authenticated user.", action, action) +} diff --git a/codersdk/toolsdk/toolsdk_test.go b/codersdk/toolsdk/toolsdk_test.go index f89f22e0089d7..8e0cc09fff25d 100644 --- a/codersdk/toolsdk/toolsdk_test.go +++ b/codersdk/toolsdk/toolsdk_test.go @@ -2,6 +2,7 @@ package toolsdk_test import ( "context" + "database/sql" "encoding/json" "fmt" "os" @@ -791,6 +792,361 @@ func TestTools(t *testing.T) { }) } }) + + t.Run("WorkspaceCreateTask", func(t *testing.T) { + t.Parallel() + + presetID := uuid.New() + // nolint:gocritic // This is in a test package and does not end up in the build + aiTV := dbfake.TemplateVersion(t, store).Seed(database.TemplateVersion{ + OrganizationID: owner.OrganizationID, + CreatedBy: member.ID, + HasAITask: sql.NullBool{ + Bool: true, + Valid: true, + }, + }).Preset(database.TemplateVersionPreset{ + ID: presetID, + DesiredInstances: sql.NullInt32{ + Int32: 1, + Valid: true, + }, + }).Do() + + tests := []struct { + name string + args toolsdk.CreateTaskArgs + error string + }{ + { + name: "OK", + args: toolsdk.CreateTaskArgs{ + TemplateVersionID: aiTV.TemplateVersion.ID.String(), + Input: "do a barrel roll", + User: "me", + }, + }, + { + name: "NoUser", + args: toolsdk.CreateTaskArgs{ + TemplateVersionID: aiTV.TemplateVersion.ID.String(), + Input: "do another barrel roll", + }, + }, + { + name: "NoInput", + args: toolsdk.CreateTaskArgs{ + TemplateVersionID: aiTV.TemplateVersion.ID.String(), + }, + error: "input is required", + }, + { + name: "NotTaskTemplate", + args: toolsdk.CreateTaskArgs{ + TemplateVersionID: r.TemplateVersion.ID.String(), + Input: "do yet another barrel roll", + }, + error: "Template does not have required parameter \"AI Prompt\"", + }, + { + name: "WithPreset", + args: toolsdk.CreateTaskArgs{ + TemplateVersionID: r.TemplateVersion.ID.String(), + TemplateVersionPresetID: presetID.String(), + Input: "not enough barrel rolls", + }, + error: "Template does not have required parameter \"AI Prompt\"", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + tb, err := toolsdk.NewDeps(memberClient) + require.NoError(t, err) + + _, err = testTool(t, toolsdk.CreateTask, tb, tt.args) + if tt.error != "" { + require.Error(t, err) + require.ErrorContains(t, err, tt.error) + } else { + require.NoError(t, err) + } + }) + } + }) + + t.Run("WorkspaceDeleteTask", func(t *testing.T) { + t.Parallel() + + // nolint:gocritic // This is in a test package and does not end up in the build + aiTV := dbfake.TemplateVersion(t, store).Seed(database.TemplateVersion{ + OrganizationID: owner.OrganizationID, + CreatedBy: member.ID, + HasAITask: sql.NullBool{ + Bool: true, + Valid: true, + }, + }).Do() + + // nolint:gocritic // This is in a test package and does not end up in the build + ws1 := dbfake.WorkspaceBuild(t, store, database.WorkspaceTable{ + Name: "delete-task-workspace-1", + OrganizationID: owner.OrganizationID, + OwnerID: member.ID, + TemplateID: aiTV.Template.ID, + }).WithTask().Do() + + // nolint:gocritic // This is in a test package and does not end up in the build + _ = dbfake.WorkspaceBuild(t, store, database.WorkspaceTable{ + Name: "delete-task-workspace-2", + OrganizationID: owner.OrganizationID, + OwnerID: member.ID, + TemplateID: aiTV.Template.ID, + }).WithTask().Do() + + tests := []struct { + name string + args toolsdk.DeleteTaskArgs + error string + }{ + { + name: "ByUUID", + args: toolsdk.DeleteTaskArgs{ + TaskID: ws1.Workspace.ID.String(), + }, + }, + { + name: "ByWorkspaceIdentifier", + args: toolsdk.DeleteTaskArgs{ + TaskID: "delete-task-workspace-2", + }, + }, + { + name: "NoID", + args: toolsdk.DeleteTaskArgs{}, + error: "task_id is required", + }, + { + name: "NoTaskByID", + args: toolsdk.DeleteTaskArgs{ + TaskID: uuid.New().String(), + }, + error: "Resource not found", + }, + { + name: "NoTaskByWorkspaceIdentifier", + args: toolsdk.DeleteTaskArgs{ + TaskID: "non-existent", + }, + error: "Resource not found", + }, + { + name: "ExistsButNotATask", + args: toolsdk.DeleteTaskArgs{ + TaskID: r.Workspace.ID.String(), + }, + error: "Resource not found", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + tb, err := toolsdk.NewDeps(memberClient) + require.NoError(t, err) + + _, err = testTool(t, toolsdk.DeleteTask, tb, tt.args) + if tt.error != "" { + require.Error(t, err) + require.ErrorContains(t, err, tt.error) + } else { + require.NoError(t, err) + } + }) + } + }) + + t.Run("WorkspaceListTasks", func(t *testing.T) { + t.Parallel() + + taskClient, taskUser := coderdtest.CreateAnotherUserMutators(t, client, owner.OrganizationID, nil) + + // nolint:gocritic // This is in a test package and does not end up in the build + aiTV := dbfake.TemplateVersion(t, store).Seed(database.TemplateVersion{ + OrganizationID: owner.OrganizationID, + CreatedBy: owner.UserID, + HasAITask: sql.NullBool{ + Bool: true, + Valid: true, + }, + }).Do() + + // This task should not show up since listing is user-scoped. + // nolint:gocritic // This is in a test package and does not end up in the build + _ = dbfake.WorkspaceBuild(t, store, database.WorkspaceTable{ + Name: "list-task-workspace-member", + OrganizationID: owner.OrganizationID, + OwnerID: member.ID, + TemplateID: aiTV.Template.ID, + }).WithTask().Do() + + // These tasks should show up. + for i := range 5 { + // nolint:gocritic // This is in a test package and does not end up in the build + var transition database.WorkspaceTransition + if i == 0 { + // nolint:gocritic // This is in a test package and does not end up in the build + transition = database.WorkspaceTransitionStop + } + // nolint:gocritic // This is in a test package and does not end up in the build + _ = dbfake.WorkspaceBuild(t, store, database.WorkspaceTable{ + Name: fmt.Sprintf("list-task-workspace-%d", i), + OrganizationID: owner.OrganizationID, + OwnerID: taskUser.ID, + TemplateID: aiTV.Template.ID, + }).Seed(database.WorkspaceBuild{Transition: transition}).WithTask().Do() + } + + tests := []struct { + name string + args toolsdk.ListTasksArgs + expected []string + error string + }{ + { + name: "ListAllOwned", + args: toolsdk.ListTasksArgs{}, + expected: []string{ + "list-task-workspace-0", + "list-task-workspace-1", + "list-task-workspace-2", + "list-task-workspace-3", + "list-task-workspace-4", + }, + }, + { + name: "ListFiltered", + args: toolsdk.ListTasksArgs{ + Status: "stopped", + }, + expected: []string{ + "list-task-workspace-0", + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + tb, err := toolsdk.NewDeps(taskClient) + require.NoError(t, err) + + res, err := testTool(t, toolsdk.ListTasks, tb, tt.args) + if tt.error != "" { + require.Error(t, err) + require.ErrorContains(t, err, tt.error) + } else { + require.NoError(t, err) + require.Len(t, res.Tasks, len(tt.expected)) + for _, task := range res.Tasks { + require.Contains(t, tt.expected, task.Name) + } + } + }) + } + }) + + t.Run("WorkspaceGetTask", func(t *testing.T) { + t.Parallel() + + // nolint:gocritic // This is in a test package and does not end up in the build + aiTV := dbfake.TemplateVersion(t, store).Seed(database.TemplateVersion{ + OrganizationID: owner.OrganizationID, + CreatedBy: member.ID, + HasAITask: sql.NullBool{ + Bool: true, + Valid: true, + }, + }).Do() + + // nolint:gocritic // This is in a test package and does not end up in the build + ws1 := dbfake.WorkspaceBuild(t, store, database.WorkspaceTable{ + Name: "get-task-workspace-1", + OrganizationID: owner.OrganizationID, + OwnerID: member.ID, + TemplateID: aiTV.Template.ID, + }).WithTask().Do() + + tests := []struct { + name string + args toolsdk.GetTaskStatusArgs + expected codersdk.WorkspaceStatus + error string + }{ + { + name: "ByUUID", + args: toolsdk.GetTaskStatusArgs{ + TaskID: ws1.Workspace.ID.String(), + }, + expected: codersdk.WorkspaceStatusRunning, + }, + { + name: "ByWorkspaceIdentifier", + args: toolsdk.GetTaskStatusArgs{ + TaskID: "get-task-workspace-1", + }, + expected: codersdk.WorkspaceStatusRunning, + }, + { + name: "NoID", + args: toolsdk.GetTaskStatusArgs{}, + error: "task_id is required", + }, + { + name: "NoTaskByID", + args: toolsdk.GetTaskStatusArgs{ + TaskID: uuid.New().String(), + }, + error: "Resource not found", + }, + { + name: "NoTaskByWorkspaceIdentifier", + args: toolsdk.GetTaskStatusArgs{ + TaskID: "non-existent", + }, + error: "Resource not found", + }, + { + name: "ExistsButNotATask", + args: toolsdk.GetTaskStatusArgs{ + TaskID: r.Workspace.ID.String(), + }, + error: "Resource not found", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + tb, err := toolsdk.NewDeps(memberClient) + require.NoError(t, err) + + res, err := testTool(t, toolsdk.GetTaskStatus, tb, tt.args) + if tt.error != "" { + require.Error(t, err) + require.ErrorContains(t, err, tt.error) + } else { + require.NoError(t, err) + require.Equal(t, tt.expected, res.Status) + } + }) + } + }) } // TestedTools keeps track of which tools have been tested. From 94c76b97bd7f1dab0b5ea485e23cc5c5beaa9b99 Mon Sep 17 00:00:00 2001 From: Asher Date: Wed, 1 Oct 2025 13:57:11 -0800 Subject: [PATCH 029/298] feat: add list_apps MCP tool (#19952) --- codersdk/toolsdk/toolsdk.go | 53 ++++++++++++ codersdk/toolsdk/toolsdk_test.go | 140 +++++++++++++++++++++++++++++++ 2 files changed, 193 insertions(+) diff --git a/codersdk/toolsdk/toolsdk.go b/codersdk/toolsdk/toolsdk.go index 63e17c2f039cd..c8228e51049d9 100644 --- a/codersdk/toolsdk/toolsdk.go +++ b/codersdk/toolsdk/toolsdk.go @@ -50,6 +50,7 @@ const ( ToolNameWorkspaceEditFile = "coder_workspace_edit_file" ToolNameWorkspaceEditFiles = "coder_workspace_edit_files" ToolNameWorkspacePortForward = "coder_workspace_port_forward" + ToolNameWorkspaceListApps = "coder_workspace_list_apps" ToolNameCreateTask = "coder_create_task" ToolNameDeleteTask = "coder_delete_task" ToolNameListTasks = "coder_list_tasks" @@ -227,6 +228,7 @@ var All = []GenericTool{ WorkspaceEditFile.Generic(), WorkspaceEditFiles.Generic(), WorkspacePortForward.Generic(), + WorkspaceListApps.Generic(), CreateTask.Generic(), DeleteTask.Generic(), ListTasks.Generic(), @@ -1756,6 +1758,57 @@ var WorkspacePortForward = Tool[WorkspacePortForwardArgs, WorkspacePortForwardRe }, } +type WorkspaceListAppsArgs struct { + Workspace string `json:"workspace"` +} + +type WorkspaceListApp struct { + Name string `json:"name"` + URL string `json:"url"` +} + +type WorkspaceListAppsResponse struct { + Apps []WorkspaceListApp `json:"apps"` +} + +var WorkspaceListApps = Tool[WorkspaceListAppsArgs, WorkspaceListAppsResponse]{ + Tool: aisdk.Tool{ + Name: ToolNameWorkspaceListApps, + Description: `List the URLs of Coder apps running in a workspace for a single agent.`, + Schema: aisdk.Schema{ + Properties: map[string]any{ + "workspace": map[string]any{ + "type": "string", + "description": workspaceDescription, + }, + }, + Required: []string{"workspace"}, + }, + }, + UserClientOptional: true, + Handler: func(ctx context.Context, deps Deps, args WorkspaceListAppsArgs) (WorkspaceListAppsResponse, error) { + workspaceName := NormalizeWorkspaceInput(args.Workspace) + _, workspaceAgent, err := findWorkspaceAndAgent(ctx, deps.coderClient, workspaceName) + if err != nil { + return WorkspaceListAppsResponse{}, xerrors.Errorf("failed to find workspace: %w", err) + } + + var res WorkspaceListAppsResponse + for _, app := range workspaceAgent.Apps { + name := app.DisplayName + if name == "" { + name = app.Slug + } + res.Apps = append(res.Apps, WorkspaceListApp{ + Name: name, + URL: app.URL, + }) + } + + return res, nil + }, +} + type CreateTaskArgs struct { Input string `json:"input"` TemplateVersionID string `json:"template_version_id"` diff --git a/codersdk/toolsdk/toolsdk_test.go b/codersdk/toolsdk/toolsdk_test.go index 8e0cc09fff25d..1bc17fabb31e2 100644 --- a/codersdk/toolsdk/toolsdk_test.go +++ b/codersdk/toolsdk/toolsdk_test.go @@ -1147,6 +1147,146 @@ func TestTools(t *testing.T) { }) } }) + + t.Run("WorkspaceListApps", func(t *testing.T) { + t.Parallel() + + // nolint:gocritic // This is in a test package and does not end up in the build + _ = dbfake.WorkspaceBuild(t, store, database.WorkspaceTable{ + Name: "list-app-workspace-one-agent", + OrganizationID: owner.OrganizationID, + OwnerID: member.ID, + }).WithAgent(func(agents []*proto.Agent) []*proto.Agent { + agents[0].Apps = []*proto.App{ + { + Slug: "zero", + Url: "http://zero.dev.coder.com", + }, + } + return agents + }).Do() + + // nolint:gocritic // This is in a test package and does not end up in the build + _ = dbfake.WorkspaceBuild(t, store, database.WorkspaceTable{ + Name: "list-app-workspace-multi-agent", + OrganizationID: owner.OrganizationID, + OwnerID: member.ID, + }).WithAgent(func(agents []*proto.Agent) []*proto.Agent { + agents[0].Apps = []*proto.App{ + { + Slug: "one", + Url: "http://one.dev.coder.com", + }, + { + Slug: "two", + Url: "http://two.dev.coder.com", + }, + { + Slug: "three", + Url: "http://three.dev.coder.com", + }, + } + agents = append(agents, &proto.Agent{ + Id: uuid.NewString(), + Name: "dev2", + Auth: &proto.Agent_Token{ + Token: uuid.NewString(), + }, + Env: map[string]string{}, + Apps: []*proto.App{ + { + Slug: "four", + Url: "http://four.dev.coder.com", + }, + }, + }) + return agents + }).Do() + + tests := []struct { + name string + args toolsdk.WorkspaceListAppsArgs + expected []toolsdk.WorkspaceListApp + error string + }{ + { + name: "NonExistentWorkspace", + args: toolsdk.WorkspaceListAppsArgs{ + Workspace: "list-appp-workspace-does-not-exist", + }, + error: "failed to find workspace", + }, + { + name: "OneAgentOneApp", + args: toolsdk.WorkspaceListAppsArgs{ + Workspace: "list-app-workspace-one-agent", + }, + expected: []toolsdk.WorkspaceListApp{ + { + Name: "zero", + URL: "http://zero.dev.coder.com", + }, + }, + }, + { + name: "MultiAgent", + args: toolsdk.WorkspaceListAppsArgs{ + Workspace: "list-app-workspace-multi-agent", + }, + error: "multiple agents found, please specify the agent name", + }, + { + name: "MultiAgentOneApp", + args: toolsdk.WorkspaceListAppsArgs{ + Workspace: "list-app-workspace-multi-agent.dev2", + }, + expected: []toolsdk.WorkspaceListApp{ + { + Name: "four", + URL: "http://four.dev.coder.com", + }, + }, + }, + { + name: "MultiAgentMultiApp", + args: toolsdk.WorkspaceListAppsArgs{ + Workspace: "list-app-workspace-multi-agent.dev", + }, + expected: []toolsdk.WorkspaceListApp{ + { + Name: "one", + URL: "http://one.dev.coder.com", + }, + { + Name: "three", + URL: "http://three.dev.coder.com", + }, + { + Name: "two", + URL: "http://two.dev.coder.com", + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + tb, err := toolsdk.NewDeps(memberClient) + require.NoError(t, err) + + res, err := testTool(t, toolsdk.WorkspaceListApps, tb, tt.args) + if tt.error != "" { + require.Error(t, err) + require.ErrorContains(t, err, tt.error) + } else { + require.NoError(t, err) + require.Equal(t, tt.expected, res.Apps) + } + }) + } + }) } // TestedTools keeps track of which tools have been tested. From 230df3eadf344606cc2b99b3689431bd9760cfc2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 22:11:37 +0000 Subject: [PATCH 030/298] chore: bump react-confetti from 6.2.2 to 6.4.0 in /site (#20126) Bumps [react-confetti](https://github.com/alampros/react-confetti) from 6.2.2 to 6.4.0.
Release notes

Sourced from react-confetti's releases.

v6.4.0

6.4.0 (2025-03-04)

Bug Fixes

  • clamp tweenProgress between 0 and tweenDuration (f988305)

Features

  • adding tweenFrom property to allow smooth transition when parameters change (dde31e0)

v6.3.0

6.3.0 (2025-03-01)

Bug Fixes

  • prevent particle flicker on removal (5cb5bd8)

Features

  • using elapsed time in physics updates (d1626dc)

v6.2.3

6.2.3 (2025-02-22)

Bug Fixes

Changelog

Sourced from react-confetti's changelog.

6.4.0 (2025-03-04)

Bug Fixes

  • clamp tweenProgress between 0 and tweenDuration (f988305)

Features

  • adding tweenFrom property to allow smooth transition when parameters change (dde31e0)

6.3.0 (2025-03-01)

Bug Fixes

  • prevent particle flicker on removal (5cb5bd8)

Features

  • using elapsed time in physics updates (d1626dc)

6.2.3 (2025-02-22)

Bug Fixes

Commits
  • 0d53537 chore(release): 6.4.0 [skip ci]
  • 641b351 Merge branch 'develop'
  • 6ac61ed lint fix
  • 90f5a59 Merge pull request #172 from AlexJDG/bugfix/fix-tweening
  • f988305 fix: clamp tweenProgress between 0 and tweenDuration
  • dde31e0 feat: adding tweenFrom property to allow smooth transition when parameters ch...
  • 5dd9f7b chore: renaming property for clarity
  • 31eb46d chore(release): 6.3.0 [skip ci]
  • e447389 Merge branch 'develop'
  • b177991 fix lint action name
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=react-confetti&package-manager=npm_and_yarn&previous-version=6.2.2&new-version=6.4.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- site/package.json | 2 +- site/pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/site/package.json b/site/package.json index 660227b1373a1..38617dce23a99 100644 --- a/site/package.json +++ b/site/package.json @@ -96,7 +96,7 @@ "pretty-bytes": "6.1.1", "react": "19.1.1", "react-color": "2.19.3", - "react-confetti": "6.2.2", + "react-confetti": "6.4.0", "react-date-range": "1.4.0", "react-dom": "19.1.1", "react-markdown": "9.1.0", diff --git a/site/pnpm-lock.yaml b/site/pnpm-lock.yaml index 068bc1959648e..eebed5b445afc 100644 --- a/site/pnpm-lock.yaml +++ b/site/pnpm-lock.yaml @@ -203,8 +203,8 @@ importers: specifier: 2.19.3 version: 2.19.3(react@19.1.1) react-confetti: - specifier: 6.2.2 - version: 6.2.2(react@19.1.1) + specifier: 6.4.0 + version: 6.4.0(react@19.1.1) react-date-range: specifier: 1.4.0 version: 1.4.0(date-fns@2.30.0)(react@19.1.1) @@ -5588,8 +5588,8 @@ packages: peerDependencies: react: '*' - react-confetti@6.2.2: - resolution: {integrity: sha512-K+kTyOPgX+ZujMZ+Rmb7pZdHBvg+DzinG/w4Eh52WOB8/pfO38efnnrtEZNJmjTvLxc16RBYO+tPM68Fg8viBA==, tarball: https://registry.npmjs.org/react-confetti/-/react-confetti-6.2.2.tgz} + react-confetti@6.4.0: + resolution: {integrity: sha512-5MdGUcqxrTU26I2EU7ltkWPwxvucQTuqMm8dUz72z2YMqTD6s9vMcDUysk7n9jnC+lXuCPeJJ7Knf98VEYE9Rg==, tarball: https://registry.npmjs.org/react-confetti/-/react-confetti-6.4.0.tgz} engines: {node: '>=16'} peerDependencies: react: ^16.3.0 || ^17.0.1 || ^18.0.0 || ^19.0.0 @@ -12427,7 +12427,7 @@ snapshots: reactcss: 1.2.3(react@19.1.1) tinycolor2: 1.6.0 - react-confetti@6.2.2(react@19.1.1): + react-confetti@6.4.0(react@19.1.1): dependencies: react: 19.1.1 tween-functions: 1.2.0 From 4e53d8b9c257b96b9360c8e2f73d461cef4ea57d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 22:12:40 +0000 Subject: [PATCH 031/298] chore: bump @biomejs/biome from 2.2.0 to 2.2.4 in /site (#20118) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [@biomejs/biome](https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome) from 2.2.0 to 2.2.4.
Release notes

Sourced from @​biomejs/biome's releases.

Biome CLI v2.2.4

2.2.4

Patch Changes

  • #7453 aa8cea3 Thanks @​arendjr! - Fixed #7242: Aliases specified in package.json's imports section now support having multiple targets as part of an array.

  • #7454 ac17183 Thanks @​arendjr! - Greatly improved performance of noImportCycles by eliminating allocations.

    In one repository, the total runtime of Biome with only noImportCycles enabled went from ~23s down to ~4s.

  • #7447 7139aad Thanks @​rriski! - Fixes #7446. The GritQL $... spread metavariable now correctly matches members in object literals, aligning its behavior with arrays and function calls.

  • #6710 98cf9af Thanks @​arendjr! - Fixed #4723: Type inference now recognises index signatures and their accesses when they are being indexed as a string.

    Example

    type BagOfPromises = {
    // This is an index signature definition. It declares that instances of
    type
      // `BagOfPromises` can be indexed using arbitrary strings.
      [property: string]: Promise<void>;
    };
    

    let bag: BagOfPromises = {}; // Because bag.iAmAPromise is equivalent to bag[&quot;iAmAPromise&quot;], this is // considered an access to the string index, and a Promise is expected. bag.iAmAPromise;

  • #7415 d042f18 Thanks @​qraqras! - Fixed #7212, now the useOptionalChain rule recognizes optional chaining using typeof (e.g., typeof foo !== 'undefined' && foo.bar).

  • #7419 576baf4 Thanks @​Conaclos! - Fixed #7323. noUnusedPrivateClassMembers no longer reports as unused TypeScript private members if the rule encounters a computed access on this.

    In the following example, member as previously reported as unused. It is no longer reported.

... (truncated)

Changelog

Sourced from @​biomejs/biome's changelog.

2.2.4

Patch Changes

  • #7453 aa8cea3 Thanks @​arendjr! - Fixed #7242: Aliases specified in package.json's imports section now support having multiple targets as part of an array.

  • #7454 ac17183 Thanks @​arendjr! - Greatly improved performance of noImportCycles by eliminating allocations.

    In one repository, the total runtime of Biome with only noImportCycles enabled went from ~23s down to ~4s.

  • #7447 7139aad Thanks @​rriski! - Fixes #7446. The GritQL $... spread metavariable now correctly matches members in object literals, aligning its behavior with arrays and function calls.

  • #6710 98cf9af Thanks @​arendjr! - Fixed #4723: Type inference now recognises index signatures and their accesses when they are being indexed as a string.

    Example

    type BagOfPromises = {
    // This is an index signature definition. It declares that instances of
    type
      // `BagOfPromises` can be indexed using arbitrary strings.
      [property: string]: Promise<void>;
    };
    

    let bag: BagOfPromises = {}; // Because bag.iAmAPromise is equivalent to bag[&quot;iAmAPromise&quot;], this is // considered an access to the string index, and a Promise is expected. bag.iAmAPromise;

  • #7415 d042f18 Thanks @​qraqras! - Fixed #7212, now the useOptionalChain rule recognizes optional chaining using typeof (e.g., typeof foo !== 'undefined' && foo.bar).

  • #7419 576baf4 Thanks @​Conaclos! - Fixed #7323. noUnusedPrivateClassMembers no longer reports as unused TypeScript private members if the rule encounters a computed access on this.

    In the following example, member as previously reported as unused. It is no longer reported.

    class TsBioo {
      private member: number;
    

    set_with_name(name: string, value: number) { this[name] = value; } }

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@biomejs/biome&package-manager=npm_and_yarn&previous-version=2.2.0&new-version=2.2.4)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- site/package.json | 2 +- site/pnpm-lock.yaml | 80 ++++++++++++++++++++++----------------------- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/site/package.json b/site/package.json index 38617dce23a99..8a6c06c110dfb 100644 --- a/site/package.json +++ b/site/package.json @@ -123,7 +123,7 @@ "yup": "1.6.1" }, "devDependencies": { - "@biomejs/biome": "2.2.0", + "@biomejs/biome": "2.2.4", "@chromatic-com/storybook": "4.1.0", "@octokit/types": "12.3.0", "@playwright/test": "1.50.1", diff --git a/site/pnpm-lock.yaml b/site/pnpm-lock.yaml index eebed5b445afc..35ed3e0545012 100644 --- a/site/pnpm-lock.yaml +++ b/site/pnpm-lock.yaml @@ -279,8 +279,8 @@ importers: version: 1.6.1 devDependencies: '@biomejs/biome': - specifier: 2.2.0 - version: 2.2.0 + specifier: 2.2.4 + version: 2.2.4 '@chromatic-com/storybook': specifier: 4.1.0 version: 4.1.0(storybook@9.1.2(@testing-library/dom@10.4.0)(msw@2.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0))) @@ -454,7 +454,7 @@ importers: version: 7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0) vite-plugin-checker: specifier: 0.11.0 - version: 0.11.0(@biomejs/biome@2.2.0)(eslint@8.52.0)(optionator@0.9.3)(typescript@5.6.3)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0)) + version: 0.11.0(@biomejs/biome@2.2.4)(eslint@8.52.0)(optionator@0.9.3)(typescript@5.6.3)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0)) packages: @@ -658,55 +658,55 @@ packages: '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==, tarball: https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz} - '@biomejs/biome@2.2.0': - resolution: {integrity: sha512-3On3RSYLsX+n9KnoSgfoYlckYBoU6VRM22cw1gB4Y0OuUVSYd/O/2saOJMrA4HFfA1Ff0eacOvMN1yAAvHtzIw==, tarball: https://registry.npmjs.org/@biomejs/biome/-/biome-2.2.0.tgz} + '@biomejs/biome@2.2.4': + resolution: {integrity: sha512-TBHU5bUy/Ok6m8c0y3pZiuO/BZoY/OcGxoLlrfQof5s8ISVwbVBdFINPQZyFfKwil8XibYWb7JMwnT8wT4WVPg==, tarball: https://registry.npmjs.org/@biomejs/biome/-/biome-2.2.4.tgz} engines: {node: '>=14.21.3'} hasBin: true - '@biomejs/cli-darwin-arm64@2.2.0': - resolution: {integrity: sha512-zKbwUUh+9uFmWfS8IFxmVD6XwqFcENjZvEyfOxHs1epjdH3wyyMQG80FGDsmauPwS2r5kXdEM0v/+dTIA9FXAg==, tarball: https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.2.0.tgz} + '@biomejs/cli-darwin-arm64@2.2.4': + resolution: {integrity: sha512-RJe2uiyaloN4hne4d2+qVj3d3gFJFbmrr5PYtkkjei1O9c+BjGXgpUPVbi8Pl8syumhzJjFsSIYkcLt2VlVLMA==, tarball: https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.2.4.tgz} engines: {node: '>=14.21.3'} cpu: [arm64] os: [darwin] - '@biomejs/cli-darwin-x64@2.2.0': - resolution: {integrity: sha512-+OmT4dsX2eTfhD5crUOPw3RPhaR+SKVspvGVmSdZ9y9O/AgL8pla6T4hOn1q+VAFBHuHhsdxDRJgFCSC7RaMOw==, tarball: https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.2.0.tgz} + '@biomejs/cli-darwin-x64@2.2.4': + resolution: {integrity: sha512-cFsdB4ePanVWfTnPVaUX+yr8qV8ifxjBKMkZwN7gKb20qXPxd/PmwqUH8mY5wnM9+U0QwM76CxFyBRJhC9tQwg==, tarball: https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.2.4.tgz} engines: {node: '>=14.21.3'} cpu: [x64] os: [darwin] - '@biomejs/cli-linux-arm64-musl@2.2.0': - resolution: {integrity: sha512-egKpOa+4FL9YO+SMUMLUvf543cprjevNc3CAgDNFLcjknuNMcZ0GLJYa3EGTCR2xIkIUJDVneBV3O9OcIlCEZQ==, tarball: https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.2.0.tgz} + '@biomejs/cli-linux-arm64-musl@2.2.4': + resolution: {integrity: sha512-7TNPkMQEWfjvJDaZRSkDCPT/2r5ESFPKx+TEev+I2BXDGIjfCZk2+b88FOhnJNHtksbOZv8ZWnxrA5gyTYhSsQ==, tarball: https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.2.4.tgz} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] - '@biomejs/cli-linux-arm64@2.2.0': - resolution: {integrity: sha512-6eoRdF2yW5FnW9Lpeivh7Mayhq0KDdaDMYOJnH9aT02KuSIX5V1HmWJCQQPwIQbhDh68Zrcpl8inRlTEan0SXw==, tarball: https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.2.0.tgz} + '@biomejs/cli-linux-arm64@2.2.4': + resolution: {integrity: sha512-M/Iz48p4NAzMXOuH+tsn5BvG/Jb07KOMTdSVwJpicmhN309BeEyRyQX+n1XDF0JVSlu28+hiTQ2L4rZPvu7nMw==, tarball: https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.2.4.tgz} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] - '@biomejs/cli-linux-x64-musl@2.2.0': - resolution: {integrity: sha512-I5J85yWwUWpgJyC1CcytNSGusu2p9HjDnOPAFG4Y515hwRD0jpR9sT9/T1cKHtuCvEQ/sBvx+6zhz9l9wEJGAg==, tarball: https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.2.0.tgz} + '@biomejs/cli-linux-x64-musl@2.2.4': + resolution: {integrity: sha512-m41nFDS0ksXK2gwXL6W6yZTYPMH0LughqbsxInSKetoH6morVj43szqKx79Iudkp8WRT5SxSh7qVb8KCUiewGg==, tarball: https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.2.4.tgz} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] - '@biomejs/cli-linux-x64@2.2.0': - resolution: {integrity: sha512-5UmQx/OZAfJfi25zAnAGHUMuOd+LOsliIt119x2soA2gLggQYrVPA+2kMUxR6Mw5M1deUF/AWWP2qpxgH7Nyfw==, tarball: https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.2.0.tgz} + '@biomejs/cli-linux-x64@2.2.4': + resolution: {integrity: sha512-orr3nnf2Dpb2ssl6aihQtvcKtLySLta4E2UcXdp7+RTa7mfJjBgIsbS0B9GC8gVu0hjOu021aU8b3/I1tn+pVQ==, tarball: https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.2.4.tgz} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] - '@biomejs/cli-win32-arm64@2.2.0': - resolution: {integrity: sha512-n9a1/f2CwIDmNMNkFs+JI0ZjFnMO0jdOyGNtihgUNFnlmd84yIYY2KMTBmMV58ZlVHjgmY5Y6E1hVTnSRieggA==, tarball: https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.2.0.tgz} + '@biomejs/cli-win32-arm64@2.2.4': + resolution: {integrity: sha512-NXnfTeKHDFUWfxAefa57DiGmu9VyKi0cDqFpdI+1hJWQjGJhJutHPX0b5m+eXvTKOaf+brU+P0JrQAZMb5yYaQ==, tarball: https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.2.4.tgz} engines: {node: '>=14.21.3'} cpu: [arm64] os: [win32] - '@biomejs/cli-win32-x64@2.2.0': - resolution: {integrity: sha512-Nawu5nHjP/zPKTIryh2AavzTc/KEg4um/MxWdXW0A6P/RZOyIpa7+QSjeXwAwX/utJGaCoXRPWtF3m5U/bB3Ww==, tarball: https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.2.0.tgz} + '@biomejs/cli-win32-x64@2.2.4': + resolution: {integrity: sha512-3Y4V4zVRarVh/B/eSHczR4LYoSVyv3Dfuvm3cWs5w/HScccS0+Wt/lHOcDTRYeHjQmMYVC3rIRWqyN2EI52+zg==, tarball: https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.2.4.tgz} engines: {node: '>=14.21.3'} cpu: [x64] os: [win32] @@ -6925,39 +6925,39 @@ snapshots: '@bcoe/v8-coverage@0.2.3': {} - '@biomejs/biome@2.2.0': + '@biomejs/biome@2.2.4': optionalDependencies: - '@biomejs/cli-darwin-arm64': 2.2.0 - '@biomejs/cli-darwin-x64': 2.2.0 - '@biomejs/cli-linux-arm64': 2.2.0 - '@biomejs/cli-linux-arm64-musl': 2.2.0 - '@biomejs/cli-linux-x64': 2.2.0 - '@biomejs/cli-linux-x64-musl': 2.2.0 - '@biomejs/cli-win32-arm64': 2.2.0 - '@biomejs/cli-win32-x64': 2.2.0 + '@biomejs/cli-darwin-arm64': 2.2.4 + '@biomejs/cli-darwin-x64': 2.2.4 + '@biomejs/cli-linux-arm64': 2.2.4 + '@biomejs/cli-linux-arm64-musl': 2.2.4 + '@biomejs/cli-linux-x64': 2.2.4 + '@biomejs/cli-linux-x64-musl': 2.2.4 + '@biomejs/cli-win32-arm64': 2.2.4 + '@biomejs/cli-win32-x64': 2.2.4 - '@biomejs/cli-darwin-arm64@2.2.0': + '@biomejs/cli-darwin-arm64@2.2.4': optional: true - '@biomejs/cli-darwin-x64@2.2.0': + '@biomejs/cli-darwin-x64@2.2.4': optional: true - '@biomejs/cli-linux-arm64-musl@2.2.0': + '@biomejs/cli-linux-arm64-musl@2.2.4': optional: true - '@biomejs/cli-linux-arm64@2.2.0': + '@biomejs/cli-linux-arm64@2.2.4': optional: true - '@biomejs/cli-linux-x64-musl@2.2.0': + '@biomejs/cli-linux-x64-musl@2.2.4': optional: true - '@biomejs/cli-linux-x64@2.2.0': + '@biomejs/cli-linux-x64@2.2.4': optional: true - '@biomejs/cli-win32-arm64@2.2.0': + '@biomejs/cli-win32-arm64@2.2.4': optional: true - '@biomejs/cli-win32-x64@2.2.0': + '@biomejs/cli-win32-x64@2.2.4': optional: true '@bundled-es-modules/cookie@2.0.1': @@ -13416,7 +13416,7 @@ snapshots: d3-time: 3.1.0 d3-timer: 3.0.1 - vite-plugin-checker@0.11.0(@biomejs/biome@2.2.0)(eslint@8.52.0)(optionator@0.9.3)(typescript@5.6.3)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0)): + vite-plugin-checker@0.11.0(@biomejs/biome@2.2.4)(eslint@8.52.0)(optionator@0.9.3)(typescript@5.6.3)(vite@7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0)): dependencies: '@babel/code-frame': 7.27.1 chokidar: 4.0.3 @@ -13428,7 +13428,7 @@ snapshots: vite: 7.1.7(@types/node@20.17.16)(jiti@1.21.7)(yaml@2.7.0) vscode-uri: 3.1.0 optionalDependencies: - '@biomejs/biome': 2.2.0 + '@biomejs/biome': 2.2.4 eslint: 8.52.0 optionator: 0.9.3 typescript: 5.6.3 From 79f5cb8b9ae314f0d8be6b3b78108ee17ac6041b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 22:46:31 +0000 Subject: [PATCH 032/298] chore: bump react-virtualized-auto-sizer and @types/react-virtualized-auto-sizer (#20078) Bumps [react-virtualized-auto-sizer](https://github.com/bvaughn/react-virtualized-auto-sizer) and [@types/react-virtualized-auto-sizer](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-virtualized-auto-sizer). These dependencies needed to be updated together. Updates `react-virtualized-auto-sizer` from 1.0.24 to 1.0.26
Release notes

Sourced from react-virtualized-auto-sizer's releases.

1.0.26

  • Changed width and height values to be based om getBoundingClientRect rather than offsetWidth and offsetHeight (which are integers and can cause rounding/flickering problems in some cases).

1.0.25

  • Dependencies updated to include React 19
Changelog

Sourced from react-virtualized-auto-sizer's changelog.

1.0.26

  • Changed width and height values to be based om getBoundingClientRect rather than offsetWidth and offsetHeight (which are integers and can cause rounding/flickering problems in some cases).

1.0.25

  • Dependencies updated to include React 19
Commits
  • 45b1270 Add deprecation warning for scaledWidth/scaledHeight accessors
  • e9c8ef0 Merge pull request #97 from bvaughn/issues/96
  • fdf25d4 Change width and height to come from getBoundingClientRect rather than offset...
  • 1898b07 Tweaked README format
  • 2dc8808 1.0.24 -> 1.0.25
  • 801cc52 Merge pull request #93 from olafbuitelaar/patch-1
  • 0b8b181 updated deps to react 19
  • 9f970c9 Update README
  • See full diff in compare view

Updates `@types/react-virtualized-auto-sizer` from 1.0.4 to 1.0.8
Commits

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- site/package.json | 4 ++-- site/pnpm-lock.yaml | 30 +++++++++++++++++------------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/site/package.json b/site/package.json index 8a6c06c110dfb..9183fed149d2e 100644 --- a/site/package.json +++ b/site/package.json @@ -105,7 +105,7 @@ "react-router": "7.8.0", "react-syntax-highlighter": "15.6.1", "react-textarea-autosize": "8.5.9", - "react-virtualized-auto-sizer": "1.0.24", + "react-virtualized-auto-sizer": "1.0.26", "react-window": "1.8.11", "recharts": "2.15.0", "remark-gfm": "4.0.0", @@ -150,7 +150,7 @@ "@types/react-date-range": "1.4.4", "@types/react-dom": "19.1.11", "@types/react-syntax-highlighter": "15.5.13", - "@types/react-virtualized-auto-sizer": "1.0.4", + "@types/react-virtualized-auto-sizer": "1.0.8", "@types/react-window": "1.8.8", "@types/semver": "7.7.1", "@types/ssh2": "1.15.5", diff --git a/site/pnpm-lock.yaml b/site/pnpm-lock.yaml index 35ed3e0545012..4570da4575185 100644 --- a/site/pnpm-lock.yaml +++ b/site/pnpm-lock.yaml @@ -230,8 +230,8 @@ importers: specifier: 8.5.9 version: 8.5.9(@types/react@19.1.17)(react@19.1.1) react-virtualized-auto-sizer: - specifier: 1.0.24 - version: 1.0.24(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + specifier: 1.0.26 + version: 1.0.26(react-dom@19.1.1(react@19.1.1))(react@19.1.1) react-window: specifier: 1.8.11 version: 1.8.11(react-dom@19.1.1(react@19.1.1))(react@19.1.1) @@ -360,8 +360,8 @@ importers: specifier: 15.5.13 version: 15.5.13 '@types/react-virtualized-auto-sizer': - specifier: 1.0.4 - version: 1.0.4 + specifier: 1.0.8 + version: 1.0.8(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@types/react-window': specifier: 1.8.8 version: 1.8.8 @@ -2987,8 +2987,9 @@ packages: peerDependencies: '@types/react': '*' - '@types/react-virtualized-auto-sizer@1.0.4': - resolution: {integrity: sha512-nhYwlFiYa8M3S+O2T9QO/e1FQUYMr/wJENUdf/O0dhRi1RS/93rjrYQFYdbUqtdFySuhrtnEDX29P6eKOttY+A==, tarball: https://registry.npmjs.org/@types/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.4.tgz} + '@types/react-virtualized-auto-sizer@1.0.8': + resolution: {integrity: sha512-keJpNyhiwfl2+N12G1ocCVA5ZDBArbPLe/S90X3kt7fam9naeHdaYYWbpe2sHczp70JWJ+2QLhBE8kLvLuVNjA==, tarball: https://registry.npmjs.org/@types/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.8.tgz} + deprecated: This is a stub types definition. react-virtualized-auto-sizer provides its own type definitions, so you do not need this installed. '@types/react-window@1.8.8': resolution: {integrity: sha512-8Ls660bHR1AUA2kuRvVG9D/4XpRC6wjAaPT9dil7Ckc76eP9TKWZwwmgfq8Q1LANX3QNDnoU4Zp48A3w+zK69Q==, tarball: https://registry.npmjs.org/@types/react-window/-/react-window-1.8.8.tgz} @@ -5728,11 +5729,11 @@ packages: react: '>=16.6.0' react-dom: '>=16.6.0' - react-virtualized-auto-sizer@1.0.24: - resolution: {integrity: sha512-3kCn7N9NEb3FlvJrSHWGQ4iVl+ydQObq2fHMn12i5wbtm74zHOPhz/i64OL3c1S1vi9i2GXtZqNqUJTQ+BnNfg==, tarball: https://registry.npmjs.org/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.24.tgz} + react-virtualized-auto-sizer@1.0.26: + resolution: {integrity: sha512-CblNyiNVw2o+hsa5/49NH2ogGxZ+t+3aweRvNSq7TVjDIlwk7ir4lencEg5HxHeSzwNarSkNkiu0qJSOXtxm5A==, tarball: https://registry.npmjs.org/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.26.tgz} peerDependencies: - react: ^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 - react-dom: ^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 + react: ^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 || ^19.0.0 react-window@1.8.11: resolution: {integrity: sha512-+SRbUVT2scadgFSWx+R1P754xHPEqvcfSfVX10QYg6POOz+WNgkN48pS+BtZNIMGiL1HYrSEiCkwsMS15QogEQ==, tarball: https://registry.npmjs.org/react-window/-/react-window-1.8.11.tgz} @@ -9187,9 +9188,12 @@ snapshots: dependencies: '@types/react': 19.1.17 - '@types/react-virtualized-auto-sizer@1.0.4': + '@types/react-virtualized-auto-sizer@1.0.8(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: - '@types/react': 19.1.17 + react-virtualized-auto-sizer: 1.0.26(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + transitivePeerDependencies: + - react + - react-dom '@types/react-window@1.8.8': dependencies: @@ -12591,7 +12595,7 @@ snapshots: react: 19.1.1 react-dom: 19.1.1(react@19.1.1) - react-virtualized-auto-sizer@1.0.24(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + react-virtualized-auto-sizer@1.0.26(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: react: 19.1.1 react-dom: 19.1.1(react@19.1.1) From 29b4cfc55b9d7a712866b013da11bac006bd746d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 17:08:26 -0600 Subject: [PATCH 033/298] chore: bump remark-gfm from 4.0.0 to 4.0.1 in /site (#20081) --- site/package.json | 2 +- site/pnpm-lock.yaml | 409 ++++++++------------------------------------ 2 files changed, 75 insertions(+), 336 deletions(-) diff --git a/site/package.json b/site/package.json index 9183fed149d2e..7ba86b7881665 100644 --- a/site/package.json +++ b/site/package.json @@ -108,7 +108,7 @@ "react-virtualized-auto-sizer": "1.0.26", "react-window": "1.8.11", "recharts": "2.15.0", - "remark-gfm": "4.0.0", + "remark-gfm": "4.0.1", "resize-observer-polyfill": "1.5.1", "semver": "7.7.2", "tailwind-merge": "2.6.0", diff --git a/site/pnpm-lock.yaml b/site/pnpm-lock.yaml index 4570da4575185..49f5c686087b8 100644 --- a/site/pnpm-lock.yaml +++ b/site/pnpm-lock.yaml @@ -239,8 +239,8 @@ importers: specifier: 2.15.0 version: 2.15.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) remark-gfm: - specifier: 4.0.0 - version: 4.0.0 + specifier: 4.0.1 + version: 4.0.1 resize-observer-polyfill: specifier: 1.5.1 version: 1.5.1 @@ -2919,9 +2919,6 @@ packages: '@types/lodash@4.17.20': resolution: {integrity: sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==, tarball: https://registry.npmjs.org/@types/lodash/-/lodash-4.17.20.tgz} - '@types/mdast@4.0.3': - resolution: {integrity: sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==, tarball: https://registry.npmjs.org/@types/mdast/-/mdast-4.0.3.tgz} - '@types/mdast@4.0.4': resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==, tarball: https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz} @@ -3041,9 +3038,6 @@ packages: '@types/unist@2.0.11': resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==, tarball: https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz} - '@types/unist@3.0.2': - resolution: {integrity: sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==, tarball: https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz} - '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==, tarball: https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz} @@ -3730,9 +3724,6 @@ packages: decimal.js@10.4.3: resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==, tarball: https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz} - decode-named-character-reference@1.0.2: - resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==, tarball: https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz} - decode-named-character-reference@1.2.0: resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==, tarball: https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz} @@ -4917,8 +4908,8 @@ packages: makeerror@1.0.12: resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==, tarball: https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz} - markdown-table@3.0.3: - resolution: {integrity: sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==, tarball: https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.3.tgz} + markdown-table@3.0.4: + resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==, tarball: https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz} material-colors@1.2.6: resolution: {integrity: sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==, tarball: https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz} @@ -4927,20 +4918,17 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==, tarball: https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz} engines: {node: '>= 0.4'} - mdast-util-find-and-replace@3.0.1: - resolution: {integrity: sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==, tarball: https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.1.tgz} - - mdast-util-from-markdown@2.0.0: - resolution: {integrity: sha512-n7MTOr/z+8NAX/wmhhDji8O3bRvPTV/U0oTCaZJkjhPSKTPhS3xufVhKGF8s1pJ7Ox4QgoIU7KHseh09S+9rTA==, tarball: https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.0.tgz} + mdast-util-find-and-replace@3.0.2: + resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==, tarball: https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz} mdast-util-from-markdown@2.0.2: resolution: {integrity: sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==, tarball: https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz} - mdast-util-gfm-autolink-literal@2.0.0: - resolution: {integrity: sha512-FyzMsduZZHSc3i0Px3PQcBT4WJY/X/RCtEJKuybiC6sjPqLv7h1yqAkmILZtuxMSsUyaLUWNp71+vQH2zqp5cg==, tarball: https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.0.tgz} + mdast-util-gfm-autolink-literal@2.0.1: + resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==, tarball: https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz} - mdast-util-gfm-footnote@2.0.0: - resolution: {integrity: sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ==, tarball: https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.0.0.tgz} + mdast-util-gfm-footnote@2.1.0: + resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==, tarball: https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz} mdast-util-gfm-strikethrough@2.0.0: resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==, tarball: https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz} @@ -4951,8 +4939,8 @@ packages: mdast-util-gfm-task-list-item@2.0.0: resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==, tarball: https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz} - mdast-util-gfm@3.0.0: - resolution: {integrity: sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw==, tarball: https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.0.0.tgz} + mdast-util-gfm@3.1.0: + resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==, tarball: https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz} mdast-util-mdx-expression@2.0.1: resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==, tarball: https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz} @@ -4963,18 +4951,12 @@ packages: mdast-util-mdxjs-esm@2.0.1: resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==, tarball: https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz} - mdast-util-phrasing@4.0.0: - resolution: {integrity: sha512-xadSsJayQIucJ9n053dfQwVu1kuXg7jCTdYsMK8rqzKZh52nLfSH/k0sAxE0u+pj/zKZX+o5wB+ML5mRayOxFA==, tarball: https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.0.0.tgz} - mdast-util-phrasing@4.1.0: resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==, tarball: https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz} mdast-util-to-hast@13.2.0: resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==, tarball: https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz} - mdast-util-to-markdown@2.1.0: - resolution: {integrity: sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==, tarball: https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.0.tgz} - mdast-util-to-markdown@2.1.2: resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==, tarball: https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz} @@ -5002,144 +4984,87 @@ packages: resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==, tarball: https://registry.npmjs.org/methods/-/methods-1.1.2.tgz} engines: {node: '>= 0.6'} - micromark-core-commonmark@2.0.0: - resolution: {integrity: sha512-jThOz/pVmAYUtkroV3D5c1osFXAMv9e0ypGDOIZuCeAe91/sD6BoE2Sjzt30yuXtwOYUmySOhMas/PVyh02itA==, tarball: https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.0.tgz} - micromark-core-commonmark@2.0.3: resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==, tarball: https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz} - micromark-extension-gfm-autolink-literal@2.0.0: - resolution: {integrity: sha512-rTHfnpt/Q7dEAK1Y5ii0W8bhfJlVJFnJMHIPisfPK3gpVNuOP0VnRl96+YJ3RYWV/P4gFeQoGKNlT3RhuvpqAg==, tarball: https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.0.0.tgz} + micromark-extension-gfm-autolink-literal@2.1.0: + resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==, tarball: https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz} - micromark-extension-gfm-footnote@2.0.0: - resolution: {integrity: sha512-6Rzu0CYRKDv3BfLAUnZsSlzx3ak6HAoI85KTiijuKIz5UxZxbUI+pD6oHgw+6UtQuiRwnGRhzMmPRv4smcz0fg==, tarball: https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.0.0.tgz} + micromark-extension-gfm-footnote@2.1.0: + resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==, tarball: https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz} - micromark-extension-gfm-strikethrough@2.0.0: - resolution: {integrity: sha512-c3BR1ClMp5fxxmwP6AoOY2fXO9U8uFMKs4ADD66ahLTNcwzSCyRVU4k7LPV5Nxo/VJiR4TdzxRQY2v3qIUceCw==, tarball: https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.0.0.tgz} + micromark-extension-gfm-strikethrough@2.1.0: + resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==, tarball: https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz} - micromark-extension-gfm-table@2.0.0: - resolution: {integrity: sha512-PoHlhypg1ItIucOaHmKE8fbin3vTLpDOUg8KAr8gRCF1MOZI9Nquq2i/44wFvviM4WuxJzc3demT8Y3dkfvYrw==, tarball: https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.0.0.tgz} + micromark-extension-gfm-table@2.1.1: + resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==, tarball: https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz} micromark-extension-gfm-tagfilter@2.0.0: resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==, tarball: https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz} - micromark-extension-gfm-task-list-item@2.0.1: - resolution: {integrity: sha512-cY5PzGcnULaN5O7T+cOzfMoHjBW7j+T9D2sucA5d/KbsBTPcYdebm9zUd9zzdgJGCwahV+/W78Z3nbulBYVbTw==, tarball: https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.0.1.tgz} + micromark-extension-gfm-task-list-item@2.1.0: + resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==, tarball: https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz} micromark-extension-gfm@3.0.0: resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==, tarball: https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz} - micromark-factory-destination@2.0.0: - resolution: {integrity: sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==, tarball: https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.0.tgz} - micromark-factory-destination@2.0.1: resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==, tarball: https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz} - micromark-factory-label@2.0.0: - resolution: {integrity: sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==, tarball: https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.0.tgz} - micromark-factory-label@2.0.1: resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==, tarball: https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz} - micromark-factory-space@2.0.0: - resolution: {integrity: sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==, tarball: https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz} - micromark-factory-space@2.0.1: resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==, tarball: https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz} - micromark-factory-title@2.0.0: - resolution: {integrity: sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==, tarball: https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.0.tgz} - micromark-factory-title@2.0.1: resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==, tarball: https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz} - micromark-factory-whitespace@2.0.0: - resolution: {integrity: sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==, tarball: https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.0.tgz} - micromark-factory-whitespace@2.0.1: resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==, tarball: https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz} - micromark-util-character@2.0.1: - resolution: {integrity: sha512-3wgnrmEAJ4T+mGXAUfMvMAbxU9RDG43XmGce4j6CwPtVxB3vfwXSZ6KhFwDzZ3mZHhmPimMAXg71veiBGzeAZw==, tarball: https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.0.1.tgz} - micromark-util-character@2.1.1: resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==, tarball: https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz} - micromark-util-chunked@2.0.0: - resolution: {integrity: sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==, tarball: https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.0.tgz} - micromark-util-chunked@2.0.1: resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==, tarball: https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz} - micromark-util-classify-character@2.0.0: - resolution: {integrity: sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==, tarball: https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.0.tgz} - micromark-util-classify-character@2.0.1: resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==, tarball: https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz} - micromark-util-combine-extensions@2.0.0: - resolution: {integrity: sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==, tarball: https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.0.tgz} - micromark-util-combine-extensions@2.0.1: resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==, tarball: https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz} - micromark-util-decode-numeric-character-reference@2.0.1: - resolution: {integrity: sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==, tarball: https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.1.tgz} - micromark-util-decode-numeric-character-reference@2.0.2: resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==, tarball: https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz} - micromark-util-decode-string@2.0.0: - resolution: {integrity: sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==, tarball: https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.0.tgz} - micromark-util-decode-string@2.0.1: resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==, tarball: https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz} micromark-util-encode@2.0.1: resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==, tarball: https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz} - micromark-util-html-tag-name@2.0.0: - resolution: {integrity: sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==, tarball: https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.0.tgz} - micromark-util-html-tag-name@2.0.1: resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==, tarball: https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz} - micromark-util-normalize-identifier@2.0.0: - resolution: {integrity: sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==, tarball: https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.0.tgz} - micromark-util-normalize-identifier@2.0.1: resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==, tarball: https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz} - micromark-util-resolve-all@2.0.0: - resolution: {integrity: sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==, tarball: https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.0.tgz} - micromark-util-resolve-all@2.0.1: resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==, tarball: https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz} micromark-util-sanitize-uri@2.0.1: resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==, tarball: https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz} - micromark-util-subtokenize@2.0.0: - resolution: {integrity: sha512-vc93L1t+gpR3p8jxeVdaYlbV2jTYteDje19rNSS/H5dlhxUYll5Fy6vJ2cDwP8RnsXi818yGty1ayP55y3W6fg==, tarball: https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.0.tgz} - micromark-util-subtokenize@2.1.0: resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==, tarball: https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz} - micromark-util-symbol@2.0.0: - resolution: {integrity: sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==, tarball: https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz} - micromark-util-symbol@2.0.1: resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==, tarball: https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz} - micromark-util-types@2.0.0: - resolution: {integrity: sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==, tarball: https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz} - micromark-util-types@2.0.2: resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==, tarball: https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz} - micromark@4.0.0: - resolution: {integrity: sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==, tarball: https://registry.npmjs.org/micromark/-/micromark-4.0.0.tgz} - micromark@4.0.2: resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==, tarball: https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz} @@ -5797,8 +5722,8 @@ packages: resolution: {integrity: sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==, tarball: https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz} engines: {node: '>= 0.4'} - remark-gfm@4.0.0: - resolution: {integrity: sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA==, tarball: https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.0.tgz} + remark-gfm@4.0.1: + resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==, tarball: https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz} remark-parse@11.0.0: resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==, tarball: https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz} @@ -6210,9 +6135,6 @@ packages: trim-lines@3.0.1: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==, tarball: https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz} - trough@2.1.0: - resolution: {integrity: sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==, tarball: https://registry.npmjs.org/trough/-/trough-2.1.0.tgz} - trough@2.2.0: resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==, tarball: https://registry.npmjs.org/trough/-/trough-2.2.0.tgz} @@ -6324,9 +6246,6 @@ packages: resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==, tarball: https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz} engines: {node: '>=18'} - unified@11.0.4: - resolution: {integrity: sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ==, tarball: https://registry.npmjs.org/unified/-/unified-11.0.4.tgz} - unified@11.0.5: resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==, tarball: https://registry.npmjs.org/unified/-/unified-11.0.5.tgz} @@ -9122,10 +9041,6 @@ snapshots: '@types/lodash@4.17.20': {} - '@types/mdast@4.0.3': - dependencies: - '@types/unist': 3.0.2 - '@types/mdast@4.0.4': dependencies: '@types/unist': 3.0.3 @@ -9242,8 +9157,6 @@ snapshots: '@types/unist@2.0.11': {} - '@types/unist@3.0.2': {} - '@types/unist@3.0.3': {} '@types/uuid@9.0.2': {} @@ -9914,10 +9827,6 @@ snapshots: decimal.js@10.4.3: {} - decode-named-character-reference@1.0.2: - dependencies: - character-entities: 2.0.2 - decode-named-character-reference@1.2.0: dependencies: character-entities: 2.0.2 @@ -11462,36 +11371,19 @@ snapshots: dependencies: tmpl: 1.0.5 - markdown-table@3.0.3: {} + markdown-table@3.0.4: {} material-colors@1.2.6: {} math-intrinsics@1.1.0: {} - mdast-util-find-and-replace@3.0.1: + mdast-util-find-and-replace@3.0.2: dependencies: '@types/mdast': 4.0.4 escape-string-regexp: 5.0.0 unist-util-is: 6.0.0 unist-util-visit-parents: 6.0.1 - mdast-util-from-markdown@2.0.0: - dependencies: - '@types/mdast': 4.0.4 - '@types/unist': 3.0.3 - decode-named-character-reference: 1.0.2 - devlop: 1.1.0 - mdast-util-to-string: 4.0.0 - micromark: 4.0.0 - micromark-util-decode-numeric-character-reference: 2.0.1 - micromark-util-decode-string: 2.0.0 - micromark-util-normalize-identifier: 2.0.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.2 - unist-util-stringify-position: 4.0.0 - transitivePeerDependencies: - - supports-color - mdast-util-from-markdown@2.0.2: dependencies: '@types/mdast': 4.0.4 @@ -11509,21 +11401,21 @@ snapshots: transitivePeerDependencies: - supports-color - mdast-util-gfm-autolink-literal@2.0.0: + mdast-util-gfm-autolink-literal@2.0.1: dependencies: '@types/mdast': 4.0.4 ccount: 2.0.1 devlop: 1.1.0 - mdast-util-find-and-replace: 3.0.1 - micromark-util-character: 2.0.1 + mdast-util-find-and-replace: 3.0.2 + micromark-util-character: 2.1.1 - mdast-util-gfm-footnote@2.0.0: + mdast-util-gfm-footnote@2.1.0: dependencies: '@types/mdast': 4.0.4 devlop: 1.1.0 mdast-util-from-markdown: 2.0.2 - mdast-util-to-markdown: 2.1.0 - micromark-util-normalize-identifier: 2.0.0 + mdast-util-to-markdown: 2.1.2 + micromark-util-normalize-identifier: 2.0.1 transitivePeerDependencies: - supports-color @@ -11531,7 +11423,7 @@ snapshots: dependencies: '@types/mdast': 4.0.4 mdast-util-from-markdown: 2.0.2 - mdast-util-to-markdown: 2.1.0 + mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color @@ -11539,9 +11431,9 @@ snapshots: dependencies: '@types/mdast': 4.0.4 devlop: 1.1.0 - markdown-table: 3.0.3 + markdown-table: 3.0.4 mdast-util-from-markdown: 2.0.2 - mdast-util-to-markdown: 2.1.0 + mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color @@ -11550,19 +11442,19 @@ snapshots: '@types/mdast': 4.0.4 devlop: 1.1.0 mdast-util-from-markdown: 2.0.2 - mdast-util-to-markdown: 2.1.0 + mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color - mdast-util-gfm@3.0.0: + mdast-util-gfm@3.1.0: dependencies: - mdast-util-from-markdown: 2.0.0 - mdast-util-gfm-autolink-literal: 2.0.0 - mdast-util-gfm-footnote: 2.0.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-gfm-autolink-literal: 2.0.1 + mdast-util-gfm-footnote: 2.1.0 mdast-util-gfm-strikethrough: 2.0.0 mdast-util-gfm-table: 2.0.0 mdast-util-gfm-task-list-item: 2.0.0 - mdast-util-to-markdown: 2.1.0 + mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color @@ -11605,11 +11497,6 @@ snapshots: transitivePeerDependencies: - supports-color - mdast-util-phrasing@4.0.0: - dependencies: - '@types/mdast': 4.0.4 - unist-util-is: 6.0.0 - mdast-util-phrasing@4.1.0: dependencies: '@types/mdast': 4.0.4 @@ -11627,17 +11514,6 @@ snapshots: unist-util-visit: 5.0.0 vfile: 6.0.3 - mdast-util-to-markdown@2.1.0: - dependencies: - '@types/mdast': 4.0.4 - '@types/unist': 3.0.3 - longest-streak: 3.1.0 - mdast-util-phrasing: 4.0.0 - mdast-util-to-string: 4.0.0 - micromark-util-decode-string: 2.0.0 - unist-util-visit: 5.0.0 - zwitch: 2.0.4 - mdast-util-to-markdown@2.1.2: dependencies: '@types/mdast': 4.0.4 @@ -11666,25 +11542,6 @@ snapshots: methods@1.1.2: {} - micromark-core-commonmark@2.0.0: - dependencies: - decode-named-character-reference: 1.2.0 - devlop: 1.1.0 - micromark-factory-destination: 2.0.0 - micromark-factory-label: 2.0.0 - micromark-factory-space: 2.0.0 - micromark-factory-title: 2.0.0 - micromark-factory-whitespace: 2.0.0 - micromark-util-character: 2.1.1 - micromark-util-chunked: 2.0.0 - micromark-util-classify-character: 2.0.0 - micromark-util-html-tag-name: 2.0.0 - micromark-util-normalize-identifier: 2.0.1 - micromark-util-resolve-all: 2.0.0 - micromark-util-subtokenize: 2.0.0 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - micromark-core-commonmark@2.0.3: dependencies: decode-named-character-reference: 1.2.0 @@ -11704,68 +11561,62 @@ snapshots: micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 - micromark-extension-gfm-autolink-literal@2.0.0: + micromark-extension-gfm-autolink-literal@2.1.0: dependencies: - micromark-util-character: 2.0.1 + micromark-util-character: 2.1.1 micromark-util-sanitize-uri: 2.0.1 - micromark-util-symbol: 2.0.0 + micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 - micromark-extension-gfm-footnote@2.0.0: + micromark-extension-gfm-footnote@2.1.0: dependencies: devlop: 1.1.0 - micromark-core-commonmark: 2.0.0 - micromark-factory-space: 2.0.0 - micromark-util-character: 2.0.1 - micromark-util-normalize-identifier: 2.0.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-normalize-identifier: 2.0.1 micromark-util-sanitize-uri: 2.0.1 - micromark-util-symbol: 2.0.0 + micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 - micromark-extension-gfm-strikethrough@2.0.0: + micromark-extension-gfm-strikethrough@2.1.0: dependencies: devlop: 1.1.0 - micromark-util-chunked: 2.0.0 - micromark-util-classify-character: 2.0.0 - micromark-util-resolve-all: 2.0.0 - micromark-util-symbol: 2.0.0 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 - micromark-extension-gfm-table@2.0.0: + micromark-extension-gfm-table@2.1.1: dependencies: devlop: 1.1.0 - micromark-factory-space: 2.0.0 - micromark-util-character: 2.0.1 - micromark-util-symbol: 2.0.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 micromark-extension-gfm-tagfilter@2.0.0: dependencies: micromark-util-types: 2.0.2 - micromark-extension-gfm-task-list-item@2.0.1: + micromark-extension-gfm-task-list-item@2.1.0: dependencies: devlop: 1.1.0 - micromark-factory-space: 2.0.0 - micromark-util-character: 2.0.1 - micromark-util-symbol: 2.0.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 micromark-extension-gfm@3.0.0: dependencies: - micromark-extension-gfm-autolink-literal: 2.0.0 - micromark-extension-gfm-footnote: 2.0.0 - micromark-extension-gfm-strikethrough: 2.0.0 - micromark-extension-gfm-table: 2.0.0 + micromark-extension-gfm-autolink-literal: 2.1.0 + micromark-extension-gfm-footnote: 2.1.0 + micromark-extension-gfm-strikethrough: 2.1.0 + micromark-extension-gfm-table: 2.1.1 micromark-extension-gfm-tagfilter: 2.0.0 - micromark-extension-gfm-task-list-item: 2.0.1 - micromark-util-combine-extensions: 2.0.0 - micromark-util-types: 2.0.0 - - micromark-factory-destination@2.0.0: - dependencies: - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 + micromark-extension-gfm-task-list-item: 2.1.0 + micromark-util-combine-extensions: 2.0.1 micromark-util-types: 2.0.2 micromark-factory-destination@2.0.1: @@ -11774,13 +11625,6 @@ snapshots: micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 - micromark-factory-label@2.0.0: - dependencies: - devlop: 1.1.0 - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - micromark-factory-label@2.0.1: dependencies: devlop: 1.1.0 @@ -11788,23 +11632,11 @@ snapshots: micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 - micromark-factory-space@2.0.0: - dependencies: - micromark-util-character: 2.1.1 - micromark-util-types: 2.0.2 - micromark-factory-space@2.0.1: dependencies: micromark-util-character: 2.1.1 micromark-util-types: 2.0.2 - micromark-factory-title@2.0.0: - dependencies: - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - micromark-factory-title@2.0.1: dependencies: micromark-factory-space: 2.0.1 @@ -11812,13 +11644,6 @@ snapshots: micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 - micromark-factory-whitespace@2.0.0: - dependencies: - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - micromark-factory-whitespace@2.0.1: dependencies: micromark-factory-space: 2.0.1 @@ -11826,61 +11651,30 @@ snapshots: micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 - micromark-util-character@2.0.1: - dependencies: - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - micromark-util-character@2.1.1: dependencies: micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 - micromark-util-chunked@2.0.0: - dependencies: - micromark-util-symbol: 2.0.1 - micromark-util-chunked@2.0.1: dependencies: micromark-util-symbol: 2.0.1 - micromark-util-classify-character@2.0.0: - dependencies: - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - micromark-util-classify-character@2.0.1: dependencies: micromark-util-character: 2.1.1 micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 - micromark-util-combine-extensions@2.0.0: - dependencies: - micromark-util-chunked: 2.0.0 - micromark-util-types: 2.0.2 - micromark-util-combine-extensions@2.0.1: dependencies: micromark-util-chunked: 2.0.1 micromark-util-types: 2.0.2 - micromark-util-decode-numeric-character-reference@2.0.1: - dependencies: - micromark-util-symbol: 2.0.1 - micromark-util-decode-numeric-character-reference@2.0.2: dependencies: micromark-util-symbol: 2.0.1 - micromark-util-decode-string@2.0.0: - dependencies: - decode-named-character-reference: 1.2.0 - micromark-util-character: 2.1.1 - micromark-util-decode-numeric-character-reference: 2.0.2 - micromark-util-symbol: 2.0.1 - micromark-util-decode-string@2.0.1: dependencies: decode-named-character-reference: 1.2.0 @@ -11890,22 +11684,12 @@ snapshots: micromark-util-encode@2.0.1: {} - micromark-util-html-tag-name@2.0.0: {} - micromark-util-html-tag-name@2.0.1: {} - micromark-util-normalize-identifier@2.0.0: - dependencies: - micromark-util-symbol: 2.0.1 - micromark-util-normalize-identifier@2.0.1: dependencies: micromark-util-symbol: 2.0.1 - micromark-util-resolve-all@2.0.0: - dependencies: - micromark-util-types: 2.0.2 - micromark-util-resolve-all@2.0.1: dependencies: micromark-util-types: 2.0.2 @@ -11916,13 +11700,6 @@ snapshots: micromark-util-encode: 2.0.1 micromark-util-symbol: 2.0.1 - micromark-util-subtokenize@2.0.0: - dependencies: - devlop: 1.1.0 - micromark-util-chunked: 2.0.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - micromark-util-subtokenize@2.1.0: dependencies: devlop: 1.1.0 @@ -11930,36 +11707,10 @@ snapshots: micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 - micromark-util-symbol@2.0.0: {} - micromark-util-symbol@2.0.1: {} - micromark-util-types@2.0.0: {} - micromark-util-types@2.0.2: {} - micromark@4.0.0: - dependencies: - '@types/debug': 4.1.12 - debug: 4.4.3 - decode-named-character-reference: 1.2.0 - devlop: 1.1.0 - micromark-core-commonmark: 2.0.0 - micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.1 - micromark-util-chunked: 2.0.0 - micromark-util-combine-extensions: 2.0.0 - micromark-util-decode-numeric-character-reference: 2.0.2 - micromark-util-encode: 2.0.1 - micromark-util-normalize-identifier: 2.0.1 - micromark-util-resolve-all: 2.0.0 - micromark-util-sanitize-uri: 2.0.1 - micromark-util-subtokenize: 2.0.0 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - transitivePeerDependencies: - - supports-color - micromark@4.0.2: dependencies: '@types/debug': 4.1.12 @@ -12684,14 +12435,14 @@ snapshots: define-properties: 1.2.1 set-function-name: 2.0.1 - remark-gfm@4.0.0: + remark-gfm@4.0.1: dependencies: - '@types/mdast': 4.0.3 - mdast-util-gfm: 3.0.0 + '@types/mdast': 4.0.4 + mdast-util-gfm: 3.1.0 micromark-extension-gfm: 3.0.0 remark-parse: 11.0.0 remark-stringify: 11.0.0 - unified: 11.0.4 + unified: 11.0.5 transitivePeerDependencies: - supports-color @@ -12715,7 +12466,7 @@ snapshots: remark-stringify@11.0.0: dependencies: '@types/mdast': 4.0.4 - mdast-util-to-markdown: 2.1.0 + mdast-util-to-markdown: 2.1.2 unified: 11.0.5 require-directory@2.1.1: {} @@ -13166,8 +12917,6 @@ snapshots: trim-lines@3.0.1: {} - trough@2.1.0: {} - trough@2.2.0: {} ts-dedent@2.2.0: {} @@ -13264,16 +13013,6 @@ snapshots: unicorn-magic@0.3.0: {} - unified@11.0.4: - dependencies: - '@types/unist': 3.0.2 - bail: 2.0.2 - devlop: 1.1.0 - extend: 3.0.2 - is-plain-obj: 4.1.0 - trough: 2.1.0 - vfile: 6.0.3 - unified@11.0.5: dependencies: '@types/unist': 3.0.3 From 41420aea3c1636e86d92d99b9b41bf68f4fe5b60 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 23:11:11 +0000 Subject: [PATCH 034/298] chore: bump ts-proto from 1.164.0 to 1.181.2 in /site (#20080) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [ts-proto](https://github.com/stephenh/ts-proto) from 1.164.0 to 1.181.2.
Release notes

Sourced from ts-proto's releases.

v1.181.2

1.181.2 (2024-08-15)

Bug Fixes

  • toJSON Function with removeEnumPrefix=true and unrecognizedEnumValue=0 Options (#1089) (2401490), closes #1086 #1086

v1.181.1

1.181.1 (2024-07-13)

Bug Fixes

  • Incorrect message names in the generated code for repeated fields (#1073) (8a95d8e), closes #1072

v1.181.0

1.181.0 (2024-07-01)

Features

v1.180.0

1.180.0 (2024-06-15)

Features

  • oneof=unions-value to use the same field name for oneof cases (#1062) (7493090), closes #1060

v1.179.0

1.179.0 (2024-06-15)

Features

v1.178.0

1.178.0 (2024-06-07)

Features

  • no-file-descriptor setting for outputSchema option (#1047) (c54f06c)

v1.177.0

1.177.0 (2024-06-07)

... (truncated)

Changelog

Sourced from ts-proto's changelog.

1.181.2 (2024-08-15)

Bug Fixes

  • toJSON Function with removeEnumPrefix=true and unrecognizedEnumValue=0 Options (#1089) (2401490), closes #1086 #1086

1.181.1 (2024-07-13)

Bug Fixes

  • Incorrect message names in the generated code for repeated fields (#1073) (8a95d8e), closes #1072

1.181.0 (2024-07-01)

Features

1.180.0 (2024-06-15)

Features

  • oneof=unions-value to use the same field name for oneof cases (#1062) (7493090), closes #1060

1.179.0 (2024-06-15)

Features

1.178.0 (2024-06-07)

Features

  • no-file-descriptor setting for outputSchema option (#1047) (c54f06c)

1.177.0 (2024-06-07)

Features

1.176.3 (2024-06-07)

... (truncated)

Commits
  • 290d0c5 chore(release): 1.181.2 [skip ci]
  • 2401490 fix: toJSON Function with removeEnumPrefix=true and `unrecognizedEnumValue=...
  • 76243a8 chore(release): 1.181.1 [skip ci]
  • 8a95d8e fix: Incorrect message names in the generated code for repeated fields (#1073)
  • 2078d1a Remove .DS_Store (#1071)
  • c5546fb chore(release): 1.181.0 [skip ci]
  • ab515cd feat: added the "typePrefix" and "typeSuffix" options. (#1069)
  • 14e7e0f docs: Describe oneof=unions-value output as Algebraic Data Type (#1065)
  • 84ccaf2 chore(release): 1.180.0 [skip ci]
  • 7493090 feat: oneof=unions-value to use the same field name for oneof cases (#1062)
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=ts-proto&package-manager=npm_and_yarn&previous-version=1.164.0&new-version=1.181.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: ケイラ --- .../e2e/google/protobuf/timestampGenerated.ts | 6 ++ site/e2e/provisionerGenerated.ts | 62 ++++++++++--------- site/package.json | 2 +- site/pnpm-lock.yaml | 33 +++++----- 4 files changed, 60 insertions(+), 43 deletions(-) diff --git a/site/e2e/google/protobuf/timestampGenerated.ts b/site/e2e/google/protobuf/timestampGenerated.ts index 6dd4b08e96087..6cddbb0b0b781 100644 --- a/site/e2e/google/protobuf/timestampGenerated.ts +++ b/site/e2e/google/protobuf/timestampGenerated.ts @@ -1,3 +1,9 @@ +// Code generated by protoc-gen-ts_proto. DO NOT EDIT. +// versions: +// protoc-gen-ts_proto v1.181.2 +// protoc v4.23.4 +// source: google/protobuf/timestamp.proto + /* eslint-disable */ import * as _m0 from "protobufjs/minimal"; diff --git a/site/e2e/provisionerGenerated.ts b/site/e2e/provisionerGenerated.ts index 82fd25db9258d..65e0557c1b779 100644 --- a/site/e2e/provisionerGenerated.ts +++ b/site/e2e/provisionerGenerated.ts @@ -1,3 +1,9 @@ +// Code generated by protoc-gen-ts_proto. DO NOT EDIT. +// versions: +// protoc-gen-ts_proto v1.181.2 +// protoc v4.23.4 +// source: provisioner.proto + /* eslint-disable */ import * as _m0 from "protobufjs/minimal"; import { Observable } from "rxjs"; @@ -560,10 +566,10 @@ export const TemplateVariable = { if (message.defaultValue !== "") { writer.uint32(34).string(message.defaultValue); } - if (message.required === true) { + if (message.required !== false) { writer.uint32(40).bool(message.required); } - if (message.sensitive === true) { + if (message.sensitive !== false) { writer.uint32(48).bool(message.sensitive); } return writer; @@ -599,7 +605,7 @@ export const RichParameter = { if (message.type !== "") { writer.uint32(26).string(message.type); } - if (message.mutable === true) { + if (message.mutable !== false) { writer.uint32(32).bool(message.mutable); } if (message.defaultValue !== "") { @@ -626,7 +632,7 @@ export const RichParameter = { if (message.validationMonotonic !== "") { writer.uint32(98).string(message.validationMonotonic); } - if (message.required === true) { + if (message.required !== false) { writer.uint32(104).bool(message.required); } if (message.displayName !== "") { @@ -635,7 +641,7 @@ export const RichParameter = { if (message.order !== 0) { writer.uint32(128).int32(message.order); } - if (message.ephemeral === true) { + if (message.ephemeral !== false) { writer.uint32(136).bool(message.ephemeral); } if (message.formType !== 0) { @@ -716,7 +722,7 @@ export const Preset = { if (message.prebuild !== undefined) { Prebuild.encode(message.prebuild, writer.uint32(26).fork()).ldelim(); } - if (message.default === true) { + if (message.default !== false) { writer.uint32(32).bool(message.default); } if (message.description !== "") { @@ -761,7 +767,7 @@ export const VariableValue = { if (message.value !== "") { writer.uint32(18).string(message.value); } - if (message.sensitive === true) { + if (message.sensitive !== false) { writer.uint32(24).bool(message.sensitive); } return writer; @@ -794,7 +800,7 @@ export const ExternalAuthProviderResource = { if (message.id !== "") { writer.uint32(10).string(message.id); } - if (message.optional === true) { + if (message.optional !== false) { writer.uint32(16).bool(message.optional); } return writer; @@ -929,7 +935,7 @@ export const ResourcesMonitoring = { export const MemoryResourceMonitor = { encode(message: MemoryResourceMonitor, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { - if (message.enabled === true) { + if (message.enabled !== false) { writer.uint32(8).bool(message.enabled); } if (message.threshold !== 0) { @@ -944,7 +950,7 @@ export const VolumeResourceMonitor = { if (message.path !== "") { writer.uint32(10).string(message.path); } - if (message.enabled === true) { + if (message.enabled !== false) { writer.uint32(16).bool(message.enabled); } if (message.threshold !== 0) { @@ -956,19 +962,19 @@ export const VolumeResourceMonitor = { export const DisplayApps = { encode(message: DisplayApps, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { - if (message.vscode === true) { + if (message.vscode !== false) { writer.uint32(8).bool(message.vscode); } - if (message.vscodeInsiders === true) { + if (message.vscodeInsiders !== false) { writer.uint32(16).bool(message.vscodeInsiders); } - if (message.webTerminal === true) { + if (message.webTerminal !== false) { writer.uint32(24).bool(message.webTerminal); } - if (message.sshHelper === true) { + if (message.sshHelper !== false) { writer.uint32(32).bool(message.sshHelper); } - if (message.portForwardingHelper === true) { + if (message.portForwardingHelper !== false) { writer.uint32(40).bool(message.portForwardingHelper); } return writer; @@ -1001,13 +1007,13 @@ export const Script = { if (message.cron !== "") { writer.uint32(34).string(message.cron); } - if (message.startBlocksLogin === true) { + if (message.startBlocksLogin !== false) { writer.uint32(40).bool(message.startBlocksLogin); } - if (message.runOnStart === true) { + if (message.runOnStart !== false) { writer.uint32(48).bool(message.runOnStart); } - if (message.runOnStop === true) { + if (message.runOnStop !== false) { writer.uint32(56).bool(message.runOnStop); } if (message.timeoutSeconds !== 0) { @@ -1052,7 +1058,7 @@ export const App = { if (message.icon !== "") { writer.uint32(42).string(message.icon); } - if (message.subdomain === true) { + if (message.subdomain !== false) { writer.uint32(48).bool(message.subdomain); } if (message.healthcheck !== undefined) { @@ -1061,13 +1067,13 @@ export const App = { if (message.sharingLevel !== 0) { writer.uint32(64).int32(message.sharingLevel); } - if (message.external === true) { + if (message.external !== false) { writer.uint32(72).bool(message.external); } if (message.order !== 0) { writer.uint32(80).int64(message.order); } - if (message.hidden === true) { + if (message.hidden !== false) { writer.uint32(88).bool(message.hidden); } if (message.openIn !== 0) { @@ -1115,7 +1121,7 @@ export const Resource = { for (const v of message.metadata) { Resource_Metadata.encode(v!, writer.uint32(34).fork()).ldelim(); } - if (message.hide === true) { + if (message.hide !== false) { writer.uint32(40).bool(message.hide); } if (message.icon !== "") { @@ -1142,10 +1148,10 @@ export const Resource_Metadata = { if (message.value !== "") { writer.uint32(18).string(message.value); } - if (message.sensitive === true) { + if (message.sensitive !== false) { writer.uint32(24).bool(message.sensitive); } - if (message.isNull === true) { + if (message.isNull !== false) { writer.uint32(32).bool(message.isNull); } return writer; @@ -1352,7 +1358,7 @@ export const PlanRequest = { for (const v of message.previousParameterValues) { RichParameterValue.encode(v!, writer.uint32(42).fork()).ldelim(); } - if (message.omitModuleFiles === true) { + if (message.omitModuleFiles !== false) { writer.uint32(48).bool(message.omitModuleFiles); } return writer; @@ -1394,13 +1400,13 @@ export const PlanComplete = { if (message.moduleFilesHash.length !== 0) { writer.uint32(98).bytes(message.moduleFilesHash); } - if (message.hasAiTasks === true) { + if (message.hasAiTasks !== false) { writer.uint32(104).bool(message.hasAiTasks); } for (const v of message.aiTasks) { AITask.encode(v!, writer.uint32(114).fork()).ldelim(); } - if (message.hasExternalAgents === true) { + if (message.hasExternalAgents !== false) { writer.uint32(120).bool(message.hasExternalAgents); } return writer; @@ -1571,7 +1577,7 @@ export interface Provisioner { } function toTimestamp(date: Date): Timestamp { - const seconds = date.getTime() / 1_000; + const seconds = Math.trunc(date.getTime() / 1_000); const nanos = (date.getTime() % 1_000) * 1_000_000; return { seconds, nanos }; } diff --git a/site/package.json b/site/package.json index 7ba86b7881665..ddb04c084ce01 100644 --- a/site/package.json +++ b/site/package.json @@ -178,7 +178,7 @@ "storybook": "9.1.2", "storybook-addon-remix-react-router": "5.0.0", "tailwindcss": "3.4.18", - "ts-proto": "1.164.0", + "ts-proto": "1.181.2", "typescript": "5.6.3", "vite": "7.1.7", "vite-plugin-checker": "0.11.0" diff --git a/site/pnpm-lock.yaml b/site/pnpm-lock.yaml index 49f5c686087b8..396028241502c 100644 --- a/site/pnpm-lock.yaml +++ b/site/pnpm-lock.yaml @@ -444,8 +444,8 @@ importers: specifier: 3.4.18 version: 3.4.18(yaml@2.7.0) ts-proto: - specifier: 1.164.0 - version: 1.164.0 + specifier: 1.181.2 + version: 1.181.2 typescript: specifier: 5.6.3 version: 5.6.3 @@ -4863,6 +4863,9 @@ packages: long@5.2.3: resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==, tarball: https://registry.npmjs.org/long/-/long-5.2.3.tgz} + long@5.3.2: + resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==, tarball: https://registry.npmjs.org/long/-/long-5.3.2.tgz} + longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==, tarball: https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz} @@ -6159,14 +6162,14 @@ packages: '@swc/wasm': optional: true - ts-poet@6.11.0: - resolution: {integrity: sha512-r5AGF8vvb+GjBsnqiTqbLhN1/U2FJt6BI+k0dfCrkKzWvUhNlwMmq9nDHuucHs45LomgHjZPvYj96dD3JawjJA==, tarball: https://registry.npmjs.org/ts-poet/-/ts-poet-6.11.0.tgz} + ts-poet@6.12.0: + resolution: {integrity: sha512-xo+iRNMWqyvXpFTaOAvLPA5QAWO6TZrSUs5s4Odaya3epqofBu/fMLHEWl8jPmjhA0s9sgj9sNvF1BmaQlmQkA==, tarball: https://registry.npmjs.org/ts-poet/-/ts-poet-6.12.0.tgz} - ts-proto-descriptors@1.15.0: - resolution: {integrity: sha512-TYyJ7+H+7Jsqawdv+mfsEpZPTIj9siDHS6EMCzG/z3b/PZiphsX+mWtqFfFVe5/N0Th6V3elK9lQqjnrgTOfrg==, tarball: https://registry.npmjs.org/ts-proto-descriptors/-/ts-proto-descriptors-1.15.0.tgz} + ts-proto-descriptors@1.16.0: + resolution: {integrity: sha512-3yKuzMLpltdpcyQji1PJZRfoo4OJjNieKTYkQY8pF7xGKsYz/RHe3aEe4KiRxcinoBmnEhmuI+yJTxLb922ULA==, tarball: https://registry.npmjs.org/ts-proto-descriptors/-/ts-proto-descriptors-1.16.0.tgz} - ts-proto@1.164.0: - resolution: {integrity: sha512-yIyMucjcozS7Vxtyy5mH6C8ltbY4gEBVNW4ymZ0kWiKlyMxsvhyUZ63CbxcF7dCKQVjHR+fLJ3SiorfgyhQ+AQ==, tarball: https://registry.npmjs.org/ts-proto/-/ts-proto-1.164.0.tgz} + ts-proto@1.181.2: + resolution: {integrity: sha512-knJ8dtjn2Pd0c5ZGZG8z9DMiD4PUY8iGI9T9tb8DvGdWRMkLpf0WcPO7G+7cmbZyxvNTAG6ci3fybEaFgMZIvg==, tarball: https://registry.npmjs.org/ts-proto/-/ts-proto-1.181.2.tgz} hasBin: true tsconfig-paths@4.2.0: @@ -11329,6 +11332,8 @@ snapshots: long@5.2.3: {} + long@5.3.2: {} + longest-streak@3.1.0: {} loose-envify@1.4.0: @@ -12944,21 +12949,21 @@ snapshots: '@swc/core': 1.3.38 optional: true - ts-poet@6.11.0: + ts-poet@6.12.0: dependencies: dprint-node: 1.0.8 - ts-proto-descriptors@1.15.0: + ts-proto-descriptors@1.16.0: dependencies: - long: 5.2.3 + long: 5.3.2 protobufjs: 7.4.0 - ts-proto@1.164.0: + ts-proto@1.181.2: dependencies: case-anything: 2.1.13 protobufjs: 7.4.0 - ts-poet: 6.11.0 - ts-proto-descriptors: 1.15.0 + ts-poet: 6.12.0 + ts-proto-descriptors: 1.16.0 tsconfig-paths@4.2.0: dependencies: From f84a789b40284e8c7f9192ac0cf2b6764a197b36 Mon Sep 17 00:00:00 2001 From: Ethan <39577870+ethanndickson@users.noreply.github.com> Date: Thu, 2 Oct 2025 10:45:52 +1000 Subject: [PATCH 035/298] feat(scaletest): add runner for thundering herd autostart (#19998) Relates to https://github.com/coder/internal/issues/911 This PR adds a scaletest runner that simulates a user with a workspace configured to autostart at a specific time. An instance of this autostart runner does the following: 1. Creates a user. 2. Creates a workspace. 3. Listens on `/api/v2/workspaces//watch` to wait for build completions throughout the run. 4. Stops the workspace, waits for the build to finish. 4. Waits for all other concurrently executing runners (per the `SetupBarrier` `WaitGroup`) to also have a stopped workspace. 5. Sets the workspace autostart time to `time.Now().Add(cfg.AutoStartDelay)` 6. Waits for the autostart workspace build to begin, enter the `pending` status, and then `running`. 7. Waits for the autostart workspace build to end. Exposes four prometheus metrics: - `autostart_job_creation_latency_seconds` - HistogramVec. Labels = {username, workspace_name} - `autostart_job_acquired_latency_seconds` - HistogramVec. Labels = {username, workspace_name} - `autostart_total_latency_seconds` - `HistogramVec`. Labels = `{username, workspace_name}`. - `autostart_errors_total` - `CounterVec`. Labels = `{username, action}`. --- scaletest/autostart/config.go | 75 +++++++++ scaletest/autostart/metrics.go | 65 ++++++++ scaletest/autostart/run.go | 245 +++++++++++++++++++++++++++++ scaletest/autostart/run_test.go | 158 +++++++++++++++++++ scaletest/workspacebuild/config.go | 3 + scaletest/workspacebuild/run.go | 40 ++--- 6 files changed, 568 insertions(+), 18 deletions(-) create mode 100644 scaletest/autostart/config.go create mode 100644 scaletest/autostart/metrics.go create mode 100644 scaletest/autostart/run.go create mode 100644 scaletest/autostart/run_test.go diff --git a/scaletest/autostart/config.go b/scaletest/autostart/config.go new file mode 100644 index 0000000000000..ad804a0b89666 --- /dev/null +++ b/scaletest/autostart/config.go @@ -0,0 +1,75 @@ +package autostart + +import ( + "sync" + "time" + + "golang.org/x/xerrors" + + "github.com/coder/coder/v2/codersdk" + "github.com/coder/coder/v2/scaletest/createusers" + "github.com/coder/coder/v2/scaletest/workspacebuild" +) + +type Config struct { + // User is the configuration for the user to create. + User createusers.Config `json:"user"` + + // Workspace is the configuration for the workspace to create. The workspace + // will be built using the new user. + // + // OrganizationID is ignored and set to the new user's organization ID. + Workspace workspacebuild.Config `json:"workspace"` + + // WorkspaceJobTimeout is how long to wait for any one workspace job + // (start or stop) to complete. + WorkspaceJobTimeout time.Duration `json:"workspace_job_timeout"` + + // AutostartDelay is how long after all the workspaces have been stopped + // to schedule them to be started again. + AutostartDelay time.Duration `json:"autostart_delay"` + + // AutostartTimeout is how long to wait for the autostart build to be + // initiated after the scheduled time. + AutostartTimeout time.Duration `json:"autostart_timeout"` + + Metrics *Metrics `json:"-"` + + // SetupBarrier is used to ensure all runners own stopped workspaces + // before setting the autostart schedule on each. + SetupBarrier *sync.WaitGroup `json:"-"` +} + +func (c Config) Validate() error { + if err := c.User.Validate(); err != nil { + return xerrors.Errorf("user config: %w", err) + } + c.Workspace.OrganizationID = c.User.OrganizationID + // This value will be overwritten during the test. + c.Workspace.UserID = codersdk.Me + if err := c.Workspace.Validate(); err != nil { + return xerrors.Errorf("workspace config: %w", err) + } + + if c.SetupBarrier == nil { + return xerrors.New("setup barrier must be set") + } + + if c.WorkspaceJobTimeout <= 0 { + return xerrors.New("workspace_job_timeout must be greater than 0") + } + + if c.AutostartDelay < time.Minute*2 { + return xerrors.New("autostart_delay must be at least 2 minutes") + } + + if c.AutostartTimeout <= 0 { + return xerrors.New("autostart_timeout must be greater than 0") + } + + if c.Metrics == nil { + return xerrors.New("metrics must be set") + } + + return nil +} diff --git a/scaletest/autostart/metrics.go b/scaletest/autostart/metrics.go new file mode 100644 index 0000000000000..d1ff94e7898c4 --- /dev/null +++ b/scaletest/autostart/metrics.go @@ -0,0 +1,65 @@ +package autostart + +import ( + "time" + + "github.com/prometheus/client_golang/prometheus" +) + +type Metrics struct { + AutostartJobCreationLatencySeconds prometheus.HistogramVec + AutostartJobAcquiredLatencySeconds prometheus.HistogramVec + AutostartTotalLatencySeconds prometheus.HistogramVec + AutostartErrorsTotal prometheus.CounterVec +} + +func NewMetrics(reg prometheus.Registerer) *Metrics { + m := &Metrics{ + AutostartJobCreationLatencySeconds: *prometheus.NewHistogramVec(prometheus.HistogramOpts{ + Namespace: "coderd", + Subsystem: "scaletest", + Name: "autostart_job_creation_latency_seconds", + Help: "Time from when the workspace is scheduled to be autostarted to when the autostart job has been created.", + }, []string{"username", "workspace_name"}), + AutostartJobAcquiredLatencySeconds: *prometheus.NewHistogramVec(prometheus.HistogramOpts{ + Namespace: "coderd", + Subsystem: "scaletest", + Name: "autostart_job_acquired_latency_seconds", + Help: "Time from when the workspace is scheduled to be autostarted to when the job has been acquired by a provisioner daemon.", + }, []string{"username", "workspace_name"}), + AutostartTotalLatencySeconds: *prometheus.NewHistogramVec(prometheus.HistogramOpts{ + Namespace: "coderd", + Subsystem: "scaletest", + Name: "autostart_total_latency_seconds", + Help: "Time from when the workspace is scheduled to be autostarted to when the autostart build has finished.", + }, []string{"username", "workspace_name"}), + AutostartErrorsTotal: *prometheus.NewCounterVec(prometheus.CounterOpts{ + Namespace: "coderd", + Subsystem: "scaletest", + Name: "autostart_errors_total", + Help: "Total number of autostart errors", + }, []string{"username", "action"}), + } + + reg.MustRegister(m.AutostartTotalLatencySeconds) + reg.MustRegister(m.AutostartJobCreationLatencySeconds) + reg.MustRegister(m.AutostartJobAcquiredLatencySeconds) + reg.MustRegister(m.AutostartErrorsTotal) + return m +} + +func (m *Metrics) RecordCompletion(elapsed time.Duration, username string, workspace string) { + m.AutostartTotalLatencySeconds.WithLabelValues(username, workspace).Observe(elapsed.Seconds()) +} + +func (m *Metrics) RecordJobCreation(elapsed time.Duration, username string, workspace string) { + m.AutostartJobCreationLatencySeconds.WithLabelValues(username, workspace).Observe(elapsed.Seconds()) +} + +func (m *Metrics) RecordJobAcquired(elapsed time.Duration, username string, workspace string) { + m.AutostartJobAcquiredLatencySeconds.WithLabelValues(username, workspace).Observe(elapsed.Seconds()) +} + +func (m *Metrics) AddError(username string, action string) { + m.AutostartErrorsTotal.WithLabelValues(username, action).Inc() +} diff --git a/scaletest/autostart/run.go b/scaletest/autostart/run.go new file mode 100644 index 0000000000000..59f50a04dd837 --- /dev/null +++ b/scaletest/autostart/run.go @@ -0,0 +1,245 @@ +package autostart + +import ( + "context" + "fmt" + "io" + "time" + + "golang.org/x/xerrors" + + "cdr.dev/slog" + "cdr.dev/slog/sloggers/sloghuman" + "github.com/coder/coder/v2/coderd/tracing" + "github.com/coder/coder/v2/codersdk" + "github.com/coder/coder/v2/scaletest/createusers" + "github.com/coder/coder/v2/scaletest/harness" + "github.com/coder/coder/v2/scaletest/loadtestutil" + "github.com/coder/coder/v2/scaletest/workspacebuild" +) + +type Runner struct { + client *codersdk.Client + cfg Config + + createUserRunner *createusers.Runner + workspacebuildRunner *workspacebuild.Runner + + autostartTotalLatency time.Duration + autostartJobCreationLatency time.Duration + autostartJobAcquiredLatency time.Duration +} + +func NewRunner(client *codersdk.Client, cfg Config) *Runner { + return &Runner{ + client: client, + cfg: cfg, + } +} + +var ( + _ harness.Runnable = &Runner{} + _ harness.Cleanable = &Runner{} + _ harness.Collectable = &Runner{} +) + +func (r *Runner) Run(ctx context.Context, id string, logs io.Writer) error { + ctx, span := tracing.StartSpan(ctx) + defer span.End() + + reachedBarrier := false + defer func() { + if !reachedBarrier { + r.cfg.SetupBarrier.Done() + } + }() + + logs = loadtestutil.NewSyncWriter(logs) + logger := slog.Make(sloghuman.Sink(logs)).Leveled(slog.LevelDebug) + r.client.SetLogger(logger) + r.client.SetLogBodies(true) + + r.createUserRunner = createusers.NewRunner(r.client, r.cfg.User) + newUserAndToken, err := r.createUserRunner.RunReturningUser(ctx, id, logs) + if err != nil { + r.cfg.Metrics.AddError("", "create_user") + return xerrors.Errorf("create user: %w", err) + } + newUser := newUserAndToken.User + + newUserClient := codersdk.New(r.client.URL, + codersdk.WithSessionToken(newUserAndToken.SessionToken), + codersdk.WithLogger(logger), + codersdk.WithLogBodies()) + + //nolint:gocritic // short log is fine + logger.Info(ctx, "user created", slog.F("username", newUser.Username), slog.F("user_id", newUser.ID.String())) + + workspaceBuildConfig := r.cfg.Workspace + workspaceBuildConfig.OrganizationID = r.cfg.User.OrganizationID + workspaceBuildConfig.UserID = newUser.ID.String() + // We'll wait for the build ourselves to avoid multiple API requests + workspaceBuildConfig.NoWaitForBuild = true + + r.workspacebuildRunner = workspacebuild.NewRunner(newUserClient, workspaceBuildConfig) + workspace, err := r.workspacebuildRunner.RunReturningWorkspace(ctx, id, logs) + if err != nil { + r.cfg.Metrics.AddError(newUser.Username, "create_workspace") + return xerrors.Errorf("create workspace: %w", err) + } + + watchCtx, cancel := context.WithCancel(ctx) + defer cancel() + workspaceUpdates, err := newUserClient.WatchWorkspace(watchCtx, workspace.ID) + if err != nil { + r.cfg.Metrics.AddError(newUser.Username, "watch_workspace") + return xerrors.Errorf("watch workspace: %w", err) + } + + createWorkspaceCtx, cancel2 := context.WithTimeout(ctx, r.cfg.WorkspaceJobTimeout) + defer cancel2() + + err = waitForWorkspaceUpdate(createWorkspaceCtx, logger, workspaceUpdates, func(ws codersdk.Workspace) bool { + return ws.LatestBuild.Transition == codersdk.WorkspaceTransitionStart && + ws.LatestBuild.Job.Status == codersdk.ProvisionerJobSucceeded + }) + if err != nil { + r.cfg.Metrics.AddError(newUser.Username, "wait_for_initial_build") + return xerrors.Errorf("timeout waiting for initial workspace build to complete: %w", err) + } + + logger.Info(ctx, "stopping workspace", slog.F("workspace_name", workspace.Name)) + + _, err = newUserClient.CreateWorkspaceBuild(ctx, workspace.ID, codersdk.CreateWorkspaceBuildRequest{ + Transition: codersdk.WorkspaceTransitionStop, + }) + if err != nil { + r.cfg.Metrics.AddError(newUser.Username, "create_stop_build") + return xerrors.Errorf("create stop build: %w", err) + } + + stopBuildCtx, cancel3 := context.WithTimeout(ctx, r.cfg.WorkspaceJobTimeout) + defer cancel3() + + err = waitForWorkspaceUpdate(stopBuildCtx, logger, workspaceUpdates, func(ws codersdk.Workspace) bool { + return ws.LatestBuild.Transition == codersdk.WorkspaceTransitionStop && + ws.LatestBuild.Job.Status == codersdk.ProvisionerJobSucceeded + }) + if err != nil { + r.cfg.Metrics.AddError(newUser.Username, "wait_for_stop_build") + return xerrors.Errorf("timeout waiting for stop build to complete: %w", err) + } + + logger.Info(ctx, "workspace stopped successfully", slog.F("workspace_name", workspace.Name)) + + logger.Info(ctx, "waiting for all runners to reach barrier") + reachedBarrier = true + r.cfg.SetupBarrier.Done() + r.cfg.SetupBarrier.Wait() + logger.Info(ctx, "all runners reached barrier, proceeding with autostart schedule") + + testStartTime := time.Now().UTC() + autostartTime := testStartTime.Add(r.cfg.AutostartDelay).Round(time.Minute) + schedule := fmt.Sprintf("CRON_TZ=UTC %d %d * * *", autostartTime.Minute(), autostartTime.Hour()) + + logger.Info(ctx, "setting autostart schedule for workspace", slog.F("workspace_name", workspace.Name), slog.F("schedule", schedule)) + + err = newUserClient.UpdateWorkspaceAutostart(ctx, workspace.ID, codersdk.UpdateWorkspaceAutostartRequest{ + Schedule: &schedule, + }) + if err != nil { + r.cfg.Metrics.AddError(newUser.Username, "update_workspace_autostart") + return xerrors.Errorf("update workspace autostart: %w", err) + } + + logger.Info(ctx, "waiting for workspace to autostart", slog.F("workspace_name", workspace.Name)) + + autostartInitiateCtx, cancel4 := context.WithDeadline(ctx, autostartTime.Add(r.cfg.AutostartDelay)) + defer cancel4() + + logger.Info(ctx, "listening for workspace updates to detect autostart build") + + err = waitForWorkspaceUpdate(autostartInitiateCtx, logger, workspaceUpdates, func(ws codersdk.Workspace) bool { + if ws.LatestBuild.Transition != codersdk.WorkspaceTransitionStart { + return false + } + + // The job has been created, but it might be pending + if r.autostartJobCreationLatency == 0 { + r.autostartJobCreationLatency = time.Since(autostartTime) + r.cfg.Metrics.RecordJobCreation(r.autostartJobCreationLatency, newUser.Username, workspace.Name) + } + + if ws.LatestBuild.Job.Status == codersdk.ProvisionerJobRunning || + ws.LatestBuild.Job.Status == codersdk.ProvisionerJobSucceeded { + // Job is no longer pending, but it might not have finished + if r.autostartJobAcquiredLatency == 0 { + r.autostartJobAcquiredLatency = time.Since(autostartTime) + r.cfg.Metrics.RecordJobAcquired(r.autostartJobAcquiredLatency, newUser.Username, workspace.Name) + } + return ws.LatestBuild.Job.Status == codersdk.ProvisionerJobSucceeded + } + + return false + }) + if err != nil { + r.cfg.Metrics.AddError(newUser.Username, "wait_for_autostart_build") + return xerrors.Errorf("timeout waiting for autostart build to be created: %w", err) + } + + r.autostartTotalLatency = time.Since(autostartTime) + + logger.Info(ctx, "autostart workspace build complete", slog.F("duration", r.autostartTotalLatency)) + r.cfg.Metrics.RecordCompletion(r.autostartTotalLatency, newUser.Username, workspace.Name) + + return nil +} + +func waitForWorkspaceUpdate(ctx context.Context, logger slog.Logger, updates <-chan codersdk.Workspace, shouldBreak func(codersdk.Workspace) bool) error { + for { + select { + case <-ctx.Done(): + return ctx.Err() + case updatedWorkspace, ok := <-updates: + if !ok { + return xerrors.New("workspace updates channel closed") + } + logger.Debug(ctx, "received workspace update", slog.F("update", updatedWorkspace)) + if shouldBreak(updatedWorkspace) { + return nil + } + } + } +} + +func (r *Runner) Cleanup(ctx context.Context, id string, logs io.Writer) error { + if r.workspacebuildRunner != nil { + _, _ = fmt.Fprintln(logs, "Cleaning up workspace...") + if err := r.workspacebuildRunner.Cleanup(ctx, id, logs); err != nil { + return xerrors.Errorf("cleanup workspace: %w", err) + } + } + + if r.createUserRunner != nil { + _, _ = fmt.Fprintln(logs, "Cleaning up user...") + if err := r.createUserRunner.Cleanup(ctx, id, logs); err != nil { + return xerrors.Errorf("cleanup user: %w", err) + } + } + + return nil +} + +const ( + AutostartTotalLatencyMetric = "autostart_total_latency_seconds" + AutostartJobCreationLatencyMetric = "autostart_job_creation_latency_seconds" + AutostartJobAcquiredLatencyMetric = "autostart_job_acquired_latency_seconds" +) + +func (r *Runner) GetMetrics() map[string]any { + return map[string]any{ + AutostartTotalLatencyMetric: r.autostartTotalLatency.Seconds(), + AutostartJobCreationLatencyMetric: r.autostartJobCreationLatency.Seconds(), + AutostartJobAcquiredLatencyMetric: r.autostartJobAcquiredLatency.Seconds(), + } +} diff --git a/scaletest/autostart/run_test.go b/scaletest/autostart/run_test.go new file mode 100644 index 0000000000000..dc0fb9fea018e --- /dev/null +++ b/scaletest/autostart/run_test.go @@ -0,0 +1,158 @@ +package autostart_test + +import ( + "io" + "strconv" + "sync" + "testing" + "time" + + "github.com/google/uuid" + "github.com/prometheus/client_golang/prometheus" + "github.com/stretchr/testify/require" + "golang.org/x/sync/errgroup" + + "github.com/coder/coder/v2/coderd/coderdtest" + "github.com/coder/coder/v2/codersdk" + "github.com/coder/coder/v2/provisioner/echo" + "github.com/coder/coder/v2/provisionersdk/proto" + "github.com/coder/coder/v2/scaletest/autostart" + "github.com/coder/coder/v2/scaletest/createusers" + "github.com/coder/coder/v2/scaletest/workspacebuild" + "github.com/coder/coder/v2/testutil" +) + +func TestRun(t *testing.T) { + t.Parallel() + numUsers := 2 + autoStartDelay := 2 * time.Minute + + // Faking a workspace autostart schedule start time at the coderd level + // is difficult and error-prone. + t.Skip("This test takes several minutes to run, and is intended as a manual regression test") + + ctx := testutil.Context(t, time.Minute*3) + + client := coderdtest.New(t, &coderdtest.Options{ + IncludeProvisionerDaemon: true, + AutobuildTicker: time.NewTicker(time.Second * 1).C, + }) + user := coderdtest.CreateFirstUser(t, client) + + authToken := uuid.NewString() + version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ + Parse: echo.ParseComplete, + ProvisionPlan: echo.PlanComplete, + ProvisionApply: []*proto.Response{ + { + Type: &proto.Response_Apply{ + Apply: &proto.ApplyComplete{ + Resources: []*proto.Resource{ + { + Name: "example", + Type: "aws_instance", + Agents: []*proto.Agent{ + { + Id: uuid.NewString(), + Name: "agent", + Auth: &proto.Agent_Token{ + Token: authToken, + }, + Apps: []*proto.App{}, + }, + }, + }, + }, + }, + }, + }, + }, + }) + + template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) + coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) + + barrier := new(sync.WaitGroup) + barrier.Add(numUsers) + metrics := autostart.NewMetrics(prometheus.NewRegistry()) + + eg, runCtx := errgroup.WithContext(ctx) + + runners := make([]*autostart.Runner, 0, numUsers) + for i := range numUsers { + cfg := autostart.Config{ + User: createusers.Config{ + OrganizationID: user.OrganizationID, + }, + Workspace: workspacebuild.Config{ + OrganizationID: user.OrganizationID, + Request: codersdk.CreateWorkspaceRequest{ + TemplateID: template.ID, + }, + NoWaitForAgents: true, + }, + WorkspaceJobTimeout: testutil.WaitMedium, + AutostartDelay: autoStartDelay, + AutostartTimeout: testutil.WaitShort, + Metrics: metrics, + SetupBarrier: barrier, + } + err := cfg.Validate() + require.NoError(t, err) + + runner := autostart.NewRunner(client, cfg) + runners = append(runners, runner) + eg.Go(func() error { + return runner.Run(runCtx, strconv.Itoa(i), io.Discard) + }) + } + + err := eg.Wait() + require.NoError(t, err) + + users, err := client.Users(ctx, codersdk.UsersRequest{}) + require.NoError(t, err) + require.Len(t, users.Users, 1+numUsers) // owner + created users + + workspaces, err := client.Workspaces(ctx, codersdk.WorkspaceFilter{}) + require.NoError(t, err) + require.Len(t, workspaces.Workspaces, numUsers) // one workspace per user + + // Verify that workspaces have autostart schedules set and are running + for _, workspace := range workspaces.Workspaces { + require.NotNil(t, workspace.AutostartSchedule) + require.Equal(t, codersdk.WorkspaceTransitionStart, workspace.LatestBuild.Transition) + require.Equal(t, codersdk.ProvisionerJobSucceeded, workspace.LatestBuild.Job.Status) + } + + cleanupEg, cleanupCtx := errgroup.WithContext(ctx) + for i, runner := range runners { + cleanupEg.Go(func() error { + return runner.Cleanup(cleanupCtx, strconv.Itoa(i), io.Discard) + }) + } + err = cleanupEg.Wait() + require.NoError(t, err) + + workspaces, err = client.Workspaces(ctx, codersdk.WorkspaceFilter{}) + require.NoError(t, err) + require.Len(t, workspaces.Workspaces, 0) + + users, err = client.Users(ctx, codersdk.UsersRequest{}) + require.NoError(t, err) + require.Len(t, users.Users, 1) // owner + + for _, runner := range runners { + metrics := runner.GetMetrics() + require.Contains(t, metrics, autostart.AutostartTotalLatencyMetric) + latency, ok := metrics[autostart.AutostartTotalLatencyMetric].(float64) + require.True(t, ok) + jobCreationLatency, ok := metrics[autostart.AutostartJobCreationLatencyMetric].(float64) + require.True(t, ok) + jobAcquiredLatency, ok := metrics[autostart.AutostartJobAcquiredLatencyMetric].(float64) + require.True(t, ok) + require.Greater(t, latency, float64(0)) + require.Greater(t, jobCreationLatency, float64(0)) + require.Greater(t, jobAcquiredLatency, float64(0)) + } +} diff --git a/scaletest/workspacebuild/config.go b/scaletest/workspacebuild/config.go index 90184dacf84e8..105883ba3b7a8 100644 --- a/scaletest/workspacebuild/config.go +++ b/scaletest/workspacebuild/config.go @@ -19,6 +19,9 @@ type Config struct { // NoWaitForAgents determines whether the test should wait for the workspace // agents to connect before returning. NoWaitForAgents bool `json:"no_wait_for_agents"` + // NoWaitForBuild determines whether the test should wait for the workspace + // build to complete before returning. + NoWaitForBuild bool `json:"no_wait_for_build"` // Retry determines how many times to retry starting a workspace build if it // fails. Retry int `json:"retry"` diff --git a/scaletest/workspacebuild/run.go b/scaletest/workspacebuild/run.go index c9a59a18d69cb..308c18f0b6a03 100644 --- a/scaletest/workspacebuild/run.go +++ b/scaletest/workspacebuild/run.go @@ -58,27 +58,31 @@ func (r *Runner) RunReturningWorkspace(ctx context.Context, id string, logs io.W } r.workspaceID = workspace.ID - err = waitForBuild(ctx, logs, r.client, workspace.LatestBuild.ID) - if err != nil { - for i := 0; i < r.cfg.Retry; i++ { - _, _ = fmt.Fprintf(logs, "Retrying build %d/%d...\n", i+1, r.cfg.Retry) - - workspace.LatestBuild, err = r.client.CreateWorkspaceBuild(ctx, workspace.ID, codersdk.CreateWorkspaceBuildRequest{ - Transition: codersdk.WorkspaceTransitionStart, - RichParameterValues: req.RichParameterValues, - TemplateVersionID: req.TemplateVersionID, - }) - if err != nil { - return codersdk.Workspace{}, xerrors.Errorf("create workspace build: %w", err) + if r.cfg.NoWaitForBuild { + _, _ = fmt.Fprintln(logs, "Skipping waiting for build") + } else { + err = waitForBuild(ctx, logs, r.client, workspace.LatestBuild.ID) + if err != nil { + for i := 0; i < r.cfg.Retry; i++ { + _, _ = fmt.Fprintf(logs, "Retrying build %d/%d...\n", i+1, r.cfg.Retry) + + workspace.LatestBuild, err = r.client.CreateWorkspaceBuild(ctx, workspace.ID, codersdk.CreateWorkspaceBuildRequest{ + Transition: codersdk.WorkspaceTransitionStart, + RichParameterValues: req.RichParameterValues, + TemplateVersionID: req.TemplateVersionID, + }) + if err != nil { + return codersdk.Workspace{}, xerrors.Errorf("create workspace build: %w", err) + } + err = waitForBuild(ctx, logs, r.client, workspace.LatestBuild.ID) + if err == nil { + break + } } - err = waitForBuild(ctx, logs, r.client, workspace.LatestBuild.ID) - if err == nil { - break + if err != nil { + return codersdk.Workspace{}, xerrors.Errorf("wait for build: %w", err) } } - if err != nil { - return codersdk.Workspace{}, xerrors.Errorf("wait for build: %w", err) - } } if r.cfg.NoWaitForAgents { From 76d6e13185b2acd8d0fd23e43e0d9c3eb8bcccc0 Mon Sep 17 00:00:00 2001 From: Ethan <39577870+ethanndickson@users.noreply.github.com> Date: Thu, 2 Oct 2025 11:22:05 +1000 Subject: [PATCH 036/298] ci: make `changes` required (#20131) Earlier today, a dependabot PR was merged despite CI not having been run. https://github.com/coder/coder/pull/20068 This was because the `changes` job failed due to a github ratelimit, which caused all the tests to be skipped, which caused `required` to pass as it passes if tests are skipped. image This PR makes `changes` required so this can't happen again (assuming we keep around the dependabot automerge around, it might). --- .github/workflows/ci.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2d41883287e54..6fbba7883afbd 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -919,6 +919,7 @@ jobs: required: runs-on: ubuntu-latest needs: + - changes - fmt - lint - gen @@ -942,6 +943,7 @@ jobs: - name: Ensure required checks run: | # zizmor: ignore[template-injection] We're just reading needs.x.result here, no risk of injection echo "Checking required checks" + echo "- changes: ${{ needs.changes.result }}" echo "- fmt: ${{ needs.fmt.result }}" echo "- lint: ${{ needs.lint.result }}" echo "- gen: ${{ needs.gen.result }}" From e5c8c9bdaf770828ded482262add1c8ef68bfee5 Mon Sep 17 00:00:00 2001 From: Dean Sheather Date: Thu, 2 Oct 2025 12:12:29 +1000 Subject: [PATCH 037/298] chore: pin dogfood to release branch during release freeze (#20028) Relates to https://github.com/coder/dogfood/pull/189 Closes https://github.com/coder/internal/issues/1021 - Adds new script `scripts/should_deploy.sh` which implements the algorithm in the linked issue - Changes the `ci.yaml` workflow to run on release branches - Moves the deployment steps out of `ci.yaml` into a new workflow `deploy.yaml` for concurrency limiting purposes - Changes the behavior of image tag pushing slightly: - Versioned tags will no longer have a `main-` prefix - `main` branch will still push the `main` and `latest` tags - `release/x.y` branches will now push `release-x.y` tags - The deploy job will exit early if `should_deploy.sh` returns false - The deploy job will now retag whatever image it's about to deploy as `dogfood` --- .github/workflows/ci.yaml | 152 ++++++++---------------------- .github/workflows/deploy.yaml | 170 ++++++++++++++++++++++++++++++++++ .github/zizmor.yml | 4 + scripts/image_tag.sh | 5 +- scripts/should_deploy.sh | 68 ++++++++++++++ 5 files changed, 280 insertions(+), 119 deletions(-) create mode 100644 .github/workflows/deploy.yaml create mode 100644 .github/zizmor.yml create mode 100755 scripts/should_deploy.sh diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 6fbba7883afbd..eb2ba62e6b764 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -4,6 +4,7 @@ on: push: branches: - main + - release/* pull_request: workflow_dispatch: @@ -969,7 +970,7 @@ jobs: needs: changes # We always build the dylibs on Go changes to verify we're not merging unbuildable code, # but they need only be signed and uploaded on coder/coder main. - if: needs.changes.outputs.go == 'true' || needs.changes.outputs.ci == 'true' || github.ref == 'refs/heads/main' + if: needs.changes.outputs.go == 'true' || needs.changes.outputs.ci == 'true' || github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/') runs-on: ${{ github.repository_owner == 'coder' && 'depot-macos-latest' || 'macos-latest' }} steps: # Harden Runner doesn't work on macOS @@ -997,7 +998,7 @@ jobs: uses: ./.github/actions/setup-go - name: Install rcodesign - if: ${{ github.repository_owner == 'coder' && github.ref == 'refs/heads/main' }} + if: ${{ github.repository_owner == 'coder' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) }} run: | set -euo pipefail wget -O /tmp/rcodesign.tar.gz https://github.com/indygreg/apple-platform-rs/releases/download/apple-codesign%2F0.22.0/apple-codesign-0.22.0-macos-universal.tar.gz @@ -1008,7 +1009,7 @@ jobs: rm /tmp/rcodesign.tar.gz - name: Setup Apple Developer certificate and API key - if: ${{ github.repository_owner == 'coder' && github.ref == 'refs/heads/main' }} + if: ${{ github.repository_owner == 'coder' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) }} run: | set -euo pipefail touch /tmp/{apple_cert.p12,apple_cert_password.txt,apple_apikey.p8} @@ -1029,12 +1030,12 @@ jobs: make gen/mark-fresh make build/coder-dylib env: - CODER_SIGN_DARWIN: ${{ github.ref == 'refs/heads/main' && '1' || '0' }} + CODER_SIGN_DARWIN: ${{ (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) && '1' || '0' }} AC_CERTIFICATE_FILE: /tmp/apple_cert.p12 AC_CERTIFICATE_PASSWORD_FILE: /tmp/apple_cert_password.txt - name: Upload build artifacts - if: ${{ github.repository_owner == 'coder' && github.ref == 'refs/heads/main' }} + if: ${{ github.repository_owner == 'coder' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) }} uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: dylibs @@ -1044,7 +1045,7 @@ jobs: retention-days: 7 - name: Delete Apple Developer certificate and API key - if: ${{ github.repository_owner == 'coder' && github.ref == 'refs/heads/main' }} + if: ${{ github.repository_owner == 'coder' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) }} run: rm -f /tmp/{apple_cert.p12,apple_cert_password.txt,apple_apikey.p8} check-build: @@ -1094,7 +1095,7 @@ jobs: needs: - changes - build-dylib - if: github.ref == 'refs/heads/main' && needs.changes.outputs.docs-only == 'false' && !github.event.pull_request.head.repo.fork + if: (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) && needs.changes.outputs.docs-only == 'false' && !github.event.pull_request.head.repo.fork runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-22.04' }} permissions: # Necessary to push docker images to ghcr.io. @@ -1247,40 +1248,45 @@ jobs: id: build-docker env: CODER_IMAGE_BASE: ghcr.io/coder/coder-preview - CODER_IMAGE_TAG_PREFIX: main DOCKER_CLI_EXPERIMENTAL: "enabled" run: | set -euxo pipefail # build Docker images for each architecture version="$(./scripts/version.sh)" - tag="main-${version//+/-}" + tag="${version//+/-}" echo "tag=$tag" >> "$GITHUB_OUTPUT" # build images for each architecture # note: omitting the -j argument to avoid race conditions when pushing make build/coder_"$version"_linux_{amd64,arm64,armv7}.tag - # only push if we are on main branch - if [ "${GITHUB_REF}" == "refs/heads/main" ]; then + # only push if we are on main branch or release branch + if [[ "${GITHUB_REF}" == "refs/heads/main" || "${GITHUB_REF}" == refs/heads/release/* ]]; then # build and push multi-arch manifest, this depends on the other images # being pushed so will automatically push them # note: omitting the -j argument to avoid race conditions when pushing make push/build/coder_"$version"_linux_{amd64,arm64,armv7}.tag # Define specific tags - tags=("$tag" "main" "latest") + tags=("$tag") + if [ "${GITHUB_REF}" == "refs/heads/main" ]; then + tags+=("main" "latest") + elif [[ "${GITHUB_REF}" == refs/heads/release/* ]]; then + tags+=("release-${GITHUB_REF#refs/heads/release/}") + fi # Create and push a multi-arch manifest for each tag # we are adding `latest` tag and keeping `main` for backward # compatibality for t in "${tags[@]}"; do - # shellcheck disable=SC2046 - ./scripts/build_docker_multiarch.sh \ - --push \ - --target "ghcr.io/coder/coder-preview:$t" \ - --version "$version" \ - $(cat build/coder_"$version"_linux_{amd64,arm64,armv7}.tag) + echo "Pushing multi-arch manifest for tag: $t" + # shellcheck disable=SC2046 + ./scripts/build_docker_multiarch.sh \ + --push \ + --target "ghcr.io/coder/coder-preview:$t" \ + --version "$version" \ + $(cat build/coder_"$version"_linux_{amd64,arm64,armv7}.tag) done fi @@ -1471,112 +1477,28 @@ jobs: ./build/*.deb retention-days: 7 + # Deploy is handled in deploy.yaml so we can apply concurrency limits. deploy: - name: "deploy" - runs-on: ubuntu-latest - timeout-minutes: 30 needs: - changes - build if: | - github.ref == 'refs/heads/main' && !github.event.pull_request.head.repo.fork + (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) && needs.changes.outputs.docs-only == 'false' + && !github.event.pull_request.head.repo.fork + uses: ./.github/workflows/deploy.yaml + with: + image: ${{ needs.build.outputs.IMAGE }} permissions: contents: read id-token: write - steps: - - name: Harden Runner - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 - with: - egress-policy: audit - - - name: Checkout - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - with: - fetch-depth: 0 - persist-credentials: false - - - name: Authenticate to Google Cloud - uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0 - with: - workload_identity_provider: ${{ vars.GCP_WORKLOAD_ID_PROVIDER }} - service_account: ${{ vars.GCP_SERVICE_ACCOUNT }} - - - name: Set up Google Cloud SDK - uses: google-github-actions/setup-gcloud@aa5489c8933f4cc7a4f7d45035b3b1440c9c10db # v3.0.1 - - - name: Set up Flux CLI - uses: fluxcd/flux2/action@6bf37f6a560fd84982d67f853162e4b3c2235edb # v2.6.4 - with: - # Keep this and the github action up to date with the version of flux installed in dogfood cluster - version: "2.5.1" - - - name: Get Cluster Credentials - uses: google-github-actions/get-gke-credentials@3da1e46a907576cefaa90c484278bb5b259dd395 # v3.0.0 - with: - cluster_name: dogfood-v2 - location: us-central1-a - project_id: coder-dogfood-v2 - - - name: Reconcile Flux - run: | - set -euxo pipefail - flux --namespace flux-system reconcile source git flux-system - flux --namespace flux-system reconcile source git coder-main - flux --namespace flux-system reconcile kustomization flux-system - flux --namespace flux-system reconcile kustomization coder - flux --namespace flux-system reconcile source chart coder-coder - flux --namespace flux-system reconcile source chart coder-coder-provisioner - flux --namespace coder reconcile helmrelease coder - flux --namespace coder reconcile helmrelease coder-provisioner - - # Just updating Flux is usually not enough. The Helm release may get - # redeployed, but unless something causes the Deployment to update the - # pods won't be recreated. It's important that the pods get recreated, - # since we use `imagePullPolicy: Always` to ensure we're running the - # latest image. - - name: Rollout Deployment - run: | - set -euxo pipefail - kubectl --namespace coder rollout restart deployment/coder - kubectl --namespace coder rollout status deployment/coder - kubectl --namespace coder rollout restart deployment/coder-provisioner - kubectl --namespace coder rollout status deployment/coder-provisioner - kubectl --namespace coder rollout restart deployment/coder-provisioner-tagged - kubectl --namespace coder rollout status deployment/coder-provisioner-tagged - - deploy-wsproxies: - runs-on: ubuntu-latest - needs: build - if: github.ref == 'refs/heads/main' && !github.event.pull_request.head.repo.fork - steps: - - name: Harden Runner - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 - with: - egress-policy: audit - - - name: Checkout - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - with: - fetch-depth: 0 - persist-credentials: false - - - name: Setup flyctl - uses: superfly/flyctl-actions/setup-flyctl@fc53c09e1bc3be6f54706524e3b82c4f462f77be # v1.5 - - - name: Deploy workspace proxies - run: | - flyctl deploy --image "$IMAGE" --app paris-coder --config ./.github/fly-wsproxies/paris-coder.toml --env "CODER_PROXY_SESSION_TOKEN=$TOKEN_PARIS" --yes - flyctl deploy --image "$IMAGE" --app sydney-coder --config ./.github/fly-wsproxies/sydney-coder.toml --env "CODER_PROXY_SESSION_TOKEN=$TOKEN_SYDNEY" --yes - flyctl deploy --image "$IMAGE" --app sao-paulo-coder --config ./.github/fly-wsproxies/sao-paulo-coder.toml --env "CODER_PROXY_SESSION_TOKEN=$TOKEN_SAO_PAULO" --yes - flyctl deploy --image "$IMAGE" --app jnb-coder --config ./.github/fly-wsproxies/jnb-coder.toml --env "CODER_PROXY_SESSION_TOKEN=$TOKEN_JNB" --yes - env: - FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} - IMAGE: ${{ needs.build.outputs.IMAGE }} - TOKEN_PARIS: ${{ secrets.FLY_PARIS_CODER_PROXY_SESSION_TOKEN }} - TOKEN_SYDNEY: ${{ secrets.FLY_SYDNEY_CODER_PROXY_SESSION_TOKEN }} - TOKEN_SAO_PAULO: ${{ secrets.FLY_SAO_PAULO_CODER_PROXY_SESSION_TOKEN }} - TOKEN_JNB: ${{ secrets.FLY_JNB_CODER_PROXY_SESSION_TOKEN }} + packages: write # to retag image as dogfood + secrets: + FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} + FLY_PARIS_CODER_PROXY_SESSION_TOKEN: ${{ secrets.FLY_PARIS_CODER_PROXY_SESSION_TOKEN }} + FLY_SYDNEY_CODER_PROXY_SESSION_TOKEN: ${{ secrets.FLY_SYDNEY_CODER_PROXY_SESSION_TOKEN }} + FLY_SAO_PAULO_CODER_PROXY_SESSION_TOKEN: ${{ secrets.FLY_SAO_PAULO_CODER_PROXY_SESSION_TOKEN }} + FLY_JNB_CODER_PROXY_SESSION_TOKEN: ${{ secrets.FLY_JNB_CODER_PROXY_SESSION_TOKEN }} # sqlc-vet runs a postgres docker container, runs Coder migrations, and then # runs sqlc-vet to ensure all queries are valid. This catches any mistakes diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml new file mode 100644 index 0000000000000..7983591246363 --- /dev/null +++ b/.github/workflows/deploy.yaml @@ -0,0 +1,170 @@ +name: deploy + +on: + # Via workflow_call, called from ci.yaml + workflow_call: + inputs: + image: + description: "Image and tag to potentially deploy. Current branch will be validated against should-deploy check." + required: true + type: string + secrets: + FLY_API_TOKEN: + required: true + FLY_PARIS_CODER_PROXY_SESSION_TOKEN: + required: true + FLY_SYDNEY_CODER_PROXY_SESSION_TOKEN: + required: true + FLY_SAO_PAULO_CODER_PROXY_SESSION_TOKEN: + required: true + FLY_JNB_CODER_PROXY_SESSION_TOKEN: + required: true + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }} # no per-branch concurrency + cancel-in-progress: false + +jobs: + # Determines if the given branch should be deployed to dogfood. + should-deploy: + name: should-deploy + runs-on: ubuntu-latest + outputs: + verdict: ${{ steps.check.outputs.verdict }} # DEPLOY or NOOP + steps: + - name: Harden Runner + uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 + with: + egress-policy: audit + + - name: Checkout + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Check if deploy is enabled + id: check + run: | + set -euo pipefail + verdict="$(./scripts/should_deploy.sh)" + echo "verdict=$verdict" >> "$GITHUB_OUTPUT" + + deploy: + name: "deploy" + runs-on: ubuntu-latest + timeout-minutes: 30 + needs: should-deploy + if: needs.should-deploy.outputs.verdict == 'DEPLOY' + permissions: + contents: read + id-token: write + packages: write # to retag image as dogfood + steps: + - name: Harden Runner + uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 + with: + egress-policy: audit + + - name: Checkout + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + fetch-depth: 0 + persist-credentials: false + + - name: GHCR Login + uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Authenticate to Google Cloud + uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0 + with: + workload_identity_provider: ${{ vars.GCP_WORKLOAD_ID_PROVIDER }} + service_account: ${{ vars.GCP_SERVICE_ACCOUNT }} + + - name: Set up Google Cloud SDK + uses: google-github-actions/setup-gcloud@aa5489c8933f4cc7a4f7d45035b3b1440c9c10db # v3.0.1 + + - name: Set up Flux CLI + uses: fluxcd/flux2/action@6bf37f6a560fd84982d67f853162e4b3c2235edb # v2.6.4 + with: + # Keep this and the github action up to date with the version of flux installed in dogfood cluster + version: "2.7.0" + + - name: Get Cluster Credentials + uses: google-github-actions/get-gke-credentials@3da1e46a907576cefaa90c484278bb5b259dd395 # v3.0.0 + with: + cluster_name: dogfood-v2 + location: us-central1-a + project_id: coder-dogfood-v2 + + - name: Tag image as dogfood + run: | + set -euxo pipefail + # Retag image as dogfood while maintaining the multi-arch manifest + docker buildx imagetools create --tag "ghcr.io/coder/coder-preview:dogfood" "$IMAGE" + + - name: Reconcile Flux + run: | + set -euxo pipefail + flux --namespace flux-system reconcile source git flux-system + flux --namespace flux-system reconcile source git coder-main + flux --namespace flux-system reconcile kustomization flux-system + flux --namespace flux-system reconcile kustomization coder + flux --namespace flux-system reconcile source chart coder-coder + flux --namespace flux-system reconcile source chart coder-coder-provisioner + flux --namespace coder reconcile helmrelease coder + flux --namespace coder reconcile helmrelease coder-provisioner + + # Just updating Flux is usually not enough. The Helm release may get + # redeployed, but unless something causes the Deployment to update the + # pods won't be recreated. It's important that the pods get recreated, + # since we use `imagePullPolicy: Always` to ensure we're running the + # latest image. + - name: Rollout Deployment + run: | + set -euxo pipefail + kubectl --namespace coder rollout restart deployment/coder + kubectl --namespace coder rollout status deployment/coder + kubectl --namespace coder rollout restart deployment/coder-provisioner + kubectl --namespace coder rollout status deployment/coder-provisioner + kubectl --namespace coder rollout restart deployment/coder-provisioner-tagged + kubectl --namespace coder rollout status deployment/coder-provisioner-tagged + + deploy-wsproxies: + runs-on: ubuntu-latest + needs: deploy + steps: + - name: Harden Runner + uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 + with: + egress-policy: audit + + - name: Checkout + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Setup flyctl + uses: superfly/flyctl-actions/setup-flyctl@fc53c09e1bc3be6f54706524e3b82c4f462f77be # v1.5 + + - name: Deploy workspace proxies + run: | + flyctl deploy --image "$IMAGE" --app paris-coder --config ./.github/fly-wsproxies/paris-coder.toml --env "CODER_PROXY_SESSION_TOKEN=$TOKEN_PARIS" --yes + flyctl deploy --image "$IMAGE" --app sydney-coder --config ./.github/fly-wsproxies/sydney-coder.toml --env "CODER_PROXY_SESSION_TOKEN=$TOKEN_SYDNEY" --yes + flyctl deploy --image "$IMAGE" --app sao-paulo-coder --config ./.github/fly-wsproxies/sao-paulo-coder.toml --env "CODER_PROXY_SESSION_TOKEN=$TOKEN_SAO_PAULO" --yes + flyctl deploy --image "$IMAGE" --app jnb-coder --config ./.github/fly-wsproxies/jnb-coder.toml --env "CODER_PROXY_SESSION_TOKEN=$TOKEN_JNB" --yes + env: + FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} + IMAGE: ${{ inputs.image }} + TOKEN_PARIS: ${{ secrets.FLY_PARIS_CODER_PROXY_SESSION_TOKEN }} + TOKEN_SYDNEY: ${{ secrets.FLY_SYDNEY_CODER_PROXY_SESSION_TOKEN }} + TOKEN_SAO_PAULO: ${{ secrets.FLY_SAO_PAULO_CODER_PROXY_SESSION_TOKEN }} + TOKEN_JNB: ${{ secrets.FLY_JNB_CODER_PROXY_SESSION_TOKEN }} diff --git a/.github/zizmor.yml b/.github/zizmor.yml new file mode 100644 index 0000000000000..e125592cfdc6a --- /dev/null +++ b/.github/zizmor.yml @@ -0,0 +1,4 @@ +rules: + cache-poisoning: + ignore: + - "ci.yaml:184" diff --git a/scripts/image_tag.sh b/scripts/image_tag.sh index 68dfbcebf99cb..8767a22cb199c 100755 --- a/scripts/image_tag.sh +++ b/scripts/image_tag.sh @@ -51,10 +51,7 @@ fi image="${CODER_IMAGE_BASE:-ghcr.io/coder/coder}" -# use CODER_IMAGE_TAG_PREFIX if set as a prefix for the tag -tag_prefix="${CODER_IMAGE_TAG_PREFIX:-}" - -tag="${tag_prefix:+$tag_prefix-}v$version" +tag="v$version" if [[ "$version" == "latest" ]]; then tag="latest" diff --git a/scripts/should_deploy.sh b/scripts/should_deploy.sh new file mode 100755 index 0000000000000..3122192956b8d --- /dev/null +++ b/scripts/should_deploy.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash + +# This script determines if a commit in either the main branch or a +# `release/x.y` branch should be deployed to dogfood. +# +# To avoid masking unrelated failures, this script will return 0 in either case, +# and will print `DEPLOY` or `NOOP` to stdout. + +set -euo pipefail +# shellcheck source=scripts/lib.sh +source "$(dirname "${BASH_SOURCE[0]}")/lib.sh" +cdroot + +deploy_branch=main + +# Determine the current branch name and check that it is one of the supported +# branch names. +branch_name=$(git branch --show-current) +if [[ "$branch_name" != "main" && ! "$branch_name" =~ ^release/[0-9]+\.[0-9]+$ ]]; then + error "Current branch '$branch_name' is not a supported branch name for dogfood, must be 'main' or 'release/x.y'" +fi +log "Current branch '$branch_name'" + +# Determine the remote name +remote=$(git remote -v | grep coder/coder | awk '{print $1}' | head -n1) +if [[ -z "${remote}" ]]; then + error "Could not find remote for coder/coder" +fi +log "Using remote '$remote'" + +# Step 1: List all release branches and sort them by major/minor so we can find +# the latest release branch. +release_branches=$( + git branch -r --format='%(refname:short)' | + grep -E "${remote}/release/[0-9]+\.[0-9]+$" | + sed "s|${remote}/||" | + sort -V +) + +# As a sanity check, release/2.26 should exist. +if ! echo "$release_branches" | grep "release/2.26" >/dev/null; then + error "Could not find existing release branches. Did you run 'git fetch -ap ${remote}'?" +fi + +latest_release_branch=$(echo "$release_branches" | tail -n 1) +latest_release_branch_version=${latest_release_branch#release/} +log "Latest release branch: $latest_release_branch" +log "Latest release branch version: $latest_release_branch_version" + +# Step 2: check if a matching tag `v.0` exists. If it does not, we will +# use the release branch as the deploy branch. +if ! git rev-parse "refs/tags/v${latest_release_branch_version}.0" >/dev/null 2>&1; then + log "Tag 'v${latest_release_branch_version}.0' does not exist, using release branch as deploy branch" + deploy_branch=$latest_release_branch +else + log "Matching tag 'v${latest_release_branch_version}.0' exists, using main as deploy branch" +fi +log "Deploy branch: $deploy_branch" + +# Finally, check if the current branch is the deploy branch. +log +if [[ "$branch_name" != "$deploy_branch" ]]; then + log "VERDICT: DO NOT DEPLOY" + echo "NOOP" # stdout +else + log "VERDICT: DEPLOY" + echo "DEPLOY" # stdout +fi From d2c286d9b0eac6d5f04b33d10323c677d5e940d4 Mon Sep 17 00:00:00 2001 From: Dean Sheather Date: Thu, 2 Oct 2025 16:08:29 +1000 Subject: [PATCH 038/298] chore: fix missing variable in deploy workflow (#20136) already in release branch and tested working --- .github/workflows/deploy.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 7983591246363..895fd32ec9851 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -104,11 +104,11 @@ jobs: location: us-central1-a project_id: coder-dogfood-v2 + # Retag image as dogfood while maintaining the multi-arch manifest - name: Tag image as dogfood - run: | - set -euxo pipefail - # Retag image as dogfood while maintaining the multi-arch manifest - docker buildx imagetools create --tag "ghcr.io/coder/coder-preview:dogfood" "$IMAGE" + run: docker buildx imagetools create --tag "ghcr.io/coder/coder-preview:dogfood" "$IMAGE" + env: + IMAGE: ${{ inputs.image }} - name: Reconcile Flux run: | From d63bb2ce2fd33acf0d4b158437dad6219ba60749 Mon Sep 17 00:00:00 2001 From: Danny Kopping Date: Thu, 2 Oct 2025 11:10:51 +0200 Subject: [PATCH 039/298] chore: add Audit Log purge advice (#20052) Audit Log entries can be deleted safely (with appropriate caveats), but we don't specifically call this out in the docs. --------- Signed-off-by: Danny Kopping --- docs/admin/security/audit-logs.md | 52 ++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/docs/admin/security/audit-logs.md b/docs/admin/security/audit-logs.md index 37ed2a1c00694..2c72528f38ea3 100644 --- a/docs/admin/security/audit-logs.md +++ b/docs/admin/security/audit-logs.md @@ -125,6 +125,56 @@ log entry: 2023-06-13 03:43:29.233 [info] coderd: audit_log ID=95f7c392-da3e-480c-a579-8909f145fbe2 Time="2023-06-13T03:43:29.230422Z" UserID=6c405053-27e3-484a-9ad7-bcb64e7bfde6 OrganizationID=00000000-0000-0000-0000-000000000000 Ip= UserAgent= ResourceType=workspace_build ResourceID=988ae133-5b73-41e3-a55e-e1e9d3ef0b66 ResourceTarget="" Action=start Diff="{}" StatusCode=200 AdditionalFields="{\"workspace_name\":\"linux-container\",\"build_number\":\"7\",\"build_reason\":\"initiator\",\"workspace_owner\":\"\"}" RequestID=9682b1b5-7b9f-4bf2-9a39-9463f8e41cd6 ResourceIcon="" ``` +## Purging Old Audit Logs + +> [!WARNING] +> Audit Logs provide critical security and compliance information. Purging Audit Logs may impact your organization's ability +> to investigate security incidents or meet compliance requirements. Consult your security and compliance teams before purging any audit data. + +Audit Logs are not automatically purged from the database, though they can account for a large amount of disk usage. +Use the following query to determine the amount of disk space used by the `audit_logs` table. + +```sql +SELECT + relname AS table_name, + pg_size_pretty(pg_total_relation_size(relid)) AS total_size, + pg_size_pretty(pg_relation_size(relid)) AS table_size, + pg_size_pretty(pg_indexes_size(relid)) AS indexes_size, + (SELECT COUNT(*) FROM audit_logs) AS total_records +FROM pg_catalog.pg_statio_user_tables +WHERE relname = 'audit_logs' +ORDER BY pg_total_relation_size(relid) DESC; +``` + +Should you wish to purge these records, it is safe to do so. This can only be done by running SQL queries +directly against the `audit_logs` table in the database. We advise users to only purge old records (>1yr) +and in accordance with your compliance requirements. + +### Backup/Archive + +Consider exporting or archiving these records before deletion: + +```sql +-- Export to CSV +COPY (SELECT * FROM audit_logs WHERE time < CURRENT_TIMESTAMP - INTERVAL '1 year') +TO '/path/to/audit_logs_archive.csv' DELIMITER ',' CSV HEADER; + +-- Copy to archive table +CREATE TABLE audit_logs_archive AS +SELECT * FROM audit_logs WHERE time < CURRENT_TIMESTAMP - INTERVAL '1 year'; +``` + +### Permanent Deletion + +> [!NOTE] +> For large `audit_logs` tables, consider running the `DELETE` operation during maintenance windows as it may impact +> database performance. You can also batch the deletions to reduce lock time. + +```sql +DELETE FROM audit_logs WHERE time < CURRENT_TIMESTAMP - INTERVAL '1 year'; +-- Consider running `VACUUM VERBOSE audit_logs` afterwards for large datasets to reclaim disk space. +``` + ## How to Enable Audit Logs -This feature is only available with a [Premium license](../licensing/index.md). +This feature is only available with a [Premium license](../licensing/index.md), and is automatically enabled. From d93629bcdef67be9547b1f061c887c05dbd532e6 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 2 Oct 2025 15:07:49 +0200 Subject: [PATCH 040/298] fix: check permission to update username (#20139) --- coderd/users.go | 8 ++++++ coderd/users_test.go | 63 ++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 66 insertions(+), 5 deletions(-) diff --git a/coderd/users.go b/coderd/users.go index 1e592d010c077..e24790e7455e7 100644 --- a/coderd/users.go +++ b/coderd/users.go @@ -753,6 +753,14 @@ func (api *API) putUserProfile(rw http.ResponseWriter, r *http.Request) { if !httpapi.Read(ctx, rw, r, ¶ms) { return } + + // If caller wants to update user's username, they need "update_users" permission. + // This is restricted to user admins only. + if params.Username != user.Username && !api.Authorize(r, policy.ActionUpdate, user) { + httpapi.ResourceNotFound(rw) + return + } + existentUser, err := api.Database.GetUserByEmailOrUsername(ctx, database.GetUserByEmailOrUsernameParams{ Username: params.Username, }) diff --git a/coderd/users_test.go b/coderd/users_test.go index 22c9fad5eebea..283b607e89df9 100644 --- a/coderd/users_test.go +++ b/coderd/users_test.go @@ -1051,7 +1051,7 @@ func TestUpdateUserProfile(t *testing.T) { require.Equal(t, database.AuditActionWrite, auditor.AuditLogs()[numLogs-1].Action) }) - t.Run("UpdateSelfAsMember", func(t *testing.T) { + t.Run("UpdateSelfAsMember_Name", func(t *testing.T) { t.Parallel() auditor := audit.NewMock() client := coderdtest.New(t, &coderdtest.Options{Auditor: auditor}) @@ -1060,29 +1060,82 @@ func TestUpdateUserProfile(t *testing.T) { firstUser := coderdtest.CreateFirstUser(t, client) numLogs++ // add an audit log for login - memberClient, _ := coderdtest.CreateAnotherUser(t, client, firstUser.OrganizationID) + memberClient, memberUser := coderdtest.CreateAnotherUser(t, client, firstUser.OrganizationID) numLogs++ // add an audit log for user creation ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) defer cancel() - newUsername := coderdtest.RandomUsername(t) newName := coderdtest.RandomName(t) userProfile, err := memberClient.UpdateUserProfile(ctx, codersdk.Me, codersdk.UpdateUserProfileRequest{ - Username: newUsername, Name: newName, + Username: memberUser.Username, }) numLogs++ // add an audit log for user update numLogs++ // add an audit log for API key creation require.NoError(t, err) - require.Equal(t, newUsername, userProfile.Username) + require.Equal(t, memberUser.Username, userProfile.Username) require.Equal(t, newName, userProfile.Name) require.Len(t, auditor.AuditLogs(), numLogs) require.Equal(t, database.AuditActionWrite, auditor.AuditLogs()[numLogs-1].Action) }) + t.Run("UpdateSelfAsMember_Username", func(t *testing.T) { + t.Parallel() + auditor := audit.NewMock() + client := coderdtest.New(t, &coderdtest.Options{Auditor: auditor}) + + firstUser := coderdtest.CreateFirstUser(t, client) + memberClient, memberUser := coderdtest.CreateAnotherUser(t, client, firstUser.OrganizationID) + + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) + defer cancel() + + newUsername := coderdtest.RandomUsername(t) + _, err := memberClient.UpdateUserProfile(ctx, codersdk.Me, codersdk.UpdateUserProfileRequest{ + Name: memberUser.Name, + Username: newUsername, + }) + + var apiErr *codersdk.Error + require.ErrorAs(t, err, &apiErr) + require.Equal(t, http.StatusNotFound, apiErr.StatusCode()) + }) + + t.Run("UpdateMemberAsAdmin_Username", func(t *testing.T) { + t.Parallel() + auditor := audit.NewMock() + adminClient := coderdtest.New(t, &coderdtest.Options{Auditor: auditor}) + numLogs := len(auditor.AuditLogs()) + + adminUser := coderdtest.CreateFirstUser(t, adminClient) + numLogs++ // add an audit log for login + + _, memberUser := coderdtest.CreateAnotherUser(t, adminClient, adminUser.OrganizationID) + numLogs++ // add an audit log for user creation + + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) + defer cancel() + + newUsername := coderdtest.RandomUsername(t) + userProfile, err := adminClient.UpdateUserProfile(ctx, codersdk.Me, codersdk.UpdateUserProfileRequest{ + Name: memberUser.Name, + Username: newUsername, + }) + + numLogs++ // add an audit log for user update + numLogs++ // add an audit log for API key creation + + require.NoError(t, err) + require.Equal(t, newUsername, userProfile.Username) + require.Equal(t, memberUser.Name, userProfile.Name) + + require.Len(t, auditor.AuditLogs(), numLogs) + require.Equal(t, database.AuditActionWrite, auditor.AuditLogs()[numLogs-1].Action) + }) + t.Run("InvalidRealUserName", func(t *testing.T) { t.Parallel() client := coderdtest.New(t, nil) From 1f71c2c12957218dcde03ed087d3fea50d312308 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Thu, 2 Oct 2025 14:59:22 +0100 Subject: [PATCH 041/298] ci(.github/workflows/traiage.yaml): fix task URL in github comment (#20142) --- .github/workflows/traiage.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/traiage.yaml b/.github/workflows/traiage.yaml index 09881ff2ee46a..30eeb5cb8bb48 100644 --- a/.github/workflows/traiage.yaml +++ b/.github/workflows/traiage.yaml @@ -217,7 +217,7 @@ jobs: echo "Creating task: $TASK_NAME" ./scripts/traiage.sh create if [[ "${ISSUE_URL}" == "https://github.com/${GITHUB_REPOSITORY}"* ]]; then - gh issue comment "${ISSUE_URL}" --body "Task created: ${TASK_NAME}" --create-if-none --edit-last + gh issue comment "${ISSUE_URL}" --body "Task created: https://dev.coder.com/tasks/${CODER_USERNAME}/${TASK_NAME}" --create-if-none --edit-last else echo "Skipping comment on other repo." fi From c517aabe43add821ee8c39b760a6740b548600d2 Mon Sep 17 00:00:00 2001 From: Rafael Rodriguez Date: Thu, 2 Oct 2025 09:32:40 -0500 Subject: [PATCH 042/298] fix: add heartbeat to keep dynamic params websocket open (#20026) ## Summary In this pull request we're adding a heartbeat to the `handleParameterWebsocket` function to ensure that the connection stays open until the 30 min timeout has been reached. Closes: https://github.com/coder/coder/issues/19805 ### Testing - Reproduced the problem mentioned in the issue (websocket connection closes after ~10 minutes of inactivity on the create workspace page) Screenshot 2025-09-26 at 15 58 51 - Confirmed that adding the heartbeat kept the connection open until the 30 min timeout was reached Screenshot 2025-09-29 at 15 51 43 --- coderd/parameters.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/coderd/parameters.go b/coderd/parameters.go index 4b8b13486934f..cb24dcd4312ec 100644 --- a/coderd/parameters.go +++ b/coderd/parameters.go @@ -139,6 +139,8 @@ func (api *API) handleParameterWebsocket(rw http.ResponseWriter, r *http.Request }) return } + go httpapi.Heartbeat(ctx, conn) + stream := wsjson.NewStream[codersdk.DynamicParametersRequest, codersdk.DynamicParametersResponse]( conn, websocket.MessageText, From 0d2ccacacdec7f00cb76808af89cc570b18ef3f1 Mon Sep 17 00:00:00 2001 From: Danielle Maywood Date: Thu, 2 Oct 2025 15:39:11 +0100 Subject: [PATCH 043/298] chore: update to coder/clistat v1.0.1 (#20143) Update https://github.com/coder/clistat to v1.0.1. This release has two bug fixes. --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 34967b03a7060..c375c113f143b 100644 --- a/go.mod +++ b/go.mod @@ -462,7 +462,7 @@ require ( sigs.k8s.io/yaml v1.5.0 // indirect ) -require github.com/coder/clistat v1.0.0 +require github.com/coder/clistat v1.0.1 require github.com/SherClockHolmes/webpush-go v1.4.0 diff --git a/go.sum b/go.sum index ecc9506cbe700..03c0c1bc96e8b 100644 --- a/go.sum +++ b/go.sum @@ -919,8 +919,8 @@ github.com/coder/boundary v1.0.1-0.20250925154134-55a44f2a7945 h1:hDUf02kTX8EGR3 github.com/coder/boundary v1.0.1-0.20250925154134-55a44f2a7945/go.mod h1:d1AMFw81rUgrGHuZzWdPNhkY0G8w7pvLNLYF0e3ceC4= github.com/coder/bubbletea v1.2.2-0.20241212190825-007a1cdb2c41 h1:SBN/DA63+ZHwuWwPHPYoCZ/KLAjHv5g4h2MS4f2/MTI= github.com/coder/bubbletea v1.2.2-0.20241212190825-007a1cdb2c41/go.mod h1:I9ULxr64UaOSUv7hcb3nX4kowodJCVS7vt7VVJk/kW4= -github.com/coder/clistat v1.0.0 h1:MjiS7qQ1IobuSSgDnxcCSyBPESs44hExnh2TEqMcGnA= -github.com/coder/clistat v1.0.0/go.mod h1:F+gLef+F9chVrleq808RBxdaoq52R4VLopuLdAsh8Y4= +github.com/coder/clistat v1.0.1 h1:0ZgnKr1C4Z0ukG1z1+RUa8Z4OoIgWIH2hdxWSXQTubE= +github.com/coder/clistat v1.0.1/go.mod h1:F+gLef+F9chVrleq808RBxdaoq52R4VLopuLdAsh8Y4= github.com/coder/flog v1.1.0 h1:kbAes1ai8fIS5OeV+QAnKBQE22ty1jRF/mcAwHpLBa4= github.com/coder/flog v1.1.0/go.mod h1:UQlQvrkJBvnRGo69Le8E24Tcl5SJleAAR7gYEHzAmdQ= github.com/coder/glog v1.0.1-0.20220322161911-7365fe7f2cd1/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= From 4d1003eace5f0604ac61786137db813e9a2ac01f Mon Sep 17 00:00:00 2001 From: Zach <3724288+zedkipp@users.noreply.github.com> Date: Thu, 2 Oct 2025 11:43:13 -0600 Subject: [PATCH 044/298] fix: remove initial global HTTP client usage (#20128) This PR makes the initial steps at removing usage of the global Go HTTP client, which was seen to have impacts on test flakiness in https://github.com/coder/internal/issues/1020. The first commit removes uses from tests, with the exception of one test that is tightly coupled to the default client. The second commit makes easy/low-risk removals from application code. This should have some impact to reduce test flakiness. --- agent/agent_test.go | 3 ++- agent/apphealth.go | 3 ++- cli/server_test.go | 6 ++++-- cli/ssh_test.go | 3 ++- coderd/healthcheck/accessurl_test.go | 2 +- coderd/healthcheck/derphealth/derp_test.go | 3 ++- coderd/mcp/mcp_e2e_test.go | 18 ++++++++++-------- coderd/oauth2_metadata_test.go | 6 ++++-- coderd/promoauth/oauth2_test.go | 3 ++- coderd/telemetry/telemetry.go | 4 +++- codersdk/agentsdk/agentsdk_test.go | 9 ++++++--- enterprise/cli/provisionerdaemonstart_test.go | 3 ++- enterprise/cli/proxyserver_test.go | 3 ++- enterprise/cli/server_test.go | 3 ++- enterprise/coderd/coderd_test.go | 3 ++- enterprise/wsproxy/wsproxy_test.go | 11 +++++++---- enterprise/x/aibridged/aibridged_test.go | 3 ++- site/site_test.go | 6 ++++-- 18 files changed, 59 insertions(+), 33 deletions(-) diff --git a/agent/agent_test.go b/agent/agent_test.go index e8b3b99a95387..d4c857dc39637 100644 --- a/agent/agent_test.go +++ b/agent/agent_test.go @@ -2027,7 +2027,8 @@ func runSubAgentMain() int { ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) defer cancel() req = req.WithContext(ctx) - resp, err := http.DefaultClient.Do(req) + client := &http.Client{} + resp, err := client.Do(req) if err != nil { _, _ = fmt.Fprintf(os.Stderr, "agent connection failed: %v\n", err) return 11 diff --git a/agent/apphealth.go b/agent/apphealth.go index 1c4e1d126902c..4fb551077a30f 100644 --- a/agent/apphealth.go +++ b/agent/apphealth.go @@ -63,6 +63,7 @@ func NewAppHealthReporterWithClock( // run a ticker for each app health check. var mu sync.RWMutex failures := make(map[uuid.UUID]int, 0) + client := &http.Client{} for _, nextApp := range apps { if !shouldStartTicker(nextApp) { continue @@ -91,7 +92,7 @@ func NewAppHealthReporterWithClock( if err != nil { return err } - res, err := http.DefaultClient.Do(req) + res, err := client.Do(req) if err != nil { return err } diff --git a/cli/server_test.go b/cli/server_test.go index 18d59f7811fc3..0748786765cb8 100644 --- a/cli/server_test.go +++ b/cli/server_test.go @@ -1254,8 +1254,9 @@ func TestServer(t *testing.T) { t.Logf("error creating request: %s", err.Error()) return false } + client := &http.Client{} // nolint:bodyclose - res, err := http.DefaultClient.Do(req) + res, err := client.Do(req) if err != nil { t.Logf("error hitting prometheus endpoint: %s", err.Error()) return false @@ -1316,8 +1317,9 @@ func TestServer(t *testing.T) { t.Logf("error creating request: %s", err.Error()) return false } + client := &http.Client{} // nolint:bodyclose - res, err := http.DefaultClient.Do(req) + res, err := client.Do(req) if err != nil { t.Logf("error hitting prometheus endpoint: %s", err.Error()) return false diff --git a/cli/ssh_test.go b/cli/ssh_test.go index be3166cc4d32a..652e0e9c01996 100644 --- a/cli/ssh_test.go +++ b/cli/ssh_test.go @@ -1242,7 +1242,8 @@ func TestSSH(t *testing.T) { // true exits the loop. return true } - resp, err := http.DefaultClient.Do(req) + client := &http.Client{} + resp, err := client.Do(req) if err != nil { t.Logf("HTTP GET http://localhost:8222/ %s", err) return false diff --git a/coderd/healthcheck/accessurl_test.go b/coderd/healthcheck/accessurl_test.go index 29bf008346b37..85f362959718e 100644 --- a/coderd/healthcheck/accessurl_test.go +++ b/coderd/healthcheck/accessurl_test.go @@ -55,7 +55,7 @@ func TestAccessURL(t *testing.T) { defer cancel() report.Run(ctx, &healthcheck.AccessURLReportOptions{ - Client: nil, // defaults to http.DefaultClient + Client: &http.Client{}, AccessURL: nil, }) diff --git a/coderd/healthcheck/derphealth/derp_test.go b/coderd/healthcheck/derphealth/derp_test.go index c009ea982d620..08dc7db97f982 100644 --- a/coderd/healthcheck/derphealth/derp_test.go +++ b/coderd/healthcheck/derphealth/derp_test.go @@ -511,7 +511,8 @@ func tsDERPMap(ctx context.Context, t testing.TB) *tailcfg.DERPMap { req, err := http.NewRequestWithContext(ctx, "GET", ipn.DefaultControlURL+"/derpmap/default", nil) require.NoError(t, err) - res, err := http.DefaultClient.Do(req) + client := &http.Client{} + res, err := client.Do(req) require.NoError(t, err) defer res.Body.Close() require.Equal(t, http.StatusOK, res.StatusCode) diff --git a/coderd/mcp/mcp_e2e_test.go b/coderd/mcp/mcp_e2e_test.go index 2813757a50310..f101cfbdd5b65 100644 --- a/coderd/mcp/mcp_e2e_test.go +++ b/coderd/mcp/mcp_e2e_test.go @@ -141,7 +141,8 @@ func TestMCPHTTP_E2E_UnauthenticatedAccess(t *testing.T) { require.NoError(t, err, "Should be able to create HTTP request") req.Header.Set("Content-Type", "application/json") - resp, err := http.DefaultClient.Do(req) + client := &http.Client{} + resp, err := client.Do(req) require.NoError(t, err, "Should be able to make HTTP request") defer resp.Body.Close() @@ -613,7 +614,7 @@ func TestMCPHTTP_E2E_OAuth2_EndToEnd(t *testing.T) { require.NoError(t, err) tokenReq.Header.Set("Content-Type", "application/x-www-form-urlencoded") - tokenResp, err := http.DefaultClient.Do(tokenReq) + tokenResp, err := client.Do(tokenReq) require.NoError(t, err) defer tokenResp.Body.Close() @@ -711,7 +712,7 @@ func TestMCPHTTP_E2E_OAuth2_EndToEnd(t *testing.T) { require.NoError(t, err) refreshReq.Header.Set("Content-Type", "application/x-www-form-urlencoded") - refreshResp, err := http.DefaultClient.Do(refreshReq) + refreshResp, err := client.Do(refreshReq) require.NoError(t, err) defer refreshResp.Body.Close() @@ -846,7 +847,7 @@ func TestMCPHTTP_E2E_OAuth2_EndToEnd(t *testing.T) { regReq.Header.Set("Content-Type", "application/json") // Dynamic client registration should not require authentication (public endpoint) - regResp, err := http.DefaultClient.Do(regReq) + regResp, err := client.Do(regReq) require.NoError(t, err) defer regResp.Body.Close() @@ -936,7 +937,7 @@ func TestMCPHTTP_E2E_OAuth2_EndToEnd(t *testing.T) { require.NoError(t, err) tokenReq.Header.Set("Content-Type", "application/x-www-form-urlencoded") - tokenResp, err := http.DefaultClient.Do(tokenReq) + tokenResp, err := client.Do(tokenReq) require.NoError(t, err) defer tokenResp.Body.Close() @@ -1037,7 +1038,7 @@ func TestMCPHTTP_E2E_OAuth2_EndToEnd(t *testing.T) { require.NoError(t, err) refreshReq.Header.Set("Content-Type", "application/x-www-form-urlencoded") - refreshResp, err := http.DefaultClient.Do(refreshReq) + refreshResp, err := client.Do(refreshReq) require.NoError(t, err) defer refreshResp.Body.Close() @@ -1151,7 +1152,8 @@ func TestMCPHTTP_E2E_OAuth2_EndToEnd(t *testing.T) { require.NoError(t, err) regReq1.Header.Set("Content-Type", "application/json") - regResp1, err := http.DefaultClient.Do(regReq1) + client := &http.Client{} + regResp1, err := client.Do(regReq1) require.NoError(t, err) defer regResp1.Body.Close() @@ -1181,7 +1183,7 @@ func TestMCPHTTP_E2E_OAuth2_EndToEnd(t *testing.T) { require.NoError(t, err) regReq2.Header.Set("Content-Type", "application/json") - regResp2, err := http.DefaultClient.Do(regReq2) + regResp2, err := client.Do(regReq2) require.NoError(t, err) defer regResp2.Body.Close() diff --git a/coderd/oauth2_metadata_test.go b/coderd/oauth2_metadata_test.go index a3e8ec1f50571..0e7ff4b1a8743 100644 --- a/coderd/oauth2_metadata_test.go +++ b/coderd/oauth2_metadata_test.go @@ -29,7 +29,8 @@ func TestOAuth2AuthorizationServerMetadata(t *testing.T) { req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil) require.NoError(t, err) - resp, err := http.DefaultClient.Do(req) + httpClient := &http.Client{} + resp, err := httpClient.Do(req) require.NoError(t, err) defer resp.Body.Close() @@ -65,7 +66,8 @@ func TestOAuth2ProtectedResourceMetadata(t *testing.T) { req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil) require.NoError(t, err) - resp, err := http.DefaultClient.Do(req) + httpClient := &http.Client{} + resp, err := httpClient.Do(req) require.NoError(t, err) defer resp.Body.Close() diff --git a/coderd/promoauth/oauth2_test.go b/coderd/promoauth/oauth2_test.go index 5aeec4f0fb949..ab8e7c33146f7 100644 --- a/coderd/promoauth/oauth2_test.go +++ b/coderd/promoauth/oauth2_test.go @@ -94,7 +94,8 @@ func TestInstrument(t *testing.T) { must[*url.URL](t)(idp.IssuerURL().Parse("/.well-known/openid-configuration")).String(), nil) require.NoError(t, err) - resp, err := http.DefaultClient.Do(req) + client := &http.Client{} + resp, err := client.Do(req) require.NoError(t, err) _ = resp.Body.Close() diff --git a/coderd/telemetry/telemetry.go b/coderd/telemetry/telemetry.go index 8f203126c99ba..075083adaa96d 100644 --- a/coderd/telemetry/telemetry.go +++ b/coderd/telemetry/telemetry.go @@ -87,6 +87,7 @@ func New(options Options) (Reporter, error) { deploymentURL: deploymentURL, snapshotURL: snapshotURL, startedAt: dbtime.Now(), + client: &http.Client{}, } go reporter.runSnapshotter() return reporter, nil @@ -119,6 +120,7 @@ type remoteReporter struct { snapshotURL *url.URL startedAt time.Time shutdownAt *time.Time + client *http.Client } func (r *remoteReporter) Enabled() bool { @@ -142,7 +144,7 @@ func (r *remoteReporter) reportSync(snapshot *Snapshot) { return } req.Header.Set(VersionHeader, buildinfo.Version()) - resp, err := http.DefaultClient.Do(req) + resp, err := r.client.Do(req) if err != nil { // If the request fails it's not necessarily an error. // In an airgapped environment, it's fine if this fails! diff --git a/codersdk/agentsdk/agentsdk_test.go b/codersdk/agentsdk/agentsdk_test.go index 4f3d7d838b524..b6646662a4536 100644 --- a/codersdk/agentsdk/agentsdk_test.go +++ b/codersdk/agentsdk/agentsdk_test.go @@ -42,7 +42,8 @@ func TestStreamAgentReinitEvents(t *testing.T) { requestCtx := testutil.Context(t, testutil.WaitShort) req, err := http.NewRequestWithContext(requestCtx, "GET", srv.URL, nil) require.NoError(t, err) - resp, err := http.DefaultClient.Do(req) + client := &http.Client{} + resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() @@ -77,7 +78,8 @@ func TestStreamAgentReinitEvents(t *testing.T) { requestCtx := testutil.Context(t, testutil.WaitShort) req, err := http.NewRequestWithContext(requestCtx, "GET", srv.URL, nil) require.NoError(t, err) - resp, err := http.DefaultClient.Do(req) + client := &http.Client{} + resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() @@ -110,7 +112,8 @@ func TestStreamAgentReinitEvents(t *testing.T) { requestCtx := testutil.Context(t, testutil.WaitShort) req, err := http.NewRequestWithContext(requestCtx, "GET", srv.URL, nil) require.NoError(t, err) - resp, err := http.DefaultClient.Do(req) + client := &http.Client{} + resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() diff --git a/enterprise/cli/provisionerdaemonstart_test.go b/enterprise/cli/provisionerdaemonstart_test.go index 58603715f8184..884c3e6436e9e 100644 --- a/enterprise/cli/provisionerdaemonstart_test.go +++ b/enterprise/cli/provisionerdaemonstart_test.go @@ -495,6 +495,7 @@ func TestProvisionerDaemon_PrometheusEnabled(t *testing.T) { // Fetch metrics from Prometheus endpoint var req *http.Request var res *http.Response + httpClient := &http.Client{} require.Eventually(t, func() bool { req, err = http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("http://127.0.0.1:%d", prometheusPort), nil) if err != nil { @@ -503,7 +504,7 @@ func TestProvisionerDaemon_PrometheusEnabled(t *testing.T) { } // nolint:bodyclose - res, err = http.DefaultClient.Do(req) + res, err = httpClient.Do(req) if err != nil { t.Logf("unable to call Prometheus endpoint: %s", err.Error()) return false diff --git a/enterprise/cli/proxyserver_test.go b/enterprise/cli/proxyserver_test.go index ae01f6ac9dda6..b8df3d2c6a072 100644 --- a/enterprise/cli/proxyserver_test.go +++ b/enterprise/cli/proxyserver_test.go @@ -114,11 +114,12 @@ func TestWorkspaceProxy_Server_PrometheusEnabled(t *testing.T) { // Fetch metrics from Prometheus endpoint var res *http.Response + client := &http.Client{} require.Eventually(t, func() bool { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("http://127.0.0.1:%d", prometheusPort), nil) assert.NoError(t, err) // nolint:bodyclose - res, err = http.DefaultClient.Do(req) + res, err = client.Do(req) return err == nil }, testutil.WaitShort, testutil.IntervalFast) defer res.Body.Close() diff --git a/enterprise/cli/server_test.go b/enterprise/cli/server_test.go index 7489699a6f3dd..38001b701a9c1 100644 --- a/enterprise/cli/server_test.go +++ b/enterprise/cli/server_test.go @@ -43,13 +43,14 @@ func TestServer_Single(t *testing.T) { ) clitest.Start(t, inv.WithContext(ctx)) accessURL := waitAccessURL(t, cfg) + client := &http.Client{} require.Eventually(t, func() bool { reqCtx := testutil.Context(t, testutil.IntervalMedium) req, err := http.NewRequestWithContext(reqCtx, http.MethodGet, accessURL.String()+"/healthz", nil) if err != nil { panic(err) } - resp, err := http.DefaultClient.Do(req) + resp, err := client.Do(req) if err != nil { t.Log("/healthz not ready yet") return false diff --git a/enterprise/coderd/coderd_test.go b/enterprise/coderd/coderd_test.go index 302b367c304cd..860c4135e0c1e 100644 --- a/enterprise/coderd/coderd_test.go +++ b/enterprise/coderd/coderd_test.go @@ -595,6 +595,7 @@ func TestSCIMDisabled(t *testing.T) { "/scim/v2/random/path/that/is/long.txt", } + client := &http.Client{} for _, p := range checkPaths { t.Run(p, func(t *testing.T) { t.Parallel() @@ -605,7 +606,7 @@ func TestSCIMDisabled(t *testing.T) { req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, u.String(), nil) require.NoError(t, err) - resp, err := http.DefaultClient.Do(req) + resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() require.Equal(t, http.StatusNotFound, resp.StatusCode) diff --git a/enterprise/wsproxy/wsproxy_test.go b/enterprise/wsproxy/wsproxy_test.go index 5d7eaada7f990..03245999350cd 100644 --- a/enterprise/wsproxy/wsproxy_test.go +++ b/enterprise/wsproxy/wsproxy_test.go @@ -689,12 +689,13 @@ func TestWorkspaceProxyDERPMeshProbe(t *testing.T) { t.Log("all replicas have pinged") // Check they're all healthy according to /healthz-report. + httpClient := &http.Client{} for _, proxy := range proxies { // GET /healthz-report u := proxy.ServerURL.ResolveReference(&url.URL{Path: "/healthz-report"}) req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil) require.NoError(t, err) - resp, err := http.DefaultClient.Do(req) + resp, err := httpClient.Do(req) require.NoError(t, err) var respJSON codersdk.ProxyHealthReport @@ -781,7 +782,8 @@ func TestWorkspaceProxyDERPMeshProbe(t *testing.T) { u := proxy.ServerURL.ResolveReference(&url.URL{Path: "/healthz-report"}) req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil) require.NoError(t, err) - resp, err := http.DefaultClient.Do(req) + httpClient := &http.Client{} + resp, err := httpClient.Do(req) require.NoError(t, err) var respJSON codersdk.ProxyHealthReport @@ -869,7 +871,8 @@ func TestWorkspaceProxyDERPMeshProbe(t *testing.T) { u := proxy.ServerURL.ResolveReference(&url.URL{Path: "/healthz-report"}) req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil) require.NoError(t, err) - resp, err := http.DefaultClient.Do(req) + httpClient := &http.Client{} + resp, err := httpClient.Do(req) require.NoError(t, err) var respJSON codersdk.ProxyHealthReport err = json.NewDecoder(resp.Body).Decode(&respJSON) @@ -903,7 +906,7 @@ func TestWorkspaceProxyDERPMeshProbe(t *testing.T) { // GET /healthz-report req, err = http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil) require.NoError(t, err) - resp, err = http.DefaultClient.Do(req) + resp, err = httpClient.Do(req) require.NoError(t, err) err = json.NewDecoder(resp.Body).Decode(&respJSON) resp.Body.Close() diff --git a/enterprise/x/aibridged/aibridged_test.go b/enterprise/x/aibridged/aibridged_test.go index 22210a00f34f3..32ef4870623ee 100644 --- a/enterprise/x/aibridged/aibridged_test.go +++ b/enterprise/x/aibridged/aibridged_test.go @@ -70,6 +70,7 @@ func TestServeHTTP_FailureModes(t *testing.T) { t.Parallel() defaultHeaders := map[string]string{"Authorization": "Bearer key"} + httpClient := &http.Client{} cases := []struct { name string @@ -155,7 +156,7 @@ func TestServeHTTP_FailureModes(t *testing.T) { req.Header.Set(k, v) } - resp, err := http.DefaultClient.Do(req) + resp, err := httpClient.Do(req) t.Cleanup(func() { if resp == nil || resp.Body == nil { return diff --git a/site/site_test.go b/site/site_test.go index fa3c0809f22a7..36ec124ef8bc8 100644 --- a/site/site_test.go +++ b/site/site_test.go @@ -232,6 +232,7 @@ func TestServingFiles(t *testing.T) { Database: db, })) defer srv.Close() + client := &http.Client{} // Create a context ctx, cancelFunc := context.WithTimeout(context.Background(), testutil.WaitShort) @@ -275,7 +276,7 @@ func TestServingFiles(t *testing.T) { req, err := http.NewRequestWithContext(ctx, "GET", path, nil) require.NoError(t, err) - resp, err := http.DefaultClient.Do(req) + resp, err := client.Do(req) require.NoError(t, err, "get file") data, _ := io.ReadAll(resp.Body) require.Equal(t, string(data), testCase.expected, "Verify file: "+testCase.path) @@ -521,6 +522,7 @@ func TestServingBin(t *testing.T) { compressor := middleware.NewCompressor(1, "text/*", "application/*") srv := httptest.NewServer(compressor.Handler(site)) defer srv.Close() + client := &http.Client{} // Create a context ctx, cancelFunc := context.WithTimeout(context.Background(), testutil.WaitShort) @@ -538,7 +540,7 @@ func TestServingBin(t *testing.T) { req.Header.Set("Accept-Encoding", "gzip") } - resp, err := http.DefaultClient.Do(req) + resp, err := client.Do(req) require.NoError(t, err, "http do failed") defer resp.Body.Close() From ffcb7a169379ea9d8b83fa39bd4a7f727b82ad0d Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Thu, 2 Oct 2025 19:54:07 +0100 Subject: [PATCH 045/298] fix(coderd): truncate task prompt to 160 characters in notifications (#20147) Truncates the task prompt used in notifications to a maximum of 160 characters. The length of 160 characters was chosen arbitrarily. --- coderd/aitasks_test.go | 29 +++++++++++++-- coderd/util/strings/strings.go | 55 +++++++++++++++++++++++++++-- coderd/util/strings/strings_test.go | 47 +++++++++++++++++++----- coderd/workspaceagents.go | 5 +++ 4 files changed, 123 insertions(+), 13 deletions(-) diff --git a/coderd/aitasks_test.go b/coderd/aitasks_test.go index 20b33e9314da4..6425e06c771db 100644 --- a/coderd/aitasks_test.go +++ b/coderd/aitasks_test.go @@ -6,8 +6,10 @@ import ( "io" "net/http" "net/http/httptest" + "strings" "testing" "time" + "unicode/utf8" "github.com/google/uuid" "github.com/stretchr/testify/assert" @@ -977,6 +979,7 @@ func TestTasksNotification(t *testing.T) { isAITask bool isNotificationSent bool notificationTemplate uuid.UUID + taskPrompt string }{ // Should not send a notification when the agent app is not an AI task. { @@ -985,6 +988,7 @@ func TestTasksNotification(t *testing.T) { newAppStatus: codersdk.WorkspaceAppStatusStateWorking, isAITask: false, isNotificationSent: false, + taskPrompt: "NoAITask", }, // Should not send a notification when the new app status is neither 'Working' nor 'Idle'. { @@ -993,6 +997,7 @@ func TestTasksNotification(t *testing.T) { newAppStatus: codersdk.WorkspaceAppStatusStateComplete, isAITask: true, isNotificationSent: false, + taskPrompt: "NonNotifiedState", }, // Should not send a notification when the new app status equals the latest status (Working). { @@ -1001,6 +1006,7 @@ func TestTasksNotification(t *testing.T) { newAppStatus: codersdk.WorkspaceAppStatusStateWorking, isAITask: true, isNotificationSent: false, + taskPrompt: "NonNotifiedTransition", }, // Should send TemplateTaskWorking when the AI task transitions to 'Working'. { @@ -1010,6 +1016,7 @@ func TestTasksNotification(t *testing.T) { isAITask: true, isNotificationSent: true, notificationTemplate: notifications.TemplateTaskWorking, + taskPrompt: "TemplateTaskWorking", }, // Should send TemplateTaskWorking when the AI task transitions to 'Working' from 'Idle'. { @@ -1022,6 +1029,7 @@ func TestTasksNotification(t *testing.T) { isAITask: true, isNotificationSent: true, notificationTemplate: notifications.TemplateTaskWorking, + taskPrompt: "TemplateTaskWorkingFromIdle", }, // Should send TemplateTaskIdle when the AI task transitions to 'Idle'. { @@ -1031,6 +1039,17 @@ func TestTasksNotification(t *testing.T) { isAITask: true, isNotificationSent: true, notificationTemplate: notifications.TemplateTaskIdle, + taskPrompt: "TemplateTaskIdle", + }, + // Long task prompts should be truncated to 160 characters. + { + name: "LongTaskPrompt", + latestAppStatuses: []codersdk.WorkspaceAppStatusState{codersdk.WorkspaceAppStatusStateWorking}, + newAppStatus: codersdk.WorkspaceAppStatusStateIdle, + isAITask: true, + isNotificationSent: true, + notificationTemplate: notifications.TemplateTaskIdle, + taskPrompt: "This is a very long task prompt that should be truncated to 160 characters. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", }, } { t.Run(tc.name, func(t *testing.T) { @@ -1067,7 +1086,7 @@ func TestTasksNotification(t *testing.T) { }).Seed(workspaceBuildSeed).Params(database.WorkspaceBuildParameter{ WorkspaceBuildID: workspaceBuildID, Name: codersdk.AITaskPromptParameterName, - Value: "task prompt", + Value: tc.taskPrompt, }).WithAgent(func(agent []*proto.Agent) []*proto.Agent { agent[0].Apps = []*proto.App{{ Id: workspaceAgentAppID.String(), @@ -1115,7 +1134,13 @@ func TestTasksNotification(t *testing.T) { require.Len(t, sent, 1) require.Equal(t, memberUser.ID, sent[0].UserID) require.Len(t, sent[0].Labels, 2) - require.Equal(t, "task prompt", sent[0].Labels["task"]) + // NOTE: len(string) is the number of bytes in the string, not the number of runes. + require.LessOrEqual(t, utf8.RuneCountInString(sent[0].Labels["task"]), 160) + if len(tc.taskPrompt) > 160 { + require.Contains(t, tc.taskPrompt, strings.TrimSuffix(sent[0].Labels["task"], "…")) + } else { + require.Equal(t, tc.taskPrompt, sent[0].Labels["task"]) + } require.Equal(t, workspace.Name, sent[0].Labels["workspace"]) } else { // Then: No notification is sent diff --git a/coderd/util/strings/strings.go b/coderd/util/strings/strings.go index 49aad579e83f5..e21908d488cd8 100644 --- a/coderd/util/strings/strings.go +++ b/coderd/util/strings/strings.go @@ -23,15 +23,64 @@ func JoinWithConjunction(s []string) string { ) } -// Truncate returns the first n characters of s. -func Truncate(s string, n int) string { +type TruncateOption int + +func (o TruncateOption) String() string { + switch o { + case TruncateWithEllipsis: + return "TruncateWithEllipsis" + case TruncateWithFullWords: + return "TruncateWithFullWords" + default: + return fmt.Sprintf("TruncateOption(%d)", o) + } +} + +const ( + // TruncateWithEllipsis adds a Unicode ellipsis character to the end of the string. + TruncateWithEllipsis TruncateOption = 1 << 0 + // TruncateWithFullWords ensures that words are not split in the middle. + // As a special case, if there is no word boundary, the string is truncated. + TruncateWithFullWords TruncateOption = 1 << 1 +) + +// Truncate truncates s to n characters. +// Additional behaviors can be specified using TruncateOptions. +func Truncate(s string, n int, opts ...TruncateOption) string { + var options TruncateOption + for _, opt := range opts { + options |= opt + } if n < 1 { return "" } if len(s) <= n { return s } - return s[:n] + + maxLen := n + if options&TruncateWithEllipsis != 0 { + maxLen-- + } + var sb strings.Builder + // If we need to truncate to full words, find the last word boundary before n. + if options&TruncateWithFullWords != 0 { + lastWordBoundary := strings.LastIndexFunc(s[:maxLen], unicode.IsSpace) + if lastWordBoundary < 0 { + // We cannot find a word boundary. At this point, we'll truncate the string. + // It's better than nothing. + _, _ = sb.WriteString(s[:maxLen]) + } else { // lastWordBoundary <= maxLen + _, _ = sb.WriteString(s[:lastWordBoundary]) + } + } else { + _, _ = sb.WriteString(s[:maxLen]) + } + + if options&TruncateWithEllipsis != 0 { + _, _ = sb.WriteString("…") + } + return sb.String() } var bmPolicy = bluemonday.StrictPolicy() diff --git a/coderd/util/strings/strings_test.go b/coderd/util/strings/strings_test.go index 7a20a06a25f28..000fa9efa11e5 100644 --- a/coderd/util/strings/strings_test.go +++ b/coderd/util/strings/strings_test.go @@ -1,6 +1,7 @@ package strings_test import ( + "fmt" "testing" "github.com/stretchr/testify/assert" @@ -23,17 +24,47 @@ func TestTruncate(t *testing.T) { s string n int expected string + options []strings.TruncateOption }{ - {"foo", 4, "foo"}, - {"foo", 3, "foo"}, - {"foo", 2, "fo"}, - {"foo", 1, "f"}, - {"foo", 0, ""}, - {"foo", -1, ""}, + {"foo", 4, "foo", nil}, + {"foo", 3, "foo", nil}, + {"foo", 2, "fo", nil}, + {"foo", 1, "f", nil}, + {"foo", 0, "", nil}, + {"foo", -1, "", nil}, + {"foo bar", 7, "foo bar", []strings.TruncateOption{strings.TruncateWithEllipsis}}, + {"foo bar", 6, "foo b…", []strings.TruncateOption{strings.TruncateWithEllipsis}}, + {"foo bar", 5, "foo …", []strings.TruncateOption{strings.TruncateWithEllipsis}}, + {"foo bar", 4, "foo…", []strings.TruncateOption{strings.TruncateWithEllipsis}}, + {"foo bar", 3, "fo…", []strings.TruncateOption{strings.TruncateWithEllipsis}}, + {"foo bar", 2, "f…", []strings.TruncateOption{strings.TruncateWithEllipsis}}, + {"foo bar", 1, "…", []strings.TruncateOption{strings.TruncateWithEllipsis}}, + {"foo bar", 0, "", []strings.TruncateOption{strings.TruncateWithEllipsis}}, + {"foo bar", 7, "foo bar", []strings.TruncateOption{strings.TruncateWithFullWords}}, + {"foo bar", 6, "foo", []strings.TruncateOption{strings.TruncateWithFullWords}}, + {"foo bar", 5, "foo", []strings.TruncateOption{strings.TruncateWithFullWords}}, + {"foo bar", 4, "foo", []strings.TruncateOption{strings.TruncateWithFullWords}}, + {"foo bar", 3, "foo", []strings.TruncateOption{strings.TruncateWithFullWords}}, + {"foo bar", 2, "fo", []strings.TruncateOption{strings.TruncateWithFullWords}}, + {"foo bar", 1, "f", []strings.TruncateOption{strings.TruncateWithFullWords}}, + {"foo bar", 0, "", []strings.TruncateOption{strings.TruncateWithFullWords}}, + {"foo bar", 7, "foo bar", []strings.TruncateOption{strings.TruncateWithFullWords, strings.TruncateWithEllipsis}}, + {"foo bar", 6, "foo…", []strings.TruncateOption{strings.TruncateWithFullWords, strings.TruncateWithEllipsis}}, + {"foo bar", 5, "foo…", []strings.TruncateOption{strings.TruncateWithFullWords, strings.TruncateWithEllipsis}}, + {"foo bar", 4, "foo…", []strings.TruncateOption{strings.TruncateWithFullWords, strings.TruncateWithEllipsis}}, + {"foo bar", 3, "fo…", []strings.TruncateOption{strings.TruncateWithFullWords, strings.TruncateWithEllipsis}}, + {"foo bar", 2, "f…", []strings.TruncateOption{strings.TruncateWithFullWords, strings.TruncateWithEllipsis}}, + {"foo bar", 1, "…", []strings.TruncateOption{strings.TruncateWithFullWords, strings.TruncateWithEllipsis}}, + {"foo bar", 0, "", []strings.TruncateOption{strings.TruncateWithFullWords, strings.TruncateWithEllipsis}}, + {"This is a very long task prompt that should be truncated to 160 characters. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", 160, "This is a very long task prompt that should be truncated to 160 characters. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor…", []strings.TruncateOption{strings.TruncateWithFullWords, strings.TruncateWithEllipsis}}, } { - t.Run(tt.expected, func(t *testing.T) { + tName := fmt.Sprintf("%s_%d", tt.s, tt.n) + for _, opt := range tt.options { + tName += fmt.Sprintf("_%v", opt) + } + t.Run(tName, func(t *testing.T) { t.Parallel() - actual := strings.Truncate(tt.s, tt.n) + actual := strings.Truncate(tt.s, tt.n, tt.options...) require.Equal(t, tt.expected, actual) }) } diff --git a/coderd/workspaceagents.go b/coderd/workspaceagents.go index eddd6510b634b..0e6d0430e3642 100644 --- a/coderd/workspaceagents.go +++ b/coderd/workspaceagents.go @@ -484,6 +484,11 @@ func (api *API) enqueueAITaskStateNotification( } } + // As task prompt may be particularly long, truncate it to 160 characters for notifications. + if len(taskName) > 160 { + taskName = strutil.Truncate(taskName, 160, strutil.TruncateWithEllipsis, strutil.TruncateWithFullWords) + } + if _, err := api.NotificationsEnqueuer.EnqueueWithData( // nolint:gocritic // Need notifier actor to enqueue notifications dbauthz.AsNotifier(ctx), From 039fa894814fd95923e44988a4e6fb7c820390d6 Mon Sep 17 00:00:00 2001 From: yyefimov Date: Thu, 2 Oct 2025 22:08:15 -0400 Subject: [PATCH 046/298] fix(coderd): correct the name of the unmarshall error variable (#20150) Incorrect error variable is used during reporting of the issue during unmarshall operations and this makes it hard to understand the underlying reason for OIDC failure: use `unmarshalError` instead of `err`. --- coderd/oauthpki/oidcpki.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coderd/oauthpki/oidcpki.go b/coderd/oauthpki/oidcpki.go index d761c43e446ff..fa1c3854af1dd 100644 --- a/coderd/oauthpki/oidcpki.go +++ b/coderd/oauthpki/oidcpki.go @@ -247,7 +247,7 @@ func (src *jwtTokenSource) Token() (*oauth2.Token, error) { } if unmarshalError != nil { - return nil, xerrors.Errorf("oauth2: cannot unmarshal token: %w", err) + return nil, xerrors.Errorf("oauth2: cannot unmarshal token: %w", unmarshalError) } newToken := &oauth2.Token{ From 091b5c88d66d4e111209d39c79e005bfda4532db Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Fri, 3 Oct 2025 09:24:55 +0100 Subject: [PATCH 047/298] =?UTF-8?q?ci(.github/workflows/traiage.yaml):=20a?= =?UTF-8?q?djust=20prompt=20for=20legibility=20when=E2=80=A6=20(#20144)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit … truncated, just like this --- .github/workflows/traiage.yaml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/traiage.yaml b/.github/workflows/traiage.yaml index 30eeb5cb8bb48..483673d2ba798 100644 --- a/.github/workflows/traiage.yaml +++ b/.github/workflows/traiage.yaml @@ -203,11 +203,10 @@ jobs: # Write a prompt to PROMPT_FILE PROMPT=$(cat < Date: Fri, 3 Oct 2025 10:01:08 +0100 Subject: [PATCH 048/298] ci(.github/workflows/traiage.yaml): remove extraneous cleanup steps (#20154) --- .github/workflows/traiage.yaml | 69 ---------------------------------- 1 file changed, 69 deletions(-) diff --git a/.github/workflows/traiage.yaml b/.github/workflows/traiage.yaml index 483673d2ba798..15adf78242686 100644 --- a/.github/workflows/traiage.yaml +++ b/.github/workflows/traiage.yaml @@ -25,11 +25,6 @@ on: required: false default: "traiage" type: string - cleanup: - description: "Cleanup workspace after triage." - required: false - default: false - type: boolean jobs: traiage: @@ -70,7 +65,6 @@ jobs: GITHUB_EVENT_NAME: ${{ github.event_name }} GITHUB_EVENT_USER_ID: ${{ github.event.sender.id }} GITHUB_EVENT_USER_LOGIN: ${{ github.event.sender.login }} - INPUTS_CLEANUP: ${{ inputs.cleanup || false }} INPUTS_ISSUE_URL: ${{ inputs.issue_url }} INPUTS_TEMPLATE_NAME: ${{ inputs.template_name || 'traiage' }} INPUTS_TEMPLATE_PRESET: ${{ inputs.template_preset || 'Default'}} @@ -86,9 +80,6 @@ jobs: echo "Using prefix: ${INPUTS_PREFIX}" echo "prefix=${INPUTS_PREFIX}" >> "${GITHUB_OUTPUT}" - echo "Using cleanup: ${INPUTS_CLEANUP}" - echo "cleanup=${INPUTS_CLEANUP}" >> "${GITHUB_OUTPUT}" - # For workflow_dispatch, use the actor who triggered it # For issues events, use the issue author. if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then @@ -222,63 +213,3 @@ jobs: fi echo "TASK_NAME=${CODER_USERNAME}/${TASK_NAME}" >> "${GITHUB_OUTPUT}" echo "TASK_NAME=${CODER_USERNAME}/${TASK_NAME}" >> "${GITHUB_ENV}" - - - name: Create and upload archive - id: create-archive - if: steps.determine-inputs.outputs.cleanup == 'true' - env: - BUCKET_PREFIX: "gs://coder-traiage-outputs/traiage" - CODER_USERNAME: ${{ steps.get-coder-username.outputs.coder_username }} - TASK_NAME: ${{ steps.create-task.outputs.TASK_NAME }} - run: | - echo "Waiting for task to complete..." - coder exp task status "${TASK_NAME}" --watch - echo "Creating archive for workspace: ${TASK_NAME}" - ./scripts/traiage.sh archive - echo "archive_url=${BUCKET_PREFIX%%/}/$TASK_NAME.tar.gz" >> "${GITHUB_OUTPUT}" - - - name: Generate a summary of the changes and post a comment on GitHub. - id: generate-summary - if: steps.determine-inputs.outputs.cleanup == 'true' - env: - ARCHIVE_URL: ${{ steps.create-archive.outputs.archive_url }} - BUCKET_PREFIX: "gs://coder-traiage-outputs/traiage" - CODER_USERNAME: ${{ steps.get-coder-username.outputs.coder_username }} - CONTEXT_KEY: ${{ steps.extract-context.outputs.context_key }} - GH_TOKEN: ${{ github.token }} - GITHUB_REPOSITORY: ${{ github.repository }} - ISSUE_URL: ${{ steps.determine-inputs.outputs.issue_url }} - TASK_NAME: ${{ steps.create-task.outputs.TASK_NAME }} - run: | - SUMMARY_FILE=$(mktemp) - trap 'rm -f "${SUMMARY_FILE}"' EXIT - AUTO_SUMMARY=$(./scripts/traiage.sh summary) - { - echo "## TrAIage Results" - echo "- **Issue URL:** ${ISSUE_URL}" - echo "- **Context Key:** ${CONTEXT_KEY}" - echo "- **Workspace:** ${TASK_NAME}" - echo "- **Archive URL:** ${ARCHIVE_URL}" - echo - echo "${AUTO_SUMMARY}" - echo - echo "To fetch the output to your own workspace:" - echo - echo '```bash' - echo "BUCKET_PREFIX=${BUCKET_PREFIX} TASK_NAME=${TASK_NAME} ./scripts/traiage.sh resume" - echo '```' - echo - } >> "${SUMMARY_FILE}" - - if [[ "${ISSUE_URL}" == "https://github.com/${GITHUB_REPOSITORY}"* ]]; then - gh issue comment "${ISSUE_URL}" --body-file "${SUMMARY_FILE}" --create-if-none --edit-last - else - echo "Skipping comment on other repo." - fi - cat "${SUMMARY_FILE}" >> "${GITHUB_STEP_SUMMARY}" - - - name: Cleanup task - if: steps.determine-inputs.outputs.cleanup == 'true' && steps.create-task.outputs.TASK_NAME != '' && steps.create-archive.outputs.archive_url != '' - run: | - echo "Cleaning up task: $TASK_NAME" - ./scripts/traiage.sh delete || true From e1619daacc95f00b8bd181f993f62fbc4fa3a27a Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Fri, 3 Oct 2025 10:17:43 +0100 Subject: [PATCH 049/298] chore(coderd): update aitasks.go to leverage agentapi-sdk-go (#20159) I only recently became aware of the existence of `agentapi-sdk-go`. Updates `aitasks.go` to make use of it. --- coderd/aitasks.go | 168 ++++++++-------------------------------------- 1 file changed, 27 insertions(+), 141 deletions(-) diff --git a/coderd/aitasks.go b/coderd/aitasks.go index 31600c69443b4..275cfe855621e 100644 --- a/coderd/aitasks.go +++ b/coderd/aitasks.go @@ -1,17 +1,13 @@ package coderd import ( - "bytes" "context" "database/sql" - "encoding/json" "errors" "fmt" - "io" "net" "net/http" "net/url" - "path" "slices" "strings" "time" @@ -31,6 +27,8 @@ import ( "github.com/coder/coder/v2/coderd/taskname" "github.com/coder/coder/v2/coderd/util/slice" "github.com/coder/coder/v2/codersdk" + + aiagentapi "github.com/coder/agentapi-sdk-go" ) // This endpoint is experimental and not guaranteed to be stable, so we're not @@ -629,61 +627,37 @@ func (api *API) taskSend(rw http.ResponseWriter, r *http.Request) { } if err = api.authAndDoWithTaskSidebarAppClient(r, taskID, func(ctx context.Context, client *http.Client, appURL *url.URL) error { - status, err := agentapiDoStatusRequest(ctx, client, appURL) + agentAPIClient, err := aiagentapi.NewClient(appURL.String(), aiagentapi.WithHTTPClient(client)) if err != nil { - return err - } - - if status != "stable" { return httperror.NewResponseError(http.StatusBadGateway, codersdk.Response{ - Message: "Task app is not ready to accept input.", - Detail: fmt.Sprintf("Status: %s", status), + Message: "Failed to create agentapi client.", + Detail: err.Error(), }) } - var reqBody struct { - Content string `json:"content"` - Type string `json:"type"` - } - reqBody.Content = req.Input - reqBody.Type = "user" - - req, err := agentapiNewRequest(ctx, http.MethodPost, appURL, "message", reqBody) - if err != nil { - return err - } - - resp, err := client.Do(req) + statusResp, err := agentAPIClient.GetStatus(ctx) if err != nil { return httperror.NewResponseError(http.StatusBadGateway, codersdk.Response{ - Message: "Failed to reach task app endpoint.", + Message: "Failed to get status from task app.", Detail: err.Error(), }) } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(io.LimitReader(resp.Body, 128)) - return httperror.NewResponseError(http.StatusBadGateway, codersdk.Response{ - Message: "Task app rejected the message.", - Detail: fmt.Sprintf("Upstream status: %d; Body: %s", resp.StatusCode, body), - }) - } - // {"$schema":"http://localhost:3284/schemas/MessageResponseBody.json","ok":true} - // {"$schema":"http://localhost:3284/schemas/ErrorModel.json","title":"Unprocessable Entity","status":422,"detail":"validation failed","errors":[{"location":"body.type","value":"oof"}]} - var respBody map[string]any - if err := json.NewDecoder(resp.Body).Decode(&respBody); err != nil { + if statusResp.Status != aiagentapi.StatusStable { return httperror.NewResponseError(http.StatusBadGateway, codersdk.Response{ - Message: "Failed to decode task app response body.", - Detail: err.Error(), + Message: "Task app is not ready to accept input.", + Detail: fmt.Sprintf("Status: %s", statusResp.Status), }) } - if v, ok := respBody["ok"].(bool); !ok || !v { + _, err = agentAPIClient.PostMessage(ctx, aiagentapi.PostMessageParams{ + Content: req.Input, + Type: aiagentapi.MessageTypeUser, + }) + if err != nil { return httperror.NewResponseError(http.StatusBadGateway, codersdk.Response{ Message: "Task app rejected the message.", - Detail: fmt.Sprintf("Upstream response: %v", respBody), + Detail: err.Error(), }) } @@ -710,51 +684,29 @@ func (api *API) taskLogs(rw http.ResponseWriter, r *http.Request) { var out codersdk.TaskLogsResponse if err := api.authAndDoWithTaskSidebarAppClient(r, taskID, func(ctx context.Context, client *http.Client, appURL *url.URL) error { - req, err := agentapiNewRequest(ctx, http.MethodGet, appURL, "messages", nil) - if err != nil { - return err - } - - resp, err := client.Do(req) + agentAPIClient, err := aiagentapi.NewClient(appURL.String(), aiagentapi.WithHTTPClient(client)) if err != nil { return httperror.NewResponseError(http.StatusBadGateway, codersdk.Response{ - Message: "Failed to reach task app endpoint.", + Message: "Failed to create agentapi client.", Detail: err.Error(), }) } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(io.LimitReader(resp.Body, 128)) - return httperror.NewResponseError(http.StatusBadGateway, codersdk.Response{ - Message: "Task app rejected the request.", - Detail: fmt.Sprintf("Upstream status: %d; Body: %s", resp.StatusCode, body), - }) - } - // {"$schema":"http://localhost:3284/schemas/MessagesResponseBody.json","messages":[]} - var respBody struct { - Messages []struct { - ID int `json:"id"` - Content string `json:"content"` - Role string `json:"role"` - Time time.Time `json:"time"` - } `json:"messages"` - } - if err := json.NewDecoder(resp.Body).Decode(&respBody); err != nil { + messagesResp, err := agentAPIClient.GetMessages(ctx) + if err != nil { return httperror.NewResponseError(http.StatusBadGateway, codersdk.Response{ - Message: "Failed to decode task app response body.", + Message: "Failed to get messages from task app.", Detail: err.Error(), }) } - logs := make([]codersdk.TaskLogEntry, 0, len(respBody.Messages)) - for _, m := range respBody.Messages { + logs := make([]codersdk.TaskLogEntry, 0, len(messagesResp.Messages)) + for _, m := range messagesResp.Messages { var typ codersdk.TaskLogType - switch strings.ToLower(m.Role) { - case "user": + switch m.Role { + case aiagentapi.RoleUser: typ = codersdk.TaskLogTypeInput - case "agent": + case aiagentapi.RoleAgent: typ = codersdk.TaskLogTypeOutput default: return httperror.NewResponseError(http.StatusBadGateway, codersdk.Response{ @@ -763,7 +715,7 @@ func (api *API) taskLogs(rw http.ResponseWriter, r *http.Request) { }) } logs = append(logs, codersdk.TaskLogEntry{ - ID: m.ID, + ID: int(m.Id), Content: m.Content, Type: typ, Time: m.Time, @@ -903,69 +855,3 @@ func (api *API) authAndDoWithTaskSidebarAppClient( } return do(ctx, client, parsedURL) } - -func agentapiNewRequest(ctx context.Context, method string, appURL *url.URL, appURLPath string, body any) (*http.Request, error) { - u := *appURL - u.Path = path.Join(appURL.Path, appURLPath) - - var bodyReader io.Reader - if body != nil { - b, err := json.Marshal(body) - if err != nil { - return nil, httperror.NewResponseError(http.StatusBadRequest, codersdk.Response{ - Message: "Failed to marshal task app request body.", - Detail: err.Error(), - }) - } - bodyReader = bytes.NewReader(b) - } - - req, err := http.NewRequestWithContext(ctx, method, u.String(), bodyReader) - if err != nil { - return nil, httperror.NewResponseError(http.StatusBadRequest, codersdk.Response{ - Message: "Failed to create task app request.", - Detail: err.Error(), - }) - } - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Accept", "application/json") - - return req, nil -} - -func agentapiDoStatusRequest(ctx context.Context, client *http.Client, appURL *url.URL) (string, error) { - req, err := agentapiNewRequest(ctx, http.MethodGet, appURL, "status", nil) - if err != nil { - return "", err - } - - resp, err := client.Do(req) - if err != nil { - return "", httperror.NewResponseError(http.StatusBadGateway, codersdk.Response{ - Message: "Failed to reach task app endpoint.", - Detail: err.Error(), - }) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return "", httperror.NewResponseError(http.StatusBadGateway, codersdk.Response{ - Message: "Task app status returned an error.", - Detail: fmt.Sprintf("Status code: %d", resp.StatusCode), - }) - } - - // {"$schema":"http://localhost:3284/schemas/StatusResponseBody.json","status":"stable"} - var respBody struct { - Status string `json:"status"` - } - - if err := json.NewDecoder(resp.Body).Decode(&respBody); err != nil { - return "", httperror.NewResponseError(http.StatusBadGateway, codersdk.Response{ - Message: "Failed to decode task app status response body.", - Detail: err.Error(), - }) - } - - return respBody.Status, nil -} From 2b4485575c1c095430a4f2a877cc6a37e5c25cf3 Mon Sep 17 00:00:00 2001 From: Ethan <39577870+ethanndickson@users.noreply.github.com> Date: Fri, 3 Oct 2025 20:50:21 +1000 Subject: [PATCH 050/298] feat(scaletest): add `autostart` scaletest command (#20161) Closes https://github.com/coder/internal/issues/911 --- cli/exp_scaletest.go | 235 +++++++++++++++++++++++++++++++++++++ scaletest/autostart/run.go | 1 + 2 files changed, 236 insertions(+) diff --git a/cli/exp_scaletest.go b/cli/exp_scaletest.go index 4a8852cf8a4fc..e6a3993035512 100644 --- a/cli/exp_scaletest.go +++ b/cli/exp_scaletest.go @@ -33,6 +33,7 @@ import ( "github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/codersdk/workspacesdk" "github.com/coder/coder/v2/scaletest/agentconn" + "github.com/coder/coder/v2/scaletest/autostart" "github.com/coder/coder/v2/scaletest/createusers" "github.com/coder/coder/v2/scaletest/createworkspaces" "github.com/coder/coder/v2/scaletest/dashboard" @@ -60,6 +61,7 @@ func (r *RootCmd) scaletestCmd() *serpent.Command { r.scaletestCreateWorkspaces(), r.scaletestWorkspaceUpdates(), r.scaletestWorkspaceTraffic(), + r.scaletestAutostart(), }, } @@ -1682,6 +1684,239 @@ func (r *RootCmd) scaletestDashboard() *serpent.Command { return cmd } +const ( + autostartTestName = "autostart" +) + +func (r *RootCmd) scaletestAutostart() *serpent.Command { + var ( + workspaceCount int64 + workspaceJobTimeout time.Duration + autostartDelay time.Duration + autostartTimeout time.Duration + template string + noCleanup bool + + parameterFlags workspaceParameterFlags + tracingFlags = &scaletestTracingFlags{} + timeoutStrategy = &timeoutFlags{} + cleanupStrategy = newScaletestCleanupStrategy() + output = &scaletestOutputFlags{} + prometheusFlags = &scaletestPrometheusFlags{} + ) + + cmd := &serpent.Command{ + Use: "autostart", + Short: "Replicate a thundering herd of autostarting workspaces", + Handler: func(inv *serpent.Invocation) error { + ctx := inv.Context() + client, err := r.InitClient(inv) + if err != nil { + return err + } + + notifyCtx, stop := signal.NotifyContext(ctx, StopSignals...) // Checked later. + defer stop() + ctx = notifyCtx + + me, err := requireAdmin(ctx, client) + if err != nil { + return err + } + + client.HTTPClient = &http.Client{ + Transport: &codersdk.HeaderTransport{ + Transport: http.DefaultTransport, + Header: map[string][]string{ + codersdk.BypassRatelimitHeader: {"true"}, + }, + }, + } + + if workspaceCount <= 0 { + return xerrors.Errorf("--workspace-count must be greater than zero") + } + + outputs, err := output.parse() + if err != nil { + return xerrors.Errorf("could not parse --output flags") + } + + tpl, err := parseTemplate(ctx, client, me.OrganizationIDs, template) + if err != nil { + return xerrors.Errorf("parse template: %w", err) + } + + cliRichParameters, err := asWorkspaceBuildParameters(parameterFlags.richParameters) + if err != nil { + return xerrors.Errorf("can't parse given parameter values: %w", err) + } + + richParameters, err := prepWorkspaceBuild(inv, client, prepWorkspaceBuildArgs{ + Action: WorkspaceCreate, + TemplateVersionID: tpl.ActiveVersionID, + + RichParameterFile: parameterFlags.richParameterFile, + RichParameters: cliRichParameters, + }) + if err != nil { + return xerrors.Errorf("prepare build: %w", err) + } + + tracerProvider, closeTracing, tracingEnabled, err := tracingFlags.provider(ctx) + if err != nil { + return xerrors.Errorf("create tracer provider: %w", err) + } + tracer := tracerProvider.Tracer(scaletestTracerName) + + reg := prometheus.NewRegistry() + metrics := autostart.NewMetrics(reg) + + setupBarrier := new(sync.WaitGroup) + setupBarrier.Add(int(workspaceCount)) + + th := harness.NewTestHarness(timeoutStrategy.wrapStrategy(harness.ConcurrentExecutionStrategy{}), cleanupStrategy.toStrategy()) + for i := range workspaceCount { + id := strconv.Itoa(int(i)) + config := autostart.Config{ + User: createusers.Config{ + OrganizationID: me.OrganizationIDs[0], + }, + Workspace: workspacebuild.Config{ + OrganizationID: me.OrganizationIDs[0], + Request: codersdk.CreateWorkspaceRequest{ + TemplateID: tpl.ID, + RichParameterValues: richParameters, + }, + }, + WorkspaceJobTimeout: workspaceJobTimeout, + AutostartDelay: autostartDelay, + AutostartTimeout: autostartTimeout, + Metrics: metrics, + SetupBarrier: setupBarrier, + } + if err := config.Validate(); err != nil { + return xerrors.Errorf("validate config: %w", err) + } + var runner harness.Runnable = autostart.NewRunner(client, config) + if tracingEnabled { + runner = &runnableTraceWrapper{ + tracer: tracer, + spanName: fmt.Sprintf("%s/%s", autostartTestName, id), + runner: runner, + } + } + th.AddRun(autostartTestName, id, runner) + } + + logger := inv.Logger + prometheusSrvClose := ServeHandler(ctx, logger, promhttp.HandlerFor(reg, promhttp.HandlerOpts{}), prometheusFlags.Address, "prometheus") + defer prometheusSrvClose() + + defer func() { + _, _ = fmt.Fprintln(inv.Stderr, "\nUploading traces...") + if err := closeTracing(ctx); err != nil { + _, _ = fmt.Fprintf(inv.Stderr, "\nError uploading traces: %+v\n", err) + } + // Wait for prometheus metrics to be scraped + _, _ = fmt.Fprintf(inv.Stderr, "Waiting %s for prometheus metrics to be scraped\n", prometheusFlags.Wait) + <-time.After(prometheusFlags.Wait) + }() + + _, _ = fmt.Fprintln(inv.Stderr, "Running autostart load test...") + testCtx, testCancel := timeoutStrategy.toContext(ctx) + defer testCancel() + err = th.Run(testCtx) + if err != nil { + return xerrors.Errorf("run test harness (harness failure, not a test failure): %w", err) + } + + // If the command was interrupted, skip stats. + if notifyCtx.Err() != nil { + return notifyCtx.Err() + } + + res := th.Results() + for _, o := range outputs { + err = o.write(res, inv.Stdout) + if err != nil { + return xerrors.Errorf("write output %q to %q: %w", o.format, o.path, err) + } + } + + if !noCleanup { + _, _ = fmt.Fprintln(inv.Stderr, "\nCleaning up...") + cleanupCtx, cleanupCancel := cleanupStrategy.toContext(ctx) + defer cleanupCancel() + err = th.Cleanup(cleanupCtx) + if err != nil { + return xerrors.Errorf("cleanup tests: %w", err) + } + } + + if res.TotalFail > 0 { + return xerrors.New("load test failed, see above for more details") + } + + return nil + }, + } + + cmd.Options = serpent.OptionSet{ + { + Flag: "workspace-count", + FlagShorthand: "c", + Env: "CODER_SCALETEST_WORKSPACE_COUNT", + Description: "Required: Total number of workspaces to create.", + Value: serpent.Int64Of(&workspaceCount), + Required: true, + }, + { + Flag: "workspace-job-timeout", + Env: "CODER_SCALETEST_WORKSPACE_JOB_TIMEOUT", + Default: "5m", + Description: "Timeout for workspace jobs (e.g. build, start).", + Value: serpent.DurationOf(&workspaceJobTimeout), + }, + { + Flag: "autostart-delay", + Env: "CODER_SCALETEST_AUTOSTART_DELAY", + Default: "2m", + Description: "How long after all the workspaces have been stopped to schedule them to be started again.", + Value: serpent.DurationOf(&autostartDelay), + }, + { + Flag: "autostart-timeout", + Env: "CODER_SCALETEST_AUTOSTART_TIMEOUT", + Default: "5m", + Description: "Timeout for the autostart build to be initiated after the scheduled start time.", + Value: serpent.DurationOf(&autostartTimeout), + }, + { + Flag: "template", + FlagShorthand: "t", + Env: "CODER_SCALETEST_TEMPLATE", + Description: "Required: Name or ID of the template to use for workspaces.", + Value: serpent.StringOf(&template), + Required: true, + }, + { + Flag: "no-cleanup", + Env: "CODER_SCALETEST_NO_CLEANUP", + Description: "Do not clean up resources after the test completes.", + Value: serpent.BoolOf(&noCleanup), + }, + } + + cmd.Options = append(cmd.Options, parameterFlags.cliParameters()...) + tracingFlags.attach(&cmd.Options) + timeoutStrategy.attach(&cmd.Options) + cleanupStrategy.attach(&cmd.Options) + output.attach(&cmd.Options) + prometheusFlags.attach(&cmd.Options) + return cmd +} + type runnableTraceWrapper struct { tracer trace.Tracer spanName string diff --git a/scaletest/autostart/run.go b/scaletest/autostart/run.go index 59f50a04dd837..c37d843ad95c2 100644 --- a/scaletest/autostart/run.go +++ b/scaletest/autostart/run.go @@ -80,6 +80,7 @@ func (r *Runner) Run(ctx context.Context, id string, logs io.Writer) error { workspaceBuildConfig.UserID = newUser.ID.String() // We'll wait for the build ourselves to avoid multiple API requests workspaceBuildConfig.NoWaitForBuild = true + workspaceBuildConfig.NoWaitForAgents = true r.workspacebuildRunner = workspacebuild.NewRunner(newUserClient, workspaceBuildConfig) workspace, err := r.workspacebuildRunner.RunReturningWorkspace(ctx, id, logs) From a36078519952e4fcf20dfd0ca9d8e2777e9e3650 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Fri, 3 Oct 2025 15:04:20 +0100 Subject: [PATCH 051/298] ci(.github/workflows/traiage.yaml): check instead for push access to repo (#20163) --- .github/workflows/traiage.yaml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/workflows/traiage.yaml b/.github/workflows/traiage.yaml index 15adf78242686..566cfc78419bc 100644 --- a/.github/workflows/traiage.yaml +++ b/.github/workflows/traiage.yaml @@ -110,20 +110,19 @@ jobs: exit 1 fi - - name: Verify organization membership + - name: Verify push access env: - GITHUB_ORG: ${{ github.repository_owner }} + GITHUB_REPOSITORY: ${{ github.repository }} GH_TOKEN: ${{ github.token }} GITHUB_USERNAME: ${{ steps.determine-inputs.outputs.github_username }} GITHUB_USER_ID: ${{ steps.determine-inputs.outputs.github_user_id }} run: | - # Check if the actor is a member of the organization - if ! gh api "orgs/${GITHUB_ORG}/members/${GITHUB_USERNAME}" --silent 2>/dev/null; then - echo "::error title=Access Denied::User ${GITHUB_USERNAME} is not a member of the ${GITHUB_ORG} organization" - echo "::error::You must be a member of the ${GITHUB_ORG} GitHub organization to run this workflow." + # Query the actor’s permission on this repo + can_push="$(gh api "/repos/${GITHUB_REPOSITORY}/collaborators/${GITHUB_USERNAME}/permission" --jq '.user.permissions.push')" + if [[ "${can_push}" != "true" ]]; then + echo "::error title=Access Denied::${GITHUB_USERNAME} does not have push access to ${GITHUB_REPOSITORY}" exit 1 fi - echo "::notice::User ${GITHUB_USERNAME} verified as member of ${GITHUB_ORG} organization" - name: Extract context key from issue id: extract-context From bb5884467d7cc441945a8fa9f5334c9b3f9620fd Mon Sep 17 00:00:00 2001 From: Rafael Rodriguez Date: Fri, 3 Oct 2025 10:20:06 -0500 Subject: [PATCH 052/298] feat(cli): prompt for missing required template variables on template creation (#19973) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary In this pull request we're adding support in the CLI for prompting the user for any missing required template variables in the `coder templates push` command and automatically retrying the template build once a user has provided any missing variable values. Closes: https://github.com/coder/coder/issues/19782 ### Demo In the following recording I created a simple template terraform file that used different variable types (string, number, boolean, and sensitive) and prompted the user to enter a value for each variable.
See example template terraform file ```tf ... # Required variables for testing interactive prompting variable "docker_image" { description = "Docker image to use for the workspace" type = string } variable "workspace_name" { description = "Name of the workspace" type = string } variable "cpu_limit" { description = "CPU limit for the container (number of cores)" type = number } variable "enable_gpu" { description = "Enable GPU access for the container" type = bool } variable "api_key" { description = "API key for external services (sensitive)" type = string sensitive = true } # Optional variable with default variable "docker_socket" { default = "/var/run/docker.sock" description = "Docker socket path" type = string } ... ```
Once the user entered a valid value for each variable, the template build would be retried. https://github.com/user-attachments/assets/770cf954-3cbc-4464-925e-2be4e32a97de
See output from recording ```shell $ ./scripts/coder-dev.sh templates push test-required-params -d examples/templates/test-required-params/ INFO : Overriding codersdk.SessionTokenCookie as we are developing inside a Coder workspace. /home/coder/coder/build/coder-slim_2.26.0-devel+a68122ca3_linux_amd64 Provisioner tags: WARN: No .terraform.lock.hcl file found | When provisioning, Coder will be unable to cache providers without a lockfile and must download them from the internet each time. | Create one by running terraform init in your template directory. > Upload "examples/templates/test-required-params"? (yes/no) yes === ✔ Queued [0ms] ==> ⧗ Running ==> ⧗ Running === ✔ Running [4ms] ==> ⧗ Setting up === ✔ Setting up [0ms] ==> ⧗ Parsing template parameters === ✔ Parsing template parameters [8ms] ==> ⧗ Cleaning Up === ✘ Cleaning Up [4ms] === ✘ Cleaning Up [8ms] Found 5 missing required variables: - docker_image (string): Docker image to use for the workspace - workspace_name (string): Name of the workspace - cpu_limit (number): CPU limit for the container (number of cores) - enable_gpu (bool): Enable GPU access for the container - api_key (string): API key for external services (sensitive) The template requires values for the following variables: var.docker_image (required) Description: Docker image to use for the workspace Type: string Current value: > Enter value: image-name var.workspace_name (required) Description: Name of the workspace Type: string Current value: > Enter value: workspace-name var.cpu_limit (required) Description: CPU limit for the container (number of cores) Type: number Current value: > Enter value: 1 var.enable_gpu (required) Description: Enable GPU access for the container Type: bool Current value: ? Select value: false var.api_key (required), sensitive Description: API key for external services (sensitive) Type: string Current value: > Enter value: (*redacted*) ****** Retrying template build with provided variables... === ✔ Queued [0ms] ==> ⧗ Running ==> ⧗ Running === ✔ Running [2ms] ==> ⧗ Setting up === ✔ Setting up [0ms] ==> ⧗ Parsing template parameters === ✔ Parsing template parameters [7ms] ==> ⧗ Detecting persistent resources 2025-09-25 22:34:14.731Z Terraform 1.13.0 2025-09-25 22:34:15.140Z data.coder_provisioner.me: Refreshing... 2025-09-25 22:34:15.140Z data.coder_workspace.me: Refreshing... 2025-09-25 22:34:15.140Z data.coder_workspace_owner.me: Refreshing... 2025-09-25 22:34:15.141Z data.coder_provisioner.me: Refresh complete after 0s [id=2bd73098-d127-4362-b3a5-628e5bce6998] 2025-09-25 22:34:15.141Z data.coder_workspace_owner.me: Refresh complete after 0s [id=c2006933-4f3e-4c45-9e04-79612c3a5eca] 2025-09-25 22:34:15.141Z data.coder_workspace.me: Refresh complete after 0s [id=36f2dc6f-0bf2-43bd-bc4d-b29768334e02] 2025-09-25 22:34:15.186Z coder_agent.main: Plan to create 2025-09-25 22:34:15.186Z module.code-server[0].coder_app.code-server: Plan to create 2025-09-25 22:34:15.186Z docker_volume.home_volume: Plan to create 2025-09-25 22:34:15.186Z module.code-server[0].coder_script.code-server: Plan to create 2025-09-25 22:34:15.187Z docker_container.workspace[0]: Plan to create 2025-09-25 22:34:15.187Z Plan: 5 to add, 0 to change, 0 to destroy. === ✔ Detecting persistent resources [3104ms] ==> ⧗ Detecting ephemeral resources 2025-09-25 22:34:16.033Z Terraform 1.13.0 2025-09-25 22:34:16.428Z data.coder_workspace.me: Refreshing... 2025-09-25 22:34:16.428Z data.coder_provisioner.me: Refreshing... 2025-09-25 22:34:16.429Z data.coder_workspace_owner.me: Refreshing... 2025-09-25 22:34:16.429Z data.coder_provisioner.me: Refresh complete after 0s [id=2d2f7083-88e6-425c-9df3-856a3bf4cc73] 2025-09-25 22:34:16.429Z data.coder_workspace.me: Refresh complete after 0s [id=c723575e-c7d3-43d7-bf54-0e34d0959dc3] 2025-09-25 22:34:16.431Z data.coder_workspace_owner.me: Refresh complete after 0s [id=d43470c2-236e-4ae9-a977-6b53688c2cb1] 2025-09-25 22:34:16.453Z coder_agent.main: Plan to create 2025-09-25 22:34:16.453Z docker_volume.home_volume: Plan to create 2025-09-25 22:34:16.454Z Plan: 2 to add, 0 to change, 0 to destroy. === ✔ Detecting ephemeral resources [1278ms] ==> ⧗ Cleaning Up === ✔ Cleaning Up [6ms] ┌──────────────────────────────────┐ │ Template Preview │ ├──────────────────────────────────┤ │ RESOURCE │ ├──────────────────────────────────┤ │ docker_container.workspace │ │ └─ main (linux, amd64) │ ├──────────────────────────────────┤ │ docker_volume.home_volume │ └──────────────────────────────────┘ The test-required-params template has been created at Sep 25 22:34:16! Developers can provision a workspace with this template using: Updated version at Sep 25 22:34:16! ```
### Changes - Added a new function to check if the provisioner failed due to a template missing required variables - Added a handler function that is called when a provisioner fails due to the "missing required variables" error. The handler function will: - Check for provided template variables and identify any missing variables - Prompt the user for any missing variables (prompt is adapted based on the variable type) - Validate user input for missing variables - Retry the template build when all variables have been provided by the user ### Testing Added tests for the following scenarios: - Ensure validation based on variable type - Ensure users are not prompted for variables with a default value - Ensure variables provided via a variables files (`--variables-file`) or a variable flag (`--variable`) take precedence over a template --- cli/templatepush.go | 161 ++++++++++++++++++- cli/templatepush_test.go | 282 +++++++++++++++++++++++++++------ codersdk/provisionerdaemons.go | 6 + codersdk/templatevariables.go | 4 +- 4 files changed, 400 insertions(+), 53 deletions(-) diff --git a/cli/templatepush.go b/cli/templatepush.go index 7a21a0f8defad..03e1ca1cee88c 100644 --- a/cli/templatepush.go +++ b/cli/templatepush.go @@ -9,6 +9,7 @@ import ( "os" "path/filepath" "slices" + "strconv" "strings" "time" @@ -461,10 +462,14 @@ func createValidTemplateVersion(inv *serpent.Invocation, args createValidTemplat }) if err != nil { var jobErr *cliui.ProvisionerJobError - if errors.As(err, &jobErr) && !codersdk.JobIsMissingParameterErrorCode(jobErr.Code) { - return nil, err + if errors.As(err, &jobErr) { + if codersdk.JobIsMissingRequiredTemplateVariableErrorCode(jobErr.Code) { + return handleMissingTemplateVariables(inv, args, version.ID) + } + if !codersdk.JobIsMissingParameterErrorCode(jobErr.Code) { + return nil, err + } } - return nil, err } version, err = client.TemplateVersion(inv.Context(), version.ID) @@ -528,3 +533,153 @@ func prettyDirectoryPath(dir string) string { } return prettyDir } + +func handleMissingTemplateVariables(inv *serpent.Invocation, args createValidTemplateVersionArgs, failedVersionID uuid.UUID) (*codersdk.TemplateVersion, error) { + client := args.Client + + templateVariables, err := client.TemplateVersionVariables(inv.Context(), failedVersionID) + if err != nil { + return nil, xerrors.Errorf("fetch template variables: %w", err) + } + + existingValues := make(map[string]string) + for _, v := range args.UserVariableValues { + existingValues[v.Name] = v.Value + } + + var missingVariables []codersdk.TemplateVersionVariable + for _, variable := range templateVariables { + if !variable.Required { + continue + } + + if existingValue, exists := existingValues[variable.Name]; exists && existingValue != "" { + continue + } + + // Only prompt for variables that don't have a default value or have a redacted default + // Sensitive variables have a default value of "*redacted*" + // See: https://github.com/coder/coder/blob/a78790c632974e04babfef6de0e2ddf044787a7a/coderd/provisionerdserver/provisionerdserver.go#L3206 + if variable.DefaultValue == "" || (variable.Sensitive && variable.DefaultValue == "*redacted*") { + missingVariables = append(missingVariables, variable) + } + } + + if len(missingVariables) == 0 { + return nil, xerrors.New("no missing required variables found") + } + + _, _ = fmt.Fprintf(inv.Stderr, "Found %d missing required variables:\n", len(missingVariables)) + for _, v := range missingVariables { + _, _ = fmt.Fprintf(inv.Stderr, " - %s (%s): %s\n", v.Name, v.Type, v.Description) + } + + _, _ = fmt.Fprintln(inv.Stderr, "\nThe template requires values for the following variables:") + + var promptedValues []codersdk.VariableValue + for _, variable := range missingVariables { + value, err := promptForTemplateVariable(inv, variable) + if err != nil { + return nil, xerrors.Errorf("prompt for variable %q: %w", variable.Name, err) + } + promptedValues = append(promptedValues, codersdk.VariableValue{ + Name: variable.Name, + Value: value, + }) + } + + combinedValues := codersdk.CombineVariableValues(args.UserVariableValues, promptedValues) + + _, _ = fmt.Fprintln(inv.Stderr, "\nRetrying template build with provided variables...") + + retryArgs := args + retryArgs.UserVariableValues = combinedValues + + return createValidTemplateVersion(inv, retryArgs) +} + +func promptForTemplateVariable(inv *serpent.Invocation, variable codersdk.TemplateVersionVariable) (string, error) { + displayVariableInfo(inv, variable) + + switch variable.Type { + case "bool": + return promptForBoolVariable(inv, variable) + case "number": + return promptForNumberVariable(inv, variable) + default: + return promptForStringVariable(inv, variable) + } +} + +func displayVariableInfo(inv *serpent.Invocation, variable codersdk.TemplateVersionVariable) { + _, _ = fmt.Fprintf(inv.Stderr, "var.%s", cliui.Bold(variable.Name)) + if variable.Required { + _, _ = fmt.Fprint(inv.Stderr, pretty.Sprint(cliui.DefaultStyles.Error, " (required)")) + } + if variable.Sensitive { + _, _ = fmt.Fprint(inv.Stderr, pretty.Sprint(cliui.DefaultStyles.Warn, ", sensitive")) + } + _, _ = fmt.Fprintln(inv.Stderr, "") + + if variable.Description != "" { + _, _ = fmt.Fprintf(inv.Stderr, " Description: %s\n", variable.Description) + } + _, _ = fmt.Fprintf(inv.Stderr, " Type: %s\n", variable.Type) + _, _ = fmt.Fprintf(inv.Stderr, " Current value: %s\n", pretty.Sprint(cliui.DefaultStyles.Placeholder, "")) +} + +func promptForBoolVariable(inv *serpent.Invocation, variable codersdk.TemplateVersionVariable) (string, error) { + defaultValue := variable.DefaultValue + if defaultValue == "" { + defaultValue = "false" + } + + return cliui.Select(inv, cliui.SelectOptions{ + Options: []string{"true", "false"}, + Default: defaultValue, + Message: "Select value:", + }) +} + +func promptForNumberVariable(inv *serpent.Invocation, variable codersdk.TemplateVersionVariable) (string, error) { + prompt := "Enter value:" + if !variable.Required && variable.DefaultValue != "" { + prompt = fmt.Sprintf("Enter value (default: %q):", variable.DefaultValue) + } + + return cliui.Prompt(inv, cliui.PromptOptions{ + Text: prompt, + Default: variable.DefaultValue, + Validate: createVariableValidator(variable), + }) +} + +func promptForStringVariable(inv *serpent.Invocation, variable codersdk.TemplateVersionVariable) (string, error) { + prompt := "Enter value:" + if !variable.Sensitive { + if !variable.Required && variable.DefaultValue != "" { + prompt = fmt.Sprintf("Enter value (default: %q):", variable.DefaultValue) + } + } + + return cliui.Prompt(inv, cliui.PromptOptions{ + Text: prompt, + Default: variable.DefaultValue, + Secret: variable.Sensitive, + Validate: createVariableValidator(variable), + }) +} + +func createVariableValidator(variable codersdk.TemplateVersionVariable) func(string) error { + return func(s string) error { + if variable.Required && s == "" && variable.DefaultValue == "" { + return xerrors.New("value is required") + } + if variable.Type == "number" && s != "" { + if _, err := strconv.ParseFloat(s, 64); err != nil { + return xerrors.Errorf("must be a valid number, got: %q", s) + } + } + return nil + } +} diff --git a/cli/templatepush_test.go b/cli/templatepush_test.go index 7c8007c96a210..28c5adc20f213 100644 --- a/cli/templatepush_test.go +++ b/cli/templatepush_test.go @@ -852,54 +852,6 @@ func TestTemplatePush(t *testing.T) { require.Equal(t, "foobar", templateVariables[1].Value) }) - t.Run("VariableIsRequiredButNotProvided", func(t *testing.T) { - t.Parallel() - client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) - owner := coderdtest.CreateFirstUser(t, client) - templateAdmin, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, rbac.RoleTemplateAdmin()) - - templateVersion := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, createEchoResponsesWithTemplateVariables(initialTemplateVariables)) - _ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, templateVersion.ID) - template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, templateVersion.ID) - - // Test the cli command. - //nolint:gocritic - modifiedTemplateVariables := append(initialTemplateVariables, - &proto.TemplateVariable{ - Name: "second_variable", - Description: "This is the second variable.", - Type: "string", - Required: true, - }, - ) - source := clitest.CreateTemplateVersionSource(t, createEchoResponsesWithTemplateVariables(modifiedTemplateVariables)) - inv, root := clitest.New(t, "templates", "push", template.Name, "--directory", source, "--test.provisioner", string(database.ProvisionerTypeEcho), "--name", "example") - clitest.SetupConfig(t, templateAdmin, root) - pty := ptytest.New(t) - inv.Stdin = pty.Input() - inv.Stdout = pty.Output() - - execDone := make(chan error) - go func() { - execDone <- inv.Run() - }() - - matches := []struct { - match string - write string - }{ - {match: "Upload", write: "yes"}, - } - for _, m := range matches { - pty.ExpectMatch(m.match) - pty.WriteLine(m.write) - } - - wantErr := <-execDone - require.Error(t, wantErr) - require.Contains(t, wantErr.Error(), "required template variables need values") - }) - t.Run("VariableIsOptionalButNotProvided", func(t *testing.T) { t.Parallel() client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) @@ -1115,6 +1067,240 @@ func TestTemplatePush(t *testing.T) { require.Len(t, templateVersions, 2) require.Equal(t, "example", templateVersions[1].Name) }) + + t.Run("PromptForDifferentRequiredTypes", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) + owner := coderdtest.CreateFirstUser(t, client) + templateAdmin, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, rbac.RoleTemplateAdmin()) + + templateVariables := []*proto.TemplateVariable{ + { + Name: "string_var", + Description: "A string variable", + Type: "string", + Required: true, + }, + { + Name: "number_var", + Description: "A number variable", + Type: "number", + Required: true, + }, + { + Name: "bool_var", + Description: "A boolean variable", + Type: "bool", + Required: true, + }, + { + Name: "sensitive_var", + Description: "A sensitive variable", + Type: "string", + Required: true, + Sensitive: true, + }, + } + + source := clitest.CreateTemplateVersionSource(t, createEchoResponsesWithTemplateVariables(templateVariables)) + inv, root := clitest.New(t, "templates", "push", "test-template", "--directory", source, "--test.provisioner", string(database.ProvisionerTypeEcho)) + clitest.SetupConfig(t, templateAdmin, root) + pty := ptytest.New(t).Attach(inv) + + execDone := make(chan error) + go func() { + execDone <- inv.Run() + }() + + // Select "Yes" for the "Upload " prompt + pty.ExpectMatch("Upload") + pty.WriteLine("yes") + + pty.ExpectMatch("var.string_var") + pty.ExpectMatch("Enter value:") + pty.WriteLine("test-string") + + pty.ExpectMatch("var.number_var") + pty.ExpectMatch("Enter value:") + pty.WriteLine("42") + + // Boolean variable automatically selects the first option ("true") + pty.ExpectMatch("var.bool_var") + + pty.ExpectMatch("var.sensitive_var") + pty.ExpectMatch("Enter value:") + pty.WriteLine("secret-value") + + require.NoError(t, <-execDone) + }) + + t.Run("ValidateNumberInput", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) + owner := coderdtest.CreateFirstUser(t, client) + templateAdmin, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, rbac.RoleTemplateAdmin()) + + templateVariables := []*proto.TemplateVariable{ + { + Name: "number_var", + Description: "A number that requires validation", + Type: "number", + Required: true, + }, + } + + source := clitest.CreateTemplateVersionSource(t, createEchoResponsesWithTemplateVariables(templateVariables)) + inv, root := clitest.New(t, "templates", "push", "test-template", "--directory", source, "--test.provisioner", string(database.ProvisionerTypeEcho)) + clitest.SetupConfig(t, templateAdmin, root) + pty := ptytest.New(t).Attach(inv) + + execDone := make(chan error) + go func() { + execDone <- inv.Run() + }() + + // Select "Yes" for the "Upload " prompt + pty.ExpectMatch("Upload") + pty.WriteLine("yes") + + pty.ExpectMatch("var.number_var") + + pty.WriteLine("not-a-number") + pty.ExpectMatch("must be a valid number") + + pty.WriteLine("123.45") + + require.NoError(t, <-execDone) + }) + + t.Run("DontPromptForDefaultValues", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) + owner := coderdtest.CreateFirstUser(t, client) + templateAdmin, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, rbac.RoleTemplateAdmin()) + + templateVariables := []*proto.TemplateVariable{ + { + Name: "with_default", + Type: "string", + Required: true, + DefaultValue: "default-value", + }, + { + Name: "without_default", + Type: "string", + Required: true, + }, + } + + source := clitest.CreateTemplateVersionSource(t, createEchoResponsesWithTemplateVariables(templateVariables)) + inv, root := clitest.New(t, "templates", "push", "test-template", "--directory", source, "--test.provisioner", string(database.ProvisionerTypeEcho)) + clitest.SetupConfig(t, templateAdmin, root) + pty := ptytest.New(t).Attach(inv) + + execDone := make(chan error) + go func() { + execDone <- inv.Run() + }() + + // Select "Yes" for the "Upload " prompt + pty.ExpectMatch("Upload") + pty.WriteLine("yes") + + pty.ExpectMatch("var.without_default") + pty.WriteLine("test-value") + + require.NoError(t, <-execDone) + }) + + t.Run("VariableSourcesPriority", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) + owner := coderdtest.CreateFirstUser(t, client) + templateAdmin, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, rbac.RoleTemplateAdmin()) + + templateVariables := []*proto.TemplateVariable{ + { + Name: "cli_flag_var", + Description: "Variable provided via CLI flag", + Type: "string", + Required: true, + }, + { + Name: "file_var", + Description: "Variable provided via file", + Type: "string", + Required: true, + }, + { + Name: "prompt_var", + Description: "Variable provided via prompt", + Type: "string", + Required: true, + }, + { + Name: "cli_overrides_file_var", + Description: "Variable in both CLI and file", + Type: "string", + Required: true, + }, + } + + source := clitest.CreateTemplateVersionSource(t, createEchoResponsesWithTemplateVariables(templateVariables)) + + // Create a temporary variables file. + tempDir := t.TempDir() + removeTmpDirUntilSuccessAfterTest(t, tempDir) + variablesFile, err := os.CreateTemp(tempDir, "variables*.yaml") + require.NoError(t, err) + _, err = variablesFile.WriteString(`file_var: from-file +cli_overrides_file_var: from-file`) + require.NoError(t, err) + require.NoError(t, variablesFile.Close()) + + inv, root := clitest.New(t, "templates", "push", "test-template", + "--directory", source, + "--test.provisioner", string(database.ProvisionerTypeEcho), + "--variables-file", variablesFile.Name(), + "--variable", "cli_flag_var=from-cli-flag", + "--variable", "cli_overrides_file_var=from-cli-override", + ) + clitest.SetupConfig(t, templateAdmin, root) + pty := ptytest.New(t).Attach(inv) + + execDone := make(chan error) + go func() { + execDone <- inv.Run() + }() + + // Select "Yes" for the "Upload " prompt + pty.ExpectMatch("Upload") + pty.WriteLine("yes") + + // Only check for prompt_var, other variables should not prompt + pty.ExpectMatch("var.prompt_var") + pty.ExpectMatch("Enter value:") + pty.WriteLine("from-prompt") + + require.NoError(t, <-execDone) + + template, err := client.TemplateByName(context.Background(), owner.OrganizationID, "test-template") + require.NoError(t, err) + + templateVersionVars, err := client.TemplateVersionVariables(context.Background(), template.ActiveVersionID) + require.NoError(t, err) + require.Len(t, templateVersionVars, 4) + + varMap := make(map[string]string) + for _, tv := range templateVersionVars { + varMap[tv.Name] = tv.Value + } + + require.Equal(t, "from-cli-flag", varMap["cli_flag_var"]) + require.Equal(t, "from-file", varMap["file_var"]) + require.Equal(t, "from-prompt", varMap["prompt_var"]) + require.Equal(t, "from-cli-override", varMap["cli_overrides_file_var"]) + }) }) } diff --git a/codersdk/provisionerdaemons.go b/codersdk/provisionerdaemons.go index 4bff7d7827aa1..b3cefa09110ec 100644 --- a/codersdk/provisionerdaemons.go +++ b/codersdk/provisionerdaemons.go @@ -175,6 +175,12 @@ func JobIsMissingParameterErrorCode(code JobErrorCode) bool { return string(code) == runner.MissingParameterErrorCode } +// JobIsMissingRequiredTemplateVariableErrorCode returns whether the error is a missing a required template +// variable error. This can indicate to consumers that they need to provide required template variables. +func JobIsMissingRequiredTemplateVariableErrorCode(code JobErrorCode) bool { + return string(code) == runner.RequiredTemplateVariablesErrorCode +} + // ProvisionerJob describes the job executed by the provisioning daemon. type ProvisionerJob struct { ID uuid.UUID `json:"id" format:"uuid" table:"id"` diff --git a/codersdk/templatevariables.go b/codersdk/templatevariables.go index 3e02f6910642f..19c614e796e1e 100644 --- a/codersdk/templatevariables.go +++ b/codersdk/templatevariables.go @@ -68,7 +68,7 @@ func ParseUserVariableValues(varsFiles []string, variablesFile string, commandLi return nil, err } - return combineVariableValues(fromVars, fromFile, fromCommandLine), nil + return CombineVariableValues(fromVars, fromFile, fromCommandLine), nil } func parseVariableValuesFromVarsFiles(varsFiles []string) ([]VariableValue, error) { @@ -252,7 +252,7 @@ func parseVariableValuesFromCommandLine(variables []string) ([]VariableValue, er return values, nil } -func combineVariableValues(valuesSets ...[]VariableValue) []VariableValue { +func CombineVariableValues(valuesSets ...[]VariableValue) []VariableValue { combinedValues := make(map[string]string) for _, values := range valuesSets { From b48e367c58a5552e08fa2049e7433b3cea0359d3 Mon Sep 17 00:00:00 2001 From: Zach <3724288+zedkipp@users.noreply.github.com> Date: Fri, 3 Oct 2025 09:42:34 -0600 Subject: [PATCH 053/298] chore: remove dead randtz package (#20156) This package was added in 65db7a71 a couple years back and was seemingly never used (backed up by `git log -S randtz`). --- coderd/database/dbtestutil/randtz/randtz.go | 1034 ------------------- 1 file changed, 1034 deletions(-) delete mode 100644 coderd/database/dbtestutil/randtz/randtz.go diff --git a/coderd/database/dbtestutil/randtz/randtz.go b/coderd/database/dbtestutil/randtz/randtz.go deleted file mode 100644 index 1a53bfaf725fd..0000000000000 --- a/coderd/database/dbtestutil/randtz/randtz.go +++ /dev/null @@ -1,1034 +0,0 @@ -package randtz - -import ( - "math/rand" - "sync" - "testing" - "time" -) - -var ( - randTZName string - randTZNameOnce sync.Once -) - -// Name returns a random timezone name from the list of all -// timezones known to PostgreSQL. -func Name(t testing.TB) string { - t.Helper() - - randTZNameOnce.Do(func() { - // nolint: gosec // not used for cryptography - rnd := rand.New(rand.NewSource(time.Now().Unix())) - idx := rnd.Intn(len(tznames)) - randTZName = tznames[idx] - t.Logf("Random db timezone is %q\nIf you need a specific timezone, use dbtestutil.WithTimezone()", randTZName) - }) - - return randTZName -} - -// tznames is a list of all timezone names known to postgresql. -// The below list was generated with the query -// select name from pg_timezone_names order by name asc; -var tznames = []string{ - "Africa/Abidjan", - "Africa/Accra", - "Africa/Addis_Ababa", - "Africa/Algiers", - "Africa/Asmara", - "Africa/Asmera", - "Africa/Bamako", - "Africa/Bangui", - "Africa/Banjul", - "Africa/Bissau", - "Africa/Blantyre", - "Africa/Brazzaville", - "Africa/Bujumbura", - "Africa/Cairo", - "Africa/Casablanca", - "Africa/Ceuta", - "Africa/Conakry", - "Africa/Dakar", - "Africa/Dar_es_Salaam", - "Africa/Djibouti", - "Africa/Douala", - "Africa/El_Aaiun", - "Africa/Freetown", - "Africa/Gaborone", - "Africa/Harare", - "Africa/Johannesburg", - "Africa/Juba", - "Africa/Kampala", - "Africa/Khartoum", - "Africa/Kigali", - "Africa/Kinshasa", - "Africa/Lagos", - "Africa/Libreville", - "Africa/Lome", - "Africa/Luanda", - "Africa/Lubumbashi", - "Africa/Lusaka", - "Africa/Malabo", - "Africa/Maputo", - "Africa/Maseru", - "Africa/Mbabane", - "Africa/Mogadishu", - "Africa/Monrovia", - "Africa/Nairobi", - "Africa/Ndjamena", - "Africa/Niamey", - "Africa/Nouakchott", - "Africa/Ouagadougou", - "Africa/Porto-Novo", - "Africa/Sao_Tome", - "Africa/Timbuktu", - "Africa/Tripoli", - "Africa/Tunis", - "Africa/Windhoek", - "America/Adak", - "America/Anchorage", - "America/Anguilla", - "America/Antigua", - "America/Araguaina", - "America/Argentina/Buenos_Aires", - "America/Argentina/Catamarca", - "America/Argentina/ComodRivadavia", - "America/Argentina/Cordoba", - "America/Argentina/Jujuy", - "America/Argentina/La_Rioja", - "America/Argentina/Mendoza", - "America/Argentina/Rio_Gallegos", - "America/Argentina/Salta", - "America/Argentina/San_Juan", - "America/Argentina/San_Luis", - "America/Argentina/Tucuman", - "America/Argentina/Ushuaia", - "America/Aruba", - "America/Asuncion", - "America/Atikokan", - "America/Atka", - "America/Bahia", - "America/Bahia_Banderas", - "America/Barbados", - "America/Belem", - "America/Belize", - "America/Blanc-Sablon", - "America/Boa_Vista", - "America/Bogota", - "America/Boise", - "America/Buenos_Aires", - "America/Cambridge_Bay", - "America/Campo_Grande", - "America/Cancun", - "America/Caracas", - "America/Catamarca", - "America/Cayenne", - "America/Cayman", - "America/Chicago", - "America/Chihuahua", - "America/Ciudad_Juarez", - "America/Coral_Harbor", - "America/Cordoba", - "America/Costa_Rica", - "America/Creston", - "America/Cuiaba", - "America/Curacao", - "America/Danmarkshavn", - "America/Dawson", - "America/Dawson_Creek", - "America/Denver", - "America/Detroit", - "America/Dominica", - "America/Edmonton", - "America/Eirunepe", - "America/El_Salvador", - "America/Ensenada", - "America/Fortaleza", - "America/Fort_Nelson", - "America/Fort_Wayne", - "America/Glace_Bay", - "America/Godthab", - "America/Goose_Bay", - "America/Grand_Turk", - "America/Grenada", - "America/Guadeloupe", - "America/Guatemala", - "America/Guayaquil", - "America/Guyana", - "America/Halifax", - "America/Havana", - "America/Hermosillo", - "America/Indiana/Indianapolis", - "America/Indiana/Knox", - "America/Indiana/Marengo", - "America/Indiana/Petersburg", - "America/Indianapolis", - "America/Indiana/Tell_City", - "America/Indiana/Vevay", - "America/Indiana/Vincennes", - "America/Indiana/Winamac", - "America/Inuvik", - "America/Iqaluit", - "America/Jamaica", - "America/Jujuy", - "America/Juneau", - "America/Kentucky/Louisville", - "America/Kentucky/Monticello", - "America/Knox_IN", - "America/Kralendijk", - "America/La_Paz", - "America/Lima", - "America/Los_Angeles", - "America/Louisville", - "America/Lower_Princes", - "America/Maceio", - "America/Managua", - "America/Manaus", - "America/Marigot", - "America/Martinique", - "America/Matamoros", - "America/Mazatlan", - "America/Mendoza", - "America/Menominee", - "America/Merida", - "America/Metlakatla", - "America/Mexico_City", - "America/Miquelon", - "America/Moncton", - "America/Monterrey", - "America/Montevideo", - "America/Montreal", - "America/Montserrat", - "America/Nassau", - "America/New_York", - "America/Nipigon", - "America/Nome", - "America/Noronha", - "America/North_Dakota/Beulah", - "America/North_Dakota/Center", - "America/North_Dakota/New_Salem", - "America/Nuuk", - "America/Ojinaga", - "America/Panama", - "America/Pangnirtung", - "America/Paramaribo", - "America/Phoenix", - "America/Port-au-Prince", - "America/Porto_Acre", - "America/Port_of_Spain", - "America/Porto_Velho", - "America/Puerto_Rico", - "America/Punta_Arenas", - "America/Rainy_River", - "America/Rankin_Inlet", - "America/Recife", - "America/Regina", - "America/Resolute", - "America/Rio_Branco", - "America/Rosario", - "America/Santa_Isabel", - "America/Santarem", - "America/Santiago", - "America/Santo_Domingo", - "America/Sao_Paulo", - "America/Scoresbysund", - "America/Shiprock", - "America/Sitka", - "America/St_Barthelemy", - "America/St_Johns", - "America/St_Kitts", - "America/St_Lucia", - "America/St_Thomas", - "America/St_Vincent", - "America/Swift_Current", - "America/Tegucigalpa", - "America/Thule", - "America/Thunder_Bay", - "America/Tijuana", - "America/Toronto", - "America/Tortola", - "America/Vancouver", - "America/Virgin", - "America/Whitehorse", - "America/Winnipeg", - "America/Yakutat", - "America/Yellowknife", - "Antarctica/Casey", - "Antarctica/Davis", - "Antarctica/DumontDUrville", - "Antarctica/Macquarie", - "Antarctica/Mawson", - "Antarctica/McMurdo", - "Antarctica/Palmer", - "Antarctica/Rothera", - "Antarctica/South_Pole", - "Antarctica/Syowa", - "Antarctica/Troll", - "Antarctica/Vostok", - "Arctic/Longyearbyen", - "Asia/Aden", - "Asia/Almaty", - "Asia/Amman", - "Asia/Anadyr", - "Asia/Aqtau", - "Asia/Aqtobe", - "Asia/Ashgabat", - "Asia/Ashkhabad", - "Asia/Atyrau", - "Asia/Baghdad", - "Asia/Bahrain", - "Asia/Baku", - "Asia/Bangkok", - "Asia/Barnaul", - "Asia/Beirut", - "Asia/Bishkek", - "Asia/Brunei", - "Asia/Calcutta", - "Asia/Chita", - "Asia/Choibalsan", - "Asia/Chongqing", - "Asia/Chungking", - "Asia/Colombo", - "Asia/Dacca", - "Asia/Damascus", - "Asia/Dhaka", - "Asia/Dili", - "Asia/Dubai", - "Asia/Dushanbe", - "Asia/Famagusta", - "Asia/Gaza", - "Asia/Harbin", - "Asia/Hebron", - "Asia/Ho_Chi_Minh", - "Asia/Hong_Kong", - "Asia/Hovd", - "Asia/Irkutsk", - "Asia/Istanbul", - "Asia/Jakarta", - "Asia/Jayapura", - "Asia/Jerusalem", - "Asia/Kabul", - "Asia/Kamchatka", - "Asia/Karachi", - "Asia/Kashgar", - "Asia/Kathmandu", - "Asia/Katmandu", - "Asia/Khandyga", - "Asia/Kolkata", - "Asia/Krasnoyarsk", - "Asia/Kuala_Lumpur", - "Asia/Kuching", - "Asia/Kuwait", - "Asia/Macao", - "Asia/Macau", - "Asia/Magadan", - "Asia/Makassar", - "Asia/Manila", - "Asia/Muscat", - "Asia/Nicosia", - "Asia/Novokuznetsk", - "Asia/Novosibirsk", - "Asia/Omsk", - "Asia/Oral", - "Asia/Phnom_Penh", - "Asia/Pontianak", - "Asia/Pyongyang", - "Asia/Qatar", - "Asia/Qostanay", - "Asia/Qyzylorda", - "Asia/Rangoon", - "Asia/Riyadh", - "Asia/Saigon", - "Asia/Sakhalin", - "Asia/Samarkand", - "Asia/Seoul", - "Asia/Shanghai", - "Asia/Singapore", - "Asia/Srednekolymsk", - "Asia/Taipei", - "Asia/Tashkent", - "Asia/Tbilisi", - "Asia/Tehran", - "Asia/Tel_Aviv", - "Asia/Thimbu", - "Asia/Thimphu", - "Asia/Tokyo", - "Asia/Tomsk", - "Asia/Ujung_Pandang", - "Asia/Ulaanbaatar", - "Asia/Ulan_Bator", - "Asia/Urumqi", - "Asia/Ust-Nera", - "Asia/Vientiane", - "Asia/Vladivostok", - "Asia/Yakutsk", - "Asia/Yangon", - "Asia/Yekaterinburg", - "Asia/Yerevan", - "Atlantic/Azores", - "Atlantic/Bermuda", - "Atlantic/Canary", - "Atlantic/Cape_Verde", - "Atlantic/Faeroe", - "Atlantic/Faroe", - "Atlantic/Jan_Mayen", - "Atlantic/Madeira", - "Atlantic/Reykjavik", - "Atlantic/South_Georgia", - "Atlantic/Stanley", - "Atlantic/St_Helena", - "Australia/ACT", - "Australia/Adelaide", - "Australia/Brisbane", - "Australia/Broken_Hill", - "Australia/Canberra", - "Australia/Currie", - "Australia/Darwin", - "Australia/Eucla", - "Australia/Hobart", - "Australia/LHI", - "Australia/Lindeman", - "Australia/Lord_Howe", - "Australia/Melbourne", - "Australia/North", - "Australia/NSW", - "Australia/Perth", - "Australia/Queensland", - "Australia/South", - "Australia/Sydney", - "Australia/Tasmania", - "Australia/Victoria", - "Australia/West", - "Australia/Yancowinna", - "Brazil/Acre", - "Brazil/DeNoronha", - "Brazil/East", - "Brazil/West", - "Canada/Atlantic", - "Canada/Central", - "Canada/Eastern", - "Canada/Mountain", - "Canada/Newfoundland", - "Canada/Pacific", - "Canada/Saskatchewan", - "Canada/Yukon", - "CET", - "Chile/Continental", - "Chile/EasterIsland", - "CST6CDT", - "Cuba", - "EET", - "Egypt", - "Eire", - "EST", - "EST5EDT", - "Etc/GMT", - "Etc/GMT+0", - "Etc/GMT-0", - "Etc/GMT0", - "Etc/GMT+1", - "Etc/GMT-1", - "Etc/GMT+10", - "Etc/GMT-10", - "Etc/GMT+11", - "Etc/GMT-11", - "Etc/GMT+12", - "Etc/GMT-12", - "Etc/GMT-13", - "Etc/GMT-14", - "Etc/GMT+2", - "Etc/GMT-2", - "Etc/GMT+3", - "Etc/GMT-3", - "Etc/GMT+4", - "Etc/GMT-4", - "Etc/GMT+5", - "Etc/GMT-5", - "Etc/GMT+6", - "Etc/GMT-6", - "Etc/GMT+7", - "Etc/GMT-7", - "Etc/GMT+8", - "Etc/GMT-8", - "Etc/GMT+9", - "Etc/GMT-9", - "Etc/Greenwich", - "Etc/UCT", - "Etc/Universal", - "Etc/UTC", - "Etc/Zulu", - "Europe/Amsterdam", - "Europe/Andorra", - "Europe/Astrakhan", - "Europe/Athens", - "Europe/Belfast", - "Europe/Belgrade", - "Europe/Berlin", - "Europe/Bratislava", - "Europe/Brussels", - "Europe/Bucharest", - "Europe/Budapest", - "Europe/Busingen", - "Europe/Chisinau", - "Europe/Copenhagen", - "Europe/Dublin", - "Europe/Gibraltar", - "Europe/Guernsey", - "Europe/Helsinki", - "Europe/Isle_of_Man", - "Europe/Istanbul", - "Europe/Jersey", - "Europe/Kaliningrad", - "Europe/Kiev", - "Europe/Kirov", - "Europe/Lisbon", - "Europe/Ljubljana", - "Europe/London", - "Europe/Luxembourg", - "Europe/Madrid", - "Europe/Malta", - "Europe/Mariehamn", - "Europe/Minsk", - "Europe/Monaco", - "Europe/Moscow", - "Europe/Nicosia", - "Europe/Oslo", - "Europe/Paris", - "Europe/Podgorica", - "Europe/Prague", - "Europe/Riga", - "Europe/Rome", - "Europe/Samara", - "Europe/San_Marino", - "Europe/Sarajevo", - "Europe/Saratov", - "Europe/Simferopol", - "Europe/Skopje", - "Europe/Sofia", - "Europe/Stockholm", - "Europe/Tallinn", - "Europe/Tirane", - "Europe/Tiraspol", - "Europe/Ulyanovsk", - "Europe/Uzhgorod", - "Europe/Vaduz", - "Europe/Vatican", - "Europe/Vienna", - "Europe/Vilnius", - "Europe/Volgograd", - "Europe/Warsaw", - "Europe/Zagreb", - "Europe/Zaporozhye", - "Europe/Zurich", - "Factory", - "GB", - "GB-Eire", - "GMT", - "GMT+0", - "GMT-0", - "GMT0", - "Greenwich", - "Hongkong", - "HST", - "Iceland", - "Indian/Antananarivo", - "Indian/Chagos", - "Indian/Christmas", - "Indian/Cocos", - "Indian/Comoro", - "Indian/Kerguelen", - "Indian/Mahe", - "Indian/Maldives", - "Indian/Mauritius", - "Indian/Mayotte", - "Indian/Reunion", - "Iran", - "Israel", - "Jamaica", - "Japan", - "Kwajalein", - "Libya", - "localtime", - "MET", - "Mexico/BajaNorte", - "Mexico/BajaSur", - "Mexico/General", - "MST", - "MST7MDT", - "Navajo", - "NZ", - "NZ-CHAT", - "Pacific/Apia", - "Pacific/Auckland", - "Pacific/Bougainville", - "Pacific/Chatham", - "Pacific/Chuuk", - "Pacific/Easter", - "Pacific/Efate", - "Pacific/Enderbury", - "Pacific/Fakaofo", - "Pacific/Fiji", - "Pacific/Funafuti", - "Pacific/Galapagos", - "Pacific/Gambier", - "Pacific/Guadalcanal", - "Pacific/Guam", - "Pacific/Honolulu", - "Pacific/Johnston", - "Pacific/Kiritimati", - "Pacific/Kosrae", - "Pacific/Kwajalein", - "Pacific/Majuro", - "Pacific/Marquesas", - "Pacific/Midway", - "Pacific/Nauru", - "Pacific/Niue", - "Pacific/Norfolk", - "Pacific/Noumea", - "Pacific/Pago_Pago", - "Pacific/Palau", - "Pacific/Pitcairn", - "Pacific/Pohnpei", - "Pacific/Ponape", - "Pacific/Port_Moresby", - "Pacific/Rarotonga", - "Pacific/Saipan", - "Pacific/Samoa", - "Pacific/Tahiti", - "Pacific/Tarawa", - "Pacific/Tongatapu", - "Pacific/Truk", - "Pacific/Wake", - "Pacific/Wallis", - "Pacific/Yap", - "Poland", - "Portugal", - "posix/Africa/Abidjan", - "posix/Africa/Accra", - "posix/Africa/Addis_Ababa", - "posix/Africa/Algiers", - "posix/Africa/Asmara", - "posix/Africa/Asmera", - "posix/Africa/Bamako", - "posix/Africa/Bangui", - "posix/Africa/Banjul", - "posix/Africa/Bissau", - "posix/Africa/Blantyre", - "posix/Africa/Brazzaville", - "posix/Africa/Bujumbura", - "posix/Africa/Cairo", - "posix/Africa/Casablanca", - "posix/Africa/Ceuta", - "posix/Africa/Conakry", - "posix/Africa/Dakar", - "posix/Africa/Dar_es_Salaam", - "posix/Africa/Djibouti", - "posix/Africa/Douala", - "posix/Africa/El_Aaiun", - "posix/Africa/Freetown", - "posix/Africa/Gaborone", - "posix/Africa/Harare", - "posix/Africa/Johannesburg", - "posix/Africa/Juba", - "posix/Africa/Kampala", - "posix/Africa/Khartoum", - "posix/Africa/Kigali", - "posix/Africa/Kinshasa", - "posix/Africa/Lagos", - "posix/Africa/Libreville", - "posix/Africa/Lome", - "posix/Africa/Luanda", - "posix/Africa/Lubumbashi", - "posix/Africa/Lusaka", - "posix/Africa/Malabo", - "posix/Africa/Maputo", - "posix/Africa/Maseru", - "posix/Africa/Mbabane", - "posix/Africa/Mogadishu", - "posix/Africa/Monrovia", - "posix/Africa/Nairobi", - "posix/Africa/Ndjamena", - "posix/Africa/Niamey", - "posix/Africa/Nouakchott", - "posix/Africa/Ouagadougou", - "posix/Africa/Porto-Novo", - "posix/Africa/Sao_Tome", - "posix/Africa/Timbuktu", - "posix/Africa/Tripoli", - "posix/Africa/Tunis", - "posix/Africa/Windhoek", - "posix/America/Adak", - "posix/America/Anchorage", - "posix/America/Anguilla", - "posix/America/Antigua", - "posix/America/Araguaina", - "posix/America/Argentina/Buenos_Aires", - "posix/America/Argentina/Catamarca", - "posix/America/Argentina/ComodRivadavia", - "posix/America/Argentina/Cordoba", - "posix/America/Argentina/Jujuy", - "posix/America/Argentina/La_Rioja", - "posix/America/Argentina/Mendoza", - "posix/America/Argentina/Rio_Gallegos", - "posix/America/Argentina/Salta", - "posix/America/Argentina/San_Juan", - "posix/America/Argentina/San_Luis", - "posix/America/Argentina/Tucuman", - "posix/America/Argentina/Ushuaia", - "posix/America/Aruba", - "posix/America/Asuncion", - "posix/America/Atikokan", - "posix/America/Atka", - "posix/America/Bahia", - "posix/America/Bahia_Banderas", - "posix/America/Barbados", - "posix/America/Belem", - "posix/America/Belize", - "posix/America/Blanc-Sablon", - "posix/America/Boa_Vista", - "posix/America/Bogota", - "posix/America/Boise", - "posix/America/Buenos_Aires", - "posix/America/Cambridge_Bay", - "posix/America/Campo_Grande", - "posix/America/Cancun", - "posix/America/Caracas", - "posix/America/Catamarca", - "posix/America/Cayenne", - "posix/America/Cayman", - "posix/America/Chicago", - "posix/America/Chihuahua", - "posix/America/Ciudad_Juarez", - "posix/America/Coral_Harbor", - "posix/America/Cordoba", - "posix/America/Costa_Rica", - "posix/America/Creston", - "posix/America/Cuiaba", - "posix/America/Curacao", - "posix/America/Danmarkshavn", - "posix/America/Dawson", - "posix/America/Dawson_Creek", - "posix/America/Denver", - "posix/America/Detroit", - "posix/America/Dominica", - "posix/America/Edmonton", - "posix/America/Eirunepe", - "posix/America/El_Salvador", - "posix/America/Ensenada", - "posix/America/Fortaleza", - "posix/America/Fort_Nelson", - "posix/America/Fort_Wayne", - "posix/America/Glace_Bay", - "posix/America/Godthab", - "posix/America/Goose_Bay", - "posix/America/Grand_Turk", - "posix/America/Grenada", - "posix/America/Guadeloupe", - "posix/America/Guatemala", - "posix/America/Guayaquil", - "posix/America/Guyana", - "posix/America/Halifax", - "posix/America/Havana", - "posix/America/Hermosillo", - "posix/America/Indiana/Indianapolis", - "posix/America/Indiana/Knox", - "posix/America/Indiana/Marengo", - "posix/America/Indiana/Petersburg", - "posix/America/Indianapolis", - "posix/America/Indiana/Tell_City", - "posix/America/Indiana/Vevay", - "posix/America/Indiana/Vincennes", - "posix/America/Indiana/Winamac", - "posix/America/Inuvik", - "posix/America/Iqaluit", - "posix/America/Jamaica", - "posix/America/Jujuy", - "posix/America/Juneau", - "posix/America/Kentucky/Louisville", - "posix/America/Kentucky/Monticello", - "posix/America/Knox_IN", - "posix/America/Kralendijk", - "posix/America/La_Paz", - "posix/America/Lima", - "posix/America/Los_Angeles", - "posix/America/Louisville", - "posix/America/Lower_Princes", - "posix/America/Maceio", - "posix/America/Managua", - "posix/America/Manaus", - "posix/America/Marigot", - "posix/America/Martinique", - "posix/America/Matamoros", - "posix/America/Mazatlan", - "posix/America/Mendoza", - "posix/America/Menominee", - "posix/America/Merida", - "posix/America/Metlakatla", - "posix/America/Mexico_City", - "posix/America/Miquelon", - "posix/America/Moncton", - "posix/America/Monterrey", - "posix/America/Montevideo", - "posix/America/Montreal", - "posix/America/Montserrat", - "posix/America/Nassau", - "posix/America/New_York", - "posix/America/Nipigon", - "posix/America/Nome", - "posix/America/Noronha", - "posix/America/North_Dakota/Beulah", - "posix/America/North_Dakota/Center", - "posix/America/North_Dakota/New_Salem", - "posix/America/Nuuk", - "posix/America/Ojinaga", - "posix/America/Panama", - "posix/America/Pangnirtung", - "posix/America/Paramaribo", - "posix/America/Phoenix", - "posix/America/Port-au-Prince", - "posix/America/Porto_Acre", - "posix/America/Port_of_Spain", - "posix/America/Porto_Velho", - "posix/America/Puerto_Rico", - "posix/America/Punta_Arenas", - "posix/America/Rainy_River", - "posix/America/Rankin_Inlet", - "posix/America/Recife", - "posix/America/Regina", - "posix/America/Resolute", - "posix/America/Rio_Branco", - "posix/America/Rosario", - "posix/America/Santa_Isabel", - "posix/America/Santarem", - "posix/America/Santiago", - "posix/America/Santo_Domingo", - "posix/America/Sao_Paulo", - "posix/America/Scoresbysund", - "posix/America/Shiprock", - "posix/America/Sitka", - "posix/America/St_Barthelemy", - "posix/America/St_Johns", - "posix/America/St_Kitts", - "posix/America/St_Lucia", - "posix/America/St_Thomas", - "posix/America/St_Vincent", - "posix/America/Swift_Current", - "posix/America/Tegucigalpa", - "posix/America/Thule", - "posix/America/Thunder_Bay", - "posix/America/Tijuana", - "posix/America/Toronto", - "posix/America/Tortola", - "posix/America/Vancouver", - "posix/America/Virgin", - "posix/America/Whitehorse", - "posix/America/Winnipeg", - "posix/America/Yakutat", - "posix/America/Yellowknife", - "posix/Antarctica/Casey", - "posix/Antarctica/Davis", - "posix/Antarctica/DumontDUrville", - "posix/Antarctica/Macquarie", - "posix/Antarctica/Mawson", - "posix/Antarctica/McMurdo", - "posix/Antarctica/Palmer", - "posix/Antarctica/Rothera", - "posix/Antarctica/South_Pole", - "posix/Antarctica/Syowa", - "posix/Antarctica/Troll", - "posix/Antarctica/Vostok", - "posix/Arctic/Longyearbyen", - "posix/Asia/Aden", - "posix/Asia/Almaty", - "posix/Asia/Amman", - "posix/Asia/Anadyr", - "posix/Asia/Aqtau", - "posix/Asia/Aqtobe", - "posix/Asia/Ashgabat", - "posix/Asia/Ashkhabad", - "posix/Asia/Atyrau", - "posix/Asia/Baghdad", - "posix/Asia/Bahrain", - "posix/Asia/Baku", - "posix/Asia/Bangkok", - "posix/Asia/Barnaul", - "posix/Asia/Beirut", - "posix/Asia/Bishkek", - "posix/Asia/Brunei", - "posix/Asia/Calcutta", - "posix/Asia/Chita", - "posix/Asia/Choibalsan", - "posix/Asia/Chongqing", - "posix/Asia/Chungking", - "posix/Asia/Colombo", - "posix/Asia/Dacca", - "posix/Asia/Damascus", - "posix/Asia/Dhaka", - "posix/Asia/Dili", - "posix/Asia/Dubai", - "posix/Asia/Dushanbe", - "posix/Asia/Famagusta", - "posix/Asia/Gaza", - "posix/Asia/Harbin", - "posix/Asia/Hebron", - "posix/Asia/Ho_Chi_Minh", - "posix/Asia/Hong_Kong", - "posix/Asia/Hovd", - "posix/Asia/Irkutsk", - "posix/Asia/Istanbul", - "posix/Asia/Jakarta", - "posix/Asia/Jayapura", - "posix/Asia/Jerusalem", - "posix/Asia/Kabul", - "posix/Asia/Kamchatka", - "posix/Asia/Karachi", - "posix/Asia/Kashgar", - "posix/Asia/Kathmandu", - "posix/Asia/Katmandu", - "posix/Asia/Khandyga", - "posix/Asia/Kolkata", - "posix/Asia/Krasnoyarsk", - "posix/Asia/Kuala_Lumpur", - "posix/Asia/Kuching", - "posix/Asia/Kuwait", - "posix/Asia/Macao", - "posix/Asia/Macau", - "posix/Asia/Magadan", - "posix/Asia/Makassar", - "posix/Asia/Manila", - "posix/Asia/Muscat", - "posix/Asia/Nicosia", - "posix/Asia/Novokuznetsk", - "posix/Asia/Novosibirsk", - "posix/Asia/Omsk", - "posix/Asia/Oral", - "posix/Asia/Phnom_Penh", - "posix/Asia/Pontianak", - "posix/Asia/Pyongyang", - "posix/Asia/Qatar", - "posix/Asia/Qostanay", - "posix/Asia/Qyzylorda", - "posix/Asia/Rangoon", - "posix/Asia/Riyadh", - "posix/Asia/Saigon", - "posix/Asia/Sakhalin", - "posix/Asia/Samarkand", - "posix/Asia/Seoul", - "posix/Asia/Shanghai", - "posix/Asia/Singapore", - "posix/Asia/Srednekolymsk", - "posix/Asia/Taipei", - "posix/Asia/Tashkent", - "posix/Asia/Tbilisi", - "posix/Asia/Tehran", - "posix/Asia/Tel_Aviv", - "posix/Asia/Thimbu", - "posix/Asia/Thimphu", - "posix/Asia/Tokyo", - "posix/Asia/Tomsk", - "posix/Asia/Ujung_Pandang", - "posix/Asia/Ulaanbaatar", - "posix/Asia/Ulan_Bator", - "posix/Asia/Urumqi", - "posix/Asia/Ust-Nera", - "posix/Asia/Vientiane", - "posix/Asia/Vladivostok", - "posix/Asia/Yakutsk", - "posix/Asia/Yangon", - "posix/Asia/Yekaterinburg", - "posix/Asia/Yerevan", - "posix/Atlantic/Azores", - "posix/Atlantic/Bermuda", - "posix/Atlantic/Canary", - "posix/Atlantic/Cape_Verde", - "posix/Atlantic/Faeroe", - "posix/Atlantic/Faroe", - "posix/Atlantic/Jan_Mayen", - "posix/Atlantic/Madeira", - "posix/Atlantic/Reykjavik", - "posix/Atlantic/South_Georgia", - "posix/Atlantic/Stanley", - "posix/Atlantic/St_Helena", - "posix/Australia/ACT", - "posix/Australia/Adelaide", - "posix/Australia/Brisbane", - "posix/Australia/Broken_Hill", - "posix/Australia/Canberra", - "posix/Australia/Currie", - "posix/Australia/Darwin", - "posix/Australia/Eucla", - "posix/Australia/Hobart", - "posix/Australia/LHI", - "posix/Australia/Lindeman", - "posix/Australia/Lord_Howe", - "posix/Australia/Melbourne", - "posix/Australia/North", - "posix/Australia/NSW", - "posix/Australia/Perth", - "posix/Australia/Queensland", - "posix/Australia/South", - "posix/Australia/Sydney", - "posix/Australia/Tasmania", - "posix/Australia/Victoria", - "posix/Australia/West", - "posix/Australia/Yancowinna", - "posix/Brazil/Acre", - "posix/Brazil/DeNoronha", - "posix/Brazil/East", - "posix/Brazil/West", - "posix/Canada/Atlantic", - "posix/Canada/Central", - "posix/Canada/Eastern", - "posix/Canada/Mountain", - "posix/Canada/Newfoundland", - "posix/Canada/Pacific", - "posix/Canada/Saskatchewan", - "posix/Canada/Yukon", - "posix/CET", - "posix/Chile/Continental", - "posix/Chile/EasterIsland", - "posix/CST6CDT", - "posix/Cuba", - "posix/EET", - "posix/Egypt", - "posix/Eire", - "posix/EST", - "posix/EST5EDT", - "posix/Etc/GMT", - "posix/Etc/GMT+0", - "posix/Etc/GMT-0", - "posix/Etc/GMT0", - "posix/Etc/GMT+1", - "posix/Etc/GMT-1", - "posix/Etc/GMT+10", - "posix/Etc/GMT-10", - "posix/Etc/GMT+11", - "posix/Etc/GMT-11", - "posix/Etc/GMT+12", - "posix/Etc/GMT-12", - "posix/Etc/GMT-13", - "posix/Etc/GMT-14", - "posix/Etc/GMT+2", - "posix/Etc/GMT-2", - "posix/Etc/GMT+3", - "posix/Etc/GMT-3", - "posix/Etc/GMT+4", - "posix/Etc/GMT-4", - "posix/Etc/GMT+5", - "posix/Etc/GMT-5", - "posix/Etc/GMT+6", - "posix/Etc/GMT-6", - "posix/Etc/GMT+7", - "posix/Etc/GMT-7", - "posix/Etc/GMT+8", - "posix/Etc/GMT-8", - "posix/Etc/GMT+9", - "posix/Etc/GMT-9", - "posix/Etc/Greenwich", - "posix/Etc/UCT", - "posix/Etc/Universal", - "posix/Etc/UTC", - "posix/Etc/Zulu", - "posix/Europe/Amsterdam", -} From 2ec9be2203e559016955d6280d39c2d193599610 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=82=B1=E3=82=A4=E3=83=A9?= Date: Fri, 3 Oct 2025 14:11:08 -0600 Subject: [PATCH 054/298] fix: only show error once when failing to update org member roles (#20155) --- .../OrganizationMembersPageView.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/site/src/pages/OrganizationSettingsPage/OrganizationMembersPageView.tsx b/site/src/pages/OrganizationSettingsPage/OrganizationMembersPageView.tsx index f720ba692d0ca..106f2d17d4914 100644 --- a/site/src/pages/OrganizationSettingsPage/OrganizationMembersPageView.tsx +++ b/site/src/pages/OrganizationSettingsPage/OrganizationMembersPageView.tsx @@ -149,14 +149,12 @@ export const OrganizationMembersPageView: FC< isLoading={isUpdatingMemberRoles} canEditUsers={canEditMembers} onEditRoles={async (roles) => { + // React doesn't mind uncaught errors in event handlers, + // but testing-library does. try { await updateMemberRoles(member, roles); displaySuccess("Roles updated successfully."); - } catch (error) { - displayError( - getErrorMessage(error, "Failed to update roles."), - ); - } + } catch {} }} /> From c1357d4e275559221007a6efaef7a647af5217d0 Mon Sep 17 00:00:00 2001 From: Atif Ali Date: Sat, 4 Oct 2025 15:58:17 +0500 Subject: [PATCH 055/298] chore(dogfood): dogfood latest version of agentAPI (#20165) --- dogfood/coder/main.tf | 1 + 1 file changed, 1 insertion(+) diff --git a/dogfood/coder/main.tf b/dogfood/coder/main.tf index 614b25cdb9ec8..cc223996ba7e9 100644 --- a/dogfood/coder/main.tf +++ b/dogfood/coder/main.tf @@ -872,6 +872,7 @@ module "claude-code" { claude_code_version = "latest" order = 999 claude_api_key = data.coder_workspace_owner.me.session_token + agentapi_version = "latest" system_prompt = local.claude_system_prompt ai_prompt = data.coder_parameter.ai_prompt.value From d17dd5d7879f44518f9f0ce1ff2bd462e25d5ddc Mon Sep 17 00:00:00 2001 From: Sas Swart Date: Mon, 6 Oct 2025 10:56:43 +0200 Subject: [PATCH 056/298] feat: add filtering by initiator to provisioner job listing in the CLI (#20137) Relates to https://github.com/coder/internal/issues/934 This PR provides a mechanism to filter provisioner jobs according to who initiated the job. This will be used to find pending prebuild jobs when prebuilds have overwhelmed the provisioner job queue. They can then be canceled. If prebuilds are overwhelming provisioners, the following steps will be taken: ```bash # pause prebuild reconciliation to limit provisioner queue pollution: coder prebuilds pause # cancel pending provisioner jobs to clear the queue coder provisioner jobs list --initiator="prebuilds" --status="pending" | jq ... | xargs -n1 -I{} coder provisioner jobs cancel {} # push a fixed template and wait for the import to complete coder templates push ... # push a fixed template # resume prebuild reconciliation coder prebuilds resume ``` This interface differs somewhat from what was specified in the issue, but still provides a mechanism that addresses the issue. The original proposal was made by myself and this simpler implementation makes sense. I might add a `--search` parameter in a follow-up if there is appetite for it. Potential follow ups: * Support for this usage: `coder provisioner jobs list --search "initiator:prebuilds status:pending"` * Adding the same parameters to `coder provisioner jobs cancel` as a convenience feature so that operators don't have to pipe through `jq` and `xargs` --- cli/provisionerjobs.go | 27 ++- cli/provisionerjobs_test.go | 192 +++++++++++++++--- cli/testdata/coder_list_--output_json.golden | 1 + .../coder_provisioner_jobs_list_--help.golden | 5 +- ...provisioner_jobs_list_--output_json.golden | 2 + coderd/apidoc/docs.go | 11 + coderd/apidoc/swagger.json | 11 + coderd/database/dbauthz/dbauthz_test.go | 2 + coderd/database/queries.sql.go | 5 +- coderd/database/queries/provisionerjobs.sql | 1 + coderd/provisionerjobs.go | 4 + coderd/provisionerjobs_test.go | 102 ++++++++++ codersdk/organizations.go | 12 +- codersdk/provisionerdaemons.go | 1 + docs/reference/api/builds.md | 6 + docs/reference/api/organizations.md | 4 + docs/reference/api/schemas.md | 6 + docs/reference/api/templates.md | 11 + docs/reference/api/workspaces.md | 6 + docs/reference/cli/provisioner_jobs_list.md | 17 +- .../coder_provisioner_jobs_list_--help.golden | 5 +- site/src/api/typesGenerated.ts | 2 + site/src/testHelpers/entities.ts | 1 + 23 files changed, 395 insertions(+), 39 deletions(-) diff --git a/cli/provisionerjobs.go b/cli/provisionerjobs.go index 3ce7da20b7dcb..3f441a1758045 100644 --- a/cli/provisionerjobs.go +++ b/cli/provisionerjobs.go @@ -43,8 +43,9 @@ func (r *RootCmd) provisionerJobsList() *serpent.Command { cliui.TableFormat([]provisionerJobRow{}, []string{"created at", "id", "type", "template display name", "status", "queue", "tags"}), cliui.JSONFormat(), ) - status []string - limit int64 + status []string + limit int64 + initiator string ) cmd := &serpent.Command{ @@ -65,9 +66,20 @@ func (r *RootCmd) provisionerJobsList() *serpent.Command { return xerrors.Errorf("current organization: %w", err) } + var initiatorID *uuid.UUID + + if initiator != "" { + user, err := client.User(ctx, initiator) + if err != nil { + return xerrors.Errorf("initiator not found: %s", initiator) + } + initiatorID = &user.ID + } + jobs, err := client.OrganizationProvisionerJobs(ctx, org.ID, &codersdk.OrganizationProvisionerJobsOptions{ - Status: slice.StringEnums[codersdk.ProvisionerJobStatus](status), - Limit: int(limit), + Status: slice.StringEnums[codersdk.ProvisionerJobStatus](status), + Limit: int(limit), + InitiatorID: initiatorID, }) if err != nil { return xerrors.Errorf("list provisioner jobs: %w", err) @@ -122,6 +134,13 @@ func (r *RootCmd) provisionerJobsList() *serpent.Command { Default: "50", Value: serpent.Int64Of(&limit), }, + { + Flag: "initiator", + FlagShorthand: "i", + Env: "CODER_PROVISIONER_JOB_LIST_INITIATOR", + Description: "Filter by initiator (user ID or username).", + Value: serpent.StringOf(&initiator), + }, }...) orgContext.AttachOptions(cmd) diff --git a/cli/provisionerjobs_test.go b/cli/provisionerjobs_test.go index 4db42e8e3c9e7..57072a6156738 100644 --- a/cli/provisionerjobs_test.go +++ b/cli/provisionerjobs_test.go @@ -5,6 +5,7 @@ import ( "database/sql" "encoding/json" "fmt" + "strings" "testing" "time" @@ -26,33 +27,32 @@ import ( func TestProvisionerJobs(t *testing.T) { t.Parallel() - db, ps := dbtestutil.NewDB(t) - client, _, coderdAPI := coderdtest.NewWithAPI(t, &coderdtest.Options{ - IncludeProvisionerDaemon: false, - Database: db, - Pubsub: ps, - }) - owner := coderdtest.CreateFirstUser(t, client) - templateAdminClient, templateAdmin := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, rbac.ScopedRoleOrgTemplateAdmin(owner.OrganizationID)) - memberClient, member := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID) - - // These CLI tests are related to provisioner job CRUD operations and as such - // do not require the overhead of starting a provisioner. Other provisioner job - // functionalities (acquisition etc.) are tested elsewhere. - template := dbgen.Template(t, db, database.Template{ - OrganizationID: owner.OrganizationID, - CreatedBy: owner.UserID, - AllowUserCancelWorkspaceJobs: true, - }) - version := dbgen.TemplateVersion(t, db, database.TemplateVersion{ - OrganizationID: owner.OrganizationID, - CreatedBy: owner.UserID, - TemplateID: uuid.NullUUID{UUID: template.ID, Valid: true}, - }) - t.Run("Cancel", func(t *testing.T) { t.Parallel() + db, ps := dbtestutil.NewDB(t) + client, _, coderdAPI := coderdtest.NewWithAPI(t, &coderdtest.Options{ + IncludeProvisionerDaemon: false, + Database: db, + Pubsub: ps, + }) + owner := coderdtest.CreateFirstUser(t, client) + templateAdminClient, templateAdmin := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, rbac.ScopedRoleOrgTemplateAdmin(owner.OrganizationID)) + memberClient, member := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID) + + // These CLI tests are related to provisioner job CRUD operations and as such + // do not require the overhead of starting a provisioner. Other provisioner job + // functionalities (acquisition etc.) are tested elsewhere. + template := dbgen.Template(t, db, database.Template{ + OrganizationID: owner.OrganizationID, + CreatedBy: owner.UserID, + AllowUserCancelWorkspaceJobs: true, + }) + version := dbgen.TemplateVersion(t, db, database.TemplateVersion{ + OrganizationID: owner.OrganizationID, + CreatedBy: owner.UserID, + TemplateID: uuid.NullUUID{UUID: template.ID, Valid: true}, + }) // Test helper to create a provisioner job of a given type with a given input. prepareJob := func(t *testing.T, jobType database.ProvisionerJobType, input json.RawMessage) database.ProvisionerJob { t.Helper() @@ -178,4 +178,148 @@ func TestProvisionerJobs(t *testing.T) { }) } }) + + t.Run("List", func(t *testing.T) { + t.Parallel() + + db, ps := dbtestutil.NewDB(t) + client, _, coderdAPI := coderdtest.NewWithAPI(t, &coderdtest.Options{ + IncludeProvisionerDaemon: false, + Database: db, + Pubsub: ps, + }) + owner := coderdtest.CreateFirstUser(t, client) + _, member := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID) + + // These CLI tests are related to provisioner job CRUD operations and as such + // do not require the overhead of starting a provisioner. Other provisioner job + // functionalities (acquisition etc.) are tested elsewhere. + template := dbgen.Template(t, db, database.Template{ + OrganizationID: owner.OrganizationID, + CreatedBy: owner.UserID, + AllowUserCancelWorkspaceJobs: true, + }) + version := dbgen.TemplateVersion(t, db, database.TemplateVersion{ + OrganizationID: owner.OrganizationID, + CreatedBy: owner.UserID, + TemplateID: uuid.NullUUID{UUID: template.ID, Valid: true}, + }) + // Create some test jobs + job1 := dbgen.ProvisionerJob(t, db, coderdAPI.Pubsub, database.ProvisionerJob{ + OrganizationID: owner.OrganizationID, + InitiatorID: owner.UserID, + Type: database.ProvisionerJobTypeTemplateVersionImport, + Input: []byte(`{"template_version_id":"` + version.ID.String() + `"}`), + Tags: database.StringMap{provisionersdk.TagScope: provisionersdk.ScopeOrganization}, + }) + + job2 := dbgen.ProvisionerJob(t, db, coderdAPI.Pubsub, database.ProvisionerJob{ + OrganizationID: owner.OrganizationID, + InitiatorID: member.ID, + Type: database.ProvisionerJobTypeWorkspaceBuild, + Input: []byte(`{"workspace_build_id":"` + uuid.New().String() + `"}`), + Tags: database.StringMap{provisionersdk.TagScope: provisionersdk.ScopeOrganization}, + }) + // Test basic list command + t.Run("Basic", func(t *testing.T) { + t.Parallel() + + inv, root := clitest.New(t, "provisioner", "jobs", "list") + clitest.SetupConfig(t, client, root) + var buf bytes.Buffer + inv.Stdout = &buf + err := inv.Run() + require.NoError(t, err) + + // Should contain both jobs + output := buf.String() + assert.Contains(t, output, job1.ID.String()) + assert.Contains(t, output, job2.ID.String()) + }) + + // Test list with JSON output + t.Run("JSON", func(t *testing.T) { + t.Parallel() + + inv, root := clitest.New(t, "provisioner", "jobs", "list", "--output", "json") + clitest.SetupConfig(t, client, root) + var buf bytes.Buffer + inv.Stdout = &buf + err := inv.Run() + require.NoError(t, err) + + // Parse JSON output + var jobs []codersdk.ProvisionerJob + err = json.Unmarshal(buf.Bytes(), &jobs) + require.NoError(t, err) + + // Should contain both jobs + jobIDs := make([]uuid.UUID, len(jobs)) + for i, job := range jobs { + jobIDs[i] = job.ID + } + assert.Contains(t, jobIDs, job1.ID) + assert.Contains(t, jobIDs, job2.ID) + }) + + // Test list with limit + t.Run("Limit", func(t *testing.T) { + t.Parallel() + + inv, root := clitest.New(t, "provisioner", "jobs", "list", "--limit", "1") + clitest.SetupConfig(t, client, root) + var buf bytes.Buffer + inv.Stdout = &buf + err := inv.Run() + require.NoError(t, err) + + // Should contain at most 1 job + output := buf.String() + jobCount := 0 + if strings.Contains(output, job1.ID.String()) { + jobCount++ + } + if strings.Contains(output, job2.ID.String()) { + jobCount++ + } + assert.LessOrEqual(t, jobCount, 1) + }) + + // Test list with initiator filter + t.Run("InitiatorFilter", func(t *testing.T) { + t.Parallel() + + // Get owner user details to access username + ctx := testutil.Context(t, testutil.WaitShort) + ownerUser, err := client.User(ctx, owner.UserID.String()) + require.NoError(t, err) + + // Test filtering by initiator (using username) + inv, root := clitest.New(t, "provisioner", "jobs", "list", "--initiator", ownerUser.Username) + clitest.SetupConfig(t, client, root) + var buf bytes.Buffer + inv.Stdout = &buf + err = inv.Run() + require.NoError(t, err) + + // Should only contain job1 (initiated by owner) + output := buf.String() + assert.Contains(t, output, job1.ID.String()) + assert.NotContains(t, output, job2.ID.String()) + }) + + // Test list with invalid user + t.Run("InvalidUser", func(t *testing.T) { + t.Parallel() + + // Test with non-existent user + inv, root := clitest.New(t, "provisioner", "jobs", "list", "--initiator", "nonexistent-user") + clitest.SetupConfig(t, client, root) + var buf bytes.Buffer + inv.Stdout = &buf + err := inv.Run() + require.Error(t, err) + assert.Contains(t, err.Error(), "initiator not found: nonexistent-user") + }) + }) } diff --git a/cli/testdata/coder_list_--output_json.golden b/cli/testdata/coder_list_--output_json.golden index 82b73f7b24989..66afcf563dfbd 100644 --- a/cli/testdata/coder_list_--output_json.golden +++ b/cli/testdata/coder_list_--output_json.golden @@ -45,6 +45,7 @@ "queue_position": 0, "queue_size": 0, "organization_id": "===========[first org ID]===========", + "initiator_id": "==========[first user ID]===========", "input": { "workspace_build_id": "========[workspace build ID]========" }, diff --git a/cli/testdata/coder_provisioner_jobs_list_--help.golden b/cli/testdata/coder_provisioner_jobs_list_--help.golden index 8e22f78e978f2..3a581bd880829 100644 --- a/cli/testdata/coder_provisioner_jobs_list_--help.golden +++ b/cli/testdata/coder_provisioner_jobs_list_--help.golden @@ -11,9 +11,12 @@ OPTIONS: -O, --org string, $CODER_ORGANIZATION Select which organization (uuid or name) to use. - -c, --column [id|created at|started at|completed at|canceled at|error|error code|status|worker id|worker name|file id|tags|queue position|queue size|organization id|template version id|workspace build id|type|available workers|template version name|template id|template name|template display name|template icon|workspace id|workspace name|logs overflowed|organization|queue] (default: created at,id,type,template display name,status,queue,tags) + -c, --column [id|created at|started at|completed at|canceled at|error|error code|status|worker id|worker name|file id|tags|queue position|queue size|organization id|initiator id|template version id|workspace build id|type|available workers|template version name|template id|template name|template display name|template icon|workspace id|workspace name|logs overflowed|organization|queue] (default: created at,id,type,template display name,status,queue,tags) Columns to display in table output. + -i, --initiator string, $CODER_PROVISIONER_JOB_LIST_INITIATOR + Filter by initiator (user ID or username). + -l, --limit int, $CODER_PROVISIONER_JOB_LIST_LIMIT (default: 50) Limit the number of jobs returned. diff --git a/cli/testdata/coder_provisioner_jobs_list_--output_json.golden b/cli/testdata/coder_provisioner_jobs_list_--output_json.golden index 6ccf672360a55..3ee6c25e34082 100644 --- a/cli/testdata/coder_provisioner_jobs_list_--output_json.golden +++ b/cli/testdata/coder_provisioner_jobs_list_--output_json.golden @@ -15,6 +15,7 @@ "queue_position": 0, "queue_size": 0, "organization_id": "===========[first org ID]===========", + "initiator_id": "==========[first user ID]===========", "input": { "template_version_id": "============[version ID]============" }, @@ -45,6 +46,7 @@ "queue_position": 0, "queue_size": 0, "organization_id": "===========[first org ID]===========", + "initiator_id": "==========[first user ID]===========", "input": { "workspace_build_id": "========[workspace build ID]========" }, diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index b93c647dad689..289e4c9a3fee9 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -3744,6 +3744,13 @@ const docTemplate = `{ "description": "Provisioner tags to filter by (JSON of the form {'tag1':'value1','tag2':'value2'})", "name": "tags", "in": "query" + }, + { + "type": "string", + "format": "uuid", + "description": "Filter results by initiator", + "name": "initiator", + "in": "query" } ], "responses": { @@ -15974,6 +15981,10 @@ const docTemplate = `{ "type": "string", "format": "uuid" }, + "initiator_id": { + "type": "string", + "format": "uuid" + }, "input": { "$ref": "#/definitions/codersdk.ProvisionerJobInput" }, diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 731e1720c09bc..4b3d4c86aaf34 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -3299,6 +3299,13 @@ "description": "Provisioner tags to filter by (JSON of the form {'tag1':'value1','tag2':'value2'})", "name": "tags", "in": "query" + }, + { + "type": "string", + "format": "uuid", + "description": "Filter results by initiator", + "name": "initiator", + "in": "query" } ], "responses": { @@ -14532,6 +14539,10 @@ "type": "string", "format": "uuid" }, + "initiator_id": { + "type": "string", + "format": "uuid" + }, "input": { "$ref": "#/definitions/codersdk.ProvisionerJobInput" }, diff --git a/coderd/database/dbauthz/dbauthz_test.go b/coderd/database/dbauthz/dbauthz_test.go index 730d5f3198478..39350ad948ff0 100644 --- a/coderd/database/dbauthz/dbauthz_test.go +++ b/coderd/database/dbauthz/dbauthz_test.go @@ -2484,10 +2484,12 @@ func (s *MethodTestSuite) TestExtraMethods() { ds, err := db.GetProvisionerJobsByOrganizationAndStatusWithQueuePositionAndProvisioner(context.Background(), database.GetProvisionerJobsByOrganizationAndStatusWithQueuePositionAndProvisionerParams{ OrganizationID: org.ID, + InitiatorID: uuid.Nil, }) s.NoError(err, "get provisioner jobs by org") check.Args(database.GetProvisionerJobsByOrganizationAndStatusWithQueuePositionAndProvisionerParams{ OrganizationID: org.ID, + InitiatorID: uuid.Nil, }).Asserts(j1, policy.ActionRead, j2, policy.ActionRead).Returns(ds) })) } diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 6bf1a3e25d8a0..3612375b8b26c 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -9834,6 +9834,7 @@ WHERE AND (COALESCE(array_length($2::uuid[], 1), 0) = 0 OR pj.id = ANY($2::uuid[])) AND (COALESCE(array_length($3::provisioner_job_status[], 1), 0) = 0 OR pj.job_status = ANY($3::provisioner_job_status[])) AND ($4::tagset = 'null'::tagset OR provisioner_tagset_contains(pj.tags::tagset, $4::tagset)) + AND ($5::uuid = '00000000-0000-0000-0000-000000000000'::uuid OR pj.initiator_id = $5::uuid) GROUP BY pj.id, qp.queue_position, @@ -9849,7 +9850,7 @@ GROUP BY ORDER BY pj.created_at DESC LIMIT - $5::int + $6::int ` type GetProvisionerJobsByOrganizationAndStatusWithQueuePositionAndProvisionerParams struct { @@ -9857,6 +9858,7 @@ type GetProvisionerJobsByOrganizationAndStatusWithQueuePositionAndProvisionerPar IDs []uuid.UUID `db:"ids" json:"ids"` Status []ProvisionerJobStatus `db:"status" json:"status"` Tags StringMap `db:"tags" json:"tags"` + InitiatorID uuid.UUID `db:"initiator_id" json:"initiator_id"` Limit sql.NullInt32 `db:"limit" json:"limit"` } @@ -9881,6 +9883,7 @@ func (q *sqlQuerier) GetProvisionerJobsByOrganizationAndStatusWithQueuePositionA pq.Array(arg.IDs), pq.Array(arg.Status), arg.Tags, + arg.InitiatorID, arg.Limit, ) if err != nil { diff --git a/coderd/database/queries/provisionerjobs.sql b/coderd/database/queries/provisionerjobs.sql index dfc95a0bb4570..02d67d628a861 100644 --- a/coderd/database/queries/provisionerjobs.sql +++ b/coderd/database/queries/provisionerjobs.sql @@ -224,6 +224,7 @@ WHERE AND (COALESCE(array_length(@ids::uuid[], 1), 0) = 0 OR pj.id = ANY(@ids::uuid[])) AND (COALESCE(array_length(@status::provisioner_job_status[], 1), 0) = 0 OR pj.job_status = ANY(@status::provisioner_job_status[])) AND (@tags::tagset = 'null'::tagset OR provisioner_tagset_contains(pj.tags::tagset, @tags::tagset)) + AND (@initiator_id::uuid = '00000000-0000-0000-0000-000000000000'::uuid OR pj.initiator_id = @initiator_id::uuid) GROUP BY pj.id, qp.queue_position, diff --git a/coderd/provisionerjobs.go b/coderd/provisionerjobs.go index e9ab5260988d4..4ba923dae2477 100644 --- a/coderd/provisionerjobs.go +++ b/coderd/provisionerjobs.go @@ -76,6 +76,7 @@ func (api *API) provisionerJob(rw http.ResponseWriter, r *http.Request) { // @Param ids query []string false "Filter results by job IDs" format(uuid) // @Param status query codersdk.ProvisionerJobStatus false "Filter results by status" enums(pending,running,succeeded,canceling,canceled,failed) // @Param tags query object false "Provisioner tags to filter by (JSON of the form {'tag1':'value1','tag2':'value2'})" +// @Param initiator query string false "Filter results by initiator" format(uuid) // @Success 200 {array} codersdk.ProvisionerJob // @Router /organizations/{organization}/provisionerjobs [get] func (api *API) provisionerJobs(rw http.ResponseWriter, r *http.Request) { @@ -110,6 +111,7 @@ func (api *API) handleAuthAndFetchProvisionerJobs(rw http.ResponseWriter, r *htt ids = p.UUIDs(qp, nil, "ids") } tags := p.JSONStringMap(qp, database.StringMap{}, "tags") + initiatorID := p.UUID(qp, uuid.Nil, "initiator_id") p.ErrorExcessParams(qp) if len(p.Errors) > 0 { httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ @@ -125,6 +127,7 @@ func (api *API) handleAuthAndFetchProvisionerJobs(rw http.ResponseWriter, r *htt Limit: sql.NullInt32{Int32: limit, Valid: limit > 0}, IDs: ids, Tags: tags, + InitiatorID: initiatorID, }) if err != nil { if httpapi.Is404Error(err) { @@ -355,6 +358,7 @@ func convertProvisionerJob(pj database.GetProvisionerJobsByIDsWithQueuePositionR job := codersdk.ProvisionerJob{ ID: provisionerJob.ID, OrganizationID: provisionerJob.OrganizationID, + InitiatorID: provisionerJob.InitiatorID, CreatedAt: provisionerJob.CreatedAt, Type: codersdk.ProvisionerJobType(provisionerJob.Type), Error: provisionerJob.Error.String, diff --git a/coderd/provisionerjobs_test.go b/coderd/provisionerjobs_test.go index 98da3ae5584e6..829c72aa4dbd0 100644 --- a/coderd/provisionerjobs_test.go +++ b/coderd/provisionerjobs_test.go @@ -58,6 +58,8 @@ func TestProvisionerJobs(t *testing.T) { StartedAt: sql.NullTime{Time: dbtime.Now(), Valid: true}, Type: database.ProvisionerJobTypeWorkspaceBuild, Input: json.RawMessage(`{"workspace_build_id":"` + wbID.String() + `"}`), + InitiatorID: member.ID, + Tags: database.StringMap{"initiatorTest": "true"}, }) dbgen.WorkspaceBuild(t, db, database.WorkspaceBuild{ ID: wbID, @@ -71,6 +73,7 @@ func TestProvisionerJobs(t *testing.T) { dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{ OrganizationID: owner.OrganizationID, Tags: database.StringMap{"count": strconv.Itoa(i)}, + InitiatorID: owner.UserID, }) } @@ -165,6 +168,94 @@ func TestProvisionerJobs(t *testing.T) { require.Len(t, jobs, 1) }) + t.Run("Initiator", func(t *testing.T) { + t.Parallel() + ctx := testutil.Context(t, testutil.WaitMedium) + + jobs, err := templateAdminClient.OrganizationProvisionerJobs(ctx, owner.OrganizationID, &codersdk.OrganizationProvisionerJobsOptions{ + InitiatorID: &member.ID, + }) + require.NoError(t, err) + require.GreaterOrEqual(t, len(jobs), 1) + require.Equal(t, member.ID, jobs[0].InitiatorID) + }) + + t.Run("InitiatorWithOtherFilters", func(t *testing.T) { + t.Parallel() + ctx := testutil.Context(t, testutil.WaitMedium) + + // Test filtering by initiator ID combined with status filter + jobs, err := templateAdminClient.OrganizationProvisionerJobs(ctx, owner.OrganizationID, &codersdk.OrganizationProvisionerJobsOptions{ + InitiatorID: &owner.UserID, + Status: []codersdk.ProvisionerJobStatus{codersdk.ProvisionerJobSucceeded}, + }) + require.NoError(t, err) + + // Verify all returned jobs have the correct initiator and status + for _, job := range jobs { + require.Equal(t, owner.UserID, job.InitiatorID) + require.Equal(t, codersdk.ProvisionerJobSucceeded, job.Status) + } + }) + + t.Run("InitiatorWithLimit", func(t *testing.T) { + t.Parallel() + ctx := testutil.Context(t, testutil.WaitMedium) + + // Test filtering by initiator ID with limit + jobs, err := templateAdminClient.OrganizationProvisionerJobs(ctx, owner.OrganizationID, &codersdk.OrganizationProvisionerJobsOptions{ + InitiatorID: &owner.UserID, + Limit: 1, + }) + require.NoError(t, err) + require.Len(t, jobs, 1) + + // Verify the returned job has the correct initiator + require.Equal(t, owner.UserID, jobs[0].InitiatorID) + }) + + t.Run("InitiatorWithTags", func(t *testing.T) { + t.Parallel() + ctx := testutil.Context(t, testutil.WaitMedium) + + // Test filtering by initiator ID combined with tags + jobs, err := templateAdminClient.OrganizationProvisionerJobs(ctx, owner.OrganizationID, &codersdk.OrganizationProvisionerJobsOptions{ + InitiatorID: &member.ID, + Tags: map[string]string{"initiatorTest": "true"}, + }) + require.NoError(t, err) + require.Len(t, jobs, 1) + + // Verify the returned job has the correct initiator and tags + require.Equal(t, member.ID, jobs[0].InitiatorID) + require.Equal(t, "true", jobs[0].Tags["initiatorTest"]) + }) + + t.Run("InitiatorNotFound", func(t *testing.T) { + t.Parallel() + ctx := testutil.Context(t, testutil.WaitMedium) + + // Test with non-existent initiator ID + nonExistentID := uuid.New() + jobs, err := templateAdminClient.OrganizationProvisionerJobs(ctx, owner.OrganizationID, &codersdk.OrganizationProvisionerJobsOptions{ + InitiatorID: &nonExistentID, + }) + require.NoError(t, err) + require.Len(t, jobs, 0) + }) + + t.Run("InitiatorNil", func(t *testing.T) { + t.Parallel() + ctx := testutil.Context(t, testutil.WaitMedium) + + // Test with nil initiator ID (should return all jobs) + jobs, err := templateAdminClient.OrganizationProvisionerJobs(ctx, owner.OrganizationID, &codersdk.OrganizationProvisionerJobsOptions{ + InitiatorID: nil, + }) + require.NoError(t, err) + require.GreaterOrEqual(t, len(jobs), 50) // Should return all jobs (up to default limit) + }) + t.Run("Limit", func(t *testing.T) { t.Parallel() ctx := testutil.Context(t, testutil.WaitMedium) @@ -185,6 +276,17 @@ func TestProvisionerJobs(t *testing.T) { require.Error(t, err) require.Len(t, jobs, 0) }) + + t.Run("MemberDeniedWithInitiator", func(t *testing.T) { + t.Parallel() + ctx := testutil.Context(t, testutil.WaitMedium) + // Member should not be able to access jobs even with initiator filter + jobs, err := memberClient.OrganizationProvisionerJobs(ctx, owner.OrganizationID, &codersdk.OrganizationProvisionerJobsOptions{ + InitiatorID: &member.ID, + }) + require.Error(t, err) + require.Len(t, jobs, 0) + }) }) // Ensures that when a provisioner job is in the succeeded state, diff --git a/codersdk/organizations.go b/codersdk/organizations.go index bca87c7bd4591..291bb9ac1baf7 100644 --- a/codersdk/organizations.go +++ b/codersdk/organizations.go @@ -397,10 +397,11 @@ func (c *Client) OrganizationProvisionerDaemons(ctx context.Context, organizatio } type OrganizationProvisionerJobsOptions struct { - Limit int - IDs []uuid.UUID - Status []ProvisionerJobStatus - Tags map[string]string + Limit int + IDs []uuid.UUID + Status []ProvisionerJobStatus + Tags map[string]string + InitiatorID *uuid.UUID } func (c *Client) OrganizationProvisionerJobs(ctx context.Context, organizationID uuid.UUID, opts *OrganizationProvisionerJobsOptions) ([]ProvisionerJob, error) { @@ -422,6 +423,9 @@ func (c *Client) OrganizationProvisionerJobs(ctx context.Context, organizationID } qp.Add("tags", string(tagsRaw)) } + if opts.InitiatorID != nil { + qp.Add("initiator_id", opts.InitiatorID.String()) + } } res, err := c.Request(ctx, http.MethodGet, diff --git a/codersdk/provisionerdaemons.go b/codersdk/provisionerdaemons.go index b3cefa09110ec..19f8cae546118 100644 --- a/codersdk/provisionerdaemons.go +++ b/codersdk/provisionerdaemons.go @@ -198,6 +198,7 @@ type ProvisionerJob struct { QueuePosition int `json:"queue_position" table:"queue position"` QueueSize int `json:"queue_size" table:"queue size"` OrganizationID uuid.UUID `json:"organization_id" format:"uuid" table:"organization id"` + InitiatorID uuid.UUID `json:"initiator_id" format:"uuid" table:"initiator id"` Input ProvisionerJobInput `json:"input" table:"input,recursive_inline"` Type ProvisionerJobType `json:"type" table:"type"` AvailableWorkers []uuid.UUID `json:"available_workers,omitempty" format:"uuid" table:"available workers"` diff --git a/docs/reference/api/builds.md b/docs/reference/api/builds.md index 232509052b7b0..41df0b9efaf30 100644 --- a/docs/reference/api/builds.md +++ b/docs/reference/api/builds.md @@ -48,6 +48,7 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -288,6 +289,7 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild} \ "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -1019,6 +1021,7 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/sta "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -1332,6 +1335,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace}/builds \ "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -1551,6 +1555,7 @@ Status Code **200** | `»» error_code` | [codersdk.JobErrorCode](schemas.md#codersdkjoberrorcode) | false | | | | `»» file_id` | string(uuid) | false | | | | `»» id` | string(uuid) | false | | | +| `»» initiator_id` | string(uuid) | false | | | | `»» input` | [codersdk.ProvisionerJobInput](schemas.md#codersdkprovisionerjobinput) | false | | | | `»»» error` | string | false | | | | `»»» template_version_id` | string(uuid) | false | | | @@ -1829,6 +1834,7 @@ curl -X POST http://coder-server:8080/api/v2/workspaces/{workspace}/builds \ "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", diff --git a/docs/reference/api/organizations.md b/docs/reference/api/organizations.md index d418a1fcba106..ffd6f78405fb1 100644 --- a/docs/reference/api/organizations.md +++ b/docs/reference/api/organizations.md @@ -366,6 +366,7 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/provisi | `ids` | query | array(uuid) | false | Filter results by job IDs | | `status` | query | string | false | Filter results by status | | `tags` | query | object | false | Provisioner tags to filter by (JSON of the form {'tag1':'value1','tag2':'value2'}) | +| `initiator` | query | string(uuid) | false | Filter results by initiator | #### Enumerated Values @@ -402,6 +403,7 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/provisi "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -454,6 +456,7 @@ Status Code **200** | `» error_code` | [codersdk.JobErrorCode](schemas.md#codersdkjoberrorcode) | false | | | | `» file_id` | string(uuid) | false | | | | `» id` | string(uuid) | false | | | +| `» initiator_id` | string(uuid) | false | | | | `» input` | [codersdk.ProvisionerJobInput](schemas.md#codersdkprovisionerjobinput) | false | | | | `»» error` | string | false | | | | `»» template_version_id` | string(uuid) | false | | | @@ -531,6 +534,7 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/provisi "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", diff --git a/docs/reference/api/schemas.md b/docs/reference/api/schemas.md index 98324941a1c70..33cb280ae15c0 100644 --- a/docs/reference/api/schemas.md +++ b/docs/reference/api/schemas.md @@ -6390,6 +6390,7 @@ Only certain features set these fields: - FeatureManagedAgentLimit| "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -6432,6 +6433,7 @@ Only certain features set these fields: - FeatureManagedAgentLimit| | `error_code` | [codersdk.JobErrorCode](#codersdkjoberrorcode) | false | | | | `file_id` | string | false | | | | `id` | string | false | | | +| `initiator_id` | string | false | | | | `input` | [codersdk.ProvisionerJobInput](#codersdkprovisionerjobinput) | false | | | | `logs_overflowed` | boolean | false | | | | `metadata` | [codersdk.ProvisionerJobMetadata](#codersdkprovisionerjobmetadata) | false | | | @@ -8118,6 +8120,7 @@ Restarts will only happen on weekdays in this list on weeks which line up with W "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -9386,6 +9389,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -10553,6 +10557,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -11389,6 +11394,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", diff --git a/docs/reference/api/templates.md b/docs/reference/api/templates.md index efc59cf7b5743..9d69949ac7cf9 100644 --- a/docs/reference/api/templates.md +++ b/docs/reference/api/templates.md @@ -475,6 +475,7 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -575,6 +576,7 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -699,6 +701,7 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/templa "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -1306,6 +1309,7 @@ curl -X GET http://coder-server:8080/api/v2/templates/{template}/versions \ "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -1382,6 +1386,7 @@ Status Code **200** | `»» error_code` | [codersdk.JobErrorCode](schemas.md#codersdkjoberrorcode) | false | | | | `»» file_id` | string(uuid) | false | | | | `»» id` | string(uuid) | false | | | +| `»» initiator_id` | string(uuid) | false | | | | `»» input` | [codersdk.ProvisionerJobInput](schemas.md#codersdkprovisionerjobinput) | false | | | | `»»» error` | string | false | | | | `»»» template_version_id` | string(uuid) | false | | | @@ -1589,6 +1594,7 @@ curl -X GET http://coder-server:8080/api/v2/templates/{template}/versions/{templ "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -1665,6 +1671,7 @@ Status Code **200** | `»» error_code` | [codersdk.JobErrorCode](schemas.md#codersdkjoberrorcode) | false | | | | `»» file_id` | string(uuid) | false | | | | `»» id` | string(uuid) | false | | | +| `»» initiator_id` | string(uuid) | false | | | | `»» input` | [codersdk.ProvisionerJobInput](schemas.md#codersdkprovisionerjobinput) | false | | | | `»»» error` | string | false | | | | `»»» template_version_id` | string(uuid) | false | | | @@ -1762,6 +1769,7 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion} \ "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -1871,6 +1879,7 @@ curl -X PATCH http://coder-server:8080/api/v2/templateversions/{templateversion} "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -2069,6 +2078,7 @@ curl -X POST http://coder-server:8080/api/v2/templateversions/{templateversion}/ "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -2143,6 +2153,7 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/d "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", diff --git a/docs/reference/api/workspaces.md b/docs/reference/api/workspaces.md index 455fefcb57749..e2a95613ed263 100644 --- a/docs/reference/api/workspaces.md +++ b/docs/reference/api/workspaces.md @@ -103,6 +103,7 @@ of the template will be used. "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -393,6 +394,7 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -708,6 +710,7 @@ of the template will be used. "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -1001,6 +1004,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces \ "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -1275,6 +1279,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace} \ "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -1824,6 +1829,7 @@ curl -X PUT http://coder-server:8080/api/v2/workspaces/{workspace}/dormant \ "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", diff --git a/docs/reference/cli/provisioner_jobs_list.md b/docs/reference/cli/provisioner_jobs_list.md index a0bff8554d610..0167dd467d60a 100644 --- a/docs/reference/cli/provisioner_jobs_list.md +++ b/docs/reference/cli/provisioner_jobs_list.md @@ -34,6 +34,15 @@ Filter by job status. Limit the number of jobs returned. +### -i, --initiator + +| | | +|-------------|----------------------------------------------------| +| Type | string | +| Environment | $CODER_PROVISIONER_JOB_LIST_INITIATOR | + +Filter by initiator (user ID or username). + ### -O, --org | | | @@ -45,10 +54,10 @@ Select which organization (uuid or name) to use. ### -c, --column -| | | -|---------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Type | [id\|created at\|started at\|completed at\|canceled at\|error\|error code\|status\|worker id\|worker name\|file id\|tags\|queue position\|queue size\|organization id\|template version id\|workspace build id\|type\|available workers\|template version name\|template id\|template name\|template display name\|template icon\|workspace id\|workspace name\|logs overflowed\|organization\|queue] | -| Default | created at,id,type,template display name,status,queue,tags | +| | | +|---------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Type | [id\|created at\|started at\|completed at\|canceled at\|error\|error code\|status\|worker id\|worker name\|file id\|tags\|queue position\|queue size\|organization id\|initiator id\|template version id\|workspace build id\|type\|available workers\|template version name\|template id\|template name\|template display name\|template icon\|workspace id\|workspace name\|logs overflowed\|organization\|queue] | +| Default | created at,id,type,template display name,status,queue,tags | Columns to display in table output. diff --git a/enterprise/cli/testdata/coder_provisioner_jobs_list_--help.golden b/enterprise/cli/testdata/coder_provisioner_jobs_list_--help.golden index 8e22f78e978f2..3a581bd880829 100644 --- a/enterprise/cli/testdata/coder_provisioner_jobs_list_--help.golden +++ b/enterprise/cli/testdata/coder_provisioner_jobs_list_--help.golden @@ -11,9 +11,12 @@ OPTIONS: -O, --org string, $CODER_ORGANIZATION Select which organization (uuid or name) to use. - -c, --column [id|created at|started at|completed at|canceled at|error|error code|status|worker id|worker name|file id|tags|queue position|queue size|organization id|template version id|workspace build id|type|available workers|template version name|template id|template name|template display name|template icon|workspace id|workspace name|logs overflowed|organization|queue] (default: created at,id,type,template display name,status,queue,tags) + -c, --column [id|created at|started at|completed at|canceled at|error|error code|status|worker id|worker name|file id|tags|queue position|queue size|organization id|initiator id|template version id|workspace build id|type|available workers|template version name|template id|template name|template display name|template icon|workspace id|workspace name|logs overflowed|organization|queue] (default: created at,id,type,template display name,status,queue,tags) Columns to display in table output. + -i, --initiator string, $CODER_PROVISIONER_JOB_LIST_INITIATOR + Filter by initiator (user ID or username). + -l, --limit int, $CODER_PROVISIONER_JOB_LIST_LIMIT (default: 50) Limit the number of jobs returned. diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 0519c9c136cec..e7c612078b6f3 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -2060,6 +2060,7 @@ export interface OrganizationProvisionerJobsOptions { readonly IDs: readonly string[]; readonly Status: readonly ProvisionerJobStatus[]; readonly Tags: Record; + readonly InitiatorID: string | null; } // From codersdk/idpsync.go @@ -2379,6 +2380,7 @@ export interface ProvisionerJob { readonly queue_position: number; readonly queue_size: number; readonly organization_id: string; + readonly initiator_id: string; readonly input: ProvisionerJobInput; readonly type: ProvisionerJobType; readonly available_workers?: readonly string[]; diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index 19c414bdd3158..0b8d87f8111ef 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -679,6 +679,7 @@ export const MockProvisionerJob: TypesGen.ProvisionerJob = { status: "succeeded", file_id: MockOrganization.id, completed_at: "2022-05-17T17:39:01.382927298Z", + initiator_id: MockUserMember.id, tags: { scope: "organization", owner: "", From b60ae0a0c44457c7c655faa3fd4b5f13d1d0af15 Mon Sep 17 00:00:00 2001 From: Thomas Kosiewski Date: Mon, 6 Oct 2025 12:08:17 +0200 Subject: [PATCH 057/298] refactor: add wildcard scope entries for API key scopes (#20032) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Add API Key Scope Wildcards This PR adds wildcard API key scopes (`resource:*`) for all RBAC resources to ensure every resource has a matching wildcard value. It also adds all individual `resource:action`​ scopes to the API documentation and TypeScript definitions. The changes include: - Adding a new database migration (000377) that adds wildcard API key scopes - Updating the API documentation to include all available scopes - Enhancing the scope generation scripts to include all resource wildcards - Updating the TypeScript definitions to match the expanded scope list These changes make creating API keys with comprehensive permissions for specific resource types easier. --- coderd/apidoc/docs.go | 300 +++++++++++++++++- coderd/apidoc/swagger.json | 300 +++++++++++++++++- coderd/database/dump.sql | 42 ++- ...00377_add_api_key_scope_wildcards.down.sql | 2 + .../000377_add_api_key_scope_wildcards.up.sql | 42 +++ coderd/database/models.go | 122 ++++++- codersdk/apikey_scopes_gen.go | 228 ++++++++++--- docs/reference/api/schemas.md | 234 +++++++++++--- scripts/apikeyscopesgen/main.go | 70 +++- scripts/check-scopes/main.go | 24 +- scripts/generate_api_key_scope_enum/main.go | 32 -- site/src/api/typesGenerated.ts | 296 +++++++++++++++++ 12 files changed, 1551 insertions(+), 141 deletions(-) create mode 100644 coderd/database/migrations/000377_add_api_key_scope_wildcards.down.sql create mode 100644 coderd/database/migrations/000377_add_api_key_scope_wildcards.up.sql delete mode 100644 scripts/generate_api_key_scope_enum/main.go diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 289e4c9a3fee9..b444d50a8c63b 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -11530,11 +11530,29 @@ const docTemplate = `{ "enum": [ "all", "application_connect", + "aibridge_interception:*", + "aibridge_interception:create", + "aibridge_interception:read", + "aibridge_interception:update", "api_key:*", "api_key:create", "api_key:delete", "api_key:read", "api_key:update", + "assign_org_role:*", + "assign_org_role:assign", + "assign_org_role:create", + "assign_org_role:delete", + "assign_org_role:read", + "assign_org_role:unassign", + "assign_org_role:update", + "assign_role:*", + "assign_role:assign", + "assign_role:read", + "assign_role:unassign", + "audit_log:*", + "audit_log:create", + "audit_log:read", "coder:all", "coder:apikeys.manage_self", "coder:application_connect", @@ -11544,40 +11562,188 @@ const docTemplate = `{ "coder:workspaces.create", "coder:workspaces.delete", "coder:workspaces.operate", + "connection_log:*", + "connection_log:read", + "connection_log:update", + "crypto_key:*", + "crypto_key:create", + "crypto_key:delete", + "crypto_key:read", + "crypto_key:update", + "debug_info:*", + "debug_info:read", + "deployment_config:*", + "deployment_config:read", + "deployment_config:update", + "deployment_stats:*", + "deployment_stats:read", "file:*", "file:create", "file:read", + "group:*", + "group:create", + "group:delete", + "group:read", + "group:update", + "group_member:*", + "group_member:read", + "idpsync_settings:*", + "idpsync_settings:read", + "idpsync_settings:update", + "inbox_notification:*", + "inbox_notification:create", + "inbox_notification:read", + "inbox_notification:update", + "license:*", + "license:create", + "license:delete", + "license:read", + "notification_message:*", + "notification_message:create", + "notification_message:delete", + "notification_message:read", + "notification_message:update", + "notification_preference:*", + "notification_preference:read", + "notification_preference:update", + "notification_template:*", + "notification_template:read", + "notification_template:update", + "oauth2_app:*", + "oauth2_app:create", + "oauth2_app:delete", + "oauth2_app:read", + "oauth2_app:update", + "oauth2_app_code_token:*", + "oauth2_app_code_token:create", + "oauth2_app_code_token:delete", + "oauth2_app_code_token:read", + "oauth2_app_secret:*", + "oauth2_app_secret:create", + "oauth2_app_secret:delete", + "oauth2_app_secret:read", + "oauth2_app_secret:update", + "organization:*", + "organization:create", + "organization:delete", + "organization:read", + "organization:update", + "organization_member:*", + "organization_member:create", + "organization_member:delete", + "organization_member:read", + "organization_member:update", + "prebuilt_workspace:*", + "prebuilt_workspace:delete", + "prebuilt_workspace:update", + "provisioner_daemon:*", + "provisioner_daemon:create", + "provisioner_daemon:delete", + "provisioner_daemon:read", + "provisioner_daemon:update", + "provisioner_jobs:*", + "provisioner_jobs:create", + "provisioner_jobs:read", + "provisioner_jobs:update", + "replicas:*", + "replicas:read", + "system:*", + "system:create", + "system:delete", + "system:read", + "system:update", + "tailnet_coordinator:*", + "tailnet_coordinator:create", + "tailnet_coordinator:delete", + "tailnet_coordinator:read", + "tailnet_coordinator:update", "template:*", "template:create", "template:delete", "template:read", "template:update", "template:use", + "template:view_insights", + "usage_event:*", + "usage_event:create", + "usage_event:read", + "usage_event:update", + "user:*", + "user:create", + "user:delete", + "user:read", "user:read_personal", + "user:update", "user:update_personal", "user_secret:*", "user_secret:create", "user_secret:delete", "user_secret:read", "user_secret:update", + "webpush_subscription:*", + "webpush_subscription:create", + "webpush_subscription:delete", + "webpush_subscription:read", "workspace:*", "workspace:application_connect", "workspace:create", + "workspace:create_agent", "workspace:delete", + "workspace:delete_agent", "workspace:read", "workspace:ssh", "workspace:start", "workspace:stop", - "workspace:update" + "workspace:update", + "workspace_agent_devcontainers:*", + "workspace_agent_devcontainers:create", + "workspace_agent_resource_monitor:*", + "workspace_agent_resource_monitor:create", + "workspace_agent_resource_monitor:read", + "workspace_agent_resource_monitor:update", + "workspace_dormant:*", + "workspace_dormant:application_connect", + "workspace_dormant:create", + "workspace_dormant:create_agent", + "workspace_dormant:delete", + "workspace_dormant:delete_agent", + "workspace_dormant:read", + "workspace_dormant:ssh", + "workspace_dormant:start", + "workspace_dormant:stop", + "workspace_dormant:update", + "workspace_proxy:*", + "workspace_proxy:create", + "workspace_proxy:delete", + "workspace_proxy:read", + "workspace_proxy:update" ], "x-enum-varnames": [ "APIKeyScopeAll", "APIKeyScopeApplicationConnect", + "APIKeyScopeAibridgeInterceptionAll", + "APIKeyScopeAibridgeInterceptionCreate", + "APIKeyScopeAibridgeInterceptionRead", + "APIKeyScopeAibridgeInterceptionUpdate", "APIKeyScopeApiKeyAll", "APIKeyScopeApiKeyCreate", "APIKeyScopeApiKeyDelete", "APIKeyScopeApiKeyRead", "APIKeyScopeApiKeyUpdate", + "APIKeyScopeAssignOrgRoleAll", + "APIKeyScopeAssignOrgRoleAssign", + "APIKeyScopeAssignOrgRoleCreate", + "APIKeyScopeAssignOrgRoleDelete", + "APIKeyScopeAssignOrgRoleRead", + "APIKeyScopeAssignOrgRoleUnassign", + "APIKeyScopeAssignOrgRoleUpdate", + "APIKeyScopeAssignRoleAll", + "APIKeyScopeAssignRoleAssign", + "APIKeyScopeAssignRoleRead", + "APIKeyScopeAssignRoleUnassign", + "APIKeyScopeAuditLogAll", + "APIKeyScopeAuditLogCreate", + "APIKeyScopeAuditLogRead", "APIKeyScopeCoderAll", "APIKeyScopeCoderApikeysManageSelf", "APIKeyScopeCoderApplicationConnect", @@ -11587,31 +11753,161 @@ const docTemplate = `{ "APIKeyScopeCoderWorkspacesCreate", "APIKeyScopeCoderWorkspacesDelete", "APIKeyScopeCoderWorkspacesOperate", + "APIKeyScopeConnectionLogAll", + "APIKeyScopeConnectionLogRead", + "APIKeyScopeConnectionLogUpdate", + "APIKeyScopeCryptoKeyAll", + "APIKeyScopeCryptoKeyCreate", + "APIKeyScopeCryptoKeyDelete", + "APIKeyScopeCryptoKeyRead", + "APIKeyScopeCryptoKeyUpdate", + "APIKeyScopeDebugInfoAll", + "APIKeyScopeDebugInfoRead", + "APIKeyScopeDeploymentConfigAll", + "APIKeyScopeDeploymentConfigRead", + "APIKeyScopeDeploymentConfigUpdate", + "APIKeyScopeDeploymentStatsAll", + "APIKeyScopeDeploymentStatsRead", "APIKeyScopeFileAll", "APIKeyScopeFileCreate", "APIKeyScopeFileRead", + "APIKeyScopeGroupAll", + "APIKeyScopeGroupCreate", + "APIKeyScopeGroupDelete", + "APIKeyScopeGroupRead", + "APIKeyScopeGroupUpdate", + "APIKeyScopeGroupMemberAll", + "APIKeyScopeGroupMemberRead", + "APIKeyScopeIdpsyncSettingsAll", + "APIKeyScopeIdpsyncSettingsRead", + "APIKeyScopeIdpsyncSettingsUpdate", + "APIKeyScopeInboxNotificationAll", + "APIKeyScopeInboxNotificationCreate", + "APIKeyScopeInboxNotificationRead", + "APIKeyScopeInboxNotificationUpdate", + "APIKeyScopeLicenseAll", + "APIKeyScopeLicenseCreate", + "APIKeyScopeLicenseDelete", + "APIKeyScopeLicenseRead", + "APIKeyScopeNotificationMessageAll", + "APIKeyScopeNotificationMessageCreate", + "APIKeyScopeNotificationMessageDelete", + "APIKeyScopeNotificationMessageRead", + "APIKeyScopeNotificationMessageUpdate", + "APIKeyScopeNotificationPreferenceAll", + "APIKeyScopeNotificationPreferenceRead", + "APIKeyScopeNotificationPreferenceUpdate", + "APIKeyScopeNotificationTemplateAll", + "APIKeyScopeNotificationTemplateRead", + "APIKeyScopeNotificationTemplateUpdate", + "APIKeyScopeOauth2AppAll", + "APIKeyScopeOauth2AppCreate", + "APIKeyScopeOauth2AppDelete", + "APIKeyScopeOauth2AppRead", + "APIKeyScopeOauth2AppUpdate", + "APIKeyScopeOauth2AppCodeTokenAll", + "APIKeyScopeOauth2AppCodeTokenCreate", + "APIKeyScopeOauth2AppCodeTokenDelete", + "APIKeyScopeOauth2AppCodeTokenRead", + "APIKeyScopeOauth2AppSecretAll", + "APIKeyScopeOauth2AppSecretCreate", + "APIKeyScopeOauth2AppSecretDelete", + "APIKeyScopeOauth2AppSecretRead", + "APIKeyScopeOauth2AppSecretUpdate", + "APIKeyScopeOrganizationAll", + "APIKeyScopeOrganizationCreate", + "APIKeyScopeOrganizationDelete", + "APIKeyScopeOrganizationRead", + "APIKeyScopeOrganizationUpdate", + "APIKeyScopeOrganizationMemberAll", + "APIKeyScopeOrganizationMemberCreate", + "APIKeyScopeOrganizationMemberDelete", + "APIKeyScopeOrganizationMemberRead", + "APIKeyScopeOrganizationMemberUpdate", + "APIKeyScopePrebuiltWorkspaceAll", + "APIKeyScopePrebuiltWorkspaceDelete", + "APIKeyScopePrebuiltWorkspaceUpdate", + "APIKeyScopeProvisionerDaemonAll", + "APIKeyScopeProvisionerDaemonCreate", + "APIKeyScopeProvisionerDaemonDelete", + "APIKeyScopeProvisionerDaemonRead", + "APIKeyScopeProvisionerDaemonUpdate", + "APIKeyScopeProvisionerJobsAll", + "APIKeyScopeProvisionerJobsCreate", + "APIKeyScopeProvisionerJobsRead", + "APIKeyScopeProvisionerJobsUpdate", + "APIKeyScopeReplicasAll", + "APIKeyScopeReplicasRead", + "APIKeyScopeSystemAll", + "APIKeyScopeSystemCreate", + "APIKeyScopeSystemDelete", + "APIKeyScopeSystemRead", + "APIKeyScopeSystemUpdate", + "APIKeyScopeTailnetCoordinatorAll", + "APIKeyScopeTailnetCoordinatorCreate", + "APIKeyScopeTailnetCoordinatorDelete", + "APIKeyScopeTailnetCoordinatorRead", + "APIKeyScopeTailnetCoordinatorUpdate", "APIKeyScopeTemplateAll", "APIKeyScopeTemplateCreate", "APIKeyScopeTemplateDelete", "APIKeyScopeTemplateRead", "APIKeyScopeTemplateUpdate", "APIKeyScopeTemplateUse", + "APIKeyScopeTemplateViewInsights", + "APIKeyScopeUsageEventAll", + "APIKeyScopeUsageEventCreate", + "APIKeyScopeUsageEventRead", + "APIKeyScopeUsageEventUpdate", + "APIKeyScopeUserAll", + "APIKeyScopeUserCreate", + "APIKeyScopeUserDelete", + "APIKeyScopeUserRead", "APIKeyScopeUserReadPersonal", + "APIKeyScopeUserUpdate", "APIKeyScopeUserUpdatePersonal", "APIKeyScopeUserSecretAll", "APIKeyScopeUserSecretCreate", "APIKeyScopeUserSecretDelete", "APIKeyScopeUserSecretRead", "APIKeyScopeUserSecretUpdate", + "APIKeyScopeWebpushSubscriptionAll", + "APIKeyScopeWebpushSubscriptionCreate", + "APIKeyScopeWebpushSubscriptionDelete", + "APIKeyScopeWebpushSubscriptionRead", "APIKeyScopeWorkspaceAll", "APIKeyScopeWorkspaceApplicationConnect", "APIKeyScopeWorkspaceCreate", + "APIKeyScopeWorkspaceCreateAgent", "APIKeyScopeWorkspaceDelete", + "APIKeyScopeWorkspaceDeleteAgent", "APIKeyScopeWorkspaceRead", "APIKeyScopeWorkspaceSsh", "APIKeyScopeWorkspaceStart", "APIKeyScopeWorkspaceStop", - "APIKeyScopeWorkspaceUpdate" + "APIKeyScopeWorkspaceUpdate", + "APIKeyScopeWorkspaceAgentDevcontainersAll", + "APIKeyScopeWorkspaceAgentDevcontainersCreate", + "APIKeyScopeWorkspaceAgentResourceMonitorAll", + "APIKeyScopeWorkspaceAgentResourceMonitorCreate", + "APIKeyScopeWorkspaceAgentResourceMonitorRead", + "APIKeyScopeWorkspaceAgentResourceMonitorUpdate", + "APIKeyScopeWorkspaceDormantAll", + "APIKeyScopeWorkspaceDormantApplicationConnect", + "APIKeyScopeWorkspaceDormantCreate", + "APIKeyScopeWorkspaceDormantCreateAgent", + "APIKeyScopeWorkspaceDormantDelete", + "APIKeyScopeWorkspaceDormantDeleteAgent", + "APIKeyScopeWorkspaceDormantRead", + "APIKeyScopeWorkspaceDormantSsh", + "APIKeyScopeWorkspaceDormantStart", + "APIKeyScopeWorkspaceDormantStop", + "APIKeyScopeWorkspaceDormantUpdate", + "APIKeyScopeWorkspaceProxyAll", + "APIKeyScopeWorkspaceProxyCreate", + "APIKeyScopeWorkspaceProxyDelete", + "APIKeyScopeWorkspaceProxyRead", + "APIKeyScopeWorkspaceProxyUpdate" ] }, "codersdk.AddLicenseRequest": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 4b3d4c86aaf34..1a222932de75a 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -10246,11 +10246,29 @@ "enum": [ "all", "application_connect", + "aibridge_interception:*", + "aibridge_interception:create", + "aibridge_interception:read", + "aibridge_interception:update", "api_key:*", "api_key:create", "api_key:delete", "api_key:read", "api_key:update", + "assign_org_role:*", + "assign_org_role:assign", + "assign_org_role:create", + "assign_org_role:delete", + "assign_org_role:read", + "assign_org_role:unassign", + "assign_org_role:update", + "assign_role:*", + "assign_role:assign", + "assign_role:read", + "assign_role:unassign", + "audit_log:*", + "audit_log:create", + "audit_log:read", "coder:all", "coder:apikeys.manage_self", "coder:application_connect", @@ -10260,40 +10278,188 @@ "coder:workspaces.create", "coder:workspaces.delete", "coder:workspaces.operate", + "connection_log:*", + "connection_log:read", + "connection_log:update", + "crypto_key:*", + "crypto_key:create", + "crypto_key:delete", + "crypto_key:read", + "crypto_key:update", + "debug_info:*", + "debug_info:read", + "deployment_config:*", + "deployment_config:read", + "deployment_config:update", + "deployment_stats:*", + "deployment_stats:read", "file:*", "file:create", "file:read", + "group:*", + "group:create", + "group:delete", + "group:read", + "group:update", + "group_member:*", + "group_member:read", + "idpsync_settings:*", + "idpsync_settings:read", + "idpsync_settings:update", + "inbox_notification:*", + "inbox_notification:create", + "inbox_notification:read", + "inbox_notification:update", + "license:*", + "license:create", + "license:delete", + "license:read", + "notification_message:*", + "notification_message:create", + "notification_message:delete", + "notification_message:read", + "notification_message:update", + "notification_preference:*", + "notification_preference:read", + "notification_preference:update", + "notification_template:*", + "notification_template:read", + "notification_template:update", + "oauth2_app:*", + "oauth2_app:create", + "oauth2_app:delete", + "oauth2_app:read", + "oauth2_app:update", + "oauth2_app_code_token:*", + "oauth2_app_code_token:create", + "oauth2_app_code_token:delete", + "oauth2_app_code_token:read", + "oauth2_app_secret:*", + "oauth2_app_secret:create", + "oauth2_app_secret:delete", + "oauth2_app_secret:read", + "oauth2_app_secret:update", + "organization:*", + "organization:create", + "organization:delete", + "organization:read", + "organization:update", + "organization_member:*", + "organization_member:create", + "organization_member:delete", + "organization_member:read", + "organization_member:update", + "prebuilt_workspace:*", + "prebuilt_workspace:delete", + "prebuilt_workspace:update", + "provisioner_daemon:*", + "provisioner_daemon:create", + "provisioner_daemon:delete", + "provisioner_daemon:read", + "provisioner_daemon:update", + "provisioner_jobs:*", + "provisioner_jobs:create", + "provisioner_jobs:read", + "provisioner_jobs:update", + "replicas:*", + "replicas:read", + "system:*", + "system:create", + "system:delete", + "system:read", + "system:update", + "tailnet_coordinator:*", + "tailnet_coordinator:create", + "tailnet_coordinator:delete", + "tailnet_coordinator:read", + "tailnet_coordinator:update", "template:*", "template:create", "template:delete", "template:read", "template:update", "template:use", + "template:view_insights", + "usage_event:*", + "usage_event:create", + "usage_event:read", + "usage_event:update", + "user:*", + "user:create", + "user:delete", + "user:read", "user:read_personal", + "user:update", "user:update_personal", "user_secret:*", "user_secret:create", "user_secret:delete", "user_secret:read", "user_secret:update", + "webpush_subscription:*", + "webpush_subscription:create", + "webpush_subscription:delete", + "webpush_subscription:read", "workspace:*", "workspace:application_connect", "workspace:create", + "workspace:create_agent", "workspace:delete", + "workspace:delete_agent", "workspace:read", "workspace:ssh", "workspace:start", "workspace:stop", - "workspace:update" + "workspace:update", + "workspace_agent_devcontainers:*", + "workspace_agent_devcontainers:create", + "workspace_agent_resource_monitor:*", + "workspace_agent_resource_monitor:create", + "workspace_agent_resource_monitor:read", + "workspace_agent_resource_monitor:update", + "workspace_dormant:*", + "workspace_dormant:application_connect", + "workspace_dormant:create", + "workspace_dormant:create_agent", + "workspace_dormant:delete", + "workspace_dormant:delete_agent", + "workspace_dormant:read", + "workspace_dormant:ssh", + "workspace_dormant:start", + "workspace_dormant:stop", + "workspace_dormant:update", + "workspace_proxy:*", + "workspace_proxy:create", + "workspace_proxy:delete", + "workspace_proxy:read", + "workspace_proxy:update" ], "x-enum-varnames": [ "APIKeyScopeAll", "APIKeyScopeApplicationConnect", + "APIKeyScopeAibridgeInterceptionAll", + "APIKeyScopeAibridgeInterceptionCreate", + "APIKeyScopeAibridgeInterceptionRead", + "APIKeyScopeAibridgeInterceptionUpdate", "APIKeyScopeApiKeyAll", "APIKeyScopeApiKeyCreate", "APIKeyScopeApiKeyDelete", "APIKeyScopeApiKeyRead", "APIKeyScopeApiKeyUpdate", + "APIKeyScopeAssignOrgRoleAll", + "APIKeyScopeAssignOrgRoleAssign", + "APIKeyScopeAssignOrgRoleCreate", + "APIKeyScopeAssignOrgRoleDelete", + "APIKeyScopeAssignOrgRoleRead", + "APIKeyScopeAssignOrgRoleUnassign", + "APIKeyScopeAssignOrgRoleUpdate", + "APIKeyScopeAssignRoleAll", + "APIKeyScopeAssignRoleAssign", + "APIKeyScopeAssignRoleRead", + "APIKeyScopeAssignRoleUnassign", + "APIKeyScopeAuditLogAll", + "APIKeyScopeAuditLogCreate", + "APIKeyScopeAuditLogRead", "APIKeyScopeCoderAll", "APIKeyScopeCoderApikeysManageSelf", "APIKeyScopeCoderApplicationConnect", @@ -10303,31 +10469,161 @@ "APIKeyScopeCoderWorkspacesCreate", "APIKeyScopeCoderWorkspacesDelete", "APIKeyScopeCoderWorkspacesOperate", + "APIKeyScopeConnectionLogAll", + "APIKeyScopeConnectionLogRead", + "APIKeyScopeConnectionLogUpdate", + "APIKeyScopeCryptoKeyAll", + "APIKeyScopeCryptoKeyCreate", + "APIKeyScopeCryptoKeyDelete", + "APIKeyScopeCryptoKeyRead", + "APIKeyScopeCryptoKeyUpdate", + "APIKeyScopeDebugInfoAll", + "APIKeyScopeDebugInfoRead", + "APIKeyScopeDeploymentConfigAll", + "APIKeyScopeDeploymentConfigRead", + "APIKeyScopeDeploymentConfigUpdate", + "APIKeyScopeDeploymentStatsAll", + "APIKeyScopeDeploymentStatsRead", "APIKeyScopeFileAll", "APIKeyScopeFileCreate", "APIKeyScopeFileRead", + "APIKeyScopeGroupAll", + "APIKeyScopeGroupCreate", + "APIKeyScopeGroupDelete", + "APIKeyScopeGroupRead", + "APIKeyScopeGroupUpdate", + "APIKeyScopeGroupMemberAll", + "APIKeyScopeGroupMemberRead", + "APIKeyScopeIdpsyncSettingsAll", + "APIKeyScopeIdpsyncSettingsRead", + "APIKeyScopeIdpsyncSettingsUpdate", + "APIKeyScopeInboxNotificationAll", + "APIKeyScopeInboxNotificationCreate", + "APIKeyScopeInboxNotificationRead", + "APIKeyScopeInboxNotificationUpdate", + "APIKeyScopeLicenseAll", + "APIKeyScopeLicenseCreate", + "APIKeyScopeLicenseDelete", + "APIKeyScopeLicenseRead", + "APIKeyScopeNotificationMessageAll", + "APIKeyScopeNotificationMessageCreate", + "APIKeyScopeNotificationMessageDelete", + "APIKeyScopeNotificationMessageRead", + "APIKeyScopeNotificationMessageUpdate", + "APIKeyScopeNotificationPreferenceAll", + "APIKeyScopeNotificationPreferenceRead", + "APIKeyScopeNotificationPreferenceUpdate", + "APIKeyScopeNotificationTemplateAll", + "APIKeyScopeNotificationTemplateRead", + "APIKeyScopeNotificationTemplateUpdate", + "APIKeyScopeOauth2AppAll", + "APIKeyScopeOauth2AppCreate", + "APIKeyScopeOauth2AppDelete", + "APIKeyScopeOauth2AppRead", + "APIKeyScopeOauth2AppUpdate", + "APIKeyScopeOauth2AppCodeTokenAll", + "APIKeyScopeOauth2AppCodeTokenCreate", + "APIKeyScopeOauth2AppCodeTokenDelete", + "APIKeyScopeOauth2AppCodeTokenRead", + "APIKeyScopeOauth2AppSecretAll", + "APIKeyScopeOauth2AppSecretCreate", + "APIKeyScopeOauth2AppSecretDelete", + "APIKeyScopeOauth2AppSecretRead", + "APIKeyScopeOauth2AppSecretUpdate", + "APIKeyScopeOrganizationAll", + "APIKeyScopeOrganizationCreate", + "APIKeyScopeOrganizationDelete", + "APIKeyScopeOrganizationRead", + "APIKeyScopeOrganizationUpdate", + "APIKeyScopeOrganizationMemberAll", + "APIKeyScopeOrganizationMemberCreate", + "APIKeyScopeOrganizationMemberDelete", + "APIKeyScopeOrganizationMemberRead", + "APIKeyScopeOrganizationMemberUpdate", + "APIKeyScopePrebuiltWorkspaceAll", + "APIKeyScopePrebuiltWorkspaceDelete", + "APIKeyScopePrebuiltWorkspaceUpdate", + "APIKeyScopeProvisionerDaemonAll", + "APIKeyScopeProvisionerDaemonCreate", + "APIKeyScopeProvisionerDaemonDelete", + "APIKeyScopeProvisionerDaemonRead", + "APIKeyScopeProvisionerDaemonUpdate", + "APIKeyScopeProvisionerJobsAll", + "APIKeyScopeProvisionerJobsCreate", + "APIKeyScopeProvisionerJobsRead", + "APIKeyScopeProvisionerJobsUpdate", + "APIKeyScopeReplicasAll", + "APIKeyScopeReplicasRead", + "APIKeyScopeSystemAll", + "APIKeyScopeSystemCreate", + "APIKeyScopeSystemDelete", + "APIKeyScopeSystemRead", + "APIKeyScopeSystemUpdate", + "APIKeyScopeTailnetCoordinatorAll", + "APIKeyScopeTailnetCoordinatorCreate", + "APIKeyScopeTailnetCoordinatorDelete", + "APIKeyScopeTailnetCoordinatorRead", + "APIKeyScopeTailnetCoordinatorUpdate", "APIKeyScopeTemplateAll", "APIKeyScopeTemplateCreate", "APIKeyScopeTemplateDelete", "APIKeyScopeTemplateRead", "APIKeyScopeTemplateUpdate", "APIKeyScopeTemplateUse", + "APIKeyScopeTemplateViewInsights", + "APIKeyScopeUsageEventAll", + "APIKeyScopeUsageEventCreate", + "APIKeyScopeUsageEventRead", + "APIKeyScopeUsageEventUpdate", + "APIKeyScopeUserAll", + "APIKeyScopeUserCreate", + "APIKeyScopeUserDelete", + "APIKeyScopeUserRead", "APIKeyScopeUserReadPersonal", + "APIKeyScopeUserUpdate", "APIKeyScopeUserUpdatePersonal", "APIKeyScopeUserSecretAll", "APIKeyScopeUserSecretCreate", "APIKeyScopeUserSecretDelete", "APIKeyScopeUserSecretRead", "APIKeyScopeUserSecretUpdate", + "APIKeyScopeWebpushSubscriptionAll", + "APIKeyScopeWebpushSubscriptionCreate", + "APIKeyScopeWebpushSubscriptionDelete", + "APIKeyScopeWebpushSubscriptionRead", "APIKeyScopeWorkspaceAll", "APIKeyScopeWorkspaceApplicationConnect", "APIKeyScopeWorkspaceCreate", + "APIKeyScopeWorkspaceCreateAgent", "APIKeyScopeWorkspaceDelete", + "APIKeyScopeWorkspaceDeleteAgent", "APIKeyScopeWorkspaceRead", "APIKeyScopeWorkspaceSsh", "APIKeyScopeWorkspaceStart", "APIKeyScopeWorkspaceStop", - "APIKeyScopeWorkspaceUpdate" + "APIKeyScopeWorkspaceUpdate", + "APIKeyScopeWorkspaceAgentDevcontainersAll", + "APIKeyScopeWorkspaceAgentDevcontainersCreate", + "APIKeyScopeWorkspaceAgentResourceMonitorAll", + "APIKeyScopeWorkspaceAgentResourceMonitorCreate", + "APIKeyScopeWorkspaceAgentResourceMonitorRead", + "APIKeyScopeWorkspaceAgentResourceMonitorUpdate", + "APIKeyScopeWorkspaceDormantAll", + "APIKeyScopeWorkspaceDormantApplicationConnect", + "APIKeyScopeWorkspaceDormantCreate", + "APIKeyScopeWorkspaceDormantCreateAgent", + "APIKeyScopeWorkspaceDormantDelete", + "APIKeyScopeWorkspaceDormantDeleteAgent", + "APIKeyScopeWorkspaceDormantRead", + "APIKeyScopeWorkspaceDormantSsh", + "APIKeyScopeWorkspaceDormantStart", + "APIKeyScopeWorkspaceDormantStop", + "APIKeyScopeWorkspaceDormantUpdate", + "APIKeyScopeWorkspaceProxyAll", + "APIKeyScopeWorkspaceProxyCreate", + "APIKeyScopeWorkspaceProxyDelete", + "APIKeyScopeWorkspaceProxyRead", + "APIKeyScopeWorkspaceProxyUpdate" ] }, "codersdk.AddLicenseRequest": { diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql index 7a4be98b95045..b2cac7185bfcf 100644 --- a/coderd/database/dump.sql +++ b/coderd/database/dump.sql @@ -157,7 +157,47 @@ CREATE TYPE api_key_scope AS ENUM ( 'coder:workspaces.access', 'coder:templates.build', 'coder:templates.author', - 'coder:apikeys.manage_self' + 'coder:apikeys.manage_self', + 'aibridge_interception:*', + 'api_key:*', + 'assign_org_role:*', + 'assign_role:*', + 'audit_log:*', + 'connection_log:*', + 'crypto_key:*', + 'debug_info:*', + 'deployment_config:*', + 'deployment_stats:*', + 'file:*', + 'group:*', + 'group_member:*', + 'idpsync_settings:*', + 'inbox_notification:*', + 'license:*', + 'notification_message:*', + 'notification_preference:*', + 'notification_template:*', + 'oauth2_app:*', + 'oauth2_app_code_token:*', + 'oauth2_app_secret:*', + 'organization:*', + 'organization_member:*', + 'prebuilt_workspace:*', + 'provisioner_daemon:*', + 'provisioner_jobs:*', + 'replicas:*', + 'system:*', + 'tailnet_coordinator:*', + 'template:*', + 'usage_event:*', + 'user:*', + 'user_secret:*', + 'webpush_subscription:*', + 'workspace:*', + 'workspace_agent_devcontainers:*', + 'workspace_agent_resource_monitor:*', + 'workspace_dormant:*', + 'workspace_proxy:*' ); CREATE TYPE app_sharing_level AS ENUM ( diff --git a/coderd/database/migrations/000377_add_api_key_scope_wildcards.down.sql b/coderd/database/migrations/000377_add_api_key_scope_wildcards.down.sql new file mode 100644 index 0000000000000..a414b39a912ee --- /dev/null +++ b/coderd/database/migrations/000377_add_api_key_scope_wildcards.down.sql @@ -0,0 +1,2 @@ +-- No-op: enum values remain to avoid churn. Removing enum values requires +-- doing a create/cast/drop cycle which is intentionally omitted here. diff --git a/coderd/database/migrations/000377_add_api_key_scope_wildcards.up.sql b/coderd/database/migrations/000377_add_api_key_scope_wildcards.up.sql new file mode 100644 index 0000000000000..aed5a18a3e31d --- /dev/null +++ b/coderd/database/migrations/000377_add_api_key_scope_wildcards.up.sql @@ -0,0 +1,42 @@ +-- Add wildcard api_key_scope entries so every RBAC resource has a matching resource:* value. +-- Generated via: CGO_ENABLED=0 go run ./scripts/generate_api_key_scope_enum +ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'aibridge_interception:*'; +ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'api_key:*'; +ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'assign_org_role:*'; +ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'assign_role:*'; +ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'audit_log:*'; +ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'connection_log:*'; +ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'crypto_key:*'; +ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'debug_info:*'; +ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'deployment_config:*'; +ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'deployment_stats:*'; +ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'file:*'; +ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'group:*'; +ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'group_member:*'; +ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'idpsync_settings:*'; +ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'inbox_notification:*'; +ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'license:*'; +ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'notification_message:*'; +ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'notification_preference:*'; +ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'notification_template:*'; +ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'oauth2_app:*'; +ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'oauth2_app_code_token:*'; +ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'oauth2_app_secret:*'; +ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'organization:*'; +ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'organization_member:*'; +ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'prebuilt_workspace:*'; +ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'provisioner_daemon:*'; +ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'provisioner_jobs:*'; +ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'replicas:*'; +ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'system:*'; +ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'tailnet_coordinator:*'; +ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'template:*'; +ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'usage_event:*'; +ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'user:*'; +ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'user_secret:*'; +ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'webpush_subscription:*'; +ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'workspace:*'; +ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'workspace_agent_devcontainers:*'; +ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'workspace_agent_resource_monitor:*'; +ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'workspace_dormant:*'; +ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'workspace_proxy:*'; diff --git a/coderd/database/models.go b/coderd/database/models.go index eecb657edb15d..06c0e98fe4f64 100644 --- a/coderd/database/models.go +++ b/coderd/database/models.go @@ -166,6 +166,46 @@ const ( ApiKeyScopeCoderTemplatesbuild APIKeyScope = "coder:templates.build" ApiKeyScopeCoderTemplatesauthor APIKeyScope = "coder:templates.author" ApiKeyScopeCoderApikeysmanageSelf APIKeyScope = "coder:apikeys.manage_self" + ApiKeyScopeAibridgeInterception APIKeyScope = "aibridge_interception:*" + ApiKeyScopeApiKey APIKeyScope = "api_key:*" + ApiKeyScopeAssignOrgRole APIKeyScope = "assign_org_role:*" + ApiKeyScopeAssignRole APIKeyScope = "assign_role:*" + ApiKeyScopeAuditLog APIKeyScope = "audit_log:*" + ApiKeyScopeConnectionLog APIKeyScope = "connection_log:*" + ApiKeyScopeCryptoKey APIKeyScope = "crypto_key:*" + ApiKeyScopeDebugInfo APIKeyScope = "debug_info:*" + ApiKeyScopeDeploymentConfig APIKeyScope = "deployment_config:*" + ApiKeyScopeDeploymentStats APIKeyScope = "deployment_stats:*" + ApiKeyScopeFile APIKeyScope = "file:*" + ApiKeyScopeGroup APIKeyScope = "group:*" + ApiKeyScopeGroupMember APIKeyScope = "group_member:*" + ApiKeyScopeIdpsyncSettings APIKeyScope = "idpsync_settings:*" + ApiKeyScopeInboxNotification APIKeyScope = "inbox_notification:*" + ApiKeyScopeLicense APIKeyScope = "license:*" + ApiKeyScopeNotificationMessage APIKeyScope = "notification_message:*" + ApiKeyScopeNotificationPreference APIKeyScope = "notification_preference:*" + ApiKeyScopeNotificationTemplate APIKeyScope = "notification_template:*" + ApiKeyScopeOauth2App APIKeyScope = "oauth2_app:*" + ApiKeyScopeOauth2AppCodeToken APIKeyScope = "oauth2_app_code_token:*" + ApiKeyScopeOauth2AppSecret APIKeyScope = "oauth2_app_secret:*" + ApiKeyScopeOrganization APIKeyScope = "organization:*" + ApiKeyScopeOrganizationMember APIKeyScope = "organization_member:*" + ApiKeyScopePrebuiltWorkspace APIKeyScope = "prebuilt_workspace:*" + ApiKeyScopeProvisionerDaemon APIKeyScope = "provisioner_daemon:*" + ApiKeyScopeProvisionerJobs APIKeyScope = "provisioner_jobs:*" + ApiKeyScopeReplicas APIKeyScope = "replicas:*" + ApiKeyScopeSystem APIKeyScope = "system:*" + ApiKeyScopeTailnetCoordinator APIKeyScope = "tailnet_coordinator:*" + ApiKeyScopeTemplate APIKeyScope = "template:*" + ApiKeyScopeUsageEvent APIKeyScope = "usage_event:*" + ApiKeyScopeUser APIKeyScope = "user:*" + ApiKeyScopeUserSecret APIKeyScope = "user_secret:*" + ApiKeyScopeWebpushSubscription APIKeyScope = "webpush_subscription:*" + ApiKeyScopeWorkspace APIKeyScope = "workspace:*" + ApiKeyScopeWorkspaceAgentDevcontainers APIKeyScope = "workspace_agent_devcontainers:*" + ApiKeyScopeWorkspaceAgentResourceMonitor APIKeyScope = "workspace_agent_resource_monitor:*" + ApiKeyScopeWorkspaceDormant APIKeyScope = "workspace_dormant:*" + ApiKeyScopeWorkspaceProxy APIKeyScope = "workspace_proxy:*" ) func (e *APIKeyScope) Scan(src interface{}) error { @@ -351,7 +391,47 @@ func (e APIKeyScope) Valid() bool { ApiKeyScopeCoderWorkspacesaccess, ApiKeyScopeCoderTemplatesbuild, ApiKeyScopeCoderTemplatesauthor, - ApiKeyScopeCoderApikeysmanageSelf: + ApiKeyScopeCoderApikeysmanageSelf, + ApiKeyScopeAibridgeInterception, + ApiKeyScopeApiKey, + ApiKeyScopeAssignOrgRole, + ApiKeyScopeAssignRole, + ApiKeyScopeAuditLog, + ApiKeyScopeConnectionLog, + ApiKeyScopeCryptoKey, + ApiKeyScopeDebugInfo, + ApiKeyScopeDeploymentConfig, + ApiKeyScopeDeploymentStats, + ApiKeyScopeFile, + ApiKeyScopeGroup, + ApiKeyScopeGroupMember, + ApiKeyScopeIdpsyncSettings, + ApiKeyScopeInboxNotification, + ApiKeyScopeLicense, + ApiKeyScopeNotificationMessage, + ApiKeyScopeNotificationPreference, + ApiKeyScopeNotificationTemplate, + ApiKeyScopeOauth2App, + ApiKeyScopeOauth2AppCodeToken, + ApiKeyScopeOauth2AppSecret, + ApiKeyScopeOrganization, + ApiKeyScopeOrganizationMember, + ApiKeyScopePrebuiltWorkspace, + ApiKeyScopeProvisionerDaemon, + ApiKeyScopeProvisionerJobs, + ApiKeyScopeReplicas, + ApiKeyScopeSystem, + ApiKeyScopeTailnetCoordinator, + ApiKeyScopeTemplate, + ApiKeyScopeUsageEvent, + ApiKeyScopeUser, + ApiKeyScopeUserSecret, + ApiKeyScopeWebpushSubscription, + ApiKeyScopeWorkspace, + ApiKeyScopeWorkspaceAgentDevcontainers, + ApiKeyScopeWorkspaceAgentResourceMonitor, + ApiKeyScopeWorkspaceDormant, + ApiKeyScopeWorkspaceProxy: return true } return false @@ -506,6 +586,46 @@ func AllAPIKeyScopeValues() []APIKeyScope { ApiKeyScopeCoderTemplatesbuild, ApiKeyScopeCoderTemplatesauthor, ApiKeyScopeCoderApikeysmanageSelf, + ApiKeyScopeAibridgeInterception, + ApiKeyScopeApiKey, + ApiKeyScopeAssignOrgRole, + ApiKeyScopeAssignRole, + ApiKeyScopeAuditLog, + ApiKeyScopeConnectionLog, + ApiKeyScopeCryptoKey, + ApiKeyScopeDebugInfo, + ApiKeyScopeDeploymentConfig, + ApiKeyScopeDeploymentStats, + ApiKeyScopeFile, + ApiKeyScopeGroup, + ApiKeyScopeGroupMember, + ApiKeyScopeIdpsyncSettings, + ApiKeyScopeInboxNotification, + ApiKeyScopeLicense, + ApiKeyScopeNotificationMessage, + ApiKeyScopeNotificationPreference, + ApiKeyScopeNotificationTemplate, + ApiKeyScopeOauth2App, + ApiKeyScopeOauth2AppCodeToken, + ApiKeyScopeOauth2AppSecret, + ApiKeyScopeOrganization, + ApiKeyScopeOrganizationMember, + ApiKeyScopePrebuiltWorkspace, + ApiKeyScopeProvisionerDaemon, + ApiKeyScopeProvisionerJobs, + ApiKeyScopeReplicas, + ApiKeyScopeSystem, + ApiKeyScopeTailnetCoordinator, + ApiKeyScopeTemplate, + ApiKeyScopeUsageEvent, + ApiKeyScopeUser, + ApiKeyScopeUserSecret, + ApiKeyScopeWebpushSubscription, + ApiKeyScopeWorkspace, + ApiKeyScopeWorkspaceAgentDevcontainers, + ApiKeyScopeWorkspaceAgentResourceMonitor, + ApiKeyScopeWorkspaceDormant, + ApiKeyScopeWorkspaceProxy, } } diff --git a/codersdk/apikey_scopes_gen.go b/codersdk/apikey_scopes_gen.go index a399a014b42d5..606a704ae4bdc 100644 --- a/codersdk/apikey_scopes_gen.go +++ b/codersdk/apikey_scopes_gen.go @@ -5,46 +5,194 @@ const ( // Deprecated: use codersdk.APIKeyScopeCoderAll instead. APIKeyScopeAll APIKeyScope = "all" // Deprecated: use codersdk.APIKeyScopeCoderApplicationConnect instead. - APIKeyScopeApplicationConnect APIKeyScope = "application_connect" - APIKeyScopeApiKeyAll APIKeyScope = "api_key:*" - APIKeyScopeApiKeyCreate APIKeyScope = "api_key:create" - APIKeyScopeApiKeyDelete APIKeyScope = "api_key:delete" - APIKeyScopeApiKeyRead APIKeyScope = "api_key:read" - APIKeyScopeApiKeyUpdate APIKeyScope = "api_key:update" - APIKeyScopeCoderAll APIKeyScope = "coder:all" - APIKeyScopeCoderApikeysManageSelf APIKeyScope = "coder:apikeys.manage_self" - APIKeyScopeCoderApplicationConnect APIKeyScope = "coder:application_connect" - APIKeyScopeCoderTemplatesAuthor APIKeyScope = "coder:templates.author" - APIKeyScopeCoderTemplatesBuild APIKeyScope = "coder:templates.build" - APIKeyScopeCoderWorkspacesAccess APIKeyScope = "coder:workspaces.access" - APIKeyScopeCoderWorkspacesCreate APIKeyScope = "coder:workspaces.create" - APIKeyScopeCoderWorkspacesDelete APIKeyScope = "coder:workspaces.delete" - APIKeyScopeCoderWorkspacesOperate APIKeyScope = "coder:workspaces.operate" - APIKeyScopeFileAll APIKeyScope = "file:*" - APIKeyScopeFileCreate APIKeyScope = "file:create" - APIKeyScopeFileRead APIKeyScope = "file:read" - APIKeyScopeTemplateAll APIKeyScope = "template:*" - APIKeyScopeTemplateCreate APIKeyScope = "template:create" - APIKeyScopeTemplateDelete APIKeyScope = "template:delete" - APIKeyScopeTemplateRead APIKeyScope = "template:read" - APIKeyScopeTemplateUpdate APIKeyScope = "template:update" - APIKeyScopeTemplateUse APIKeyScope = "template:use" - APIKeyScopeUserReadPersonal APIKeyScope = "user:read_personal" - APIKeyScopeUserUpdatePersonal APIKeyScope = "user:update_personal" - APIKeyScopeUserSecretAll APIKeyScope = "user_secret:*" - APIKeyScopeUserSecretCreate APIKeyScope = "user_secret:create" - APIKeyScopeUserSecretDelete APIKeyScope = "user_secret:delete" - APIKeyScopeUserSecretRead APIKeyScope = "user_secret:read" - APIKeyScopeUserSecretUpdate APIKeyScope = "user_secret:update" - APIKeyScopeWorkspaceAll APIKeyScope = "workspace:*" - APIKeyScopeWorkspaceApplicationConnect APIKeyScope = "workspace:application_connect" - APIKeyScopeWorkspaceCreate APIKeyScope = "workspace:create" - APIKeyScopeWorkspaceDelete APIKeyScope = "workspace:delete" - APIKeyScopeWorkspaceRead APIKeyScope = "workspace:read" - APIKeyScopeWorkspaceSsh APIKeyScope = "workspace:ssh" - APIKeyScopeWorkspaceStart APIKeyScope = "workspace:start" - APIKeyScopeWorkspaceStop APIKeyScope = "workspace:stop" - APIKeyScopeWorkspaceUpdate APIKeyScope = "workspace:update" + APIKeyScopeApplicationConnect APIKeyScope = "application_connect" + APIKeyScopeAibridgeInterceptionAll APIKeyScope = "aibridge_interception:*" + APIKeyScopeAibridgeInterceptionCreate APIKeyScope = "aibridge_interception:create" + APIKeyScopeAibridgeInterceptionRead APIKeyScope = "aibridge_interception:read" + APIKeyScopeAibridgeInterceptionUpdate APIKeyScope = "aibridge_interception:update" + APIKeyScopeApiKeyAll APIKeyScope = "api_key:*" + APIKeyScopeApiKeyCreate APIKeyScope = "api_key:create" + APIKeyScopeApiKeyDelete APIKeyScope = "api_key:delete" + APIKeyScopeApiKeyRead APIKeyScope = "api_key:read" + APIKeyScopeApiKeyUpdate APIKeyScope = "api_key:update" + APIKeyScopeAssignOrgRoleAll APIKeyScope = "assign_org_role:*" + APIKeyScopeAssignOrgRoleAssign APIKeyScope = "assign_org_role:assign" + APIKeyScopeAssignOrgRoleCreate APIKeyScope = "assign_org_role:create" + APIKeyScopeAssignOrgRoleDelete APIKeyScope = "assign_org_role:delete" + APIKeyScopeAssignOrgRoleRead APIKeyScope = "assign_org_role:read" + APIKeyScopeAssignOrgRoleUnassign APIKeyScope = "assign_org_role:unassign" + APIKeyScopeAssignOrgRoleUpdate APIKeyScope = "assign_org_role:update" + APIKeyScopeAssignRoleAll APIKeyScope = "assign_role:*" + APIKeyScopeAssignRoleAssign APIKeyScope = "assign_role:assign" + APIKeyScopeAssignRoleRead APIKeyScope = "assign_role:read" + APIKeyScopeAssignRoleUnassign APIKeyScope = "assign_role:unassign" + APIKeyScopeAuditLogAll APIKeyScope = "audit_log:*" + APIKeyScopeAuditLogCreate APIKeyScope = "audit_log:create" + APIKeyScopeAuditLogRead APIKeyScope = "audit_log:read" + APIKeyScopeCoderAll APIKeyScope = "coder:all" + APIKeyScopeCoderApikeysManageSelf APIKeyScope = "coder:apikeys.manage_self" + APIKeyScopeCoderApplicationConnect APIKeyScope = "coder:application_connect" + APIKeyScopeCoderTemplatesAuthor APIKeyScope = "coder:templates.author" + APIKeyScopeCoderTemplatesBuild APIKeyScope = "coder:templates.build" + APIKeyScopeCoderWorkspacesAccess APIKeyScope = "coder:workspaces.access" + APIKeyScopeCoderWorkspacesCreate APIKeyScope = "coder:workspaces.create" + APIKeyScopeCoderWorkspacesDelete APIKeyScope = "coder:workspaces.delete" + APIKeyScopeCoderWorkspacesOperate APIKeyScope = "coder:workspaces.operate" + APIKeyScopeConnectionLogAll APIKeyScope = "connection_log:*" + APIKeyScopeConnectionLogRead APIKeyScope = "connection_log:read" + APIKeyScopeConnectionLogUpdate APIKeyScope = "connection_log:update" + APIKeyScopeCryptoKeyAll APIKeyScope = "crypto_key:*" + APIKeyScopeCryptoKeyCreate APIKeyScope = "crypto_key:create" + APIKeyScopeCryptoKeyDelete APIKeyScope = "crypto_key:delete" + APIKeyScopeCryptoKeyRead APIKeyScope = "crypto_key:read" + APIKeyScopeCryptoKeyUpdate APIKeyScope = "crypto_key:update" + APIKeyScopeDebugInfoAll APIKeyScope = "debug_info:*" + APIKeyScopeDebugInfoRead APIKeyScope = "debug_info:read" + APIKeyScopeDeploymentConfigAll APIKeyScope = "deployment_config:*" + APIKeyScopeDeploymentConfigRead APIKeyScope = "deployment_config:read" + APIKeyScopeDeploymentConfigUpdate APIKeyScope = "deployment_config:update" + APIKeyScopeDeploymentStatsAll APIKeyScope = "deployment_stats:*" + APIKeyScopeDeploymentStatsRead APIKeyScope = "deployment_stats:read" + APIKeyScopeFileAll APIKeyScope = "file:*" + APIKeyScopeFileCreate APIKeyScope = "file:create" + APIKeyScopeFileRead APIKeyScope = "file:read" + APIKeyScopeGroupAll APIKeyScope = "group:*" + APIKeyScopeGroupCreate APIKeyScope = "group:create" + APIKeyScopeGroupDelete APIKeyScope = "group:delete" + APIKeyScopeGroupRead APIKeyScope = "group:read" + APIKeyScopeGroupUpdate APIKeyScope = "group:update" + APIKeyScopeGroupMemberAll APIKeyScope = "group_member:*" + APIKeyScopeGroupMemberRead APIKeyScope = "group_member:read" + APIKeyScopeIdpsyncSettingsAll APIKeyScope = "idpsync_settings:*" + APIKeyScopeIdpsyncSettingsRead APIKeyScope = "idpsync_settings:read" + APIKeyScopeIdpsyncSettingsUpdate APIKeyScope = "idpsync_settings:update" + APIKeyScopeInboxNotificationAll APIKeyScope = "inbox_notification:*" + APIKeyScopeInboxNotificationCreate APIKeyScope = "inbox_notification:create" + APIKeyScopeInboxNotificationRead APIKeyScope = "inbox_notification:read" + APIKeyScopeInboxNotificationUpdate APIKeyScope = "inbox_notification:update" + APIKeyScopeLicenseAll APIKeyScope = "license:*" + APIKeyScopeLicenseCreate APIKeyScope = "license:create" + APIKeyScopeLicenseDelete APIKeyScope = "license:delete" + APIKeyScopeLicenseRead APIKeyScope = "license:read" + APIKeyScopeNotificationMessageAll APIKeyScope = "notification_message:*" + APIKeyScopeNotificationMessageCreate APIKeyScope = "notification_message:create" + APIKeyScopeNotificationMessageDelete APIKeyScope = "notification_message:delete" + APIKeyScopeNotificationMessageRead APIKeyScope = "notification_message:read" + APIKeyScopeNotificationMessageUpdate APIKeyScope = "notification_message:update" + APIKeyScopeNotificationPreferenceAll APIKeyScope = "notification_preference:*" + APIKeyScopeNotificationPreferenceRead APIKeyScope = "notification_preference:read" + APIKeyScopeNotificationPreferenceUpdate APIKeyScope = "notification_preference:update" + APIKeyScopeNotificationTemplateAll APIKeyScope = "notification_template:*" + APIKeyScopeNotificationTemplateRead APIKeyScope = "notification_template:read" + APIKeyScopeNotificationTemplateUpdate APIKeyScope = "notification_template:update" + APIKeyScopeOauth2AppAll APIKeyScope = "oauth2_app:*" + APIKeyScopeOauth2AppCreate APIKeyScope = "oauth2_app:create" + APIKeyScopeOauth2AppDelete APIKeyScope = "oauth2_app:delete" + APIKeyScopeOauth2AppRead APIKeyScope = "oauth2_app:read" + APIKeyScopeOauth2AppUpdate APIKeyScope = "oauth2_app:update" + APIKeyScopeOauth2AppCodeTokenAll APIKeyScope = "oauth2_app_code_token:*" + APIKeyScopeOauth2AppCodeTokenCreate APIKeyScope = "oauth2_app_code_token:create" + APIKeyScopeOauth2AppCodeTokenDelete APIKeyScope = "oauth2_app_code_token:delete" + APIKeyScopeOauth2AppCodeTokenRead APIKeyScope = "oauth2_app_code_token:read" + APIKeyScopeOauth2AppSecretAll APIKeyScope = "oauth2_app_secret:*" + APIKeyScopeOauth2AppSecretCreate APIKeyScope = "oauth2_app_secret:create" + APIKeyScopeOauth2AppSecretDelete APIKeyScope = "oauth2_app_secret:delete" + APIKeyScopeOauth2AppSecretRead APIKeyScope = "oauth2_app_secret:read" + APIKeyScopeOauth2AppSecretUpdate APIKeyScope = "oauth2_app_secret:update" + APIKeyScopeOrganizationAll APIKeyScope = "organization:*" + APIKeyScopeOrganizationCreate APIKeyScope = "organization:create" + APIKeyScopeOrganizationDelete APIKeyScope = "organization:delete" + APIKeyScopeOrganizationRead APIKeyScope = "organization:read" + APIKeyScopeOrganizationUpdate APIKeyScope = "organization:update" + APIKeyScopeOrganizationMemberAll APIKeyScope = "organization_member:*" + APIKeyScopeOrganizationMemberCreate APIKeyScope = "organization_member:create" + APIKeyScopeOrganizationMemberDelete APIKeyScope = "organization_member:delete" + APIKeyScopeOrganizationMemberRead APIKeyScope = "organization_member:read" + APIKeyScopeOrganizationMemberUpdate APIKeyScope = "organization_member:update" + APIKeyScopePrebuiltWorkspaceAll APIKeyScope = "prebuilt_workspace:*" + APIKeyScopePrebuiltWorkspaceDelete APIKeyScope = "prebuilt_workspace:delete" + APIKeyScopePrebuiltWorkspaceUpdate APIKeyScope = "prebuilt_workspace:update" + APIKeyScopeProvisionerDaemonAll APIKeyScope = "provisioner_daemon:*" + APIKeyScopeProvisionerDaemonCreate APIKeyScope = "provisioner_daemon:create" + APIKeyScopeProvisionerDaemonDelete APIKeyScope = "provisioner_daemon:delete" + APIKeyScopeProvisionerDaemonRead APIKeyScope = "provisioner_daemon:read" + APIKeyScopeProvisionerDaemonUpdate APIKeyScope = "provisioner_daemon:update" + APIKeyScopeProvisionerJobsAll APIKeyScope = "provisioner_jobs:*" + APIKeyScopeProvisionerJobsCreate APIKeyScope = "provisioner_jobs:create" + APIKeyScopeProvisionerJobsRead APIKeyScope = "provisioner_jobs:read" + APIKeyScopeProvisionerJobsUpdate APIKeyScope = "provisioner_jobs:update" + APIKeyScopeReplicasAll APIKeyScope = "replicas:*" + APIKeyScopeReplicasRead APIKeyScope = "replicas:read" + APIKeyScopeSystemAll APIKeyScope = "system:*" + APIKeyScopeSystemCreate APIKeyScope = "system:create" + APIKeyScopeSystemDelete APIKeyScope = "system:delete" + APIKeyScopeSystemRead APIKeyScope = "system:read" + APIKeyScopeSystemUpdate APIKeyScope = "system:update" + APIKeyScopeTailnetCoordinatorAll APIKeyScope = "tailnet_coordinator:*" + APIKeyScopeTailnetCoordinatorCreate APIKeyScope = "tailnet_coordinator:create" + APIKeyScopeTailnetCoordinatorDelete APIKeyScope = "tailnet_coordinator:delete" + APIKeyScopeTailnetCoordinatorRead APIKeyScope = "tailnet_coordinator:read" + APIKeyScopeTailnetCoordinatorUpdate APIKeyScope = "tailnet_coordinator:update" + APIKeyScopeTemplateAll APIKeyScope = "template:*" + APIKeyScopeTemplateCreate APIKeyScope = "template:create" + APIKeyScopeTemplateDelete APIKeyScope = "template:delete" + APIKeyScopeTemplateRead APIKeyScope = "template:read" + APIKeyScopeTemplateUpdate APIKeyScope = "template:update" + APIKeyScopeTemplateUse APIKeyScope = "template:use" + APIKeyScopeTemplateViewInsights APIKeyScope = "template:view_insights" + APIKeyScopeUsageEventAll APIKeyScope = "usage_event:*" + APIKeyScopeUsageEventCreate APIKeyScope = "usage_event:create" + APIKeyScopeUsageEventRead APIKeyScope = "usage_event:read" + APIKeyScopeUsageEventUpdate APIKeyScope = "usage_event:update" + APIKeyScopeUserAll APIKeyScope = "user:*" + APIKeyScopeUserCreate APIKeyScope = "user:create" + APIKeyScopeUserDelete APIKeyScope = "user:delete" + APIKeyScopeUserRead APIKeyScope = "user:read" + APIKeyScopeUserReadPersonal APIKeyScope = "user:read_personal" + APIKeyScopeUserUpdate APIKeyScope = "user:update" + APIKeyScopeUserUpdatePersonal APIKeyScope = "user:update_personal" + APIKeyScopeUserSecretAll APIKeyScope = "user_secret:*" + APIKeyScopeUserSecretCreate APIKeyScope = "user_secret:create" + APIKeyScopeUserSecretDelete APIKeyScope = "user_secret:delete" + APIKeyScopeUserSecretRead APIKeyScope = "user_secret:read" + APIKeyScopeUserSecretUpdate APIKeyScope = "user_secret:update" + APIKeyScopeWebpushSubscriptionAll APIKeyScope = "webpush_subscription:*" + APIKeyScopeWebpushSubscriptionCreate APIKeyScope = "webpush_subscription:create" + APIKeyScopeWebpushSubscriptionDelete APIKeyScope = "webpush_subscription:delete" + APIKeyScopeWebpushSubscriptionRead APIKeyScope = "webpush_subscription:read" + APIKeyScopeWorkspaceAll APIKeyScope = "workspace:*" + APIKeyScopeWorkspaceApplicationConnect APIKeyScope = "workspace:application_connect" + APIKeyScopeWorkspaceCreate APIKeyScope = "workspace:create" + APIKeyScopeWorkspaceCreateAgent APIKeyScope = "workspace:create_agent" + APIKeyScopeWorkspaceDelete APIKeyScope = "workspace:delete" + APIKeyScopeWorkspaceDeleteAgent APIKeyScope = "workspace:delete_agent" + APIKeyScopeWorkspaceRead APIKeyScope = "workspace:read" + APIKeyScopeWorkspaceSsh APIKeyScope = "workspace:ssh" + APIKeyScopeWorkspaceStart APIKeyScope = "workspace:start" + APIKeyScopeWorkspaceStop APIKeyScope = "workspace:stop" + APIKeyScopeWorkspaceUpdate APIKeyScope = "workspace:update" + APIKeyScopeWorkspaceAgentDevcontainersAll APIKeyScope = "workspace_agent_devcontainers:*" + APIKeyScopeWorkspaceAgentDevcontainersCreate APIKeyScope = "workspace_agent_devcontainers:create" + APIKeyScopeWorkspaceAgentResourceMonitorAll APIKeyScope = "workspace_agent_resource_monitor:*" + APIKeyScopeWorkspaceAgentResourceMonitorCreate APIKeyScope = "workspace_agent_resource_monitor:create" + APIKeyScopeWorkspaceAgentResourceMonitorRead APIKeyScope = "workspace_agent_resource_monitor:read" + APIKeyScopeWorkspaceAgentResourceMonitorUpdate APIKeyScope = "workspace_agent_resource_monitor:update" + APIKeyScopeWorkspaceDormantAll APIKeyScope = "workspace_dormant:*" + APIKeyScopeWorkspaceDormantApplicationConnect APIKeyScope = "workspace_dormant:application_connect" + APIKeyScopeWorkspaceDormantCreate APIKeyScope = "workspace_dormant:create" + APIKeyScopeWorkspaceDormantCreateAgent APIKeyScope = "workspace_dormant:create_agent" + APIKeyScopeWorkspaceDormantDelete APIKeyScope = "workspace_dormant:delete" + APIKeyScopeWorkspaceDormantDeleteAgent APIKeyScope = "workspace_dormant:delete_agent" + APIKeyScopeWorkspaceDormantRead APIKeyScope = "workspace_dormant:read" + APIKeyScopeWorkspaceDormantSsh APIKeyScope = "workspace_dormant:ssh" + APIKeyScopeWorkspaceDormantStart APIKeyScope = "workspace_dormant:start" + APIKeyScopeWorkspaceDormantStop APIKeyScope = "workspace_dormant:stop" + APIKeyScopeWorkspaceDormantUpdate APIKeyScope = "workspace_dormant:update" + APIKeyScopeWorkspaceProxyAll APIKeyScope = "workspace_proxy:*" + APIKeyScopeWorkspaceProxyCreate APIKeyScope = "workspace_proxy:create" + APIKeyScopeWorkspaceProxyDelete APIKeyScope = "workspace_proxy:delete" + APIKeyScopeWorkspaceProxyRead APIKeyScope = "workspace_proxy:read" + APIKeyScopeWorkspaceProxyUpdate APIKeyScope = "workspace_proxy:update" ) // PublicAPIKeyScopes lists all public low-level API key scopes. diff --git a/docs/reference/api/schemas.md b/docs/reference/api/schemas.md index 33cb280ae15c0..c121096b094d6 100644 --- a/docs/reference/api/schemas.md +++ b/docs/reference/api/schemas.md @@ -711,49 +711,197 @@ #### Enumerated Values -| Value | -|---------------------------------| -| `all` | -| `application_connect` | -| `api_key:*` | -| `api_key:create` | -| `api_key:delete` | -| `api_key:read` | -| `api_key:update` | -| `coder:all` | -| `coder:apikeys.manage_self` | -| `coder:application_connect` | -| `coder:templates.author` | -| `coder:templates.build` | -| `coder:workspaces.access` | -| `coder:workspaces.create` | -| `coder:workspaces.delete` | -| `coder:workspaces.operate` | -| `file:*` | -| `file:create` | -| `file:read` | -| `template:*` | -| `template:create` | -| `template:delete` | -| `template:read` | -| `template:update` | -| `template:use` | -| `user:read_personal` | -| `user:update_personal` | -| `user_secret:*` | -| `user_secret:create` | -| `user_secret:delete` | -| `user_secret:read` | -| `user_secret:update` | -| `workspace:*` | -| `workspace:application_connect` | -| `workspace:create` | -| `workspace:delete` | -| `workspace:read` | -| `workspace:ssh` | -| `workspace:start` | -| `workspace:stop` | -| `workspace:update` | +| Value | +|-------------------------------------------| +| `all` | +| `application_connect` | +| `aibridge_interception:*` | +| `aibridge_interception:create` | +| `aibridge_interception:read` | +| `aibridge_interception:update` | +| `api_key:*` | +| `api_key:create` | +| `api_key:delete` | +| `api_key:read` | +| `api_key:update` | +| `assign_org_role:*` | +| `assign_org_role:assign` | +| `assign_org_role:create` | +| `assign_org_role:delete` | +| `assign_org_role:read` | +| `assign_org_role:unassign` | +| `assign_org_role:update` | +| `assign_role:*` | +| `assign_role:assign` | +| `assign_role:read` | +| `assign_role:unassign` | +| `audit_log:*` | +| `audit_log:create` | +| `audit_log:read` | +| `coder:all` | +| `coder:apikeys.manage_self` | +| `coder:application_connect` | +| `coder:templates.author` | +| `coder:templates.build` | +| `coder:workspaces.access` | +| `coder:workspaces.create` | +| `coder:workspaces.delete` | +| `coder:workspaces.operate` | +| `connection_log:*` | +| `connection_log:read` | +| `connection_log:update` | +| `crypto_key:*` | +| `crypto_key:create` | +| `crypto_key:delete` | +| `crypto_key:read` | +| `crypto_key:update` | +| `debug_info:*` | +| `debug_info:read` | +| `deployment_config:*` | +| `deployment_config:read` | +| `deployment_config:update` | +| `deployment_stats:*` | +| `deployment_stats:read` | +| `file:*` | +| `file:create` | +| `file:read` | +| `group:*` | +| `group:create` | +| `group:delete` | +| `group:read` | +| `group:update` | +| `group_member:*` | +| `group_member:read` | +| `idpsync_settings:*` | +| `idpsync_settings:read` | +| `idpsync_settings:update` | +| `inbox_notification:*` | +| `inbox_notification:create` | +| `inbox_notification:read` | +| `inbox_notification:update` | +| `license:*` | +| `license:create` | +| `license:delete` | +| `license:read` | +| `notification_message:*` | +| `notification_message:create` | +| `notification_message:delete` | +| `notification_message:read` | +| `notification_message:update` | +| `notification_preference:*` | +| `notification_preference:read` | +| `notification_preference:update` | +| `notification_template:*` | +| `notification_template:read` | +| `notification_template:update` | +| `oauth2_app:*` | +| `oauth2_app:create` | +| `oauth2_app:delete` | +| `oauth2_app:read` | +| `oauth2_app:update` | +| `oauth2_app_code_token:*` | +| `oauth2_app_code_token:create` | +| `oauth2_app_code_token:delete` | +| `oauth2_app_code_token:read` | +| `oauth2_app_secret:*` | +| `oauth2_app_secret:create` | +| `oauth2_app_secret:delete` | +| `oauth2_app_secret:read` | +| `oauth2_app_secret:update` | +| `organization:*` | +| `organization:create` | +| `organization:delete` | +| `organization:read` | +| `organization:update` | +| `organization_member:*` | +| `organization_member:create` | +| `organization_member:delete` | +| `organization_member:read` | +| `organization_member:update` | +| `prebuilt_workspace:*` | +| `prebuilt_workspace:delete` | +| `prebuilt_workspace:update` | +| `provisioner_daemon:*` | +| `provisioner_daemon:create` | +| `provisioner_daemon:delete` | +| `provisioner_daemon:read` | +| `provisioner_daemon:update` | +| `provisioner_jobs:*` | +| `provisioner_jobs:create` | +| `provisioner_jobs:read` | +| `provisioner_jobs:update` | +| `replicas:*` | +| `replicas:read` | +| `system:*` | +| `system:create` | +| `system:delete` | +| `system:read` | +| `system:update` | +| `tailnet_coordinator:*` | +| `tailnet_coordinator:create` | +| `tailnet_coordinator:delete` | +| `tailnet_coordinator:read` | +| `tailnet_coordinator:update` | +| `template:*` | +| `template:create` | +| `template:delete` | +| `template:read` | +| `template:update` | +| `template:use` | +| `template:view_insights` | +| `usage_event:*` | +| `usage_event:create` | +| `usage_event:read` | +| `usage_event:update` | +| `user:*` | +| `user:create` | +| `user:delete` | +| `user:read` | +| `user:read_personal` | +| `user:update` | +| `user:update_personal` | +| `user_secret:*` | +| `user_secret:create` | +| `user_secret:delete` | +| `user_secret:read` | +| `user_secret:update` | +| `webpush_subscription:*` | +| `webpush_subscription:create` | +| `webpush_subscription:delete` | +| `webpush_subscription:read` | +| `workspace:*` | +| `workspace:application_connect` | +| `workspace:create` | +| `workspace:create_agent` | +| `workspace:delete` | +| `workspace:delete_agent` | +| `workspace:read` | +| `workspace:ssh` | +| `workspace:start` | +| `workspace:stop` | +| `workspace:update` | +| `workspace_agent_devcontainers:*` | +| `workspace_agent_devcontainers:create` | +| `workspace_agent_resource_monitor:*` | +| `workspace_agent_resource_monitor:create` | +| `workspace_agent_resource_monitor:read` | +| `workspace_agent_resource_monitor:update` | +| `workspace_dormant:*` | +| `workspace_dormant:application_connect` | +| `workspace_dormant:create` | +| `workspace_dormant:create_agent` | +| `workspace_dormant:delete` | +| `workspace_dormant:delete_agent` | +| `workspace_dormant:read` | +| `workspace_dormant:ssh` | +| `workspace_dormant:start` | +| `workspace_dormant:stop` | +| `workspace_dormant:update` | +| `workspace_proxy:*` | +| `workspace_proxy:create` | +| `workspace_proxy:delete` | +| `workspace_proxy:read` | +| `workspace_proxy:update` | ## codersdk.AddLicenseRequest diff --git a/scripts/apikeyscopesgen/main.go b/scripts/apikeyscopesgen/main.go index 988c4cb2f0e10..b2c74c72c0adf 100644 --- a/scripts/apikeyscopesgen/main.go +++ b/scripts/apikeyscopesgen/main.go @@ -25,8 +25,8 @@ func main() { } func generate() ([]byte, error) { - names := rbac.ExternalScopeNames() - slices.Sort(names) + allNames := collectAllScopeNames() + publicNames := rbac.ExternalScopeNames() var b bytes.Buffer if _, err := b.WriteString("// Code generated by scripts/apikeyscopesgen. DO NOT EDIT.\n"); err != nil { @@ -61,13 +61,9 @@ func generate() ([]byte, error) { if _, err := b.WriteString("\tAPIKeyScopeApplicationConnect APIKeyScope = \"application_connect\"\n"); err != nil { return nil, err } - for _, n := range names { - res, act := splitRA(n) - if act == policy.WildcardSymbol { - act = "All" - } - constName := fmt.Sprintf("APIKeyScope%s%s", pascal(res), pascal(act)) - if _, err := fmt.Fprintf(&b, "\t%s APIKeyScope = \"%s\"\n", constName, n); err != nil { + for _, name := range allNames { + constName := constNameForScope(name) + if _, err := fmt.Fprintf(&b, "\t%s APIKeyScope = \"%s\"\n", constName, name); err != nil { return nil, err } } @@ -82,12 +78,8 @@ func generate() ([]byte, error) { if _, err := b.WriteString("var PublicAPIKeyScopes = []APIKeyScope{\n"); err != nil { return nil, err } - for _, n := range names { - res, act := splitRA(n) - if act == policy.WildcardSymbol { - act = "All" - } - constName := fmt.Sprintf("APIKeyScope%s%s", pascal(res), pascal(act)) + for _, name := range publicNames { + constName := constNameForScope(name) if _, err := fmt.Fprintf(&b, "\t%s,\n", constName); err != nil { return nil, err } @@ -99,6 +91,54 @@ func generate() ([]byte, error) { return format.Source(b.Bytes()) } +func collectAllScopeNames() []string { + seen := make(map[string]struct{}) + var names []string + add := func(name string) { + if name == "" { + return + } + if _, ok := seen[name]; ok { + return + } + seen[name] = struct{}{} + names = append(names, name) + } + + for resource, def := range policy.RBACPermissions { + if resource == policy.WildcardSymbol { + continue + } + add(resource + ":" + policy.WildcardSymbol) + for action := range def.Actions { + add(resource + ":" + string(action)) + } + } + + for _, name := range rbac.CompositeScopeNames() { + add(name) + } + + for _, name := range rbac.BuiltinScopeNames() { + s := string(name) + if !strings.Contains(s, ":") { + continue + } + add(s) + } + + slices.Sort(names) + return names +} + +func constNameForScope(name string) string { + resource, action := splitRA(name) + if action == policy.WildcardSymbol { + action = "All" + } + return fmt.Sprintf("APIKeyScope%s%s", pascal(resource), pascal(action)) +} + func splitRA(name string) (resource string, action string) { parts := strings.SplitN(name, ":", 2) if len(parts) != 2 { diff --git a/scripts/check-scopes/main.go b/scripts/check-scopes/main.go index e79be506ae804..56ba0d4657e31 100644 --- a/scripts/check-scopes/main.go +++ b/scripts/check-scopes/main.go @@ -58,23 +58,37 @@ func main() { os.Exit(1) } -// expectedFromRBAC returns the set of : pairs derived from RBACPermissions. +// expectedFromRBAC returns the set of scope names the DB enum must support. func expectedFromRBAC() map[string]struct{} { want := make(map[string]struct{}) - // Low-level : + add := func(name string) { + if name == "" { + return + } + want[name] = struct{}{} + } + // Low-level : and synthesized :* wildcards for resource, def := range policy.RBACPermissions { if resource == policy.WildcardSymbol { // Ignore wildcard entry; it has no concrete : pairs. continue } + add(resource + ":" + policy.WildcardSymbol) for action := range def.Actions { - key := resource + ":" + string(action) - want[key] = struct{}{} + add(resource + ":" + string(action)) } } // Composite coder:* names for _, n := range rbac.CompositeScopeNames() { - want[n] = struct{}{} + add(n) + } + // Built-in coder-prefixed scopes such as coder:all + for _, n := range rbac.BuiltinScopeNames() { + s := string(n) + if !strings.Contains(s, ":") { + continue + } + add(s) } return want } diff --git a/scripts/generate_api_key_scope_enum/main.go b/scripts/generate_api_key_scope_enum/main.go deleted file mode 100644 index 130dd865334b8..0000000000000 --- a/scripts/generate_api_key_scope_enum/main.go +++ /dev/null @@ -1,32 +0,0 @@ -package main - -import ( - "fmt" - "sort" - - "github.com/coder/coder/v2/coderd/rbac" - "github.com/coder/coder/v2/coderd/rbac/policy" -) - -func main() { - seen := map[string]struct{}{} - var vals []string - for resource, def := range policy.RBACPermissions { - if resource == policy.WildcardSymbol { - continue - } - for action := range def.Actions { - vals = append(vals, fmt.Sprintf("%s:%s", resource, action)) - } - } - // Include composite coder:* scopes as first-class enum values - vals = append(vals, rbac.CompositeScopeNames()...) - sort.Strings(vals) - for _, v := range vals { - if _, ok := seen[v]; ok { - continue - } - seen[v] = struct{}{} - _, _ = fmt.Printf("ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS '%s';\n", v) - } -} diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index e7c612078b6f3..0352ce1c97d9a 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -112,6 +112,10 @@ export interface APIKey { // From codersdk/apikey.go export type APIKeyScope = + | "aibridge_interception:*" + | "aibridge_interception:create" + | "aibridge_interception:read" + | "aibridge_interception:update" | "all" | "api_key:*" | "api_key:create" @@ -119,6 +123,20 @@ export type APIKeyScope = | "api_key:read" | "api_key:update" | "application_connect" + | "assign_org_role:*" + | "assign_org_role:assign" + | "assign_org_role:create" + | "assign_org_role:delete" + | "assign_org_role:read" + | "assign_org_role:unassign" + | "assign_org_role:update" + | "assign_role:*" + | "assign_role:assign" + | "assign_role:read" + | "assign_role:unassign" + | "audit_log:*" + | "audit_log:create" + | "audit_log:read" | "coder:all" | "coder:apikeys.manage_self" | "coder:application_connect" @@ -128,26 +146,156 @@ export type APIKeyScope = | "coder:workspaces.create" | "coder:workspaces.delete" | "coder:workspaces.operate" + | "connection_log:*" + | "connection_log:read" + | "connection_log:update" + | "crypto_key:*" + | "crypto_key:create" + | "crypto_key:delete" + | "crypto_key:read" + | "crypto_key:update" + | "debug_info:*" + | "debug_info:read" + | "deployment_config:*" + | "deployment_config:read" + | "deployment_config:update" + | "deployment_stats:*" + | "deployment_stats:read" | "file:*" | "file:create" | "file:read" + | "group:*" + | "group:create" + | "group:delete" + | "group_member:*" + | "group_member:read" + | "group:read" + | "group:update" + | "idpsync_settings:*" + | "idpsync_settings:read" + | "idpsync_settings:update" + | "inbox_notification:*" + | "inbox_notification:create" + | "inbox_notification:read" + | "inbox_notification:update" + | "license:*" + | "license:create" + | "license:delete" + | "license:read" + | "notification_message:*" + | "notification_message:create" + | "notification_message:delete" + | "notification_message:read" + | "notification_message:update" + | "notification_preference:*" + | "notification_preference:read" + | "notification_preference:update" + | "notification_template:*" + | "notification_template:read" + | "notification_template:update" + | "oauth2_app:*" + | "oauth2_app_code_token:*" + | "oauth2_app_code_token:create" + | "oauth2_app_code_token:delete" + | "oauth2_app_code_token:read" + | "oauth2_app:create" + | "oauth2_app:delete" + | "oauth2_app:read" + | "oauth2_app_secret:*" + | "oauth2_app_secret:create" + | "oauth2_app_secret:delete" + | "oauth2_app_secret:read" + | "oauth2_app_secret:update" + | "oauth2_app:update" + | "organization:*" + | "organization:create" + | "organization:delete" + | "organization_member:*" + | "organization_member:create" + | "organization_member:delete" + | "organization_member:read" + | "organization_member:update" + | "organization:read" + | "organization:update" + | "prebuilt_workspace:*" + | "prebuilt_workspace:delete" + | "prebuilt_workspace:update" + | "provisioner_daemon:*" + | "provisioner_daemon:create" + | "provisioner_daemon:delete" + | "provisioner_daemon:read" + | "provisioner_daemon:update" + | "provisioner_jobs:*" + | "provisioner_jobs:create" + | "provisioner_jobs:read" + | "provisioner_jobs:update" + | "replicas:*" + | "replicas:read" + | "system:*" + | "system:create" + | "system:delete" + | "system:read" + | "system:update" + | "tailnet_coordinator:*" + | "tailnet_coordinator:create" + | "tailnet_coordinator:delete" + | "tailnet_coordinator:read" + | "tailnet_coordinator:update" | "template:*" | "template:create" | "template:delete" | "template:read" | "template:update" | "template:use" + | "template:view_insights" + | "usage_event:*" + | "usage_event:create" + | "usage_event:read" + | "usage_event:update" + | "user:*" + | "user:create" + | "user:delete" + | "user:read" | "user:read_personal" | "user_secret:*" | "user_secret:create" | "user_secret:delete" | "user_secret:read" | "user_secret:update" + | "user:update" | "user:update_personal" + | "webpush_subscription:*" + | "webpush_subscription:create" + | "webpush_subscription:delete" + | "webpush_subscription:read" + | "workspace_agent_devcontainers:*" + | "workspace_agent_devcontainers:create" + | "workspace_agent_resource_monitor:*" + | "workspace_agent_resource_monitor:create" + | "workspace_agent_resource_monitor:read" + | "workspace_agent_resource_monitor:update" | "workspace:*" | "workspace:application_connect" | "workspace:create" + | "workspace:create_agent" | "workspace:delete" + | "workspace:delete_agent" + | "workspace_dormant:*" + | "workspace_dormant:application_connect" + | "workspace_dormant:create" + | "workspace_dormant:create_agent" + | "workspace_dormant:delete" + | "workspace_dormant:delete_agent" + | "workspace_dormant:read" + | "workspace_dormant:ssh" + | "workspace_dormant:start" + | "workspace_dormant:stop" + | "workspace_dormant:update" + | "workspace_proxy:*" + | "workspace_proxy:create" + | "workspace_proxy:delete" + | "workspace_proxy:read" + | "workspace_proxy:update" | "workspace:read" | "workspace:ssh" | "workspace:start" @@ -155,6 +303,10 @@ export type APIKeyScope = | "workspace:update"; export const APIKeyScopes: APIKeyScope[] = [ + "aibridge_interception:*", + "aibridge_interception:create", + "aibridge_interception:read", + "aibridge_interception:update", "all", "api_key:*", "api_key:create", @@ -162,6 +314,20 @@ export const APIKeyScopes: APIKeyScope[] = [ "api_key:read", "api_key:update", "application_connect", + "assign_org_role:*", + "assign_org_role:assign", + "assign_org_role:create", + "assign_org_role:delete", + "assign_org_role:read", + "assign_org_role:unassign", + "assign_org_role:update", + "assign_role:*", + "assign_role:assign", + "assign_role:read", + "assign_role:unassign", + "audit_log:*", + "audit_log:create", + "audit_log:read", "coder:all", "coder:apikeys.manage_self", "coder:application_connect", @@ -171,26 +337,156 @@ export const APIKeyScopes: APIKeyScope[] = [ "coder:workspaces.create", "coder:workspaces.delete", "coder:workspaces.operate", + "connection_log:*", + "connection_log:read", + "connection_log:update", + "crypto_key:*", + "crypto_key:create", + "crypto_key:delete", + "crypto_key:read", + "crypto_key:update", + "debug_info:*", + "debug_info:read", + "deployment_config:*", + "deployment_config:read", + "deployment_config:update", + "deployment_stats:*", + "deployment_stats:read", "file:*", "file:create", "file:read", + "group:*", + "group:create", + "group:delete", + "group_member:*", + "group_member:read", + "group:read", + "group:update", + "idpsync_settings:*", + "idpsync_settings:read", + "idpsync_settings:update", + "inbox_notification:*", + "inbox_notification:create", + "inbox_notification:read", + "inbox_notification:update", + "license:*", + "license:create", + "license:delete", + "license:read", + "notification_message:*", + "notification_message:create", + "notification_message:delete", + "notification_message:read", + "notification_message:update", + "notification_preference:*", + "notification_preference:read", + "notification_preference:update", + "notification_template:*", + "notification_template:read", + "notification_template:update", + "oauth2_app:*", + "oauth2_app_code_token:*", + "oauth2_app_code_token:create", + "oauth2_app_code_token:delete", + "oauth2_app_code_token:read", + "oauth2_app:create", + "oauth2_app:delete", + "oauth2_app:read", + "oauth2_app_secret:*", + "oauth2_app_secret:create", + "oauth2_app_secret:delete", + "oauth2_app_secret:read", + "oauth2_app_secret:update", + "oauth2_app:update", + "organization:*", + "organization:create", + "organization:delete", + "organization_member:*", + "organization_member:create", + "organization_member:delete", + "organization_member:read", + "organization_member:update", + "organization:read", + "organization:update", + "prebuilt_workspace:*", + "prebuilt_workspace:delete", + "prebuilt_workspace:update", + "provisioner_daemon:*", + "provisioner_daemon:create", + "provisioner_daemon:delete", + "provisioner_daemon:read", + "provisioner_daemon:update", + "provisioner_jobs:*", + "provisioner_jobs:create", + "provisioner_jobs:read", + "provisioner_jobs:update", + "replicas:*", + "replicas:read", + "system:*", + "system:create", + "system:delete", + "system:read", + "system:update", + "tailnet_coordinator:*", + "tailnet_coordinator:create", + "tailnet_coordinator:delete", + "tailnet_coordinator:read", + "tailnet_coordinator:update", "template:*", "template:create", "template:delete", "template:read", "template:update", "template:use", + "template:view_insights", + "usage_event:*", + "usage_event:create", + "usage_event:read", + "usage_event:update", + "user:*", + "user:create", + "user:delete", + "user:read", "user:read_personal", "user_secret:*", "user_secret:create", "user_secret:delete", "user_secret:read", "user_secret:update", + "user:update", "user:update_personal", + "webpush_subscription:*", + "webpush_subscription:create", + "webpush_subscription:delete", + "webpush_subscription:read", + "workspace_agent_devcontainers:*", + "workspace_agent_devcontainers:create", + "workspace_agent_resource_monitor:*", + "workspace_agent_resource_monitor:create", + "workspace_agent_resource_monitor:read", + "workspace_agent_resource_monitor:update", "workspace:*", "workspace:application_connect", "workspace:create", + "workspace:create_agent", "workspace:delete", + "workspace:delete_agent", + "workspace_dormant:*", + "workspace_dormant:application_connect", + "workspace_dormant:create", + "workspace_dormant:create_agent", + "workspace_dormant:delete", + "workspace_dormant:delete_agent", + "workspace_dormant:read", + "workspace_dormant:ssh", + "workspace_dormant:start", + "workspace_dormant:stop", + "workspace_dormant:update", + "workspace_proxy:*", + "workspace_proxy:create", + "workspace_proxy:delete", + "workspace_proxy:read", + "workspace_proxy:update", "workspace:read", "workspace:ssh", "workspace:start", From 815e58e872d936616b9110fba86a021f6290cd71 Mon Sep 17 00:00:00 2001 From: Danielle Maywood Date: Mon, 6 Oct 2025 12:21:21 +0100 Subject: [PATCH 058/298] chore: upgrade clistat to v1.0.2 (#20173) clistat@v1.0.2 contains a bug fix for when `cpu.max` cgroup file is missing --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index c375c113f143b..17addc97e8524 100644 --- a/go.mod +++ b/go.mod @@ -462,7 +462,7 @@ require ( sigs.k8s.io/yaml v1.5.0 // indirect ) -require github.com/coder/clistat v1.0.1 +require github.com/coder/clistat v1.0.2 require github.com/SherClockHolmes/webpush-go v1.4.0 diff --git a/go.sum b/go.sum index 03c0c1bc96e8b..2c18dc1f5adf6 100644 --- a/go.sum +++ b/go.sum @@ -919,8 +919,8 @@ github.com/coder/boundary v1.0.1-0.20250925154134-55a44f2a7945 h1:hDUf02kTX8EGR3 github.com/coder/boundary v1.0.1-0.20250925154134-55a44f2a7945/go.mod h1:d1AMFw81rUgrGHuZzWdPNhkY0G8w7pvLNLYF0e3ceC4= github.com/coder/bubbletea v1.2.2-0.20241212190825-007a1cdb2c41 h1:SBN/DA63+ZHwuWwPHPYoCZ/KLAjHv5g4h2MS4f2/MTI= github.com/coder/bubbletea v1.2.2-0.20241212190825-007a1cdb2c41/go.mod h1:I9ULxr64UaOSUv7hcb3nX4kowodJCVS7vt7VVJk/kW4= -github.com/coder/clistat v1.0.1 h1:0ZgnKr1C4Z0ukG1z1+RUa8Z4OoIgWIH2hdxWSXQTubE= -github.com/coder/clistat v1.0.1/go.mod h1:F+gLef+F9chVrleq808RBxdaoq52R4VLopuLdAsh8Y4= +github.com/coder/clistat v1.0.2 h1:9SjtcTVMGfM5LbdhjnkyxO3RCngrLvVu1+NXBs9QUy0= +github.com/coder/clistat v1.0.2/go.mod h1:F+gLef+F9chVrleq808RBxdaoq52R4VLopuLdAsh8Y4= github.com/coder/flog v1.1.0 h1:kbAes1ai8fIS5OeV+QAnKBQE22ty1jRF/mcAwHpLBa4= github.com/coder/flog v1.1.0/go.mod h1:UQlQvrkJBvnRGo69Le8E24Tcl5SJleAAR7gYEHzAmdQ= github.com/coder/glog v1.0.1-0.20220322161911-7365fe7f2cd1/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= From dd99d40ad69df231a1ff241c72c3a3110c7c76cc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Oct 2025 11:57:20 +0000 Subject: [PATCH 059/298] chore: bump github.com/go-playground/validator/v10 from 10.27.0 to 10.28.0 (#20176) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [github.com/go-playground/validator/v10](https://github.com/go-playground/validator) from 10.27.0 to 10.28.0.
Release notes

Sourced from github.com/go-playground/validator/v10's releases.

Release 10.28.0

What's Changed

New Contributors

Full Changelog: https://github.com/go-playground/validator/compare/v10.27.0...v10.28.0

Commits
  • bdc3a7d Add alphaspace validator (#1343)
  • 63594a0 docs: add description for 'port' validator (#1435)
  • 45f3a8e Added https_url tag (#1461)
  • 7a23bca Remove Go version 1.23 from CI workflow
  • 13130d2 Fixed missing keys from returned errors in map validation (#1284)
  • 94e89f0 fix: should panic when define duplicate field param in required_if (#1468)
  • 6905468 Bump golang.org/x/crypto from 0.33.0 to 0.42.0 (#1467)
  • 77ef70e Bump actions/setup-go from 5 to 6 (#1465)
  • 78d05ae Bump golang.org/x/text from 0.22.0 to 0.29.0 (#1464)
  • 34aea1f Bump github.com/gabriel-vasile/mimetype from 1.4.8 to 1.4.10 (#1463)
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/go-playground/validator/v10&package-manager=go_modules&previous-version=10.27.0&new-version=10.28.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 17addc97e8524..655dde3efe426 100644 --- a/go.mod +++ b/go.mod @@ -125,7 +125,7 @@ require ( github.com/go-chi/httprate v0.15.0 github.com/go-jose/go-jose/v4 v4.1.1 github.com/go-logr/logr v1.4.3 - github.com/go-playground/validator/v10 v10.27.0 + github.com/go-playground/validator/v10 v10.28.0 github.com/gofrs/flock v0.12.1 github.com/gohugoio/hugo v0.150.0 github.com/golang-jwt/jwt/v4 v4.5.2 @@ -296,7 +296,7 @@ require ( github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.8 // indirect + github.com/gabriel-vasile/mimetype v1.4.10 // indirect github.com/go-chi/hostrouter v0.3.0 // indirect github.com/go-ini/ini v1.67.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect diff --git a/go.sum b/go.sum index 2c18dc1f5adf6..d441935a07652 100644 --- a/go.sum +++ b/go.sum @@ -1094,8 +1094,8 @@ github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa h1:RDBNVkRviHZtvD github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= -github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= -github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= +github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0= +github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= github.com/gen2brain/beeep v0.11.1 h1:EbSIhrQZFDj1K2fzlMpAYlFOzV8YuNe721A58XcCTYI= github.com/gen2brain/beeep v0.11.1/go.mod h1:jQVvuwnLuwOcdctHn/uyh8horSBNJ8uGb9Cn2W4tvoc= github.com/getkin/kin-openapi v0.133.0 h1:pJdmNohVIJ97r4AUFtEXRXwESr8b0bD721u/Tz6k8PQ= @@ -1163,8 +1163,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4= -github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= +github.com/go-playground/validator/v10 v10.28.0 h1:Q7ibns33JjyW48gHkuFT91qX48KG0ktULL6FgHdG688= +github.com/go-playground/validator/v10 v10.28.0/go.mod h1:GoI6I1SjPBh9p7ykNE/yj3fFYbyDOpwMn5KXd+m2hUU= github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU= github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= From a7c9e623fe7b57145498efd512dfdc809c4ef0b6 Mon Sep 17 00:00:00 2001 From: Sas Swart Date: Mon, 6 Oct 2025 13:58:37 +0200 Subject: [PATCH 060/298] chore: fix api param name (#20172) In https://github.com/coder/coder/pull/20137, we added a new flag to `coder provisioner jobs list`, namely `--initiator`. To make some follow-up worth it, I need to rename an API param used in the process before it becomes part of our released and tagged API. Instead of only accepting UUIDs, we accept an arbitrary string. We still validate it as a UUID now, but we will expand its validation to allow any string and then resolve that string the same way that we resolve the user parameter elsewhere in the API. --- cli/provisionerjobs.go | 10 ++++------ coderd/provisionerjobs.go | 2 +- coderd/provisionerjobs_test.go | 20 ++++++++++---------- codersdk/organizations.go | 14 +++++++------- site/src/api/typesGenerated.ts | 2 +- 5 files changed, 23 insertions(+), 25 deletions(-) diff --git a/cli/provisionerjobs.go b/cli/provisionerjobs.go index 3f441a1758045..ee29476ef09dd 100644 --- a/cli/provisionerjobs.go +++ b/cli/provisionerjobs.go @@ -66,20 +66,18 @@ func (r *RootCmd) provisionerJobsList() *serpent.Command { return xerrors.Errorf("current organization: %w", err) } - var initiatorID *uuid.UUID - if initiator != "" { user, err := client.User(ctx, initiator) if err != nil { return xerrors.Errorf("initiator not found: %s", initiator) } - initiatorID = &user.ID + initiator = user.ID.String() } jobs, err := client.OrganizationProvisionerJobs(ctx, org.ID, &codersdk.OrganizationProvisionerJobsOptions{ - Status: slice.StringEnums[codersdk.ProvisionerJobStatus](status), - Limit: int(limit), - InitiatorID: initiatorID, + Status: slice.StringEnums[codersdk.ProvisionerJobStatus](status), + Limit: int(limit), + Initiator: initiator, }) if err != nil { return xerrors.Errorf("list provisioner jobs: %w", err) diff --git a/coderd/provisionerjobs.go b/coderd/provisionerjobs.go index 4ba923dae2477..68f2207f2f90c 100644 --- a/coderd/provisionerjobs.go +++ b/coderd/provisionerjobs.go @@ -111,7 +111,7 @@ func (api *API) handleAuthAndFetchProvisionerJobs(rw http.ResponseWriter, r *htt ids = p.UUIDs(qp, nil, "ids") } tags := p.JSONStringMap(qp, database.StringMap{}, "tags") - initiatorID := p.UUID(qp, uuid.Nil, "initiator_id") + initiatorID := p.UUID(qp, uuid.Nil, "initiator") p.ErrorExcessParams(qp) if len(p.Errors) > 0 { httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ diff --git a/coderd/provisionerjobs_test.go b/coderd/provisionerjobs_test.go index 829c72aa4dbd0..91096e3b64905 100644 --- a/coderd/provisionerjobs_test.go +++ b/coderd/provisionerjobs_test.go @@ -173,7 +173,7 @@ func TestProvisionerJobs(t *testing.T) { ctx := testutil.Context(t, testutil.WaitMedium) jobs, err := templateAdminClient.OrganizationProvisionerJobs(ctx, owner.OrganizationID, &codersdk.OrganizationProvisionerJobsOptions{ - InitiatorID: &member.ID, + Initiator: member.ID.String(), }) require.NoError(t, err) require.GreaterOrEqual(t, len(jobs), 1) @@ -186,8 +186,8 @@ func TestProvisionerJobs(t *testing.T) { // Test filtering by initiator ID combined with status filter jobs, err := templateAdminClient.OrganizationProvisionerJobs(ctx, owner.OrganizationID, &codersdk.OrganizationProvisionerJobsOptions{ - InitiatorID: &owner.UserID, - Status: []codersdk.ProvisionerJobStatus{codersdk.ProvisionerJobSucceeded}, + Initiator: owner.UserID.String(), + Status: []codersdk.ProvisionerJobStatus{codersdk.ProvisionerJobSucceeded}, }) require.NoError(t, err) @@ -204,8 +204,8 @@ func TestProvisionerJobs(t *testing.T) { // Test filtering by initiator ID with limit jobs, err := templateAdminClient.OrganizationProvisionerJobs(ctx, owner.OrganizationID, &codersdk.OrganizationProvisionerJobsOptions{ - InitiatorID: &owner.UserID, - Limit: 1, + Initiator: owner.UserID.String(), + Limit: 1, }) require.NoError(t, err) require.Len(t, jobs, 1) @@ -220,8 +220,8 @@ func TestProvisionerJobs(t *testing.T) { // Test filtering by initiator ID combined with tags jobs, err := templateAdminClient.OrganizationProvisionerJobs(ctx, owner.OrganizationID, &codersdk.OrganizationProvisionerJobsOptions{ - InitiatorID: &member.ID, - Tags: map[string]string{"initiatorTest": "true"}, + Initiator: member.ID.String(), + Tags: map[string]string{"initiatorTest": "true"}, }) require.NoError(t, err) require.Len(t, jobs, 1) @@ -238,7 +238,7 @@ func TestProvisionerJobs(t *testing.T) { // Test with non-existent initiator ID nonExistentID := uuid.New() jobs, err := templateAdminClient.OrganizationProvisionerJobs(ctx, owner.OrganizationID, &codersdk.OrganizationProvisionerJobsOptions{ - InitiatorID: &nonExistentID, + Initiator: nonExistentID.String(), }) require.NoError(t, err) require.Len(t, jobs, 0) @@ -250,7 +250,7 @@ func TestProvisionerJobs(t *testing.T) { // Test with nil initiator ID (should return all jobs) jobs, err := templateAdminClient.OrganizationProvisionerJobs(ctx, owner.OrganizationID, &codersdk.OrganizationProvisionerJobsOptions{ - InitiatorID: nil, + Initiator: "", }) require.NoError(t, err) require.GreaterOrEqual(t, len(jobs), 50) // Should return all jobs (up to default limit) @@ -282,7 +282,7 @@ func TestProvisionerJobs(t *testing.T) { ctx := testutil.Context(t, testutil.WaitMedium) // Member should not be able to access jobs even with initiator filter jobs, err := memberClient.OrganizationProvisionerJobs(ctx, owner.OrganizationID, &codersdk.OrganizationProvisionerJobsOptions{ - InitiatorID: &member.ID, + Initiator: member.ID.String(), }) require.Error(t, err) require.Len(t, jobs, 0) diff --git a/codersdk/organizations.go b/codersdk/organizations.go index 291bb9ac1baf7..823169d385b22 100644 --- a/codersdk/organizations.go +++ b/codersdk/organizations.go @@ -397,11 +397,11 @@ func (c *Client) OrganizationProvisionerDaemons(ctx context.Context, organizatio } type OrganizationProvisionerJobsOptions struct { - Limit int - IDs []uuid.UUID - Status []ProvisionerJobStatus - Tags map[string]string - InitiatorID *uuid.UUID + Limit int + IDs []uuid.UUID + Status []ProvisionerJobStatus + Tags map[string]string + Initiator string } func (c *Client) OrganizationProvisionerJobs(ctx context.Context, organizationID uuid.UUID, opts *OrganizationProvisionerJobsOptions) ([]ProvisionerJob, error) { @@ -423,8 +423,8 @@ func (c *Client) OrganizationProvisionerJobs(ctx context.Context, organizationID } qp.Add("tags", string(tagsRaw)) } - if opts.InitiatorID != nil { - qp.Add("initiator_id", opts.InitiatorID.String()) + if opts.Initiator != "" { + qp.Add("initiator", opts.Initiator) } } diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 0352ce1c97d9a..1e07cf1aa2895 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -2356,7 +2356,7 @@ export interface OrganizationProvisionerJobsOptions { readonly IDs: readonly string[]; readonly Status: readonly ProvisionerJobStatus[]; readonly Tags: Record; - readonly InitiatorID: string | null; + readonly Initiator: string; } // From codersdk/idpsync.go From 5fdc4185c75affca050851fb7ce42201d49a4e28 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Oct 2025 11:58:57 +0000 Subject: [PATCH 061/298] chore: bump github.com/moby/moby from 28.4.0+incompatible to 28.5.0+incompatible (#20177) Bumps [github.com/moby/moby](https://github.com/moby/moby) from 28.4.0+incompatible to 28.5.0+incompatible.
Release notes

Sourced from github.com/moby/moby's releases.

v28.5.0

28.5.0

For a full list of pull requests and changes in this release, refer to the relevant GitHub milestones:

Bug fixes and enhancements

  • Don't print warnings in docker info for broken symlinks in CLI-plugin directories. docker/cli#6476
  • Fix a panic during stats on empty event Actor.ID. docker/cli#6471

Packaging updates

Networking

  • Eliminated harmless warning about deletion of endpoint_count from the data store. moby/moby#51064
  • Fix a bug causing IPAM plugins to not be loaded on Windows. moby/moby#51035

API

  • Deprecate support for kernel memory TCP accounting (KernelMemoryTCP). moby/moby#51067
  • Fix GET containers/{name}/checkpoints returning null instead of empty JSON array when there are no checkpoints. moby/moby#51052

Go SDK

Deprecations

  • Go-SDK: cli/command: deprecate DockerCli.Apply. This method is no longer used and will be removed in the next release if there are no remaining uses. docker/cli#6497
  • Go-SDK: cli/command: deprecate DockerCli.ContentTrustEnabled. This method is no longer used and will be removed in the next release. docker/cli#6495
  • Go-SDK: cli/command: deprecate DockerCli.DefaultVersion. This method is no longer used and will be removed in the next release. docker/cli#6491
  • Go-SDK: cli/command: deprecate ResolveDefaultContext utility. docker/cli#6529
  • Go-SDK: cli/command: deprecate WithContentTrustFromEnv, WithContentTrust options. These options were used internally, and will be removed in the next release.. docker/cli#6489
  • Go-SDK: cli/manifest/store: deprecate IsNotFound(). docker/cli#6514
  • Go-SDK: templates: deprecate NewParse() function. docker/cli#6469

v28.5.0-rc.1

28.5.0-rc.1

For a full list of pull requests and changes in this release, refer to the relevant GitHub milestones:

... (truncated)

Commits
  • cd04830 Merge pull request #51075 from vvoland/51074-28.x
  • e29d6be vendor: github.com/moby/buildkit v0.25.0
  • 9b43690 Merge pull request #51069 from thaJeztah/28.x_backport_docs_rm_deprecated_vir...
  • 4f35725 api: swagger: remove VirtualSize fields for API > v1.43
  • 79f310d Merge pull request #51067 from austinvazquez/cherry-pick-deprecate-kernel-mem...
  • deb4bbb api: deprecate KernelMemoryTCP support
  • 423a7fd Merge pull request #51064 from thaJeztah/28.x_backport_fix_epcnt_warning
  • fbf2fe8 Eliminate warning about endpoint count store delete
  • 252a1eb Merge pull request #51061 from thaJeztah/28.x_backport_rm_email_example
  • 2c15eb6 api/docs: remove email field from example auth
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/moby/moby&package-manager=go_modules&previous-version=28.4.0+incompatible&new-version=28.5.0+incompatible)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 655dde3efe426..584cc4595e339 100644 --- a/go.mod +++ b/go.mod @@ -154,7 +154,7 @@ require ( github.com/mattn/go-isatty v0.0.20 github.com/mitchellh/go-wordwrap v1.0.1 github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c - github.com/moby/moby v28.4.0+incompatible + github.com/moby/moby v28.5.0+incompatible github.com/mocktools/go-smtp-mock/v2 v2.5.0 github.com/muesli/termenv v0.16.0 github.com/natefinch/atomic v1.0.1 diff --git a/go.sum b/go.sum index d441935a07652..cdd3ae49e5e69 100644 --- a/go.sum +++ b/go.sum @@ -1586,8 +1586,8 @@ github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3N github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ= github.com/moby/go-archive v0.1.0/go.mod h1:G9B+YoujNohJmrIYFBpSd54GTUB4lt9S+xVQvsJyFuo= -github.com/moby/moby v28.4.0+incompatible h1:Rhu/o+7EaHGx0MV3KOouThtr3hY33m3aKyA6GDR2QmQ= -github.com/moby/moby v28.4.0+incompatible/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc= +github.com/moby/moby v28.5.0+incompatible h1:eN6ksRE7BojoGW18USJGfyqhx/FWJPLs0jqaTNlfSsM= +github.com/moby/moby v28.5.0+incompatible/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc= github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= From 3f49e283086ae17cecc6d9b0d917e0bc50882ebd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Oct 2025 11:59:26 +0000 Subject: [PATCH 062/298] chore: bump github.com/coreos/go-oidc/v3 from 3.15.0 to 3.16.0 (#20178) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [github.com/coreos/go-oidc/v3](https://github.com/coreos/go-oidc) from 3.15.0 to 3.16.0.
Release notes

Sourced from github.com/coreos/go-oidc/v3's releases.

v3.16.0

What's Changed

New Contributors

Full Changelog: https://github.com/coreos/go-oidc/compare/v3.15.0...v3.16.0

Commits
  • e958473 bump go to 1.24, remove 1.23 support, bump go-jose dependency, remove x/net d...
  • 69b1670 refactor: Remove unused time injection from RemoteKeySet
  • See full diff in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/coreos/go-oidc/v3&package-manager=go_modules&previous-version=3.15.0&new-version=3.16.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 584cc4595e339..f5d8227f9a9c1 100644 --- a/go.mod +++ b/go.mod @@ -104,7 +104,7 @@ require ( github.com/coder/terraform-provider-coder/v2 v2.11.0 github.com/coder/websocket v1.8.13 github.com/coder/wgtunnel v0.1.13-0.20240522110300-ade90dfb2da0 - github.com/coreos/go-oidc/v3 v3.15.0 + github.com/coreos/go-oidc/v3 v3.16.0 github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf github.com/creack/pty v1.1.21 github.com/dave/dst v0.27.2 @@ -123,7 +123,7 @@ require ( github.com/go-chi/chi/v5 v5.2.2 github.com/go-chi/cors v1.2.1 github.com/go-chi/httprate v0.15.0 - github.com/go-jose/go-jose/v4 v4.1.1 + github.com/go-jose/go-jose/v4 v4.1.3 github.com/go-logr/logr v1.4.3 github.com/go-playground/validator/v10 v10.28.0 github.com/gofrs/flock v0.12.1 diff --git a/go.sum b/go.sum index cdd3ae49e5e69..193bb9536325b 100644 --- a/go.sum +++ b/go.sum @@ -970,8 +970,8 @@ github.com/containerd/platforms v1.0.0-rc.1 h1:83KIq4yy1erSRgOVHNk1HYdPvzdJ5CnsW github.com/containerd/platforms v1.0.0-rc.1/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLVRzJGXfNuWCZCllLA4= github.com/coreos/go-iptables v0.6.0 h1:is9qnZMPYjLd8LYqmm/qlE+wwEgJIkTYdhV3rfZo4jk= github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= -github.com/coreos/go-oidc/v3 v3.15.0 h1:R6Oz8Z4bqWR7VFQ+sPSvZPQv4x8M+sJkDO5ojgwlyAg= -github.com/coreos/go-oidc/v3 v3.15.0/go.mod h1:HaZ3szPaZ0e4r6ebqvsLWlk2Tn+aejfmrfah6hnSYEU= +github.com/coreos/go-oidc/v3 v3.16.0 h1:qRQUCFstKpXwmEjDQTIbyY/5jF00+asXzSkmkoa/mow= +github.com/coreos/go-oidc/v3 v3.16.0/go.mod h1:wqPbKFrVnE90vty060SB40FCJ8fTHTxSwyXJqZH+sI8= github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU= github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= @@ -1130,8 +1130,8 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= -github.com/go-jose/go-jose/v4 v4.1.1 h1:JYhSgy4mXXzAdF3nUx3ygx347LRXJRrpgyU3adRmkAI= -github.com/go-jose/go-jose/v4 v4.1.1/go.mod h1:BdsZGqgdO3b6tTc6LSE56wcDbMMLuPsw5d4ZD5f94kA= +github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs= +github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-json-experiment/json v0.0.0-20250725192818-e39067aee2d2 h1:iizUGZ9pEquQS5jTGkh4AqeeHCMbfbjeb0zMt0aEFzs= github.com/go-json-experiment/json v0.0.0-20250725192818-e39067aee2d2/go.mod h1:TiCD2a1pcmjd7YnhGH0f/zKNcCD06B029pHhzV23c2M= github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= From 840afb225b8050eae32a1eca8910f356e203a8e9 Mon Sep 17 00:00:00 2001 From: Bruno Quaresma Date: Mon, 6 Oct 2025 10:23:53 -0300 Subject: [PATCH 063/298] feat: select template version for tasks (#20146) Allows users with `updateTemplates` permission to select a specific template version when creating a task. One of the biggest changes was moving `TaskPrompt` into `modules/task` and adding a Storybook entry for it. I also moved some stories from `TasksPage` to simplify its story. Screenshot 2025-10-02 at 12 09 06 Closes https://github.com/coder/coder/issues/19986 --- .../tasks/TaskPrompt/TaskPrompt.stories.tsx | 296 ++++++++++++++++++ .../tasks/TaskPrompt}/TaskPrompt.tsx | 112 +++++-- .../src/pages/TasksPage/TasksPage.stories.tsx | 256 +-------------- site/src/pages/TasksPage/TasksPage.tsx | 2 +- 4 files changed, 386 insertions(+), 280 deletions(-) create mode 100644 site/src/modules/tasks/TaskPrompt/TaskPrompt.stories.tsx rename site/src/{pages/TasksPage => modules/tasks/TaskPrompt}/TaskPrompt.tsx (82%) diff --git a/site/src/modules/tasks/TaskPrompt/TaskPrompt.stories.tsx b/site/src/modules/tasks/TaskPrompt/TaskPrompt.stories.tsx new file mode 100644 index 0000000000000..1710bb3c347f4 --- /dev/null +++ b/site/src/modules/tasks/TaskPrompt/TaskPrompt.stories.tsx @@ -0,0 +1,296 @@ +import { + MockAIPromptPresets, + MockNewTaskData, + MockPresets, + MockTask, + MockTasks, + MockTemplate, + MockTemplateVersion, + MockTemplateVersionExternalAuthGithub, + MockTemplateVersionExternalAuthGithubAuthenticated, + MockUserOwner, + mockApiError, +} from "testHelpers/entities"; +import { withAuthProvider, withGlobalSnackbar } from "testHelpers/storybook"; +import type { Meta, StoryObj } from "@storybook/react-vite"; +import { API } from "api/api"; +import { expect, spyOn, userEvent, waitFor, within } from "storybook/test"; +import type TasksPage from "../../../pages/TasksPage/TasksPage"; +import { TaskPrompt } from "./TaskPrompt"; + +const meta: Meta = { + title: "modules/tasks/TaskPrompt", + component: TaskPrompt, + decorators: [withAuthProvider], + parameters: { + user: MockUserOwner, + permissions: { + updateTemplates: true, + }, + }, + beforeEach: () => { + spyOn(API, "getTemplateVersionExternalAuth").mockResolvedValue([]); + spyOn(API, "getTemplates").mockResolvedValue([ + MockTemplate, + { + ...MockTemplate, + id: "test-template-2", + name: "template 2", + display_name: "Template 2", + }, + ]); + spyOn(API, "getTemplateVersions").mockResolvedValue([ + { + ...MockTemplateVersion, + name: "v1.0.0", + }, + ]); + spyOn(API, "getTemplateVersionPresets").mockResolvedValue(null); + }, + args: { + templates: [MockTemplate], + }, +}; + +export default meta; +type Story = StoryObj; + +export const LoadingTemplates: Story = { + args: { + templates: undefined, + }, +}; + +export const EmptyTemplates: Story = { + args: { + templates: [], + }, +}; + +export const WithPresets: Story = { + beforeEach: () => { + spyOn(API, "getTemplateVersionPresets").mockResolvedValue(MockPresets); + }, +}; + +export const ReadOnlyPresetPrompt: Story = { + beforeEach: () => { + spyOn(API, "getTemplateVersionPresets").mockResolvedValue( + MockAIPromptPresets, + ); + }, +}; + +export const OnSuccess: Story = { + decorators: [withGlobalSnackbar], + parameters: { + permissions: { + updateTemplates: false, + }, + }, + beforeEach: () => { + const activeVersionId = `${MockTemplate.active_version_id}-latest`; + spyOn(API, "getTemplate").mockResolvedValue({ + ...MockTemplate, + active_version_id: activeVersionId, + }); + spyOn(API.experimental, "createTask").mockResolvedValue(MockTask); + }, + play: async ({ canvasElement, step }) => { + const canvas = within(canvasElement); + + await step("Run task", async () => { + const prompt = await canvas.findByLabelText(/prompt/i); + await userEvent.type(prompt, MockNewTaskData.prompt); + const submitButton = canvas.getByRole("button", { name: /run task/i }); + await waitFor(() => expect(submitButton).toBeEnabled()); + await userEvent.click(submitButton); + }); + + await step("Uses latest template version", () => { + expect(API.experimental.createTask).toHaveBeenCalledWith( + MockUserOwner.id, + { + input: MockNewTaskData.prompt, + template_version_id: `${MockTemplate.active_version_id}-latest`, + template_version_preset_id: undefined, + }, + ); + }); + + await step("Displays success message", async () => { + const body = within(canvasElement.ownerDocument.body); + const successMessage = await body.findByText(/task created/i); + expect(successMessage).toBeInTheDocument(); + }); + }, +}; + +export const SelectTemplateVersion: Story = { + decorators: [withGlobalSnackbar], + beforeEach: () => { + spyOn(API, "getTemplateVersions").mockResolvedValue([ + { + ...MockTemplateVersion, + id: "test-template-version-2", + name: "v2.0.0", + }, + { + ...MockTemplateVersion, + name: "v1.0.0", + }, + ]); + spyOn(API.experimental, "createTask").mockResolvedValue(MockTask); + }, + play: async ({ canvasElement, step }) => { + const canvas = within(canvasElement); + + await step("Fill prompt", async () => { + const prompt = await canvas.findByLabelText(/prompt/i); + await userEvent.type(prompt, MockNewTaskData.prompt); + }); + + await step("Select version", async () => { + const body = within(canvasElement.ownerDocument.body); + const versionSelect = await canvas.findByLabelText(/template version/i); + await userEvent.click(versionSelect); + const versionOption = await body.findByRole("option", { + name: /v2.0.0/i, + }); + await userEvent.click(versionOption); + }); + + await step("Submit form", async () => { + const submitButton = canvas.getByRole("button", { name: /run task/i }); + await waitFor(() => expect(submitButton).toBeEnabled()); + await userEvent.click(submitButton); + }); + + await step("Uses selected version", () => { + expect(API.experimental.createTask).toHaveBeenCalledWith( + MockUserOwner.id, + { + input: MockNewTaskData.prompt, + template_version_id: "test-template-version-2", + template_version_preset_id: undefined, + }, + ); + }); + + await step("Displays success message", async () => { + const body = within(canvasElement.ownerDocument.body); + const successMessage = await body.findByText(/task created/i); + expect(successMessage).toBeInTheDocument(); + }); + }, +}; + +export const OnError: Story = { + decorators: [withGlobalSnackbar], + beforeEach: () => { + spyOn(API, "getTemplates").mockResolvedValue([MockTemplate]); + spyOn(API, "getTemplate").mockResolvedValue(MockTemplate); + spyOn(API.experimental, "getTasks").mockResolvedValue(MockTasks); + spyOn(API.experimental, "createTask").mockRejectedValue( + mockApiError({ + message: "Failed to create task", + detail: "You don't have permission to create tasks.", + }), + ); + }, + play: async ({ canvasElement, step }) => { + const canvas = within(canvasElement); + + await step("Run task", async () => { + const prompt = await canvas.findByLabelText(/prompt/i); + await userEvent.type(prompt, "Create a new task"); + const submitButton = canvas.getByRole("button", { name: /run task/i }); + await waitFor(() => expect(submitButton).toBeEnabled()); + await userEvent.click(submitButton); + }); + + await step("Verify error", async () => { + await canvas.findByText(/failed to create task/i); + }); + }, +}; + +export const AuthenticatedExternalAuth: Story = { + beforeEach: () => { + spyOn(API.experimental, "getTasks") + .mockResolvedValueOnce(MockTasks) + .mockResolvedValue([MockNewTaskData, ...MockTasks]); + spyOn(API.experimental, "createTask").mockResolvedValue(MockTask); + spyOn(API, "getTemplateVersionExternalAuth").mockResolvedValue([ + MockTemplateVersionExternalAuthGithubAuthenticated, + ]); + }, + play: async ({ canvasElement, step }) => { + const canvas = within(canvasElement); + + await step("Does not render external auth", async () => { + expect( + canvas.queryByText(/external authentication/), + ).not.toBeInTheDocument(); + }); + }, + parameters: { + chromatic: { + disableSnapshot: true, + }, + }, +}; + +export const MissingExternalAuth: Story = { + beforeEach: () => { + spyOn(API.experimental, "getTasks") + .mockResolvedValueOnce(MockTasks) + .mockResolvedValue([MockNewTaskData, ...MockTasks]); + spyOn(API.experimental, "createTask").mockResolvedValue(MockTask); + spyOn(API, "getTemplateVersionExternalAuth").mockResolvedValue([ + MockTemplateVersionExternalAuthGithub, + ]); + }, + play: async ({ canvasElement, step }) => { + const canvas = within(canvasElement); + + await step("Submit is disabled", async () => { + const prompt = await canvas.findByLabelText(/prompt/i); + await userEvent.type(prompt, MockNewTaskData.prompt); + const submitButton = canvas.getByRole("button", { name: /run task/i }); + expect(submitButton).toBeDisabled(); + }); + + await step("Renders external authentication", async () => { + await canvas.findByRole("button", { name: /connect to github/i }); + }); + }, +}; + +export const ExternalAuthError: Story = { + beforeEach: () => { + spyOn(API.experimental, "getTasks") + .mockResolvedValueOnce(MockTasks) + .mockResolvedValue([MockNewTaskData, ...MockTasks]); + spyOn(API.experimental, "createTask").mockResolvedValue(MockTask); + spyOn(API, "getTemplateVersionExternalAuth").mockRejectedValue( + mockApiError({ + message: "Failed to load external auth", + }), + ); + }, + play: async ({ canvasElement, step }) => { + const canvas = within(canvasElement); + + await step("Submit is disabled", async () => { + const prompt = await canvas.findByLabelText(/prompt/i); + await userEvent.type(prompt, MockNewTaskData.prompt); + const submitButton = canvas.getByRole("button", { name: /run task/i }); + expect(submitButton).toBeDisabled(); + }); + + await step("Renders error", async () => { + await canvas.findByText(/failed to load external auth/i); + }); + }, +}; diff --git a/site/src/pages/TasksPage/TaskPrompt.tsx b/site/src/modules/tasks/TaskPrompt/TaskPrompt.tsx similarity index 82% rename from site/src/pages/TasksPage/TaskPrompt.tsx rename to site/src/modules/tasks/TaskPrompt/TaskPrompt.tsx index c08f0ee8a7775..2f399ac3e4d87 100644 --- a/site/src/pages/TasksPage/TaskPrompt.tsx +++ b/site/src/modules/tasks/TaskPrompt/TaskPrompt.tsx @@ -1,7 +1,10 @@ import type { SelectTriggerProps } from "@radix-ui/react-select"; import { API } from "api/api"; import { getErrorDetail, getErrorMessage } from "api/errors"; -import { templateVersionPresets } from "api/queries/templates"; +import { + templateVersionPresets, + templateVersions, +} from "api/queries/templates"; import type { Preset, Task, @@ -135,12 +138,14 @@ type CreateTaskFormProps = { }; const CreateTaskForm: FC = ({ templates, onSuccess }) => { - const { user } = useAuthenticated(); + const { user, permissions } = useAuthenticated(); const queryClient = useQueryClient(); + const [prompt, setPrompt] = useState(""); + + // Template const [selectedTemplateId, setSelectedTemplateId] = useState( templates[0].id, ); - const [selectedPresetId, setSelectedPresetId] = useState(); const selectedTemplate = templates.find( (t) => t.id === selectedTemplateId, ) as Template; @@ -152,24 +157,38 @@ const CreateTaskForm: FC = ({ templates, onSuccess }) => { isLoadingExternalAuth, } = useExternalAuth(selectedTemplate.active_version_id); - // Fetch presets when template changes - const { data: presets, isLoading: isLoadingPresets } = useQuery( - templateVersionPresets(selectedTemplate.active_version_id), + // Template versions + const [selectedVersionId, setSelectedVersionId] = useState( + selectedTemplate.active_version_id, ); - const defaultPreset = presets?.find((p) => p.Default); + const versionsQuery = useQuery({ + ...templateVersions(selectedTemplate.id), + enabled: permissions.updateTemplates, + }); - // Handle preset selection when data changes + // Presets + const { data: presets, isLoading: isLoadingPresets } = useQuery( + templateVersionPresets(selectedVersionId), + ); + const [selectedPresetId, setSelectedPresetId] = useState(); useEffect(() => { - setSelectedPresetId(defaultPreset?.ID); - }, [defaultPreset?.ID]); - - // Extract AI prompt from selected preset + const defaultPreset = presets?.find((p) => p.Default); + setSelectedPresetId(defaultPreset?.ID ?? presets?.[0]?.ID); + }, [presets]); const selectedPreset = presets?.find((p) => p.ID === selectedPresetId); - const presetAIPrompt = selectedPreset?.Parameters?.find( + + // Read-only prompt if defined in preset + const presetPrompt = selectedPreset?.Parameters?.find( (param) => param.Name === AI_PROMPT_PARAMETER_NAME, )?.Value; - const isPromptReadOnly = !!presetAIPrompt; + const isPromptReadOnly = !!presetPrompt; + useEffect(() => { + if (presetPrompt) { + setPrompt(presetPrompt); + } + }, [presetPrompt]); + // External Auth const missedExternalAuth = externalAuth?.filter( (auth) => !auth.optional && !auth.authenticated, ); @@ -178,13 +197,26 @@ const CreateTaskForm: FC = ({ templates, onSuccess }) => { : true; const createTaskMutation = useMutation({ - mutationFn: async ({ prompt }: CreateTaskMutationFnProps) => - createTaskWithLatestTemplateVersion( + mutationFn: async ({ prompt }: CreateTaskMutationFnProps) => { + // Users with updateTemplates permission can select the version to use. + if (permissions.updateTemplates) { + return API.experimental.createTask(user.id, { + input: prompt, + template_version_id: selectedVersionId, + template_version_preset_id: selectedPresetId, + }); + } + + // For regular users we want to enforce task creation to always use the latest + // active template version, to avoid issues when the active version changes + // between template load and user action. + return createTaskWithLatestTemplateVersion( prompt, user.id, selectedTemplate.id, selectedPresetId, - ), + ); + }, onSuccess: async (task) => { await queryClient.invalidateQueries({ queryKey: ["tasks"] }); onSuccess(task); @@ -194,10 +226,6 @@ const CreateTaskForm: FC = ({ templates, onSuccess }) => { const onSubmit = async (e: React.FormEvent) => { e.preventDefault(); - const form = e.currentTarget; - const formData = new FormData(form); - const prompt = presetAIPrompt || (formData.get("prompt") as string); - try { await createTaskMutation.mutateAsync({ prompt, @@ -225,7 +253,7 @@ const CreateTaskForm: FC = ({ templates, onSuccess }) => { htmlFor="prompt" className={ isPromptReadOnly - ? "text-xs font-medium text-content-primary mb-2 block" + ? "text-xs font-medium text-content-primary block px-3 pt-2" : "sr-only" } > @@ -233,12 +261,13 @@ const CreateTaskForm: FC = ({ templates, onSuccess }) => { setPrompt(e.target.value)} readOnly={isPromptReadOnly} />
-
+
@@ -265,7 +294,34 @@ const CreateTaskForm: FC = ({ templates, onSuccess }) => {
-
+ {versionsQuery.data && ( +
+ + +
+ )} + +
@@ -273,11 +329,12 @@ const CreateTaskForm: FC = ({ templates, onSuccess }) => { ) : ( presets && - presets.length > 0 && ( + presets.length > 0 && + selectedPresetId && ( | --stdin]", - Short: "Send input to a task", + Use: "send [ | --stdin]", + Short: "Send input to a task", + Long: FormatExamples(Example{ + Description: "Send direct input to a task.", + Command: "coder exp task send task1 \"Please also add unit tests\"", + }, Example{ + Description: "Send input from stdin to a task.", + Command: "echo \"Please also add unit tests\" | coder exp task send task1 --stdin", + }), Middleware: serpent.RequireRangeArgs(1, 2), Options: serpent.OptionSet{ { diff --git a/cli/exp_task_status.go b/cli/exp_task_status.go index f0e1e7b865860..a43fd4feedfe2 100644 --- a/cli/exp_task_status.go +++ b/cli/exp_task_status.go @@ -44,7 +44,17 @@ func (r *RootCmd) taskStatus() *serpent.Command { watchIntervalArg time.Duration ) cmd := &serpent.Command{ - Short: "Show the status of a task.", + Short: "Show the status of a task.", + Long: FormatExamples( + Example{ + Description: "Show the status of a given task.", + Command: "coder exp task status task1", + }, + Example{ + Description: "Watch the status of a given task until it completes (idle or stopped).", + Command: "coder exp task status task1 --watch", + }, + ), Use: "status", Aliases: []string{"stat"}, Options: serpent.OptionSet{ diff --git a/docs/ai-coder/cli.md b/docs/ai-coder/cli.md new file mode 100644 index 0000000000000..6d337b458d6a7 --- /dev/null +++ b/docs/ai-coder/cli.md @@ -0,0 +1,230 @@ +# Tasks CLI + +The Coder CLI provides experimental commands for managing tasks programmatically. These are available under `coder exp task`: + +```console +USAGE: + coder exp task + + Experimental task commands. + + Aliases: tasks + +SUBCOMMANDS: + create Create an experimental task + delete Delete experimental tasks + list List experimental tasks + logs Show a task's logs + send Send input to a task + status Show the status of a task. +``` + +## Creating tasks + +```console +USAGE: + coder exp task create [flags] [input] + + Create an experimental task + + - Create a task with direct input: + + $ coder exp task create "Add authentication to the user service" + + - Create a task with stdin input: + + $ echo "Add authentication to the user service" | coder exp task create + + - Create a task with a specific name: + + $ coder exp task create --name task1 "Add authentication to the user service" + + - Create a task from a specific template / preset: + + $ coder exp task create --template backend-dev --preset "My Preset" "Add authentication to the user service" + + - Create a task for another user (requires appropriate permissions): + + $ coder exp task create --owner user@example.com "Add authentication to the user service" + +OPTIONS: + -O, --org string, $CODER_ORGANIZATION + Select which organization (uuid or name) to use. + + --name string + Specify the name of the task. If you do not specify one, a name will be generated for you. + + --owner string (default: me) + Specify the owner of the task. Defaults to the current user. + + --preset string, $CODER_TASK_PRESET_NAME (default: none) + -q, --quiet bool + Only display the created task's ID. + + --stdin bool + Reads from stdin for the task input. + + --template string, $CODER_TASK_TEMPLATE_NAME + --template-version string, $CODER_TASK_TEMPLATE_VERSION +``` + +## Deleting Tasks + +```console +USAGE: + coder exp task delete [flags] [ ...] + + Delete experimental tasks + + Aliases: rm + + - Delete a single task.: + + $ $ coder exp task delete task1 + + - Delete multiple tasks.: + + $ $ coder exp task delete task1 task2 task3 + + - Delete a task without confirmation.: + + $ $ coder exp task delete task4 --yes + +OPTIONS: + -y, --yes bool + Bypass prompts. +``` + +## Listing tasks + +```console +USAGE: + coder exp task list [flags] + + List experimental tasks + + Aliases: ls + + - List tasks for the current user.: + + $ coder exp task list + + - List tasks for a specific user.: + + $ coder exp task list --user someone-else + + - List all tasks you can view.: + + $ coder exp task list --all + + - List all your running tasks.: + + $ coder exp task list --status running + + - As above, but only show IDs.: + + $ coder exp task list --status running --quiet + +OPTIONS: + -a, --all bool (default: false) + List tasks for all users you can view. + + -c, --column [id|organization id|owner id|owner name|name|template id|template name|template display name|template icon|workspace id|workspace agent id|workspace agent lifecycle|workspace agent health|initial prompt|status|state|message|created at|updated at|state changed] (default: name,status,state,state changed,message) + Columns to display in table output. + + -o, --output table|json (default: table) + Output format. + + -q, --quiet bool (default: false) + Only display task IDs. + + --status string + Filter by task status (e.g. running, failed, etc). + + --user string + List tasks for the specified user (username, "me"). +``` + +## Viewing Task Logs + +```console +USAGE: + coder exp task logs [flags] + + Show a task's logs + + - Show logs for a given task.: + + $ coder exp task logs task1 + +OPTIONS: + -c, --column [id|content|type|time] (default: type,content) + Columns to display in table output. + + -o, --output table|json (default: table) + Output format. +``` + +## Sending input to a task + +```console +USAGE: + coder exp task send [flags] [ | --stdin] + + Send input to a task + + - Send direct input to a task.: + + $ coder exp task send task1 "Please also add unit tests" + + - Send input from stdin to a task.: + + $ echo "Please also add unit tests" | coder exp task send task1 --stdin + +OPTIONS: + --stdin bool + Reads the input from stdin. +``` + +## Viewing Task Status + +```console +USAGE: + coder exp task status [flags] + + Show the status of a task. + + Aliases: stat + + - Show the status of a given task.: + + $ coder exp task status task1 + + - Watch the status of a given task until it completes (idle or stopped).: + + $ coder exp task status task1 --watch + +OPTIONS: + -c, --column [state changed|status|healthy|state|message] (default: state changed,status,healthy,state,message) + Columns to display in table output. + + -o, --output table|json (default: table) + Output format. + + --watch bool (default: false) + Watch the task status output. This will stream updates to the terminal until the underlying workspace is stopped. +``` + +> **Note**: The `--watch` flag will automatically exit when the task reaches a terminal state. Watch mode ends when: +> +> - The workspace is stopped +> - The workspace agent becomes unhealthy or is shutting down +> - The task completes (reaches a non-working state like completed, failed, or canceled) + +## Identifying Tasks + +Tasks can be identified in CLI commands using either: + +- **Task Name**: The human-readable name (e.g., `my-task-name`) + > Note: Tasks owned by other users can be identified by their owner and name (e.g., `alice/her-task`). +- **Task ID**: The UUID identifier (e.g., `550e8400-e29b-41d4-a716-446655440000`) diff --git a/docs/ai-coder/tasks.md b/docs/ai-coder/tasks.md index 78b9a9adbcbf0..878c542350472 100644 --- a/docs/ai-coder/tasks.md +++ b/docs/ai-coder/tasks.md @@ -63,7 +63,7 @@ data "coder_parameter" "setup_script" { # The Claude Code module does the automatic task reporting # Other agent modules: https://registry.coder.com/modules?search=agent -# Or use a custom agent: +# Or use a custom agent: module "claude-code" { source = "registry.coder.com/coder/claude-code/coder" version = "3.0.1" @@ -128,6 +128,10 @@ Coder can automatically generate a name your tasks if you set the `ANTHROPIC_API If you tried Tasks and decided you don't want to use it, you can hide the Tasks tab by starting `coder server` with the `CODER_HIDE_AI_TASKS=true` environment variable or the `--hide-ai-tasks` flag. +## Command Line Interface + +See [Tasks CLI](./cli.md). + ## Next Steps From 6c5b741bed0d70b4be3a41b66cf00f0e0e91ac10 Mon Sep 17 00:00:00 2001 From: david-fraley <67079030+david-fraley@users.noreply.github.com> Date: Wed, 8 Oct 2025 10:32:21 -0500 Subject: [PATCH 087/298] docs: list tasks CLI docs in manifest.json (#20220) --- docs/manifest.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/manifest.json b/docs/manifest.json index 0cfb2a446fdca..df054c789ec25 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -914,6 +914,13 @@ "path": "./ai-coder/ai-bridge.md", "icon_path": "./images/icons/api.svg", "state": ["premium", "early access"] + }, + { + "title": "Tasks CLI", + "description": "Coder CLI for managing tasks programmatically", + "path": "./ai-coder/cli.md", + "icon_path": "./images/icons/api.svg", + "state": ["beta"] } ] }, From 79736154db612b649579c541677f8d0bf76d9d41 Mon Sep 17 00:00:00 2001 From: Jiachen Jiang Date: Wed, 8 Oct 2025 09:14:14 -0700 Subject: [PATCH 088/298] docs: add documentation for upcoming Agent Boundary feature (#20099) ## PR Description tbd @jcjiang See a preview at: https://coder.com/docs/@boundaries-docs/ai-coder/agent-boundary --------- Co-authored-by: David Fraley Co-authored-by: david-fraley <67079030+david-fraley@users.noreply.github.com> --- docs/ai-coder/agent-boundary.md | 121 ++++++++++++++++++++++ docs/ai-coder/index.md | 10 +- docs/ai-coder/security.md | 12 +-- docs/images/guides/ai-agents/boundary.png | Bin 0 -> 485326 bytes docs/manifest.json | 6 ++ 5 files changed, 139 insertions(+), 10 deletions(-) create mode 100644 docs/ai-coder/agent-boundary.md create mode 100644 docs/images/guides/ai-agents/boundary.png diff --git a/docs/ai-coder/agent-boundary.md b/docs/ai-coder/agent-boundary.md new file mode 100644 index 0000000000000..e7605d1481bea --- /dev/null +++ b/docs/ai-coder/agent-boundary.md @@ -0,0 +1,121 @@ +# Agent Boundary + +Agent Boundaries are process-level firewalls that restrict and audit what autonomous programs, such as AI agents, can access and use. + +![Screenshot of Agent Boundaries blocking a process](../images/guides/ai-agents/boundary.png)Example of Agent Boundaries blocking a process. + +The easiest way to use Agent Boundaries is through existing Coder modules, such as the [Claude Code module](https://registry.coder.com/modules/coder/claude-code). It can also be ran directly in the terminal by installing the [CLI](https://github.com/coder/boundary). + +> [!NOTE] +> The Coder Boundary CLI is free and open source. Integrations with the core product, such as with modules offering stronger isolation, are available to Coder Premium customers. + +## Supported Agents + +Boundary supports the securing of any terminal-based agent, including your own custom agents. + +## Features + +Boundaries extend Coder's trusted workspaces with a defense-in-depth model that detects and prevents destructive actions without reducing productivity by slowing down workflows or blocking automation. They offer the following features: + +- _Policy-driven access controls_: limit what an agent can access (repos, registries, APIs, files, commands) +- _Network policy enforcement_: block domains, subnets, or HTTP verbs to prevent exfiltration +- _Audit-ready_: centralize logs, exportable for compliance, with full visibility into agent actions + +## Getting Started with Boundary + +For Early Access, users can use Agent Boundaries through its [open source CLI](https://github.com/coder/boundary), which can be run to wrap any process or invoked through rules in a YAML file. + +### Wrap the agent process with the Boundary CLI + +Users can also run Boundary directly in your workspace and configure it per template or per script. While free tier users won't get centralized policy management or the deeper, "strong isolation," they can still enforce per workspace network rules and log decisions locally. + +1. Install the [binary](https://github.com/coder/boundary) into the workspace image or at start-up. You can do so with the following command: + + ```hcl + curl -fsSL https://raw.githubusercontent.com/coder/boundary/main/install.sh | bash + ``` + +1. Use the included `Makefile` to build your project. Here are a few example commands: + + ```hcl + make build # Build for current platform + make build-all # Build for all platforms + make test # Run tests + make test-coverage # Run tests with coverage + make clean # Clean build artifacts + make fmt # Format code + make lint # Lint code + ``` + +From here, there are two ways to integrate the open source Boundary CLI into a workspace. + +#### Wrap a command inline with flags + +1. Wrap the tool you want to guard. Below are some examples of usage: + + ```hcl + # Allow only requests to github.com + boundary --allow "github.com" -- curl https://github.com + + # Allow full access to GitHub issues API, but only GET/HEAD elsewhere on GitHub + boundary \ + --allow "github.com/api/issues/*" \ + --allow "GET,HEAD github.com" \ + -- npm install + + # Default deny-all: everything is blocked unless explicitly allowed + boundary -- curl https://example.com + ``` + + Additional information, such as Allow Rules, can be found in the [repository README](https://github.com/coder/boundary). + +#### Use a config file (YAML) to set rules + +Another option is to define rules in a YAML file, which only needs to be invoked once as opposed to through flags with each command. + +1. Create a YAML file to store rules that will be applied to all `boundary` commands run in the Workspace. In this example, we call it `boundary.yaml`. + A config example can be seen below: + + ```hcl + allow: + + - domain: [github.com](http://github.com) + + path: /api/issues/* + + - domain: [github.com](http://github.com) + + methods: [GET, HEAD] + ``` + +1. Run a `boundary` command. For example: + + ```hcl + boundary run --config ./boundary.yaml -- claude + ``` + + You will notice that the rules are automatically applied without any need for additional customization. + +### Unprivileged vs. Privileged Mode + +There are two approaches you can take to secure your agentic workflows with Agent Boundary. + +#### Unprivileged Mode + +In this case, a specific agent process or tool (for example, Claude Code or a CLI agent) runs inside of a constrained sandbox. This is the default mode in which Boundary will operate in and does not require root access. + +Agents are prevented from reaching restricted domains or exfiltrating data, without blocking the rest of the dev's environment. + +This is the fastest way to add real guardrails, but a determined user could still operate a tool outside of Boundary restrictions because the broader environment allows it. This mode relies on tools respecting certain settings, like HTTP proxies, and can lead to silent failures if a tool bypasses them. + +#### Privileged Mode + +In this case, boundaries are enforced at the level of the environment that the agent lives in. These are workspace- or session-level controls, including how the developer connects to it. + +Currently, this must be turned on with a flag and ran with higher-level permissions such as root access or `CapNetAdmin`. + +In addition to process-level egress rules, privileged mode locks down all pathways that could bypass policy, such as restricting or disabling SSH tunnels or parallel unbound IDEs. This delivers deterministic, policy-as-code enforcement and offers the highest assurance for regulated environments, but results in slightly more friction for mixed human-and-agent workflows. + +### Opting out of Boundary + +If you tried Boundary through a Coder module and decided you don't want to use it, you can turn it off by setting the flag to `boundary_enabled=false`. diff --git a/docs/ai-coder/index.md b/docs/ai-coder/index.md index d14caa35c33ab..eb1fe33d7f24d 100644 --- a/docs/ai-coder/index.md +++ b/docs/ai-coder/index.md @@ -16,4 +16,12 @@ In cases where the IDE is secondary, such as prototyping or long-running backgro ![Coder Tasks UI](../images/guides/ai-agents/tasks-ui.png) -[Learn more about Coder Tasks](./tasks.md) to how to get started and best practices. +[Learn more about Coder Tasks](./tasks.md) for best practices and how to get started. + +## Secure Your Workflows with Agent Boundaries (Beta) + +AI agents can be powerful teammates, but must be treated as untrusted and unpredictable interns as opposed to tools. Without the right controls, they can go rogue. + +[Agent Boundaries](./agent-boundary.md) is a new tool that offers process-level safeguards that detect and prevent destructive actions. Unlike traditional mitigation methods like firewalls, service meshes, and RBAC systems, Agent Boundaries is an agent-aware, centralized control point that can either be embedded in the same secure Coder Workspaces that enterprises already trust, or used through an open source CLI. + +To learn more about features, implementation details, and how to get started, check out the [Agent Boundary documentation](./agent-boundary.md). diff --git a/docs/ai-coder/security.md b/docs/ai-coder/security.md index 8d1e07ae1d329..86a252b8c4f2e 100644 --- a/docs/ai-coder/security.md +++ b/docs/ai-coder/security.md @@ -19,16 +19,10 @@ not access or upload sensitive information. Many agents require API keys to access external services. It is recommended to create a separate API key for your agent with the minimum permissions required. -This will likely involve editing your template for Agents to set different scopes or tokens -from the standard one. +This will likely involve editing your template for Agents to set different scopes or tokens from the standard one. Additional guidance and tooling is coming in future releases of Coder. -## Set Up Agent Boundaries (Premium) +## Set Up Agent Boundaries -Agent Boundaries add an additional layer and isolation of security between the -agent and the rest of the environment inside of your Coder workspace, allowing -humans to have more privileges and access compared to agents inside the same -workspace. - -- [Contact us for more information](https://coder.com/contact) and for early access to agent boundaries +Agent Boundaries are process-level "agent firewalls" that lets you restrict and audit what AI agents can access within Coder workspaces. To learn more about this feature, see [Agent Boundary](./agent-boundary.md). diff --git a/docs/images/guides/ai-agents/boundary.png b/docs/images/guides/ai-agents/boundary.png new file mode 100644 index 0000000000000000000000000000000000000000..34f8d14a6b6428c1e863758257143746e531babc GIT binary patch literal 485326 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

_#0Ry{GMGN0`g#COVr!XF9un7ZfYWIIJv)a%>47 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�dwKok0lb8{|}bn3wnZ7oBuA%TV>F-zxJgJ)?lW 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

9#E3*?fiK};~SLk+w2V`7SM?HVJjWCt> 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 UGc0{QBN>3e)78&qol`;+0QFZE)c^nh literal 0 HcmV?d00001 diff --git a/docs/manifest.json b/docs/manifest.json index df054c789ec25..023479cad22bb 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -908,6 +908,12 @@ "path": "./ai-coder/mcp-server.md", "state": ["beta"] }, + { + "title": "Agent Boundaries", + "description": "Understanding Agent Boundaries in Coder Tasks", + "path": "./ai-coder/agent-boundary.md", + "state": ["beta"] + }, { "title": "AI Bridge", "description": "Centralized LLM and MCP proxy for platform teams", From 55be304e532def2024bb0fd1654ec06b535f4716 Mon Sep 17 00:00:00 2001 From: Bruno Quaresma Date: Wed, 8 Oct 2025 13:39:47 -0300 Subject: [PATCH 089/298] chore: replace remaining MUI buttons (#20200) --- biome.jsonc | 1 + site/src/@types/mui.d.ts | 10 -- .../InputGroup/InputGroup.stories.tsx | 6 +- .../DeploymentBanner/DeploymentBannerView.tsx | 6 +- site/src/modules/resources/Resources.tsx | 5 +- .../UpdateBuildParametersDialog.tsx | 11 +- .../WorkspaceTiming/WorkspaceTimings.tsx | 4 +- .../CreateTemplatePage/BuildLogsDrawer.tsx | 6 +- .../LicensesSettingsPage/LicenseCard.tsx | 13 +-- .../LicensesSettingsPageView.tsx | 14 +-- .../NotificationsPage/NotificationEvents.tsx | 36 +++--- .../OAuth2AppsSettingsPageView.tsx | 13 +-- .../ExternalAuthPage/ExternalAuthPage.tsx | 3 +- site/src/pages/GroupsPage/GroupPage.tsx | 28 ++--- site/src/pages/HealthPage/DERPPage.tsx | 15 +-- site/src/pages/LoginPage/OAuthSignInForm.tsx | 61 +++++----- .../CustomRolesPage/CustomRolesPageView.tsx | 26 ++--- .../ResetPasswordPage/ChangePasswordPage.tsx | 13 +-- site/src/pages/SetupPage/SetupPageView.tsx | 18 +-- .../pages/TemplatePage/TemplatePageHeader.tsx | 15 +-- site/src/theme/constants.ts | 2 - site/src/theme/light/mui.ts | 61 ---------- site/src/theme/mui.ts | 108 ------------------ 23 files changed, 129 insertions(+), 346 deletions(-) diff --git a/biome.jsonc b/biome.jsonc index daa99a8b90ce4..d1258a77d65ae 100644 --- a/biome.jsonc +++ b/biome.jsonc @@ -51,6 +51,7 @@ "@mui/material/Popover": "Use components/Popover/Popover instead.", "@mui/material/Typography": "Use native HTML elements instead. Eg: ,

,

, etc.", "@mui/material/Box": "Use a
instead.", + "@mui/material/Button": "Use a components/Button/Button instead.", "@mui/material/styles": "Import from @emotion/react instead.", "lodash": "Use lodash/ instead." } diff --git a/site/src/@types/mui.d.ts b/site/src/@types/mui.d.ts index 49804d33f8971..daad165f7d335 100644 --- a/site/src/@types/mui.d.ts +++ b/site/src/@types/mui.d.ts @@ -13,16 +13,6 @@ declare module "@mui/material/styles" { } } -declare module "@mui/material/Button" { - interface ButtonPropsColorOverrides { - neutral: true; - } - - interface ButtonPropsSizeOverrides { - xlarge: true; - } -} - declare module "@mui/material/Checkbox" { interface CheckboxPropsSizeOverrides { xsmall: true; diff --git a/site/src/components/InputGroup/InputGroup.stories.tsx b/site/src/components/InputGroup/InputGroup.stories.tsx index 77c86e52344ae..728970af93c49 100644 --- a/site/src/components/InputGroup/InputGroup.stories.tsx +++ b/site/src/components/InputGroup/InputGroup.stories.tsx @@ -1,6 +1,6 @@ -import Button from "@mui/material/Button"; import TextField from "@mui/material/TextField"; import type { Meta, StoryObj } from "@storybook/react-vite"; +import { Button } from "components/Button/Button"; import { InputGroup } from "./InputGroup"; const meta: Meta = { @@ -15,7 +15,9 @@ export const Default: Story = { args: { children: ( <> - + ), diff --git a/site/src/modules/dashboard/DeploymentBanner/DeploymentBannerView.tsx b/site/src/modules/dashboard/DeploymentBanner/DeploymentBannerView.tsx index 4f9838e0255da..eccd9ad6b62bb 100644 --- a/site/src/modules/dashboard/DeploymentBanner/DeploymentBannerView.tsx +++ b/site/src/modules/dashboard/DeploymentBanner/DeploymentBannerView.tsx @@ -1,5 +1,4 @@ import { css, type Interpolation, type Theme, useTheme } from "@emotion/react"; -import Button from "@mui/material/Button"; import Link from "@mui/material/Link"; import Tooltip from "@mui/material/Tooltip"; import type { @@ -7,6 +6,7 @@ import type { HealthcheckReport, WorkspaceStatus, } from "api/typesGenerated"; +import { Button } from "components/Button/Button"; import { HelpTooltipTitle } from "components/HelpTooltip/HelpTooltip"; import { JetBrainsIcon } from "components/Icons/JetBrainsIcon"; import { RocketIcon } from "components/Icons/RocketIcon"; @@ -323,9 +323,9 @@ export const DeploymentBannerView: FC = ({ fetchStats(); } }} - variant="text" + variant="subtle" > - + {timeUntilRefresh}s diff --git a/site/src/modules/resources/Resources.tsx b/site/src/modules/resources/Resources.tsx index 094092619347b..13e1bbf835252 100644 --- a/site/src/modules/resources/Resources.tsx +++ b/site/src/modules/resources/Resources.tsx @@ -1,5 +1,5 @@ -import Button from "@mui/material/Button"; import type { WorkspaceAgent, WorkspaceResource } from "api/typesGenerated"; +import { Button } from "components/Button/Button"; import { DropdownArrow } from "components/DropdownArrow/DropdownArrow"; import { Stack } from "components/Stack/Stack"; import { type FC, type JSX, useState } from "react"; @@ -37,8 +37,9 @@ export const Resources: FC = ({ resources, agentRow }) => { {hasHideResources && (
- diff --git a/site/src/modules/workspaces/WorkspaceTiming/WorkspaceTimings.tsx b/site/src/modules/workspaces/WorkspaceTiming/WorkspaceTimings.tsx index 847b531949c00..7614013c9bfbf 100644 --- a/site/src/modules/workspaces/WorkspaceTiming/WorkspaceTimings.tsx +++ b/site/src/modules/workspaces/WorkspaceTiming/WorkspaceTimings.tsx @@ -1,5 +1,4 @@ import type { Interpolation, Theme } from "@emotion/react"; -import Button from "@mui/material/Button"; import Collapse from "@mui/material/Collapse"; import Skeleton from "@mui/material/Skeleton"; import type { @@ -7,6 +6,7 @@ import type { AgentScriptTiming, ProvisionerTiming, } from "api/typesGenerated"; +import { Button } from "components/Button/Button"; import sortBy from "lodash/sortBy"; import uniqBy from "lodash/uniqBy"; import { ChevronDownIcon, ChevronUpIcon } from "lucide-react"; @@ -96,7 +96,7 @@ export const WorkspaceTimings: FC = ({
} > @@ -86,15 +85,14 @@ export const NotificationEvents: FC = ({ - Read the docs + } > diff --git a/site/src/pages/DeploymentSettingsPage/OAuth2AppsSettingsPage/OAuth2AppsSettingsPageView.tsx b/site/src/pages/DeploymentSettingsPage/OAuth2AppsSettingsPage/OAuth2AppsSettingsPageView.tsx index 55ee649353158..4b42f946758b8 100644 --- a/site/src/pages/DeploymentSettingsPage/OAuth2AppsSettingsPage/OAuth2AppsSettingsPageView.tsx +++ b/site/src/pages/DeploymentSettingsPage/OAuth2AppsSettingsPage/OAuth2AppsSettingsPageView.tsx @@ -1,5 +1,4 @@ import { useTheme } from "@emotion/react"; -import Button from "@mui/material/Button"; import Table from "@mui/material/Table"; import TableBody from "@mui/material/TableBody"; import TableCell from "@mui/material/TableCell"; @@ -9,6 +8,7 @@ import TableRow from "@mui/material/TableRow"; import type * as TypesGen from "api/typesGenerated"; import { ErrorAlert } from "components/Alert/ErrorAlert"; import { Avatar } from "components/Avatar/Avatar"; +import { Button } from "components/Button/Button"; import { SettingsHeader, SettingsHeaderDescription, @@ -48,12 +48,11 @@ const OAuth2AppsSettingsPageView: FC = ({
- diff --git a/site/src/pages/ExternalAuthPage/ExternalAuthPage.tsx b/site/src/pages/ExternalAuthPage/ExternalAuthPage.tsx index 105e7ee501b38..c4ae645d015c2 100644 --- a/site/src/pages/ExternalAuthPage/ExternalAuthPage.tsx +++ b/site/src/pages/ExternalAuthPage/ExternalAuthPage.tsx @@ -1,4 +1,3 @@ -import Button from "@mui/material/Button"; import type { ApiErrorResponse } from "api/errors"; import { exchangeExternalAuthDevice, @@ -6,6 +5,7 @@ import { externalAuthProvider, } from "api/queries/externalAuth"; import { isAxiosError } from "axios"; +import { Button } from "components/Button/Button"; import { isExchangeErrorRetryable, newRetryDelay, @@ -91,6 +91,7 @@ const ExternalAuthPage: FC = () => {


+ )} @@ -353,12 +351,6 @@ const styles = { autoComplete: { width: 300, }, - removeButton: (theme) => ({ - color: theme.palette.error.main, - "&:hover": { - backgroundColor: "transparent", - }, - }), status: { textTransform: "capitalize", }, diff --git a/site/src/pages/HealthPage/DERPPage.tsx b/site/src/pages/HealthPage/DERPPage.tsx index 8b4b85bd37551..3a913f23e05d5 100644 --- a/site/src/pages/HealthPage/DERPPage.tsx +++ b/site/src/pages/HealthPage/DERPPage.tsx @@ -1,11 +1,11 @@ import { useTheme } from "@emotion/react"; -import Button from "@mui/material/Button"; import type { HealthcheckReport, HealthSeverity, NetcheckReport, } from "api/typesGenerated"; import { Alert } from "components/Alert/Alert"; +import { Button } from "components/Button/Button"; import { MapPinIcon } from "lucide-react"; import type { FC } from "react"; import { Link, useOutletContext } from "react-router"; @@ -100,10 +100,9 @@ const DERPPage: FC = () => { }) .map(({ severity, region }) => { return ( - ); })} diff --git a/site/src/pages/LoginPage/OAuthSignInForm.tsx b/site/src/pages/LoginPage/OAuthSignInForm.tsx index e4872d6600389..343353d18dfca 100644 --- a/site/src/pages/LoginPage/OAuthSignInForm.tsx +++ b/site/src/pages/LoginPage/OAuthSignInForm.tsx @@ -1,16 +1,11 @@ import GitHubIcon from "@mui/icons-material/GitHub"; import KeyIcon from "@mui/icons-material/VpnKey"; -import Button from "@mui/material/Button"; import { visuallyHidden } from "@mui/utils"; import type { AuthMethods } from "api/typesGenerated"; +import { Button } from "components/Button/Button"; import { type FC, useId } from "react"; import { Language } from "./Language"; -const iconStyles = { - width: 16, - height: 16, -}; - type OAuthSignInFormProps = { isSigningIn: boolean; redirectTo: string; @@ -26,41 +21,45 @@ export const OAuthSignInForm: FC = ({
{authMethods?.github.enabled && ( )} {authMethods?.oidc.enabled && ( )}
@@ -80,7 +79,7 @@ const OidcIcon: FC = ({ iconUrl }) => { // if that happens, but also still need a way to inject accessible text return ( <> - +
Open ID Connect
diff --git a/site/src/pages/OrganizationSettingsPage/CustomRolesPage/CustomRolesPageView.tsx b/site/src/pages/OrganizationSettingsPage/CustomRolesPage/CustomRolesPageView.tsx index cd94b6a18e669..e3074f3ada94e 100644 --- a/site/src/pages/OrganizationSettingsPage/CustomRolesPage/CustomRolesPageView.tsx +++ b/site/src/pages/OrganizationSettingsPage/CustomRolesPage/CustomRolesPageView.tsx @@ -1,8 +1,7 @@ import type { Interpolation, Theme } from "@emotion/react"; -import Button from "@mui/material/Button"; import Skeleton from "@mui/material/Skeleton"; import type { AssignableRoles, Role } from "api/typesGenerated"; -import { Button as ShadcnButton } from "components/Button/Button"; +import { Button, Button as ShadcnButton } from "components/Button/Button"; import { ChooseOne, Cond } from "components/Conditionals/ChooseOne"; import { DropdownMenu, @@ -72,12 +71,11 @@ export const CustomRolesPageView: FC = ({ {canCreateOrgRole && isCustomRolesEnabled && ( - )} @@ -157,13 +155,11 @@ const RoleTable: FC = ({ cta={ canCreateOrgRole && isCustomRolesEnabled && ( - ) } diff --git a/site/src/pages/ResetPasswordPage/ChangePasswordPage.tsx b/site/src/pages/ResetPasswordPage/ChangePasswordPage.tsx index c7ae619950b43..9ec361a39932c 100644 --- a/site/src/pages/ResetPasswordPage/ChangePasswordPage.tsx +++ b/site/src/pages/ResetPasswordPage/ChangePasswordPage.tsx @@ -1,5 +1,4 @@ import type { Interpolation, Theme } from "@emotion/react"; -import MUIButton from "@mui/material/Button"; import TextField from "@mui/material/TextField"; import { isApiValidationError } from "api/errors"; import { changePasswordWithOTP } from "api/queries/users"; @@ -119,15 +118,9 @@ const ChangePasswordPage: FC = ({ redirect }) => { Reset password - - Back to login - + diff --git a/site/src/pages/SetupPage/SetupPageView.tsx b/site/src/pages/SetupPage/SetupPageView.tsx index 96654bdda8514..57534ddbe74cd 100644 --- a/site/src/pages/SetupPage/SetupPageView.tsx +++ b/site/src/pages/SetupPage/SetupPageView.tsx @@ -1,7 +1,6 @@ import GitHubIcon from "@mui/icons-material/GitHub"; import AlertTitle from "@mui/material/AlertTitle"; import Autocomplete from "@mui/material/Autocomplete"; -import MuiButton from "@mui/material/Button"; import Checkbox from "@mui/material/Checkbox"; import Link from "@mui/material/Link"; import MenuItem from "@mui/material/MenuItem"; @@ -173,17 +172,12 @@ export const SetupPageView: FC = ({ {authMethods?.github.enabled && ( <> - } - type="submit" - size="xlarge" - > - {Language.githubCreate} - +
diff --git a/site/src/pages/TemplatePage/TemplatePageHeader.tsx b/site/src/pages/TemplatePage/TemplatePageHeader.tsx index dd93054f64d9e..8db6afcd07292 100644 --- a/site/src/pages/TemplatePage/TemplatePageHeader.tsx +++ b/site/src/pages/TemplatePage/TemplatePageHeader.tsx @@ -1,4 +1,3 @@ -import Button from "@mui/material/Button"; import { API } from "api/api"; import { workspaces } from "api/queries/workspaces"; import type { @@ -7,7 +6,7 @@ import type { TemplateVersion, } from "api/typesGenerated"; import { Avatar } from "components/Avatar/Avatar"; -import { Button as ShadcnButton } from "components/Button/Button"; +import { Button, Button as ShadcnButton } from "components/Button/Button"; import { ConfirmDialog } from "components/Dialogs/ConfirmDialog/ConfirmDialog"; import { DeleteDialog } from "components/Dialogs/DeleteDialog/DeleteDialog"; import { @@ -222,13 +221,11 @@ export const TemplatePageHeader: FC = ({ <> {!template.deprecated && workspacePermissions.createWorkspaceForUserID && ( - )} diff --git a/site/src/theme/constants.ts b/site/src/theme/constants.ts index f74611cefd324..28b5234d8f138 100644 --- a/site/src/theme/constants.ts +++ b/site/src/theme/constants.ts @@ -34,7 +34,5 @@ export const containerWidthMedium = 1080; export const sidePadding = 24; // MUI does not have aligned heights for buttons and inputs so we have to "hack" it a little bit -export const BUTTON_XL_HEIGHT = 44; export const BUTTON_LG_HEIGHT = 40; export const BUTTON_MD_HEIGHT = 36; -export const BUTTON_SM_HEIGHT = 32; diff --git a/site/src/theme/light/mui.ts b/site/src/theme/light/mui.ts index 8092b5f8cbe80..8fe2e25b6ca0d 100644 --- a/site/src/theme/light/mui.ts +++ b/site/src/theme/light/mui.ts @@ -108,67 +108,6 @@ const muiTheme = createTheme({ }, }, }, - MuiButton: { - ...components.MuiButton, - styleOverrides: { - ...components.MuiButton.styleOverrides, - outlined: ({ theme }) => ({ - boxShadow: "0 1px 4px #0001", - ":hover": { - boxShadow: "0 1px 4px #0001", - border: `1px solid ${theme.palette.secondary.main}`, - }, - "&.Mui-disabled": { - boxShadow: "none !important", - }, - }), - ["outlinedNeutral" as MuiStyle]: { - borderColor: tw.zinc[300], - - "&.Mui-disabled": { - borderColor: tw.zinc[200], - color: tw.zinc[500], - - "& > .MuiLoadingButton-loadingIndicator": { - color: tw.zinc[500], - }, - }, - }, - contained: { - boxShadow: "0 1px 4px #0001", - "&.Mui-disabled": { - boxShadow: "none !important", - }, - ":hover": { - boxShadow: "0 1px 4px #0001", - }, - }, - ["containedNeutral" as MuiStyle]: { - backgroundColor: tw.zinc[100], - border: `1px solid ${tw.zinc[200]}`, - - "&.Mui-disabled": { - backgroundColor: tw.zinc[50], - border: `1px solid ${tw.zinc[100]}`, - }, - - "&:hover": { - backgroundColor: tw.zinc[200], - border: `1px solid ${tw.zinc[300]}`, - }, - }, - }, - }, - MuiButtonGroup: { - styleOverrides: { - root: { - ">button:hover+button": { - // The !important is unfortunate, but necessary for the border. - borderLeftColor: `${tw.zinc[300]} !important`, - }, - }, - }, - }, MuiChip: { styleOverrides: { root: { diff --git a/site/src/theme/mui.ts b/site/src/theme/mui.ts index fc20b0a9f9be1..4435579a33153 100644 --- a/site/src/theme/mui.ts +++ b/site/src/theme/mui.ts @@ -6,8 +6,6 @@ import { BODY_FONT_FAMILY, BUTTON_LG_HEIGHT, BUTTON_MD_HEIGHT, - BUTTON_SM_HEIGHT, - BUTTON_XL_HEIGHT, borderRadius, } from "./constants"; import tw from "./tailwindColors"; @@ -61,112 +59,6 @@ export const components = { }), }, }, - // Button styles are based on - // https://tailwindui.com/components/application-ui/elements/buttons - MuiButtonBase: { - defaultProps: { - disableRipple: true, - }, - }, - MuiButton: { - defaultProps: { - variant: "outlined", - color: "neutral", - }, - styleOverrides: { - root: ({ theme }) => ({ - textTransform: "none", - letterSpacing: "normal", - fontWeight: 500, - height: BUTTON_MD_HEIGHT, - padding: "8px 16px", - borderRadius: "6px", - fontSize: 14, - - whiteSpace: "nowrap", - ":focus-visible": { - outline: `2px solid ${theme.palette.primary.main}`, - }, - - "& .MuiLoadingButton-loadingIndicator": { - width: 14, - height: 14, - }, - - "& .MuiLoadingButton-loadingIndicator .MuiCircularProgress-root": { - width: "inherit !important", - height: "inherit !important", - }, - }), - sizeSmall: { - height: BUTTON_SM_HEIGHT, - }, - sizeLarge: { - height: BUTTON_LG_HEIGHT, - }, - ["sizeXlarge" as MuiStyle]: { - height: BUTTON_XL_HEIGHT, - - // With higher size we need to increase icon spacing. - "& .MuiButton-startIcon": { - marginRight: 12, - }, - "& .MuiButton-endIcon": { - marginLeft: 12, - }, - }, - outlined: ({ theme }) => ({ - ":hover": { - border: `1px solid ${theme.palette.secondary.main}`, - }, - }), - ["outlinedNeutral" as MuiStyle]: { - borderColor: tw.zinc[600], - - "&.Mui-disabled": { - borderColor: tw.zinc[700], - color: tw.zinc[500], - - "& > .MuiLoadingButton-loadingIndicator": { - color: tw.zinc[500], - }, - }, - }, - ["containedNeutral" as MuiStyle]: { - backgroundColor: tw.zinc[800], - - "&:hover": { - backgroundColor: tw.zinc[700], - }, - }, - iconSizeMedium: { - "& > .MuiSvgIcon-root": { - fontSize: 14, - }, - }, - iconSizeSmall: { - "& > .MuiSvgIcon-root": { - fontSize: 13, - }, - }, - }, - }, - MuiButtonGroup: { - styleOverrides: { - root: ({ theme }) => ({ - ">button:hover+button": { - // The !important is unfortunate, but necessary for the border. - borderLeftColor: `${theme.palette.secondary.main} !important`, - }, - }), - }, - }, - ["MuiLoadingButton" as MuiStyle]: { - defaultProps: { - variant: "outlined", - color: "neutral", - }, - }, MuiTableContainer: { styleOverrides: { root: ({ theme }) => ({ From e5d7f43c5168de8daa5fb62c408d48d4a52dfdf5 Mon Sep 17 00:00:00 2001 From: DevCats Date: Wed, 8 Oct 2025 12:58:56 -0500 Subject: [PATCH 090/298] chore: add auto-dev-server-icon and set to monochromatic (#20219) --- site/src/theme/externalImages.ts | 1 + site/src/theme/icons.json | 1 + site/static/icon/auto-dev-server.svg | 4 ++++ 3 files changed, 6 insertions(+) create mode 100644 site/static/icon/auto-dev-server.svg diff --git a/site/src/theme/externalImages.ts b/site/src/theme/externalImages.ts index d92fdee61a722..60316c594be86 100644 --- a/site/src/theme/externalImages.ts +++ b/site/src/theme/externalImages.ts @@ -143,6 +143,7 @@ export function getExternalImageStylesFromUrl( export const defaultParametersForBuiltinIcons = new Map([ ["/icon/apple-black.svg", "monochrome"], ["/icon/auggie.svg", "monochrome"], + ["/icon/auto-dev-server.svg", "monochrome"], ["/icon/aws.png", "whiteWithColor&brightness=1.5"], ["/icon/aws.svg", "blackWithColor&brightness=1.5"], ["/icon/aws-monochrome.svg", "monochrome"], diff --git a/site/src/theme/icons.json b/site/src/theme/icons.json index 390d8b0becefc..aa3db061f8f92 100644 --- a/site/src/theme/icons.json +++ b/site/src/theme/icons.json @@ -8,6 +8,7 @@ "apple-grey.svg", "argo-workflows.svg", "auggie.svg", + "auto-dev-server.svg", "aws-dark.svg", "aws-light.svg", "aws-monochrome.svg", diff --git a/site/static/icon/auto-dev-server.svg b/site/static/icon/auto-dev-server.svg new file mode 100644 index 0000000000000..f043b56d0eeb6 --- /dev/null +++ b/site/static/icon/auto-dev-server.svg @@ -0,0 +1,4 @@ + + + + From 4b8d73994fac27d296d52397f518c49a5cf752fe Mon Sep 17 00:00:00 2001 From: Bruno Quaresma Date: Wed, 8 Oct 2025 15:01:01 -0300 Subject: [PATCH 091/298] chore: migrate MUI icons to lucide-react (#20193) --- site/package.json | 2 +- site/pnpm-lock.yaml | 10 ++++---- .../modules/resources/AppLink/ShareIcon.tsx | 16 +++++++------ .../modules/resources/PortForwardButton.tsx | 16 ++++++------- .../TemplateFiles/TemplateFileTree.tsx | 12 ++++++---- .../templates/TemplateFiles/TemplateFiles.tsx | 23 ++++++++----------- .../pages/HealthPage/DismissWarningButton.tsx | 7 +++--- site/src/pages/HealthPage/HealthLayout.tsx | 15 +++--------- .../pages/HealthPage/WorkspaceProxyPage.tsx | 4 ++-- site/src/pages/LoginPage/OAuthSignInForm.tsx | 2 +- .../TemplateEmbedPageExperimental.tsx | 7 +++--- .../TemplateVersionPageView.tsx | 2 +- .../UsersPage/UsersTable/UsersTableBody.tsx | 21 +++++++++-------- site/src/pages/WorkspacePage/Workspace.tsx | 9 ++++---- 14 files changed, 70 insertions(+), 76 deletions(-) diff --git a/site/package.json b/site/package.json index ddb04c084ce01..62b2b8dd64981 100644 --- a/site/package.json +++ b/site/package.json @@ -91,7 +91,7 @@ "humanize-duration": "3.32.2", "jszip": "3.10.1", "lodash": "4.17.21", - "lucide-react": "0.474.0", + "lucide-react": "0.545.0", "monaco-editor": "0.53.0", "pretty-bytes": "6.1.1", "react": "19.1.1", diff --git a/site/pnpm-lock.yaml b/site/pnpm-lock.yaml index 396028241502c..e788524025aeb 100644 --- a/site/pnpm-lock.yaml +++ b/site/pnpm-lock.yaml @@ -188,8 +188,8 @@ importers: specifier: 4.17.21 version: 4.17.21 lucide-react: - specifier: 0.474.0 - version: 0.474.0(react@19.1.1) + specifier: 0.545.0 + version: 0.545.0(react@19.1.1) monaco-editor: specifier: 0.53.0 version: 0.53.0 @@ -4885,8 +4885,8 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==, tarball: https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz} - lucide-react@0.474.0: - resolution: {integrity: sha512-CmghgHkh0OJNmxGKWc0qfPJCYHASPMVSyGY8fj3xgk4v84ItqDg64JNKFZn5hC6E0vHi6gxnbCgwhyVB09wQtA==, tarball: https://registry.npmjs.org/lucide-react/-/lucide-react-0.474.0.tgz} + lucide-react@0.545.0: + resolution: {integrity: sha512-7r1/yUuflQDSt4f1bpn5ZAocyIxcTyVyBBChSVtBKn5M+392cPmI5YJMWOJKk/HUWGm5wg83chlAZtCcGbEZtw==, tarball: https://registry.npmjs.org/lucide-react/-/lucide-react-0.545.0.tgz} peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -11353,7 +11353,7 @@ snapshots: dependencies: yallist: 3.1.1 - lucide-react@0.474.0(react@19.1.1): + lucide-react@0.545.0(react@19.1.1): dependencies: react: 19.1.1 diff --git a/site/src/modules/resources/AppLink/ShareIcon.tsx b/site/src/modules/resources/AppLink/ShareIcon.tsx index 7e6660fe4b162..d9d536f999259 100644 --- a/site/src/modules/resources/AppLink/ShareIcon.tsx +++ b/site/src/modules/resources/AppLink/ShareIcon.tsx @@ -1,9 +1,11 @@ -import BusinessIcon from "@mui/icons-material/Business"; -import GroupOutlinedIcon from "@mui/icons-material/GroupOutlined"; -import PublicOutlinedIcon from "@mui/icons-material/PublicOutlined"; import Tooltip from "@mui/material/Tooltip"; import type * as TypesGen from "api/typesGenerated"; -import { SquareArrowOutUpRightIcon } from "lucide-react"; +import { + Building2Icon, + GlobeIcon, + SquareArrowOutUpRightIcon, + UsersIcon, +} from "lucide-react"; interface ShareIconProps { app: TypesGen.WorkspaceApp; @@ -20,21 +22,21 @@ export const ShareIcon = ({ app }: ShareIconProps) => { if (app.sharing_level === "authenticated") { return ( - + ); } if (app.sharing_level === "organization") { return ( - + ); } if (app.sharing_level === "public") { return ( - + ); } diff --git a/site/src/modules/resources/PortForwardButton.tsx b/site/src/modules/resources/PortForwardButton.tsx index 749ddfc32bc01..78d4e999b340c 100644 --- a/site/src/modules/resources/PortForwardButton.tsx +++ b/site/src/modules/resources/PortForwardButton.tsx @@ -1,8 +1,4 @@ import { type Interpolation, type Theme, useTheme } from "@emotion/react"; -import BusinessIcon from "@mui/icons-material/Business"; -import LockIcon from "@mui/icons-material/Lock"; -import LockOpenIcon from "@mui/icons-material/LockOpen"; -import SensorsIcon from "@mui/icons-material/Sensors"; import FormControl from "@mui/material/FormControl"; import Link from "@mui/material/Link"; import MenuItem from "@mui/material/MenuItem"; @@ -45,8 +41,12 @@ import { } from "components/Tooltip/Tooltip"; import { useFormik } from "formik"; import { + BuildingIcon, ChevronDownIcon, ExternalLinkIcon, + LockIcon, + LockOpenIcon, + RadioIcon, ShareIcon, X as XIcon, } from "lucide-react"; @@ -385,7 +385,7 @@ export const PortForwardPopoverView: FC = ({ target="_blank" rel="noreferrer" > - + {port.port} = ({ rel="noreferrer" > {share.share_level === "public" ? ( - + ) : share.share_level === "organization" ? ( - + ) : ( - + )} {label} diff --git a/site/src/modules/templates/TemplateFiles/TemplateFileTree.tsx b/site/src/modules/templates/TemplateFiles/TemplateFileTree.tsx index 9578d4aba1548..c4c0c7e041d46 100644 --- a/site/src/modules/templates/TemplateFiles/TemplateFileTree.tsx +++ b/site/src/modules/templates/TemplateFiles/TemplateFileTree.tsx @@ -1,11 +1,13 @@ import { css } from "@emotion/react"; -import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; -import FormatAlignLeftOutlined from "@mui/icons-material/FormatAlignLeftOutlined"; import Menu from "@mui/material/Menu"; import MenuItem from "@mui/material/MenuItem"; import { SimpleTreeView, TreeItem } from "@mui/x-tree-view"; import { DockerIcon } from "components/Icons/DockerIcon"; -import { ChevronRightIcon } from "lucide-react"; +import { + ChevronDownIcon, + ChevronRightIcon, + TextAlignStartIcon, +} from "lucide-react"; import { type CSSProperties, type ElementType, @@ -90,7 +92,7 @@ export const TemplateFileTree: FC = ({ let icon: ElementType | undefined; if (isFolder(content)) { - icon = FormatAlignLeftOutlined; + icon = TextAlignStartIcon; } else if (filename.endsWith(".tf")) { icon = FileTypeTerraform; } else if (filename.endsWith(".md")) { @@ -201,7 +203,7 @@ export const TemplateFileTree: FC = ({ return ( = ({ return (
- {filename} - {info.hasDiff && ( - - )} + + {filename} +
= ({ }, }} > - + Edit
diff --git a/site/src/pages/HealthPage/DismissWarningButton.tsx b/site/src/pages/HealthPage/DismissWarningButton.tsx index 09ff78d9c01b2..b1d04d2859b7a 100644 --- a/site/src/pages/HealthPage/DismissWarningButton.tsx +++ b/site/src/pages/HealthPage/DismissWarningButton.tsx @@ -1,11 +1,10 @@ -import NotificationsOffOutlined from "@mui/icons-material/NotificationsOffOutlined"; -import NotificationOutlined from "@mui/icons-material/NotificationsOutlined"; import Skeleton from "@mui/material/Skeleton"; import { healthSettings, updateHealthSettings } from "api/queries/debug"; import type { HealthSection } from "api/typesGenerated"; import { Button } from "components/Button/Button"; import { displaySuccess } from "components/GlobalSnackbar/utils"; import { Spinner } from "components/Spinner/Spinner"; +import { BellIcon, BellOffIcon } from "lucide-react"; import { useMutation, useQuery, useQueryClient } from "react-query"; export const DismissWarningButton = (props: { healthcheck: HealthSection }) => { @@ -49,7 +48,7 @@ export const DismissWarningButton = (props: { healthcheck: HealthSection }) => { }} > - + Enable warnings @@ -69,7 +68,7 @@ export const DismissWarningButton = (props: { healthcheck: HealthSection }) => { }} > - + Dismiss warnings diff --git a/site/src/pages/HealthPage/HealthLayout.tsx b/site/src/pages/HealthPage/HealthLayout.tsx index 10c5b1695dc5b..5c9c44dad039d 100644 --- a/site/src/pages/HealthPage/HealthLayout.tsx +++ b/site/src/pages/HealthPage/HealthLayout.tsx @@ -1,6 +1,3 @@ -import { useTheme } from "@emotion/react"; -import NotificationsOffOutlined from "@mui/icons-material/NotificationsOffOutlined"; -import ReplayIcon from "@mui/icons-material/Replay"; import CircularProgress from "@mui/material/CircularProgress"; import IconButton from "@mui/material/IconButton"; import Tooltip from "@mui/material/Tooltip"; @@ -9,6 +6,7 @@ import type { HealthSeverity } from "api/typesGenerated"; import { ErrorAlert } from "components/Alert/ErrorAlert"; import { Loader } from "components/Loader/Loader"; import kebabCase from "lodash/fp/kebabCase"; +import { BellOffIcon, RotateCcwIcon } from "lucide-react"; import { DashboardFullPage } from "modules/dashboard/DashboardLayout"; import { type FC, Suspense } from "react"; import { useMutation, useQuery, useQueryClient } from "react-query"; @@ -28,7 +26,6 @@ const linkStyles = { }; export const HealthLayout: FC = () => { - const theme = useTheme(); const queryClient = useQueryClient(); const { data: healthStatus, @@ -91,7 +88,7 @@ export const HealthLayout: FC = () => { {isRefreshing ? ( ) : ( - + )} @@ -155,13 +152,7 @@ export const HealthLayout: FC = () => { /> {label} {healthSection.dismissed && ( - + )} ); diff --git a/site/src/pages/HealthPage/WorkspaceProxyPage.tsx b/site/src/pages/HealthPage/WorkspaceProxyPage.tsx index 74fc699bb10c7..2276c24cbcbc5 100644 --- a/site/src/pages/HealthPage/WorkspaceProxyPage.tsx +++ b/site/src/pages/HealthPage/WorkspaceProxyPage.tsx @@ -2,7 +2,7 @@ import { useTheme } from "@emotion/react"; import Tooltip from "@mui/material/Tooltip"; import type { HealthcheckReport } from "api/typesGenerated"; import { Alert } from "components/Alert/Alert"; -import { EarthIcon, HashIcon } from "lucide-react"; +import { GlobeIcon, HashIcon } from "lucide-react"; import type { FC } from "react"; import { useOutletContext } from "react-router"; import { createDayString } from "utils/createDayString"; @@ -107,7 +107,7 @@ const WorkspaceProxyPage: FC = () => {
{region.wildcard_hostname && ( - }> + }> {region.wildcard_hostname} diff --git a/site/src/pages/LoginPage/OAuthSignInForm.tsx b/site/src/pages/LoginPage/OAuthSignInForm.tsx index 343353d18dfca..e2c08caf69f35 100644 --- a/site/src/pages/LoginPage/OAuthSignInForm.tsx +++ b/site/src/pages/LoginPage/OAuthSignInForm.tsx @@ -1,8 +1,8 @@ import GitHubIcon from "@mui/icons-material/GitHub"; -import KeyIcon from "@mui/icons-material/VpnKey"; import { visuallyHidden } from "@mui/utils"; import type { AuthMethods } from "api/typesGenerated"; import { Button } from "components/Button/Button"; +import { KeyIcon } from "lucide-react"; import { type FC, useId } from "react"; import { Language } from "./Language"; diff --git a/site/src/pages/TemplatePage/TemplateEmbedPage/TemplateEmbedPageExperimental.tsx b/site/src/pages/TemplatePage/TemplateEmbedPage/TemplateEmbedPageExperimental.tsx index c9b63f1422478..34c55313d60a3 100644 --- a/site/src/pages/TemplatePage/TemplateEmbedPage/TemplateEmbedPageExperimental.tsx +++ b/site/src/pages/TemplatePage/TemplateEmbedPage/TemplateEmbedPageExperimental.tsx @@ -1,5 +1,3 @@ -import CheckOutlined from "@mui/icons-material/CheckOutlined"; -import FileCopyOutlined from "@mui/icons-material/FileCopyOutlined"; import { API } from "api/api"; import { DetailedError } from "api/errors"; import type { @@ -18,6 +16,7 @@ import { Skeleton } from "components/Skeleton/Skeleton"; import { useAuthenticated } from "hooks"; import { useEffectEvent } from "hooks/hookPolyfills"; import { useClipboard } from "hooks/useClipboard"; +import { CheckIcon, CopyIcon } from "lucide-react"; import { Diagnostics, DynamicParameter, @@ -308,8 +307,8 @@ const ButtonPreview: FC = ({ template, buttonValues }) => { clipboard.copyToClipboard(textToCopy); }} > - {clipboard.showCopiedSuccess ? : }{" "} - Copy button code + {clipboard.showCopiedSuccess ? : } Copy button + code
); diff --git a/site/src/pages/TemplateVersionPage/TemplateVersionPageView.tsx b/site/src/pages/TemplateVersionPage/TemplateVersionPageView.tsx index 3a6b7daeece66..7c7390682b6d1 100644 --- a/site/src/pages/TemplateVersionPage/TemplateVersionPageView.tsx +++ b/site/src/pages/TemplateVersionPage/TemplateVersionPageView.tsx @@ -58,7 +58,7 @@ export const TemplateVersionPageView: FC = ({ )} diff --git a/site/src/pages/UsersPage/UsersTable/UsersTableBody.tsx b/site/src/pages/UsersPage/UsersTable/UsersTableBody.tsx index 408ea411a84f9..a070ad7913a68 100644 --- a/site/src/pages/UsersPage/UsersTable/UsersTableBody.tsx +++ b/site/src/pages/UsersPage/UsersTable/UsersTableBody.tsx @@ -1,9 +1,5 @@ import type { Interpolation, Theme } from "@emotion/react"; import GitHub from "@mui/icons-material/GitHub"; -import HideSourceOutlined from "@mui/icons-material/HideSourceOutlined"; -import KeyOutlined from "@mui/icons-material/KeyOutlined"; -import PasswordOutlined from "@mui/icons-material/PasswordOutlined"; -import ShieldOutlined from "@mui/icons-material/ShieldOutlined"; import Skeleton from "@mui/material/Skeleton"; import type { GroupsByUserId } from "api/queries/groups"; import type * as TypesGen from "api/typesGenerated"; @@ -28,7 +24,14 @@ import { } from "components/TableLoader/TableLoader"; import dayjs from "dayjs"; import relativeTime from "dayjs/plugin/relativeTime"; -import { EllipsisVertical, TrashIcon } from "lucide-react"; +import { + BanIcon, + EllipsisVertical, + KeyIcon, + ShieldIcon, + TrashIcon, + UserLockIcon, +} from "lucide-react"; import type { FC } from "react"; import { UserRoleCell } from "../../OrganizationSettingsPage/UserTable/UserRoleCell"; import { UserGroupsCell } from "./UserGroupsCell"; @@ -260,22 +263,22 @@ const LoginType: FC = ({ authMethods, value }) => { if (value === "password") { displayName = "Password"; - icon = ; + icon = ; } else if (value === "none") { displayName = "None"; - icon = ; + icon = ; } else if (value === "github") { displayName = "GitHub"; icon = ; } else if (value === "token") { displayName = "Token"; - icon = ; + icon = ; } else if (value === "oidc") { displayName = authMethods.oidc.signInText === "" ? "OIDC" : authMethods.oidc.signInText; icon = authMethods.oidc.iconUrl === "" ? ( - + ) : ( Open ID Connect icon = ({ setSidebarOption("resources"); }} > - + + Resources = ({ setSidebarOption("history"); }} > - + + History
From 9935da86c624bbf46e4b47789052e7b5e988c799 Mon Sep 17 00:00:00 2001 From: Jaayden Halko Date: Wed, 8 Oct 2025 21:13:04 +0100 Subject: [PATCH 092/298] chore: migrate appearanceform to Tailwind and shadcn (#20204) --- site/src/index.css | 3 +- .../AppearancePage/AppearanceForm.tsx | 265 ++++++------------ 2 files changed, 89 insertions(+), 179 deletions(-) diff --git a/site/src/index.css b/site/src/index.css index 6486e162568ed..40adcb9fbab58 100644 --- a/site/src/index.css +++ b/site/src/index.css @@ -8,7 +8,8 @@ @tailwind utilities; @layer base { - :root { + :root, + .light { --content-primary: 240 10% 4%; --content-secondary: 240 5% 34%; --content-link: 221 83% 53%; diff --git a/site/src/pages/UserSettingsPage/AppearancePage/AppearanceForm.tsx b/site/src/pages/UserSettingsPage/AppearancePage/AppearanceForm.tsx index aa10f315b6f2d..0b86d8fdf73b6 100644 --- a/site/src/pages/UserSettingsPage/AppearancePage/AppearanceForm.tsx +++ b/site/src/pages/UserSettingsPage/AppearancePage/AppearanceForm.tsx @@ -1,10 +1,3 @@ -import type { Interpolation } from "@emotion/react"; -import CircularProgress from "@mui/material/CircularProgress"; -import FormControl from "@mui/material/FormControl"; -import FormControlLabel from "@mui/material/FormControlLabel"; -import Radio from "@mui/material/Radio"; -import RadioGroup from "@mui/material/RadioGroup"; -import { visuallyHidden } from "@mui/utils"; import { type TerminalFontName, TerminalFontNames, @@ -12,10 +5,11 @@ import { } from "api/typesGenerated"; import { ErrorAlert } from "components/Alert/ErrorAlert"; import { PreviewBadge } from "components/Badges/Badges"; -import { Stack } from "components/Stack/Stack"; -import { ThemeOverride } from "contexts/ThemeProvider"; +import { Label } from "components/Label/Label"; +import { RadioGroup, RadioGroupItem } from "components/RadioGroup/RadioGroup"; +import { Spinner } from "components/Spinner/Spinner"; import type { FC } from "react"; -import themes, { DEFAULT_THEME, type Theme } from "theme"; +import { DEFAULT_THEME } from "theme"; import { DEFAULT_TERMINAL_FONT, terminalFontLabels, @@ -67,67 +61,65 @@ export const AppearanceForm: FC = ({
+
Theme - {isUpdating && } - + +
} layout="fluid" + className="mb-12" > - +
onChangeTheme("auto")} /> onChangeTheme("dark")} /> onChangeTheme("light")} /> - +
-
- Terminal Font - {isUpdating && } - +
+ Terminal Font + +
} layout="fluid" > - - - onChangeTerminalFont(toTerminalFontName(value)) - } - > - {TerminalFontNames.filter((name) => name !== "").map((name) => ( - } - label={ -
- {terminalFontLabels[name]} -
- } - /> - ))} -
-
+ + onChangeTerminalFont(toTerminalFontName(value)) + } + > + {TerminalFontNames.filter((name) => name !== "").map((name) => ( +
+ + +
+ ))} +
); @@ -139,8 +131,10 @@ function toTerminalFontName(value: string): TerminalFontName { : ""; } +type ThemeMode = "dark" | "light"; + interface AutoThemePreviewButtonProps extends Omit { - themes: [Theme, Theme]; + themes: [ThemeMode, ThemeMode]; onSelect?: () => void; } @@ -163,13 +157,15 @@ const AutoThemePreviewButton: FC = ({ value={displayName} checked={active} onChange={onSelect} - css={{ ...visuallyHidden }} + className="sr-only" /> -