dsh-hyperframes 0.3.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +4 -3
- package/README.md +5 -4
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -0
- package/package.json +2 -2
- package/skills/embedded-captions/SKILL.md +28 -3
- package/skills/embedded-captions/references/aesthetic-principles.md +2 -3
- package/skills/embedded-captions/references/rail.md +1 -1
- package/skills/embedded-captions/scripts/check-occlusion.cjs +4 -1
- package/skills/embedded-captions/scripts/check-overflow.cjs +1 -0
- package/skills/embedded-captions/scripts/check-rail-climax.cjs +5 -1
- package/skills/embedded-captions/scripts/fixtures/heroless/theme.json +11 -0
- package/skills/embedded-captions/scripts/hf-cli.cjs +33 -0
- package/skills/embedded-captions/scripts/make-theme.cjs +95 -57
- package/skills/embedded-captions/scripts/make-theme.test.mjs +131 -0
- package/skills/embedded-captions/scripts/matte.cjs +1 -13
- package/skills/embedded-captions/scripts/measure-layout.cjs +2 -1
- package/skills/embedded-captions/scripts/preview-frames.cjs +39 -11
- package/skills/embedded-captions/scripts/preview-frames.test.mjs +24 -0
- package/skills/embedded-captions/scripts/render-and-composite.sh +8 -52
- package/skills/embedded-captions/scripts/safe-zones.cjs +4 -1
- package/skills/embedded-captions/scripts/transcribe.cjs +5 -14
- package/skills/embedded-captions/scripts/transcribe.test.mjs +41 -0
- package/skills/faceless-explainer/SKILL.md +20 -16
- package/skills/faceless-explainer/references/story-design.md +1 -1
- package/skills/faceless-explainer/scripts/assemble-index.mjs +6 -4
- package/skills/faceless-explainer/scripts/assemble-index.test.mjs +57 -2
- package/skills/faceless-explainer/scripts/audio.mjs +2 -0
- package/skills/faceless-explainer/scripts/build-frame.mjs +6 -3
- package/skills/faceless-explainer/scripts/captions.mjs +7 -4
- package/skills/faceless-explainer/scripts/frame-packets.mjs +4 -4
- package/skills/faceless-explainer/scripts/lib/bgm-volume.mjs +4 -0
- package/skills/faceless-explainer/scripts/lib/captured-fonts.mjs +58 -0
- package/skills/{hyperframes-core → faceless-explainer}/scripts/lib/frame-packets-core.mjs +5 -23
- package/skills/faceless-explainer/scripts/lib/storyboard.mjs +1 -1
- package/skills/faceless-explainer/scripts/lib/transitions.json +3 -3
- package/skills/faceless-explainer/scripts/transitions.mjs +1 -0
- package/skills/faceless-explainer/scripts/transitions.test.mjs +2 -0
- package/skills/faceless-explainer/sub-agents/frame-worker.md +1 -1
- package/skills/figma/SKILL.md +2 -0
- package/skills/general-video/SKILL.md +13 -11
- package/skills/general-video/scripts/frame-packets.mjs +4 -4
- package/skills/general-video/scripts/lib/frame-packets-core.mjs +194 -0
- package/skills/general-video/sub-agents/frame-worker.md +1 -1
- package/skills/hyperframes/SKILL.md +40 -30
- package/skills/{hyperframes-core → hyperframes}/references/brief-contract.md +15 -15
- package/skills/{hyperframes-core → hyperframes}/references/brief-format.md +8 -8
- package/skills/hyperframes/references/capability-menu.md +2 -2
- package/skills/{hyperframes-core → hyperframes}/references/frame-worker-core.md +1 -1
- package/skills/hyperframes/references/intent-interview.md +16 -16
- package/skills/hyperframes/references/plugin-installation.md +43 -0
- package/skills/{hyperframes-core → hyperframes}/references/production-loop.md +11 -11
- package/skills/hyperframes/references/review-loop.md +37 -0
- package/skills/hyperframes/references/route-briefs.md +1 -1
- package/skills/hyperframes/references/routes/motion-graphics.md +1 -1
- package/skills/{hyperframes-core → hyperframes}/references/script-format.md +3 -3
- package/skills/hyperframes/references/skill-lifecycle.md +2 -0
- package/skills/{hyperframes-core → hyperframes}/references/storyboard-format.md +3 -34
- package/skills/hyperframes/scripts/lib/frame-packets-core.mjs +194 -0
- package/skills/hyperframes/scripts/plugin-cli.mjs +88 -0
- package/skills/hyperframes/scripts/plugin-cli.test.mjs +117 -0
- package/skills/hyperframes-animation/SKILL.md +5 -1
- package/skills/hyperframes-animation/adapters/gsap.md +1 -1
- package/skills/hyperframes-animation/adapters/lottie.md +15 -2
- package/skills/hyperframes-animation/adapters/three.md +14 -0
- package/skills/hyperframes-animation/blueprints/titlecard-reveal.md +1 -1
- package/skills/hyperframes-animation/references/motion-blur.md +133 -0
- package/skills/hyperframes-animation/rules/cursor-click-ripple.md +1 -1
- package/skills/hyperframes-animation/rules/dynamic-content-sequencing.md +1 -1
- package/skills/hyperframes-animation/rules/hacker-flip-3d.md +1 -1
- package/skills/hyperframes-animation/rules/kinetic-beat-slam.md +2 -2
- package/skills/hyperframes-animation/rules/motion-blur-streak.md +1 -1
- package/skills/hyperframes-animation/rules/vertical-spring-ticker.md +1 -1
- package/skills/hyperframes-animation/scripts/package-loader.mjs +47 -16
- package/skills/hyperframes-animation/scripts/package-loader.test.mjs +52 -0
- package/skills/hyperframes-animation/techniques.md +1 -1
- package/skills/hyperframes-animation/transitions/TRANSITION-REGISTRY.md +3 -3
- package/skills/hyperframes-animation/transitions/css-push.md +4 -4
- package/skills/hyperframes-audio/SKILL.md +30 -22
- package/skills/hyperframes-audio/scripts/carve.mjs +6 -3
- package/skills/hyperframes-audio/scripts/carve.test.mjs +16 -1
- package/skills/hyperframes-cli/SKILL.md +44 -35
- package/skills/hyperframes-cli/references/init-and-scaffold.md +8 -6
- package/skills/hyperframes-cli/references/lint-validate-inspect.md +3 -3
- package/skills/hyperframes-cli/references/preview-render.md +35 -32
- package/skills/hyperframes-cli/references/upgrade-info-misc.md +50 -1
- package/skills/hyperframes-core/SKILL.md +30 -24
- package/skills/hyperframes-core/references/composition-patterns.md +3 -42
- package/skills/hyperframes-core/references/creator-editing-recipes.md +131 -7
- package/skills/hyperframes-core/references/data-attributes.md +11 -11
- package/skills/hyperframes-core/references/determinism-rules.md +2 -19
- package/skills/hyperframes-core/references/full-screen-motion.md +0 -9
- package/skills/hyperframes-core/references/minimal-composition.md +2 -2
- package/skills/hyperframes-core/references/sub-compositions.md +3 -66
- package/skills/hyperframes-core/references/tailwind.md +1 -20
- package/skills/hyperframes-core/references/tracks-and-clips.md +2 -6
- package/skills/hyperframes-core/references/variables-and-media.md +3 -3
- package/skills/hyperframes-creative/SKILL.md +2 -0
- package/skills/hyperframes-creative/references/house-style.md +15 -15
- package/skills/hyperframes-creative/references/story-spine.md +3 -1
- package/skills/hyperframes-creative/references/storyboard-recipe.md +57 -0
- package/skills/hyperframes-creative/references/typography.md +1 -1
- package/skills/hyperframes-creative/scripts/package-loader.mjs +47 -16
- package/skills/hyperframes-creative/scripts/package-loader.test.mjs +52 -0
- package/skills/hyperframes-keyframes/SKILL.md +4 -2
- package/skills/hyperframes-registry/SKILL.md +3 -1
- package/skills/hyperframes-registry/references/contributing.md +8 -2
- package/skills/hyperframes-registry/references/discovery.md +20 -34
- package/skills/hyperframes-studio/SKILL.md +77 -0
- package/skills/media-use/SKILL.md +14 -12
- package/skills/media-use/audio/references/captions/authoring.md +4 -0
- package/skills/media-use/audio/references/remove-background.md +1 -1
- package/skills/media-use/audio/references/tts-to-captions.md +11 -2
- package/skills/media-use/audio/references/tts.md +84 -6
- package/skills/media-use/audio/scripts/audio.mjs +15 -6
- package/skills/media-use/audio/scripts/gemini-pipeline.test.mjs +113 -0
- package/skills/media-use/audio/scripts/lib/audio-meta.mjs +39 -0
- package/skills/media-use/audio/scripts/lib/audio-meta.test.mjs +136 -0
- package/skills/media-use/audio/scripts/lib/bgm-volume.mjs +4 -0
- package/skills/media-use/audio/scripts/lib/bgm.mjs +2 -6
- package/skills/media-use/audio/scripts/lib/gemini-auth.mjs +43 -0
- package/skills/media-use/audio/scripts/lib/gemini-auth.py +52 -0
- package/skills/media-use/audio/scripts/lib/gemini-auth.test.mjs +86 -0
- package/skills/media-use/audio/scripts/lib/gemini-auth_test.py +90 -0
- package/skills/media-use/audio/scripts/lib/gemini-tts.mjs +122 -0
- package/skills/media-use/audio/scripts/lib/gemini-tts.test.mjs +243 -0
- package/skills/media-use/audio/scripts/lib/heygen.mjs +2 -1
- package/skills/media-use/audio/scripts/lib/tts.mjs +15 -3
- package/skills/media-use/audio/scripts/wait-bgm.mjs +8 -4
- package/skills/media-use/audio/scripts/wait-bgm.test.mjs +65 -0
- package/skills/media-use/references/audio.md +3 -2
- package/skills/media-use/references/grading.md +7 -7
- package/skills/media-use/references/media-treatment-recipes.md +1 -1
- package/skills/media-use/references/memory.md +2 -2
- package/skills/media-use/references/meta.md +2 -2
- package/skills/media-use/references/operations.md +15 -13
- package/skills/media-use/references/resolve.md +22 -11
- package/skills/media-use/references/setup-providers.md +11 -11
- package/skills/media-use/scripts/audio-duck.mjs +19 -27
- package/skills/media-use/scripts/compatibility.test.mjs +96 -0
- package/skills/media-use/scripts/lib/config-lock.mjs +45 -0
- package/skills/media-use/scripts/lib/duck.mjs +16 -10
- package/skills/media-use/scripts/lib/media-fetch.mjs +85 -0
- package/skills/media-use/scripts/lib/media-home.mjs +20 -0
- package/skills/media-use/scripts/lib/npx-sync.mjs +28 -2
- package/skills/media-use/scripts/lib/prefs-store.mjs +2 -2
- package/skills/media-use/scripts/lib/recipe-store.mjs +2 -2
- package/skills/media-use/scripts/lib/telemetry.mjs +29 -41
- package/skills/media-use/scripts/lib/transcriptCutFade.mjs +21 -0
- package/skills/media-use/scripts/prefs.mjs +1 -1
- package/skills/media-use/scripts/recipe.mjs +1 -1
- package/skills/media-use/scripts/resolve-plugin.test.mjs +62 -0
- package/skills/media-use/scripts/resolve.mjs +27 -1267
- package/skills/media-use/scripts/transcribe.mjs +39 -25
- package/skills/media-use/scripts/transcribe.test.mjs +112 -0
- package/skills/media-use/scripts/transcript-cut.mjs +39 -8
- package/skills/media-use/scripts/transcript-cut.test.mjs +117 -0
- package/skills/motion-graphics/SKILL.md +3 -1
- package/skills/motion-graphics/agents/builder.md +1 -0
- package/skills/motion-graphics/agents/director.md +2 -1
- package/skills/motion-graphics/catalog-map.md +25 -13
- package/skills/motion-graphics/categories/charts/module.md +2 -1
- package/skills/motion-graphics/categories/lower-thirds/module.md +4 -0
- package/skills/motion-graphics/categories/news/module.md +1 -1
- package/skills/motion-graphics/references/motion-vocabulary.md +4 -0
- package/skills/music-to-video/SKILL.md +8 -6
- package/skills/music-to-video/references/montage.md +3 -6
- package/skills/music-to-video/references/motion-primitives/3d-card-flip/index.html +10 -111
- package/skills/music-to-video/references/motion-primitives/3d-card-flip/scene.html +119 -0
- package/skills/music-to-video/references/motion-primitives/bg-flow-field/index.html +11 -284
- package/skills/music-to-video/references/motion-primitives/bg-flow-field/scene.html +294 -0
- package/skills/music-to-video/references/motion-primitives/binary-decrypt/index.html +10 -76
- package/skills/music-to-video/references/motion-primitives/binary-decrypt/scene.html +85 -0
- package/skills/music-to-video/references/motion-primitives/blur-resolve/index.html +10 -52
- package/skills/music-to-video/references/motion-primitives/blur-resolve/scene.html +60 -0
- package/skills/music-to-video/references/motion-primitives/braam-punch/index.html +10 -92
- package/skills/music-to-video/references/motion-primitives/braam-punch/scene.html +100 -0
- package/skills/music-to-video/references/motion-primitives/chromatic-split/index.html +10 -103
- package/skills/music-to-video/references/motion-primitives/chromatic-split/scene.html +111 -0
- package/skills/music-to-video/references/motion-primitives/chrome-sweep/index.html +10 -60
- package/skills/music-to-video/references/motion-primitives/chrome-sweep/scene.html +68 -0
- package/skills/music-to-video/references/motion-primitives/counting-punch/index.html +10 -110
- package/skills/music-to-video/references/motion-primitives/counting-punch/scene.html +119 -0
- package/skills/music-to-video/references/motion-primitives/crash-zoom-in/index.html +10 -112
- package/skills/music-to-video/references/motion-primitives/crash-zoom-in/scene.html +120 -0
- package/skills/music-to-video/references/motion-primitives/datamosh-smear/index.html +10 -121
- package/skills/music-to-video/references/motion-primitives/datamosh-smear/scene.html +131 -0
- package/skills/music-to-video/references/motion-primitives/directional-fill/index.html +10 -90
- package/skills/music-to-video/references/motion-primitives/directional-fill/scene.html +103 -0
- package/skills/music-to-video/references/motion-primitives/dolly-zoom/index.html +10 -94
- package/skills/music-to-video/references/motion-primitives/dolly-zoom/scene.html +102 -0
- package/skills/music-to-video/references/motion-primitives/electric-arc/index.html +10 -100
- package/skills/music-to-video/references/motion-primitives/electric-arc/scene.html +105 -0
- package/skills/music-to-video/references/motion-primitives/flash-cut/index.html +10 -89
- package/skills/music-to-video/references/motion-primitives/flash-cut/scene.html +97 -0
- package/skills/music-to-video/references/motion-primitives/gooey-metaball/index.html +10 -197
- package/skills/music-to-video/references/motion-primitives/gooey-metaball/scene.html +206 -0
- package/skills/music-to-video/references/motion-primitives/hard-cut/index.html +10 -74
- package/skills/music-to-video/references/motion-primitives/hard-cut/scene.html +82 -0
- package/skills/music-to-video/references/motion-primitives/hypercut-whip/index.html +10 -36
- package/skills/music-to-video/references/motion-primitives/hypercut-whip/scene.html +44 -0
- package/skills/music-to-video/references/motion-primitives/iris-open/index.html +10 -61
- package/skills/music-to-video/references/motion-primitives/iris-open/scene.html +69 -0
- package/skills/music-to-video/references/motion-primitives/kinetic-letter-in/index.html +10 -55
- package/skills/music-to-video/references/motion-primitives/kinetic-letter-in/scene.html +64 -0
- package/skills/music-to-video/references/motion-primitives/liquid-morph/index.html +10 -88
- package/skills/music-to-video/references/motion-primitives/liquid-morph/scene.html +96 -0
- package/skills/music-to-video/references/motion-primitives/mask-reveal/index.html +10 -66
- package/skills/music-to-video/references/motion-primitives/mask-reveal/scene.html +74 -0
- package/skills/music-to-video/references/motion-primitives/mosaic-pack/index.html +10 -81
- package/skills/music-to-video/references/motion-primitives/mosaic-pack/scene.html +89 -0
- package/skills/music-to-video/references/motion-primitives/neon-flicker/index.html +10 -65
- package/skills/music-to-video/references/motion-primitives/neon-flicker/scene.html +73 -0
- package/skills/music-to-video/references/motion-primitives/outline-to-fill/index.html +10 -74
- package/skills/music-to-video/references/motion-primitives/outline-to-fill/scene.html +82 -0
- package/skills/music-to-video/references/motion-primitives/palette-flip/index.html +10 -89
- package/skills/music-to-video/references/motion-primitives/palette-flip/scene.html +97 -0
- package/skills/music-to-video/references/motion-primitives/particle-burst/index.html +10 -98
- package/skills/music-to-video/references/motion-primitives/particle-burst/scene.html +106 -0
- package/skills/music-to-video/references/motion-primitives/pixel-dissolve/index.html +10 -78
- package/skills/music-to-video/references/motion-primitives/pixel-dissolve/scene.html +86 -0
- package/skills/music-to-video/references/motion-primitives/radial-burst-lines/index.html +10 -117
- package/skills/music-to-video/references/motion-primitives/radial-burst-lines/scene.html +130 -0
- package/skills/music-to-video/references/motion-primitives/screen-shake/index.html +10 -70
- package/skills/music-to-video/references/motion-primitives/screen-shake/scene.html +78 -0
- package/skills/music-to-video/references/motion-primitives/slot-machine-reveal/index.html +10 -87
- package/skills/music-to-video/references/motion-primitives/slot-machine-reveal/scene.html +100 -0
- package/skills/music-to-video/references/motion-primitives/spotlight-sweep/index.html +10 -94
- package/skills/music-to-video/references/motion-primitives/spotlight-sweep/scene.html +102 -0
- package/skills/music-to-video/references/motion-primitives/staggered-exit/index.html +10 -74
- package/skills/music-to-video/references/motion-primitives/staggered-exit/scene.html +82 -0
- package/skills/music-to-video/references/motion-primitives/text-spectral-rays/index.html +11 -271
- package/skills/music-to-video/references/motion-primitives/text-spectral-rays/scene.html +280 -0
- package/skills/music-to-video/references/motion-primitives/text-wave-distort/index.html +10 -92
- package/skills/music-to-video/references/motion-primitives/text-wave-distort/scene.html +106 -0
- package/skills/music-to-video/references/motion-primitives/tile-mosaic/index.html +10 -101
- package/skills/music-to-video/references/motion-primitives/tile-mosaic/scene.html +109 -0
- package/skills/music-to-video/references/motion-primitives/typewriter-reveal/index.html +10 -79
- package/skills/music-to-video/references/motion-primitives/typewriter-reveal/scene.html +91 -0
- package/skills/music-to-video/references/motion-primitives/word-grid-burst/index.html +10 -83
- package/skills/music-to-video/references/motion-primitives/word-grid-burst/scene.html +96 -0
- package/skills/music-to-video/references/planning.md +2 -2
- package/skills/music-to-video/scripts/lib/storyboard.mjs +1 -1
- package/skills/music-to-video/sub-agents/frame-worker.md +2 -2
- package/skills/pr-to-video/SKILL.md +20 -16
- package/skills/pr-to-video/references/code-vocabulary.md +14 -12
- package/skills/pr-to-video/references/story-design.md +1 -1
- package/skills/pr-to-video/scripts/assemble-index.mjs +6 -4
- package/skills/pr-to-video/scripts/assemble-index.test.mjs +57 -2
- package/skills/pr-to-video/scripts/audio.mjs +2 -0
- package/skills/pr-to-video/scripts/build-frame.mjs +6 -3
- package/skills/pr-to-video/scripts/captions.mjs +7 -4
- package/skills/pr-to-video/scripts/captions.test.mjs +41 -0
- package/skills/pr-to-video/scripts/frame-packets.mjs +4 -4
- package/skills/pr-to-video/scripts/lib/bgm-volume.mjs +4 -0
- package/skills/pr-to-video/scripts/lib/captured-fonts.mjs +58 -0
- package/skills/pr-to-video/scripts/lib/frame-packets-core.mjs +194 -0
- package/skills/pr-to-video/scripts/lib/storyboard.mjs +1 -1
- package/skills/pr-to-video/scripts/lib/transitions.json +3 -3
- package/skills/pr-to-video/scripts/transitions.mjs +7 -5
- package/skills/pr-to-video/sub-agents/frame-worker.md +1 -1
- package/skills/product-launch-video/SKILL.md +20 -16
- package/skills/product-launch-video/references/story-design.md +1 -1
- package/skills/product-launch-video/scripts/assemble-index.mjs +6 -4
- package/skills/product-launch-video/scripts/assemble-index.test.mjs +57 -2
- package/skills/product-launch-video/scripts/audio.mjs +2 -0
- package/skills/product-launch-video/scripts/audio.test.mjs +16 -0
- package/skills/product-launch-video/scripts/build-frame-subsets.test.mjs +152 -0
- package/skills/product-launch-video/scripts/build-frame.mjs +6 -3
- package/skills/product-launch-video/scripts/captions.mjs +7 -4
- package/skills/product-launch-video/scripts/frame-packets.mjs +4 -4
- package/skills/product-launch-video/scripts/lib/bgm-volume.mjs +4 -0
- package/skills/product-launch-video/scripts/lib/captured-fonts.mjs +58 -0
- package/skills/product-launch-video/scripts/lib/frame-packets-core.mjs +194 -0
- package/skills/product-launch-video/scripts/lib/storyboard.mjs +1 -1
- package/skills/product-launch-video/scripts/lib/transitions.json +3 -3
- package/skills/product-launch-video/scripts/transitions.mjs +7 -5
- package/skills/product-launch-video/scripts/transitions.test.mjs +68 -0
- package/skills/product-launch-video/sub-agents/frame-worker.md +1 -1
- package/skills/remotion-to-hyperframes/SKILL.md +7 -4
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/expected.json +5 -5
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/hf-src/compositions/title.html +31 -0
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/hf-src/index.html +10 -25
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/README.md +8 -8
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/expected.json +9 -9
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/hf-src/compositions/scene-1.html +35 -0
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/hf-src/compositions/scene-2.html +35 -0
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/hf-src/compositions/scene-3.html +36 -0
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/hf-src/index.html +28 -71
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/expected.json +3 -3
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/hf-src/compositions/scene-1.html +52 -0
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/hf-src/compositions/scene-2.html +99 -0
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/hf-src/compositions/scene-3.html +55 -0
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/hf-src/index.html +13 -212
- package/skills/remotion-to-hyperframes/references/api-map.md +16 -16
- package/skills/remotion-to-hyperframes/references/limitations.md +7 -7
- package/skills/remotion-to-hyperframes/references/lottie.md +5 -7
- package/skills/remotion-to-hyperframes/references/media.md +3 -3
- package/skills/remotion-to-hyperframes/references/parameters.md +5 -0
- package/skills/remotion-to-hyperframes/references/sequencing.md +71 -18
- package/skills/slideshow/SKILL.md +4 -1
- package/skills/slideshow/references/standalone-harness.md +0 -3
- package/skills/talking-head-recut/SKILL.md +10 -1
- package/skills/hyperframes-core/references/review-loop.md +0 -41
- package/skills/media-use/scripts/lib/adopt.mjs +0 -128
- package/skills/media-use/scripts/lib/adopt.test.mjs +0 -95
- package/skills/media-use/scripts/lib/bgm-provider.mjs +0 -20
- package/skills/media-use/scripts/lib/brand-provider.mjs +0 -59
- package/skills/media-use/scripts/lib/bundled-sfx-provider.mjs +0 -108
- package/skills/media-use/scripts/lib/bundled-sfx-provider.test.mjs +0 -85
- package/skills/media-use/scripts/lib/cache.mjs +0 -150
- package/skills/media-use/scripts/lib/candidates.mjs +0 -91
- package/skills/media-use/scripts/lib/candidates.test.mjs +0 -155
- package/skills/media-use/scripts/lib/codex-provider.mjs +0 -161
- package/skills/media-use/scripts/lib/codex-provider.test.mjs +0 -34
- package/skills/media-use/scripts/lib/coverage.test.mjs +0 -117
- package/skills/media-use/scripts/lib/cube-build.mjs +0 -181
- package/skills/media-use/scripts/lib/cube-build.test.mjs +0 -80
- package/skills/media-use/scripts/lib/cube-validate.mjs +0 -198
- package/skills/media-use/scripts/lib/cube-validate.test.mjs +0 -125
- package/skills/media-use/scripts/lib/cutlist.test.mjs +0 -148
- package/skills/media-use/scripts/lib/duck.test.mjs +0 -118
- package/skills/media-use/scripts/lib/error-diffusion.test.mjs +0 -102
- package/skills/media-use/scripts/lib/freeze.mjs +0 -70
- package/skills/media-use/scripts/lib/freeze.test.mjs +0 -46
- package/skills/media-use/scripts/lib/grade-analyzer.mjs +0 -264
- package/skills/media-use/scripts/lib/grade-analyzer.test.mjs +0 -285
- package/skills/media-use/scripts/lib/heygen-cli.mjs +0 -191
- package/skills/media-use/scripts/lib/heygen-cli.test.mjs +0 -301
- package/skills/media-use/scripts/lib/heygen-search.mjs +0 -45
- package/skills/media-use/scripts/lib/heygen-search.test.mjs +0 -49
- package/skills/media-use/scripts/lib/heygen-video-provider.mjs +0 -127
- package/skills/media-use/scripts/lib/heygen-video-provider.test.mjs +0 -344
- package/skills/media-use/scripts/lib/image-provider.mjs +0 -44
- package/skills/media-use/scripts/lib/local-models.mjs +0 -318
- package/skills/media-use/scripts/lib/local-models.test.mjs +0 -220
- package/skills/media-use/scripts/lib/local-run.mjs +0 -78
- package/skills/media-use/scripts/lib/local-run.test.mjs +0 -121
- package/skills/media-use/scripts/lib/logo-provider.mjs +0 -222
- package/skills/media-use/scripts/lib/logo-provider.test.mjs +0 -137
- package/skills/media-use/scripts/lib/ltx-video-provider.mjs +0 -94
- package/skills/media-use/scripts/lib/ltx-video-provider.test.mjs +0 -316
- package/skills/media-use/scripts/lib/lut-preset-provider.mjs +0 -266
- package/skills/media-use/scripts/lib/lut-preset-provider.test.mjs +0 -176
- package/skills/media-use/scripts/lib/manifest.test.mjs +0 -388
- package/skills/media-use/scripts/lib/match.mjs +0 -47
- package/skills/media-use/scripts/lib/mflux-provider.mjs +0 -128
- package/skills/media-use/scripts/lib/mflux-provider.test.mjs +0 -174
- package/skills/media-use/scripts/lib/misses.mjs +0 -49
- package/skills/media-use/scripts/lib/misses.test.mjs +0 -78
- package/skills/media-use/scripts/lib/npx-sync.test.mjs +0 -50
- package/skills/media-use/scripts/lib/parakeet-words.test.mjs +0 -44
- package/skills/media-use/scripts/lib/prefs-store.test.mjs +0 -189
- package/skills/media-use/scripts/lib/probe.mjs +0 -39
- package/skills/media-use/scripts/lib/probe.test.mjs +0 -31
- package/skills/media-use/scripts/lib/providers.mjs +0 -5
- package/skills/media-use/scripts/lib/recipe-store.test.mjs +0 -367
- package/skills/media-use/scripts/lib/registry.mjs +0 -218
- package/skills/media-use/scripts/lib/registry.test.mjs +0 -261
- package/skills/media-use/scripts/lib/search.mjs +0 -24
- package/skills/media-use/scripts/lib/search.test.mjs +0 -32
- package/skills/media-use/scripts/lib/sfx-provider.mjs +0 -23
- package/skills/media-use/scripts/lib/specs.mjs +0 -132
- package/skills/media-use/scripts/lib/specs.test.mjs +0 -157
- package/skills/media-use/scripts/lib/stats.mjs +0 -117
- package/skills/media-use/scripts/lib/stats.test.mjs +0 -165
- package/skills/media-use/scripts/lib/telemetry.test.mjs +0 -373
- package/skills/media-use/scripts/lib/tts-local-provider.mjs +0 -92
- package/skills/media-use/scripts/lib/tts-local-provider.test.mjs +0 -72
- package/skills/media-use/scripts/lib/usage.mjs +0 -57
- package/skills/media-use/scripts/lib/usage.test.mjs +0 -54
- package/skills/media-use/scripts/lib/voice-provider.mjs +0 -57
- package/skills/media-use/scripts/lib/voice-provider.test.mjs +0 -46
- package/skills/media-use/scripts/resolve.test.mjs +0 -1120
- /package/skills/{hyperframes-core → hyperframes}/references/subagent-dispatch.md +0 -0
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
// Machine-capability probe for the spec-gated local-model fallback.
|
|
2
|
-
//
|
|
3
|
-
// Local models are USER-INSTALLED and local-use-only — media-use recommends,
|
|
4
|
-
// spec-checks, and assists install, but never bundles or runs them as a service.
|
|
5
|
-
// This probe answers "what tier can this machine actually run?" so selection can
|
|
6
|
-
// offer a medium/large local model, or fall back to recommending the CLI path.
|
|
7
|
-
//
|
|
8
|
-
// `osMod` and `exec` are injectable for tests. `exec(cmd)` returns the command's
|
|
9
|
-
// stdout as a string, or throws / returns null on failure.
|
|
10
|
-
|
|
11
|
-
import os from "node:os";
|
|
12
|
-
import { statfsSync } from "node:fs";
|
|
13
|
-
import { dirname, join } from "node:path";
|
|
14
|
-
import { execSync } from "node:child_process";
|
|
15
|
-
|
|
16
|
-
function defaultExec(cmd) {
|
|
17
|
-
return execSync(cmd, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 4000 });
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// Available (not just total) RAM — the real budget for "will this model fit
|
|
21
|
-
// alongside the OS + open apps". On unified-memory Macs the model shares system
|
|
22
|
-
// RAM, so a big model on a busy machine OOMs/thrashes even if total RAM looks
|
|
23
|
-
// ample. macOS: vm_stat free + inactive + speculative + purgeable pages (all
|
|
24
|
-
// reclaimable). Linux: /proc/meminfo MemAvailable. Fallback: total (optimistic
|
|
25
|
-
// but stable when the probe is unavailable, e.g. in tests).
|
|
26
|
-
function availableRamMB(platform, exec, totalMB) {
|
|
27
|
-
try {
|
|
28
|
-
if (platform === "darwin") {
|
|
29
|
-
const out = String(exec("vm_stat"));
|
|
30
|
-
const pageSize = parseInt((out.match(/page size of (\d+)/) || [])[1] || "16384", 10);
|
|
31
|
-
const pages = (name) => {
|
|
32
|
-
const m = out.match(new RegExp(`${name}:\\s+(\\d+)`));
|
|
33
|
-
return m ? parseInt(m[1], 10) : 0;
|
|
34
|
-
};
|
|
35
|
-
const reclaimable =
|
|
36
|
-
pages("Pages free") +
|
|
37
|
-
pages("Pages inactive") +
|
|
38
|
-
pages("Pages speculative") +
|
|
39
|
-
pages("Pages purgeable");
|
|
40
|
-
const mb = Math.round((reclaimable * pageSize) / (1024 * 1024));
|
|
41
|
-
if (mb > 0) return mb;
|
|
42
|
-
} else if (platform === "linux") {
|
|
43
|
-
const out = String(exec("cat /proc/meminfo"));
|
|
44
|
-
const m = out.match(/MemAvailable:\s+(\d+)\s+kB/);
|
|
45
|
-
if (m) return Math.round(parseInt(m[1], 10) / 1024);
|
|
46
|
-
}
|
|
47
|
-
} catch {
|
|
48
|
-
// probe unavailable — fall through to the total-RAM estimate
|
|
49
|
-
}
|
|
50
|
-
return totalMB;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function detectGpu(platform, arch, ramMB, exec) {
|
|
54
|
-
// Apple Silicon: Metal GPU with unified memory — VRAM tracks system RAM.
|
|
55
|
-
if (platform === "darwin" && arch === "arm64") {
|
|
56
|
-
return { present: true, kind: "apple", vramMB: ramMB };
|
|
57
|
-
}
|
|
58
|
-
// NVIDIA: query total VRAM. Any failure (no driver, no GPU) -> no GPU.
|
|
59
|
-
try {
|
|
60
|
-
const out = exec("nvidia-smi --query-gpu=memory.total --format=csv,noheader,nounits");
|
|
61
|
-
const mb = parseInt(String(out).trim().split(/\r?\n/)[0], 10);
|
|
62
|
-
if (Number.isFinite(mb) && mb > 0) return { present: true, kind: "nvidia", vramMB: mb };
|
|
63
|
-
} catch {
|
|
64
|
-
// fall through — no usable GPU
|
|
65
|
-
}
|
|
66
|
-
return { present: false, kind: null, vramMB: 0 };
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export function probeSpecs({ osMod = os, exec = defaultExec } = {}) {
|
|
70
|
-
const platform = osMod.platform();
|
|
71
|
-
const arch = osMod.arch();
|
|
72
|
-
const cpuCores = osMod.cpus().length;
|
|
73
|
-
const ramMB = Math.round(osMod.totalmem() / (1024 * 1024));
|
|
74
|
-
return {
|
|
75
|
-
platform,
|
|
76
|
-
arch,
|
|
77
|
-
cpuCores,
|
|
78
|
-
ramMB,
|
|
79
|
-
availableRamMB: availableRamMB(platform, exec, ramMB),
|
|
80
|
-
appleSilicon: platform === "darwin" && arch === "arm64",
|
|
81
|
-
gpu: detectGpu(platform, arch, ramMB, exec),
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Where Hugging Face actually puts downloaded weights. A free-space check
|
|
86
|
-
// against cwd measures the wrong filesystem, so the disk question has to be
|
|
87
|
-
// asked about this directory. Precedence follows huggingface_hub's own order.
|
|
88
|
-
export function weightsCacheDir({ env = process.env, osMod = os } = {}) {
|
|
89
|
-
if (env.HF_HUB_CACHE) return env.HF_HUB_CACHE;
|
|
90
|
-
if (env.HUGGINGFACE_HUB_CACHE) return env.HUGGINGFACE_HUB_CACHE;
|
|
91
|
-
if (env.HF_HOME) return join(env.HF_HOME, "hub");
|
|
92
|
-
return join(osMod.homedir(), ".cache", "huggingface", "hub");
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Free space on the filesystem that will hold the weights. The cache dir
|
|
96
|
-
// usually does not exist until the first download and statfs throws on a
|
|
97
|
-
// missing path, so walk up to the deepest ancestor that does exist. Returns
|
|
98
|
-
// null when even the root cannot be read, so callers can say "unknown" instead
|
|
99
|
-
// of implying zero and scaring someone off a download that would have worked.
|
|
100
|
-
export function freeSpaceMB(dir, statfsFn = statfsSync) {
|
|
101
|
-
let path = dir;
|
|
102
|
-
for (;;) {
|
|
103
|
-
try {
|
|
104
|
-
const { bavail, bsize } = statfsFn(path);
|
|
105
|
-
return (bavail * bsize) / 1e6;
|
|
106
|
-
} catch {
|
|
107
|
-
const parent = dirname(path);
|
|
108
|
-
if (parent === path) return null;
|
|
109
|
-
path = parent;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// One line the user reads BEFORE agreeing to a pull that can be tens of GB.
|
|
115
|
-
// Always names the size and where it lands. When it will not fit we say so
|
|
116
|
-
// plainly rather than withholding the tier: a machine that could free up space
|
|
117
|
-
// should still be told the tier exists. `statfsFn` / `env` / `osMod` are
|
|
118
|
-
// injectable for tests.
|
|
119
|
-
export function describeDownload(
|
|
120
|
-
sizeMB,
|
|
121
|
-
{ statfsFn = statfsSync, env = process.env, osMod = os } = {},
|
|
122
|
-
) {
|
|
123
|
-
const dir = weightsCacheDir({ env, osMod });
|
|
124
|
-
const gb = (mb) => (mb / 1000).toFixed(1);
|
|
125
|
-
const head = `downloads ~${gb(sizeMB)}GB of weights to ${dir}`;
|
|
126
|
-
const free = freeSpaceMB(dir, statfsFn);
|
|
127
|
-
if (free == null) return `${head} (free space unknown)`;
|
|
128
|
-
if (free < sizeMB) {
|
|
129
|
-
return `${head}, but only ${gb(free)}GB is free there, so it will NOT fit as-is`;
|
|
130
|
-
}
|
|
131
|
-
return `${head} (${gb(free)}GB free there)`;
|
|
132
|
-
}
|
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
import { strict as assert } from "node:assert";
|
|
2
|
-
import { test } from "node:test";
|
|
3
|
-
import { describeDownload, freeSpaceMB, probeSpecs, weightsCacheDir } from "./specs.mjs";
|
|
4
|
-
|
|
5
|
-
// Fake os module + exec so the probe is deterministic across CI machines.
|
|
6
|
-
const fakeOs = (over = {}) => ({
|
|
7
|
-
platform: () => over.platform ?? "linux",
|
|
8
|
-
arch: () => over.arch ?? "x64",
|
|
9
|
-
cpus: () => Array.from({ length: over.cores ?? 8 }),
|
|
10
|
-
totalmem: () => (over.ramMB ?? 16384) * 1024 * 1024,
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
test("probeSpecs reports structured caps", () => {
|
|
14
|
-
const s = probeSpecs({ osMod: fakeOs({ cores: 12, ramMB: 32768 }), exec: () => null });
|
|
15
|
-
assert.equal(s.cpuCores, 12);
|
|
16
|
-
assert.equal(s.ramMB, 32768);
|
|
17
|
-
assert.equal(s.platform, "linux");
|
|
18
|
-
assert.equal(s.gpu.present, false);
|
|
19
|
-
// probe unavailable (exec returns null) -> availableRamMB falls back to total
|
|
20
|
-
assert.equal(s.availableRamMB, 32768);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
test("availableRamMB is read from /proc/meminfo on Linux", () => {
|
|
24
|
-
const exec = (cmd) =>
|
|
25
|
-
cmd.includes("meminfo") ? "MemTotal: 33554432 kB\nMemAvailable: 8388608 kB\n" : null;
|
|
26
|
-
const s = probeSpecs({ osMod: fakeOs({ platform: "linux", ramMB: 32768 }), exec });
|
|
27
|
-
assert.equal(s.availableRamMB, 8192, "8388608 kB -> 8192 MB");
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
test("availableRamMB is summed from reclaimable vm_stat pages on macOS", () => {
|
|
31
|
-
const vmStat =
|
|
32
|
-
"Mach Virtual Memory Statistics: (page size of 16384 bytes)\n" +
|
|
33
|
-
"Pages free: 100000.\n" +
|
|
34
|
-
"Pages inactive: 200000.\n" +
|
|
35
|
-
"Pages speculative: 50000.\n" +
|
|
36
|
-
"Pages purgeable: 10000.\n";
|
|
37
|
-
const exec = (cmd) => (cmd.includes("vm_stat") ? vmStat : null);
|
|
38
|
-
const s = probeSpecs({
|
|
39
|
-
osMod: fakeOs({ platform: "darwin", arch: "arm64", ramMB: 24576 }),
|
|
40
|
-
exec,
|
|
41
|
-
});
|
|
42
|
-
// (100000+200000+50000+10000) pages * 16384 B / 1MiB = 5625 MB
|
|
43
|
-
assert.equal(s.availableRamMB, 5625);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
test("Apple Silicon is detected as a unified-memory GPU", () => {
|
|
47
|
-
const s = probeSpecs({
|
|
48
|
-
osMod: fakeOs({ platform: "darwin", arch: "arm64", ramMB: 24576 }),
|
|
49
|
-
exec: () => null,
|
|
50
|
-
});
|
|
51
|
-
assert.equal(s.appleSilicon, true);
|
|
52
|
-
assert.equal(s.gpu.present, true);
|
|
53
|
-
assert.equal(s.gpu.kind, "apple");
|
|
54
|
-
// unified memory: VRAM tracks system RAM
|
|
55
|
-
assert.equal(s.gpu.vramMB, 24576);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
test("NVIDIA GPU is detected via nvidia-smi VRAM query", () => {
|
|
59
|
-
const exec = (cmd) => (cmd.includes("nvidia-smi") ? "24564" : null);
|
|
60
|
-
const s = probeSpecs({ osMod: fakeOs({ platform: "linux" }), exec });
|
|
61
|
-
assert.equal(s.gpu.present, true);
|
|
62
|
-
assert.equal(s.gpu.kind, "nvidia");
|
|
63
|
-
assert.equal(s.gpu.vramMB, 24564);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
test("no GPU when nvidia-smi is absent / fails", () => {
|
|
67
|
-
const s = probeSpecs({
|
|
68
|
-
osMod: fakeOs({ platform: "linux" }),
|
|
69
|
-
exec: () => {
|
|
70
|
-
throw new Error("command not found");
|
|
71
|
-
},
|
|
72
|
-
});
|
|
73
|
-
assert.equal(s.gpu.present, false);
|
|
74
|
-
assert.equal(s.gpu.vramMB, 0);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
// --- download disclosure: what the user is agreeing to before the pull ---
|
|
78
|
-
|
|
79
|
-
const fakeHome = { homedir: () => "/home/tester" };
|
|
80
|
-
// statfs reports blocks, not bytes: bavail * bsize. 1 MB blocks keep the sums
|
|
81
|
-
// readable, and mirror the real struct's shape.
|
|
82
|
-
const fakeStatfs = (freeMB, existsOnly) => (path) => {
|
|
83
|
-
if (existsOnly && path !== existsOnly) throw new Error(`ENOENT: ${path}`);
|
|
84
|
-
return { bavail: freeMB, bsize: 1e6 };
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
test("weightsCacheDir follows huggingface_hub's precedence", () => {
|
|
88
|
-
assert.equal(
|
|
89
|
-
weightsCacheDir({ env: {}, osMod: fakeHome }),
|
|
90
|
-
"/home/tester/.cache/huggingface/hub",
|
|
91
|
-
);
|
|
92
|
-
assert.equal(weightsCacheDir({ env: { HF_HOME: "/data/hf" }, osMod: fakeHome }), "/data/hf/hub");
|
|
93
|
-
assert.equal(
|
|
94
|
-
weightsCacheDir({ env: { HF_HOME: "/data/hf", HUGGINGFACE_HUB_CACHE: "/c" }, osMod: fakeHome }),
|
|
95
|
-
"/c",
|
|
96
|
-
"HUGGINGFACE_HUB_CACHE outranks HF_HOME",
|
|
97
|
-
);
|
|
98
|
-
assert.equal(
|
|
99
|
-
weightsCacheDir({ env: { HF_HUB_CACHE: "/a", HUGGINGFACE_HUB_CACHE: "/c" }, osMod: fakeHome }),
|
|
100
|
-
"/a",
|
|
101
|
-
"HF_HUB_CACHE wins outright",
|
|
102
|
-
);
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
test("freeSpaceMB walks up to the deepest existing ancestor", () => {
|
|
106
|
-
// the cache dir does not exist until the first download, and statfs throws
|
|
107
|
-
// on a missing path - so the answer has to come from an ancestor
|
|
108
|
-
const statfs = fakeStatfs(4096, "/home");
|
|
109
|
-
assert.equal(freeSpaceMB("/home/tester/.cache/huggingface/hub", statfs), 4096);
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
test("freeSpaceMB reports null rather than zero when nothing can be read", () => {
|
|
113
|
-
assert.equal(
|
|
114
|
-
freeSpaceMB("/home/tester/.cache", () => {
|
|
115
|
-
throw new Error("EACCES");
|
|
116
|
-
}),
|
|
117
|
-
null,
|
|
118
|
-
"unknown must not be reported as no-space",
|
|
119
|
-
);
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
test("describeDownload names the size and where it lands", () => {
|
|
123
|
-
const msg = describeDownload(87500, {
|
|
124
|
-
statfsFn: fakeStatfs(200000),
|
|
125
|
-
env: {},
|
|
126
|
-
osMod: fakeHome,
|
|
127
|
-
});
|
|
128
|
-
assert.match(msg, /~87\.5GB/);
|
|
129
|
-
assert.match(msg, /\/home\/tester\/\.cache\/huggingface\/hub/);
|
|
130
|
-
assert.match(msg, /200\.0GB free/);
|
|
131
|
-
assert.equal(/NOT fit/.test(msg), false, "it fits, so no warning");
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
test("describeDownload says plainly when the weights will not fit", () => {
|
|
135
|
-
const msg = describeDownload(87500, {
|
|
136
|
-
statfsFn: fakeStatfs(14000),
|
|
137
|
-
env: {},
|
|
138
|
-
osMod: fakeHome,
|
|
139
|
-
});
|
|
140
|
-
assert.match(msg, /only 14\.0GB is free there/);
|
|
141
|
-
assert.match(msg, /will NOT fit as-is/);
|
|
142
|
-
// the tier is still described, not withheld: a machine that could free up
|
|
143
|
-
// space should know the tier exists
|
|
144
|
-
assert.match(msg, /~87\.5GB/);
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
test("describeDownload admits when free space is unknown", () => {
|
|
148
|
-
const msg = describeDownload(87500, {
|
|
149
|
-
statfsFn: () => {
|
|
150
|
-
throw new Error("EACCES");
|
|
151
|
-
},
|
|
152
|
-
env: {},
|
|
153
|
-
osMod: fakeHome,
|
|
154
|
-
});
|
|
155
|
-
assert.match(msg, /free space unknown/);
|
|
156
|
-
assert.equal(/NOT fit/.test(msg), false, "unknown is not a refusal");
|
|
157
|
-
});
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import { statSync } from "node:fs";
|
|
2
|
-
import { readGlobalManifest } from "./cache.mjs";
|
|
3
|
-
import { readManifest } from "./manifest.mjs";
|
|
4
|
-
import { readMisses } from "./misses.mjs";
|
|
5
|
-
|
|
6
|
-
const TOP_MISSES = 5;
|
|
7
|
-
|
|
8
|
-
function emptyReport() {
|
|
9
|
-
return {
|
|
10
|
-
total_resolves: 0,
|
|
11
|
-
by_type: {},
|
|
12
|
-
by_source: {},
|
|
13
|
-
by_provider: {},
|
|
14
|
-
by_via: {},
|
|
15
|
-
misses: 0,
|
|
16
|
-
hit_rate: null,
|
|
17
|
-
top_missed_intents: {},
|
|
18
|
-
global_cache_assets: 0,
|
|
19
|
-
global_cache_disk_bytes: 0,
|
|
20
|
-
cross_project_reuse: 0,
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function increment(map, key) {
|
|
25
|
-
if (!key) return;
|
|
26
|
-
map[key] = (map[key] || 0) + 1;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function timestampOf(record) {
|
|
30
|
-
return record?.ts || record?.timestamp || record?.created_at || record?.createdAt || null;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function inWindow(record, cutoff) {
|
|
34
|
-
if (!cutoff) return true;
|
|
35
|
-
const ts = timestampOf(record);
|
|
36
|
-
// Older manifest records may not carry a timestamp; keep them in the report
|
|
37
|
-
// because --days can only window records/misses that carry a ts/timestamp.
|
|
38
|
-
if (!ts) return true;
|
|
39
|
-
const time = Date.parse(ts);
|
|
40
|
-
return Number.isNaN(time) ? true : time >= cutoff;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function sourceOf(record) {
|
|
44
|
-
return record?._source || record?.source || record?.provenance?.source || "unknown";
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function normalizeIntent(intent) {
|
|
48
|
-
return String(intent ?? "")
|
|
49
|
-
.trim()
|
|
50
|
-
.toLowerCase()
|
|
51
|
-
.replace(/\s+/g, " ");
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function topMissedIntents(misses) {
|
|
55
|
-
const grouped = {};
|
|
56
|
-
for (const miss of misses) {
|
|
57
|
-
const type = miss?.type || "unknown";
|
|
58
|
-
const intent = normalizeIntent(miss?.intent);
|
|
59
|
-
if (!intent) continue;
|
|
60
|
-
grouped[type] ||= {};
|
|
61
|
-
grouped[type][intent] = (grouped[type][intent] || 0) + 1;
|
|
62
|
-
}
|
|
63
|
-
const out = {};
|
|
64
|
-
for (const [type, intents] of Object.entries(grouped)) {
|
|
65
|
-
out[type] = Object.entries(intents)
|
|
66
|
-
.map(([intent, count]) => ({ intent, count }))
|
|
67
|
-
.sort((a, b) => b.count - a.count || a.intent.localeCompare(b.intent))
|
|
68
|
-
.slice(0, TOP_MISSES);
|
|
69
|
-
}
|
|
70
|
-
return out;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function diskBytes(records) {
|
|
74
|
-
let total = 0;
|
|
75
|
-
for (const record of records) {
|
|
76
|
-
const p = record?.cached_path || record?.path;
|
|
77
|
-
if (!p) continue;
|
|
78
|
-
try {
|
|
79
|
-
total += statSync(p).size;
|
|
80
|
-
} catch {
|
|
81
|
-
// cache entries can outlive files; stats skips missing files
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
return total;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export function buildStats({ projectDir, days, now = Date.now() } = {}) {
|
|
88
|
-
// Only a positive finite --days windows the report; null / NaN / <= 0 mean
|
|
89
|
-
// "all time" rather than silently excluding everything (a negative cutoff
|
|
90
|
-
// would land in the future and drop every record). The reads below are each
|
|
91
|
-
// best-effort (they return [] / skip on IO errors), so there is no top-level
|
|
92
|
-
// catch masking a real logic bug as an all-zero "no usage" report.
|
|
93
|
-
const n = Number(days);
|
|
94
|
-
const cutoff = Number.isFinite(n) && n > 0 ? Number(now) - n * 24 * 60 * 60 * 1000 : null;
|
|
95
|
-
const records = (projectDir ? readManifest(projectDir) : []).filter((r) => inWindow(r, cutoff));
|
|
96
|
-
const misses = readMisses().filter((miss) => inWindow(miss, cutoff));
|
|
97
|
-
const globalRecords = readGlobalManifest();
|
|
98
|
-
const report = emptyReport();
|
|
99
|
-
|
|
100
|
-
report.total_resolves = records.length;
|
|
101
|
-
report.misses = misses.length;
|
|
102
|
-
for (const record of records) {
|
|
103
|
-
increment(report.by_type, record?.type || "unknown");
|
|
104
|
-
increment(report.by_source, sourceOf(record));
|
|
105
|
-
increment(report.by_provider, record?.provenance?.provider);
|
|
106
|
-
increment(report.by_via, record?.provenance?.via);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const attempts = report.total_resolves + report.misses;
|
|
110
|
-
report.hit_rate = attempts === 0 ? null : report.total_resolves / attempts;
|
|
111
|
-
report.top_missed_intents = topMissedIntents(misses);
|
|
112
|
-
report.global_cache_assets = globalRecords.length;
|
|
113
|
-
report.global_cache_disk_bytes = diskBytes(globalRecords);
|
|
114
|
-
report.cross_project_reuse = globalRecords.filter((r) => r?.provenance?.reused_by).length;
|
|
115
|
-
|
|
116
|
-
return report;
|
|
117
|
-
}
|
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
import { strict as assert } from "node:assert";
|
|
2
|
-
import { test } from "node:test";
|
|
3
|
-
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
4
|
-
import { tmpdir } from "node:os";
|
|
5
|
-
import { join } from "node:path";
|
|
6
|
-
import { buildStats } from "./stats.mjs";
|
|
7
|
-
|
|
8
|
-
function sandbox() {
|
|
9
|
-
const root = mkdtempSync(join(tmpdir(), "mu-stats-"));
|
|
10
|
-
const home = join(root, "home");
|
|
11
|
-
const projectDir = join(root, "project");
|
|
12
|
-
mkdirSync(home, { recursive: true });
|
|
13
|
-
mkdirSync(projectDir, { recursive: true });
|
|
14
|
-
process.env.HOME = home;
|
|
15
|
-
return { root, home, projectDir };
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function restoreEnv(saved) {
|
|
19
|
-
for (const k of Object.keys(process.env)) if (!(k in saved)) delete process.env[k];
|
|
20
|
-
Object.assign(process.env, saved);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function seedManifest(dir, records) {
|
|
24
|
-
mkdirSync(join(dir, ".media"), { recursive: true });
|
|
25
|
-
writeFileSync(
|
|
26
|
-
join(dir, ".media/manifest.jsonl"),
|
|
27
|
-
records.map((record) => JSON.stringify(record)).join("\n") + "\n",
|
|
28
|
-
);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function isoDaysAgo(days) {
|
|
32
|
-
return new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString();
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
test("buildStats aggregates resolves, misses, providers, sources, and reuse", () => {
|
|
36
|
-
const savedEnv = { ...process.env };
|
|
37
|
-
const { root, home, projectDir } = sandbox();
|
|
38
|
-
try {
|
|
39
|
-
seedManifest(projectDir, [
|
|
40
|
-
{
|
|
41
|
-
id: "bgm_001",
|
|
42
|
-
type: "bgm",
|
|
43
|
-
source: "search",
|
|
44
|
-
ts: isoDaysAgo(0),
|
|
45
|
-
provenance: { provider: "heygen.audio.sounds", prompt: "upbeat", via: "url" },
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
id: "image_001",
|
|
49
|
-
type: "image",
|
|
50
|
-
_source: "reused-explicit",
|
|
51
|
-
timestamp: isoDaysAgo(0),
|
|
52
|
-
provenance: { provider: "local", reused_by: "agent" },
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
id: "bgm_002",
|
|
56
|
-
type: "bgm",
|
|
57
|
-
source: "generated",
|
|
58
|
-
provenance: { provider: "codex" },
|
|
59
|
-
},
|
|
60
|
-
]);
|
|
61
|
-
const cachedFile = join(home, ".media/mu-v1-cache/asset.wav");
|
|
62
|
-
mkdirSync(join(cachedFile, ".."), { recursive: true });
|
|
63
|
-
writeFileSync(cachedFile, "12345");
|
|
64
|
-
seedManifest(home, [
|
|
65
|
-
{
|
|
66
|
-
id: "bgm_009",
|
|
67
|
-
type: "bgm",
|
|
68
|
-
reusable: true,
|
|
69
|
-
cached_path: cachedFile,
|
|
70
|
-
provenance: { provider: "heygen.audio.sounds", reused_by: "agent" },
|
|
71
|
-
},
|
|
72
|
-
]);
|
|
73
|
-
writeFileSync(
|
|
74
|
-
join(home, ".media/misses.jsonl"),
|
|
75
|
-
JSON.stringify({
|
|
76
|
-
ts: isoDaysAgo(0),
|
|
77
|
-
type: "bgm",
|
|
78
|
-
intent: "dark cinematic riser",
|
|
79
|
-
}) + "\n",
|
|
80
|
-
{ flag: "a" },
|
|
81
|
-
);
|
|
82
|
-
|
|
83
|
-
const stats = buildStats({ projectDir });
|
|
84
|
-
assert.equal(stats.total_resolves, 3);
|
|
85
|
-
assert.deepEqual(stats.by_type, { bgm: 2, image: 1 });
|
|
86
|
-
assert.deepEqual(stats.by_source, { search: 1, "reused-explicit": 1, generated: 1 });
|
|
87
|
-
assert.equal(stats.by_provider["heygen.audio.sounds"], 1);
|
|
88
|
-
assert.equal(stats.by_via.url, 1);
|
|
89
|
-
assert.equal(stats.misses, 1);
|
|
90
|
-
assert.equal(stats.hit_rate, 0.75);
|
|
91
|
-
assert.deepEqual(stats.top_missed_intents.bgm[0], {
|
|
92
|
-
intent: "dark cinematic riser",
|
|
93
|
-
count: 1,
|
|
94
|
-
});
|
|
95
|
-
assert.equal(stats.global_cache_assets, 1);
|
|
96
|
-
assert.equal(stats.global_cache_disk_bytes, 5);
|
|
97
|
-
assert.equal(stats.cross_project_reuse, 1);
|
|
98
|
-
} finally {
|
|
99
|
-
restoreEnv(savedEnv);
|
|
100
|
-
rmSync(root, { recursive: true, force: true });
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
test("buildStats returns a zeroed report when nothing exists", () => {
|
|
105
|
-
const savedEnv = { ...process.env };
|
|
106
|
-
const { root, projectDir } = sandbox();
|
|
107
|
-
try {
|
|
108
|
-
const stats = buildStats({ projectDir });
|
|
109
|
-
assert.equal(stats.total_resolves, 0);
|
|
110
|
-
assert.deepEqual(stats.by_type, {});
|
|
111
|
-
assert.deepEqual(stats.by_source, {});
|
|
112
|
-
assert.deepEqual(stats.by_provider, {});
|
|
113
|
-
assert.deepEqual(stats.by_via, {});
|
|
114
|
-
assert.equal(stats.misses, 0);
|
|
115
|
-
assert.equal(stats.hit_rate, null);
|
|
116
|
-
assert.deepEqual(stats.top_missed_intents, {});
|
|
117
|
-
assert.equal(stats.global_cache_assets, 0);
|
|
118
|
-
assert.equal(stats.global_cache_disk_bytes, 0);
|
|
119
|
-
assert.equal(stats.cross_project_reuse, 0);
|
|
120
|
-
} finally {
|
|
121
|
-
restoreEnv(savedEnv);
|
|
122
|
-
rmSync(root, { recursive: true, force: true });
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
test("buildStats returns JSON-safe output", () => {
|
|
127
|
-
const savedEnv = { ...process.env };
|
|
128
|
-
const { root, projectDir } = sandbox();
|
|
129
|
-
try {
|
|
130
|
-
const stats = buildStats({ projectDir });
|
|
131
|
-
assert.deepEqual(JSON.parse(JSON.stringify(stats)), stats);
|
|
132
|
-
} finally {
|
|
133
|
-
restoreEnv(savedEnv);
|
|
134
|
-
rmSync(root, { recursive: true, force: true });
|
|
135
|
-
}
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
test("buildStats applies days window to timestamped records and misses", () => {
|
|
139
|
-
const savedEnv = { ...process.env };
|
|
140
|
-
const { root, home, projectDir } = sandbox();
|
|
141
|
-
try {
|
|
142
|
-
seedManifest(projectDir, [
|
|
143
|
-
{ id: "bgm_old", type: "bgm", ts: isoDaysAgo(100), provenance: { provider: "old" } },
|
|
144
|
-
{ id: "bgm_new", type: "bgm", ts: isoDaysAgo(1), provenance: { provider: "new" } },
|
|
145
|
-
{ id: "image_untimed", type: "image", provenance: { provider: "none" } },
|
|
146
|
-
]);
|
|
147
|
-
mkdirSync(join(home, ".media"), { recursive: true });
|
|
148
|
-
writeFileSync(
|
|
149
|
-
join(home, ".media/misses.jsonl"),
|
|
150
|
-
[
|
|
151
|
-
JSON.stringify({ ts: isoDaysAgo(100), type: "bgm", intent: "old miss" }),
|
|
152
|
-
JSON.stringify({ ts: isoDaysAgo(1), type: "bgm", intent: "new miss" }),
|
|
153
|
-
].join("\n"),
|
|
154
|
-
);
|
|
155
|
-
|
|
156
|
-
const stats = buildStats({ projectDir, days: 7 });
|
|
157
|
-
assert.equal(stats.total_resolves, 2);
|
|
158
|
-
assert.deepEqual(stats.by_type, { bgm: 1, image: 1 });
|
|
159
|
-
assert.equal(stats.misses, 1);
|
|
160
|
-
assert.equal(stats.top_missed_intents.bgm[0].intent, "new miss");
|
|
161
|
-
} finally {
|
|
162
|
-
restoreEnv(savedEnv);
|
|
163
|
-
rmSync(root, { recursive: true, force: true });
|
|
164
|
-
}
|
|
165
|
-
});
|