/** * bundle-writer.ts — writeBundle(loopOrBeeId, outPath) (spec §7A.2). * * M7 / spec §7A.2 / §10 RL-9. * * Writes a `peaks.bundle/1` tar.gz that captures: * * - One anchor asset: a loop_release (kind='loop') OR a single * bee_release (kind='bee'). A loop bundle additionally carries * related_bee_releases (all loop↔bee relations resolved) and * loop_bee_relations rows. * - All evidence_briefs (crystallization_event rows) that * reference the anchor (created_loop_release_id / * updated_loop_release_id / created_bee_release_id / * updated_bee_release_id). * * Hard rules enforced at THIS layer: * * - The source release MUST have `shareable !== false`. A * `shareable=false` source THROWS * `SHARE_BUNDLE_NOT_SHAREABLE` BEFORE the tarball is written. * The CLI layer is the canonical gate (per spec §7A.2 / * §10 RL-9); the writer enforces the same gate at the * service boundary for defense in depth. * - The bundle NEVER captures private run_state, the personal * `.peaks/memory/personal/` directory, or raw `state.db` * rows. The exclusion is declared in the manifest's * `exclusion_manifest` field; physically, the writer only * pulls rows from the structured tables. * - The bundle is a tarball whose root contains `manifest.json`, * `relations.json`, `evidence_briefs/*.json`, and * `blobs/` content-addressed blobs (mirroring the * SkillHub release pattern). * * The writer runs `tar -czf -C .` exactly the * same way `release-export.ts` does — same layout, same tar * invocation, so the `bundle-reader.ts` symmetrical side can * extract via `runTar(['-xzf', inPath, '-C', stageDir])` with no * extra plumbing. */ import type { SqliteDatabase } from '../sqlite/sqlite-driver.js'; import { SHARE_BUNDLE_ERROR_CODES, type PeaksBundleKind } from './bundle-types.js'; /** * Inputs to `writeBundle`. `kind` decides which asset table the * `id` resolves against; the writer throws on unknown assets and * on `shareable=false` sources. */ export type WriteBundleArgs = { db: SqliteDatabase; /** Blobs root for content-addressed file lookup. */ blobsDir: string; /** Bundle kind — `loop` or `bee`. */ kind: PeaksBundleKind; /** The asset id — loop id (kebab-case) for `kind:'loop'`, integer bee_release.id for `kind:'bee'`. */ id: string | number; /** Output `.tar.gz` path. */ outPath: string; }; /** * Thrown when the source release has `shareable === false`. The * writer throws BEFORE any tarball work; the CLI layer surfaces * `code = SHARE_BUNDLE_NOT_SHAREABLE`. */ export declare class BundleNotShareableError extends Error { readonly code: typeof SHARE_BUNDLE_ERROR_CODES.NOT_SHAREABLE; constructor(message: string); } /** * Thrown when the writer cannot locate the source asset (loop id * not present in `loop_release`, or bee_release.id not present). * Includes both `LOOP_NOT_FOUND` and `BEE_NOT_FOUND` flavors — * both fall under `BUNDLE_ASSET_NOT_FOUND` for the CLI envelope. */ export declare class BundleAssetNotFoundError extends Error { readonly code: 'BUNDLE_ASSET_NOT_FOUND'; readonly assetKind: PeaksBundleKind; readonly assetId: string | number; constructor(assetKind: PeaksBundleKind, assetId: string | number); } /** * Write a `peaks.bundle/1` tar.gz to `outPath`. The bundle * captures ONE anchor asset (`loop` or `bee`) plus its relations, * supporting bees (loop kind only), and evidence briefs. * * Hard blocks raised at THIS layer: * * - `BundleNotShareableError`: the source has `shareable === false`. * - `BundleAssetNotFoundError`: the source id was not present in the table. * * On success the call returns the resolved asset id and kind; the * CLI layer uses this to render a one-line status. */ export declare function writeBundle(args: WriteBundleArgs): { outPath: string; kind: PeaksBundleKind; assetId: string | number; };