/**
 * Shared R2 management service — bucket creation + local-asset sync.
 *
 * Lifted out of `src/cli/commands/r2.ts` so the CLI and the HTTP API
 * (`/api/r2/*`) share one implementation. The CLI handlers in r2.ts
 * still own user-facing prompts (clack), spinners, and process.exit;
 * this module just runs the underlying S3 work and emits progress
 * through a callback so each surface can render it natively.
 */
import { S3Client } from '@aws-sdk/client-s3';
export declare function contentType(filePath: string): string;
export declare function walkDir(dir: string): string[];
export interface R2Credentials {
    accountId: string;
    accessKeyId: string;
    secretAccessKey: string;
}
export declare function buildR2Client(c: R2Credentials): S3Client;
export interface CreateBucketResult {
    bucket: string;
    alreadyExists: boolean;
}
/**
 * Create an R2 bucket. Idempotent: BucketAlreadyExists /
 * BucketAlreadyOwnedByYou / 409 are treated as success with
 * alreadyExists=true.
 */
export declare function createR2Bucket(creds: R2Credentials, bucket: string): Promise<CreateBucketResult>;
export interface SyncProgress {
    /** Total files we've discovered to upload. Grows during scan, then stable. */
    totalFiles: number;
    /** Files successfully uploaded so far. */
    uploadedFiles: number;
    /** Markdown / tokens files we've rewritten with the new R2 URLs. */
    rewrittenFiles: number;
    /** Files that failed to upload (e.g. missing referenced asset, network). */
    failedFiles: number;
    /** Current file the worker is uploading. Empty when idle or done. */
    currentFile: string;
    /** Status text — high-level phase. */
    phase: 'scanning' | 'uploading' | 'done' | 'cancelled' | 'error';
    /** Cumulative per-file errors. UI can surface count + recent messages. */
    errors: Array<{
        file: string;
        message: string;
    }>;
}
export interface SyncOptions {
    workspacePath: string;
    repoPaths: string[];
    credentials: R2Credentials;
    bucket: string;
    publicUrl: string;
    /** Called whenever progress changes. Receives a snapshot — caller may mutate. */
    onProgress?: (p: SyncProgress) => void;
    /** Returns true to abort the sync at the next checkpoint. */
    shouldCancel?: () => boolean;
}
export interface SyncResult {
    uploaded: number;
    rewritten: number;
    failed: number;
    cancelled: boolean;
    /** Per-repo lists of markdown files that pass-2 rewrote (absolute paths).
     *  Callers commit these through their git pipeline — the sync itself does
     *  raw writeFileSync, leaving the repos dirty if no one commits. The map
     *  is keyed by the repoPath the caller passed in; empty if pass-2 didn't
     *  touch anything. */
    rewrittenByRepo: Map<string, string[]>;
}
/**
 * Run a full local-asset sync: tokens.json files under
 * .studiograph/apps/*​/theme/ plus markdown references across every
 * repo. Mirrors the legacy CLI command's two-pass shape; the progress
 * callback gets fired after every per-file step so the UI can render
 * smoothly.
 *
 * Cancellation is cooperative — the loop checks `shouldCancel()` between
 * files. In-flight uploads complete (they're per-file Promise.all batches
 * inside each markdown file).
 */
export declare function syncLocalAssetsToR2(opts: SyncOptions): Promise<SyncResult>;
/**
 * Count files under `.studiograph/assets/`. Used by the UI banner to tell
 * the operator there's work to migrate after switching from local to R2.
 * Returns 0 if the directory doesn't exist.
 */
export declare function countLocalAssets(workspacePath: string): number;
