@synthetic-ai/premiere-mcp 2.0.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 (104) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/LICENSE +21 -0
  3. package/README.md +242 -0
  4. package/cep-plugin/.debug +8 -0
  5. package/cep-plugin/CSInterface.js +70 -0
  6. package/cep-plugin/CSXS/manifest.xml +49 -0
  7. package/cep-plugin/host.jsx +7 -0
  8. package/cep-plugin/index.html +419 -0
  9. package/cep-plugin/main.js +327 -0
  10. package/cep-plugin/premiere.jsx +24947 -0
  11. package/cep-plugin/synthetic-logo.png +0 -0
  12. package/dist/bridge/file-bridge.d.ts +29 -0
  13. package/dist/bridge/file-bridge.js +145 -0
  14. package/dist/bridge/script-builder.d.ts +19 -0
  15. package/dist/bridge/script-builder.js +408 -0
  16. package/dist/http-server.d.ts +21 -0
  17. package/dist/http-server.js +140 -0
  18. package/dist/index.d.ts +3 -0
  19. package/dist/index.js +70 -0
  20. package/dist/resources/extendscript-reference.d.ts +2 -0
  21. package/dist/resources/extendscript-reference.js +219 -0
  22. package/dist/server.d.ts +4 -0
  23. package/dist/server.js +276 -0
  24. package/dist/tools/advanced.d.ts +530 -0
  25. package/dist/tools/advanced.js +923 -0
  26. package/dist/tools/audio-advanced.d.ts +847 -0
  27. package/dist/tools/audio-advanced.js +1653 -0
  28. package/dist/tools/audio.d.ts +84 -0
  29. package/dist/tools/audio.js +123 -0
  30. package/dist/tools/captions.d.ts +30 -0
  31. package/dist/tools/captions.js +73 -0
  32. package/dist/tools/clipboard.d.ts +153 -0
  33. package/dist/tools/clipboard.js +381 -0
  34. package/dist/tools/color-advanced.d.ts +821 -0
  35. package/dist/tools/color-advanced.js +772 -0
  36. package/dist/tools/discovery.d.ts +128 -0
  37. package/dist/tools/discovery.js +445 -0
  38. package/dist/tools/effects.d.ts +211 -0
  39. package/dist/tools/effects.js +554 -0
  40. package/dist/tools/export-presets.d.ts +333 -0
  41. package/dist/tools/export-presets.js +490 -0
  42. package/dist/tools/export.d.ts +320 -0
  43. package/dist/tools/export.js +618 -0
  44. package/dist/tools/health.d.ts +9 -0
  45. package/dist/tools/health.js +25 -0
  46. package/dist/tools/inspection.d.ts +179 -0
  47. package/dist/tools/inspection.js +1089 -0
  48. package/dist/tools/keyframes.d.ts +253 -0
  49. package/dist/tools/keyframes.js +611 -0
  50. package/dist/tools/markers.d.ts +117 -0
  51. package/dist/tools/markers.js +206 -0
  52. package/dist/tools/media.d.ts +297 -0
  53. package/dist/tools/media.js +493 -0
  54. package/dist/tools/metadata.d.ts +178 -0
  55. package/dist/tools/metadata.js +278 -0
  56. package/dist/tools/multicam.d.ts +199 -0
  57. package/dist/tools/multicam.js +212 -0
  58. package/dist/tools/playback.d.ts +34 -0
  59. package/dist/tools/playback.js +63 -0
  60. package/dist/tools/playhead.d.ts +77 -0
  61. package/dist/tools/playhead.js +185 -0
  62. package/dist/tools/project-manager.d.ts +65 -0
  63. package/dist/tools/project-manager.js +81 -0
  64. package/dist/tools/project.d.ts +390 -0
  65. package/dist/tools/project.js +596 -0
  66. package/dist/tools/scripting.d.ts +98 -0
  67. package/dist/tools/scripting.js +434 -0
  68. package/dist/tools/selection.d.ts +120 -0
  69. package/dist/tools/selection.js +334 -0
  70. package/dist/tools/sequence-advanced.d.ts +84 -0
  71. package/dist/tools/sequence-advanced.js +471 -0
  72. package/dist/tools/sequence-settings.d.ts +397 -0
  73. package/dist/tools/sequence-settings.js +967 -0
  74. package/dist/tools/sequence.d.ts +211 -0
  75. package/dist/tools/sequence.js +378 -0
  76. package/dist/tools/source-monitor.d.ts +95 -0
  77. package/dist/tools/source-monitor.js +180 -0
  78. package/dist/tools/text.d.ts +97 -0
  79. package/dist/tools/text.js +159 -0
  80. package/dist/tools/timeline.d.ts +265 -0
  81. package/dist/tools/timeline.js +575 -0
  82. package/dist/tools/track-management.d.ts +482 -0
  83. package/dist/tools/track-management.js +787 -0
  84. package/dist/tools/track-targeting.d.ts +666 -0
  85. package/dist/tools/track-targeting.js +1318 -0
  86. package/dist/tools/tracks.d.ts +90 -0
  87. package/dist/tools/tracks.js +183 -0
  88. package/dist/tools/transitions-motion.d.ts +788 -0
  89. package/dist/tools/transitions-motion.js +680 -0
  90. package/dist/tools/transitions.d.ts +124 -0
  91. package/dist/tools/transitions.js +267 -0
  92. package/dist/tools/utility.d.ts +469 -0
  93. package/dist/tools/utility.js +1118 -0
  94. package/dist/tools/workspace.d.ts +25 -0
  95. package/dist/tools/workspace.js +61 -0
  96. package/package.json +66 -0
  97. package/scripts/build-chat-dmg.sh +210 -0
  98. package/scripts/build-chat-zip.sh +291 -0
  99. package/scripts/install-cep-windows.ps1 +99 -0
  100. package/scripts/install-cep.sh +66 -0
  101. package/scripts/install-chat-plugin.bat +67 -0
  102. package/scripts/install-chat-plugin.sh +85 -0
  103. package/scripts/install-windows.bat +95 -0
  104. package/scripts/package.mjs +218 -0
Binary file
@@ -0,0 +1,29 @@
1
+ export interface BridgeOptions {
2
+ tempDir?: string;
3
+ timeoutMs?: number;
4
+ }
5
+ export interface CommandResult {
6
+ success: boolean;
7
+ data?: unknown;
8
+ error?: string;
9
+ }
10
+ export declare function getTempDir(options?: BridgeOptions): string;
11
+ /**
12
+ * Send a command (ExtendScript) to the CEP plugin and wait for a response.
13
+ *
14
+ * Protocol:
15
+ * 1. Write script to <tempDir>/cmd_<id>.jsx
16
+ * 2. CEP plugin picks it up, executes, writes result to <tempDir>/res_<id>.json
17
+ * 3. We poll for the response file and parse it.
18
+ */
19
+ export declare function sendCommand(script: string, options?: BridgeOptions): Promise<CommandResult>;
20
+ /**
21
+ * Send a raw/custom ExtendScript allowing all patterns (for LLM-authored scripts).
22
+ * Still enforces size limit. The script should already include helpers via buildToolScript.
23
+ */
24
+ export declare function sendRawCommand(script: string, options?: BridgeOptions): Promise<CommandResult>;
25
+ /**
26
+ * Clean up any stale command/response files from the temp directory.
27
+ */
28
+ export declare function cleanupTempDir(options?: BridgeOptions): void;
29
+ //# sourceMappingURL=file-bridge.d.ts.map
@@ -0,0 +1,145 @@
1
+ import { mkdirSync, writeFileSync, readFileSync, unlinkSync, existsSync, readdirSync, renameSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { tmpdir } from "node:os";
4
+ const DEFAULT_TEMP_DIR = join(tmpdir(), "premiere-mcp-bridge");
5
+ const POLL_INTERVAL_MS = 100;
6
+ const DEFAULT_TIMEOUT_MS = 30000;
7
+ let commandCounter = 0;
8
+ function ensureDir(dir) {
9
+ if (!existsSync(dir)) {
10
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
11
+ }
12
+ }
13
+ export function getTempDir(options) {
14
+ return options?.tempDir || process.env.PREMIERE_TEMP_DIR || DEFAULT_TEMP_DIR;
15
+ }
16
+ /**
17
+ * Send a command (ExtendScript) to the CEP plugin and wait for a response.
18
+ *
19
+ * Protocol:
20
+ * 1. Write script to <tempDir>/cmd_<id>.jsx
21
+ * 2. CEP plugin picks it up, executes, writes result to <tempDir>/res_<id>.json
22
+ * 3. We poll for the response file and parse it.
23
+ */
24
+ export async function sendCommand(script, options) {
25
+ const tempDir = getTempDir(options);
26
+ const timeoutMs = options?.timeoutMs || DEFAULT_TIMEOUT_MS;
27
+ ensureDir(tempDir);
28
+ const id = `${Date.now()}_${++commandCounter}`;
29
+ const cmdFile = join(tempDir, `cmd_${id}.jsx`);
30
+ const resFile = join(tempDir, `res_${id}.json`);
31
+ // Validate script
32
+ validateScript(script);
33
+ // Write command file atomically: write to a temp name the CEP poller ignores
34
+ // (it only matches the `cmd_` prefix), then rename into place. This prevents
35
+ // the plugin from reading a half-written command file.
36
+ const cmdTmp = join(tempDir, `tmp_${id}.jsx`);
37
+ writeFileSync(cmdTmp, script, "utf-8");
38
+ renameSync(cmdTmp, cmdFile);
39
+ // Poll for response
40
+ const result = await pollForResponse(resFile, timeoutMs);
41
+ // Cleanup
42
+ safeUnlink(cmdFile);
43
+ safeUnlink(resFile);
44
+ return result;
45
+ }
46
+ function validateScript(script, allowUnsafe = false) {
47
+ const MAX_SCRIPT_SIZE = 500 * 1024; // 500KB
48
+ if (Buffer.byteLength(script, "utf-8") > MAX_SCRIPT_SIZE) {
49
+ throw new Error("Script exceeds 500KB size limit");
50
+ }
51
+ if (allowUnsafe)
52
+ return;
53
+ // Block dangerous patterns in user-provided parameters
54
+ // Note: we don't block these in our own generated code, only check for injection
55
+ const dangerousPatterns = [
56
+ /\beval\s*\(/,
57
+ /\bnew\s+Function\s*\(/,
58
+ /\bSystem\s*\.\s*callSystem\s*\(/,
59
+ ];
60
+ for (const pattern of dangerousPatterns) {
61
+ if (pattern.test(script)) {
62
+ throw new Error(`Script contains blocked pattern: ${pattern.source}`);
63
+ }
64
+ }
65
+ }
66
+ /**
67
+ * Send a raw/custom ExtendScript allowing all patterns (for LLM-authored scripts).
68
+ * Still enforces size limit. The script should already include helpers via buildToolScript.
69
+ */
70
+ export async function sendRawCommand(script, options) {
71
+ const tempDir = getTempDir(options);
72
+ const timeoutMs = options?.timeoutMs || DEFAULT_TIMEOUT_MS;
73
+ ensureDir(tempDir);
74
+ const id = `${Date.now()}_${++commandCounter}`;
75
+ const cmdFile = join(tempDir, `cmd_${id}.jsx`);
76
+ const resFile = join(tempDir, `res_${id}.json`);
77
+ validateScript(script, true);
78
+ const cmdTmp = join(tempDir, `tmp_${id}.jsx`);
79
+ writeFileSync(cmdTmp, script, "utf-8");
80
+ renameSync(cmdTmp, cmdFile);
81
+ const result = await pollForResponse(resFile, timeoutMs);
82
+ safeUnlink(cmdFile);
83
+ safeUnlink(resFile);
84
+ return result;
85
+ }
86
+ async function pollForResponse(resFile, timeoutMs) {
87
+ const start = Date.now();
88
+ return new Promise((resolve, reject) => {
89
+ const check = () => {
90
+ if (existsSync(resFile)) {
91
+ try {
92
+ const raw = readFileSync(resFile, "utf-8");
93
+ const result = JSON.parse(raw);
94
+ resolve(result);
95
+ }
96
+ catch (e) {
97
+ resolve({
98
+ success: false,
99
+ error: `Failed to parse response: ${e instanceof Error ? e.message : String(e)}`,
100
+ });
101
+ }
102
+ return;
103
+ }
104
+ if (Date.now() - start > timeoutMs) {
105
+ resolve({
106
+ success: false,
107
+ error: `Command timed out after ${timeoutMs}ms. Is the CEP plugin running in Premiere Pro?`,
108
+ });
109
+ return;
110
+ }
111
+ setTimeout(check, POLL_INTERVAL_MS);
112
+ };
113
+ check();
114
+ });
115
+ }
116
+ function safeUnlink(path) {
117
+ try {
118
+ if (existsSync(path)) {
119
+ unlinkSync(path);
120
+ }
121
+ }
122
+ catch {
123
+ // Ignore cleanup errors
124
+ }
125
+ }
126
+ /**
127
+ * Clean up any stale command/response files from the temp directory.
128
+ */
129
+ export function cleanupTempDir(options) {
130
+ const tempDir = getTempDir(options);
131
+ if (!existsSync(tempDir))
132
+ return;
133
+ try {
134
+ const files = readdirSync(tempDir);
135
+ for (const file of files) {
136
+ if (file.startsWith("cmd_") || file.startsWith("res_") || file.startsWith("tmp_")) {
137
+ safeUnlink(join(tempDir, file));
138
+ }
139
+ }
140
+ }
141
+ catch {
142
+ // Ignore cleanup errors
143
+ }
144
+ }
145
+ //# sourceMappingURL=file-bridge.js.map
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Builds ExtendScript strings with helper functions prepended.
3
+ * All generated code must be ES3-compatible (var, no arrow functions, no let/const).
4
+ */
5
+ /**
6
+ * Build a complete ExtendScript by wrapping user code in an IIFE with helpers.
7
+ */
8
+ export declare function buildScript(code: string): string;
9
+ /**
10
+ * Escape a string for safe embedding in ExtendScript.
11
+ */
12
+ export declare function escapeForExtendScript(value: string): string;
13
+ /**
14
+ * Build a script that wraps code returning a value.
15
+ * The code should use `return __result(...)` or `return __error(...)`.
16
+ * @deprecated Use buildScript() directly. This is an alias kept for backward compatibility.
17
+ */
18
+ export declare const buildToolScript: typeof buildScript;
19
+ //# sourceMappingURL=script-builder.d.ts.map
@@ -0,0 +1,408 @@
1
+ /**
2
+ * Builds ExtendScript strings with helper functions prepended.
3
+ * All generated code must be ES3-compatible (var, no arrow functions, no let/const).
4
+ */
5
+ const HELPERS = `
6
+ // === MCP Bridge Helpers (auto-prepended) ===
7
+
8
+ var TICKS_PER_SECOND = 254016000000;
9
+
10
+ function __ticksToSeconds(ticks) {
11
+ return parseFloat(ticks) / TICKS_PER_SECOND;
12
+ }
13
+
14
+ function __secondsToTicks(seconds) {
15
+ return Math.round(parseFloat(seconds) * TICKS_PER_SECOND);
16
+ }
17
+
18
+ function __ticksToTimecode(ticks, fps) {
19
+ var totalSeconds = __ticksToSeconds(ticks);
20
+ var hours = Math.floor(totalSeconds / 3600);
21
+ var minutes = Math.floor((totalSeconds % 3600) / 60);
22
+ var secs = Math.floor(totalSeconds % 60);
23
+ var frames = Math.floor((totalSeconds % 1) * fps);
24
+ return __pad(hours) + ":" + __pad(minutes) + ":" + __pad(secs) + ":" + __pad(frames);
25
+ }
26
+
27
+ function __pad(n) {
28
+ return n < 10 ? "0" + n : "" + n;
29
+ }
30
+
31
+ function __findSequence(idOrName) {
32
+ var project = app.project;
33
+ for (var i = 0; i < project.sequences.numSequences; i++) {
34
+ var seq = project.sequences[i];
35
+ if (seq.sequenceID === idOrName || seq.name === idOrName) {
36
+ return seq;
37
+ }
38
+ }
39
+ return null;
40
+ }
41
+
42
+ function __findProjectItem(nodeIdOrName, rootItem) {
43
+ if (!rootItem) rootItem = app.project.rootItem;
44
+ for (var i = 0; i < rootItem.children.numItems; i++) {
45
+ var item = rootItem.children[i];
46
+ if (item.nodeId === nodeIdOrName || item.name === nodeIdOrName) {
47
+ return item;
48
+ }
49
+ if (item.type === 2) { // Bin
50
+ var found = __findProjectItem(nodeIdOrName, item);
51
+ if (found) return found;
52
+ }
53
+ }
54
+ return null;
55
+ }
56
+
57
+ function __findClip(nodeId) {
58
+ var seq = app.project.activeSequence;
59
+ if (!seq) return null;
60
+
61
+ // Search video tracks
62
+ for (var t = 0; t < seq.videoTracks.numTracks; t++) {
63
+ var track = seq.videoTracks[t];
64
+ for (var c = 0; c < track.clips.numItems; c++) {
65
+ var clip = track.clips[c];
66
+ if (clip.nodeId === nodeId) {
67
+ return { clip: clip, trackIndex: t, clipIndex: c, trackType: "video" };
68
+ }
69
+ }
70
+ }
71
+
72
+ // Search audio tracks
73
+ for (var t = 0; t < seq.audioTracks.numTracks; t++) {
74
+ var track = seq.audioTracks[t];
75
+ for (var c = 0; c < track.clips.numItems; c++) {
76
+ var clip = track.clips[c];
77
+ if (clip.nodeId === nodeId) {
78
+ return { clip: clip, trackIndex: t, clipIndex: c, trackType: "audio" };
79
+ }
80
+ }
81
+ }
82
+
83
+ return null;
84
+ }
85
+
86
+ function __getAllClips(seq) {
87
+ if (!seq) seq = app.project.activeSequence;
88
+ if (!seq) return [];
89
+ var clips = [];
90
+
91
+ for (var t = 0; t < seq.videoTracks.numTracks; t++) {
92
+ var track = seq.videoTracks[t];
93
+ for (var c = 0; c < track.clips.numItems; c++) {
94
+ var clip = track.clips[c];
95
+ clips.push({
96
+ nodeId: clip.nodeId,
97
+ name: clip.name,
98
+ trackIndex: t,
99
+ trackType: "video",
100
+ inPoint: __ticksToSeconds(clip.inPoint.ticks),
101
+ outPoint: __ticksToSeconds(clip.outPoint.ticks),
102
+ start: __ticksToSeconds(clip.start.ticks),
103
+ end: __ticksToSeconds(clip.end.ticks),
104
+ duration: __ticksToSeconds(clip.duration.ticks),
105
+ mediaType: clip.mediaType
106
+ });
107
+ }
108
+ }
109
+
110
+ for (var t = 0; t < seq.audioTracks.numTracks; t++) {
111
+ var track = seq.audioTracks[t];
112
+ for (var c = 0; c < track.clips.numItems; c++) {
113
+ var clip = track.clips[c];
114
+ clips.push({
115
+ nodeId: clip.nodeId,
116
+ name: clip.name,
117
+ trackIndex: t,
118
+ trackType: "audio",
119
+ inPoint: __ticksToSeconds(clip.inPoint.ticks),
120
+ outPoint: __ticksToSeconds(clip.outPoint.ticks),
121
+ start: __ticksToSeconds(clip.start.ticks),
122
+ end: __ticksToSeconds(clip.end.ticks),
123
+ duration: __ticksToSeconds(clip.duration.ticks),
124
+ mediaType: clip.mediaType
125
+ });
126
+ }
127
+ }
128
+
129
+ return clips;
130
+ }
131
+
132
+ // Recursively coerce undefined to null in an object tree so that the result
133
+ // can be passed to ExtendScript's JSON.stringify safely. ExtendScript's
134
+ // JSON.stringify (a) does not honor the 'replacer' parameter and (b) emits the
135
+ // bare token 'undefined' for undefined values, which is invalid JSON and
136
+ // crashes the CEP-side JSON.parse. Pre-sanitizing is the only reliable fix.
137
+ function __sanitizeForJSON(obj) {
138
+ if (obj === undefined || obj === null) return null;
139
+ // ExtendScript also emits NaN / Infinity literals which are not valid JSON.
140
+ // Coerce them to null so the CEP-side JSON.parse always succeeds.
141
+ if (typeof obj === "number") {
142
+ if (isNaN(obj) || obj === Infinity || obj === -Infinity) return null;
143
+ return obj;
144
+ }
145
+ if (typeof obj !== "object") return obj;
146
+ if (obj instanceof Array) {
147
+ var arr = [];
148
+ for (var i = 0; i < obj.length; i++) arr.push(__sanitizeForJSON(obj[i]));
149
+ return arr;
150
+ }
151
+ // Don't recurse into Premiere host objects (Time, ProjectItem, etc.) — they
152
+ // serialize as [object Object] and have inaccessible internals. Detect them
153
+ // by lack of plain-object constructor.
154
+ try {
155
+ if (obj.constructor && obj.constructor !== Object && obj.constructor !== Array) {
156
+ // For host objects, fall back to their toString / accessible primitive
157
+ return obj.toString ? String(obj.toString()) : "[object]";
158
+ }
159
+ } catch (e) { /* fall through */ }
160
+ var out = {};
161
+ for (var k in obj) {
162
+ if (obj.hasOwnProperty(k)) out[k] = __sanitizeForJSON(obj[k]);
163
+ }
164
+ return out;
165
+ }
166
+
167
+ function __jsonStringify(obj) {
168
+ // ES3-compatible JSON stringify. ExtendScript's JSON.stringify emits the
169
+ // literal token 'undefined' for undefined values (vs Node/Browser which omit
170
+ // them) AND ignores the replacer parameter, making the produced string
171
+ // invalid JSON and crashing the CEP-side JSON.parse. Sanitize first.
172
+ if (typeof JSON !== "undefined" && JSON.stringify) {
173
+ return JSON.stringify(__sanitizeForJSON(obj));
174
+ }
175
+ // Fallback for very old ExtendScript
176
+ if (obj === null) return "null";
177
+ if (obj === undefined) return "undefined";
178
+ if (typeof obj === "string") return '"' + obj.replace(/\\\\/g, "\\\\\\\\").replace(/"/g, '\\\\"').replace(/\\n/g, "\\\\n") + '"';
179
+ if (typeof obj === "number" || typeof obj === "boolean") return String(obj);
180
+ if (obj instanceof Array) {
181
+ var arr = [];
182
+ for (var i = 0; i < obj.length; i++) {
183
+ arr.push(__jsonStringify(obj[i]));
184
+ }
185
+ return "[" + arr.join(",") + "]";
186
+ }
187
+ if (typeof obj === "object") {
188
+ var parts = [];
189
+ for (var k in obj) {
190
+ if (obj.hasOwnProperty(k)) {
191
+ parts.push(__jsonStringify(k) + ":" + __jsonStringify(obj[k]));
192
+ }
193
+ }
194
+ return "{" + parts.join(",") + "}";
195
+ }
196
+ return String(obj);
197
+ }
198
+
199
+ function __result(data) {
200
+ return __jsonStringify({ success: true, data: data });
201
+ }
202
+
203
+ function __error(msg) {
204
+ return __jsonStringify({ success: false, error: String(msg) });
205
+ }
206
+
207
+ // --- Locale & version resilient lookups -------------------------------------
208
+
209
+ // Candidate matchNames for Premiere's intrinsic (fixed) effects. matchName is
210
+ // locale-INDEPENDENT, so trying these first makes tools work on any language
211
+ // install. displayName fallbacks are language-specific and only a last resort.
212
+ var __INTRINSIC = {
213
+ motion: { match: ["AE.ADBE Motion", "ADBE Motion", "motionControlEffect"], display: ["Motion", "Movimiento", "Bewegung", "Mouvement"] },
214
+ opacity: { match: ["AE.ADBE Opacity", "ADBE Opacity", "opacityEffect"], display: ["Opacity", "Opacidad", "Deckkraft", "Opacité"] },
215
+ volume: { match: ["AE.ADBE Audio Levels", "audioVolume", "audioGain", "ADBE Audio Levels"], display: ["Volume", "Volumen", "Lautstärke"] },
216
+ panner: { match: ["AE.ADBE Pan", "audioPanner", "ADBE Pan"], display: ["Panner", "Panoramización", "Balance"] },
217
+ lumetri: { match: ["AE.ADBE Lumetri", "ADBE Lumetri", "PR.ADBE Lumetri"], display: ["Lumetri Color", "Color Lumetri", "Lumetri-Farbe"] },
218
+ timeRemap:{ match: ["AE.ADBE Time Remapping", "ADBE Time Remapping"], display: ["Time Remapping", "Reasignación de tiempo"] }
219
+ };
220
+
221
+ // Find a component on a clip by trying matchName candidates then displayName candidates.
222
+ // matchNames/displayNames are arrays of strings. Returns the component or null.
223
+ function __findComponent(clip, matchNames, displayNames) {
224
+ if (!clip || !clip.components) return null;
225
+ var i, c, k;
226
+ if (matchNames) {
227
+ for (i = 0; i < clip.components.numItems; i++) {
228
+ c = clip.components[i];
229
+ for (k = 0; k < matchNames.length; k++) {
230
+ if (c.matchName === matchNames[k]) return c;
231
+ }
232
+ }
233
+ }
234
+ if (displayNames) {
235
+ for (i = 0; i < clip.components.numItems; i++) {
236
+ c = clip.components[i];
237
+ for (k = 0; k < displayNames.length; k++) {
238
+ if (c.displayName === displayNames[k]) return c;
239
+ }
240
+ }
241
+ }
242
+ return null;
243
+ }
244
+
245
+ // Shorthand: find an intrinsic component by key from the __INTRINSIC table.
246
+ function __findIntrinsic(clip, key) {
247
+ var def = __INTRINSIC[key];
248
+ if (!def) return null;
249
+ return __findComponent(clip, def.match, def.display);
250
+ }
251
+
252
+ // Find a property inside a component by displayName candidates, descending one
253
+ // level into nested property groups (needed for Lumetri's grouped params).
254
+ function __findProp(comp, names) {
255
+ if (!comp || !comp.properties) return null;
256
+ var p, n, prop, sp, sub;
257
+ for (p = 0; p < comp.properties.numItems; p++) {
258
+ prop = comp.properties[p];
259
+ for (n = 0; n < names.length; n++) {
260
+ if (prop.displayName === names[n]) return prop;
261
+ }
262
+ }
263
+ // second pass: nested groups
264
+ for (p = 0; p < comp.properties.numItems; p++) {
265
+ prop = comp.properties[p];
266
+ if (prop.properties && prop.properties.numItems) {
267
+ for (sp = 0; sp < prop.properties.numItems; sp++) {
268
+ sub = prop.properties[sp];
269
+ for (n = 0; n < names.length; n++) {
270
+ if (sub.displayName === names[n]) return sub;
271
+ }
272
+ }
273
+ }
274
+ }
275
+ return null;
276
+ }
277
+
278
+ // Locale-aware aliases for common Premiere Pro transition names. matchName
279
+ // would be ideal but QE's transition objects don't reliably expose it, so we
280
+ // map the English canonical name to localized displayNames seen in the wild.
281
+ var __TRANSITION_ALIASES = {
282
+ "Cross Dissolve": ["Cross Dissolve", "Disolvencia cruzada", "Fondu enchaîné", "Weiche Blende"],
283
+ "Dip to Black": ["Dip to Black", "Inmersión a negro", "Fondu au noir", "Schwarzblende"],
284
+ "Dip to White": ["Dip to White", "Inmersión a blanco", "Fondu au blanc", "Weißblende"],
285
+ "Film Dissolve": ["Film Dissolve", "Disolvencia de película"],
286
+ "Additive Dissolve": ["Additive Dissolve", "Disolvencia aditiva"],
287
+ "Morph Cut": ["Morph Cut", "Corte por transformación"],
288
+ "Constant Power": ["Constant Power", "Potencia constante"],
289
+ "Constant Gain": ["Constant Gain", "Ganancia constante"],
290
+ "Exponential Fade": ["Exponential Fade", "Fundido exponencial"]
291
+ };
292
+
293
+ // Resolve a transition by name from a QE transition list, locale-resilient.
294
+ // First tries qe.project.getVideoTransitionByName / getAudioTransitionByName,
295
+ // then iterates the list matching the requested name OR any localized alias.
296
+ // kind: "video" | "audio". Returns the QE transition object or null.
297
+ function __findTransition(name, kind) {
298
+ try { app.enableQE(); } catch (e) {}
299
+ if (typeof qe === "undefined" || !qe.project) return null;
300
+ var direct = null;
301
+ try {
302
+ if (kind === "audio" && qe.project.getAudioTransitionByName) {
303
+ direct = qe.project.getAudioTransitionByName(name);
304
+ } else if (qe.project.getVideoTransitionByName) {
305
+ direct = qe.project.getVideoTransitionByName(name);
306
+ }
307
+ } catch (e) {}
308
+ if (direct) return direct;
309
+ var list = null;
310
+ try {
311
+ list = (kind === "audio") ? qe.project.getAudioTransitionList() : qe.project.getVideoTransitionList();
312
+ } catch (e) { list = null; }
313
+ if (!list || !list.numItems) return null;
314
+ // Candidate names: the requested name, plus aliases for it (forward map),
315
+ // plus the canonical name if the request matches any alias (reverse map).
316
+ var candidates = [name];
317
+ var k, j, aliases;
318
+ if (__TRANSITION_ALIASES[name]) {
319
+ aliases = __TRANSITION_ALIASES[name];
320
+ for (j = 0; j < aliases.length; j++) candidates.push(aliases[j]);
321
+ }
322
+ for (k in __TRANSITION_ALIASES) {
323
+ if (__TRANSITION_ALIASES.hasOwnProperty(k)) {
324
+ aliases = __TRANSITION_ALIASES[k];
325
+ for (j = 0; j < aliases.length; j++) {
326
+ if (aliases[j] === name) {
327
+ candidates.push(k);
328
+ for (var jj = 0; jj < aliases.length; jj++) candidates.push(aliases[jj]);
329
+ }
330
+ }
331
+ }
332
+ }
333
+ for (var i = 0; i < list.numItems; i++) {
334
+ var t = list[i];
335
+ var tName = t.name;
336
+ var tMatch = t.matchName;
337
+ for (var c = 0; c < candidates.length; c++) {
338
+ if (tName === candidates[c]) return t;
339
+ if (tMatch && tMatch === candidates[c]) return t;
340
+ }
341
+ }
342
+ return null;
343
+ }
344
+
345
+ // Resolve an ID that could be either a project item nodeId/name OR a timeline
346
+ // clip nodeId, to a ProjectItem. Used by tools whose schema says "project item
347
+ // node_id or name" but where callers commonly pass timeline clip nodeIds
348
+ // (which come from get_active_sequence / list_sequence_tracks).
349
+ // Returns the ProjectItem or null.
350
+ function __resolveProjectItemOrTimelineClip(idOrName) {
351
+ if (!idOrName) return null;
352
+ // First try the project tree
353
+ var pi = __findProjectItem(idOrName);
354
+ if (pi) return pi;
355
+ // Fall back: treat as timeline clip nodeId, resolve to its projectItem
356
+ try {
357
+ var r = __findClip(idOrName);
358
+ if (r && r.clip && r.clip.projectItem) return r.clip.projectItem;
359
+ } catch (e) {}
360
+ return null;
361
+ }
362
+
363
+ // Read a member that some Premiere versions expose as a property and others as
364
+ // a zero-arg method (e.g. isMuted, isSelected, isOffline, isSequence). Returns
365
+ // the resolved value, or the provided default if it cannot be read.
366
+ function __readMaybe(obj, name, dflt) {
367
+ try {
368
+ var v = obj[name];
369
+ if (typeof v === "function") return v.call(obj);
370
+ if (v === undefined) return dflt;
371
+ return v;
372
+ } catch(e) { return dflt; }
373
+ }
374
+
375
+ // === End MCP Bridge Helpers ===
376
+ `;
377
+ /**
378
+ * Build a complete ExtendScript by wrapping user code in an IIFE with helpers.
379
+ */
380
+ export function buildScript(code) {
381
+ return `${HELPERS}
382
+ (function() {
383
+ try {
384
+ ${code}
385
+ } catch(e) {
386
+ return __error(e.toString());
387
+ }
388
+ })();`;
389
+ }
390
+ /**
391
+ * Escape a string for safe embedding in ExtendScript.
392
+ */
393
+ export function escapeForExtendScript(value) {
394
+ return value
395
+ .replace(/\\/g, "\\\\")
396
+ .replace(/"/g, '\\"')
397
+ .replace(/'/g, "\\'")
398
+ .replace(/\n/g, "\\n")
399
+ .replace(/\r/g, "\\r")
400
+ .replace(/\t/g, "\\t");
401
+ }
402
+ /**
403
+ * Build a script that wraps code returning a value.
404
+ * The code should use `return __result(...)` or `return __error(...)`.
405
+ * @deprecated Use buildScript() directly. This is an alias kept for backward compatibility.
406
+ */
407
+ export const buildToolScript = buildScript;
408
+ //# sourceMappingURL=script-builder.js.map
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * HTTP/SSE transport entry point for remote deployment (e.g. Fly.io).
4
+ *
5
+ * The MCP server is identical to the stdio version — only the transport differs.
6
+ * Clients connect via the MCP Streamable HTTP transport:
7
+ * POST /mcp — send JSON-RPC messages
8
+ * GET /mcp — open SSE stream
9
+ *
10
+ * The bridge still uses the local filesystem temp directory, so the CEP plugin
11
+ * must be reachable from the same machine OR you must set PREMIERE_TEMP_DIR to
12
+ * a shared volume mount that the CEP plugin also writes to.
13
+ *
14
+ * Environment variables:
15
+ * PORT HTTP port to listen on (default: 3000)
16
+ * PREMIERE_TEMP_DIR Shared temp directory for the file bridge
17
+ * PREMIERE_TIMEOUT_MS Command timeout in ms (default: 30000)
18
+ * MCP_AUTH_TOKEN Optional bearer token to require on all requests
19
+ */
20
+ export {};
21
+ //# sourceMappingURL=http-server.d.ts.map