afterpresets 0.2.0 → 0.3.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/README.md +6 -4
- package/native-runtime/CMakeLists.txt +82 -0
- package/native-runtime/README.md +40 -0
- package/native-runtime/THIRD_PARTY_NOTICES.md +14 -0
- package/native-runtime/include/ap_native.h +223 -0
- package/native-runtime/src/apply.cpp +199 -0
- package/native-runtime/src/crypto.cpp +225 -0
- package/native-runtime/src/crypto.hpp +51 -0
- package/native-runtime/src/dialog.cpp +62 -0
- package/native-runtime/src/dialog.hpp +35 -0
- package/native-runtime/src/engine.cpp +909 -0
- package/native-runtime/src/engine.hpp +232 -0
- package/native-runtime/src/fsutil.cpp +291 -0
- package/native-runtime/src/fsutil.hpp +57 -0
- package/native-runtime/src/json.cpp +326 -0
- package/native-runtime/src/json.hpp +70 -0
- package/native-runtime/src/platform.hpp +83 -0
- package/native-runtime/src/platform_common.cpp +60 -0
- package/native-runtime/src/platform_mac.mm +314 -0
- package/native-runtime/src/platform_win.cpp +277 -0
- package/native-runtime/src/runtime.cpp +473 -0
- package/native-runtime/src/util.cpp +169 -0
- package/native-runtime/src/util.hpp +32 -0
- package/native-runtime/src/zip.cpp +243 -0
- package/native-runtime/src/zip.hpp +58 -0
- package/native-runtime/third_party/monocypher/LICENCE.md +167 -0
- package/native-runtime/third_party/monocypher/monocypher-ed25519.c +500 -0
- package/native-runtime/third_party/monocypher/monocypher-ed25519.h +140 -0
- package/native-runtime/third_party/monocypher/monocypher.c +2956 -0
- package/native-runtime/third_party/monocypher/monocypher.h +321 -0
- package/native-runtime/third_party/tinf/LICENSE +22 -0
- package/native-runtime/third_party/tinf/crc32.c +57 -0
- package/native-runtime/third_party/tinf/tinf.h +140 -0
- package/native-runtime/third_party/tinf/tinflate.c +636 -0
- package/native-runtime/vendor-files.json +39 -0
- package/package.json +6 -3
- package/runtime/docs/cep/host.md +8 -0
- package/runtime/docs/common/README.md +1 -1
- package/runtime/docs/native/README.md +44 -0
- package/runtime/docs/native/debugging.md +140 -0
- package/runtime/docs/native/rules.md +402 -0
- package/runtime/docs/native/testing.md +154 -0
- package/runtime/package.json +1 -1
- package/runtime/scripts/ae-sdk-docs/config.js +102 -0
- package/runtime/scripts/ae-sdk-docs/convert.js +430 -0
- package/runtime/scripts/ae-sdk-docs/index.js +314 -0
- package/runtime/scripts/ae-sdk-docs/source.js +205 -0
- package/runtime/scripts/ae-sdk-docs/split.js +170 -0
- package/runtime/scripts/ae-sdk-docs.js +72 -0
- package/runtime/src/host.js +1 -1
- package/src/api/error-messages.js +1 -1
- package/src/check/check.js +3 -3
- package/src/check/native.js +14 -0
- package/src/check/pricing.js +1 -1
- package/src/cli.js +2 -2
- package/src/commands/build.js +8 -2
- package/src/commands/check.js +10 -2
- package/src/commands/dev.js +9 -2
- package/src/commands/docs.js +5 -1
- package/src/commands/init.js +10 -7
- package/src/commands/push.js +5 -2
- package/src/commands/sdk.js +14 -0
- package/src/commands/testers.js +1 -1
- package/src/commands/upgrade.js +2 -2
- package/src/commands/whoami.js +1 -1
- package/src/dev/bridge.js +8 -0
- package/src/dev/native.js +3 -0
- package/src/dev/session.js +4 -3
- package/src/mcp/extendscript.js +9 -8
- package/src/mcp/host-target.js +1 -0
- package/src/mcp/panels.js +1 -1
- package/src/mcp/png.js +1 -0
- package/src/mcp/server.js +2 -2
- package/src/mcp/tools/capture.js +3 -0
- package/src/mcp/tools/host.js +1 -1
- package/src/mcp/tools/native.js +6 -0
- package/src/mcp/tools/project.js +48 -32
- package/src/mcp/tools/releases.js +23 -20
- package/src/native/build.js +27 -0
- package/src/native/cmake/CMakeLists.txt +88 -0
- package/src/native/cmake/zig-windows.cmake +19 -0
- package/src/native/effect.js +3 -0
- package/src/native/install.js +8 -0
- package/src/native/installers.js +4 -0
- package/src/native/pipl.js +20 -0
- package/src/native/restart.js +2 -0
- package/src/native/sdk.js +8 -0
- package/src/project/docs.js +3 -3
- package/src/project/project.js +2 -2
- package/src/project/template.js +5 -4
- package/src/prompt.js +1 -1
- package/src/release/native.js +3 -0
- package/src/release/publish.js +1 -1
- package/src/system/run-command.js +1 -1
- package/src/targets.js +1 -1
- package/template/cep/AGENTS.md +99 -164
- package/template/cep/README.md +7 -2
- package/template/cep/docs/code_guide.md +67 -0
- package/template/cep/docs/initial_setup.md +58 -12
- package/template/cep/docs/release_checklist.md +24 -0
- package/template/hosts/AGENTS.two-apps.md +12 -0
- package/template/native/.mcp.json +8 -0
- package/template/native/AGENTS.md +105 -0
- package/template/native/README.md +71 -0
- package/template/native/docs/code_guide.md +72 -0
- package/template/native/docs/initial_setup.md +179 -0
- package/template/native/docs/release_checklist.md +24 -0
- package/template/native/effect.json +16 -0
- package/template/native/gitignore +8 -0
- package/template/native/package.json +14 -0
- package/template/native/src/Effect.cpp +224 -0
- package/template/native/src/License.cpp +95 -0
- package/template/native/src/License.h +29 -0
- package/template/native/src/Render.h +68 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
// Generates the After Effects SDK topic files for native (C++) projects.
|
|
5
|
+
//
|
|
6
|
+
// node scripts/ae-sdk-docs.js --out <dir> download the pinned guide (cached) and generate
|
|
7
|
+
// node scripts/ae-sdk-docs.js --out <dir> --source <checkout> [--allow-unpinned]
|
|
8
|
+
// options: --cache <dir> (default ~/.afterpresets/cache), --quiet, --json
|
|
9
|
+
//
|
|
10
|
+
// Adobe's guide is not redistributed: it is downloaded by the user's machine
|
|
11
|
+
// from GitHub at the commit pinned in ae-sdk-docs/config.js and checked
|
|
12
|
+
// against the pinned digest. See ../docs/native/README.md.
|
|
13
|
+
|
|
14
|
+
const { generate } = require("./ae-sdk-docs/index");
|
|
15
|
+
|
|
16
|
+
function parseArgs(argv) {
|
|
17
|
+
const opts = {};
|
|
18
|
+
for (let i = 0; i < argv.length; i++) {
|
|
19
|
+
const arg = argv[i];
|
|
20
|
+
const value = () => {
|
|
21
|
+
if (i + 1 >= argv.length) throw new Error(arg + " needs a value.");
|
|
22
|
+
return argv[++i];
|
|
23
|
+
};
|
|
24
|
+
if (arg === "--out") opts.out = value();
|
|
25
|
+
else if (arg === "--source") opts.source = value();
|
|
26
|
+
else if (arg === "--cache") opts.cacheDir = value();
|
|
27
|
+
else if (arg === "--allow-unpinned") opts.allowUnpinned = true;
|
|
28
|
+
else if (arg === "--quiet") opts.quiet = true;
|
|
29
|
+
else if (arg === "--json") opts.json = true;
|
|
30
|
+
else if (arg === "--help" || arg === "-h") opts.help = true;
|
|
31
|
+
else throw new Error("Unknown option " + arg);
|
|
32
|
+
}
|
|
33
|
+
return opts;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function main() {
|
|
37
|
+
let opts;
|
|
38
|
+
try {
|
|
39
|
+
opts = parseArgs(process.argv.slice(2));
|
|
40
|
+
} catch (error) {
|
|
41
|
+
console.error(error.message);
|
|
42
|
+
process.exitCode = 2;
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (opts.help || !opts.out) {
|
|
46
|
+
console.log("Usage: node scripts/ae-sdk-docs.js --out <dir> [--source <checkout>] [--allow-unpinned] [--cache <dir>] [--quiet] [--json]");
|
|
47
|
+
if (!opts.help) process.exitCode = 2;
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const result = await generate(opts);
|
|
51
|
+
if (opts.json) {
|
|
52
|
+
console.log(JSON.stringify(result, null, 2));
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (!opts.quiet) {
|
|
56
|
+
const width = Math.max.apply(null, result.files.map((f) => f.name.length));
|
|
57
|
+
result.files.forEach((f) => console.log(f.name.padEnd(width) + " " + String(f.tokens).padStart(6)));
|
|
58
|
+
console.log("");
|
|
59
|
+
}
|
|
60
|
+
const sizes = result.files.map((f) => f.tokens).sort((a, b) => a - b);
|
|
61
|
+
console.log(
|
|
62
|
+
result.files.length + " files from " + result.pages + " pages, about " + result.totalTokens + " tokens (source pages: about " + result.sourceTokens + "). " +
|
|
63
|
+
"Median file " + sizes[Math.floor(sizes.length / 2)] + ", largest " + sizes[sizes.length - 1] + "."
|
|
64
|
+
);
|
|
65
|
+
console.log("Source: commit " + result.commit + ", " + result.digest + " (" + result.from + ")");
|
|
66
|
+
result.warnings.forEach((w) => console.warn("warning: " + w));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
main().catch((error) => {
|
|
70
|
+
console.error("ae-sdk-docs: " + error.message);
|
|
71
|
+
process.exitCode = 1;
|
|
72
|
+
});
|
package/runtime/src/host.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(
|
|
1
|
+
(function(d){"use strict";var s=d.AP,i=s._,S=/^[A-Za-z_$][A-Za-z0-9_$]*(\.[A-Za-z_$][A-Za-z0-9_$]*)*$/,y="EvalScript error.",o="idle",f=null,p=[];function a(e){var t=JSON.stringify(e===void 0?null:e);return t===void 0&&(t="null"),t.replace(/\u2028/g,"\\u2028").replace(/\u2029/g,"\\u2029")}i.jsxLiteral=a;function h(e,t){for(var n=[],r=0;r<t.length;r++)n.push(a(t[r]));return["(function () {"," function apQ(s) { s = String(s); return '\"' + s.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"')"," .replace(/\\r/g, '\\\\r').replace(/\\n/g, '\\\\n').replace(/\\t/g, '\\\\t') + '\"'; }"," try {"," var r = "+e+"("+n.join(", ")+");"," if (r === undefined || r === null) return 'null';"," return String(r);"," } catch (e) {"," return '{\"error\":' + apQ(e && e.message ? e.message : e) + '}';"," }","}())"].join("\n")}i.wrapHostCall=h;function v(e,t){var n=i.cs();if(!n){t(JSON.stringify({error:"CEP host API is unavailable."}));return}n.evalScript(e,t)}function c(){try{var e=i.cs(),t=e&&e.getHostEnvironment();return t&&t.appName?String(t.appName):null}catch(n){return null}}i.hostAppName=c;function R(e){return!!e&&typeof e.code=="string"&&!!e.code}function _(){var e=d.AP_JSX;if(!e||typeof e!="object")return{missing:"This bundle has no host script (AP_JSX). Rebuild it with the AfterPresets CLI."};var t=c();return t&&Object.prototype.hasOwnProperty.call(e,t)&&R(e[t])?e[t]:{missing:"This extension has no host script for "+(t||"this app")+"."}}function O(){var e=_();return e.missing?"'AP_LOAD_ERROR:' + "+a(e.missing):"try { eval("+a(e.code)+"); 'ok'; } catch (e) { 'AP_LOAD_ERROR:' + (e && e.message ? e.message : e); }"}function g(e){if(e=e||function(){},o==="loaded"){e(null);return}if(o==="failed"){e(f);return}if(p.push(e),o==="loading")return;o="loading";function t(r){o=r?"failed":"loaded",f=r,r&&i.log("error",r);var u=p;p=[];for(var l=0;l<u.length;l++)u[l](r)}var n=O();v(n,function(r){t(r==="ok"?null:String(r||"Could not load host script.").replace(/^AP_LOAD_ERROR:/,"Could not load host script: "))})}i.preloadHost=function(){g(null)};function A(e){if(e===y)return{error:"The host script failed to run."};if(e==null||e===""||e==="undefined")return null;try{return JSON.parse(e)}catch(t){return{error:"The host returned a non-JSON result.",raw:String(e)}}}i.parseHostResult=A,s.host={eval:function(e,t,n){if(typeof t=="function"&&n===void 0&&(n=t,t=[]),n=typeof n=="function"?n:function(){},typeof e!="string"||!S.test(e)){n({error:"Invalid host function name: "+String(e)});return}t==null&&(t=[]),Object.prototype.toString.call(t)!=="[object Array]"&&(t=[t]);var r=h(e,t);g(function(u){if(u){n({error:String(u)});return}v(r,function(l){n(A(l))})})},reload:function(){o="idle",f=null}},Object.defineProperty(s.host,"app",{enumerable:!0,get:c})})(typeof window!="undefined"?window:this);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";function n(e,s){const a=s&&s.webBase?s.webBase:"https://afterpresets.com",t=e.details||{};switch(e.code){case"push_quota_exceeded":return{message:"You have used all production pushes for this month.",hint:"Test-channel pushes and rollbacks do not count. Push to test with `--channel test`, wait for the next UTC month, or upgrade to Creator or Studio: "+a+"/membership"};case"version_not_greater":return{message:"This version is not greater than every existing release.",hint:"Check `ap releases list`, then push again with a larger --bump."};case"runtime_retired":return{message:"The @afterpresets/runtime version in this build has been retired.",hint:"Update it with `ap upgrade`, test with `ap dev`, and push again. Published releases keep working."};case"unauthorized":case"invalid_token":case"token_revoked":return{message:"Your CLI login is no longer valid.",hint:"Run `ap login` again."};case"forbidden":case"not_owner":return{message:"Your account does not own this extension.",hint:"Check that you are logged in as the right account with `ap whoami`."};case"not_found":case"product_not_found":return{message:e.serverMessage||"Not found.",hint:"Check the bundle ID in afterpresets.json and `ap whoami`."};case"expired_token":return{message:"The login request expired before it was approved.",hint:"Run `ap login` again."};case"slug_taken":case"slug_conflict":return{message:e.serverMessage||"That slug is already used by one of your extensions.",hint:"Choose a different slug."};case"tester_limit":case"tester_limit_reached":return{message:"
|
|
1
|
+
"use strict";function n(e,s){const a=s&&s.webBase?s.webBase:"https://afterpresets.com",t=e.details||{};switch(e.code){case"push_quota_exceeded":return{message:"You have used all production pushes for this month.",hint:"Test-channel pushes and rollbacks do not count. Push to test with `--channel test`, wait for the next UTC month, or upgrade to Creator or Studio: "+a+"/membership"};case"version_not_greater":return{message:"This version is not greater than every existing release.",hint:"Check `ap releases list`, then push again with a larger --bump."};case"runtime_retired":return{message:"The @afterpresets/runtime version in this build has been retired.",hint:"Update it with `ap upgrade`, test with `ap dev`, and push again. Published releases keep working."};case"unauthorized":case"invalid_token":case"token_revoked":return{message:"Your CLI login is no longer valid.",hint:"Run `ap login` again."};case"forbidden":case"not_owner":return{message:"Your account does not own this extension.",hint:"Check that you are logged in as the right account with `ap whoami`."};case"not_found":case"product_not_found":return{message:e.serverMessage||"Not found.",hint:"Check the bundle ID in afterpresets.json and `ap whoami`."};case"expired_token":return{message:"The login request expired before it was approved.",hint:"Run `ap login` again."};case"slug_taken":case"slug_conflict":return{message:e.serverMessage||"That slug is already used by one of your extensions.",hint:"Choose a different slug."};case"tester_limit":case"tester_limit_reached":return{message:e.serverMessage||"You've reached your plan's tester limit.",hint:"Remove a tester with `ap testers remove <email>`, or upgrade your plan on afterpresets.com/membership."};case"payload_too_large":return{message:"The bundle is larger than 200 MB.",hint:"Remove unused assets from the asset folders."};case"network_error":return{message:e.serverMessage,hint:"Check your internet connection. AI agents: your sandbox may be blocking the internet; ask the user to allow this command to run outside the sandbox. (When testing locally, check AP_API_BASE.)"};default:return{message:e.serverMessage||"The AfterPresets API returned HTTP "+e.status+".",hint:t.manageDevicesUrl?"Manage devices: "+t.manageDevicesUrl:null}}}module.exports={describeApiError:n};
|
package/src/check/check.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
"use strict";const l=require("fs"),m=require("path"),{processIndexHtml:L}=require("../build/html"),{panelSources:q,SRC_DIR:b}=require("../build/build"),{bundlePanel:
|
|
2
|
-
`;)n++;continue}if(i==="/"&&f==="*"){const u=s.indexOf("*/",n+2);n=u===-1?s.length:u+2;continue}e+=i,n++}return e}function
|
|
3
|
-
`),c=
|
|
1
|
+
"use strict";const l=require("fs"),m=require("path"),{processIndexHtml:L}=require("../build/html"),{panelSources:q,SRC_DIR:b}=require("../build/build"),{bundlePanel:k,assetUrl:v}=require("../build/bundle"),{JSX_DIR:x,jsxDirFiles:T,hostDirOf:F}=require("../build/jsx"),{TARGETS:I,projectTargets:R}=require("../targets"),E=["signed_out","signing_in","checking","active","not_owned","refunded","offline_grace","offline_expired","error"],O=/\blicense\s*:\s*(?:true\b|\{)/,N=/\bAP\s*\.\s*boot\s*\(/;function j(s){if(!l.existsSync(s))return[];const e=[];return l.readdirSync(s,{withFileTypes:!0}).forEach(n=>{const o=m.join(s,n.name);n.isDirectory()?e.push.apply(e,j(o)):n.isFile()&&e.push(o)}),e}function P(s){let e="",n=0,o=null;for(;n<s.length;){const i=s[n],f=s[n+1];if(o){if(e+=i,i==="\\"){e+=f||"",n+=2;continue}i===o&&(o=null),n++;continue}if(i==='"'||i==="'"||i==="`"){o=i,e+=i,n++;continue}if(i==="/"&&f==="/"){for(;n<s.length&&s[n]!==`
|
|
2
|
+
`;)n++;continue}if(i==="/"&&f==="*"){const u=s.indexOf("*/",n+2);n=u===-1?s.length:u+2;continue}e+=i,n++}return e}function B(s,e){return m.relative(s,e).split(m.sep).join("/")}function d(s,e){return m.join(s,e.split("/").join(m.sep))}function D(s){const e=P(s),n=O.test(e);return{boots:N.test(e),licensed:n,rendersLicense:/\brenderLicense\b/.test(e),browserStorage:/\b(localStorage|sessionStorage|indexedDB)\b/.test(e),missingStatuses:E.filter(o=>!new RegExp("[\"'`]"+o+"[\"'`]").test(e))}}async function $(s){if(s.settings&&s.settings.platform==="native")return require("./native").checkNativeProject(s);const e=s.root,n=[],o=[],i=(t,r,h)=>t.push({code:r,message:h}),f={},u=[];for(const t of s.panels){const r=q(t.id);f[r.html]=!0;const h={id:t.id,boots:!1,licensed:!1};if(u.push(h),!l.existsSync(d(e,r.dir))){i(n,"missing_panel",'afterpresets.json lists panel "'+t.id+'" but '+r.dir+"/ does not exist. Run `ap add panel "+t.id+"` or remove it from afterpresets.json.");continue}if([r.html,r.js].forEach(a=>{l.existsSync(d(e,a))||i(n,"missing_entry","Missing "+a+".")}),l.existsSync(d(e,r.html))){const a=d(e,r.html),w=L(l.readFileSync(a,"utf8"),r.html,r.js,g=>v(e,s.settings,a,g));w.errors.forEach(g=>i(n,"index_html",g)),w.warnings.forEach(g=>i(o,"index_html",g))}if(!l.existsSync(d(e,r.js)))continue;let p;try{p=await k(e,r.js,s.settings)}catch(a){i(n,"bundle_failed",a.message);continue}p.inputs.forEach(a=>f[a]=!0);const A=p.inputs.filter(a=>/\.(js|html)$/i.test(a)&&!/(^|\/)node_modules\//.test(a)).map(a=>l.readFileSync(d(e,a),"utf8")).join(`
|
|
3
|
+
`),c=D(A);h.boots=c.boots,h.licensed=c.licensed,c.boots||i(n,"missing_boot",'Panel "'+t.id+'" never calls AP.boot(...). Start the panel from AP.boot({ start: ... }) in '+r.js+"."),c.browserStorage&&i(o,"browser_storage",'Panel "'+t.id+'" uses localStorage, sessionStorage or indexedDB. In CEP each panel gets its own copy and it can be cleared; save data with AP.storage instead. See docs/afterpresets/storage.md.'),c.licensed&&!c.rendersLicense&&i(n,"missing_render_license",'Panel "'+t.id+'" boots with licensing on but has no renderLicense. Pass renderLicense: function (api, root) { ... } to AP.boot. See docs/afterpresets/.'),c.licensed&&c.rendersLicense&&c.missingStatuses.forEach(a=>i(o,"license_status_unhandled",'Panel "'+t.id+'": license status "'+a+'" is not handled by its license screen code. See docs/afterpresets/.'))}j(d(e,b)).map(t=>B(e,t)).filter(t=>/\.(js|css|html)$/i.test(t)&&!f[t]).forEach(t=>i(o,"unused_file",t+" is not imported by any panel, so it is not built. Import it, or delete it."));const C=R(s.settings).map(t=>t.jsxDir).filter(Boolean),S={};T(e).forEach(t=>{const r=F(t);if(r&&C.indexOf(r)===-1){if(!S[r]){S[r]=!0;const h=I.find(p=>p.jsxDir===r).appName;i(o,"jsx_other_host",x+"/"+r+"/ is only loaded in "+h+", which this project does not target, so it is not built. Add the app with `ap add host "+r+"`, or move the files.")}return}/\.jsx$/i.test(t)||i(o,"jsx_not_loaded",t+" is not loaded: only .jsx files in "+x+"/ go into the build.")});const y={};s.panels.forEach(t=>y[t.id]=!0);const _=d(e,b+"/panels");return l.existsSync(_)&&l.readdirSync(_,{withFileTypes:!0}).filter(t=>t.isDirectory()&&!y[t.name]).forEach(t=>i(o,"unlisted_panel",b+"/panels/"+t.name+'/ is not listed in afterpresets.json, so it is not built. Add it to "panels" or delete the folder.')),{errors:n,warnings:o,panels:u,licensed:u.some(t=>t.licensed)}}function H(s,e){e.errors.forEach(o=>s.error(o.message)),e.warnings.forEach(o=>s.warn(o.message));const n=e.errors.length+" error(s), "+e.warnings.length+" warning(s)";e.errors.length?s.log(s.style.red("Check failed: "+n)):e.warnings.length?s.log(s.style.yellow("Check passed with "+n)):s.success("Check passed")}module.exports={checkProject:$,printCheck:H,stripComments:P,analyzePanelCode:D,LICENSE_STATUSES:E};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";const U=require("crypto"),L=require("fs"),R=require("path"),H=/\.(c|cc|cpp|cxx|h|hh|hpp|hxx|m|mm|inl)$/i,B=/\.(c|cc|cpp|cxx|h|hh|hpp|hxx|m|mm|inl|r|rc|txt|md|json|cmake|plist|js|jsx|py|sh|bat|command)$/i,K=["build","dist","dist-dev","node_modules",".git","vendor","out"],X=["docs/afterpresets"],$=2*1024*1024,I={slider:"fs_d",angle:"ad",color:"cd",point:"td",checkbox:"bd",popup:"pd"},W=["sd","fs_d","ad","cd","td","bd","pd","ld","path_d","arb_d","button_d"],j=["A.h","AE_Effect.h","AE_EffectCB.h","AE_EffectCBSuites.h","AE_EffectSuites.h","AE_EffectSuitesHelper.h","AE_EffectUI.h","AE_EffectPixelFormat.h","AE_EffectGPUSuites.h","AE_EffectVers.h","AE_GeneralPlug.h","AE_GeneralPlugOld.h","AE_GeneralPlugPanels.h","AE_Hook.h","AE_IO.h","AE_IO_FileExt.h","AE_Macros.h","AE_PluginData.h","AEConfig.h","AEFX_ArbParseHelper.h","AEFX_ChannelDepthTpl.h","AEFX_SuiteHelper.h","AEGP_SuiteHandler.h","AEGP_SuiteHandler.cpp","AEGP_Utils.h","AE_ChannelSuites.h","AE_AdvEffectSuites.h","AE_ComputeCacheSuite.h","DrawbotSuite.h","FIEL_Public.h","Param_Utils.h","PF_Masks.h","PR_Public.h","PT_Public.h","SPAccess.h","SPAdapts.h","SPBasic.h","SPBckDbg.h","SPCaches.h","SPConfig.h","SPErrorCodes.h","SPErrors.h","SPFiles.h","SPHost.h","SPInterf.h","SPMData.h","SPPiPL.h","SPPlugs.h","SPProps.h","SPSuites.h","SPTypes.h","Smart_Utils.h","Smart_Utils.cpp","String_Utils.h","entry.h","AE_Effect.r","AE_General.r","AE_PluginData.r","AE_SDK.xcconfig","PiPLtool.exe","PiPLTool.exe"],Z=[/ADOBE\s+CONFIDENTIAL/,/Copyright\s*(?:\(c\)|©)?\s*(?:19|20)\d\d(?:\s*[-,]\s*(?:19|20)\d\d)?[^\n]{0,20}Adobe/i,/Adobe\s+Systems\s+Incorporated/i,/Adobe\s+permits\s+you\s+to\s+use,\s+modify,\s+and\s+distribute/i],q=["3521eb14b4afc632c9fa9e77093bfc501832c04adaf864c46e1bd8776f87f77e","0461c1fe4a979d33a1d8bda78c548d2af04b38e7ac772d405fe1efea3010d38f"],z=[{re:/\b(?:usleep|nanosleep|sleep|Sleep|SleepEx)\s*\(/,what:"sleeps"},{re:/\bsleep_for\s*\(|\bsleep_until\s*\(/,what:"sleeps"},{re:/\b(?:socket|getaddrinfo|gethostbyname|WSAStartup|recv|send)\s*\(/,what:"uses the network"},{re:/\bcurl_\w+\s*\(|\bWinHttp\w*\s*\(|\bInternetOpen\w*\s*\(|\bNSURLSession\b|\bNSURLConnection\b|\bCFNetwork\b|\bURLSession\b/,what:"uses the network"},{re:/\b(?:system|popen|_popen|fork|execv|execvp|CreateProcess\w*|ShellExecute\w*)\s*\(/,what:"starts a process"},{re:/\bMessageBox\w*\s*\(|\bNSAlert\b|\bstd::cin\b|\bgetchar\s*\(/,what:"waits for the user"}];function N(t,o,e){let i;try{i=L.readdirSync(o,{withFileTypes:!0})}catch{return e}return i.forEach(n=>{const r=R.join(o,n.name),c=R.relative(t,r).split(R.sep).join("/");if(n.isDirectory()){if(K.indexOf(n.name)!==-1||X.indexOf(c)!==-1||/\.(plugin|bundle)$/i.test(n.name))return;N(t,r,e)}else n.isFile()&&e.push(c)}),e}function v(t,o){const e=R.join(t,o.split("/").join(R.sep));try{return L.statSync(e).size>$?null:L.readFileSync(e,"utf8")}catch{return null}}function M(t){let o="",e=0;const i=t.length;let n=!0;for(;e<i;){const r=t[e],c=t[e+1];if(n&&r==="#"){for(;e<i&&t[e]!==`
|
|
2
|
+
`;){if(t[e]==="\\"&&t[e+1]===`
|
|
3
|
+
`){o+=`
|
|
4
|
+
`,e+=2;continue}o+=" ",e++}continue}if(r==="/"&&c==="/"){for(;e<i&&t[e]!==`
|
|
5
|
+
`;)o+=" ",e++;continue}if(r==="/"&&c==="*"){for(o+=" ",e+=2;e<i&&!(t[e]==="*"&&t[e+1]==="/");)o+=t[e]===`
|
|
6
|
+
`?`
|
|
7
|
+
`:" ",e++;e<i&&(o+=" ",e+=2);continue}if(r==='"'||r==="'"){for(o+=r,e++;e<i&&t[e]!==r&&t[e]!==`
|
|
8
|
+
`;)t[e]==="\\"&&(o+=" ",e++),o+=e<i?t[e]===`
|
|
9
|
+
`?`
|
|
10
|
+
`:" ":"",e++;e<i&&t[e]===r&&(o+=r,e++);continue}r===`
|
|
11
|
+
`?n=!0:r!==" "&&r!==" "&&r!=="\r"&&(n=!1),o+=r,e++}return o}function F(t,o){let e=1;for(let i=0;i<o&&i<t.length;i++)t.charCodeAt(i)===10&&e++;return e}function V(t){const o={};return t.split(`
|
|
12
|
+
`).forEach((e,i)=>{const n=e.match(/ap-check:\s*allow\b([^\n]*)/);n&&(o[i+1]=n[1])}),o}function T(t,o,e){return[o,o-1].some(i=>{const n=t[i];return n!==void 0&&(!n.trim()||n.indexOf(e)!==-1)})}const Y=/^(?:const|constexpr|consteval|constinit|typedef|using|extern|template|return|namespace|class|struct|union|enum|friend|case|default|goto|public|private|protected|static_assert|thread_local|delete|throw|break|continue|operator|virtual|explicit|inline\s+constexpr)\b/,J=/\b(?:std::)?(?:atomic\w*|mutex|shared_mutex|recursive_mutex|once_flag|condition_variable)\b|\bthread_local\b|\bconst\b|\bconstexpr\b/;function G(t){const o=[],e=[];let i=-1,n="",r=0;const c=()=>e.filter(a=>a==="block").length;function A(a,m){const _=n.replace(/\s+/g," ").trim(),P=i;if(n="",i=-1,!_)return;const p=c(),E=/^(?:\[\[[^\]]*\]\]\s*)?static\b/.test(_);if(p>0&&!E)return;const s=_.replace(/^(?:\[\[[^\]]*\]\]\s*)?static\s+/,"").replace(/^inline\s+/,"");if(Y.test(s)||J.test(_)||a==="{")return;const h=s.indexOf("{"),d=s.indexOf("="),f=h!==-1&&(d===-1||h<d)?h:d,l=s.indexOf("(");if(l!==-1&&(f===-1||l<f))return;const u=f===-1?s:s.slice(0,f);/^[A-Za-z_][\w:<>,\s*&]*[\s*&]\**\s*[A-Za-z_]\w*\s*(\[[^\]]*\]\s*)*$/.test(u.trim())&&o.push({line:F(t,P),text:_.length>80?_.slice(0,77)+"...":_})}for(let a=0;a<t.length;a++){const m=t[a];if(m==="("&&r++,m===")"&&(r=Math.max(0,r-1)),r>0){n+=m;continue}if(m===";"){A(";",a);continue}if(m==="{"){const _=n.replace(/\s+/g," ").trim();if(/=\s*$/.test(_)||/^(?:static\s+)?[A-Za-z_][\w:<>,\s*&]*\s[A-Za-z_]\w*\s*$/.test(_)&&!/^(?:namespace|class|struct|union|enum|extern)\b/.test(_)&&c()===0&&!/\)\s*(?:const)?\s*$/.test(_)){let p=1;n+="{";let E=a+1;for(;E<t.length&&p>0;E++)t[E]==="{"?p++:t[E]==="}"&&p--,n+=t[E];a=E-1;continue}const P=/^(?:inline\s+)?namespace\b/.test(_)||/^extern\s*""\s*$/.test(_)||/^extern\s*"C"\s*$/.test(_);A("{",a),e.push(P?"ns":"block");continue}if(m==="}"){A("}",a),e.pop();continue}i===-1&&!/\s/.test(m)&&(i=a),n+=m}return o}function Q(t){return String(t).toUpperCase()}function ee(t,o){const e=o||{},i=t.root,n=[],r=[],c=(s,h,d,f,l)=>s.push({code:h,file:d||null,line:f||null,message:(d?d+(f?":"+f:"")+": ":"")+l}),A=e.effectLib||te("../native/effect");let a=null;if(!A)c(r,"effect_json","effect.json",null,"This CLI cannot validate effect.json (no native toolchain). Run `ap upgrade`.");else try{a=A.loadEffect(i)}catch(s){(s&&Array.isArray(s.problems)?s.problems:[s.message]).forEach(d=>c(n,"effect_json","effect.json",null,d))}const m=t.settings.releasedEffect;a&&m&&A&&typeof A.compatibilityProblems=="function"&&A.compatibilityProblems({matchName:m.matchName,params:m.params||[]},a).forEach(s=>c(n,"effect_changed","effect.json",null,s+" (Version "+(m.version||"?")+" was released with the old value.)"));const _=N(i,i,[]),P=_.filter(s=>/^src\//.test(s)&&H.test(s)),p={};P.forEach(s=>{const h=v(i,s);h!==null&&(p[s]={raw:h,code:M(h),allow:V(h)})});const E=Object.keys(p).map(s=>p[s].code).join(`
|
|
13
|
+
`);if(P.length&&!/\bEffectMain\b/.test(E)&&c(n,"missing_entry_point","src/",null,"No EffectMain function. After Effects calls the entry point named in the PiPL (EffectMain); keep the starter's `PF_Err EffectMain(...)` with its export macro."),a){const s=a.outFlags||[],h=a.outFlags2||[];Object.keys(p).forEach(f=>{const l=p[f],u=/\bout_flags(2?)\s*(\|?=)\s*([^;]+);/g;let b;for(;b=u.exec(l.code);){const g=b[1]?"AP_OUT_FLAGS2":"AP_OUT_FLAGS",D=F(l.code,b.index);if(T(l.allow,D,"out_flags"))continue;const y=b[1]?A.OUT_FLAGS2||{}:A.OUT_FLAGS||{},S=(b[3].match(/PF_OutFlag2?_(\w+)/g)||[]).map(x=>x.replace(/^PF_OutFlag2?_/,"")).filter(x=>Object.prototype.hasOwnProperty.call(y,x));(b[2]==="|="?S.length>0:!/\bAP_OUT_FLAGS2?\b/.test(b[3])||S.length>0)&&c(n,"out_flags",f,D,"Set out_data->out_flags"+b[1]+" = "+g+'; exactly (generated from effect.json). The PiPL carries the same flags and After Effects refuses an effect whose flags disagree. Change flags in effect.json "outFlags'+b[1]+'".')}}),h.indexOf("SUPPORTS_SMART_RENDER")!==-1&&P.length&&!/\bPF_Cmd_SMART_RENDER\b/.test(E)&&c(n,"smart_render","src/",null,"effect.json promises Smart Render (SUPPORTS_SMART_RENDER) but no code handles PF_Cmd_SMART_PRE_RENDER / PF_Cmd_SMART_RENDER. Handle both (see the starter), or remove the flag."),s.indexOf("DEEP_COLOR_AWARE")!==-1&&P.length&&!/\bPF_Pixel16\b|\bPF_PixelFormat_ARGB64\b|\biterate16\b|\bPF_WORLD_IS_DEEP\b/.test(E)&&c(r,"bit_depth","src/",null,"effect.json says 16 bpc (DEEP_COLOR_AWARE) but no code handles PF_Pixel16 / PF_PixelFormat_ARGB64. Render 16-bpc worlds too, or remove the flag."),h.indexOf("FLOAT_COLOR_AWARE")!==-1&&P.length&&!/\bPF_PixelFloat\b|\bPF_PixelFormat_ARGB128\b|\bPF_Pixel32\b/.test(E)&&c(r,"bit_depth","src/",null,"effect.json says 32 bpc (FLOAT_COLOR_AWARE) but no code handles PF_PixelFloat / PF_PixelFormat_ARGB128. Render float worlds too, or remove the flag."),s.indexOf("I_DO_DIALOG")!==-1&&P.length&&!/\bPF_Cmd_DO_DIALOG\b/.test(E)&&c(r,"dialog","src/",null,"effect.json sets I_DO_DIALOG (the Options button) but no code handles PF_Cmd_DO_DIALOG."),h.indexOf("SUPPORTS_THREADED_RENDERING")!==-1&&Object.keys(p).forEach(f=>{const l=p[f];G(l.code).forEach(u=>{T(l.allow,u.line,"mfr_global")||c(n,"mfr_global",f,u.line,"`"+u.text+"` is a mutable global/static, but multi-frame rendering (SUPPORTS_THREADED_RENDERING) renders several frames at once on different threads. Make it const, keep the value in a local variable or the effect's sequence data, or use std::atomic / a mutex. If it is written only in GLOBAL_SETUP/GLOBAL_SETDOWN (before or after every render) or is already thread-safe, put `// ap-check: allow mfr_global` on that line or the line above.")})});const d={};a.params.forEach(f=>d["AP_PARAM_"+Q(f.key)]=f),Object.keys(p).forEach(f=>{const l=p[f],u=(w,S,O)=>{const x=d[w],k=x&&I[x.type];if(!k||S===k||W.indexOf(S)===-1)return;const C=F(l.code,O);T(l.allow,C,"param_union")||c(n,"param_union",f,C,w+" is a "+x.type+' ("'+x.name+'"): read it with u.'+k+" (not u."+S+"); the other members hold garbage for this type.")},b=/\[\s*(AP_PARAM_[A-Z0-9_]+)\s*\]\s*(?:->|\.)\s*u\s*\.\s*(\w+)/g;let g;for(;g=b.exec(l.code);)u(g[1],g[2],g.index);const D=/PF_CHECKOUT_PARAM\s*\(\s*[^,]+,\s*(AP_PARAM_[A-Z0-9_]+)\s*,[^;]*?&\s*([A-Za-z_]\w*)\s*\)/g,y={};for(;g=D.exec(l.code);)y[g[2]]=g[1];Object.keys(y).forEach(w=>{const S=new RegExp("\\b"+w+"\\s*(?:->|\\.)\\s*u\\s*\\.\\s*(\\w+)","g");let O;for(;O=S.exec(l.code);)u(y[w],O[1],O.index)})})}return Object.keys(p).forEach(s=>{const h=p[s],d=/\b65535(?:\.0*)?[fFuU]?\b|\b0[xX][fF]{4}\b/g;let f;for(;f=d.exec(h.code);){const l=F(h.code,f.index);T(h.allow,l,"chan16_max")||c(n,"chan16_max",s,l,"After Effects' 16-bpc white is 32768 (PF_MAX_CHAN16), not 65535. Use PF_MAX_CHAN16, or add `// ap-check: allow chan16_max` if this is not a pixel value.")}h.code.split(`
|
|
14
|
+
`).forEach((l,u)=>{z.forEach(b=>{!b.re.test(l)||T(h.allow,u+1,"blocking_call")||c(n,"blocking_call",s,u+1,"This "+b.what+". After Effects calls Render on many threads at once and waits for it, so it must never wait or do I/O; ap_native already checks licences and updates on its own thread. Move it out of the render path, or add `// ap-check: allow blocking_call` for code that never runs while rendering.")})})}),_.forEach(s=>{const h=R.posix.basename(s);if(j.indexOf(h)!==-1){c(n,"sdk_file",s,null,"This is a file from Adobe's After Effects SDK. Delete it: the build reads the SDK from `ap sdk`'s folder on this computer, and Adobe's files must not be copied into your project or shipped.");return}if(/\.r$/i.test(s)&&/^src\//.test(s)){const u=v(i,s);u&&/\bPiPL\b/.test(u)&&c(r,"pipl_file",s,null,"The build generates the PiPL from effect.json; this hand-written PiPL is not used. Delete it and change effect.json instead.")}if(!B.test(s))return;const d=v(i,s);if(d===null)return;for(const u of Z){const b=u.exec(d);if(b){c(n,"sdk_file",s,F(d,b.index),"This file contains Adobe's SDK copyright text, so it looks copied from the SDK. Write your own code (the SDK guide in docs/afterpresets/sdk/ explains the APIs) and delete the copied text.");break}}const l=(d.match(/\bsave[A-Za-z]{4,20}\b/g)||[]).find(u=>q.indexOf(U.createHash("sha256").update(u).digest("hex"))!==-1);l&&c(n,"private_api",s,F(d,d.indexOf(l)),"`"+l+"` is not part of After Effects' public scripting API. Remove it from the project; use the documented API (to look at frames while developing, use comp_capture).")}),a&&a.license&&a.license.enabled&&P.length&&!/\bap_is_unlocked\s*\(/.test(E)&&c(r,"license_unused","src/",null,"Licensing is on in effect.json but no code calls ap_is_unlocked(). Render must check it (the starter draws a watermark when it returns 0), or buyers without a licence get the full effect."),{errors:n,warnings:r,panels:[],licensed:!!(a&&a.license&&a.license.enabled),native:!0,effect:a}}function te(t){try{return require(t)}catch{return null}}function se(t,o){return{version:o,matchName:t.matchName,params:t.params.map(e=>({id:e.id,type:e.type,name:e.name}))}}module.exports={checkNativeProject:ee,releasedEffectRecord:se,stripCode:M,mutableGlobals:G,SDK_FILE_NAMES:j,UNION_MEMBER:I};
|
package/src/check/pricing.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";const{readCredentials:
|
|
1
|
+
"use strict";const{readCredentials:u}=require("../auth/credentials"),{createApiClient:a}=require("../api/client"),{productPath:l}=require("../api/session");function c(r,t){const e=(r/100).toFixed(2);return t?e+" "+String(t).toUpperCase():e}function d(r,t){const e=[],i=[],s=r?r.priceMinor:void 0;if(s===void 0)return i.push({code:"price_unknown",message:"The API did not report this product's price, so the paid-product licensing rule could not be verified."}),{errors:e,warnings:i};if(!(Number(s)>0))return{errors:e,warnings:i};const o=c(Number(s),r.currency);return!t.licensed&&t.native?(e.push({code:"paid_without_license",message:"This product is paid ("+o+') but effect.json turns licensing off ("license": false). Remove that line (licensing is on by default). See docs/afterpresets/.'}),{errors:e,warnings:i}):t.licensed?(t.panels.filter(n=>n.boots&&!n.licensed).forEach(n=>i.push({code:"paid_panel_unlicensed",message:'Panel "'+n.id+'" boots without licensing although the product is paid. Make sure it does nothing useful without a license.'})),{errors:e,warnings:i}):(e.push({code:"paid_without_license",message:"This product is paid ("+o+") but no panel boots with licensing on. Pass `license: true` and a renderLicense function to AP.boot in src/panels/<id>/main.js. See docs/afterpresets/."}),{errors:e,warnings:i})}async function p(r,t,e){const i=u(r),s=n=>({errors:[],warnings:[{code:"price_unverified",message:"Could not verify the product price ("+n+"). Paid products must boot with licensing on; `ap push` enforces this."}],verified:!1});if(!i)return s("not logged in");let o;try{o=await a(r,i).get(l(t.settings.bundleId))}catch(n){return s(n.code==="network_error"?"offline":n.code||n.message)}return Object.assign(d(o.product||null,e),{verified:!0})}module.exports={pricingProblems:d,verifyPricingOnline:p,formatPrice:c};
|
package/src/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
"use strict";const{parseArgs:c}=require("./args"),{CliError:g}=require("./errors"),{findProjectRoot:m}=require("./project/project"),i=["login","logout","whoami","init","dev","check","add","build","push","releases","testers","docs","upgrade","config","mcp"].map(e=>require("./commands/"+e)),s=require("../package.json").version,l=require("./system/update-check");async function h(e,r){if(!(r==="mcp"||r==="upgrade"||!(e.stdout&&e.stdout.isTTY)))try{const o=await l.newerVersion(e);o&&e.out.log(`
|
|
2
|
-
`+e.out.style.dim(l.noticeText(o)))}catch{}}function a(e){return i.find(r=>r.name===e)||null}function f(e){e.banner("CLI "+s+" \xB7 build and publish After Effects and Premiere Pro extensions"),e.log("Usage: "+e.style.cyan("ap <command>")+` [options]
|
|
1
|
+
"use strict";const{parseArgs:c}=require("./args"),{CliError:g}=require("./errors"),{findProjectRoot:m}=require("./project/project"),i=["login","logout","whoami","init","dev","check","add","build","push","releases","testers","docs","sdk","upgrade","config","mcp"].map(e=>require("./commands/"+e)),s=require("../package.json").version,l=require("./system/update-check");async function h(e,r){if(!(r==="mcp"||r==="upgrade"||!(e.stdout&&e.stdout.isTTY)))try{const o=await l.newerVersion(e);o&&e.out.log(`
|
|
2
|
+
`+e.out.style.dim(l.noticeText(o)))}catch{}}function a(e){return i.find(r=>r.name===e)||null}function f(e){e.banner("CLI "+s+" \xB7 build and publish After Effects and Premiere Pro extensions and effect plugins"),e.log("Usage: "+e.style.cyan("ap <command>")+` [options]
|
|
3
3
|
`),i.forEach(r=>e.log(" "+e.style.cyan(r.name.padEnd(10))+r.summary)),e.log(`
|
|
4
4
|
`+e.style.dim("Run `ap <command> --help` for details. Agent reference: docs/afterpresets/ in your project."))}function d(e,r){e.log("Usage: "+r.usage+`
|
|
5
5
|
`),e.log(r.description)}async function y(e,r){const o=e[0];if(!o&&r.prompt&&!m(r.cwd))return p(r,a("init"),[],n=>require("./commands/init").wizard(n));if(!o||o==="help"||o==="--help"||o==="-h"){const n=o==="help"&&e[1]?a(e[1]):null;return n?d(r.out,n):f(r.out),0}if(o==="--version"||o==="-v")return r.out.log(s),0;const t=a(o);return t?p(r,t,e.slice(1)):(r.out.error("Unknown command: "+o),f(r.out),2)}async function p(e,r,o,t){try{let n;if(t)n=await t(e);else{const u=c(o,r.flags);if(u.flags.help)return d(e.out,r),0;n=await r.run(e,u)}return await h(e,r.name),typeof n=="number"?n:0}catch(n){return n instanceof g?(e.out.error(n.message),n.hint&&e.out.log(" "+n.hint),n.exitCode===2&&e.out.log(" Usage: "+r.usage),n.exitCode||1):(e.out.error("Unexpected error: "+(n&&n.message?n.message:String(n))),e.env.AP_DEBUG?e.out.log(n&&n.stack):e.out.log(" Set AP_DEBUG=1 for the stack trace."),1)}}module.exports={main:y,COMMANDS:i,VERSION:s};
|
package/src/commands/build.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
"use strict";const
|
|
1
|
+
"use strict";const u=require("path"),c=require("../config"),{readCredentials:b}=require("../auth/credentials"),{loadProject:w}=require("../project/project"),{checkProject:v,printCheck:y}=require("../check/check"),{pricingProblems:k}=require("../check/pricing"),{buildProject:B,productionOutDir:I}=require("../build/build"),{CliError:h}=require("../errors");async function f(e,i,t){const n=await v(i);if(t!==void 0){const s=k(t,n);n.errors.push.apply(n.errors,s.errors),n.warnings.push.apply(n.warnings,s.warnings)}if((n.errors.length||n.warnings.length)&&y(e.out,n),n.errors.length)throw new h("Fix the check errors above before building.");return n}async function g(e,i,t,n){n&&n.checked||await f(e,i);const s=c.apiBase(e,b(e));s!==c.DEFAULT_API_BASE&&e.out.warn("This build talks to "+s+" (AP_API_BASE, `ap config`, or your login), not production.");const a=await B(e,i,{outDir:I(i),dev:!1,version:t,apiBase:s,compileJsx:e.compileJsx});return a.warnings.forEach(r=>e.out.warn(r)),a}async function m(e,i,t){const n=require("fs"),s=require("../release/native"),a=s.resolveReleaseOses(e,t&&t.os),r=await e.out.task("Building "+i.settings.name+" for "+s.osesLabel(a),()=>s.buildNativeRelease(e,i,i.version,Object.assign({},t,{os:a})),o=>"Built "+i.settings.bundleId+" "+o.version+e.out.style.dim(" (ap_native "+o.runtimeVersion+")"));r.warnings.forEach(o=>e.out.warn(o));const l=s.assembleNativeZip(e,r.artefacts,{bundleId:i.settings.bundleId,name:i.settings.name,version:r.version,listingUrl:i.settings.listingUrl||null}),d=s.nativeZipPath(i,r.version);n.mkdirSync(u.dirname(d),{recursive:!0}),n.writeFileSync(d,l.buffer);const p=l.entries.filter(o=>!/\.plugin\//.test(o)).concat(r.artefacts.mac?[u.basename(r.artefacts.mac)+"/"]:[]);return e.out.success("Wrote "+u.relative(e.cwd,d)+s.publishedOsSuffix(null,r.oses)+e.out.style.dim(" ("+Math.max(1,Math.round(l.sizeBytes/1024))+" KB: "+p.join(", ")+")")),0}module.exports={name:"build",summary:"Build the production bundle into dist/<bundleId>/",usage:"ap build [--os mac,win]",description:`Runs the check, then for every panel in afterpresets.json writes one main.js with
|
|
2
2
|
the JSXBIN-compiled jsx/ folder, the panel's imported CSS, its index.html <body>,
|
|
3
3
|
the runtime and src/panels/<id>/main.js with its imports, plus a thin index.html;
|
|
4
4
|
generates CSXS/manifest.xml, and
|
|
5
5
|
copies the asset folders. Needs a signing public key (AP_SIGNING_PUBLIC_KEY or
|
|
6
|
-
\`ap config set signing-public-key\`) until the production key is embedded
|
|
6
|
+
\`ap config set signing-public-key\`) until the production key is embedded.
|
|
7
|
+
|
|
8
|
+
Native effect plugins: runs the check, builds the release plugin for macOS
|
|
9
|
+
(universal, ad-hoc signed) and Windows (.aex), and writes the buyer ZIP with its
|
|
10
|
+
install scripts to dist/<bundleId>-<version>.zip, exactly what \`ap push\` uploads.
|
|
11
|
+
A Mac builds both; Windows builds the Windows version only (build the macOS
|
|
12
|
+
version on a Mac). --os mac, --os win or --os mac,win picks the versions.`,flags:{os:{type:"string"}},checkedProductionBuild:g,buildNativeZip:m,runGate:f,async run(e,i){const t=w(e),n=i&&i.flags?i.flags.os:void 0;if(t.settings.platform==="native")return m(e,t,{os:n});if(n!==void 0)throw new h("--os is for native effect plugins; CEP extensions run on both macOS and Windows.");return await e.out.task("Building "+t.settings.bundleId,()=>g(e,t),s=>"Built "+t.settings.bundleId+" "+s.version+e.out.style.dim(" (runtime "+s.runtimeVersion+") into "+u.relative(e.cwd,s.outDir))),0}};
|
package/src/commands/check.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";const{loadProject:l}=require("../project/project"),{checkProject:
|
|
1
|
+
"use strict";const{loadProject:l}=require("../project/project"),{checkProject:c,printCheck:p}=require("../check/check"),{verifyPricingOnline:h}=require("../check/pricing"),{typeHints:d,hintLines:u,typesLabel:g}=require("../check/types");module.exports={name:"check",summary:"Run the static checks that gate a push",usage:"ap check [--types] [--types-all]",description:`Errors: a panel folder lacks index.html or main.js; a panel's code does not bundle
|
|
2
2
|
(a missing import, or syntax newer than ES5); a panel never calls AP.boot(; a panel
|
|
3
3
|
turns licensing on without a renderLicense; a panel's index.html does not load
|
|
4
4
|
main.js; or the product is paid and no panel boots with licensing on.
|
|
@@ -10,4 +10,12 @@ The paid-product rule needs \`ap login\`; offline it is a warning (ap push enfor
|
|
|
10
10
|
app's API types (After Effects, Premiere Pro) and lists likely mistakes (misspelled names and
|
|
11
11
|
properties, wrong argument counts, methods ES3 lacks such as "".trim()).
|
|
12
12
|
Hints can be wrong (the types have gaps) and never fail the check.
|
|
13
|
-
--types-all Also lists every other hint. Expect false alarms on working code
|
|
13
|
+
--types-all Also lists every other hint. Expect false alarms on working code.
|
|
14
|
+
|
|
15
|
+
Native effect plugins: effect.json is valid; matchName and param ids/types unchanged
|
|
16
|
+
since the last release; an EffectMain entry point; out_flags set from AP_OUT_FLAGS
|
|
17
|
+
(the PiPL uses the same flags); Smart Render handled when promised; no mutable
|
|
18
|
+
globals/statics with multi-frame rendering; params read with the right union member;
|
|
19
|
+
16-bpc white is 32768, not 65535; no sleeping, network or process calls in src/; no
|
|
20
|
+
Adobe SDK files or text copied into the project. Each finding names file:line.
|
|
21
|
+
Silence a false alarm on one line with \`// ap-check: allow <code>\`.`,flags:{types:{type:"boolean"},"types-all":{type:"boolean"}},async run(n,t){const s=l(n),e=await c(s),a=await h(n,s,e);e.errors.push.apply(e.errors,a.errors),e.warnings.push.apply(e.warnings,a.warnings),p(n.out,e);const i=!!t.flags["types-all"];return(i||t.flags.types)&&(n.out.log(""),u(d(s),i,g(s)).forEach((r,o)=>n.out.log(o===1?n.out.style.dim(r):r))),e.errors.length?1:0}};
|
package/src/commands/dev.js
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
|
-
"use strict";const{loadProject:d}=require("../project/project"),{createDevSession:
|
|
1
|
+
"use strict";const{loadProject:d}=require("../project/project"),{createDevSession:a,otherDevProcess:u}=require("../dev/session"),{CliError:f}=require("../errors"),{debugPorts:c}=require("../dev/debug-file"),{projectDebugPort:p}=require("../config"),{hostAppNames:i}=require("../targets");module.exports={name:"dev",summary:"Develop inside After Effects or Premiere Pro with rebuild on change",usage:"ap dev [--once] [--no-reload] [--restart]",description:`Builds a readable dev bundle into dist-dev/<bundleId>/, links it into your CEP
|
|
2
2
|
extensions folder, enables PlayerDebugMode, runs \`ap check\`, and rebuilds whenever
|
|
3
3
|
a project file changes. Open panels reload themselves after each rebuild (dev
|
|
4
4
|
builds only; releases never include it). The .debug file gives panel i of
|
|
5
5
|
afterpresets.json the remote-debugging port debugPort + i.
|
|
6
|
-
--once builds and links without watching. --no-reload turns live reload off
|
|
6
|
+
--once builds and links without watching. --no-reload turns live reload off.
|
|
7
|
+
|
|
8
|
+
Native effect plugins (platform "native"): builds the plugin for this computer,
|
|
9
|
+
installs it into MediaCore/AfterPresets Dev/, installs the dev bridge (for host_eval
|
|
10
|
+
and comp_capture), converts Adobe's SDK guide into docs/afterpresets/sdk/, and says
|
|
11
|
+
when After Effects must restart (it loads plugins only at launch). Rebuilds on save.
|
|
12
|
+
--restart restarts After Effects when needed, but only when that is safe (the open
|
|
13
|
+
project is saved or a scratch project); it never force-quits.`,flags:{once:{type:"boolean"},"no-reload":{type:"boolean"},restart:{type:"boolean"}},async run(e,s){const t=d(e),o=u(t);if(o)throw new f("`ap dev` is already running for this project (process "+o+").",{hint:"Two can't build into the same folder. Use that one, or stop it (Ctrl+C in its terminal, or `kill "+o+"`) first."});if(t.settings.platform==="native")return h(e,t,s);const n=a(e,t,{liveReload:!s.flags["no-reload"]});return s.flags.once||n.claim(),await n.rebuild(),await n.link(),e.out.log(""),e.out.log("Open "+i(t.settings)+" > Window > Extensions > "+e.out.style.bold(t.panels[0].title)),e.out.log("Debug ports (Chrome DevTools; the afterpresets MCP finds them itself):"),c(t.panels,p(t.settings)).forEach(r=>{e.out.log(" "+r.panelId.padEnd(16)+"http://127.0.0.1:"+r.port)}),e.out.log((s.flags["no-reload"]?"Reload a panel to pick up changes.":"Panels reload by themselves when you save.")+" Restart "+i(t.settings)+" after adding a panel."),s.flags.once?0:l(e,n)}};function l(e,s){e.out.info("Watching for changes. Press Ctrl+C to stop.");const t=s.watch();return new Promise(o=>{const n=()=>{Promise.resolve(t.close()).then(()=>o(0))};e.onDevStarted?e.onDevStarted({stop:n,session:s}):process.once("SIGINT",n)})}async function h(e,s,t){const o=a(e,s,{restart:!!t.flags.restart});t.flags.once||o.claim();const n=await o.rebuild(),r=o.lastReport();return e.out.log(""),n&&e.out.log("Test it in After Effects: apply "+e.out.style.bold(s.settings.name)+" from the Effects menu (or with host_eval by its match name), then look at frames with comp_capture."),t.flags.once?(o.release&&o.release(),n?0:1):r&&r.stage==="sdk"?(o.release(),1):l(e,o)}
|
package/src/commands/docs.js
CHANGED
|
@@ -1 +1,5 @@
|
|
|
1
|
-
"use strict";const
|
|
1
|
+
"use strict";const d=require("fs"),i=require("path"),{loadProject:f}=require("../project/project"),{findRuntimeDir:c}=require("../build/runtime"),{syncRuntimeDocs:p}=require("../project/docs"),{CliError:l}=require("../errors");module.exports={name:"docs",summary:"Refresh docs/afterpresets/ from the installed runtime",usage:"ap docs",description:`Replaces the project's docs/afterpresets/ folder with the docs/*.md files of the
|
|
2
|
+
runtime that ships with the installed CLI. \`ap upgrade\` runs it for you. Your agent
|
|
3
|
+
reads these files before using AP.*. Native effect plugins also get
|
|
4
|
+
docs/afterpresets/sdk/: Adobe's SDK guide, converted on this computer (downloaded
|
|
5
|
+
once, then works offline), and vendor/ap_native/ is refreshed from this CLI.`,flags:{},async run(e){const t=f(e),n=c(e,t.root);if(!n)throw new l("The AfterPresets runtime was not found.",{hint:"Run `npm install` in the project, or `ap upgrade`."});const a=JSON.parse(d.readFileSync(i.join(n,"package.json"),"utf8")).version,o=p(n,t.root,t.settings);if(e.out.success("Copied "+o.files.length+" file(s) from @afterpresets/runtime "+a+" into "+i.relative(e.cwd,o.target)+"/"),o.files.forEach(r=>e.out.log(" "+r)),t.settings.platform==="native"){const r=require("../dev/native").vendorNativeRuntime(e,t.root);r.status!=="current"&&e.out.success((r.status==="created"?"Added":"Updated")+" ap_native "+(r.version||"")+" in vendor/ap_native/");const{ensureSdkDocs:u}=require("../project/docs"),s=await u(e,n,t.root,t.settings);if(s.status==="failed")return e.out.warn(s.message),s.hint&&e.out.log(" "+s.hint),1;s.status==="skipped"?e.out.warn(s.message+(s.hint?" "+s.hint:"")):e.out.success(s.message)}return 0}};
|
package/src/commands/init.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
"use strict";const
|
|
2
|
-
`).find(
|
|
1
|
+
"use strict";const y=require("fs"),h=require("path"),{apiSession:z}=require("../api/session"),{ask:k,select:O}=require("../prompt"),{copyProjectTemplate:G}=require("../project/template"),{writeProjectFile:H}=require("../project/project"),{findRuntimeDir:X}=require("../build/runtime"),{syncRuntimeDocs:W}=require("../project/docs"),{CliError:P,UsageError:w}=require("../errors"),{debugPortFor:Y}=require("../config"),{TARGETS:K,PLATFORMS:b,targetFor:$,parseApp:C,appNameOf:j}=require("../targets"),{isLocalCheckout:N}=require("../system/update-check"),A=/^[a-z0-9-]{2,40}$/,U={cep:{blank:"Blank panel"},uxp:{blank:"Blank panel"},native:{effect:"Effect with a few parameters (C++)"}},q=U.cep,Q=[{value:"aftereffects",label:"After Effects"},{value:"premierepro",label:"Premiere Pro"},{value:"both",label:"Both (one CEP extension for After Effects and Premiere Pro)"}],D=["aftereffects","premierepro"];function L(e){const t=String(e||"").trim().toLowerCase();if(/^both\b/.test(t))return"both";const n=t.split(/\s*(?:,|\+|&|\band\b)\s*/).map(C).filter(Boolean);return D.every(r=>n.indexOf(r)!==-1)?"both":n.length&&n.every(r=>r===n[0])?n[0]:C(t)}const _=require("../../package.json").version;function Z(e){return e.replace(/[-_]+/g," ").replace(/\b\w/g,t=>t.toUpperCase()).trim()}function T(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40)}async function x(e,t,n){const r=e.out;if(N(e)){r.log(r.style.dim(" Skipped npm install: this CLI runs from a local checkout and uses its bundled runtime."));return}const a=r.spinner("Installing dependencies"),s=await e.runCommand("npm",["install"],{cwd:t,quiet:!0});if(s.code===0){a.succeed("Installed dependencies");return}const l=String(s.stderr||"")+String(s.stdout||""),m=/E404/.test(l)&&/afterpresets/.test(l);if(a.warn(m?"Skipped dependencies: the afterpresets package is not on npm yet. The CLI's bundled runtime is used.":"npm install failed. Run it yourself inside "+n+"."),!m){const f=l.split(`
|
|
2
|
+
`).find(u=>/npm (error|ERR!)/.test(u));f&&r.log(" "+r.style.dim(f.trim()))}}function ee(e){return String(e||"").toLowerCase().replace(/[^a-z0-9]/g,"")||"you"}const te=[".DS_Store","Thumbs.db","desktop.ini",".git",".claude",".vscode",".idea",".cursor"];function B(e){if(!y.existsSync(e))return;if(!y.statSync(e).isDirectory())throw new P(e+" already exists and is not a folder.",{hint:"Choose a new folder name."});const t=y.readdirSync(e).filter(n=>te.indexOf(n)===-1);if(t.length)throw new P(e+" is not empty ("+t.slice(0,3).join(", ")+(t.length>3?", \u2026":"")+").",{hint:"Use an empty folder, or pass a new folder name."})}function ne(e){const t=e.platform,r=(Array.isArray(e.hosts)?e.hosts:[]).map(a=>{const s=$(t,a);if(!s)throw new P("AfterPresets registered an unknown target ("+t+" "+a+"). Update the CLI: ap upgrade.");return{app:s.host,minVersion:s.minVersion}});if(!r.length)throw new P("AfterPresets did not say which app this extension is for. Update the CLI: ap upgrade.");return{platform:t,hosts:r}}function F(e,t){const n=ne(e);if(n.platform==="native"){const a={bundleId:e.bundleId,productId:e.id,name:e.name,version:"0.1.0",platform:"native",hosts:n.hosts.map(s=>s.app)};return e.listingUrl&&(a.listingUrl=e.listingUrl),a}const r={bundleId:e.bundleId,productId:e.id,name:e.name,version:"0.1.0",platform:n.platform,hosts:n.hosts,debugPort:t};return e.listingUrl&&(r.listingUrl=e.listingUrl),r.panels=[{id:"main",title:e.name,type:"panel",width:400,height:580,menu:!0}],r}module.exports={name:"init",initialSettings:F,summary:"Create and register a new extension project",usage:"ap init <dir> [--app aeft|ppro] [--platform cep|native] [--starter blank|effect] [--name <name>] [--slug <id>] [--skip-install]",description:`Asks which app the extension is for (After Effects or Premiere Pro), registers it
|
|
3
3
|
on AfterPresets (its bundle ID com.afterpresets.<you>.<slug> is permanent), copies
|
|
4
4
|
the project template into <dir>, writes afterpresets.json, copies the runtime docs
|
|
5
5
|
into docs/afterpresets/, and runs \`npm install\`. Requires \`ap login\`.
|
|
6
6
|
--app aeft (After Effects) or ppro (Premiere Pro); aeft,ppro for both.
|
|
7
7
|
Required when there is no terminal to ask in.
|
|
8
|
-
--platform cep (a CEP panel)
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
--platform cep (a CEP panel) or native (an After Effects effect plugin in C++,
|
|
9
|
+
After Effects only). uxp (UXP plugin) is coming soon. The platform
|
|
10
|
+
is permanent: another platform later is a separate product.
|
|
11
|
+
--starter blank (CEP, default) or effect (native, default).
|
|
12
|
+
Native projects also get vendor/ap_native/ (licensing and updates) and need
|
|
13
|
+
Adobe's After Effects SDK once per computer: \`ap sdk install\`.
|
|
14
|
+
Add the other app to a CEP extension later with \`ap add host\`.`,flags:{name:{type:"string"},slug:{type:"string"},app:{type:"string"},platform:{type:"string"},starter:{type:"string"},"skip-install":{type:"boolean"}},async run(e,t){const n=t.positionals[0];if(!n)throw new w("Tell me where to create the project.","Example: ap init my-extension (or ap init . for this folder)");const r=h.resolve(e.cwd,n),a=e.out,s=await J(e,{target:r,name:t.flags.name,slug:t.flags.slug,app:t.flags.app,platform:t.flags.platform,starter:t.flags.starter,install:!t.flags["skip-install"]}),l=r===h.resolve(e.cwd);a.success("Created "+a.style.bold(l?s.product.name+" in this folder":h.relative(e.cwd,r)||r)),s.product.dashboardUrl&&a.log(" "+a.style.dim("Dashboard (releases, testers, listing): ")+s.product.dashboardUrl),a.log("");const m=(u,p,i)=>a.style.dim(u+" ")+(p?a.style.bold(a.style.cyan(p.padEnd(10)))+" "+i:i),f=[];return l||f.push(["cd "+n,""]),s.native?(s.sdk&&!s.sdk.ok&&f.push(["ap sdk install","one time per computer: Adobe's After Effects SDK"]),f.push(["ap dev","build, install into After Effects, rebuild on save"])):f.push(["ap dev","open it in "+s.appNames+"; reloads as you edit"]),f.push([null,"Open your AI agent here and describe your extension"]),f.push(["ap push","publish when it's ready (test channel first)"]),a.box(f.map((u,p)=>m(p+1,u[0],u[1])),{title:"Next steps",color:"cyan"}),a.log(""),0}};async function J(e,t){const n=t.target;B(n);const r=z(e),a=await r.get("/v1/api/developer/me");e.out.banner("New extension"),e.out.info("Creating an extension as "+e.out.style.bold(a.user&&a.user.username||"you"));const s=await V(e,t),l=s.targets.map(o=>o.appName).join(" or "),m=t.name||await k(e,"Extension name",{default:Z(h.basename(n)),help:"Shown in "+l+". You can change it later.",flagHint:"Pass --name <name>."}),f=ee(a.user&&a.user.username),u=t.slug||(t.name&&A.test(T(t.name))&&!e.prompt?T(t.name):null)||await k(e,"Extension ID",{default:T(m),help:"Permanent \xB7 lowercase letters, numbers, dashes",live:o=>"Becomes com.afterpresets."+f+"."+(o||"<id>")+(o&&!A.test(o)?" \u2717 not a valid ID":""),flagHint:"Pass --slug <id>.",validate:o=>A.test(o)?null:"Use 2-40 lowercase letters, numbers, or dashes, e.g. my-tool."});if(e.out.log(""),!A.test(u))throw new w("Invalid extension ID "+JSON.stringify(u)+". Use 2-40 lowercase letters, numbers, or dashes.");const i=(await e.out.task("Registering "+m+" on AfterPresets",()=>r.post("/v1/api/developer/products",{name:m,slug:u,platform:s.platform,hosts:s.targets.map(o=>o.host)}),o=>"Registered "+o.product.name+" as "+e.out.style.bold(o.product.bundleId))).product;e.out.log("");const d=X(e,null),c=d?JSON.parse(y.readFileSync(h.join(d,"package.json"),"utf8")).version:_,g=s.platform==="native",S=Y(i.bundleId),E=F(i,S),R={DEBUG_PORT:S,BUNDLE_ID:i.bundleId,EXTENSION_NAME:i.name,SLUG:i.slug||u,CLI_VERSION:_,RUNTIME_VERSION:c};g&&Object.assign(R,re(e,E)),G(n,R,s.targets,e.templateDir),H(n,E),N(e)&&se(n);let v=null;if(g){const o=require("../dev/native"),M=o.vendorNativeRuntime(e,n);e.out.success("Added ap_native "+(M.version||"")+" (licensing and updates) in vendor/ap_native/"),v=await o.sdkStatus(e),v.ok?e.out.success(v.message):(e.out.warn(v.message),e.out.log(" "+v.next))}if(d)try{W(d,n,E)}catch(o){e.out.warn(o.message+" Run `ap docs` after `npm install`.")}else e.out.warn("Could not find @afterpresets/runtime; run `ap docs` after `npm install` to copy its docs.");if(g){const o=await require("../dev/native").ensureNativeDocs(e,{root:n,settings:E});o&&o.status==="generated"?e.out.success(o.message):o&&o.status==="failed"&&e.out.warn(o.message+(o.hint?" "+o.hint:""))}return t.install&&y.existsSync(h.join(n,"package.json"))&&(await x(e,n,h.relative(e.cwd,n)||"."),e.out.log("")),{product:i,target:n,runtimeVersion:c,appNames:l,native:g,sdk:v}}function re(e,t){return require("../dev/native").nativeLib(e,"effect").templateValues(t)}function I(e){const t=String(e||"").trim().toLowerCase();return b[t]?t:/^native\b|c\+\+|\beffect\b|after effects plugin/.test(t)?"native":/^cep\b|panel/.test(t)?"cep":/^uxp\b|plugin/.test(t)?"uxp":null}async function V(e,t){const n=e.out;let r;if(t.app)r=[],String(t.app).split(",").forEach(p=>{const i=C(p);if(!i)throw new w("Unknown app "+JSON.stringify(p.trim())+".","Use --app aeft (After Effects) or --app ppro (Premiere Pro).");r.indexOf(i)===-1&&r.push(i)});else if(e.prompt){const p=await O(e,"Which app is the extension for?",Q,{match:L});r=p==="both"?D.slice():[p]}else throw new w("Which app is the extension for?","Pass --app aeft (After Effects) or --app ppro (Premiere Pro).");const a=(p,i)=>K.find(d=>d.platform===p&&d.app===i)||null,s=p=>r.every(i=>{const d=a(p,i);return d&&d.available});let l=null;if(t.platform){if(l=I(t.platform),!l)throw new w("Unknown platform "+JSON.stringify(t.platform)+".","Use --platform cep (a panel) or --platform native (an After Effects effect plugin).")}else{const i=Object.keys(b).filter(c=>c!=="native"||r.every(g=>{const S=a(c,g);return S&&S.available})).map(c=>({value:c,label:b[c].name+" ("+b[c].label+")",disabled:!s(c),note:s(c)?null:"coming soon"})),d=i.filter(c=>!c.disabled);if(e.prompt&&d.length>1)l=await O(e,"Which technology?",i,{match:I});else if(l=d.length?d[0].value:"cep",e.prompt){const c=i.filter(g=>g.disabled).map(g=>b[g.value].name+": coming soon");n.log(""),n.log(n.style.cyan("\u2713")+" Technology: "+n.style.bold(b[l].name)+(c.length?n.style.dim(" ("+c.join(", ")+")"):""))}}if(l==="uxp"&&r.length>1)throw new w("A UXP plugin runs in one app.","Pick one app with --app, or use --platform cep for both.");if(l==="native"&&r.length>1)throw new w("An effect plugin is made for After Effects only for now.","Use --app aeft --platform native.");const m=r.map(p=>a(l,p));m.forEach((p,i)=>{if(!p||!p.available)throw new w(l==="native"?j(r[i])+" effect plugins are coming soon; native plugins are for After Effects for now.":j(r[i])+" "+b[l].name+"s are coming soon.",l==="native"?"Use --app aeft --platform native, or --platform cep for a panel.":"Use --platform cep for now. The platform is permanent, so a UXP version later is a separate extension.")});const f=U[l]||q,u=t.starter?String(t.starter).trim().toLowerCase():Object.keys(f)[0];if(!f[u])throw new w("Unknown starter "+JSON.stringify(t.starter)+".","Use --starter "+Object.keys(f).join(" or ")+".");return e.prompt&&!t.starter&&n.log(n.style.cyan("\u2713")+" Starter: "+n.style.bold(f[u])+n.style.dim(" (more starters later)")),{platform:l,targets:m,starter:u}}function se(e){const t=h.join(e,".mcp.json");if(!y.existsSync(t))return;const n=JSON.parse(y.readFileSync(t,"utf8")),r=n.mcpServers&&n.mcpServers.afterpresets;r&&(r.args=[h.resolve(__dirname,"../../bin/ap.js"),"mcp"],y.writeFileSync(t,JSON.stringify(n,null,2)+`
|
|
15
|
+
`))}module.exports.createProject=J,module.exports.parseAppChoice=L;async function ae(e){const t=e.out,{readCredentials:n}=require("../auth/credentials");if(t.banner("Create an extension"),!n(e)){t.info("First, log in to AfterPresets (a free account is enough).");const s=await require("./login").run(e,{flags:{},positionals:[]});if(s)return s}const r=(()=>{try{return B(e.cwd),!0}catch{return!1}})(),a=await k(e,"Project folder",{default:r?".":"my-extension",help:r?"This folder is empty; Enter creates the project here.":"A new folder inside "+e.cwd,validate:s=>s.trim()?null:"Enter a folder name."});return module.exports.run(e,{flags:{},positionals:[a]})}module.exports.wizard=ae,module.exports.chooseTarget=V,module.exports.STARTERS=q,module.exports.STARTERS_BY_PLATFORM=U,module.exports.parsePlatform=I;
|
package/src/commands/push.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
"use strict";const
|
|
1
|
+
"use strict";const b=require("../project/semver"),{loadProject:C}=require("../project/project"),{apiSession:k}=require("../api/session"),{ask:O,select:y}=require("../prompt"),{CHANNELS:w,BUMPS:v,validateChangelog:g,preparePush:S,checkPushQuota:W,runPush:A}=require("../release/publish"),{UsageError:d}=require("../errors");module.exports={name:"push",summary:"Check, bump, build, and publish a release",usage:"ap push [--channel test|production] [--bump patch|minor|major] [-m <changelog>] [--os mac,win]",description:`Runs the check, bumps the version, and builds the production bundle. Only the
|
|
2
2
|
merged panel scripts go to AfterPresets to be obfuscated; the buyer ZIP (with
|
|
3
3
|
installers) is assembled here and uploaded straight to storage, then signed.
|
|
4
4
|
Production pushes count toward your monthly quota (test-channel pushes do not),
|
|
5
5
|
and AfterPresets emails you after each one. Paid products must boot with licensing
|
|
6
6
|
on (checked against the price on AfterPresets). The new version is written to
|
|
7
|
-
afterpresets.json only after the upload succeeds
|
|
7
|
+
afterpresets.json only after the upload succeeds.
|
|
8
|
+
Native effect plugins: --os mac, --os win or --os mac,win picks the versions in
|
|
9
|
+
the release (default: all this computer can build, both on a Mac, Windows only on
|
|
10
|
+
a Windows PC).`,flags:{channel:{type:"string",alias:"c"},bump:{type:"string",alias:"b"},message:{type:"string",alias:"m"},os:{type:"string"}},async run(n,u){const s=u.flags,t=C(n),c=t.settings.bundleId;if(s.channel&&w.indexOf(s.channel)===-1)throw new d("--channel must be test or production.");if(s.bump&&v.indexOf(s.bump)===-1)throw new d("--bump must be patch, minor, or major.");if(s.message!==void 0&&g(s.message))throw new d(g(s.message));if(s.os!==void 0&&t.settings.platform!=="native")throw new d("--os is for native effect plugins; CEP extensions run on both macOS and Windows.");const m=t.settings.platform==="native"?require("../release/native").resolveReleaseOses(n,s.os):null,p=k(n),{details:P,base:i}=await S(n,p,t);i!==t.version&&n.out.warn("The latest release is "+i+"; bumping from it instead of afterpresets.json's "+t.version+".");const a=s.channel||await y(n,"Channel",w.map(h=>({value:h,label:h})),{default:"test",flagHint:"Pass --channel test|production."}),q=s.bump||await H(n,i),l=b.bump(i,q),j=s.message||await O(n,"Changelog for "+l,{validate:g,flagHint:'Pass -m "What changed".'}),o=a==="production"?await W(p):null,e=n.out;if(e.log(""),e.log(" "+e.style.bold(c)+" "+e.style.dim(i+" \u2192")+" "+e.style.cyan(l)+e.style.dim(" on ")+(a==="production"?e.style.magenta("production"):e.style.blue("test"))),e.log(""),o&&o.pushes){const h=o.pushes.limit===null||o.pushes.limit===void 0?"unlimited":o.pushes.limit;e.info("Production pushes this month: "+e.style.bold(o.pushes.used+1+" / "+h)+e.style.dim(" (including this one)"))}const r=await A(n,p,t,{details:P,version:l,channel:a,changeLog:j,oses:m});e.log("");const f=[e.style.dim("version ")+e.style.bold(r.version)+(m?require("../release/native").publishedOsSuffix(r.release,m):""),e.style.dim("channel ")+a,e.style.dim("sha256 ")+r.sha256.slice(0,16)+"\u2026"];return r.listingUrl&&f.push(e.style.dim("listing ")+e.style.cyan(r.listingUrl)),e.box(f,{title:"\u{1F680} Published "+c,color:"green"}),e.log(""),e.log(e.style.dim("afterpresets.json is now at "+l+". Commit it.")),a==="test"?e.log(e.style.dim("Testers get it on their next update check. Promote with `ap releases promote "+l+"`.")):e.log(e.style.dim("Buyers get it on their next update check (at launch and every 6 hours). We emailed you a confirmation.")),0}};function H(n,u){const s=v.map(t=>({value:t,label:b.bump(u,t),note:t}));return y(n,"New version (now "+u+")",s,{default:"patch",flagHint:"Pass --bump patch|minor|major.",match:t=>(s.find(c=>c.label===t)||{}).value||null})}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";const{nativeLib:l}=require("../dev/native"),{UsageError:p,CliError:u}=require("../errors"),c=10;async function k(e){const n=l(e,"sdk").findSdk(e),o=e.out;return n.found?(o.success(n.message),n.versions.length>1&&o.log(" Installed: "+n.versions.map(s=>s.version).join(", ")),n.sdk.specVersion&&o.log(" "+o.style.dim("API "+n.sdk.specVersion.major+"."+n.sdk.specVersion.minor+(n.sdk.verified?", a version AfterPresets knows":", not in AfterPresets' tested list yet"))),0):(o.warn("The After Effects SDK is not installed."),o.log(n.message),1)}function g(e,a){return typeof a.downloadsLabel=="function"?a.downloadsLabel(e):"~/Downloads"}async function h(e,a){const n=l(e,"sdk"),o=g(e,n),s=e.out,w=a.positionals[1],i=a.flags;let d=w||null;if(!d&&!n.findDownloadedSdk(e)){const t=n.sdkDownloadUrl();if(!(i.wait||!!e.prompt))return s.warn("No AfterEffectsSDK zip in "+o+" yet."),s.log(n.notInstalledMessage(e)),s.log(" Or run `ap sdk install --wait`: it opens the page and installs the download as soon as it lands."),1;if(i["no-open"])s.info("Download page: "+t);else{s.info("Opening Adobe's download page: "+t);try{await e.openBrowser(t)}catch{s.log(" Open it yourself: "+t)}}s.log(' Sign in with your Adobe ID and download "After Effects SDK" for '+(e.platform==="win32"?"Windows":"macOS")+". Waiting for it in "+o+"\u2026");const r=i.minutes?Number(i.minutes):c;if(!(r>0&&r<=60))throw new p("--minutes must be between 1 and 60.");const f=await n.waitForDownloadedSdk(e,{timeoutMs:r*60*1e3});if(!f)throw new u("No After Effects SDK download appeared in "+o+" within "+r+" minutes.",{hint:"Download it from "+t+", then run `ap sdk install` (or `ap sdk install <path to the zip>`)."});d=f.path}return((await s.task("Installing the After Effects SDK",async()=>{const t=await n.installSdk(e,{zipPath:d||void 0,force:!!i.force});if(!t.ok)throw new u(t.message);return t},t=>t.message)).warnings||[]).forEach(t=>s.warn(t)),s.log(" "+s.style.dim("Every native project on this computer uses it. You can delete the downloaded zip.")),0}module.exports={name:"sdk",summary:"Install Adobe's After Effects SDK (native effect plugins)",usage:"ap sdk [status] | ap sdk install [<zip-or-folder>] [--wait] [--no-open] [--minutes <n>] [--force] | ap sdk open",description:`Native effect plugins are built against Adobe's After Effects SDK. Adobe gives it
|
|
2
|
+
out with a free Adobe ID, so you download it once per computer and \`ap sdk install\`
|
|
3
|
+
unpacks it into ~/.afterpresets/sdk/<version>/ for every project. Nothing from the
|
|
4
|
+
SDK goes into your project or your releases. On Windows: download the Windows SDK
|
|
5
|
+
(AfterEffectsSDK_<version>_win.zip); ~ is %USERPROFILE%.
|
|
6
|
+
ap sdk Shows the installed SDK (exit code 1 when none).
|
|
7
|
+
ap sdk install Installs the newest AfterEffectsSDK zip from ~/Downloads, or the
|
|
8
|
+
zip/folder you name. Checks it is a real, supported SDK first.
|
|
9
|
+
--wait No download yet: opens Adobe's page and waits (default 10
|
|
10
|
+
minutes, --minutes) for the zip to appear in ~/Downloads.
|
|
11
|
+
On by default in a terminal.
|
|
12
|
+
--no-open Do not open the browser.
|
|
13
|
+
--force Reinstall a version that is already installed.
|
|
14
|
+
ap sdk open Opens Adobe's download page.`,flags:{wait:{type:"boolean"},"no-open":{type:"boolean"},minutes:{type:"string"},force:{type:"boolean"}},async run(e,a){const n=a.positionals[0]||"status";if(n==="status")return k(e);if(n==="install")return h(e,a);if(n==="open"){const o=l(e,"sdk").sdkDownloadUrl();return e.out.info("Opening "+o),await e.openBrowser(o),0}throw new p("Unknown `ap sdk` action "+JSON.stringify(n)+".","Use `ap sdk`, `ap sdk install` or `ap sdk open`.")}};
|
package/src/commands/testers.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";const{loadProject:
|
|
1
|
+
"use strict";const{loadProject:m}=require("../project/project"),{apiSession:h,productPath:p}=require("../api/session"),{UsageError:d}=require("../errors"),g=/^[^\s@]+@[^\s@]+\.[^\s@]+$/;module.exports={name:"testers",summary:"Manage who can install test-channel releases",usage:"ap testers [list | add <email> | remove <email>]",description:"Testers get test-channel releases. How many per extension depends on your plan (`ap whoami`).\nAdd their email, then send them the tester link: they sign in with that email (verified),\ndownload the test build, and get later test builds as updates. `ap testers list` prints the link.",flags:{},async run(e,l){const s=l.positionals[0]||"list",u=m(e),o=h(e),n=p(u.settings.bundleId)+"/testers";if(s==="list"){const t=await o.get(n),a=t.testers||[];return t.limit!==void 0&&e.out.log(e.out.style.dim("Testers: "+a.length+(t.limit===null?"":"/"+t.limit))),a.length||e.out.log("No testers yet. Add one with `ap testers add <email>`."),a.forEach(r=>e.out.log(typeof r=="string"?r:r.email)),t.testerUrl&&e.out.log(e.out.style.dim("Tester link: ")+t.testerUrl),0}if(s!=="add"&&s!=="remove")throw new d("Unknown testers action: "+s,"Use list, add, or remove.");const i=String(l.positionals[1]||"").trim().toLowerCase();if(!g.test(i))throw new d("Give a valid email. Example: ap testers "+s+" friend@example.com");if(s==="add"){const t=await o.post(n,{email:i});e.out.success("Added "+i+" as a tester."),t&&t.testerUrl&&(e.out.log(" "+e.out.style.dim("Send them this link: ")+t.testerUrl),e.out.log(" "+e.out.style.dim("They sign in as "+i+" to download the test build.")))}else await o.del(n,{email:i}),e.out.success("Removed "+i+".");return 0}};
|
package/src/commands/upgrade.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";const m=require("fs"),d=require("path"),{findProjectRoot:g}=require("../project/project"),{CliError:w}=require("../errors"),{refresh:y,isLocalCheckout:
|
|
2
|
-
`).find(a=>/npm (error|ERR!)/.test(a));throw new w(i?i.trim():"npm install exited with code "+o.code+".",{hint:"Run `npm install --save-dev "+
|
|
1
|
+
"use strict";const m=require("fs"),d=require("path"),{findProjectRoot:g}=require("../project/project"),{CliError:w}=require("../errors"),{refresh:y,isLocalCheckout:v,CURRENT:j,PACKAGE:n}=require("../system/update-check");function p(t){try{return JSON.parse(m.readFileSync(d.join(t,"node_modules",n,"package.json"),"utf8")).version}catch{return null}}module.exports={name:"upgrade",summary:"Update the AfterPresets CLI and runtime in this project",usage:"ap upgrade",description:"Installs the latest `afterpresets` package into this project (the runtime ships inside it),\nrefreshes docs/afterpresets/ (and vendor/ap_native/ in native projects), and runs `ap check`. Test with `ap dev`, then push: buyers\nget the new runtime with your next release. Outside a project it prints how to update\na global install.",flags:{},async run(t){const e=t.out;if(v(t))return e.info("This CLI runs from the AfterPresets source code. Update it with `git pull` there."),0;const s=g(t.cwd),r=await y(t);if(!s)return e.info("Not inside an AfterPresets project."),e.log(" Update a global install with: "+e.style.cyan("npm install -g "+n+"@latest")),r&&e.log(" "+e.style.dim("Latest version: "+r+" (this one: "+j+")")),0;const l=p(s);if(r&&l===r)return e.success("Already on the latest AfterPresets CLI ("+r+")."),0;const c=e.spinner("Installing "+n+"@latest"),o=await t.runCommand("npm",["install","--save-dev",n+"@latest"],{cwd:s,quiet:!0});if(o.code!==0){c.fail("npm install failed");const i=(String(o.stderr||"")+String(o.stdout||"")).split(`
|
|
2
|
+
`).find(a=>/npm (error|ERR!)/.test(a));throw new w(i?i.trim():"npm install exited with code "+o.code+".",{hint:"Run `npm install --save-dev "+n+"@latest` in "+s+" to see the full output."})}const h=p(s)||r||"latest";c.succeed("Installed "+n+" "+h+(l?" (was "+l+")":""));const f=d.join(s,"node_modules",n,"bin","ap.js");for(const u of[["docs"],["check"]]){const i=await t.runCommand("node",[f].concat(u),{cwd:s,quiet:!0}),a=(String(i.stdout||"")+String(i.stderr||"")).trim();if(a&&e.log(a),i.code!==0)return e.warn("`ap "+u[0]+"` reported problems. Fix them before your next push."),1}return e.log(""),e.success("Upgraded. Test with `ap dev`; buyers get the new runtime with your next push."),0}};
|
package/src/commands/whoami.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";const{apiSession:
|
|
1
|
+
"use strict";const{apiSession:u}=require("../api/session");module.exports={name:"whoami",summary:"Show the logged-in account, plan, and push usage",usage:"ap whoami",description:"Prints your username, plan tier, production pushes used this UTC month, and testers allowed per extension.",flags:{},async run(e){const o=u(e),s=await o.get("/v1/api/developer/me"),i=s.user||{},t=s.pushes||{};if(e.out.log(e.out.style.bold(i.username||"(no username)")+(i.email?" "+i.email:"")),e.out.log("Plan: "+(s.tier||"free")),e.out.log("Production pushes: "+(t.used||0)+" / "+(t.limit===null||t.limit===void 0?"unlimited":t.limit)+" this month"),s.testers){const n=s.testers.limitPerExtension;e.out.log("Testers: "+(n??"unlimited")+" per extension")}return e.out.log(e.out.style.dim("API: "+o.base)),0}};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";const r=require("fs"),a=require("path"),{CliError:v}=require("../errors"),{userExtensionsDir:I}=require("./cep-link"),{enableDebugMode:R}=require("./debug-mode"),{escapeXml:D}=require("../project/manifest"),l="com.afterpresets.devbridge",m=l+".host",s="1.0.0",u="AfterPresets dev bridge",g={AEFT:9097,PPRO:9098},x=[{app:"AEFT",minVersion:"17.0"},{app:"PPRO",minVersion:"14.0"}],b=".afterpresets-dev-bridge",y=["com.adobe.csxs.events.ApplicationActivate","applicationActivate","com.afterpresets.devbridge.start"];function A(){const t=D(m);return['<?xml version="1.0" encoding="UTF-8"?>',"<!-- "+u+" "+s+", installed by the AfterPresets CLI for development. Safe to delete. -->",'<ExtensionManifest Version="7.0" ExtensionBundleId="'+l+'" ExtensionBundleVersion="'+s+'" ExtensionBundleName="'+u+'" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'," <ExtensionList>",' <Extension Id="'+t+'" Version="'+s+'" />'," </ExtensionList>"," <ExecutionEnvironment>"," <HostList>",x.map(e=>' <Host Name="'+e.app+'" Version="['+e.minVersion+',99.9]" />').join(`
|
|
2
|
+
`)," </HostList>"," <LocaleList>",' <Locale Code="All" />'," </LocaleList>"," <RequiredRuntimeList>",' <RequiredRuntime Name="CSXS" Version="9.0" />'," </RequiredRuntimeList>"," </ExecutionEnvironment>"," <DispatchInfoList>",' <Extension Id="'+t+'">'," <DispatchInfo>"," <Resources>"," <MainPath>./index.html</MainPath>"," </Resources>"," <Lifecycle>"," <AutoVisible>false</AutoVisible>"," <StartOn>",y.map(e=>" <Event>"+e+"</Event>").join(`
|
|
3
|
+
`)," </StartOn>"," </Lifecycle>"," <UI>"," <Type>Custom</Type>"," <Geometry>"," <Size>"," <Height>10</Height>"," <Width>10</Width>"," </Size>"," </Geometry>"," </UI>"," </DispatchInfo>"," </Extension>"," </DispatchInfoList>","</ExtensionManifest>",""].join(`
|
|
4
|
+
`)}function T(){return["<!doctype html>","<html>","<head>",'<meta charset="utf-8">',"<title>"+u+"</title>",'<meta name="afterpresets-dev-bridge" content="'+s+'">',"</head>","<body>","<!-- Lets the AfterPresets MCP server (ap mcp) run ExtendScript in this app during development. No UI, no Node.js. -->","</body>","</html>",""].join(`
|
|
5
|
+
`)}function N(){return['<?xml version="1.0" encoding="UTF-8"?>',"<ExtensionList>",' <Extension Id="'+m+'">'," <HostList>",x.map(t=>' <Host Name="'+t.app+'" Port="'+g[t.app]+'" />').join(`
|
|
6
|
+
`)," </HostList>"," </Extension>","</ExtensionList>",""].join(`
|
|
7
|
+
`)}function p(){return{"CSXS/manifest.xml":A(),"index.html":T(),".debug":N(),[b]:s+`
|
|
8
|
+
`}}function S(t){return a.join(I(t),l)}function L(t,e){try{return r.readFileSync(t,"utf8")}catch{return e}}function E(t){const e=S(t);let n=null;try{n=r.lstatSync(e)}catch(i){if(i.code!=="ENOENT")throw i}if(!n)return{dir:e,installed:!1,current:!1,foreign:!1,version:null};const o=n.isDirectory()&&!n.isSymbolicLink()?L(a.join(e,b),null):null;if(o===null)return{dir:e,installed:!1,current:!1,foreign:!0,version:null};const c=p(),d=Object.keys(c).every(i=>L(a.join(e,i),null)===c[i]);return{dir:e,installed:!0,current:d,foreign:!1,version:o.trim()}}async function P(t,e){const n=E(t);if(n.foreign)throw new v(n.dir+" exists but was not created by AfterPresets.",{hint:"Move or delete it yourself, then install the dev bridge again."});let o="unchanged";if(!n.current){const d=p();Object.keys(d).forEach(i=>{const f=a.join(n.dir,i);r.mkdirSync(a.dirname(f),{recursive:!0});const h=f+".tmp-"+process.pid;r.writeFileSync(h,d[i]),r.renameSync(h,f)}),o=n.installed?"updated":"created"}const c=e&&e.debugMode===!1?[]:await R(t);return{dir:n.dir,status:o,debugModeFailures:c}}function j(t){const e=E(t);if(e.foreign)throw new v(e.dir+" was not created by AfterPresets; left it alone.");return e.installed?(r.rmSync(e.dir,{recursive:!0,force:!0}),{dir:e.dir,removed:!0}):{dir:e.dir,removed:!1}}module.exports={BRIDGE_BUNDLE_ID:l,BRIDGE_EXTENSION_ID:m,BRIDGE_VERSION:s,BRIDGE_TITLE:u,BRIDGE_PORTS:g,START_ON_EVENTS:y,bridgeFiles:p,bridgeDir:S,bridgeStatus:E,installDevBridge:P,removeDevBridge:j};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
"use strict";const P=require("crypto"),u=require("fs"),a=require("path"),{CliError:h}=require("../errors"),S=a.join(__dirname,"..",".."),y=a.join("vendor","ap_native"),E=".ap-native.json",m="vendor-files.json";function v(e,s){if(e&&e.nativeLib&&e.nativeLib[s])return e.nativeLib[s];try{return require("../native/"+s)}catch(t){throw t&&t.code==="MODULE_NOT_FOUND"&&String(t.message).indexOf(a.join("native",s))!==-1?new h("This CLI has no native toolchain module (src/native/"+s+".js).",{hint:"Update the CLI with `ap upgrade`."}):t}}function b(e){return e&&e.nativeRuntimeDir?e.nativeRuntimeDir:[a.join(S,"native-runtime"),a.join(S,"..","native-runtime")].find(t=>u.existsSync(a.join(t,m))&&u.existsSync(a.join(t,"include","ap_native.h")))||null}function A(e){try{const s=u.readFileSync(a.join(e,"include","ap_native.h"),"utf8").match(/#define\s+AP_NATIVE_VERSION\s+"([0-9A-Za-z.+-]+)"/);if(s)return s[1]}catch{}try{const s=u.readFileSync(a.join(e,"CMakeLists.txt"),"utf8").match(/project\s*\(\s*ap_native\s+VERSION\s+([0-9.]+)/);return s?s[1]:null}catch{return null}}function R(e){let s;try{s=JSON.parse(u.readFileSync(a.join(e,m),"utf8")).files}catch{throw new h("ap_native's "+m+" is missing or invalid ("+e+").",{hint:"Update the CLI with `ap upgrade`."})}if(!Array.isArray(s)||!s.length)throw new h("ap_native's "+m+" lists no files.");return s.map(t=>{const r=String(t);if(a.isAbsolute(r)||r.split("/").some(i=>i===".."||i===""||i===".")||r.indexOf("\\")!==-1)throw new h("ap_native's "+m+" has an invalid path: "+JSON.stringify(r)+".");if(!u.existsSync(a.join(e,r.split("/").join(a.sep))))throw new h("ap_native file listed in "+m+" is missing: "+r);return r}).sort()}function B(e,s){const t=P.createHash("sha256");return s.forEach(r=>{t.update(r+"\0"),t.update(u.readFileSync(a.join(e,r.split("/").join(a.sep)))),t.update("\0")}),t.digest("hex")}function D(e,s){const t=b(e);if(!t)throw new h("This CLI does not include ap_native, the AfterPresets licensing library for native plugins.",{hint:"Update the CLI with `ap upgrade`."});const r=R(t),i=B(t,r),n=A(t),o=a.join(s,y);let l=null;try{l=JSON.parse(u.readFileSync(a.join(o,E),"utf8"))}catch{l=null}if(l&&l.sha256===i&&u.existsSync(a.join(o,"include","ap_native.h")))return{status:"current",version:n,dir:o};const d=u.existsSync(o);u.mkdirSync(a.dirname(o),{recursive:!0});const c=u.mkdtempSync(a.join(a.dirname(o),".ap_native-"));try{r.forEach(f=>{const g=a.join(c,f.split("/").join(a.sep));u.mkdirSync(a.dirname(g),{recursive:!0}),u.copyFileSync(a.join(t,f.split("/").join(a.sep)),g)}),u.writeFileSync(a.join(c,E),JSON.stringify({note:"Copied by the AfterPresets CLI. Do not edit: `ap dev` and `ap build` replace this folder when the CLI updates.",version:n,sha256:i},null,2)+`
|
|
2
|
+
`),u.rmSync(o,{recursive:!0,force:!0}),u.renameSync(c,o)}catch(f){throw u.rmSync(c,{recursive:!0,force:!0}),f}return{status:d?"updated":"created",version:n,dir:o}}async function j(e){const s=v(e,"sdk");let t;try{t=await s.findSdk(e)||{}}catch(i){t={found:!1,message:i.message}}const r=!!(t.found&&t.sdk);return{ok:r,version:r?t.sdk.version:null,dir:r?t.sdk.root:null,message:r?t.message:"Adobe's After Effects SDK is not installed on this computer.",next:r?null:t.message||(typeof s.notInstalledMessage=="function"?s.notInstalledMessage():"Run `ap sdk install`."),raw:r?t.sdk:null}}function p(e){return e?typeof e=="string"?{file:null,line:null,column:null,message:e}:{file:e.file||null,line:e.line||null,column:e.column||null,message:String(e.message||e.text||"")}:null}function k(e){return(e.file?e.file+(e.line?":"+e.line+(e.column?":"+e.column:""):"")+": ":"")+e.message}async function _(e,s,t){const r=v(e,"build");try{const i=await r.buildNative(e,Object.assign({project:s,root:s.root,settings:s.settings},t))||{},n=(i.errors||i.diagnostics||[]).map(p).filter(Boolean),o=i.ok!==!1&&!n.length;return{ok:o,result:i,artefacts:i.artefacts||i.artifacts||{},errors:n,warnings:(i.warnings||[]).map(l=>typeof l=="string"?l:k(p(l))),error:o?null:(n.length&&i.message?String(i.message).split(`
|
|
3
|
+
`)[0]:i.message||i.error)||(n.length?n.length+" build error(s)":"The build failed."),hint:i.hint||null}}catch(i){if(!(i instanceof h)&&!(i&&(i.diagnostics||i.errors)))throw i;return{ok:!1,result:null,artefacts:{},errors:(i.diagnostics||i.errors||[]).map(p).filter(Boolean),warnings:[],error:i.message,hint:i.hint||null}}}async function O(e,s){try{const t=await v(e,"install").devStatus(e,{project:s,root:s.root,settings:s.settings})||{},r=!!(t.restartNeeded||t.needsRestart),i=t.aeRunning!==void 0?!!t.aeRunning:t.running!==void 0?!!t.running:null;return{needed:r,running:i,message:(t.message?String(t.message).replace(/^RESTART NEEDED:\s*/,""):"")||(r?"Restart After Effects: it loads plugins only at launch, and the installed build changed since it started.":i===!1?"After Effects is not running; start it to load the plugin.":"After Effects runs the current build.")}}catch(t){return{needed:null,running:null,message:"Could not tell whether After Effects needs a restart: "+t.message}}}function N(e){return require("../mcp/server").createAfterPresetsMcp(Object.assign({},e),{})}function I(e){return require("../mcp/extendscript").isAeScratchPath(e)}function C(e){const{wrapEval:s}=require("../mcp/extendscript");return async t=>{const r=await e(void 0,s("return "+t,{undo:!1}).script,15e3,"AEFT");return r&&r.ok?{ok:!0,result:r.value}:{ok:!1,error:r&&r.error||"no answer"}}}async function M(e,s){let t=null;try{t=s(e);const r=await C(t.env.hostEval)("'ok'");return r.ok&&r.result==="ok"}catch{return!1}finally{t&&await t.close().catch(()=>{})}}async function L(e,s,t){const r=t||{},i=v(e,"restart"),n=i.restartAfterEffects||i.restart||i.safeRestart;if(typeof n!="function")throw new h("This CLI cannot restart After Effects (no restart function in src/native/restart.js).");let o,l=null;try{let c=r.hostEval;c||(l=(r.createServer||N)(e),c=l.env.hostEval);const f=C(c),g=typeof i.projectState=="function"?await i.projectState(f):void 0,q=!!(g&&g.ok&&I(g.file)),U=r.waitForBridge||(()=>M(e,r.createServer||N));o=await n(e,{evalScript:f,state:g,scratch:q,waitForBridge:U,project:s})||{}}catch(c){o={restarted:!1,message:c.message}}finally{l&&await l.close().catch(()=>{})}const d=!!(o.restarted||o.ok);return{restarted:d,notRunning:!!o.notRunning,askUser:o.askUser!==void 0?!!o.askUser:!d,message:o.message||(d?"Restarted After Effects; it loaded the new build.":"Could not restart After Effects safely. Ask the user to save their work and restart After Effects.")}}async function V(e,s,t){const r=t||{},i=e.out,n={ok:!1,stage:"sdk",errors:[],warnings:[],artefacts:{},sdk:null,restart:null},o=D(e,s.root);if(o.status!=="current"&&i.info((o.status==="created"?"Added":"Updated")+" ap_native "+(o.version||"")+" in "+y.split(a.sep).join("/")+"/"),n.sdk=await j(e),!n.sdk.ok)return n.error=n.sdk.message,n.hint=n.sdk.next,n;n.stage="build";const l=await _(e,s,{mode:"dev",targets:[w(e)],nativeRuntimeDir:o.dir,sdk:n.sdk.raw});if(n.errors=l.errors,n.warnings=l.warnings,n.artefacts=l.artefacts,!l.ok)return n.error=l.error,n.hint=l.hint,n;n.stage="install";try{n.install=await v(e,"install").installDev(e,{project:s,root:s.root,settings:s.settings,artefacts:l.artefacts,build:l.result})||{}}catch(d){return n.error=d.message,n.hint=d.hint||null,n}if(n.install.ok===!1)return n.error=n.install.message||"Could not install the dev build.",n.hint=n.install.needsAdmin&&n.install.command?w(e)==="win"?"One-time setup needs an administrator (never elevate yourself). Ask the user to run this one line "+(n.install.where||"in PowerShell opened as administrator")+", then run the build again: "+n.install.command:"One-time setup needs the user's admin password. Ask the user to run this in Terminal: "+n.install.command:null,n;if(r.bridge!==!1)try{n.bridge=await require("./bridge").installDevBridge(e)}catch(d){n.bridge={status:"failed",error:d.message},n.warnings.push("Could not install the AfterPresets dev bridge ("+d.message+"). host_eval and comp_capture need it; run host_bridge to retry.")}if(r.docs&&(n.docs=await F(e,s)),n.stage="done",n.ok=!0,n.restart=await O(e,s),n.bridge&&(n.bridge.status==="created"||n.bridge.status==="updated")&&(n.restart.needed=!0,n.restart.message="Restart After Effects once: the dev bridge was just installed, and plugins load only at launch."),r.restart&&n.restart.needed&&n.restart.running!==!1){const d=w(e)==="win"?{ok:!0}:await(r.precheck||T)(e),c=d.ok?await L(e,s):{restarted:!1,askUser:!0,message:d.message};n.restart=Object.assign({},n.restart,{attempted:!0,restarted:c.restarted,askUser:c.askUser,message:c.message}),(c.restarted||c.notRunning)&&(n.restart.needed=!1)}return n}async function T(e){const s=r=>({ok:!1,message:"Did not restart After Effects: "+r+" Ask the user to save their work and restart After Effects."});let t=null;try{t=require("../mcp/server").createAfterPresetsMcp(Object.assign({},e),{});const r=await t.env.hostEval(void 0,require("../mcp/extendscript").projectInfoScript(),15e3,"AEFT");if(!r||!r.ok)return s("could not read its project ("+(r&&r.error||"no answer")+").");const i=r.value||{};return i.unsaved&&i.items>0&&!I(i.path)?s("the open project has unsaved changes."):{ok:!0,message:""}}catch(r){return s("could not read its project ("+r.message+").")}finally{t&&await t.close().catch(()=>{})}}async function F(e,s){try{const t=require("../project/docs");if(typeof t.ensureSdkDocs!="function")return{status:"skipped",message:"This CLI cannot convert the SDK guide yet."};const r=require("../build/runtime").findRuntimeDir(e,s.root);return await t.ensureSdkDocs(e,r,s.root,s.settings)}catch(t){return{status:"failed",message:t.message}}}function w(e){return(e.platform||process.platform)==="win32"?"win":"mac"}function J(e){const s=[];if(e.ok){const t=e.install&&(e.install.path||e.install.dir);s.push("Dev build installed"+(t?": "+t:"")+".")}else s.push((e.stage==="sdk"?"Cannot build yet: ":"Build FAILED: ")+(e.error||"unknown error")),e.hint&&s.push(e.hint);return e.errors&&e.errors.length&&(s.push("Errors:"),e.errors.slice(0,30).forEach(t=>s.push("- "+k(t))),e.errors.length>30&&s.push("- \u2026 "+(e.errors.length-30)+" more")),(e.warnings||[]).slice(0,20).forEach(t=>s.push("Warning: "+t)),e.docs&&e.docs.status!=="current"&&e.docs.message&&s.push(e.docs.message+(e.docs.hint?" "+e.docs.hint:"")),e.restart&&(e.restart.attempted?s.push(e.restart.message):e.restart.needed?s.push("RESTART NEEDED: "+e.restart.message):e.restart.message&&s.push(e.restart.message)),s}module.exports={VENDOR_DIR:y,VENDOR_LIST:m,nativeLib:v,findNativeRuntimeDir:b,nativeRuntimeVersion:A,runtimeFiles:R,vendorNativeRuntime:D,sdkStatus:j,diagnostic:p,formatDiagnostic:k,buildNativeSafe:_,restartState:O,restartAfterEffects:L,bridgePrecheck:T,runNativeDevBuild:V,ensureNativeDocs:F,describeDevRound:J,hostOs:w};
|