davinci-resolve-mcp 2.57.4 → 2.58.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.
Files changed (328) hide show
  1. package/AGENTS.md +20 -0
  2. package/CHANGELOG.md +197 -2
  3. package/LICENSE +1 -1
  4. package/README.md +73 -1
  5. package/bin/davinci-resolve-advanced-mcp.mjs +20 -0
  6. package/docs/README.md +19 -0
  7. package/docs/SKILL.md +62 -1
  8. package/docs/contributing.md +1 -1
  9. package/docs/guides/control-panel.md +9 -0
  10. package/docs/kernels/README.md +21 -0
  11. package/docs/kernels/audio-fairlight-kernel.md +22 -0
  12. package/docs/kernels/color-grade-kernel.md +51 -0
  13. package/docs/kernels/fusion-composition-kernel.md +17 -0
  14. package/docs/kernels/media-pool-ingest-kernel.md +20 -0
  15. package/docs/kernels/render-deliver-kernel.md +32 -0
  16. package/docs/kernels/timeline-conform-interchange-kernel.md +39 -0
  17. package/docs/kernels/timeline-edit-kernel.md +17 -0
  18. package/docs/process/release-process.md +29 -7
  19. package/docs/reference/api-coverage.md +1 -1
  20. package/docs/reference/api-limitations.md +38 -1
  21. package/install.py +27 -4
  22. package/package.json +23 -2
  23. package/resolve-advanced/README.md +194 -0
  24. package/resolve-advanced/package.json +50 -0
  25. package/resolve-advanced/server/black-balance.mjs +92 -0
  26. package/resolve-advanced/server/capabilities.mjs +86 -0
  27. package/resolve-advanced/server/cdl-io.mjs +76 -0
  28. package/resolve-advanced/server/contrast-normalize.mjs +180 -0
  29. package/resolve-advanced/server/db-patch.mjs +91 -0
  30. package/resolve-advanced/server/deliverable-entities.mjs +86 -0
  31. package/resolve-advanced/server/deliverable-qc.mjs +270 -0
  32. package/resolve-advanced/server/editorial.mjs +370 -0
  33. package/resolve-advanced/server/exposure-level.mjs +129 -0
  34. package/resolve-advanced/server/extract-frames.mjs +196 -0
  35. package/resolve-advanced/server/ffprobe-media.mjs +88 -0
  36. package/resolve-advanced/server/gamut-legal.mjs +112 -0
  37. package/resolve-advanced/server/grade-body-patch.mjs +246 -0
  38. package/resolve-advanced/server/grade-transfer.mjs +66 -0
  39. package/resolve-advanced/server/group-grade-read.mjs +137 -0
  40. package/resolve-advanced/server/index.mjs +105 -0
  41. package/resolve-advanced/server/lib.mjs +84 -0
  42. package/resolve-advanced/server/libs.mjs +65 -0
  43. package/resolve-advanced/server/lineage-db.mjs +479 -0
  44. package/resolve-advanced/server/lut-apply.mjs +61 -0
  45. package/resolve-advanced/server/match-to-reference.mjs +173 -0
  46. package/resolve-advanced/server/media-inventory.mjs +169 -0
  47. package/resolve-advanced/server/media-ops.mjs +243 -0
  48. package/resolve-advanced/server/node-meta-db.mjs +62 -0
  49. package/resolve-advanced/server/node-provenance.mjs +102 -0
  50. package/resolve-advanced/server/offline-ref-db.mjs +420 -0
  51. package/resolve-advanced/server/offline-ref.mjs +179 -0
  52. package/resolve-advanced/server/project-db.mjs +233 -0
  53. package/resolve-advanced/server/provenance-audit.mjs +210 -0
  54. package/resolve-advanced/server/qc-frame.mjs +224 -0
  55. package/resolve-advanced/server/qc-sampler.mjs +67 -0
  56. package/resolve-advanced/server/readback.mjs +77 -0
  57. package/resolve-advanced/server/render-manifest.mjs +93 -0
  58. package/resolve-advanced/server/reverse-clip-db.mjs +270 -0
  59. package/resolve-advanced/server/runner-apply-contract.mjs +64 -0
  60. package/resolve-advanced/server/runner.mjs +244 -0
  61. package/resolve-advanced/server/saturation-match.mjs +108 -0
  62. package/resolve-advanced/server/scope-read.mjs +257 -0
  63. package/resolve-advanced/server/season-look.mjs +96 -0
  64. package/resolve-advanced/server/shot-intent.mjs +82 -0
  65. package/resolve-advanced/server/shot-match.mjs +147 -0
  66. package/resolve-advanced/server/skin-match.mjs +205 -0
  67. package/resolve-advanced/server/spec-compile.mjs +217 -0
  68. package/resolve-advanced/server/tone-curve-transfer.mjs +184 -0
  69. package/resolve-advanced/server/tool-catalog.mjs +265 -0
  70. package/resolve-advanced/server/tools/audio.mjs +54 -0
  71. package/resolve-advanced/server/tools/audio_plan.mjs +58 -0
  72. package/resolve-advanced/server/tools/capabilities.mjs +16 -0
  73. package/resolve-advanced/server/tools/color_trace.mjs +165 -0
  74. package/resolve-advanced/server/tools/conform.mjs +291 -0
  75. package/resolve-advanced/server/tools/deliverable.mjs +105 -0
  76. package/resolve-advanced/server/tools/drp.mjs +232 -0
  77. package/resolve-advanced/server/tools/drt.mjs +200 -0
  78. package/resolve-advanced/server/tools/drx.mjs +943 -0
  79. package/resolve-advanced/server/tools/editorial.mjs +69 -0
  80. package/resolve-advanced/server/tools/fairlight.mjs +108 -0
  81. package/resolve-advanced/server/tools/fusion.mjs +64 -0
  82. package/resolve-advanced/server/tools/media.mjs +102 -0
  83. package/resolve-advanced/server/tools/offline_ref.mjs +123 -0
  84. package/resolve-advanced/server/tools/pipeline.mjs +137 -0
  85. package/resolve-advanced/server/tools/project_db.mjs +200 -0
  86. package/resolve-advanced/server/tools/project_read.mjs +212 -0
  87. package/resolve-advanced/server/tools/provenance.mjs +73 -0
  88. package/resolve-advanced/server/verify-grade.mjs +113 -0
  89. package/resolve-advanced/server/white-balance-match.mjs +122 -0
  90. package/resolve-advanced/vendor/audio/format-converter.js +330 -0
  91. package/resolve-advanced/vendor/audio/split.js +173 -0
  92. package/resolve-advanced/vendor/audio/trim.js +188 -0
  93. package/resolve-advanced/vendor/audio-fairlight/index.js +391 -0
  94. package/resolve-advanced/vendor/conform-qc/adapters/frame-sampler.js +56 -0
  95. package/resolve-advanced/vendor/conform-qc/adapters/local-ffmpeg-sampler.js +65 -0
  96. package/resolve-advanced/vendor/conform-qc/adapters/rendernode-ffmpeg-sampler.js +50 -0
  97. package/resolve-advanced/vendor/conform-qc/adapters/resolve/driver.py +176 -0
  98. package/resolve-advanced/vendor/conform-qc/adapters/resolve-driver.js +61 -0
  99. package/resolve-advanced/vendor/conform-qc/adapters/resolve-headless-driver.js +86 -0
  100. package/resolve-advanced/vendor/conform-qc/adapters/vision-validator.js +75 -0
  101. package/resolve-advanced/vendor/conform-qc/cli.js +63 -0
  102. package/resolve-advanced/vendor/conform-qc/compare/advisory.js +106 -0
  103. package/resolve-advanced/vendor/conform-qc/compare/decode.js +36 -0
  104. package/resolve-advanced/vendor/conform-qc/compare/index.js +61 -0
  105. package/resolve-advanced/vendor/conform-qc/compare/locate.js +70 -0
  106. package/resolve-advanced/vendor/conform-qc/compare/metrics.js +409 -0
  107. package/resolve-advanced/vendor/conform-qc/docs/runbook.md +72 -0
  108. package/resolve-advanced/vendor/conform-qc/index.js +49 -0
  109. package/resolve-advanced/vendor/conform-qc/knowledge/index.js +110 -0
  110. package/resolve-advanced/vendor/conform-qc/ops/conform-core.js +137 -0
  111. package/resolve-advanced/vendor/conform-qc/ops/conform-diff.js +109 -0
  112. package/resolve-advanced/vendor/conform-qc/ops/insert.js +108 -0
  113. package/resolve-advanced/vendor/conform-qc/ops/local-conform.js +61 -0
  114. package/resolve-advanced/vendor/conform-qc/ops/patch.js +80 -0
  115. package/resolve-advanced/vendor/conform-qc/ops/trigger.js +32 -0
  116. package/resolve-advanced/vendor/conform-qc/ops/verify.js +105 -0
  117. package/resolve-advanced/vendor/conform-qc/ops/workflow.js +30 -0
  118. package/resolve-advanced/vendor/conform-qc/oracle/emulate.js +155 -0
  119. package/resolve-advanced/vendor/conform-qc/oracle/index.js +38 -0
  120. package/resolve-advanced/vendor/conform-qc/oracle/resolve.js +196 -0
  121. package/resolve-advanced/vendor/conform-qc/package.json +16 -0
  122. package/resolve-advanced/vendor/conform-qc/packaging/emit-aaf.js +30 -0
  123. package/resolve-advanced/vendor/conform-qc/packaging/emit-fcp7.js +64 -0
  124. package/resolve-advanced/vendor/conform-qc/packaging/index.js +169 -0
  125. package/resolve-advanced/vendor/conform-qc/packaging/media-ops.js +56 -0
  126. package/resolve-advanced/vendor/conform-qc/packaging/otio.js +50 -0
  127. package/resolve-advanced/vendor/conform-qc/packaging/relink.js +50 -0
  128. package/resolve-advanced/vendor/conform-qc/packaging/surgical-relink.js +195 -0
  129. package/resolve-advanced/vendor/conform-qc/parse/index.js +54 -0
  130. package/resolve-advanced/vendor/conform-qc/parse/merge-dto.js +27 -0
  131. package/resolve-advanced/vendor/conform-qc/parse/xmeml-geometry.js +256 -0
  132. package/resolve-advanced/vendor/conform-qc/reference/burnin-ocr.js +100 -0
  133. package/resolve-advanced/vendor/conform-qc/reference/sidecar.js +37 -0
  134. package/resolve-advanced/vendor/conform-qc/repair/analysis.js +62 -0
  135. package/resolve-advanced/vendor/conform-qc/repair/index.js +78 -0
  136. package/resolve-advanced/vendor/conform-qc/repair/media-analysis.js +139 -0
  137. package/resolve-advanced/vendor/conform-qc/repair/media-index.js +135 -0
  138. package/resolve-advanced/vendor/conform-qc/repair/strategies.js +250 -0
  139. package/resolve-advanced/vendor/conform-qc/report/index.js +133 -0
  140. package/resolve-advanced/vendor/conform-qc/roles/index.js +96 -0
  141. package/resolve-advanced/vendor/conform-qc/synthetic/generate.js +233 -0
  142. package/resolve-advanced/vendor/conform-qc/test/advisory.test.js +85 -0
  143. package/resolve-advanced/vendor/conform-qc/test/cli.test.js +39 -0
  144. package/resolve-advanced/vendor/conform-qc/test/compare.test.js +152 -0
  145. package/resolve-advanced/vendor/conform-qc/test/conform-diff.test.js +70 -0
  146. package/resolve-advanced/vendor/conform-qc/test/emulate.test.js +51 -0
  147. package/resolve-advanced/vendor/conform-qc/test/frame-sampler.test.js +30 -0
  148. package/resolve-advanced/vendor/conform-qc/test/geometry.test.js +112 -0
  149. package/resolve-advanced/vendor/conform-qc/test/knowledge.test.js +56 -0
  150. package/resolve-advanced/vendor/conform-qc/test/lifecycle.test.js +110 -0
  151. package/resolve-advanced/vendor/conform-qc/test/local-ffmpeg-sampler.test.js +66 -0
  152. package/resolve-advanced/vendor/conform-qc/test/locate.test.js +63 -0
  153. package/resolve-advanced/vendor/conform-qc/test/media-analysis.test.js +63 -0
  154. package/resolve-advanced/vendor/conform-qc/test/media-index-normalized.test.js +65 -0
  155. package/resolve-advanced/vendor/conform-qc/test/media-index-origin.test.js +53 -0
  156. package/resolve-advanced/vendor/conform-qc/test/metrics-fast.test.js +91 -0
  157. package/resolve-advanced/vendor/conform-qc/test/oracle-reverse.test.js +86 -0
  158. package/resolve-advanced/vendor/conform-qc/test/oracle.test.js +136 -0
  159. package/resolve-advanced/vendor/conform-qc/test/p0-acceptance.test.js +60 -0
  160. package/resolve-advanced/vendor/conform-qc/test/p1-acceptance.test.js +75 -0
  161. package/resolve-advanced/vendor/conform-qc/test/p45-patch-acceptance.test.js +102 -0
  162. package/resolve-advanced/vendor/conform-qc/test/packaging.test.js +133 -0
  163. package/resolve-advanced/vendor/conform-qc/test/parse-stubs.test.js +28 -0
  164. package/resolve-advanced/vendor/conform-qc/test/repair-strategies.test.js +128 -0
  165. package/resolve-advanced/vendor/conform-qc/test/repair.test.js +84 -0
  166. package/resolve-advanced/vendor/conform-qc/test/report.test.js +52 -0
  167. package/resolve-advanced/vendor/conform-qc/test/resolve-readback.test.js +71 -0
  168. package/resolve-advanced/vendor/conform-qc/test/roles.test.js +107 -0
  169. package/resolve-advanced/vendor/conform-qc/test/runbook.test.js +30 -0
  170. package/resolve-advanced/vendor/conform-qc/test/scale-residual.test.js +40 -0
  171. package/resolve-advanced/vendor/conform-qc/test/sidecar.test.js +51 -0
  172. package/resolve-advanced/vendor/conform-qc/test/smoke.test.js +87 -0
  173. package/resolve-advanced/vendor/conform-qc/test/surgical-relink.test.js +73 -0
  174. package/resolve-advanced/vendor/conform-qc/test/synthetic.test.js +98 -0
  175. package/resolve-advanced/vendor/conform-qc/test/verify.test.js +93 -0
  176. package/resolve-advanced/vendor/conform-qc/test/vision-validator.test.js +43 -0
  177. package/resolve-advanced/vendor/conform-qc/test/workflow-driver.test.js +46 -0
  178. package/resolve-advanced/vendor/conform-qc/toolset/index.js +53 -0
  179. package/resolve-advanced/vendor/drp-format/README.md +116 -0
  180. package/resolve-advanced/vendor/drp-format/__tests__/_resolve-verify.js +39 -0
  181. package/resolve-advanced/vendor/drp-format/__tests__/add-media-clip.test.js +30 -0
  182. package/resolve-advanced/vendor/drp-format/__tests__/assemble-timeline.test.js +68 -0
  183. package/resolve-advanced/vendor/drp-format/__tests__/author-project.test.js +57 -0
  184. package/resolve-advanced/vendor/drp-format/__tests__/composition-text.test.js +98 -0
  185. package/resolve-advanced/vendor/drp-format/__tests__/diff.test.js +272 -0
  186. package/resolve-advanced/vendor/drp-format/__tests__/effect-filters-compressed.test.js +143 -0
  187. package/resolve-advanced/vendor/drp-format/__tests__/fixtures/p0-3-session30-empty-effectfilters.drp +0 -0
  188. package/resolve-advanced/vendor/drp-format/__tests__/fixtures/retimed-timemap-50pct.hex +1 -0
  189. package/resolve-advanced/vendor/drp-format/__tests__/fixtures/retimed-timemap-dynamic.hex +1 -0
  190. package/resolve-advanced/vendor/drp-format/__tests__/inject-grades.test.js +238 -0
  191. package/resolve-advanced/vendor/drp-format/__tests__/keyed-dict.test.js +85 -0
  192. package/resolve-advanced/vendor/drp-format/__tests__/lut-refs.test.js +119 -0
  193. package/resolve-advanced/vendor/drp-format/__tests__/media-blobs.test.js +25 -0
  194. package/resolve-advanced/vendor/drp-format/__tests__/media-timemap.test.js +119 -0
  195. package/resolve-advanced/vendor/drp-format/__tests__/place-fusion-title.test.js +52 -0
  196. package/resolve-advanced/vendor/drp-format/__tests__/place-generator.test.js +40 -0
  197. package/resolve-advanced/vendor/drp-format/__tests__/place-transition.test.js +48 -0
  198. package/resolve-advanced/vendor/drp-format/__tests__/protobuf-wire.test.js +64 -0
  199. package/resolve-advanced/vendor/drp-format/__tests__/relink-media.test.js +106 -0
  200. package/resolve-advanced/vendor/drp-format/__tests__/splice-clips.test.js +256 -0
  201. package/resolve-advanced/vendor/drp-format/assemble-timeline.js +65 -0
  202. package/resolve-advanced/vendor/drp-format/author-project.js +118 -0
  203. package/resolve-advanced/vendor/drp-format/composition-text.js +213 -0
  204. package/resolve-advanced/vendor/drp-format/curves-encoder.js +763 -0
  205. package/resolve-advanced/vendor/drp-format/diff.js +310 -0
  206. package/resolve-advanced/vendor/drp-format/drp-packager.js +201 -0
  207. package/resolve-advanced/vendor/drp-format/drp-validator.js +809 -0
  208. package/resolve-advanced/vendor/drp-format/effect-encoder.js +640 -0
  209. package/resolve-advanced/vendor/drp-format/extract-lut-refs.js +119 -0
  210. package/resolve-advanced/vendor/drp-format/grade-encoder.example.js +133 -0
  211. package/resolve-advanced/vendor/drp-format/grade-encoder.js +401 -0
  212. package/resolve-advanced/vendor/drp-format/grade-encoder.test.js +171 -0
  213. package/resolve-advanced/vendor/drp-format/grade-encoder.verify.js +249 -0
  214. package/resolve-advanced/vendor/drp-format/grade-node-extractor.js +495 -0
  215. package/resolve-advanced/vendor/drp-format/grade-parameter-decoder.js +774 -0
  216. package/resolve-advanced/vendor/drp-format/index.js +174 -0
  217. package/resolve-advanced/vendor/drp-format/inject-grades.js +219 -0
  218. package/resolve-advanced/vendor/drp-format/keyed-dict.js +227 -0
  219. package/resolve-advanced/vendor/drp-format/marker-encoder.js +532 -0
  220. package/resolve-advanced/vendor/drp-format/media-blobs.js +53 -0
  221. package/resolve-advanced/vendor/drp-format/media-timemap.js +164 -0
  222. package/resolve-advanced/vendor/drp-format/mp-folder-builder.js +293 -0
  223. package/resolve-advanced/vendor/drp-format/node-tree-encoder.js +1246 -0
  224. package/resolve-advanced/vendor/drp-format/package.json +18 -0
  225. package/resolve-advanced/vendor/drp-format/place-fusion-title.js +131 -0
  226. package/resolve-advanced/vendor/drp-format/place-generator.js +77 -0
  227. package/resolve-advanced/vendor/drp-format/place-transition.js +91 -0
  228. package/resolve-advanced/vendor/drp-format/power-window-encoder.js +776 -0
  229. package/resolve-advanced/vendor/drp-format/protobuf-wire.js +111 -0
  230. package/resolve-advanced/vendor/drp-format/qualifier-encoder.js +706 -0
  231. package/resolve-advanced/vendor/drp-format/relink-media.js +218 -0
  232. package/resolve-advanced/vendor/drp-format/rich-title-encoder.js +447 -0
  233. package/resolve-advanced/vendor/drp-format/seq-container-builder.js +572 -0
  234. package/resolve-advanced/vendor/drp-format/seq-surgery.js +155 -0
  235. package/resolve-advanced/vendor/drp-format/splice-clips.js +445 -0
  236. package/resolve-advanced/vendor/drp-format/templates/empty-project.drp +0 -0
  237. package/resolve-advanced/vendor/drp-format/templates/fusion-title.xml +56 -0
  238. package/resolve-advanced/vendor/drp-format/templates/generator-solid-color.xml +21 -0
  239. package/resolve-advanced/vendor/drp-format/templates/media-clip-h264.drp +0 -0
  240. package/resolve-advanced/vendor/drp-format/templates/transition-cross-dissolve.xml +22 -0
  241. package/resolve-advanced/vendor/drp-format/tool-registry.descriptor.js +80 -0
  242. package/resolve-advanced/vendor/drp-format/utils/safe-archive.js +54 -0
  243. package/resolve-advanced/vendor/drp-format/xml-builder.js +759 -0
  244. package/resolve-advanced/vendor/drt-format/__tests__/_resolve-verify.js +21 -0
  245. package/resolve-advanced/vendor/drt-format/__tests__/build.test.js +113 -0
  246. package/resolve-advanced/vendor/drt-format/__tests__/multi-format.test.js +93 -0
  247. package/resolve-advanced/vendor/drt-format/__tests__/parse-real-resolve21.test.js +51 -0
  248. package/resolve-advanced/vendor/drt-format/__tests__/parse.test.js +134 -0
  249. package/resolve-advanced/vendor/drt-format/__tests__/resolve-versions.test.js +120 -0
  250. package/resolve-advanced/vendor/drt-format/__tests__/schema-fingerprint.test.js +62 -0
  251. package/resolve-advanced/vendor/drt-format/__tests__/smoke.test.js +27 -0
  252. package/resolve-advanced/vendor/drt-format/__tests__/validate.test.js +109 -0
  253. package/resolve-advanced/vendor/drt-format/capabilities.js +69 -0
  254. package/resolve-advanced/vendor/drt-format/capability-domains.js +56 -0
  255. package/resolve-advanced/vendor/drt-format/drt-builder.js +42 -0
  256. package/resolve-advanced/vendor/drt-format/drt-parser.js +164 -0
  257. package/resolve-advanced/vendor/drt-format/drt-validator.js +98 -0
  258. package/resolve-advanced/vendor/drt-format/index.js +45 -0
  259. package/resolve-advanced/vendor/drt-format/package.json +17 -0
  260. package/resolve-advanced/vendor/drt-format/resolve-versions.js +153 -0
  261. package/resolve-advanced/vendor/drt-format/schema-fingerprint.js +74 -0
  262. package/resolve-advanced/vendor/drt-format/templates/drt-project-shell.xml +124 -0
  263. package/resolve-advanced/vendor/drx-codec/__tests__/curves-roundtrip.test.js +148 -0
  264. package/resolve-advanced/vendor/drx-codec/__tests__/fixtures/p2-1-face-refinement.drx +58 -0
  265. package/resolve-advanced/vendor/drx-codec/__tests__/fixtures/p2-1-filmgrain.drx +58 -0
  266. package/resolve-advanced/vendor/drx-codec/__tests__/fixtures/p2-1-glow.drx +58 -0
  267. package/resolve-advanced/vendor/drx-codec/__tests__/fixtures/p2-1-lens-flare.drx +58 -0
  268. package/resolve-advanced/vendor/drx-codec/__tests__/fixtures/p2-3-beauty-25.drx +58 -0
  269. package/resolve-advanced/vendor/drx-codec/__tests__/fixtures/p2-3-beauty-75.drx +58 -0
  270. package/resolve-advanced/vendor/drx-codec/__tests__/fixtures/p5-1-node-no-lut.drx +58 -0
  271. package/resolve-advanced/vendor/drx-codec/__tests__/fixtures/p5-1-node-with-lut.drx +58 -0
  272. package/resolve-advanced/vendor/drx-codec/__tests__/hsl-curves-roundtrip.test.js +117 -0
  273. package/resolve-advanced/vendor/drx-codec/__tests__/lut-refs-roundtrip.test.js +112 -0
  274. package/resolve-advanced/vendor/drx-codec/__tests__/matte-roundtrip.test.js +167 -0
  275. package/resolve-advanced/vendor/drx-codec/__tests__/ofx-roundtrip.test.js +215 -0
  276. package/resolve-advanced/vendor/drx-codec/__tests__/ofx-slugs-discovery.test.js +149 -0
  277. package/resolve-advanced/vendor/drx-codec/__tests__/power-window-roundtrip.test.js +230 -0
  278. package/resolve-advanced/vendor/drx-codec/__tests__/qualifier-roundtrip.test.js +226 -0
  279. package/resolve-advanced/vendor/drx-codec/cdl-exporter.js +470 -0
  280. package/resolve-advanced/vendor/drx-codec/color-nlp-parser.js +833 -0
  281. package/resolve-advanced/vendor/drx-codec/data/color-nlp-action-mappings.json +2232 -0
  282. package/resolve-advanced/vendor/drx-codec/drx-generator.js +4123 -0
  283. package/resolve-advanced/vendor/drx-codec/drx-merger.js +320 -0
  284. package/resolve-advanced/vendor/drx-codec/drx-parser.js +1808 -0
  285. package/resolve-advanced/vendor/drx-codec/extract-custom-curves.js +215 -0
  286. package/resolve-advanced/vendor/drx-codec/extract-hsl-curves.js +180 -0
  287. package/resolve-advanced/vendor/drx-codec/extract-lut-refs.js +158 -0
  288. package/resolve-advanced/vendor/drx-codec/extract-matte-finesse.js +83 -0
  289. package/resolve-advanced/vendor/drx-codec/extract-ofx-params.js +226 -0
  290. package/resolve-advanced/vendor/drx-codec/extract-power-window.js +184 -0
  291. package/resolve-advanced/vendor/drx-codec/extract-qualifier.js +99 -0
  292. package/resolve-advanced/vendor/drx-codec/index.js +109 -0
  293. package/resolve-advanced/vendor/drx-codec/node-layout.js +249 -0
  294. package/resolve-advanced/vendor/drx-codec/resolved-drx-generator.js +393 -0
  295. package/resolve-advanced/vendor/drx-codec/tool-resolver.js +974 -0
  296. package/resolve-advanced/vendor/drx-codec/training-examples.js +548 -0
  297. package/resolve-advanced/vendor/drx-codec/vocabulary-intents.js +539 -0
  298. package/resolve-advanced/vendor/drx-parameters/CALIBRATION-STATUS.md +319 -0
  299. package/resolve-advanced/vendor/drx-parameters/DRX-VALUE-SCALING.md +251 -0
  300. package/resolve-advanced/vendor/drx-parameters/calibrated-ranges.json +17737 -0
  301. package/resolve-advanced/vendor/drx-parameters/corrector-types.js +120 -0
  302. package/resolve-advanced/vendor/drx-parameters/index.js +193 -0
  303. package/resolve-advanced/vendor/drx-parameters/parameter-codec.js +451 -0
  304. package/resolve-advanced/vendor/drx-parameters/parameter-ids.js +1292 -0
  305. package/resolve-advanced/vendor/drx-parameters/parameter-ranges.js +570 -0
  306. package/resolve-advanced/vendor/drx-parameters/parameter-validator.js +392 -0
  307. package/resolve-advanced/vendor/drx-parameters/resolvefx-registry.json +14973 -0
  308. package/resolve-advanced/vendor/drx-parameters/tool-registry.descriptor.js +38 -0
  309. package/resolve-advanced/vendor/drx-parameters/tools/drx-analyzer.js +371 -0
  310. package/resolve-advanced/vendor/fairlight/index.js +833 -0
  311. package/resolve-advanced/vendor/fusion-codec/composition-generator.js +401 -0
  312. package/resolve-advanced/vendor/fusion-codec/templates/blur-region.js +103 -0
  313. package/resolve-advanced/vendor/fusion-codec/templates/color-correct.js +87 -0
  314. package/resolve-advanced/vendor/fusion-codec/templates/film-grain.js +106 -0
  315. package/resolve-advanced/vendor/fusion-codec/templates/lower-third.js +241 -0
  316. package/resolve-advanced/vendor/fusion-codec/templates/picture-in-picture.js +188 -0
  317. package/resolve-advanced/vendor/fusion-codec/templates/text-overlay.js +161 -0
  318. package/resolve-advanced/vendor/fusion-codec/templates/title-card.js +184 -0
  319. package/resolve-advanced/vendor/fusion-codec/templates/vignette.js +107 -0
  320. package/resolve-advanced/vendor/fusion-codec/templates/watermark.js +101 -0
  321. package/scripts/agent-rules/README.md +68 -0
  322. package/src/analysis_dashboard.py +233 -1
  323. package/src/granular/common.py +1 -1
  324. package/src/server.py +479 -29
  325. package/src/utils/api_truth.py +117 -0
  326. package/src/utils/cut_ir.py +1 -1
  327. package/src/utils/failure_tracker.py +0 -2
  328. package/src/utils/timeline_xml.py +405 -0
@@ -0,0 +1,246 @@
1
+ /**
2
+ * grade-body-patch — surgically set/clear a node's LABEL (field F6) or COLOR (field F15) inside a DRX
3
+ * grade Body, WITHOUT going through the lossy generator. Node convention/provenance spec (2026-06-22).
4
+ *
5
+ * Why surgical: the drx generator only re-emits primaries (functional test: it drops Color Warper /
6
+ * curves / windows / keyframes on re-encode). So to change a label/color we must edit the COMPRESSED
7
+ * Body in place: decompress → byte-copy every untouched field verbatim, splice only the F6/F15 string,
8
+ * fix the enclosing length prefixes → recompress. Everything other than the edited string stays
9
+ * byte-identical (lossless), the same philosophy as the offline-ref / reel-name DB patches.
10
+ *
11
+ * Body format: [0x81] + zstd(protobuf). Node tree: root → F1 → F7[] (nodes). Node fields: F6 = label
12
+ * (string), F15 = color (string "ClipColor<Name>"). Absent F6/F15 = no label / no color.
13
+ *
14
+ * patchGradeBody(bodyHex, nodeIndex, { label, color }):
15
+ * label/color: undefined = leave unchanged · null = clear (remove the field) · string = set.
16
+ * color "Blue" is written as "ClipColorBlue"; reading strips the prefix (see drx-parser node.color).
17
+ */
18
+
19
+ import * as fzstd from 'fzstd';
20
+ import nodeZlib from 'node:zlib';
21
+ import zstdCodecPkg from 'zstd-codec';
22
+ import parserPkg from '../vendor/drx-codec/drx-parser.js';
23
+ import { createRequire } from 'node:module';
24
+
25
+ const require = createRequire(import.meta.url);
26
+ const { NODE_LUT_REF } = require('../vendor/drx-parameters');
27
+ const { ProtobufParser } = parserPkg;
28
+ const { ZstdCodec } = zstdCodecPkg;
29
+
30
+ const HAS_NATIVE_ZSTD = typeof nodeZlib.zstdCompressSync === 'function';
31
+
32
+ // ── varint / field encoders ─────────────────────────────────────────────────
33
+ function encodeVarint(n) {
34
+ const out = [];
35
+ let v = n >>> 0 === n ? n : Number(n);
36
+ v = Math.trunc(v);
37
+ do {
38
+ let b = v & 0x7f;
39
+ v = Math.floor(v / 128);
40
+ if (v > 0) b |= 0x80;
41
+ out.push(b);
42
+ } while (v > 0);
43
+ return Buffer.from(out);
44
+ }
45
+ function encodeLenDelimField(fieldNum, content) {
46
+ const tag = (fieldNum << 3) | 2;
47
+ return Buffer.concat([encodeVarint(tag), encodeVarint(content.length), content]);
48
+ }
49
+
50
+ // ── byte-range rebuild of a protobuf container ───────────────────────────────
51
+ // fields = a ProtobufParser._fields array (in wire order, each with.pos relative to `content`).
52
+ // replaceMap: Map<fieldEntry, Buffer> — emit these bytes instead of the field's original range.
53
+ // removeFieldNums: Set<number> — drop these fields entirely. appends: extra field Buffers at the end.
54
+ function rebuildContainer(content, fields, { replaceMap = null, removeFieldNums = null, appends = [] } = {}) {
55
+ const parts = [];
56
+ for (let i = 0; i < fields.length; i++) {
57
+ const f = fields[i];
58
+ const start = f.pos;
59
+ const end = i + 1 < fields.length ? fields[i + 1].pos : content.length;
60
+ if (removeFieldNums && removeFieldNums.has(f.fieldNum)) continue;
61
+ if (replaceMap && replaceMap.has(f)) parts.push(replaceMap.get(f));
62
+ else parts.push(content.subarray(start, end));
63
+ }
64
+ for (const a of appends) parts.push(a);
65
+ return Buffer.concat(parts);
66
+ }
67
+
68
+ // Rebuild a node's content, inserting/replacing fields in ASCENDING field-number order. CRITICAL: Resolve
69
+ // writes node fields sorted by field number (F1 F2 F4 F5 F6 F7 F8 F9 F10 F12 F15) and its loader reads the
70
+ // label (F6) from its canonical slot (between F5 and F7) — appending F6 at the end means Resolve never sees
71
+ // it. So we emit a field-number-ordered merge of the kept fields + the new ones (RE'd live 2026-06-22).
72
+ function rebuildNodeSorted(content, fields, removeFieldNums, newFields) {
73
+ const items = [];
74
+ for (let i = 0; i < fields.length; i++) {
75
+ const f = fields[i];
76
+ if (removeFieldNums.has(f.fieldNum)) continue;
77
+ const end = i + 1 < fields.length ? fields[i + 1].pos : content.length;
78
+ items.push({ fieldNum: f.fieldNum, buf: content.subarray(f.pos, end) });
79
+ }
80
+ for (const nf of newFields) items.push(nf); // {fieldNum, buf (tag+len+content)}
81
+ items.sort((a, b) => a.fieldNum - b.fieldNum); // stable (ES2019+) → repeats keep relative order
82
+ return Buffer.concat(items.map((x) => x.buf));
83
+ }
84
+
85
+ function asBuf(x) {
86
+ return Buffer.isBuffer(x) ? x : Buffer.from(x);
87
+ }
88
+ function parseContent(buf) {
89
+ return new ProtobufParser(buf).parse(12);
90
+ }
91
+
92
+ // ── zstd compress (native Node 22+ → zstd-codec WASM fallback, matching the generator) ──
93
+ let _wasm = null;
94
+ function getWasm() {
95
+ if (_wasm) return Promise.resolve(_wasm);
96
+ return new Promise((resolve) => {
97
+ ZstdCodec.run((z) => {
98
+ _wasm = new z.Simple();
99
+ resolve(_wasm);
100
+ });
101
+ });
102
+ }
103
+ async function compress(buf) {
104
+ if (HAS_NATIVE_ZSTD) return nodeZlib.zstdCompressSync(buf);
105
+ const w = await getWasm();
106
+ return Buffer.from(w.compress(new Uint8Array(buf)));
107
+ }
108
+ export function decompressBody(bodyHex) {
109
+ const buf = Buffer.from(bodyHex, 'hex');
110
+ if (buf[0] !== 0x81) throw new Error('not a DRX grade Body (missing 0x81 prefix)');
111
+ return Buffer.from(fzstd.decompress(new Uint8Array(buf.subarray(1))));
112
+ }
113
+
114
+ /**
115
+ * Set/clear a node's label (F6) and/or color (F15) in a grade Body. Returns the new Body hex.
116
+ * Everything except the edited string(s) is preserved byte-for-byte in the decompressed protobuf.
117
+ */
118
+ export async function patchGradeBody(bodyHex, nodeIndex, { label, color } = {}) {
119
+ const dec = decompressBody(bodyHex);
120
+ const root = parseContent(dec);
121
+ const f1Entry = root._fields.find((f) => f.fieldNum === 1 && f.wireType === 2);
122
+ if (!f1Entry) throw new Error('grade Body has no F1 (node container)');
123
+ const f1Content = asBuf(f1Entry.rawValue);
124
+ const f1Parsed = parseContent(f1Content);
125
+ const f7Entries = f1Parsed._fields.filter((f) => f.fieldNum === 7 && f.wireType === 2);
126
+ if (nodeIndex < 0 || nodeIndex >= f7Entries.length) throw new Error(`node ${nodeIndex} out of range (have ${f7Entries.length})`);
127
+ const nodeEntry = f7Entries[nodeIndex];
128
+ const nodeContent = asBuf(nodeEntry.rawValue);
129
+ const nodeParsed = parseContent(nodeContent);
130
+
131
+ const removeFieldNums = new Set();
132
+ const newFields = [];
133
+ if (label !== undefined) {
134
+ removeFieldNums.add(6);
135
+ if (label !== null) newFields.push({ fieldNum: 6, buf: encodeLenDelimField(6, Buffer.from(String(label), 'utf8')) });
136
+ }
137
+ if (color !== undefined) {
138
+ removeFieldNums.add(15);
139
+ if (color !== null) newFields.push({ fieldNum: 15, buf: encodeLenDelimField(15, Buffer.from(`ClipColor${color}`, 'utf8')) });
140
+ }
141
+
142
+ // Insert F6/F15 in canonical (ascending field-number) order — Resolve's loader is position-sensitive.
143
+ const newNodeContent = rebuildNodeSorted(nodeContent, nodeParsed._fields, removeFieldNums, newFields);
144
+ const newF7 = encodeLenDelimField(7, newNodeContent);
145
+ const newF1Content = rebuildContainer(f1Content, f1Parsed._fields, { replaceMap: new Map([[nodeEntry, newF7]]) });
146
+ const newF1 = encodeLenDelimField(1, newF1Content);
147
+ const newRoot = rebuildContainer(dec, root._fields, { replaceMap: new Map([[f1Entry, newF1]]) });
148
+
149
+ const compressed = await compress(newRoot);
150
+ return '81' + Buffer.from(compressed).toString('hex');
151
+ }
152
+
153
+ // ── LUT-node WRITE path (P3, RE'd 2026-07-01 from the p5-1 fixtures) ─────────────
154
+ // A node's F9 holds [F1 corrector?][F2 5-byte tail]. A LUT attaches an F1 corrector whose nested
155
+ // params carry SLOT_META (varint) + LUT_PATH (F5 string). Byte-exact reconstruction of the captured
156
+ // with-LUT fixture confirmed the encoding (see lut-apply round-trip test). Constant wire shape:
157
+ // corrector = F1{ 08 01, 18 01, F6{ F2{ 08 01, F3{SLOT_META param}, F3{LUT_PATH param} } } }
158
+ // SLOT_META param = 08 <id> 12{ 10 <slot> } (value envelope F2 = varint slot)
159
+ // LUT_PATH param = 08 <id> 12{ 2a <len> <path> } (value envelope F5 = string)
160
+ function buildLutCorrector(lutPath, slotMeta) {
161
+ const slotEnv = encodeLenDelimField(2, Buffer.concat([Buffer.from([0x10]), encodeVarint(slotMeta)]));
162
+ const slotParam = Buffer.concat([Buffer.from([0x08]), encodeVarint(NODE_LUT_REF.SLOT_META), slotEnv]);
163
+ const pathEnv = encodeLenDelimField(2, encodeLenDelimField(5, Buffer.from(lutPath, 'utf8')));
164
+ const pathParam = Buffer.concat([Buffer.from([0x08]), encodeVarint(NODE_LUT_REF.LUT_PATH), pathEnv]);
165
+ const paramsList = Buffer.concat([Buffer.from([0x08, 0x01]), encodeLenDelimField(3, slotParam), encodeLenDelimField(3, pathParam)]);
166
+ const f6 = encodeLenDelimField(6, encodeLenDelimField(2, paramsList));
167
+ const corrector = Buffer.concat([Buffer.from([0x08, 0x01, 0x18, 0x01]), f6]);
168
+ return encodeLenDelimField(1, corrector); // the F9-inner F1 field bytes
169
+ }
170
+
171
+ const DEFAULT_F9_TAIL = Buffer.from('12050304050612', 'hex'); // the F2 common tail all nodes carry
172
+
173
+ /**
174
+ * Inject a per-node LUT reference (LUT_PATH + SLOT_META) into a grade Body, replacing any existing
175
+ * LUT corrector on that node. Returns the new Body hex. Everything else is preserved byte-for-byte.
176
+ * @param {string} bodyHex
177
+ * @param {number} nodeIndex
178
+ * @param {{lutPath:string, slotMeta?:number}} opts
179
+ */
180
+ export async function injectNodeLut(bodyHex, nodeIndex, { lutPath, slotMeta = 6 } = {}) {
181
+ if (!lutPath || typeof lutPath !== 'string') throw new Error('injectNodeLut: lutPath (string) required');
182
+ const dec = decompressBody(bodyHex);
183
+ const root = parseContent(dec);
184
+ const f1Entry = root._fields.find((f) => f.fieldNum === 1 && f.wireType === 2);
185
+ if (!f1Entry) throw new Error('grade Body has no F1 (node container)');
186
+ const f1Content = asBuf(f1Entry.rawValue);
187
+ const f1Parsed = parseContent(f1Content);
188
+ const f7Entries = f1Parsed._fields.filter((f) => f.fieldNum === 7 && f.wireType === 2);
189
+ if (nodeIndex < 0 || nodeIndex >= f7Entries.length) throw new Error(`node ${nodeIndex} out of range (have ${f7Entries.length})`);
190
+ const nodeEntry = f7Entries[nodeIndex];
191
+ const nodeContent = asBuf(nodeEntry.rawValue);
192
+ const nodeParsed = parseContent(nodeContent);
193
+
194
+ const corrector = buildLutCorrector(lutPath, slotMeta);
195
+ // Keep the existing F9 TAIL (F2), drop any prior F1 LUT corrector, prepend the new one.
196
+ const f9Entry = nodeParsed._fields.find((f) => f.fieldNum === 9 && f.wireType === 2);
197
+ let tail = DEFAULT_F9_TAIL;
198
+ if (f9Entry) {
199
+ const f9Content = asBuf(f9Entry.rawValue);
200
+ const f9Parsed = parseContent(f9Content);
201
+ tail = rebuildContainer(f9Content, f9Parsed._fields, { removeFieldNums: new Set([1]) });
202
+ }
203
+ const newF9 = encodeLenDelimField(9, Buffer.concat([corrector, tail]));
204
+ const newNodeContent = rebuildNodeSorted(nodeContent, nodeParsed._fields, new Set([9]), [{ fieldNum: 9, buf: newF9 }]);
205
+ const newF7 = encodeLenDelimField(7, newNodeContent);
206
+ const newF1Content = rebuildContainer(f1Content, f1Parsed._fields, { replaceMap: new Map([[nodeEntry, newF7]]) });
207
+ const newF1 = encodeLenDelimField(1, newF1Content);
208
+ const newRoot = rebuildContainer(dec, root._fields, { replaceMap: new Map([[f1Entry, newF1]]) });
209
+ const compressed = await compress(newRoot);
210
+ return '81' + Buffer.from(compressed).toString('hex');
211
+ }
212
+
213
+ // Read a node's label (F6) + color (F15) straight from a Body — used for read-back verification.
214
+ export function readNodeMeta(bodyHex, nodeIndex = 0) {
215
+ const dec = decompressBody(bodyHex);
216
+ const root = parseContent(dec);
217
+ const f1Entry = root._fields.find((f) => f.fieldNum === 1 && f.wireType === 2);
218
+ if (!f1Entry) return null;
219
+ const f1Parsed = parseContent(asBuf(f1Entry.rawValue));
220
+ const f7 = f1Parsed._fields.filter((f) => f.fieldNum === 7 && f.wireType === 2)[nodeIndex];
221
+ if (!f7) return null;
222
+ const node = parseContent(asBuf(f7.rawValue));
223
+ const str = (fn) => {
224
+ const e = node._fields.find((f) => f.fieldNum === fn && f.wireType === 2);
225
+ return e ? asBuf(e.rawValue).toString('utf8') : null;
226
+ };
227
+ const label = str(6);
228
+ const rawColor = str(15);
229
+ return { label: label || null, color: rawColor ? rawColor.replace(/^ClipColor/, '') : null };
230
+ }
231
+
232
+ // Exposed for tests: the field-number order of a node (must stay ascending — Resolve reads positionally).
233
+ export function _nodeFieldOrder(bodyHex, nodeIndex = 0) {
234
+ const dec = decompressBody(bodyHex);
235
+ const root = parseContent(dec);
236
+ const f1 = parseContent(asBuf(root._fields.find((f) => f.fieldNum === 1 && f.wireType === 2).rawValue));
237
+ const node = parseContent(asBuf(f1._fields.filter((f) => f.fieldNum === 7 && f.wireType === 2)[nodeIndex].rawValue));
238
+ return node._fields.map((f) => f.fieldNum);
239
+ }
240
+
241
+ // Exposed for the faithful-round-trip self-test (no-op rebuild must reproduce the decompressed bytes).
242
+ export function _noopRebuild(bodyHex) {
243
+ const dec = decompressBody(bodyHex);
244
+ const root = parseContent(dec);
245
+ return rebuildContainer(dec, root._fields, {});
246
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Lossless grade transfer — Route A copy (C3 `grade_transfer`).
3
+ *
4
+ * Carry an EXACT grade from a source onto an apply-ready .drx, WITHOUT decoding and
5
+ * re-encoding (decode→re-encode is lossy for OFX/ResolveFX and the uncalibrated tail).
6
+ * We copy the raw <Body> blob verbatim and re-wrap it in the Gallery::GyStill envelope
7
+ * Resolve's ApplyGradeFromDRX actually applies — so the transfer is byte-faithful.
8
+ *
9
+ * Sources:
10
+ * { drpPath, group, which?:'post'|'pre' } — a colour-group look from an exported .drp
11
+ * (the Route A workflow: read the season host look, transfer to this episode).
12
+ * { drxPath } | { content } — re-wrap an existing .drx's grade body.
13
+ *
14
+ * GUARD: the wrapped body is parsed and must decode to ≥1 node — a transfer that yields
15
+ * an empty grade is refused, not shipped (the silent-no-op bug class).
16
+ * LOCAL & deterministic; emits the .drx. APPLY (ApplyGradeFromDRX) is the caller's job.
17
+ */
18
+ import fs from 'node:fs';
19
+ import { readProjectXml, groupSegment, groupBodies } from './group-grade-read.mjs';
20
+ import { drxEnvelope } from './tools/color_trace.mjs';
21
+ import { drxTool } from './tools/drx.mjs';
22
+
23
+ /** Count nodes a raw body decodes to, via the parse-safe Resolve_Color_Exchange envelope. */
24
+ async function bodyNodeCount(bodyHex) {
25
+ const content = `<?xml version="1.0" encoding="UTF-8"?>\n<Resolve_Color_Exchange><Label>assert</Label><Width>1920</Width><Height>1080</Height><Body>${bodyHex}</Body></Resolve_Color_Exchange>`;
26
+ const r = await drxTool.handler({ action: 'parse', args: { content } });
27
+ return (r.nodes || []).length;
28
+ }
29
+
30
+ /**
31
+ * @param {{drpPath?:string, group?:string, which?:'post'|'pre', drxPath?:string, content?:string}} source
32
+ * @param {{outPath?:string, label?:string}} opts
33
+ * @returns {Promise<{outPath?:string, content:string, nodeCount:number, bodyBytes:number, label:string, source:string}>}
34
+ */
35
+ export async function transferGrade(source = {}, opts = {}) {
36
+ let bodyHex, srcLabel, srcKind;
37
+ if (source.drpPath) {
38
+ if (!source.group) throw new Error('source.group required with drpPath');
39
+ const xml = readProjectXml(source.drpPath);
40
+ const seg = groupSegment(xml, source.group);
41
+ if (!seg) throw new Error(`group '${source.group}' not found in ${source.drpPath}`);
42
+ const bodies = groupBodies(seg);
43
+ if (!bodies.length) throw new Error(`group '${source.group}' has no grade <Body>`);
44
+ const which = source.which || 'post';
45
+ bodyHex = which === 'pre' ? bodies[0] : bodies[bodies.length - 1];
46
+ srcLabel = `${source.group} ${which}`;
47
+ srcKind = `drp:${source.group}:${which}`;
48
+ } else if (source.drxPath || source.content) {
49
+ const xml = source.content || fs.readFileSync(source.drxPath, 'utf8');
50
+ const m = xml.match(/<Body>([0-9a-fA-F]+)<\/Body>/);
51
+ if (!m) throw new Error('no <Body> blob in source.drx');
52
+ bodyHex = m[1];
53
+ srcLabel = source.drxPath ? source.drxPath.split('/').pop() : 'drx';
54
+ srcKind = `drx:${srcLabel}`;
55
+ } else {
56
+ throw new Error('provide source.drpPath+group, or source.drxPath/content');
57
+ }
58
+
59
+ const nodeCount = await bodyNodeCount(bodyHex);
60
+ if (nodeCount === 0) throw new Error(`grade_transfer refused: source '${srcKind}' decodes to 0 nodes (empty grade)`);
61
+
62
+ const label = opts.label || srcLabel;
63
+ const content = drxEnvelope(label, bodyHex);
64
+ if (opts.outPath) fs.writeFileSync(opts.outPath, content);
65
+ return { outPath: opts.outPath, content, nodeCount, bodyBytes: bodyHex.length / 2, label, source: srcKind };
66
+ }
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Color-group grade READ PATH (Route A) — decode a project's Color Group pre/post-clip
3
+ * grades from an exported .drp, OFFLINE, no Resolve required.
4
+ *
5
+ * Closes the gap noted in test/group-grade-calibration.test.mjs: a group's Pre/Post-Clip
6
+ * grade is stored as a standard DRX `<Body>` inside `project.xml`'s `<Sm2Group>` element —
7
+ * same format as a clip grade, so the existing drx-parser decodes it unchanged. The only
8
+ * missing piece was a READ PATH (locate the group bodies); this is it.
9
+ *
10
+ * Pipeline:.drp (zip) -> project.xml -> per-group <Body> (pre, post) -> drx-parser.parse
11
+ * -> flatten (LUT refs, HSL curves) -> DRX->UI scale -> compact per-node summary.
12
+ *
13
+ * Values for the calibrated native control set (primaries, log wheels, curves, HSL/RGB/3D
14
+ * qualifier ranges, windows, Color Warper, ColorSlice, HDR zones) are EXACT; OFX/long-tail
15
+ * IDs are raw (see parse().valueFidelity).
16
+ */
17
+ import fs from 'node:fs';
18
+ import os from 'node:os';
19
+ import path from 'node:path';
20
+ import { spawnSync } from 'node:child_process';
21
+ import { createRequire } from 'node:module';
22
+ import { drxTool } from './tools/drx.mjs';
23
+
24
+ const require = createRequire(import.meta.url);
25
+ const { extractDrxLutRefs } = require('../vendor/drx-codec/extract-lut-refs.js');
26
+ const { extractHSLCurves } = require('../vendor/drx-codec/extract-hsl-curves.js');
27
+
28
+ // DRX -> UI scaling (vendor/drx-parameters/DRX-VALUE-SCALING.md)
29
+ export function scaleParam(name, v) {
30
+ if (typeof v !== 'number') return v;
31
+ const n = name.toLowerCase();
32
+ if (/saturation/.test(n)) return +(v * 100).toFixed(2); // %
33
+ if (/contrast/.test(n)) return +(v * 100).toFixed(2); // %
34
+ if (/(^|\.)lift/.test(n)) return +(v / 2).toFixed(4);
35
+ if (/(^|\.)gamma/.test(n)) return +(v / 4).toFixed(4);
36
+ if (/offset/.test(n)) return +(v * 2500).toFixed(2);
37
+ return +(+v).toFixed(4);
38
+ }
39
+
40
+ const IDENTITY_3X3 = '1,0,0,0,1,0,0,0,1';
41
+ const isIdentity = (a) => Array.isArray(a) && a.length === 9 && a.join(',') === IDENTITY_3X3;
42
+ const STRUCTURAL = /hslcurves|trackingblob|polygonshape|gradientwindow|nodelut|softmatrix|(^|\.)matrix$/i;
43
+
44
+ export function readProjectXml(drpPath) {
45
+ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ggr-'));
46
+ const r = spawnSync('unzip', ['-o', drpPath, 'project.xml', '-d', tmp], { encoding: 'utf8' });
47
+ if (r.status !== 0) throw new Error(`unzip failed for ${drpPath}: ${(r.stderr || '').slice(-200)}`);
48
+ const xml = fs.readFileSync(path.join(tmp, 'project.xml'), 'utf8');
49
+ try {
50
+ fs.rmSync(tmp, { recursive: true, force: true });
51
+ } catch {
52
+ /* ignore */
53
+ }
54
+ return xml;
55
+ }
56
+
57
+ /** All color-group names present in the project (from <Sm2Group><Name>…). */
58
+ export function listGroupNames(xml) {
59
+ const names = [];
60
+ for (const m of xml.matchAll(/<Sm2Group\b[\s\S]*?<Name>([^<]+)<\/Name>/g)) names.push(m[1]);
61
+ return [...new Set(names)];
62
+ }
63
+
64
+ export function groupSegment(xml, name) {
65
+ const j = xml.indexOf(`<Name>${name}</Name>`);
66
+ if (j < 0) return null;
67
+ const start = xml.lastIndexOf('<Sm2Group', j);
68
+ const end = xml.indexOf('</Sm2Group>', j);
69
+ if (start < 0 || end < 0) return null;
70
+ return xml.slice(start, end + '</Sm2Group>'.length);
71
+ }
72
+
73
+ export const groupBodies = (seg) => [...seg.matchAll(/<Body>([0-9a-fA-F]+)<\/Body>/g)].map((m) => m[1]);
74
+
75
+ async function decodeBody(bodyHex, label) {
76
+ const content = `<?xml version="1.0" encoding="UTF-8"?>\n<Resolve_Color_Exchange><Label>${label}</Label><Width>1920</Width><Height>1080</Height><Body>${bodyHex}</Body></Resolve_Color_Exchange>`;
77
+ let r;
78
+ try {
79
+ r = await drxTool.handler({ action: 'parse', args: { content } });
80
+ } catch (e) {
81
+ return { error: String(e.message || e), node_count: 0, nodes: [] };
82
+ }
83
+ const lutRefs = {};
84
+ for (const l of extractDrxLutRefs(r) || []) lutRefs[l.nodeIndex] = l.lutPath;
85
+ const nodes = (r.nodes || [])
86
+ .map((n) => {
87
+ const idx = n.nodeIndex ?? n.index;
88
+ const all = (n.correctors || []).flatMap((c) => c.parameters || []);
89
+ const params = {};
90
+ for (const p of all) {
91
+ if (!p.name || /^unknown/i.test(p.name) || STRUCTURAL.test(p.name)) continue;
92
+ if (typeof p.value === 'object') continue;
93
+ if (typeof p.value === 'number' && Math.abs(p.value) < 1e-6) continue;
94
+ params[p.name] = scaleParam(p.name, p.value);
95
+ }
96
+ let curves;
97
+ try {
98
+ const h = extractHSLCurves(all);
99
+ if (h) curves = Object.keys(h);
100
+ } catch {
101
+ /* ignore */
102
+ }
103
+ const win = all.find((p) => /softmatrix|polygonshape\.matrix|gradientwindow/i.test(p.name) && !isIdentity(p.value));
104
+ const node = { node: idx, tools: (n.correctors || []).map((c) => c.type ?? c.correctorType) };
105
+ if (lutRefs[idx]) node.lut = lutRefs[idx];
106
+ if (Object.keys(params).length) node.params = params;
107
+ if (curves && curves.length) node.curves = curves;
108
+ if (win) node.window = true;
109
+ return node;
110
+ })
111
+ .filter((n) => n.params || n.lut || n.curves || n.window);
112
+ return { node_count: (r.nodes || []).length, valueFidelity: r.valueFidelity?.level ?? null, nodes };
113
+ }
114
+
115
+ /**
116
+ * Decode color-group grades from a .drp.
117
+ * @param {string} drpPath
118
+ * @param {{groups?: string[], includePreClip?: boolean}} [opts]
119
+ * @returns {Promise<Object>} { <group>: { pre_clip?, post_clip } }
120
+ */
121
+ export async function decodeGroupGrades(drpPath, opts = {}) {
122
+ const xml = readProjectXml(drpPath);
123
+ const groups = opts.groups && opts.groups.length ? opts.groups : listGroupNames(xml);
124
+ const out = {};
125
+ for (const g of groups) {
126
+ const seg = groupSegment(xml, g);
127
+ if (!seg) {
128
+ out[g] = { error: 'group not found' };
129
+ continue;
130
+ }
131
+ const bs = groupBodies(seg); // typically [pre, post]
132
+ out[g] = {};
133
+ if (bs.length >= 2 && opts.includePreClip !== false) out[g].pre_clip = await decodeBody(bs[0], `${g} pre`);
134
+ if (bs.length >= 1) out[g].post_clip = await decodeBody(bs[bs.length - 1], `${g} post`);
135
+ }
136
+ return out;
137
+ }
@@ -0,0 +1,105 @@
1
+ /**
2
+ * davinci-resolve-advanced-mcp — MCP server (stdio).
3
+ *
4
+ * The "beyond-the-API" sibling to the live Python davinci-resolve-mcp: authors
5
+ * and edits DaVinci Resolve files (.drp/.drt/.drx) with no Resolve running.
6
+ * Runs cloud or local. Tools dispatch on an `action` enum, mirroring the
7
+ * Python server's shape.
8
+ */
9
+
10
+ // CRITICAL (stdio MCP): stdout is the JSON-RPC channel — any stray write
11
+ // corrupts the protocol. Several vendored libs (e.g. drx-generator) use
12
+ // console.log for debug output. Redirect the stdout-bound console methods to
13
+ // stderr BEFORE anything else loads, so vendored chatter can't break the wire.
14
+ for (const m of ['log', 'info', 'debug']) {
15
+ const orig = console[m].bind(console);
16
+ console[m] = (...a) => {
17
+ try {
18
+ process.stderr.write(a.map(String).join(' ') + '\n');
19
+ } catch {
20
+ orig(...a);
21
+ }
22
+ };
23
+ }
24
+
25
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
26
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
27
+ import { z } from 'zod';
28
+
29
+ import { drpTool } from './tools/drp.mjs';
30
+ import { drtTool } from './tools/drt.mjs';
31
+ import { drxTool } from './tools/drx.mjs';
32
+ import { offlineRefTool } from './tools/offline_ref.mjs';
33
+ import { fusionTool } from './tools/fusion.mjs';
34
+ import { audioPlanTool } from './tools/audio_plan.mjs';
35
+ import { fairlightTool } from './tools/fairlight.mjs';
36
+ import { audioTool } from './tools/audio.mjs';
37
+ import { conformTool } from './tools/conform.mjs';
38
+ import { projectDbTool } from './tools/project_db.mjs';
39
+ import { projectReadTool } from './tools/project_read.mjs';
40
+ import { colorTraceTool } from './tools/color_trace.mjs';
41
+ import { capabilitiesTool } from './tools/capabilities.mjs';
42
+ import { pipelineTool } from './tools/pipeline.mjs';
43
+ import { deliverableTool } from './tools/deliverable.mjs';
44
+ import { mediaTool } from './tools/media.mjs';
45
+ import { editorialTool } from './tools/editorial.mjs';
46
+ import { provenanceTool } from './tools/provenance.mjs';
47
+ import { logCapabilities } from './capabilities.mjs';
48
+
49
+ const TOOLS = [
50
+ drpTool,
51
+ drtTool,
52
+ drxTool,
53
+ offlineRefTool,
54
+ fusionTool,
55
+ audioPlanTool,
56
+ fairlightTool,
57
+ audioTool,
58
+ conformTool,
59
+ projectDbTool,
60
+ projectReadTool,
61
+ colorTraceTool,
62
+ capabilitiesTool,
63
+ pipelineTool,
64
+ deliverableTool,
65
+ mediaTool,
66
+ editorialTool,
67
+ provenanceTool,
68
+ ];
69
+ const NAME = 'davinci-resolve-advanced-mcp';
70
+ const VERSION = '0.0.0-dev';
71
+
72
+ const toContent = (payload) => ({ content: [{ type: 'text', text: JSON.stringify(payload, null, 2) }] });
73
+ const toError = (err) => ({
74
+ isError: true,
75
+ content: [{ type: 'text', text: JSON.stringify({ error: err?.message || String(err) }, null, 2) }],
76
+ });
77
+
78
+ export async function startServer() {
79
+ const server = new McpServer({ name: NAME, version: VERSION }, { capabilities: { tools: {} } });
80
+
81
+ for (const tool of TOOLS) {
82
+ server.registerTool(
83
+ tool.name,
84
+ {
85
+ description: tool.description,
86
+ inputSchema: {
87
+ action: z.string().describe('Action name within this tool'),
88
+ args: z.object({}).passthrough().optional().describe('Action arguments'),
89
+ },
90
+ },
91
+ async ({ action, args }) => {
92
+ try {
93
+ return toContent(await tool.handler({ action, args: args || {} }));
94
+ } catch (err) {
95
+ return toError(err);
96
+ }
97
+ },
98
+ );
99
+ }
100
+
101
+ await server.connect(new StdioServerTransport());
102
+ // stdio MCP servers must never write to stdout (it's the protocol channel).
103
+ process.stderr.write(`[${NAME}] ready — ${TOOLS.length} tools registered\n`);
104
+ logCapabilities();
105
+ }
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Public library surface (A0) — the importable API of the Resolve-advanced engine, so a
3
+ * downstream application can `import { … } from 'davinci-resolve-advanced-mcp'` instead of
4
+ * only spawning the stdio MCP server: downstream consumers depend on THIS upstream package.
5
+ * The MCP server is a thin shell over these same exports.
6
+ *
7
+ * Grouping: codec (offline .drp/.drt/.drx) · grading (deterministic compute cores) ·
8
+ * pipeline (DB-as-truth foundation) · tools (MCP tool handlers) · mcp (stdio server entry).
9
+ * Publish name/registry is a separate, parked decision — this just makes the boundary
10
+ * explicit and testable under the current package.
11
+ */
12
+
13
+ // ── codec — offline DaVinci file formats (no Resolve) ──────────────────────────
14
+ export * as codec from './libs.mjs';
15
+
16
+ // ── grading — deterministic, local compute cores (the C-tier catalog) ──────────
17
+ export { measureMeanRGB, generateAssertedGainDRX, computeLevels } from './exposure-level.mjs';
18
+ export { isSkin, measureSkinMeanRGB, computeSkinMatch } from './skin-match.mjs';
19
+ export { measureToneRGB, computeShotMatch } from './shot-match.mjs';
20
+ export { measurePatchMeanRGB, computeWhiteBalanceMatch } from './white-balance-match.mjs';
21
+ export { measureBWPoints, generateAssertedAffineDRX, computeContrastNormalize } from './contrast-normalize.mjs';
22
+ export { measureLegal, computeGamutLegal } from './gamut-legal.mjs';
23
+ export { scopeRead, SKIN_LINE_DEG } from './scope-read.mjs';
24
+ export { measureMeanStd, computeMatchToReference } from './match-to-reference.mjs';
25
+ export { measureSaturation, computeSaturationMatch } from './saturation-match.mjs';
26
+ export { measureBlackPoint, computeBlackBalance } from './black-balance.mjs';
27
+ export { measureLumaCDF, computeToneCurve, computeToneCurveTransfer } from './tone-curve-transfer.mjs';
28
+ export { provenanceLabel, provenanceRecord, parseProvenanceLabel, isAutoLabel, gist, TOOL_VERSIONS } from './node-provenance.mjs';
29
+ export { deriveIntentTags, shouldExcludeFromNeutralize, INTENT_EXCLUDE_TAGS } from './shot-intent.mjs';
30
+ export { verifyGrade } from './verify-grade.mjs';
31
+ export { extractFrames, probeStream, assessColorspace, resolvePosition } from './extract-frames.mjs';
32
+ export { importCDL } from './cdl-io.mjs';
33
+ export { transferGrade } from './grade-transfer.mjs';
34
+ export { authorLook, carryLook } from './season-look.mjs';
35
+ export { applyLut, extractBodyHex } from './lut-apply.mjs';
36
+ export { injectNodeLut } from './grade-body-patch.mjs';
37
+ export { scaleParam, listGroupNames, decodeGroupGrades, readProjectXml, groupSegment, groupBodies } from './group-grade-read.mjs';
38
+
39
+ // ── pipeline — DB-as-truth foundation (canonical DB, compile, readback, runner) ─
40
+ export * as projectDb from './project-db.mjs';
41
+ export { deepMerge, validateResolved, resolveDeliverableInherits, compileSpecs, loadYamlDir } from './spec-compile.mjs';
42
+ export { getByPath, recordReadback, detectDrift, reconcile, driftReport } from './readback.mjs';
43
+ export { planRun, executeStage, runAll, approveGate, markStageApplied, rerunStage, resumeRun } from './runner.mjs';
44
+ export { descriptorSchema, CATALOG, getDescriptor, listCatalog, STAGE_PLAN } from './tool-catalog.mjs';
45
+ export { APPLY_CONTRACT, toApplyContract } from './runner-apply-contract.mjs';
46
+
47
+ // ── tools — MCP tool handlers (programmatic dispatch: tool.handler({action, args})) ─
48
+ export { drpTool } from './tools/drp.mjs';
49
+ export { drtTool } from './tools/drt.mjs';
50
+ export { drxTool } from './tools/drx.mjs';
51
+ export { conformTool } from './tools/conform.mjs';
52
+ export { colorTraceTool } from './tools/color_trace.mjs';
53
+ export { offlineRefTool } from './tools/offline_ref.mjs';
54
+ export { fusionTool } from './tools/fusion.mjs';
55
+ export { audioTool } from './tools/audio.mjs';
56
+ export { audioPlanTool } from './tools/audio_plan.mjs';
57
+ export { fairlightTool } from './tools/fairlight.mjs';
58
+ export { projectDbTool } from './tools/project_db.mjs';
59
+ export { projectReadTool } from './tools/project_read.mjs';
60
+ export { capabilitiesTool } from './tools/capabilities.mjs';
61
+ export { pipelineTool } from './tools/pipeline.mjs';
62
+ export { deliverableTool } from './tools/deliverable.mjs';
63
+
64
+ // ── deliverable QC (Cluster D) — compute cores ─────────────────────────
65
+ export { checkDeliverable, deliverableQc, loudnessQc, parseEbur128, reframeBlankingCheck, conformCompleteness, reDeliveryDiff, compareRenders } from './deliverable-qc.mjs';
66
+ export { buildManifest, reconcileManifest, checksumFile } from './render-manifest.mjs';
67
+ export { expandDeliverable, ENTITY_DEFAULTS } from './deliverable-entities.mjs';
68
+ export { probeMedia, ratio } from './ffprobe-media.mjs';
69
+
70
+ // ── media front-end (Cluster M) — compute cores ────────────────────────
71
+ export { mediaTool } from './tools/media.mjs';
72
+ export { sealFiles, verifyFiles, findDupesByHash, ingestVerify, relinkManifest, renamePlan, reelNormalize, projectHygiene, turnoverPackage } from './media-ops.mjs';
73
+ export { mediaInventory, syncByTC, tcToFrames, framesToTc } from './media-inventory.mjs';
74
+
75
+ // ── editorial integrity (Cluster E) — compute cores ────────────────────
76
+ export { editorialTool } from './tools/editorial.mjs';
77
+ export { parseEDL, parseOTIO, parseXMEMLEvents, parseInterchange, diffChangelist, timingGuards, conformManifest, markerRoundtrip } from './editorial.mjs';
78
+
79
+ // ── provenance / audit (Cluster P) — compute cores ─────────────────────
80
+ export { provenanceTool } from './tools/provenance.mjs';
81
+ export { galleryLineage, makeStillLabel, validateStillLabel, gradeProvenance, cdlExport, cdlDiff, revisionHistory, episodeReport } from './provenance-audit.mjs';
82
+
83
+ // ── mcp — the stdio server entry (the thin shell over the above) ───────────────
84
+ export { startServer } from './index.mjs';