A lot has changed since 2019. I’ve posted my updated proposal in the Adobe Devs Slack but cross posting here as well:
UXP CLI Command Proposal
The Problem:
Executing CLI Commands (Command Prompt / Terminal) in UXP panels currently requires developers to build, sign, notarize, and ship binaries for each platform and call them with uxp.shell.openPath() even if all they are trying to accomplish are simple / one-liner commands.
The Solution:
An equivalent to child_process.exec() / child_process.execSync() in Node.js / CEP but for UXP, but with improved security.
A CLI command can do anything a shipped binary can, so permissions of each are roughly the same.
However, an app that can access the command line and internet is always a security concern and can be subject to remote code execution vulnerabilities.
A possible solution for this is to disallow code generation to be used in exec / execSync the same way that "allowCodeGenerationFromStrings": true currently works in Manifest v5.
A list of allowed commands could be included in the manifest like so:
Gathering system info needed for plugin such as system specs, network settings, managing environment variables.
Running CLI tools like ffmpeg, gimp.
Accessing a developer’s custom licensing framework.
I think adding this feature in some form would be an enormous relief for devs as this was one of the large concerns that arose in the community when UXP was announced alongside filesystem and webview which I’m happy to see are making progress since the start of UXP.
Even though UXP Hybrid Plugins now exist, all we would be doing is writing our own execSync(). We are still very interested in a UXP CLI as proposed here without having to take on the overhead of compiling a C++ plugin for the sole purpose of calling external tools required in a studio environment. Any thoughts as to something like this UXP CLI proposal happening? Thanks!
I very much agree with @joshy. We actually went the route of implementing a C++ hybrid plugin for this purpose but ultimately would love a more integrated solution that comes with UXP right away. I also think it would lower the bar for pipeline integration in a VFX/animation setting, where this kind of thing is currently a major obstacle.
For those interested in gaining CLI access, currently it’s not possible via UXP directly, however it can be via UXP C++ Hybrid Plugins.
Starting your own hybrid plugin from scratch or based off the example isn’t easy, but in our new UXP boilerplate Bolt UXP we have a Hybrid Plugin template available that comes pre-packaged with an execSync() function for you to start using.
let hybridModule: {
execSync: (cmd: string) => string;
} = await require("bolt-uxp-hybrid.uxpaddon");
let execSyncRes = hybridModule.execSync("echo test");
You only need C++ experience if you want to modify the hybrid portion of the plugin, if you don’t want or need to mess with that portion, you can just use as is from the JS side and start accessing the command line.
Bolt UXP is free and open-source, so feel free to use it, or if you have your own thing copy the Hybrid Plugin files into your own system:
First of all, super exciting to have a framework to facilitate this for people without in-depth C++ knowledge! Thank you @justin2taylor!!
Similar to @Jarda, I’m curious too if you have any experience with an async version? On my end I ended up implementing both a sync as well as async version, but ran into a few issues with the async version that might be somewhat inherent to the way Photoshop is scheduling its threads under the hood. To me it looked like while a sync version would run in the separate scripting thread, the async execution would be scheduled back on the main thread, not a child thread of the main thread and hence would be blocking. At least that was my observation. Then again, I didn’t spend a whole lot of time tinkering with it. I did also bring this up here.
P.S. In my version of execSync I ended up returning a tuple of status code and output, was wondering if there was a design decision to leave the exit status out?
Answered some of your questions in the response to Jarda here:
Haven’t yet added or attempted an async / standard exec() version yet, that is good to know your findings and link to the other thread. If there’s no way around that, then requesting more flexibility from the UXP dev team would be good.
And yea a tuple with result and error is a great idea, I’ll probably add something like that to this one.
Tracking here if you want to leave any additional comments:
Absolutely! If you end up using it would love to hear your feedback here or in our Discord we’ve got a bunch of Bolt CEP devs and now starting to get some Bolt UXP devs since the release: