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,337 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from 'node:fs';
4
+ import os from 'node:os';
5
+ import path from 'node:path';
6
+ import { spawnSync } from 'node:child_process';
7
+ import readline from 'node:readline';
8
+ import { fileURLToPath } from 'node:url';
9
+
10
+ const DEFAULT_REPO = 'yumehiko/ae-agent-skills';
11
+ const SKILL_SOURCES = [
12
+ { sourceName: 'aftereffects-cli.SKILL.md', destinationName: 'aftereffects-cli' },
13
+ { sourceName: 'aftereffects-declarative.SKILL.md', destinationName: 'aftereffects-declarative' },
14
+ ];
15
+
16
+ function printHelp() {
17
+ console.log(`ae-agent-setup
18
+
19
+ Usage:
20
+ ae-agent-setup install [options]
21
+
22
+ Options:
23
+ --agent <codex|gemini|both> Agent target. If omitted, interactive selection is shown.
24
+ --repo <owner/repo> GitHub repository for release and CLI install (default: ${DEFAULT_REPO}).
25
+ --zxp <path-or-url> Local ZXP path or direct download URL.
26
+ --skip-extension Skip ZXP extension installation.
27
+ --skip-cli Skip ae-cli installation.
28
+ --skip-skills Skip skill installation.
29
+ -h, --help Show this help.
30
+ `);
31
+ }
32
+
33
+ function run(cmd, args, options = {}) {
34
+ const pretty = `${cmd} ${args.join(' ')}`.trim();
35
+ console.log(`$ ${pretty}`);
36
+ const res = spawnSync(cmd, args, { stdio: 'inherit', ...options });
37
+ if (res.error) {
38
+ throw new Error(`Command failed: ${pretty}\n${res.error.message}`);
39
+ }
40
+ if (res.status !== 0) {
41
+ throw new Error(`Command failed with exit code ${res.status}: ${pretty}`);
42
+ }
43
+ }
44
+
45
+ function commandExists(cmd) {
46
+ const res = spawnSync('which', [cmd], { stdio: 'pipe', encoding: 'utf8' });
47
+ return res.status === 0;
48
+ }
49
+
50
+ function detectInstaller() {
51
+ if (commandExists('upia')) return { cmd: 'upia', args: ['--install'] };
52
+ if (commandExists('UPIA')) return { cmd: 'UPIA', args: ['--install'] };
53
+
54
+ const knownUpiaPaths = [
55
+ '/Library/Application Support/Adobe/Adobe Desktop Common/RemoteComponents/UPI/UnifiedPluginInstallerAgent/UnifiedPluginInstallerAgent.app/Contents/MacOS/UnifiedPluginInstallerAgent',
56
+ '/Library/Application Support/Adobe/Adobe Desktop Common/HDBox/UPIA/UnifiedPluginInstallerAgent',
57
+ ];
58
+ for (const knownUpiaPath of knownUpiaPaths) {
59
+ if (fs.existsSync(knownUpiaPath)) return { cmd: knownUpiaPath, args: ['--install'] };
60
+ }
61
+
62
+ if (commandExists('ExManCmd')) return { cmd: 'ExManCmd', args: ['--install'] };
63
+ return null;
64
+ }
65
+
66
+ function parseArgs(argv) {
67
+ const opts = {
68
+ agent: '',
69
+ repo: DEFAULT_REPO,
70
+ zxp: '',
71
+ skipExtension: false,
72
+ skipCli: false,
73
+ skipSkills: false,
74
+ };
75
+
76
+ for (let i = 0; i < argv.length; i += 1) {
77
+ const arg = argv[i];
78
+ if (arg === '--agent') {
79
+ opts.agent = argv[i + 1] || '';
80
+ i += 1;
81
+ continue;
82
+ }
83
+ if (arg === '--repo') {
84
+ opts.repo = argv[i + 1] || '';
85
+ i += 1;
86
+ continue;
87
+ }
88
+ if (arg === '--zxp') {
89
+ opts.zxp = argv[i + 1] || '';
90
+ i += 1;
91
+ continue;
92
+ }
93
+ if (arg === '--skip-extension') {
94
+ opts.skipExtension = true;
95
+ continue;
96
+ }
97
+ if (arg === '--skip-cli') {
98
+ opts.skipCli = true;
99
+ continue;
100
+ }
101
+ if (arg === '--skip-skills') {
102
+ opts.skipSkills = true;
103
+ continue;
104
+ }
105
+ if (arg === '-h' || arg === '--help') {
106
+ opts.help = true;
107
+ continue;
108
+ }
109
+ throw new Error(`Unknown argument: ${arg}`);
110
+ }
111
+
112
+ return opts;
113
+ }
114
+
115
+ function getDetectedAgents() {
116
+ const detected = [];
117
+ const home = os.homedir();
118
+ if (process.env.CODEX_HOME || fs.existsSync(path.join(home, '.codex'))) detected.push('codex');
119
+ if (fs.existsSync(path.join(home, '.gemini'))) detected.push('gemini');
120
+ return detected;
121
+ }
122
+
123
+ function ask(question) {
124
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
125
+ return new Promise((resolve) => {
126
+ rl.question(question, (answer) => {
127
+ rl.close();
128
+ resolve(answer.trim());
129
+ });
130
+ });
131
+ }
132
+
133
+ async function resolveAgent(agentArg) {
134
+ if (agentArg) {
135
+ const value = agentArg.toLowerCase();
136
+ if (!['codex', 'gemini', 'both'].includes(value)) {
137
+ throw new Error(`Invalid --agent value: ${agentArg}`);
138
+ }
139
+ return value;
140
+ }
141
+
142
+ const detected = getDetectedAgents();
143
+ const recommended = detected[0] || 'codex';
144
+ console.log('Select which coding agent should receive skills:');
145
+ console.log(` 1) codex${recommended === 'codex' ? ' (recommended)' : ''}`);
146
+ console.log(` 2) gemini${recommended === 'gemini' ? ' (recommended)' : ''}`);
147
+ console.log(' 3) both');
148
+
149
+ if (!process.stdin.isTTY) {
150
+ console.log(`No interactive terminal detected. Defaulting to: ${recommended}`);
151
+ return recommended;
152
+ }
153
+
154
+ const answer = await ask('Enter 1, 2, or 3 [1]: ');
155
+ if (!answer || answer === '1') return 'codex';
156
+ if (answer === '2') return 'gemini';
157
+ if (answer === '3') return 'both';
158
+ throw new Error(`Invalid selection: ${answer}`);
159
+ }
160
+
161
+ function fetchJson(url) {
162
+ return new Promise((resolve, reject) => {
163
+ const req = fetch(url, {
164
+ headers: {
165
+ Accept: 'application/vnd.github+json',
166
+ 'User-Agent': 'ae-agent-setup',
167
+ },
168
+ });
169
+ req.then(async (res) => {
170
+ if (!res.ok) {
171
+ reject(new Error(`Request failed: ${res.status} ${res.statusText}`));
172
+ return;
173
+ }
174
+ try {
175
+ const body = await res.json();
176
+ resolve(body);
177
+ } catch (err) {
178
+ reject(err);
179
+ }
180
+ }).catch(reject);
181
+ });
182
+ }
183
+
184
+ function downloadToFile(url, destination) {
185
+ return fetch(url, {
186
+ headers: {
187
+ Accept: 'application/octet-stream',
188
+ 'User-Agent': 'ae-agent-setup',
189
+ },
190
+ }).then(async (res) => {
191
+ if (!res.ok) {
192
+ throw new Error(`Download failed: ${res.status} ${res.statusText}`);
193
+ }
194
+ const data = Buffer.from(await res.arrayBuffer());
195
+ fs.writeFileSync(destination, data);
196
+ return destination;
197
+ });
198
+ }
199
+
200
+ async function resolveZxpPath(opts) {
201
+ if (opts.zxp) {
202
+ if (opts.zxp.startsWith('http://') || opts.zxp.startsWith('https://')) {
203
+ const output = path.join(fs.mkdtempSync(path.join(os.tmpdir(), 'ae-agent-zxp-')), 'extension.zxp');
204
+ console.log(`Downloading ZXP from URL: ${opts.zxp}`);
205
+ await downloadToFile(opts.zxp, output);
206
+ return output;
207
+ }
208
+
209
+ const local = path.resolve(process.cwd(), opts.zxp);
210
+ if (!fs.existsSync(local)) {
211
+ throw new Error(`ZXP not found: ${local}`);
212
+ }
213
+ return local;
214
+ }
215
+
216
+ console.log(`Fetching latest release from GitHub: ${opts.repo}`);
217
+ const release = await fetchJson(`https://api.github.com/repos/${opts.repo}/releases/latest`);
218
+ const asset = (release.assets || []).find((a) => typeof a.name === 'string' && a.name.endsWith('.zxp'));
219
+ if (!asset) {
220
+ throw new Error(`No .zxp asset found in latest release of ${opts.repo}`);
221
+ }
222
+
223
+ const output = path.join(fs.mkdtempSync(path.join(os.tmpdir(), 'ae-agent-zxp-')), asset.name);
224
+ console.log(`Downloading asset: ${asset.name}`);
225
+ await downloadToFile(asset.browser_download_url, output);
226
+ return output;
227
+ }
228
+
229
+ function installSkills(agent) {
230
+ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
231
+ const sourceRoot = path.join(root, 'templates', 'skills');
232
+
233
+ const targets = [];
234
+ if (agent === 'codex' || agent === 'both') {
235
+ const codexHome = process.env.CODEX_HOME || path.join(os.homedir(), '.codex');
236
+ targets.push({ kind: 'codex', destRoot: path.join(codexHome, 'skills') });
237
+ }
238
+ if (agent === 'gemini' || agent === 'both') {
239
+ targets.push({ kind: 'gemini', destRoot: path.join(os.homedir(), '.gemini', 'skills') });
240
+ }
241
+
242
+ for (const target of targets) {
243
+ fs.mkdirSync(target.destRoot, { recursive: true });
244
+ for (const skill of SKILL_SOURCES) {
245
+ const source = path.join(sourceRoot, skill.sourceName);
246
+ const destination = path.join(target.destRoot, skill.destinationName);
247
+ if (!fs.existsSync(source)) {
248
+ throw new Error(`Skill source not found: ${source}`);
249
+ }
250
+ fs.rmSync(destination, { recursive: true, force: true });
251
+ fs.mkdirSync(destination, { recursive: true });
252
+ fs.copyFileSync(source, path.join(destination, 'SKILL.md'));
253
+ console.log(`Installed ${target.kind} skill: ${destination}`);
254
+ }
255
+ }
256
+ }
257
+
258
+ function installCli(repo) {
259
+ if (!commandExists('python3')) {
260
+ throw new Error('python3 is required to install ae-cli.');
261
+ }
262
+ const spec = `git+https://github.com/${repo}.git`;
263
+ run('python3', ['-m', 'pip', 'install', '--user', '--upgrade', spec]);
264
+
265
+ const helpRes = spawnSync('ae-cli', ['--help'], { stdio: 'inherit' });
266
+ if (helpRes.status !== 0) {
267
+ console.log('ae-cli was installed, but not found on PATH.');
268
+ console.log('Run this once in your shell profile if needed:');
269
+ console.log(' export PATH="$HOME/Library/Python/3.*/bin:$PATH"');
270
+ }
271
+ }
272
+
273
+ async function installCommand(opts) {
274
+ console.log('Starting ae-agent setup...');
275
+
276
+ const agent = await resolveAgent(opts.agent);
277
+ console.log(`Selected agent target: ${agent}`);
278
+
279
+ if (!opts.skipExtension) {
280
+ const installer = detectInstaller();
281
+ if (!installer) {
282
+ throw new Error('Neither UPIA nor ExManCmd was found. Install one of them and run again.');
283
+ }
284
+
285
+ const zxpPath = await resolveZxpPath(opts);
286
+ console.log(`Installing extension via ${installer.cmd}...`);
287
+ run(installer.cmd, [...installer.args, zxpPath]);
288
+ } else {
289
+ console.log('Skipping extension installation (--skip-extension).');
290
+ }
291
+
292
+ if (!opts.skipCli) {
293
+ console.log('Installing ae-cli...');
294
+ installCli(opts.repo);
295
+ } else {
296
+ console.log('Skipping ae-cli installation (--skip-cli).');
297
+ }
298
+
299
+ if (!opts.skipSkills) {
300
+ console.log('Installing agent skills...');
301
+ installSkills(agent);
302
+ } else {
303
+ console.log('Skipping skill installation (--skip-skills).');
304
+ }
305
+
306
+ console.log('Setup finished.');
307
+ console.log('Next actions:');
308
+ console.log(' 1) Fully restart After Effects.');
309
+ console.log(' 2) Open the panel: Window > Extensions (Beta) > ae-agent-skill.');
310
+ console.log(' 3) Run: ae-cli health');
311
+ }
312
+
313
+ async function main() {
314
+ const [command, ...rest] = process.argv.slice(2);
315
+
316
+ if (!command || command === '-h' || command === '--help') {
317
+ printHelp();
318
+ return;
319
+ }
320
+
321
+ if (command !== 'install') {
322
+ throw new Error(`Unknown command: ${command}`);
323
+ }
324
+
325
+ const opts = parseArgs(rest);
326
+ if (opts.help) {
327
+ printHelp();
328
+ return;
329
+ }
330
+
331
+ await installCommand(opts);
332
+ }
333
+
334
+ main().catch((err) => {
335
+ console.error(`ERROR: ${err.message}`);
336
+ process.exit(1);
337
+ });