@synthetic-ai/premiere-mcp 2.2.0 → 2.3.0-dev.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 (57) hide show
  1. package/CHANGELOG.md +135 -135
  2. package/LICENSE +21 -21
  3. package/README.md +242 -242
  4. package/cep-plugin/.debug +8 -8
  5. package/cep-plugin/CSInterface.js +92 -92
  6. package/cep-plugin/host.jsx +7 -7
  7. package/cep-plugin/index.html +472 -472
  8. package/cep-plugin/main.js +6 -0
  9. package/cep-plugin/premiere.jsx +24947 -24947
  10. package/dist/bridge/script-builder.js +378 -378
  11. package/dist/license.js +38 -0
  12. package/dist/server.js +60 -60
  13. package/dist/tools/advanced.js +357 -357
  14. package/dist/tools/audio-advanced.js +1049 -1049
  15. package/dist/tools/audio.js +42 -42
  16. package/dist/tools/captions.js +27 -27
  17. package/dist/tools/clipboard.js +214 -214
  18. package/dist/tools/color-advanced.js +253 -253
  19. package/dist/tools/discovery.js +291 -291
  20. package/dist/tools/effects.js +285 -285
  21. package/dist/tools/export-presets.js +206 -206
  22. package/dist/tools/export.js +255 -255
  23. package/dist/tools/health.js +10 -10
  24. package/dist/tools/inspection.js +885 -885
  25. package/dist/tools/keyframes.js +369 -369
  26. package/dist/tools/markers.js +89 -89
  27. package/dist/tools/media.js +178 -178
  28. package/dist/tools/metadata.js +94 -94
  29. package/dist/tools/multicam.js +50 -50
  30. package/dist/tools/playback.js +15 -15
  31. package/dist/tools/playhead.js +95 -95
  32. package/dist/tools/project-manager.js +21 -21
  33. package/dist/tools/project.js +149 -149
  34. package/dist/tools/scripting.js +319 -319
  35. package/dist/tools/selection.js +202 -202
  36. package/dist/tools/sequence-advanced.js +379 -379
  37. package/dist/tools/sequence-settings.js +564 -564
  38. package/dist/tools/sequence.js +167 -167
  39. package/dist/tools/source-monitor.js +68 -68
  40. package/dist/tools/text.js +58 -58
  41. package/dist/tools/timeline.js +312 -312
  42. package/dist/tools/track-management.js +446 -446
  43. package/dist/tools/track-targeting.js +635 -635
  44. package/dist/tools/tracks.js +87 -87
  45. package/dist/tools/transitions-motion.js +174 -174
  46. package/dist/tools/transitions.js +111 -111
  47. package/dist/tools/utility.js +606 -606
  48. package/dist/tools/workspace.js +29 -29
  49. package/package.json +66 -66
  50. package/scripts/build-chat-dmg.sh +210 -210
  51. package/scripts/build-chat-zip.sh +291 -291
  52. package/scripts/install-cep-windows.ps1 +99 -99
  53. package/scripts/install-cep.sh +66 -66
  54. package/scripts/install-chat-plugin.bat +67 -67
  55. package/scripts/install-chat-plugin.sh +85 -85
  56. package/scripts/install-windows.bat +95 -95
  57. package/scripts/package.mjs +254 -254
@@ -1,254 +1,254 @@
1
- /**
2
- * Synthetic Premiere Pro MCP — Build & Package
3
- *
4
- * Produces a distributable, IP-protected release in ./release :
5
- * release/
6
- * server/index.mjs bundled + minified (+ obfuscated) MCP server
7
- * server/package.json minimal ESM marker
8
- * MCPBridgeCEP.zxp signed CEP plugin (obfuscated main.js, verbatim premiere.jsx, no .debug)
9
- * install/ install scripts
10
- * README.md LICENSE
11
- * synthetic-premiere-mcp-v{VERSION}.zip
12
- *
13
- * Usage: node scripts/package.mjs
14
- * Env: ZXP_SIGN_CMD (path to ZXPSignCmd.exe), ZXP_CERT_PASSWORD (default "synthetic")
15
- */
16
- import {
17
- readFileSync, writeFileSync, mkdirSync, copyFileSync, existsSync,
18
- rmSync, readdirSync, statSync,
19
- } from "fs";
20
- import { join, dirname } from "path";
21
- import { fileURLToPath } from "url";
22
- import { execSync, spawnSync } from "child_process";
23
- import { build as esbuild } from "esbuild";
24
- import JavaScriptObfuscator from "javascript-obfuscator";
25
-
26
- const __dirname = dirname(fileURLToPath(import.meta.url));
27
- const ROOT = join(__dirname, "..");
28
- const REL = join(ROOT, "release");
29
- const pkg = JSON.parse(readFileSync(join(ROOT, "package.json"), "utf-8"));
30
- const V = pkg.version;
31
-
32
- console.log(`\nšŸ”Ø Packaging Synthetic Premiere MCP v${V}\n`);
33
-
34
- // ─── 1. Clean ────────────────────────────────────────────────────────────────
35
- if (existsSync(REL)) rmSync(REL, { recursive: true, force: true });
36
- mkdirSync(join(REL, "server"), { recursive: true });
37
- console.log("āœ“ Cleaned release/");
38
-
39
- // ─── 2. Compile TS, then bundle with esbuild (single minified ESM file) ──────
40
- console.log("āš™ tsc build...");
41
- execSync("npm run build", { cwd: ROOT, stdio: "pipe" });
42
- const serverOut = join(REL, "server", "index.mjs");
43
- await esbuild({
44
- entryPoints: [join(ROOT, "dist", "index.js")],
45
- bundle: true,
46
- platform: "node",
47
- format: "esm",
48
- target: "node18",
49
- minify: true,
50
- legalComments: "none",
51
- outfile: serverOut,
52
- });
53
- const bundledSize = (statSync(serverOut).size / 1024).toFixed(1);
54
- console.log(`āœ“ Bundled server → release/server/index.mjs (${bundledSize} KB)`);
55
-
56
- // ─── 3. Obfuscate the bundle (conservative; falls back to minify-only) ───────
57
- function validateServer(file) {
58
- // Start the server; it must print "Registered 437 tools" on stderr then be ready.
59
- const r = spawnSync(process.execPath, [file], {
60
- input: "",
61
- timeout: 8000,
62
- encoding: "utf-8",
63
- env: { ...process.env },
64
- });
65
- const out = (r.stderr || "") + (r.stdout || "");
66
- const m = out.match(/Registered (\d+) tools/);
67
- return m ? parseInt(m[1], 10) : 0;
68
- }
69
-
70
- const minifiedCode = readFileSync(serverOut, "utf-8");
71
- let serverProtected = "minify-only";
72
- try {
73
- console.log("šŸ”’ Obfuscating server bundle...");
74
- // Strip the shebang before obfuscating; re-add after.
75
- const noShebang = minifiedCode.replace(/^#![^\n]*\n/, "");
76
- const obf = JavaScriptObfuscator.obfuscate(noShebang, {
77
- compact: true,
78
- target: "node",
79
- controlFlowFlattening: false,
80
- deadCodeInjection: false,
81
- debugProtection: false,
82
- disableConsoleOutput: false,
83
- identifierNamesGenerator: "hexadecimal",
84
- numbersToExpressions: false,
85
- renameGlobals: false,
86
- selfDefending: false,
87
- simplify: true,
88
- splitStrings: false,
89
- stringArray: true,
90
- stringArrayEncoding: ["base64"],
91
- stringArrayThreshold: 0.75,
92
- unicodeEscapeSequence: false,
93
- }).getObfuscatedCode();
94
- writeFileSync(serverOut, "#!/usr/bin/env node\n" + obf);
95
-
96
- const n = validateServer(serverOut);
97
- if (n >= 400) {
98
- serverProtected = "obfuscated";
99
- console.log(`āœ“ Obfuscated server validated (registered ${n} tools)`);
100
- } else {
101
- throw new Error(`obfuscated server registered ${n} tools (expected 437)`);
102
- }
103
- } catch (e) {
104
- console.warn(` ⚠ Obfuscation rejected (${e.message}). Falling back to minify-only.`);
105
- writeFileSync(serverOut, minifiedCode);
106
- const n = validateServer(serverOut);
107
- if (n < 400) {
108
- console.error(`āœ— Minified server also failed to register tools (${n}). Aborting.`);
109
- process.exit(1);
110
- }
111
- console.log(`āœ“ Minified server validated (registered ${n} tools)`);
112
- }
113
- writeFileSync(join(REL, "server", "package.json"),
114
- JSON.stringify({ name: "synthetic-premiere-mcp", version: V, type: "module", private: true }, null, 2));
115
-
116
- // ─── 4. Stage the CEP plugin (obfuscate premiere.jsx + main.js, strip .debug) ─
117
- const CEP_SRC = join(ROOT, "cep-plugin");
118
- const CEP_STAGE = join(REL, "_cepstage");
119
- copyDirSync(CEP_SRC, CEP_STAGE);
120
-
121
- // 4a. Strip the dev debug file (opens a CEF debug port — must not ship).
122
- const dbg = join(CEP_STAGE, ".debug");
123
- if (existsSync(dbg)) { rmSync(dbg); console.log("āœ“ Stripped cep .debug"); }
124
-
125
- // 4b. Stamp version into the staged manifest.
126
- const mxPath = join(CEP_STAGE, "CSXS", "manifest.xml");
127
- let mx = readFileSync(mxPath, "utf-8")
128
- .replace(/ExtensionBundleVersion="[^"]*"/, `ExtensionBundleVersion="${V}"`)
129
- .replace(/(<Extension Id="[^"]*" Version=")[^"]*(")/, `$1${V}$2`);
130
- writeFileSync(mxPath, mx);
131
-
132
- // 4b2. Stamp version into main.js's own PANEL_VERSION constant. This is
133
- // what checkForUpdates() actually compares against the backend manifest —
134
- // the manifest.xml stamp above is cosmetic. Without this, the hand-
135
- // maintained literal silently drifts from package.json (found 2026-09-23
136
- // in the sibling AE MCP repo: main.js was stuck one release behind for a
137
- // while), which makes every future release re-offer the SAME update
138
- // forever, even after a customer successfully applies it. Must run before
139
- // 4c obfuscates main.js.
140
- const preObfMainJsPath = join(CEP_STAGE, "main.js");
141
- if (existsSync(preObfMainJsPath)) {
142
- const mainSrc = readFileSync(preObfMainJsPath, "utf-8");
143
- const versionRe = /var PANEL_VERSION = "[^"]*";/;
144
- if (!versionRe.test(mainSrc)) {
145
- console.error("āœ— Could not find PANEL_VERSION literal in main.js to stamp — aborting so a stale version never ships silently.");
146
- process.exit(1);
147
- }
148
- writeFileSync(preObfMainJsPath, mainSrc.replace(versionRe, `var PANEL_VERSION = "${V}";`));
149
- console.log(`āœ“ Stamped version ${V} into main.js PANEL_VERSION`);
150
- }
151
-
152
- // 4c. Obfuscate the panel logic (main.js only). ExtendScript-safe settings:
153
- // renameGlobals MUST stay false (helper fns are called by name), no control-flow
154
- // flattening / selfDefending (incompatible with the ExtendScript engine).
155
- // stringArray is INTENTIONALLY OFF: it deadlocks the ExtendScript engine at
156
- // ~100% CPU on Premiere/AE 2026 26.3+ the moment the obfuscated code actually
157
- // runs in ExtendScript. Found 2026-09-22 auditing every Synthetic CEP product
158
- // after the identical bug (same obfuscation profile) shipped in AE Agent's
159
- // tools.jsx and hung a paying customer's After Effects -- see
160
- // synthetic-ae-agent CHANGELOG.md v1.9.10 and synthetic-autoedit-pro v1.0.1.
161
- //
162
- // premiere.jsx is shipped UNOBFUSCATED, same as host.jsx. Not a stylistic
163
- // choice: obfuscating it (even with stringArray off) made Premiere's
164
- // ExtendScript engine fail evalFile() outright with "EvalScript error." —
165
- // no hang, just a flat parse/load failure — on Premiere Pro 26.5.1, most
166
- // likely a size ceiling (this file is 25k+ lines / 437 tools, an order of
167
- // magnitude bigger than ae-bridge.jsx or AE Agent's tools.jsx, both of
168
- // which obfuscate fine). Verified live 2026-09-23: identical file loads
169
- // "OK" unobfuscated, fails obfuscated, nothing else changed. IP exposure
170
- // here is the accepted tradeoff for the panel actually working — same
171
- // precedent as ae-bridge.jsx above.
172
- const ES_SAFE = {
173
- compact: true,
174
- controlFlowFlattening: false,
175
- deadCodeInjection: false,
176
- debugProtection: false,
177
- disableConsoleOutput: false,
178
- identifierNamesGenerator: "hexadecimal",
179
- numbersToExpressions: false,
180
- renameGlobals: false,
181
- selfDefending: false,
182
- simplify: true,
183
- splitStrings: false,
184
- stringArray: false, // DEADLOCKS ExtendScript on Premiere/AE 2026 26.3+ — do not re-enable
185
- unicodeEscapeSequence: false,
186
- };
187
- for (const f of ["main.js"]) {
188
- const p = join(CEP_STAGE, f);
189
- if (!existsSync(p)) continue;
190
- const src = readFileSync(p, "utf-8").replace(/^#!.*\n/, "");
191
- const obf = JavaScriptObfuscator.obfuscate(src, ES_SAFE).getObfuscatedCode();
192
- writeFileSync(p, obf);
193
- console.log(`āœ“ Obfuscated cep/${f} (${(obf.length / 1024).toFixed(1)} KB)`);
194
- }
195
- console.log("āœ“ Shipped cep/premiere.jsx verbatim (no obfuscation — see comment above)");
196
-
197
- // ─── 5. Sign the staged plugin into a .zxp ───────────────────────────────────
198
- const ZXP = process.env.ZXP_SIGN_CMD ||
199
- "C:\\Users\\Artear\\Documents\\CEP-Resources\\ZXPSignCMD\\4.1.3\\x64\\ZXPSignCmd.exe";
200
- const certPath = process.env.ZXP_CERT_PATH || join(ROOT, ".cert", "self-signed.p12");
201
- const certPass = process.env.ZXP_CERT_PASSWORD || "synthetic";
202
- const zxpOut = join(REL, "MCPBridgeCEP.zxp");
203
-
204
- if (!existsSync(certPath)) { console.error(`āœ— Cert not found: ${certPath}`); process.exit(1); }
205
- function sign(withTsa) {
206
- const tsa = withTsa ? ` -tsa "http://timestamp.digicert.com"` : "";
207
- execSync(`"${ZXP}" -sign "${CEP_STAGE}" "${zxpOut}" "${certPath}" "${certPass}"${tsa}`,
208
- { cwd: ROOT, stdio: "pipe" });
209
- }
210
- console.log("šŸ–Š Signing MCPBridgeCEP.zxp...");
211
- try {
212
- try { sign(true); } catch { if (existsSync(zxpOut)) rmSync(zxpOut); console.warn(" ⚠ TSA failed — signing offline."); sign(false); }
213
- console.log(`āœ“ Signed → release/MCPBridgeCEP.zxp (${(statSync(zxpOut).size / 1024).toFixed(1)} KB)`);
214
- } catch (e) {
215
- console.error("āœ— Signing failed:", (e.stderr || e.message || "").toString());
216
- process.exit(1);
217
- }
218
- rmSync(CEP_STAGE, { recursive: true, force: true });
219
-
220
- // ─── 6. Install scripts + docs ───────────────────────────────────────────────
221
- mkdirSync(join(REL, "install"), { recursive: true });
222
- for (const s of ["install-cep-windows.ps1", "install-windows.bat", "install-cep.sh"]) {
223
- const p = join(ROOT, "scripts", s);
224
- if (existsSync(p)) copyFileSync(p, join(REL, "install", s));
225
- }
226
- for (const f of ["LICENSE"]) {
227
- if (existsSync(join(ROOT, f))) copyFileSync(join(ROOT, f), join(REL, f));
228
- }
229
- console.log("āœ“ Copied install scripts + LICENSE");
230
-
231
- // ─── 7. Zip ──────────────────────────────────────────────────────────────────
232
- const zipName = `synthetic-premiere-mcp-v${V}.zip`;
233
- const zipPath = join(ROOT, zipName);
234
- if (existsSync(zipPath)) rmSync(zipPath);
235
- try {
236
- execSync(`powershell -NoProfile -Command "Compress-Archive -Path '${REL}\\*' -DestinationPath '${zipPath}' -Force"`,
237
- { stdio: "pipe" });
238
- console.log(`āœ“ Zipped → ${zipName} (${(statSync(zipPath).size / 1024).toFixed(1)} KB)`);
239
- } catch (e) {
240
- console.warn(" ⚠ Zip step skipped:", (e.stderr || e.message || "").toString().slice(0, 120));
241
- }
242
-
243
- console.log(`\nāœ… Done. Server protection: ${serverProtected}.`);
244
- console.log(` release/ + ${zipName}\n`);
245
-
246
- // ─── helper ──────────────────────────────────────────────────────────────────
247
- function copyDirSync(src, dest) {
248
- mkdirSync(dest, { recursive: true });
249
- for (const entry of readdirSync(src)) {
250
- const s = join(src, entry), d = join(dest, entry);
251
- if (statSync(s).isDirectory()) copyDirSync(s, d);
252
- else copyFileSync(s, d);
253
- }
254
- }
1
+ /**
2
+ * Synthetic Premiere Pro MCP — Build & Package
3
+ *
4
+ * Produces a distributable, IP-protected release in ./release :
5
+ * release/
6
+ * server/index.mjs bundled + minified (+ obfuscated) MCP server
7
+ * server/package.json minimal ESM marker
8
+ * MCPBridgeCEP.zxp signed CEP plugin (obfuscated main.js, verbatim premiere.jsx, no .debug)
9
+ * install/ install scripts
10
+ * README.md LICENSE
11
+ * synthetic-premiere-mcp-v{VERSION}.zip
12
+ *
13
+ * Usage: node scripts/package.mjs
14
+ * Env: ZXP_SIGN_CMD (path to ZXPSignCmd.exe), ZXP_CERT_PASSWORD (default "synthetic")
15
+ */
16
+ import {
17
+ readFileSync, writeFileSync, mkdirSync, copyFileSync, existsSync,
18
+ rmSync, readdirSync, statSync,
19
+ } from "fs";
20
+ import { join, dirname } from "path";
21
+ import { fileURLToPath } from "url";
22
+ import { execSync, spawnSync } from "child_process";
23
+ import { build as esbuild } from "esbuild";
24
+ import JavaScriptObfuscator from "javascript-obfuscator";
25
+
26
+ const __dirname = dirname(fileURLToPath(import.meta.url));
27
+ const ROOT = join(__dirname, "..");
28
+ const REL = join(ROOT, "release");
29
+ const pkg = JSON.parse(readFileSync(join(ROOT, "package.json"), "utf-8"));
30
+ const V = pkg.version;
31
+
32
+ console.log(`\nšŸ”Ø Packaging Synthetic Premiere MCP v${V}\n`);
33
+
34
+ // ─── 1. Clean ────────────────────────────────────────────────────────────────
35
+ if (existsSync(REL)) rmSync(REL, { recursive: true, force: true });
36
+ mkdirSync(join(REL, "server"), { recursive: true });
37
+ console.log("āœ“ Cleaned release/");
38
+
39
+ // ─── 2. Compile TS, then bundle with esbuild (single minified ESM file) ──────
40
+ console.log("āš™ tsc build...");
41
+ execSync("npm run build", { cwd: ROOT, stdio: "pipe" });
42
+ const serverOut = join(REL, "server", "index.mjs");
43
+ await esbuild({
44
+ entryPoints: [join(ROOT, "dist", "index.js")],
45
+ bundle: true,
46
+ platform: "node",
47
+ format: "esm",
48
+ target: "node18",
49
+ minify: true,
50
+ legalComments: "none",
51
+ outfile: serverOut,
52
+ });
53
+ const bundledSize = (statSync(serverOut).size / 1024).toFixed(1);
54
+ console.log(`āœ“ Bundled server → release/server/index.mjs (${bundledSize} KB)`);
55
+
56
+ // ─── 3. Obfuscate the bundle (conservative; falls back to minify-only) ───────
57
+ function validateServer(file) {
58
+ // Start the server; it must print "Registered 437 tools" on stderr then be ready.
59
+ const r = spawnSync(process.execPath, [file], {
60
+ input: "",
61
+ timeout: 8000,
62
+ encoding: "utf-8",
63
+ env: { ...process.env },
64
+ });
65
+ const out = (r.stderr || "") + (r.stdout || "");
66
+ const m = out.match(/Registered (\d+) tools/);
67
+ return m ? parseInt(m[1], 10) : 0;
68
+ }
69
+
70
+ const minifiedCode = readFileSync(serverOut, "utf-8");
71
+ let serverProtected = "minify-only";
72
+ try {
73
+ console.log("šŸ”’ Obfuscating server bundle...");
74
+ // Strip the shebang before obfuscating; re-add after.
75
+ const noShebang = minifiedCode.replace(/^#![^\n]*\n/, "");
76
+ const obf = JavaScriptObfuscator.obfuscate(noShebang, {
77
+ compact: true,
78
+ target: "node",
79
+ controlFlowFlattening: false,
80
+ deadCodeInjection: false,
81
+ debugProtection: false,
82
+ disableConsoleOutput: false,
83
+ identifierNamesGenerator: "hexadecimal",
84
+ numbersToExpressions: false,
85
+ renameGlobals: false,
86
+ selfDefending: false,
87
+ simplify: true,
88
+ splitStrings: false,
89
+ stringArray: true,
90
+ stringArrayEncoding: ["base64"],
91
+ stringArrayThreshold: 0.75,
92
+ unicodeEscapeSequence: false,
93
+ }).getObfuscatedCode();
94
+ writeFileSync(serverOut, "#!/usr/bin/env node\n" + obf);
95
+
96
+ const n = validateServer(serverOut);
97
+ if (n >= 400) {
98
+ serverProtected = "obfuscated";
99
+ console.log(`āœ“ Obfuscated server validated (registered ${n} tools)`);
100
+ } else {
101
+ throw new Error(`obfuscated server registered ${n} tools (expected 437)`);
102
+ }
103
+ } catch (e) {
104
+ console.warn(` ⚠ Obfuscation rejected (${e.message}). Falling back to minify-only.`);
105
+ writeFileSync(serverOut, minifiedCode);
106
+ const n = validateServer(serverOut);
107
+ if (n < 400) {
108
+ console.error(`āœ— Minified server also failed to register tools (${n}). Aborting.`);
109
+ process.exit(1);
110
+ }
111
+ console.log(`āœ“ Minified server validated (registered ${n} tools)`);
112
+ }
113
+ writeFileSync(join(REL, "server", "package.json"),
114
+ JSON.stringify({ name: "synthetic-premiere-mcp", version: V, type: "module", private: true }, null, 2));
115
+
116
+ // ─── 4. Stage the CEP plugin (obfuscate premiere.jsx + main.js, strip .debug) ─
117
+ const CEP_SRC = join(ROOT, "cep-plugin");
118
+ const CEP_STAGE = join(REL, "_cepstage");
119
+ copyDirSync(CEP_SRC, CEP_STAGE);
120
+
121
+ // 4a. Strip the dev debug file (opens a CEF debug port — must not ship).
122
+ const dbg = join(CEP_STAGE, ".debug");
123
+ if (existsSync(dbg)) { rmSync(dbg); console.log("āœ“ Stripped cep .debug"); }
124
+
125
+ // 4b. Stamp version into the staged manifest.
126
+ const mxPath = join(CEP_STAGE, "CSXS", "manifest.xml");
127
+ let mx = readFileSync(mxPath, "utf-8")
128
+ .replace(/ExtensionBundleVersion="[^"]*"/, `ExtensionBundleVersion="${V}"`)
129
+ .replace(/(<Extension Id="[^"]*" Version=")[^"]*(")/, `$1${V}$2`);
130
+ writeFileSync(mxPath, mx);
131
+
132
+ // 4b2. Stamp version into main.js's own PANEL_VERSION constant. This is
133
+ // what checkForUpdates() actually compares against the backend manifest —
134
+ // the manifest.xml stamp above is cosmetic. Without this, the hand-
135
+ // maintained literal silently drifts from package.json (found 2026-09-23
136
+ // in the sibling AE MCP repo: main.js was stuck one release behind for a
137
+ // while), which makes every future release re-offer the SAME update
138
+ // forever, even after a customer successfully applies it. Must run before
139
+ // 4c obfuscates main.js.
140
+ const preObfMainJsPath = join(CEP_STAGE, "main.js");
141
+ if (existsSync(preObfMainJsPath)) {
142
+ const mainSrc = readFileSync(preObfMainJsPath, "utf-8");
143
+ const versionRe = /var PANEL_VERSION = "[^"]*";/;
144
+ if (!versionRe.test(mainSrc)) {
145
+ console.error("āœ— Could not find PANEL_VERSION literal in main.js to stamp — aborting so a stale version never ships silently.");
146
+ process.exit(1);
147
+ }
148
+ writeFileSync(preObfMainJsPath, mainSrc.replace(versionRe, `var PANEL_VERSION = "${V}";`));
149
+ console.log(`āœ“ Stamped version ${V} into main.js PANEL_VERSION`);
150
+ }
151
+
152
+ // 4c. Obfuscate the panel logic (main.js only). ExtendScript-safe settings:
153
+ // renameGlobals MUST stay false (helper fns are called by name), no control-flow
154
+ // flattening / selfDefending (incompatible with the ExtendScript engine).
155
+ // stringArray is INTENTIONALLY OFF: it deadlocks the ExtendScript engine at
156
+ // ~100% CPU on Premiere/AE 2026 26.3+ the moment the obfuscated code actually
157
+ // runs in ExtendScript. Found 2026-09-22 auditing every Synthetic CEP product
158
+ // after the identical bug (same obfuscation profile) shipped in AE Agent's
159
+ // tools.jsx and hung a paying customer's After Effects -- see
160
+ // synthetic-ae-agent CHANGELOG.md v1.9.10 and synthetic-autoedit-pro v1.0.1.
161
+ //
162
+ // premiere.jsx is shipped UNOBFUSCATED, same as host.jsx. Not a stylistic
163
+ // choice: obfuscating it (even with stringArray off) made Premiere's
164
+ // ExtendScript engine fail evalFile() outright with "EvalScript error." —
165
+ // no hang, just a flat parse/load failure — on Premiere Pro 26.5.1, most
166
+ // likely a size ceiling (this file is 25k+ lines / 437 tools, an order of
167
+ // magnitude bigger than ae-bridge.jsx or AE Agent's tools.jsx, both of
168
+ // which obfuscate fine). Verified live 2026-09-23: identical file loads
169
+ // "OK" unobfuscated, fails obfuscated, nothing else changed. IP exposure
170
+ // here is the accepted tradeoff for the panel actually working — same
171
+ // precedent as ae-bridge.jsx above.
172
+ const ES_SAFE = {
173
+ compact: true,
174
+ controlFlowFlattening: false,
175
+ deadCodeInjection: false,
176
+ debugProtection: false,
177
+ disableConsoleOutput: false,
178
+ identifierNamesGenerator: "hexadecimal",
179
+ numbersToExpressions: false,
180
+ renameGlobals: false,
181
+ selfDefending: false,
182
+ simplify: true,
183
+ splitStrings: false,
184
+ stringArray: false, // DEADLOCKS ExtendScript on Premiere/AE 2026 26.3+ — do not re-enable
185
+ unicodeEscapeSequence: false,
186
+ };
187
+ for (const f of ["main.js"]) {
188
+ const p = join(CEP_STAGE, f);
189
+ if (!existsSync(p)) continue;
190
+ const src = readFileSync(p, "utf-8").replace(/^#!.*\n/, "");
191
+ const obf = JavaScriptObfuscator.obfuscate(src, ES_SAFE).getObfuscatedCode();
192
+ writeFileSync(p, obf);
193
+ console.log(`āœ“ Obfuscated cep/${f} (${(obf.length / 1024).toFixed(1)} KB)`);
194
+ }
195
+ console.log("āœ“ Shipped cep/premiere.jsx verbatim (no obfuscation — see comment above)");
196
+
197
+ // ─── 5. Sign the staged plugin into a .zxp ───────────────────────────────────
198
+ const ZXP = process.env.ZXP_SIGN_CMD ||
199
+ "C:\\Users\\Artear\\Documents\\CEP-Resources\\ZXPSignCMD\\4.1.3\\x64\\ZXPSignCmd.exe";
200
+ const certPath = process.env.ZXP_CERT_PATH || join(ROOT, ".cert", "self-signed.p12");
201
+ const certPass = process.env.ZXP_CERT_PASSWORD || "synthetic";
202
+ const zxpOut = join(REL, "MCPBridgeCEP.zxp");
203
+
204
+ if (!existsSync(certPath)) { console.error(`āœ— Cert not found: ${certPath}`); process.exit(1); }
205
+ function sign(withTsa) {
206
+ const tsa = withTsa ? ` -tsa "http://timestamp.digicert.com"` : "";
207
+ execSync(`"${ZXP}" -sign "${CEP_STAGE}" "${zxpOut}" "${certPath}" "${certPass}"${tsa}`,
208
+ { cwd: ROOT, stdio: "pipe" });
209
+ }
210
+ console.log("šŸ–Š Signing MCPBridgeCEP.zxp...");
211
+ try {
212
+ try { sign(true); } catch { if (existsSync(zxpOut)) rmSync(zxpOut); console.warn(" ⚠ TSA failed — signing offline."); sign(false); }
213
+ console.log(`āœ“ Signed → release/MCPBridgeCEP.zxp (${(statSync(zxpOut).size / 1024).toFixed(1)} KB)`);
214
+ } catch (e) {
215
+ console.error("āœ— Signing failed:", (e.stderr || e.message || "").toString());
216
+ process.exit(1);
217
+ }
218
+ rmSync(CEP_STAGE, { recursive: true, force: true });
219
+
220
+ // ─── 6. Install scripts + docs ───────────────────────────────────────────────
221
+ mkdirSync(join(REL, "install"), { recursive: true });
222
+ for (const s of ["install-cep-windows.ps1", "install-windows.bat", "install-cep.sh"]) {
223
+ const p = join(ROOT, "scripts", s);
224
+ if (existsSync(p)) copyFileSync(p, join(REL, "install", s));
225
+ }
226
+ for (const f of ["LICENSE"]) {
227
+ if (existsSync(join(ROOT, f))) copyFileSync(join(ROOT, f), join(REL, f));
228
+ }
229
+ console.log("āœ“ Copied install scripts + LICENSE");
230
+
231
+ // ─── 7. Zip ──────────────────────────────────────────────────────────────────
232
+ const zipName = `synthetic-premiere-mcp-v${V}.zip`;
233
+ const zipPath = join(ROOT, zipName);
234
+ if (existsSync(zipPath)) rmSync(zipPath);
235
+ try {
236
+ execSync(`powershell -NoProfile -Command "Compress-Archive -Path '${REL}\\*' -DestinationPath '${zipPath}' -Force"`,
237
+ { stdio: "pipe" });
238
+ console.log(`āœ“ Zipped → ${zipName} (${(statSync(zipPath).size / 1024).toFixed(1)} KB)`);
239
+ } catch (e) {
240
+ console.warn(" ⚠ Zip step skipped:", (e.stderr || e.message || "").toString().slice(0, 120));
241
+ }
242
+
243
+ console.log(`\nāœ… Done. Server protection: ${serverProtected}.`);
244
+ console.log(` release/ + ${zipName}\n`);
245
+
246
+ // ─── helper ──────────────────────────────────────────────────────────────────
247
+ function copyDirSync(src, dest) {
248
+ mkdirSync(dest, { recursive: true });
249
+ for (const entry of readdirSync(src)) {
250
+ const s = join(src, entry), d = join(dest, entry);
251
+ if (statSync(s).isDirectory()) copyDirSync(s, d);
252
+ else copyFileSync(s, d);
253
+ }
254
+ }