ae-agent-setup 0.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 (34) hide show
  1. package/CSXS/manifest.xml +44 -0
  2. package/README.ja.md +249 -0
  3. package/README.md +249 -0
  4. package/bin/ae-agent-setup.mjs +337 -0
  5. package/client/CSInterface.js +1291 -0
  6. package/client/index.html +64 -0
  7. package/client/lib/bridge_utils.js +56 -0
  8. package/client/lib/logging.js +10 -0
  9. package/client/lib/request_handlers.js +468 -0
  10. package/client/lib/request_handlers_essential.js +35 -0
  11. package/client/lib/request_handlers_layer_structure.js +180 -0
  12. package/client/lib/request_handlers_scene.js +38 -0
  13. package/client/lib/request_handlers_shape.js +288 -0
  14. package/client/lib/request_handlers_timeline.js +115 -0
  15. package/client/lib/runtime.js +35 -0
  16. package/client/lib/server.js +33 -0
  17. package/client/main.js +1 -0
  18. package/host/index.jsx +11 -0
  19. package/host/json2.js +504 -0
  20. package/host/lib/common.jsx +128 -0
  21. package/host/lib/mutation_handlers.jsx +358 -0
  22. package/host/lib/mutation_keyframe_handlers.jsx +265 -0
  23. package/host/lib/mutation_layer_structure_handlers.jsx +235 -0
  24. package/host/lib/mutation_scene_handlers.jsx +1226 -0
  25. package/host/lib/mutation_shape_handlers.jsx +358 -0
  26. package/host/lib/mutation_timeline_handlers.jsx +137 -0
  27. package/host/lib/property_utils.jsx +105 -0
  28. package/host/lib/query_handlers.jsx +427 -0
  29. package/package.json +21 -0
  30. package/scripts/signing/build-zxp.sh +56 -0
  31. package/scripts/signing/create-dev-cert.sh +97 -0
  32. package/scripts/signing/install-zxp.sh +29 -0
  33. package/templates/skills/aftereffects-cli.SKILL.md +74 -0
  34. package/templates/skills/aftereffects-declarative.SKILL.md +112 -0
@@ -0,0 +1,64 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title>ae-agent-skill</title>
6
+ <style>
7
+ body {
8
+ font-family: sans-serif;
9
+ background-color: #323232;
10
+ color: #E0E0E0;
11
+ padding: 0;
12
+ margin: 0;
13
+ height: 100vh;
14
+ display: flex;
15
+ flex-direction: column;
16
+ }
17
+ h1 {
18
+ font-size: 14px;
19
+ padding: 10px;
20
+ margin: 0;
21
+ border-bottom: 1px solid #444;
22
+ display: flex;
23
+ justify-content: space-between;
24
+ align-items: center;
25
+ }
26
+ .version {
27
+ font-size: 12px;
28
+ color: #B0B0B0;
29
+ }
30
+ #log {
31
+ flex-grow: 1;
32
+ box-sizing: border-box;
33
+ width: 100%;
34
+ padding: 10px;
35
+ background-color: #282828;
36
+ color: #E0E0E0;
37
+ border: none;
38
+ font-family: monospace;
39
+ white-space: pre-wrap;
40
+ resize: none;
41
+ }
42
+ </style>
43
+ </head>
44
+ <body>
45
+ <h1>
46
+ <span>ae-agent-skill Logs</span>
47
+ <span class="version">v0.2.0</span>
48
+ </h1>
49
+ <textarea id="log" readonly></textarea>
50
+
51
+ <script type="text/javascript" src="./CSInterface.js"></script>
52
+ <script type="text/javascript" src="./lib/runtime.js"></script>
53
+ <script type="text/javascript" src="./lib/logging.js"></script>
54
+ <script type="text/javascript" src="./lib/bridge_utils.js"></script>
55
+ <script type="text/javascript" src="./lib/request_handlers_shape.js"></script>
56
+ <script type="text/javascript" src="./lib/request_handlers_scene.js"></script>
57
+ <script type="text/javascript" src="./lib/request_handlers_essential.js"></script>
58
+ <script type="text/javascript" src="./lib/request_handlers_timeline.js"></script>
59
+ <script type="text/javascript" src="./lib/request_handlers_layer_structure.js"></script>
60
+ <script type="text/javascript" src="./lib/request_handlers.js"></script>
61
+ <script type="text/javascript" src="./lib/server.js"></script>
62
+ <script type="text/javascript" src="./main.js"></script>
63
+ </body>
64
+ </html>
@@ -0,0 +1,56 @@
1
+ const ENCODE_PREFIX = '__ENC__';
2
+
3
+ function parseBridgeResult(result) {
4
+ if (typeof result !== 'string' || result.length === 0) {
5
+ throw new Error('ExtendScript returned an empty result.');
6
+ }
7
+
8
+ let decoded = result;
9
+ if (result.startsWith(ENCODE_PREFIX)) {
10
+ const encodedPayload = result.slice(ENCODE_PREFIX.length);
11
+ try {
12
+ decoded = decodeURIComponent(encodedPayload);
13
+ } catch (e) {
14
+ throw new Error(`Failed to decode ExtendScript payload: ${e.toString()}`);
15
+ }
16
+ }
17
+
18
+ return JSON.parse(decoded);
19
+ }
20
+
21
+ function sendJson(res, statusCode, payload) {
22
+ res.writeHead(statusCode);
23
+ res.end(JSON.stringify(payload));
24
+ }
25
+
26
+ function sendBadRequest(res, message, error) {
27
+ const payload = { status: 'error', message };
28
+ if (error) {
29
+ payload.error = error.toString();
30
+ }
31
+ sendJson(res, 400, payload);
32
+ }
33
+
34
+ function sendBridgeParseError(res, result, error) {
35
+ sendJson(res, 500, {
36
+ status: 'error',
37
+ message: 'Failed to parse ExtendScript result.',
38
+ error: error.toString(),
39
+ rawResult: result,
40
+ });
41
+ }
42
+
43
+ function readJsonBody(req, res, onParsed) {
44
+ let body = '';
45
+ req.on('data', (chunk) => {
46
+ body += chunk.toString();
47
+ });
48
+ req.on('end', () => {
49
+ try {
50
+ const parsed = JSON.parse(body);
51
+ onParsed(parsed);
52
+ } catch (e) {
53
+ sendBadRequest(res, 'Invalid JSON', e);
54
+ }
55
+ });
56
+ }
@@ -0,0 +1,10 @@
1
+ function appendLog(source, message) {
2
+ const logTextarea = document.getElementById('log');
3
+ const timestamp = new Date().toLocaleTimeString();
4
+ const prefix = source ? `[${source}] ` : '';
5
+ logTextarea.value = `${timestamp} ${prefix}${message}\n` + logTextarea.value;
6
+ }
7
+
8
+ function log(message) {
9
+ appendLog('Panel', message);
10
+ }
@@ -0,0 +1,468 @@
1
+ function applyCommonResponseHeaders(res) {
2
+ res.setHeader('Access-Control-Allow-Origin', '*');
3
+ res.setHeader('Content-Type', 'application/json');
4
+ }
5
+
6
+ function handleCorsPreflight(req, res) {
7
+ if (req.method !== 'OPTIONS') {
8
+ return false;
9
+ }
10
+ res.writeHead(204, {
11
+ 'Access-Control-Allow-Origin': '*',
12
+ 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
13
+ 'Access-Control-Allow-Headers': 'Content-Type',
14
+ });
15
+ res.end();
16
+ return true;
17
+ }
18
+
19
+ function handleBridgeDataCall(script, res, contextLabel) {
20
+ log(`Calling ExtendScript: ${contextLabel}`);
21
+ evalHostScript(script, (result) => {
22
+ try {
23
+ const parsedResult = parseBridgeResult(result);
24
+ sendJson(res, 200, { status: 'success', data: parsedResult });
25
+ log(`${contextLabel} successful.`);
26
+ } catch (e) {
27
+ sendBridgeParseError(res, result, e);
28
+ log(`${contextLabel} failed: ${e.toString()}`);
29
+ }
30
+ });
31
+ }
32
+
33
+ function handleBridgeMutationCall(script, res, contextLabel, fallbackMessage) {
34
+ log(`Calling ExtendScript: ${contextLabel}`);
35
+ evalHostScript(script, (result) => {
36
+ try {
37
+ const parsedResult = parseBridgeResult(result);
38
+ if (parsedResult && parsedResult.status === 'error') {
39
+ sendJson(res, 500, {
40
+ status: 'error',
41
+ message: parsedResult.message || fallbackMessage,
42
+ });
43
+ log(`${contextLabel} failed: ${parsedResult.message || 'Unknown error'}`);
44
+ return;
45
+ }
46
+ sendJson(res, 200, { status: 'success', data: parsedResult });
47
+ log(`${contextLabel} successful.`);
48
+ } catch (e) {
49
+ sendBridgeParseError(res, result, e);
50
+ log(`${contextLabel} failed: ${e.toString()}`);
51
+ }
52
+ });
53
+ }
54
+
55
+ function normalizeLayerSelector(layerIdRaw, layerNameRaw) {
56
+ const hasLayerId = layerIdRaw !== undefined && layerIdRaw !== null;
57
+ const hasLayerName = typeof layerNameRaw === 'string' && layerNameRaw.trim().length > 0;
58
+ if ((hasLayerId && hasLayerName) || (!hasLayerId && !hasLayerName)) {
59
+ return { ok: false, error: 'Provide exactly one of layerId or layerName' };
60
+ }
61
+ if (hasLayerId) {
62
+ if (typeof layerIdRaw !== 'number' || !Number.isInteger(layerIdRaw) || layerIdRaw <= 0) {
63
+ return { ok: false, error: 'layerId must be a positive integer when specified' };
64
+ }
65
+ return { ok: true, layerIdLiteral: String(layerIdRaw), layerNameLiteral: 'null' };
66
+ }
67
+ return {
68
+ ok: true,
69
+ layerIdLiteral: 'null',
70
+ layerNameLiteral: toExtendScriptStringLiteral(layerNameRaw.trim()),
71
+ };
72
+ }
73
+
74
+ function handleHealth(res) {
75
+ sendJson(res, 200, { status: 'ok' });
76
+ log('Health check responded with ok.');
77
+ }
78
+
79
+ function handleGetLayers(res) {
80
+ handleBridgeDataCall('getLayers()', res, 'getLayers()');
81
+ }
82
+
83
+ function handleGetComps(res) {
84
+ handleBridgeDataCall('listComps()', res, 'listComps()');
85
+ }
86
+
87
+ function handleGetSelectedProperties(res) {
88
+ handleBridgeDataCall('getSelectedProperties()', res, 'getSelectedProperties()');
89
+ }
90
+
91
+ function handleGetExpressionErrors(res) {
92
+ handleBridgeDataCall('getExpressionErrors()', res, 'getExpressionErrors()');
93
+ }
94
+
95
+ function handleCreateComp(req, res) {
96
+ readJsonBody(req, res, ({ name, width, height, duration, frameRate, pixelAspect }) => {
97
+ if (!name || typeof name !== 'string') {
98
+ sendBadRequest(res, 'name is required and must be a string');
99
+ log('createComp failed: invalid name');
100
+ return;
101
+ }
102
+ if (typeof width !== 'number' || width <= 0) {
103
+ sendBadRequest(res, 'width is required and must be a positive number');
104
+ log('createComp failed: invalid width');
105
+ return;
106
+ }
107
+ if (typeof height !== 'number' || height <= 0) {
108
+ sendBadRequest(res, 'height is required and must be a positive number');
109
+ log('createComp failed: invalid height');
110
+ return;
111
+ }
112
+ if (typeof duration !== 'number' || duration <= 0) {
113
+ sendBadRequest(res, 'duration is required and must be a positive number');
114
+ log('createComp failed: invalid duration');
115
+ return;
116
+ }
117
+ if (typeof frameRate !== 'number' || frameRate <= 0) {
118
+ sendBadRequest(res, 'frameRate is required and must be a positive number');
119
+ log('createComp failed: invalid frameRate');
120
+ return;
121
+ }
122
+ if (pixelAspect !== undefined && (typeof pixelAspect !== 'number' || pixelAspect <= 0)) {
123
+ sendBadRequest(res, 'pixelAspect must be a positive number when specified');
124
+ log('createComp failed: invalid pixelAspect');
125
+ return;
126
+ }
127
+
128
+ const nameLiteral = toExtendScriptStringLiteral(name);
129
+ const pixelAspectValue = pixelAspect === undefined ? 1.0 : pixelAspect;
130
+ const script = `createComp(${nameLiteral}, ${width}, ${height}, ${pixelAspectValue}, ${duration}, ${frameRate})`;
131
+ handleBridgeMutationCall(script, res, 'createComp()', 'Failed to create comp');
132
+ });
133
+ }
134
+
135
+ function handleSetActiveComp(req, res) {
136
+ readJsonBody(req, res, ({ compId, compName }) => {
137
+ const hasCompId = compId !== undefined;
138
+ const hasCompName = compName !== undefined && compName !== null && compName !== '';
139
+ if ((hasCompId && hasCompName) || (!hasCompId && !hasCompName)) {
140
+ sendBadRequest(res, 'Provide exactly one of compId or compName');
141
+ log('setActiveComp failed: invalid selector');
142
+ return;
143
+ }
144
+ if (hasCompId && typeof compId !== 'number') {
145
+ sendBadRequest(res, 'compId must be a number');
146
+ log('setActiveComp failed: compId must be number');
147
+ return;
148
+ }
149
+ if (hasCompName && typeof compName !== 'string') {
150
+ sendBadRequest(res, 'compName must be a string');
151
+ log('setActiveComp failed: compName must be string');
152
+ return;
153
+ }
154
+
155
+ const compIdLiteral = hasCompId ? String(compId) : 'null';
156
+ const compNameLiteral = hasCompName ? toExtendScriptStringLiteral(compName) : 'null';
157
+ const script = `setActiveComp(${compIdLiteral}, ${compNameLiteral})`;
158
+ handleBridgeMutationCall(script, res, 'setActiveComp()', 'Failed to set active comp');
159
+ });
160
+ }
161
+
162
+ function handleGetProperties(searchParams, res) {
163
+ const layerIdParam = searchParams.get('layerId');
164
+ const layerNameParam = searchParams.get('layerName');
165
+ const hasLayerId = layerIdParam !== null && layerIdParam !== '';
166
+ const hasLayerName = layerNameParam !== null && layerNameParam.trim() !== '';
167
+ if ((hasLayerId && hasLayerName) || (!hasLayerId && !hasLayerName)) {
168
+ sendBadRequest(res, 'Provide exactly one of layerId or layerName');
169
+ log('getProperties failed: invalid layer selector');
170
+ return;
171
+ }
172
+ let layerId = null;
173
+ if (hasLayerId) {
174
+ const parsedLayerId = parseInt(layerIdParam, 10);
175
+ if (isNaN(parsedLayerId) || parsedLayerId <= 0) {
176
+ sendBadRequest(res, 'layerId must be a positive integer');
177
+ log('getProperties failed: invalid layerId');
178
+ return;
179
+ }
180
+ layerId = parsedLayerId;
181
+ }
182
+
183
+ const includeGroups = searchParams.getAll('includeGroup').filter(Boolean);
184
+ const excludeGroups = searchParams.getAll('excludeGroup').filter(Boolean);
185
+ const maxDepthParam = searchParams.get('maxDepth');
186
+ const includeGroupChildrenParam = searchParams.get('includeGroupChildren');
187
+ const timeParam = searchParams.get('time');
188
+
189
+ let maxDepth;
190
+ if (maxDepthParam !== null) {
191
+ const parsedDepth = parseInt(maxDepthParam, 10);
192
+ if (isNaN(parsedDepth) || parsedDepth <= 0) {
193
+ sendBadRequest(res, 'maxDepth must be a positive integer');
194
+ log('getProperties failed: Invalid maxDepth');
195
+ return;
196
+ }
197
+ maxDepth = parsedDepth;
198
+ }
199
+ let includeGroupChildren;
200
+ if (includeGroupChildrenParam !== null) {
201
+ if (!['true', 'false'].includes(includeGroupChildrenParam)) {
202
+ sendBadRequest(res, 'includeGroupChildren must be true or false');
203
+ log('getProperties failed: invalid includeGroupChildren');
204
+ return;
205
+ }
206
+ includeGroupChildren = includeGroupChildrenParam === 'true';
207
+ }
208
+ let time;
209
+ if (timeParam !== null) {
210
+ const parsedTime = Number(timeParam);
211
+ if (!isFinite(parsedTime)) {
212
+ sendBadRequest(res, 'time must be a finite number');
213
+ log('getProperties failed: invalid time');
214
+ return;
215
+ }
216
+ time = parsedTime;
217
+ }
218
+
219
+ const options = {};
220
+ if (hasLayerName) options.layerName = layerNameParam.trim();
221
+ if (includeGroups.length > 0) options.includeGroups = includeGroups;
222
+ if (excludeGroups.length > 0) options.excludeGroups = excludeGroups;
223
+ if (maxDepth !== undefined) options.maxDepth = maxDepth;
224
+ if (includeGroupChildren !== undefined) options.includeGroupChildren = includeGroupChildren;
225
+ if (time !== undefined) options.time = time;
226
+
227
+ const optionsLiteral = Object.keys(options).length > 0
228
+ ? toExtendScriptStringLiteral(JSON.stringify(options))
229
+ : 'null';
230
+ const optionsLabel = optionsLiteral === 'null' ? 'null' : 'custom';
231
+ const layerIdLiteral = layerId === null ? 'null' : String(layerId);
232
+ const script = `getProperties(${layerIdLiteral}, ${optionsLiteral})`;
233
+
234
+ handleBridgeDataCall(script, res, `getProperties(${layerIdLiteral}, options=${optionsLabel})`);
235
+ }
236
+
237
+ function handleSetExpression(req, res) {
238
+ readJsonBody(req, res, ({ layerId, layerName, propertyPath, expression }) => {
239
+ if (!propertyPath || expression === undefined) {
240
+ sendBadRequest(res, 'Missing parameters');
241
+ log('setExpression failed: Missing parameters');
242
+ return;
243
+ }
244
+ const selector = normalizeLayerSelector(layerId, layerName);
245
+ if (!selector.ok) {
246
+ sendBadRequest(res, selector.error);
247
+ log(`setExpression failed: ${selector.error}`);
248
+ return;
249
+ }
250
+ if (typeof expression !== 'string') {
251
+ sendBadRequest(res, 'Expression must be a string');
252
+ log('setExpression failed: Expression must be a string');
253
+ return;
254
+ }
255
+
256
+ const escapedPath = escapeForExtendScript(propertyPath);
257
+ const expressionLiteral = toExtendScriptStringLiteral(expression);
258
+ const script = `setExpression(${selector.layerIdLiteral}, ${selector.layerNameLiteral}, "${escapedPath}", ${expressionLiteral})`;
259
+
260
+ log(`Calling ExtendScript: ${script}`);
261
+ evalHostScript(script, (result) => {
262
+ if (result === 'success') {
263
+ sendJson(res, 200, { status: 'success', message: 'Expression set successfully' });
264
+ log('setExpression successful.');
265
+ return;
266
+ }
267
+ sendJson(res, 500, { status: 'error', message: result });
268
+ log(`setExpression failed: ${result}`);
269
+ });
270
+ });
271
+ }
272
+
273
+ function handleSetPropertyValue(req, res) {
274
+ readJsonBody(req, res, ({ layerId, layerName, propertyPath, value }) => {
275
+ if (!propertyPath || value === undefined) {
276
+ sendBadRequest(res, 'Missing parameters');
277
+ log('setPropertyValue failed: Missing parameters');
278
+ return;
279
+ }
280
+ const selector = normalizeLayerSelector(layerId, layerName);
281
+ if (!selector.ok) {
282
+ sendBadRequest(res, selector.error);
283
+ log(`setPropertyValue failed: ${selector.error}`);
284
+ return;
285
+ }
286
+ const pathLiteral = toExtendScriptStringLiteral(propertyPath);
287
+ const valueLiteral = toExtendScriptStringLiteral(JSON.stringify(value));
288
+ const script = `setPropertyValue(${selector.layerIdLiteral}, ${selector.layerNameLiteral}, ${pathLiteral}, ${valueLiteral})`;
289
+ handleBridgeMutationCall(script, res, 'setPropertyValue()', 'Failed to set property value');
290
+ });
291
+ }
292
+
293
+ function handleSetKeyframe(req, res) {
294
+ readJsonBody(req, res, ({ layerId, layerName, propertyPath, time, value, inInterp, outInterp, easeIn, easeOut }) => {
295
+ if (!propertyPath || time === undefined || value === undefined) {
296
+ sendBadRequest(res, 'Missing parameters');
297
+ log('setKeyframe failed: Missing parameters');
298
+ return;
299
+ }
300
+ const selector = normalizeLayerSelector(layerId, layerName);
301
+ if (!selector.ok) {
302
+ sendBadRequest(res, selector.error);
303
+ log(`setKeyframe failed: ${selector.error}`);
304
+ return;
305
+ }
306
+ if (typeof time !== 'number' || !isFinite(time)) {
307
+ sendBadRequest(res, 'time must be a number');
308
+ log('setKeyframe failed: invalid time');
309
+ return;
310
+ }
311
+ if (inInterp !== undefined && !['linear', 'bezier', 'hold'].includes(inInterp)) {
312
+ sendBadRequest(res, 'inInterp must be one of: linear, bezier, hold');
313
+ log('setKeyframe failed: invalid inInterp');
314
+ return;
315
+ }
316
+ if (outInterp !== undefined && !['linear', 'bezier', 'hold'].includes(outInterp)) {
317
+ sendBadRequest(res, 'outInterp must be one of: linear, bezier, hold');
318
+ log('setKeyframe failed: invalid outInterp');
319
+ return;
320
+ }
321
+
322
+ const pathLiteral = toExtendScriptStringLiteral(propertyPath);
323
+ const valueLiteral = toExtendScriptStringLiteral(JSON.stringify(value));
324
+ const options = {};
325
+ if (inInterp !== undefined) options.inInterp = inInterp;
326
+ if (outInterp !== undefined) options.outInterp = outInterp;
327
+ if (easeIn !== undefined) options.easeIn = easeIn;
328
+ if (easeOut !== undefined) options.easeOut = easeOut;
329
+ const optionsLiteral = Object.keys(options).length === 0
330
+ ? 'null'
331
+ : toExtendScriptStringLiteral(JSON.stringify(options));
332
+ const script = `setKeyframe(${selector.layerIdLiteral}, ${selector.layerNameLiteral}, ${pathLiteral}, ${time}, ${valueLiteral}, ${optionsLiteral})`;
333
+ handleBridgeMutationCall(script, res, 'setKeyframe()', 'Failed to set keyframe');
334
+ });
335
+ }
336
+
337
+ function handleAddEffect(req, res) {
338
+ readJsonBody(req, res, ({ layerId, layerName, effectMatchName, effectName }) => {
339
+ if (!effectMatchName) {
340
+ sendBadRequest(res, 'Missing parameters');
341
+ log('addEffect failed: Missing parameters');
342
+ return;
343
+ }
344
+ const selector = normalizeLayerSelector(layerId, layerName);
345
+ if (!selector.ok) {
346
+ sendBadRequest(res, selector.error);
347
+ log(`addEffect failed: ${selector.error}`);
348
+ return;
349
+ }
350
+ if (effectName !== undefined && typeof effectName !== 'string') {
351
+ sendBadRequest(res, 'effectName must be a string when specified');
352
+ log('addEffect failed: effectName must be a string');
353
+ return;
354
+ }
355
+
356
+ const matchNameLiteral = toExtendScriptStringLiteral(effectMatchName);
357
+ const effectNameLiteral = effectName === undefined
358
+ ? 'null'
359
+ : toExtendScriptStringLiteral(effectName);
360
+ const script = `addEffect(${selector.layerIdLiteral}, ${selector.layerNameLiteral}, ${matchNameLiteral}, ${effectNameLiteral})`;
361
+
362
+ log(`Calling ExtendScript: ${script}`);
363
+ evalHostScript(script, (result) => {
364
+ try {
365
+ const parsedResult = parseBridgeResult(result);
366
+ if (parsedResult && parsedResult.status === 'error') {
367
+ sendJson(res, 500, {
368
+ status: 'error',
369
+ message: parsedResult.message || 'Failed to add effect',
370
+ });
371
+ log(`addEffect failed: ${parsedResult.message || 'Unknown error'}`);
372
+ return;
373
+ }
374
+ sendJson(res, 200, { status: 'success', data: parsedResult });
375
+ log('addEffect successful.');
376
+ } catch (e) {
377
+ sendBridgeParseError(res, result, e);
378
+ log(`addEffect failed: ${e.toString()}`);
379
+ }
380
+ });
381
+ });
382
+ }
383
+
384
+ function handleNotFound(req, res) {
385
+ sendJson(res, 404, { status: 'error', message: 'Not Found' });
386
+ log(`404 Not Found: ${req.method} ${req.url}`);
387
+ }
388
+
389
+ function routeRequest(req, res) {
390
+ log(`Request received: ${req.method} ${req.url}`);
391
+
392
+ if (handleCorsPreflight(req, res)) {
393
+ return;
394
+ }
395
+
396
+ applyCommonResponseHeaders(res);
397
+
398
+ const [pathname, queryString = ''] = req.url.split('?');
399
+ const method = (req.method || 'GET').toUpperCase();
400
+ const searchParams = new URLSearchParams(queryString);
401
+
402
+ if (pathname === '/health' && method === 'GET') {
403
+ handleHealth(res);
404
+ return;
405
+ }
406
+ if (pathname === '/layers' && method === 'GET') {
407
+ handleGetLayers(res);
408
+ return;
409
+ }
410
+ if (pathname === '/comps' && method === 'GET') {
411
+ handleGetComps(res);
412
+ return;
413
+ }
414
+ if (typeof routeShapeRequest === 'function' && routeShapeRequest(pathname, method, req, res)) {
415
+ return;
416
+ }
417
+ if (typeof routeSceneRequest === 'function' && routeSceneRequest(pathname, method, req, res)) {
418
+ return;
419
+ }
420
+ if (pathname === '/comps' && method === 'POST') {
421
+ handleCreateComp(req, res);
422
+ return;
423
+ }
424
+ if (pathname === '/active-comp' && method === 'POST') {
425
+ handleSetActiveComp(req, res);
426
+ return;
427
+ }
428
+ if (pathname === '/properties' && method === 'GET') {
429
+ handleGetProperties(searchParams, res);
430
+ return;
431
+ }
432
+ if (pathname === '/selected-properties' && method === 'GET') {
433
+ handleGetSelectedProperties(res);
434
+ return;
435
+ }
436
+ if (pathname === '/expression-errors' && method === 'GET') {
437
+ handleGetExpressionErrors(res);
438
+ return;
439
+ }
440
+ if (pathname === '/expression' && method === 'POST') {
441
+ handleSetExpression(req, res);
442
+ return;
443
+ }
444
+ if (pathname === '/property-value' && method === 'POST') {
445
+ handleSetPropertyValue(req, res);
446
+ return;
447
+ }
448
+ if (pathname === '/keyframes' && method === 'POST') {
449
+ handleSetKeyframe(req, res);
450
+ return;
451
+ }
452
+ if (typeof routeEssentialRequest === 'function' && routeEssentialRequest(pathname, method, req, res)) {
453
+ return;
454
+ }
455
+ if (pathname === '/effects' && method === 'POST') {
456
+ handleAddEffect(req, res);
457
+ return;
458
+ }
459
+ if (typeof routeTimelineRequest === 'function' && routeTimelineRequest(pathname, method, req, res)) {
460
+ return;
461
+ }
462
+ if (typeof routeLayerStructureRequest === 'function'
463
+ && routeLayerStructureRequest(pathname, method, req, res)) {
464
+ return;
465
+ }
466
+
467
+ handleNotFound(req, res);
468
+ }
@@ -0,0 +1,35 @@
1
+ function handleAddEssentialProperty(req, res) {
2
+ readJsonBody(req, res, ({ layerId, layerName, propertyPath, essentialName }) => {
3
+ if (!propertyPath || typeof propertyPath !== 'string') {
4
+ sendBadRequest(res, 'propertyPath is required and must be a string');
5
+ log('addEssentialProperty failed: invalid propertyPath');
6
+ return;
7
+ }
8
+ const selector = normalizeLayerSelector(layerId, layerName);
9
+ if (!selector.ok) {
10
+ sendBadRequest(res, selector.error);
11
+ log(`addEssentialProperty failed: ${selector.error}`);
12
+ return;
13
+ }
14
+ if (essentialName !== undefined && typeof essentialName !== 'string') {
15
+ sendBadRequest(res, 'essentialName must be a string when specified');
16
+ log('addEssentialProperty failed: invalid essentialName');
17
+ return;
18
+ }
19
+
20
+ const pathLiteral = toExtendScriptStringLiteral(propertyPath);
21
+ const essentialNameLiteral = essentialName === undefined
22
+ ? 'null'
23
+ : toExtendScriptStringLiteral(essentialName);
24
+ const script = `addEssentialProperty(${selector.layerIdLiteral}, ${selector.layerNameLiteral}, ${pathLiteral}, ${essentialNameLiteral})`;
25
+ handleBridgeMutationCall(script, res, 'addEssentialProperty()', 'Failed to add essential property');
26
+ });
27
+ }
28
+
29
+ function routeEssentialRequest(pathname, method, req, res) {
30
+ if (pathname === '/essential-property' && method === 'POST') {
31
+ handleAddEssentialProperty(req, res);
32
+ return true;
33
+ }
34
+ return false;
35
+ }