feat(media-use): core infrastructure — manifest, cache, adopt, probe (#1682)

* feat(media-use): core infrastructure — manifest, cache, adopt, probe

Foundation for media-use — the media resolution layer for HyperFrames.

- manifest.mjs: JSONL read/write/find for .media/manifest.jsonl
- index-gen.mjs: regenerate agent-readable index.md from manifest
- cache.mjs: content-addressed global cache at ~/.media/ (SHA-256, sentinel)
- freeze.mjs: download URL or copy local file to .media/
- probe.mjs: extract duration/dimensions via ffprobe
- adopt.mjs: scan assets/ directory, register existing files with metadata
- 19 passing tests (manifest round-trip, cache, promote, index generation)

* fix(media-use): oxfmt formatting + cap freeze download size

Format adopt/cache/probe/manifest.test (CI oxfmt --check gate).
Cap freezeUrl downloads at 256MB so a hostile/runaway URL can't fill
the disk (addresses CodeQL #670: network data written to file).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Miguel Ángel
2026-06-24 20:28:38 -04:00
committed by GitHub
co-authored by Claude Opus 4.8
parent eee376cb5e
commit 73f6d3e5be
8 changed files with 736 additions and 0 deletions
+36
View File
@@ -0,0 +1,36 @@
import { execSync } from "node:child_process";
import { extname } from "node:path";
const IMAGE_EXT = new Set([".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg", ".ico"]);
export function probe(filePath) {
const ext = extname(filePath).toLowerCase();
if (ext === ".svg") return { width: null, height: null, duration: null, codec: "svg" };
try {
const raw = execSync(
`ffprobe -v quiet -print_format json -show_format -show_streams "${filePath}"`,
{ encoding: "utf8", timeout: 5000 },
);
const info = JSON.parse(raw);
const stream = info.streams?.[0];
const format = info.format;
const isImage = IMAGE_EXT.has(ext);
const duration = isImage
? null
: parseFloat(format?.duration) || parseFloat(stream?.duration) || null;
const width = parseInt(stream?.width, 10) || null;
const height = parseInt(stream?.height, 10) || null;
const codec = stream?.codec_name || null;
return {
duration: duration != null ? Math.round(duration * 10) / 10 : null,
width,
height,
codec,
};
} catch {
return { duration: null, width: null, height: null, codec: null };
}
}