diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 956323d7c5516..179c403e5017c 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1625,23 +1625,6 @@ jobs: "emoji": true } }, - { - "type": "section", - "fields": [ - { - "type": "mrkdwn", - "text": "*Workflow:*\n'"${GITHUB_WORKFLOW}"'" - }, - { - "type": "mrkdwn", - "text": "*Committer:*\n'"${GITHUB_ACTOR}"'" - }, - { - "type": "mrkdwn", - "text": "*Commit:*\n'"${GITHUB_SHA}"'" - } - ] - }, { "type": "section", "text": { diff --git a/.github/workflows/nightly-gauntlet.yaml b/.github/workflows/nightly-gauntlet.yaml index 4535ae7f363cb..a578ae9757ba5 100644 --- a/.github/workflows/nightly-gauntlet.yaml +++ b/.github/workflows/nightly-gauntlet.yaml @@ -182,23 +182,6 @@ jobs: "emoji": true } }, - { - "type": "section", - "fields": [ - { - "type": "mrkdwn", - "text": "*Workflow:*\n'"${GITHUB_WORKFLOW}"'" - }, - { - "type": "mrkdwn", - "text": "*Committer:*\n'"${GITHUB_ACTOR}"'" - }, - { - "type": "mrkdwn", - "text": "*Commit:*\n'"${GITHUB_SHA}"'" - } - ] - }, { "type": "section", "text": { diff --git a/cli/server.go b/cli/server.go index 097dd9460c57c..554ab5331bf12 100644 --- a/cli/server.go +++ b/cli/server.go @@ -2722,6 +2722,12 @@ func parseExternalAuthProvidersFromEnv(prefix string, environ []string) ([]coder provider.DisplayName = v.Value case "DISPLAY_ICON": provider.DisplayIcon = v.Value + case "MCP_URL": + provider.MCPURL = v.Value + case "MCP_TOOL_ALLOW_REGEX": + provider.MCPToolAllowRegex = v.Value + case "MCP_TOOL_DENY_REGEX": + provider.MCPToolDenyRegex = v.Value } providers[providerNum] = provider } diff --git a/cli/sharing_test.go b/cli/sharing_test.go index 9044ed4968170..71e153c64e4f6 100644 --- a/cli/sharing_test.go +++ b/cli/sharing_test.go @@ -21,15 +21,14 @@ import ( func TestSharingShare(t *testing.T) { t.Parallel() - dv := coderdtest.DeploymentValues(t) - dv.Experiments = []string{string(codersdk.ExperimentWorkspaceSharing)} - t.Run("ShareWithUsers_Simple", func(t *testing.T) { t.Parallel() var ( client, db = coderdtest.NewWithDatabase(t, &coderdtest.Options{ - DeploymentValues: dv, + DeploymentValues: coderdtest.DeploymentValues(t, func(dv *codersdk.DeploymentValues) { + dv.Experiments = []string{string(codersdk.ExperimentWorkspaceSharing)} + }), }) orgOwner = coderdtest.CreateFirstUser(t, client) workspaceOwnerClient, workspaceOwner = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID, rbac.ScopedRoleOrgAuditor(orgOwner.OrganizationID)) @@ -69,7 +68,9 @@ func TestSharingShare(t *testing.T) { var ( client, db = coderdtest.NewWithDatabase(t, &coderdtest.Options{ - DeploymentValues: dv, + DeploymentValues: coderdtest.DeploymentValues(t, func(dv *codersdk.DeploymentValues) { + dv.Experiments = []string{string(codersdk.ExperimentWorkspaceSharing)} + }), }) orgOwner = coderdtest.CreateFirstUser(t, client) @@ -124,7 +125,9 @@ func TestSharingShare(t *testing.T) { var ( client, db = coderdtest.NewWithDatabase(t, &coderdtest.Options{ - DeploymentValues: dv, + DeploymentValues: coderdtest.DeploymentValues(t, func(dv *codersdk.DeploymentValues) { + dv.Experiments = []string{string(codersdk.ExperimentWorkspaceSharing)} + }), }) orgOwner = coderdtest.CreateFirstUser(t, client) workspaceOwnerClient, workspaceOwner = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID, rbac.ScopedRoleOrgAuditor(orgOwner.OrganizationID)) @@ -171,15 +174,14 @@ func TestSharingShare(t *testing.T) { func TestSharingStatus(t *testing.T) { t.Parallel() - dv := coderdtest.DeploymentValues(t) - dv.Experiments = []string{string(codersdk.ExperimentWorkspaceSharing)} - t.Run("ListSharedUsers", func(t *testing.T) { t.Parallel() var ( client, db = coderdtest.NewWithDatabase(t, &coderdtest.Options{ - DeploymentValues: dv, + DeploymentValues: coderdtest.DeploymentValues(t, func(dv *codersdk.DeploymentValues) { + dv.Experiments = []string{string(codersdk.ExperimentWorkspaceSharing)} + }), }) orgOwner = coderdtest.CreateFirstUser(t, client) workspaceOwnerClient, workspaceOwner = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID, rbac.ScopedRoleOrgAuditor(orgOwner.OrganizationID)) @@ -220,15 +222,14 @@ func TestSharingStatus(t *testing.T) { func TestSharingRemove(t *testing.T) { t.Parallel() - dv := coderdtest.DeploymentValues(t) - dv.Experiments = []string{string(codersdk.ExperimentWorkspaceSharing)} - t.Run("RemoveSharedUser_Simple", func(t *testing.T) { t.Parallel() var ( client, db = coderdtest.NewWithDatabase(t, &coderdtest.Options{ - DeploymentValues: dv, + DeploymentValues: coderdtest.DeploymentValues(t, func(dv *codersdk.DeploymentValues) { + dv.Experiments = []string{string(codersdk.ExperimentWorkspaceSharing)} + }), }) orgOwner = coderdtest.CreateFirstUser(t, client) workspaceOwnerClient, workspaceOwner = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID, rbac.ScopedRoleOrgAuditor(orgOwner.OrganizationID)) @@ -287,7 +288,9 @@ func TestSharingRemove(t *testing.T) { var ( client, db = coderdtest.NewWithDatabase(t, &coderdtest.Options{ - DeploymentValues: dv, + DeploymentValues: coderdtest.DeploymentValues(t, func(dv *codersdk.DeploymentValues) { + dv.Experiments = []string{string(codersdk.ExperimentWorkspaceSharing)} + }), }) orgOwner = coderdtest.CreateFirstUser(t, client) workspaceOwnerClient, workspaceOwner = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID, rbac.ScopedRoleOrgAuditor(orgOwner.OrganizationID)) diff --git a/cli/ssh_internal_test.go b/cli/ssh_internal_test.go index a7fac11c7254c..3cf562ce82765 100644 --- a/cli/ssh_internal_test.go +++ b/cli/ssh_internal_test.go @@ -158,7 +158,7 @@ func TestCloserStack_CloseAfterContext(t *testing.T) { logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Leveled(slog.LevelDebug) uut := newCloserStack(ctx, logger, quartz.NewMock(t)) ac := newAsyncCloser(testCtx, t) - defer ac.complete() + defer ac.unblock() err := uut.push("async", ac) require.NoError(t, err) cancel() @@ -178,8 +178,9 @@ func TestCloserStack_CloseAfterContext(t *testing.T) { t.Fatal("closed before stack was finished") } - ac.complete() + ac.unblock() testutil.TryReceive(testCtx, t, closed) + testutil.TryReceive(testCtx, t, ac.done) } func TestCloserStack_Timeout(t *testing.T) { @@ -198,7 +199,8 @@ func TestCloserStack_Timeout(t *testing.T) { } defer func() { for _, a := range ac { - a.complete() + a.unblock() + testutil.TryReceive(ctx, t, a.done) // ensure we don't race with context cancellation } }() @@ -215,7 +217,7 @@ func TestCloserStack_Timeout(t *testing.T) { testutil.TryReceive(ctx, t, ac[1].started) // middle one finishes - ac[1].complete() + ac[1].unblock() // bottom starts, but also hangs testutil.TryReceive(ctx, t, ac[0].started) @@ -317,34 +319,37 @@ func (c *fakeCloser) Close() error { } type asyncCloser struct { - t *testing.T - ctx context.Context - started chan struct{} - isComplete chan struct{} - comepleteOnce sync.Once + t *testing.T + ctx context.Context + started chan struct{} + done chan struct{} + isUnblocked chan struct{} + unblockOnce sync.Once } func (c *asyncCloser) Close() error { close(c.started) + defer close(c.done) select { case <-c.ctx.Done(): c.t.Error("timed out") return c.ctx.Err() - case <-c.isComplete: + case <-c.isUnblocked: return nil } } -func (c *asyncCloser) complete() { - c.comepleteOnce.Do(func() { close(c.isComplete) }) +func (c *asyncCloser) unblock() { + c.unblockOnce.Do(func() { close(c.isUnblocked) }) } func newAsyncCloser(ctx context.Context, t *testing.T) *asyncCloser { return &asyncCloser{ - t: t, - ctx: ctx, - isComplete: make(chan struct{}), - started: make(chan struct{}), + t: t, + ctx: ctx, + isUnblocked: make(chan struct{}), + started: make(chan struct{}), + done: make(chan struct{}), } } diff --git a/cli/testdata/server-config.yaml.golden b/cli/testdata/server-config.yaml.golden index 2d28ada458766..40666c10e8394 100644 --- a/cli/testdata/server-config.yaml.golden +++ b/cli/testdata/server-config.yaml.golden @@ -713,3 +713,20 @@ workspace_prebuilds: # limit; disabled when set to zero. # (default: 3, type: int) failure_hard_limit: 3 +aibridge: + # Whether to start an in-memory aibridged instance ("aibridge" experiment must be + # enabled, too). + # (default: false, type: bool) + enabled: false + # The base URL of the OpenAI API. + # (default: https://api.openai.com/v1/, type: string) + openai_base_url: https://api.openai.com/v1/ + # The key to authenticate against the OpenAI API. + # (default: , type: string) + openai_key: "" + # The base URL of the Anthropic API. + # (default: https://api.anthropic.com/, type: string) + base_url: https://api.anthropic.com/ + # The key to authenticate against the Anthropic API. + # (default: , type: string) + key: "" diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 0182c92c2f32a..58e5da2f9a991 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -11175,6 +11175,50 @@ const docTemplate = `{ } } }, + "codersdk.AIBridgeAnthropicConfig": { + "type": "object", + "properties": { + "base_url": { + "type": "string" + }, + "key": { + "type": "string" + } + } + }, + "codersdk.AIBridgeConfig": { + "type": "object", + "properties": { + "anthropic": { + "$ref": "#/definitions/codersdk.AIBridgeAnthropicConfig" + }, + "enabled": { + "type": "boolean" + }, + "openai": { + "$ref": "#/definitions/codersdk.AIBridgeOpenAIConfig" + } + } + }, + "codersdk.AIBridgeOpenAIConfig": { + "type": "object", + "properties": { + "base_url": { + "type": "string" + }, + "key": { + "type": "string" + } + } + }, + "codersdk.AIConfig": { + "type": "object", + "properties": { + "bridge": { + "$ref": "#/definitions/codersdk.AIBridgeConfig" + } + } + }, "codersdk.APIKey": { "type": "object", "required": [ @@ -12811,6 +12855,9 @@ const docTemplate = `{ "agent_stat_refresh_interval": { "type": "integer" }, + "ai": { + "$ref": "#/definitions/codersdk.AIConfig" + }, "allow_workspace_renames": { "type": "boolean" }, @@ -13138,9 +13185,11 @@ const docTemplate = `{ "web-push", "oauth2", "mcp-server-http", - "workspace-sharing" + "workspace-sharing", + "aibridge" ], "x-enum-comments": { + "ExperimentAIBridge": "Enables AI Bridge functionality.", "ExperimentAutoFillParameters": "This should not be taken out of experiments until we have redesigned the feature.", "ExperimentExample": "This isn't used for anything.", "ExperimentMCPServerHTTP": "Enables the MCP HTTP server functionality.", @@ -13158,7 +13207,8 @@ const docTemplate = `{ "ExperimentWebPush", "ExperimentOAuth2", "ExperimentMCPServerHTTP", - "ExperimentWorkspaceSharing" + "ExperimentWorkspaceSharing", + "ExperimentAIBridge" ] }, "codersdk.ExternalAgentCredentials": { @@ -13256,6 +13306,15 @@ const docTemplate = `{ "description": "ID is a unique identifier for the auth config.\nIt defaults to ` + "`" + `type` + "`" + ` when not provided.", "type": "string" }, + "mcp_tool_allow_regex": { + "type": "string" + }, + "mcp_tool_deny_regex": { + "type": "string" + }, + "mcp_url": { + "type": "string" + }, "no_refresh": { "type": "boolean" }, @@ -15942,6 +16001,7 @@ const docTemplate = `{ "type": "string", "enum": [ "*", + "aibridge_interception", "api_key", "assign_org_role", "assign_role", @@ -15984,6 +16044,7 @@ const docTemplate = `{ ], "x-enum-varnames": [ "ResourceWildcard", + "ResourceAibridgeInterception", "ResourceApiKey", "ResourceAssignOrgRole", "ResourceAssignRole", diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index af2e2d43e2c70..b44f2dfcce015 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -9909,6 +9909,50 @@ } } }, + "codersdk.AIBridgeAnthropicConfig": { + "type": "object", + "properties": { + "base_url": { + "type": "string" + }, + "key": { + "type": "string" + } + } + }, + "codersdk.AIBridgeConfig": { + "type": "object", + "properties": { + "anthropic": { + "$ref": "#/definitions/codersdk.AIBridgeAnthropicConfig" + }, + "enabled": { + "type": "boolean" + }, + "openai": { + "$ref": "#/definitions/codersdk.AIBridgeOpenAIConfig" + } + } + }, + "codersdk.AIBridgeOpenAIConfig": { + "type": "object", + "properties": { + "base_url": { + "type": "string" + }, + "key": { + "type": "string" + } + } + }, + "codersdk.AIConfig": { + "type": "object", + "properties": { + "bridge": { + "$ref": "#/definitions/codersdk.AIBridgeConfig" + } + } + }, "codersdk.APIKey": { "type": "object", "required": [ @@ -11454,6 +11498,9 @@ "agent_stat_refresh_interval": { "type": "integer" }, + "ai": { + "$ref": "#/definitions/codersdk.AIConfig" + }, "allow_workspace_renames": { "type": "boolean" }, @@ -11774,9 +11821,11 @@ "web-push", "oauth2", "mcp-server-http", - "workspace-sharing" + "workspace-sharing", + "aibridge" ], "x-enum-comments": { + "ExperimentAIBridge": "Enables AI Bridge functionality.", "ExperimentAutoFillParameters": "This should not be taken out of experiments until we have redesigned the feature.", "ExperimentExample": "This isn't used for anything.", "ExperimentMCPServerHTTP": "Enables the MCP HTTP server functionality.", @@ -11794,7 +11843,8 @@ "ExperimentWebPush", "ExperimentOAuth2", "ExperimentMCPServerHTTP", - "ExperimentWorkspaceSharing" + "ExperimentWorkspaceSharing", + "ExperimentAIBridge" ] }, "codersdk.ExternalAgentCredentials": { @@ -11892,6 +11942,15 @@ "description": "ID is a unique identifier for the auth config.\nIt defaults to `type` when not provided.", "type": "string" }, + "mcp_tool_allow_regex": { + "type": "string" + }, + "mcp_tool_deny_regex": { + "type": "string" + }, + "mcp_url": { + "type": "string" + }, "no_refresh": { "type": "boolean" }, @@ -14477,6 +14536,7 @@ "type": "string", "enum": [ "*", + "aibridge_interception", "api_key", "assign_org_role", "assign_role", @@ -14519,6 +14579,7 @@ ], "x-enum-varnames": [ "ResourceWildcard", + "ResourceAibridgeInterception", "ResourceApiKey", "ResourceAssignOrgRole", "ResourceAssignRole", diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go index e38a174f83e8a..477a0b83c10ab 100644 --- a/coderd/database/dbauthz/dbauthz.go +++ b/coderd/database/dbauthz/dbauthz.go @@ -175,6 +175,22 @@ func (q *querier) authorizePrebuiltWorkspace(ctx context.Context, action policy. return xerrors.Errorf("authorize context: %w", workspaceErr) } +// authorizeAIBridgeInterceptionUpdate validates that the context's actor matches the initiator of the AIBridgeInterception. +// This is used by all of the sub-resources which fall under the [ResourceAibridgeInterception] umbrella. +func (q *querier) authorizeAIBridgeInterceptionUpdate(ctx context.Context, interceptionID uuid.UUID) error { + inter, err := q.db.GetAIBridgeInterceptionByID(ctx, interceptionID) + if err != nil { + return xerrors.Errorf("fetch aibridge interception %q: %w", interceptionID, err) + } + + err = q.authorizeContext(ctx, policy.ActionUpdate, inter.RBACObject()) + if err != nil { + return err + } + + return nil +} + type authContextKey struct{} // ActorFromContext returns the authorization subject from the context. @@ -542,6 +558,29 @@ var ( }), Scope: rbac.ScopeAll, }.WithCachedASTValue() + + // See aibridged package. + subjectAibridged = rbac.Subject{ + Type: rbac.SubjectAibridged, + FriendlyName: "AIBridge Daemon", + ID: uuid.Nil.String(), + Roles: rbac.Roles([]rbac.Role{ + { + Identifier: rbac.RoleIdentifier{Name: "aibridged"}, + DisplayName: "AIBridge Daemon", + Site: rbac.Permissions(map[string][]policy.Action{ + rbac.ResourceUser.Type: { + policy.ActionReadPersonal, // Required to read users' external auth links. // TODO: this is too broad; reduce scope to just external_auth_links by creating separate resource. + }, + rbac.ResourceApiKey.Type: {policy.ActionRead}, // Validate API keys. + rbac.ResourceAibridgeInterception.Type: {policy.ActionCreate, policy.ActionRead, policy.ActionUpdate}, + }), + Org: map[string][]rbac.Permission{}, + User: []rbac.Permission{}, + }, + }), + Scope: rbac.ScopeAll, + }.WithCachedASTValue() ) // AsProvisionerd returns a context with an actor that has permissions required @@ -624,6 +663,12 @@ func AsUsagePublisher(ctx context.Context) context.Context { return As(ctx, subjectUsagePublisher) } +// AsAIBridged returns a context with an actor that has permissions +// required for creating, reading, and updating aibridge-related resources. +func AsAIBridged(ctx context.Context) context.Context { + return As(ctx, subjectAibridged) +} + var AsRemoveActor = rbac.Subject{ ID: "remove-actor", } @@ -1878,6 +1923,10 @@ func (q *querier) FindMatchingPresetID(ctx context.Context, arg database.FindMat return q.db.FindMatchingPresetID(ctx, arg) } +func (q *querier) GetAIBridgeInterceptionByID(ctx context.Context, id uuid.UUID) (database.AIBridgeInterception, error) { + return fetch(q.log, q.auth, q.db.GetAIBridgeInterceptionByID)(ctx, id) +} + func (q *querier) GetAPIKeyByID(ctx context.Context, id string) (database.APIKey, error) { return fetch(q.log, q.auth, q.db.GetAPIKeyByID)(ctx, id) } @@ -3757,6 +3806,34 @@ func (q *querier) GetWorkspacesEligibleForTransition(ctx context.Context, now ti return q.db.GetWorkspacesEligibleForTransition(ctx, now) } +func (q *querier) InsertAIBridgeInterception(ctx context.Context, arg database.InsertAIBridgeInterceptionParams) (database.AIBridgeInterception, error) { + return insert(q.log, q.auth, rbac.ResourceAibridgeInterception.WithOwner(arg.InitiatorID.String()), q.db.InsertAIBridgeInterception)(ctx, arg) +} + +func (q *querier) InsertAIBridgeTokenUsage(ctx context.Context, arg database.InsertAIBridgeTokenUsageParams) error { + // All aibridge_token_usages records belong to the initiator of their associated interception. + if err := q.authorizeAIBridgeInterceptionUpdate(ctx, arg.InterceptionID); err != nil { + return err + } + return q.db.InsertAIBridgeTokenUsage(ctx, arg) +} + +func (q *querier) InsertAIBridgeToolUsage(ctx context.Context, arg database.InsertAIBridgeToolUsageParams) error { + // All aibridge_tool_usages records belong to the initiator of their associated interception. + if err := q.authorizeAIBridgeInterceptionUpdate(ctx, arg.InterceptionID); err != nil { + return err + } + return q.db.InsertAIBridgeToolUsage(ctx, arg) +} + +func (q *querier) InsertAIBridgeUserPrompt(ctx context.Context, arg database.InsertAIBridgeUserPromptParams) error { + // All aibridge_user_prompts records belong to the initiator of their associated interception. + if err := q.authorizeAIBridgeInterceptionUpdate(ctx, arg.InterceptionID); err != nil { + return err + } + return q.db.InsertAIBridgeUserPrompt(ctx, arg) +} + func (q *querier) InsertAPIKey(ctx context.Context, arg database.InsertAPIKeyParams) (database.APIKey, error) { // TODO(Cian): ideally this would be encoded in the policy, but system users are just members and we // don't currently have a capability to conditionally deny creating resources by owner ID in a role. diff --git a/coderd/database/dbauthz/dbauthz_test.go b/coderd/database/dbauthz/dbauthz_test.go index 08e87b80c6076..174cc88002df4 100644 --- a/coderd/database/dbauthz/dbauthz_test.go +++ b/coderd/database/dbauthz/dbauthz_test.go @@ -4332,3 +4332,55 @@ func TestInsertAPIKey_AsPrebuildsUser(t *testing.T) { _, err := dbz.InsertAPIKey(ctx, testutil.Fake(t, faker, database.InsertAPIKeyParams{})) require.True(t, dbauthz.IsNotAuthorizedError(err)) } + +func (s *MethodTestSuite) TestAIBridge() { + s.Run("GetAIBridgeInterceptionByID", s.Mocked(func(db *dbmock.MockStore, faker *gofakeit.Faker, check *expects) { + sessID := uuid.UUID{2} + sess := testutil.Fake(s.T(), faker, database.AIBridgeInterception{ID: sessID}) + db.EXPECT().GetAIBridgeInterceptionByID(gomock.Any(), sessID).Return(sess, nil).AnyTimes() + check.Args(sessID).Asserts(sess, policy.ActionRead).Returns(sess) + })) + + s.Run("InsertAIBridgeInterception", s.Mocked(func(db *dbmock.MockStore, faker *gofakeit.Faker, check *expects) { + initID := uuid.UUID{3} + user := testutil.Fake(s.T(), faker, database.User{ID: initID}) + // testutil.Fake cannot distinguish between a zero value and an explicitly requested value which is equivalent. + user.IsSystem = false + user.Deleted = false + + sessID := uuid.UUID{2} + sess := testutil.Fake(s.T(), faker, database.AIBridgeInterception{ID: sessID, InitiatorID: initID}) + + params := database.InsertAIBridgeInterceptionParams{ID: sess.ID, InitiatorID: sess.InitiatorID, Provider: sess.Provider, Model: sess.Model} + db.EXPECT().GetUserByID(gomock.Any(), initID).Return(user, nil).AnyTimes() // Validation. + db.EXPECT().InsertAIBridgeInterception(gomock.Any(), params).Return(sess, nil).AnyTimes() + check.Args(params).Asserts(sess, policy.ActionCreate) + })) + + s.Run("InsertAIBridgeTokenUsage", s.Mocked(func(db *dbmock.MockStore, faker *gofakeit.Faker, check *expects) { + sessID := uuid.UUID{2} + sess := testutil.Fake(s.T(), faker, database.AIBridgeInterception{ID: sessID}) + params := database.InsertAIBridgeTokenUsageParams{InterceptionID: sess.ID} + db.EXPECT().GetAIBridgeInterceptionByID(gomock.Any(), sessID).Return(sess, nil).AnyTimes() // Validation. + db.EXPECT().InsertAIBridgeTokenUsage(gomock.Any(), params).Return(nil).AnyTimes() + check.Args(params).Asserts(sess, policy.ActionUpdate) + })) + + s.Run("InsertAIBridgeToolUsage", s.Mocked(func(db *dbmock.MockStore, faker *gofakeit.Faker, check *expects) { + sessID := uuid.UUID{2} + sess := testutil.Fake(s.T(), faker, database.AIBridgeInterception{ID: sessID}) + params := database.InsertAIBridgeToolUsageParams{InterceptionID: sess.ID} + db.EXPECT().GetAIBridgeInterceptionByID(gomock.Any(), sessID).Return(sess, nil).AnyTimes() // Validation. + db.EXPECT().InsertAIBridgeToolUsage(gomock.Any(), params).Return(nil).AnyTimes() + check.Args(params).Asserts(sess, policy.ActionUpdate) + })) + + s.Run("InsertAIBridgeUserPrompt", s.Mocked(func(db *dbmock.MockStore, faker *gofakeit.Faker, check *expects) { + sessID := uuid.UUID{2} + sess := testutil.Fake(s.T(), faker, database.AIBridgeInterception{ID: sessID}) + params := database.InsertAIBridgeUserPromptParams{InterceptionID: sess.ID} + db.EXPECT().GetAIBridgeInterceptionByID(gomock.Any(), sessID).Return(sess, nil).AnyTimes() // Validation. + db.EXPECT().InsertAIBridgeUserPrompt(gomock.Any(), params).Return(nil).AnyTimes() + check.Args(params).Asserts(sess, policy.ActionUpdate) + })) +} diff --git a/coderd/database/dbmetrics/querymetrics.go b/coderd/database/dbmetrics/querymetrics.go index 014ec0c12880e..6f520a904a29e 100644 --- a/coderd/database/dbmetrics/querymetrics.go +++ b/coderd/database/dbmetrics/querymetrics.go @@ -586,6 +586,13 @@ func (m queryMetricsStore) FindMatchingPresetID(ctx context.Context, arg databas return r0, r1 } +func (m queryMetricsStore) GetAIBridgeInterceptionByID(ctx context.Context, id uuid.UUID) (database.AIBridgeInterception, error) { + start := time.Now() + r0, r1 := m.s.GetAIBridgeInterceptionByID(ctx, id) + m.queryLatencies.WithLabelValues("GetAIBridgeInterceptionByID").Observe(time.Since(start).Seconds()) + return r0, r1 +} + func (m queryMetricsStore) GetAPIKeyByID(ctx context.Context, id string) (database.APIKey, error) { start := time.Now() apiKey, err := m.s.GetAPIKeyByID(ctx, id) @@ -2168,6 +2175,34 @@ func (m queryMetricsStore) GetWorkspacesEligibleForTransition(ctx context.Contex return workspaces, err } +func (m queryMetricsStore) InsertAIBridgeInterception(ctx context.Context, arg database.InsertAIBridgeInterceptionParams) (database.AIBridgeInterception, error) { + start := time.Now() + r0, r1 := m.s.InsertAIBridgeInterception(ctx, arg) + m.queryLatencies.WithLabelValues("InsertAIBridgeInterception").Observe(time.Since(start).Seconds()) + return r0, r1 +} + +func (m queryMetricsStore) InsertAIBridgeTokenUsage(ctx context.Context, arg database.InsertAIBridgeTokenUsageParams) error { + start := time.Now() + r0 := m.s.InsertAIBridgeTokenUsage(ctx, arg) + m.queryLatencies.WithLabelValues("InsertAIBridgeTokenUsage").Observe(time.Since(start).Seconds()) + return r0 +} + +func (m queryMetricsStore) InsertAIBridgeToolUsage(ctx context.Context, arg database.InsertAIBridgeToolUsageParams) error { + start := time.Now() + r0 := m.s.InsertAIBridgeToolUsage(ctx, arg) + m.queryLatencies.WithLabelValues("InsertAIBridgeToolUsage").Observe(time.Since(start).Seconds()) + return r0 +} + +func (m queryMetricsStore) InsertAIBridgeUserPrompt(ctx context.Context, arg database.InsertAIBridgeUserPromptParams) error { + start := time.Now() + r0 := m.s.InsertAIBridgeUserPrompt(ctx, arg) + m.queryLatencies.WithLabelValues("InsertAIBridgeUserPrompt").Observe(time.Since(start).Seconds()) + return r0 +} + func (m queryMetricsStore) InsertAPIKey(ctx context.Context, arg database.InsertAPIKeyParams) (database.APIKey, error) { start := time.Now() key, err := m.s.InsertAPIKey(ctx, arg) diff --git a/coderd/database/dbmock/dbmock.go b/coderd/database/dbmock/dbmock.go index 2a33ea2d52a95..e96759f8288ab 100644 --- a/coderd/database/dbmock/dbmock.go +++ b/coderd/database/dbmock/dbmock.go @@ -1094,6 +1094,21 @@ func (mr *MockStoreMockRecorder) FindMatchingPresetID(ctx, arg any) *gomock.Call return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindMatchingPresetID", reflect.TypeOf((*MockStore)(nil).FindMatchingPresetID), ctx, arg) } +// GetAIBridgeInterceptionByID mocks base method. +func (m *MockStore) GetAIBridgeInterceptionByID(ctx context.Context, id uuid.UUID) (database.AIBridgeInterception, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAIBridgeInterceptionByID", ctx, id) + ret0, _ := ret[0].(database.AIBridgeInterception) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetAIBridgeInterceptionByID indicates an expected call of GetAIBridgeInterceptionByID. +func (mr *MockStoreMockRecorder) GetAIBridgeInterceptionByID(ctx, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAIBridgeInterceptionByID", reflect.TypeOf((*MockStore)(nil).GetAIBridgeInterceptionByID), ctx, id) +} + // GetAPIKeyByID mocks base method. func (m *MockStore) GetAPIKeyByID(ctx context.Context, id string) (database.APIKey, error) { m.ctrl.T.Helper() @@ -4633,6 +4648,63 @@ func (mr *MockStoreMockRecorder) InTx(arg0, arg1 any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InTx", reflect.TypeOf((*MockStore)(nil).InTx), arg0, arg1) } +// InsertAIBridgeInterception mocks base method. +func (m *MockStore) InsertAIBridgeInterception(ctx context.Context, arg database.InsertAIBridgeInterceptionParams) (database.AIBridgeInterception, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "InsertAIBridgeInterception", ctx, arg) + ret0, _ := ret[0].(database.AIBridgeInterception) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// InsertAIBridgeInterception indicates an expected call of InsertAIBridgeInterception. +func (mr *MockStoreMockRecorder) InsertAIBridgeInterception(ctx, arg any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertAIBridgeInterception", reflect.TypeOf((*MockStore)(nil).InsertAIBridgeInterception), ctx, arg) +} + +// InsertAIBridgeTokenUsage mocks base method. +func (m *MockStore) InsertAIBridgeTokenUsage(ctx context.Context, arg database.InsertAIBridgeTokenUsageParams) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "InsertAIBridgeTokenUsage", ctx, arg) + ret0, _ := ret[0].(error) + return ret0 +} + +// InsertAIBridgeTokenUsage indicates an expected call of InsertAIBridgeTokenUsage. +func (mr *MockStoreMockRecorder) InsertAIBridgeTokenUsage(ctx, arg any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertAIBridgeTokenUsage", reflect.TypeOf((*MockStore)(nil).InsertAIBridgeTokenUsage), ctx, arg) +} + +// InsertAIBridgeToolUsage mocks base method. +func (m *MockStore) InsertAIBridgeToolUsage(ctx context.Context, arg database.InsertAIBridgeToolUsageParams) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "InsertAIBridgeToolUsage", ctx, arg) + ret0, _ := ret[0].(error) + return ret0 +} + +// InsertAIBridgeToolUsage indicates an expected call of InsertAIBridgeToolUsage. +func (mr *MockStoreMockRecorder) InsertAIBridgeToolUsage(ctx, arg any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertAIBridgeToolUsage", reflect.TypeOf((*MockStore)(nil).InsertAIBridgeToolUsage), ctx, arg) +} + +// InsertAIBridgeUserPrompt mocks base method. +func (m *MockStore) InsertAIBridgeUserPrompt(ctx context.Context, arg database.InsertAIBridgeUserPromptParams) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "InsertAIBridgeUserPrompt", ctx, arg) + ret0, _ := ret[0].(error) + return ret0 +} + +// InsertAIBridgeUserPrompt indicates an expected call of InsertAIBridgeUserPrompt. +func (mr *MockStoreMockRecorder) InsertAIBridgeUserPrompt(ctx, arg any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertAIBridgeUserPrompt", reflect.TypeOf((*MockStore)(nil).InsertAIBridgeUserPrompt), ctx, arg) +} + // InsertAPIKey mocks base method. func (m *MockStore) InsertAPIKey(ctx context.Context, arg database.InsertAPIKeyParams) (database.APIKey, error) { m.ctrl.T.Helper() diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql index b097766f0d51e..e68c2008e9205 100644 --- a/coderd/database/dump.sql +++ b/coderd/database/dump.sql @@ -847,6 +847,68 @@ BEGIN END; $$; +CREATE TABLE aibridge_interceptions ( + id uuid NOT NULL, + initiator_id uuid NOT NULL, + provider text NOT NULL, + model text NOT NULL, + started_at timestamp with time zone NOT NULL +); + +COMMENT ON TABLE aibridge_interceptions IS 'Audit log of requests intercepted by AI Bridge'; + +COMMENT ON COLUMN aibridge_interceptions.initiator_id IS 'Relates to a users record, but FK is elided for performance.'; + +CREATE TABLE aibridge_token_usages ( + id uuid NOT NULL, + interception_id uuid NOT NULL, + provider_response_id text NOT NULL, + input_tokens bigint NOT NULL, + output_tokens bigint NOT NULL, + metadata jsonb, + created_at timestamp with time zone NOT NULL +); + +COMMENT ON TABLE aibridge_token_usages IS 'Audit log of tokens used by intercepted requests in AI Bridge'; + +COMMENT ON COLUMN aibridge_token_usages.provider_response_id IS 'The ID for the response in which the tokens were used, produced by the provider.'; + +CREATE TABLE aibridge_tool_usages ( + id uuid NOT NULL, + interception_id uuid NOT NULL, + provider_response_id text NOT NULL, + server_url text, + tool text NOT NULL, + input text NOT NULL, + injected boolean DEFAULT false NOT NULL, + invocation_error text, + metadata jsonb, + created_at timestamp with time zone NOT NULL +); + +COMMENT ON TABLE aibridge_tool_usages IS 'Audit log of tool calls in intercepted requests in AI Bridge'; + +COMMENT ON COLUMN aibridge_tool_usages.provider_response_id IS 'The ID for the response in which the tools were used, produced by the provider.'; + +COMMENT ON COLUMN aibridge_tool_usages.server_url IS 'The name of the MCP server against which this tool was invoked. May be NULL, in which case the tool was defined by the client, not injected.'; + +COMMENT ON COLUMN aibridge_tool_usages.injected IS 'Whether this tool was injected; i.e. Bridge injected these tools into the request from an MCP server. If false it means a tool was defined by the client and already existed in the request (MCP or built-in).'; + +COMMENT ON COLUMN aibridge_tool_usages.invocation_error IS 'Only injected tools are invoked.'; + +CREATE TABLE aibridge_user_prompts ( + id uuid NOT NULL, + interception_id uuid NOT NULL, + provider_response_id text NOT NULL, + prompt text NOT NULL, + metadata jsonb, + created_at timestamp with time zone NOT NULL +); + +COMMENT ON TABLE aibridge_user_prompts IS 'Audit log of prompts used by intercepted requests in AI Bridge'; + +COMMENT ON COLUMN aibridge_user_prompts.provider_response_id IS 'The ID for the response to the given prompt, produced by the provider.'; + CREATE TABLE api_keys ( id text NOT NULL, hashed_secret bytea NOT NULL, @@ -2597,6 +2659,18 @@ ALTER TABLE ONLY workspace_resource_metadata ALTER COLUMN id SET DEFAULT nextval ALTER TABLE ONLY workspace_agent_stats ADD CONSTRAINT agent_stats_pkey PRIMARY KEY (id); +ALTER TABLE ONLY aibridge_interceptions + ADD CONSTRAINT aibridge_interceptions_pkey PRIMARY KEY (id); + +ALTER TABLE ONLY aibridge_token_usages + ADD CONSTRAINT aibridge_token_usages_pkey PRIMARY KEY (id); + +ALTER TABLE ONLY aibridge_tool_usages + ADD CONSTRAINT aibridge_tool_usages_pkey PRIMARY KEY (id); + +ALTER TABLE ONLY aibridge_user_prompts + ADD CONSTRAINT aibridge_user_prompts_pkey PRIMARY KEY (id); + ALTER TABLE ONLY api_keys ADD CONSTRAINT api_keys_pkey PRIMARY KEY (id); @@ -2896,6 +2970,20 @@ CREATE INDEX idx_agent_stats_created_at ON workspace_agent_stats USING btree (cr CREATE INDEX idx_agent_stats_user_id ON workspace_agent_stats USING btree (user_id); +CREATE INDEX idx_aibridge_interceptions_initiator_id ON aibridge_interceptions USING btree (initiator_id); + +CREATE INDEX idx_aibridge_token_usages_interception_id ON aibridge_token_usages USING btree (interception_id); + +CREATE INDEX idx_aibridge_token_usages_provider_response_id ON aibridge_token_usages USING btree (provider_response_id); + +CREATE INDEX idx_aibridge_tool_usages_interception_id ON aibridge_tool_usages USING btree (interception_id); + +CREATE INDEX idx_aibridge_tool_usagesprovider_response_id ON aibridge_tool_usages USING btree (provider_response_id); + +CREATE INDEX idx_aibridge_user_prompts_interception_id ON aibridge_user_prompts USING btree (interception_id); + +CREATE INDEX idx_aibridge_user_prompts_provider_response_id ON aibridge_user_prompts USING btree (provider_response_id); + CREATE UNIQUE INDEX idx_api_key_name ON api_keys USING btree (user_id, token_name) WHERE (login_type = 'token'::login_type); CREATE INDEX idx_api_keys_user ON api_keys USING btree (user_id); diff --git a/coderd/database/migrations/000370_aibridge.down.sql b/coderd/database/migrations/000370_aibridge.down.sql new file mode 100644 index 0000000000000..1107b68778900 --- /dev/null +++ b/coderd/database/migrations/000370_aibridge.down.sql @@ -0,0 +1,4 @@ +DROP TABLE IF EXISTS aibridge_tool_usages CASCADE; +DROP TABLE IF EXISTS aibridge_user_prompts CASCADE; +DROP TABLE IF EXISTS aibridge_token_usages CASCADE; +DROP TABLE IF EXISTS aibridge_interceptions CASCADE; diff --git a/coderd/database/migrations/000370_aibridge.up.sql b/coderd/database/migrations/000370_aibridge.up.sql new file mode 100644 index 0000000000000..94f501e18d5a5 --- /dev/null +++ b/coderd/database/migrations/000370_aibridge.up.sql @@ -0,0 +1,68 @@ +CREATE TABLE IF NOT EXISTS aibridge_interceptions ( + id UUID PRIMARY KEY, + initiator_id uuid NOT NULL, + provider TEXT NOT NULL, + model TEXT NOT NULL, + started_at TIMESTAMP WITH TIME ZONE NOT NULL +); + +COMMENT ON TABLE aibridge_interceptions IS 'Audit log of requests intercepted by AI Bridge'; +COMMENT ON COLUMN aibridge_interceptions.initiator_id IS 'Relates to a users record, but FK is elided for performance.'; + +CREATE INDEX idx_aibridge_interceptions_initiator_id ON aibridge_interceptions (initiator_id); + +CREATE TABLE IF NOT EXISTS aibridge_token_usages ( + id UUID PRIMARY KEY, + interception_id UUID NOT NULL, + provider_response_id TEXT NOT NULL, + input_tokens BIGINT NOT NULL, + output_tokens BIGINT NOT NULL, + metadata JSONB DEFAULT NULL, + created_at TIMESTAMP WITH TIME ZONE NOT NULL +); + +COMMENT ON TABLE aibridge_token_usages IS 'Audit log of tokens used by intercepted requests in AI Bridge'; +COMMENT ON COLUMN aibridge_token_usages.provider_response_id IS 'The ID for the response in which the tokens were used, produced by the provider.'; + +CREATE INDEX idx_aibridge_token_usages_interception_id ON aibridge_token_usages (interception_id); + +CREATE INDEX idx_aibridge_token_usages_provider_response_id ON aibridge_token_usages (provider_response_id); + +CREATE TABLE IF NOT EXISTS aibridge_user_prompts ( + id UUID PRIMARY KEY, + interception_id UUID NOT NULL, + provider_response_id TEXT NOT NULL, + prompt TEXT NOT NULL, + metadata JSONB DEFAULT NULL, + created_at TIMESTAMP WITH TIME ZONE NOT NULL +); + +COMMENT ON TABLE aibridge_user_prompts IS 'Audit log of prompts used by intercepted requests in AI Bridge'; +COMMENT ON COLUMN aibridge_user_prompts.provider_response_id IS 'The ID for the response to the given prompt, produced by the provider.'; + +CREATE INDEX idx_aibridge_user_prompts_interception_id ON aibridge_user_prompts (interception_id); + +CREATE INDEX idx_aibridge_user_prompts_provider_response_id ON aibridge_user_prompts (provider_response_id); + +CREATE TABLE IF NOT EXISTS aibridge_tool_usages ( + id UUID PRIMARY KEY, + interception_id UUID NOT NULL, + provider_response_id TEXT NOT NULL, + server_url TEXT NULL, + tool TEXT NOT NULL, + input TEXT NOT NULL, + injected BOOLEAN NOT NULL DEFAULT FALSE, + invocation_error TEXT NULL, + metadata JSONB DEFAULT NULL, + created_at TIMESTAMP WITH TIME ZONE NOT NULL +); + +COMMENT ON TABLE aibridge_tool_usages IS 'Audit log of tool calls in intercepted requests in AI Bridge'; +COMMENT ON COLUMN aibridge_tool_usages.provider_response_id IS 'The ID for the response in which the tools were used, produced by the provider.'; +COMMENT ON COLUMN aibridge_tool_usages.server_url IS 'The name of the MCP server against which this tool was invoked. May be NULL, in which case the tool was defined by the client, not injected.'; +COMMENT ON COLUMN aibridge_tool_usages.injected IS 'Whether this tool was injected; i.e. Bridge injected these tools into the request from an MCP server. If false it means a tool was defined by the client and already existed in the request (MCP or built-in).'; +COMMENT ON COLUMN aibridge_tool_usages.invocation_error IS 'Only injected tools are invoked.'; + +CREATE INDEX idx_aibridge_tool_usages_interception_id ON aibridge_tool_usages (interception_id); + +CREATE INDEX idx_aibridge_tool_usagesprovider_response_id ON aibridge_tool_usages (provider_response_id); diff --git a/coderd/database/migrations/testdata/fixtures/000370_aibridge.up.sql b/coderd/database/migrations/testdata/fixtures/000370_aibridge.up.sql new file mode 100644 index 0000000000000..0a66555eea0f1 --- /dev/null +++ b/coderd/database/migrations/testdata/fixtures/000370_aibridge.up.sql @@ -0,0 +1,79 @@ +INSERT INTO + aibridge_interceptions ( + id, + initiator_id, + provider, + model, + started_at + ) +VALUES ( + 'be003e1e-b38f-43bf-847d-928074dd0aa8', + '30095c71-380b-457a-8995-97b8ee6e5307', + 'openai', + 'gpt-5', + '2025-09-15 12:45:13.921148+00' + ); + +INSERT INTO + aibridge_token_usages ( + id, + interception_id, + provider_response_id, + input_tokens, + output_tokens, + metadata, + created_at + ) +VALUES ( + 'c56ca89d-af65-47b0-871f-0b9cd2af6575', + 'be003e1e-b38f-43bf-847d-928074dd0aa8', + 'chatcmpl-CG2s28QlpKIoooUtXuLTmGbdtyS1k', + 10950, + 118, + '{"prompt_audio": 0, "prompt_cached": 5376, "completion_audio": 0, "completion_reasoning": 64, "completion_accepted_prediction": 0, "completion_rejected_prediction": 0}', + '2025-09-15 12:45:21.674413+00' + ); + +INSERT INTO + aibridge_tool_usages ( + id, + interception_id, + provider_response_id, + server_url, + tool, + input, + injected, + invocation_error, + metadata, + created_at + ) +VALUES ( + '613b4cfa-a257-4e88-99e6-4d2e99ea25f0', + 'be003e1e-b38f-43bf-847d-928074dd0aa8', + 'chatcmpl-CG2ryDxMp6n53aMjgo7P6BHno3fTr', + 'http://localhost:3000/api/experimental/mcp/http', + 'coder_list_workspaces', + '{}', + true, + NULL, + '{}', + '2025-09-15 12:45:17.65274+00' + ); + +INSERT INTO + aibridge_user_prompts ( + id, + interception_id, + provider_response_id, + prompt, + metadata, + created_at + ) +VALUES ( + 'ac1ea8c3-5109-4105-9b62-489fca220ef7', + 'be003e1e-b38f-43bf-847d-928074dd0aa8', + 'chatcmpl-CG2s28QlpKIoooUtXuLTmGbdtyS1k', + 'how many workspaces do i have', + '{}', + '2025-09-15 12:45:21.674335+00' + ); diff --git a/coderd/database/modelmethods.go b/coderd/database/modelmethods.go index e080c7d7e4217..3f3d7b3b85275 100644 --- a/coderd/database/modelmethods.go +++ b/coderd/database/modelmethods.go @@ -636,3 +636,7 @@ func (m WorkspaceAgentVolumeResourceMonitor) Debounce( func (s UserSecret) RBACObject() rbac.Object { return rbac.ResourceUserSecret.WithID(s.ID).WithOwner(s.UserID.String()) } + +func (s AIBridgeInterception) RBACObject() rbac.Object { + return rbac.ResourceAibridgeInterception.WithOwner(s.InitiatorID.String()) +} diff --git a/coderd/database/modelqueries.go b/coderd/database/modelqueries.go index b558bba91efde..49c13e136564d 100644 --- a/coderd/database/modelqueries.go +++ b/coderd/database/modelqueries.go @@ -275,6 +275,7 @@ func (q *sqlQuerier) GetAuthorizedWorkspaces(ctx context.Context, arg GetWorkspa arg.UsingActive, arg.HasAITask, arg.HasExternalAgent, + arg.Shared, arg.RequesterID, arg.Offset, arg.Limit, diff --git a/coderd/database/models.go b/coderd/database/models.go index 7edc4277e4812..622441cd5a4f2 100644 --- a/coderd/database/models.go +++ b/coderd/database/models.go @@ -2955,6 +2955,57 @@ func AllWorkspaceTransitionValues() []WorkspaceTransition { } } +// Audit log of requests intercepted by AI Bridge +type AIBridgeInterception struct { + ID uuid.UUID `db:"id" json:"id"` + // Relates to a users record, but FK is elided for performance. + InitiatorID uuid.UUID `db:"initiator_id" json:"initiator_id"` + Provider string `db:"provider" json:"provider"` + Model string `db:"model" json:"model"` + StartedAt time.Time `db:"started_at" json:"started_at"` +} + +// Audit log of tokens used by intercepted requests in AI Bridge +type AIBridgeTokenUsage struct { + ID uuid.UUID `db:"id" json:"id"` + InterceptionID uuid.UUID `db:"interception_id" json:"interception_id"` + // The ID for the response in which the tokens were used, produced by the provider. + ProviderResponseID string `db:"provider_response_id" json:"provider_response_id"` + InputTokens int64 `db:"input_tokens" json:"input_tokens"` + OutputTokens int64 `db:"output_tokens" json:"output_tokens"` + Metadata pqtype.NullRawMessage `db:"metadata" json:"metadata"` + CreatedAt time.Time `db:"created_at" json:"created_at"` +} + +// Audit log of tool calls in intercepted requests in AI Bridge +type AIBridgeToolUsage struct { + ID uuid.UUID `db:"id" json:"id"` + InterceptionID uuid.UUID `db:"interception_id" json:"interception_id"` + // The ID for the response in which the tools were used, produced by the provider. + ProviderResponseID string `db:"provider_response_id" json:"provider_response_id"` + // The name of the MCP server against which this tool was invoked. May be NULL, in which case the tool was defined by the client, not injected. + ServerUrl sql.NullString `db:"server_url" json:"server_url"` + Tool string `db:"tool" json:"tool"` + Input string `db:"input" json:"input"` + // Whether this tool was injected; i.e. Bridge injected these tools into the request from an MCP server. If false it means a tool was defined by the client and already existed in the request (MCP or built-in). + Injected bool `db:"injected" json:"injected"` + // Only injected tools are invoked. + InvocationError sql.NullString `db:"invocation_error" json:"invocation_error"` + Metadata pqtype.NullRawMessage `db:"metadata" json:"metadata"` + CreatedAt time.Time `db:"created_at" json:"created_at"` +} + +// Audit log of prompts used by intercepted requests in AI Bridge +type AIBridgeUserPrompt struct { + ID uuid.UUID `db:"id" json:"id"` + InterceptionID uuid.UUID `db:"interception_id" json:"interception_id"` + // The ID for the response to the given prompt, produced by the provider. + ProviderResponseID string `db:"provider_response_id" json:"provider_response_id"` + Prompt string `db:"prompt" json:"prompt"` + Metadata pqtype.NullRawMessage `db:"metadata" json:"metadata"` + CreatedAt time.Time `db:"created_at" json:"created_at"` +} + type APIKey struct { ID string `db:"id" json:"id"` // hashed_secret contains a SHA256 hash of the key secret. This is considered a secret and MUST NOT be returned from the API as it is used for API key encryption in app proxying code. diff --git a/coderd/database/querier.go b/coderd/database/querier.go index 1c46afa39821e..2cd8bd3b25f74 100644 --- a/coderd/database/querier.go +++ b/coderd/database/querier.go @@ -148,6 +148,7 @@ type sqlcQuerier interface { // The query finds presets where all preset parameters are present in the provided parameters, // and returns the preset with the most parameters (largest subset). FindMatchingPresetID(ctx context.Context, arg FindMatchingPresetIDParams) (uuid.UUID, error) + GetAIBridgeInterceptionByID(ctx context.Context, id uuid.UUID) (AIBridgeInterception, error) GetAPIKeyByID(ctx context.Context, id string) (APIKey, error) // there is no unique constraint on empty token names GetAPIKeyByName(ctx context.Context, arg GetAPIKeyByNameParams) (APIKey, error) @@ -502,6 +503,10 @@ type sqlcQuerier interface { GetWorkspacesAndAgentsByOwnerID(ctx context.Context, ownerID uuid.UUID) ([]GetWorkspacesAndAgentsByOwnerIDRow, error) GetWorkspacesByTemplateID(ctx context.Context, templateID uuid.UUID) ([]WorkspaceTable, error) GetWorkspacesEligibleForTransition(ctx context.Context, now time.Time) ([]GetWorkspacesEligibleForTransitionRow, error) + InsertAIBridgeInterception(ctx context.Context, arg InsertAIBridgeInterceptionParams) (AIBridgeInterception, error) + InsertAIBridgeTokenUsage(ctx context.Context, arg InsertAIBridgeTokenUsageParams) error + InsertAIBridgeToolUsage(ctx context.Context, arg InsertAIBridgeToolUsageParams) error + InsertAIBridgeUserPrompt(ctx context.Context, arg InsertAIBridgeUserPromptParams) error InsertAPIKey(ctx context.Context, arg InsertAPIKeyParams) (APIKey, error) // We use the organization_id as the id // for simplicity since all users is diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index ebff2c5453150..06342a2ab676f 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -111,6 +111,153 @@ func (q *sqlQuerier) ActivityBumpWorkspace(ctx context.Context, arg ActivityBump return err } +const getAIBridgeInterceptionByID = `-- name: GetAIBridgeInterceptionByID :one +SELECT id, initiator_id, provider, model, started_at FROM aibridge_interceptions WHERE id = $1::uuid +` + +func (q *sqlQuerier) GetAIBridgeInterceptionByID(ctx context.Context, id uuid.UUID) (AIBridgeInterception, error) { + row := q.db.QueryRowContext(ctx, getAIBridgeInterceptionByID, id) + var i AIBridgeInterception + err := row.Scan( + &i.ID, + &i.InitiatorID, + &i.Provider, + &i.Model, + &i.StartedAt, + ) + return i, err +} + +const insertAIBridgeInterception = `-- name: InsertAIBridgeInterception :one +INSERT INTO aibridge_interceptions (id, initiator_id, provider, model, started_at) +VALUES ($1::uuid, $2::uuid, $3, $4, $5) +RETURNING id, initiator_id, provider, model, started_at +` + +type InsertAIBridgeInterceptionParams struct { + ID uuid.UUID `db:"id" json:"id"` + InitiatorID uuid.UUID `db:"initiator_id" json:"initiator_id"` + Provider string `db:"provider" json:"provider"` + Model string `db:"model" json:"model"` + StartedAt time.Time `db:"started_at" json:"started_at"` +} + +func (q *sqlQuerier) InsertAIBridgeInterception(ctx context.Context, arg InsertAIBridgeInterceptionParams) (AIBridgeInterception, error) { + row := q.db.QueryRowContext(ctx, insertAIBridgeInterception, + arg.ID, + arg.InitiatorID, + arg.Provider, + arg.Model, + arg.StartedAt, + ) + var i AIBridgeInterception + err := row.Scan( + &i.ID, + &i.InitiatorID, + &i.Provider, + &i.Model, + &i.StartedAt, + ) + return i, err +} + +const insertAIBridgeTokenUsage = `-- name: InsertAIBridgeTokenUsage :exec +INSERT INTO aibridge_token_usages ( + id, interception_id, provider_response_id, input_tokens, output_tokens, metadata, created_at +) VALUES ( + $1, $2, $3, $4, $5, COALESCE($6::jsonb, '{}'::jsonb), $7 +) +` + +type InsertAIBridgeTokenUsageParams struct { + ID uuid.UUID `db:"id" json:"id"` + InterceptionID uuid.UUID `db:"interception_id" json:"interception_id"` + ProviderResponseID string `db:"provider_response_id" json:"provider_response_id"` + InputTokens int64 `db:"input_tokens" json:"input_tokens"` + OutputTokens int64 `db:"output_tokens" json:"output_tokens"` + Metadata json.RawMessage `db:"metadata" json:"metadata"` + CreatedAt time.Time `db:"created_at" json:"created_at"` +} + +func (q *sqlQuerier) InsertAIBridgeTokenUsage(ctx context.Context, arg InsertAIBridgeTokenUsageParams) error { + _, err := q.db.ExecContext(ctx, insertAIBridgeTokenUsage, + arg.ID, + arg.InterceptionID, + arg.ProviderResponseID, + arg.InputTokens, + arg.OutputTokens, + arg.Metadata, + arg.CreatedAt, + ) + return err +} + +const insertAIBridgeToolUsage = `-- name: InsertAIBridgeToolUsage :exec +INSERT INTO aibridge_tool_usages ( + id, interception_id, provider_response_id, tool, server_url, input, injected, invocation_error, metadata, created_at +) VALUES ( + $1, $2, $3, $4, $5, $6, $7, $8, COALESCE($9::jsonb, '{}'::jsonb), $10 +) +` + +type InsertAIBridgeToolUsageParams struct { + ID uuid.UUID `db:"id" json:"id"` + InterceptionID uuid.UUID `db:"interception_id" json:"interception_id"` + ProviderResponseID string `db:"provider_response_id" json:"provider_response_id"` + Tool string `db:"tool" json:"tool"` + ServerUrl sql.NullString `db:"server_url" json:"server_url"` + Input string `db:"input" json:"input"` + Injected bool `db:"injected" json:"injected"` + InvocationError sql.NullString `db:"invocation_error" json:"invocation_error"` + Metadata json.RawMessage `db:"metadata" json:"metadata"` + CreatedAt time.Time `db:"created_at" json:"created_at"` +} + +func (q *sqlQuerier) InsertAIBridgeToolUsage(ctx context.Context, arg InsertAIBridgeToolUsageParams) error { + _, err := q.db.ExecContext(ctx, insertAIBridgeToolUsage, + arg.ID, + arg.InterceptionID, + arg.ProviderResponseID, + arg.Tool, + arg.ServerUrl, + arg.Input, + arg.Injected, + arg.InvocationError, + arg.Metadata, + arg.CreatedAt, + ) + return err +} + +const insertAIBridgeUserPrompt = `-- name: InsertAIBridgeUserPrompt :exec +INSERT INTO aibridge_user_prompts ( + id, interception_id, provider_response_id, prompt, metadata, created_at +) VALUES ( + $1, $2, $3, $4, COALESCE($5::jsonb, '{}'::jsonb), $6 +) +` + +type InsertAIBridgeUserPromptParams struct { + ID uuid.UUID `db:"id" json:"id"` + InterceptionID uuid.UUID `db:"interception_id" json:"interception_id"` + ProviderResponseID string `db:"provider_response_id" json:"provider_response_id"` + Prompt string `db:"prompt" json:"prompt"` + Metadata json.RawMessage `db:"metadata" json:"metadata"` + CreatedAt time.Time `db:"created_at" json:"created_at"` +} + +func (q *sqlQuerier) InsertAIBridgeUserPrompt(ctx context.Context, arg InsertAIBridgeUserPromptParams) error { + _, err := q.db.ExecContext(ctx, insertAIBridgeUserPrompt, + arg.ID, + arg.InterceptionID, + arg.ProviderResponseID, + arg.Prompt, + arg.Metadata, + arg.CreatedAt, + ) + return err +} + const deleteAPIKeyByID = `-- name: DeleteAPIKeyByID :exec DELETE FROM api_keys @@ -20960,6 +21107,13 @@ WHERE latest_build.has_external_agent = $20 :: boolean ELSE true END + -- Filter by shared status + AND CASE + WHEN $21 :: boolean IS NOT NULL THEN + (workspaces.user_acl != '{}'::jsonb OR workspaces.group_acl != '{}'::jsonb) = $21 :: boolean + ELSE true + END + -- Authorize Filter clause will be injected below in GetAuthorizedWorkspaces -- @authorize_filter ), filtered_workspaces_order AS ( @@ -20969,7 +21123,7 @@ WHERE filtered_workspaces fw ORDER BY -- To ensure that 'favorite' workspaces show up first in the list only for their owner. - CASE WHEN owner_id = $21 AND favorite THEN 0 ELSE 1 END ASC, + CASE WHEN owner_id = $22 AND favorite THEN 0 ELSE 1 END ASC, (latest_build_completed_at IS NOT NULL AND latest_build_canceled_at IS NULL AND latest_build_error IS NULL AND @@ -20978,11 +21132,11 @@ WHERE LOWER(name) ASC LIMIT CASE - WHEN $23 :: integer > 0 THEN - $23 + WHEN $24 :: integer > 0 THEN + $24 END OFFSET - $22 + $23 ), filtered_workspaces_order_with_summary AS ( SELECT fwo.id, fwo.created_at, fwo.updated_at, fwo.owner_id, fwo.organization_id, fwo.template_id, fwo.deleted, fwo.name, fwo.autostart_schedule, fwo.ttl, fwo.last_used_at, fwo.dormant_at, fwo.deleting_at, fwo.automatic_updates, fwo.favorite, fwo.next_start_at, fwo.group_acl, fwo.user_acl, fwo.owner_avatar_url, fwo.owner_username, fwo.owner_name, fwo.organization_name, fwo.organization_display_name, fwo.organization_icon, fwo.organization_description, fwo.template_name, fwo.template_display_name, fwo.template_icon, fwo.template_description, fwo.template_version_id, fwo.template_version_name, fwo.latest_build_completed_at, fwo.latest_build_canceled_at, fwo.latest_build_error, fwo.latest_build_transition, fwo.latest_build_status, fwo.latest_build_has_ai_task, fwo.latest_build_has_external_agent @@ -21032,7 +21186,7 @@ WHERE false, -- latest_build_has_ai_task false -- latest_build_has_external_agent WHERE - $24 :: boolean = true + $25 :: boolean = true ), total_count AS ( SELECT count(*) AS count @@ -21069,6 +21223,7 @@ type GetWorkspacesParams struct { UsingActive sql.NullBool `db:"using_active" json:"using_active"` HasAITask sql.NullBool `db:"has_ai_task" json:"has_ai_task"` HasExternalAgent sql.NullBool `db:"has_external_agent" json:"has_external_agent"` + Shared sql.NullBool `db:"shared" json:"shared"` RequesterID uuid.UUID `db:"requester_id" json:"requester_id"` Offset int32 `db:"offset_" json:"offset_"` Limit int32 `db:"limit_" json:"limit_"` @@ -21142,6 +21297,7 @@ func (q *sqlQuerier) GetWorkspaces(ctx context.Context, arg GetWorkspacesParams) arg.UsingActive, arg.HasAITask, arg.HasExternalAgent, + arg.Shared, arg.RequesterID, arg.Offset, arg.Limit, diff --git a/coderd/database/queries/aibridge.sql b/coderd/database/queries/aibridge.sql new file mode 100644 index 0000000000000..0416bebefecc2 --- /dev/null +++ b/coderd/database/queries/aibridge.sql @@ -0,0 +1,28 @@ +-- name: InsertAIBridgeInterception :one +INSERT INTO aibridge_interceptions (id, initiator_id, provider, model, started_at) +VALUES (@id::uuid, @initiator_id::uuid, @provider, @model, @started_at) +RETURNING *; + +-- name: InsertAIBridgeTokenUsage :exec +INSERT INTO aibridge_token_usages ( + id, interception_id, provider_response_id, input_tokens, output_tokens, metadata, created_at +) VALUES ( + @id, @interception_id, @provider_response_id, @input_tokens, @output_tokens, COALESCE(@metadata::jsonb, '{}'::jsonb), @created_at +); + +-- name: InsertAIBridgeUserPrompt :exec +INSERT INTO aibridge_user_prompts ( + id, interception_id, provider_response_id, prompt, metadata, created_at +) VALUES ( + @id, @interception_id, @provider_response_id, @prompt, COALESCE(@metadata::jsonb, '{}'::jsonb), @created_at +); + +-- name: InsertAIBridgeToolUsage :exec +INSERT INTO aibridge_tool_usages ( + id, interception_id, provider_response_id, tool, server_url, input, injected, invocation_error, metadata, created_at +) VALUES ( + @id, @interception_id, @provider_response_id, @tool, @server_url, @input, @injected, @invocation_error, COALESCE(@metadata::jsonb, '{}'::jsonb), @created_at +); + +-- name: GetAIBridgeInterceptionByID :one +SELECT * FROM aibridge_interceptions WHERE id = @id::uuid; diff --git a/coderd/database/queries/workspaces.sql b/coderd/database/queries/workspaces.sql index a4a3200f5c3d5..9190c263c5d65 100644 --- a/coderd/database/queries/workspaces.sql +++ b/coderd/database/queries/workspaces.sql @@ -378,6 +378,13 @@ WHERE latest_build.has_external_agent = sqlc.narg('has_external_agent') :: boolean ELSE true END + -- Filter by shared status + AND CASE + WHEN sqlc.narg('shared') :: boolean IS NOT NULL THEN + (workspaces.user_acl != '{}'::jsonb OR workspaces.group_acl != '{}'::jsonb) = sqlc.narg('shared') :: boolean + ELSE true + END + -- Authorize Filter clause will be injected below in GetAuthorizedWorkspaces -- @authorize_filter ), filtered_workspaces_order AS ( diff --git a/coderd/database/sqlc.yaml b/coderd/database/sqlc.yaml index 689eb1aaeb53b..f23d8df2aa043 100644 --- a/coderd/database/sqlc.yaml +++ b/coderd/database/sqlc.yaml @@ -163,6 +163,10 @@ sql: ai_task_sidebar_app_id: AITaskSidebarAppID latest_build_has_ai_task: LatestBuildHasAITask cors_behavior: CorsBehavior + aibridge_interception: AIBridgeInterception + aibridge_tool_usage: AIBridgeToolUsage + aibridge_token_usage: AIBridgeTokenUsage + aibridge_user_prompt: AIBridgeUserPrompt rules: - name: do-not-use-public-schema-in-queries message: "do not use public schema in queries" diff --git a/coderd/database/unique_constraint.go b/coderd/database/unique_constraint.go index 02982edc517fb..36fca8f058135 100644 --- a/coderd/database/unique_constraint.go +++ b/coderd/database/unique_constraint.go @@ -7,6 +7,10 @@ type UniqueConstraint string // UniqueConstraint enums. const ( UniqueAgentStatsPkey UniqueConstraint = "agent_stats_pkey" // ALTER TABLE ONLY workspace_agent_stats ADD CONSTRAINT agent_stats_pkey PRIMARY KEY (id); + UniqueAibridgeInterceptionsPkey UniqueConstraint = "aibridge_interceptions_pkey" // ALTER TABLE ONLY aibridge_interceptions ADD CONSTRAINT aibridge_interceptions_pkey PRIMARY KEY (id); + UniqueAibridgeTokenUsagesPkey UniqueConstraint = "aibridge_token_usages_pkey" // ALTER TABLE ONLY aibridge_token_usages ADD CONSTRAINT aibridge_token_usages_pkey PRIMARY KEY (id); + UniqueAibridgeToolUsagesPkey UniqueConstraint = "aibridge_tool_usages_pkey" // ALTER TABLE ONLY aibridge_tool_usages ADD CONSTRAINT aibridge_tool_usages_pkey PRIMARY KEY (id); + UniqueAibridgeUserPromptsPkey UniqueConstraint = "aibridge_user_prompts_pkey" // ALTER TABLE ONLY aibridge_user_prompts ADD CONSTRAINT aibridge_user_prompts_pkey PRIMARY KEY (id); UniqueAPIKeysPkey UniqueConstraint = "api_keys_pkey" // ALTER TABLE ONLY api_keys ADD CONSTRAINT api_keys_pkey PRIMARY KEY (id); UniqueAuditLogsPkey UniqueConstraint = "audit_logs_pkey" // ALTER TABLE ONLY audit_logs ADD CONSTRAINT audit_logs_pkey PRIMARY KEY (id); UniqueConnectionLogsPkey UniqueConstraint = "connection_logs_pkey" // ALTER TABLE ONLY connection_logs ADD CONSTRAINT connection_logs_pkey PRIMARY KEY (id); diff --git a/coderd/externalauth/externalauth.go b/coderd/externalauth/externalauth.go index 24ebe13d03074..55dba499e6845 100644 --- a/coderd/externalauth/externalauth.go +++ b/coderd/externalauth/externalauth.go @@ -81,6 +81,19 @@ type Config struct { // AppInstallationsURL is an API endpoint that returns a list of // installations for the user. This is used for GitHub Apps. AppInstallationsURL string + // MCPURL is the endpoint that clients must use to communicate with the associated + // MCP server. + MCPURL string + // MCPToolAllowRegex is a [regexp.Regexp] to match tools which are explicitly allowed to be + // injected into Coder AI Bridge upstream requests. + // In the case of conflicts, [MCPToolDenylistPattern] overrides items evaluated by this list. + // This field can be nil if unspecified in the config. + MCPToolAllowRegex *regexp.Regexp + // MCPToolDenyRegex is a [regexp.Regexp] to match tools which are explicitly NOT allowed to be + // injected into Coder AI Bridge upstream requests. + // In the case of conflicts, items evaluated by this list override [MCPToolAllowRegex]. + // This field can be nil if unspecified in the config. + MCPToolDenyRegex *regexp.Regexp } // GenerateTokenExtra generates the extra token data to store in the database. @@ -608,6 +621,21 @@ func ConvertConfig(instrument *promoauth.Factory, entries []codersdk.ExternalAut instrumented = instrument.NewGithub(entry.ID, oauthConfig) } + var mcpToolAllow *regexp.Regexp + var mcpToolDeny *regexp.Regexp + if entry.MCPToolAllowRegex != "" { + mcpToolAllow, err = regexp.Compile(entry.MCPToolAllowRegex) + if err != nil { + return nil, xerrors.Errorf("compile MCP tool allow regex for external auth provider %q: %w", entry.ID, entry.MCPToolAllowRegex) + } + } + if entry.MCPToolDenyRegex != "" { + mcpToolDeny, err = regexp.Compile(entry.MCPToolDenyRegex) + if err != nil { + return nil, xerrors.Errorf("compile MCP tool deny regex for external auth provider %q: %w", entry.ID, entry.MCPToolDenyRegex) + } + } + cfg := &Config{ InstrumentedOAuth2Config: instrumented, ID: entry.ID, @@ -620,6 +648,9 @@ func ConvertConfig(instrument *promoauth.Factory, entries []codersdk.ExternalAut DisplayName: entry.DisplayName, DisplayIcon: entry.DisplayIcon, ExtraTokenKeys: entry.ExtraTokenKeys, + MCPURL: entry.MCPURL, + MCPToolAllowRegex: mcpToolAllow, + MCPToolDenyRegex: mcpToolDeny, } if entry.DeviceFlow { diff --git a/coderd/rbac/authz.go b/coderd/rbac/authz.go index 0b48a24aebe83..0715a8ead7783 100644 --- a/coderd/rbac/authz.go +++ b/coderd/rbac/authz.go @@ -77,6 +77,7 @@ const ( SubjectTypeSubAgentAPI SubjectType = "sub_agent_api" SubjectTypeFileReader SubjectType = "file_reader" SubjectTypeUsagePublisher SubjectType = "usage_publisher" + SubjectAibridged SubjectType = "aibridged" ) const ( diff --git a/coderd/rbac/object_gen.go b/coderd/rbac/object_gen.go index de05dced2693d..d0c78bd480766 100644 --- a/coderd/rbac/object_gen.go +++ b/coderd/rbac/object_gen.go @@ -15,6 +15,15 @@ var ( Type: "*", } + // ResourceAibridgeInterception + // Valid Actions + // - "ActionCreate" :: create aibridge interceptions & related records + // - "ActionRead" :: read aibridge interceptions & related records + // - "ActionUpdate" :: update aibridge interceptions & related records + ResourceAibridgeInterception = Object{ + Type: "aibridge_interception", + } + // ResourceApiKey // Valid Actions // - "ActionCreate" :: create an api key @@ -391,6 +400,7 @@ var ( func AllResources() []Objecter { return []Objecter{ ResourceWildcard, + ResourceAibridgeInterception, ResourceApiKey, ResourceAssignOrgRole, ResourceAssignRole, diff --git a/coderd/rbac/policy/policy.go b/coderd/rbac/policy/policy.go index 25fb87bfc2d94..93b0ba4e76215 100644 --- a/coderd/rbac/policy/policy.go +++ b/coderd/rbac/policy/policy.go @@ -358,4 +358,11 @@ var RBACPermissions = map[string]PermissionDefinition{ ActionUpdate: "update usage events", }, }, + "aibridge_interception": { + Actions: map[Action]ActionDefinition{ + ActionRead: "read aibridge interceptions & related records", + ActionUpdate: "update aibridge interceptions & related records", + ActionCreate: "create aibridge interceptions & related records", + }, + }, } diff --git a/coderd/rbac/roles_test.go b/coderd/rbac/roles_test.go index 57a5022392b51..18d521d4748eb 100644 --- a/coderd/rbac/roles_test.go +++ b/coderd/rbac/roles_test.go @@ -888,6 +888,21 @@ func TestRolePermissions(t *testing.T) { }, }, }, + { + Name: "AIBridgeInterceptions", + Actions: []policy.Action{policy.ActionCreate, policy.ActionRead, policy.ActionUpdate}, + Resource: rbac.ResourceAibridgeInterception.WithOwner(currentUser.String()), + AuthorizeMap: map[bool][]hasAuthSubjects{ + true: {owner, memberMe, orgMemberMe}, + false: { + otherOrgMember, + orgAdmin, otherOrgAdmin, + orgAuditor, otherOrgAuditor, + templateAdmin, orgTemplateAdmin, otherOrgTemplateAdmin, + userAdmin, orgUserAdmin, otherOrgUserAdmin, + }, + }, + }, } // We expect every permission to be tested above. diff --git a/coderd/searchquery/search.go b/coderd/searchquery/search.go index 0ab700fbee501..5e7644d99bfda 100644 --- a/coderd/searchquery/search.go +++ b/coderd/searchquery/search.go @@ -225,6 +225,7 @@ func Workspaces(ctx context.Context, db database.Store, query string, page coder filter.HasAITask = parser.NullableBoolean(values, sql.NullBool{}, "has-ai-task") filter.HasExternalAgent = parser.NullableBoolean(values, sql.NullBool{}, "has_external_agent") filter.OrganizationID = parseOrganization(ctx, db, parser, values, "organization") + filter.Shared = parser.NullableBoolean(values, sql.NullBool{}, "shared") type paramMatch struct { name string diff --git a/coderd/searchquery/search_test.go b/coderd/searchquery/search_test.go index 5c52e1585164b..60e97c6d63659 100644 --- a/coderd/searchquery/search_test.go +++ b/coderd/searchquery/search_test.go @@ -282,6 +282,36 @@ func TestSearchWorkspace(t *testing.T) { }, }, }, + { + Name: "SharedTrue", + Query: "shared:true", + Expected: database.GetWorkspacesParams{ + Shared: sql.NullBool{ + Bool: true, + Valid: true, + }, + }, + }, + { + Name: "SharedFalse", + Query: "shared:false", + Expected: database.GetWorkspacesParams{ + Shared: sql.NullBool{ + Bool: false, + Valid: true, + }, + }, + }, + { + Name: "SharedMissing", + Query: "", + Expected: database.GetWorkspacesParams{ + Shared: sql.NullBool{ + Bool: false, + Valid: false, + }, + }, + }, // Failures { diff --git a/coderd/workspaceagentsrpc_internal_test.go b/coderd/workspaceagentsrpc_internal_test.go index f2a2c7c87fa37..d44a6669790e5 100644 --- a/coderd/workspaceagentsrpc_internal_test.go +++ b/coderd/workspaceagentsrpc_internal_test.go @@ -150,9 +150,14 @@ func TestAgentConnectionMonitor_PingTimeout(t *testing.T) { AnyTimes(). Return(database.WorkspaceBuild{ID: build.ID}, nil) - go uut.monitor(ctx) + done := make(chan struct{}) + go func() { + uut.monitor(ctx) + close(done) + }() fConn.requireEventuallyClosed(t, websocket.StatusGoingAway, "ping timeout") fUpdater.requireEventuallySomeUpdates(t, build.WorkspaceID) + _ = testutil.TryReceive(ctx, t, done) // ensure monitor() exits before mDB assertions are checked. } func TestAgentConnectionMonitor_BuildOutdated(t *testing.T) { diff --git a/coderd/workspaces_test.go b/coderd/workspaces_test.go index 6045745debb3d..98cfba017a17a 100644 --- a/coderd/workspaces_test.go +++ b/coderd/workspaces_test.go @@ -1812,6 +1812,82 @@ func TestWorkspaceFilter(t *testing.T) { require.ElementsMatch(t, exp, workspaces, "expected workspaces returned") }) } + + t.Run("SharedWithUser", func(t *testing.T) { + t.Parallel() + + dv := coderdtest.DeploymentValues(t) + dv.Experiments = []string{string(codersdk.ExperimentWorkspaceSharing)} + + var ( + client, db = coderdtest.NewWithDatabase(t, &coderdtest.Options{ + DeploymentValues: dv, + }) + orgOwner = coderdtest.CreateFirstUser(t, client) + _, workspaceOwner = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID, rbac.ScopedRoleOrgAuditor(orgOwner.OrganizationID)) + sharedWorkspace = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{ + OwnerID: workspaceOwner.ID, + OrganizationID: orgOwner.OrganizationID, + }).Do().Workspace + _ = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{ + OwnerID: workspaceOwner.ID, + OrganizationID: orgOwner.OrganizationID, + }).Do().Workspace + _, toShareWithUser = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID) + ctx = testutil.Context(t, testutil.WaitMedium) + ) + + client.UpdateWorkspaceACL(ctx, sharedWorkspace.ID, codersdk.UpdateWorkspaceACL{ + UserRoles: map[string]codersdk.WorkspaceRole{ + toShareWithUser.ID.String(): codersdk.WorkspaceRoleUse, + }, + }) + + workspaces, err := client.Workspaces(ctx, codersdk.WorkspaceFilter{ + Shared: ptr.Ref(true), + }) + require.NoError(t, err, "fetch workspaces") + require.Equal(t, 1, workspaces.Count, "expected only one workspace") + require.Equal(t, workspaces.Workspaces[0].ID, sharedWorkspace.ID) + }) + + t.Run("NotSharedWithUser", func(t *testing.T) { + t.Parallel() + + dv := coderdtest.DeploymentValues(t) + dv.Experiments = []string{string(codersdk.ExperimentWorkspaceSharing)} + + var ( + client, db = coderdtest.NewWithDatabase(t, &coderdtest.Options{ + DeploymentValues: dv, + }) + orgOwner = coderdtest.CreateFirstUser(t, client) + _, workspaceOwner = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID, rbac.ScopedRoleOrgAuditor(orgOwner.OrganizationID)) + sharedWorkspace = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{ + OwnerID: workspaceOwner.ID, + OrganizationID: orgOwner.OrganizationID, + }).Do().Workspace + notSharedWorkspace = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{ + OwnerID: workspaceOwner.ID, + OrganizationID: orgOwner.OrganizationID, + }).Do().Workspace + _, toShareWithUser = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID) + ctx = testutil.Context(t, testutil.WaitMedium) + ) + + client.UpdateWorkspaceACL(ctx, sharedWorkspace.ID, codersdk.UpdateWorkspaceACL{ + UserRoles: map[string]codersdk.WorkspaceRole{ + toShareWithUser.ID.String(): codersdk.WorkspaceRoleUse, + }, + }) + + workspaces, err := client.Workspaces(ctx, codersdk.WorkspaceFilter{ + Shared: ptr.Ref(false), + }) + require.NoError(t, err, "fetch workspaces") + require.Equal(t, 1, workspaces.Count, "expected only one workspace") + require.Equal(t, workspaces.Workspaces[0].ID, notSharedWorkspace.ID) + }) } // TestWorkspaceFilterManual runs some specific setups with basic checks. @@ -4917,15 +4993,14 @@ func TestUpdateWorkspaceACL(t *testing.T) { func TestDeleteWorkspaceACL(t *testing.T) { t.Parallel() - dv := coderdtest.DeploymentValues(t) - dv.Experiments = []string{string(codersdk.ExperimentWorkspaceSharing)} - t.Run("WorkspaceOwnerCanDelete", func(t *testing.T) { t.Parallel() var ( client, db = coderdtest.NewWithDatabase(t, &coderdtest.Options{ - DeploymentValues: dv, + DeploymentValues: coderdtest.DeploymentValues(t, func(dv *codersdk.DeploymentValues) { + dv.Experiments = []string{string(codersdk.ExperimentWorkspaceSharing)} + }), }) admin = coderdtest.CreateFirstUser(t, client) workspaceOwnerClient, workspaceOwner = coderdtest.CreateAnotherUser(t, client, admin.OrganizationID) @@ -4958,7 +5033,9 @@ func TestDeleteWorkspaceACL(t *testing.T) { var ( client, db = coderdtest.NewWithDatabase(t, &coderdtest.Options{ - DeploymentValues: dv, + DeploymentValues: coderdtest.DeploymentValues(t, func(dv *codersdk.DeploymentValues) { + dv.Experiments = []string{string(codersdk.ExperimentWorkspaceSharing)} + }), }) admin = coderdtest.CreateFirstUser(t, client) workspaceOwnerClient, workspaceOwner = coderdtest.CreateAnotherUser(t, client, admin.OrganizationID) diff --git a/codersdk/deployment.go b/codersdk/deployment.go index 2284e0266e148..47ae4f51c1379 100644 --- a/codersdk/deployment.go +++ b/codersdk/deployment.go @@ -500,6 +500,7 @@ type DeploymentValues struct { WorkspaceHostnameSuffix serpent.String `json:"workspace_hostname_suffix,omitempty" typescript:",notnull"` Prebuilds PrebuildsConfig `json:"workspace_prebuilds,omitempty" typescript:",notnull"` HideAITasks serpent.Bool `json:"hide_ai_tasks,omitempty" typescript:",notnull"` + AI AIConfig `json:"ai,omitempty"` Config serpent.YAMLConfigPath `json:"config,omitempty" typescript:",notnull"` WriteConfig serpent.Bool `json:"write_config,omitempty" typescript:",notnull"` @@ -741,6 +742,9 @@ type ExternalAuthConfig struct { ExtraTokenKeys []string `json:"-" yaml:"extra_token_keys"` DeviceFlow bool `json:"device_flow" yaml:"device_flow"` DeviceCodeURL string `json:"device_code_url" yaml:"device_code_url"` + MCPURL string `json:"mcp_url" yaml:"mcp_url"` + MCPToolAllowRegex string `json:"mcp_tool_allow_regex" yaml:"mcp_tool_allow_regex"` + MCPToolDenyRegex string `json:"mcp_tool_deny_regex" yaml:"mcp_tool_deny_regex"` // Regex allows API requesters to match an auth config by // a string (e.g. coder.com) instead of by it's type. // @@ -1163,6 +1167,10 @@ func (c *DeploymentValues) Options() serpent.OptionSet { Parent: &deploymentGroupNotifications, YAML: "inbox", } + deploymentGroupAIBridge = serpent.Group{ + Name: "AIBridge", + YAML: "aibridge", + } ) httpAddress := serpent.Option{ @@ -3223,11 +3231,88 @@ Write out the current server config as YAML to stdout.`, Group: &deploymentGroupClient, YAML: "hideAITasks", }, + + // AIBridge Options + { + Name: "AIBridge Enabled", + Description: fmt.Sprintf("Whether to start an in-memory aibridged instance (%q experiment must be enabled, too).", ExperimentAIBridge), + Flag: "aibridge-enabled", + Env: "CODER_AIBRIDGE_ENABLED", + Value: &c.AI.BridgeConfig.Enabled, + Default: "false", + Group: &deploymentGroupAIBridge, + YAML: "enabled", + Hidden: true, + }, + { + Name: "AIBridge OpenAI Base URL", + Description: "The base URL of the OpenAI API.", + Flag: "aibridge-openai-base-url", + Env: "CODER_AIBRIDGE_OPENAI_BASE_URL", + Value: &c.AI.BridgeConfig.OpenAI.BaseURL, + Default: "https://api.openai.com/v1/", + Group: &deploymentGroupAIBridge, + YAML: "openai_base_url", + Hidden: true, + }, + { + Name: "AIBridge OpenAI Key", + Description: "The key to authenticate against the OpenAI API.", + Flag: "aibridge-openai-key", + Env: "CODER_AIBRIDGE_OPENAI_KEY", + Value: &c.AI.BridgeConfig.OpenAI.Key, + Default: "", + Group: &deploymentGroupAIBridge, + YAML: "openai_key", + Hidden: true, + }, + { + Name: "AIBridge Anthropic Base URL", + Description: "The base URL of the Anthropic API.", + Flag: "aibridge-anthropic-base-url", + Env: "CODER_AIBRIDGE_ANTHROPIC_BASE_URL", + Value: &c.AI.BridgeConfig.Anthropic.BaseURL, + Default: "https://api.anthropic.com/", + Group: &deploymentGroupAIBridge, + YAML: "base_url", + Hidden: true, + }, + { + Name: "AIBridge Anthropic KEY", + Description: "The key to authenticate against the Anthropic API.", + Flag: "aibridge-anthropic-key", + Env: "CODER_AIBRIDGE_ANTHROPIC_KEY", + Value: &c.AI.BridgeConfig.Anthropic.Key, + Default: "", + Group: &deploymentGroupAIBridge, + YAML: "key", + Hidden: true, + }, } return opts } +type AIBridgeConfig struct { + Enabled serpent.Bool `json:"enabled" typescript:",notnull"` + OpenAI AIBridgeOpenAIConfig `json:"openai" typescript:",notnull"` + Anthropic AIBridgeAnthropicConfig `json:"anthropic" typescript:",notnull"` +} + +type AIBridgeOpenAIConfig struct { + BaseURL serpent.String `json:"base_url" typescript:",notnull"` + Key serpent.String `json:"key" typescript:",notnull"` +} + +type AIBridgeAnthropicConfig struct { + BaseURL serpent.String `json:"base_url" typescript:",notnull"` + Key serpent.String `json:"key" typescript:",notnull"` +} + +type AIConfig struct { + BridgeConfig AIBridgeConfig `json:"bridge,omitempty"` +} + type SupportConfig struct { Links serpent.Struct[[]LinkConfig] `json:"links" typescript:",notnull"` } @@ -3475,6 +3560,7 @@ const ( ExperimentOAuth2 Experiment = "oauth2" // Enables OAuth2 provider functionality. ExperimentMCPServerHTTP Experiment = "mcp-server-http" // Enables the MCP HTTP server functionality. ExperimentWorkspaceSharing Experiment = "workspace-sharing" // Enables updating workspace ACLs for sharing with users and groups. + ExperimentAIBridge Experiment = "aibridge" // Enables AI Bridge functionality. ) func (e Experiment) DisplayName() string { @@ -3495,6 +3581,8 @@ func (e Experiment) DisplayName() string { return "MCP HTTP Server Functionality" case ExperimentWorkspaceSharing: return "Workspace Sharing" + case ExperimentAIBridge: + return "AI Bridge" default: // Split on hyphen and convert to title case // e.g. "web-push" -> "Web Push", "mcp-server-http" -> "Mcp Server Http" @@ -3513,6 +3601,7 @@ var ExperimentsKnown = Experiments{ ExperimentOAuth2, ExperimentMCPServerHTTP, ExperimentWorkspaceSharing, + ExperimentAIBridge, } // ExperimentsSafe should include all experiments that are safe for diff --git a/codersdk/deployment_test.go b/codersdk/deployment_test.go index c113d46cc50e4..fc8f532d8f1f5 100644 --- a/codersdk/deployment_test.go +++ b/codersdk/deployment_test.go @@ -399,6 +399,9 @@ func TestExternalAuthYAMLConfig(t *testing.T) { Regex: "^https://example.com/.*$", DisplayName: "GitHub", DisplayIcon: "/static/icons/github.svg", + MCPURL: "https://api.githubcopilot.com/mcp/", + MCPToolAllowRegex: ".*", + MCPToolDenyRegex: "create_gist", } // Input the github section twice for testing a slice of configs. diff --git a/codersdk/rbacresources_gen.go b/codersdk/rbacresources_gen.go index 54532106a6fd1..6a8185e6eb62a 100644 --- a/codersdk/rbacresources_gen.go +++ b/codersdk/rbacresources_gen.go @@ -5,6 +5,7 @@ type RBACResource string const ( ResourceWildcard RBACResource = "*" + ResourceAibridgeInterception RBACResource = "aibridge_interception" ResourceApiKey RBACResource = "api_key" ResourceAssignOrgRole RBACResource = "assign_org_role" ResourceAssignRole RBACResource = "assign_role" @@ -71,6 +72,7 @@ const ( // said resource type. var RBACResourceActions = map[RBACResource][]RBACAction{ ResourceWildcard: {}, + ResourceAibridgeInterception: {ActionCreate, ActionRead, ActionUpdate}, ResourceApiKey: {ActionCreate, ActionDelete, ActionRead, ActionUpdate}, ResourceAssignOrgRole: {ActionAssign, ActionCreate, ActionDelete, ActionRead, ActionUnassign, ActionUpdate}, ResourceAssignRole: {ActionAssign, ActionRead, ActionUnassign}, diff --git a/codersdk/testdata/githubcfg.yaml b/codersdk/testdata/githubcfg.yaml index 50c16fb30f63c..c88cea947d53b 100644 --- a/codersdk/testdata/githubcfg.yaml +++ b/codersdk/testdata/githubcfg.yaml @@ -17,6 +17,9 @@ externalAuthProviders: - token device_flow: true device_code_url: https://example.com/device + mcp_url: https://api.githubcopilot.com/mcp/ + mcp_tool_allow_regex: .* + mcp_tool_deny_regex: create_gist regex: ^https://example.com/.*$ display_name: GitHub display_icon: /static/icons/github.svg diff --git a/codersdk/workspaces.go b/codersdk/workspaces.go index a006595f0eba6..32ec26643ca56 100644 --- a/codersdk/workspaces.go +++ b/codersdk/workspaces.go @@ -516,6 +516,8 @@ type WorkspaceFilter struct { Offset int `json:"offset,omitempty" typescript:"-"` // Limit is a limit on the number of workspaces returned. Limit int `json:"limit,omitempty" typescript:"-"` + // Shared is a whether the workspace is shared with any users or groups + Shared *bool `json:"shared,omitempty" typescript:"-"` // FilterQuery supports a raw filter query string FilterQuery string `json:"q,omitempty"` } @@ -539,6 +541,9 @@ func (f WorkspaceFilter) asRequestOption() RequestOption { if f.Status != "" { params = append(params, fmt.Sprintf("status:%q", f.Status)) } + if f.Shared != nil { + params = append(params, fmt.Sprintf("shared:%v", *f.Shared)) + } if f.FilterQuery != "" { // If custom stuff is added, just add it on here. params = append(params, f.FilterQuery) diff --git a/docs/ai-coder/tasks.md b/docs/ai-coder/tasks.md index ef47a6b3fb874..38349b2ae8a20 100644 --- a/docs/ai-coder/tasks.md +++ b/docs/ai-coder/tasks.md @@ -46,7 +46,7 @@ To import the template and begin configuring it, follow the [documentation in th ### Option 2) Create or Duplicate Your Own Template -A template becomes a Task template if it defines a `coder_ai_task` resource and a `coder_parameter` named `"AI Prompt"`. Coder analyzes template files during template version import to determine if these requirements are met. +A template becomes a Task template if it defines a `coder_ai_task` resource and a `coder_parameter` named `"AI Prompt"`. Coder analyzes template files during template version import to determine if these requirements are met. Try adding this terraform block to an existing template where you'll add our Claude Code module. Note: the `coder_ai_task` resource is defined within the [Claude Code Module](https://registry.coder.com/modules/coder/claude-code?tab=readme), so it's not defined within this block. ```hcl data "coder_parameter" "ai_prompt" { @@ -54,16 +54,45 @@ data "coder_parameter" "ai_prompt" { type = "string" } -# Multiple coder_ai_tasks can be defined in a template -resource "coder_ai_task" "claude-code" { - # At most one coder ai task can be instantiated during a workspace build. - # Coder fails the build if it would instantiate more than 1. - count = data.coder_parameter.ai_prompt.value != "" ? 1 : 0 +data "coder_parameter" "setup_script" { + name = "setup_script" + display_name = "Setup Script" + type = "string" + form_type = "textarea" + description = "Script to run before running the agent" + mutable = false + default = "" +} + +# The Claude Code module does the automatic task reporting +# Other agent modules: https://registry.coder.com/modules?search=agent +# Or use a custom agent: +module "claude-code" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder/claude-code/coder" + version = "2.2.0" + agent_id = coder_agent.main.id + folder = "/home/coder/projects" + install_claude_code = true + claude_code_version = "latest" + order = 999 + + # experiment_post_install_script = data.coder_parameter.setup_script.value + + # This enables Coder Tasks + experiment_report_tasks = true +} + +variable "anthropic_api_key" { + type = string + description = "Generate one at: https://console.anthropic.com/settings/keys" + sensitive = true +} - sidebar_app { - # which app to display in the sidebar on the task page - id = coder_app.claude-code.id - } +resource "coder_env" "anthropic_api_key" { + agent_id = coder_agent.main.id + name = "CODER_MCP_CLAUDE_API_KEY" + value = var.anthropic_api_key } ``` diff --git a/docs/install/kubernetes.md b/docs/install/kubernetes.md index 0b9c506878517..3d36e0479031d 100644 --- a/docs/install/kubernetes.md +++ b/docs/install/kubernetes.md @@ -135,7 +135,7 @@ We support two release channels: mainline and stable - read the helm install coder coder-v2/coder \ --namespace coder \ --values values.yaml \ - --version 2.25.0 + --version 2.26.0 ``` - **OCI Registry** @@ -146,7 +146,7 @@ We support two release channels: mainline and stable - read the helm install coder oci://ghcr.io/coder/chart/coder \ --namespace coder \ --values values.yaml \ - --version 2.25.0 + --version 2.26.0 ``` - **Stable** Coder release: @@ -159,7 +159,7 @@ We support two release channels: mainline and stable - read the helm install coder coder-v2/coder \ --namespace coder \ --values values.yaml \ - --version 2.25.0 + --version 2.25.2 ``` - **OCI Registry** @@ -170,7 +170,7 @@ We support two release channels: mainline and stable - read the helm install coder oci://ghcr.io/coder/chart/coder \ --namespace coder \ --values values.yaml \ - --version 2.25.0 + --version 2.25.2 ``` You can watch Coder start up by running `kubectl get pods -n coder`. Once Coder diff --git a/docs/install/kubernetes/kubernetes-azure-app-gateway.md b/docs/install/kubernetes/kubernetes-azure-app-gateway.md index 99923ca9e2105..ab946f4bea2d2 100644 --- a/docs/install/kubernetes/kubernetes-azure-app-gateway.md +++ b/docs/install/kubernetes/kubernetes-azure-app-gateway.md @@ -129,7 +129,7 @@ The steps here follow the Microsoft tutorial for a Coder deployment. helm install coder coder-v2/coder \ --namespace coder \ --values values.yaml \ - --version 2.18.5 + --version 2.25.2 ``` 1. Clean up Azure resources: diff --git a/docs/install/releases/index.md b/docs/install/releases/index.md index 83efc16aefe17..2a8ef1d704273 100644 --- a/docs/install/releases/index.md +++ b/docs/install/releases/index.md @@ -55,15 +55,15 @@ pages. ## Release schedule -| Release name | Release Date | Status | Latest Release | -|------------------------------------------------|-----------------|------------------|----------------------------------------------------------------| -| [2.20](https://coder.com/changelog/coder-2-20) | March 04, 2025 | Not Supported | [v2.20.3](https://github.com/coder/coder/releases/tag/v2.20.3) | -| [2.21](https://coder.com/changelog/coder-2-21) | April 02, 2025 | Not Supported | [v2.21.3](https://github.com/coder/coder/releases/tag/v2.21.3) | -| [2.22](https://coder.com/changelog/coder-2-22) | May 16, 2025 | Security Support | [v2.22.1](https://github.com/coder/coder/releases/tag/v2.22.1) | -| [2.23](https://coder.com/changelog/coder-2-23) | June 03, 2025 | Security Support | [v2.23.2](https://github.com/coder/coder/releases/tag/v2.23.4) | -| [2.24](https://coder.com/changelog/coder-2-24) | July 01, 2025 | Stable | [v2.24.2](https://github.com/coder/coder/releases/tag/v2.24.2) | -| [2.24](https://coder.com/changelog/coder-2-24) | August 05, 2025 | Mainline | [v2.25.0](https://github.com/coder/coder/releases/tag/v2.25.0) | -| 2.25 | | Not Released | N/A | +| Release name | Release Date | Status | Latest Release | +|------------------------------------------------|--------------------|------------------|----------------------------------------------------------------| +| [2.21](https://coder.com/changelog/coder-2-21) | April 02, 2025 | Not Supported | [v2.21.3](https://github.com/coder/coder/releases/tag/v2.21.3) | +| [2.22](https://coder.com/changelog/coder-2-22) | May 16, 2025 | Not Supported | [v2.22.1](https://github.com/coder/coder/releases/tag/v2.22.1) | +| [2.23](https://coder.com/changelog/coder-2-23) | June 03, 2025 | Security Support | [v2.23.2](https://github.com/coder/coder/releases/tag/v2.23.4) | +| [2.24](https://coder.com/changelog/coder-2-24) | August 07, 2025 | Security Support | [v2.24.3](https://github.com/coder/coder/releases/tag/v2.24.3) | +| [2.25](https://coder.com/changelog/coder-2-25) | September 04, 2025 | Stable | [v2.25.2](https://github.com/coder/coder/releases/tag/v2.25.2) | +| [2.26](https://coder.com/changelog/coder-2-26) | September 02, 2025 | Mainline | [v2.26.0](https://github.com/coder/coder/releases/tag/v2.26.0) | +| 2.27 | October 07, 2025 | Not Released | N/A | > [!TIP] diff --git a/docs/reference/api/general.md b/docs/reference/api/general.md index 569ef067d8434..eab4e5e417bd7 100644 --- a/docs/reference/api/general.md +++ b/docs/reference/api/general.md @@ -161,6 +161,19 @@ curl -X GET http://coder-server:8080/api/v2/deployment/config \ "user": {} }, "agent_stat_refresh_interval": 0, + "ai": { + "bridge": { + "anthropic": { + "base_url": "string", + "key": "string" + }, + "enabled": true, + "openai": { + "base_url": "string", + "key": "string" + } + } + }, "allow_workspace_renames": true, "autobuild_poll_interval": 0, "browser_only": true, @@ -241,6 +254,9 @@ curl -X GET http://coder-server:8080/api/v2/deployment/config \ "display_icon": "string", "display_name": "string", "id": "string", + "mcp_tool_allow_regex": "string", + "mcp_tool_deny_regex": "string", + "mcp_url": "string", "no_refresh": true, "regex": "string", "scopes": [ diff --git a/docs/reference/api/members.md b/docs/reference/api/members.md index 5a6bd2c861bac..6e02b90bc6b9d 100644 --- a/docs/reference/api/members.md +++ b/docs/reference/api/members.md @@ -183,6 +183,7 @@ Status Code **200** | `action` | `start` | | `action` | `stop` | | `resource_type` | `*` | +| `resource_type` | `aibridge_interception` | | `resource_type` | `api_key` | | `resource_type` | `assign_org_role` | | `resource_type` | `assign_role` | @@ -355,6 +356,7 @@ Status Code **200** | `action` | `start` | | `action` | `stop` | | `resource_type` | `*` | +| `resource_type` | `aibridge_interception` | | `resource_type` | `api_key` | | `resource_type` | `assign_org_role` | | `resource_type` | `assign_role` | @@ -527,6 +529,7 @@ Status Code **200** | `action` | `start` | | `action` | `stop` | | `resource_type` | `*` | +| `resource_type` | `aibridge_interception` | | `resource_type` | `api_key` | | `resource_type` | `assign_org_role` | | `resource_type` | `assign_role` | @@ -668,6 +671,7 @@ Status Code **200** | `action` | `start` | | `action` | `stop` | | `resource_type` | `*` | +| `resource_type` | `aibridge_interception` | | `resource_type` | `api_key` | | `resource_type` | `assign_org_role` | | `resource_type` | `assign_role` | @@ -1031,6 +1035,7 @@ Status Code **200** | `action` | `start` | | `action` | `stop` | | `resource_type` | `*` | +| `resource_type` | `aibridge_interception` | | `resource_type` | `api_key` | | `resource_type` | `assign_org_role` | | `resource_type` | `assign_role` | diff --git a/docs/reference/api/schemas.md b/docs/reference/api/schemas.md index df8aac21157ae..6e41efd10817c 100644 --- a/docs/reference/api/schemas.md +++ b/docs/reference/api/schemas.md @@ -335,6 +335,86 @@ | `groups` | array of [codersdk.Group](#codersdkgroup) | false | | | | `users` | array of [codersdk.ReducedUser](#codersdkreduceduser) | false | | | +## codersdk.AIBridgeAnthropicConfig + +```json +{ + "base_url": "string", + "key": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|------------|--------|----------|--------------|-------------| +| `base_url` | string | false | | | +| `key` | string | false | | | + +## codersdk.AIBridgeConfig + +```json +{ + "anthropic": { + "base_url": "string", + "key": "string" + }, + "enabled": true, + "openai": { + "base_url": "string", + "key": "string" + } +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|-------------|----------------------------------------------------------------------|----------|--------------|-------------| +| `anthropic` | [codersdk.AIBridgeAnthropicConfig](#codersdkaibridgeanthropicconfig) | false | | | +| `enabled` | boolean | false | | | +| `openai` | [codersdk.AIBridgeOpenAIConfig](#codersdkaibridgeopenaiconfig) | false | | | + +## codersdk.AIBridgeOpenAIConfig + +```json +{ + "base_url": "string", + "key": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|------------|--------|----------|--------------|-------------| +| `base_url` | string | false | | | +| `key` | string | false | | | + +## codersdk.AIConfig + +```json +{ + "bridge": { + "anthropic": { + "base_url": "string", + "key": "string" + }, + "enabled": true, + "openai": { + "base_url": "string", + "key": "string" + } + } +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|----------|----------------------------------------------------|----------|--------------|-------------| +| `bridge` | [codersdk.AIBridgeConfig](#codersdkaibridgeconfig) | false | | | + ## codersdk.APIKey ```json @@ -2185,6 +2265,19 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o "user": {} }, "agent_stat_refresh_interval": 0, + "ai": { + "bridge": { + "anthropic": { + "base_url": "string", + "key": "string" + }, + "enabled": true, + "openai": { + "base_url": "string", + "key": "string" + } + } + }, "allow_workspace_renames": true, "autobuild_poll_interval": 0, "browser_only": true, @@ -2265,6 +2358,9 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o "display_icon": "string", "display_name": "string", "id": "string", + "mcp_tool_allow_regex": "string", + "mcp_tool_deny_regex": "string", + "mcp_url": "string", "no_refresh": true, "regex": "string", "scopes": [ @@ -2673,6 +2769,19 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o "user": {} }, "agent_stat_refresh_interval": 0, + "ai": { + "bridge": { + "anthropic": { + "base_url": "string", + "key": "string" + }, + "enabled": true, + "openai": { + "base_url": "string", + "key": "string" + } + } + }, "allow_workspace_renames": true, "autobuild_poll_interval": 0, "browser_only": true, @@ -2753,6 +2862,9 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o "display_icon": "string", "display_name": "string", "id": "string", + "mcp_tool_allow_regex": "string", + "mcp_tool_deny_regex": "string", + "mcp_url": "string", "no_refresh": true, "regex": "string", "scopes": [ @@ -3052,6 +3164,7 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o | `address` | [serpent.HostPort](#serpenthostport) | false | | Deprecated: Use HTTPAddress or TLS.Address instead. | | `agent_fallback_troubleshooting_url` | [serpent.URL](#serpenturl) | false | | | | `agent_stat_refresh_interval` | integer | false | | | +| `ai` | [codersdk.AIConfig](#codersdkaiconfig) | false | | | | `allow_workspace_renames` | boolean | false | | | | `autobuild_poll_interval` | integer | false | | | | `browser_only` | boolean | false | | | @@ -3356,6 +3469,7 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o | `oauth2` | | `mcp-server-http` | | `workspace-sharing` | +| `aibridge` | ## codersdk.ExternalAgentCredentials @@ -3454,6 +3568,9 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o "display_icon": "string", "display_name": "string", "id": "string", + "mcp_tool_allow_regex": "string", + "mcp_tool_deny_regex": "string", + "mcp_url": "string", "no_refresh": true, "regex": "string", "scopes": [ @@ -3478,6 +3595,9 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o | `display_icon` | string | false | | Display icon is a URL to an icon to display in the UI. | | `display_name` | string | false | | Display name is shown in the UI to identify the auth config. | | `id` | string | false | | ID is a unique identifier for the auth config. It defaults to `type` when not provided. | +| `mcp_tool_allow_regex` | string | false | | | +| `mcp_tool_deny_regex` | string | false | | | +| `mcp_url` | string | false | | | | `no_refresh` | boolean | false | | | |`regex`|string|false||Regex allows API requesters to match an auth config by a string (e.g. coder.com) instead of by it's type. Git clone makes use of this by parsing the URL from: 'Username for "https://github.com":' And sending it to the Coder server to match against the Regex.| @@ -6399,6 +6519,7 @@ Only certain features set these fields: - FeatureManagedAgentLimit| | Value | |------------------------------------| | `*` | +| `aibridge_interception` | | `api_key` | | `assign_org_role` | | `assign_role` | @@ -12742,6 +12863,9 @@ None "display_icon": "string", "display_name": "string", "id": "string", + "mcp_tool_allow_regex": "string", + "mcp_tool_deny_regex": "string", + "mcp_url": "string", "no_refresh": true, "regex": "string", "scopes": [ diff --git a/docs/tutorials/quickstart.md b/docs/tutorials/quickstart.md index f1fd78ab18e01..19f9571326cf7 100644 --- a/docs/tutorials/quickstart.md +++ b/docs/tutorials/quickstart.md @@ -20,13 +20,13 @@ In this quickstart, you'll: Before diving in, here are the core concepts that power Coder explained through a cooking analogy: -| Component | What It Is | Real-World Analogy | -|----------------|--------------------------------------------------------------------------------------|--------------------------------------------| -| **You** | The engineer/developer/builder working | The head chef cooking the meal | -| **Templates** | A Terraform blueprint that defines your dev environment (OS, tools, resources) | Recipe for a meal | -| **Workspaces** | The actual running environment created from the template | The cooked meal | -| **Tasks** | AI-powered coding agents that run inside a workspace | Smart kitchen appliance that help you cook | -| **Users** | A developer who launches the workspace from a template and does their work inside it | The people eating the meal | +| Component | What It Is | Real-World Analogy | +|----------------|--------------------------------------------------------------------------------------|---------------------------------------------| +| **You** | The engineer/developer/builder working | The head chef cooking the meal | +| **Templates** | A Terraform blueprint that defines your dev environment (OS, tools, resources) | Recipe for a meal | +| **Workspaces** | The actual running environment created from the template | The cooked meal | +| **Tasks** | AI-powered coding agents that run inside a workspace | Smart kitchen appliance that helps you cook | +| **Users** | A developer who launches the workspace from a template and does their work inside it | The people eating the meal | **Putting it Together:** Coder separates who _defines_ environments from who _uses_ them. Admins create and manage Templates, the recipes, while developers use those Templates to launch Workspaces, the meals. Inside those Workspaces, developers can also run Tasks, the smart kitchen appliance, to help speed up day-to-day work. @@ -152,7 +152,7 @@ Templates define what's in your development environment. Let's start simple: 1. Click **"Templates"** → **"New Template"** -2. **Choose a starter template:** +1. **Choose a starter template:** | Starter | Best For | Includes | |-------------------------------------|---------------------------------------------------------|--------------------------------------------------------| @@ -160,17 +160,17 @@ Templates define what's in your development environment. Let's start simple: | **Kubernetes (Deployment)** | Cloud-native teams, scalable workspaces | Pod-based workspaces, Kubernetes orchestration | | **AWS EC2 (Linux)** | Teams needing full VMs, AWS-native infrastructure | Full EC2 instances with AWS integration | -3. Click **"Use template"** on **Docker Containers**. Note: running this template requires Docker to be running in the background, so make sure Docker is running! +1. Click **"Use template"** on **Docker Containers**. Note: running this template requires Docker to be running in the background, so make sure Docker is running! -4. **Name your template:** +1. **Name your template:** - Name: `quickstart` - Display name: `quickstart doc template` - Description: `Provision Docker containers as Coder workspaces` -![Create template](../images/screenshots/create-template.png) - 1. Click **"Save"** + ![Create template](../images/screenshots/create-template.png) + **What just happened?** You defined a template — a reusable blueprint for dev environments — in your Coder deployment. It's now stored in your organization's template list, where you and any teammates in the same org can create workspaces @@ -252,21 +252,21 @@ Coder maintains the [Tasks on Docker](https://registry.coder.com/templates/coder 1. Head to the [Tasks on Docker](https://registry.coder.com/templates/coder-labs/tasks-docker?_gl=1*19yewmn*_gcl_au*MTc0MzUwMTQ2NC4xNzU2MzA3MDkxLjk3NTM3MjgyNy4xNzU3Njg2NDY2LjE3NTc2ODc0Mzc.*_ga*NzUxMDI1NjIxLjE3NTYzMDcwOTE.*_ga_FTQQJCDWDM*czE3NTc3MDg4MDkkbzQ1JGcxJHQxNzU3NzA4ODE4JGo1MSRsMCRoMA..) template 1. Clone the Coder Registry repo to your local machine -```hcl -git clone https://github.com/coder/registry.git -``` + ```hcl + git clone https://github.com/coder/registry.git + ``` 1. Switch to the template directory -```hcl -cd registry/registry/coder-labs/templates/tasks-docker -``` + ```hcl + cd registry/registry/coder-labs/templates/tasks-docker + ``` 1. Push the template to your Coder deployment. Note: this command differs from the registry since we're defining the Anthropic API Key as an environment variable -```hcl -coder template push tasks-docker -d . --variable anthropic_api_key="your-api-key" -``` + ```hcl + coder template push tasks-docker -d . --variable anthropic_api_key="your-api-key" + ``` 1. **Create the new Workspace** 1. In your Coder Deployment, click **Workspaces** in the upper left hand corner @@ -274,12 +274,13 @@ coder template push tasks-docker -d . --variable anthropic_api_key="your-api-key 1. Fill in the Workspace name. Add in an AI Prompt for Claude Code like "Make the background yellow". Click **Create workspace** 1. **See Tasks in action** 1. Once your workspace is running, click **View tasks** with your workspace. This will bring you to the Tasks view where you can see Claude Code (left panel), preview the sample application, and interact with the code in code-server. You might need to wait for Claude Code to finish changing the background color of the application. - ![Tasks changing background color of demo application](../images/screenshots/quickstart-tasks-background-change.png) 1. Navigate to the **Tasks** tab in the upper left hand corner 1. Try typing in a new request to Claude Code: "make the background red" 1. Let's exit out of this specific Task view, so we can see all the running tasks 1. You can start a new task by prompting in the "Prompt your AI agent to start a task" box. You can select which template to run this from, so tasks-docker here, and that will spin up a new Workspace + ![Tasks changing background color of demo application](../images/screenshots/quickstart-tasks-background-change.png) + Congratulation! You now have a Coder Task running. This demo has shown you how to spin up a task, and prompt Claude Code to change parts of your application. Learn more specifics about Coder Tasks [here](https://coder.com/docs/ai-coder/tasks). ## Troubleshooting diff --git a/enterprise/cli/sharing_test.go b/enterprise/cli/sharing_test.go index 65b8ce53a2bf1..9e99b85886328 100644 --- a/enterprise/cli/sharing_test.go +++ b/enterprise/cli/sharing_test.go @@ -26,16 +26,15 @@ import ( func TestSharingShare(t *testing.T) { t.Parallel() - dv := coderdtest.DeploymentValues(t) - dv.Experiments = []string{string(codersdk.ExperimentWorkspaceSharing)} - t.Run("ShareWithGroups_Simple", func(t *testing.T) { t.Parallel() var ( client, db, orgOwner = coderdenttest.NewWithDatabase(t, &coderdenttest.Options{ Options: &coderdtest.Options{ - DeploymentValues: dv, + DeploymentValues: coderdtest.DeploymentValues(t, func(dv *codersdk.DeploymentValues) { + dv.Experiments = []string{string(codersdk.ExperimentWorkspaceSharing)} + }), }, LicenseOptions: &coderdenttest.LicenseOptions{ Features: license.Features{ @@ -86,7 +85,9 @@ func TestSharingShare(t *testing.T) { var ( client, db, orgOwner = coderdenttest.NewWithDatabase(t, &coderdenttest.Options{ Options: &coderdtest.Options{ - DeploymentValues: dv, + DeploymentValues: coderdtest.DeploymentValues(t, func(dv *codersdk.DeploymentValues) { + dv.Experiments = []string{string(codersdk.ExperimentWorkspaceSharing)} + }), }, LicenseOptions: &coderdenttest.LicenseOptions{ Features: license.Features{ @@ -140,7 +141,9 @@ func TestSharingShare(t *testing.T) { var ( client, db, orgOwner = coderdenttest.NewWithDatabase(t, &coderdenttest.Options{ Options: &coderdtest.Options{ - DeploymentValues: dv, + DeploymentValues: coderdtest.DeploymentValues(t, func(dv *codersdk.DeploymentValues) { + dv.Experiments = []string{string(codersdk.ExperimentWorkspaceSharing)} + }), }, LicenseOptions: &coderdenttest.LicenseOptions{ Features: license.Features{ @@ -190,16 +193,15 @@ func TestSharingShare(t *testing.T) { func TestSharingStatus(t *testing.T) { t.Parallel() - dv := coderdtest.DeploymentValues(t) - dv.Experiments = []string{string(codersdk.ExperimentWorkspaceSharing)} - t.Run("ListSharedUsers", func(t *testing.T) { t.Parallel() var ( client, db, orgOwner = coderdenttest.NewWithDatabase(t, &coderdenttest.Options{ Options: &coderdtest.Options{ - DeploymentValues: dv, + DeploymentValues: coderdtest.DeploymentValues(t, func(dv *codersdk.DeploymentValues) { + dv.Experiments = []string{string(codersdk.ExperimentWorkspaceSharing)} + }), }, LicenseOptions: &coderdenttest.LicenseOptions{ Features: license.Features{ @@ -248,16 +250,15 @@ func TestSharingStatus(t *testing.T) { func TestSharingRemove(t *testing.T) { t.Parallel() - dv := coderdtest.DeploymentValues(t) - dv.Experiments = []string{string(codersdk.ExperimentWorkspaceSharing)} - t.Run("RemoveSharedGroup_Single", func(t *testing.T) { t.Parallel() var ( client, db, orgOwner = coderdenttest.NewWithDatabase(t, &coderdenttest.Options{ Options: &coderdtest.Options{ - DeploymentValues: dv, + DeploymentValues: coderdtest.DeploymentValues(t, func(dv *codersdk.DeploymentValues) { + dv.Experiments = []string{string(codersdk.ExperimentWorkspaceSharing)} + }), }, LicenseOptions: &coderdenttest.LicenseOptions{ Features: license.Features{ @@ -328,7 +329,9 @@ func TestSharingRemove(t *testing.T) { var ( client, db, orgOwner = coderdenttest.NewWithDatabase(t, &coderdenttest.Options{ Options: &coderdtest.Options{ - DeploymentValues: dv, + DeploymentValues: coderdtest.DeploymentValues(t, func(dv *codersdk.DeploymentValues) { + dv.Experiments = []string{string(codersdk.ExperimentWorkspaceSharing)} + }), }, LicenseOptions: &coderdenttest.LicenseOptions{ Features: license.Features{ diff --git a/enterprise/coderd/workspaces_test.go b/enterprise/coderd/workspaces_test.go index 745af8df23c7f..35214c4bf3134 100644 --- a/enterprise/coderd/workspaces_test.go +++ b/enterprise/coderd/workspaces_test.go @@ -3575,6 +3575,154 @@ func TestWorkspacesFiltering(t *testing.T) { } } }) + + t.Run("SharedWithGroup", func(t *testing.T) { + t.Parallel() + + dv := coderdtest.DeploymentValues(t) + dv.Experiments = []string{string(codersdk.ExperimentWorkspaceSharing)} + + var ( + client, db, orgOwner = coderdenttest.NewWithDatabase(t, &coderdenttest.Options{ + Options: &coderdtest.Options{ + DeploymentValues: dv, + }, + LicenseOptions: &coderdenttest.LicenseOptions{ + Features: license.Features{ + codersdk.FeatureTemplateRBAC: 1, + }, + }, + }) + _, workspaceOwner = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID, rbac.ScopedRoleOrgAuditor(orgOwner.OrganizationID)) + sharedWorkspace = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{ + OwnerID: workspaceOwner.ID, + OrganizationID: orgOwner.OrganizationID, + }).Do().Workspace + _ = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{ + OwnerID: workspaceOwner.ID, + OrganizationID: orgOwner.OrganizationID, + }).Do().Workspace + ctx = testutil.Context(t, testutil.WaitMedium) + ) + + group, err := client.CreateGroup(ctx, orgOwner.OrganizationID, codersdk.CreateGroupRequest{ + Name: "wibble", + }) + require.NoError(t, err, "create group") + + client.UpdateWorkspaceACL(ctx, sharedWorkspace.ID, codersdk.UpdateWorkspaceACL{ + GroupRoles: map[string]codersdk.WorkspaceRole{ + group.ID.String(): codersdk.WorkspaceRoleUse, + }, + }) + + workspaces, err := client.Workspaces(ctx, codersdk.WorkspaceFilter{ + Shared: ptr.Ref(true), + }) + require.NoError(t, err, "fetch workspaces") + require.Equal(t, 1, workspaces.Count, "expected only one workspace") + require.Equal(t, workspaces.Workspaces[0].ID, sharedWorkspace.ID) + }) + + t.Run("SharedWithUserAndGroup", func(t *testing.T) { + t.Parallel() + + dv := coderdtest.DeploymentValues(t) + dv.Experiments = []string{string(codersdk.ExperimentWorkspaceSharing)} + + var ( + client, db, orgOwner = coderdenttest.NewWithDatabase(t, &coderdenttest.Options{ + Options: &coderdtest.Options{ + DeploymentValues: dv, + }, + LicenseOptions: &coderdenttest.LicenseOptions{ + Features: license.Features{ + codersdk.FeatureTemplateRBAC: 1, + }, + }, + }) + _, workspaceOwner = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID, rbac.ScopedRoleOrgAuditor(orgOwner.OrganizationID)) + sharedWorkspace = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{ + OwnerID: workspaceOwner.ID, + OrganizationID: orgOwner.OrganizationID, + }).Do().Workspace + _ = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{ + OwnerID: workspaceOwner.ID, + OrganizationID: orgOwner.OrganizationID, + }).Do().Workspace + _, toShareWithUser = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID) + ctx = testutil.Context(t, testutil.WaitMedium) + ) + + group, err := client.CreateGroup(ctx, orgOwner.OrganizationID, codersdk.CreateGroupRequest{ + Name: "wibble", + }) + require.NoError(t, err, "create group") + + client.UpdateWorkspaceACL(ctx, sharedWorkspace.ID, codersdk.UpdateWorkspaceACL{ + UserRoles: map[string]codersdk.WorkspaceRole{ + toShareWithUser.ID.String(): codersdk.WorkspaceRoleUse, + }, + GroupRoles: map[string]codersdk.WorkspaceRole{ + group.ID.String(): codersdk.WorkspaceRoleUse, + }, + }) + + workspaces, err := client.Workspaces(ctx, codersdk.WorkspaceFilter{ + Shared: ptr.Ref(true), + }) + require.NoError(t, err, "fetch workspaces") + require.Equal(t, 1, workspaces.Count, "expected only one workspace") + require.Equal(t, workspaces.Workspaces[0].ID, sharedWorkspace.ID) + }) + + t.Run("NotSharedWithGroup", func(t *testing.T) { + t.Parallel() + + dv := coderdtest.DeploymentValues(t) + dv.Experiments = []string{string(codersdk.ExperimentWorkspaceSharing)} + + var ( + client, db, orgOwner = coderdenttest.NewWithDatabase(t, &coderdenttest.Options{ + Options: &coderdtest.Options{ + DeploymentValues: dv, + }, + LicenseOptions: &coderdenttest.LicenseOptions{ + Features: license.Features{ + codersdk.FeatureTemplateRBAC: 1, + }, + }, + }) + _, workspaceOwner = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID, rbac.ScopedRoleOrgAuditor(orgOwner.OrganizationID)) + sharedWorkspace = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{ + OwnerID: workspaceOwner.ID, + OrganizationID: orgOwner.OrganizationID, + }).Do().Workspace + notSharedWorkspace = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{ + OwnerID: workspaceOwner.ID, + OrganizationID: orgOwner.OrganizationID, + }).Do().Workspace + ctx = testutil.Context(t, testutil.WaitMedium) + ) + + group, err := client.CreateGroup(ctx, orgOwner.OrganizationID, codersdk.CreateGroupRequest{ + Name: "wibble", + }) + require.NoError(t, err, "create group") + + client.UpdateWorkspaceACL(ctx, sharedWorkspace.ID, codersdk.UpdateWorkspaceACL{ + GroupRoles: map[string]codersdk.WorkspaceRole{ + group.ID.String(): codersdk.WorkspaceRoleUse, + }, + }) + + workspaces, err := client.Workspaces(ctx, codersdk.WorkspaceFilter{ + Shared: ptr.Ref(false), + }) + require.NoError(t, err, "fetch workspaces") + require.Equal(t, 1, workspaces.Count, "expected only one workspace") + require.Equal(t, workspaces.Workspaces[0].ID, notSharedWorkspace.ID) + }) } // TestWorkspacesWithoutTemplatePerms creates a workspace for a user, then drops @@ -4106,16 +4254,15 @@ func TestUpdateWorkspaceACL(t *testing.T) { func TestDeleteWorkspaceACL(t *testing.T) { t.Parallel() - dv := coderdtest.DeploymentValues(t) - dv.Experiments = []string{string(codersdk.ExperimentWorkspaceSharing)} - t.Run("WorkspaceOwnerCanDelete_Groups", func(t *testing.T) { t.Parallel() var ( client, db, admin = coderdenttest.NewWithDatabase(t, &coderdenttest.Options{ Options: &coderdtest.Options{ - DeploymentValues: dv, + DeploymentValues: coderdtest.DeploymentValues(t, func(dv *codersdk.DeploymentValues) { + dv.Experiments = []string{string(codersdk.ExperimentWorkspaceSharing)} + }), }, LicenseOptions: &coderdenttest.LicenseOptions{ Features: license.Features{ @@ -4157,7 +4304,9 @@ func TestDeleteWorkspaceACL(t *testing.T) { var ( client, db, admin = coderdenttest.NewWithDatabase(t, &coderdenttest.Options{ Options: &coderdtest.Options{ - DeploymentValues: dv, + DeploymentValues: coderdtest.DeploymentValues(t, func(dv *codersdk.DeploymentValues) { + dv.Experiments = []string{string(codersdk.ExperimentWorkspaceSharing)} + }), }, LicenseOptions: &coderdenttest.LicenseOptions{ Features: license.Features{ diff --git a/site/package.json b/site/package.json index b4e780527722b..bc442901d5181 100644 --- a/site/package.json +++ b/site/package.json @@ -94,11 +94,11 @@ "lucide-react": "0.474.0", "monaco-editor": "0.52.2", "pretty-bytes": "6.1.1", - "react": "18.3.1", + "react": "19.1.1", "react-color": "2.19.3", "react-confetti": "6.2.2", "react-date-range": "1.4.0", - "react-dom": "18.3.1", + "react-dom": "19.1.1", "react-helmet-async": "2.0.5", "react-markdown": "9.0.3", "react-query": "npm:@tanstack/react-query@5.77.0", @@ -146,10 +146,10 @@ "@types/jest": "29.5.14", "@types/lodash": "4.17.15", "@types/node": "20.17.16", - "@types/react": "18.3.12", + "@types/react": "19.1.13", "@types/react-color": "3.0.13", "@types/react-date-range": "1.4.4", - "@types/react-dom": "18.3.1", + "@types/react-dom": "19.1.9", "@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 3c210114406ac..1b4603a43e72c 100644 --- a/site/pnpm-lock.yaml +++ b/site/pnpm-lock.yaml @@ -24,7 +24,7 @@ importers: version: 1.2.1 '@emoji-mart/react': specifier: 1.1.1 - version: 1.1.1(emoji-mart@5.6.0)(react@18.3.1) + version: 1.1.1(emoji-mart@5.6.0)(react@19.1.1) '@emotion/cache': specifier: 11.14.0 version: 11.14.0 @@ -33,10 +33,10 @@ importers: version: 11.13.5 '@emotion/react': specifier: 11.14.0 - version: 11.14.0(@types/react@18.3.12)(react@18.3.1) + version: 11.14.0(@types/react@19.1.13)(react@19.1.1) '@emotion/styled': specifier: 11.14.0 - version: 11.14.0(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1) + version: 11.14.0(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react@19.1.1) '@fontsource-variable/inter': specifier: 5.1.1 version: 5.1.1 @@ -54,70 +54,70 @@ importers: version: 5.2.5 '@monaco-editor/react': specifier: 4.7.0 - version: 4.7.0(monaco-editor@0.52.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + 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.16.14 - version: 5.16.14(@mui/material@5.16.14(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.12)(react@18.3.1) + version: 5.16.14(@mui/material@5.16.14(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@emotion/styled@11.14.0(@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/material': specifier: 5.16.14 - version: 5.16.14(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 5.16.14(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@emotion/styled@11.14.0(@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': specifier: 5.16.14 - version: 5.16.14(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1) + version: 5.16.14(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@emotion/styled@11.14.0(@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': specifier: 5.16.14 - version: 5.16.14(@types/react@18.3.12)(react@18.3.1) + version: 5.16.14(@types/react@19.1.13)(react@19.1.1) '@mui/x-tree-view': specifier: 7.25.0 - version: 7.25.0(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@mui/material@5.16.14(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@5.16.14(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 7.25.0(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@emotion/styled@11.14.0(@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.16.14(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@emotion/styled@11.14.0(@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.16.14(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@emotion/styled@11.14.0(@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) '@radix-ui/react-avatar': specifier: 1.1.2 - version: 1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + 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-checkbox': specifier: 1.1.4 - version: 1.1.4(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + 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-collapsible': specifier: 1.1.2 - version: 1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + 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.4 - version: 1.1.4(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + 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@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + 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) '@radix-ui/react-label': specifier: 2.1.0 - version: 2.1.0(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + 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) '@radix-ui/react-popover': specifier: 1.1.5 - version: 1.1.5(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + 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) '@radix-ui/react-radio-group': specifier: 1.2.3 - version: 1.2.3(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + 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) '@radix-ui/react-scroll-area': specifier: 1.2.3 - version: 1.2.3(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + 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) '@radix-ui/react-select': specifier: 2.1.4 - version: 2.1.4(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + 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) '@radix-ui/react-separator': specifier: 1.1.7 - version: 1.1.7(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + 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) '@radix-ui/react-slider': specifier: 1.2.2 - version: 1.2.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + 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) '@radix-ui/react-slot': specifier: 1.1.1 - version: 1.1.1(@types/react@18.3.12)(react@18.3.1) + version: 1.1.1(@types/react@19.1.13)(react@19.1.1) '@radix-ui/react-switch': specifier: 1.1.1 - version: 1.1.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.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) '@radix-ui/react-tooltip': specifier: 1.1.7 - version: 1.1.7(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + 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) '@tanstack/react-query-devtools': specifier: 5.77.0 - version: 5.77.0(@tanstack/react-query@5.77.0(react@18.3.1))(react@18.3.1) + version: 5.77.0(@tanstack/react-query@5.77.0(react@19.1.1))(react@19.1.1) '@xterm/addon-canvas': specifier: 0.7.0 version: 0.7.0(@xterm/xterm@5.5.0) @@ -153,7 +153,7 @@ importers: version: 2.1.1 cmdk: specifier: 1.0.4 - version: 1.0.4(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + 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) color-convert: specifier: 2.0.1 version: 2.0.1 @@ -174,7 +174,7 @@ importers: version: 2.0.5 formik: specifier: 2.4.6 - version: 2.4.6(react@18.3.1) + version: 2.4.6(react@19.1.1) front-matter: specifier: 4.0.2 version: 4.0.2 @@ -189,7 +189,7 @@ importers: version: 4.17.21 lucide-react: specifier: 0.474.0 - version: 0.474.0(react@18.3.1) + version: 0.474.0(react@19.1.1) monaco-editor: specifier: 0.52.2 version: 0.52.2 @@ -197,50 +197,50 @@ importers: specifier: 6.1.1 version: 6.1.1 react: - specifier: 18.3.1 - version: 18.3.1 + specifier: 19.1.1 + version: 19.1.1 react-color: specifier: 2.19.3 - version: 2.19.3(react@18.3.1) + version: 2.19.3(react@19.1.1) react-confetti: specifier: 6.2.2 - version: 6.2.2(react@18.3.1) + version: 6.2.2(react@19.1.1) react-date-range: specifier: 1.4.0 - version: 1.4.0(date-fns@2.30.0)(react@18.3.1) + version: 1.4.0(date-fns@2.30.0)(react@19.1.1) react-dom: - specifier: 18.3.1 - version: 18.3.1(react@18.3.1) + specifier: 19.1.1 + version: 19.1.1(react@19.1.1) react-helmet-async: specifier: 2.0.5 - version: 2.0.5(react@18.3.1) + version: 2.0.5(react@19.1.1) react-markdown: specifier: 9.0.3 - version: 9.0.3(@types/react@18.3.12)(react@18.3.1) + version: 9.0.3(@types/react@19.1.13)(react@19.1.1) react-query: specifier: npm:@tanstack/react-query@5.77.0 - version: '@tanstack/react-query@5.77.0(react@18.3.1)' + version: '@tanstack/react-query@5.77.0(react@19.1.1)' react-resizable-panels: specifier: 3.0.3 - version: 3.0.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 3.0.3(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@18.3.1(react@18.3.1))(react@18.3.1) + version: 7.8.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) react-syntax-highlighter: specifier: 15.6.1 - version: 15.6.1(react@18.3.1) + version: 15.6.1(react@19.1.1) react-textarea-autosize: specifier: 8.5.9 - version: 8.5.9(@types/react@18.3.12)(react@18.3.1) + version: 8.5.9(@types/react@19.1.13)(react@19.1.1) react-virtualized-auto-sizer: specifier: 1.0.24 - version: 1.0.24(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 1.0.24(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@18.3.1(react@18.3.1))(react@18.3.1) + version: 1.8.11(react-dom@19.1.1(react@19.1.1))(react@19.1.1) recharts: specifier: 2.15.0 - version: 2.15.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + 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 @@ -295,16 +295,16 @@ importers: version: 1.47.0 '@storybook/addon-docs': specifier: 9.1.2 - version: 9.1.2(@types/react@18.3.12)(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.4(@types/node@20.17.16)(jiti@2.4.2)(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.4(@types/node@20.17.16)(jiti@2.4.2)(yaml@2.7.0))) '@storybook/addon-links': specifier: 9.1.2 - version: 9.1.2(react@18.3.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.4(@types/node@20.17.16)(jiti@2.4.2)(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.4(@types/node@20.17.16)(jiti@2.4.2)(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.4(@types/node@20.17.16)(jiti@2.4.2)(yaml@2.7.0))) '@storybook/react-vite': specifier: 9.1.2 - version: 9.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.46.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.4(@types/node@20.17.16)(jiti@2.4.2)(yaml@2.7.0)))(typescript@5.6.3)(vite@7.1.4(@types/node@20.17.16)(jiti@2.4.2)(yaml@2.7.0)) + version: 9.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(rollup@4.46.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.4(@types/node@20.17.16)(jiti@2.4.2)(yaml@2.7.0)))(typescript@5.6.3)(vite@7.1.4(@types/node@20.17.16)(jiti@2.4.2)(yaml@2.7.0)) '@swc/core': specifier: 1.3.38 version: 1.3.38 @@ -319,7 +319,7 @@ importers: version: 6.6.3 '@testing-library/react': specifier: 14.3.1 - version: 14.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 14.3.1(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) @@ -348,17 +348,17 @@ importers: specifier: 20.17.16 version: 20.17.16 '@types/react': - specifier: 18.3.12 - version: 18.3.12 + specifier: 19.1.13 + version: 19.1.13 '@types/react-color': specifier: 3.0.13 - version: 3.0.13(@types/react@18.3.12) + version: 3.0.13(@types/react@19.1.13) '@types/react-date-range': specifier: 1.4.4 version: 1.4.4 '@types/react-dom': - specifier: 18.3.1 - version: 18.3.1 + specifier: 19.1.9 + version: 19.1.9(@types/react@19.1.13) '@types/react-syntax-highlighter': specifier: 15.5.13 version: 15.5.13 @@ -442,7 +442,7 @@ importers: 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.4(@types/node@20.17.16)(jiti@2.4.2)(yaml@2.7.0)) storybook-addon-remix-react-router: specifier: 5.0.0 - version: 5.0.0(react-dom@18.3.1(react@18.3.1))(react-router@7.8.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.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.4(@types/node@20.17.16)(jiti@2.4.2)(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.4(@types/node@20.17.16)(jiti@2.4.2)(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)) @@ -2566,9 +2566,6 @@ packages: '@types/parse-json@4.0.0': resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==, tarball: https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz} - '@types/prop-types@15.7.13': - resolution: {integrity: sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==, tarball: https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz} - '@types/prop-types@15.7.14': resolution: {integrity: sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==, tarball: https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz} @@ -2589,6 +2586,11 @@ packages: '@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@19.1.9': + resolution: {integrity: sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==, tarball: https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.9.tgz} + peerDependencies: + '@types/react': ^19.0.0 + '@types/react-syntax-highlighter@15.5.13': resolution: {integrity: sha512-uLGJ87j6Sz8UaBAooU0T6lWJ0dBmjZgN1PZTrj05TNql2/XpC6+4HhMT5syIdFUUt+FASfCeLLv4kBygNU+8qA==, tarball: https://registry.npmjs.org/@types/react-syntax-highlighter/-/react-syntax-highlighter-15.5.13.tgz} @@ -2603,8 +2605,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@18.3.12': - resolution: {integrity: sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==, tarball: https://registry.npmjs.org/@types/react/-/react-18.3.12.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/reactcss@1.2.13': resolution: {integrity: sha512-gi3S+aUi6kpkF5vdhUsnkwbiSEIU/BEJyD7kBy2SudWBUuKmJk8AQKE0OVcQQeEy40Azh0lV6uynxlikYIJuwg==, tarball: https://registry.npmjs.org/@types/reactcss/-/reactcss-1.2.13.tgz} @@ -5228,10 +5230,10 @@ packages: resolution: {integrity: sha512-kmob/FOTwep7DUWf9KjuenKX0vyvChr3oTdvvPt09V60Iz75FJp+T/0ZeHMbAfJj2WaVWqAPP5Hmm3PYzSPPKg==, tarball: https://registry.npmjs.org/react-docgen/-/react-docgen-8.0.0.tgz} engines: {node: ^20.9.0 || >=22} - react-dom@18.3.1: - resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==, tarball: https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz} + react-dom@19.1.1: + resolution: {integrity: sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==, tarball: https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz} peerDependencies: - react: ^18.3.1 + react: ^19.1.1 react-fast-compare@2.0.4: resolution: {integrity: sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==, tarball: https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz} @@ -5368,8 +5370,8 @@ packages: react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react@18.3.1: - resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==, tarball: https://registry.npmjs.org/react/-/react-18.3.1.tgz} + react@19.1.1: + resolution: {integrity: sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==, tarball: https://registry.npmjs.org/react/-/react-19.1.1.tgz} engines: {node: '>=0.10.0'} reactcss@1.2.3: @@ -5516,8 +5518,8 @@ packages: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==, tarball: https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz} engines: {node: '>=v12.22.7'} - scheduler@0.23.2: - resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==, tarball: https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz} + scheduler@0.26.0: + resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==, tarball: https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz} semver@7.6.2: resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==, tarball: https://registry.npmjs.org/semver/-/semver-7.6.2.tgz} @@ -6802,10 +6804,10 @@ snapshots: '@emoji-mart/data@1.2.1': {} - '@emoji-mart/react@1.1.1(emoji-mart@5.6.0)(react@18.3.1)': + '@emoji-mart/react@1.1.1(emoji-mart@5.6.0)(react@19.1.1)': dependencies: emoji-mart: 5.6.0 - react: 18.3.1 + react: 19.1.1 '@emotion/babel-plugin@11.13.5': dependencies: @@ -6849,19 +6851,19 @@ snapshots: '@emotion/memoize@0.9.0': {} - '@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1)': + '@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1)': dependencies: '@babel/runtime': 7.26.10 '@emotion/babel-plugin': 11.13.5 '@emotion/cache': 11.14.0 '@emotion/serialize': 1.3.3 - '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@18.3.1) + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.1.1) '@emotion/utils': 1.4.2 '@emotion/weak-memoize': 0.4.0 hoist-non-react-statics: 3.3.2 - react: 18.3.1 + react: 19.1.1 optionalDependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 transitivePeerDependencies: - supports-color @@ -6875,26 +6877,26 @@ snapshots: '@emotion/sheet@1.4.0': {} - '@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1)': + '@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react@19.1.1)': dependencies: '@babel/runtime': 7.26.10 '@emotion/babel-plugin': 11.13.5 '@emotion/is-prop-valid': 1.3.1 - '@emotion/react': 11.14.0(@types/react@18.3.12)(react@18.3.1) + '@emotion/react': 11.14.0(@types/react@19.1.13)(react@19.1.1) '@emotion/serialize': 1.3.3 - '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@18.3.1) + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.1.1) '@emotion/utils': 1.4.2 - react: 18.3.1 + react: 19.1.1 optionalDependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 transitivePeerDependencies: - supports-color '@emotion/unitless@0.10.0': {} - '@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@18.3.1)': + '@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@19.1.1)': dependencies: - react: 18.3.1 + react: 19.1.1 '@emotion/utils@1.4.2': {} @@ -7011,11 +7013,11 @@ snapshots: '@floating-ui/core': 1.6.9 '@floating-ui/utils': 0.2.9 - '@floating-ui/react-dom@2.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@floating-ui/react-dom@2.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: '@floating-ui/dom': 1.6.13 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) '@floating-ui/utils@0.2.9': {} @@ -7044,9 +7046,9 @@ snapshots: '@humanwhocodes/object-schema@2.0.3': optional: true - '@icons/material@0.2.4(react@18.3.1)': + '@icons/material@0.2.4(react@19.1.1)': dependencies: - react: 18.3.1 + react: 19.1.1 '@inquirer/confirm@3.2.0': dependencies: @@ -7334,11 +7336,11 @@ snapshots: '@leeoniya/ufuzzy@1.0.10': {} - '@mdx-js/react@3.0.1(@types/react@18.3.12)(react@18.3.1)': + '@mdx-js/react@3.0.1(@types/react@19.1.13)(react@19.1.1)': dependencies: '@types/mdx': 2.0.9 - '@types/react': 18.3.12 - react: 18.3.1 + '@types/react': 19.1.13 + react: 19.1.1 '@mjackson/form-data-parser@0.4.0': dependencies: @@ -7354,12 +7356,12 @@ snapshots: dependencies: state-local: 1.0.7 - '@monaco-editor/react@4.7.0(monaco-editor@0.52.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@monaco-editor/react@4.7.0(monaco-editor@0.52.2)(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 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) '@mswjs/interceptors@0.35.9': dependencies: @@ -7372,111 +7374,111 @@ snapshots: '@mui/core-downloads-tracker@5.16.14': {} - '@mui/icons-material@5.16.14(@mui/material@5.16.14(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.12)(react@18.3.1)': + '@mui/icons-material@5.16.14(@mui/material@5.16.14(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@emotion/styled@11.14.0(@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)': dependencies: '@babel/runtime': 7.26.10 - '@mui/material': 5.16.14(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 + '@mui/material': 5.16.14(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@emotion/styled@11.14.0(@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) + react: 19.1.1 optionalDependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 - '@mui/material@5.16.14(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@mui/material@5.16.14(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@emotion/styled@11.14.0(@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)': dependencies: '@babel/runtime': 7.26.10 '@mui/core-downloads-tracker': 5.16.14 - '@mui/system': 5.16.14(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1) - '@mui/types': 7.2.21(@types/react@18.3.12) - '@mui/utils': 5.16.14(@types/react@18.3.12)(react@18.3.1) + '@mui/system': 5.16.14(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@emotion/styled@11.14.0(@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.21(@types/react@19.1.13) + '@mui/utils': 5.16.14(@types/react@19.1.13)(react@19.1.1) '@popperjs/core': 2.11.8 - '@types/react-transition-group': 4.4.12(@types/react@18.3.12) + '@types/react-transition-group': 4.4.12(@types/react@19.1.13) clsx: 2.1.1 csstype: 3.1.3 prop-types: 15.8.1 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) react-is: 19.0.0 - react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.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@18.3.12)(react@18.3.1) - '@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1) - '@types/react': 18.3.12 + '@emotion/react': 11.14.0(@types/react@19.1.13)(react@19.1.1) + '@emotion/styled': 11.14.0(@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 - '@mui/private-theming@5.16.14(@types/react@18.3.12)(react@18.3.1)': + '@mui/private-theming@5.16.14(@types/react@19.1.13)(react@19.1.1)': dependencies: '@babel/runtime': 7.26.10 - '@mui/utils': 5.16.14(@types/react@18.3.12)(react@18.3.1) + '@mui/utils': 5.16.14(@types/react@19.1.13)(react@19.1.1) prop-types: 15.8.1 - react: 18.3.1 + react: 19.1.1 optionalDependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 - '@mui/styled-engine@5.16.14(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(react@18.3.1)': + '@mui/styled-engine@5.16.14(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@emotion/styled@11.14.0(@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)': dependencies: '@babel/runtime': 7.26.10 '@emotion/cache': 11.14.0 csstype: 3.1.3 prop-types: 15.8.1 - react: 18.3.1 + react: 19.1.1 optionalDependencies: - '@emotion/react': 11.14.0(@types/react@18.3.12)(react@18.3.1) - '@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1) + '@emotion/react': 11.14.0(@types/react@19.1.13)(react@19.1.1) + '@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react@19.1.1) - '@mui/system@5.16.14(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1)': + '@mui/system@5.16.14(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@emotion/styled@11.14.0(@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)': dependencies: '@babel/runtime': 7.26.10 - '@mui/private-theming': 5.16.14(@types/react@18.3.12)(react@18.3.1) - '@mui/styled-engine': 5.16.14(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(react@18.3.1) - '@mui/types': 7.2.21(@types/react@18.3.12) - '@mui/utils': 5.16.14(@types/react@18.3.12)(react@18.3.1) + '@mui/private-theming': 5.16.14(@types/react@19.1.13)(react@19.1.1) + '@mui/styled-engine': 5.16.14(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@emotion/styled@11.14.0(@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.21(@types/react@19.1.13) + '@mui/utils': 5.16.14(@types/react@19.1.13)(react@19.1.1) clsx: 2.1.1 csstype: 3.1.3 prop-types: 15.8.1 - react: 18.3.1 + react: 19.1.1 optionalDependencies: - '@emotion/react': 11.14.0(@types/react@18.3.12)(react@18.3.1) - '@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1) - '@types/react': 18.3.12 + '@emotion/react': 11.14.0(@types/react@19.1.13)(react@19.1.1) + '@emotion/styled': 11.14.0(@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 - '@mui/types@7.2.21(@types/react@18.3.12)': + '@mui/types@7.2.21(@types/react@19.1.13)': optionalDependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 - '@mui/utils@5.16.14(@types/react@18.3.12)(react@18.3.1)': + '@mui/utils@5.16.14(@types/react@19.1.13)(react@19.1.1)': dependencies: '@babel/runtime': 7.26.10 - '@mui/types': 7.2.21(@types/react@18.3.12) + '@mui/types': 7.2.21(@types/react@19.1.13) '@types/prop-types': 15.7.14 clsx: 2.1.1 prop-types: 15.8.1 - react: 18.3.1 + react: 19.1.1 react-is: 19.0.0 optionalDependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 - '@mui/x-internals@7.25.0(@types/react@18.3.12)(react@18.3.1)': + '@mui/x-internals@7.25.0(@types/react@19.1.13)(react@19.1.1)': dependencies: '@babel/runtime': 7.26.10 - '@mui/utils': 5.16.14(@types/react@18.3.12)(react@18.3.1) - react: 18.3.1 + '@mui/utils': 5.16.14(@types/react@19.1.13)(react@19.1.1) + react: 19.1.1 transitivePeerDependencies: - '@types/react' - '@mui/x-tree-view@7.25.0(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@mui/material@5.16.14(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@5.16.14(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@mui/x-tree-view@7.25.0(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@emotion/styled@11.14.0(@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.16.14(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@emotion/styled@11.14.0(@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.16.14(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@emotion/styled@11.14.0(@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)': dependencies: '@babel/runtime': 7.26.10 - '@mui/material': 5.16.14(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@mui/system': 5.16.14(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1) - '@mui/utils': 5.16.14(@types/react@18.3.12)(react@18.3.1) - '@mui/x-internals': 7.25.0(@types/react@18.3.12)(react@18.3.1) - '@types/react-transition-group': 4.4.12(@types/react@18.3.12) + '@mui/material': 5.16.14(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@emotion/styled@11.14.0(@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.16.14(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@emotion/styled@11.14.0(@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.16.14(@types/react@19.1.13)(react@19.1.1) + '@mui/x-internals': 7.25.0(@types/react@19.1.13)(react@19.1.1) + '@types/react-transition-group': 4.4.12(@types/react@19.1.13) clsx: 2.1.1 prop-types: 15.8.1 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.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@18.3.12)(react@18.3.1) - '@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1) + '@emotion/react': 11.14.0(@types/react@19.1.13)(react@19.1.1) + '@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react@19.1.1) transitivePeerDependencies: - '@types/react' @@ -7547,575 +7549,575 @@ snapshots: '@radix-ui/primitive@1.1.1': {} - '@radix-ui/react-arrow@1.1.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.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)': dependencies: - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.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 + react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 18.3.12 - '@types/react-dom': 18.3.1 + '@types/react': 19.1.13 + '@types/react-dom': 19.1.9(@types/react@19.1.13) - '@radix-ui/react-avatar@1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@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)': dependencies: - '@radix-ui/react-context': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.12)(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.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) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 18.3.12 - '@types/react-dom': 18.3.1 + '@types/react': 19.1.13 + '@types/react-dom': 19.1.9(@types/react@19.1.13) - '@radix-ui/react-checkbox@1.1.4(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@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)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-use-previous': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-use-size': 1.1.0(@types/react@18.3.12)(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.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) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 18.3.12 - '@types/react-dom': 18.3.1 + '@types/react': 19.1.13 + '@types/react-dom': 19.1.9(@types/react@19.1.13) - '@radix-ui/react-collapsible@1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@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)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-id': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.12)(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.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) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 18.3.12 - '@types/react-dom': 18.3.1 + '@types/react': 19.1.13 + '@types/react-dom': 19.1.9(@types/react@19.1.13) - '@radix-ui/react-collection@1.1.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.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)': dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.1.1(@types/react@18.3.12)(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.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-slot': 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': 18.3.12 - '@types/react-dom': 18.3.1 + '@types/react': 19.1.13 + '@types/react-dom': 19.1.9(@types/react@19.1.13) - '@radix-ui/react-collection@1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.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)': dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.1.2(@types/react@18.3.12)(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.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.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) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 18.3.12 - '@types/react-dom': 18.3.1 + '@types/react': 19.1.13 + '@types/react-dom': 19.1.9(@types/react@19.1.13) - '@radix-ui/react-compose-refs@1.1.0(@types/react@18.3.12)(react@18.3.1)': + '@radix-ui/react-compose-refs@1.1.0(@types/react@19.1.13)(react@19.1.1)': dependencies: - react: 18.3.1 + react: 19.1.1 optionalDependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 - '@radix-ui/react-compose-refs@1.1.1(@types/react@18.3.12)(react@18.3.1)': + '@radix-ui/react-compose-refs@1.1.1(@types/react@19.1.13)(react@19.1.1)': dependencies: - react: 18.3.1 + react: 19.1.1 optionalDependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 - '@radix-ui/react-compose-refs@1.1.2(@types/react@18.3.12)(react@18.3.1)': + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.1.13)(react@19.1.1)': dependencies: - react: 18.3.1 + react: 19.1.1 optionalDependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 - '@radix-ui/react-context@1.1.1(@types/react@18.3.12)(react@18.3.1)': + '@radix-ui/react-context@1.1.1(@types/react@19.1.13)(react@19.1.1)': dependencies: - react: 18.3.1 + react: 19.1.1 optionalDependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 - '@radix-ui/react-dialog@1.1.4(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.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.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-dismissable-layer': 1.1.3(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-focus-guards': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-focus-scope': 1.1.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-id': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-portal': 1.1.3(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.12)(react@18.3.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.4 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - react-remove-scroll: 2.6.2(@types/react@18.3.12)(react@18.3.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + react-remove-scroll: 2.6.2(@types/react@19.1.13)(react@19.1.1) optionalDependencies: - '@types/react': 18.3.12 - '@types/react-dom': 18.3.1 + '@types/react': 19.1.13 + '@types/react-dom': 19.1.9(@types/react@19.1.13) - '@radix-ui/react-direction@1.1.0(@types/react@18.3.12)(react@18.3.1)': + '@radix-ui/react-direction@1.1.0(@types/react@19.1.13)(react@19.1.1)': dependencies: - react: 18.3.1 + react: 19.1.1 optionalDependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 - '@radix-ui/react-dismissable-layer@1.1.3(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.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)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-use-escape-keydown': 1.1.0(@types/react@18.3.12)(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.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) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 18.3.12 - '@types/react-dom': 18.3.1 + '@types/react': 19.1.13 + '@types/react-dom': 19.1.9(@types/react@19.1.13) - '@radix-ui/react-dismissable-layer@1.1.4(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.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)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-use-escape-keydown': 1.1.0(@types/react@18.3.12)(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.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) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 18.3.12 - '@types/react-dom': 18.3.1 + '@types/react': 19.1.13 + '@types/react-dom': 19.1.9(@types/react@19.1.13) - '@radix-ui/react-dropdown-menu@2.1.4(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@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)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-id': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-menu': 2.1.4(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.12)(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.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) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 18.3.12 - '@types/react-dom': 18.3.1 + '@types/react': 19.1.13 + '@types/react-dom': 19.1.9(@types/react@19.1.13) - '@radix-ui/react-focus-guards@1.1.1(@types/react@18.3.12)(react@18.3.1)': + '@radix-ui/react-focus-guards@1.1.1(@types/react@19.1.13)(react@19.1.1)': dependencies: - react: 18.3.1 + react: 19.1.1 optionalDependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 - '@radix-ui/react-focus-scope@1.1.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.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)': dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.12)(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.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) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 18.3.12 - '@types/react-dom': 18.3.1 + '@types/react': 19.1.13 + '@types/react-dom': 19.1.9(@types/react@19.1.13) - '@radix-ui/react-id@1.1.0(@types/react@18.3.12)(react@18.3.1)': + '@radix-ui/react-id@1.1.0(@types/react@19.1.13)(react@19.1.1)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.12)(react@18.3.1) - react: 18.3.1 + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.1.13)(react@19.1.1) + react: 19.1.1 optionalDependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 - '@radix-ui/react-label@2.1.0(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@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)': dependencies: - '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.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) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 18.3.12 - '@types/react-dom': 18.3.1 + '@types/react': 19.1.13 + '@types/react-dom': 19.1.9(@types/react@19.1.13) - '@radix-ui/react-menu@2.1.4(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.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)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-collection': 1.1.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-direction': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-dismissable-layer': 1.1.3(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-focus-guards': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-focus-scope': 1.1.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-id': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-popper': 1.2.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-portal': 1.1.3(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-roving-focus': 1.1.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.12)(react@18.3.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) aria-hidden: 1.2.4 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - react-remove-scroll: 2.6.3(@types/react@18.3.12)(react@18.3.1) + 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) optionalDependencies: - '@types/react': 18.3.12 - '@types/react-dom': 18.3.1 + '@types/react': 19.1.13 + '@types/react-dom': 19.1.9(@types/react@19.1.13) - '@radix-ui/react-popover@1.1.5(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@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)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-dismissable-layer': 1.1.4(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-focus-guards': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-focus-scope': 1.1.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-id': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-popper': 1.2.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-portal': 1.1.3(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.12)(react@18.3.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) aria-hidden: 1.2.4 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - react-remove-scroll: 2.6.3(@types/react@18.3.12)(react@18.3.1) + 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) optionalDependencies: - '@types/react': 18.3.12 - '@types/react-dom': 18.3.1 - - '@radix-ui/react-popper@1.2.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@floating-ui/react-dom': 2.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-arrow': 1.1.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-use-rect': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-use-size': 1.1.0(@types/react@18.3.12)(react@18.3.1) + '@types/react': 19.1.13 + '@types/react-dom': 19.1.9(@types/react@19.1.13) + + '@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)': + 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/rect': 1.1.0 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 18.3.12 - '@types/react-dom': 18.3.1 + '@types/react': 19.1.13 + '@types/react-dom': 19.1.9(@types/react@19.1.13) - '@radix-ui/react-portal@1.1.3(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.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)': dependencies: - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.12)(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.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-layout-effect': 1.1.0(@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': 18.3.12 - '@types/react-dom': 18.3.1 + '@types/react': 19.1.13 + '@types/react-dom': 19.1.9(@types/react@19.1.13) - '@radix-ui/react-presence@1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.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)': dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.12)(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + '@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) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 18.3.12 - '@types/react-dom': 18.3.1 + '@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@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.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)': dependencies: - '@radix-ui/react-slot': 1.1.0(@types/react@18.3.12)(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + '@radix-ui/react-slot': 1.1.0(@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': 18.3.12 - '@types/react-dom': 18.3.1 + '@types/react': 19.1.13 + '@types/react-dom': 19.1.9(@types/react@19.1.13) - '@radix-ui/react-primitive@2.0.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.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)': dependencies: - '@radix-ui/react-slot': 1.1.1(@types/react@18.3.12)(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + '@radix-ui/react-slot': 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': 18.3.12 - '@types/react-dom': 18.3.1 + '@types/react': 19.1.13 + '@types/react-dom': 19.1.9(@types/react@19.1.13) - '@radix-ui/react-primitive@2.0.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.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)': dependencies: - '@radix-ui/react-slot': 1.1.2(@types/react@18.3.12)(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + '@radix-ui/react-slot': 1.1.2(@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': 18.3.12 - '@types/react-dom': 18.3.1 + '@types/react': 19.1.13 + '@types/react-dom': 19.1.9(@types/react@19.1.13) - '@radix-ui/react-primitive@2.1.3(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.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)': dependencies: - '@radix-ui/react-slot': 1.2.3(@types/react@18.3.12)(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + '@radix-ui/react-slot': 1.2.3(@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': 18.3.12 - '@types/react-dom': 18.3.1 + '@types/react': 19.1.13 + '@types/react-dom': 19.1.9(@types/react@19.1.13) - '@radix-ui/react-radio-group@1.2.3(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@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)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-direction': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-roving-focus': 1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-use-previous': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-use-size': 1.1.0(@types/react@18.3.12)(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.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) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 18.3.12 - '@types/react-dom': 18.3.1 + '@types/react': 19.1.13 + '@types/react-dom': 19.1.9(@types/react@19.1.13) - '@radix-ui/react-roving-focus@1.1.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.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)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-collection': 1.1.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-direction': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-id': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.12)(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.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) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 18.3.12 - '@types/react-dom': 18.3.1 + '@types/react': 19.1.13 + '@types/react-dom': 19.1.9(@types/react@19.1.13) - '@radix-ui/react-roving-focus@1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.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)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-collection': 1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-direction': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-id': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.12)(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.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) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 18.3.12 - '@types/react-dom': 18.3.1 + '@types/react': 19.1.13 + '@types/react-dom': 19.1.9(@types/react@19.1.13) - '@radix-ui/react-scroll-area@1.2.3(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@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)': dependencies: '@radix-ui/number': 1.1.0 '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-direction': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.12)(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.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) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 18.3.12 - '@types/react-dom': 18.3.1 + '@types/react': 19.1.13 + '@types/react-dom': 19.1.9(@types/react@19.1.13) - '@radix-ui/react-select@2.1.4(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-select@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)': dependencies: '@radix-ui/number': 1.1.0 '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-collection': 1.1.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-direction': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-dismissable-layer': 1.1.3(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-focus-guards': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-focus-scope': 1.1.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-id': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-popper': 1.2.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-portal': 1.1.3(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-use-previous': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-visually-hidden': 1.1.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.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-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-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-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-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) aria-hidden: 1.2.4 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - react-remove-scroll: 2.6.2(@types/react@18.3.12)(react@18.3.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + react-remove-scroll: 2.6.2(@types/react@19.1.13)(react@19.1.1) optionalDependencies: - '@types/react': 18.3.12 - '@types/react-dom': 18.3.1 + '@types/react': 19.1.13 + '@types/react-dom': 19.1.9(@types/react@19.1.13) - '@radix-ui/react-separator@1.1.7(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@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)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.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) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 18.3.12 - '@types/react-dom': 18.3.1 + '@types/react': 19.1.13 + '@types/react-dom': 19.1.9(@types/react@19.1.13) - '@radix-ui/react-slider@1.2.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@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)': dependencies: '@radix-ui/number': 1.1.0 '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-collection': 1.1.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-direction': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-use-previous': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-use-size': 1.1.0(@types/react@18.3.12)(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.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) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 18.3.12 - '@types/react-dom': 18.3.1 + '@types/react': 19.1.13 + '@types/react-dom': 19.1.9(@types/react@19.1.13) - '@radix-ui/react-slot@1.1.0(@types/react@18.3.12)(react@18.3.1)': + '@radix-ui/react-slot@1.1.0(@types/react@19.1.13)(react@19.1.1)': dependencies: - '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.12)(react@18.3.1) - react: 18.3.1 + '@radix-ui/react-compose-refs': 1.1.0(@types/react@19.1.13)(react@19.1.1) + react: 19.1.1 optionalDependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 - '@radix-ui/react-slot@1.1.1(@types/react@18.3.12)(react@18.3.1)': + '@radix-ui/react-slot@1.1.1(@types/react@19.1.13)(react@19.1.1)': dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.12)(react@18.3.1) - react: 18.3.1 + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.13)(react@19.1.1) + react: 19.1.1 optionalDependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 - '@radix-ui/react-slot@1.1.2(@types/react@18.3.12)(react@18.3.1)': + '@radix-ui/react-slot@1.1.2(@types/react@19.1.13)(react@19.1.1)': dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.12)(react@18.3.1) - react: 18.3.1 + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.13)(react@19.1.1) + react: 19.1.1 optionalDependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 - '@radix-ui/react-slot@1.2.3(@types/react@18.3.12)(react@18.3.1)': + '@radix-ui/react-slot@1.2.3(@types/react@19.1.13)(react@19.1.1)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.12)(react@18.3.1) - react: 18.3.1 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.13)(react@19.1.1) + react: 19.1.1 optionalDependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 - '@radix-ui/react-switch@1.1.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@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)': dependencies: '@radix-ui/primitive': 1.1.0 - '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-use-previous': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-use-size': 1.1.0(@types/react@18.3.12)(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + '@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) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 18.3.12 - '@types/react-dom': 18.3.1 + '@types/react': 19.1.13 + '@types/react-dom': 19.1.9(@types/react@19.1.13) - '@radix-ui/react-tooltip@1.1.7(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@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)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-dismissable-layer': 1.1.4(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-id': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-popper': 1.2.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-portal': 1.1.3(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.1.1(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-visually-hidden': 1.1.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.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) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 18.3.12 - '@types/react-dom': 18.3.1 + '@types/react': 19.1.13 + '@types/react-dom': 19.1.9(@types/react@19.1.13) - '@radix-ui/react-use-callback-ref@1.1.0(@types/react@18.3.12)(react@18.3.1)': + '@radix-ui/react-use-callback-ref@1.1.0(@types/react@19.1.13)(react@19.1.1)': dependencies: - react: 18.3.1 + react: 19.1.1 optionalDependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 - '@radix-ui/react-use-controllable-state@1.1.0(@types/react@18.3.12)(react@18.3.1)': + '@radix-ui/react-use-controllable-state@1.1.0(@types/react@19.1.13)(react@19.1.1)': dependencies: - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.12)(react@18.3.1) - react: 18.3.1 + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.1.13)(react@19.1.1) + react: 19.1.1 optionalDependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 - '@radix-ui/react-use-escape-keydown@1.1.0(@types/react@18.3.12)(react@18.3.1)': + '@radix-ui/react-use-escape-keydown@1.1.0(@types/react@19.1.13)(react@19.1.1)': dependencies: - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.12)(react@18.3.1) - react: 18.3.1 + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.1.13)(react@19.1.1) + react: 19.1.1 optionalDependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 - '@radix-ui/react-use-layout-effect@1.1.0(@types/react@18.3.12)(react@18.3.1)': + '@radix-ui/react-use-layout-effect@1.1.0(@types/react@19.1.13)(react@19.1.1)': dependencies: - react: 18.3.1 + react: 19.1.1 optionalDependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 - '@radix-ui/react-use-previous@1.1.0(@types/react@18.3.12)(react@18.3.1)': + '@radix-ui/react-use-previous@1.1.0(@types/react@19.1.13)(react@19.1.1)': dependencies: - react: 18.3.1 + react: 19.1.1 optionalDependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 - '@radix-ui/react-use-rect@1.1.0(@types/react@18.3.12)(react@18.3.1)': + '@radix-ui/react-use-rect@1.1.0(@types/react@19.1.13)(react@19.1.1)': dependencies: '@radix-ui/rect': 1.1.0 - react: 18.3.1 + react: 19.1.1 optionalDependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 - '@radix-ui/react-use-size@1.1.0(@types/react@18.3.12)(react@18.3.1)': + '@radix-ui/react-use-size@1.1.0(@types/react@19.1.13)(react@19.1.1)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.12)(react@18.3.1) - react: 18.3.1 + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.1.13)(react@19.1.1) + react: 19.1.1 optionalDependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 - '@radix-ui/react-visually-hidden@1.1.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.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)': dependencies: - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.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 + react-dom: 19.1.1(react@19.1.1) optionalDependencies: - '@types/react': 18.3.12 - '@types/react-dom': 18.3.1 + '@types/react': 19.1.13 + '@types/react-dom': 19.1.9(@types/react@19.1.13) '@radix-ui/rect@1.1.0': {} @@ -8199,25 +8201,25 @@ snapshots: dependencies: '@sinonjs/commons': 3.0.0 - '@storybook/addon-docs@9.1.2(@types/react@18.3.12)(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.4(@types/node@20.17.16)(jiti@2.4.2)(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.4(@types/node@20.17.16)(jiti@2.4.2)(yaml@2.7.0)))': dependencies: - '@mdx-js/react': 3.0.1(@types/react@18.3.12)(react@18.3.1) + '@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.4.8(typescript@5.6.3))(prettier@3.4.1)(vite@7.1.4(@types/node@20.17.16)(jiti@2.4.2)(yaml@2.7.0))) - '@storybook/icons': 1.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@storybook/react-dom-shim': 9.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.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.4(@types/node@20.17.16)(jiti@2.4.2)(yaml@2.7.0))) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + '@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.4(@types/node@20.17.16)(jiti@2.4.2)(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.4(@types/node@20.17.16)(jiti@2.4.2)(yaml@2.7.0)) ts-dedent: 2.2.0 transitivePeerDependencies: - '@types/react' - '@storybook/addon-links@9.1.2(react@18.3.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.4(@types/node@20.17.16)(jiti@2.4.2)(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.4(@types/node@20.17.16)(jiti@2.4.2)(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.4(@types/node@20.17.16)(jiti@2.4.2)(yaml@2.7.0)) optionalDependencies: - react: 18.3.1 + 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.4(@types/node@20.17.16)(jiti@2.4.2)(yaml@2.7.0)))': dependencies: @@ -8238,28 +8240,28 @@ snapshots: '@storybook/global@5.0.0': {} - '@storybook/icons@1.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@storybook/icons@1.4.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) - '@storybook/react-dom-shim@9.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.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.4(@types/node@20.17.16)(jiti@2.4.2)(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.4(@types/node@20.17.16)(jiti@2.4.2)(yaml@2.7.0)))': dependencies: - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + 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.4(@types/node@20.17.16)(jiti@2.4.2)(yaml@2.7.0)) - '@storybook/react-vite@9.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.46.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.4(@types/node@20.17.16)(jiti@2.4.2)(yaml@2.7.0)))(typescript@5.6.3)(vite@7.1.4(@types/node@20.17.16)(jiti@2.4.2)(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.46.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.4(@types/node@20.17.16)(jiti@2.4.2)(yaml@2.7.0)))(typescript@5.6.3)(vite@7.1.4(@types/node@20.17.16)(jiti@2.4.2)(yaml@2.7.0))': dependencies: '@joshwooding/vite-plugin-react-docgen-typescript': 0.6.1(typescript@5.6.3)(vite@7.1.4(@types/node@20.17.16)(jiti@2.4.2)(yaml@2.7.0)) '@rollup/pluginutils': 5.0.5(rollup@4.46.2) '@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.4(@types/node@20.17.16)(jiti@2.4.2)(yaml@2.7.0)))(vite@7.1.4(@types/node@20.17.16)(jiti@2.4.2)(yaml@2.7.0)) - '@storybook/react': 9.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.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.4(@types/node@20.17.16)(jiti@2.4.2)(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.4(@types/node@20.17.16)(jiti@2.4.2)(yaml@2.7.0)))(typescript@5.6.3) find-up: 7.0.0 magic-string: 0.30.17 - react: 18.3.1 + react: 19.1.1 react-docgen: 8.0.0 - react-dom: 18.3.1(react@18.3.1) + 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.4(@types/node@20.17.16)(jiti@2.4.2)(yaml@2.7.0)) tsconfig-paths: 4.2.0 @@ -8269,12 +8271,12 @@ snapshots: - supports-color - typescript - '@storybook/react@9.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.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.4(@types/node@20.17.16)(jiti@2.4.2)(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.4(@types/node@20.17.16)(jiti@2.4.2)(yaml@2.7.0)))(typescript@5.6.3)': dependencies: '@storybook/global': 5.0.0 - '@storybook/react-dom-shim': 9.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.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.4(@types/node@20.17.16)(jiti@2.4.2)(yaml@2.7.0))) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.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.4(@types/node@20.17.16)(jiti@2.4.2)(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.4(@types/node@20.17.16)(jiti@2.4.2)(yaml@2.7.0)) optionalDependencies: typescript: 5.6.3 @@ -8343,16 +8345,16 @@ snapshots: '@tanstack/query-devtools@5.76.0': {} - '@tanstack/react-query-devtools@5.77.0(@tanstack/react-query@5.77.0(react@18.3.1))(react@18.3.1)': + '@tanstack/react-query-devtools@5.77.0(@tanstack/react-query@5.77.0(react@19.1.1))(react@19.1.1)': dependencies: '@tanstack/query-devtools': 5.76.0 - '@tanstack/react-query': 5.77.0(react@18.3.1) - react: 18.3.1 + '@tanstack/react-query': 5.77.0(react@19.1.1) + react: 19.1.1 - '@tanstack/react-query@5.77.0(react@18.3.1)': + '@tanstack/react-query@5.77.0(react@19.1.1)': dependencies: '@tanstack/query-core': 5.77.0 - react: 18.3.1 + react: 19.1.1 '@testing-library/dom@10.4.0': dependencies: @@ -8386,13 +8388,13 @@ snapshots: lodash: 4.17.21 redent: 3.0.0 - '@testing-library/react@14.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@testing-library/react@14.3.1(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 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.0)': dependencies: @@ -8530,7 +8532,7 @@ snapshots: '@types/hoist-non-react-statics@3.3.5': dependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 hoist-non-react-statics: 3.3.2 '@types/http-errors@2.0.1': {} @@ -8604,52 +8606,53 @@ snapshots: '@types/parse-json@4.0.0': {} - '@types/prop-types@15.7.13': {} - '@types/prop-types@15.7.14': {} '@types/qs@6.9.7': {} '@types/range-parser@1.2.4': {} - '@types/react-color@3.0.13(@types/react@18.3.12)': + '@types/react-color@3.0.13(@types/react@19.1.13)': dependencies: - '@types/react': 18.3.12 - '@types/reactcss': 1.2.13(@types/react@18.3.12) + '@types/react': 19.1.13 + '@types/reactcss': 1.2.13(@types/react@19.1.13) '@types/react-date-range@1.4.4': dependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 date-fns: 2.30.0 '@types/react-dom@18.3.1': dependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 + + '@types/react-dom@19.1.9(@types/react@19.1.13)': + dependencies: + '@types/react': 19.1.13 '@types/react-syntax-highlighter@15.5.13': dependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 - '@types/react-transition-group@4.4.12(@types/react@18.3.12)': + '@types/react-transition-group@4.4.12(@types/react@19.1.13)': dependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 '@types/react-virtualized-auto-sizer@1.0.4': dependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 '@types/react-window@1.8.8': dependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 - '@types/react@18.3.12': + '@types/react@19.1.13': dependencies: - '@types/prop-types': 15.7.13 csstype: 3.1.3 - '@types/reactcss@1.2.13(@types/react@18.3.12)': + '@types/reactcss@1.2.13(@types/react@19.1.13)': dependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 '@types/resolve@1.20.4': {} @@ -9177,14 +9180,14 @@ snapshots: clsx@2.1.1: {} - cmdk@1.0.4(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.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): dependencies: - '@radix-ui/react-dialog': 1.1.4(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-id': 1.1.0(@types/react@18.3.12)(react@18.3.1) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - use-sync-external-store: 1.4.0(react@18.3.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 + react-dom: 19.1.1(react@19.1.1) + use-sync-external-store: 1.4.0(react@19.1.1) transitivePeerDependencies: - '@types/react' - '@types/react-dom' @@ -9869,14 +9872,14 @@ snapshots: format@0.2.2: {} - formik@2.4.6(react@18.3.1): + formik@2.4.6(react@19.1.1): dependencies: '@types/hoist-non-react-statics': 3.3.5 deepmerge: 2.2.1 hoist-non-react-statics: 3.3.2 lodash: 4.17.21 lodash-es: 4.17.21 - react: 18.3.1 + react: 19.1.1 react-fast-compare: 2.0.4 tiny-warning: 1.0.3 tslib: 2.6.2 @@ -10873,9 +10876,9 @@ snapshots: dependencies: yallist: 3.1.1 - lucide-react@0.474.0(react@18.3.1): + lucide-react@0.474.0(react@19.1.1): dependencies: - react: 18.3.1 + react: 19.1.1 luxon@3.3.0: {} @@ -11846,29 +11849,29 @@ snapshots: iconv-lite: 0.4.24 unpipe: 1.0.0 - react-color@2.19.3(react@18.3.1): + react-color@2.19.3(react@19.1.1): dependencies: - '@icons/material': 0.2.4(react@18.3.1) + '@icons/material': 0.2.4(react@19.1.1) lodash: 4.17.21 lodash-es: 4.17.21 material-colors: 1.2.6 prop-types: 15.8.1 - react: 18.3.1 - reactcss: 1.2.3(react@18.3.1) + react: 19.1.1 + reactcss: 1.2.3(react@19.1.1) tinycolor2: 1.6.0 - react-confetti@6.2.2(react@18.3.1): + react-confetti@6.2.2(react@19.1.1): dependencies: - react: 18.3.1 + react: 19.1.1 tween-functions: 1.2.0 - react-date-range@1.4.0(date-fns@2.30.0)(react@18.3.1): + react-date-range@1.4.0(date-fns@2.30.0)(react@19.1.1): dependencies: classnames: 2.3.2 date-fns: 2.30.0 prop-types: 15.8.1 - react: 18.3.1 - react-list: 0.8.17(react@18.3.1) + react: 19.1.1 + react-list: 0.8.17(react@19.1.1) shallow-equal: 1.2.1 react-docgen-typescript@2.2.2(typescript@5.6.3): @@ -11890,26 +11893,25 @@ snapshots: transitivePeerDependencies: - supports-color - react-dom@18.3.1(react@18.3.1): + react-dom@19.1.1(react@19.1.1): dependencies: - loose-envify: 1.4.0 - react: 18.3.1 - scheduler: 0.23.2 + react: 19.1.1 + scheduler: 0.26.0 react-fast-compare@2.0.4: {} react-fast-compare@3.2.2: {} - react-helmet-async@2.0.5(react@18.3.1): + react-helmet-async@2.0.5(react@19.1.1): dependencies: invariant: 2.2.4 - react: 18.3.1 + react: 19.1.1 react-fast-compare: 3.2.2 shallowequal: 1.1.0 - react-inspector@6.0.2(react@18.3.1): + react-inspector@6.0.2(react@19.1.1): dependencies: - react: 18.3.1 + react: 19.1.1 react-is@16.13.1: {} @@ -11919,20 +11921,20 @@ snapshots: react-is@19.0.0: {} - react-list@0.8.17(react@18.3.1): + react-list@0.8.17(react@19.1.1): dependencies: prop-types: 15.8.1 - react: 18.3.1 + react: 19.1.1 - react-markdown@9.0.3(@types/react@18.3.12)(react@18.3.1): + react-markdown@9.0.3(@types/react@19.1.13)(react@19.1.1): dependencies: '@types/hast': 3.0.4 - '@types/react': 18.3.12 + '@types/react': 19.1.13 devlop: 1.1.0 hast-util-to-jsx-runtime: 2.3.2 html-url-attributes: 3.0.1 mdast-util-to-hast: 13.2.0 - react: 18.3.1 + react: 19.1.1 remark-parse: 11.0.0 remark-rehype: 11.1.1 unified: 11.0.5 @@ -11943,113 +11945,111 @@ snapshots: react-refresh@0.17.0: {} - react-remove-scroll-bar@2.3.8(@types/react@18.3.12)(react@18.3.1): + react-remove-scroll-bar@2.3.8(@types/react@19.1.13)(react@19.1.1): dependencies: - react: 18.3.1 - react-style-singleton: 2.2.3(@types/react@18.3.12)(react@18.3.1) + react: 19.1.1 + react-style-singleton: 2.2.3(@types/react@19.1.13)(react@19.1.1) tslib: 2.8.1 optionalDependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 - react-remove-scroll@2.6.2(@types/react@18.3.12)(react@18.3.1): + react-remove-scroll@2.6.2(@types/react@19.1.13)(react@19.1.1): dependencies: - react: 18.3.1 - react-remove-scroll-bar: 2.3.8(@types/react@18.3.12)(react@18.3.1) - react-style-singleton: 2.2.3(@types/react@18.3.12)(react@18.3.1) + 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) tslib: 2.6.2 - use-callback-ref: 1.3.3(@types/react@18.3.12)(react@18.3.1) - use-sidecar: 1.1.2(@types/react@18.3.12)(react@18.3.1) + use-callback-ref: 1.3.3(@types/react@19.1.13)(react@19.1.1) + use-sidecar: 1.1.2(@types/react@19.1.13)(react@19.1.1) optionalDependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 - react-remove-scroll@2.6.3(@types/react@18.3.12)(react@18.3.1): + react-remove-scroll@2.6.3(@types/react@19.1.13)(react@19.1.1): dependencies: - react: 18.3.1 - react-remove-scroll-bar: 2.3.8(@types/react@18.3.12)(react@18.3.1) - react-style-singleton: 2.2.3(@types/react@18.3.12)(react@18.3.1) + 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) tslib: 2.8.1 - use-callback-ref: 1.3.3(@types/react@18.3.12)(react@18.3.1) - use-sidecar: 1.1.3(@types/react@18.3.12)(react@18.3.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) optionalDependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 - react-resizable-panels@3.0.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + react-resizable-panels@3.0.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) - react-router@7.8.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + react-router@7.8.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: cookie: 1.0.2 - react: 18.3.1 + react: 19.1.1 set-cookie-parser: 2.7.1 optionalDependencies: - react-dom: 18.3.1(react@18.3.1) + react-dom: 19.1.1(react@19.1.1) - react-smooth@4.0.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + react-smooth@4.0.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: fast-equals: 5.2.2 prop-types: 15.8.1 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.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) - react-style-singleton@2.2.3(@types/react@18.3.12)(react@18.3.1): + react-style-singleton@2.2.3(@types/react@19.1.13)(react@19.1.1): dependencies: get-nonce: 1.0.1 - react: 18.3.1 + react: 19.1.1 tslib: 2.8.1 optionalDependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 - react-syntax-highlighter@15.6.1(react@18.3.1): + react-syntax-highlighter@15.6.1(react@19.1.1): dependencies: '@babel/runtime': 7.26.10 highlight.js: 10.7.3 highlightjs-vue: 1.0.0 lowlight: 1.20.0 prismjs: 1.30.0 - react: 18.3.1 + react: 19.1.1 refractor: 3.6.0 - react-textarea-autosize@8.5.9(@types/react@18.3.12)(react@18.3.1): + react-textarea-autosize@8.5.9(@types/react@19.1.13)(react@19.1.1): dependencies: '@babel/runtime': 7.26.10 - react: 18.3.1 - use-composed-ref: 1.4.0(@types/react@18.3.12)(react@18.3.1) - use-latest: 1.3.0(@types/react@18.3.12)(react@18.3.1) + 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) transitivePeerDependencies: - '@types/react' - react-transition-group@4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + react-transition-group@4.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: '@babel/runtime': 7.26.10 dom-helpers: 5.2.1 loose-envify: 1.4.0 prop-types: 15.8.1 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) - react-virtualized-auto-sizer@1.0.24(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + react-virtualized-auto-sizer@1.0.24(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) - react-window@1.8.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + react-window@1.8.11(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: '@babel/runtime': 7.26.10 memoize-one: 5.2.1 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) - react@18.3.1: - dependencies: - loose-envify: 1.4.0 + react@19.1.1: {} - reactcss@1.2.3(react@18.3.1): + reactcss@1.2.3(react@19.1.1): dependencies: lodash: 4.17.21 - react: 18.3.1 + react: 19.1.1 read-cache@1.0.0: dependencies: @@ -12089,15 +12089,15 @@ snapshots: dependencies: decimal.js-light: 2.5.1 - recharts@2.15.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + recharts@2.15.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: clsx: 2.1.1 eventemitter3: 4.0.7 lodash: 4.17.21 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) react-is: 18.3.1 - react-smooth: 4.0.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-smooth: 4.0.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) recharts-scale: 0.4.5 tiny-invariant: 1.3.3 victory-vendor: 36.9.2 @@ -12242,9 +12242,7 @@ snapshots: dependencies: xmlchars: 2.2.0 - scheduler@0.23.2: - dependencies: - loose-envify: 1.4.0 + scheduler@0.26.0: {} semver@7.6.2: {} @@ -12383,16 +12381,16 @@ snapshots: dependencies: internal-slot: 1.0.6 - storybook-addon-remix-react-router@5.0.0(react-dom@18.3.1(react@18.3.1))(react-router@7.8.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.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.4(@types/node@20.17.16)(jiti@2.4.2)(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.4(@types/node@20.17.16)(jiti@2.4.2)(yaml@2.7.0))): dependencies: '@mjackson/form-data-parser': 0.4.0 compare-versions: 6.1.0 - react-inspector: 6.0.2(react@18.3.1) - react-router: 7.8.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + 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.4(@types/node@20.17.16)(jiti@2.4.2)(yaml@2.7.0)) optionalDependencies: - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + 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.4(@types/node@20.17.16)(jiti@2.4.2)(yaml@2.7.0)): dependencies: @@ -12785,51 +12783,51 @@ snapshots: querystringify: 2.2.0 requires-port: 1.0.0 - use-callback-ref@1.3.3(@types/react@18.3.12)(react@18.3.1): + use-callback-ref@1.3.3(@types/react@19.1.13)(react@19.1.1): dependencies: - react: 18.3.1 + react: 19.1.1 tslib: 2.8.1 optionalDependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 - use-composed-ref@1.4.0(@types/react@18.3.12)(react@18.3.1): + use-composed-ref@1.4.0(@types/react@19.1.13)(react@19.1.1): dependencies: - react: 18.3.1 + react: 19.1.1 optionalDependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 - use-isomorphic-layout-effect@1.2.1(@types/react@18.3.12)(react@18.3.1): + use-isomorphic-layout-effect@1.2.1(@types/react@19.1.13)(react@19.1.1): dependencies: - react: 18.3.1 + react: 19.1.1 optionalDependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 - use-latest@1.3.0(@types/react@18.3.12)(react@18.3.1): + use-latest@1.3.0(@types/react@19.1.13)(react@19.1.1): dependencies: - react: 18.3.1 - use-isomorphic-layout-effect: 1.2.1(@types/react@18.3.12)(react@18.3.1) + react: 19.1.1 + use-isomorphic-layout-effect: 1.2.1(@types/react@19.1.13)(react@19.1.1) optionalDependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 - use-sidecar@1.1.2(@types/react@18.3.12)(react@18.3.1): + use-sidecar@1.1.2(@types/react@19.1.13)(react@19.1.1): dependencies: detect-node-es: 1.1.0 - react: 18.3.1 + react: 19.1.1 tslib: 2.6.2 optionalDependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 - use-sidecar@1.1.3(@types/react@18.3.12)(react@18.3.1): + use-sidecar@1.1.3(@types/react@19.1.13)(react@19.1.1): dependencies: detect-node-es: 1.1.0 - react: 18.3.1 + react: 19.1.1 tslib: 2.8.1 optionalDependencies: - '@types/react': 18.3.12 + '@types/react': 19.1.13 - use-sync-external-store@1.4.0(react@18.3.1): + use-sync-external-store@1.4.0(react@19.1.1): dependencies: - react: 18.3.1 + react: 19.1.1 util-deprecate@1.0.2: {} diff --git a/site/src/api/rbacresourcesGenerated.ts b/site/src/api/rbacresourcesGenerated.ts index 145b9ff9f8d7f..e2a394894965f 100644 --- a/site/src/api/rbacresourcesGenerated.ts +++ b/site/src/api/rbacresourcesGenerated.ts @@ -8,6 +8,11 @@ import type { RBACAction, RBACResource } from "./typesGenerated"; export const RBACResourceActions: Partial< Record>> > = { + aibridge_interception: { + create: "create aibridge interceptions & related records", + read: "read aibridge interceptions & related records", + update: "update aibridge interceptions & related records", + }, api_key: { create: "create an api key", delete: "delete an api key", diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 8943e6928908c..44b4bf7370a5c 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -6,6 +6,30 @@ export interface ACLAvailable { readonly groups: readonly Group[]; } +// From codersdk/deployment.go +export interface AIBridgeAnthropicConfig { + readonly base_url: string; + readonly key: string; +} + +// From codersdk/deployment.go +export interface AIBridgeConfig { + readonly enabled: boolean; + readonly openai: AIBridgeOpenAIConfig; + readonly anthropic: AIBridgeAnthropicConfig; +} + +// From codersdk/deployment.go +export interface AIBridgeOpenAIConfig { + readonly base_url: string; + readonly key: string; +} + +// From codersdk/deployment.go +export interface AIConfig { + readonly bridge?: AIBridgeConfig; +} + // From codersdk/aitasks.go export const AITaskPromptParameterName = "AI Prompt"; @@ -839,6 +863,7 @@ export interface DeploymentValues { readonly workspace_hostname_suffix?: string; readonly workspace_prebuilds?: PrebuildsConfig; readonly hide_ai_tasks?: boolean; + readonly ai?: AIConfig; readonly config?: string; readonly write_config?: boolean; readonly address?: string; @@ -930,6 +955,7 @@ export const EntitlementsWarningHeader = "X-Coder-Entitlements-Warning"; // From codersdk/deployment.go export type Experiment = + | "aibridge" | "auto-fill-parameters" | "example" | "mcp-server-http" @@ -940,6 +966,7 @@ export type Experiment = | "workspace-usage"; export const Experiments: Experiment[] = [ + "aibridge", "auto-fill-parameters", "example", "mcp-server-http", @@ -988,6 +1015,9 @@ export interface ExternalAuthConfig { readonly scopes: readonly string[]; readonly device_flow: boolean; readonly device_code_url: string; + readonly mcp_url: string; + readonly mcp_tool_allow_regex: string; + readonly mcp_tool_deny_regex: string; readonly regex: string; readonly display_name: string; readonly display_icon: string; @@ -2385,6 +2415,7 @@ export const RBACActions: RBACAction[] = [ // From codersdk/rbacresources_gen.go export type RBACResource = + | "aibridge_interception" | "api_key" | "assign_org_role" | "assign_role" @@ -2427,6 +2458,7 @@ export type RBACResource = | "workspace_proxy"; export const RBACResources: RBACResource[] = [ + "aibridge_interception", "api_key", "assign_org_role", "assign_role", diff --git a/site/src/components/Conditionals/ChooseOne.tsx b/site/src/components/Conditionals/ChooseOne.tsx index cfd5dada2a9e7..2cad54fd6a513 100644 --- a/site/src/components/Conditionals/ChooseOne.tsx +++ b/site/src/components/Conditionals/ChooseOne.tsx @@ -1,6 +1,7 @@ import { Children, type FC, + type JSX, type PropsWithChildren, type ReactNode, } from "react"; diff --git a/site/src/components/Dialogs/DeleteDialog/DeleteDialog.test.tsx b/site/src/components/Dialogs/DeleteDialog/DeleteDialog.test.tsx index ec2635ee191ce..94fe49bcd3639 100644 --- a/site/src/components/Dialogs/DeleteDialog/DeleteDialog.test.tsx +++ b/site/src/components/Dialogs/DeleteDialog/DeleteDialog.test.tsx @@ -1,7 +1,7 @@ import { renderComponent } from "testHelpers/renderHelpers"; import { screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; -import { act } from "react-dom/test-utils"; +import { act } from "react"; import { DeleteDialog } from "./DeleteDialog"; const inputTestId = "delete-dialog-name-confirmation"; diff --git a/site/src/components/Icons/DockerIcon.tsx b/site/src/components/Icons/DockerIcon.tsx index e6892a94bcf17..282e5f33f3c04 100644 --- a/site/src/components/Icons/DockerIcon.tsx +++ b/site/src/components/Icons/DockerIcon.tsx @@ -1,5 +1,7 @@ import SvgIcon, { type SvgIconProps } from "@mui/material/SvgIcon"; +import type { JSX } from "react"; + export const DockerIcon = (props: SvgIconProps): JSX.Element => ( ( diff --git a/site/src/components/Icons/ErrorIcon.tsx b/site/src/components/Icons/ErrorIcon.tsx index 30e4f4ba6975e..b5da4c50b46fc 100644 --- a/site/src/components/Icons/ErrorIcon.tsx +++ b/site/src/components/Icons/ErrorIcon.tsx @@ -1,5 +1,7 @@ import SvgIcon, { type SvgIconProps } from "@mui/material/SvgIcon"; +import type { JSX } from "react"; + export const ErrorIcon = (props: SvgIconProps): JSX.Element => ( ( diff --git a/site/src/components/Icons/JetBrainsIcon.tsx b/site/src/components/Icons/JetBrainsIcon.tsx index 8c08ae51f8e0e..277df9a1118f0 100644 --- a/site/src/components/Icons/JetBrainsIcon.tsx +++ b/site/src/components/Icons/JetBrainsIcon.tsx @@ -1,5 +1,7 @@ import SvgIcon, { type SvgIconProps } from "@mui/material/SvgIcon"; +import type { JSX } from "react"; + export const JetBrainsIcon = (props: SvgIconProps): JSX.Element => ( diff --git a/site/src/components/Icons/RocketIcon.tsx b/site/src/components/Icons/RocketIcon.tsx index 2e19ced4685e5..d1b4893182e4a 100644 --- a/site/src/components/Icons/RocketIcon.tsx +++ b/site/src/components/Icons/RocketIcon.tsx @@ -1,5 +1,7 @@ import SvgIcon, { type SvgIconProps } from "@mui/material/SvgIcon"; +import type { JSX } from "react"; + export const RocketIcon = (props: SvgIconProps): JSX.Element => ( diff --git a/site/src/components/Icons/TerminalIcon.tsx b/site/src/components/Icons/TerminalIcon.tsx index 6b44b8d928e63..c50f48881473b 100644 --- a/site/src/components/Icons/TerminalIcon.tsx +++ b/site/src/components/Icons/TerminalIcon.tsx @@ -1,5 +1,7 @@ import SvgIcon, { type SvgIconProps } from "@mui/material/SvgIcon"; +import type { JSX } from "react"; + export const TerminalIcon = (props: SvgIconProps): JSX.Element => ( diff --git a/site/src/components/Icons/VSCodeIcon.tsx b/site/src/components/Icons/VSCodeIcon.tsx index 277f7593d44c0..1c432f972cc57 100644 --- a/site/src/components/Icons/VSCodeIcon.tsx +++ b/site/src/components/Icons/VSCodeIcon.tsx @@ -1,5 +1,7 @@ import SvgIcon, { type SvgIconProps } from "@mui/material/SvgIcon"; +import type { JSX } from "react"; + export const VSCodeIcon = (props: SvgIconProps): JSX.Element => ( diff --git a/site/src/components/Icons/VSCodeInsidersIcon.tsx b/site/src/components/Icons/VSCodeInsidersIcon.tsx index 10d6db0a39d57..bb4f80f0ddf8a 100644 --- a/site/src/components/Icons/VSCodeInsidersIcon.tsx +++ b/site/src/components/Icons/VSCodeInsidersIcon.tsx @@ -1,5 +1,7 @@ import SvgIcon, { type SvgIconProps } from "@mui/material/SvgIcon"; +import type { JSX } from "react"; + export const VSCodeInsidersIcon = (props: SvgIconProps): JSX.Element => ( - isValidElement(node), - ); + const mainParentNode = jsxChildren.find(isValidElement); let parentChildren = mainParentNode?.props.children; if (typeof parentChildren === "string") { // Children will only be an array if the parsed text contains other diff --git a/site/src/components/Pill/Pill.tsx b/site/src/components/Pill/Pill.tsx index 867b75bc09bdf..05af2ec91a07c 100644 --- a/site/src/components/Pill/Pill.tsx +++ b/site/src/components/Pill/Pill.tsx @@ -45,9 +45,9 @@ export const Pill: FC = forwardRef( ref={ref} css={[ styles.pill, - icon && size === "md" && styles.pillWithIcon, + Boolean(icon) && size === "md" && styles.pillWithIcon, size === "lg" && styles.pillLg, - icon && size === "lg" && styles.pillLgWithIcon, + Boolean(icon) && size === "lg" && styles.pillLgWithIcon, typeStyles, ]} {...divProps} diff --git a/site/src/components/Timeline/Timeline.tsx b/site/src/components/Timeline/Timeline.tsx index 59da6028898a9..6e2e1e1e2e049 100644 --- a/site/src/components/Timeline/Timeline.tsx +++ b/site/src/components/Timeline/Timeline.tsx @@ -1,5 +1,5 @@ import { TimelineDateRow } from "components/Timeline/TimelineDateRow"; -import { Fragment } from "react"; +import { Fragment, type JSX } from "react"; type GetDateFn = (data: TData) => Date; diff --git a/site/src/components/deprecated/Popover/Popover.tsx b/site/src/components/deprecated/Popover/Popover.tsx index cd30a40f1a784..f4ba5852c4d90 100644 --- a/site/src/components/deprecated/Popover/Popover.tsx +++ b/site/src/components/deprecated/Popover/Popover.tsx @@ -21,7 +21,7 @@ import { type TriggerMode = "hover" | "click"; -type TriggerRef = RefObject; +type TriggerRef = RefObject; // Have to append ReactNode type to satisfy React's cloneElement function. It // has absolutely no bearing on what happens at runtime @@ -146,6 +146,8 @@ export const PopoverTrigger: FC = (props) => { return cloneElement(evaluatedChildren, { ...elementProps, ...(popover.mode === "click" ? clickProps : hoverProps), + // @ts-expect-error I would usually not hack around this, but this component + // is going to be deleted imminently. "aria-haspopup": true, "aria-owns": popover.id, "aria-expanded": popover.open, diff --git a/site/src/hooks/useClickable.ts b/site/src/hooks/useClickable.ts index d0e4081e498b4..3f59851b8fc3b 100644 --- a/site/src/hooks/useClickable.ts +++ b/site/src/hooks/useClickable.ts @@ -23,7 +23,7 @@ export type UseClickableResult< TElement extends ClickableElement = ClickableElement, TRole extends ClickableAriaRole = ClickableAriaRole, > = Readonly<{ - ref: RefObject; + ref: RefObject; tabIndex: 0; role: TRole; onClick: MouseEventHandler; diff --git a/site/src/hooks/useClipboard.ts b/site/src/hooks/useClipboard.ts index 8fde4d7833e99..1eb91cc356155 100644 --- a/site/src/hooks/useClipboard.ts +++ b/site/src/hooks/useClipboard.ts @@ -44,7 +44,7 @@ export const useClipboard = (input: UseClipboardInput): UseClipboardResult => { const { textToCopy, onError: errorCallback } = input; const [showCopiedSuccess, setShowCopiedSuccess] = useState(false); const [error, setError] = useState(); - const timeoutIdRef = useRef(); + const timeoutIdRef = useRef(undefined); useEffect(() => { const clearIdOnUnmount = () => window.clearTimeout(timeoutIdRef.current); diff --git a/site/src/hooks/useWorkspaceBuildLogs.ts b/site/src/hooks/useWorkspaceBuildLogs.ts index fd8e348dc88ea..896ea49219908 100644 --- a/site/src/hooks/useWorkspaceBuildLogs.ts +++ b/site/src/hooks/useWorkspaceBuildLogs.ts @@ -9,7 +9,7 @@ export const useWorkspaceBuildLogs = ( enabled = true, ) => { const [logs, setLogs] = useState(); - const socket = useRef(); + const socket = useRef(undefined); useEffect(() => { if (!buildId || !enabled) { diff --git a/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdownContent.tsx b/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdownContent.tsx index 55b41bc3c65a5..59bb14200b6f7 100644 --- a/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdownContent.tsx +++ b/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdownContent.tsx @@ -22,7 +22,7 @@ import { MonitorDownIcon, SquareArrowOutUpRightIcon, } from "lucide-react"; -import type { FC } from "react"; +import type { FC, JSX } from "react"; import { Link } from "react-router"; export const Language = { diff --git a/site/src/modules/resources/AgentLogs/AgentLogs.tsx b/site/src/modules/resources/AgentLogs/AgentLogs.tsx index e46dabdb7ca83..c7cb35713445c 100644 --- a/site/src/modules/resources/AgentLogs/AgentLogs.tsx +++ b/site/src/modules/resources/AgentLogs/AgentLogs.tsx @@ -2,7 +2,7 @@ import type { Interpolation, Theme } from "@emotion/react"; import Tooltip from "@mui/material/Tooltip"; import type { WorkspaceAgentLogSource } from "api/typesGenerated"; import type { Line } from "components/Logs/LogLine"; -import { type ComponentProps, forwardRef, useMemo } from "react"; +import { type ComponentProps, forwardRef, type JSX, useMemo } from "react"; import { FixedSizeList as List } from "react-window"; import { AGENT_LOG_LINE_HEIGHT, AgentLogLine } from "./AgentLogLine"; diff --git a/site/src/modules/resources/ResourceCard.tsx b/site/src/modules/resources/ResourceCard.tsx index 94ab3a9f08f62..d40ea5563a31c 100644 --- a/site/src/modules/resources/ResourceCard.tsx +++ b/site/src/modules/resources/ResourceCard.tsx @@ -6,7 +6,7 @@ import { CopyableValue } from "components/CopyableValue/CopyableValue"; import { DropdownArrow } from "components/DropdownArrow/DropdownArrow"; import { MemoizedInlineMarkdown } from "components/Markdown/Markdown"; import { Stack } from "components/Stack/Stack"; -import { Children, type FC, useState } from "react"; +import { Children, type FC, type JSX, useState } from "react"; import { ResourceAvatar } from "./ResourceAvatar"; import { SensitiveValue } from "./SensitiveValue"; diff --git a/site/src/modules/resources/Resources.tsx b/site/src/modules/resources/Resources.tsx index b68fe4b8d0e80..094092619347b 100644 --- a/site/src/modules/resources/Resources.tsx +++ b/site/src/modules/resources/Resources.tsx @@ -2,7 +2,7 @@ import Button from "@mui/material/Button"; import type { WorkspaceAgent, WorkspaceResource } from "api/typesGenerated"; import { DropdownArrow } from "components/DropdownArrow/DropdownArrow"; import { Stack } from "components/Stack/Stack"; -import { type FC, useState } from "react"; +import { type FC, type JSX, useState } from "react"; import { ResourceCard } from "./ResourceCard"; const countAgents = (resource: WorkspaceResource) => { diff --git a/site/src/modules/templates/TemplateFiles/TemplateFileTree.tsx b/site/src/modules/templates/TemplateFiles/TemplateFileTree.tsx index 7c61519574254..9578d4aba1548 100644 --- a/site/src/modules/templates/TemplateFiles/TemplateFileTree.tsx +++ b/site/src/modules/templates/TemplateFiles/TemplateFileTree.tsx @@ -6,7 +6,13 @@ 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 { type CSSProperties, type ElementType, type FC, useState } from "react"; +import { + type CSSProperties, + type ElementType, + type FC, + type JSX, + useState, +} from "react"; import type { FileTree } from "utils/filetree"; const isFolder = (content?: FileTree | string): content is FileTree => diff --git a/site/src/modules/workspaces/DynamicParameter/DynamicParameter.tsx b/site/src/modules/workspaces/DynamicParameter/DynamicParameter.tsx index f6d9862dd75db..a298270e9a65f 100644 --- a/site/src/modules/workspaces/DynamicParameter/DynamicParameter.tsx +++ b/site/src/modules/workspaces/DynamicParameter/DynamicParameter.tsx @@ -266,7 +266,7 @@ const DebouncedParameterField: FC = ({ const debouncedLocalValue = useDebouncedValue(localValue, 500); const onChangeEvent = useEffectEvent(onChange); // prevDebouncedValueRef is to prevent calling the onChangeEvent on the initial render - const prevDebouncedValueRef = useRef(); + const prevDebouncedValueRef = useRef(undefined); const prevValueRef = useRef(value); // Necessary for dynamic defaults or fields being set by preset parameters diff --git a/site/src/modules/workspaces/WorkspaceBuildLogs/WorkspaceBuildLogs.tsx b/site/src/modules/workspaces/WorkspaceBuildLogs/WorkspaceBuildLogs.tsx index 161efe260ed96..7b4d52bb7b719 100644 --- a/site/src/modules/workspaces/WorkspaceBuildLogs/WorkspaceBuildLogs.tsx +++ b/site/src/modules/workspaces/WorkspaceBuildLogs/WorkspaceBuildLogs.tsx @@ -1,10 +1,16 @@ -import { type Interpolation, type Theme, useTheme } from "@emotion/react"; +import type { Interpolation, Theme } from "@emotion/react"; import type { ProvisionerJobLog, WorkspaceBuild } from "api/typesGenerated"; import type { Line } from "components/Logs/LogLine"; import { DEFAULT_LOG_LINE_SIDE_PADDING, Logs } from "components/Logs/Logs"; import dayjs from "dayjs"; -import { type FC, Fragment, type HTMLAttributes, useMemo } from "react"; -import { BODY_FONT_FAMILY, MONOSPACE_FONT_FAMILY } from "theme/constants"; +import { + type FC, + Fragment, + type HTMLAttributes, + useLayoutEffect, + useRef, +} from "react"; +import { BODY_FONT_FAMILY } from "theme/constants"; const Language = { seconds: "seconds", @@ -43,6 +49,7 @@ interface WorkspaceBuildLogsProps extends HTMLAttributes { sticky?: boolean; logs: ProvisionerJobLog[]; build?: WorkspaceBuild; + disableAutoscroll?: boolean; } export const WorkspaceBuildLogs: FC = ({ @@ -50,38 +57,24 @@ export const WorkspaceBuildLogs: FC = ({ sticky, logs, build, + disableAutoscroll, + className, ...attrs }) => { - const theme = useTheme(); - - const _processedLogs = useMemo(() => { - const allLogs = logs || []; - - // Add synthetic overflow message if needed - if (build?.job?.logs_overflowed) { - allLogs.push({ - id: -1, - created_at: new Date().toISOString(), - log_level: "error", - log_source: "provisioner", - output: - "Provisioner logs exceeded the max size of 1MB. Will not continue to write provisioner logs for workspace build.", - stage: "overflow", - }); - } - - return allLogs; - }, [logs, build?.job?.logs_overflowed]); - const groupedLogsByStage = groupLogsByStage(logs); + const ref = useRef(null); + useLayoutEffect(() => { + if (disableAutoscroll || logs.length === 0) { + return; + } + ref.current?.scrollIntoView({ block: "end" }); + }, [logs, disableAutoscroll]); + return (
{Object.entries(groupedLogsByStage).map(([stage, logs]) => { diff --git a/site/src/pages/CreateTemplatePage/BuildLogsDrawer.tsx b/site/src/pages/CreateTemplatePage/BuildLogsDrawer.tsx index 195b61cce5339..fbca0b5626307 100644 --- a/site/src/pages/CreateTemplatePage/BuildLogsDrawer.tsx +++ b/site/src/pages/CreateTemplatePage/BuildLogsDrawer.tsx @@ -11,7 +11,7 @@ import { AlertVariant } from "modules/provisioners/ProvisionerAlert"; import { ProvisionerStatusAlert } from "modules/provisioners/ProvisionerStatusAlert"; import { useWatchVersionLogs } from "modules/templates/useWatchVersionLogs"; import { WorkspaceBuildLogs } from "modules/workspaces/WorkspaceBuildLogs/WorkspaceBuildLogs"; -import { type FC, useLayoutEffect, useRef } from "react"; +import type { FC } from "react"; import { navHeight } from "theme/constants"; type BuildLogsDrawerProps = { @@ -19,7 +19,7 @@ type BuildLogsDrawerProps = { open: boolean; onClose: () => void; templateVersion: TemplateVersion | undefined; - variablesSectionRef: React.RefObject; + variablesSectionRef: React.RefObject; }; export const BuildLogsDrawer: FC = ({ @@ -29,27 +29,6 @@ export const BuildLogsDrawer: FC = ({ ...drawerProps }) => { const logs = useWatchVersionLogs(templateVersion); - const logsContainer = useRef(null); - - const scrollToBottom = () => { - setTimeout(() => { - if (logsContainer.current) { - logsContainer.current.scrollTop = logsContainer.current.scrollHeight; - } - }, 0); - }; - - // biome-ignore lint/correctness/useExhaustiveDependencies: consider refactoring - useLayoutEffect(() => { - scrollToBottom(); - }, [logs]); - - // biome-ignore lint/correctness/useExhaustiveDependencies: consider refactoring - useLayoutEffect(() => { - if (drawerProps.open) { - scrollToBottom(); - } - }, [drawerProps.open]); const isMissingVariables = error instanceof JobError && @@ -58,6 +37,7 @@ export const BuildLogsDrawer: FC = ({ const matchingProvisioners = templateVersion?.matched_provisioners?.count; const availableProvisioners = templateVersion?.matched_provisioners?.available; + const hasLogs = logs && logs.length > 0; return ( @@ -70,8 +50,6 @@ export const BuildLogsDrawer: FC = ({ - {} - {isMissingVariables ? ( { @@ -80,23 +58,28 @@ export const BuildLogsDrawer: FC = ({ }); const firstVariableInput = variablesSectionRef.current?.querySelector("input"); - setTimeout(() => firstVariableInput?.focus(), 0); + firstVariableInput?.focus(); drawerProps.onClose(); }} /> - ) : availableProvisioners && availableProvisioners > 0 && logs ? ( -
- -
) : ( <> - - + {(matchingProvisioners === 0 || !hasLogs) && ( + + )} + + {hasLogs ? ( +
+ +
+ ) : ( + + )} )}
diff --git a/site/src/pages/CreateTemplatePage/CreateTemplateForm.tsx b/site/src/pages/CreateTemplatePage/CreateTemplateForm.tsx index ddd967554134b..e3528e9b707be 100644 --- a/site/src/pages/CreateTemplatePage/CreateTemplateForm.tsx +++ b/site/src/pages/CreateTemplatePage/CreateTemplateForm.tsx @@ -186,7 +186,7 @@ type CreateTemplateFormProps = ( jobError?: string; logs?: ProvisionerJobLog[]; allowAdvancedScheduling: boolean; - variablesSectionRef: React.RefObject; + variablesSectionRef: React.RefObject; showOrganizationPicker?: boolean; }; diff --git a/site/src/pages/CreateTemplatePage/types.ts b/site/src/pages/CreateTemplatePage/types.ts index 2bf4894ff884f..418bf72b24a3e 100644 --- a/site/src/pages/CreateTemplatePage/types.ts +++ b/site/src/pages/CreateTemplatePage/types.ts @@ -3,7 +3,7 @@ import type { CreateTemplateOptions } from "api/queries/templates"; export type CreateTemplatePageViewProps = { onCreateTemplate: (options: CreateTemplateOptions) => Promise; onOpenBuildLogsDrawer: () => void; - variablesSectionRef: React.RefObject; + variablesSectionRef: React.RefObject; error: unknown; isCreating: boolean; }; diff --git a/site/src/pages/DeploymentSettingsPage/ExternalAuthSettingsPage/ExternalAuthSettingsPageView.stories.tsx b/site/src/pages/DeploymentSettingsPage/ExternalAuthSettingsPage/ExternalAuthSettingsPageView.stories.tsx index 5184219b38cca..f44ecd4144891 100644 --- a/site/src/pages/DeploymentSettingsPage/ExternalAuthSettingsPage/ExternalAuthSettingsPageView.stories.tsx +++ b/site/src/pages/DeploymentSettingsPage/ExternalAuthSettingsPage/ExternalAuthSettingsPageView.stories.tsx @@ -23,6 +23,9 @@ const meta: Meta = { device_code_url: "", display_icon: "", display_name: "GitHub", + mcp_url: "", + mcp_tool_allow_regex: "", + mcp_tool_deny_regex: "", }, ], }, diff --git a/site/src/pages/DeploymentSettingsPage/Fieldset.tsx b/site/src/pages/DeploymentSettingsPage/Fieldset.tsx index 3fc5c8523eb5a..9a6e479fa8bc7 100644 --- a/site/src/pages/DeploymentSettingsPage/Fieldset.tsx +++ b/site/src/pages/DeploymentSettingsPage/Fieldset.tsx @@ -1,6 +1,6 @@ import { type CSSObject, useTheme } from "@emotion/react"; import { Button } from "components/Button/Button"; -import type { FC, FormEventHandler, ReactNode } from "react"; +import type { FC, FormEventHandler, JSX, ReactNode } from "react"; interface FieldsetProps { children: ReactNode; diff --git a/site/src/pages/DeploymentSettingsPage/UserAuthSettingsPage/UserAuthSettingsPageView.tsx b/site/src/pages/DeploymentSettingsPage/UserAuthSettingsPage/UserAuthSettingsPageView.tsx index 7b50eb486bf56..7e2636616a4d2 100644 --- a/site/src/pages/DeploymentSettingsPage/UserAuthSettingsPage/UserAuthSettingsPageView.tsx +++ b/site/src/pages/DeploymentSettingsPage/UserAuthSettingsPage/UserAuthSettingsPageView.tsx @@ -7,6 +7,7 @@ import { SettingsHeaderTitle, } from "components/SettingsHeader/SettingsHeader"; import { Stack } from "components/Stack/Stack"; +import type { JSX } from "react"; import { deploymentGroupHasParent, useDeploymentOptions, diff --git a/site/src/pages/HealthPage/AccessURLPage.tsx b/site/src/pages/HealthPage/AccessURLPage.tsx index 12b11e50374f5..2eaa8d04c5b1e 100644 --- a/site/src/pages/HealthPage/AccessURLPage.tsx +++ b/site/src/pages/HealthPage/AccessURLPage.tsx @@ -39,7 +39,7 @@ const AccessURLPage = () => { {accessUrl.warnings.map((warning) => { return ( } key={warning.code} severity="warning" > diff --git a/site/src/pages/HealthPage/Content.tsx b/site/src/pages/HealthPage/Content.tsx index b3e39343c1e02..97729a12184c3 100644 --- a/site/src/pages/HealthPage/Content.tsx +++ b/site/src/pages/HealthPage/Content.tsx @@ -1,4 +1,3 @@ -import { css } from "@emotion/css"; import { useTheme } from "@emotion/react"; import Link from "@mui/material/Link"; import type { HealthCode, HealthSeverity } from "api/typesGenerated"; @@ -157,7 +156,7 @@ export const SectionLabel: FC> = (props) => { }; type PillProps = HTMLAttributes & { - icon: ReactElement; + icon: ReactElement>; }; export const Pill = forwardRef((props, ref) => { @@ -181,7 +180,7 @@ export const Pill = forwardRef((props, ref) => { }} {...divProps} > - {cloneElement(icon, { className: css({ width: 14, height: 14 }) })} + {cloneElement(icon, { className: "size-[14px]" })} {children} ); diff --git a/site/src/pages/HealthPage/DERPPage.tsx b/site/src/pages/HealthPage/DERPPage.tsx index 08b2a121b445f..5f35e98d930f9 100644 --- a/site/src/pages/HealthPage/DERPPage.tsx +++ b/site/src/pages/HealthPage/DERPPage.tsx @@ -3,7 +3,6 @@ import LocationOnOutlined from "@mui/icons-material/LocationOnOutlined"; import Button from "@mui/material/Button"; import type { HealthcheckReport, - HealthMessage, HealthSeverity, NetcheckReport, } from "api/typesGenerated"; @@ -65,10 +64,10 @@ const DERPPage: FC = () => {
- {derp.warnings.map((warning: HealthMessage) => { + {derp.warnings.map((warning) => { return ( } key={warning.code} severity="warning" > diff --git a/site/src/pages/HealthPage/DERPRegionPage.tsx b/site/src/pages/HealthPage/DERPRegionPage.tsx index 1c81196412795..ef4df96a171ee 100644 --- a/site/src/pages/HealthPage/DERPRegionPage.tsx +++ b/site/src/pages/HealthPage/DERPRegionPage.tsx @@ -4,7 +4,6 @@ import type { DERPNodeReport, DERPRegionReport, HealthcheckReport, - HealthMessage, HealthSeverity, } from "api/typesGenerated"; import { Alert } from "components/Alert/Alert"; @@ -75,10 +74,10 @@ const DERPRegionPage: FC = () => {
- {warnings.map((warning: HealthMessage) => { + {warnings.map((warning) => { return ( } key={warning.code} severity="warning" > diff --git a/site/src/pages/HealthPage/DatabasePage.tsx b/site/src/pages/HealthPage/DatabasePage.tsx index e7b4cc9578288..81efc47b4a89c 100644 --- a/site/src/pages/HealthPage/DatabasePage.tsx +++ b/site/src/pages/HealthPage/DatabasePage.tsx @@ -37,7 +37,7 @@ const DatabasePage = () => { {database.warnings.map((warning) => { return ( } key={warning.code} severity="warning" > diff --git a/site/src/pages/HealthPage/ProvisionerDaemonsPage.tsx b/site/src/pages/HealthPage/ProvisionerDaemonsPage.tsx index fb473b8d6cae7..4c4d2d2f205fa 100644 --- a/site/src/pages/HealthPage/ProvisionerDaemonsPage.tsx +++ b/site/src/pages/HealthPage/ProvisionerDaemonsPage.tsx @@ -37,7 +37,7 @@ const ProvisionerDaemonsPage: FC = () => { {daemons.warnings.map((warning) => { return ( } key={warning.code} severity="warning" > diff --git a/site/src/pages/HealthPage/WorkspaceProxyPage.tsx b/site/src/pages/HealthPage/WorkspaceProxyPage.tsx index 25188463d0126..a076891620e5c 100644 --- a/site/src/pages/HealthPage/WorkspaceProxyPage.tsx +++ b/site/src/pages/HealthPage/WorkspaceProxyPage.tsx @@ -47,7 +47,7 @@ const WorkspaceProxyPage: FC = () => { {workspace_proxy.warnings.map((warning) => { return ( } key={warning.code} severity="warning" > diff --git a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditor.tsx b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditor.tsx index 855ccbd19e5ef..cba63cdf652ea 100644 --- a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditor.tsx +++ b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditor.tsx @@ -174,7 +174,7 @@ export const TemplateVersionEditor: FC = ({ }, [triggerPreview]); // Automatically switch to the template preview tab when the build succeeds. - const previousVersion = useRef(); + const previousVersion = useRef(undefined); useEffect(() => { if (!previousVersion.current) { previousVersion.current = templateVersion; @@ -197,15 +197,6 @@ export const TemplateVersionEditor: FC = ({ const isEditorValueBinary = typeof editorValue === "string" ? isBinaryData(editorValue) : false; - // Auto scroll - const logsContentRef = useRef(null); - // biome-ignore lint/correctness/useExhaustiveDependencies: consider refactoring - useEffect(() => { - if (logsContentRef.current) { - logsContentRef.current.scrollTop = logsContentRef.current.scrollHeight; - } - }, [buildLogs, resources]); - useLeaveSiteWarning(dirty); const canBuild = !isBuilding; @@ -596,10 +587,7 @@ export const TemplateVersionEditor: FC = ({ {selectedTab === "logs" && ( -
+
{templateVersion.job.error ? (
{ appearanceSettingsQuery.data?.terminal_font || DEFAULT_TERMINAL_FONT; // Create the terminal! - const fitAddonRef = useRef(); + const fitAddonRef = useRef(undefined); useEffect(() => { if (!terminalWrapperRef.current || config.isLoading) { return; @@ -209,7 +209,7 @@ const TerminalPage: FC = () => { }, [navigate, reconnectionToken, searchParams]); // Hook up the terminal through a web socket. - const websocketRef = useRef(); + const websocketRef = useRef(undefined); useEffect(() => { if (!terminal) { return; diff --git a/site/src/pages/UsersPage/ResetPasswordDialog.tsx b/site/src/pages/UsersPage/ResetPasswordDialog.tsx index a958504263eac..dfe26593ce87a 100644 --- a/site/src/pages/UsersPage/ResetPasswordDialog.tsx +++ b/site/src/pages/UsersPage/ResetPasswordDialog.tsx @@ -1,7 +1,7 @@ import type * as TypesGen from "api/typesGenerated"; import { CodeExample } from "components/CodeExample/CodeExample"; import { ConfirmDialog } from "components/Dialogs/ConfirmDialog/ConfirmDialog"; -import type { FC } from "react"; +import type { FC, JSX } from "react"; interface ResetPasswordDialogProps { open: boolean; diff --git a/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.tsx b/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.tsx index 75849e0790f67..2cfffc18d55d8 100644 --- a/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.tsx +++ b/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.tsx @@ -299,6 +299,7 @@ const BuildLogsContent: FC<{ }} logs={sortLogsByCreatedAt(logs)} build={build} + disableAutoscroll /> ); }; diff --git a/site/src/pages/WorkspacePage/WorkspaceBuildLogsSection.tsx b/site/src/pages/WorkspacePage/WorkspaceBuildLogsSection.tsx index 9c3e6d5479bf6..e5d74cf3c422f 100644 --- a/site/src/pages/WorkspacePage/WorkspaceBuildLogsSection.tsx +++ b/site/src/pages/WorkspacePage/WorkspaceBuildLogsSection.tsx @@ -2,7 +2,7 @@ import { useTheme } from "@emotion/react"; import type { ProvisionerJobLog } from "api/typesGenerated"; import { Loader } from "components/Loader/Loader"; import { WorkspaceBuildLogs } from "modules/workspaces/WorkspaceBuildLogs/WorkspaceBuildLogs"; -import { type FC, useEffect, useRef } from "react"; +import type { FC } from "react"; interface WorkspaceBuildLogsSectionProps { logs?: ProvisionerJobLog[]; @@ -11,22 +11,8 @@ interface WorkspaceBuildLogsSectionProps { export const WorkspaceBuildLogsSection: FC = ({ logs, }) => { - const scrollRef = useRef(null); const theme = useTheme(); - // biome-ignore lint/correctness/useExhaustiveDependencies: reset scroll when logs change - useEffect(() => { - // Auto scrolling makes hard to snapshot test using Chromatic - if (process.env.STORYBOOK === "true") { - return; - } - - const scrollEl = scrollRef.current; - if (scrollEl) { - scrollEl.scrollTop = scrollEl.scrollHeight; - } - }, [logs]); - return (
= ({ > Build logs -
+
{logs ? ( = ({ ); const deadlinePlusEnabled = maxDeadlineIncrease >= 1; const deadlineMinusEnabled = maxDeadlineDecrease >= 1; - const deadlineUpdateTimeout = useRef(); + const deadlineUpdateTimeout = useRef(undefined); const lastStableDeadline = useRef(deadline); const updateWorkspaceDeadlineQueryData = (deadline: Dayjs) => { diff --git a/site/src/testHelpers/renderHelpers.tsx b/site/src/testHelpers/renderHelpers.tsx index c928e376992bd..8e20fef5d3e0d 100644 --- a/site/src/testHelpers/renderHelpers.tsx +++ b/site/src/testHelpers/renderHelpers.tsx @@ -12,7 +12,7 @@ import type { DashboardProvider } from "modules/dashboard/DashboardProvider"; import OrganizationSettingsLayout from "modules/management/OrganizationSettingsLayout"; import { TemplateSettingsLayout } from "pages/TemplateSettingsPage/TemplateSettingsLayout"; import { WorkspaceSettingsLayout } from "pages/WorkspaceSettingsPage/WorkspaceSettingsLayout"; -import type { ReactNode } from "react"; +import type { JSX, ReactNode } from "react"; import { QueryClient } from "react-query"; import { createMemoryRouter, @@ -236,7 +236,7 @@ export const waitForLoaderToBeRemoved = async (): Promise => { ); }; -export const renderComponent = (component: React.ReactElement) => { +export const renderComponent = (component: React.ReactNode) => { return testingLibraryRender(component, { wrapper: ({ children }) => ( {children}