Unified · Split
| @@ -208,7 +208,7 @@ function WorkspaceSelector({ inHeader = false }: { inHeader?: boolean }) { · 207 unmodified lines | |||
| 208 | New workspace | 208 | New workspace |
| 209 | </DropdownMenuItem> | 209 | </DropdownMenuItem> |
| 210 | <DropdownMenuLabel className="text-[10px] text-muted-foreground"> | 210 | <DropdownMenuLabel className="text-[10px] text-muted-foreground"> |
| 211 | Invites via {clientConfig?.invitesProvider === "workos" ? "WorkOS" : "local provider"} | 211 | Invites via WorkOS |
| 212 | </DropdownMenuLabel> | 212 | </DropdownMenuLabel> |
| 213 | </> | 213 | </> |
| 214 | ) : null} | 214 | ) : null} |
| @@ -18,7 +18,6 @@ const ROLE_OPTIONS: Role[] = ["owner", "admin", "member", "billing_admin"]; · 17 unmodified lines | |||
| 18 | export function MembersView() { | 18 | export function MembersView() { |
| 19 | const { | 19 | const { |
| 20 | context, | 20 | context, |
| 21 | clientConfig, | ||
| 22 | workspaces, | 21 | workspaces, |
| 23 | } = useSession(); | 22 | } = useSession(); |
| 24 | 23 | ||
| @@ -145,9 +144,7 @@ export function MembersView() { · 120 unmodified lines | |||
| 145 | {inviteState === "sending" ? "Sending..." : "Send invite"} | 144 | {inviteState === "sending" ? "Sending..." : "Send invite"} |
| 146 | </Button> | 145 | </Button> |
| 147 | </div> | 146 | </div> |
| 148 | <p className="text-xs text-muted-foreground"> | 147 | <p className="text-xs text-muted-foreground">Provider: WorkOS</p> |
| 149 | Provider: {clientConfig?.invitesProvider === "workos" ? "WorkOS" : "Local invite flow"} | ||
| 150 | </p> | ||
| 151 | {inviteMessage ? ( | 148 | {inviteMessage ? ( |
| 152 | <p className={inviteState === "failed" ? "text-xs text-destructive" : "text-xs text-muted-foreground"}> | 149 | <p className={inviteState === "failed" ? "text-xs text-destructive" : "text-xs text-muted-foreground"}> |
| 153 | {inviteMessage} | 150 | {inviteMessage} |
| @@ -8,7 +8,7 @@ export const getClientConfig = query({ · 7 unmodified lines | |||
| 8 | handler: async () => { | 8 | handler: async () => { |
| 9 | return { | 9 | return { |
| 10 | authProviderMode: workosEnabled ? "workos" : "local", | 10 | authProviderMode: workosEnabled ? "workos" : "local", |
| 11 | invitesProvider: workosEnabled ? "workos" : "local", | 11 | invitesProvider: workosEnabled ? "workos" : "disabled", |
| 12 | features: { | 12 | features: { |
| 13 | organizations: true, | 13 | organizations: true, |
| 14 | billing: true, | 14 | billing: true, |
| @@ -1,5 +1,4 @@ | |||
| 1 | import { AuthKit, type AuthFunctions } from "@convex-dev/workos-authkit"; | 1 | import { AuthKit, type AuthFunctions } from "@convex-dev/workos-authkit"; |
| 2 | import { v } from "convex/values"; | ||
| 3 | import { components, internal } from "./_generated/api"; | 2 | import { components, internal } from "./_generated/api"; |
| 4 | import type { DataModel, Doc, Id } from "./_generated/dataModel"; | 3 | import type { DataModel, Doc, Id } from "./_generated/dataModel"; |
| 5 | import type { MutationCtx } from "./_generated/server"; | 4 | import type { MutationCtx } from "./_generated/server"; |
| @@ -16,10 +15,10 @@ const workosEnabled = Boolean( · 10 unmodified lines | |||
| 16 | process.env.WORKOS_CLIENT_ID && process.env.WORKOS_API_KEY && process.env.WORKOS_WEBHOOK_SECRET, | 15 | process.env.WORKOS_CLIENT_ID && process.env.WORKOS_API_KEY && process.env.WORKOS_WEBHOOK_SECRET, |
| 17 | ); | 16 | ); |
| 18 | 17 | ||
| 19 | const authFunctions = (internal as any).auth as AuthFunctions; | 18 | const authFunctions: AuthFunctions = internal.auth; |
| 20 | 19 | ||
| 21 | const authKitInstance = workosEnabled | 20 | const authKitInstance = workosEnabled |
| 22 | ? new AuthKit<DataModel>((components as any).workOSAuthKit, { | 21 | ? new AuthKit<DataModel>(components.workOSAuthKit, { |
| 23 | authFunctions, | 22 | authFunctions, |
| 24 | additionalEventTypes: [ | 23 | additionalEventTypes: [ |
| 25 | "organization.created", | 24 | "organization.created", |
| @@ -233,7 +232,7 @@ function getIdentityString(identity: Record<string, unknown>, keys: string[]): s · 207 unmodified lines | |||
| 233 | 232 | ||
| 234 | async function getAuthKitUserProfile(ctx: RunQueryCtx, workosUserId: string) { | 233 | async function getAuthKitUserProfile(ctx: RunQueryCtx, workosUserId: string) { |
| 235 | try { | 234 | try { |
| 236 | return await ctx.runQuery((components as any).workOSAuthKit.lib.getAuthUser, { | 235 | return await ctx.runQuery(components.workOSAuthKit.lib.getAuthUser, { |
| 237 | id: workosUserId, | 236 | id: workosUserId, |
| 238 | }); | 237 | }); |
| 239 | } catch { | 238 | } catch { |
| @@ -241,7 +240,7 @@ async function getAuthKitUserProfile(ctx: RunQueryCtx, workosUserId: string) { · 1 unmodified line | |||
| 241 | } | 240 | } |
| 242 | } | 241 | } |
| 243 | 242 | ||
| 244 | const workosEventHandlers: Record<string, (ctx: WorkosEventCtx, event: any) => Promise<void>> = { | 243 | const workosEventHandlers = { |
| 245 | "user.created": async (ctx, event) => { | 244 | "user.created": async (ctx, event) => { |
| 246 | const now = Date.now(); | 245 | const now = Date.now(); |
| 247 | const data = event.data; | 246 | const data = event.data; |
| @@ -572,15 +571,16 @@ const workosEventHandlers: Record<string, (ctx: WorkosEventCtx, event: any) => P · 324 unmodified lines | |||
| 572 | 571 | ||
| 573 | await ctx.db.delete(membership._id); | 572 | await ctx.db.delete(membership._id); |
| 574 | }, | 573 | }, |
| 575 | }; | 574 | } satisfies Partial<Parameters<AuthKit<DataModel>["events"]>[0]>; |
| 576 | 575 | ||
| 577 | export const authKitEvent = | 576 | const authKitEvents = workosEnabled && authKitInstance |
| 578 | workosEnabled && authKitInstance | 577 | ? authKitInstance.events(workosEventHandlers) |
| 579 | ? (authKitInstance.events(workosEventHandlers as any) as any).authKitEvent | 578 | : null; |
| 580 | : internalMutation({ | 579 | |
| 581 | args: {}, | 580 | export const authKitEvent = authKitEvents?.authKitEvent ?? internalMutation({ |
| 582 | handler: async () => null, | 581 | args: {}, |
| 583 | }); | 582 | handler: async () => null, |
| 583 | }); | ||
| 584 | 584 | ||
| 585 | export const bootstrapCurrentWorkosAccount = mutation({ | 585 | export const bootstrapCurrentWorkosAccount = mutation({ |
| 586 | args: {}, | 586 | args: {}, |
| @@ -8,6 +8,18 @@ const DEFAULT_TIMEOUT_MS = 15_000; · 7 unmodified lines | |||
| 8 | type OrganizationRole = "owner" | "admin" | "member" | "billing_admin"; | 8 | type OrganizationRole = "owner" | "admin" | "member" | "billing_admin"; |
| 9 | type OrganizationMemberStatus = "active" | "pending" | "removed"; | 9 | type OrganizationMemberStatus = "active" | "pending" | "removed"; |
| 10 | 10 | ||
| 11 | const completedTaskStatusValidator = v.union( | ||
| 12 | v.literal("completed"), | ||
| 13 | v.literal("failed"), | ||
| 14 | v.literal("timed_out"), | ||
| 15 | v.literal("denied"), | ||
| 16 | ); | ||
| 17 | const approvalStatusValidator = v.union(v.literal("pending"), v.literal("approved"), v.literal("denied")); | ||
| 18 | const policyDecisionValidator = v.union(v.literal("allow"), v.literal("require_approval"), v.literal("deny")); | ||
| 19 | const credentialScopeValidator = v.union(v.literal("workspace"), v.literal("actor")); | ||
| 20 | const toolSourceTypeValidator = v.union(v.literal("mcp"), v.literal("openapi"), v.literal("graphql")); | ||
| 21 | const agentTaskStatusValidator = v.union(v.literal("running"), v.literal("completed"), v.literal("failed")); | ||
| 22 | |||
| 11 | function normalizeOptional(value?: string): string { | 23 | function normalizeOptional(value?: string): string { |
| 12 | if (typeof value !== "string") { | 24 | if (typeof value !== "string") { |
| 13 | return ""; | 25 | return ""; |
| @@ -426,7 +438,7 @@ export const markTaskRunning = mutation({ · 412 unmodified lines | |||
| 426 | export const markTaskFinished = mutation({ | 438 | export const markTaskFinished = mutation({ |
| 427 | args: { | 439 | args: { |
| 428 | taskId: v.string(), | 440 | taskId: v.string(), |
| 429 | status: v.string(), | 441 | status: completedTaskStatusValidator, |
| 430 | stdout: v.string(), | 442 | stdout: v.string(), |
| 431 | stderr: v.string(), | 443 | stderr: v.string(), |
| 432 | exitCode: v.optional(v.number()), | 444 | exitCode: v.optional(v.number()), |
| @@ -502,7 +514,7 @@ export const getApproval = query({ · 69 unmodified lines | |||
| 502 | export const listApprovals = query({ | 514 | export const listApprovals = query({ |
| 503 | args: { | 515 | args: { |
| 504 | workspaceId: v.string(), | 516 | workspaceId: v.string(), |
| 505 | status: v.optional(v.string()), | 517 | status: v.optional(approvalStatusValidator), |
| 506 | }, | 518 | }, |
| 507 | handler: async (ctx, args) => { | 519 | handler: async (ctx, args) => { |
| 508 | if (args.status) { | 520 | if (args.status) { |
| @@ -570,7 +582,7 @@ export const listPendingApprovals = query({ · 61 unmodified lines | |||
| 570 | export const resolveApproval = mutation({ | 582 | export const resolveApproval = mutation({ |
| 571 | args: { | 583 | args: { |
| 572 | approvalId: v.string(), | 584 | approvalId: v.string(), |
| 573 | decision: v.string(), | 585 | decision: v.union(v.literal("approved"), v.literal("denied")), |
| 574 | reviewerId: v.optional(v.string()), | 586 | reviewerId: v.optional(v.string()), |
| 575 | reason: v.optional(v.string()), | 587 | reason: v.optional(v.string()), |
| 576 | }, | 588 | }, |
| @@ -673,7 +685,7 @@ export const getAgentTask = query({ · 96 unmodified lines | |||
| 673 | export const updateAgentTask = mutation({ | 685 | export const updateAgentTask = mutation({ |
| 674 | args: { | 686 | args: { |
| 675 | agentTaskId: v.string(), | 687 | agentTaskId: v.string(), |
| 676 | status: v.optional(v.string()), | 688 | status: v.optional(agentTaskStatusValidator), |
| 677 | resultText: v.optional(v.string()), | 689 | resultText: v.optional(v.string()), |
| 678 | error: v.optional(v.string()), | 690 | error: v.optional(v.string()), |
| 679 | codeRuns: v.optional(v.number()), | 691 | codeRuns: v.optional(v.number()), |
| @@ -772,7 +784,7 @@ export const upsertAccessPolicy = mutation({ · 92 unmodified lines | |||
| 772 | actorId: v.optional(v.string()), | 784 | actorId: v.optional(v.string()), |
| 773 | clientId: v.optional(v.string()), | 785 | clientId: v.optional(v.string()), |
| 774 | toolPathPattern: v.string(), | 786 | toolPathPattern: v.string(), |
| 775 | decision: v.string(), | 787 | decision: policyDecisionValidator, |
| 776 | priority: v.optional(v.number()), | 788 | priority: v.optional(v.number()), |
| 777 | }, | 789 | }, |
| 778 | handler: async (ctx, args) => { | 790 | handler: async (ctx, args) => { |
| @@ -842,7 +854,7 @@ export const upsertCredential = mutation({ · 63 unmodified lines | |||
| 842 | id: v.optional(v.string()), | 854 | id: v.optional(v.string()), |
| 843 | workspaceId: v.string(), | 855 | workspaceId: v.string(), |
| 844 | sourceKey: v.string(), | 856 | sourceKey: v.string(), |
| 845 | scope: v.string(), | 857 | scope: credentialScopeValidator, |
| 846 | actorId: v.optional(v.string()), | 858 | actorId: v.optional(v.string()), |
| 847 | secretJson: v.any(), | 859 | secretJson: v.any(), |
| 848 | }, | 860 | }, |
| @@ -914,7 +926,7 @@ export const resolveCredential = query({ · 65 unmodified lines | |||
| 914 | args: { | 926 | args: { |
| 915 | workspaceId: v.string(), | 927 | workspaceId: v.string(), |
| 916 | sourceKey: v.string(), | 928 | sourceKey: v.string(), |
| 917 | scope: v.string(), | 929 | scope: credentialScopeValidator, |
| 918 | actorId: v.optional(v.string()), | 930 | actorId: v.optional(v.string()), |
| 919 | }, | 931 | }, |
| 920 | handler: async (ctx, args) => { | 932 | handler: async (ctx, args) => { |
| @@ -958,7 +970,7 @@ export const upsertToolSource = mutation({ · 37 unmodified lines | |||
| 958 | id: v.optional(v.string()), | 970 | id: v.optional(v.string()), |
| 959 | workspaceId: v.string(), | 971 | workspaceId: v.string(), |
| 960 | name: v.string(), | 972 | name: v.string(), |
| 961 | type: v.string(), | 973 | type: toolSourceTypeValidator, |
| 962 | config: v.any(), | 974 | config: v.any(), |
| 963 | enabled: v.optional(v.boolean()), | 975 | enabled: v.optional(v.boolean()), |
| 964 | }, | 976 | }, |
| @@ -2,8 +2,7 @@ import { WorkOS } from "@workos-inc/node"; · 1 unmodified line | |||
| 2 | import { v } from "convex/values"; | 2 | import { v } from "convex/values"; |
| 3 | import { internal } from "./_generated/api"; | 3 | import { internal } from "./_generated/api"; |
| 4 | import { internalAction, internalMutation, internalQuery } from "./_generated/server"; | 4 | import { internalAction, internalMutation, internalQuery } from "./_generated/server"; |
| 5 | import { getOrganizationMembership } from "./lib/identity"; | 5 | import { organizationMutation, organizationQuery } from "./lib/functionBuilders"; |
| 6 | import { authedMutation, organizationMutation, organizationQuery } from "./lib/functionBuilders"; | ||
| 7 | 6 | ||
| 8 | const workosEnabled = Boolean(process.env.WORKOS_CLIENT_ID && process.env.WORKOS_API_KEY); | 7 | const workosEnabled = Boolean(process.env.WORKOS_CLIENT_ID && process.env.WORKOS_API_KEY); |
| 9 | const workosClient = process.env.WORKOS_API_KEY ? new WorkOS(process.env.WORKOS_API_KEY) : null; | 8 | const workosClient = process.env.WORKOS_API_KEY ? new WorkOS(process.env.WORKOS_API_KEY) : null; |
| @@ -16,14 +15,6 @@ const organizationRoleValidator = v.union( · 6 unmodified lines | |||
| 16 | v.literal("billing_admin"), | 15 | v.literal("billing_admin"), |
| 17 | ); | 16 | ); |
| 18 | 17 | ||
| 19 | async function sha256Hex(value: string): Promise<string> { | ||
| 20 | const bytes = new TextEncoder().encode(value); | ||
| 21 | const digest = await crypto.subtle.digest("SHA-256", bytes); | ||
| 22 | return Array.from(new Uint8Array(digest)) | ||
| 23 | .map((value) => value.toString(16).padStart(2, "0")) | ||
| 24 | .join(""); | ||
| 25 | } | ||
| 26 | |||
| 27 | type WorkosInvitationResponse = { | 18 | type WorkosInvitationResponse = { |
| 28 | id: string; | 19 | id: string; |
| 29 | state: string; | 20 | state: string; |
| @@ -136,8 +127,6 @@ export const create = organizationMutation({ · 106 unmodified lines | |||
| 136 | } | 127 | } |
| 137 | const inviterWorkosUserId = ctx.account.providerAccountId; | 128 | const inviterWorkosUserId = ctx.account.providerAccountId; |
| 138 | 129 | ||
| 139 | const token = `invite_${crypto.randomUUID()}`; | ||
| 140 | const tokenHash = await sha256Hex(token); | ||
| 141 | const provider = "workos"; | 130 | const provider = "workos"; |
| 142 | 131 | ||
| 143 | const inviteId = await ctx.db.insert("invites", { | 132 | const inviteId = await ctx.db.insert("invites", { |
| @@ -146,7 +135,6 @@ export const create = organizationMutation({ · 2 unmodified lines | |||
| 146 | email: normalizedEmail, | 135 | email: normalizedEmail, |
| 147 | role: args.role, | 136 | role: args.role, |
| 148 | status: "pending", | 137 | status: "pending", |
| 149 | tokenHash, | ||
| 150 | provider, | 138 | provider, |
| 151 | invitedByAccountId: ctx.account._id, | 139 | invitedByAccountId: ctx.account._id, |
| 152 | expiresAt, | 140 | expiresAt, |
| @@ -174,7 +162,6 @@ export const create = organizationMutation({ · 21 unmodified lines | |||
| 174 | role: invite.role, | 162 | role: invite.role, |
| 175 | status: invite.status, | 163 | status: invite.status, |
| 176 | expiresAt: invite.expiresAt, | 164 | expiresAt: invite.expiresAt, |
| 177 | token, | ||
| 178 | }, | 165 | }, |
| 179 | delivery: { | 166 | delivery: { |
| 180 | provider, | 167 | provider, |
| @@ -388,69 +375,3 @@ export const markInviteDeliveryFailed = internalMutation({ · 207 unmodified lines | |||
| 388 | }); | 375 | }); |
| 389 | }, | 376 | }, |
| 390 | }); | 377 | }); |
| 391 | |||
| 392 | export const accept = authedMutation({ | ||
| 393 | args: { | ||
| 394 | token: v.string(), | ||
| 395 | }, | ||
| 396 | handler: async (ctx, args) => { | ||
| 397 | const tokenHash = await sha256Hex(args.token); | ||
| 398 | |||
| 399 | const invite = await ctx.db | ||
| 400 | .query("invites") | ||
| 401 | .withIndex("by_token_hash", (q) => q.eq("tokenHash", tokenHash)) | ||
| 402 | .unique(); | ||
| 403 | |||
| 404 | if (!invite || invite.status !== "pending") { | ||
| 405 | throw new Error("Invite is invalid or expired"); | ||
| 406 | } | ||
| 407 | if (invite.expiresAt < Date.now()) { | ||
| 408 | await ctx.db.patch(invite._id, { | ||
| 409 | status: "expired", | ||
| 410 | updatedAt: Date.now(), | ||
| 411 | }); | ||
| 412 | throw new Error("Invite has expired"); | ||
| 413 | } | ||
| 414 | |||
| 415 | const existing = await getOrganizationMembership(ctx, invite.organizationId, ctx.account._id); | ||
| 416 | if (existing) { | ||
| 417 | await ctx.db.patch(existing._id, { | ||
| 418 | role: invite.role, | ||
| 419 | status: "active", | ||
| 420 | joinedAt: existing.joinedAt ?? Date.now(), | ||
| 421 | updatedAt: Date.now(), | ||
| 422 | }); | ||
| 423 | } else { | ||
| 424 | await ctx.db.insert("organizationMembers", { | ||
| 425 | organizationId: invite.organizationId, | ||
| 426 | accountId: ctx.account._id, | ||
| 427 | role: invite.role, | ||
| 428 | status: "active", | ||
| 429 | billable: true, | ||
| 430 | invitedByAccountId: invite.invitedByAccountId, | ||
| 431 | joinedAt: Date.now(), | ||
| 432 | createdAt: Date.now(), | ||
| 433 | updatedAt: Date.now(), | ||
| 434 | }); | ||
| 435 | } | ||
| 436 | |||
| 437 | await ctx.db.patch(invite._id, { | ||
| 438 | status: "accepted", | ||
| 439 | acceptedAt: Date.now(), | ||
| 440 | updatedAt: Date.now(), | ||
| 441 | }); | ||
| 442 | |||
| 443 | const nextVersion = await ctx.runMutation(internal.billingInternal.bumpSeatSyncVersion, { | ||
| 444 | organizationId: invite.organizationId, | ||
| 445 | }); | ||
| 446 | await ctx.scheduler.runAfter(0, internal.billingSync.syncSeatQuantity, { | ||
| 447 | organizationId: invite.organizationId, | ||
| 448 | expectedVersion: nextVersion, | ||
| 449 | }); | ||
| 450 | |||
| 451 | return { | ||
| 452 | ok: true, | ||
| 453 | organizationId: invite.organizationId, | ||
| 454 | }; | ||
| 455 | }, | ||
| 456 | }); | ||
| @@ -8,6 +8,16 @@ const orgRole = v.union(v.literal("owner"), v.literal("admin"), v.literal("membe · 7 unmodified lines | |||
| 8 | const orgMemberStatus = v.union(v.literal("active"), v.literal("pending"), v.literal("removed")); | 8 | const orgMemberStatus = v.union(v.literal("active"), v.literal("pending"), v.literal("removed")); |
| 9 | const workspaceMemberRole = v.union(v.literal("owner"), v.literal("admin"), v.literal("member")); | 9 | const workspaceMemberRole = v.union(v.literal("owner"), v.literal("admin"), v.literal("member")); |
| 10 | const workspaceMemberStatus = v.union(v.literal("active"), v.literal("pending"), v.literal("removed")); | 10 | const workspaceMemberStatus = v.union(v.literal("active"), v.literal("pending"), v.literal("removed")); |
| 11 | const billingSubscriptionStatus = v.union( | ||
| 12 | v.literal("incomplete"), | ||
| 13 | v.literal("incomplete_expired"), | ||
| 14 | v.literal("trialing"), | ||
| 15 | v.literal("active"), | ||
| 16 | v.literal("past_due"), | ||
| 17 | v.literal("canceled"), | ||
| 18 | v.literal("unpaid"), | ||
| 19 | v.literal("paused"), | ||
| 20 | ); | ||
| 11 | const inviteStatus = v.union( | 21 | const inviteStatus = v.union( |
| 12 | v.literal("pending"), | 22 | v.literal("pending"), |
| 13 | v.literal("accepted"), | 23 | v.literal("accepted"), |
| @@ -16,6 +26,19 @@ const inviteStatus = v.union( · 2 unmodified lines | |||
| 16 | v.literal("failed"), | 26 | v.literal("failed"), |
| 17 | ); | 27 | ); |
| 18 | const inviteProvider = v.literal("workos"); | 28 | const inviteProvider = v.literal("workos"); |
| 29 | const taskStatus = v.union( | ||
| 30 | v.literal("queued"), | ||
| 31 | v.literal("running"), | ||
| 32 | v.literal("completed"), | ||
| 33 | v.literal("failed"), | ||
| 34 | v.literal("timed_out"), | ||
| 35 | v.literal("denied"), | ||
| 36 | ); | ||
| 37 | const approvalStatus = v.union(v.literal("pending"), v.literal("approved"), v.literal("denied")); | ||
| 38 | const policyDecision = v.union(v.literal("allow"), v.literal("require_approval"), v.literal("deny")); | ||
| 39 | const credentialScope = v.union(v.literal("workspace"), v.literal("actor")); | ||
| 40 | const toolSourceType = v.union(v.literal("mcp"), v.literal("openapi"), v.literal("graphql")); | ||
| 41 | const agentTaskStatus = v.union(v.literal("running"), v.literal("completed"), v.literal("failed")); | ||
| 19 | 42 | ||
| 20 | export default defineSchema({ | 43 | export default defineSchema({ |
| 21 | accounts: defineTable({ | 44 | accounts: defineTable({ |
| @@ -102,7 +125,6 @@ export default defineSchema({ · 80 unmodified lines | |||
| 102 | email: v.string(), | 125 | email: v.string(), |
| 103 | role: orgRole, | 126 | role: orgRole, |
| 104 | status: inviteStatus, | 127 | status: inviteStatus, |
| 105 | tokenHash: v.string(), | ||
| 106 | provider: inviteProvider, | 128 | provider: inviteProvider, |
| 107 | providerInviteId: v.optional(v.string()), | 129 | providerInviteId: v.optional(v.string()), |
| 108 | invitedByAccountId: v.id("accounts"), | 130 | invitedByAccountId: v.id("accounts"), |
| @@ -111,7 +133,6 @@ export default defineSchema({ · 2 unmodified lines | |||
| 111 | createdAt: v.number(), | 133 | createdAt: v.number(), |
| 112 | updatedAt: v.number(), | 134 | updatedAt: v.number(), |
| 113 | }) | 135 | }) |
| 114 | .index("by_token_hash", ["tokenHash"]) | ||
| 115 | .index("by_org", ["organizationId"]) | 136 | .index("by_org", ["organizationId"]) |
| 116 | .index("by_org_status_created", ["organizationId", "status", "createdAt"]) | 137 | .index("by_org_status_created", ["organizationId", "status", "createdAt"]) |
| 117 | .index("by_org_email_status", ["organizationId", "email", "status"]), | 138 | .index("by_org_email_status", ["organizationId", "email", "status"]), |
| @@ -129,7 +150,7 @@ export default defineSchema({ · 11 unmodified lines | |||
| 129 | organizationId: v.id("organizations"), | 150 | organizationId: v.id("organizations"), |
| 130 | stripeSubscriptionId: v.string(), | 151 | stripeSubscriptionId: v.string(), |
| 131 | stripePriceId: v.string(), | 152 | stripePriceId: v.string(), |
| 132 | status: v.string(), | 153 | status: billingSubscriptionStatus, |
| 133 | currentPeriodStart: v.optional(v.number()), | 154 | currentPeriodStart: v.optional(v.number()), |
| 134 | currentPeriodEnd: v.optional(v.number()), | 155 | currentPeriodEnd: v.optional(v.number()), |
| 135 | cancelAtPeriodEnd: v.boolean(), | 156 | cancelAtPeriodEnd: v.boolean(), |
| @@ -158,7 +179,7 @@ export default defineSchema({ · 22 unmodified lines | |||
| 158 | workspaceId: v.string(), | 179 | workspaceId: v.string(), |
| 159 | actorId: v.string(), | 180 | actorId: v.string(), |
| 160 | clientId: v.string(), | 181 | clientId: v.string(), |
| 161 | status: v.string(), | 182 | status: taskStatus, |
| 162 | timeoutMs: v.number(), | 183 | timeoutMs: v.number(), |
| 163 | metadata: v.any(), | 184 | metadata: v.any(), |
| 164 | error: v.optional(v.string()), | 185 | error: v.optional(v.string()), |
| @@ -180,7 +201,7 @@ export default defineSchema({ · 15 unmodified lines | |||
| 180 | workspaceId: v.string(), | 201 | workspaceId: v.string(), |
| 181 | toolPath: v.string(), | 202 | toolPath: v.string(), |
| 182 | input: v.any(), | 203 | input: v.any(), |
| 183 | status: v.string(), | 204 | status: approvalStatus, |
| 184 | reason: v.optional(v.string()), | 205 | reason: v.optional(v.string()), |
| 185 | reviewerId: v.optional(v.string()), | 206 | reviewerId: v.optional(v.string()), |
| 186 | createdAt: v.number(), | 207 | createdAt: v.number(), |
| @@ -208,7 +229,7 @@ export default defineSchema({ · 21 unmodified lines | |||
| 208 | actorId: v.string(), | 229 | actorId: v.string(), |
| 209 | clientId: v.string(), | 230 | clientId: v.string(), |
| 210 | toolPathPattern: v.string(), | 231 | toolPathPattern: v.string(), |
| 211 | decision: v.string(), | 232 | decision: policyDecision, |
| 212 | priority: v.number(), | 233 | priority: v.number(), |
| 213 | createdAt: v.number(), | 234 | createdAt: v.number(), |
| 214 | updatedAt: v.number(), | 235 | updatedAt: v.number(), |
| @@ -220,7 +241,7 @@ export default defineSchema({ · 5 unmodified lines | |||
| 220 | credentialId: v.string(), | 241 | credentialId: v.string(), |
| 221 | workspaceId: v.string(), | 242 | workspaceId: v.string(), |
| 222 | sourceKey: v.string(), | 243 | sourceKey: v.string(), |
| 223 | scope: v.string(), | 244 | scope: credentialScope, |
| 224 | actorId: v.string(), | 245 | actorId: v.string(), |
| 225 | secretJson: v.any(), | 246 | secretJson: v.any(), |
| 226 | createdAt: v.number(), | 247 | createdAt: v.number(), |
| @@ -234,7 +255,7 @@ export default defineSchema({ · 7 unmodified lines | |||
| 234 | sourceId: v.string(), | 255 | sourceId: v.string(), |
| 235 | workspaceId: v.string(), | 256 | workspaceId: v.string(), |
| 236 | name: v.string(), | 257 | name: v.string(), |
| 237 | type: v.string(), | 258 | type: toolSourceType, |
| 238 | config: v.any(), | 259 | config: v.any(), |
| 239 | enabled: v.boolean(), | 260 | enabled: v.boolean(), |
| 240 | createdAt: v.number(), | 261 | createdAt: v.number(), |
| @@ -251,7 +272,7 @@ export default defineSchema({ · 10 unmodified lines | |||
| 251 | requesterId: v.string(), | 272 | requesterId: v.string(), |
| 252 | workspaceId: v.string(), | 273 | workspaceId: v.string(), |
| 253 | actorId: v.string(), | 274 | actorId: v.string(), |
| 254 | status: v.string(), // "running" | "completed" | "failed" | 275 | status: agentTaskStatus, |
| 255 | resultText: v.optional(v.string()), | 276 | resultText: v.optional(v.string()), |
| 256 | error: v.optional(v.string()), | 277 | error: v.optional(v.string()), |
| 257 | codeRuns: v.number(), | 278 | codeRuns: v.number(), |