@hybridlabor-api/aos 4.4.0 → 4.4.2-beta.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 (306) hide show
  1. package/.agents/skills/firecrawl/SKILL.md +149 -0
  2. package/.agents/skills/firecrawl/rules/install.md +82 -0
  3. package/.agents/skills/firecrawl/rules/security.md +26 -0
  4. package/.agents/skills/firecrawl-agent/SKILL.md +58 -0
  5. package/.agents/skills/firecrawl-build/SKILL.md +39 -0
  6. package/.agents/skills/firecrawl-build-interact/SKILL.md +68 -0
  7. package/.agents/skills/firecrawl-build-onboarding/SKILL.md +103 -0
  8. package/.agents/skills/firecrawl-build-onboarding/references/auth-flow.md +39 -0
  9. package/.agents/skills/firecrawl-build-onboarding/references/project-setup.md +20 -0
  10. package/.agents/skills/firecrawl-build-onboarding/references/sdk-installation.md +17 -0
  11. package/.agents/skills/firecrawl-build-scrape/SKILL.md +69 -0
  12. package/.agents/skills/firecrawl-build-search/SKILL.md +69 -0
  13. package/.agents/skills/firecrawl-crawl/SKILL.md +59 -0
  14. package/.agents/skills/firecrawl-download/SKILL.md +70 -0
  15. package/.agents/skills/firecrawl-interact/SKILL.md +84 -0
  16. package/.agents/skills/firecrawl-map/SKILL.md +51 -0
  17. package/.agents/skills/firecrawl-scrape/SKILL.md +69 -0
  18. package/.agents/skills/firecrawl-search/SKILL.md +60 -0
  19. package/.claude/hooks/memb-inject.mjs +6 -0
  20. package/CLAUDE.md +573 -0
  21. package/README.de.md +0 -8
  22. package/README.md +0 -8
  23. package/README.pt.md +0 -8
  24. package/assets/brand/ao-ant-mark.svg +91 -0
  25. package/assets/brand/bdb-core-mark.svg +34 -0
  26. package/assets/brand/heimdall-guardian-mark.svg +16 -0
  27. package/assets/brand/memb-crystal-mark.svg +18 -0
  28. package/bin/aos-config.mjs +89 -0
  29. package/bin/aos-dashboard.mjs +608 -0
  30. package/bin/aos-uninstall.mjs +227 -0
  31. package/docs/sessions/audit-2026-09-14-silent-failures.md +114 -0
  32. package/docs/skills_table.md +0 -3
  33. package/installer.js +428 -237
  34. package/mcps/RhinoMCP/cc-plugin/.claude/settings.json +10 -0
  35. package/mcps/after-effects-mcp/build/index.js +840 -0
  36. package/mcps/after-effects-mcp/build/scripts/applyEffect.jsx +153 -0
  37. package/mcps/after-effects-mcp/build/scripts/applyEffectTemplate.jsx +218 -0
  38. package/mcps/after-effects-mcp/build/scripts/createComposition.jsx +71 -0
  39. package/mcps/after-effects-mcp/build/scripts/createShapeLayer.jsx +147 -0
  40. package/mcps/after-effects-mcp/build/scripts/createSolidLayer.jsx +114 -0
  41. package/mcps/after-effects-mcp/build/scripts/createTextLayer.jsx +115 -0
  42. package/mcps/after-effects-mcp/build/scripts/getLayerInfo.jsx +192 -0
  43. package/mcps/after-effects-mcp/build/scripts/getProjectInfo.jsx +90 -0
  44. package/mcps/after-effects-mcp/build/scripts/listCompositions.jsx +50 -0
  45. package/mcps/after-effects-mcp/build/scripts/mcp-bridge-auto.jsx +1773 -0
  46. package/mcps/after-effects-mcp/build/scripts/setLayerProperties.jsx +160 -0
  47. package/mcps/computer-use-mcp/dist/client.d.ts +150 -0
  48. package/mcps/computer-use-mcp/dist/client.js +136 -0
  49. package/mcps/computer-use-mcp/dist/entrypoint.d.ts +16 -0
  50. package/mcps/computer-use-mcp/dist/entrypoint.js +26 -0
  51. package/mcps/computer-use-mcp/dist/native.d.ts +212 -0
  52. package/mcps/computer-use-mcp/dist/native.js +50 -0
  53. package/mcps/computer-use-mcp/dist/server.d.ts +32 -0
  54. package/mcps/computer-use-mcp/dist/server.js +342 -0
  55. package/mcps/computer-use-mcp/dist/session.d.ts +101 -0
  56. package/mcps/computer-use-mcp/dist/session.js +2372 -0
  57. package/package.json +13 -4
  58. package/skills/global_config/aos-project-init/SKILL.md +97 -130
  59. package/skills/global_config/aos-project-init/assets/openwikiignore.template +62 -0
  60. package/skills/global_config/aos-project-init/scripts/aos-project-doctor.mjs +38 -1
  61. package/skills/global_config/aos-setup/SKILL.md +110 -11
  62. package/skills/global_config/aos-setup/scripts/aos-doctor.mjs +48 -4
  63. package/skills/global_config/ask-tim/SKILL.md +2 -7
  64. package/skills/global_config/openwiki-skill/scripts/openwiki_daemon.py +65 -12
  65. package/bin/setup-saas.mjs +0 -792
  66. package/mcps/blender-mcp-server/.dockerignore +0 -19
  67. package/mcps/blender-mcp-server/.github/workflows/ci.yml +0 -67
  68. package/mcps/blender-mcp-server/.github/workflows/publish-pypi.yml +0 -54
  69. package/mcps/blender-mcp-server/CHANGELOG.md +0 -64
  70. package/mcps/blender-mcp-server/CONTRIBUTING.md +0 -107
  71. package/mcps/blender-mcp-server/Dockerfile +0 -19
  72. package/mcps/blender-mcp-server/LICENSE +0 -21
  73. package/mcps/blender-mcp-server/README.md +0 -409
  74. package/mcps/blender-mcp-server/addon/__init__.py +0 -1440
  75. package/mcps/blender-mcp-server/addon/models.py +0 -223
  76. package/mcps/blender-mcp-server/pyproject.toml +0 -120
  77. package/mcps/blender-mcp-server/scripts/blender_bridge_request.py +0 -71
  78. package/mcps/blender-mcp-server/scripts/blender_create_test_cube.py +0 -49
  79. package/mcps/blender-mcp-server/scripts/blender_scene_info.py +0 -38
  80. package/mcps/blender-mcp-server/scripts/build_addon_zip.sh +0 -29
  81. package/mcps/blender-mcp-server/scripts/demos/README.md +0 -186
  82. package/mcps/blender-mcp-server/scripts/demos/dam_break_scene.py +0 -276
  83. package/mcps/blender-mcp-server/scripts/demos/open_dam_break_gui.py +0 -33
  84. package/mcps/blender-mcp-server/scripts/demos/pipe_movement_study.py +0 -518
  85. package/mcps/blender-mcp-server/scripts/demos/pipe_pressure_failure_scene.py +0 -806
  86. package/mcps/blender-mcp-server/scripts/demos/procedural_dam_break_scene.py +0 -600
  87. package/mcps/blender-mcp-server/scripts/demos/run_dam_break.py +0 -510
  88. package/mcps/blender-mcp-server/scripts/launch_blender_gui.py +0 -41
  89. package/mcps/blender-mcp-server/scripts/library/README.md +0 -64
  90. package/mcps/blender-mcp-server/scripts/library/apply_transforms.py +0 -46
  91. package/mcps/blender-mcp-server/scripts/library/camera.py +0 -51
  92. package/mcps/blender-mcp-server/scripts/library/collections.py +0 -65
  93. package/mcps/blender-mcp-server/scripts/library/create_mesh.py +0 -41
  94. package/mcps/blender-mcp-server/scripts/library/effector.py +0 -48
  95. package/mcps/blender-mcp-server/scripts/library/fluid_domain.py +0 -80
  96. package/mcps/blender-mcp-server/scripts/library/fluid_inflow.py +0 -98
  97. package/mcps/blender-mcp-server/scripts/library/frame_range.py +0 -35
  98. package/mcps/blender-mcp-server/scripts/library/keyframes.py +0 -59
  99. package/mcps/blender-mcp-server/scripts/library/rigid_body.py +0 -52
  100. package/mcps/blender-mcp-server/scripts/library/save_blend.py +0 -40
  101. package/mcps/blender-mcp-server/scripts/server_start.sh +0 -26
  102. package/mcps/blender-mcp-server/src/blender_mcp_server/__init__.py +0 -3
  103. package/mcps/blender-mcp-server/src/blender_mcp_server/headless.py +0 -330
  104. package/mcps/blender-mcp-server/src/blender_mcp_server/server.py +0 -606
  105. package/mcps/davinci-mcp-professional/.bandit +0 -35
  106. package/mcps/davinci-mcp-professional/.claude/commands/security-review.md +0 -191
  107. package/mcps/davinci-mcp-professional/.claude/settings.json +0 -1
  108. package/mcps/davinci-mcp-professional/.editorconfig +0 -27
  109. package/mcps/davinci-mcp-professional/.gitattributes +0 -207
  110. package/mcps/davinci-mcp-professional/.github/CODEOWNERS +0 -2
  111. package/mcps/davinci-mcp-professional/.github/ISSUE_TEMPLATE/bug_report.md +0 -33
  112. package/mcps/davinci-mcp-professional/.github/ISSUE_TEMPLATE/feature_request.md +0 -19
  113. package/mcps/davinci-mcp-professional/.github/PULL_REQUEST_TEMPLATE.md +0 -20
  114. package/mcps/davinci-mcp-professional/.github/SECURITY.md +0 -97
  115. package/mcps/davinci-mcp-professional/.github/dependabot.yml +0 -27
  116. package/mcps/davinci-mcp-professional/.github/workflows/ci.yml +0 -39
  117. package/mcps/davinci-mcp-professional/ATTRIBUTION.md +0 -4
  118. package/mcps/davinci-mcp-professional/AUTHORS.md +0 -9
  119. package/mcps/davinci-mcp-professional/BUGS.md +0 -55
  120. package/mcps/davinci-mcp-professional/CLAUDE.md +0 -229
  121. package/mcps/davinci-mcp-professional/CONTRIBUTING.md +0 -51
  122. package/mcps/davinci-mcp-professional/COPYING.md +0 -228
  123. package/mcps/davinci-mcp-professional/Doxyfile +0 -379
  124. package/mcps/davinci-mcp-professional/README.md +0 -143
  125. package/mcps/davinci-mcp-professional/SECURITY.md +0 -40
  126. package/mcps/davinci-mcp-professional/USING.md +0 -301
  127. package/mcps/davinci-mcp-professional/VERSION.md +0 -13
  128. package/mcps/davinci-mcp-professional/build.py +0 -169
  129. package/mcps/davinci-mcp-professional/claude_desktop_config_template.json +0 -11
  130. package/mcps/davinci-mcp-professional/hooks/ci-check.sh +0 -29
  131. package/mcps/davinci-mcp-professional/hooks/pre-push +0 -46
  132. package/mcps/davinci-mcp-professional/main.py +0 -40
  133. package/mcps/davinci-mcp-professional/mcp_server.py +0 -30
  134. package/mcps/davinci-mcp-professional/pyproject.toml +0 -115
  135. package/mcps/davinci-mcp-professional/security_audit.py +0 -379
  136. package/mcps/davinci-mcp-professional/src/davinci_mcp/__init__.py +0 -19
  137. package/mcps/davinci-mcp-professional/src/davinci_mcp/cli.py +0 -138
  138. package/mcps/davinci-mcp-professional/src/davinci_mcp/resolve_client.py +0 -342
  139. package/mcps/davinci-mcp-professional/src/davinci_mcp/resources/__init__.py +0 -60
  140. package/mcps/davinci-mcp-professional/src/davinci_mcp/server.py +0 -185
  141. package/mcps/davinci-mcp-professional/src/davinci_mcp/tools/__init__.py +0 -146
  142. package/mcps/davinci-mcp-professional/src/davinci_mcp/types.py +0 -125
  143. package/mcps/davinci-mcp-professional/src/davinci_mcp/utils/__init__.py +0 -19
  144. package/mcps/davinci-mcp-professional/src/davinci_mcp/utils/platform.py +0 -239
  145. package/mcps/davinci-mcp-professional/test.py +0 -186
  146. package/mcps/davinci-mcp-professional/uv.lock +0 -2073
  147. package/mcps/davinci-resolve-mcp/.claude/skills/media-analysis.md +0 -44
  148. package/mcps/davinci-resolve-mcp/.clinerules +0 -17
  149. package/mcps/davinci-resolve-mcp/.cursorrules +0 -17
  150. package/mcps/davinci-resolve-mcp/.github/copilot-instructions.md +0 -17
  151. package/mcps/davinci-resolve-mcp/.github/workflows/npm-publish.yml +0 -109
  152. package/mcps/davinci-resolve-mcp/.windsurfrules +0 -17
  153. package/mcps/davinci-resolve-mcp/AGENTS.md +0 -114
  154. package/mcps/davinci-resolve-mcp/CHANGELOG.md +0 -2648
  155. package/mcps/davinci-resolve-mcp/CLAUDE.md +0 -15
  156. package/mcps/davinci-resolve-mcp/LICENSE +0 -21
  157. package/mcps/davinci-resolve-mcp/README.md +0 -173
  158. package/mcps/davinci-resolve-mcp/SECURITY.md +0 -53
  159. package/mcps/davinci-resolve-mcp/bin/davinci-resolve-mcp.mjs +0 -450
  160. package/mcps/davinci-resolve-mcp/examples/README.md +0 -53
  161. package/mcps/davinci-resolve-mcp/examples/markers/README.md +0 -81
  162. package/mcps/davinci-resolve-mcp/examples/media/README.md +0 -94
  163. package/mcps/davinci-resolve-mcp/examples/timeline/README.md +0 -98
  164. package/mcps/davinci-resolve-mcp/install.py +0 -1687
  165. package/mcps/davinci-resolve-mcp/package.json +0 -52
  166. package/mcps/davinci-resolve-mcp/release-notes/v2.22.0.md +0 -51
  167. package/mcps/davinci-resolve-mcp/release-notes/v2.23.0.md +0 -51
  168. package/mcps/davinci-resolve-mcp/release-notes/v2.23.1.md +0 -35
  169. package/mcps/davinci-resolve-mcp/release-notes/v2.24.1.md +0 -32
  170. package/mcps/davinci-resolve-mcp/scripts/audit_api_parity.py +0 -275
  171. package/mcps/davinci-resolve-mcp/scripts/audit_readwrite_symmetry.py +0 -80
  172. package/mcps/davinci-resolve-mcp/scripts/doctor.py +0 -227
  173. package/mcps/davinci-resolve-mcp/scripts/gen_api_limitations.py +0 -144
  174. package/mcps/davinci-resolve-mcp/scripts/live_media_analysis_polish_probe.py +0 -65
  175. package/mcps/davinci-resolve-mcp/scripts/measure_bridge_cost.py +0 -65
  176. package/mcps/davinci-resolve-mcp/scripts/regen_panel_screenshots.py +0 -76
  177. package/mcps/davinci-resolve-mcp/src/__init__.py +0 -3
  178. package/mcps/davinci-resolve-mcp/src/analysis_dashboard.py +0 -15505
  179. package/mcps/davinci-resolve-mcp/src/batch_cli.py +0 -540
  180. package/mcps/davinci-resolve-mcp/src/control_panel.py +0 -13
  181. package/mcps/davinci-resolve-mcp/src/granular/__init__.py +0 -17
  182. package/mcps/davinci-resolve-mcp/src/granular/common.py +0 -741
  183. package/mcps/davinci-resolve-mcp/src/granular/folder.py +0 -420
  184. package/mcps/davinci-resolve-mcp/src/granular/gallery.py +0 -306
  185. package/mcps/davinci-resolve-mcp/src/granular/graph.py +0 -309
  186. package/mcps/davinci-resolve-mcp/src/granular/media_pool.py +0 -679
  187. package/mcps/davinci-resolve-mcp/src/granular/media_pool_item.py +0 -1020
  188. package/mcps/davinci-resolve-mcp/src/granular/media_storage.py +0 -179
  189. package/mcps/davinci-resolve-mcp/src/granular/project.py +0 -1654
  190. package/mcps/davinci-resolve-mcp/src/granular/resolve_control.py +0 -538
  191. package/mcps/davinci-resolve-mcp/src/granular/timeline.py +0 -1075
  192. package/mcps/davinci-resolve-mcp/src/granular/timeline_item.py +0 -2251
  193. package/mcps/davinci-resolve-mcp/src/resolve_mcp_server.py +0 -43
  194. package/mcps/davinci-resolve-mcp/src/server.py +0 -24116
  195. package/mcps/davinci-resolve-mcp/src/utils/__init__.py +0 -3
  196. package/mcps/davinci-resolve-mcp/src/utils/actor_identity.py +0 -46
  197. package/mcps/davinci-resolve-mcp/src/utils/analysis_caps.py +0 -678
  198. package/mcps/davinci-resolve-mcp/src/utils/analysis_memory.py +0 -755
  199. package/mcps/davinci-resolve-mcp/src/utils/analysis_runs.py +0 -307
  200. package/mcps/davinci-resolve-mcp/src/utils/analysis_store.py +0 -945
  201. package/mcps/davinci-resolve-mcp/src/utils/api_truth.py +0 -525
  202. package/mcps/davinci-resolve-mcp/src/utils/app_control.py +0 -319
  203. package/mcps/davinci-resolve-mcp/src/utils/audio_fairlight_live_probe.py +0 -263
  204. package/mcps/davinci-resolve-mcp/src/utils/brain_edits.py +0 -387
  205. package/mcps/davinci-resolve-mcp/src/utils/bridge_metrics.py +0 -76
  206. package/mcps/davinci-resolve-mcp/src/utils/cdl.py +0 -20
  207. package/mcps/davinci-resolve-mcp/src/utils/clip_query.py +0 -85
  208. package/mcps/davinci-resolve-mcp/src/utils/cloud_operations.py +0 -192
  209. package/mcps/davinci-resolve-mcp/src/utils/color_grade_live_probe.py +0 -444
  210. package/mcps/davinci-resolve-mcp/src/utils/contracts.py +0 -102
  211. package/mcps/davinci-resolve-mcp/src/utils/cut_ir.py +0 -115
  212. package/mcps/davinci-resolve-mcp/src/utils/dctl_templates.py +0 -368
  213. package/mcps/davinci-resolve-mcp/src/utils/deep_vision.py +0 -643
  214. package/mcps/davinci-resolve-mcp/src/utils/destructive_hook.py +0 -606
  215. package/mcps/davinci-resolve-mcp/src/utils/edit_engine.py +0 -737
  216. package/mcps/davinci-resolve-mcp/src/utils/embeddings.py +0 -922
  217. package/mcps/davinci-resolve-mcp/src/utils/entities.py +0 -579
  218. package/mcps/davinci-resolve-mcp/src/utils/extension_authoring_live_probe.py +0 -292
  219. package/mcps/davinci-resolve-mcp/src/utils/failure_tracker.py +0 -119
  220. package/mcps/davinci-resolve-mcp/src/utils/fuse_templates.py +0 -1968
  221. package/mcps/davinci-resolve-mcp/src/utils/fusion_composition_live_probe.py +0 -284
  222. package/mcps/davinci-resolve-mcp/src/utils/fusion_group_settings.py +0 -334
  223. package/mcps/davinci-resolve-mcp/src/utils/layout_presets.py +0 -333
  224. package/mcps/davinci-resolve-mcp/src/utils/mcp_stdio.py +0 -32
  225. package/mcps/davinci-resolve-mcp/src/utils/mcp_transport.py +0 -128
  226. package/mcps/davinci-resolve-mcp/src/utils/media_analysis.py +0 -7690
  227. package/mcps/davinci-resolve-mcp/src/utils/media_analysis_jobs.py +0 -805
  228. package/mcps/davinci-resolve-mcp/src/utils/media_pool_changes.py +0 -121
  229. package/mcps/davinci-resolve-mcp/src/utils/media_pool_ingest_live_probe.py +0 -592
  230. package/mcps/davinci-resolve-mcp/src/utils/multicam.py +0 -393
  231. package/mcps/davinci-resolve-mcp/src/utils/object_inspection.py +0 -301
  232. package/mcps/davinci-resolve-mcp/src/utils/page_lock.py +0 -75
  233. package/mcps/davinci-resolve-mcp/src/utils/platform.py +0 -157
  234. package/mcps/davinci-resolve-mcp/src/utils/proc.py +0 -26
  235. package/mcps/davinci-resolve-mcp/src/utils/project_cleanup.py +0 -73
  236. package/mcps/davinci-resolve-mcp/src/utils/project_lifecycle_live_probe.py +0 -376
  237. package/mcps/davinci-resolve-mcp/src/utils/project_lint.py +0 -131
  238. package/mcps/davinci-resolve-mcp/src/utils/project_properties.py +0 -601
  239. package/mcps/davinci-resolve-mcp/src/utils/project_spec.py +0 -459
  240. package/mcps/davinci-resolve-mcp/src/utils/readback.py +0 -91
  241. package/mcps/davinci-resolve-mcp/src/utils/render_deliver_live_probe.py +0 -384
  242. package/mcps/davinci-resolve-mcp/src/utils/resolve_ai_governance.py +0 -214
  243. package/mcps/davinci-resolve-mcp/src/utils/resolve_ai_ledger.py +0 -244
  244. package/mcps/davinci-resolve-mcp/src/utils/resolve_busy.py +0 -171
  245. package/mcps/davinci-resolve-mcp/src/utils/resolve_connection.py +0 -77
  246. package/mcps/davinci-resolve-mcp/src/utils/review_annotation_live_probe.py +0 -352
  247. package/mcps/davinci-resolve-mcp/src/utils/script_templates.py +0 -1193
  248. package/mcps/davinci-resolve-mcp/src/utils/shot_relationships.py +0 -544
  249. package/mcps/davinci-resolve-mcp/src/utils/structural_diff.py +0 -175
  250. package/mcps/davinci-resolve-mcp/src/utils/sync_detection.py +0 -889
  251. package/mcps/davinci-resolve-mcp/src/utils/timeline_brain_db.py +0 -826
  252. package/mcps/davinci-resolve-mcp/src/utils/timeline_conform_live_probe.py +0 -280
  253. package/mcps/davinci-resolve-mcp/src/utils/timeline_kernel_live_probe.py +0 -1092
  254. package/mcps/davinci-resolve-mcp/src/utils/timeline_kernel_probe.py +0 -185
  255. package/mcps/davinci-resolve-mcp/src/utils/timeline_title_text.py +0 -87
  256. package/mcps/davinci-resolve-mcp/src/utils/timeline_versioning.py +0 -730
  257. package/mcps/davinci-resolve-mcp/src/utils/update_check.py +0 -685
  258. package/mcps/vectorworks-mcp/.dockerignore +0 -8
  259. package/mcps/vectorworks-mcp/Dockerfile +0 -28
  260. package/mcps/vectorworks-mcp/README.md +0 -156
  261. package/mcps/vectorworks-mcp/app/__init__.py +0 -2
  262. package/mcps/vectorworks-mcp/app/api.py +0 -52
  263. package/mcps/vectorworks-mcp/app/chunking.py +0 -56
  264. package/mcps/vectorworks-mcp/app/config.py +0 -29
  265. package/mcps/vectorworks-mcp/app/embeddings.py +0 -28
  266. package/mcps/vectorworks-mcp/app/indexer.py +0 -106
  267. package/mcps/vectorworks-mcp/app/main.py +0 -33
  268. package/mcps/vectorworks-mcp/app/mcp_server.py +0 -71
  269. package/mcps/vectorworks-mcp/app/search.py +0 -54
  270. package/mcps/vectorworks-mcp/app/utils.py +0 -14
  271. package/mcps/vectorworks-mcp/data/.gitkeep +0 -1
  272. package/mcps/vectorworks-mcp/data/vw-app-help/VW2022_PluginScript.htm +0 -95
  273. package/mcps/vectorworks-mcp/data/vw-app-help/VW2022_ScriptEditor.htm +0 -266
  274. package/mcps/vectorworks-mcp/data/vw-app-help/VW2022_Scripting.htm +0 -106
  275. package/mcps/vectorworks-mcp/data/vw-app-help/VW2023_CreatingScriptedPlugins.htm +0 -327
  276. package/mcps/vectorworks-mcp/data/vw-app-help/VW2024_RunningScripts.htm +0 -116
  277. package/mcps/vectorworks-mcp/data/vw-devwiki/VS_Function_Reference_category.html +0 -2129
  278. package/mcps/vectorworks-mcp/data/vw-jp-ref/ObjectsSymbols.html +0 -2035
  279. package/mcps/vectorworks-mcp/data/vw-jp-ref/ScriptFunctionReference.html +0 -29
  280. package/mcps/vectorworks-mcp/design.md +0 -57
  281. package/mcps/vectorworks-mcp/docker-compose.yml +0 -18
  282. package/mcps/vectorworks-mcp/index/.gitkeep +0 -1
  283. package/mcps/vectorworks-mcp/index/meta.jsonl +0 -3956
  284. package/mcps/vectorworks-mcp/index/vw.faiss +0 -0
  285. package/mcps/vectorworks-mcp/requirements.txt +0 -10
  286. package/mcps/vectorworks-mcp/scripts/fetch_docs_minimal.sh +0 -84
  287. package/mcps/vectorworks-mcp/scripts/fetch_github_vectorworks.sh +0 -85
  288. package/mcps/vectorworks-mcp/templates/index.html +0 -40
  289. package/scripts/ecosystem-health-audit.js +0 -257
  290. package/skills/bdb-dev-os-skill/SKILL.md +0 -49
  291. package/skills/bdb-dev-os-skill/package.json +0 -11
  292. package/skills/bdbsaastraining/SKILL.md +0 -316
  293. package/skills/bdbsaastraining/references/exam-pool.md +0 -313
  294. package/skills/bdbsaastraining/references/interaction-modes.md +0 -94
  295. package/skills/bdbsaastraining/references/track-a-ai-agent.md +0 -111
  296. package/skills/bdbsaastraining/references/track-b-wordpress.md +0 -111
  297. package/skills/bdbsaastraining/references/track-c-mailserver.md +0 -107
  298. package/skills/bdbsaastraining/references/track-d-debian.md +0 -109
  299. package/skills/bdbsaastraining/references/track-e-custom.md +0 -83
  300. package/skills/bdbsaastraining/scripts/build_profile.py +0 -529
  301. package/skills/bdbsaastraining/scripts/generate_certificate.py +0 -161
  302. package/skills/bdbsaastraining/scripts/preflight_check.sh +0 -150
  303. package/skills/bdbsaastraining/templates/certificate_template.html +0 -508
  304. package/skills/global_config/bdb-ecosystem-health/SKILL.md +0 -68
  305. package/skills/global_config/bdbsaas-ops/SKILL.md +0 -16
  306. package/skills/global_config/bdbsaashost/SKILL.md +0 -125
@@ -1,741 +0,0 @@
1
- """Shared bootstrap and helpers for the granular Resolve MCP server."""
2
-
3
- import logging
4
- import os
5
- import platform
6
- import subprocess
7
- import sys
8
- import tempfile
9
- import time
10
- from typing import Any, Dict, List, Optional, Union
11
-
12
- CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
13
- SRC_DIR = os.path.dirname(CURRENT_DIR)
14
- PROJECT_DIR = os.path.dirname(SRC_DIR)
15
-
16
- for path in (SRC_DIR, PROJECT_DIR):
17
- if path not in sys.path:
18
- sys.path.insert(0, path)
19
-
20
- from mcp.server.fastmcp import FastMCP
21
- from mcp.types import ToolAnnotations
22
-
23
- from src.utils.app_control import (
24
- get_app_state,
25
- open_preferences,
26
- open_project_settings,
27
- quit_resolve_app,
28
- restart_resolve_app,
29
- )
30
- from src.utils.cdl import normalize_cdl_payload
31
- from src.utils.cloud_operations import (
32
- create_cloud_project,
33
- import_cloud_project,
34
- load_cloud_project,
35
- restore_cloud_project,
36
- )
37
- from src.utils.layout_presets import (
38
- delete_layout_preset,
39
- export_layout_preset,
40
- import_layout_preset,
41
- list_layout_presets,
42
- load_layout_preset,
43
- save_layout_preset,
44
- )
45
- from src.utils.object_inspection import inspect_object, print_object_help
46
- from src.utils.platform import get_platform, get_resolve_paths
47
- from src.utils.project_properties import (
48
- get_all_project_properties,
49
- get_color_settings,
50
- get_project_info,
51
- get_project_metadata,
52
- get_project_property,
53
- get_superscale_settings,
54
- get_timeline_format_settings,
55
- set_color_science_mode,
56
- set_color_space,
57
- set_project_property,
58
- set_superscale_settings,
59
- set_timeline_format,
60
- )
61
-
62
- paths = get_resolve_paths()
63
- RESOLVE_API_PATH = os.environ.get("RESOLVE_SCRIPT_API") or paths["api_path"]
64
- RESOLVE_LIB_PATH = os.environ.get("RESOLVE_SCRIPT_LIB") or paths["lib_path"]
65
- RESOLVE_MODULES_PATH = (
66
- os.path.join(RESOLVE_API_PATH, "Modules") if RESOLVE_API_PATH else paths["modules_path"]
67
- )
68
-
69
- if RESOLVE_API_PATH:
70
- os.environ["RESOLVE_SCRIPT_API"] = RESOLVE_API_PATH
71
- if RESOLVE_LIB_PATH:
72
- os.environ["RESOLVE_SCRIPT_LIB"] = RESOLVE_LIB_PATH
73
- if RESOLVE_MODULES_PATH and RESOLVE_MODULES_PATH not in sys.path:
74
- sys.path.append(RESOLVE_MODULES_PATH)
75
-
76
- if not logging.getLogger().handlers:
77
- logging.basicConfig(
78
- level=logging.INFO,
79
- format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
80
- handlers=[logging.StreamHandler()],
81
- )
82
-
83
- VERSION = "2.57.5"
84
- logger = logging.getLogger("davinci-resolve-mcp")
85
- logger.info(f"Starting DaVinci Resolve MCP Server v{VERSION}")
86
- logger.info(f"Detected platform: {get_platform()}")
87
- logger.info(f"Using Resolve API path: {RESOLVE_API_PATH}")
88
- logger.info(f"Using Resolve library path: {RESOLVE_LIB_PATH}")
89
-
90
- mcp = FastMCP("DaVinciResolveMCP")
91
-
92
- READ_ONLY_TOOL = ToolAnnotations(
93
- readOnlyHint=True,
94
- destructiveHint=False,
95
- idempotentHint=True,
96
- openWorldHint=False,
97
- )
98
- WRITE_TOOL = ToolAnnotations(
99
- readOnlyHint=False,
100
- destructiveHint=False,
101
- idempotentHint=False,
102
- openWorldHint=False,
103
- )
104
- IDEMPOTENT_WRITE_TOOL = ToolAnnotations(
105
- readOnlyHint=False,
106
- destructiveHint=False,
107
- idempotentHint=True,
108
- openWorldHint=False,
109
- )
110
- DESTRUCTIVE_TOOL = ToolAnnotations(
111
- readOnlyHint=False,
112
- destructiveHint=True,
113
- idempotentHint=False,
114
- openWorldHint=False,
115
- )
116
- EXTERNAL_READ_TOOL = ToolAnnotations(
117
- readOnlyHint=True,
118
- destructiveHint=False,
119
- idempotentHint=True,
120
- openWorldHint=True,
121
- )
122
- EXTERNAL_WRITE_TOOL = ToolAnnotations(
123
- readOnlyHint=False,
124
- destructiveHint=False,
125
- idempotentHint=False,
126
- openWorldHint=True,
127
- )
128
- EXTERNAL_DESTRUCTIVE_TOOL = ToolAnnotations(
129
- readOnlyHint=False,
130
- destructiveHint=True,
131
- idempotentHint=False,
132
- openWorldHint=True,
133
- )
134
-
135
-
136
- def _annotations_for_tool_name(tool_name: str) -> ToolAnnotations:
137
- """Infer conservative MCP client-safety hints for legacy granular tools."""
138
- name = (tool_name or "").lower()
139
- read_prefixes = (
140
- "get_",
141
- "list_",
142
- "inspect_",
143
- "probe_",
144
- "validate_",
145
- "compare_",
146
- "detect_",
147
- "summarize_",
148
- "review_",
149
- "is_",
150
- "has_",
151
- )
152
- destructive_prefixes = (
153
- "delete_",
154
- "remove_",
155
- "clear_",
156
- "reset_",
157
- "replace_",
158
- "unlink_",
159
- "quit",
160
- "restart",
161
- "close_",
162
- "stop_",
163
- "overwrite_",
164
- "lift_",
165
- "set_",
166
- "load_",
167
- "switch_",
168
- )
169
- write_prefixes = (
170
- "add_",
171
- "append_",
172
- "apply_",
173
- "assign_",
174
- "copy_",
175
- "create_",
176
- "duplicate_",
177
- "export_",
178
- "import_",
179
- "insert_",
180
- "link_",
181
- "move_",
182
- "open_",
183
- "render_",
184
- "rename_",
185
- "save_",
186
- "start_",
187
- "sync_",
188
- "transcribe_",
189
- )
190
- if name.startswith(read_prefixes):
191
- return READ_ONLY_TOOL
192
- if name.startswith(destructive_prefixes):
193
- return DESTRUCTIVE_TOOL
194
- if name.startswith(write_prefixes):
195
- return WRITE_TOOL
196
- return WRITE_TOOL
197
-
198
-
199
- _original_mcp_tool = mcp.tool
200
-
201
-
202
- def _tool_with_default_annotations(
203
- name=None,
204
- title=None,
205
- description=None,
206
- annotations=None,
207
- icons=None,
208
- meta=None,
209
- structured_output=None,
210
- ):
211
- """Default unannotated granular tools to explicit MCP safety hints."""
212
-
213
- def decorator(func):
214
- tool_name = name or getattr(func, "__name__", "")
215
- return _original_mcp_tool(
216
- name=name,
217
- title=title,
218
- description=description,
219
- annotations=annotations or _annotations_for_tool_name(tool_name),
220
- icons=icons,
221
- meta=meta,
222
- structured_output=structured_output,
223
- )(func)
224
-
225
- return decorator
226
-
227
-
228
- mcp.tool = _tool_with_default_annotations
229
-
230
- resolve = None
231
- dvr_script = None
232
-
233
- try:
234
- import DaVinciResolveScript as dvr_script # type: ignore
235
-
236
- resolve = dvr_script.scriptapp("Resolve")
237
- if resolve:
238
- logger.info(
239
- f"Connected to DaVinci Resolve: {resolve.GetProductName()} {resolve.GetVersionString()}"
240
- )
241
- else:
242
- logger.error("Failed to get Resolve object. Is DaVinci Resolve running?")
243
- except ImportError as exc:
244
- logger.error(f"Failed to import DaVinciResolveScript: {exc}")
245
- logger.error("Check that DaVinci Resolve is installed and running.")
246
- logger.error(f"RESOLVE_SCRIPT_API: {RESOLVE_API_PATH}")
247
- logger.error(f"RESOLVE_SCRIPT_LIB: {RESOLVE_LIB_PATH}")
248
- logger.error(f"RESOLVE_MODULES_PATH: {RESOLVE_MODULES_PATH}")
249
- logger.error(f"sys.path: {sys.path}")
250
- resolve = None
251
- except Exception as exc:
252
- logger.error(f"Unexpected error initializing Resolve: {exc}")
253
- resolve = None
254
-
255
-
256
- def _normalize_cdl(cdl):
257
- """Normalize CDL payloads to the string format Resolve's SetCDL expects."""
258
- return normalize_cdl_payload(cdl)
259
-
260
-
261
- class ResolveProxy:
262
- """Late-bound proxy for modules that pass the shared Resolve object around."""
263
-
264
- def _target(self):
265
- return get_resolve()
266
-
267
- def __bool__(self):
268
- return self._target() is not None
269
-
270
- def __getattr__(self, name):
271
- target = self._target()
272
- if target is None:
273
- raise AttributeError("DaVinci Resolve is not connected")
274
- return getattr(target, name)
275
-
276
-
277
- def _resolve_safe_dir(path):
278
- """Redirect sandbox/temp paths that Resolve can't access to ~/Desktop/resolve-stills.
279
-
280
- Covers macOS (/var/folders, /private/var), Linux (/tmp, /var/tmp),
281
- and Windows (AppData\\Local\\Temp) sandbox temp directories.
282
- """
283
- system_temp = tempfile.gettempdir()
284
- _is_sandbox = False
285
- if platform.system() == "Darwin":
286
- _is_sandbox = path.startswith("/var/") or path.startswith("/private/var/")
287
- elif platform.system() == "Linux":
288
- _is_sandbox = path.startswith("/tmp") or path.startswith("/var/tmp")
289
- elif platform.system() == "Windows":
290
- try:
291
- _is_sandbox = os.path.commonpath([os.path.abspath(path), os.path.abspath(system_temp)]) == os.path.abspath(system_temp)
292
- except ValueError:
293
- _is_sandbox = False
294
- if _is_sandbox:
295
- return os.path.join(os.path.expanduser("~"), "Documents", "resolve-stills")
296
- return path
297
-
298
- def _is_resolve_handle_live(candidate) -> bool:
299
- """Return True when a cached Resolve handle still answers root API calls."""
300
- try:
301
- get_version = getattr(candidate, "GetVersion", None)
302
- if not callable(get_version):
303
- return False
304
- return bool(get_version())
305
- except Exception as exc:
306
- logger.warning(f"Cached Resolve handle is stale: {exc}")
307
- return False
308
-
309
-
310
- def _try_connect():
311
- """Attempt to connect to Resolve once. Returns resolve object or None."""
312
- global resolve
313
- try:
314
- candidate = dvr_script.scriptapp("Resolve")
315
- if candidate and _is_resolve_handle_live(candidate):
316
- resolve = candidate
317
- logger.info(f"Connected: {resolve.GetProductName()} {resolve.GetVersionString()}")
318
- else:
319
- resolve = None
320
- return resolve
321
- except Exception as e:
322
- logger.error(f"Connection error: {e}")
323
- resolve = None
324
- return None
325
-
326
- def _launch_resolve():
327
- """Launch DaVinci Resolve and wait for it to become available."""
328
- sys_name = platform.system().lower()
329
- if sys_name == "darwin":
330
- app_path = "/Applications/DaVinci Resolve/DaVinci Resolve.app"
331
- if not os.path.exists(app_path):
332
- return False
333
- subprocess.Popen(["open", app_path], stdin=subprocess.DEVNULL)
334
- elif sys_name == "windows":
335
- app_path = r"C:\Program Files\Blackmagic Design\DaVinci Resolve\Resolve.exe"
336
- if not os.path.exists(app_path):
337
- return False
338
- subprocess.Popen([app_path], stdin=subprocess.DEVNULL)
339
- elif sys_name == "linux":
340
- app_path = "/opt/resolve/bin/resolve"
341
- if not os.path.exists(app_path):
342
- return False
343
- subprocess.Popen([app_path], stdin=subprocess.DEVNULL)
344
- else:
345
- return False
346
- logger.info("Launched DaVinci Resolve, waiting for it to respond...")
347
- for i in range(30):
348
- time.sleep(2)
349
- if _try_connect():
350
- logger.info(f"Resolve responded after {(i+1)*2}s")
351
- return True
352
- logger.warning("Resolve did not respond within 60s after launch")
353
- return False
354
-
355
- def get_resolve():
356
- """Lazy connection to Resolve — connects on first tool call, auto-launches if needed."""
357
- global resolve
358
- if resolve is not None and _is_resolve_handle_live(resolve):
359
- return resolve
360
- resolve = None
361
- if _try_connect():
362
- return resolve
363
- logger.info("Resolve not running, attempting to launch automatically...")
364
- _launch_resolve()
365
- return resolve
366
-
367
- def get_project_manager():
368
- """Get ProjectManager with lazy connection and null guard."""
369
- r = get_resolve()
370
- if not r:
371
- return None
372
- pm = r.GetProjectManager()
373
- return pm
374
-
375
- def get_current_project():
376
- """Get current project with lazy connection and null guards."""
377
- pm = get_project_manager()
378
- if not pm:
379
- return None, None
380
- proj = pm.GetCurrentProject()
381
- return pm, proj
382
-
383
- def iter_all_media_pool_clips(media_pool):
384
- """Yield every clip in the media pool, walking subfolders lazily.
385
-
386
- Unlike get_all_media_pool_clips(), this does not materialize the whole tree.
387
- A caller that breaks early (e.g. find-by-name) stops the walk as soon as it
388
- has a match, avoiding a full traversal. Each folder access is a Resolve
389
- bridge round-trip, so early exit is a real win on large projects.
390
-
391
- Yields in the same pre-order (parent clips before descendants, siblings in
392
- listed order) as the eager get_all_media_pool_clips().
393
- """
394
- root_folder = media_pool.GetRootFolder()
395
- if not root_folder:
396
- return
397
- stack = [root_folder]
398
- while stack:
399
- folder = stack.pop()
400
- folder_clips = folder.GetClipList()
401
- if folder_clips:
402
- for clip in folder_clips:
403
- yield clip
404
- sub_folders = folder.GetSubFolderList()
405
- if sub_folders:
406
- # Reversed so the LIFO stack pops siblings in their listed order.
407
- stack.extend(reversed(list(sub_folders)))
408
-
409
-
410
- def get_all_media_pool_clips(media_pool):
411
- """Get all clips from media pool recursively including subfolders."""
412
- return list(iter_all_media_pool_clips(media_pool))
413
-
414
- def get_all_media_pool_folders(media_pool):
415
- """Get all folders from media pool recursively."""
416
- folders = []
417
- root_folder = media_pool.GetRootFolder()
418
-
419
- def process_folder(folder):
420
- folders.append(folder)
421
-
422
- sub_folders = folder.GetSubFolderList()
423
- for sub_folder in sub_folders:
424
- process_folder(sub_folder)
425
-
426
- process_folder(root_folder)
427
- return folders
428
-
429
- def _get_mp():
430
- resolve = get_resolve()
431
- if resolve is None:
432
- return None, None, {"error": "Not connected to DaVinci Resolve"}
433
- project = resolve.GetProjectManager().GetCurrentProject()
434
- if not project:
435
- return None, None, {"error": "No project currently open"}
436
- mp = project.GetMediaPool()
437
- if not mp:
438
- return project, None, {"error": "Failed to get MediaPool"}
439
- return project, mp, None
440
-
441
- def _find_clip_by_id(folder, target_id):
442
- for clip in (folder.GetClipList() or []):
443
- if clip.GetUniqueId() == target_id:
444
- return clip
445
- for sub in (folder.GetSubFolderList() or []):
446
- found = _find_clip_by_id(sub, target_id)
447
- if found:
448
- return found
449
- return None
450
-
451
- _SUBTITLE_LANGUAGE_SUFFIXES = {
452
- "auto": "AUTO",
453
- "danish": "DANISH",
454
- "dutch": "DUTCH",
455
- "english": "ENGLISH",
456
- "french": "FRENCH",
457
- "german": "GERMAN",
458
- "italian": "ITALIAN",
459
- "japanese": "JAPANESE",
460
- "korean": "KOREAN",
461
- "mandarin_simplified": "MANDARIN_SIMPLIFIED",
462
- "mandarin-simplified": "MANDARIN_SIMPLIFIED",
463
- "mandarin_traditional": "MANDARIN_TRADITIONAL",
464
- "mandarin-traditional": "MANDARIN_TRADITIONAL",
465
- "norwegian": "NORWEGIAN",
466
- "portuguese": "PORTUGUESE",
467
- "russian": "RUSSIAN",
468
- "spanish": "SPANISH",
469
- "swedish": "SWEDISH",
470
- }
471
-
472
- _SUBTITLE_PRESET_SUFFIXES = {
473
- "default": "SUBTITLE_DEFAULT",
474
- "subtitle_default": "SUBTITLE_DEFAULT",
475
- "subtitle-default": "SUBTITLE_DEFAULT",
476
- "teletext": "TELETEXT",
477
- "netflix": "NETFLIX",
478
- }
479
-
480
- _SUBTITLE_LINE_BREAK_SUFFIXES = {
481
- "single": "LINE_SINGLE",
482
- "double": "LINE_DOUBLE",
483
- }
484
-
485
-
486
- def _build_subtitle_settings(resolve_obj, language=None, preset=None,
487
- chars_per_line=None, line_break=None, gap=None):
488
- """Build the autoCaptionSettings dict for Timeline.CreateSubtitlesFromAudio.
489
-
490
- Maps user-friendly strings to resolve.AUTO_CAPTION_* constants per docs
491
- lines 720-761. Returns (settings_dict, None) or (None, error_dict).
492
- """
493
- settings = {}
494
- if language is not None:
495
- suffix = _SUBTITLE_LANGUAGE_SUFFIXES.get(str(language).strip().lower())
496
- if not suffix:
497
- valid = sorted(set(_SUBTITLE_LANGUAGE_SUFFIXES.keys()))
498
- return None, {"error": f"Unknown language '{language}'. Valid: {valid}"}
499
- settings[resolve_obj.SUBTITLE_LANGUAGE] = getattr(resolve_obj, f"AUTO_CAPTION_{suffix}")
500
- if preset is not None:
501
- suffix = _SUBTITLE_PRESET_SUFFIXES.get(str(preset).strip().lower())
502
- if not suffix:
503
- valid = sorted(set(_SUBTITLE_PRESET_SUFFIXES.keys()))
504
- return None, {"error": f"Unknown preset '{preset}'. Valid: {valid}"}
505
- settings[resolve_obj.SUBTITLE_CAPTION_PRESET] = getattr(resolve_obj, f"AUTO_CAPTION_{suffix}")
506
- if chars_per_line is not None:
507
- if not isinstance(chars_per_line, int) or not (1 <= chars_per_line <= 60):
508
- return None, {"error": "chars_per_line must be an integer between 1 and 60"}
509
- settings[resolve_obj.SUBTITLE_CHARS_PER_LINE] = chars_per_line
510
- if line_break is not None:
511
- suffix = _SUBTITLE_LINE_BREAK_SUFFIXES.get(str(line_break).strip().lower())
512
- if not suffix:
513
- valid = sorted(set(_SUBTITLE_LINE_BREAK_SUFFIXES.keys()))
514
- return None, {"error": f"Unknown line_break '{line_break}'. Valid: {valid}"}
515
- settings[resolve_obj.SUBTITLE_LINE_BREAK] = getattr(resolve_obj, f"AUTO_CAPTION_{suffix}")
516
- if gap is not None:
517
- if not isinstance(gap, int) or not (0 <= gap <= 10):
518
- return None, {"error": "gap must be an integer between 0 and 10"}
519
- settings[resolve_obj.SUBTITLE_GAP] = gap
520
- return settings, None
521
-
522
-
523
- def _frame_int(value):
524
- if value is None:
525
- return None
526
- try:
527
- return int(round(float(value)))
528
- except (TypeError, ValueError):
529
- return None
530
-
531
-
532
- def _timeline_start_frame(timeline):
533
- if not timeline:
534
- return None
535
- try:
536
- return _frame_int(timeline.GetStartFrame())
537
- except Exception:
538
- return None
539
-
540
-
541
- def _normalize_record_frame(ci, index, timeline_start_frame=None):
542
- rf = _frame_int(ci.get("recordFrame", ci.get("record_frame")))
543
- if rf is None:
544
- return None, {"error": f"clip_infos[{index}] record_frame/recordFrame must be numeric"}
545
-
546
- mode_raw = ci.get("recordFrameMode", ci.get("record_frame_mode", "relative"))
547
- mode = str(mode_raw or "relative").strip().lower()
548
- mode_aliases = {
549
- "relative": "relative",
550
- "timeline_relative": "relative",
551
- "offset": "relative",
552
- "absolute": "absolute",
553
- "timeline_absolute": "absolute",
554
- "auto": "auto",
555
- }
556
- mode = mode_aliases.get(mode)
557
- if not mode:
558
- return None, {
559
- "error": f"clip_infos[{index}] record_frame_mode must be 'relative', 'absolute', or 'auto'"
560
- }
561
-
562
- start = _frame_int(timeline_start_frame)
563
- if start in (None, 0) or mode == "absolute":
564
- return rf, None
565
- if mode == "auto":
566
- return (start + rf) if rf < start else rf, None
567
- return start + rf, None
568
-
569
-
570
- def _build_append_clip_info_dict(root, ci, index, timeline_start_frame=None):
571
- """Build one MediaPool.AppendToTimeline clipInfo map (6 keys per docs line 221).
572
-
573
- Required: clip_id (or media_pool_item_id), start_frame, end_frame,
574
- record_frame, track_index. Optional: media_type (1=video only, 2=audio only).
575
- """
576
- if not isinstance(ci, dict):
577
- return None, {"error": f"clip_infos[{index}] must be an object"}
578
- cid = ci.get("clip_id") or ci.get("media_pool_item_id")
579
- if not cid:
580
- return None, {"error": f"clip_infos[{index}] requires clip_id or media_pool_item_id"}
581
- mp_item = _find_clip_by_id(root, cid)
582
- if not mp_item:
583
- return None, {"error": f"clip_infos[{index}]: media pool clip not found: {cid}"}
584
- sf = ci.get("startFrame", ci.get("start_frame"))
585
- ef = ci.get("endFrame", ci.get("end_frame"))
586
- if sf is None or ef is None:
587
- return None, {"error": f"clip_infos[{index}] requires start_frame/startFrame and end_frame/endFrame"}
588
- rf = ci.get("recordFrame", ci.get("record_frame"))
589
- if rf is None:
590
- return None, {"error": f"clip_infos[{index}] requires record_frame/recordFrame"}
591
- rf, rf_err = _normalize_record_frame(ci, index, timeline_start_frame)
592
- if rf_err:
593
- return None, rf_err
594
- ti = ci.get("trackIndex", ci.get("track_index"))
595
- if ti is None:
596
- return None, {"error": f"clip_infos[{index}] requires track_index/trackIndex"}
597
- out: Dict[str, Any] = {
598
- "mediaPoolItem": mp_item,
599
- "startFrame": sf,
600
- "endFrame": ef,
601
- "recordFrame": rf,
602
- "trackIndex": ti,
603
- }
604
- mt = ci.get("mediaType", ci.get("media_type"))
605
- if mt is not None:
606
- out["mediaType"] = mt
607
- return out, None
608
-
609
-
610
- _AUDIO_SYNC_MODE_SUFFIXES = {
611
- "waveform": "WAVEFORM",
612
- "timecode": "TIMECODE",
613
- }
614
-
615
- _AUDIO_SYNC_CHANNEL_SPECIAL = {
616
- "automatic": "CHANNEL_AUTOMATIC",
617
- "auto": "CHANNEL_AUTOMATIC",
618
- "mix": "CHANNEL_MIX",
619
- }
620
-
621
-
622
- def _build_audio_sync_settings(resolve_obj, sync_mode=None, channel_number=None,
623
- retain_embedded_audio=None, retain_video_metadata=None):
624
- """Build the {audioSyncSettings} dict for MediaPool.AutoSyncAudio per docs lines 600-614.
625
-
626
- Returns (settings_dict, None) on success or (None, error_dict) on validation failure.
627
- """
628
- settings: Dict[Any, Any] = {}
629
- if sync_mode is not None:
630
- suffix = _AUDIO_SYNC_MODE_SUFFIXES.get(str(sync_mode).strip().lower())
631
- if not suffix:
632
- valid = sorted(_AUDIO_SYNC_MODE_SUFFIXES.keys())
633
- return None, {"error": f"Unknown sync_mode '{sync_mode}'. Valid: {valid}"}
634
- settings[resolve_obj.AUDIO_SYNC_MODE] = getattr(resolve_obj, f"AUDIO_SYNC_{suffix}")
635
- if channel_number is not None:
636
- if isinstance(channel_number, str):
637
- special = _AUDIO_SYNC_CHANNEL_SPECIAL.get(channel_number.strip().lower())
638
- if not special:
639
- return None, {"error": f"Unknown channel_number '{channel_number}'. Use an int >= 1 or 'automatic'/'mix'."}
640
- settings[resolve_obj.AUDIO_SYNC_CHANNEL_NUMBER] = getattr(resolve_obj, f"AUDIO_SYNC_{special}")
641
- elif isinstance(channel_number, int):
642
- settings[resolve_obj.AUDIO_SYNC_CHANNEL_NUMBER] = channel_number
643
- else:
644
- return None, {"error": f"channel_number must be int >= 1 or 'automatic'/'mix', got {type(channel_number).__name__}"}
645
- if retain_embedded_audio is not None:
646
- settings[resolve_obj.AUDIO_SYNC_RETAIN_EMBEDDED_AUDIO] = bool(retain_embedded_audio)
647
- if retain_video_metadata is not None:
648
- settings[resolve_obj.AUDIO_SYNC_RETAIN_VIDEO_METADATA] = bool(retain_video_metadata)
649
- return settings, None
650
-
651
-
652
- def _build_create_clip_info_dict(root, ci, index, timeline_start_frame=None):
653
- """Build one MediaPool.CreateTimelineFromClips clipInfo map.
654
-
655
- See docs/reference/resolve_scripting_api.txt line 224: 4 keys — mediaPoolItem,
656
- startFrame, endFrame, recordFrame.
657
- """
658
- if not isinstance(ci, dict):
659
- return None, {"error": f"clip_infos[{index}] must be an object"}
660
- cid = ci.get("clip_id") or ci.get("media_pool_item_id")
661
- if not cid:
662
- return None, {"error": f"clip_infos[{index}] requires clip_id or media_pool_item_id"}
663
- mp_item = _find_clip_by_id(root, cid)
664
- if not mp_item:
665
- return None, {"error": f"clip_infos[{index}]: media pool clip not found: {cid}"}
666
- sf = ci.get("startFrame", ci.get("start_frame"))
667
- ef = ci.get("endFrame", ci.get("end_frame"))
668
- if sf is None or ef is None:
669
- return None, {"error": f"clip_infos[{index}] requires start_frame/startFrame and end_frame/endFrame"}
670
- rf = ci.get("recordFrame", ci.get("record_frame"))
671
- if rf is None:
672
- return None, {"error": f"clip_infos[{index}] requires record_frame/recordFrame"}
673
- rf, rf_err = _normalize_record_frame(ci, index, timeline_start_frame)
674
- if rf_err:
675
- return None, rf_err
676
- return {
677
- "mediaPoolItem": mp_item,
678
- "startFrame": sf,
679
- "endFrame": ef,
680
- "recordFrame": rf,
681
- }, None
682
-
683
-
684
- def _find_clips_by_ids(folder, ids_set):
685
- found = []
686
- for clip in (folder.GetClipList() or []):
687
- if clip.GetUniqueId() in ids_set:
688
- found.append(clip)
689
- for sub in (folder.GetSubFolderList() or []):
690
- found.extend(_find_clips_by_ids(sub, ids_set))
691
- return found
692
-
693
- def _navigate_to_folder(mp, folder_path):
694
- root = mp.GetRootFolder()
695
- if not folder_path or folder_path in ("Master", "/", ""):
696
- return root
697
- parts = folder_path.strip("/").split("/")
698
- if parts[0] == "Master":
699
- parts = parts[1:]
700
- current = root
701
- for part in parts:
702
- found = False
703
- for sub in (current.GetSubFolderList() or []):
704
- if sub.GetName() == part:
705
- current = sub
706
- found = True
707
- break
708
- if not found:
709
- return None
710
- return current
711
-
712
- def _get_timeline():
713
- resolve = get_resolve()
714
- if resolve is None:
715
- return None, None, {"error": "Not connected to DaVinci Resolve"}
716
- project = resolve.GetProjectManager().GetCurrentProject()
717
- if not project:
718
- return None, None, {"error": "No project currently open"}
719
- tl = project.GetCurrentTimeline()
720
- if not tl:
721
- return project, None, {"error": "No current timeline"}
722
- return project, tl, None
723
-
724
- def _get_timeline_item(track_type="video", track_index=1, item_index=0):
725
- _, tl, err = _get_timeline()
726
- if err:
727
- return None, err
728
- items = tl.GetItemListInTrack(track_type, track_index)
729
- if not items or item_index >= len(items):
730
- return None, {"error": f"No item at index {item_index} on {track_type} track {track_index}"}
731
- return items[item_index], None
732
-
733
- def _has_method(obj, method_name):
734
- return callable(getattr(obj, method_name, None))
735
-
736
- def _requires_method(obj, method_name, min_version):
737
- if _has_method(obj, method_name):
738
- return None
739
- return {"error": f"{method_name} requires DaVinci Resolve {min_version}+"}
740
-
741
- __all__ = [name for name in globals() if not name.startswith("__")]