@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.
- package/CHANGELOG.md +135 -135
- package/LICENSE +21 -21
- package/README.md +242 -242
- package/cep-plugin/.debug +8 -8
- package/cep-plugin/CSInterface.js +92 -92
- package/cep-plugin/host.jsx +7 -7
- package/cep-plugin/index.html +472 -472
- package/cep-plugin/main.js +6 -0
- package/cep-plugin/premiere.jsx +24947 -24947
- package/dist/bridge/script-builder.js +378 -378
- package/dist/license.js +38 -0
- package/dist/server.js +60 -60
- package/dist/tools/advanced.js +357 -357
- package/dist/tools/audio-advanced.js +1049 -1049
- package/dist/tools/audio.js +42 -42
- package/dist/tools/captions.js +27 -27
- package/dist/tools/clipboard.js +214 -214
- package/dist/tools/color-advanced.js +253 -253
- package/dist/tools/discovery.js +291 -291
- package/dist/tools/effects.js +285 -285
- package/dist/tools/export-presets.js +206 -206
- package/dist/tools/export.js +255 -255
- package/dist/tools/health.js +10 -10
- package/dist/tools/inspection.js +885 -885
- package/dist/tools/keyframes.js +369 -369
- package/dist/tools/markers.js +89 -89
- package/dist/tools/media.js +178 -178
- package/dist/tools/metadata.js +94 -94
- package/dist/tools/multicam.js +50 -50
- package/dist/tools/playback.js +15 -15
- package/dist/tools/playhead.js +95 -95
- package/dist/tools/project-manager.js +21 -21
- package/dist/tools/project.js +149 -149
- package/dist/tools/scripting.js +319 -319
- package/dist/tools/selection.js +202 -202
- package/dist/tools/sequence-advanced.js +379 -379
- package/dist/tools/sequence-settings.js +564 -564
- package/dist/tools/sequence.js +167 -167
- package/dist/tools/source-monitor.js +68 -68
- package/dist/tools/text.js +58 -58
- package/dist/tools/timeline.js +312 -312
- package/dist/tools/track-management.js +446 -446
- package/dist/tools/track-targeting.js +635 -635
- package/dist/tools/tracks.js +87 -87
- package/dist/tools/transitions-motion.js +174 -174
- package/dist/tools/transitions.js +111 -111
- package/dist/tools/utility.js +606 -606
- package/dist/tools/workspace.js +29 -29
- package/package.json +66 -66
- package/scripts/build-chat-dmg.sh +210 -210
- package/scripts/build-chat-zip.sh +291 -291
- package/scripts/install-cep-windows.ps1 +99 -99
- package/scripts/install-cep.sh +66 -66
- package/scripts/install-chat-plugin.bat +67 -67
- package/scripts/install-chat-plugin.sh +85 -85
- package/scripts/install-windows.bat +95 -95
- package/scripts/package.mjs +254 -254
package/scripts/package.mjs
CHANGED
|
@@ -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
|
+
}
|