@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,1092 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Live timeline edit kernel boundary probe.
3
-
4
- Creates a disposable Resolve project with synthetic media, probes the deepest
5
- timeline-editing API surfaces this project knows about, writes JSON/Markdown
6
- evidence reports, and deletes the project unless --keep-open is provided.
7
-
8
- Run with Python 3.10-3.12 against a running Resolve Studio instance:
9
-
10
- python3.11 tests/live_duplicate_clips_validation.py --output-dir /tmp/timeline-kernel-probe
11
- """
12
-
13
- from __future__ import annotations
14
-
15
- import json
16
- import platform
17
- import subprocess
18
- import sys
19
- import tempfile
20
- import time
21
- import types
22
- import traceback
23
- from pathlib import Path
24
- from typing import Any, Dict, Iterable, List, Optional, Tuple
25
-
26
- from src.utils.timeline_kernel_probe import (
27
- ProbeRecorder,
28
- ordered_unique,
29
- parse_api_class_methods,
30
- parse_timeline_item_property_keys,
31
- render_markdown_report,
32
- utc_timestamp,
33
- values_match,
34
- )
35
-
36
- PROPERTY_CANDIDATES: Dict[str, Any] = {
37
- "Pan": 0.33,
38
- "Tilt": -0.25,
39
- "ZoomX": 1.15,
40
- "ZoomY": 1.1,
41
- "ZoomGang": False,
42
- "RotationAngle": 8.0,
43
- "AnchorPointX": 0.05,
44
- "AnchorPointY": -0.05,
45
- "Pitch": 0.1,
46
- "Yaw": -0.1,
47
- "FlipX": True,
48
- "FlipY": False,
49
- "CropLeft": 2.0,
50
- "CropRight": 1.0,
51
- "CropTop": 1.0,
52
- "CropBottom": 2.0,
53
- "CropSoftness": 4.0,
54
- "CropRetain": True,
55
- "DynamicZoomEnable": True,
56
- "DynamicZoomMode": 1,
57
- "DynamicZoomEase": 2,
58
- "CompositeMode": 1,
59
- "Opacity": 72.0,
60
- "Distortion": 0.1,
61
- "Speed": 100.0,
62
- "RetimeProcess": 1,
63
- "MotionEstimation": 1,
64
- "Scaling": 2,
65
- "ResizeFilter": 3,
66
- "StabilizationEnable": True,
67
- "StabilizationMethod": 1,
68
- "StabilizationStrength": 0.75,
69
- "Volume": -6.0,
70
- "AudioSyncOffsetIsManual": False,
71
- "AudioSyncOffset": 0,
72
- "EQEnable": False,
73
- "NormalizeEnable": False,
74
- "NormalizeLevel": -12.0,
75
- }
76
-
77
- EXTRA_TIMELINE_METHODS = [
78
- "GetItemsInTrack",
79
- "GetCurrentVideoItem",
80
- "GetCurrentTimecode",
81
- "SetCurrentTimecode",
82
- ]
83
-
84
- EXTRA_TIMELINE_ITEM_METHODS = [
85
- "GetType",
86
- "GetMediaType",
87
- "AddKeyframe",
88
- "GetKeyframeCount",
89
- "GetKeyframeAtIndex",
90
- "GetPropertyAtKeyframeIndex",
91
- "SetKeyframeInterpolation",
92
- "GetIsColorOutputCacheEnabled",
93
- "SetColorOutputCache",
94
- "GetIsFusionOutputCacheEnabled",
95
- "SetFusionOutputCache",
96
- "GetVoiceIsolationState",
97
- "SetVoiceIsolationState",
98
- ]
99
-
100
-
101
- def _install_mcp_stubs() -> None:
102
- """Allow importing src.server when MCP deps are absent from Python 3.11."""
103
-
104
- class FastMCP:
105
- def __init__(self, *args, **kwargs):
106
- pass
107
-
108
- def tool(self, *args, **kwargs):
109
- def decorate(func):
110
- return func
111
-
112
- return decorate
113
-
114
- def resource(self, *args, **kwargs):
115
- def decorate(func):
116
- return func
117
-
118
- return decorate
119
-
120
- def stdio_server(*args, **kwargs):
121
- raise RuntimeError("stdio_server is not used by the live timeline kernel probe")
122
-
123
- anyio = types.ModuleType("anyio")
124
- anyio.run = lambda func: func()
125
-
126
- mcp = types.ModuleType("mcp")
127
- server = types.ModuleType("mcp.server")
128
- fastmcp = types.ModuleType("mcp.server.fastmcp")
129
- stdio = types.ModuleType("mcp.server.stdio")
130
-
131
- fastmcp.FastMCP = FastMCP
132
- stdio.stdio_server = stdio_server
133
-
134
- sys.modules.setdefault("anyio", anyio)
135
- sys.modules.setdefault("mcp", mcp)
136
- sys.modules.setdefault("mcp.server", server)
137
- sys.modules.setdefault("mcp.server.fastmcp", fastmcp)
138
- sys.modules.setdefault("mcp.server.stdio", stdio)
139
-
140
-
141
- def _require_success(label: str, result: Dict[str, Any]) -> Dict[str, Any]:
142
- if not isinstance(result, dict):
143
- raise AssertionError(f"{label}: expected dict, got {result!r}")
144
- if result.get("error"):
145
- raise AssertionError(f"{label}: {result['error']}")
146
- if "success" in result and result["success"] is not True:
147
- raise AssertionError(f"{label}: expected success=True, got {result!r}")
148
- return result
149
-
150
-
151
- def _frame_int(value) -> int:
152
- return int(round(float(value)))
153
-
154
-
155
- def _source_start(item) -> int:
156
- if hasattr(item, "GetSourceStartFrame"):
157
- try:
158
- value = item.GetSourceStartFrame()
159
- if value is not None:
160
- return _frame_int(value)
161
- except Exception:
162
- pass
163
- return _frame_int(item.GetLeftOffset())
164
-
165
-
166
- def _make_synthetic_media(work_dir: Path) -> Path:
167
- media_path = work_dir / "timeline_kernel_probe_source.mov"
168
- subprocess.run(
169
- [
170
- "ffmpeg",
171
- "-hide_banner",
172
- "-loglevel",
173
- "error",
174
- "-f",
175
- "lavfi",
176
- "-i",
177
- "testsrc2=size=320x180:rate=24:duration=5",
178
- "-f",
179
- "lavfi",
180
- "-i",
181
- "sine=frequency=440:sample_rate=48000:duration=5",
182
- "-shortest",
183
- "-pix_fmt",
184
- "yuv420p",
185
- "-c:v",
186
- "libx264",
187
- "-c:a",
188
- "aac",
189
- "-y",
190
- str(media_path),
191
- ],
192
- check=True,
193
- )
194
- return media_path
195
-
196
-
197
- def _safe_call(func, *args):
198
- try:
199
- return func(*args), None
200
- except Exception as exc:
201
- return None, exc
202
-
203
-
204
- def _safe_item_id(item) -> Optional[str]:
205
- if not item:
206
- return None
207
- try:
208
- return str(item.GetUniqueId())
209
- except Exception:
210
- return None
211
-
212
-
213
- def _safe_timeline_id(timeline) -> Optional[str]:
214
- if not timeline:
215
- return None
216
- try:
217
- return str(timeline.GetUniqueId())
218
- except Exception:
219
- return None
220
-
221
-
222
- def _ensure_current_timeline(
223
- recorder: ProbeRecorder,
224
- project,
225
- timeline,
226
- label: str,
227
- *,
228
- required: bool = False,
229
- ) -> bool:
230
- """Best-effort current timeline guard for Resolve bridge versions with flaky setters."""
231
- if not project or not timeline:
232
- if required:
233
- raise AssertionError("No project or timeline available to set current timeline")
234
- recorder.record(
235
- "runtime.current_timeline",
236
- label,
237
- "unsupported",
238
- details={"reason": "No project or timeline available"},
239
- )
240
- return False
241
-
242
- target_id = _safe_timeline_id(timeline)
243
- get_current = getattr(project, "GetCurrentTimeline", None)
244
- if callable(get_current):
245
- current, current_exc = _safe_call(get_current)
246
- if current:
247
- current_id = _safe_timeline_id(current)
248
- if not target_id or not current_id or current_id == target_id:
249
- return True
250
- elif current_exc:
251
- recorder.record(
252
- "runtime.current_timeline",
253
- f"{label}.get_current",
254
- "error",
255
- details={"exception": repr(current_exc)},
256
- )
257
-
258
- setter = getattr(project, "SetCurrentTimeline", None)
259
- if callable(setter):
260
- ok, set_exc = _safe_call(setter, timeline)
261
- if ok:
262
- return True
263
- recorder.record(
264
- "runtime.current_timeline",
265
- f"{label}.set_current",
266
- "partially_supported",
267
- details={"returned": ok, "exception": repr(set_exc) if set_exc else None},
268
- )
269
- else:
270
- recorder.record(
271
- "runtime.current_timeline",
272
- f"{label}.set_current",
273
- "version_or_page_dependent",
274
- details={"reason": "Project.SetCurrentTimeline is not callable in this Resolve bridge state"},
275
- )
276
-
277
- if callable(get_current):
278
- current, _ = _safe_call(get_current)
279
- if current:
280
- current_id = _safe_timeline_id(current)
281
- if not target_id or not current_id or current_id == target_id:
282
- return True
283
-
284
- if required:
285
- raise AssertionError("Failed to create or set current timeline")
286
- return False
287
-
288
-
289
- def _record_method_availability(
290
- recorder: ProbeRecorder,
291
- obj,
292
- category: str,
293
- class_name: str,
294
- method_names: Iterable[str],
295
- ) -> None:
296
- for method_name in ordered_unique(method_names):
297
- recorder.record(
298
- category,
299
- f"{class_name}.{method_name}",
300
- "supported" if callable(getattr(obj, method_name, None)) else "unsupported",
301
- )
302
-
303
-
304
- def _record_tool_result(
305
- recorder: ProbeRecorder,
306
- category: str,
307
- name: str,
308
- result: Dict[str, Any],
309
- *,
310
- expected_boundary: bool = False,
311
- ) -> None:
312
- if not isinstance(result, dict):
313
- recorder.record(category, name, "error", details={"reason": "non-dict result", "result": repr(result)})
314
- return
315
- if result.get("error"):
316
- recorder.record(
317
- category,
318
- name,
319
- "unsupported" if expected_boundary else "error",
320
- details={"reason": result.get("error"), "expected_boundary": expected_boundary},
321
- evidence=result,
322
- )
323
- return
324
- if "success" in result and result["success"] is not True:
325
- recorder.record(
326
- category,
327
- name,
328
- "partially_supported",
329
- details={"reason": "success flag was false"},
330
- evidence=result,
331
- )
332
- return
333
- if result.get("results") and any(row.get("success") is False for row in result["results"] if isinstance(row, dict)):
334
- recorder.record(category, name, "partially_supported", evidence=result)
335
- return
336
- recorder.record(category, name, "supported", evidence=result)
337
-
338
-
339
- def _probe_property(recorder: ProbeRecorder, item, item_label: str, key: str) -> None:
340
- get_property = getattr(item, "GetProperty", None)
341
- set_property = getattr(item, "SetProperty", None)
342
- if not callable(get_property):
343
- recorder.record(f"properties.{item_label}", key, "unsupported", details={"reason": "GetProperty missing"})
344
- return
345
-
346
- original, read_error = _safe_call(get_property, key)
347
- read_available = read_error is None and original is not None
348
- candidate = PROPERTY_CANDIDATES.get(key, original if original is not None else 1)
349
-
350
- if not callable(set_property):
351
- status = "read_only" if read_available else "unsupported"
352
- recorder.record(
353
- f"properties.{item_label}",
354
- key,
355
- status,
356
- details={"read": original, "reason": "SetProperty missing" if read_available else "property unavailable"},
357
- )
358
- return
359
-
360
- write_result, write_error = _safe_call(set_property, key, candidate)
361
- readback, readback_error = _safe_call(get_property, key)
362
- restore_result = None
363
- if read_available and write_result:
364
- restore_result, _ = _safe_call(set_property, key, original)
365
-
366
- details = {
367
- "read": original,
368
- "write": bool(write_result) if write_error is None else False,
369
- "write_error": repr(write_error) if write_error else None,
370
- "readback": readback,
371
- "readback_error": repr(readback_error) if readback_error else None,
372
- "restore": bool(restore_result) if restore_result is not None else None,
373
- }
374
-
375
- if read_error is not None:
376
- status = "error"
377
- details["read_error"] = repr(read_error)
378
- elif write_error is not None:
379
- status = "partially_supported" if read_available else "unsupported"
380
- elif not write_result:
381
- status = "partially_supported" if read_available else "unsupported"
382
- details["reason"] = "SetProperty returned false"
383
- elif readback is None:
384
- status = "write_only_unverifiable"
385
- elif values_match(readback, candidate):
386
- status = "supported"
387
- else:
388
- status = "partially_supported"
389
- details["reason"] = "SetProperty returned true but readback did not match"
390
-
391
- recorder.record(f"properties.{item_label}", key, status, details=details)
392
-
393
-
394
- def _probe_keyframe(recorder: ProbeRecorder, item, item_label: str, key: str) -> None:
395
- add_keyframe = getattr(item, "AddKeyframe", None)
396
- get_count = getattr(item, "GetKeyframeCount", None)
397
- get_at_index = getattr(item, "GetKeyframeAtIndex", None)
398
- get_value = getattr(item, "GetPropertyAtKeyframeIndex", None)
399
- if not callable(add_keyframe):
400
- recorder.record(f"keyframes.{item_label}", key, "unsupported", details={"reason": "AddKeyframe missing"})
401
- return
402
-
403
- before = None
404
- if callable(get_count):
405
- before, _ = _safe_call(get_count, key)
406
- value = PROPERTY_CANDIDATES.get(key, 1)
407
- added, add_error = _safe_call(add_keyframe, key, 0, value)
408
- after = None
409
- if callable(get_count):
410
- after, _ = _safe_call(get_count, key)
411
-
412
- evidence: List[Dict[str, Any]] = []
413
- if callable(get_at_index) and callable(get_value) and after:
414
- for index in range(int(after)):
415
- keyframe, keyframe_error = _safe_call(get_at_index, key, index)
416
- prop_value, value_error = _safe_call(get_value, key, index)
417
- evidence.append(
418
- {
419
- "index": index,
420
- "keyframe": keyframe,
421
- "keyframe_error": repr(keyframe_error) if keyframe_error else None,
422
- "value": prop_value,
423
- "value_error": repr(value_error) if value_error else None,
424
- }
425
- )
426
-
427
- details = {
428
- "before": before,
429
- "added": bool(added) if add_error is None else False,
430
- "add_error": repr(add_error) if add_error else None,
431
- "after": after,
432
- "read_methods": {
433
- "GetKeyframeCount": callable(get_count),
434
- "GetKeyframeAtIndex": callable(get_at_index),
435
- "GetPropertyAtKeyframeIndex": callable(get_value),
436
- },
437
- }
438
- if add_error:
439
- status = "error"
440
- elif added and after is not None and int(after) > int(before or 0):
441
- status = "supported"
442
- elif added:
443
- status = "write_only_unverifiable"
444
- elif before is not None:
445
- status = "partially_supported"
446
- details["reason"] = "AddKeyframe returned false"
447
- else:
448
- status = "unsupported"
449
- details["reason"] = "AddKeyframe returned false and keyframe readback is unavailable"
450
- recorder.record(f"keyframes.{item_label}", key, status, details=details, evidence=evidence)
451
-
452
-
453
- def _probe_markers_flags_color_enabled(recorder: ProbeRecorder, item, item_label: str) -> None:
454
- marker_frame = 9
455
- add_marker = getattr(item, "AddMarker", None)
456
- if callable(add_marker):
457
- added, exc = _safe_call(add_marker, marker_frame, "Green", "Probe marker", "Timeline kernel probe", 1, "kernel-probe")
458
- markers, markers_exc = _safe_call(item.GetMarkers) if callable(getattr(item, "GetMarkers", None)) else ({}, None)
459
- custom, custom_exc = (
460
- _safe_call(item.GetMarkerCustomData, marker_frame)
461
- if callable(getattr(item, "GetMarkerCustomData", None))
462
- else (None, None)
463
- )
464
- deleted, delete_exc = (
465
- _safe_call(item.DeleteMarkerByCustomData, "kernel-probe")
466
- if callable(getattr(item, "DeleteMarkerByCustomData", None))
467
- else (None, None)
468
- )
469
- status = "supported" if added and markers_exc is None else "partially_supported"
470
- recorder.record(
471
- f"metadata.{item_label}",
472
- "markers",
473
- status,
474
- details={
475
- "added": bool(added),
476
- "add_error": repr(exc) if exc else None,
477
- "marker_count": len(markers or {}) if isinstance(markers, dict) else None,
478
- "custom_data": custom,
479
- "custom_error": repr(custom_exc) if custom_exc else None,
480
- "deleted": bool(deleted) if deleted is not None else None,
481
- "delete_error": repr(delete_exc) if delete_exc else None,
482
- },
483
- )
484
- else:
485
- recorder.record(f"metadata.{item_label}", "markers", "unsupported", details={"reason": "AddMarker missing"})
486
-
487
- if callable(getattr(item, "AddFlag", None)):
488
- added, exc = _safe_call(item.AddFlag, "Green")
489
- flags, flags_exc = _safe_call(item.GetFlagList) if callable(getattr(item, "GetFlagList", None)) else ([], None)
490
- cleared, clear_exc = _safe_call(item.ClearFlags, "Green") if callable(getattr(item, "ClearFlags", None)) else (None, None)
491
- recorder.record(
492
- f"metadata.{item_label}",
493
- "flags",
494
- "supported" if added and flags_exc is None else "partially_supported",
495
- details={
496
- "added": bool(added),
497
- "add_error": repr(exc) if exc else None,
498
- "flags": flags,
499
- "flags_error": repr(flags_exc) if flags_exc else None,
500
- "cleared": bool(cleared) if cleared is not None else None,
501
- "clear_error": repr(clear_exc) if clear_exc else None,
502
- },
503
- )
504
- else:
505
- recorder.record(f"metadata.{item_label}", "flags", "unsupported", details={"reason": "AddFlag missing"})
506
-
507
- if callable(getattr(item, "SetClipColor", None)):
508
- original, _ = _safe_call(item.GetClipColor) if callable(getattr(item, "GetClipColor", None)) else (None, None)
509
- set_result, exc = _safe_call(item.SetClipColor, "Teal")
510
- readback, read_exc = _safe_call(item.GetClipColor) if callable(getattr(item, "GetClipColor", None)) else (None, None)
511
- clear_result, clear_exc = _safe_call(item.ClearClipColor) if callable(getattr(item, "ClearClipColor", None)) else (None, None)
512
- if original:
513
- _safe_call(item.SetClipColor, original)
514
- recorder.record(
515
- f"metadata.{item_label}",
516
- "clip_color",
517
- "supported" if set_result and readback == "Teal" else "partially_supported",
518
- details={
519
- "read": original,
520
- "write": bool(set_result),
521
- "write_error": repr(exc) if exc else None,
522
- "readback": readback,
523
- "readback_error": repr(read_exc) if read_exc else None,
524
- "clear": bool(clear_result) if clear_result is not None else None,
525
- "clear_error": repr(clear_exc) if clear_exc else None,
526
- },
527
- )
528
- else:
529
- recorder.record(f"metadata.{item_label}", "clip_color", "unsupported", details={"reason": "SetClipColor missing"})
530
-
531
- if callable(getattr(item, "SetClipEnabled", None)):
532
- original, _ = _safe_call(item.GetClipEnabled) if callable(getattr(item, "GetClipEnabled", None)) else (None, None)
533
- set_result, exc = _safe_call(item.SetClipEnabled, False)
534
- readback, read_exc = _safe_call(item.GetClipEnabled) if callable(getattr(item, "GetClipEnabled", None)) else (None, None)
535
- if original is not None:
536
- _safe_call(item.SetClipEnabled, bool(original))
537
- recorder.record(
538
- f"metadata.{item_label}",
539
- "enabled_state",
540
- "supported" if set_result and readback is False else "partially_supported",
541
- details={
542
- "read": original,
543
- "write": bool(set_result),
544
- "write_error": repr(exc) if exc else None,
545
- "readback": readback,
546
- "readback_error": repr(read_exc) if read_exc else None,
547
- },
548
- )
549
- else:
550
- recorder.record(f"metadata.{item_label}", "enabled_state", "unsupported", details={"reason": "SetClipEnabled missing"})
551
-
552
-
553
- def _probe_cache_voice_takes_fusion_grade(
554
- recorder: ProbeRecorder,
555
- item,
556
- duplicate_item,
557
- media_pool_item,
558
- item_label: str,
559
- output_dir: Path,
560
- ) -> None:
561
- for cache_name, getter_name, setter_name, value in (
562
- ("color_cache", "GetIsColorOutputCacheEnabled", "SetColorOutputCache", "On"),
563
- ("fusion_cache", "GetIsFusionOutputCacheEnabled", "SetFusionOutputCache", "Auto"),
564
- ):
565
- getter = getattr(item, getter_name, None)
566
- setter = getattr(item, setter_name, None)
567
- if not callable(getter) or not callable(setter):
568
- recorder.record(f"advanced.{item_label}", cache_name, "unsupported", details={"reason": "cache API missing"})
569
- continue
570
- original, read_exc = _safe_call(getter)
571
- set_result, set_exc = _safe_call(setter, value)
572
- readback, readback_exc = _safe_call(getter)
573
- if original is not None:
574
- _safe_call(setter, original)
575
- recorder.record(
576
- f"advanced.{item_label}",
577
- cache_name,
578
- "supported" if set_result and readback is not None else "partially_supported",
579
- details={
580
- "read": original,
581
- "read_error": repr(read_exc) if read_exc else None,
582
- "write": bool(set_result),
583
- "write_error": repr(set_exc) if set_exc else None,
584
- "readback": readback,
585
- "readback_error": repr(readback_exc) if readback_exc else None,
586
- },
587
- )
588
-
589
- if callable(getattr(item, "GetVoiceIsolationState", None)) and callable(getattr(item, "SetVoiceIsolationState", None)):
590
- original, read_exc = _safe_call(item.GetVoiceIsolationState)
591
- set_result, set_exc = _safe_call(item.SetVoiceIsolationState, {"isEnabled": True, "amount": 25})
592
- readback, readback_exc = _safe_call(item.GetVoiceIsolationState)
593
- if original:
594
- _safe_call(item.SetVoiceIsolationState, original)
595
- recorder.record(
596
- f"advanced.{item_label}",
597
- "item_voice_isolation",
598
- "supported" if set_result and readback else "partially_supported",
599
- details={
600
- "read": original,
601
- "read_error": repr(read_exc) if read_exc else None,
602
- "write": bool(set_result),
603
- "write_error": repr(set_exc) if set_exc else None,
604
- "readback": readback,
605
- "readback_error": repr(readback_exc) if readback_exc else None,
606
- },
607
- )
608
- else:
609
- recorder.record(
610
- f"advanced.{item_label}",
611
- "item_voice_isolation",
612
- "unsupported",
613
- details={"reason": "item voice isolation API missing"},
614
- )
615
-
616
- if callable(getattr(item, "AddTake", None)):
617
- added, add_exc = _safe_call(item.AddTake, media_pool_item, 24, 71)
618
- count, count_exc = _safe_call(item.GetTakesCount) if callable(getattr(item, "GetTakesCount", None)) else (None, None)
619
- take, take_exc = (
620
- _safe_call(item.GetTakeByIndex, int(count or 1))
621
- if callable(getattr(item, "GetTakeByIndex", None)) and count
622
- else (None, None)
623
- )
624
- selected, select_exc = (
625
- _safe_call(item.SelectTakeByIndex, int(count))
626
- if callable(getattr(item, "SelectTakeByIndex", None)) and count
627
- else (None, None)
628
- )
629
- deleted, delete_exc = (
630
- _safe_call(item.DeleteTakeByIndex, int(count))
631
- if callable(getattr(item, "DeleteTakeByIndex", None)) and count
632
- else (None, None)
633
- )
634
- recorder.record(
635
- f"advanced.{item_label}",
636
- "takes",
637
- "supported" if added and count else "partially_supported",
638
- details={
639
- "added": bool(added),
640
- "add_error": repr(add_exc) if add_exc else None,
641
- "count": count,
642
- "count_error": repr(count_exc) if count_exc else None,
643
- "take_read": bool(take),
644
- "take_error": repr(take_exc) if take_exc else None,
645
- "selected": bool(selected) if selected is not None else None,
646
- "select_error": repr(select_exc) if select_exc else None,
647
- "deleted": bool(deleted) if deleted is not None else None,
648
- "delete_error": repr(delete_exc) if delete_exc else None,
649
- },
650
- )
651
- else:
652
- recorder.record(f"advanced.{item_label}", "takes", "unsupported", details={"reason": "AddTake missing"})
653
-
654
- if callable(getattr(item, "AddFusionComp", None)):
655
- comp, add_exc = _safe_call(item.AddFusionComp)
656
- count, count_exc = _safe_call(item.GetFusionCompCount) if callable(getattr(item, "GetFusionCompCount", None)) else (None, None)
657
- names, names_exc = (
658
- _safe_call(item.GetFusionCompNameList)
659
- if callable(getattr(item, "GetFusionCompNameList", None))
660
- else (None, None)
661
- )
662
- export_path = output_dir / f"{item_label}_fusion_comp.setting"
663
- exported, export_exc = (
664
- _safe_call(item.ExportFusionComp, str(export_path), 1)
665
- if callable(getattr(item, "ExportFusionComp", None)) and count
666
- else (None, None)
667
- )
668
- recorder.record(
669
- f"advanced.{item_label}",
670
- "fusion_comps",
671
- "supported" if comp and count else "partially_supported",
672
- details={
673
- "added": bool(comp),
674
- "add_error": repr(add_exc) if add_exc else None,
675
- "count": count,
676
- "count_error": repr(count_exc) if count_exc else None,
677
- "names": names,
678
- "names_error": repr(names_exc) if names_exc else None,
679
- "exported": bool(exported) if exported is not None else None,
680
- "export_error": repr(export_exc) if export_exc else None,
681
- "export_path": str(export_path) if exported else None,
682
- },
683
- )
684
- else:
685
- recorder.record(f"advanced.{item_label}", "fusion_comps", "unsupported", details={"reason": "AddFusionComp missing"})
686
-
687
- if callable(getattr(item, "CopyGrades", None)) and duplicate_item:
688
- copied, copy_exc = _safe_call(item.CopyGrades, [duplicate_item])
689
- recorder.record(
690
- f"advanced.{item_label}",
691
- "copy_grades",
692
- "supported" if copied else "partially_supported",
693
- details={"copied": bool(copied), "copy_error": repr(copy_exc) if copy_exc else None},
694
- )
695
- else:
696
- recorder.record(f"advanced.{item_label}", "copy_grades", "unsupported", details={"reason": "CopyGrades missing"})
697
-
698
-
699
- def _probe_timeline_operations(recorder: ProbeRecorder, server, timeline, source_id: str, source_duration: int) -> Optional[str]:
700
- duplicate_ids: List[str] = []
701
-
702
- def duplicate(name: str, params: Dict[str, Any]) -> Optional[str]:
703
- payload = dict(params)
704
- payload.setdefault("clip_ids", [source_id])
705
- result = server.timeline("duplicate_clips", payload)
706
- _record_tool_result(recorder, "operations.duplicate", name, result)
707
- try:
708
- item_id = result["results"][0]["timeline_item_id"]
709
- except Exception:
710
- return None
711
- if item_id:
712
- duplicate_ids.append(item_id)
713
- return item_id
714
-
715
- duplicate("same_time_cross_track", {"placement": "same_time", "target_track_index": 2})
716
- duplicate("offset", {"record_frame_offset": 180, "target_track_index": 2})
717
- duplicate("track_above", {"placement": "track_above"})
718
- duplicate("after_source", {"placement": "after_source"})
719
- duplicate("next_gap", {"placement": "next_gap", "target_track_index": 2})
720
- server.timeline_markers("set_current_timecode", {"timecode": "00:00:28:00"})
721
- duplicate("at_playhead", {"placement": "at_playhead", "target_track_index": 2})
722
- duplicate("include_linked_audio", {"record_frame": 780, "target_track_index": 2, "include_linked": True})
723
-
724
- copy_alias = server.timeline("copy_clips", {"clip_ids": [source_id], "target_track_index": 2, "record_frame": 840})
725
- _record_tool_result(recorder, "operations.duplicate", "copy_clips_alias", copy_alias)
726
-
727
- selected = server.timeline("duplicate_clips", {"selected": True, "target_track_index": 2, "record_frame": 880})
728
- if isinstance(selected, dict) and selected.get("error"):
729
- recorder.record(
730
- "operations.duplicate",
731
- "selected_or_current_fallback",
732
- "version_or_page_dependent",
733
- details={"reason": selected.get("error")},
734
- evidence=selected,
735
- )
736
- else:
737
- _record_tool_result(recorder, "operations.duplicate", "selected_or_current_fallback", selected)
738
-
739
- copy_range = server.timeline(
740
- "copy_range",
741
- {"start_frame": 110, "end_frame": 130, "record_frame": 930, "track_types": ["video"], "target_track_index": 2},
742
- )
743
- _record_tool_result(recorder, "operations.range", "copy_range", copy_range)
744
-
745
- duplicate_range = server.timeline(
746
- "duplicate_range",
747
- {"start_frame": 112, "end_frame": 128, "record_frame": 970, "track_types": ["video"], "target_track_index": 2},
748
- )
749
- _record_tool_result(recorder, "operations.range", "duplicate_range", duplicate_range)
750
-
751
- occupant_id = duplicate("overwrite_destination_fixture", {"target_track_index": 2, "record_frame": 1020})
752
- overwrite = server.timeline(
753
- "overwrite_range",
754
- {"start_frame": 100, "end_frame": 120, "record_frame": 1020, "track_types": ["video"], "target_track_index": 2},
755
- )
756
- _record_tool_result(recorder, "operations.range", "overwrite_range", overwrite)
757
- if occupant_id and not server._find_timeline_item_by_id(timeline, occupant_id):
758
- recorder.record("operations.range", "overwrite_deleted_destination_overlap", "supported")
759
- elif occupant_id:
760
- recorder.record("operations.range", "overwrite_deleted_destination_overlap", "partially_supported")
761
-
762
- lift_fixture_id = duplicate("lift_fixture", {"target_track_index": 1, "record_frame": 1120})
763
- lift = server.timeline(
764
- "lift_range",
765
- {"start_frame": 1120, "end_frame": 1120 + source_duration, "track_types": ["video"], "track_indices": [1]},
766
- )
767
- _record_tool_result(recorder, "operations.range", "lift_range_exact_item", lift)
768
- if lift_fixture_id and not server._find_timeline_item_by_id(timeline, lift_fixture_id):
769
- recorder.record("operations.range", "lift_deleted_exact_item", "supported")
770
- elif lift_fixture_id:
771
- recorder.record("operations.range", "lift_deleted_exact_item", "partially_supported")
772
-
773
- partial_lift = server.timeline(
774
- "lift_range",
775
- {"start_frame": 105, "end_frame": 110, "track_types": ["video"], "track_indices": [1]},
776
- )
777
- _record_tool_result(recorder, "operations.boundaries", "partial_lift_without_razor", partial_lift, expected_boundary=True)
778
-
779
- move_fixture_id = duplicate("move_fixture", {"target_track_index": 1, "record_frame": 1220})
780
- if move_fixture_id:
781
- move = server.timeline(
782
- "move_clips",
783
- {"clip_ids": [move_fixture_id], "target_track_index": 2, "record_frame": 1280},
784
- )
785
- _record_tool_result(recorder, "operations.duplicate", "move_clips", move)
786
-
787
- invalid_track = server.timeline("duplicate_clips", {"clip_ids": [source_id], "target_track_index": 99})
788
- _record_tool_result(recorder, "operations.boundaries", "invalid_target_track", invalid_track, expected_boundary=True)
789
-
790
- return duplicate_ids[0] if duplicate_ids else None
791
-
792
-
793
- def _probe_source_less_boundaries(recorder: ProbeRecorder, server, timeline) -> None:
794
- probes: List[Tuple[str, Any]] = []
795
- for name, call in (
796
- ("insert_fusion_composition", lambda: timeline.InsertFusionCompositionIntoTimeline()),
797
- ("insert_title_text", lambda: timeline.InsertTitleIntoTimeline("Text")),
798
- ):
799
- item, exc = _safe_call(call)
800
- if exc:
801
- recorder.record("operations.source_less", name, "error", details={"exception": repr(exc)})
802
- continue
803
- if not item:
804
- recorder.record("operations.source_less", name, "unsupported", details={"reason": "Resolve returned no item"})
805
- continue
806
- probes.append((name, item))
807
- recorder.record("operations.source_less", name, "supported", details={"timeline_item_id": _safe_item_id(item)})
808
-
809
- for name, item in probes:
810
- item_id = _safe_item_id(item)
811
- if not item_id:
812
- continue
813
- result = server.timeline("duplicate_clips", {"clip_ids": [item_id], "target_track_index": 2, "record_frame": 1400})
814
- _record_tool_result(
815
- recorder,
816
- "operations.boundaries",
817
- f"{name}_append_clone_without_media_pool_item",
818
- result,
819
- expected_boundary=True,
820
- )
821
-
822
-
823
- def _probe_track_controls(recorder: ProbeRecorder, server, timeline) -> None:
824
- for track_type, index in (("video", 1), ("audio", 1)):
825
- name = f"{track_type}.{index}"
826
- original_name = None
827
- try:
828
- original_name = timeline.GetTrackName(track_type, index)
829
- except Exception:
830
- pass
831
- result = server.timeline("set_track_name", {"track_type": track_type, "index": index, "name": f"Probe {name}"})
832
- _record_tool_result(recorder, "operations.tracks", f"set_track_name.{name}", result)
833
- enabled = server.timeline("set_track_enable", {"track_type": track_type, "index": index, "enabled": True})
834
- _record_tool_result(recorder, "operations.tracks", f"set_track_enable.{name}", enabled)
835
- locked = server.timeline("set_track_lock", {"track_type": track_type, "index": index, "locked": False})
836
- _record_tool_result(recorder, "operations.tracks", f"set_track_lock.{name}", locked)
837
- if original_name:
838
- server.timeline("set_track_name", {"track_type": track_type, "index": index, "name": original_name})
839
-
840
- voice_get = server.timeline("get_voice_isolation_state", {"track_index": 1})
841
- _record_tool_result(recorder, "operations.tracks", "audio_track_voice_isolation_get", voice_get)
842
- voice_set = server.timeline("set_voice_isolation_state", {"track_index": 1, "state": {"isEnabled": True, "amount": 20}})
843
- _record_tool_result(recorder, "operations.tracks", "audio_track_voice_isolation_set", voice_set)
844
-
845
-
846
- def run_probe(server, output_dir: Path, keep_open: bool = False) -> Dict[str, Any]:
847
- recorder = ProbeRecorder()
848
- project_name = f"_mcp_timeline_kernel_probe_{int(time.time())}"
849
- timeline_name = "timeline_kernel_boundary_probe"
850
- work_dir = Path(tempfile.mkdtemp(prefix="mcp_timeline_kernel_probe_"))
851
- created_project = False
852
- delete_result = None
853
- metadata: Dict[str, Any] = {
854
- "timestamp_utc": utc_timestamp(),
855
- "python": sys.version.replace("\n", " "),
856
- "platform": platform.platform(),
857
- "project_name": project_name,
858
- }
859
-
860
- try:
861
- version = _require_success("resolve_control.get_version", server.resolve_control("get_version"))
862
- metadata.update(
863
- {
864
- "product": version.get("product"),
865
- "version": version.get("version"),
866
- "version_string": version.get("version_string"),
867
- }
868
- )
869
- print(f"Connected to {metadata['product']} {metadata['version_string']}")
870
-
871
- _require_success("project_manager.create", server.project_manager("create", {"name": project_name}))
872
- created_project = True
873
- print(f"Created disposable project: {project_name}")
874
- _require_success("resolve_control.open_page", server.resolve_control("open_page", {"page": "edit"}))
875
-
876
- media_path = _make_synthetic_media(work_dir)
877
- metadata["synthetic_media"] = str(media_path)
878
- print(f"Generated synthetic media: {media_path}")
879
-
880
- resolve = server.get_resolve()
881
- project = resolve.GetProjectManager().GetCurrentProject()
882
- media_pool = project.GetMediaPool()
883
- imported = media_pool.ImportMedia([str(media_path)])
884
- if not imported:
885
- raise AssertionError(f"Failed to import synthetic media: {media_path}")
886
- media_pool_item = imported[0]
887
- media_pool_item_id = media_pool_item.GetUniqueId()
888
-
889
- timeline = media_pool.CreateEmptyTimeline(timeline_name)
890
- if not timeline:
891
- raise AssertionError("Failed to create timeline")
892
- _ensure_current_timeline(recorder, project, timeline, "initial", required=True)
893
-
894
- for _ in range(max(0, 3 - int(timeline.GetTrackCount("video") or 0))):
895
- _require_success("timeline.add_track video", server.timeline("add_track", {"track_type": "video"}))
896
- for _ in range(max(0, 2 - int(timeline.GetTrackCount("audio") or 0))):
897
- _require_success(
898
- "timeline.add_track audio",
899
- server.timeline("add_track", {"track_type": "audio", "options": {"audio_type": "stereo"}}),
900
- )
901
- server.timeline("set_start_timecode", {"timecode": "00:00:00:00"})
902
-
903
- append = _require_success(
904
- "media_pool.append_to_timeline video",
905
- server.media_pool(
906
- "append_to_timeline",
907
- {
908
- "clip_infos": [
909
- {
910
- "media_pool_item_id": media_pool_item_id,
911
- "start_frame": 24,
912
- "end_frame": 71,
913
- "record_frame": 100,
914
- "track_index": 1,
915
- "media_type": 1,
916
- }
917
- ]
918
- },
919
- ),
920
- )
921
- source_id = append["items"][0]["timeline_item_id"]
922
- source_item = server._find_timeline_item_by_id(timeline, source_id)
923
- if not source_item:
924
- raise AssertionError(f"Could not recover source item: {source_id}")
925
-
926
- audio_append = _require_success(
927
- "media_pool.append_to_timeline linked audio",
928
- server.media_pool(
929
- "append_to_timeline",
930
- {
931
- "clip_infos": [
932
- {
933
- "media_pool_item_id": media_pool_item_id,
934
- "start_frame": 24,
935
- "end_frame": 71,
936
- "record_frame": 100,
937
- "track_index": 1,
938
- "media_type": 2,
939
- }
940
- ]
941
- },
942
- ),
943
- )
944
- audio_id = audio_append["items"][0]["timeline_item_id"]
945
- audio_item = server._find_timeline_item_by_id(timeline, audio_id)
946
- if not audio_item:
947
- raise AssertionError(f"Could not recover audio item: {audio_id}")
948
- timeline.SetClipsLinked([source_item, audio_item], True)
949
-
950
- source_duration = _frame_int(source_item.GetDuration())
951
- metadata["source"] = {
952
- "timeline_item_id": source_id,
953
- "duration": source_duration,
954
- "source_start": _source_start(source_item),
955
- }
956
-
957
- api_text = Path("docs/reference/resolve_scripting_api.txt").read_text(encoding="utf-8")
958
- documented_property_keys = parse_timeline_item_property_keys(api_text)
959
- local_property_keys = []
960
- for keys in server._DUPLICATE_COPY_PROPERTY_KEYS.values():
961
- local_property_keys.extend(keys)
962
- property_keys = ordered_unique(documented_property_keys + local_property_keys + list(PROPERTY_CANDIDATES))
963
-
964
- timeline_methods = ordered_unique(parse_api_class_methods(api_text, "Timeline") + EXTRA_TIMELINE_METHODS)
965
- timeline_item_methods = ordered_unique(parse_api_class_methods(api_text, "TimelineItem") + EXTRA_TIMELINE_ITEM_METHODS)
966
- _record_method_availability(recorder, timeline, "runtime_methods.timeline", "Timeline", timeline_methods)
967
- _record_method_availability(recorder, source_item, "runtime_methods.timeline_item.video", "TimelineItem", timeline_item_methods)
968
- _record_method_availability(recorder, audio_item, "runtime_methods.timeline_item.audio", "TimelineItem", timeline_item_methods)
969
-
970
- duplicate_for_advanced_id = _probe_timeline_operations(recorder, server, timeline, source_id, source_duration)
971
- duplicate_for_advanced = (
972
- server._find_timeline_item_by_id(timeline, duplicate_for_advanced_id) if duplicate_for_advanced_id else None
973
- )
974
- _probe_track_controls(recorder, server, timeline)
975
- _probe_source_less_boundaries(recorder, server, timeline)
976
- _ensure_current_timeline(recorder, project, timeline, "before_property_probe")
977
-
978
- for key in property_keys:
979
- _probe_property(recorder, source_item, "video", key)
980
- _probe_property(recorder, audio_item, "audio", key)
981
-
982
- for key in property_keys:
983
- _probe_keyframe(recorder, source_item, "video", key)
984
-
985
- _probe_markers_flags_color_enabled(recorder, source_item, "video")
986
- _probe_markers_flags_color_enabled(recorder, audio_item, "audio")
987
- _probe_cache_voice_takes_fusion_grade(
988
- recorder,
989
- source_item,
990
- duplicate_for_advanced,
991
- media_pool_item,
992
- "video",
993
- output_dir,
994
- )
995
-
996
- _ensure_current_timeline(recorder, project, timeline, "before_mcp_capabilities")
997
- capabilities = server.timeline("edit_kernel_capabilities")
998
- _record_tool_result(recorder, "mcp_capabilities", "edit_kernel_capabilities", capabilities)
999
- probe = server.timeline("probe_edit_kernel_item", {"clip_ids": [source_id, audio_id]})
1000
- if isinstance(probe, dict) and probe.get("error") == "No current timeline":
1001
- recorder.record(
1002
- "mcp_capabilities",
1003
- "probe_edit_kernel_item",
1004
- "version_or_page_dependent",
1005
- details={
1006
- "reason": (
1007
- "Project.SetCurrentTimeline was not callable after source-less item probing; "
1008
- "the live validation covers this tool when Resolve keeps a current timeline"
1009
- )
1010
- },
1011
- evidence=probe,
1012
- )
1013
- direct_probe = server._timeline_probe_edit_kernel_item(timeline, {"clip_ids": [source_id, audio_id]})
1014
- if isinstance(direct_probe, dict) and direct_probe.get("error"):
1015
- recorder.record(
1016
- "mcp_capabilities",
1017
- "probe_edit_kernel_item_direct_timeline_object",
1018
- "version_or_page_dependent",
1019
- details={
1020
- "reason": (
1021
- "The retained timeline object could not resolve the original item IDs after "
1022
- "source-less item probing changed Resolve's timeline focus"
1023
- )
1024
- },
1025
- evidence=direct_probe,
1026
- )
1027
- else:
1028
- _record_tool_result(
1029
- recorder,
1030
- "mcp_capabilities",
1031
- "probe_edit_kernel_item_direct_timeline_object",
1032
- direct_probe,
1033
- )
1034
- else:
1035
- _record_tool_result(recorder, "mcp_capabilities", "probe_edit_kernel_item", probe)
1036
-
1037
- recorder.record(
1038
- "operations.boundaries",
1039
- "transition_cloning",
1040
- "unsupported",
1041
- details={"reason": "Resolve public API exposes no transition cloning primitive on TimelineItem"},
1042
- )
1043
- recorder.record(
1044
- "operations.boundaries",
1045
- "razor_or_split",
1046
- "unsupported",
1047
- details={"reason": "Resolve public API exposes no direct timeline split/razor primitive"},
1048
- )
1049
- recorder.record(
1050
- "operations.boundaries",
1051
- "opaque_speed_ramp_internals",
1052
- "partially_supported",
1053
- details={
1054
- "reason": "Speed/RetimeProcess/MotionEstimation and keyframes are visible when Resolve exposes them; opaque speed ramp curves are not independently inspectable"
1055
- },
1056
- )
1057
-
1058
- if keep_open:
1059
- server.project_manager("save")
1060
- print(f"LEFT PROJECT OPEN FOR INSPECTION: {project_name}")
1061
- created_project = False
1062
-
1063
- except Exception as exc:
1064
- recorder.record(
1065
- "probe",
1066
- "fatal",
1067
- "error",
1068
- details={"exception": repr(exc), "traceback": traceback.format_exc()},
1069
- )
1070
- raise
1071
- finally:
1072
- if created_project:
1073
- server.project_manager("save")
1074
- server.project_manager("close")
1075
- delete_result = server.project_manager("delete", {"name": project_name})
1076
- print(f"Deleted disposable project: {delete_result}")
1077
-
1078
- if delete_result is not None and delete_result.get("success") is not True:
1079
- raise AssertionError(f"Cleanup failed for {project_name}: {delete_result!r}")
1080
-
1081
- output_dir.mkdir(parents=True, exist_ok=True)
1082
- metadata["output_dir"] = str(output_dir)
1083
- report = recorder.to_report(metadata, artifacts={})
1084
- json_path = output_dir / "timeline-edit-kernel-probe.json"
1085
- markdown_path = output_dir / "timeline-edit-kernel-probe.md"
1086
- report["artifacts"] = {"json": str(json_path), "markdown": str(markdown_path)}
1087
- json_path.write_text(json.dumps(report, indent=2, sort_keys=True, default=str), encoding="utf-8")
1088
- markdown_path.write_text(render_markdown_report(report), encoding="utf-8")
1089
- print(f"Wrote JSON report: {json_path}")
1090
- print(f"Wrote Markdown report: {markdown_path}")
1091
- print(f"Counts: {json.dumps(report['counts'], sort_keys=True)}")
1092
- return report