diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..1512871d --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,95 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +Obiente Cloud is a distributed Platform-as-a-Service (PaaS) built as an Nx monorepo with 14 Go microservices and a Nuxt 4 dashboard. Services communicate via ConnectRPC (protocol buffers). Deployed on Docker Swarm. + +## Build & Development Commands + +### Package Management +```bash +pnpm install # Install all JS/TS dependencies +``` + +### Dashboard (Nuxt 4 frontend) +```bash +nx serve dashboard # Dev server on port 3000 +nx nuxt:build dashboard # Production build +nx lint dashboard # ESLint +nx typecheck dashboard # Type checking +``` + +### Go Services +```bash +cd apps/ +go run main.go # Run locally +go build # Build binary +go test ./... # Run tests +``` + +### Protocol Buffers +```bash +cd packages/proto +pnpm build # Regenerate all proto code (buf generate) +``` +Generated Go code goes to `apps/shared/proto/`, TypeScript to `packages/proto/src/generated/`. + +### Docker +```bash +docker compose up -d # Local dev (all services) +docker build -f apps//Dockerfile -t ghcr.io/obiente/cloud-:latest . # Build image +./scripts/deploy-swarm-dev.sh # Swarm dev deploy +./scripts/deploy-swarm-dev.sh -b # Build + deploy +``` + +### Nx +Always prefer running tasks through `nx` rather than underlying tooling directly. Use `nx run`, `nx run-many`, `nx affected`. + +## Architecture + +### Service Ports +| Service | Port | +|---------|------| +| Dashboard | 3000 | +| API Gateway | 3001 | +| Auth | 3002 | +| Organizations | 3003 | +| Billing | 3004 | +| Deployments | 3005 | +| GameServers | 3006 | +| Orchestrator | 3007 | +| VPS | 3008 | +| Support | 3009 | +| Audit | 3010 | +| Superadmin | 3011 | +| Notifications | 3012 | +| DNS | 8053 | + +### Key Architectural Patterns + +- **API Gateway** routes all external requests to backend services. Supports both direct service routing and Traefik-based routing. +- **ConnectRPC** is used for all inter-service communication. Proto definitions live in `packages/proto/proto/obiente/cloud/`. Buf generates both Go and TypeScript clients. +- **Go workspace** (`go.work`) links all 15 Go modules. Shared code is in `apps/shared/` with packages for auth, database, docker, middleware, orchestrator, quota, etc. +- **Auth** is handled via Zitadel integration with RBAC. The auth-service validates tokens and manages roles/permissions. +- **Orchestrator** handles intelligent node selection and load balancing across the Docker Swarm cluster. +- **Database**: PostgreSQL (primary), TimescaleDB (metrics/audit), Redis (cache, build logs). +- **Dashboard** uses Nuxt 4, Vue 3, Tailwind CSS v4, Pinia for state, Ark UI for components, and `@connectrpc/connect-web` for API calls. + +### Monorepo Structure +- `apps/` - All microservices + dashboard +- `packages/proto/` - Protobuf definitions and generated code +- `packages/database/` - Drizzle ORM schemas and migrations +- `packages/config/` - Shared ESLint, Prettier, TypeScript configs +- `packages/types/` - Shared TypeScript types +- `tools/nxsh/` - Custom Nx shell executor +- `monitoring/` - Prometheus & Grafana configs +- `scripts/` - Deployment and operational scripts + +### Docker Compose Files +- `docker-compose.yml` - Local development +- `docker-compose.base.yml` - Shared env vars (YAML anchors) +- `docker-compose.swarm.yml` - Production swarm +- `docker-compose.swarm.dev.yml` - Dev swarm (must use `docker stack deploy`, not `docker compose`) +- `docker-compose.swarm.ha.yml` - HA production with PostgreSQL cluster diff --git a/apps/dashboard/app/pages/superadmin/organizations/index.vue b/apps/dashboard/app/pages/superadmin/organizations/index.vue index 110b3527..4803fb5c 100644 --- a/apps/dashboard/app/pages/superadmin/organizations/index.vue +++ b/apps/dashboard/app/pages/superadmin/organizations/index.vue @@ -130,6 +130,61 @@ + + + + + + Organization + {{ selectedOrgName }} + + + + Current Plan + + {{ prettyPlan(selectedOrgCurrentPlan) || 'None' }} + + + + + + + + + + diff --git a/apps/dashboard/nuxt.config.ts b/apps/dashboard/nuxt.config.ts index 4d28e7c1..115427e2 100644 --- a/apps/dashboard/nuxt.config.ts +++ b/apps/dashboard/nuxt.config.ts @@ -168,7 +168,7 @@ export default defineNuxtConfig({ // Use API Gateway for all requests (routes to microservices) // When running locally (not in Docker), use localhost with Traefik port // When running in Docker, use api-gateway service name - apiHostInternal: process.env.NUXT_API_HOST_INTERNAL || process.env.NUXT_PUBLIC_API_HOST || "http://localhost:80", + apiHostInternal: process.env.NUXT_API_HOST_INTERNAL || process.env.NUXT_PUBLIC_API_HOST || "http://api.localhost", githubClientSecret: process.env.GITHUB_CLIENT_SECRET || "", // Server-side only - never expose to client session: { password: "changeme_" + crypto.randomUUID(), // CHANGE THIS IN PRODUCTION, should be at least 32 characters @@ -196,6 +196,9 @@ export default defineNuxtConfig({ port: 3000, host: "0.0.0.0", }, + future: { + compatibilityVersion: 4 + }, app: { head: { diff --git a/apps/organizations-service/internal/service/service.go b/apps/organizations-service/internal/service/service.go index 23b81c19..22c65fef 100644 --- a/apps/organizations-service/internal/service/service.go +++ b/apps/organizations-service/internal/service/service.go @@ -2111,3 +2111,69 @@ func (s *Service) GetMyPermissions(ctx context.Context, req *connect.Request[org Permissions: permissions, }), nil } + +// AdminSetPlan sets the active plan for an organization (superadmin only) +func (s *Service) AdminSetPlan(ctx context.Context, req *connect.Request[organizationsv1.AdminSetPlanRequest]) (*connect.Response[organizationsv1.AdminSetPlanResponse], error) { + // Authenticate the user + user, err := auth.GetUserFromContext(ctx) + if err != nil { + return nil, connect.NewError(connect.CodeUnauthenticated, fmt.Errorf("unauthenticated")) + } + + // Ensure the user is a superadmin + if !auth.IsSuperadmin(ctx, user) { + return nil, connect.NewError(connect.CodePermissionDenied, fmt.Errorf("permission denied")) + } + + // Validate the organization and plan IDs + orgID := req.Msg.GetOrganizationId() + planID := req.Msg.GetPlanId() + if orgID == "" || planID == "" { + return nil, connect.NewError(connect.CodeInvalidArgument, fmt.Errorf("organization_id and plan_id are required")) + } + + // Update the organization's plan in the database + result := database.DB.Exec(` + UPDATE organizations + SET plan = ? + WHERE id = ? + `, planID, orgID) + if result.Error != nil { + return nil, connect.NewError(connect.CodeInternal, fmt.Errorf("failed to update organization plan: %w", result.Error)) + } + + // Fetch the updated organization using the same row pattern as ListOrganizations + type row struct { + Id, Name, Slug, Plan, Status string + Domain *string + Credits int64 + TotalPaidCents int64 + CreatedAt time.Time + } + var r row + err = database.DB.Raw(` + SELECT id, name, slug, plan, status, domain, credits, total_paid_cents, created_at + FROM organizations + WHERE id = ? + `, orgID).Scan(&r).Error + if err != nil { + return nil, connect.NewError(connect.CodeInternal, fmt.Errorf("failed to fetch updated organization: %w", err)) + } + + org := &database.Organization{ + ID: r.Id, + Name: r.Name, + Slug: r.Slug, + Plan: r.Plan, + Status: r.Status, + Domain: r.Domain, + Credits: r.Credits, + TotalPaidCents: r.TotalPaidCents, + CreatedAt: r.CreatedAt, + } + + return connect.NewResponse(&organizationsv1.AdminSetPlanResponse{ + Organization: organizationToProto(org), + PlanId: planID, + }), nil +} diff --git a/apps/shared/proto/obiente/cloud/deployments/v1/deployment_service.pb.go b/apps/shared/proto/obiente/cloud/deployments/v1/deployment_service.pb.go index d8528391..55dcb73f 100644 --- a/apps/shared/proto/obiente/cloud/deployments/v1/deployment_service.pb.go +++ b/apps/shared/proto/obiente/cloud/deployments/v1/deployment_service.pb.go @@ -333,7 +333,7 @@ func (BuildStatus) EnumDescriptor() ([]byte, []int) { type HealthCheckType int32 const ( - HealthCheckType_HEALTHCHECK_TYPE_UNSPECIFIED HealthCheckType = 0 // No health check + HealthCheckType_HEALTHCHECK_TYPE_UNSPECIFIED HealthCheckType = 0 // Auto-detect (TCP if routing exists, otherwise no healthcheck) HealthCheckType_HEALTHCHECK_DISABLED HealthCheckType = 1 // Explicitly disabled HealthCheckType_HEALTHCHECK_TCP HealthCheckType = 2 // TCP port check (nc) HealthCheckType_HEALTHCHECK_HTTP HealthCheckType = 3 // HTTP endpoint check diff --git a/apps/shared/proto/obiente/cloud/organizations/v1/organization_service.pb.go b/apps/shared/proto/obiente/cloud/organizations/v1/organization_service.pb.go index 5d12844e..84eaa2db 100644 --- a/apps/shared/proto/obiente/cloud/organizations/v1/organization_service.pb.go +++ b/apps/shared/proto/obiente/cloud/organizations/v1/organization_service.pb.go @@ -2847,6 +2847,112 @@ func (x *GetMyPermissionsResponse) GetPermissions() []string { return nil } +// Request to set the active plan for an organization (superadmin only) +type AdminSetPlanRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + OrganizationId string `protobuf:"bytes,1,opt,name=organization_id,json=organizationId,proto3" json:"organization_id,omitempty"` + PlanId string `protobuf:"bytes,2,opt,name=plan_id,json=planId,proto3" json:"plan_id,omitempty"` // The plan to assign (must match OrganizationPlan.ID) + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AdminSetPlanRequest) Reset() { + *x = AdminSetPlanRequest{} + mi := &file_obiente_cloud_organizations_v1_organization_service_proto_msgTypes[45] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AdminSetPlanRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AdminSetPlanRequest) ProtoMessage() {} + +func (x *AdminSetPlanRequest) ProtoReflect() protoreflect.Message { + mi := &file_obiente_cloud_organizations_v1_organization_service_proto_msgTypes[45] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AdminSetPlanRequest.ProtoReflect.Descriptor instead. +func (*AdminSetPlanRequest) Descriptor() ([]byte, []int) { + return file_obiente_cloud_organizations_v1_organization_service_proto_rawDescGZIP(), []int{45} +} + +func (x *AdminSetPlanRequest) GetOrganizationId() string { + if x != nil { + return x.OrganizationId + } + return "" +} + +func (x *AdminSetPlanRequest) GetPlanId() string { + if x != nil { + return x.PlanId + } + return "" +} + +// Response for AdminSetPlan +type AdminSetPlanResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Organization *Organization `protobuf:"bytes,1,opt,name=organization,proto3" json:"organization,omitempty"` + PlanId string `protobuf:"bytes,2,opt,name=plan_id,json=planId,proto3" json:"plan_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AdminSetPlanResponse) Reset() { + *x = AdminSetPlanResponse{} + mi := &file_obiente_cloud_organizations_v1_organization_service_proto_msgTypes[46] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AdminSetPlanResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AdminSetPlanResponse) ProtoMessage() {} + +func (x *AdminSetPlanResponse) ProtoReflect() protoreflect.Message { + mi := &file_obiente_cloud_organizations_v1_organization_service_proto_msgTypes[46] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AdminSetPlanResponse.ProtoReflect.Descriptor instead. +func (*AdminSetPlanResponse) Descriptor() ([]byte, []int) { + return file_obiente_cloud_organizations_v1_organization_service_proto_rawDescGZIP(), []int{46} +} + +func (x *AdminSetPlanResponse) GetOrganization() *Organization { + if x != nil { + return x.Organization + } + return nil +} + +func (x *AdminSetPlanResponse) GetPlanId() string { + if x != nil { + return x.PlanId + } + return "" +} + var File_obiente_cloud_organizations_v1_organization_service_proto protoreflect.FileDescriptor const file_obiente_cloud_organizations_v1_organization_service_proto_rawDesc = "" + @@ -3078,8 +3184,15 @@ const file_obiente_cloud_organizations_v1_organization_service_proto_rawDesc = " "\x17GetMyPermissionsRequest\x12'\n" + "\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\"<\n" + "\x18GetMyPermissionsResponse\x12 \n" + - "\vpermissions\x18\x01 \x03(\tR\vpermissions2\xa1\x13\n" + - "\x13OrganizationService\x12\x88\x01\n" + + "\vpermissions\x18\x01 \x03(\tR\vpermissions\"W\n" + + "\x13AdminSetPlanRequest\x12'\n" + + "\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x12\x17\n" + + "\aplan_id\x18\x02 \x01(\tR\x06planId\"\x81\x01\n" + + "\x14AdminSetPlanResponse\x12P\n" + + "\forganization\x18\x01 \x01(\v2,.obiente.cloud.organizations.v1.OrganizationR\forganization\x12\x17\n" + + "\aplan_id\x18\x02 \x01(\tR\x06planId2\x9c\x14\n" + + "\x13OrganizationService\x12y\n" + + "\fAdminSetPlan\x123.obiente.cloud.organizations.v1.AdminSetPlanRequest\x1a4.obiente.cloud.organizations.v1.AdminSetPlanResponse\x12\x88\x01\n" + "\x11ListOrganizations\x128.obiente.cloud.organizations.v1.ListOrganizationsRequest\x1a9.obiente.cloud.organizations.v1.ListOrganizationsResponse\x12\x8b\x01\n" + "\x12CreateOrganization\x129.obiente.cloud.organizations.v1.CreateOrganizationRequest\x1a:.obiente.cloud.organizations.v1.CreateOrganizationResponse\x12\x82\x01\n" + "\x0fGetOrganization\x126.obiente.cloud.organizations.v1.GetOrganizationRequest\x1a7.obiente.cloud.organizations.v1.GetOrganizationResponse\x12\x8b\x01\n" + @@ -3113,7 +3226,7 @@ func file_obiente_cloud_organizations_v1_organization_service_proto_rawDescGZIP( return file_obiente_cloud_organizations_v1_organization_service_proto_rawDescData } -var file_obiente_cloud_organizations_v1_organization_service_proto_msgTypes = make([]protoimpl.MessageInfo, 45) +var file_obiente_cloud_organizations_v1_organization_service_proto_msgTypes = make([]protoimpl.MessageInfo, 47) var file_obiente_cloud_organizations_v1_organization_service_proto_goTypes = []any{ (*GetUsageRequest)(nil), // 0: obiente.cloud.organizations.v1.GetUsageRequest (*GetUsageResponse)(nil), // 1: obiente.cloud.organizations.v1.GetUsageResponse @@ -3160,81 +3273,86 @@ var file_obiente_cloud_organizations_v1_organization_service_proto_goTypes = []a (*CreditTransaction)(nil), // 42: obiente.cloud.organizations.v1.CreditTransaction (*GetMyPermissionsRequest)(nil), // 43: obiente.cloud.organizations.v1.GetMyPermissionsRequest (*GetMyPermissionsResponse)(nil), // 44: obiente.cloud.organizations.v1.GetMyPermissionsResponse - (*v1.Pagination)(nil), // 45: obiente.cloud.common.v1.Pagination - (*timestamppb.Timestamp)(nil), // 46: google.protobuf.Timestamp - (*v11.User)(nil), // 47: obiente.cloud.auth.v1.User + (*AdminSetPlanRequest)(nil), // 45: obiente.cloud.organizations.v1.AdminSetPlanRequest + (*AdminSetPlanResponse)(nil), // 46: obiente.cloud.organizations.v1.AdminSetPlanResponse + (*v1.Pagination)(nil), // 47: obiente.cloud.common.v1.Pagination + (*timestamppb.Timestamp)(nil), // 48: google.protobuf.Timestamp + (*v11.User)(nil), // 49: obiente.cloud.auth.v1.User } var file_obiente_cloud_organizations_v1_organization_service_proto_depIdxs = []int32{ 2, // 0: obiente.cloud.organizations.v1.GetUsageResponse.current:type_name -> obiente.cloud.organizations.v1.UsageMetrics 2, // 1: obiente.cloud.organizations.v1.GetUsageResponse.estimated_monthly:type_name -> obiente.cloud.organizations.v1.UsageMetrics 3, // 2: obiente.cloud.organizations.v1.GetUsageResponse.quota:type_name -> obiente.cloud.organizations.v1.UsageQuota 31, // 3: obiente.cloud.organizations.v1.ListOrganizationsResponse.organizations:type_name -> obiente.cloud.organizations.v1.Organization - 45, // 4: obiente.cloud.organizations.v1.ListOrganizationsResponse.pagination:type_name -> obiente.cloud.common.v1.Pagination + 47, // 4: obiente.cloud.organizations.v1.ListOrganizationsResponse.pagination:type_name -> obiente.cloud.common.v1.Pagination 31, // 5: obiente.cloud.organizations.v1.CreateOrganizationResponse.organization:type_name -> obiente.cloud.organizations.v1.Organization 31, // 6: obiente.cloud.organizations.v1.GetOrganizationResponse.organization:type_name -> obiente.cloud.organizations.v1.Organization 31, // 7: obiente.cloud.organizations.v1.UpdateOrganizationResponse.organization:type_name -> obiente.cloud.organizations.v1.Organization 33, // 8: obiente.cloud.organizations.v1.ListMembersResponse.members:type_name -> obiente.cloud.organizations.v1.OrganizationMember - 45, // 9: obiente.cloud.organizations.v1.ListMembersResponse.pagination:type_name -> obiente.cloud.common.v1.Pagination + 47, // 9: obiente.cloud.organizations.v1.ListMembersResponse.pagination:type_name -> obiente.cloud.common.v1.Pagination 33, // 10: obiente.cloud.organizations.v1.InviteMemberResponse.member:type_name -> obiente.cloud.organizations.v1.OrganizationMember 20, // 11: obiente.cloud.organizations.v1.ListMyInvitesResponse.invites:type_name -> obiente.cloud.organizations.v1.PendingInvite - 45, // 12: obiente.cloud.organizations.v1.ListMyInvitesResponse.pagination:type_name -> obiente.cloud.common.v1.Pagination - 46, // 13: obiente.cloud.organizations.v1.PendingInvite.invited_at:type_name -> google.protobuf.Timestamp + 47, // 12: obiente.cloud.organizations.v1.ListMyInvitesResponse.pagination:type_name -> obiente.cloud.common.v1.Pagination + 48, // 13: obiente.cloud.organizations.v1.PendingInvite.invited_at:type_name -> google.protobuf.Timestamp 33, // 14: obiente.cloud.organizations.v1.AcceptInviteResponse.member:type_name -> obiente.cloud.organizations.v1.OrganizationMember 31, // 15: obiente.cloud.organizations.v1.AcceptInviteResponse.organization:type_name -> obiente.cloud.organizations.v1.Organization 33, // 16: obiente.cloud.organizations.v1.UpdateMemberResponse.member:type_name -> obiente.cloud.organizations.v1.OrganizationMember - 46, // 17: obiente.cloud.organizations.v1.Organization.created_at:type_name -> google.protobuf.Timestamp + 48, // 17: obiente.cloud.organizations.v1.Organization.created_at:type_name -> google.protobuf.Timestamp 32, // 18: obiente.cloud.organizations.v1.Organization.plan_info:type_name -> obiente.cloud.organizations.v1.PlanInfo - 47, // 19: obiente.cloud.organizations.v1.OrganizationMember.user:type_name -> obiente.cloud.auth.v1.User - 46, // 20: obiente.cloud.organizations.v1.OrganizationMember.joined_at:type_name -> google.protobuf.Timestamp + 49, // 19: obiente.cloud.organizations.v1.OrganizationMember.user:type_name -> obiente.cloud.auth.v1.User + 48, // 20: obiente.cloud.organizations.v1.OrganizationMember.joined_at:type_name -> google.protobuf.Timestamp 31, // 21: obiente.cloud.organizations.v1.AddCreditsResponse.organization:type_name -> obiente.cloud.organizations.v1.Organization 31, // 22: obiente.cloud.organizations.v1.AdminAddCreditsResponse.organization:type_name -> obiente.cloud.organizations.v1.Organization 31, // 23: obiente.cloud.organizations.v1.AdminRemoveCreditsResponse.organization:type_name -> obiente.cloud.organizations.v1.Organization 42, // 24: obiente.cloud.organizations.v1.GetCreditLogResponse.transactions:type_name -> obiente.cloud.organizations.v1.CreditTransaction - 45, // 25: obiente.cloud.organizations.v1.GetCreditLogResponse.pagination:type_name -> obiente.cloud.common.v1.Pagination - 46, // 26: obiente.cloud.organizations.v1.CreditTransaction.created_at:type_name -> google.protobuf.Timestamp - 4, // 27: obiente.cloud.organizations.v1.OrganizationService.ListOrganizations:input_type -> obiente.cloud.organizations.v1.ListOrganizationsRequest - 6, // 28: obiente.cloud.organizations.v1.OrganizationService.CreateOrganization:input_type -> obiente.cloud.organizations.v1.CreateOrganizationRequest - 8, // 29: obiente.cloud.organizations.v1.OrganizationService.GetOrganization:input_type -> obiente.cloud.organizations.v1.GetOrganizationRequest - 10, // 30: obiente.cloud.organizations.v1.OrganizationService.UpdateOrganization:input_type -> obiente.cloud.organizations.v1.UpdateOrganizationRequest - 12, // 31: obiente.cloud.organizations.v1.OrganizationService.ListMembers:input_type -> obiente.cloud.organizations.v1.ListMembersRequest - 14, // 32: obiente.cloud.organizations.v1.OrganizationService.InviteMember:input_type -> obiente.cloud.organizations.v1.InviteMemberRequest - 16, // 33: obiente.cloud.organizations.v1.OrganizationService.ResendInvite:input_type -> obiente.cloud.organizations.v1.ResendInviteRequest - 18, // 34: obiente.cloud.organizations.v1.OrganizationService.ListMyInvites:input_type -> obiente.cloud.organizations.v1.ListMyInvitesRequest - 21, // 35: obiente.cloud.organizations.v1.OrganizationService.AcceptInvite:input_type -> obiente.cloud.organizations.v1.AcceptInviteRequest - 23, // 36: obiente.cloud.organizations.v1.OrganizationService.DeclineInvite:input_type -> obiente.cloud.organizations.v1.DeclineInviteRequest - 25, // 37: obiente.cloud.organizations.v1.OrganizationService.UpdateMember:input_type -> obiente.cloud.organizations.v1.UpdateMemberRequest - 27, // 38: obiente.cloud.organizations.v1.OrganizationService.RemoveMember:input_type -> obiente.cloud.organizations.v1.RemoveMemberRequest - 29, // 39: obiente.cloud.organizations.v1.OrganizationService.TransferOwnership:input_type -> obiente.cloud.organizations.v1.TransferOwnershipRequest - 0, // 40: obiente.cloud.organizations.v1.OrganizationService.GetUsage:input_type -> obiente.cloud.organizations.v1.GetUsageRequest - 34, // 41: obiente.cloud.organizations.v1.OrganizationService.AddCredits:input_type -> obiente.cloud.organizations.v1.AddCreditsRequest - 36, // 42: obiente.cloud.organizations.v1.OrganizationService.AdminAddCredits:input_type -> obiente.cloud.organizations.v1.AdminAddCreditsRequest - 38, // 43: obiente.cloud.organizations.v1.OrganizationService.AdminRemoveCredits:input_type -> obiente.cloud.organizations.v1.AdminRemoveCreditsRequest - 40, // 44: obiente.cloud.organizations.v1.OrganizationService.GetCreditLog:input_type -> obiente.cloud.organizations.v1.GetCreditLogRequest - 43, // 45: obiente.cloud.organizations.v1.OrganizationService.GetMyPermissions:input_type -> obiente.cloud.organizations.v1.GetMyPermissionsRequest - 5, // 46: obiente.cloud.organizations.v1.OrganizationService.ListOrganizations:output_type -> obiente.cloud.organizations.v1.ListOrganizationsResponse - 7, // 47: obiente.cloud.organizations.v1.OrganizationService.CreateOrganization:output_type -> obiente.cloud.organizations.v1.CreateOrganizationResponse - 9, // 48: obiente.cloud.organizations.v1.OrganizationService.GetOrganization:output_type -> obiente.cloud.organizations.v1.GetOrganizationResponse - 11, // 49: obiente.cloud.organizations.v1.OrganizationService.UpdateOrganization:output_type -> obiente.cloud.organizations.v1.UpdateOrganizationResponse - 13, // 50: obiente.cloud.organizations.v1.OrganizationService.ListMembers:output_type -> obiente.cloud.organizations.v1.ListMembersResponse - 15, // 51: obiente.cloud.organizations.v1.OrganizationService.InviteMember:output_type -> obiente.cloud.organizations.v1.InviteMemberResponse - 17, // 52: obiente.cloud.organizations.v1.OrganizationService.ResendInvite:output_type -> obiente.cloud.organizations.v1.ResendInviteResponse - 19, // 53: obiente.cloud.organizations.v1.OrganizationService.ListMyInvites:output_type -> obiente.cloud.organizations.v1.ListMyInvitesResponse - 22, // 54: obiente.cloud.organizations.v1.OrganizationService.AcceptInvite:output_type -> obiente.cloud.organizations.v1.AcceptInviteResponse - 24, // 55: obiente.cloud.organizations.v1.OrganizationService.DeclineInvite:output_type -> obiente.cloud.organizations.v1.DeclineInviteResponse - 26, // 56: obiente.cloud.organizations.v1.OrganizationService.UpdateMember:output_type -> obiente.cloud.organizations.v1.UpdateMemberResponse - 28, // 57: obiente.cloud.organizations.v1.OrganizationService.RemoveMember:output_type -> obiente.cloud.organizations.v1.RemoveMemberResponse - 30, // 58: obiente.cloud.organizations.v1.OrganizationService.TransferOwnership:output_type -> obiente.cloud.organizations.v1.TransferOwnershipResponse - 1, // 59: obiente.cloud.organizations.v1.OrganizationService.GetUsage:output_type -> obiente.cloud.organizations.v1.GetUsageResponse - 35, // 60: obiente.cloud.organizations.v1.OrganizationService.AddCredits:output_type -> obiente.cloud.organizations.v1.AddCreditsResponse - 37, // 61: obiente.cloud.organizations.v1.OrganizationService.AdminAddCredits:output_type -> obiente.cloud.organizations.v1.AdminAddCreditsResponse - 39, // 62: obiente.cloud.organizations.v1.OrganizationService.AdminRemoveCredits:output_type -> obiente.cloud.organizations.v1.AdminRemoveCreditsResponse - 41, // 63: obiente.cloud.organizations.v1.OrganizationService.GetCreditLog:output_type -> obiente.cloud.organizations.v1.GetCreditLogResponse - 44, // 64: obiente.cloud.organizations.v1.OrganizationService.GetMyPermissions:output_type -> obiente.cloud.organizations.v1.GetMyPermissionsResponse - 46, // [46:65] is the sub-list for method output_type - 27, // [27:46] is the sub-list for method input_type - 27, // [27:27] is the sub-list for extension type_name - 27, // [27:27] is the sub-list for extension extendee - 0, // [0:27] is the sub-list for field type_name + 47, // 25: obiente.cloud.organizations.v1.GetCreditLogResponse.pagination:type_name -> obiente.cloud.common.v1.Pagination + 48, // 26: obiente.cloud.organizations.v1.CreditTransaction.created_at:type_name -> google.protobuf.Timestamp + 31, // 27: obiente.cloud.organizations.v1.AdminSetPlanResponse.organization:type_name -> obiente.cloud.organizations.v1.Organization + 45, // 28: obiente.cloud.organizations.v1.OrganizationService.AdminSetPlan:input_type -> obiente.cloud.organizations.v1.AdminSetPlanRequest + 4, // 29: obiente.cloud.organizations.v1.OrganizationService.ListOrganizations:input_type -> obiente.cloud.organizations.v1.ListOrganizationsRequest + 6, // 30: obiente.cloud.organizations.v1.OrganizationService.CreateOrganization:input_type -> obiente.cloud.organizations.v1.CreateOrganizationRequest + 8, // 31: obiente.cloud.organizations.v1.OrganizationService.GetOrganization:input_type -> obiente.cloud.organizations.v1.GetOrganizationRequest + 10, // 32: obiente.cloud.organizations.v1.OrganizationService.UpdateOrganization:input_type -> obiente.cloud.organizations.v1.UpdateOrganizationRequest + 12, // 33: obiente.cloud.organizations.v1.OrganizationService.ListMembers:input_type -> obiente.cloud.organizations.v1.ListMembersRequest + 14, // 34: obiente.cloud.organizations.v1.OrganizationService.InviteMember:input_type -> obiente.cloud.organizations.v1.InviteMemberRequest + 16, // 35: obiente.cloud.organizations.v1.OrganizationService.ResendInvite:input_type -> obiente.cloud.organizations.v1.ResendInviteRequest + 18, // 36: obiente.cloud.organizations.v1.OrganizationService.ListMyInvites:input_type -> obiente.cloud.organizations.v1.ListMyInvitesRequest + 21, // 37: obiente.cloud.organizations.v1.OrganizationService.AcceptInvite:input_type -> obiente.cloud.organizations.v1.AcceptInviteRequest + 23, // 38: obiente.cloud.organizations.v1.OrganizationService.DeclineInvite:input_type -> obiente.cloud.organizations.v1.DeclineInviteRequest + 25, // 39: obiente.cloud.organizations.v1.OrganizationService.UpdateMember:input_type -> obiente.cloud.organizations.v1.UpdateMemberRequest + 27, // 40: obiente.cloud.organizations.v1.OrganizationService.RemoveMember:input_type -> obiente.cloud.organizations.v1.RemoveMemberRequest + 29, // 41: obiente.cloud.organizations.v1.OrganizationService.TransferOwnership:input_type -> obiente.cloud.organizations.v1.TransferOwnershipRequest + 0, // 42: obiente.cloud.organizations.v1.OrganizationService.GetUsage:input_type -> obiente.cloud.organizations.v1.GetUsageRequest + 34, // 43: obiente.cloud.organizations.v1.OrganizationService.AddCredits:input_type -> obiente.cloud.organizations.v1.AddCreditsRequest + 36, // 44: obiente.cloud.organizations.v1.OrganizationService.AdminAddCredits:input_type -> obiente.cloud.organizations.v1.AdminAddCreditsRequest + 38, // 45: obiente.cloud.organizations.v1.OrganizationService.AdminRemoveCredits:input_type -> obiente.cloud.organizations.v1.AdminRemoveCreditsRequest + 40, // 46: obiente.cloud.organizations.v1.OrganizationService.GetCreditLog:input_type -> obiente.cloud.organizations.v1.GetCreditLogRequest + 43, // 47: obiente.cloud.organizations.v1.OrganizationService.GetMyPermissions:input_type -> obiente.cloud.organizations.v1.GetMyPermissionsRequest + 46, // 48: obiente.cloud.organizations.v1.OrganizationService.AdminSetPlan:output_type -> obiente.cloud.organizations.v1.AdminSetPlanResponse + 5, // 49: obiente.cloud.organizations.v1.OrganizationService.ListOrganizations:output_type -> obiente.cloud.organizations.v1.ListOrganizationsResponse + 7, // 50: obiente.cloud.organizations.v1.OrganizationService.CreateOrganization:output_type -> obiente.cloud.organizations.v1.CreateOrganizationResponse + 9, // 51: obiente.cloud.organizations.v1.OrganizationService.GetOrganization:output_type -> obiente.cloud.organizations.v1.GetOrganizationResponse + 11, // 52: obiente.cloud.organizations.v1.OrganizationService.UpdateOrganization:output_type -> obiente.cloud.organizations.v1.UpdateOrganizationResponse + 13, // 53: obiente.cloud.organizations.v1.OrganizationService.ListMembers:output_type -> obiente.cloud.organizations.v1.ListMembersResponse + 15, // 54: obiente.cloud.organizations.v1.OrganizationService.InviteMember:output_type -> obiente.cloud.organizations.v1.InviteMemberResponse + 17, // 55: obiente.cloud.organizations.v1.OrganizationService.ResendInvite:output_type -> obiente.cloud.organizations.v1.ResendInviteResponse + 19, // 56: obiente.cloud.organizations.v1.OrganizationService.ListMyInvites:output_type -> obiente.cloud.organizations.v1.ListMyInvitesResponse + 22, // 57: obiente.cloud.organizations.v1.OrganizationService.AcceptInvite:output_type -> obiente.cloud.organizations.v1.AcceptInviteResponse + 24, // 58: obiente.cloud.organizations.v1.OrganizationService.DeclineInvite:output_type -> obiente.cloud.organizations.v1.DeclineInviteResponse + 26, // 59: obiente.cloud.organizations.v1.OrganizationService.UpdateMember:output_type -> obiente.cloud.organizations.v1.UpdateMemberResponse + 28, // 60: obiente.cloud.organizations.v1.OrganizationService.RemoveMember:output_type -> obiente.cloud.organizations.v1.RemoveMemberResponse + 30, // 61: obiente.cloud.organizations.v1.OrganizationService.TransferOwnership:output_type -> obiente.cloud.organizations.v1.TransferOwnershipResponse + 1, // 62: obiente.cloud.organizations.v1.OrganizationService.GetUsage:output_type -> obiente.cloud.organizations.v1.GetUsageResponse + 35, // 63: obiente.cloud.organizations.v1.OrganizationService.AddCredits:output_type -> obiente.cloud.organizations.v1.AddCreditsResponse + 37, // 64: obiente.cloud.organizations.v1.OrganizationService.AdminAddCredits:output_type -> obiente.cloud.organizations.v1.AdminAddCreditsResponse + 39, // 65: obiente.cloud.organizations.v1.OrganizationService.AdminRemoveCredits:output_type -> obiente.cloud.organizations.v1.AdminRemoveCreditsResponse + 41, // 66: obiente.cloud.organizations.v1.OrganizationService.GetCreditLog:output_type -> obiente.cloud.organizations.v1.GetCreditLogResponse + 44, // 67: obiente.cloud.organizations.v1.OrganizationService.GetMyPermissions:output_type -> obiente.cloud.organizations.v1.GetMyPermissionsResponse + 48, // [48:68] is the sub-list for method output_type + 28, // [28:48] is the sub-list for method input_type + 28, // [28:28] is the sub-list for extension type_name + 28, // [28:28] is the sub-list for extension extendee + 0, // [0:28] is the sub-list for field type_name } func init() { file_obiente_cloud_organizations_v1_organization_service_proto_init() } @@ -3258,7 +3376,7 @@ func file_obiente_cloud_organizations_v1_organization_service_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_obiente_cloud_organizations_v1_organization_service_proto_rawDesc), len(file_obiente_cloud_organizations_v1_organization_service_proto_rawDesc)), NumEnums: 0, - NumMessages: 45, + NumMessages: 47, NumExtensions: 0, NumServices: 1, }, diff --git a/apps/shared/proto/obiente/cloud/organizations/v1/organizationsv1connect/organization_service.connect.go b/apps/shared/proto/obiente/cloud/organizations/v1/organizationsv1connect/organization_service.connect.go index 715d2425..ee9d4c45 100644 --- a/apps/shared/proto/obiente/cloud/organizations/v1/organizationsv1connect/organization_service.connect.go +++ b/apps/shared/proto/obiente/cloud/organizations/v1/organizationsv1connect/organization_service.connect.go @@ -33,6 +33,9 @@ const ( // reflection-formatted method names, remove the leading slash and convert the remaining slash to a // period. const ( + // OrganizationServiceAdminSetPlanProcedure is the fully-qualified name of the OrganizationService's + // AdminSetPlan RPC. + OrganizationServiceAdminSetPlanProcedure = "/obiente.cloud.organizations.v1.OrganizationService/AdminSetPlan" // OrganizationServiceListOrganizationsProcedure is the fully-qualified name of the // OrganizationService's ListOrganizations RPC. OrganizationServiceListOrganizationsProcedure = "/obiente.cloud.organizations.v1.OrganizationService/ListOrganizations" @@ -95,6 +98,8 @@ const ( // OrganizationServiceClient is a client for the obiente.cloud.organizations.v1.OrganizationService // service. type OrganizationServiceClient interface { + // Admin: Set the active plan for an organization (superadmin only) + AdminSetPlan(context.Context, *connect.Request[v1.AdminSetPlanRequest]) (*connect.Response[v1.AdminSetPlanResponse], error) // List user's organizations ListOrganizations(context.Context, *connect.Request[v1.ListOrganizationsRequest]) (*connect.Response[v1.ListOrganizationsResponse], error) // Create new organization @@ -147,6 +152,12 @@ func NewOrganizationServiceClient(httpClient connect.HTTPClient, baseURL string, baseURL = strings.TrimRight(baseURL, "/") organizationServiceMethods := v1.File_obiente_cloud_organizations_v1_organization_service_proto.Services().ByName("OrganizationService").Methods() return &organizationServiceClient{ + adminSetPlan: connect.NewClient[v1.AdminSetPlanRequest, v1.AdminSetPlanResponse]( + httpClient, + baseURL+OrganizationServiceAdminSetPlanProcedure, + connect.WithSchema(organizationServiceMethods.ByName("AdminSetPlan")), + connect.WithClientOptions(opts...), + ), listOrganizations: connect.NewClient[v1.ListOrganizationsRequest, v1.ListOrganizationsResponse]( httpClient, baseURL+OrganizationServiceListOrganizationsProcedure, @@ -266,6 +277,7 @@ func NewOrganizationServiceClient(httpClient connect.HTTPClient, baseURL string, // organizationServiceClient implements OrganizationServiceClient. type organizationServiceClient struct { + adminSetPlan *connect.Client[v1.AdminSetPlanRequest, v1.AdminSetPlanResponse] listOrganizations *connect.Client[v1.ListOrganizationsRequest, v1.ListOrganizationsResponse] createOrganization *connect.Client[v1.CreateOrganizationRequest, v1.CreateOrganizationResponse] getOrganization *connect.Client[v1.GetOrganizationRequest, v1.GetOrganizationResponse] @@ -287,6 +299,11 @@ type organizationServiceClient struct { getMyPermissions *connect.Client[v1.GetMyPermissionsRequest, v1.GetMyPermissionsResponse] } +// AdminSetPlan calls obiente.cloud.organizations.v1.OrganizationService.AdminSetPlan. +func (c *organizationServiceClient) AdminSetPlan(ctx context.Context, req *connect.Request[v1.AdminSetPlanRequest]) (*connect.Response[v1.AdminSetPlanResponse], error) { + return c.adminSetPlan.CallUnary(ctx, req) +} + // ListOrganizations calls obiente.cloud.organizations.v1.OrganizationService.ListOrganizations. func (c *organizationServiceClient) ListOrganizations(ctx context.Context, req *connect.Request[v1.ListOrganizationsRequest]) (*connect.Response[v1.ListOrganizationsResponse], error) { return c.listOrganizations.CallUnary(ctx, req) @@ -385,6 +402,8 @@ func (c *organizationServiceClient) GetMyPermissions(ctx context.Context, req *c // OrganizationServiceHandler is an implementation of the // obiente.cloud.organizations.v1.OrganizationService service. type OrganizationServiceHandler interface { + // Admin: Set the active plan for an organization (superadmin only) + AdminSetPlan(context.Context, *connect.Request[v1.AdminSetPlanRequest]) (*connect.Response[v1.AdminSetPlanResponse], error) // List user's organizations ListOrganizations(context.Context, *connect.Request[v1.ListOrganizationsRequest]) (*connect.Response[v1.ListOrganizationsResponse], error) // Create new organization @@ -432,6 +451,12 @@ type OrganizationServiceHandler interface { // and JSON codecs. They also support gzip compression. func NewOrganizationServiceHandler(svc OrganizationServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) { organizationServiceMethods := v1.File_obiente_cloud_organizations_v1_organization_service_proto.Services().ByName("OrganizationService").Methods() + organizationServiceAdminSetPlanHandler := connect.NewUnaryHandler( + OrganizationServiceAdminSetPlanProcedure, + svc.AdminSetPlan, + connect.WithSchema(organizationServiceMethods.ByName("AdminSetPlan")), + connect.WithHandlerOptions(opts...), + ) organizationServiceListOrganizationsHandler := connect.NewUnaryHandler( OrganizationServiceListOrganizationsProcedure, svc.ListOrganizations, @@ -548,6 +573,8 @@ func NewOrganizationServiceHandler(svc OrganizationServiceHandler, opts ...conne ) return "/obiente.cloud.organizations.v1.OrganizationService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { + case OrganizationServiceAdminSetPlanProcedure: + organizationServiceAdminSetPlanHandler.ServeHTTP(w, r) case OrganizationServiceListOrganizationsProcedure: organizationServiceListOrganizationsHandler.ServeHTTP(w, r) case OrganizationServiceCreateOrganizationProcedure: @@ -595,6 +622,10 @@ func NewOrganizationServiceHandler(svc OrganizationServiceHandler, opts ...conne // UnimplementedOrganizationServiceHandler returns CodeUnimplemented from all methods. type UnimplementedOrganizationServiceHandler struct{} +func (UnimplementedOrganizationServiceHandler) AdminSetPlan(context.Context, *connect.Request[v1.AdminSetPlanRequest]) (*connect.Response[v1.AdminSetPlanResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("obiente.cloud.organizations.v1.OrganizationService.AdminSetPlan is not implemented")) +} + func (UnimplementedOrganizationServiceHandler) ListOrganizations(context.Context, *connect.Request[v1.ListOrganizationsRequest]) (*connect.Response[v1.ListOrganizationsResponse], error) { return nil, connect.NewError(connect.CodeUnimplemented, errors.New("obiente.cloud.organizations.v1.OrganizationService.ListOrganizations is not implemented")) } diff --git a/packages/proto/proto/obiente/cloud/organizations/v1/organization_service.proto b/packages/proto/proto/obiente/cloud/organizations/v1/organization_service.proto index 06507409..6e013484 100644 --- a/packages/proto/proto/obiente/cloud/organizations/v1/organization_service.proto +++ b/packages/proto/proto/obiente/cloud/organizations/v1/organization_service.proto @@ -2,46 +2,49 @@ syntax = "proto3"; package obiente.cloud.organizations.v1; -option go_package = "github.com/obiente/cloud/apps/shared/proto/obiente/cloud/organizations/v1;organizationsv1"; - import "google/protobuf/timestamp.proto"; import "obiente/cloud/auth/v1/auth_service.proto"; import "obiente/cloud/common/v1/common.proto"; +option go_package = "github.com/obiente/cloud/apps/shared/proto/obiente/cloud/organizations/v1;organizationsv1"; + service OrganizationService { + // Admin: Set the active plan for an organization (superadmin only) + rpc AdminSetPlan(AdminSetPlanRequest) returns (AdminSetPlanResponse); + // List user's organizations rpc ListOrganizations(ListOrganizationsRequest) returns (ListOrganizationsResponse); - + // Create new organization rpc CreateOrganization(CreateOrganizationRequest) returns (CreateOrganizationResponse); - + // Get organization details rpc GetOrganization(GetOrganizationRequest) returns (GetOrganizationResponse); - + // Update organization rpc UpdateOrganization(UpdateOrganizationRequest) returns (UpdateOrganizationResponse); - + // List organization members rpc ListMembers(ListMembersRequest) returns (ListMembersResponse); - + // Invite user to organization rpc InviteMember(InviteMemberRequest) returns (InviteMemberResponse); - + // Resend invitation email to a pending member rpc ResendInvite(ResendInviteRequest) returns (ResendInviteResponse); - + // List invites sent to the current user rpc ListMyInvites(ListMyInvitesRequest) returns (ListMyInvitesResponse); - + // Accept an invitation to join an organization rpc AcceptInvite(AcceptInviteRequest) returns (AcceptInviteResponse); - + // Decline an invitation to join an organization rpc DeclineInvite(DeclineInviteRequest) returns (DeclineInviteResponse); - + // Update member role/permissions rpc UpdateMember(UpdateMemberRequest) returns (UpdateMemberResponse); - + // Remove member from organization rpc RemoveMember(RemoveMemberRequest) returns (RemoveMemberResponse); @@ -297,7 +300,6 @@ message OrganizationMember { google.protobuf.Timestamp joined_at = 5; } - message AddCreditsRequest { string organization_id = 1; // Amount in cents ($0.01 units). Must be positive. @@ -382,4 +384,16 @@ message GetMyPermissionsRequest { message GetMyPermissionsResponse { repeated string permissions = 1; -} \ No newline at end of file +} + +// Request to set the active plan for an organization (superadmin only) +message AdminSetPlanRequest { + string organization_id = 1; + string plan_id = 2; // The plan to assign (must match OrganizationPlan.ID) +} + +// Response for AdminSetPlan +message AdminSetPlanResponse { + Organization organization = 1; + string plan_id = 2; +} diff --git a/packages/proto/src/generated/obiente/cloud/deployments/v1/deployment_service_pb.ts b/packages/proto/src/generated/obiente/cloud/deployments/v1/deployment_service_pb.ts index e3262c86..2f587cdb 100644 --- a/packages/proto/src/generated/obiente/cloud/deployments/v1/deployment_service_pb.ts +++ b/packages/proto/src/generated/obiente/cloud/deployments/v1/deployment_service_pb.ts @@ -4622,7 +4622,7 @@ export const BuildStatusSchema: GenEnum = /*@__PURE__*/ */ export enum HealthCheckType { /** - * No health check + * Auto-detect (TCP if routing exists, otherwise no healthcheck) * * @generated from enum value: HEALTHCHECK_TYPE_UNSPECIFIED = 0; */ diff --git a/packages/proto/src/generated/obiente/cloud/organizations/v1/organization_service_pb.ts b/packages/proto/src/generated/obiente/cloud/organizations/v1/organization_service_pb.ts index 0af4b1ba..078a9321 100644 --- a/packages/proto/src/generated/obiente/cloud/organizations/v1/organization_service_pb.ts +++ b/packages/proto/src/generated/obiente/cloud/organizations/v1/organization_service_pb.ts @@ -16,7 +16,7 @@ import type { Message } from "@bufbuild/protobuf"; * Describes the file obiente/cloud/organizations/v1/organization_service.proto. */ export const file_obiente_cloud_organizations_v1_organization_service: GenFile = /*@__PURE__*/ - fileDesc("CjlvYmllbnRlL2Nsb3VkL29yZ2FuaXphdGlvbnMvdjEvb3JnYW5pemF0aW9uX3NlcnZpY2UucHJvdG8SHm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MSJICg9HZXRVc2FnZVJlcXVlc3QSFwoPb3JnYW5pemF0aW9uX2lkGAEgASgJEhIKBW1vbnRoGAIgASgJSACIAQFCCAoGX21vbnRoIv0BChBHZXRVc2FnZVJlc3BvbnNlEhcKD29yZ2FuaXphdGlvbl9pZBgBIAEoCRINCgVtb250aBgCIAEoCRI9CgdjdXJyZW50GAMgASgLMiwub2JpZW50ZS5jbG91ZC5vcmdhbml6YXRpb25zLnYxLlVzYWdlTWV0cmljcxJHChFlc3RpbWF0ZWRfbW9udGhseRgEIAEoCzIsLm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5Vc2FnZU1ldHJpY3MSOQoFcXVvdGEYBSABKAsyKi5vYmllbnRlLmNsb3VkLm9yZ2FuaXphdGlvbnMudjEuVXNhZ2VRdW90YSLpAwoMVXNhZ2VNZXRyaWNzEhgKEGNwdV9jb3JlX3NlY29uZHMYASABKAMSGwoTbWVtb3J5X2J5dGVfc2Vjb25kcxgCIAEoAxIaChJiYW5kd2lkdGhfcnhfYnl0ZXMYAyABKAMSGgoSYmFuZHdpZHRoX3R4X2J5dGVzGAQgASgDEhUKDXN0b3JhZ2VfYnl0ZXMYBSABKAMSHwoXZGVwbG95bWVudHNfYWN0aXZlX3BlYWsYBiABKAUSHAoUZXN0aW1hdGVkX2Nvc3RfY2VudHMYByABKAMSGwoOY3B1X2Nvc3RfY2VudHMYCCABKANIAIgBARIeChFtZW1vcnlfY29zdF9jZW50cxgJIAEoA0gBiAEBEiEKFGJhbmR3aWR0aF9jb3N0X2NlbnRzGAogASgDSAKIAQESHwoSc3RvcmFnZV9jb3N0X2NlbnRzGAsgASgDSAOIAQESIQoUcHVibGljX2lwX2Nvc3RfY2VudHMYDCABKANIBIgBAUIRCg9fY3B1X2Nvc3RfY2VudHNCFAoSX21lbW9yeV9jb3N0X2NlbnRzQhcKFV9iYW5kd2lkdGhfY29zdF9jZW50c0IVChNfc3RvcmFnZV9jb3N0X2NlbnRzQhcKFV9wdWJsaWNfaXBfY29zdF9jZW50cyKkAQoKVXNhZ2VRdW90YRIgChhjcHVfY29yZV9zZWNvbmRzX21vbnRobHkYASABKAMSIwobbWVtb3J5X2J5dGVfc2Vjb25kc19tb250aGx5GAIgASgDEh8KF2JhbmR3aWR0aF9ieXRlc19tb250aGx5GAMgASgDEhUKDXN0b3JhZ2VfYnl0ZXMYBCABKAMSFwoPZGVwbG95bWVudHNfbWF4GAUgASgFImAKGExpc3RPcmdhbml6YXRpb25zUmVxdWVzdBIMCgRwYWdlGAEgASgFEhAKCHBlcl9wYWdlGAIgASgFEhYKCW9ubHlfbWluZRgDIAEoCEgAiAEBQgwKCl9vbmx5X21pbmUimQEKGUxpc3RPcmdhbml6YXRpb25zUmVzcG9uc2USQwoNb3JnYW5pemF0aW9ucxgBIAMoCzIsLm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5Pcmdhbml6YXRpb24SNwoKcGFnaW5hdGlvbhgCIAEoCzIjLm9iaWVudGUuY2xvdWQuY29tbW9uLnYxLlBhZ2luYXRpb24iRQoZQ3JlYXRlT3JnYW5pemF0aW9uUmVxdWVzdBIMCgRuYW1lGAEgASgJEgwKBHNsdWcYAiABKAkSDAoEcGxhbhgDIAEoCSJgChpDcmVhdGVPcmdhbml6YXRpb25SZXNwb25zZRJCCgxvcmdhbml6YXRpb24YASABKAsyLC5vYmllbnRlLmNsb3VkLm9yZ2FuaXphdGlvbnMudjEuT3JnYW5pemF0aW9uIjEKFkdldE9yZ2FuaXphdGlvblJlcXVlc3QSFwoPb3JnYW5pemF0aW9uX2lkGAEgASgJIl0KF0dldE9yZ2FuaXphdGlvblJlc3BvbnNlEkIKDG9yZ2FuaXphdGlvbhgBIAEoCzIsLm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5Pcmdhbml6YXRpb24icAoZVXBkYXRlT3JnYW5pemF0aW9uUmVxdWVzdBIXCg9vcmdhbml6YXRpb25faWQYASABKAkSEQoEbmFtZRgCIAEoCUgAiAEBEhMKBmRvbWFpbhgDIAEoCUgBiAEBQgcKBV9uYW1lQgkKB19kb21haW4iYAoaVXBkYXRlT3JnYW5pemF0aW9uUmVzcG9uc2USQgoMb3JnYW5pemF0aW9uGAEgASgLMiwub2JpZW50ZS5jbG91ZC5vcmdhbml6YXRpb25zLnYxLk9yZ2FuaXphdGlvbiJNChJMaXN0TWVtYmVyc1JlcXVlc3QSFwoPb3JnYW5pemF0aW9uX2lkGAEgASgJEgwKBHBhZ2UYAiABKAUSEAoIcGVyX3BhZ2UYAyABKAUikwEKE0xpc3RNZW1iZXJzUmVzcG9uc2USQwoHbWVtYmVycxgBIAMoCzIyLm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5Pcmdhbml6YXRpb25NZW1iZXISNwoKcGFnaW5hdGlvbhgCIAEoCzIjLm9iaWVudGUuY2xvdWQuY29tbW9uLnYxLlBhZ2luYXRpb24iSwoTSW52aXRlTWVtYmVyUmVxdWVzdBIXCg9vcmdhbml6YXRpb25faWQYASABKAkSDQoFZW1haWwYAiABKAkSDAoEcm9sZRgDIAEoCSJaChRJbnZpdGVNZW1iZXJSZXNwb25zZRJCCgZtZW1iZXIYASABKAsyMi5vYmllbnRlLmNsb3VkLm9yZ2FuaXphdGlvbnMudjEuT3JnYW5pemF0aW9uTWVtYmVyIkEKE1Jlc2VuZEludml0ZVJlcXVlc3QSFwoPb3JnYW5pemF0aW9uX2lkGAEgASgJEhEKCW1lbWJlcl9pZBgCIAEoCSInChRSZXNlbmRJbnZpdGVSZXNwb25zZRIPCgdzdWNjZXNzGAEgASgIIjYKFExpc3RNeUludml0ZXNSZXF1ZXN0EgwKBHBhZ2UYASABKAUSEAoIcGVyX3BhZ2UYAiABKAUikAEKFUxpc3RNeUludml0ZXNSZXNwb25zZRI+CgdpbnZpdGVzGAEgAygLMi0ub2JpZW50ZS5jbG91ZC5vcmdhbml6YXRpb25zLnYxLlBlbmRpbmdJbnZpdGUSNwoKcGFnaW5hdGlvbhgCIAEoCzIjLm9iaWVudGUuY2xvdWQuY29tbW9uLnYxLlBhZ2luYXRpb24ipAEKDVBlbmRpbmdJbnZpdGUSCgoCaWQYASABKAkSFwoPb3JnYW5pemF0aW9uX2lkGAIgASgJEhkKEW9yZ2FuaXphdGlvbl9uYW1lGAMgASgJEgwKBHJvbGUYBCABKAkSLgoKaW52aXRlZF9hdBgFIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASFQoNaW52aXRlcl9lbWFpbBgGIAEoCSJBChNBY2NlcHRJbnZpdGVSZXF1ZXN0EhcKD29yZ2FuaXphdGlvbl9pZBgBIAEoCRIRCgltZW1iZXJfaWQYAiABKAkingEKFEFjY2VwdEludml0ZVJlc3BvbnNlEkIKBm1lbWJlchgBIAEoCzIyLm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5Pcmdhbml6YXRpb25NZW1iZXISQgoMb3JnYW5pemF0aW9uGAIgASgLMiwub2JpZW50ZS5jbG91ZC5vcmdhbml6YXRpb25zLnYxLk9yZ2FuaXphdGlvbiJCChREZWNsaW5lSW52aXRlUmVxdWVzdBIXCg9vcmdhbml6YXRpb25faWQYASABKAkSEQoJbWVtYmVyX2lkGAIgASgJIigKFURlY2xpbmVJbnZpdGVSZXNwb25zZRIPCgdzdWNjZXNzGAEgASgIIl0KE1VwZGF0ZU1lbWJlclJlcXVlc3QSFwoPb3JnYW5pemF0aW9uX2lkGAEgASgJEhEKCW1lbWJlcl9pZBgCIAEoCRIRCgRyb2xlGAMgASgJSACIAQFCBwoFX3JvbGUiWgoUVXBkYXRlTWVtYmVyUmVzcG9uc2USQgoGbWVtYmVyGAEgASgLMjIub2JpZW50ZS5jbG91ZC5vcmdhbml6YXRpb25zLnYxLk9yZ2FuaXphdGlvbk1lbWJlciJBChNSZW1vdmVNZW1iZXJSZXF1ZXN0EhcKD29yZ2FuaXphdGlvbl9pZBgBIAEoCRIRCgltZW1iZXJfaWQYAiABKAkiJwoUUmVtb3ZlTWVtYmVyUmVzcG9uc2USDwoHc3VjY2VzcxgBIAEoCCJnChhUcmFuc2Zlck93bmVyc2hpcFJlcXVlc3QSFwoPb3JnYW5pemF0aW9uX2lkGAEgASgJEhsKE25ld19vd25lcl9tZW1iZXJfaWQYAiABKAkSFQoNZmFsbGJhY2tfcm9sZRgDIAEoCSKCAQoZVHJhbnNmZXJPd25lcnNoaXBSZXNwb25zZRIPCgdzdWNjZXNzGAEgASgIEiAKGHByZXZpb3VzX293bmVyX21lbWJlcl9pZBgCIAEoCRIbChNuZXdfb3duZXJfbWVtYmVyX2lkGAMgASgJEhUKDWZhbGxiYWNrX3JvbGUYBCABKAki7QIKDE9yZ2FuaXphdGlvbhIKCgJpZBgBIAEoCRIMCgRuYW1lGAIgASgJEgwKBHNsdWcYAyABKAkSEwoGZG9tYWluGAQgASgJSACIAQESDAoEcGxhbhgFIAEoCRIOCgZzdGF0dXMYBiABKAkSLgoKY3JlYXRlZF9hdBgHIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASFwoPbWF4X2RlcGxveW1lbnRzGAggASgFEhkKEW1heF92cHNfaW5zdGFuY2VzGAkgASgFEhgKEG1heF90ZWFtX21lbWJlcnMYCiABKAUSDwoHY3JlZGl0cxgLIAEoAxJACglwbGFuX2luZm8YDCABKAsyKC5vYmllbnRlLmNsb3VkLm9yZ2FuaXphdGlvbnMudjEuUGxhbkluZm9IAYgBARIYChB0b3RhbF9wYWlkX2NlbnRzGA0gASgDQgkKB19kb21haW5CDAoKX3BsYW5faW5mbyKtAgoIUGxhbkluZm8SDwoHcGxhbl9pZBgBIAEoCRIRCglwbGFuX25hbWUYAiABKAkSEwoLZGVzY3JpcHRpb24YAyABKAkSEQoJY3B1X2NvcmVzGAQgASgFEhQKDG1lbW9yeV9ieXRlcxgFIAEoAxIXCg9kZXBsb3ltZW50c19tYXgYBiABKAUSGQoRbWF4X3Zwc19pbnN0YW5jZXMYCyABKAUSHQoVYmFuZHdpZHRoX2J5dGVzX21vbnRoGAcgASgDEhUKDXN0b3JhZ2VfYnl0ZXMYCCABKAMSHQoVbWluaW11bV9wYXltZW50X2NlbnRzGAkgASgDEiIKGm1vbnRobHlfZnJlZV9jcmVkaXRzX2NlbnRzGAogASgDEhIKCnRyaWFsX2RheXMYDCABKAUimAEKEk9yZ2FuaXphdGlvbk1lbWJlchIKCgJpZBgBIAEoCRIpCgR1c2VyGAIgASgLMhsub2JpZW50ZS5jbG91ZC5hdXRoLnYxLlVzZXISDAoEcm9sZRgDIAEoCRIOCgZzdGF0dXMYBCABKAkSLQoJam9pbmVkX2F0GAUgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcCJeChFBZGRDcmVkaXRzUmVxdWVzdBIXCg9vcmdhbml6YXRpb25faWQYASABKAkSFAoMYW1vdW50X2NlbnRzGAIgASgDEhEKBG5vdGUYAyABKAlIAIgBAUIHCgVfbm90ZSKPAQoSQWRkQ3JlZGl0c1Jlc3BvbnNlEkIKDG9yZ2FuaXphdGlvbhgBIAEoCzIsLm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5Pcmdhbml6YXRpb24SGQoRbmV3X2JhbGFuY2VfY2VudHMYAiABKAMSGgoSYW1vdW50X2FkZGVkX2NlbnRzGAMgASgDImMKFkFkbWluQWRkQ3JlZGl0c1JlcXVlc3QSFwoPb3JnYW5pemF0aW9uX2lkGAEgASgJEhQKDGFtb3VudF9jZW50cxgCIAEoAxIRCgRub3RlGAMgASgJSACIAQFCBwoFX25vdGUilAEKF0FkbWluQWRkQ3JlZGl0c1Jlc3BvbnNlEkIKDG9yZ2FuaXphdGlvbhgBIAEoCzIsLm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5Pcmdhbml6YXRpb24SGQoRbmV3X2JhbGFuY2VfY2VudHMYAiABKAMSGgoSYW1vdW50X2FkZGVkX2NlbnRzGAMgASgDImYKGUFkbWluUmVtb3ZlQ3JlZGl0c1JlcXVlc3QSFwoPb3JnYW5pemF0aW9uX2lkGAEgASgJEhQKDGFtb3VudF9jZW50cxgCIAEoAxIRCgRub3RlGAMgASgJSACIAQFCBwoFX25vdGUimQEKGkFkbWluUmVtb3ZlQ3JlZGl0c1Jlc3BvbnNlEkIKDG9yZ2FuaXphdGlvbhgBIAEoCzIsLm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5Pcmdhbml6YXRpb24SGQoRbmV3X2JhbGFuY2VfY2VudHMYAiABKAMSHAoUYW1vdW50X3JlbW92ZWRfY2VudHMYAyABKAMiTgoTR2V0Q3JlZGl0TG9nUmVxdWVzdBIXCg9vcmdhbml6YXRpb25faWQYASABKAkSDAoEcGFnZRgCIAEoBRIQCghwZXJfcGFnZRgDIAEoBSKYAQoUR2V0Q3JlZGl0TG9nUmVzcG9uc2USRwoMdHJhbnNhY3Rpb25zGAEgAygLMjEub2JpZW50ZS5jbG91ZC5vcmdhbml6YXRpb25zLnYxLkNyZWRpdFRyYW5zYWN0aW9uEjcKCnBhZ2luYXRpb24YAiABKAsyIy5vYmllbnRlLmNsb3VkLmNvbW1vbi52MS5QYWdpbmF0aW9uIvcBChFDcmVkaXRUcmFuc2FjdGlvbhIKCgJpZBgBIAEoCRIXCg9vcmdhbml6YXRpb25faWQYAiABKAkSFAoMYW1vdW50X2NlbnRzGAMgASgDEhUKDWJhbGFuY2VfYWZ0ZXIYBCABKAMSDAoEdHlwZRgFIAEoCRIOCgZzb3VyY2UYBiABKAkSEQoEbm90ZRgHIAEoCUgAiAEBEhcKCmNyZWF0ZWRfYnkYCCABKAlIAYgBARIuCgpjcmVhdGVkX2F0GAkgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcEIHCgVfbm90ZUINCgtfY3JlYXRlZF9ieSIyChdHZXRNeVBlcm1pc3Npb25zUmVxdWVzdBIXCg9vcmdhbml6YXRpb25faWQYASABKAkiLwoYR2V0TXlQZXJtaXNzaW9uc1Jlc3BvbnNlEhMKC3Blcm1pc3Npb25zGAEgAygJMqETChNPcmdhbml6YXRpb25TZXJ2aWNlEogBChFMaXN0T3JnYW5pemF0aW9ucxI4Lm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5MaXN0T3JnYW5pemF0aW9uc1JlcXVlc3QaOS5vYmllbnRlLmNsb3VkLm9yZ2FuaXphdGlvbnMudjEuTGlzdE9yZ2FuaXphdGlvbnNSZXNwb25zZRKLAQoSQ3JlYXRlT3JnYW5pemF0aW9uEjkub2JpZW50ZS5jbG91ZC5vcmdhbml6YXRpb25zLnYxLkNyZWF0ZU9yZ2FuaXphdGlvblJlcXVlc3QaOi5vYmllbnRlLmNsb3VkLm9yZ2FuaXphdGlvbnMudjEuQ3JlYXRlT3JnYW5pemF0aW9uUmVzcG9uc2USggEKD0dldE9yZ2FuaXphdGlvbhI2Lm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5HZXRPcmdhbml6YXRpb25SZXF1ZXN0Gjcub2JpZW50ZS5jbG91ZC5vcmdhbml6YXRpb25zLnYxLkdldE9yZ2FuaXphdGlvblJlc3BvbnNlEosBChJVcGRhdGVPcmdhbml6YXRpb24SOS5vYmllbnRlLmNsb3VkLm9yZ2FuaXphdGlvbnMudjEuVXBkYXRlT3JnYW5pemF0aW9uUmVxdWVzdBo6Lm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5VcGRhdGVPcmdhbml6YXRpb25SZXNwb25zZRJ2CgtMaXN0TWVtYmVycxIyLm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5MaXN0TWVtYmVyc1JlcXVlc3QaMy5vYmllbnRlLmNsb3VkLm9yZ2FuaXphdGlvbnMudjEuTGlzdE1lbWJlcnNSZXNwb25zZRJ5CgxJbnZpdGVNZW1iZXISMy5vYmllbnRlLmNsb3VkLm9yZ2FuaXphdGlvbnMudjEuSW52aXRlTWVtYmVyUmVxdWVzdBo0Lm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5JbnZpdGVNZW1iZXJSZXNwb25zZRJ5CgxSZXNlbmRJbnZpdGUSMy5vYmllbnRlLmNsb3VkLm9yZ2FuaXphdGlvbnMudjEuUmVzZW5kSW52aXRlUmVxdWVzdBo0Lm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5SZXNlbmRJbnZpdGVSZXNwb25zZRJ8Cg1MaXN0TXlJbnZpdGVzEjQub2JpZW50ZS5jbG91ZC5vcmdhbml6YXRpb25zLnYxLkxpc3RNeUludml0ZXNSZXF1ZXN0GjUub2JpZW50ZS5jbG91ZC5vcmdhbml6YXRpb25zLnYxLkxpc3RNeUludml0ZXNSZXNwb25zZRJ5CgxBY2NlcHRJbnZpdGUSMy5vYmllbnRlLmNsb3VkLm9yZ2FuaXphdGlvbnMudjEuQWNjZXB0SW52aXRlUmVxdWVzdBo0Lm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5BY2NlcHRJbnZpdGVSZXNwb25zZRJ8Cg1EZWNsaW5lSW52aXRlEjQub2JpZW50ZS5jbG91ZC5vcmdhbml6YXRpb25zLnYxLkRlY2xpbmVJbnZpdGVSZXF1ZXN0GjUub2JpZW50ZS5jbG91ZC5vcmdhbml6YXRpb25zLnYxLkRlY2xpbmVJbnZpdGVSZXNwb25zZRJ5CgxVcGRhdGVNZW1iZXISMy5vYmllbnRlLmNsb3VkLm9yZ2FuaXphdGlvbnMudjEuVXBkYXRlTWVtYmVyUmVxdWVzdBo0Lm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5VcGRhdGVNZW1iZXJSZXNwb25zZRJ5CgxSZW1vdmVNZW1iZXISMy5vYmllbnRlLmNsb3VkLm9yZ2FuaXphdGlvbnMudjEuUmVtb3ZlTWVtYmVyUmVxdWVzdBo0Lm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5SZW1vdmVNZW1iZXJSZXNwb25zZRKIAQoRVHJhbnNmZXJPd25lcnNoaXASOC5vYmllbnRlLmNsb3VkLm9yZ2FuaXphdGlvbnMudjEuVHJhbnNmZXJPd25lcnNoaXBSZXF1ZXN0Gjkub2JpZW50ZS5jbG91ZC5vcmdhbml6YXRpb25zLnYxLlRyYW5zZmVyT3duZXJzaGlwUmVzcG9uc2USbQoIR2V0VXNhZ2USLy5vYmllbnRlLmNsb3VkLm9yZ2FuaXphdGlvbnMudjEuR2V0VXNhZ2VSZXF1ZXN0GjAub2JpZW50ZS5jbG91ZC5vcmdhbml6YXRpb25zLnYxLkdldFVzYWdlUmVzcG9uc2UScwoKQWRkQ3JlZGl0cxIxLm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5BZGRDcmVkaXRzUmVxdWVzdBoyLm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5BZGRDcmVkaXRzUmVzcG9uc2USggEKD0FkbWluQWRkQ3JlZGl0cxI2Lm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5BZG1pbkFkZENyZWRpdHNSZXF1ZXN0Gjcub2JpZW50ZS5jbG91ZC5vcmdhbml6YXRpb25zLnYxLkFkbWluQWRkQ3JlZGl0c1Jlc3BvbnNlEosBChJBZG1pblJlbW92ZUNyZWRpdHMSOS5vYmllbnRlLmNsb3VkLm9yZ2FuaXphdGlvbnMudjEuQWRtaW5SZW1vdmVDcmVkaXRzUmVxdWVzdBo6Lm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5BZG1pblJlbW92ZUNyZWRpdHNSZXNwb25zZRJ5CgxHZXRDcmVkaXRMb2cSMy5vYmllbnRlLmNsb3VkLm9yZ2FuaXphdGlvbnMudjEuR2V0Q3JlZGl0TG9nUmVxdWVzdBo0Lm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5HZXRDcmVkaXRMb2dSZXNwb25zZRKFAQoQR2V0TXlQZXJtaXNzaW9ucxI3Lm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5HZXRNeVBlcm1pc3Npb25zUmVxdWVzdBo4Lm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5HZXRNeVBlcm1pc3Npb25zUmVzcG9uc2VCW1pZZ2l0aHViLmNvbS9vYmllbnRlL2Nsb3VkL2FwcHMvc2hhcmVkL3Byb3RvL29iaWVudGUvY2xvdWQvb3JnYW5pemF0aW9ucy92MTtvcmdhbml6YXRpb25zdjFiBnByb3RvMw", [file_google_protobuf_timestamp, file_obiente_cloud_auth_v1_auth_service, file_obiente_cloud_common_v1_common]); + fileDesc("CjlvYmllbnRlL2Nsb3VkL29yZ2FuaXphdGlvbnMvdjEvb3JnYW5pemF0aW9uX3NlcnZpY2UucHJvdG8SHm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MSJICg9HZXRVc2FnZVJlcXVlc3QSFwoPb3JnYW5pemF0aW9uX2lkGAEgASgJEhIKBW1vbnRoGAIgASgJSACIAQFCCAoGX21vbnRoIv0BChBHZXRVc2FnZVJlc3BvbnNlEhcKD29yZ2FuaXphdGlvbl9pZBgBIAEoCRINCgVtb250aBgCIAEoCRI9CgdjdXJyZW50GAMgASgLMiwub2JpZW50ZS5jbG91ZC5vcmdhbml6YXRpb25zLnYxLlVzYWdlTWV0cmljcxJHChFlc3RpbWF0ZWRfbW9udGhseRgEIAEoCzIsLm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5Vc2FnZU1ldHJpY3MSOQoFcXVvdGEYBSABKAsyKi5vYmllbnRlLmNsb3VkLm9yZ2FuaXphdGlvbnMudjEuVXNhZ2VRdW90YSLpAwoMVXNhZ2VNZXRyaWNzEhgKEGNwdV9jb3JlX3NlY29uZHMYASABKAMSGwoTbWVtb3J5X2J5dGVfc2Vjb25kcxgCIAEoAxIaChJiYW5kd2lkdGhfcnhfYnl0ZXMYAyABKAMSGgoSYmFuZHdpZHRoX3R4X2J5dGVzGAQgASgDEhUKDXN0b3JhZ2VfYnl0ZXMYBSABKAMSHwoXZGVwbG95bWVudHNfYWN0aXZlX3BlYWsYBiABKAUSHAoUZXN0aW1hdGVkX2Nvc3RfY2VudHMYByABKAMSGwoOY3B1X2Nvc3RfY2VudHMYCCABKANIAIgBARIeChFtZW1vcnlfY29zdF9jZW50cxgJIAEoA0gBiAEBEiEKFGJhbmR3aWR0aF9jb3N0X2NlbnRzGAogASgDSAKIAQESHwoSc3RvcmFnZV9jb3N0X2NlbnRzGAsgASgDSAOIAQESIQoUcHVibGljX2lwX2Nvc3RfY2VudHMYDCABKANIBIgBAUIRCg9fY3B1X2Nvc3RfY2VudHNCFAoSX21lbW9yeV9jb3N0X2NlbnRzQhcKFV9iYW5kd2lkdGhfY29zdF9jZW50c0IVChNfc3RvcmFnZV9jb3N0X2NlbnRzQhcKFV9wdWJsaWNfaXBfY29zdF9jZW50cyKkAQoKVXNhZ2VRdW90YRIgChhjcHVfY29yZV9zZWNvbmRzX21vbnRobHkYASABKAMSIwobbWVtb3J5X2J5dGVfc2Vjb25kc19tb250aGx5GAIgASgDEh8KF2JhbmR3aWR0aF9ieXRlc19tb250aGx5GAMgASgDEhUKDXN0b3JhZ2VfYnl0ZXMYBCABKAMSFwoPZGVwbG95bWVudHNfbWF4GAUgASgFImAKGExpc3RPcmdhbml6YXRpb25zUmVxdWVzdBIMCgRwYWdlGAEgASgFEhAKCHBlcl9wYWdlGAIgASgFEhYKCW9ubHlfbWluZRgDIAEoCEgAiAEBQgwKCl9vbmx5X21pbmUimQEKGUxpc3RPcmdhbml6YXRpb25zUmVzcG9uc2USQwoNb3JnYW5pemF0aW9ucxgBIAMoCzIsLm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5Pcmdhbml6YXRpb24SNwoKcGFnaW5hdGlvbhgCIAEoCzIjLm9iaWVudGUuY2xvdWQuY29tbW9uLnYxLlBhZ2luYXRpb24iRQoZQ3JlYXRlT3JnYW5pemF0aW9uUmVxdWVzdBIMCgRuYW1lGAEgASgJEgwKBHNsdWcYAiABKAkSDAoEcGxhbhgDIAEoCSJgChpDcmVhdGVPcmdhbml6YXRpb25SZXNwb25zZRJCCgxvcmdhbml6YXRpb24YASABKAsyLC5vYmllbnRlLmNsb3VkLm9yZ2FuaXphdGlvbnMudjEuT3JnYW5pemF0aW9uIjEKFkdldE9yZ2FuaXphdGlvblJlcXVlc3QSFwoPb3JnYW5pemF0aW9uX2lkGAEgASgJIl0KF0dldE9yZ2FuaXphdGlvblJlc3BvbnNlEkIKDG9yZ2FuaXphdGlvbhgBIAEoCzIsLm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5Pcmdhbml6YXRpb24icAoZVXBkYXRlT3JnYW5pemF0aW9uUmVxdWVzdBIXCg9vcmdhbml6YXRpb25faWQYASABKAkSEQoEbmFtZRgCIAEoCUgAiAEBEhMKBmRvbWFpbhgDIAEoCUgBiAEBQgcKBV9uYW1lQgkKB19kb21haW4iYAoaVXBkYXRlT3JnYW5pemF0aW9uUmVzcG9uc2USQgoMb3JnYW5pemF0aW9uGAEgASgLMiwub2JpZW50ZS5jbG91ZC5vcmdhbml6YXRpb25zLnYxLk9yZ2FuaXphdGlvbiJNChJMaXN0TWVtYmVyc1JlcXVlc3QSFwoPb3JnYW5pemF0aW9uX2lkGAEgASgJEgwKBHBhZ2UYAiABKAUSEAoIcGVyX3BhZ2UYAyABKAUikwEKE0xpc3RNZW1iZXJzUmVzcG9uc2USQwoHbWVtYmVycxgBIAMoCzIyLm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5Pcmdhbml6YXRpb25NZW1iZXISNwoKcGFnaW5hdGlvbhgCIAEoCzIjLm9iaWVudGUuY2xvdWQuY29tbW9uLnYxLlBhZ2luYXRpb24iSwoTSW52aXRlTWVtYmVyUmVxdWVzdBIXCg9vcmdhbml6YXRpb25faWQYASABKAkSDQoFZW1haWwYAiABKAkSDAoEcm9sZRgDIAEoCSJaChRJbnZpdGVNZW1iZXJSZXNwb25zZRJCCgZtZW1iZXIYASABKAsyMi5vYmllbnRlLmNsb3VkLm9yZ2FuaXphdGlvbnMudjEuT3JnYW5pemF0aW9uTWVtYmVyIkEKE1Jlc2VuZEludml0ZVJlcXVlc3QSFwoPb3JnYW5pemF0aW9uX2lkGAEgASgJEhEKCW1lbWJlcl9pZBgCIAEoCSInChRSZXNlbmRJbnZpdGVSZXNwb25zZRIPCgdzdWNjZXNzGAEgASgIIjYKFExpc3RNeUludml0ZXNSZXF1ZXN0EgwKBHBhZ2UYASABKAUSEAoIcGVyX3BhZ2UYAiABKAUikAEKFUxpc3RNeUludml0ZXNSZXNwb25zZRI+CgdpbnZpdGVzGAEgAygLMi0ub2JpZW50ZS5jbG91ZC5vcmdhbml6YXRpb25zLnYxLlBlbmRpbmdJbnZpdGUSNwoKcGFnaW5hdGlvbhgCIAEoCzIjLm9iaWVudGUuY2xvdWQuY29tbW9uLnYxLlBhZ2luYXRpb24ipAEKDVBlbmRpbmdJbnZpdGUSCgoCaWQYASABKAkSFwoPb3JnYW5pemF0aW9uX2lkGAIgASgJEhkKEW9yZ2FuaXphdGlvbl9uYW1lGAMgASgJEgwKBHJvbGUYBCABKAkSLgoKaW52aXRlZF9hdBgFIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASFQoNaW52aXRlcl9lbWFpbBgGIAEoCSJBChNBY2NlcHRJbnZpdGVSZXF1ZXN0EhcKD29yZ2FuaXphdGlvbl9pZBgBIAEoCRIRCgltZW1iZXJfaWQYAiABKAkingEKFEFjY2VwdEludml0ZVJlc3BvbnNlEkIKBm1lbWJlchgBIAEoCzIyLm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5Pcmdhbml6YXRpb25NZW1iZXISQgoMb3JnYW5pemF0aW9uGAIgASgLMiwub2JpZW50ZS5jbG91ZC5vcmdhbml6YXRpb25zLnYxLk9yZ2FuaXphdGlvbiJCChREZWNsaW5lSW52aXRlUmVxdWVzdBIXCg9vcmdhbml6YXRpb25faWQYASABKAkSEQoJbWVtYmVyX2lkGAIgASgJIigKFURlY2xpbmVJbnZpdGVSZXNwb25zZRIPCgdzdWNjZXNzGAEgASgIIl0KE1VwZGF0ZU1lbWJlclJlcXVlc3QSFwoPb3JnYW5pemF0aW9uX2lkGAEgASgJEhEKCW1lbWJlcl9pZBgCIAEoCRIRCgRyb2xlGAMgASgJSACIAQFCBwoFX3JvbGUiWgoUVXBkYXRlTWVtYmVyUmVzcG9uc2USQgoGbWVtYmVyGAEgASgLMjIub2JpZW50ZS5jbG91ZC5vcmdhbml6YXRpb25zLnYxLk9yZ2FuaXphdGlvbk1lbWJlciJBChNSZW1vdmVNZW1iZXJSZXF1ZXN0EhcKD29yZ2FuaXphdGlvbl9pZBgBIAEoCRIRCgltZW1iZXJfaWQYAiABKAkiJwoUUmVtb3ZlTWVtYmVyUmVzcG9uc2USDwoHc3VjY2VzcxgBIAEoCCJnChhUcmFuc2Zlck93bmVyc2hpcFJlcXVlc3QSFwoPb3JnYW5pemF0aW9uX2lkGAEgASgJEhsKE25ld19vd25lcl9tZW1iZXJfaWQYAiABKAkSFQoNZmFsbGJhY2tfcm9sZRgDIAEoCSKCAQoZVHJhbnNmZXJPd25lcnNoaXBSZXNwb25zZRIPCgdzdWNjZXNzGAEgASgIEiAKGHByZXZpb3VzX293bmVyX21lbWJlcl9pZBgCIAEoCRIbChNuZXdfb3duZXJfbWVtYmVyX2lkGAMgASgJEhUKDWZhbGxiYWNrX3JvbGUYBCABKAki7QIKDE9yZ2FuaXphdGlvbhIKCgJpZBgBIAEoCRIMCgRuYW1lGAIgASgJEgwKBHNsdWcYAyABKAkSEwoGZG9tYWluGAQgASgJSACIAQESDAoEcGxhbhgFIAEoCRIOCgZzdGF0dXMYBiABKAkSLgoKY3JlYXRlZF9hdBgHIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASFwoPbWF4X2RlcGxveW1lbnRzGAggASgFEhkKEW1heF92cHNfaW5zdGFuY2VzGAkgASgFEhgKEG1heF90ZWFtX21lbWJlcnMYCiABKAUSDwoHY3JlZGl0cxgLIAEoAxJACglwbGFuX2luZm8YDCABKAsyKC5vYmllbnRlLmNsb3VkLm9yZ2FuaXphdGlvbnMudjEuUGxhbkluZm9IAYgBARIYChB0b3RhbF9wYWlkX2NlbnRzGA0gASgDQgkKB19kb21haW5CDAoKX3BsYW5faW5mbyKtAgoIUGxhbkluZm8SDwoHcGxhbl9pZBgBIAEoCRIRCglwbGFuX25hbWUYAiABKAkSEwoLZGVzY3JpcHRpb24YAyABKAkSEQoJY3B1X2NvcmVzGAQgASgFEhQKDG1lbW9yeV9ieXRlcxgFIAEoAxIXCg9kZXBsb3ltZW50c19tYXgYBiABKAUSGQoRbWF4X3Zwc19pbnN0YW5jZXMYCyABKAUSHQoVYmFuZHdpZHRoX2J5dGVzX21vbnRoGAcgASgDEhUKDXN0b3JhZ2VfYnl0ZXMYCCABKAMSHQoVbWluaW11bV9wYXltZW50X2NlbnRzGAkgASgDEiIKGm1vbnRobHlfZnJlZV9jcmVkaXRzX2NlbnRzGAogASgDEhIKCnRyaWFsX2RheXMYDCABKAUimAEKEk9yZ2FuaXphdGlvbk1lbWJlchIKCgJpZBgBIAEoCRIpCgR1c2VyGAIgASgLMhsub2JpZW50ZS5jbG91ZC5hdXRoLnYxLlVzZXISDAoEcm9sZRgDIAEoCRIOCgZzdGF0dXMYBCABKAkSLQoJam9pbmVkX2F0GAUgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcCJeChFBZGRDcmVkaXRzUmVxdWVzdBIXCg9vcmdhbml6YXRpb25faWQYASABKAkSFAoMYW1vdW50X2NlbnRzGAIgASgDEhEKBG5vdGUYAyABKAlIAIgBAUIHCgVfbm90ZSKPAQoSQWRkQ3JlZGl0c1Jlc3BvbnNlEkIKDG9yZ2FuaXphdGlvbhgBIAEoCzIsLm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5Pcmdhbml6YXRpb24SGQoRbmV3X2JhbGFuY2VfY2VudHMYAiABKAMSGgoSYW1vdW50X2FkZGVkX2NlbnRzGAMgASgDImMKFkFkbWluQWRkQ3JlZGl0c1JlcXVlc3QSFwoPb3JnYW5pemF0aW9uX2lkGAEgASgJEhQKDGFtb3VudF9jZW50cxgCIAEoAxIRCgRub3RlGAMgASgJSACIAQFCBwoFX25vdGUilAEKF0FkbWluQWRkQ3JlZGl0c1Jlc3BvbnNlEkIKDG9yZ2FuaXphdGlvbhgBIAEoCzIsLm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5Pcmdhbml6YXRpb24SGQoRbmV3X2JhbGFuY2VfY2VudHMYAiABKAMSGgoSYW1vdW50X2FkZGVkX2NlbnRzGAMgASgDImYKGUFkbWluUmVtb3ZlQ3JlZGl0c1JlcXVlc3QSFwoPb3JnYW5pemF0aW9uX2lkGAEgASgJEhQKDGFtb3VudF9jZW50cxgCIAEoAxIRCgRub3RlGAMgASgJSACIAQFCBwoFX25vdGUimQEKGkFkbWluUmVtb3ZlQ3JlZGl0c1Jlc3BvbnNlEkIKDG9yZ2FuaXphdGlvbhgBIAEoCzIsLm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5Pcmdhbml6YXRpb24SGQoRbmV3X2JhbGFuY2VfY2VudHMYAiABKAMSHAoUYW1vdW50X3JlbW92ZWRfY2VudHMYAyABKAMiTgoTR2V0Q3JlZGl0TG9nUmVxdWVzdBIXCg9vcmdhbml6YXRpb25faWQYASABKAkSDAoEcGFnZRgCIAEoBRIQCghwZXJfcGFnZRgDIAEoBSKYAQoUR2V0Q3JlZGl0TG9nUmVzcG9uc2USRwoMdHJhbnNhY3Rpb25zGAEgAygLMjEub2JpZW50ZS5jbG91ZC5vcmdhbml6YXRpb25zLnYxLkNyZWRpdFRyYW5zYWN0aW9uEjcKCnBhZ2luYXRpb24YAiABKAsyIy5vYmllbnRlLmNsb3VkLmNvbW1vbi52MS5QYWdpbmF0aW9uIvcBChFDcmVkaXRUcmFuc2FjdGlvbhIKCgJpZBgBIAEoCRIXCg9vcmdhbml6YXRpb25faWQYAiABKAkSFAoMYW1vdW50X2NlbnRzGAMgASgDEhUKDWJhbGFuY2VfYWZ0ZXIYBCABKAMSDAoEdHlwZRgFIAEoCRIOCgZzb3VyY2UYBiABKAkSEQoEbm90ZRgHIAEoCUgAiAEBEhcKCmNyZWF0ZWRfYnkYCCABKAlIAYgBARIuCgpjcmVhdGVkX2F0GAkgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcEIHCgVfbm90ZUINCgtfY3JlYXRlZF9ieSIyChdHZXRNeVBlcm1pc3Npb25zUmVxdWVzdBIXCg9vcmdhbml6YXRpb25faWQYASABKAkiLwoYR2V0TXlQZXJtaXNzaW9uc1Jlc3BvbnNlEhMKC3Blcm1pc3Npb25zGAEgAygJIj8KE0FkbWluU2V0UGxhblJlcXVlc3QSFwoPb3JnYW5pemF0aW9uX2lkGAEgASgJEg8KB3BsYW5faWQYAiABKAkiawoUQWRtaW5TZXRQbGFuUmVzcG9uc2USQgoMb3JnYW5pemF0aW9uGAEgASgLMiwub2JpZW50ZS5jbG91ZC5vcmdhbml6YXRpb25zLnYxLk9yZ2FuaXphdGlvbhIPCgdwbGFuX2lkGAIgASgJMpwUChNPcmdhbml6YXRpb25TZXJ2aWNlEnkKDEFkbWluU2V0UGxhbhIzLm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5BZG1pblNldFBsYW5SZXF1ZXN0GjQub2JpZW50ZS5jbG91ZC5vcmdhbml6YXRpb25zLnYxLkFkbWluU2V0UGxhblJlc3BvbnNlEogBChFMaXN0T3JnYW5pemF0aW9ucxI4Lm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5MaXN0T3JnYW5pemF0aW9uc1JlcXVlc3QaOS5vYmllbnRlLmNsb3VkLm9yZ2FuaXphdGlvbnMudjEuTGlzdE9yZ2FuaXphdGlvbnNSZXNwb25zZRKLAQoSQ3JlYXRlT3JnYW5pemF0aW9uEjkub2JpZW50ZS5jbG91ZC5vcmdhbml6YXRpb25zLnYxLkNyZWF0ZU9yZ2FuaXphdGlvblJlcXVlc3QaOi5vYmllbnRlLmNsb3VkLm9yZ2FuaXphdGlvbnMudjEuQ3JlYXRlT3JnYW5pemF0aW9uUmVzcG9uc2USggEKD0dldE9yZ2FuaXphdGlvbhI2Lm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5HZXRPcmdhbml6YXRpb25SZXF1ZXN0Gjcub2JpZW50ZS5jbG91ZC5vcmdhbml6YXRpb25zLnYxLkdldE9yZ2FuaXphdGlvblJlc3BvbnNlEosBChJVcGRhdGVPcmdhbml6YXRpb24SOS5vYmllbnRlLmNsb3VkLm9yZ2FuaXphdGlvbnMudjEuVXBkYXRlT3JnYW5pemF0aW9uUmVxdWVzdBo6Lm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5VcGRhdGVPcmdhbml6YXRpb25SZXNwb25zZRJ2CgtMaXN0TWVtYmVycxIyLm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5MaXN0TWVtYmVyc1JlcXVlc3QaMy5vYmllbnRlLmNsb3VkLm9yZ2FuaXphdGlvbnMudjEuTGlzdE1lbWJlcnNSZXNwb25zZRJ5CgxJbnZpdGVNZW1iZXISMy5vYmllbnRlLmNsb3VkLm9yZ2FuaXphdGlvbnMudjEuSW52aXRlTWVtYmVyUmVxdWVzdBo0Lm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5JbnZpdGVNZW1iZXJSZXNwb25zZRJ5CgxSZXNlbmRJbnZpdGUSMy5vYmllbnRlLmNsb3VkLm9yZ2FuaXphdGlvbnMudjEuUmVzZW5kSW52aXRlUmVxdWVzdBo0Lm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5SZXNlbmRJbnZpdGVSZXNwb25zZRJ8Cg1MaXN0TXlJbnZpdGVzEjQub2JpZW50ZS5jbG91ZC5vcmdhbml6YXRpb25zLnYxLkxpc3RNeUludml0ZXNSZXF1ZXN0GjUub2JpZW50ZS5jbG91ZC5vcmdhbml6YXRpb25zLnYxLkxpc3RNeUludml0ZXNSZXNwb25zZRJ5CgxBY2NlcHRJbnZpdGUSMy5vYmllbnRlLmNsb3VkLm9yZ2FuaXphdGlvbnMudjEuQWNjZXB0SW52aXRlUmVxdWVzdBo0Lm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5BY2NlcHRJbnZpdGVSZXNwb25zZRJ8Cg1EZWNsaW5lSW52aXRlEjQub2JpZW50ZS5jbG91ZC5vcmdhbml6YXRpb25zLnYxLkRlY2xpbmVJbnZpdGVSZXF1ZXN0GjUub2JpZW50ZS5jbG91ZC5vcmdhbml6YXRpb25zLnYxLkRlY2xpbmVJbnZpdGVSZXNwb25zZRJ5CgxVcGRhdGVNZW1iZXISMy5vYmllbnRlLmNsb3VkLm9yZ2FuaXphdGlvbnMudjEuVXBkYXRlTWVtYmVyUmVxdWVzdBo0Lm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5VcGRhdGVNZW1iZXJSZXNwb25zZRJ5CgxSZW1vdmVNZW1iZXISMy5vYmllbnRlLmNsb3VkLm9yZ2FuaXphdGlvbnMudjEuUmVtb3ZlTWVtYmVyUmVxdWVzdBo0Lm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5SZW1vdmVNZW1iZXJSZXNwb25zZRKIAQoRVHJhbnNmZXJPd25lcnNoaXASOC5vYmllbnRlLmNsb3VkLm9yZ2FuaXphdGlvbnMudjEuVHJhbnNmZXJPd25lcnNoaXBSZXF1ZXN0Gjkub2JpZW50ZS5jbG91ZC5vcmdhbml6YXRpb25zLnYxLlRyYW5zZmVyT3duZXJzaGlwUmVzcG9uc2USbQoIR2V0VXNhZ2USLy5vYmllbnRlLmNsb3VkLm9yZ2FuaXphdGlvbnMudjEuR2V0VXNhZ2VSZXF1ZXN0GjAub2JpZW50ZS5jbG91ZC5vcmdhbml6YXRpb25zLnYxLkdldFVzYWdlUmVzcG9uc2UScwoKQWRkQ3JlZGl0cxIxLm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5BZGRDcmVkaXRzUmVxdWVzdBoyLm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5BZGRDcmVkaXRzUmVzcG9uc2USggEKD0FkbWluQWRkQ3JlZGl0cxI2Lm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5BZG1pbkFkZENyZWRpdHNSZXF1ZXN0Gjcub2JpZW50ZS5jbG91ZC5vcmdhbml6YXRpb25zLnYxLkFkbWluQWRkQ3JlZGl0c1Jlc3BvbnNlEosBChJBZG1pblJlbW92ZUNyZWRpdHMSOS5vYmllbnRlLmNsb3VkLm9yZ2FuaXphdGlvbnMudjEuQWRtaW5SZW1vdmVDcmVkaXRzUmVxdWVzdBo6Lm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5BZG1pblJlbW92ZUNyZWRpdHNSZXNwb25zZRJ5CgxHZXRDcmVkaXRMb2cSMy5vYmllbnRlLmNsb3VkLm9yZ2FuaXphdGlvbnMudjEuR2V0Q3JlZGl0TG9nUmVxdWVzdBo0Lm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5HZXRDcmVkaXRMb2dSZXNwb25zZRKFAQoQR2V0TXlQZXJtaXNzaW9ucxI3Lm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5HZXRNeVBlcm1pc3Npb25zUmVxdWVzdBo4Lm9iaWVudGUuY2xvdWQub3JnYW5pemF0aW9ucy52MS5HZXRNeVBlcm1pc3Npb25zUmVzcG9uc2VCW1pZZ2l0aHViLmNvbS9vYmllbnRlL2Nsb3VkL2FwcHMvc2hhcmVkL3Byb3RvL29iaWVudGUvY2xvdWQvb3JnYW5pemF0aW9ucy92MTtvcmdhbml6YXRpb25zdjFiBnByb3RvMw", [file_google_protobuf_timestamp, file_obiente_cloud_auth_v1_auth_service, file_obiente_cloud_common_v1_common]); /** * @generated from message obiente.cloud.organizations.v1.GetUsageRequest @@ -1367,10 +1367,70 @@ export type GetMyPermissionsResponse = Message<"obiente.cloud.organizations.v1.G export const GetMyPermissionsResponseSchema: GenMessage = /*@__PURE__*/ messageDesc(file_obiente_cloud_organizations_v1_organization_service, 44); +/** + * Request to set the active plan for an organization (superadmin only) + * + * @generated from message obiente.cloud.organizations.v1.AdminSetPlanRequest + */ +export type AdminSetPlanRequest = Message<"obiente.cloud.organizations.v1.AdminSetPlanRequest"> & { + /** + * @generated from field: string organization_id = 1; + */ + organizationId: string; + + /** + * The plan to assign (must match OrganizationPlan.ID) + * + * @generated from field: string plan_id = 2; + */ + planId: string; +}; + +/** + * Describes the message obiente.cloud.organizations.v1.AdminSetPlanRequest. + * Use `create(AdminSetPlanRequestSchema)` to create a new message. + */ +export const AdminSetPlanRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_obiente_cloud_organizations_v1_organization_service, 45); + +/** + * Response for AdminSetPlan + * + * @generated from message obiente.cloud.organizations.v1.AdminSetPlanResponse + */ +export type AdminSetPlanResponse = Message<"obiente.cloud.organizations.v1.AdminSetPlanResponse"> & { + /** + * @generated from field: obiente.cloud.organizations.v1.Organization organization = 1; + */ + organization?: Organization; + + /** + * @generated from field: string plan_id = 2; + */ + planId: string; +}; + +/** + * Describes the message obiente.cloud.organizations.v1.AdminSetPlanResponse. + * Use `create(AdminSetPlanResponseSchema)` to create a new message. + */ +export const AdminSetPlanResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_obiente_cloud_organizations_v1_organization_service, 46); + /** * @generated from service obiente.cloud.organizations.v1.OrganizationService */ export const OrganizationService: GenService<{ + /** + * Admin: Set the active plan for an organization (superadmin only) + * + * @generated from rpc obiente.cloud.organizations.v1.OrganizationService.AdminSetPlan + */ + adminSetPlan: { + methodKind: "unary"; + input: typeof AdminSetPlanRequestSchema; + output: typeof AdminSetPlanResponseSchema; + }, /** * List user's organizations *