feat(import-export): complete company portability — secrets export/import and env round-tripping

Adds opt-in secret export/import: secret values are resolved (and optionally
decrypted) into the portability manifest, and re-created with conflict
handling on import. Fixes env round-tripping so both secret_ref and plain
bindings survive export/import cycles.
This commit is contained in:
2026-05-01 08:18:50 -04:00
parent 3494e84a29
commit e8579d5c66
8 changed files with 579 additions and 21 deletions
+1
View File
@@ -473,6 +473,7 @@ export type {
CompanyPortabilityImportRequest,
CompanyPortabilityImportResult,
CompanyPortabilityExportRequest,
CompanyPortabilitySecretEntry,
EnvBinding,
AgentEnvConfig,
CompanySecret,
@@ -1,4 +1,4 @@
import type { AgentEnvConfig } from "./secrets.js";
import type { AgentEnvConfig, SecretProvider } from "./secrets.js";
import type { RoutineVariable } from "./routine.js";
export interface CompanyPortabilityInclude {
@@ -18,6 +18,10 @@ export interface CompanyPortabilityEnvInput {
requirement: "required" | "optional";
defaultValue: string | null;
portability: "portable" | "system_dependent";
secretName?: string | null;
secretProvider?: string | null;
/** Binding type — stored in extension.inputs.env but not in the manifest type itself */
type?: "secret_ref" | "plain";
}
export type CompanyPortabilityFileEntry =
@@ -166,6 +170,15 @@ export interface CompanyPortabilityManifest {
projects: CompanyPortabilityProjectManifestEntry[];
issues: CompanyPortabilityIssueManifestEntry[];
envInputs: CompanyPortabilityEnvInput[];
secrets?: CompanyPortabilitySecretEntry[];
}
export interface CompanyPortabilitySecretEntry {
name: string;
provider: SecretProvider;
description: string | null;
latestVersion: number;
currentValue: string;
}
export interface CompanyPortabilityExportResult {
@@ -317,4 +330,5 @@ export interface CompanyPortabilityExportRequest {
selectedFiles?: string[];
expandReferencedSkills?: boolean;
sidebarOrder?: Partial<CompanyPortabilitySidebarOrder>;
includeSecrets?: boolean;
}
+1
View File
@@ -304,6 +304,7 @@ export type {
CompanyPortabilityImportRequest,
CompanyPortabilityImportResult,
CompanyPortabilityExportRequest,
CompanyPortabilitySecretEntry,
} from "./company-portability.js";
export type {
JsonSchema,
@@ -21,6 +21,9 @@ export const portabilityEnvInputSchema = z.object({
requirement: z.enum(["required", "optional"]),
defaultValue: z.string().nullable(),
portability: z.enum(["portable", "system_dependent"]),
secretName: z.string().min(1).nullable().optional(),
secretProvider: z.string().min(1).nullable().optional(),
type: z.enum(["secret_ref", "plain"]).optional(),
});
export const portabilityFileEntrySchema = z.union([
@@ -175,6 +178,13 @@ export const portabilityManifestSchema = z.object({
projects: z.array(portabilityProjectManifestEntrySchema).default([]),
issues: z.array(portabilityIssueManifestEntrySchema).default([]),
envInputs: z.array(portabilityEnvInputSchema).default([]),
secrets: z.array(z.object({
name: z.string().min(1),
provider: z.string().min(1),
description: z.string().nullable(),
latestVersion: z.number().int().nonnegative(),
currentValue: z.string(),
})).optional(),
});
export const portabilitySourceSchema = z.discriminatedUnion("type", [
@@ -217,6 +227,7 @@ export const companyPortabilityExportSchema = z.object({
selectedFiles: z.array(z.string().min(1)).optional(),
expandReferencedSkills: z.boolean().optional(),
sidebarOrder: portabilitySidebarOrderSchema.partial().optional(),
includeSecrets: z.boolean().optional(),
});
export type CompanyPortabilityExport = z.infer<typeof companyPortabilityExportSchema>;