import type { Loop, LoopRun, WorkflowSpec } from "../types.js"; import type { Store, StoreMigrationChecks } from "./store.js"; export declare const LOOPS_MIGRATION_SCHEMA = "open-loops.migration/v1"; export declare const LOOPS_CONTROL_PLANE_PUSH_MANIFEST_SCHEMA = "open-loops.self-hosted-push-manifest/v1"; export type LoopsMigrationResource = "workflow" | "loop" | "run" | "remote"; export type LoopsMigrationAction = "insert" | "update" | "skip" | "conflict" | "blocked"; export interface LoopsMigrationBundle { schema: typeof LOOPS_MIGRATION_SCHEMA; packageVersion: string; exportedAt: string; source: { backend: "sqlite"; schemaVersion: number; hostname: string; }; checks: StoreMigrationChecks; importable: boolean; counts: { workflows: number; loops: number; runs: number; }; data: { workflows: WorkflowSpec[]; loops: Loop[]; runs: LoopRun[]; }; blockers: LoopsMigrationPlanRow[]; warnings: string[]; hash: string; } export interface ExportLoopsMigrationOptions { includeRuns?: boolean; } export interface ImportLoopsMigrationOptions { includeRuns?: boolean; replace?: boolean; dryRun?: boolean; } export interface LoopsMigrationPlanRow { resource: LoopsMigrationResource; id: string; name?: string; action: LoopsMigrationAction; reason?: string; incomingHash?: string; currentHash?: string; } export interface LoopsMigrationPlanSummary { dryRun: boolean; replace: boolean; importable: boolean; workflows: number; loops: number; runs: number; insert: number; update: number; skip: number; conflict: number; blocked: number; } export interface LoopsMigrationPlan { schema: typeof LOOPS_MIGRATION_SCHEMA; operation: "import" | "push" | "pull" | "migrate"; dryRun: boolean; replace: boolean; importable: boolean; summary: LoopsMigrationPlanSummary; rows: LoopsMigrationPlanRow[]; warnings: string[]; manifest?: ControlPlanePushManifest; } export interface ApplyLoopsMigrationResult { plan: LoopsMigrationPlan; applied: { workflows: number; loops: number; runs: number; }; } export interface ControlPlanePlanOptions { operation: "push" | "pull" | "migrate"; apiUrl?: string; apiKey?: string; timeoutMs?: number; fetchImpl?: typeof fetch; env?: NodeJS.ProcessEnv; includeRuns?: boolean; replace?: boolean; } export declare function migrationHash(value: unknown): string; export declare function exportLoopsMigrationBundle(store: Store, opts?: ExportLoopsMigrationOptions): LoopsMigrationBundle; export declare function validateLoopsMigrationBundle(value: unknown): LoopsMigrationBundle; export declare function buildImportMigrationPlan(store: Store, bundle: LoopsMigrationBundle, opts?: ImportLoopsMigrationOptions): LoopsMigrationPlan; export declare function applyImportMigrationBundle(store: Store, bundle: LoopsMigrationBundle, opts?: ImportLoopsMigrationOptions): ApplyLoopsMigrationResult; export declare function buildControlPlaneMigrationPlan(store: Store, opts: ControlPlanePlanOptions): Promise; export interface ControlPlanePushOptions { apiUrl?: string; apiKey?: string; timeoutMs?: number; includeRuns?: boolean; replace?: boolean; fetchImpl?: typeof fetch; env?: NodeJS.ProcessEnv; /** Max rows per workflow/loop batch (default 200). */ batchRows?: number; /** Approx max JSON bytes per run batch (default 4 MiB). */ runBatchBytes?: number; onProgress?: (event: { phase: "workflows" | "loops" | "runs"; sent: number; requests: number; }) => void; } export interface ControlPlanePushResult { ok: boolean; apiUrl: string; applied: { workflows: number; loops: number; runs: number; }; skipped: { runningRuns: number; orphanRuns: number; }; requests: number; manifest: ControlPlanePushManifest; } export interface ControlPlanePushManifest { schema: typeof LOOPS_CONTROL_PLANE_PUSH_MANIFEST_SCHEMA; generatedAt: string; apiUrl?: string; dryRun: boolean; replace: boolean; includeRuns: boolean; safety: { forcedLoopStatus: "paused"; clearedLoopRunPointers: true; forcedWorkflowStatus: "archived"; resumesLoops: false; }; counts: { source: { workflows: number; loops: number; runs: number; }; remoteBefore: { workflows?: number; loops?: number; runs?: number; }; remoteAfter?: { workflows?: number; loops?: number; runs?: number; }; plan: LoopsMigrationPlanSummary; applied?: { workflows: number; loops: number; runs: number; }; skipped?: { runningRuns: number; orphanRuns: number; }; requests?: number; }; missingIds: { workflows: string[]; loops: string[]; runs: string[]; }; existingIds: { workflows: string[]; loops: string[]; runs: string[]; }; unsafe: { blocked: LoopsMigrationPlanRow[]; conflicts: LoopsMigrationPlanRow[]; unsupported: string[]; warnings: string[]; }; rollback: { notes: string[]; commands: string[]; }; } /** * Apply a local-to-control-plane backfill through the control plane's id-preserving * `/v1/import` endpoint. Rows are pushed FK-safe (workflows, loops, then runs). * Runs are streamed in bounded pages so a busy host's multi-hundred-MB run * history never loads into memory at once; volatile `running` runs and orphan * runs (whose parent loop is absent) are dropped and counted. Idempotent: the * endpoint upserts by id, so re-running never duplicates rows. */ export declare function applyControlPlanePush(store: Store, opts: ControlPlanePushOptions): Promise; export declare function publicMigrationBundle(bundle: LoopsMigrationBundle): Record; export declare function controlPlaneSummary(env?: NodeJS.ProcessEnv): Record;