Add secrets infrastructure: DB tables, shared types, env binding model, and migration improvements
Introduce company_secrets and company_secret_versions tables for encrypted secret storage. Add EnvBinding discriminated union (plain vs secret_ref) to replace raw string env values in adapter configs. Add hiddenAt column to issues for soft-hiding. Improve migration system with journal-ordered application and manual fallback when Drizzle migrator can't reconcile history. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,11 +4,25 @@ import {
|
||||
AGENT_ROLES,
|
||||
AGENT_STATUSES,
|
||||
} from "../constants.js";
|
||||
import { envConfigSchema } from "./secret.js";
|
||||
|
||||
export const agentPermissionsSchema = z.object({
|
||||
canCreateAgents: z.boolean().optional().default(false),
|
||||
});
|
||||
|
||||
const adapterConfigSchema = z.record(z.unknown()).superRefine((value, ctx) => {
|
||||
const envValue = value.env;
|
||||
if (envValue === undefined) return;
|
||||
const parsed = envConfigSchema.safeParse(envValue);
|
||||
if (!parsed.success) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "adapterConfig.env must be a map of valid env bindings",
|
||||
path: ["env"],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export const createAgentSchema = z.object({
|
||||
name: z.string().min(1),
|
||||
role: z.enum(AGENT_ROLES).optional().default("general"),
|
||||
@@ -16,7 +30,7 @@ export const createAgentSchema = z.object({
|
||||
reportsTo: z.string().uuid().optional().nullable(),
|
||||
capabilities: z.string().optional().nullable(),
|
||||
adapterType: z.enum(AGENT_ADAPTER_TYPES).optional().default("process"),
|
||||
adapterConfig: z.record(z.unknown()).optional().default({}),
|
||||
adapterConfig: adapterConfigSchema.optional().default({}),
|
||||
runtimeConfig: z.record(z.unknown()).optional().default({}),
|
||||
budgetMonthlyCents: z.number().int().nonnegative().optional().default(0),
|
||||
permissions: agentPermissionsSchema.optional(),
|
||||
|
||||
@@ -63,6 +63,19 @@ export {
|
||||
type AddApprovalComment,
|
||||
} from "./approval.js";
|
||||
|
||||
export {
|
||||
envBindingPlainSchema,
|
||||
envBindingSecretRefSchema,
|
||||
envBindingSchema,
|
||||
envConfigSchema,
|
||||
createSecretSchema,
|
||||
rotateSecretSchema,
|
||||
updateSecretSchema,
|
||||
type CreateSecret,
|
||||
type RotateSecret,
|
||||
type UpdateSecret,
|
||||
} from "./secret.js";
|
||||
|
||||
export {
|
||||
createCostEventSchema,
|
||||
updateBudgetSchema,
|
||||
|
||||
@@ -18,6 +18,7 @@ export type CreateIssue = z.infer<typeof createIssueSchema>;
|
||||
|
||||
export const updateIssueSchema = createIssueSchema.partial().extend({
|
||||
comment: z.string().min(1).optional(),
|
||||
hiddenAt: z.string().datetime().nullable().optional(),
|
||||
});
|
||||
|
||||
export type UpdateIssue = z.infer<typeof updateIssueSchema>;
|
||||
|
||||
47
packages/shared/src/validators/secret.ts
Normal file
47
packages/shared/src/validators/secret.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { z } from "zod";
|
||||
import { SECRET_PROVIDERS } from "../constants.js";
|
||||
|
||||
export const envBindingPlainSchema = z.object({
|
||||
type: z.literal("plain"),
|
||||
value: z.string(),
|
||||
});
|
||||
|
||||
export const envBindingSecretRefSchema = z.object({
|
||||
type: z.literal("secret_ref"),
|
||||
secretId: z.string().uuid(),
|
||||
version: z.union([z.literal("latest"), z.number().int().positive()]).optional(),
|
||||
});
|
||||
|
||||
// Backward-compatible union that accepts legacy inline values.
|
||||
export const envBindingSchema = z.union([
|
||||
z.string(),
|
||||
envBindingPlainSchema,
|
||||
envBindingSecretRefSchema,
|
||||
]);
|
||||
|
||||
export const envConfigSchema = z.record(envBindingSchema);
|
||||
|
||||
export const createSecretSchema = z.object({
|
||||
name: z.string().min(1),
|
||||
provider: z.enum(SECRET_PROVIDERS).optional(),
|
||||
value: z.string().min(1),
|
||||
description: z.string().optional().nullable(),
|
||||
externalRef: z.string().optional().nullable(),
|
||||
});
|
||||
|
||||
export type CreateSecret = z.infer<typeof createSecretSchema>;
|
||||
|
||||
export const rotateSecretSchema = z.object({
|
||||
value: z.string().min(1),
|
||||
externalRef: z.string().optional().nullable(),
|
||||
});
|
||||
|
||||
export type RotateSecret = z.infer<typeof rotateSecretSchema>;
|
||||
|
||||
export const updateSecretSchema = z.object({
|
||||
name: z.string().min(1).optional(),
|
||||
description: z.string().optional().nullable(),
|
||||
externalRef: z.string().optional().nullable(),
|
||||
});
|
||||
|
||||
export type UpdateSecret = z.infer<typeof updateSecretSchema>;
|
||||
Reference in New Issue
Block a user