@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
@@ -0,0 +1,1773 @@
1
+ // mcp-bridge-auto.jsx
2
+ // Auto-running MCP Bridge panel for After Effects
3
+
4
+ // Remove #include directives as we define functions below
5
+ /*
6
+ #include "createComposition.jsx"
7
+ #include "createTextLayer.jsx"
8
+ #include "createShapeLayer.jsx"
9
+ #include "createSolidLayer.jsx"
10
+ #include "setLayerProperties.jsx"
11
+ */
12
+
13
+ // --- Function Definitions ---
14
+
15
+ // --- createComposition (from createComposition.jsx) ---
16
+ function createComposition(args) {
17
+ try {
18
+ var name = args.name || "New Composition";
19
+ var width = parseInt(args.width) || 1920;
20
+ var height = parseInt(args.height) || 1080;
21
+ var pixelAspect = parseFloat(args.pixelAspect) || 1.0;
22
+ var duration = parseFloat(args.duration) || 10.0;
23
+ var frameRate = parseFloat(args.frameRate) || 30.0;
24
+ var bgColor = args.backgroundColor ? [args.backgroundColor.r/255, args.backgroundColor.g/255, args.backgroundColor.b/255] : [0, 0, 0];
25
+ var newComp = app.project.items.addComp(name, width, height, pixelAspect, duration, frameRate);
26
+ if (args.backgroundColor) {
27
+ newComp.bgColor = bgColor;
28
+ }
29
+ return JSON.stringify({
30
+ status: "success", message: "Composition created successfully",
31
+ composition: { name: newComp.name, id: newComp.id, width: newComp.width, height: newComp.height, pixelAspect: newComp.pixelAspect, duration: newComp.duration, frameRate: newComp.frameRate, bgColor: newComp.bgColor }
32
+ }, null, 2);
33
+ } catch (error) {
34
+ return JSON.stringify({ status: "error", message: error.toString() }, null, 2);
35
+ }
36
+ }
37
+
38
+ // --- createTextLayer (from createTextLayer.jsx) ---
39
+ function createTextLayer(args) {
40
+ try {
41
+ var compName = args.compName || "";
42
+ var text = args.text || "Text Layer";
43
+ var position = args.position || [960, 540];
44
+ var fontSize = args.fontSize || 72;
45
+ var color = args.color || [1, 1, 1];
46
+ var startTime = args.startTime || 0;
47
+ var duration = args.duration || 5;
48
+ var fontFamily = args.fontFamily || "Arial";
49
+ var alignment = args.alignment || "center";
50
+ var comp = null;
51
+ for (var i = 1; i <= app.project.numItems; i++) {
52
+ var item = app.project.item(i);
53
+ if (item instanceof CompItem && item.name === compName) { comp = item; break; }
54
+ }
55
+ if (!comp) {
56
+ if (app.project.activeItem instanceof CompItem) { comp = app.project.activeItem; }
57
+ else { throw new Error("No composition found with name '" + compName + "' and no active composition"); }
58
+ }
59
+ var textLayer = comp.layers.addText(text);
60
+ var textProp = textLayer.property("ADBE Text Properties").property("ADBE Text Document");
61
+ var textDocument = textProp.value;
62
+ textDocument.fontSize = fontSize;
63
+ textDocument.fillColor = color;
64
+ textDocument.font = fontFamily;
65
+ if (alignment === "left") { textDocument.justification = ParagraphJustification.LEFT_JUSTIFY; }
66
+ else if (alignment === "center") { textDocument.justification = ParagraphJustification.CENTER_JUSTIFY; }
67
+ else if (alignment === "right") { textDocument.justification = ParagraphJustification.RIGHT_JUSTIFY; }
68
+ textProp.setValue(textDocument);
69
+ textLayer.property("Position").setValue(position);
70
+ textLayer.startTime = startTime;
71
+ if (duration > 0) { textLayer.outPoint = startTime + duration; }
72
+ return JSON.stringify({
73
+ status: "success", message: "Text layer created successfully",
74
+ layer: { name: textLayer.name, index: textLayer.index, type: "text", inPoint: textLayer.inPoint, outPoint: textLayer.outPoint, position: textLayer.property("Position").value }
75
+ }, null, 2);
76
+ } catch (error) {
77
+ return JSON.stringify({ status: "error", message: error.toString() }, null, 2);
78
+ }
79
+ }
80
+
81
+ // --- createShapeLayer (from createShapeLayer.jsx) ---
82
+ function createShapeLayer(args) {
83
+ try {
84
+ var compName = args.compName || "";
85
+ var shapeType = args.shapeType || "rectangle";
86
+ var position = args.position || [960, 540];
87
+ var size = args.size || [200, 200];
88
+ var fillColor = args.fillColor || [1, 0, 0];
89
+ var strokeColor = args.strokeColor || [0, 0, 0];
90
+ var strokeWidth = args.strokeWidth || 0;
91
+ var startTime = args.startTime || 0;
92
+ var duration = args.duration || 5;
93
+ var name = args.name || "Shape Layer";
94
+ var points = args.points || 5;
95
+ var comp = null;
96
+ for (var i = 1; i <= app.project.numItems; i++) {
97
+ var item = app.project.item(i);
98
+ if (item instanceof CompItem && item.name === compName) { comp = item; break; }
99
+ }
100
+ if (!comp) {
101
+ if (app.project.activeItem instanceof CompItem) { comp = app.project.activeItem; }
102
+ else { throw new Error("No composition found with name '" + compName + "' and no active composition"); }
103
+ }
104
+ var shapeLayer = comp.layers.addShape();
105
+ shapeLayer.name = name;
106
+ var contents = shapeLayer.property("Contents");
107
+ var shapeGroup = contents.addProperty("ADBE Vector Group");
108
+ var groupContents = shapeGroup.property("Contents");
109
+ var shapePathProperty;
110
+ if (shapeType === "rectangle") {
111
+ shapePathProperty = groupContents.addProperty("ADBE Vector Shape - Rect");
112
+ shapePathProperty.property("Size").setValue(size);
113
+ } else if (shapeType === "ellipse") {
114
+ shapePathProperty = groupContents.addProperty("ADBE Vector Shape - Ellipse");
115
+ shapePathProperty.property("Size").setValue(size);
116
+ } else if (shapeType === "polygon" || shapeType === "star") {
117
+ shapePathProperty = groupContents.addProperty("ADBE Vector Shape - Star");
118
+ shapePathProperty.property("Type").setValue(shapeType === "polygon" ? 1 : 2);
119
+ shapePathProperty.property("Points").setValue(points);
120
+ shapePathProperty.property("Outer Radius").setValue(size[0] / 2);
121
+ if (shapeType === "star") { shapePathProperty.property("Inner Radius").setValue(size[0] / 3); }
122
+ }
123
+ var fill = groupContents.addProperty("ADBE Vector Graphic - Fill");
124
+ fill.property("Color").setValue(fillColor);
125
+ fill.property("Opacity").setValue(100);
126
+ if (strokeWidth > 0) {
127
+ var stroke = groupContents.addProperty("ADBE Vector Graphic - Stroke");
128
+ stroke.property("Color").setValue(strokeColor);
129
+ stroke.property("Stroke Width").setValue(strokeWidth);
130
+ stroke.property("Opacity").setValue(100);
131
+ }
132
+ shapeLayer.property("Position").setValue(position);
133
+ shapeLayer.startTime = startTime;
134
+ if (duration > 0) { shapeLayer.outPoint = startTime + duration; }
135
+ return JSON.stringify({
136
+ status: "success", message: "Shape layer created successfully",
137
+ layer: { name: shapeLayer.name, index: shapeLayer.index, type: "shape", shapeType: shapeType, inPoint: shapeLayer.inPoint, outPoint: shapeLayer.outPoint, position: shapeLayer.property("Position").value }
138
+ }, null, 2);
139
+ } catch (error) {
140
+ return JSON.stringify({ status: "error", message: error.toString() }, null, 2);
141
+ }
142
+ }
143
+
144
+ // --- createCamera ---
145
+ function createCamera(args) {
146
+ try {
147
+ var compName = args.compName || "";
148
+ var name = args.name || "Camera";
149
+ var zoom = args.zoom || 1777.78; // Default ~50mm equivalent
150
+ var position = args.position; // Optional [x, y, z]
151
+ var pointOfInterest = args.pointOfInterest; // Optional [x, y, z]
152
+ var oneNode = args.oneNode || false; // If true, create a one-node camera (no point of interest)
153
+
154
+ var comp = null;
155
+ for (var i = 1; i <= app.project.numItems; i++) {
156
+ var item = app.project.item(i);
157
+ if (item instanceof CompItem && item.name === compName) { comp = item; break; }
158
+ }
159
+ if (!comp) {
160
+ if (app.project.activeItem instanceof CompItem) { comp = app.project.activeItem; }
161
+ else { throw new Error("No composition found with name '" + compName + "' and no active composition"); }
162
+ }
163
+
164
+ var centerPoint = [comp.width / 2, comp.height / 2];
165
+ var cameraLayer = comp.layers.addCamera(name, centerPoint);
166
+ cameraLayer.property("Camera Options").property("Zoom").setValue(zoom);
167
+
168
+ if (oneNode) {
169
+ cameraLayer.autoOrient = AutoOrientType.NO_AUTO_ORIENT;
170
+ }
171
+
172
+ if (position !== undefined && position !== null) {
173
+ cameraLayer.property("Position").setValue(position);
174
+ }
175
+
176
+ if (pointOfInterest !== undefined && pointOfInterest !== null && !oneNode) {
177
+ cameraLayer.property("Point of Interest").setValue(pointOfInterest);
178
+ }
179
+
180
+ var result = {
181
+ name: cameraLayer.name,
182
+ index: cameraLayer.index,
183
+ zoom: cameraLayer.property("Camera Options").property("Zoom").value,
184
+ position: cameraLayer.property("Position").value,
185
+ oneNode: oneNode
186
+ };
187
+ if (!oneNode) {
188
+ result.pointOfInterest = cameraLayer.property("Point of Interest").value;
189
+ }
190
+
191
+ return JSON.stringify({
192
+ status: "success",
193
+ message: "Camera created successfully",
194
+ layer: result
195
+ }, null, 2);
196
+ } catch (error) {
197
+ return JSON.stringify({ status: "error", message: error.toString() }, null, 2);
198
+ }
199
+ }
200
+
201
+ // --- duplicateLayer ---
202
+ function duplicateLayer(args) {
203
+ try {
204
+ var compName = args.compName || "";
205
+ var layerIndex = args.layerIndex;
206
+ var layerName = args.layerName || "";
207
+ var newName = args.newName; // optional rename
208
+
209
+ var comp = null;
210
+ for (var i = 1; i <= app.project.numItems; i++) {
211
+ var item = app.project.item(i);
212
+ if (item instanceof CompItem && item.name === compName) { comp = item; break; }
213
+ }
214
+ if (!comp) {
215
+ if (app.project.activeItem instanceof CompItem) { comp = app.project.activeItem; }
216
+ else { throw new Error("No composition found with name '" + compName + "' and no active composition"); }
217
+ }
218
+
219
+ var layer = null;
220
+ if (layerIndex !== undefined && layerIndex !== null) {
221
+ if (layerIndex > 0 && layerIndex <= comp.numLayers) { layer = comp.layer(layerIndex); }
222
+ else { throw new Error("Layer index out of bounds: " + layerIndex); }
223
+ } else if (layerName) {
224
+ for (var j = 1; j <= comp.numLayers; j++) {
225
+ if (comp.layer(j).name === layerName) { layer = comp.layer(j); break; }
226
+ }
227
+ }
228
+ if (!layer) { throw new Error("Layer not found: " + (layerName || "index " + layerIndex)); }
229
+
230
+ var newLayer = layer.duplicate();
231
+ if (newName) { newLayer.name = newName; }
232
+
233
+ return JSON.stringify({
234
+ status: "success",
235
+ message: "Layer duplicated successfully",
236
+ original: { name: layer.name, index: layer.index },
237
+ duplicate: { name: newLayer.name, index: newLayer.index }
238
+ }, null, 2);
239
+ } catch (error) {
240
+ return JSON.stringify({ status: "error", message: error.toString() }, null, 2);
241
+ }
242
+ }
243
+
244
+ // --- deleteLayer ---
245
+ function deleteLayer(args) {
246
+ try {
247
+ var compName = args.compName || "";
248
+ var layerIndex = args.layerIndex;
249
+ var layerName = args.layerName || "";
250
+
251
+ var comp = null;
252
+ for (var i = 1; i <= app.project.numItems; i++) {
253
+ var item = app.project.item(i);
254
+ if (item instanceof CompItem && item.name === compName) { comp = item; break; }
255
+ }
256
+ if (!comp) {
257
+ if (app.project.activeItem instanceof CompItem) { comp = app.project.activeItem; }
258
+ else { throw new Error("No composition found with name '" + compName + "' and no active composition"); }
259
+ }
260
+
261
+ var layer = null;
262
+ if (layerIndex !== undefined && layerIndex !== null) {
263
+ if (layerIndex > 0 && layerIndex <= comp.numLayers) { layer = comp.layer(layerIndex); }
264
+ else { throw new Error("Layer index out of bounds: " + layerIndex); }
265
+ } else if (layerName) {
266
+ for (var j = 1; j <= comp.numLayers; j++) {
267
+ if (comp.layer(j).name === layerName) { layer = comp.layer(j); break; }
268
+ }
269
+ }
270
+ if (!layer) { throw new Error("Layer not found: " + (layerName || "index " + layerIndex)); }
271
+
272
+ var deletedName = layer.name;
273
+ var deletedIndex = layer.index;
274
+ layer.remove();
275
+
276
+ return JSON.stringify({
277
+ status: "success",
278
+ message: "Layer deleted successfully",
279
+ deleted: { name: deletedName, index: deletedIndex }
280
+ }, null, 2);
281
+ } catch (error) {
282
+ return JSON.stringify({ status: "error", message: error.toString() }, null, 2);
283
+ }
284
+ }
285
+
286
+ // --- setLayerMask: create or modify a mask on a layer ---
287
+ function setLayerMask(args) {
288
+ try {
289
+ var compName = args.compName || "";
290
+ var layerIndex = args.layerIndex;
291
+ var layerName = args.layerName || "";
292
+ var maskIndex = args.maskIndex; // optional — if provided, modify existing mask
293
+ var maskPath = args.maskPath; // array of [x, y] points defining the mask shape
294
+ var maskRect = args.maskRect; // shorthand: {top, left, width, height} for rectangular masks
295
+ var maskMode = args.maskMode || "add"; // "add", "subtract", "intersect", "none"
296
+ var maskFeather = args.maskFeather; // optional [x, y] feather
297
+ var maskOpacity = args.maskOpacity; // optional 0-100
298
+ var maskExpansion = args.maskExpansion; // optional pixels
299
+ var maskName = args.maskName; // optional rename
300
+
301
+ var comp = null;
302
+ for (var i = 1; i <= app.project.numItems; i++) {
303
+ var item = app.project.item(i);
304
+ if (item instanceof CompItem && item.name === compName) { comp = item; break; }
305
+ }
306
+ if (!comp) {
307
+ if (app.project.activeItem instanceof CompItem) { comp = app.project.activeItem; }
308
+ else { throw new Error("No composition found with name '" + compName + "' and no active composition"); }
309
+ }
310
+
311
+ var layer = null;
312
+ if (layerIndex !== undefined && layerIndex !== null) {
313
+ if (layerIndex > 0 && layerIndex <= comp.numLayers) { layer = comp.layer(layerIndex); }
314
+ else { throw new Error("Layer index out of bounds: " + layerIndex); }
315
+ } else if (layerName) {
316
+ for (var j = 1; j <= comp.numLayers; j++) {
317
+ if (comp.layer(j).name === layerName) { layer = comp.layer(j); break; }
318
+ }
319
+ }
320
+ if (!layer) { throw new Error("Layer not found: " + (layerName || "index " + layerIndex)); }
321
+
322
+ // Build the mask shape
323
+ var shapePoints = [];
324
+ if (maskRect) {
325
+ // Rectangle shorthand
326
+ var t = maskRect.top || 0;
327
+ var l = maskRect.left || 0;
328
+ var w = maskRect.width || comp.width;
329
+ var h = maskRect.height || comp.height;
330
+ shapePoints = [[l, t], [l + w, t], [l + w, t + h], [l, t + h]];
331
+ } else if (maskPath && maskPath.length >= 3) {
332
+ shapePoints = maskPath;
333
+ } else {
334
+ throw new Error("Must provide either maskRect or maskPath with at least 3 points");
335
+ }
336
+
337
+ // Create the shape object
338
+ var myShape = new Shape();
339
+ var vertices = [];
340
+ for (var p = 0; p < shapePoints.length; p++) {
341
+ vertices.push(shapePoints[p]);
342
+ }
343
+ myShape.vertices = vertices;
344
+ myShape.closed = true;
345
+
346
+ var changed = [];
347
+ var mask;
348
+
349
+ if (maskIndex !== undefined && maskIndex !== null) {
350
+ // Modify existing mask
351
+ if (maskIndex > 0 && maskIndex <= layer.property("Masks").numProperties) {
352
+ mask = layer.property("Masks").property(maskIndex);
353
+ } else {
354
+ throw new Error("Mask index out of bounds: " + maskIndex);
355
+ }
356
+ mask.property("Mask Path").setValue(myShape);
357
+ changed.push("maskPath");
358
+ } else {
359
+ // Create new mask
360
+ mask = layer.property("Masks").addProperty("Mask");
361
+ mask.property("Mask Path").setValue(myShape);
362
+ changed.push("newMask");
363
+ }
364
+
365
+ // Set mask mode
366
+ var modes = {
367
+ "none": MaskMode.NONE,
368
+ "add": MaskMode.ADD,
369
+ "subtract": MaskMode.SUBTRACT,
370
+ "intersect": MaskMode.INTERSECT,
371
+ "lighten": MaskMode.LIGHTEN,
372
+ "darken": MaskMode.DARKEN,
373
+ "difference": MaskMode.DIFFERENCE
374
+ };
375
+ if (modes[maskMode] !== undefined) {
376
+ mask.maskMode = modes[maskMode];
377
+ changed.push("maskMode");
378
+ }
379
+
380
+ if (maskFeather !== undefined && maskFeather !== null) {
381
+ mask.property("Mask Feather").setValue(maskFeather);
382
+ changed.push("maskFeather");
383
+ }
384
+ if (maskOpacity !== undefined && maskOpacity !== null) {
385
+ mask.property("Mask Opacity").setValue(maskOpacity);
386
+ changed.push("maskOpacity");
387
+ }
388
+ if (maskExpansion !== undefined && maskExpansion !== null) {
389
+ mask.property("Mask Expansion").setValue(maskExpansion);
390
+ changed.push("maskExpansion");
391
+ }
392
+ if (maskName) {
393
+ mask.name = maskName;
394
+ changed.push("maskName");
395
+ }
396
+
397
+ return JSON.stringify({
398
+ status: "success",
399
+ message: "Mask set successfully",
400
+ layer: { name: layer.name, index: layer.index },
401
+ mask: {
402
+ name: mask.name,
403
+ index: mask.propertyIndex,
404
+ mode: maskMode,
405
+ changedProperties: changed
406
+ }
407
+ }, null, 2);
408
+ } catch (error) {
409
+ return JSON.stringify({ status: "error", message: error.toString() }, null, 2);
410
+ }
411
+ }
412
+
413
+ // --- createSolidLayer (from createSolidLayer.jsx) ---
414
+ function createSolidLayer(args) {
415
+ try {
416
+ var compName = args.compName || "";
417
+ var color = args.color || [1, 1, 1];
418
+ var name = args.name || "Solid Layer";
419
+ var position = args.position || [960, 540];
420
+ var size = args.size;
421
+ var startTime = args.startTime || 0;
422
+ var duration = args.duration || 5;
423
+ var isAdjustment = args.isAdjustment || false;
424
+ var comp = null;
425
+ for (var i = 1; i <= app.project.numItems; i++) {
426
+ var item = app.project.item(i);
427
+ if (item instanceof CompItem && item.name === compName) { comp = item; break; }
428
+ }
429
+ if (!comp) {
430
+ if (app.project.activeItem instanceof CompItem) { comp = app.project.activeItem; }
431
+ else { throw new Error("No composition found with name '" + compName + "' and no active composition"); }
432
+ }
433
+ if (!size) { size = [comp.width, comp.height]; }
434
+ var solidLayer;
435
+ if (isAdjustment) {
436
+ solidLayer = comp.layers.addSolid([0, 0, 0], name, size[0], size[1], 1);
437
+ solidLayer.adjustmentLayer = true;
438
+ } else {
439
+ solidLayer = comp.layers.addSolid(color, name, size[0], size[1], 1);
440
+ }
441
+ solidLayer.property("Position").setValue(position);
442
+ solidLayer.startTime = startTime;
443
+ if (duration > 0) { solidLayer.outPoint = startTime + duration; }
444
+ return JSON.stringify({
445
+ status: "success", message: isAdjustment ? "Adjustment layer created successfully" : "Solid layer created successfully",
446
+ layer: { name: solidLayer.name, index: solidLayer.index, type: isAdjustment ? "adjustment" : "solid", inPoint: solidLayer.inPoint, outPoint: solidLayer.outPoint, position: solidLayer.property("Position").value, isAdjustment: solidLayer.adjustmentLayer }
447
+ }, null, 2);
448
+ } catch (error) {
449
+ return JSON.stringify({ status: "error", message: error.toString() }, null, 2);
450
+ }
451
+ }
452
+
453
+ // --- setLayerProperties (modified to handle text properties) ---
454
+ function setLayerProperties(args) {
455
+ try {
456
+ var compName = args.compName || "";
457
+ var layerName = args.layerName || "";
458
+ var layerIndex = args.layerIndex;
459
+
460
+ // General Properties
461
+ var position = args.position;
462
+ var scale = args.scale;
463
+ var rotation = args.rotation;
464
+ var opacity = args.opacity;
465
+ var startTime = args.startTime;
466
+ var duration = args.duration;
467
+
468
+ // Text Specific Properties
469
+ var textContent = args.text; // New: text content
470
+ var fontFamily = args.fontFamily; // New: font family
471
+ var fontSize = args.fontSize; // New: font size
472
+ var fillColor = args.fillColor; // New: font color
473
+
474
+ // Find the composition (same logic as before)
475
+ var comp = null;
476
+ for (var i = 1; i <= app.project.numItems; i++) {
477
+ var item = app.project.item(i);
478
+ if (item instanceof CompItem && item.name === compName) { comp = item; break; }
479
+ }
480
+ if (!comp) {
481
+ if (app.project.activeItem instanceof CompItem) { comp = app.project.activeItem; }
482
+ else { throw new Error("No composition found with name '" + compName + "' and no active composition"); }
483
+ }
484
+
485
+ // Find the layer (same logic as before)
486
+ var layer = null;
487
+ if (layerIndex !== undefined && layerIndex !== null) {
488
+ if (layerIndex > 0 && layerIndex <= comp.numLayers) { layer = comp.layer(layerIndex); }
489
+ else { throw new Error("Layer index out of bounds: " + layerIndex); }
490
+ } else if (layerName) {
491
+ for (var j = 1; j <= comp.numLayers; j++) {
492
+ if (comp.layer(j).name === layerName) { layer = comp.layer(j); break; }
493
+ }
494
+ }
495
+ if (!layer) { throw new Error("Layer not found: " + (layerName || "index " + layerIndex)); }
496
+
497
+ var changedProperties = [];
498
+ var textDocumentChanged = false;
499
+ var textProp = null;
500
+ var textDocument = null;
501
+
502
+ // --- Text Property Handling ---
503
+ if (layer instanceof TextLayer && (textContent !== undefined || fontFamily !== undefined || fontSize !== undefined || fillColor !== undefined)) {
504
+ var sourceTextProp = layer.property("Source Text");
505
+ if (sourceTextProp && sourceTextProp.value) {
506
+ var currentTextDocument = sourceTextProp.value; // Get the current value
507
+ var updated = false;
508
+
509
+ if (textContent !== undefined && textContent !== null && currentTextDocument.text !== textContent) {
510
+ currentTextDocument.text = textContent;
511
+ changedProperties.push("text");
512
+ updated = true;
513
+ }
514
+ if (fontFamily !== undefined && fontFamily !== null && currentTextDocument.font !== fontFamily) {
515
+ // Add basic validation/logging for font existence if needed
516
+ // try { app.fonts.findFont(fontFamily); } catch (e) { logToPanel("Warning: Font '"+fontFamily+"' might not be installed."); }
517
+ currentTextDocument.font = fontFamily;
518
+ changedProperties.push("fontFamily");
519
+ updated = true;
520
+ }
521
+ if (fontSize !== undefined && fontSize !== null && currentTextDocument.fontSize !== fontSize) {
522
+ currentTextDocument.fontSize = fontSize;
523
+ changedProperties.push("fontSize");
524
+ updated = true;
525
+ }
526
+ // Comparing colors needs care due to potential floating point inaccuracies if set via UI
527
+ // Simple comparison for now
528
+ if (fillColor !== undefined && fillColor !== null &&
529
+ (currentTextDocument.fillColor[0] !== fillColor[0] ||
530
+ currentTextDocument.fillColor[1] !== fillColor[1] ||
531
+ currentTextDocument.fillColor[2] !== fillColor[2])) {
532
+ currentTextDocument.fillColor = fillColor;
533
+ changedProperties.push("fillColor");
534
+ updated = true;
535
+ }
536
+
537
+ // Only set the value if something actually changed
538
+ if (updated) {
539
+ try {
540
+ sourceTextProp.setValue(currentTextDocument);
541
+ logToPanel("Applied changes to Text Document for layer: " + layer.name);
542
+ } catch (e) {
543
+ logToPanel("ERROR applying Text Document changes: " + e.toString());
544
+ // Decide if we should throw or just log the error for text properties
545
+ // For now, just log, other properties might still succeed
546
+ }
547
+ }
548
+ // Store the potentially updated document for the return value
549
+ textDocument = currentTextDocument;
550
+
551
+ } else {
552
+ logToPanel("Warning: Could not access Source Text property for layer: " + layer.name);
553
+ }
554
+ }
555
+
556
+ // --- Enabled/Visible ---
557
+ var enabled = args.enabled;
558
+ if (enabled !== undefined && enabled !== null) { layer.enabled = !!enabled; changedProperties.push("enabled"); }
559
+
560
+ // --- Blend Mode ---
561
+ var blendMode = args.blendMode;
562
+ if (blendMode !== undefined && blendMode !== null) {
563
+ var modes = {
564
+ "normal": BlendingMode.NORMAL,
565
+ "add": BlendingMode.ADD,
566
+ "multiply": BlendingMode.MULTIPLY,
567
+ "screen": BlendingMode.SCREEN,
568
+ "overlay": BlendingMode.OVERLAY,
569
+ "softLight": BlendingMode.SOFT_LIGHT,
570
+ "hardLight": BlendingMode.HARD_LIGHT,
571
+ "colorDodge": BlendingMode.COLOR_DODGE,
572
+ "colorBurn": BlendingMode.COLOR_BURN,
573
+ "darken": BlendingMode.DARKEN,
574
+ "lighten": BlendingMode.LIGHTEN,
575
+ "difference": BlendingMode.DIFFERENCE,
576
+ "exclusion": BlendingMode.EXCLUSION,
577
+ "hue": BlendingMode.HUE,
578
+ "saturation": BlendingMode.SATURATION,
579
+ "color": BlendingMode.COLOR,
580
+ "luminosity": BlendingMode.LUMINOSITY
581
+ };
582
+ if (modes[blendMode] !== undefined) {
583
+ layer.blendingMode = modes[blendMode];
584
+ changedProperties.push("blendMode");
585
+ }
586
+ }
587
+
588
+ // --- Track Matte ---
589
+ var trackMatteType = args.trackMatteType;
590
+ if (trackMatteType !== undefined && trackMatteType !== null) {
591
+ // Values: "none", "alpha", "alphaInverted", "luma", "lumaInverted"
592
+ var matteTypes = {
593
+ "none": TrackMatteType.NO_TRACK_MATTE,
594
+ "alpha": TrackMatteType.ALPHA,
595
+ "alphaInverted": TrackMatteType.ALPHA_INVERTED,
596
+ "luma": TrackMatteType.LUMA,
597
+ "lumaInverted": TrackMatteType.LUMA_INVERTED
598
+ };
599
+ if (matteTypes[trackMatteType] !== undefined) {
600
+ layer.trackMatteType = matteTypes[trackMatteType];
601
+ changedProperties.push("trackMatteType");
602
+ }
603
+ }
604
+
605
+ // --- General Property Handling ---
606
+ var threeDLayer = args.threeDLayer;
607
+ if (threeDLayer !== undefined && threeDLayer !== null) { layer.threeDLayer = !!threeDLayer; changedProperties.push("threeDLayer"); }
608
+ if (position !== undefined && position !== null) {
609
+ var posProp = layer.property("Position");
610
+ if (posProp.numKeys > 0) { while (posProp.numKeys > 0) { posProp.removeKey(1); } }
611
+ posProp.setValue(position);
612
+ changedProperties.push("position");
613
+ }
614
+ if (scale !== undefined && scale !== null) { layer.property("Scale").setValue(scale); changedProperties.push("scale"); }
615
+ if (rotation !== undefined && rotation !== null) {
616
+ if (layer.threeDLayer) {
617
+ // For 3D layers, Z rotation is often what's intended by a single value
618
+ layer.property("Z Rotation").setValue(rotation);
619
+ } else {
620
+ layer.property("Rotation").setValue(rotation);
621
+ }
622
+ changedProperties.push("rotation");
623
+ }
624
+ if (opacity !== undefined && opacity !== null) { layer.property("Opacity").setValue(opacity); changedProperties.push("opacity"); }
625
+ if (startTime !== undefined && startTime !== null) { layer.startTime = startTime; changedProperties.push("startTime"); }
626
+ if (duration !== undefined && duration !== null && duration > 0) {
627
+ var actualStartTime = (startTime !== undefined && startTime !== null) ? startTime : layer.startTime;
628
+ layer.outPoint = actualStartTime + duration;
629
+ changedProperties.push("duration");
630
+ }
631
+
632
+ // Return success with updated layer details (including text if changed)
633
+ var returnLayerInfo = {
634
+ name: layer.name,
635
+ index: layer.index,
636
+ threeDLayer: layer.threeDLayer,
637
+ position: layer.property("Position").value,
638
+ scale: layer.property("Scale").value,
639
+ rotation: layer.threeDLayer ? layer.property("Z Rotation").value : layer.property("Rotation").value, // Return appropriate rotation
640
+ opacity: layer.property("Opacity").value,
641
+ inPoint: layer.inPoint,
642
+ outPoint: layer.outPoint,
643
+ changedProperties: changedProperties
644
+ };
645
+ // Add text properties to the return object if it was a text layer
646
+ if (layer instanceof TextLayer && textDocument) {
647
+ returnLayerInfo.text = textDocument.text;
648
+ returnLayerInfo.fontFamily = textDocument.font;
649
+ returnLayerInfo.fontSize = textDocument.fontSize;
650
+ returnLayerInfo.fillColor = textDocument.fillColor;
651
+ }
652
+
653
+ // *** ADDED LOGGING HERE ***
654
+ logToPanel("Final check before return:");
655
+ logToPanel(" Changed Properties: " + changedProperties.join(", "));
656
+ logToPanel(" Return Layer Info Font: " + (returnLayerInfo.fontFamily || "N/A"));
657
+ logToPanel(" TextDocument Font: " + (textDocument ? textDocument.font : "N/A"));
658
+
659
+ return JSON.stringify({
660
+ status: "success", message: "Layer properties updated successfully",
661
+ layer: returnLayerInfo
662
+ }, null, 2);
663
+ } catch (error) {
664
+ // Error handling remains similar, but add more specific checks if needed
665
+ return JSON.stringify({ status: "error", message: error.toString() }, null, 2);
666
+ }
667
+ }
668
+
669
+ // --- batchSetLayerProperties: apply properties to multiple layers in one call ---
670
+ function batchSetLayerProperties(args) {
671
+ try {
672
+ var compName = args.compName || "";
673
+ var operations = args.operations; // Array of {layerIndex, threeDLayer, position, scale, rotation, opacity, ...}
674
+
675
+ if (!operations || !operations.length) {
676
+ throw new Error("No operations provided. Pass an array of {layerIndex, ...properties}");
677
+ }
678
+
679
+ var comp = null;
680
+ for (var i = 1; i <= app.project.numItems; i++) {
681
+ var item = app.project.item(i);
682
+ if (item instanceof CompItem && item.name === compName) { comp = item; break; }
683
+ }
684
+ if (!comp) {
685
+ if (app.project.activeItem instanceof CompItem) { comp = app.project.activeItem; }
686
+ else { throw new Error("No composition found with name '" + compName + "' and no active composition"); }
687
+ }
688
+
689
+ var results = [];
690
+ for (var o = 0; o < operations.length; o++) {
691
+ var op = operations[o];
692
+ var layer = null;
693
+ if (op.layerIndex !== undefined && op.layerIndex !== null) {
694
+ if (op.layerIndex > 0 && op.layerIndex <= comp.numLayers) { layer = comp.layer(op.layerIndex); }
695
+ else { results.push({ layerIndex: op.layerIndex, status: "error", message: "Layer index out of bounds" }); continue; }
696
+ } else if (op.layerName) {
697
+ for (var j = 1; j <= comp.numLayers; j++) {
698
+ if (comp.layer(j).name === op.layerName) { layer = comp.layer(j); break; }
699
+ }
700
+ }
701
+ if (!layer) { results.push({ layerIndex: op.layerIndex, layerName: op.layerName, status: "error", message: "Layer not found" }); continue; }
702
+
703
+ var changed = [];
704
+ if (op.threeDLayer !== undefined && op.threeDLayer !== null) { layer.threeDLayer = !!op.threeDLayer; changed.push("threeDLayer"); }
705
+ if (op.position !== undefined && op.position !== null) {
706
+ var posProp = layer.property("Position");
707
+ if (posProp.numKeys > 0) {
708
+ while (posProp.numKeys > 0) { posProp.removeKey(1); }
709
+ }
710
+ posProp.setValue(op.position);
711
+ changed.push("position");
712
+ }
713
+ if (op.scale !== undefined && op.scale !== null) { layer.property("Scale").setValue(op.scale); changed.push("scale"); }
714
+ if (op.rotation !== undefined && op.rotation !== null) {
715
+ if (layer.threeDLayer) { layer.property("Z Rotation").setValue(op.rotation); }
716
+ else { layer.property("Rotation").setValue(op.rotation); }
717
+ changed.push("rotation");
718
+ }
719
+ if (op.opacity !== undefined && op.opacity !== null) { layer.property("Opacity").setValue(op.opacity); changed.push("opacity"); }
720
+ if (op.blendMode !== undefined && op.blendMode !== null) {
721
+ var bModes = {"normal":BlendingMode.NORMAL,"add":BlendingMode.ADD,"multiply":BlendingMode.MULTIPLY,"screen":BlendingMode.SCREEN,"overlay":BlendingMode.OVERLAY,"softLight":BlendingMode.SOFT_LIGHT,"hardLight":BlendingMode.HARD_LIGHT,"darken":BlendingMode.DARKEN,"lighten":BlendingMode.LIGHTEN,"difference":BlendingMode.DIFFERENCE};
722
+ if (bModes[op.blendMode] !== undefined) { layer.blendingMode = bModes[op.blendMode]; changed.push("blendMode"); }
723
+ }
724
+ if (op.startTime !== undefined && op.startTime !== null) { layer.startTime = op.startTime; changed.push("startTime"); }
725
+ if (op.outPoint !== undefined && op.outPoint !== null) { layer.outPoint = op.outPoint; changed.push("outPoint"); }
726
+
727
+ results.push({
728
+ layerIndex: layer.index,
729
+ name: layer.name,
730
+ status: "success",
731
+ threeDLayer: layer.threeDLayer,
732
+ position: layer.property("Position").value,
733
+ changedProperties: changed
734
+ });
735
+ }
736
+
737
+ return JSON.stringify({ status: "success", results: results }, null, 2);
738
+ } catch (error) {
739
+ return JSON.stringify({ status: "error", message: error.toString() }, null, 2);
740
+ }
741
+ }
742
+
743
+ /**
744
+ * Sets a keyframe for a specific property on a layer.
745
+ * Indices are 1-based for After Effects collections.
746
+ * @param {number} compIndex - The index of the composition (1-based).
747
+ * @param {number} layerIndex - The index of the layer within the composition (1-based).
748
+ * @param {string} propertyName - The name of the property (e.g., "Position", "Scale", "Rotation", "Opacity").
749
+ * @param {number} timeInSeconds - The time (in seconds) for the keyframe.
750
+ * @param {any} value - The value for the keyframe (e.g., [x, y] for Position, [w, h] for Scale, angle for Rotation, percentage for Opacity).
751
+ * @returns {string} JSON string indicating success or error.
752
+ */
753
+ function setLayerKeyframe(compIndex, layerIndex, propertyName, timeInSeconds, value) {
754
+ try {
755
+ // Use 1-based indices as per After Effects API
756
+ var comp = app.project.items[compIndex];
757
+ if (!comp || !(comp instanceof CompItem)) {
758
+ return JSON.stringify({ success: false, message: "Composition not found at index " + compIndex });
759
+ }
760
+ var layer = comp.layers[layerIndex];
761
+ if (!layer) {
762
+ return JSON.stringify({ success: false, message: "Layer not found at index " + layerIndex + " in composition '" + comp.name + "'"});
763
+ }
764
+
765
+ var transformGroup = layer.property("Transform");
766
+ if (!transformGroup) {
767
+ return JSON.stringify({ success: false, message: "Transform properties not found for layer '" + layer.name + "' (type: " + layer.matchName + ")." });
768
+ }
769
+
770
+ var property = transformGroup.property(propertyName);
771
+ if (!property) {
772
+ // Check other common property groups if not in Transform
773
+ if (layer.property("Effects") && layer.property("Effects").property(propertyName)) {
774
+ property = layer.property("Effects").property(propertyName);
775
+ } else if (layer.property("Text") && layer.property("Text").property(propertyName)) {
776
+ property = layer.property("Text").property(propertyName);
777
+ } // Add more groups if needed (e.g., Masks, Shapes)
778
+
779
+ if (!property) {
780
+ return JSON.stringify({ success: false, message: "Property '" + propertyName + "' not found on layer '" + layer.name + "'." });
781
+ }
782
+ }
783
+
784
+
785
+ // Ensure the property can be keyframed
786
+ if (!property.canVaryOverTime) {
787
+ return JSON.stringify({ success: false, message: "Property '" + propertyName + "' cannot be keyframed." });
788
+ }
789
+
790
+ // Make sure the property is enabled for keyframing
791
+ if (property.numKeys === 0 && !property.isTimeVarying) {
792
+ property.setValueAtTime(comp.time, property.value); // Set initial keyframe if none exist
793
+ }
794
+
795
+
796
+ property.setValueAtTime(timeInSeconds, value);
797
+
798
+ return JSON.stringify({ success: true, message: "Keyframe set for '" + propertyName + "' on layer '" + layer.name + "' at " + timeInSeconds + "s." });
799
+ } catch (e) {
800
+ return JSON.stringify({ success: false, message: "Error setting keyframe: " + e.toString() + " (Line: " + e.line + ")" });
801
+ }
802
+ }
803
+
804
+
805
+ /**
806
+ * Sets an expression for a specific property on a layer.
807
+ * @param {number} compIndex - The index of the composition (1-based).
808
+ * @param {number} layerIndex - The index of the layer within the composition (1-based).
809
+ * @param {string} propertyName - The name of the property (e.g., "Position", "Scale", "Rotation", "Opacity").
810
+ * @param {string} expressionString - The JavaScript expression string. Use "" to remove expression.
811
+ * @returns {string} JSON string indicating success or error.
812
+ */
813
+ function setLayerExpression(compIndex, layerIndex, propertyName, expressionString) {
814
+ try {
815
+ // Adjust indices to be 0-based for ExtendScript arrays
816
+ var comp = app.project.items[compIndex];
817
+ if (!comp || !(comp instanceof CompItem)) {
818
+ return JSON.stringify({ success: false, message: "Composition not found at index " + compIndex });
819
+ }
820
+ var layer = comp.layers[layerIndex];
821
+ if (!layer) {
822
+ return JSON.stringify({ success: false, message: "Layer not found at index " + layerIndex + " in composition '" + comp.name + "'"});
823
+ }
824
+
825
+ var transformGroup = layer.property("Transform");
826
+ if (!transformGroup) {
827
+ // Allow expressions on non-transformable layers if property exists elsewhere
828
+ // return JSON.stringify({ success: false, message: "Transform properties not found for layer '" + layer.name + "' (type: " + layer.matchName + ")." });
829
+ }
830
+
831
+ var property = transformGroup ? transformGroup.property(propertyName) : null;
832
+ if (!property) {
833
+ // Check other common property groups if not in Transform
834
+ if (layer.property("Effects") && layer.property("Effects").property(propertyName)) {
835
+ property = layer.property("Effects").property(propertyName);
836
+ } else if (layer.property("Text") && layer.property("Text").property(propertyName)) {
837
+ property = layer.property("Text").property(propertyName);
838
+ }
839
+
840
+ // Search inside individual effects for sub-properties
841
+ if (!property && layer.property("Effects")) {
842
+ var effects = layer.property("Effects");
843
+ for (var ei = 1; ei <= effects.numProperties; ei++) {
844
+ var eff = effects.property(ei);
845
+ try {
846
+ var subProp = eff.property(propertyName);
847
+ if (subProp) { property = subProp; break; }
848
+ } catch (e2) {}
849
+ }
850
+ }
851
+
852
+ if (!property) {
853
+ return JSON.stringify({ success: false, message: "Property '" + propertyName + "' not found on layer '" + layer.name + "'." });
854
+ }
855
+ }
856
+
857
+ if (!property.canSetExpression) {
858
+ return JSON.stringify({ success: false, message: "Property '" + propertyName + "' does not support expressions." });
859
+ }
860
+
861
+ property.expression = expressionString;
862
+
863
+ var action = expressionString === "" ? "removed" : "set";
864
+ return JSON.stringify({ success: true, message: "Expression " + action + " for '" + propertyName + "' on layer '" + layer.name + "'." });
865
+ } catch (e) {
866
+ return JSON.stringify({ success: false, message: "Error setting expression: " + e.toString() + " (Line: " + e.line + ")" });
867
+ }
868
+ }
869
+
870
+ // --- applyEffect (from applyEffect.jsx) ---
871
+ function applyEffect(args) {
872
+ try {
873
+ // Extract parameters
874
+ var compIndex = args.compIndex || 1; // Default to first comp
875
+ var layerIndex = args.layerIndex || 1; // Default to first layer
876
+ var effectName = args.effectName; // Name of the effect to apply
877
+ var effectMatchName = args.effectMatchName; // After Effects internal name (more reliable)
878
+ var effectCategory = args.effectCategory || ""; // Optional category for filtering
879
+ var presetPath = args.presetPath; // Optional path to an effect preset
880
+ var effectSettings = args.effectSettings || {}; // Optional effect parameters
881
+
882
+ if (!effectName && !effectMatchName && !presetPath) {
883
+ throw new Error("You must specify either effectName, effectMatchName, or presetPath");
884
+ }
885
+
886
+ // Find the composition by index
887
+ var comp = app.project.item(compIndex);
888
+ if (!comp || !(comp instanceof CompItem)) {
889
+ throw new Error("Composition not found at index " + compIndex);
890
+ }
891
+
892
+ // Find the layer by index
893
+ var layer = comp.layer(layerIndex);
894
+ if (!layer) {
895
+ throw new Error("Layer not found at index " + layerIndex + " in composition '" + comp.name + "'");
896
+ }
897
+
898
+ var effectResult;
899
+
900
+ // Apply preset if a path is provided
901
+ if (presetPath) {
902
+ var presetFile = new File(presetPath);
903
+ if (!presetFile.exists) {
904
+ throw new Error("Effect preset file not found: " + presetPath);
905
+ }
906
+
907
+ // Apply the preset to the layer
908
+ layer.applyPreset(presetFile);
909
+ effectResult = {
910
+ type: "preset",
911
+ name: presetPath.split('/').pop().split('\\').pop(),
912
+ applied: true
913
+ };
914
+ }
915
+ // Apply effect by match name (more reliable method)
916
+ else if (effectMatchName) {
917
+ var effect = layer.Effects.addProperty(effectMatchName);
918
+ effectResult = {
919
+ type: "effect",
920
+ name: effect.name,
921
+ matchName: effect.matchName,
922
+ index: effect.propertyIndex
923
+ };
924
+
925
+ // Apply settings if provided
926
+ applyEffectSettings(effect, effectSettings);
927
+ }
928
+ // Apply effect by display name
929
+ else {
930
+ // Get the effect from the Effect menu
931
+ var effect = layer.Effects.addProperty(effectName);
932
+ effectResult = {
933
+ type: "effect",
934
+ name: effect.name,
935
+ matchName: effect.matchName,
936
+ index: effect.propertyIndex
937
+ };
938
+
939
+ // Apply settings if provided
940
+ applyEffectSettings(effect, effectSettings);
941
+ }
942
+
943
+ return JSON.stringify({
944
+ status: "success",
945
+ message: "Effect applied successfully",
946
+ effect: effectResult,
947
+ layer: {
948
+ name: layer.name,
949
+ index: layerIndex
950
+ },
951
+ composition: {
952
+ name: comp.name,
953
+ index: compIndex
954
+ }
955
+ }, null, 2);
956
+ } catch (error) {
957
+ return JSON.stringify({
958
+ status: "error",
959
+ message: error.toString()
960
+ }, null, 2);
961
+ }
962
+ }
963
+
964
+ // Helper function to apply effect settings
965
+ function applyEffectSettings(effect, settings) {
966
+ // Skip if no settings are provided
967
+ if (!settings) return;
968
+ var hasKeys = false;
969
+ for (var k in settings) { if (settings.hasOwnProperty(k)) { hasKeys = true; break; } }
970
+ if (!hasKeys) return;
971
+
972
+ // Iterate through all provided settings
973
+ for (var propName in settings) {
974
+ if (settings.hasOwnProperty(propName)) {
975
+ try {
976
+ // Find the property in the effect
977
+ var property = null;
978
+
979
+ // Try direct property access first
980
+ try {
981
+ property = effect.property(propName);
982
+ } catch (e) {
983
+ // If direct access fails, search through all properties
984
+ for (var i = 1; i <= effect.numProperties; i++) {
985
+ var prop = effect.property(i);
986
+ if (prop.name === propName) {
987
+ property = prop;
988
+ break;
989
+ }
990
+ }
991
+ }
992
+
993
+ // Set the property value if found
994
+ if (property && property.setValue) {
995
+ property.setValue(settings[propName]);
996
+ }
997
+ } catch (e) {
998
+ // Log error but continue with other properties
999
+ $.writeln("Error setting effect property '" + propName + "': " + e.toString());
1000
+ }
1001
+ }
1002
+ }
1003
+ }
1004
+
1005
+ // --- applyEffectTemplate (from applyEffectTemplate.jsx) ---
1006
+ function applyEffectTemplate(args) {
1007
+ try {
1008
+ // Extract parameters
1009
+ var compIndex = args.compIndex || 1; // Default to first comp
1010
+ var layerIndex = args.layerIndex || 1; // Default to first layer
1011
+ var templateName = args.templateName; // Name of the template to apply
1012
+ var customSettings = args.customSettings || {}; // Optional customizations
1013
+
1014
+ if (!templateName) {
1015
+ throw new Error("You must specify a templateName");
1016
+ }
1017
+
1018
+ // Find the composition by index
1019
+ var comp = app.project.item(compIndex);
1020
+ if (!comp || !(comp instanceof CompItem)) {
1021
+ throw new Error("Composition not found at index " + compIndex);
1022
+ }
1023
+
1024
+ // Find the layer by index
1025
+ var layer = comp.layer(layerIndex);
1026
+ if (!layer) {
1027
+ throw new Error("Layer not found at index " + layerIndex + " in composition '" + comp.name + "'");
1028
+ }
1029
+
1030
+ // Template definitions
1031
+ var templates = {
1032
+ // Blur effects
1033
+ "gaussian-blur": {
1034
+ effectMatchName: "ADBE Gaussian Blur 2",
1035
+ settings: {
1036
+ "Blurriness": customSettings.blurriness || 20
1037
+ }
1038
+ },
1039
+ "directional-blur": {
1040
+ effectMatchName: "ADBE Directional Blur",
1041
+ settings: {
1042
+ "Direction": customSettings.direction || 0,
1043
+ "Blur Length": customSettings.length || 10
1044
+ }
1045
+ },
1046
+
1047
+ // Color correction effects
1048
+ "color-balance": {
1049
+ effectMatchName: "ADBE Color Balance (HLS)",
1050
+ settings: {
1051
+ "Hue": customSettings.hue || 0,
1052
+ "Lightness": customSettings.lightness || 0,
1053
+ "Saturation": customSettings.saturation || 0
1054
+ }
1055
+ },
1056
+ "brightness-contrast": {
1057
+ effectMatchName: "ADBE Brightness & Contrast 2",
1058
+ settings: {
1059
+ "Brightness": customSettings.brightness || 0,
1060
+ "Contrast": customSettings.contrast || 0,
1061
+ "Use Legacy": false
1062
+ }
1063
+ },
1064
+ "curves": {
1065
+ effectMatchName: "ADBE CurvesCustom",
1066
+ // Curves are complex and would need special handling
1067
+ },
1068
+
1069
+ // Stylistic effects
1070
+ "glow": {
1071
+ effectMatchName: "ADBE Glow",
1072
+ settings: {
1073
+ "Glow Threshold": customSettings.threshold || 50,
1074
+ "Glow Radius": customSettings.radius || 15,
1075
+ "Glow Intensity": customSettings.intensity || 1
1076
+ }
1077
+ },
1078
+ "drop-shadow": {
1079
+ effectMatchName: "ADBE Drop Shadow",
1080
+ settings: {
1081
+ "Shadow Color": customSettings.color || [0, 0, 0, 1],
1082
+ "Opacity": customSettings.opacity || 50,
1083
+ "Direction": customSettings.direction || 135,
1084
+ "Distance": customSettings.distance || 10,
1085
+ "Softness": customSettings.softness || 10
1086
+ }
1087
+ },
1088
+
1089
+ // Common effect chains
1090
+ "cinematic-look": {
1091
+ effects: [
1092
+ {
1093
+ effectMatchName: "ADBE CurvesCustom",
1094
+ settings: {}
1095
+ },
1096
+ {
1097
+ effectMatchName: "ADBE Vibrance",
1098
+ settings: {
1099
+ "Vibrance": 15,
1100
+ "Saturation": -5
1101
+ }
1102
+ }
1103
+ ]
1104
+ },
1105
+ "text-pop": {
1106
+ effects: [
1107
+ {
1108
+ effectMatchName: "ADBE Drop Shadow",
1109
+ settings: {
1110
+ "Shadow Color": [0, 0, 0, 1],
1111
+ "Opacity": 75,
1112
+ "Distance": 5,
1113
+ "Softness": 10
1114
+ }
1115
+ },
1116
+ {
1117
+ effectMatchName: "ADBE Glow",
1118
+ settings: {
1119
+ "Glow Threshold": 50,
1120
+ "Glow Radius": 10,
1121
+ "Glow Intensity": 1.5
1122
+ }
1123
+ }
1124
+ ]
1125
+ }
1126
+ };
1127
+
1128
+ // Check if the requested template exists
1129
+ var template = templates[templateName];
1130
+ if (!template) {
1131
+ var availableTemplates = Object.keys(templates).join(", ");
1132
+ throw new Error("Template '" + templateName + "' not found. Available templates: " + availableTemplates);
1133
+ }
1134
+
1135
+ var appliedEffects = [];
1136
+
1137
+ // Apply single effect or multiple effects based on template structure
1138
+ if (template.effectMatchName) {
1139
+ // Single effect template
1140
+ var effect = layer.Effects.addProperty(template.effectMatchName);
1141
+
1142
+ // Apply settings
1143
+ for (var propName in template.settings) {
1144
+ try {
1145
+ var property = effect.property(propName);
1146
+ if (property) {
1147
+ property.setValue(template.settings[propName]);
1148
+ }
1149
+ } catch (e) {
1150
+ $.writeln("Warning: Could not set " + propName + " on effect " + effect.name + ": " + e);
1151
+ }
1152
+ }
1153
+
1154
+ appliedEffects.push({
1155
+ name: effect.name,
1156
+ matchName: effect.matchName
1157
+ });
1158
+ } else if (template.effects) {
1159
+ // Multiple effects template
1160
+ for (var i = 0; i < template.effects.length; i++) {
1161
+ var effectData = template.effects[i];
1162
+ var effect = layer.Effects.addProperty(effectData.effectMatchName);
1163
+
1164
+ // Apply settings
1165
+ for (var propName in effectData.settings) {
1166
+ try {
1167
+ var property = effect.property(propName);
1168
+ if (property) {
1169
+ property.setValue(effectData.settings[propName]);
1170
+ }
1171
+ } catch (e) {
1172
+ $.writeln("Warning: Could not set " + propName + " on effect " + effect.name + ": " + e);
1173
+ }
1174
+ }
1175
+
1176
+ appliedEffects.push({
1177
+ name: effect.name,
1178
+ matchName: effect.matchName
1179
+ });
1180
+ }
1181
+ }
1182
+
1183
+ return JSON.stringify({
1184
+ status: "success",
1185
+ message: "Effect template '" + templateName + "' applied successfully",
1186
+ appliedEffects: appliedEffects,
1187
+ layer: {
1188
+ name: layer.name,
1189
+ index: layerIndex
1190
+ },
1191
+ composition: {
1192
+ name: comp.name,
1193
+ index: compIndex
1194
+ }
1195
+ }, null, 2);
1196
+ } catch (error) {
1197
+ return JSON.stringify({
1198
+ status: "error",
1199
+ message: error.toString()
1200
+ }, null, 2);
1201
+ }
1202
+ }
1203
+
1204
+ // --- End of Function Definitions ---
1205
+
1206
+ // --- Bridge test function to verify communication and effects application ---
1207
+ function bridgeTestEffects(args) {
1208
+ try {
1209
+ var compIndex = (args && args.compIndex) ? args.compIndex : 1;
1210
+ var layerIndex = (args && args.layerIndex) ? args.layerIndex : 1;
1211
+
1212
+ // Apply a light Gaussian Blur
1213
+ var blurRes = JSON.parse(applyEffect({
1214
+ compIndex: compIndex,
1215
+ layerIndex: layerIndex,
1216
+ effectMatchName: "ADBE Gaussian Blur 2",
1217
+ effectSettings: { "Blurriness": 5 }
1218
+ }));
1219
+
1220
+ // Apply a simple drop shadow via template
1221
+ var shadowRes = JSON.parse(applyEffectTemplate({
1222
+ compIndex: compIndex,
1223
+ layerIndex: layerIndex,
1224
+ templateName: "drop-shadow"
1225
+ }));
1226
+
1227
+ return JSON.stringify({
1228
+ status: "success",
1229
+ message: "Bridge test effects applied.",
1230
+ results: [blurRes, shadowRes]
1231
+ }, null, 2);
1232
+ } catch (e) {
1233
+ return JSON.stringify({ status: "error", message: e.toString() }, null, 2);
1234
+ }
1235
+ }
1236
+
1237
+ // JSON polyfill for ExtendScript (when JSON is undefined)
1238
+ if (typeof JSON === "undefined") {
1239
+ JSON = {};
1240
+ }
1241
+ if (typeof JSON.parse !== "function") {
1242
+ JSON.parse = function (text) {
1243
+ // Safe-ish fallback for trusted input (our own command file)
1244
+ return eval("(" + text + ")");
1245
+ };
1246
+ }
1247
+ if (typeof JSON.stringify !== "function") {
1248
+ (function () {
1249
+ function esc(str) {
1250
+ return (str + "")
1251
+ .replace(/\\/g, "\\\\")
1252
+ .replace(/"/g, '\\"')
1253
+ .replace(/\n/g, "\\n")
1254
+ .replace(/\r/g, "\\r")
1255
+ .replace(/\t/g, "\\t");
1256
+ }
1257
+ function toJSON(val) {
1258
+ if (val === null) return "null";
1259
+ var t = typeof val;
1260
+ if (t === "number" || t === "boolean") return String(val);
1261
+ if (t === "string") return '"' + esc(val) + '"';
1262
+ if (val instanceof Array) {
1263
+ var a = [];
1264
+ for (var i = 0; i < val.length; i++) a.push(toJSON(val[i]));
1265
+ return "[" + a.join(",") + "]";
1266
+ }
1267
+ if (t === "object") {
1268
+ var props = [];
1269
+ for (var k in val) {
1270
+ if (val.hasOwnProperty(k) && typeof val[k] !== "function" && typeof val[k] !== "undefined") {
1271
+ props.push('"' + esc(k) + '":' + toJSON(val[k]));
1272
+ }
1273
+ }
1274
+ return "{" + props.join(",") + "}";
1275
+ }
1276
+ return "null";
1277
+ }
1278
+ JSON.stringify = function (value, _replacer, _space) {
1279
+ return toJSON(value);
1280
+ };
1281
+ })();
1282
+ }
1283
+
1284
+ // Detect AE version (AE 2025 = version 25.x, AE 2026 = version 26.x)
1285
+ var aeVersion = parseFloat(app.version);
1286
+ var isAE2025OrLater = aeVersion >= 25.0;
1287
+
1288
+ // Always create a floating palette window for AE 2025+
1289
+ var panel = new Window("palette", "MCP Bridge Auto", undefined);
1290
+ panel.orientation = "column";
1291
+ panel.alignChildren = ["fill", "top"];
1292
+ panel.spacing = 10;
1293
+ panel.margins = 16;
1294
+
1295
+ // Status display
1296
+ var statusText = panel.add("statictext", undefined, "Waiting for commands...");
1297
+ statusText.alignment = ["fill", "top"];
1298
+
1299
+ // Add log area
1300
+ var logPanel = panel.add("panel", undefined, "Command Log");
1301
+ logPanel.orientation = "column";
1302
+ logPanel.alignChildren = ["fill", "fill"];
1303
+ var logText = logPanel.add("edittext", undefined, "", {multiline: true, readonly: true});
1304
+ logText.preferredSize.height = 200;
1305
+
1306
+ // AE 2025 warning
1307
+ if (isAE2025OrLater) {
1308
+ var warning = panel.add("statictext", undefined, "AE 2025+: Dockable panels are not supported. Floating window only.");
1309
+ warning.graphics.foregroundColor = warning.graphics.newPen(warning.graphics.PenType.SOLID_COLOR, [1,0.3,0,1], 1);
1310
+ }
1311
+
1312
+ // Auto-run checkbox
1313
+ var autoRunCheckbox = panel.add("checkbox", undefined, "Auto-run commands");
1314
+ autoRunCheckbox.value = true;
1315
+
1316
+ // Check interval (ms)
1317
+ var checkInterval = 2000;
1318
+ var isChecking = false;
1319
+
1320
+ // Command file path - use Documents folder for reliable access
1321
+ function getCommandFilePath() {
1322
+ var userFolder = Folder.myDocuments;
1323
+ var bridgeFolder = new Folder(userFolder.fsName + "/ae-mcp-bridge");
1324
+ if (!bridgeFolder.exists) {
1325
+ bridgeFolder.create();
1326
+ }
1327
+ return bridgeFolder.fsName + "/ae_command.json";
1328
+ }
1329
+
1330
+ // Result file path - use Documents folder for reliable access
1331
+ function getResultFilePath() {
1332
+ var userFolder = Folder.myDocuments;
1333
+ var bridgeFolder = new Folder(userFolder.fsName + "/ae-mcp-bridge");
1334
+ if (!bridgeFolder.exists) {
1335
+ bridgeFolder.create();
1336
+ }
1337
+ return bridgeFolder.fsName + "/ae_mcp_result.json";
1338
+ }
1339
+
1340
+ // --- setCompositionProperties: set duration, frameRate, etc. on active or named comp ---
1341
+ function setCompositionProperties(args) {
1342
+ try {
1343
+ var compName = args.compName || "";
1344
+ var comp = null;
1345
+ for (var i = 1; i <= app.project.numItems; i++) {
1346
+ var item = app.project.item(i);
1347
+ if (item instanceof CompItem && item.name === compName) { comp = item; break; }
1348
+ }
1349
+ if (!comp) {
1350
+ if (app.project.activeItem instanceof CompItem) { comp = app.project.activeItem; }
1351
+ else { throw new Error("No composition found with name '" + compName + "' and no active composition"); }
1352
+ }
1353
+ var changed = [];
1354
+ if (args.duration !== undefined && args.duration !== null) { comp.duration = args.duration; changed.push("duration"); }
1355
+ if (args.frameRate !== undefined && args.frameRate !== null) { comp.frameRate = args.frameRate; changed.push("frameRate"); }
1356
+ if (args.width !== undefined && args.width !== null && args.height !== undefined && args.height !== null) {
1357
+ comp.width = args.width; comp.height = args.height; changed.push("dimensions");
1358
+ }
1359
+ return JSON.stringify({
1360
+ status: "success",
1361
+ composition: { name: comp.name, duration: comp.duration, frameRate: comp.frameRate, width: comp.width, height: comp.height },
1362
+ changedProperties: changed
1363
+ }, null, 2);
1364
+ } catch (error) {
1365
+ return JSON.stringify({ status: "error", message: error.toString() }, null, 2);
1366
+ }
1367
+ }
1368
+
1369
+ // Functions for each script type
1370
+ function getProjectInfo() {
1371
+ var project = app.project;
1372
+ var result = {
1373
+ projectName: project.file ? project.file.name : "Untitled Project",
1374
+ path: project.file ? project.file.fsName : "",
1375
+ numItems: project.numItems,
1376
+ bitsPerChannel: project.bitsPerChannel,
1377
+ timeMode: project.timeDisplayType === TimeDisplayType.FRAMES ? "Frames" : "Timecode",
1378
+ items: []
1379
+ };
1380
+
1381
+ // Count item types
1382
+ var countByType = {
1383
+ compositions: 0,
1384
+ footage: 0,
1385
+ folders: 0,
1386
+ solids: 0
1387
+ };
1388
+
1389
+ // Get item information (limited for performance)
1390
+ for (var i = 1; i <= Math.min(project.numItems, 50); i++) {
1391
+ var item = project.item(i);
1392
+ var itemType = "";
1393
+
1394
+ if (item instanceof CompItem) {
1395
+ itemType = "Composition";
1396
+ countByType.compositions++;
1397
+ } else if (item instanceof FolderItem) {
1398
+ itemType = "Folder";
1399
+ countByType.folders++;
1400
+ } else if (item instanceof FootageItem) {
1401
+ if (item.mainSource instanceof SolidSource) {
1402
+ itemType = "Solid";
1403
+ countByType.solids++;
1404
+ } else {
1405
+ itemType = "Footage";
1406
+ countByType.footage++;
1407
+ }
1408
+ }
1409
+
1410
+ result.items.push({
1411
+ id: item.id,
1412
+ name: item.name,
1413
+ type: itemType
1414
+ });
1415
+ }
1416
+
1417
+ result.itemCounts = countByType;
1418
+
1419
+ // Include active composition metadata if available
1420
+ if (app.project.activeItem instanceof CompItem) {
1421
+ var ac = app.project.activeItem;
1422
+ result.activeComp = {
1423
+ id: ac.id,
1424
+ name: ac.name,
1425
+ width: ac.width,
1426
+ height: ac.height,
1427
+ duration: ac.duration,
1428
+ frameRate: ac.frameRate,
1429
+ numLayers: ac.numLayers
1430
+ };
1431
+ }
1432
+
1433
+ return JSON.stringify(result, null, 2);
1434
+ }
1435
+
1436
+ function listCompositions() {
1437
+ var project = app.project;
1438
+ var result = {
1439
+ compositions: []
1440
+ };
1441
+
1442
+ // Loop through items in the project
1443
+ for (var i = 1; i <= project.numItems; i++) {
1444
+ var item = project.item(i);
1445
+
1446
+ // Check if the item is a composition
1447
+ if (item instanceof CompItem) {
1448
+ result.compositions.push({
1449
+ id: item.id,
1450
+ name: item.name,
1451
+ duration: item.duration,
1452
+ frameRate: item.frameRate,
1453
+ width: item.width,
1454
+ height: item.height,
1455
+ numLayers: item.numLayers
1456
+ });
1457
+ }
1458
+ }
1459
+
1460
+ return JSON.stringify(result, null, 2);
1461
+ }
1462
+
1463
+ function getLayerInfo() {
1464
+ var project = app.project;
1465
+ var result = {
1466
+ layers: []
1467
+ };
1468
+
1469
+ // Get the active composition
1470
+ var activeComp = null;
1471
+ if (app.project.activeItem instanceof CompItem) {
1472
+ activeComp = app.project.activeItem;
1473
+ } else {
1474
+ return JSON.stringify({ error: "No active composition" }, null, 2);
1475
+ }
1476
+
1477
+ // Loop through layers in the active composition
1478
+ for (var i = 1; i <= activeComp.numLayers; i++) {
1479
+ var layer = activeComp.layer(i);
1480
+ var layerInfo = {
1481
+ index: layer.index,
1482
+ name: layer.name,
1483
+ enabled: layer.enabled,
1484
+ locked: layer.locked,
1485
+ threeDLayer: layer.threeDLayer,
1486
+ position: layer.property("Position").value,
1487
+ inPoint: layer.inPoint,
1488
+ outPoint: layer.outPoint
1489
+ };
1490
+
1491
+ result.layers.push(layerInfo);
1492
+ }
1493
+
1494
+ return JSON.stringify(result, null, 2);
1495
+ }
1496
+
1497
+ // Execute command
1498
+ function executeCommand(command, args) {
1499
+ var result = "";
1500
+
1501
+ logToPanel("Executing command: " + command);
1502
+ statusText.text = "Running: " + command;
1503
+ panel.update();
1504
+
1505
+ try {
1506
+ logToPanel("Attempting to execute: " + command); // Log before switch
1507
+ // Use a switch statement for clarity
1508
+ switch (command) {
1509
+ case "getProjectInfo":
1510
+ result = getProjectInfo();
1511
+ break;
1512
+ case "listCompositions":
1513
+ result = listCompositions();
1514
+ break;
1515
+ case "getLayerInfo":
1516
+ result = getLayerInfo();
1517
+ break;
1518
+ case "createComposition":
1519
+ logToPanel("Calling createComposition function...");
1520
+ result = createComposition(args);
1521
+ logToPanel("Returned from createComposition.");
1522
+ break;
1523
+ case "createTextLayer":
1524
+ logToPanel("Calling createTextLayer function...");
1525
+ result = createTextLayer(args);
1526
+ logToPanel("Returned from createTextLayer.");
1527
+ break;
1528
+ case "createShapeLayer":
1529
+ logToPanel("Calling createShapeLayer function...");
1530
+ result = createShapeLayer(args);
1531
+ logToPanel("Returned from createShapeLayer. Result type: " + typeof result);
1532
+ break;
1533
+ case "createSolidLayer":
1534
+ logToPanel("Calling createSolidLayer function...");
1535
+ result = createSolidLayer(args);
1536
+ logToPanel("Returned from createSolidLayer.");
1537
+ break;
1538
+ case "setLayerProperties":
1539
+ logToPanel("Calling setLayerProperties function...");
1540
+ result = setLayerProperties(args);
1541
+ logToPanel("Returned from setLayerProperties.");
1542
+ break;
1543
+ case "setLayerKeyframe":
1544
+ logToPanel("Calling setLayerKeyframe function...");
1545
+ result = setLayerKeyframe(args.compIndex, args.layerIndex, args.propertyName, args.timeInSeconds, args.value);
1546
+ logToPanel("Returned from setLayerKeyframe.");
1547
+ break;
1548
+ case "setLayerExpression":
1549
+ logToPanel("Calling setLayerExpression function...");
1550
+ result = setLayerExpression(args.compIndex, args.layerIndex, args.propertyName, args.expressionString);
1551
+ logToPanel("Returned from setLayerExpression.");
1552
+ break;
1553
+ case "applyEffect":
1554
+ logToPanel("Calling applyEffect function...");
1555
+ result = applyEffect(args);
1556
+ logToPanel("Returned from applyEffect.");
1557
+ break;
1558
+ case "applyEffectTemplate":
1559
+ logToPanel("Calling applyEffectTemplate function...");
1560
+ result = applyEffectTemplate(args);
1561
+ logToPanel("Returned from applyEffectTemplate.");
1562
+ break;
1563
+ case "bridgeTestEffects":
1564
+ logToPanel("Calling bridgeTestEffects function...");
1565
+ result = bridgeTestEffects(args);
1566
+ logToPanel("Returned from bridgeTestEffects.");
1567
+ break;
1568
+ case "createCamera":
1569
+ logToPanel("Calling createCamera function...");
1570
+ result = createCamera(args);
1571
+ logToPanel("Returned from createCamera.");
1572
+ break;
1573
+ case "batchSetLayerProperties":
1574
+ logToPanel("Calling batchSetLayerProperties function...");
1575
+ result = batchSetLayerProperties(args);
1576
+ logToPanel("Returned from batchSetLayerProperties.");
1577
+ break;
1578
+ case "setCompositionProperties":
1579
+ logToPanel("Calling setCompositionProperties function...");
1580
+ result = setCompositionProperties(args);
1581
+ logToPanel("Returned from setCompositionProperties.");
1582
+ break;
1583
+ case "duplicateLayer":
1584
+ logToPanel("Calling duplicateLayer function...");
1585
+ result = duplicateLayer(args);
1586
+ logToPanel("Returned from duplicateLayer.");
1587
+ break;
1588
+ case "deleteLayer":
1589
+ logToPanel("Calling deleteLayer function...");
1590
+ result = deleteLayer(args);
1591
+ logToPanel("Returned from deleteLayer.");
1592
+ break;
1593
+ case "setLayerMask":
1594
+ logToPanel("Calling setLayerMask function...");
1595
+ result = setLayerMask(args);
1596
+ logToPanel("Returned from setLayerMask.");
1597
+ break;
1598
+ default:
1599
+ result = JSON.stringify({ error: "Unknown command: " + command });
1600
+ }
1601
+ logToPanel("Execution finished for: " + command); // Log after switch
1602
+
1603
+ // Save the result (ensure result is always a string)
1604
+ logToPanel("Preparing to write result file...");
1605
+ var resultString = (typeof result === 'string') ? result : JSON.stringify(result);
1606
+
1607
+ // Try to parse the result as JSON to add a timestamp
1608
+ try {
1609
+ var resultObj = JSON.parse(resultString);
1610
+ // Add a timestamp to help identify if we're getting fresh results
1611
+ resultObj._responseTimestamp = new Date().toISOString();
1612
+ resultObj._commandExecuted = command;
1613
+ resultString = JSON.stringify(resultObj, null, 2);
1614
+ logToPanel("Added timestamp to result JSON for tracking freshness.");
1615
+ } catch (parseError) {
1616
+ // If it's not valid JSON, append the timestamp as a comment
1617
+ logToPanel("Could not parse result as JSON to add timestamp: " + parseError.toString());
1618
+ // We'll still continue with the original string
1619
+ }
1620
+
1621
+ var resultFile = new File(getResultFilePath());
1622
+ resultFile.encoding = "UTF-8"; // Ensure UTF-8 encoding
1623
+ logToPanel("Opening result file for writing...");
1624
+ var opened = resultFile.open("w");
1625
+ if (!opened) {
1626
+ logToPanel("ERROR: Failed to open result file for writing: " + resultFile.fsName);
1627
+ throw new Error("Failed to open result file for writing.");
1628
+ }
1629
+ logToPanel("Writing to result file...");
1630
+ var written = resultFile.write(resultString);
1631
+ if (!written) {
1632
+ logToPanel("ERROR: Failed to write to result file (write returned false): " + resultFile.fsName);
1633
+ // Still try to close, but log the error
1634
+ }
1635
+ logToPanel("Closing result file...");
1636
+ var closed = resultFile.close();
1637
+ if (!closed) {
1638
+ logToPanel("ERROR: Failed to close result file: " + resultFile.fsName);
1639
+ // Continue, but log the error
1640
+ }
1641
+ logToPanel("Result file write process complete.");
1642
+
1643
+ logToPanel("Command completed successfully: " + command); // Changed log message
1644
+ statusText.text = "Command completed: " + command;
1645
+
1646
+ // Update command file status
1647
+ logToPanel("Updating command status to completed...");
1648
+ updateCommandStatus("completed");
1649
+ logToPanel("Command status updated.");
1650
+
1651
+ } catch (error) {
1652
+ var errorMsg = "ERROR in executeCommand for '" + command + "': " + error.toString() + (error.line ? " (line: " + error.line + ")" : "");
1653
+ logToPanel(errorMsg); // Log detailed error
1654
+ statusText.text = "Error: " + error.toString();
1655
+
1656
+ // Write detailed error to result file
1657
+ try {
1658
+ logToPanel("Attempting to write ERROR to result file...");
1659
+ var errorResult = JSON.stringify({
1660
+ status: "error",
1661
+ command: command,
1662
+ message: error.toString(),
1663
+ line: error.line,
1664
+ fileName: error.fileName
1665
+ });
1666
+ var errorFile = new File(getResultFilePath());
1667
+ errorFile.encoding = "UTF-8";
1668
+ if (errorFile.open("w")) {
1669
+ errorFile.write(errorResult);
1670
+ errorFile.close();
1671
+ logToPanel("Successfully wrote ERROR to result file.");
1672
+ } else {
1673
+ logToPanel("CRITICAL ERROR: Failed to open result file to write error!");
1674
+ }
1675
+ } catch (writeError) {
1676
+ logToPanel("CRITICAL ERROR: Failed to write error to result file: " + writeError.toString());
1677
+ }
1678
+
1679
+ // Update command file status even after error
1680
+ logToPanel("Updating command status to error...");
1681
+ updateCommandStatus("error");
1682
+ logToPanel("Command status updated to error.");
1683
+ }
1684
+ }
1685
+
1686
+ // Update command file status
1687
+ function updateCommandStatus(status) {
1688
+ try {
1689
+ var commandFile = new File(getCommandFilePath());
1690
+ if (commandFile.exists) {
1691
+ commandFile.open("r");
1692
+ var content = commandFile.read();
1693
+ commandFile.close();
1694
+
1695
+ if (content) {
1696
+ var commandData = JSON.parse(content);
1697
+ commandData.status = status;
1698
+
1699
+ commandFile.open("w");
1700
+ commandFile.write(JSON.stringify(commandData, null, 2));
1701
+ commandFile.close();
1702
+ }
1703
+ }
1704
+ } catch (e) {
1705
+ logToPanel("Error updating command status: " + e.toString());
1706
+ }
1707
+ }
1708
+
1709
+ // Log message to panel
1710
+ function logToPanel(message) {
1711
+ var timestamp = new Date().toLocaleTimeString();
1712
+ logText.text = timestamp + ": " + message + "\n" + logText.text;
1713
+ }
1714
+
1715
+ // Check for new commands
1716
+ function checkForCommands() {
1717
+ if (!autoRunCheckbox.value || isChecking) return;
1718
+
1719
+ isChecking = true;
1720
+
1721
+ try {
1722
+ var commandFile = new File(getCommandFilePath());
1723
+ if (commandFile.exists) {
1724
+ commandFile.open("r");
1725
+ var content = commandFile.read();
1726
+ commandFile.close();
1727
+
1728
+ if (content) {
1729
+ var commandData = (typeof JSON !== "undefined" && JSON.parse)
1730
+ ? JSON.parse(content)
1731
+ : eval("(" + content + ")");
1732
+
1733
+ // Only execute pending commands
1734
+ if (commandData.status === "pending") {
1735
+ // Update status to running
1736
+ updateCommandStatus("running");
1737
+
1738
+ // Execute the command
1739
+ executeCommand(commandData.command, commandData.args || {});
1740
+ }
1741
+ }
1742
+ }
1743
+ } catch (e) {
1744
+ logToPanel("Error checking for commands: " + e.toString());
1745
+ }
1746
+
1747
+ isChecking = false;
1748
+ }
1749
+
1750
+ // Set up timer to check for commands
1751
+ function startCommandChecker() {
1752
+ app.scheduleTask("checkForCommands()", checkInterval, true);
1753
+ }
1754
+
1755
+ // Add manual check button
1756
+ var checkButton = panel.add("button", undefined, "Check for Commands Now");
1757
+ checkButton.onClick = function() {
1758
+ logToPanel("Manually checking for commands");
1759
+ checkForCommands();
1760
+ };
1761
+
1762
+ // Log startup
1763
+ logToPanel("MCP Bridge Auto started");
1764
+ logToPanel("Command file: " + getCommandFilePath());
1765
+ statusText.text = "Ready - Auto-run is " + (autoRunCheckbox.value ? "ON" : "OFF");
1766
+
1767
+ // Start the command checker
1768
+ startCommandChecker();
1769
+
1770
+ // Show the panel
1771
+ panel.center();
1772
+ panel.show();
1773
+