2026-02-16 13:31:47 -06:00
|
|
|
import { z } from "zod";
|
Expand data model with companies, approvals, costs, and heartbeats
Add new DB schemas: companies, agent_api_keys, approvals, cost_events,
heartbeat_runs, issue_comments. Add corresponding shared types and
validators. Update existing schemas (agents, goals, issues, projects)
with new fields for company association, budgets, and richer metadata.
Generate initial Drizzle migration. Update seed data.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 09:07:22 -06:00
|
|
|
import { PROJECT_STATUSES } from "../constants.js";
|
2026-02-16 13:31:47 -06:00
|
|
|
|
2026-03-10 09:03:31 -05:00
|
|
|
const executionWorkspaceStrategySchema = z
|
|
|
|
|
.object({
|
2026-03-13 17:12:25 -05:00
|
|
|
type: z.enum(["project_primary", "git_worktree", "adapter_managed", "cloud_sandbox"]).optional(),
|
2026-03-10 09:03:31 -05:00
|
|
|
baseRef: z.string().optional().nullable(),
|
|
|
|
|
branchTemplate: z.string().optional().nullable(),
|
|
|
|
|
worktreeParentDir: z.string().optional().nullable(),
|
2026-03-10 12:42:36 -05:00
|
|
|
provisionCommand: z.string().optional().nullable(),
|
|
|
|
|
teardownCommand: z.string().optional().nullable(),
|
2026-03-10 09:03:31 -05:00
|
|
|
})
|
|
|
|
|
.strict();
|
|
|
|
|
|
|
|
|
|
export const projectExecutionWorkspacePolicySchema = z
|
|
|
|
|
.object({
|
|
|
|
|
enabled: z.boolean(),
|
2026-03-13 17:12:25 -05:00
|
|
|
defaultMode: z.enum(["shared_workspace", "isolated_workspace", "operator_branch", "adapter_default"]).optional(),
|
2026-03-10 09:03:31 -05:00
|
|
|
allowIssueOverride: z.boolean().optional(),
|
2026-03-13 17:12:25 -05:00
|
|
|
defaultProjectWorkspaceId: z.string().uuid().optional().nullable(),
|
2026-03-10 09:03:31 -05:00
|
|
|
workspaceStrategy: executionWorkspaceStrategySchema.optional().nullable(),
|
|
|
|
|
workspaceRuntime: z.record(z.unknown()).optional().nullable(),
|
|
|
|
|
branchPolicy: z.record(z.unknown()).optional().nullable(),
|
|
|
|
|
pullRequestPolicy: z.record(z.unknown()).optional().nullable(),
|
2026-03-13 17:12:25 -05:00
|
|
|
runtimePolicy: z.record(z.unknown()).optional().nullable(),
|
2026-03-10 09:03:31 -05:00
|
|
|
cleanupPolicy: z.record(z.unknown()).optional().nullable(),
|
|
|
|
|
})
|
|
|
|
|
.strict();
|
|
|
|
|
|
2026-03-13 17:12:25 -05:00
|
|
|
const projectWorkspaceSourceTypeSchema = z.enum(["local_path", "git_repo", "remote_managed", "non_git_path"]);
|
|
|
|
|
const projectWorkspaceVisibilitySchema = z.enum(["default", "advanced"]);
|
|
|
|
|
|
2026-02-25 21:35:33 -06:00
|
|
|
const projectWorkspaceFields = {
|
|
|
|
|
name: z.string().min(1).optional(),
|
2026-03-13 17:12:25 -05:00
|
|
|
sourceType: projectWorkspaceSourceTypeSchema.optional(),
|
2026-02-25 21:35:33 -06:00
|
|
|
cwd: z.string().min(1).optional().nullable(),
|
2026-02-25 08:38:46 -06:00
|
|
|
repoUrl: z.string().url().optional().nullable(),
|
|
|
|
|
repoRef: z.string().optional().nullable(),
|
2026-03-13 17:12:25 -05:00
|
|
|
defaultRef: z.string().optional().nullable(),
|
|
|
|
|
visibility: projectWorkspaceVisibilitySchema.optional(),
|
|
|
|
|
setupCommand: z.string().optional().nullable(),
|
|
|
|
|
cleanupCommand: z.string().optional().nullable(),
|
|
|
|
|
remoteProvider: z.string().optional().nullable(),
|
|
|
|
|
remoteWorkspaceRef: z.string().optional().nullable(),
|
|
|
|
|
sharedWorkspaceKey: z.string().optional().nullable(),
|
2026-02-25 08:38:46 -06:00
|
|
|
metadata: z.record(z.unknown()).optional().nullable(),
|
2026-02-25 21:35:33 -06:00
|
|
|
};
|
|
|
|
|
|
2026-03-13 17:12:25 -05:00
|
|
|
function validateProjectWorkspace(value: Record<string, unknown>, ctx: z.RefinementCtx) {
|
|
|
|
|
const sourceType = value.sourceType ?? "local_path";
|
2026-02-25 21:35:33 -06:00
|
|
|
const hasCwd = typeof value.cwd === "string" && value.cwd.trim().length > 0;
|
|
|
|
|
const hasRepo = typeof value.repoUrl === "string" && value.repoUrl.trim().length > 0;
|
2026-03-13 17:12:25 -05:00
|
|
|
const hasRemoteRef = typeof value.remoteWorkspaceRef === "string" && value.remoteWorkspaceRef.trim().length > 0;
|
|
|
|
|
|
|
|
|
|
if (sourceType === "remote_managed") {
|
|
|
|
|
if (!hasRemoteRef && !hasRepo) {
|
|
|
|
|
ctx.addIssue({
|
|
|
|
|
code: z.ZodIssueCode.custom,
|
|
|
|
|
message: "Remote-managed workspace requires remoteWorkspaceRef or repoUrl.",
|
|
|
|
|
path: ["remoteWorkspaceRef"],
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-25 21:35:33 -06:00
|
|
|
if (!hasCwd && !hasRepo) {
|
|
|
|
|
ctx.addIssue({
|
|
|
|
|
code: z.ZodIssueCode.custom,
|
|
|
|
|
message: "Workspace requires at least one of cwd or repoUrl.",
|
|
|
|
|
path: ["cwd"],
|
|
|
|
|
});
|
|
|
|
|
}
|
2026-03-13 17:12:25 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const createProjectWorkspaceSchema = z.object({
|
|
|
|
|
...projectWorkspaceFields,
|
|
|
|
|
isPrimary: z.boolean().optional().default(false),
|
|
|
|
|
}).superRefine(validateProjectWorkspace);
|
2026-02-25 08:38:46 -06:00
|
|
|
|
|
|
|
|
export type CreateProjectWorkspace = z.infer<typeof createProjectWorkspaceSchema>;
|
|
|
|
|
|
2026-02-25 21:35:33 -06:00
|
|
|
export const updateProjectWorkspaceSchema = z.object({
|
|
|
|
|
...projectWorkspaceFields,
|
|
|
|
|
isPrimary: z.boolean().optional(),
|
|
|
|
|
}).partial();
|
2026-02-25 08:38:46 -06:00
|
|
|
|
|
|
|
|
export type UpdateProjectWorkspace = z.infer<typeof updateProjectWorkspaceSchema>;
|
2026-03-02 13:32:15 -06:00
|
|
|
|
|
|
|
|
const projectFields = {
|
|
|
|
|
/** @deprecated Use goalIds instead */
|
|
|
|
|
goalId: z.string().uuid().optional().nullable(),
|
|
|
|
|
goalIds: z.array(z.string().uuid()).optional(),
|
|
|
|
|
name: z.string().min(1),
|
|
|
|
|
description: z.string().optional().nullable(),
|
|
|
|
|
status: z.enum(PROJECT_STATUSES).optional().default("backlog"),
|
|
|
|
|
leadAgentId: z.string().uuid().optional().nullable(),
|
|
|
|
|
targetDate: z.string().optional().nullable(),
|
|
|
|
|
color: z.string().optional().nullable(),
|
2026-03-10 09:03:31 -05:00
|
|
|
executionWorkspacePolicy: projectExecutionWorkspacePolicySchema.optional().nullable(),
|
2026-03-02 13:32:15 -06:00
|
|
|
archivedAt: z.string().datetime().optional().nullable(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const createProjectSchema = z.object({
|
|
|
|
|
...projectFields,
|
|
|
|
|
workspace: createProjectWorkspaceSchema.optional(),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
export type CreateProject = z.infer<typeof createProjectSchema>;
|
|
|
|
|
|
|
|
|
|
export const updateProjectSchema = z.object(projectFields).partial();
|
|
|
|
|
|
|
|
|
|
export type UpdateProject = z.infer<typeof updateProjectSchema>;
|
2026-03-10 09:03:31 -05:00
|
|
|
|
|
|
|
|
export type ProjectExecutionWorkspacePolicy = z.infer<typeof projectExecutionWorkspacePolicySchema>;
|