@synthetic-ai/premiere-mcp 2.0.0 → 2.2.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 (60) hide show
  1. package/CHANGELOG.md +135 -73
  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 -70
  6. package/cep-plugin/CSXS/manifest.xml +49 -49
  7. package/cep-plugin/host.jsx +7 -7
  8. package/cep-plugin/index.html +472 -419
  9. package/cep-plugin/main.js +683 -327
  10. package/cep-plugin/premiere.jsx +24947 -24947
  11. package/dist/bridge/script-builder.js +378 -378
  12. package/dist/index.js +117 -18
  13. package/dist/license.d.ts +22 -0
  14. package/dist/license.js +89 -0
  15. package/dist/server.js +67 -61
  16. package/dist/tools/advanced.js +357 -357
  17. package/dist/tools/audio-advanced.js +1049 -1049
  18. package/dist/tools/audio.js +42 -42
  19. package/dist/tools/captions.js +27 -27
  20. package/dist/tools/clipboard.js +214 -214
  21. package/dist/tools/color-advanced.js +253 -253
  22. package/dist/tools/discovery.js +291 -291
  23. package/dist/tools/effects.js +285 -285
  24. package/dist/tools/export-presets.js +206 -206
  25. package/dist/tools/export.js +255 -255
  26. package/dist/tools/health.js +10 -10
  27. package/dist/tools/inspection.js +885 -885
  28. package/dist/tools/keyframes.js +369 -369
  29. package/dist/tools/markers.js +89 -89
  30. package/dist/tools/media.js +178 -178
  31. package/dist/tools/metadata.js +94 -94
  32. package/dist/tools/multicam.js +50 -50
  33. package/dist/tools/playback.js +15 -15
  34. package/dist/tools/playhead.js +95 -95
  35. package/dist/tools/project-manager.js +21 -21
  36. package/dist/tools/project.js +149 -149
  37. package/dist/tools/scripting.js +319 -319
  38. package/dist/tools/selection.js +202 -202
  39. package/dist/tools/sequence-advanced.js +379 -374
  40. package/dist/tools/sequence-settings.js +564 -556
  41. package/dist/tools/sequence.js +169 -150
  42. package/dist/tools/source-monitor.js +68 -68
  43. package/dist/tools/text.js +58 -58
  44. package/dist/tools/timeline.js +312 -312
  45. package/dist/tools/track-management.js +446 -446
  46. package/dist/tools/track-targeting.js +635 -635
  47. package/dist/tools/tracks.js +87 -87
  48. package/dist/tools/transitions-motion.js +174 -174
  49. package/dist/tools/transitions.js +111 -111
  50. package/dist/tools/utility.js +606 -606
  51. package/dist/tools/workspace.js +29 -29
  52. package/package.json +1 -1
  53. package/scripts/build-chat-dmg.sh +210 -210
  54. package/scripts/build-chat-zip.sh +291 -291
  55. package/scripts/install-cep-windows.ps1 +99 -99
  56. package/scripts/install-cep.sh +66 -66
  57. package/scripts/install-chat-plugin.bat +67 -67
  58. package/scripts/install-chat-plugin.sh +85 -85
  59. package/scripts/install-windows.bat +95 -95
  60. package/scripts/package.mjs +254 -218
@@ -1,218 +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 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
- // 4c. Obfuscate the ExtendScript library + panel logic. ExtendScript-safe settings:
133
- // renameGlobals MUST stay false (helper fns are called by name), no control-flow
134
- // flattening / selfDefending (incompatible with the ExtendScript engine).
135
- const ES_SAFE = {
136
- compact: true,
137
- controlFlowFlattening: false,
138
- deadCodeInjection: false,
139
- debugProtection: false,
140
- disableConsoleOutput: false,
141
- identifierNamesGenerator: "hexadecimal",
142
- numbersToExpressions: false,
143
- renameGlobals: false,
144
- selfDefending: false,
145
- simplify: true,
146
- splitStrings: false,
147
- stringArray: true,
148
- stringArrayEncoding: ["base64"],
149
- stringArrayThreshold: 0.75,
150
- unicodeEscapeSequence: false,
151
- };
152
- for (const f of ["premiere.jsx", "main.js"]) {
153
- const p = join(CEP_STAGE, f);
154
- if (!existsSync(p)) continue;
155
- const src = readFileSync(p, "utf-8").replace(/^#!.*\n/, "");
156
- const obf = JavaScriptObfuscator.obfuscate(src, ES_SAFE).getObfuscatedCode();
157
- writeFileSync(p, obf);
158
- console.log(`āœ“ Obfuscated cep/${f} (${(obf.length / 1024).toFixed(1)} KB)`);
159
- }
160
-
161
- // ─── 5. Sign the staged plugin into a .zxp ───────────────────────────────────
162
- const ZXP = process.env.ZXP_SIGN_CMD ||
163
- "C:\\Users\\Artear\\Documents\\CEP-Resources\\ZXPSignCMD\\4.1.3\\x64\\ZXPSignCmd.exe";
164
- const certPath = process.env.ZXP_CERT_PATH || join(ROOT, ".cert", "self-signed.p12");
165
- const certPass = process.env.ZXP_CERT_PASSWORD || "synthetic";
166
- const zxpOut = join(REL, "MCPBridgeCEP.zxp");
167
-
168
- if (!existsSync(certPath)) { console.error(`āœ— Cert not found: ${certPath}`); process.exit(1); }
169
- function sign(withTsa) {
170
- const tsa = withTsa ? ` -tsa "http://timestamp.digicert.com"` : "";
171
- execSync(`"${ZXP}" -sign "${CEP_STAGE}" "${zxpOut}" "${certPath}" "${certPass}"${tsa}`,
172
- { cwd: ROOT, stdio: "pipe" });
173
- }
174
- console.log("šŸ–Š Signing MCPBridgeCEP.zxp...");
175
- try {
176
- try { sign(true); } catch { if (existsSync(zxpOut)) rmSync(zxpOut); console.warn(" ⚠ TSA failed — signing offline."); sign(false); }
177
- console.log(`āœ“ Signed → release/MCPBridgeCEP.zxp (${(statSync(zxpOut).size / 1024).toFixed(1)} KB)`);
178
- } catch (e) {
179
- console.error("āœ— Signing failed:", (e.stderr || e.message || "").toString());
180
- process.exit(1);
181
- }
182
- rmSync(CEP_STAGE, { recursive: true, force: true });
183
-
184
- // ─── 6. Install scripts + docs ───────────────────────────────────────────────
185
- mkdirSync(join(REL, "install"), { recursive: true });
186
- for (const s of ["install-cep-windows.ps1", "install-windows.bat", "install-cep.sh"]) {
187
- const p = join(ROOT, "scripts", s);
188
- if (existsSync(p)) copyFileSync(p, join(REL, "install", s));
189
- }
190
- for (const f of ["LICENSE"]) {
191
- if (existsSync(join(ROOT, f))) copyFileSync(join(ROOT, f), join(REL, f));
192
- }
193
- console.log("āœ“ Copied install scripts + LICENSE");
194
-
195
- // ─── 7. Zip ──────────────────────────────────────────────────────────────────
196
- const zipName = `synthetic-premiere-mcp-v${V}.zip`;
197
- const zipPath = join(ROOT, zipName);
198
- if (existsSync(zipPath)) rmSync(zipPath);
199
- try {
200
- execSync(`powershell -NoProfile -Command "Compress-Archive -Path '${REL}\\*' -DestinationPath '${zipPath}' -Force"`,
201
- { stdio: "pipe" });
202
- console.log(`āœ“ Zipped → ${zipName} (${(statSync(zipPath).size / 1024).toFixed(1)} KB)`);
203
- } catch (e) {
204
- console.warn(" ⚠ Zip step skipped:", (e.stderr || e.message || "").toString().slice(0, 120));
205
- }
206
-
207
- console.log(`\nāœ… Done. Server protection: ${serverProtected}.`);
208
- console.log(` release/ + ${zipName}\n`);
209
-
210
- // ─── helper ──────────────────────────────────────────────────────────────────
211
- function copyDirSync(src, dest) {
212
- mkdirSync(dest, { recursive: true });
213
- for (const entry of readdirSync(src)) {
214
- const s = join(src, entry), d = join(dest, entry);
215
- if (statSync(s).isDirectory()) copyDirSync(s, d);
216
- else copyFileSync(s, d);
217
- }
218
- }
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
+ }