feat: company portability — export/import companies and agents

Add company export, import preview, and import endpoints with manifest-
based bundle format. Includes URL key utilities for agents and projects,
collision detection/rename strategies, and secret requirement tracking.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Forgotten
2026-03-02 09:06:58 -06:00
parent 79a9dffc80
commit cc2c724ad2
10 changed files with 1197 additions and 3 deletions

View File

@@ -0,0 +1,112 @@
import { z } from "zod";
export const portabilityIncludeSchema = z
.object({
company: z.boolean().optional(),
agents: z.boolean().optional(),
})
.partial();
export const portabilitySecretRequirementSchema = z.object({
key: z.string().min(1),
description: z.string().nullable(),
agentSlug: z.string().min(1).nullable(),
providerHint: z.string().nullable(),
});
export const portabilityCompanyManifestEntrySchema = z.object({
path: z.string().min(1),
name: z.string().min(1),
description: z.string().nullable(),
brandColor: z.string().nullable(),
requireBoardApprovalForNewAgents: z.boolean(),
});
export const portabilityAgentManifestEntrySchema = z.object({
slug: z.string().min(1),
name: z.string().min(1),
path: z.string().min(1),
role: z.string().min(1),
title: z.string().nullable(),
icon: z.string().nullable(),
capabilities: z.string().nullable(),
reportsToSlug: z.string().min(1).nullable(),
adapterType: z.string().min(1),
adapterConfig: z.record(z.unknown()),
runtimeConfig: z.record(z.unknown()),
permissions: z.record(z.unknown()),
budgetMonthlyCents: z.number().int().nonnegative(),
metadata: z.record(z.unknown()).nullable(),
});
export const portabilityManifestSchema = z.object({
schemaVersion: z.number().int().positive(),
generatedAt: z.string().datetime(),
source: z
.object({
companyId: z.string().uuid(),
companyName: z.string().min(1),
})
.nullable(),
includes: z.object({
company: z.boolean(),
agents: z.boolean(),
}),
company: portabilityCompanyManifestEntrySchema.nullable(),
agents: z.array(portabilityAgentManifestEntrySchema),
requiredSecrets: z.array(portabilitySecretRequirementSchema).default([]),
});
export const portabilitySourceSchema = z.discriminatedUnion("type", [
z.object({
type: z.literal("inline"),
manifest: portabilityManifestSchema,
files: z.record(z.string()),
}),
z.object({
type: z.literal("url"),
url: z.string().url(),
}),
z.object({
type: z.literal("github"),
url: z.string().url(),
}),
]);
export const portabilityTargetSchema = z.discriminatedUnion("mode", [
z.object({
mode: z.literal("new_company"),
newCompanyName: z.string().min(1).optional().nullable(),
}),
z.object({
mode: z.literal("existing_company"),
companyId: z.string().uuid(),
}),
]);
export const portabilityAgentSelectionSchema = z.union([
z.literal("all"),
z.array(z.string().min(1)),
]);
export const portabilityCollisionStrategySchema = z.enum(["rename", "skip", "replace"]);
export const companyPortabilityExportSchema = z.object({
include: portabilityIncludeSchema.optional(),
});
export type CompanyPortabilityExport = z.infer<typeof companyPortabilityExportSchema>;
export const companyPortabilityPreviewSchema = z.object({
source: portabilitySourceSchema,
include: portabilityIncludeSchema.optional(),
target: portabilityTargetSchema,
agents: portabilityAgentSelectionSchema.optional(),
collisionStrategy: portabilityCollisionStrategySchema.optional(),
});
export type CompanyPortabilityPreview = z.infer<typeof companyPortabilityPreviewSchema>;
export const companyPortabilityImportSchema = companyPortabilityPreviewSchema;
export type CompanyPortabilityImport = z.infer<typeof companyPortabilityImportSchema>;

View File

@@ -112,3 +112,21 @@ export {
type UpdateMemberPermissions,
type UpdateUserCompanyAccess,
} from "./access.js";
export {
portabilityIncludeSchema,
portabilitySecretRequirementSchema,
portabilityCompanyManifestEntrySchema,
portabilityAgentManifestEntrySchema,
portabilityManifestSchema,
portabilitySourceSchema,
portabilityTargetSchema,
portabilityAgentSelectionSchema,
portabilityCollisionStrategySchema,
companyPortabilityExportSchema,
companyPortabilityPreviewSchema,
companyPortabilityImportSchema,
type CompanyPortabilityExport,
type CompanyPortabilityPreview,
type CompanyPortabilityImport,
} from "./company-portability.js";