@synthetic-ai/premiere-mcp 2.1.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 (58) hide show
  1. package/CHANGELOG.md +135 -123
  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/index.js +103 -4
  12. package/dist/license.js +38 -0
  13. package/dist/server.js +60 -60
  14. package/dist/tools/advanced.js +357 -357
  15. package/dist/tools/audio-advanced.js +1049 -1049
  16. package/dist/tools/audio.js +42 -42
  17. package/dist/tools/captions.js +27 -27
  18. package/dist/tools/clipboard.js +214 -214
  19. package/dist/tools/color-advanced.js +253 -253
  20. package/dist/tools/discovery.js +291 -291
  21. package/dist/tools/effects.js +285 -285
  22. package/dist/tools/export-presets.js +206 -206
  23. package/dist/tools/export.js +255 -255
  24. package/dist/tools/health.js +10 -10
  25. package/dist/tools/inspection.js +885 -885
  26. package/dist/tools/keyframes.js +369 -369
  27. package/dist/tools/markers.js +89 -89
  28. package/dist/tools/media.js +178 -178
  29. package/dist/tools/metadata.js +94 -94
  30. package/dist/tools/multicam.js +50 -50
  31. package/dist/tools/playback.js +15 -15
  32. package/dist/tools/playhead.js +95 -95
  33. package/dist/tools/project-manager.js +21 -21
  34. package/dist/tools/project.js +149 -149
  35. package/dist/tools/scripting.js +319 -319
  36. package/dist/tools/selection.js +202 -202
  37. package/dist/tools/sequence-advanced.js +379 -379
  38. package/dist/tools/sequence-settings.js +564 -564
  39. package/dist/tools/sequence.js +167 -167
  40. package/dist/tools/source-monitor.js +68 -68
  41. package/dist/tools/text.js +58 -58
  42. package/dist/tools/timeline.js +312 -312
  43. package/dist/tools/track-management.js +446 -446
  44. package/dist/tools/track-targeting.js +635 -635
  45. package/dist/tools/tracks.js +87 -87
  46. package/dist/tools/transitions-motion.js +174 -174
  47. package/dist/tools/transitions.js +111 -111
  48. package/dist/tools/utility.js +606 -606
  49. package/dist/tools/workspace.js +29 -29
  50. package/package.json +66 -66
  51. package/scripts/build-chat-dmg.sh +210 -210
  52. package/scripts/build-chat-zip.sh +291 -291
  53. package/scripts/install-cep-windows.ps1 +99 -99
  54. package/scripts/install-cep.sh +66 -66
  55. package/scripts/install-chat-plugin.bat +67 -67
  56. package/scripts/install-chat-plugin.sh +85 -85
  57. package/scripts/install-windows.bat +95 -95
  58. 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
+ }