premiere-cli 0.2.0__py3-none-any.whl

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 (247) hide show
  1. premiere_cli/__init__.py +11 -0
  2. premiere_cli/cli.py +3664 -0
  3. premiere_cli/log.py +66 -0
  4. premiere_cli/panel/.debug +8 -0
  5. premiere_cli/panel/CSXS/manifest.xml +52 -0
  6. premiere_cli/panel/css/style.css +53 -0
  7. premiere_cli/panel/host/commands/add-adjustment-layer.jsx +190 -0
  8. premiere_cli/panel/host/commands/add-audio-keyframes.jsx +176 -0
  9. premiere_cli/panel/host/commands/add-custom-metadata-field.jsx +63 -0
  10. premiere_cli/panel/host/commands/add-keyframe.jsx +181 -0
  11. premiere_cli/panel/host/commands/add-marker-to-project-item.jsx +194 -0
  12. premiere_cli/panel/host/commands/add-marker.jsx +129 -0
  13. premiere_cli/panel/host/commands/add-text-overlay.jsx +101 -0
  14. premiere_cli/panel/host/commands/add-to-render-queue.jsx +120 -0
  15. premiere_cli/panel/host/commands/add-to-timeline.jsx +300 -0
  16. premiere_cli/panel/host/commands/add-track.jsx +188 -0
  17. premiere_cli/panel/host/commands/add-transition.jsx +134 -0
  18. premiere_cli/panel/host/commands/adjust-audio-levels.jsx +132 -0
  19. premiere_cli/panel/host/commands/apply-audio-effect.jsx +118 -0
  20. premiere_cli/panel/host/commands/apply-effect.jsx +122 -0
  21. premiere_cli/panel/host/commands/apply-lut.jsx +107 -0
  22. premiere_cli/panel/host/commands/attach-custom-property.jsx +66 -0
  23. premiere_cli/panel/host/commands/auto-reframe-sequence.jsx +124 -0
  24. premiere_cli/panel/host/commands/batch-add-transitions.jsx +158 -0
  25. premiere_cli/panel/host/commands/batch-apply-effect.jsx +182 -0
  26. premiere_cli/panel/host/commands/batch-rename-clips.jsx +170 -0
  27. premiere_cli/panel/host/commands/batch-set-clips-enabled.jsx +145 -0
  28. premiere_cli/panel/host/commands/clear-item-in-out.jsx +117 -0
  29. premiere_cli/panel/host/commands/clear-sequence-in-out.jsx +100 -0
  30. premiere_cli/panel/host/commands/close-all-source-clips.jsx +32 -0
  31. premiere_cli/panel/host/commands/close-sequence.jsx +80 -0
  32. premiere_cli/panel/host/commands/close-source-monitor.jsx +32 -0
  33. premiere_cli/panel/host/commands/color-correct.jsx +164 -0
  34. premiere_cli/panel/host/commands/copy-effect-values.jsx +183 -0
  35. premiere_cli/panel/host/commands/copy-effects-between-clips.jsx +234 -0
  36. premiere_cli/panel/host/commands/create-bars-and-tone.jsx +152 -0
  37. premiere_cli/panel/host/commands/create-bin.jsx +76 -0
  38. premiere_cli/panel/host/commands/create-caption-track.jsx +159 -0
  39. premiere_cli/panel/host/commands/create-sequence-from-clips.jsx +170 -0
  40. premiere_cli/panel/host/commands/create-sequence-from-preset.jsx +90 -0
  41. premiere_cli/panel/host/commands/create-sequence.jsx +166 -0
  42. premiere_cli/panel/host/commands/create-smart-bin.jsx +80 -0
  43. premiere_cli/panel/host/commands/create-subclip.jsx +248 -0
  44. premiere_cli/panel/host/commands/create-subsequence.jsx +136 -0
  45. premiere_cli/panel/host/commands/debug-qe-inspect.jsx +248 -0
  46. premiere_cli/panel/host/commands/debug-qe-try-mutate.jsx +305 -0
  47. premiere_cli/panel/host/commands/delete-marker.jsx +178 -0
  48. premiere_cli/panel/host/commands/deselect-all-clips.jsx +98 -0
  49. premiere_cli/panel/host/commands/detach-proxy.jsx +116 -0
  50. premiere_cli/panel/host/commands/duplicate-clip.jsx +140 -0
  51. premiere_cli/panel/host/commands/duplicate-sequence.jsx +150 -0
  52. premiere_cli/panel/host/commands/encode-file.jsx +86 -0
  53. premiere_cli/panel/host/commands/encode-project-item.jsx +145 -0
  54. premiere_cli/panel/host/commands/export-aaf.jsx +77 -0
  55. premiere_cli/panel/host/commands/export-fcp-xml.jsx +65 -0
  56. premiere_cli/panel/host/commands/export-frame.jsx +381 -0
  57. premiere_cli/panel/host/commands/export-omf.jsx +95 -0
  58. premiere_cli/panel/host/commands/export-sequence-as-project.jsx +65 -0
  59. premiere_cli/panel/host/commands/export-sequence.jsx +100 -0
  60. premiere_cli/panel/host/commands/extract-audio-track.jsx +181 -0
  61. premiere_cli/panel/host/commands/extract-selection.jsx +113 -0
  62. premiere_cli/panel/host/commands/find-items-by-media-path.jsx +112 -0
  63. premiere_cli/panel/host/commands/freeze-frame.jsx +215 -0
  64. premiere_cli/panel/host/commands/get-active-sequence.jsx +103 -0
  65. premiere_cli/panel/host/commands/get-all-project-paths.jsx +72 -0
  66. premiere_cli/panel/host/commands/get-bin-contents.jsx +176 -0
  67. premiere_cli/panel/host/commands/get-clip-adjustment-layer.jsx +83 -0
  68. premiere_cli/panel/host/commands/get-clip-at-playhead.jsx +99 -0
  69. premiere_cli/panel/host/commands/get-clip-at-position.jsx +120 -0
  70. premiere_cli/panel/host/commands/get-clip-links.jsx +135 -0
  71. premiere_cli/panel/host/commands/get-clip-markers.jsx +144 -0
  72. premiere_cli/panel/host/commands/get-clip-speed.jsx +69 -0
  73. premiere_cli/panel/host/commands/get-color-label.jsx +89 -0
  74. premiere_cli/panel/host/commands/get-color-space.jsx +99 -0
  75. premiere_cli/panel/host/commands/get-duplicate-media.jsx +78 -0
  76. premiere_cli/panel/host/commands/get-effect-properties.jsx +129 -0
  77. premiere_cli/panel/host/commands/get-encoder-presets.jsx +131 -0
  78. premiere_cli/panel/host/commands/get-export-file-extension.jsx +56 -0
  79. premiere_cli/panel/host/commands/get-footage-interpretation.jsx +113 -0
  80. premiere_cli/panel/host/commands/get-full-clip-info.jsx +162 -0
  81. premiere_cli/panel/host/commands/get-full-project-overview.jsx +193 -0
  82. premiere_cli/panel/host/commands/get-full-sequence-info.jsx +184 -0
  83. premiere_cli/panel/host/commands/get-graphics-white-luminance.jsx +31 -0
  84. premiere_cli/panel/host/commands/get-insertion-bin.jsx +35 -0
  85. premiere_cli/panel/host/commands/get-item-metadata.jsx +94 -0
  86. premiere_cli/panel/host/commands/get-keyframes.jsx +158 -0
  87. premiere_cli/panel/host/commands/get-mogrt-component.jsx +111 -0
  88. premiere_cli/panel/host/commands/get-next-edit-point.jsx +131 -0
  89. premiere_cli/panel/host/commands/get-offline-media.jsx +67 -0
  90. premiere_cli/panel/host/commands/get-open-projects.jsx +50 -0
  91. premiere_cli/panel/host/commands/get-premiere-state.jsx +164 -0
  92. premiere_cli/panel/host/commands/get-project-info.jsx +63 -0
  93. premiere_cli/panel/host/commands/get-project-item-info.jsx +247 -0
  94. premiere_cli/panel/host/commands/get-project-panel-metadata.jsx +30 -0
  95. premiere_cli/panel/host/commands/get-qe-clip-info.jsx +198 -0
  96. premiere_cli/panel/host/commands/get-render-queue-status.jsx +35 -0
  97. premiere_cli/panel/host/commands/get-sequence-count.jsx +24 -0
  98. premiere_cli/panel/host/commands/get-sequence-in-out.jsx +76 -0
  99. premiere_cli/panel/host/commands/get-sequence-markers-by-type.jsx +89 -0
  100. premiere_cli/panel/host/commands/get-source-monitor-info.jsx +34 -0
  101. premiere_cli/panel/host/commands/get-source-monitor-position.jsx +30 -0
  102. premiere_cli/panel/host/commands/get-target-tracks.jsx +76 -0
  103. premiere_cli/panel/host/commands/get-timeline-gaps.jsx +124 -0
  104. premiere_cli/panel/host/commands/get-timeline-summary.jsx +154 -0
  105. premiere_cli/panel/host/commands/get-total-clip-count.jsx +67 -0
  106. premiere_cli/panel/host/commands/get-track-info.jsx +117 -0
  107. premiere_cli/panel/host/commands/get-unused-media.jsx +99 -0
  108. premiere_cli/panel/host/commands/get-used-media-report.jsx +125 -0
  109. premiere_cli/panel/host/commands/get-value-at-time.jsx +135 -0
  110. premiere_cli/panel/host/commands/get-version-info.jsx +33 -0
  111. premiere_cli/panel/host/commands/get-work-area.jsx +52 -0
  112. premiere_cli/panel/host/commands/get-workspaces.jsx +29 -0
  113. premiere_cli/panel/host/commands/get-xmp-metadata.jsx +107 -0
  114. premiere_cli/panel/host/commands/import-ae-comps.jsx +129 -0
  115. premiere_cli/panel/host/commands/import-fcp-xml.jsx +103 -0
  116. premiere_cli/panel/host/commands/import-folder.jsx +193 -0
  117. premiere_cli/panel/host/commands/import-image-sequence.jsx +171 -0
  118. premiere_cli/panel/host/commands/import-media.jsx +217 -0
  119. premiere_cli/panel/host/commands/import-mogrt-from-library.jsx +105 -0
  120. premiere_cli/panel/host/commands/import-mogrt.jsx +111 -0
  121. premiere_cli/panel/host/commands/import-sequences-from-project.jsx +80 -0
  122. premiere_cli/panel/host/commands/insert-from-source.jsx +162 -0
  123. premiere_cli/panel/host/commands/inspect-dom-object.jsx +162 -0
  124. premiere_cli/panel/host/commands/invert-selection.jsx +114 -0
  125. premiere_cli/panel/host/commands/is-work-area-enabled.jsx +88 -0
  126. premiere_cli/panel/host/commands/lift-selection.jsx +106 -0
  127. premiere_cli/panel/host/commands/link-selection.jsx +111 -0
  128. premiere_cli/panel/host/commands/list-available-audio-effects.jsx +33 -0
  129. premiere_cli/panel/host/commands/list-available-audio-transitions.jsx +35 -0
  130. premiere_cli/panel/host/commands/list-available-effects.jsx +33 -0
  131. premiere_cli/panel/host/commands/list-available-transitions.jsx +74 -0
  132. premiere_cli/panel/host/commands/list-markers.jsx +69 -0
  133. premiere_cli/panel/host/commands/list-project-items.jsx +105 -0
  134. premiere_cli/panel/host/commands/lock-track.jsx +114 -0
  135. premiere_cli/panel/host/commands/manage-proxies.jsx +176 -0
  136. premiere_cli/panel/host/commands/match-frame.jsx +141 -0
  137. premiere_cli/panel/host/commands/move-clip-to-track.jsx +220 -0
  138. premiere_cli/panel/host/commands/move-clip.jsx +97 -0
  139. premiere_cli/panel/host/commands/move-items-to-bin.jsx +194 -0
  140. premiere_cli/panel/host/commands/move-playhead-to-edit.jsx +174 -0
  141. premiere_cli/panel/host/commands/move-playhead.jsx +137 -0
  142. premiere_cli/panel/host/commands/nest-clips.jsx +179 -0
  143. premiere_cli/panel/host/commands/open-in-source.jsx +117 -0
  144. premiere_cli/panel/host/commands/open-project.jsx +61 -0
  145. premiere_cli/panel/host/commands/overwrite-clip-at.jsx +350 -0
  146. premiere_cli/panel/host/commands/overwrite-from-source.jsx +150 -0
  147. premiere_cli/panel/host/commands/play-source-monitor.jsx +36 -0
  148. premiere_cli/panel/host/commands/play-timeline.jsx +24 -0
  149. premiere_cli/panel/host/commands/razor-all-tracks.jsx +171 -0
  150. premiere_cli/panel/host/commands/redo.jsx +69 -0
  151. premiere_cli/panel/host/commands/refresh-media.jsx +101 -0
  152. premiere_cli/panel/host/commands/relink-media.jsx +132 -0
  153. premiere_cli/panel/host/commands/remove-all-effects.jsx +114 -0
  154. premiere_cli/panel/host/commands/remove-effect-by-name.jsx +143 -0
  155. premiere_cli/panel/host/commands/remove-effect.jsx +122 -0
  156. premiere_cli/panel/host/commands/remove-from-timeline.jsx +93 -0
  157. premiere_cli/panel/host/commands/remove-keyframe-range.jsx +173 -0
  158. premiere_cli/panel/host/commands/remove-keyframe.jsx +192 -0
  159. premiere_cli/panel/host/commands/remove-selected-clips.jsx +126 -0
  160. premiere_cli/panel/host/commands/remove-track-intervals.jsx +254 -0
  161. premiere_cli/panel/host/commands/remove-transition.jsx +164 -0
  162. premiere_cli/panel/host/commands/rename-bin.jsx +88 -0
  163. premiere_cli/panel/host/commands/rename-clip.jsx +127 -0
  164. premiere_cli/panel/host/commands/rename-project-item.jsx +111 -0
  165. premiere_cli/panel/host/commands/rename-track.jsx +84 -0
  166. premiere_cli/panel/host/commands/replace-clip-media.jsx +140 -0
  167. premiere_cli/panel/host/commands/replace-clip.jsx +188 -0
  168. premiere_cli/panel/host/commands/reverse-clip.jsx +187 -0
  169. premiere_cli/panel/host/commands/ripple-delete-clip.jsx +156 -0
  170. premiere_cli/panel/host/commands/roll-edit.jsx +162 -0
  171. premiere_cli/panel/host/commands/save-project-as.jsx +55 -0
  172. premiere_cli/panel/host/commands/save-project.jsx +57 -0
  173. premiere_cli/panel/host/commands/scene-edit-detection.jsx +128 -0
  174. premiere_cli/panel/host/commands/search-project-items.jsx +180 -0
  175. premiere_cli/panel/host/commands/select-all-clips.jsx +107 -0
  176. premiere_cli/panel/host/commands/select-clips-by-color.jsx +142 -0
  177. premiere_cli/panel/host/commands/select-clips-by-name.jsx +133 -0
  178. premiere_cli/panel/host/commands/select-clips-in-range.jsx +133 -0
  179. premiere_cli/panel/host/commands/select-disabled-clips.jsx +127 -0
  180. premiere_cli/panel/host/commands/select-project-item.jsx +115 -0
  181. premiere_cli/panel/host/commands/set-active-project.jsx +184 -0
  182. premiere_cli/panel/host/commands/set-active-sequence.jsx +87 -0
  183. premiere_cli/panel/host/commands/set-all-tracks-targeted.jsx +112 -0
  184. premiere_cli/panel/host/commands/set-anti-alias-quality.jsx +125 -0
  185. premiere_cli/panel/host/commands/set-blend-mode.jsx +111 -0
  186. premiere_cli/panel/host/commands/set-clip-anchor-point.jsx +102 -0
  187. premiere_cli/panel/host/commands/set-clip-enabled.jsx +127 -0
  188. premiere_cli/panel/host/commands/set-clip-opacity.jsx +102 -0
  189. premiere_cli/panel/host/commands/set-clip-pan.jsx +107 -0
  190. premiere_cli/panel/host/commands/set-clip-position.jsx +103 -0
  191. premiere_cli/panel/host/commands/set-clip-properties.jsx +221 -0
  192. premiere_cli/panel/host/commands/set-clip-rotation.jsx +102 -0
  193. premiere_cli/panel/host/commands/set-clip-scale.jsx +102 -0
  194. premiere_cli/panel/host/commands/set-clip-selection.jsx +144 -0
  195. premiere_cli/panel/host/commands/set-clip-speed.jsx +160 -0
  196. premiere_cli/panel/host/commands/set-clip-volume.jsx +128 -0
  197. premiere_cli/panel/host/commands/set-color-label.jsx +123 -0
  198. premiere_cli/panel/host/commands/set-color-value.jsx +155 -0
  199. premiere_cli/panel/host/commands/set-effect-property.jsx +124 -0
  200. premiere_cli/panel/host/commands/set-footage-interpretation.jsx +173 -0
  201. premiere_cli/panel/host/commands/set-frame-blend.jsx +145 -0
  202. premiere_cli/panel/host/commands/set-graphics-white-luminance.jsx +73 -0
  203. premiere_cli/panel/host/commands/set-item-in-out.jsx +180 -0
  204. premiere_cli/panel/host/commands/set-item-metadata.jsx +134 -0
  205. premiere_cli/panel/host/commands/set-item-offline.jsx +109 -0
  206. premiere_cli/panel/host/commands/set-item-start-time.jsx +139 -0
  207. premiere_cli/panel/host/commands/set-keyframe-interpolation.jsx +186 -0
  208. premiere_cli/panel/host/commands/set-override-frame-rate.jsx +123 -0
  209. premiere_cli/panel/host/commands/set-override-pixel-aspect-ratio.jsx +129 -0
  210. premiere_cli/panel/host/commands/set-poster-frame.jsx +192 -0
  211. premiere_cli/panel/host/commands/set-project-panel-metadata.jsx +69 -0
  212. premiere_cli/panel/host/commands/set-scale-to-frame-size.jsx +101 -0
  213. premiere_cli/panel/host/commands/set-scale-width-height.jsx +140 -0
  214. premiere_cli/panel/host/commands/set-sequence-in-out.jsx +65 -0
  215. premiere_cli/panel/host/commands/set-sequence-settings.jsx +184 -0
  216. premiere_cli/panel/host/commands/set-source-in-out.jsx +98 -0
  217. premiere_cli/panel/host/commands/set-target-track.jsx +91 -0
  218. premiere_cli/panel/host/commands/set-time-interpolation.jsx +141 -0
  219. premiere_cli/panel/host/commands/set-track-mute.jsx +91 -0
  220. premiere_cli/panel/host/commands/set-track-visibility.jsx +94 -0
  221. premiere_cli/panel/host/commands/set-transcode-on-ingest.jsx +78 -0
  222. premiere_cli/panel/host/commands/set-uniform-scale.jsx +104 -0
  223. premiere_cli/panel/host/commands/set-work-area.jsx +76 -0
  224. premiere_cli/panel/host/commands/set-workspace.jsx +35 -0
  225. premiere_cli/panel/host/commands/set-xmp-metadata.jsx +157 -0
  226. premiere_cli/panel/host/commands/slide-edit.jsx +162 -0
  227. premiere_cli/panel/host/commands/slip-edit.jsx +167 -0
  228. premiere_cli/panel/host/commands/split-clip.jsx +144 -0
  229. premiere_cli/panel/host/commands/stabilize-clip.jsx +139 -0
  230. premiere_cli/panel/host/commands/stop-playback.jsx +23 -0
  231. premiere_cli/panel/host/commands/trim-clip.jsx +111 -0
  232. premiere_cli/panel/host/commands/undo.jsx +114 -0
  233. premiere_cli/panel/host/commands/unlink-selection.jsx +106 -0
  234. premiere_cli/panel/host/commands/unnest-sequence.jsx +207 -0
  235. premiere_cli/panel/host/commands/update-marker.jsx +120 -0
  236. premiere_cli/panel/host/index.jsx +1227 -0
  237. premiere_cli/panel/host/presets/default-4k-25fps.sqpreset +63 -0
  238. premiere_cli/panel/index.html +16 -0
  239. premiere_cli/panel/js/cs-interface.js +11 -0
  240. premiere_cli/panel/js/main.js +409 -0
  241. premiere_cli/panel_install.py +220 -0
  242. premiere_cli-0.2.0.dist-info/METADATA +127 -0
  243. premiere_cli-0.2.0.dist-info/RECORD +247 -0
  244. premiere_cli-0.2.0.dist-info/WHEEL +5 -0
  245. premiere_cli-0.2.0.dist-info/entry_points.txt +3 -0
  246. premiere_cli-0.2.0.dist-info/licenses/LICENSE +428 -0
  247. premiere_cli-0.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,305 @@
1
+ // Command: debug-qe-try-mutate → ppb_debugQeTryMutate
2
+ // Loaded lazily by ppb_dispatch (host/index.jsx) on first use, into the
3
+ // same global ExtendScript context — shared helpers (ensureQEEnabled, ...)
4
+ // are already defined there.
5
+ //
6
+ // TEMPORARY / EXPERIMENTAL — mutation test, not a supported panel feature.
7
+ // Reflection (see ppb_debugQeInspect) revealed method NAMES on QE Clip
8
+ // objects (remove, move, moveToTrack, ...) but no signatures — every
9
+ // method's .reflect description/parameters came back empty on this build.
10
+ // This tries a short list of plausible argument counts for `remove` and
11
+ // `moveToTrack` against real clips and reports what happened, so the real
12
+ // signature can be inferred from behavior instead of guessed blind.
13
+ //
14
+ // SAFETY GUARD: only runs if the *active* sequence's name exactly matches
15
+ // args.sequenceName. Point this at a duplicate/throwaway sequence, never
16
+ // at real project media — a wrong guess could ripple-shift or delete
17
+ // clips on whatever sequence is active.
18
+ function scanAudioClips(qeSeq, numAudioTracks) {
19
+ var clips = [];
20
+ var emptyTrackIndices = [];
21
+
22
+ for (var t = 0; t < numAudioTracks; t++) {
23
+ var track = qeSeq.getAudioTrackAt(t);
24
+ var numItems = track.numItems;
25
+ var allEmpty = true;
26
+
27
+ for (var i = 0; i < numItems; i++) {
28
+ var item = track.getItemAt(i);
29
+ if (item.type !== "Empty") {
30
+ allEmpty = false;
31
+ clips.push({ trackIndex: t, itemIndex: i, name: item.name, item: item });
32
+ }
33
+ }
34
+
35
+ if (allEmpty) {
36
+ emptyTrackIndices.push(t);
37
+ }
38
+ }
39
+
40
+ return { clips: clips, emptyTrackIndices: emptyTrackIndices };
41
+ }
42
+
43
+ function describeAudioTrack(qeSeq, trackIndex) {
44
+ var track = qeSeq.getAudioTrackAt(trackIndex);
45
+ var items = [];
46
+ for (var i = 0; i < track.numItems; i++) {
47
+ var item = track.getItemAt(i);
48
+ items.push({ index: i, type: item.type, name: item.type !== "Empty" ? item.name : "" });
49
+ }
50
+ return items;
51
+ }
52
+
53
+ function tryCallGuesses(fn, thisArg, guesses) {
54
+ var attempts = [];
55
+ var succeededWithArgs = null;
56
+
57
+ for (var g = 0; g < guesses.length; g++) {
58
+ var callArgs = guesses[g];
59
+ try {
60
+ fn.apply(thisArg, callArgs);
61
+ attempts.push({ args: describeArgsForLog(callArgs), success: true });
62
+ succeededWithArgs = describeArgsForLog(callArgs);
63
+ break;
64
+ } catch (e) {
65
+ attempts.push({ args: describeArgsForLog(callArgs), success: false, error: e.toString() });
66
+ }
67
+ }
68
+
69
+ return { attempts: attempts, succeededWithArgs: succeededWithArgs };
70
+ }
71
+
72
+ function describeArgsForLog(callArgs) {
73
+ var described = [];
74
+ for (var i = 0; i < callArgs.length; i++) {
75
+ var a = callArgs[i];
76
+ if (a && typeof a === "object" && typeof a.numItems !== "undefined") {
77
+ described.push("<Track>");
78
+ } else if (a && typeof a === "object" && typeof a.seconds !== "undefined") {
79
+ described.push("<Time seconds=" + a.seconds + ">");
80
+ } else {
81
+ described.push(a);
82
+ }
83
+ }
84
+ return described;
85
+ }
86
+
87
+ function ppb_debugQeTryMutate(argsJson) {
88
+ try {
89
+ var args;
90
+ try {
91
+ args = JSON.parse(argsJson);
92
+ } catch (e) {
93
+ return JSON.stringify({ ok: false, error: "invalid args JSON: " + e.toString() });
94
+ }
95
+
96
+ if (!args.sequenceName || typeof args.sequenceName !== "string") {
97
+ return JSON.stringify({ ok: false, error: "sequenceName is required (safety guard — must exactly match the active sequence's name)" });
98
+ }
99
+
100
+ if (!app.project) {
101
+ return JSON.stringify({ ok: false, error: "no project open" });
102
+ }
103
+
104
+ try {
105
+ ensureQEEnabled();
106
+ } catch (e) {
107
+ return JSON.stringify({ ok: false, error: "app.enableQE() failed: " + e.toString() });
108
+ }
109
+
110
+ if (typeof qe === "undefined" || !qe.project) {
111
+ return JSON.stringify({ ok: false, error: "QE DOM not available after enableQE()" });
112
+ }
113
+
114
+ var qeSeq;
115
+ try {
116
+ qeSeq = qe.project.getActiveSequence();
117
+ } catch (e) {
118
+ return JSON.stringify({ ok: false, error: "qe.project.getActiveSequence() failed: " + e.toString() });
119
+ }
120
+
121
+ if (!qeSeq) {
122
+ return JSON.stringify({ ok: false, error: "no active sequence (open/select a sequence tab in Premiere)" });
123
+ }
124
+
125
+ if (qeSeq.name !== args.sequenceName) {
126
+ return JSON.stringify({
127
+ ok: false,
128
+ error: "active sequence is \"" + qeSeq.name + "\", expected \"" + args.sequenceName +
129
+ "\" — select the intended duplicate/test sequence's tab in Premiere before running this"
130
+ });
131
+ }
132
+
133
+ var numAudioTracks = qeSeq.numAudioTracks;
134
+ var scan = scanAudioClips(qeSeq, numAudioTracks);
135
+
136
+ if (scan.clips.length < 1) {
137
+ return JSON.stringify({
138
+ ok: true,
139
+ result: { note: "no audio clips found on any audio track — nothing to test on", numAudioTracks: numAudioTracks }
140
+ });
141
+ }
142
+
143
+ var result = { before: {}, removeTest: null, moveTest: null };
144
+ var skipRemoveTest = args.skipRemoveTest === true;
145
+ var removeTargetTrackIndex = null;
146
+
147
+ if (skipRemoveTest) {
148
+ result.removeTest = { skipped: true, reason: "skipRemoveTest was true" };
149
+ } else {
150
+ // --- remove() test ---
151
+ var removeTarget = scan.clips[0];
152
+ removeTargetTrackIndex = removeTarget.trackIndex;
153
+ result.before.removeTarget = { track: removeTarget.trackIndex, item: removeTarget.itemIndex, name: removeTarget.name };
154
+
155
+ var removeGuesses = [[], [true], [false], [true, true], [true, false], [false, true]];
156
+ var removeOutcome = tryCallGuesses(removeTarget.item.remove, removeTarget.item, removeGuesses);
157
+
158
+ result.removeTest = {
159
+ attempts: removeOutcome.attempts,
160
+ succeededWithArgs: removeOutcome.succeededWithArgs,
161
+ trackAfter: describeAudioTrack(qeSeq, removeTarget.trackIndex)
162
+ };
163
+ }
164
+
165
+ // --- moveToTrack() test — pick a clip on a DIFFERENT track than the
166
+ // remove target (if any), so the two tests don't interfere with each
167
+ // other's track-index bookkeeping.
168
+ var moveTarget = null;
169
+ for (var c = 0; c < scan.clips.length; c++) {
170
+ if (scan.clips[c].trackIndex !== removeTargetTrackIndex) {
171
+ moveTarget = scan.clips[c];
172
+ break;
173
+ }
174
+ }
175
+
176
+ if (!moveTarget) {
177
+ result.moveTest = { skipped: true, reason: "only one clip (or all clips on the same track) found; need a second clip on a distinct track" };
178
+ return JSON.stringify({ ok: true, result: result });
179
+ }
180
+
181
+ result.before.moveTarget = { track: moveTarget.trackIndex, item: moveTarget.itemIndex, name: moveTarget.name };
182
+
183
+ var destTrackIndex = null;
184
+ for (var e2 = 0; e2 < scan.emptyTrackIndices.length; e2++) {
185
+ if (scan.emptyTrackIndices[e2] !== moveTarget.trackIndex) {
186
+ destTrackIndex = scan.emptyTrackIndices[e2];
187
+ break;
188
+ }
189
+ }
190
+
191
+ if (destTrackIndex === null) {
192
+ result.moveTest = { skipped: true, reason: "no empty audio track available as a safe move destination" };
193
+ return JSON.stringify({ ok: true, result: result });
194
+ }
195
+
196
+ var destTrack = qeSeq.getAudioTrackAt(destTrackIndex);
197
+
198
+ // Prior runs: Track object at position 0 -> "Illegal Parameter type";
199
+ // plain number at position 0 with <3 total args -> "Not Enough
200
+ // Parameters" (count accepted, so a numeric track index IS the right
201
+ // type there). With >=3 plain-number/boolean args, every guess became
202
+ // "Illegal Parameter type" instead -- count is satisfied but some
203
+ // position now fails type-checking. Likely candidate: a "position"
204
+ // argument wants a real Time object, not a raw number. Try constructing
205
+ // one via the global `Time` class (shared with the standard Premiere
206
+ // DOM) and via reusing the moved clip's own `.start` (already a
207
+ // confirmed-valid QETime instance) in that slot instead of `0`.
208
+ var zeroTime = null;
209
+ try {
210
+ zeroTime = new Time();
211
+ zeroTime.seconds = 0;
212
+ } catch (e) {
213
+ zeroTime = null;
214
+ }
215
+
216
+ var clipStartTime = null;
217
+ try {
218
+ clipStartTime = moveTarget.item.start;
219
+ } catch (e) {
220
+ clipStartTime = null;
221
+ }
222
+
223
+ var moveGuesses = [];
224
+ if (zeroTime !== null) {
225
+ moveGuesses.push([destTrackIndex, zeroTime, 0]);
226
+ moveGuesses.push([destTrackIndex, zeroTime, true]);
227
+ moveGuesses.push([destTrackIndex, zeroTime, false]);
228
+ moveGuesses.push([moveTarget.trackIndex, destTrackIndex, zeroTime]);
229
+ }
230
+ if (clipStartTime !== null) {
231
+ moveGuesses.push([destTrackIndex, clipStartTime, 0]);
232
+ moveGuesses.push([destTrackIndex, clipStartTime, true]);
233
+ moveGuesses.push([moveTarget.trackIndex, destTrackIndex, clipStartTime]);
234
+ }
235
+ // Fallbacks in case neither Time construction path is available.
236
+ moveGuesses.push([destTrackIndex, 0, 0]);
237
+ moveGuesses.push([destTrackIndex, 0, true]);
238
+
239
+ result.moveTest_zeroTimeConstructed = zeroTime !== null;
240
+ result.moveTest_clipStartTimeReadable = clipStartTime !== null;
241
+
242
+ var moveOutcome = tryCallGuesses(moveTarget.item.moveToTrack, moveTarget.item, moveGuesses);
243
+
244
+ result.moveTest = {
245
+ destTrackIndex: destTrackIndex,
246
+ attempts: moveOutcome.attempts,
247
+ succeededWithArgs: moveOutcome.succeededWithArgs,
248
+ sourceTrackAfter: describeAudioTrack(qeSeq, moveTarget.trackIndex),
249
+ destTrackAfter: describeAudioTrack(qeSeq, destTrackIndex)
250
+ };
251
+
252
+ // --- Alternative to moveToTrack(): place a NEW instance of the same
253
+ // source clip directly onto the destination track via Track-level
254
+ // insert()/overwrite(), mirroring the standard DOM's documented
255
+ // Track.insertClip(projectItem, time) / overwriteClip(projectItem, time).
256
+ // Doesn't require moving the existing TrackItem at all.
257
+ var projectItem = null;
258
+ try {
259
+ projectItem = moveTarget.item.getProjectItem();
260
+ } catch (e) {
261
+ result.insertOverwriteTest = { skipped: true, reason: "getProjectItem() failed: " + e.toString() };
262
+ return JSON.stringify({ ok: true, result: result });
263
+ }
264
+
265
+ if (!projectItem) {
266
+ result.insertOverwriteTest = { skipped: true, reason: "getProjectItem() returned nothing" };
267
+ return JSON.stringify({ ok: true, result: result });
268
+ }
269
+
270
+ var insertGuesses = [
271
+ [projectItem, zeroTime],
272
+ [projectItem, 0],
273
+ [projectItem],
274
+ [projectItem, zeroTime, 0],
275
+ [projectItem, zeroTime, true]
276
+ ];
277
+ var insertOutcome = tryCallGuesses(destTrack.insert, destTrack, insertGuesses);
278
+
279
+ var insertResult = {
280
+ attempts: insertOutcome.attempts,
281
+ succeededWithArgs: insertOutcome.succeededWithArgs,
282
+ destTrackAfterInsert: describeAudioTrack(qeSeq, destTrackIndex)
283
+ };
284
+
285
+ // Only try overwrite() if insert() didn't already place something —
286
+ // avoids stacking two clips onto the same (short) destination track.
287
+ if (insertOutcome.succeededWithArgs === null) {
288
+ var overwriteGuesses = [
289
+ [projectItem, zeroTime],
290
+ [projectItem, 0],
291
+ [projectItem]
292
+ ];
293
+ var overwriteOutcome = tryCallGuesses(destTrack.overwrite, destTrack, overwriteGuesses);
294
+ insertResult.overwriteAttempts = overwriteOutcome.attempts;
295
+ insertResult.overwriteSucceededWithArgs = overwriteOutcome.succeededWithArgs;
296
+ insertResult.destTrackAfterOverwrite = describeAudioTrack(qeSeq, destTrackIndex);
297
+ }
298
+
299
+ result.insertOverwriteTest = insertResult;
300
+
301
+ return JSON.stringify({ ok: true, result: result });
302
+ } catch (e) {
303
+ return JSON.stringify({ ok: false, error: e.toString() });
304
+ }
305
+ }
@@ -0,0 +1,178 @@
1
+ // Command: delete-marker → ppb_deleteMarker
2
+ // Loaded lazily by ppb_dispatch (host/index.jsx) on first use, into the
3
+ // same global ExtendScript context — shared helpers (TICKS_PER_SECOND,
4
+ // findSequenceByName, timeValueToSeconds, ...) are already defined there.
5
+ //
6
+ // Ported from leancoderkavy's premiere-pro-mcp delete_marker tool, but
7
+ // re-addressed like update-marker.jsx: identifies the target by .guid
8
+ // rather than matching a time value within a tolerance window. Deletion
9
+ // itself is markers.deleteMarker(marker) per PREMIERE_API_NOTES.md.
10
+ //
11
+ // Also accepts PROJECT-ITEM addressing (nodeId or itemName instead of
12
+ // sequenceName) to delete markers created by add-marker-to-project-item —
13
+ // needed because undo is non-functional on this Premiere 2026 build
14
+ // (app.project.undo missing; qe.project.undo is a confirmed silent no-op),
15
+ // so item markers would otherwise be undeletable from the bridge.
16
+ //
17
+ // MUTATION RULE: verified by comparing the collection's total marker count
18
+ // before and after (must drop by exactly one) AND confirming the guid no
19
+ // longer appears in a fresh getFirstMarker()/getNextMarker() walk — never
20
+ // trusting deleteMarker()'s own (lack of a) return value.
21
+
22
+ function ppbDeleteMarker_findByGuidAndCount(markerCollection, guid, markerName) {
23
+ var found = null;
24
+ var matches = 0;
25
+ var count = 0;
26
+ var m = markerCollection.getFirstMarker();
27
+ var iterations = 0;
28
+ while (m !== null && typeof m !== "undefined" && iterations < 10000) {
29
+ count++;
30
+ var thisGuid = null;
31
+ var thisName = null;
32
+ try { thisGuid = m.guid; } catch (e) { thisGuid = null; }
33
+ try { thisName = m.name; } catch (e) { thisName = null; }
34
+ var isMatch = (guid !== null && thisGuid === guid) ||
35
+ (guid === null && markerName !== null && thisName === markerName);
36
+ if (isMatch) {
37
+ found = m;
38
+ matches++;
39
+ }
40
+ m = markerCollection.getNextMarker(m);
41
+ iterations++;
42
+ }
43
+ return { found: found, count: count, matches: matches };
44
+ }
45
+
46
+ // Depth-first project-item lookup by nodeId or exact name (same approach
47
+ // as get-project-item-info's walker: recurse on children presence, NOT on
48
+ // isBin — the root item's type is ROOT).
49
+ function ppbDeleteMarker_findItem(item, nodeId, name, depth) {
50
+ if (depth > 32) {
51
+ return null;
52
+ }
53
+ try {
54
+ if (nodeId !== null && item.nodeId === nodeId) {
55
+ return item;
56
+ }
57
+ if (name !== null && item.name === name && item.type !== ProjectItemType.BIN && item.type !== ProjectItemType.ROOT) {
58
+ return item;
59
+ }
60
+ } catch (e) {
61
+ // fall through
62
+ }
63
+ if (item.children && item.children.numItems > 0) {
64
+ for (var i = 0; i < item.children.numItems; i++) {
65
+ var found = ppbDeleteMarker_findItem(item.children[i], nodeId, name, depth + 1);
66
+ if (found) {
67
+ return found;
68
+ }
69
+ }
70
+ }
71
+ return null;
72
+ }
73
+
74
+ function ppb_deleteMarker(argsJson) {
75
+ try {
76
+ var args;
77
+ try {
78
+ args = JSON.parse(argsJson);
79
+ } catch (e) {
80
+ return JSON.stringify({ ok: false, error: "invalid args JSON: " + e.toString() });
81
+ }
82
+
83
+ var hasGuid = typeof args.guid === "string" && args.guid.length > 0;
84
+ var hasMarkerName = typeof args.markerName === "string" && args.markerName.length > 0;
85
+ if (!hasGuid && !hasMarkerName) {
86
+ return JSON.stringify({ ok: false, error: "one of guid or markerName is required" });
87
+ }
88
+
89
+ if (!app.project) {
90
+ return JSON.stringify({ ok: false, error: "no project open" });
91
+ }
92
+
93
+ var hasItemAddress = (typeof args.nodeId === "string" && args.nodeId.length > 0) ||
94
+ (typeof args.itemName === "string" && args.itemName.length > 0);
95
+
96
+ var markerCollection = null;
97
+ var scopeLabel = null;
98
+
99
+ if (hasItemAddress) {
100
+ // Project-item marker deletion
101
+ var item = ppbDeleteMarker_findItem(
102
+ app.project.rootItem,
103
+ (typeof args.nodeId === "string" && args.nodeId.length > 0) ? args.nodeId : null,
104
+ (typeof args.itemName === "string" && args.itemName.length > 0) ? args.itemName : null,
105
+ 0
106
+ );
107
+ if (!item) {
108
+ return JSON.stringify({ ok: false, error: "no project item found matching the given nodeId/itemName" });
109
+ }
110
+ try {
111
+ markerCollection = item.getMarkers();
112
+ } catch (e) {
113
+ return JSON.stringify({ ok: false, error: "getMarkers() failed on the project item: " + e.toString() });
114
+ }
115
+ scopeLabel = item.name;
116
+ } else {
117
+ var seq = null;
118
+ if (args.sequenceName && typeof args.sequenceName === "string") {
119
+ seq = findSequenceByName(args.sequenceName);
120
+ if (!seq) {
121
+ return JSON.stringify({ ok: false, error: "no sequence named \"" + args.sequenceName + "\" is open" });
122
+ }
123
+ } else {
124
+ seq = app.project.activeSequence;
125
+ if (!seq) {
126
+ return JSON.stringify({ ok: false, error: "no active sequence, and no sequenceName given" });
127
+ }
128
+ }
129
+ markerCollection = seq.markers;
130
+ scopeLabel = seq.name;
131
+ }
132
+
133
+ var guidArg = hasGuid ? args.guid : null;
134
+ var nameArg = hasMarkerName ? args.markerName : null;
135
+ var before = ppbDeleteMarker_findByGuidAndCount(markerCollection, guidArg, nameArg);
136
+ if (!before.found) {
137
+ return JSON.stringify({ ok: false, error: "no marker found matching " + (hasGuid ? "guid \"" + args.guid + "\"" : "name \"" + args.markerName + "\"") + " on " + scopeLabel });
138
+ }
139
+ if (!hasGuid && before.matches > 1) {
140
+ return JSON.stringify({ ok: false, error: before.matches + " markers named \"" + args.markerName + "\" on " + scopeLabel + " — use --guid to disambiguate" });
141
+ }
142
+
143
+ try {
144
+ markerCollection.deleteMarker(before.found);
145
+ } catch (e) {
146
+ return JSON.stringify({ ok: false, error: "deleteMarker failed: " + e.toString() });
147
+ }
148
+
149
+ var after = ppbDeleteMarker_findByGuidAndCount(markerCollection, guidArg, nameArg);
150
+
151
+ var deleted = after.found === null && after.count === before.count - 1;
152
+
153
+ if (!deleted) {
154
+ return JSON.stringify({
155
+ ok: false,
156
+ error: "deleteMarker did not throw, but the marker count/guid check afterward doesn't confirm deletion",
157
+ countBefore: before.count,
158
+ countAfter: after.count,
159
+ stillPresent: after.found !== null
160
+ });
161
+ }
162
+
163
+ return JSON.stringify({
164
+ ok: true,
165
+ result: {
166
+ scope: hasItemAddress ? "projectItem" : "sequence",
167
+ scopeName: scopeLabel,
168
+ deleted: true,
169
+ guid: hasGuid ? args.guid : null,
170
+ markerName: hasMarkerName ? args.markerName : null,
171
+ markerCountBefore: before.count,
172
+ markerCountAfter: after.count
173
+ }
174
+ });
175
+ } catch (e) {
176
+ return JSON.stringify({ ok: false, error: e.toString() });
177
+ }
178
+ }
@@ -0,0 +1,98 @@
1
+ // Command: deselect-all-clips → ppb_deselectAllClips
2
+ // Loaded lazily by ppb_dispatch (host/index.jsx) on first use, into the
3
+ // same global ExtendScript context — shared helpers (findSequenceByName,
4
+ // timeValueToSeconds, ...) are already defined there.
5
+ //
6
+ // Standard-DOM only (clip.setSelected(false, true)) — no QE needed. Ported
7
+ // from leancoderkavy's premiere-pro-mcp `deselect_all_clips` tool
8
+ // (selection.ts). Mutation — verified via seq.getSelection() afterward
9
+ // (expected to read back empty).
10
+
11
+ function ppb_deselectAllClipsCollectSelection(seq) {
12
+ var sel = null;
13
+ try {
14
+ sel = seq.getSelection();
15
+ } catch (e) {
16
+ sel = null;
17
+ }
18
+ var selCount = 0;
19
+ if (sel) {
20
+ if (typeof sel.numItems === "number") {
21
+ selCount = sel.numItems;
22
+ } else if (typeof sel.length === "number") {
23
+ selCount = sel.length;
24
+ }
25
+ }
26
+ var selectedClips = [];
27
+ var cap = selCount > 20 ? 20 : selCount;
28
+ for (var i = 0; i < cap; i++) {
29
+ var item = sel[i];
30
+ var entry = { name: null, mediaType: null, startSeconds: null, endSeconds: null, nodeId: null };
31
+ try { entry.name = item.name; } catch (e) { entry.name = null; }
32
+ try { entry.mediaType = item.mediaType; } catch (e) { entry.mediaType = null; }
33
+ try { entry.startSeconds = timeValueToSeconds(item.start); } catch (e) { entry.startSeconds = null; }
34
+ try { entry.endSeconds = timeValueToSeconds(item.end); } catch (e) { entry.endSeconds = null; }
35
+ try { entry.nodeId = item.nodeId; } catch (e) { entry.nodeId = null; }
36
+ selectedClips.push(entry);
37
+ }
38
+ var result = { selectedCount: selCount, selectedClips: selectedClips };
39
+ if (selCount > 20) {
40
+ result.truncated = true;
41
+ }
42
+ return result;
43
+ }
44
+
45
+ function ppb_deselectAllClips(argsJson) {
46
+ try {
47
+ var args;
48
+ try {
49
+ args = JSON.parse(argsJson);
50
+ } catch (e) {
51
+ return JSON.stringify({ ok: false, error: "invalid args JSON: " + e.toString() });
52
+ }
53
+
54
+ if (!app.project) {
55
+ return JSON.stringify({ ok: false, error: "no project open" });
56
+ }
57
+
58
+ var seq = null;
59
+ if (args.sequenceName && typeof args.sequenceName === "string") {
60
+ seq = findSequenceByName(args.sequenceName);
61
+ if (!seq) {
62
+ return JSON.stringify({ ok: false, error: "no sequence named \"" + args.sequenceName + "\" is open" });
63
+ }
64
+ } else {
65
+ seq = app.project.activeSequence;
66
+ if (!seq) {
67
+ return JSON.stringify({ ok: false, error: "no active sequence, and no sequenceName given" });
68
+ }
69
+ }
70
+
71
+ function deselectAllInTracks(tracks) {
72
+ for (var t = 0; t < tracks.numTracks; t++) {
73
+ var track = tracks[t];
74
+ for (var c = 0; c < track.clips.numItems; c++) {
75
+ try { track.clips[c].setSelected(false, true); } catch (e) { /* best-effort */ }
76
+ }
77
+ }
78
+ }
79
+
80
+ deselectAllInTracks(seq.videoTracks);
81
+ deselectAllInTracks(seq.audioTracks);
82
+
83
+ var selectionResult = ppb_deselectAllClipsCollectSelection(seq);
84
+
85
+ var result = {
86
+ sequenceName: seq.name
87
+ };
88
+ result.selectedCount = selectionResult.selectedCount;
89
+ result.selectedClips = selectionResult.selectedClips;
90
+ if (selectionResult.truncated) {
91
+ result.truncated = true;
92
+ }
93
+
94
+ return JSON.stringify({ ok: true, result: result });
95
+ } catch (e) {
96
+ return JSON.stringify({ ok: false, error: e.toString() });
97
+ }
98
+ }
@@ -0,0 +1,116 @@
1
+ // Command: detach-proxy → ppb_detachProxy
2
+ // Loaded lazily by ppb_dispatch (host/index.jsx) on first use, into the
3
+ // same global ExtendScript context — shared helpers (TICKS_PER_SECOND, ...)
4
+ // are already defined there.
5
+ //
6
+ // Ports the reference project's media.ts detach_proxy tool. Item
7
+ // resolution by nodeId/name uses children-presence recursion (NOT an
8
+ // isBin() gate), per get-project-item-info.jsx's live-debugged finding.
9
+ // Checks hasProxy() first and fails honestly if no proxy is attached,
10
+ // rather than calling detachProxy() unconditionally. Verified via a
11
+ // hasProxy() read-back (expected false afterward).
12
+
13
+ function ppbDetachProxy_findByNodeId(item, nodeId, depth) {
14
+ if (depth > 32) {
15
+ return null;
16
+ }
17
+ try {
18
+ if (item.nodeId === nodeId) {
19
+ return item;
20
+ }
21
+ } catch (e) {
22
+ // fall through
23
+ }
24
+ if (item.children && item.children.numItems > 0) {
25
+ for (var i = 0; i < item.children.numItems; i++) {
26
+ var found = ppbDetachProxy_findByNodeId(item.children[i], nodeId, depth + 1);
27
+ if (found) {
28
+ return found;
29
+ }
30
+ }
31
+ }
32
+ return null;
33
+ }
34
+
35
+ function ppbDetachProxy_findByName(item, name, depth) {
36
+ if (depth > 32) {
37
+ return null;
38
+ }
39
+ try {
40
+ if (item.name === name) {
41
+ return item;
42
+ }
43
+ } catch (e) {
44
+ // fall through
45
+ }
46
+ if (item.children && item.children.numItems > 0) {
47
+ for (var i = 0; i < item.children.numItems; i++) {
48
+ var found = ppbDetachProxy_findByName(item.children[i], name, depth + 1);
49
+ if (found) {
50
+ return found;
51
+ }
52
+ }
53
+ }
54
+ return null;
55
+ }
56
+
57
+ function ppb_detachProxy(argsJson) {
58
+ try {
59
+ var args;
60
+ try {
61
+ args = JSON.parse(argsJson);
62
+ } catch (e) {
63
+ return JSON.stringify({ ok: false, error: "invalid args JSON: " + e.toString() });
64
+ }
65
+
66
+ var hasNodeId = typeof args.nodeId === "string" && args.nodeId.length > 0;
67
+ var hasName = typeof args.name === "string" && args.name.length > 0;
68
+ if (!hasNodeId && !hasName) {
69
+ return JSON.stringify({ ok: false, error: "either nodeId or name is required" });
70
+ }
71
+
72
+ if (!app.project) {
73
+ return JSON.stringify({ ok: false, error: "no project open" });
74
+ }
75
+
76
+ var item = hasNodeId
77
+ ? ppbDetachProxy_findByNodeId(app.project.rootItem, args.nodeId, 0)
78
+ : ppbDetachProxy_findByName(app.project.rootItem, args.name, 0);
79
+ if (!item) {
80
+ return JSON.stringify({ ok: false, error: "no project item found matching the given nodeId/name" });
81
+ }
82
+
83
+ var hadProxy = null;
84
+ try { hadProxy = item.hasProxy(); } catch (e) { hadProxy = null; }
85
+ if (hadProxy === false) {
86
+ return JSON.stringify({
87
+ ok: false,
88
+ error: "hasProxy() returned false — no proxy is attached to this item",
89
+ nodeId: item.nodeId,
90
+ name: item.name
91
+ });
92
+ }
93
+
94
+ try {
95
+ item.detachProxy();
96
+ } catch (e) {
97
+ return JSON.stringify({ ok: false, error: "detachProxy() failed: " + e.toString() });
98
+ }
99
+
100
+ var hasProxyAfter = null;
101
+ try { hasProxyAfter = item.hasProxy(); } catch (e) { hasProxyAfter = null; }
102
+
103
+ return JSON.stringify({
104
+ ok: true,
105
+ result: {
106
+ nodeId: item.nodeId,
107
+ name: item.name,
108
+ hadProxy: hadProxy,
109
+ hasProxyAfter: hasProxyAfter,
110
+ verified: hasProxyAfter === false
111
+ }
112
+ });
113
+ } catch (e) {
114
+ return JSON.stringify({ ok: false, error: e.toString() });
115
+ }
116
+ }