
ffcpe-app-builder-actions
Describes how to implement FFCPE (Firefly Creative Production for Enterprise) custom-action web and worker OpenWhisk actions using @adobe/ffcpe-custom-node-core and @adobe/ffcpe-custom-node-app-builder (Hono, mountFfcpeNodeRoutes, createFfcpeNodeWorker, submit/status contract). Describes adding actions to existing Adobe App Builder repos: co-located .webworkerentry files, .config.yaml updates, and webpack + esbuild-loader partial for TypeScript with CommonJS output. Use when scaffolding or migrating FFCPE web/worker pairs, fixing ext.config.yaml / runtimeManifest, or debugging hono-openwhisk-adapter web actions (web: raw). After deploy, use aio ffcpe catalog with co-located .entry.json (skills in adobe/aio-cli-plugin-ffcpe) to register the action.
- Installs
- 68
- Last 24h
- —
- Trending rank
- —
- Security
- Not audited
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/adobe/ffcpe-custom-node-sdk --skill ffcpe-app-builder-actions- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads ffcpe-app-builder-actions automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
FFCPE App Builder actions (@adobe/ffcpe-custom-node-core, @adobe/ffcpe-custom-node-app-builder)
Use this skill when implementing or extending Workflow Builder custom actions in an Adobe App Builder (OpenWhisk) repo with @adobe/ffcpe-custom-node-core and @adobe/ffcpe-custom-node-app-builder.
For HTTP field-level contract details, see docs/custom-action-requests.md in this SDK repo. For consumer install and examples, see README.md. For package install, local linking, and security guidance, see ffcpe-custom-node-sdk. For catalog-entry.json and aio ffcpe catalog, use ffcpe-catalog-entry-json and aio-ffcpe-cli from adobe/aio-cli-plugin-ffcpe:
npx skills add adobe/aio-cli-plugin-ffcpe --all -y
Packages and peers
@adobe/ffcpe-custom-node-core— contract types,handleFfcpeSubmit/handleFfcpeStatus,runWorkerJob, input/output helpers, IMS inbound auth helpers.@adobe/ffcpe-custom-node-app-builder—mountFfcpeNodeRoutes,createFfcpeNodeWorker, AIOJobStore, OpenWhisk orchestrator,buildStatusUrl,createAioLogger.
Declare hono as a peer (your app chooses the version). Typical App Builder peers also include @adobe/aio-lib-state, @adobe/aio-lib-ims, @adobe/aio-lib-core-logging, and openwhisk — align versions with your template’s package.json.
Web action (Hono)
- Import
Hono,ToOpenWhiskActionfromhono-openwhisk-adapter, andmountFfcpeNodeRoutesfrom@adobe/ffcpe-custom-node-app-builder. const app = new Hono(), thenmountFfcpeNodeRoutes(app, { worker: { package, name }, web: { package, name } }).webandworkermust match OpenWhisk package and action names in your manifest (forstatusUrland async invoke).- Default routes are
POST /submitandGET /status; omitroutesunless paths differ from defaults. export const main = ToOpenWhiskAction(app)(or the entry shape your bundler expects).
The web action in *.config.yaml / ext.config.yaml must use web: "raw" and require-adobe-auth: false, plus a supported Node runtime (e.g. nodejs:22):
runtimeManifest:
packages:
my-ffcpe-app:
actions:
my-web-action:
function: actions/my-web-action.ts
web: "raw"
runtime: nodejs:22
annotations:
require-adobe-auth: false
my-worker:
function: actions/my-worker.ts
runtime: nodejs:22
Worker action
- Implement
FfcpeNodeWorkerHandler:ctxhasjobId,inputs(FfcpeInput[]),params(submitparameters),logger, optionalauthContext,args(fullmain(args)). - Return
{ status: "completed", outputs?: Output[] }or{ status: "failed", error: string }. UsecreateTextOutput,createImageOutput,createVideoOutput,createJsonOutput. export const main = createFfcpeNodeWorker(handler, options?).requiredInputNamesrefers to each input’sport, not the displayname.
Use getTextInput, getImageInput, etc. with ctx.inputs (helpers take the inputs array, not the full submit body).
Manifest inputs (secrets): usually environment variables — use process.env, not ctx.inputs. Do not log secrets.
Starting from scratch with init-bare
If you have no App Builder project yet, the fastest path is aio app init --standalone-app (or the init-bare script wrapper). Warning: this generator produces more than a bare skeleton — it scaffolds web-src/, actions/generic/, actions/publish-events/, test/, and e2e/ that are irrelevant to a headless FFCPE action. Follow these steps in order:
1. Create the Console project and workspace
# Create the project
aio console project create -n myproject -t "My Project Title" --json
# Stage workspace is created automatically; select it (use the project ID from the create output)
aio console project select myproject
aio console workspace select Stage --projectId <PROJECT_ID>
Console project name constraints (from real setup failures):
| Rule | Detail |
|---|---|
| Alphanumeric only | Hyphens and special characters are rejected (demo-qr-custom-ffcpe-nodes → Project name … is invalid). Use camelCase or a short slug: demoQrFfcpe. |
| Max 20 characters | Longer names fail with Project name length must be less than 20. |
| Name ≠ OpenWhisk package | The Console project name (e.g. demoQrFfcpe) can differ from the runtimeManifest package name in app.config.yaml (e.g. demo-qr-custom-ffcpe-nodes). Hyphens are fine in the YAML package and action names. |
| Stage auto-created | Production and Stage workspaces are created with the project; you usually do not need aio console workspace create for a first deploy. |
Console CLI flag reference — flags differ by command; mismatches throw NonExistentFlagsError:
| Command | Key flags |
|---|---|
aio console workspace create | --projectName <name> (required), --name <name> (required) |
aio console workspace select | positional [WORKSPACEIDORNAME] + --projectId <id> |
aio console workspace list | --projectId <id> (not --projectName) |
aio console workspace download | positional [DESTINATION] + --projectId <id> |
2. Initialise the project
aio app init -y --no-login --standalone-app --no-install
3. Wire the local app to the Console workspace
aio app use --no-input alone fails after init-bare because the local .aio file has no org/project/workspace context yet. The correct sequence is:
# Download the workspace credentials/config from Console
aio console workspace download # saves e.g. <orgId>-<project>-Stage.json
# Import that config — this populates .aio and .env
aio app use <orgId>-<project>-Stage.json --overwrite --no-input
If .env already exists (common after aio app init), a bare aio app use <file>.json prompts interactively and can hang in non-interactive/agent sessions. Always pass --overwrite --no-input when importing a downloaded workspace config into a scaffolded project.
The downloaded JSON file contains secrets (client credentials, API keys). Add it to .gitignore immediately after downloading — before any git add. The generated .gitignore does not cover this file automatically:
# Add the specific file, or use the pattern [0-9]*-*-*.json to cover all workspaces
echo '<orgId>-<project>-Stage.json' >> .gitignore
Also confirm .env is ignored (the generated .gitignore covers this via .env*, but verify it is present).
4. Clean up the scaffolded files
The generator creates files that conflict with a headless FFCPE action. Remove them:
rm -rf web-src actions/generic actions/publish-events test e2e
5. Fix app.config.yaml
The generated YAML includes web: web-src at the application level, which causes the frontend build step to run (and fail) on a headless project. Remove that line and replace the scaffolded actions with your FFCPE pair:
application:
actions: actions
runtimeManifest:
packages:
my-ffcpe-app:
license: Apache-2.0
actions:
my-action-web:
function: actions/my-action/my-action.web.ts
web: "raw"
runtime: nodejs:22
annotations:
require-adobe-auth: false
my-action-worker:
function: actions/my-action/my-action.worker.ts
runtime: nodejs:22
inputs:
LOG_LEVEL: debug
6. Replace webpack-config.js
The generator emits a config that uses ts-loader and is missing libraryTarget: "commonjs2". Replace it entirely (not a merge — the generated output section is wrong):
// webpack-config.js
module.exports = {
output: {
libraryTarget: 'commonjs2',
},
module: {
rules: [
{
test: /\.[jt]sx?$/,
loader: 'esbuild-loader',
options: { target: 'es2020' },
},
],
},
resolve: {
extensions: ['.ts', '.js'],
},
}
Install the loader:
npm install -D esbuild-loader
# or: pnpm add -D esbuild-loader
7. Install FFCPE packages
npm view @adobe/ffcpe-custom-node-core version
npm view @adobe/ffcpe-custom-node-app-builder version
npm view hono-openwhisk-adapter version
npm install @adobe/ffcpe-custom-node-core@latest @adobe/ffcpe-custom-node-app-builder@latest hono hono-openwhisk-adapter@latest
Use the latest published versions — run npm view <package> version before install (or install with @latest as above). Do not assume or pin stale ranges such as 0.1.x; those may not exist on the registry. Omitting a version also resolves to latest, but @latest makes intent explicit in docs and scripts. After install, package-lock.json pins exact versions for reproducibility.
Image outputs: createImageOutput expects an HTTPS URL, not raw bytes. Upload with @adobe/aio-lib-files (init(), write(), generatePresignURL()) — see the thumbnail example in the SDK README.md.
Multiple custom actions in one app
Each FFCPE node needs its own web + worker pair and co-located <action-name>.entry.json. Declare all pairs under one runtimeManifest package (or split across packages if your org prefers). Example with two nodes:
runtimeManifest:
packages:
my-ffcpe-app:
actions:
generate-foo-web: { function: actions/generate-foo/generate-foo.web.ts, web: "raw", … }
generate-foo-worker: { function: actions/generate-foo/generate-foo.worker.ts, … }
decode-foo-web: { function: actions/decode-foo/decode-foo.web.ts, web: "raw", … }
decode-foo-worker: { function: actions/decode-foo/decode-foo.worker.ts, … }
Each .web.ts calls mountFfcpeNodeRoutes with its own worker.name / web.name matching the YAML action keys.
Adding actions to an existing App Builder repo
1. Co-located files (recommended)
For each custom action, keep web, worker, and catalog entry JSON in the same directory, sharing the same <action-name> prefix:
<action-name>.web.tsor.js— Hono submit/status web action<action-name>.worker.tsor.js— async worker<action-name>.entry.json— run-workflow catalog entry (same document you pass toaio ffcpe catalog validate/register)
Example layout:
actions/
resize-image/
resize-image.web.ts
resize-image.worker.ts
resize-image.entry.json
The .entry.json file is not a separate template artifact — it is the catalog registration payload (handlerType: "custom-action", ports, customActionConfig, discovery fields). Author it with skill ffcpe-catalog-entry-json.
Flat co-location (same folder, no subdir) is fine if your repo already uses actions/resize-image.web.ts next to actions/resize-image.worker.ts — still name the catalog file resize-image.entry.json beside them.
2. *.config.yaml
Update runtimeManifest (or equivalent): declare the web action with web: "raw" and annotations.require-adobe-auth: false, plus the worker action; function paths must match your build outputs. mountFfcpeNodeRoutes package / name must match this YAML.
3. TypeScript + webpack (CJS for dist)
Install:
npm install -D esbuild-loader
# or
pnpm add -D esbuild-loader
If the project was initialized with init-bare, replace webpack-config.js entirely — the generated file uses ts-loader and lacks libraryTarget: "commonjs2". For other existing projects, merge into *webpack-config.js (preserve existing rules via webpack-merge or careful Object.assign):
output: {
libraryTarget: "commonjs2", // Important for CommonJS output
},
module: {
rules: [
{
// Match `.js`, `.jsx`, `.ts` or `.tsx` files
test: /\.[jt]sx?$/,
loader: "esbuild-loader",
options: {
// JavaScript version to compile to
target: "es2020",
},
},
],
},
resolve: {
extensions: [".ts", ".js"], // Add .ts and .js to the list of extensions to resolve
},
Deployed Runtime bundles are CommonJS; source can still be ESM-style TypeScript.
Register in the catalog (after deploy)
-
Deploy web + worker actions:
aio app build aio app deployDeploy output lists web action base URLs, e.g.:
https://3326322-myproject-stage.adobeioruntime.net/api/v1/web/my-ffcpe-app/my-action-web -
Derive catalog endpoints from that base URL (default Hono routes from
mountFfcpeNodeRoutes):Field Pattern submitEndpoint{web-action-base-url}/submitstatusEndpoint{web-action-base-url}/statusUse
/api/v1/web/(not/apis/v1/). The Runtime namespace in the hostname is lowercase (e.g.3326322-demoqrffcpe-stageeven when the Console project isdemoQrFfcpe). -
Fill in
<action-name>.entry.json—handlerType: "custom-action", port names matching the worker, endpoints from step 2, andcustomActionConfig.authentication. When the web action uses defaultmountFfcpeNodeRoutesauth (noauthenticate: null), set"authentication": { "type": "ims_service_token" }— notnone. Seeffcpe-catalog-entry-json. -
Install and auth the CLI plugin:
npm install -g @adobe/aio-cli aio plugins:install @adobe/aio-cli-plugin-ffcpe aio login aio console org select -
Validate and register — do not use raw curl (path is the co-located
.entry.json):aio console org select # required before catalog API calls aio ffcpe catalog validate --file ./actions/resize-image/resize-image.entry.json aio ffcpe catalog register --file ./actions/resize-image/resize-image.entry.jsonIf the action type already exists, use
aio ffcpe catalog update <actionType> --file …instead ofregister.
Agent checklist
- New project: ran
init-bare→ downloaded workspace config →aio app use <file>.json --overwrite --no-input→ removedweb-src/,actions/generic/,actions/publish-events/,test/,e2e/. - Console project name: alphanumeric, ≤20 chars; may differ from
runtimeManifestpackage name. app.config.yaml: noweb: web-srcline; FFCPE web + worker pairs declared (one pair per custom action).webpack-config.js:libraryTarget: "commonjs2",esbuild-loader,.ts/.jsextensions — replace theinit-baregenerated file, don't merge.- Web + worker names align with
mountFfcpeNodeRoutesand YAML. - Web:
web: "raw",annotations.require-adobe-auth: false. - Worker: FFCPE data from
ctx.inputs; secrets fromprocess.envonly; image outputs viaaio-lib-filespresigned URLs. - Catalog:
<action-name>.entry.jsonco-located; port names match worker ports;submitEndpoint/statusEndpointcopied from deploy output +/submit//status; authims_service_tokenwhen using default route auth;aio ffcpe catalog validatethenregister(orupdate).
FAQ
What does the ffcpe-app-builder-actions skill do?
Describes how to implement FFCPE (Firefly Creative Production for Enterprise) custom-action web and worker OpenWhisk actions using @adobe/ffcpe-custom-node-core and @adobe/ffcpe-custom-node-app-builder (Hono, mountFfcpeNodeRoutes, createFfcpeNodeWorker, submit/status contract). Describes adding actions to existing Adobe App Builder repos: co-located .webworkerentry files, .config.yaml updates, and webpack + esbuild-loader partial for TypeScript with CommonJS output. Use when scaffolding or migrating FFCPE web/worker pairs, fixing ext.config.yaml / runtimeManifest, or debugging hono-openwhisk-adapter web actions (web: raw). After deploy, use aio ffcpe catalog with co-located .entry.json (skills in adobe/aio-cli-plugin-ffcpe) to register the action.
How do I install ffcpe-app-builder-actions?
Run npx skills add https://github.com/adobe/ffcpe-custom-node-sdk --skill ffcpe-app-builder-actions in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/ffcpe-app-builder-actions/ (personal) or .claude/skills/ffcpe-app-builder-actions/ (project).
Which AI agents can use ffcpe-app-builder-actions?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is ffcpe-app-builder-actions safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains ffcpe-app-builder-actions?
It is published by adobe in adobe/ffcpe-custom-node-sdk. It has 68 recorded installs.
More from adobe/ffcpe-custom-node-sdk
ffcpe-custom-node-sdk
adobe/ffcpe-custom-node-sdk
Helps Adobe App Builder developers build FFCPE (Firefly Creative Production for Enterprise) custom actions using @adobe/ffcpe-custom-node-core and @adobe/ffcpe-custom-node-app-builder (OpenWhisk web + worker actions, Hono, submit/status HTTP contract, inputs/outputs, IMS-aware routes). Use when the user is wiring a custom-action app in App Builder, implementing FfcpeNodeWorkerHandler, mounting routes with mountFfcpeNodeRoutes, configuring web: raw OpenWhisk actions, debugging submit/statusUrl polling, handling Text/Image/Video/Json inputs, or producing action outputs for run-workflow. For catalog-entry.json and aio ffcpe catalog CLI, use companion skills from adobe/aio-cli-plugin-ffcpe.
Popular code quality & workflow skills
grill-me
mattpocock/skills
A relentless interview to sharpen a plan or design.
improve-codebase-architecture
mattpocock/skills
Scan a codebase for deepening opportunities, present them as a visual HTML report, then grill through whichever one you pick.
handoff
mattpocock/skills
Compact the current conversation into a handoff document for another agent to pick up.
domain-modeling
mattpocock/skills
Build and sharpen a project's domain model. Use when discussing codebase terminology, writing or editing a GLOSSARY.md, or recording or editing an ADR.
codebase-design
mattpocock/skills
Shared vocabulary for designing deep modules. Use when the user wants to design or improve a module's interface, find deepening opportunities, decide where a seam goes, make code more testable or AI-navigable, or when another skill needs the deep-module vocabulary.
diagnosing-bugs
mattpocock/skills
Diagnosis loop for hard bugs and performance regressions. Use when the user says "diagnose"/"debug this", or reports something broken/throwing/failing/slow.