ConusAI vendor v0.1.0-dev.20261027+9b1131fb
ci / gate (push) Waiting to run

This commit is contained in:
ConusAI vendor v0.1.0-dev.20261027+9b1131fb
2026-10-27 18:05:17 +00:00
commit 3102273ec7
538 changed files with 80203 additions and 0 deletions
+313
View File
@@ -0,0 +1,313 @@
// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
// Object-store effects are Node actions; each pairs with internal mutations in files.ts.
import { type Actor, AppError, toProblem, unauthenticated, validationFailed } from "@conusai-kit/core"
import { systemActor } from "@conusai-kit/core/worker"
import { DEFAULT_FILES_POLICY, digestStream, platformObjectStore, StreamTooLarge } from "@conusai-kit/files"
// The object-store entry alone: the package root also exports Node-only adapters (the audit
// client), which a Convex action outside "use node" cannot bundle.
import { createObjectStore } from "@conusai-kit/platform/object-store"
import { makeFunctionReference, type UserIdentity } from "convex/server"
import { ConvexError, type Value, v } from "convex/values"
import { action, internalAction } from "./_generated/server"
import { CONVEX_BFF_ISSUER } from "./lib/runtime"
const requiredEnv = (name: string): string => {
const value = process.env[name]?.trim()
if (!value) throw new Error(`Convex deployment is missing ${name}`)
return value
}
const objectStore = () => {
const apiUrl = requiredEnv("CONUSAI_API_URL")
let parsed: URL
try {
parsed = new URL(apiUrl)
} catch {
throw new Error("Convex deployment has an invalid CONUSAI_API_URL")
}
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
throw new Error("Convex deployment has an invalid CONUSAI_API_URL")
}
return platformObjectStore({
blob: createObjectStore({
CONUSAI_API_URL: apiUrl,
CONUSAI_API_TOKEN: requiredEnv("CONUSAI_API_TOKEN"),
CONUSAI_PROJECT_ID: requiredEnv("CONUSAI_PROJECT_ID"),
}),
})
}
const trustedIdentity = (identity: UserIdentity): Actor => {
if (identity.issuer !== CONVEX_BFF_ISSUER || !identity.subject) {
throwProblem(unauthenticated("file actions accept only an authenticated app BFF identity"))
}
return { userId: identity.subject }
}
function throwProblem(error: unknown): never {
if (error instanceof AppError) throw new ConvexError(toProblem(error) as unknown as Value)
throw error
}
const identityForMutation = (identity: UserIdentity) => ({
issuer: identity.issuer,
subject: identity.subject,
tokenIdentifier: identity.tokenIdentifier,
...("conusaiOrgId" in identity && typeof identity.conusaiOrgId === "string"
? { conusaiOrgId: identity.conusaiOrgId }
: {}),
})
const identityValidator = v.object({
issuer: v.string(),
subject: v.string(),
tokenIdentifier: v.string(),
email: v.optional(v.string()),
name: v.optional(v.string()),
conusaiOrgId: v.optional(v.string()),
})
const mutationRef = (name: string) => makeFunctionReference<"mutation">(name)
const actionRef = (name: string) => makeFunctionReference<"action">(name)
/** Called by web readiness to prove that the deployed backend can execute its file action module. */
export const readiness = action({
args: {},
handler: async () => {
objectStore()
requiredEnv("CONUSAI_API_TOKEN")
requiredEnv("CONUSAI_PROJECT_ID")
return { ready: true }
},
})
export const begin = action({
args: { input: v.any() },
handler: async (ctx, { input }) => {
const identity = await ctx.auth.getUserIdentity()
if (!identity) throwProblem(unauthenticated())
trustedIdentity(identity)
const safeIdentity = identityForMutation(identity)
const started = await ctx.runMutation(mutationRef("files:beginIntent"), {
identity: safeIdentity,
input,
} as never)
const plan = await objectStore()
.beginUpload(
{ level: "project", actor: trustedIdentity(identity), ...started.scope },
{ key: started.intent.storageKey, contentType: started.intent.contentType, size: started.intent.declaredSize },
)
.catch((error: unknown) => {
if (error instanceof AppError) throwProblem(error)
throw new Error("the platform could not open a file upload session")
})
const pending = await ctx.runMutation(mutationRef("files:saveUploadSession"), {
identity: safeIdentity,
input,
intent: started.intent,
plan,
} as never)
return { fileId: pending.fileId, parts: pending.parts, expiresAt: pending.expiresAt }
},
})
export const complete = action({
args: { input: v.any() },
handler: async (ctx, { input }) => {
const identity = await ctx.auth.getUserIdentity()
if (!identity) throwProblem(unauthenticated())
trustedIdentity(identity)
const safeIdentity = identityForMutation(identity)
const info = await ctx.runMutation(mutationRef("files:completeInfo"), {
identity: safeIdentity,
input,
} as never)
if (info.alreadyStored) return info.result
const scope = { level: "project" as const, actor: trustedIdentity(identity), ...info.scope }
let stored: { size: number; sha256: string }
try {
stored = await objectStore().completeUpload(scope, {
key: info.storageKey,
uploadId: info.uploadId,
parts: input.parts,
})
} catch (error) {
// A lost response after Blob completed is settled by reading the durable object. Repeating
// finalize is safe, and a missing object preserves the original failure.
const existing = await objectStore()
.get(scope, info.storageKey)
.catch(() => null)
if (!existing) throwProblem(error)
const digest = await digestStream(existing.body, DEFAULT_FILES_POLICY.maxBytes)
stored = { size: digest.size, sha256: digest.sha256 }
}
if (stored.size !== info.declaredSize) {
throwProblem(
validationFailed([{ path: "size", message: "the stored file does not match the size declared for it" }]),
)
}
return ctx.runMutation(mutationRef("files:finalizeUpload"), {
identity: safeIdentity,
input,
fileId: info.fileId,
stored,
} as never)
},
})
export const remove = action({
args: { input: v.any() },
handler: async (ctx, { input }) => {
const identity = await ctx.auth.getUserIdentity()
if (!identity) throwProblem(unauthenticated())
trustedIdentity(identity)
const safeIdentity = identityForMutation(identity)
const marked = await ctx.runMutation(mutationRef("files:markDelete"), {
identity: safeIdentity,
input,
} as never)
try {
const scope = { level: "project" as const, actor: trustedIdentity(identity), ...marked.scope }
await objectStore().delete(scope, marked.storageKey)
await ctx.runMutation(mutationRef("files:finalizeDelete"), {
identity: safeIdentity,
input,
fileId: marked.fileId,
} as never)
return null
} catch (error) {
await ctx.scheduler.runAfter(1_000, actionRef("filesActions:retryDelete"), {
identity: safeIdentity,
input,
attempt: 0,
})
throwProblem(error)
}
},
})
export const retryDelete = internalAction({
args: { identity: identityValidator, input: v.any(), attempt: v.number() },
handler: async (ctx, { identity, input, attempt }) => {
try {
const marked = await ctx.runMutation(mutationRef("files:markDelete"), { identity, input } as never)
const scope = { level: "project" as const, actor: trustedIdentity(identity), ...marked.scope }
await objectStore().delete(scope, marked.storageKey)
await ctx.runMutation(mutationRef("files:finalizeDelete"), {
identity,
input,
fileId: marked.fileId,
} as never)
} catch (error) {
if (attempt < 8) {
await ctx.scheduler.runAfter(Math.min(300_000, 1_000 * 2 ** attempt), actionRef("filesActions:retryDelete"), {
identity,
input,
attempt: attempt + 1,
})
}
throw error
}
},
})
export const cleanupDelete = internalAction({
args: {
orgId: v.string(),
workspaceId: v.string(),
projectId: v.string(),
fileId: v.string(),
attempt: v.number(),
},
handler: async (ctx, args) => {
try {
const { attempt, ...ids } = args
const info = await ctx.runMutation(mutationRef("files:systemDeleteInfo"), ids as never)
if (!info) return
const scope = {
level: "project" as const,
actor: systemActor("files.convex-recovery"),
orgId: args.orgId,
workspaceId: args.workspaceId,
projectId: args.projectId,
}
await objectStore().delete(scope, info.storageKey)
await ctx.runMutation(mutationRef("files:systemFinalizeDelete"), ids as never)
} catch (error) {
if (args.attempt < 8) {
await ctx.scheduler.runAfter(
Math.min(300_000, 1_000 * 2 ** args.attempt),
actionRef("filesActions:cleanupDelete"),
{ ...args, attempt: args.attempt + 1 },
)
}
throw error
}
},
})
/** Reconcile an upload whose request may have crashed at any boundary after its row was created. */
export const recoverUpload = internalAction({
args: {
orgId: v.string(),
workspaceId: v.string(),
projectId: v.string(),
fileId: v.string(),
attempt: v.number(),
},
handler: async (ctx, args) => {
const { attempt, ...ids } = args
try {
const info = await ctx.runMutation(mutationRef("files:systemPendingInfo"), ids as never)
if (!info) return
const scope = {
level: "project" as const,
actor: systemActor("files.convex-recovery"),
orgId: args.orgId,
workspaceId: args.workspaceId,
projectId: args.projectId,
}
const stored = await objectStore().get(scope, info.storageKey)
if (!stored || info.declaredSize === null) {
await ctx.runMutation(mutationRef("files:systemAbandonUpload"), ids as never)
await ctx.scheduler.runAfter(0, actionRef("filesActions:cleanupDelete"), {
...ids,
attempt: 0,
})
return
}
let digest: { size: number; sha256: string }
try {
digest = await digestStream(stored.body, DEFAULT_FILES_POLICY.maxBytes)
} catch (error) {
if (!(error instanceof StreamTooLarge)) throw error
await ctx.runMutation(mutationRef("files:systemAbandonUpload"), ids as never)
await ctx.scheduler.runAfter(0, actionRef("filesActions:cleanupDelete"), {
...ids,
attempt: 0,
})
return
}
if (digest.size !== info.declaredSize) {
await ctx.runMutation(mutationRef("files:systemAbandonUpload"), ids as never)
await ctx.scheduler.runAfter(0, actionRef("filesActions:cleanupDelete"), {
...ids,
attempt: 0,
})
return
}
await ctx.runMutation(mutationRef("files:systemFinalizeUpload"), {
...ids,
stored: digest,
} as never)
} catch (error) {
if (attempt < 8) {
await ctx.scheduler.runAfter(Math.min(300_000, 1_000 * 2 ** attempt), actionRef("filesActions:recoverUpload"), {
...args,
attempt: attempt + 1,
})
}
throw error
}
},
})