532 lines
16 KiB
TypeScript
532 lines
16 KiB
TypeScript
import { authTables } from '@convex-dev/auth/server';
|
|
import { defineSchema, defineTable } from 'convex/server';
|
|
import { v } from 'convex/values';
|
|
|
|
const applicationTables = {
|
|
/*
|
|
* Below is the users table definition from authTables
|
|
* You can add additional fields here. You can also remove
|
|
* the users table here & create a 'profiles' table if you
|
|
* prefer to keep auth data separate from application data.
|
|
*/
|
|
users: defineTable({
|
|
name: v.optional(v.string()),
|
|
image: v.optional(v.string()),
|
|
email: v.optional(v.string()),
|
|
emailVerificationTime: v.optional(v.number()),
|
|
phone: v.optional(v.string()),
|
|
phoneVerificationTime: v.optional(v.number()),
|
|
isAnonymous: v.optional(v.boolean()),
|
|
/* Fields below here are custom & not defined in authTables */
|
|
isAdmin: v.optional(v.boolean()),
|
|
role: v.optional(v.union(v.literal('owner'), v.literal('member'))),
|
|
lastSeenAt: v.optional(v.number()),
|
|
themePreference: v.optional(
|
|
v.union(v.literal('light'), v.literal('dark'), v.literal('system')),
|
|
),
|
|
})
|
|
.index('email', ['email'])
|
|
.index('phone', ['phone'])
|
|
/* Indexes below here are custom & not defined in authTables */
|
|
.index('name', ['name']),
|
|
gitConnections: defineTable({
|
|
userId: v.id('users'),
|
|
provider: v.union(
|
|
v.literal('github'),
|
|
v.literal('gitea'),
|
|
v.literal('gitlab'),
|
|
v.literal('other'),
|
|
),
|
|
providerAccountId: v.optional(v.string()),
|
|
displayName: v.string(),
|
|
username: v.optional(v.string()),
|
|
avatarUrl: v.optional(v.string()),
|
|
installationId: v.optional(v.string()),
|
|
scopes: v.optional(v.array(v.string())),
|
|
status: v.union(
|
|
v.literal('active'),
|
|
v.literal('needs_reauth'),
|
|
v.literal('revoked'),
|
|
),
|
|
connectedAt: v.number(),
|
|
updatedAt: v.number(),
|
|
})
|
|
.index('by_user', ['userId'])
|
|
.index('by_user_provider', ['userId', 'provider'])
|
|
.index('by_status', ['status']),
|
|
spoons: defineTable({
|
|
ownerId: v.id('users'),
|
|
name: v.string(),
|
|
description: v.optional(v.string()),
|
|
provider: v.union(
|
|
v.literal('github'),
|
|
v.literal('gitea'),
|
|
v.literal('gitlab'),
|
|
v.literal('other'),
|
|
),
|
|
upstreamOwner: v.string(),
|
|
upstreamRepo: v.string(),
|
|
upstreamDefaultBranch: v.string(),
|
|
upstreamUrl: v.string(),
|
|
forkOwner: v.optional(v.string()),
|
|
forkRepo: v.optional(v.string()),
|
|
forkDefaultBranch: v.optional(v.string()),
|
|
forkUrl: v.optional(v.string()),
|
|
visibility: v.union(
|
|
v.literal('public'),
|
|
v.literal('private'),
|
|
v.literal('internal'),
|
|
v.literal('unknown'),
|
|
),
|
|
maintenanceMode: v.union(
|
|
v.literal('watch'),
|
|
v.literal('auto_pr'),
|
|
v.literal('paused'),
|
|
),
|
|
syncCadence: v.union(
|
|
v.literal('daily'),
|
|
v.literal('weekly'),
|
|
v.literal('manual'),
|
|
),
|
|
productionRefStrategy: v.union(
|
|
v.literal('default_branch'),
|
|
v.literal('latest_release'),
|
|
v.literal('tag_pattern'),
|
|
),
|
|
tagPattern: v.optional(v.string()),
|
|
status: v.union(
|
|
v.literal('draft'),
|
|
v.literal('active'),
|
|
v.literal('needs_connection'),
|
|
v.literal('paused'),
|
|
v.literal('archived'),
|
|
),
|
|
lastCheckedAt: v.optional(v.number()),
|
|
lastUpstreamCommit: v.optional(v.string()),
|
|
lastForkCommit: v.optional(v.string()),
|
|
connectionId: v.optional(v.id('gitConnections')),
|
|
githubInstallationId: v.optional(v.string()),
|
|
githubRepositoryId: v.optional(v.number()),
|
|
upstreamRepositoryId: v.optional(v.number()),
|
|
syncStatus: v.optional(
|
|
v.union(
|
|
v.literal('unknown'),
|
|
v.literal('up_to_date'),
|
|
v.literal('behind'),
|
|
v.literal('ahead'),
|
|
v.literal('diverged'),
|
|
v.literal('checking'),
|
|
v.literal('conflict'),
|
|
v.literal('error'),
|
|
),
|
|
),
|
|
upstreamAheadBy: v.optional(v.number()),
|
|
forkAheadBy: v.optional(v.number()),
|
|
lastMergeBaseCommit: v.optional(v.string()),
|
|
lastSyncRunId: v.optional(v.id('syncRuns')),
|
|
lastAiReviewId: v.optional(v.id('aiReviews')),
|
|
lastGithubRefreshAt: v.optional(v.number()),
|
|
lastSuccessfulRefreshAt: v.optional(v.number()),
|
|
lastError: v.optional(v.string()),
|
|
createdAt: v.number(),
|
|
updatedAt: v.number(),
|
|
})
|
|
.index('by_owner', ['ownerId'])
|
|
.index('by_owner_status', ['ownerId', 'status'])
|
|
.index('by_owner_provider', ['ownerId', 'provider'])
|
|
.index('by_upstream', ['provider', 'upstreamOwner', 'upstreamRepo']),
|
|
spoonRepositoryStates: defineTable({
|
|
spoonId: v.id('spoons'),
|
|
ownerId: v.id('users'),
|
|
upstreamFullName: v.string(),
|
|
forkFullName: v.string(),
|
|
upstreamDefaultBranch: v.string(),
|
|
forkDefaultBranch: v.string(),
|
|
upstreamHeadSha: v.optional(v.string()),
|
|
forkHeadSha: v.optional(v.string()),
|
|
mergeBaseSha: v.optional(v.string()),
|
|
upstreamAheadBy: v.number(),
|
|
forkAheadBy: v.number(),
|
|
status: v.union(
|
|
v.literal('up_to_date'),
|
|
v.literal('behind'),
|
|
v.literal('ahead'),
|
|
v.literal('diverged'),
|
|
v.literal('unknown'),
|
|
),
|
|
openForkPullRequestCount: v.number(),
|
|
openUpstreamPullRequestCount: v.number(),
|
|
lastCommitAt: v.optional(v.number()),
|
|
rawCompareUrl: v.optional(v.string()),
|
|
refreshedAt: v.number(),
|
|
createdAt: v.number(),
|
|
updatedAt: v.number(),
|
|
})
|
|
.index('by_spoon', ['spoonId'])
|
|
.index('by_owner', ['ownerId'])
|
|
.index('by_status', ['ownerId', 'status']),
|
|
spoonCommits: defineTable({
|
|
spoonId: v.id('spoons'),
|
|
ownerId: v.id('users'),
|
|
sha: v.string(),
|
|
side: v.union(v.literal('upstream'), v.literal('fork')),
|
|
message: v.string(),
|
|
authorName: v.optional(v.string()),
|
|
authorEmail: v.optional(v.string()),
|
|
authorLogin: v.optional(v.string()),
|
|
committedAt: v.optional(v.number()),
|
|
htmlUrl: v.optional(v.string()),
|
|
filesChanged: v.optional(v.number()),
|
|
additions: v.optional(v.number()),
|
|
deletions: v.optional(v.number()),
|
|
createdAt: v.number(),
|
|
updatedAt: v.number(),
|
|
})
|
|
.index('by_spoon_side', ['spoonId', 'side'])
|
|
.index('by_owner', ['ownerId'])
|
|
.index('by_sha', ['spoonId', 'sha'])
|
|
.index('by_committed', ['spoonId', 'committedAt']),
|
|
spoonPullRequests: defineTable({
|
|
spoonId: v.id('spoons'),
|
|
ownerId: v.id('users'),
|
|
githubId: v.number(),
|
|
number: v.number(),
|
|
repoFullName: v.string(),
|
|
scope: v.union(
|
|
v.literal('fork'),
|
|
v.literal('upstream'),
|
|
v.literal('from_fork_to_upstream'),
|
|
),
|
|
title: v.string(),
|
|
state: v.union(v.literal('open'), v.literal('closed'), v.literal('merged')),
|
|
draft: v.boolean(),
|
|
authorLogin: v.optional(v.string()),
|
|
baseRef: v.string(),
|
|
headRef: v.string(),
|
|
headRepoFullName: v.optional(v.string()),
|
|
htmlUrl: v.string(),
|
|
createdAtGithub: v.optional(v.number()),
|
|
updatedAtGithub: v.optional(v.number()),
|
|
mergedAtGithub: v.optional(v.number()),
|
|
createdAt: v.number(),
|
|
updatedAt: v.number(),
|
|
})
|
|
.index('by_spoon', ['spoonId'])
|
|
.index('by_spoon_scope', ['spoonId', 'scope'])
|
|
.index('by_owner', ['ownerId'])
|
|
.index('by_github_id', ['githubId'])
|
|
.index('by_state', ['spoonId', 'state']),
|
|
syncRuns: defineTable({
|
|
spoonId: v.id('spoons'),
|
|
ownerId: v.id('users'),
|
|
kind: v.union(
|
|
v.literal('scheduled_check'),
|
|
v.literal('manual_check'),
|
|
v.literal('upstream_update'),
|
|
v.literal('merge_attempt'),
|
|
v.literal('ai_review'),
|
|
),
|
|
status: v.union(
|
|
v.literal('queued'),
|
|
v.literal('running'),
|
|
v.literal('clean'),
|
|
v.literal('conflict'),
|
|
v.literal('needs_review'),
|
|
v.literal('failed'),
|
|
v.literal('merged'),
|
|
),
|
|
upstreamFrom: v.optional(v.string()),
|
|
upstreamTo: v.optional(v.string()),
|
|
summary: v.optional(v.string()),
|
|
aiAssessment: v.optional(v.string()),
|
|
mergeRequestUrl: v.optional(v.string()),
|
|
error: v.optional(v.string()),
|
|
createdAt: v.number(),
|
|
updatedAt: v.number(),
|
|
})
|
|
.index('by_owner', ['ownerId'])
|
|
.index('by_spoon', ['spoonId'])
|
|
.index('by_owner_status', ['ownerId', 'status'])
|
|
.index('by_created', ['createdAt']),
|
|
aiReviews: defineTable({
|
|
spoonId: v.id('spoons'),
|
|
ownerId: v.id('users'),
|
|
syncRunId: v.optional(v.id('syncRuns')),
|
|
model: v.string(),
|
|
status: v.union(
|
|
v.literal('queued'),
|
|
v.literal('running'),
|
|
v.literal('completed'),
|
|
v.literal('failed'),
|
|
),
|
|
reviewType: v.union(
|
|
v.literal('upstream_update'),
|
|
v.literal('manual_prompt'),
|
|
v.literal('merge_safety'),
|
|
),
|
|
inputSummary: v.string(),
|
|
outputSummary: v.optional(v.string()),
|
|
risk: v.union(
|
|
v.literal('unknown'),
|
|
v.literal('low'),
|
|
v.literal('medium'),
|
|
v.literal('high'),
|
|
),
|
|
compatible: v.boolean(),
|
|
requiresHumanReview: v.boolean(),
|
|
recommendedAction: v.union(
|
|
v.literal('sync'),
|
|
v.literal('open_review_pr'),
|
|
v.literal('manual_review'),
|
|
v.literal('do_not_sync'),
|
|
v.literal('unknown'),
|
|
),
|
|
potentialConflicts: v.optional(v.array(v.string())),
|
|
importantFiles: v.optional(v.array(v.string())),
|
|
reasoningSummary: v.optional(v.string()),
|
|
error: v.optional(v.string()),
|
|
createdAt: v.number(),
|
|
updatedAt: v.number(),
|
|
completedAt: v.optional(v.number()),
|
|
})
|
|
.index('by_spoon', ['spoonId'])
|
|
.index('by_owner', ['ownerId'])
|
|
.index('by_status', ['ownerId', 'status'])
|
|
.index('by_sync_run', ['syncRunId'])
|
|
.index('by_created', ['createdAt']),
|
|
spoonSettings: defineTable({
|
|
spoonId: v.id('spoons'),
|
|
ownerId: v.id('users'),
|
|
autoRefreshEnabled: v.boolean(),
|
|
autoReviewEnabled: v.boolean(),
|
|
autoSyncEnabled: v.boolean(),
|
|
requireAiLowRiskForSync: v.boolean(),
|
|
requireCleanCompareForSync: v.boolean(),
|
|
ignoredFilePatterns: v.optional(v.array(v.string())),
|
|
importantFilePatterns: v.optional(v.array(v.string())),
|
|
createdAt: v.number(),
|
|
updatedAt: v.number(),
|
|
})
|
|
.index('by_spoon', ['spoonId'])
|
|
.index('by_owner', ['ownerId']),
|
|
spoonRemotes: defineTable({
|
|
spoonId: v.id('spoons'),
|
|
ownerId: v.id('users'),
|
|
label: v.string(),
|
|
url: v.string(),
|
|
remoteName: v.optional(v.string()),
|
|
createdAt: v.number(),
|
|
updatedAt: v.number(),
|
|
})
|
|
.index('by_spoon', ['spoonId'])
|
|
.index('by_owner', ['ownerId']),
|
|
userAiSettings: defineTable({
|
|
userId: v.id('users'),
|
|
provider: v.literal('openai'),
|
|
encryptedApiKey: v.optional(v.string()),
|
|
apiKeyPreview: v.optional(v.string()),
|
|
model: v.string(),
|
|
reasoningEffort: v.union(
|
|
v.literal('none'),
|
|
v.literal('minimal'),
|
|
v.literal('low'),
|
|
v.literal('medium'),
|
|
v.literal('high'),
|
|
v.literal('xhigh'),
|
|
),
|
|
createdAt: v.number(),
|
|
updatedAt: v.number(),
|
|
})
|
|
.index('by_user', ['userId'])
|
|
.index('by_user_provider', ['userId', 'provider']),
|
|
agentRequests: defineTable({
|
|
spoonId: v.id('spoons'),
|
|
ownerId: v.id('users'),
|
|
agentJobId: v.optional(v.id('agentJobs')),
|
|
prompt: v.string(),
|
|
requestType: v.optional(
|
|
v.union(
|
|
v.literal('manual_prompt'),
|
|
v.literal('upstream_review'),
|
|
v.literal('future_code_change'),
|
|
),
|
|
),
|
|
priority: v.optional(
|
|
v.union(v.literal('low'), v.literal('normal'), v.literal('high')),
|
|
),
|
|
source: v.optional(v.union(v.literal('user'), v.literal('system'))),
|
|
status: v.union(
|
|
v.literal('draft'),
|
|
v.literal('queued'),
|
|
v.literal('running'),
|
|
v.literal('changes_ready'),
|
|
v.literal('merge_request_opened'),
|
|
v.literal('failed'),
|
|
v.literal('cancelled'),
|
|
),
|
|
targetBranch: v.optional(v.string()),
|
|
selectedSecretIds: v.optional(v.array(v.id('spoonSecrets'))),
|
|
baseBranch: v.optional(v.string()),
|
|
requestedBranchName: v.optional(v.string()),
|
|
mergeRequestUrl: v.optional(v.string()),
|
|
summary: v.optional(v.string()),
|
|
error: v.optional(v.string()),
|
|
createdAt: v.number(),
|
|
updatedAt: v.number(),
|
|
})
|
|
.index('by_owner', ['ownerId'])
|
|
.index('by_spoon', ['spoonId'])
|
|
.index('by_owner_status', ['ownerId', 'status'])
|
|
.index('by_created', ['createdAt']),
|
|
spoonSecrets: defineTable({
|
|
spoonId: v.id('spoons'),
|
|
ownerId: v.id('users'),
|
|
name: v.string(),
|
|
encryptedValue: v.string(),
|
|
valuePreview: v.optional(v.string()),
|
|
description: v.optional(v.string()),
|
|
createdAt: v.number(),
|
|
updatedAt: v.number(),
|
|
})
|
|
.index('by_spoon', ['spoonId'])
|
|
.index('by_owner', ['ownerId'])
|
|
.index('by_name', ['spoonId', 'name']),
|
|
spoonAgentSettings: defineTable({
|
|
spoonId: v.id('spoons'),
|
|
ownerId: v.id('users'),
|
|
enabled: v.boolean(),
|
|
defaultBaseBranch: v.optional(v.string()),
|
|
branchPrefix: v.string(),
|
|
installCommand: v.optional(v.string()),
|
|
checkCommand: v.optional(v.string()),
|
|
testCommand: v.optional(v.string()),
|
|
agentModel: v.string(),
|
|
reasoningEffort: v.union(
|
|
v.literal('none'),
|
|
v.literal('minimal'),
|
|
v.literal('low'),
|
|
v.literal('medium'),
|
|
v.literal('high'),
|
|
v.literal('xhigh'),
|
|
),
|
|
maxJobDurationMs: v.number(),
|
|
maxOutputBytes: v.number(),
|
|
createdAt: v.number(),
|
|
updatedAt: v.number(),
|
|
})
|
|
.index('by_spoon', ['spoonId'])
|
|
.index('by_owner', ['ownerId']),
|
|
agentJobs: defineTable({
|
|
spoonId: v.id('spoons'),
|
|
ownerId: v.id('users'),
|
|
agentRequestId: v.id('agentRequests'),
|
|
status: v.union(
|
|
v.literal('queued'),
|
|
v.literal('claimed'),
|
|
v.literal('preparing'),
|
|
v.literal('running'),
|
|
v.literal('checks_running'),
|
|
v.literal('changes_ready'),
|
|
v.literal('draft_pr_opened'),
|
|
v.literal('failed'),
|
|
v.literal('cancelled'),
|
|
v.literal('timed_out'),
|
|
),
|
|
prompt: v.string(),
|
|
baseBranch: v.string(),
|
|
workBranch: v.string(),
|
|
githubInstallationId: v.optional(v.string()),
|
|
forkOwner: v.string(),
|
|
forkRepo: v.string(),
|
|
forkUrl: v.string(),
|
|
upstreamOwner: v.string(),
|
|
upstreamRepo: v.string(),
|
|
selectedSecretIds: v.array(v.id('spoonSecrets')),
|
|
model: v.string(),
|
|
reasoningEffort: v.union(
|
|
v.literal('none'),
|
|
v.literal('minimal'),
|
|
v.literal('low'),
|
|
v.literal('medium'),
|
|
v.literal('high'),
|
|
v.literal('xhigh'),
|
|
),
|
|
commitSha: v.optional(v.string()),
|
|
pullRequestUrl: v.optional(v.string()),
|
|
pullRequestNumber: v.optional(v.number()),
|
|
summary: v.optional(v.string()),
|
|
error: v.optional(v.string()),
|
|
claimedBy: v.optional(v.string()),
|
|
claimedAt: v.optional(v.number()),
|
|
startedAt: v.optional(v.number()),
|
|
completedAt: v.optional(v.number()),
|
|
createdAt: v.number(),
|
|
updatedAt: v.number(),
|
|
})
|
|
.index('by_owner', ['ownerId'])
|
|
.index('by_spoon', ['spoonId'])
|
|
.index('by_request', ['agentRequestId'])
|
|
.index('by_status', ['status'])
|
|
.index('by_claim', ['status', 'createdAt']),
|
|
agentJobEvents: defineTable({
|
|
jobId: v.id('agentJobs'),
|
|
spoonId: v.id('spoons'),
|
|
ownerId: v.id('users'),
|
|
level: v.union(
|
|
v.literal('debug'),
|
|
v.literal('info'),
|
|
v.literal('warn'),
|
|
v.literal('error'),
|
|
),
|
|
phase: v.union(
|
|
v.literal('queued'),
|
|
v.literal('clone'),
|
|
v.literal('plan'),
|
|
v.literal('edit'),
|
|
v.literal('install'),
|
|
v.literal('check'),
|
|
v.literal('test'),
|
|
v.literal('commit'),
|
|
v.literal('push'),
|
|
v.literal('pr'),
|
|
v.literal('cleanup'),
|
|
),
|
|
message: v.string(),
|
|
metadata: v.optional(v.string()),
|
|
createdAt: v.number(),
|
|
})
|
|
.index('by_job', ['jobId'])
|
|
.index('by_spoon', ['spoonId'])
|
|
.index('by_owner', ['ownerId']),
|
|
agentJobArtifacts: defineTable({
|
|
jobId: v.id('agentJobs'),
|
|
spoonId: v.id('spoons'),
|
|
ownerId: v.id('users'),
|
|
kind: v.union(
|
|
v.literal('plan'),
|
|
v.literal('diff'),
|
|
v.literal('test_output'),
|
|
v.literal('summary'),
|
|
v.literal('error'),
|
|
v.literal('pr_body'),
|
|
),
|
|
title: v.string(),
|
|
content: v.string(),
|
|
contentType: v.union(
|
|
v.literal('text/markdown'),
|
|
v.literal('text/plain'),
|
|
v.literal('application/json'),
|
|
v.literal('text/x-diff'),
|
|
),
|
|
createdAt: v.number(),
|
|
})
|
|
.index('by_job', ['jobId'])
|
|
.index('by_spoon', ['spoonId'])
|
|
.index('by_owner', ['ownerId']),
|
|
};
|
|
|
|
export default defineSchema({
|
|
...authTables,
|
|
...applicationTables,
|
|
});
|