/** * Bytes backed by an ArrayBuffer this process owns. * * Spelled out rather than left as a bare `Uint8Array` because Node's readFileSync and * Bun's gzipSync return views over pooled or shared buffers, and passing one of those to * `new Response(...)` or letting it outlive the call is how a view ends up describing * bytes that belong to something else. */ export type OwnedBytes = Uint8Array; /** Copy any byte view into a buffer of its own. */ export declare function ownBytes(view: Uint8Array | ArrayBuffer): OwnedBytes; export interface SkillBundleEntry { path: string; bytes: OwnedBytes; mode: number; } export interface PackedSkillBundle { /** The gzipped tar. This is what is uploaded and what the digest is taken over. */ bytes: OwnedBytes; /** Lowercase hex sha-256 of `bytes`. The skill's content address. */ sha256: string; fileCount: number; /** Total uncompressed size of the packed files, for a size limit that means something. */ unpackedByteSize: number; /** Relative paths included, sorted. Surfaced so `push --dry-run` can show them. */ paths: string[]; } export interface PackSkillBundleOptions { /** Reject before compressing when the sources exceed this. 0 disables the check. */ maxUnpackedBytes?: number; } export declare function sha256Hex(bytes: Uint8Array): string; /** Collect the files a bundle would contain, in archive order. */ export declare function collectSkillBundleEntries(dir: string): SkillBundleEntry[]; export declare function packSkillBundle(dir: string, options?: PackSkillBundleOptions): PackedSkillBundle; /** Legacy synchronous reader for trusted inputs. Unbounded; use inspectSkillBundle for uploads. */ export declare function unpackSkillBundle(bundle: Uint8Array): SkillBundleEntry[]; /** Hard ceilings. Callers may tighten these limits, never disable or raise them. */ export interface SkillBundleInspectionLimits { compressedBytes: number; decompressedBytes: number; entries: number; fileBytes: number; pathBytes: number; timeoutMs: number; } export declare const SKILL_BUNDLE_INSPECTION_LIMITS: Readonly; export interface InspectSkillBundleOptions { limits?: Partial; signal?: AbortSignal; } export interface InspectedSkillBundle { /** Each body has its own buffer. No path has been written or executed. */ entries: SkillBundleEntry[]; /** Hash of the complete owned compressed snapshot, not of extracted files. */ sha256: string; compressedByteSize: number; /** Includes headers, body padding, terminators and trailing zero padding. */ decompressedByteSize: number; unpackedByteSize: number; fileCount: number; } export type SkillBundleInspectionErrorCode = "BUNDLE_INVALID" | "BUNDLE_LIMIT" | "BUNDLE_ABORTED" | "BUNDLE_TIMEOUT"; export declare class SkillBundleInspectionError extends Error { readonly code: SkillBundleInspectionErrorCode; constructor(code: SkillBundleInspectionErrorCode, message: string); } /** * Inspect a bounded gzipped ustar bundle without filesystem writes or execution. * Resolves only after the entire gzip stream and tar structure validate. Returned * entries are NOT permission to execute code or safely extract through existing * filesystem symlinks; consumers still own storage, authorization and isolation. * * Only regular files, canonical relative slash paths (100 UTF-8 bytes maximum), * no prefix/extension/link records, and modes without special bits are supported. * Paths are unique under NFC + lowercase and cannot collide with a file ancestor. * Noncanonical dot/empty/backslash segments are refused, not silently rewritten. * Two zero tar blocks are required, followed only by complete zero padding blocks. * Gzip members are decoded as one stream: an empty extra member is harmless, but * a second nonempty archive after the tar terminator is rejected. * * The decoder has bounded stream buffers; decompressed bytes are counted before * parsing or retaining each chunk, so expansion is stopped during decompression. * Memory is bounded by the compressed snapshot, accepted file bodies, and stream * buffers. The deadline covers copying, hashing, decoding and parsing, with both * a timer and monotonic checks. Decoding yields to timers every 256 KiB so Bun's * stream microtasks cannot starve a caller's scheduled abort. No partial entries * escape on any failure. */ export declare function inspectSkillBundle(bundle: Uint8Array, options?: InspectSkillBundleOptions): Promise;