cf-agent
A command-line tool for managing Adobe AEM Content Fragments for the Moveworks Marketplace — connectors and plugins.
Create, edit, publish, and inspect fragments across PROD / STAGE / DEV with an interactive guided mode or scriptable one-liners. Every rule (field types, allowed values, length limits, required fields) is read live from the AEM model, so the CLI always matches what AEM enforces.
Features
- Interactive guided mode (
-i) for creating and editing — pick from lists, see field descriptions, validate as you type. - Scriptable one-liners (
-f name=value) for automation. - Edit without a UUID — find and select a fragment by model + name filter, or by slug.
- Live validation against the model: required fields, enums, max-length, regex, kebab-case slugs, duplicate-slug detection, and cross-field rules.
- Content guides from markdown files, with automatic checking that every referenced AEM image exists.
- Upload assets straight from your machine (
asset upload) — logos and content-guide images go into the DAM without opening AEM. - Smart defaults — logo/asset folders auto-prefixed, plugin slugs seeded with the system name, model folders chosen automatically.
- Guardrails — immutable fields and review-locked fragments are caught up front, not after you fill in a form.
- Command history — ↑/↓ recall previous entries in interactive prompts.
Table of Contents
- Installation
- Authentication
- Environments
- Field reference
- Creating a fragment
- Updating a fragment
- Content guides & images
- Other fragment commands
- Models & assets
- Uploading assets
- Troubleshooting / Debug
Installation
Requires Python 3.10+. Install into a virtual environment to keep it isolated.
# 1. Create & activate a virtual environment
python3 -m venv ~/.venvs/cf-agent
source ~/.venvs/cf-agent/bin/activate
# 2. Install cf-agent
pip install "git+https://github.com/krishnakumar1990/cf-agent.git"
# 3. Verify
cf-agent --help
New terminal later? Re-activate first: source ~/.venvs/cf-agent/bin/activate
To auto-activate in every shell: echo 'source ~/.venvs/cf-agent/bin/activate' >> ~/.zshrc
To upgrade, uninstall, or reinstall, see Troubleshooting / Debug.
Authentication
Login uses Adobe IMS OAuth (browser-based). Credentials are stored in ~/.cf-agent/config, tokens in ~/.cf-agent/tokens.
cf-agent login # prompts for Adobe Client ID + Secret, opens browser, then pick an environment
cf-agent whoami # show the authenticated user, org, scopes, and active environment
cf-agent logout # clear stored tokens
If your team shares a preset with pre-filled config:
cf-agent login --preset shared.env
Tokens expire; if you see "Not logged in" or a 401, run cf-agent login again.
Environments
One login works across all environments. The active environment is where every fragment operation runs.
cf-agent env list # show PROD / STAGE / DEV and which is current
cf-agent env select # switch interactively
cf-agent env current # show the active environment
Field reference
Field names, types, and rules are read from the live model. These are the fields for each type today.
Connector (marketplace-connector)
| Field | Required | Rules |
|---|---|---|
marketplace_name |
✅ | Title Case; max 255 |
slug |
✅ | lowercase kebab-case; contains the system name; unique; immutable |
description |
✅ | ends with .; max 400 |
logo |
✅ | an SVG hosted in AEM; enter the file name only (auto-prefixed to the logos folder) |
solution_tags |
✅ | pick from the list or type a new Title Case tag; multiple allowed |
product_family |
✅ | from the model list (e.g. Google Cloud, Microsoft Graph) |
content_guide |
— | markdown guide; supply a file path |
video |
— | YouTube / Vimeo / Loom URL |
Plugin (marketplace-plugin)
| Field | Required | Rules |
|---|---|---|
marketplace_name |
✅ | Title Case; must not include the system name; max 255 |
slug |
✅ | lowercase kebab-case; must start with a system (e.g. workday-view-pto); unique; immutable |
description |
✅ | ends with .; max 400 |
availability |
✅ | from the model (VALIDATED, INSTALLABLE, IDEA, BUILT_IN); immutable after create |
installation_asset_uuid |
⛔/✅ | required when availability = INSTALLABLE, forbidden otherwise; lowercase UUID |
solution_tags |
✅ | pick from the list or type a new Title Case tag; multiple allowed |
purple_chat_link |
✅ | starts with https://marketplace.moveworks.com/purple-chat?conversation; no mock_id; max 30000 |
systems |
✅ | from the model list; multiple allowed; immutable |
agent_capabilities |
— | from the model list (e.g. Ambient Agent) |
content_guide |
— | markdown guide; supply a file path |
video |
— | YouTube / Vimeo / Loom URL |
Field names come from the live model — the interactive prompts always show the exact current name.
installation_asset_uuidwas formerlyinstallation_uuid; the CLI accepts both during the transition.
reviewRequired— setting this totruesends the fragment to review, which locks it from further edits until an approver releases it. Leave it off until the content is final.
Creating a fragment
Interactive (recommended)
cf-agent fragments create -i
Walks you through it: pick the model, then it fills the folder automatically and prompts each field with its rules. For plugins it asks for systems first and pre-seeds the slug (workday-…); installation_asset_uuid is only asked when availability is INSTALLABLE. Each value is validated as you enter it.
One-liner — Connector
cf-agent fragments create \
--parent-path "/content/dam/marketplace/content-fragment-resources/connector" \
--model-path "/conf/marketplace/settings/dam/cfm/models/marketplace-connector" \
--name "google-drive-connector" \
--title "Google Drive Connector" \
-f marketplace_name="Google Drive Connector" \
-f slug="google-drive-connector" \
-f description="Connects to Google Drive to search and retrieve files." \
-f logo="google-drive.svg" \
-f solution_tags="IT,Productivity" \
-f product_family="Google Workspace" \
-f content_guide=~/Desktop/connector-guide.md
One-liner — Plugin
cf-agent fragments create \
--parent-path "/content/dam/marketplace/content-fragment-resources/plugin" \
--model-path "/conf/marketplace/settings/dam/cfm/models/marketplace-plugin" \
--name "workday-view-pto-balance" \
--title "View PTO Balance" \
-f marketplace_name="View PTO Balance" \
-f slug="workday-view-pto-balance" \
-f description="Check your current PTO balance from the Moveworks AI Assistant." \
-f availability="VALIDATED" \
-f solution_tags="HR - Time & Absence,HR - Employee Records" \
-f purple_chat_link="https://marketplace.moveworks.com/purple-chat?conversation=%7B%22messages%22%3A%5B%5D%7D" \
-f systems="workday" \
-f agent_capabilities="Ambient Agent" \
-f content_guide=~/Desktop/plugin-guide.md
For an INSTALLABLE plugin, use -f availability="INSTALLABLE" and add -f installation_asset_uuid="34cff60f-f3c8-48f9-b1c9-7658ead0d994".
Multi-value fields are comma-separated inside one flag:
-f solution_tags="HR - Benefits,IT".
Updating a fragment
You can identify the fragment three ways — no UUID required.
1. Interactive (recommended)
cf-agent fragments update -i
- Pick the model (connector / plugin).
- Type a filter (name or slug) — or Enter to list all.
- Choose from the numbered results.
- Edit each editable field; press Enter to skip (keep the current value). Enums show their pick-list, exactly like create.
2. By slug (scriptable)
cf-agent fragments update --slug google-drive-connector \
--model-path "/conf/marketplace/settings/dam/cfm/models/marketplace-connector" \
-f content_guide=~/Desktop/updated-guide.md
3. By id
cf-agent fragments update <id> -f description="Updated description."
Which fields are editable
Only these fields can be changed on update — everything else is locked.
| Model | Editable fields |
|---|---|
| Connector | logo, content_guide |
| Plugin | marketplace_name, description, purple_chat_link, solution_tags, installation_asset_uuid, content_guide |
Locked (cannot be changed after creation): slug, systems, availability. Attempting to change one — or to edit any non-listed field — is rejected with a clear message.
Review-locked fragments: if a fragment was sent to review (
reviewRequired = true), the CLI tells you immediately on selection that it's locked and can't be edited until the review is completed or cancelled in AEM — so you don't fill in a form only to be refused at the end.
Content guides & images
content_guide is a markdown guide supplied as a file path (both create and update):
-f content_guide=~/Desktop/my-guide.md
When you provide the file, the CLI reads it and verifies every AEM image it references exists. It checks /content/dam/... paths in markdown images , links [](…), and HTML <img src="…"> (full AEM URLs too). If any referenced image is missing, the operation is blocked and the missing paths are listed.
Workflow for images: upload the image to AEM first — cf-agent asset upload ./shot.png --image (see Uploading assets) or by hand in AEM — reference it by the /content/dam/... path it returns, then create/update the guide. Relative image paths from a raw export (e.g. ) are not uploaded automatically — upload them and reference them by DAM path.
Other fragment commands
# List (optionally by folder)
cf-agent fragments list --path /content/dam/marketplace/content-fragment-resources/connector --limit 25
# Get one by id
cf-agent fragments get <id>
# Dry-run validate a payload against the model — no write
cf-agent fragments validate --model-path "$CONN_M" -f description="A connector." --partial
# Publish one or more
cf-agent fragments publish <id> [<id> ...]
# Delete (‑‑yes to skip the prompt)
cf-agent fragments delete <id> --yes
# Copy to another folder (‑‑deep to include references)
cf-agent fragments copy <id> --destination /content/dam/.../archive [--deep]
# List variations
cf-agent fragments variations <id>
cf-agent fragments searchexists but the underlying AEM search endpoint is not available on all environments — preferfragments listor theupdate -ifilter to find fragments.
Models & assets
# List Content Fragment Models (connector, plugin, …)
cf-agent models list
# Check whether an asset exists in the DAM (bare name resolved against a known folder)
cf-agent asset exists workday.svg --logo
cf-agent asset exists /content/dam/marketplace/logos/workday.svg
asset exists returns exit code 0 if present, 1 if not — usable in scripts. It requires the aem.assets.author scope.
Uploading assets
asset upload puts a local file into the AEM DAM, so logos and content-guide images no longer have to be uploaded by hand in AEM.
One-time setup
Store your AWS credentials once — they are used to stage the file (see How it works below). Ask the team for a key; it will be sent to you securely.
cf-agent asset credentials set
You are prompted for both values. The secret is hidden as you paste it, so it never lands in your shell history.
cf-agent asset credentials show # confirm they're stored (never prints the secret)
cf-agent asset credentials clear # remove them
Credentials are kept in your OS keychain — macOS Keychain, Windows Credential Manager, or the Linux Secret Service — encrypted at rest, never in a plaintext file and never in the repo. Nothing else needs configuring: the staging bucket, region and prefix ship with the CLI.
Upload
# Into the marketplace logos folder
cf-agent asset upload ./workday.svg --logo
# Into the marketplace images folder
cf-agent asset upload ./screenshot.png --image
# Into any DAM folder
cf-agent asset upload ./diagram.png --root /content/dam/marketplace/screenshots
# Rename on the way in
cf-agent asset upload ./local-name.png --logo --name workday.svg
On success it prints the DAM path and assetId:
✓ Uploaded: /content/dam/marketplace/logos/workday.svg
assetId: urn:aaid:aem:d3fc8f49-4c8f-49ee-b01f-3f00999201b0
Use that /content/dam/... path directly as a logo value or in content-guide markdown. Add --json for scripting.
How it works
AEM's Assets API cannot accept a binary directly from your user token — it can only pull an asset from a URL. So the CLI uploads the file to an S3 staging bucket, hands AEM a short-lived pre-signed URL to fetch it from, waits for the import to finish, then deletes the staged copy. This is why AWS credentials are needed at all; the staged object is temporary and removed automatically.
Credential precedence:
AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEYenvironment variables win if set (useful in CI), then the OS keychain, then any~/.awsprofile. Set nothing and the keychain is used.
Troubleshooting / Debug
Update the CLI to the latest version
source ~/.venvs/cf-agent/bin/activate
pip install --upgrade "git+https://github.com/krishnakumar1990/cf-agent.git"
cf-agent --help # confirm it still runs
Uninstall & reinstall
source ~/.venvs/cf-agent/bin/activate
# uninstall
pip uninstall cf-agent
# reinstall (fresh copy)
pip install "git+https://github.com/krishnakumar1990/cf-agent.git"
For a completely clean slate, delete and recreate the virtual environment:
deactivate 2>/dev/null
rm -rf ~/.venvs/cf-agent
python3 -m venv ~/.venvs/cf-agent
source ~/.venvs/cf-agent/bin/activate
pip install "git+https://github.com/krishnakumar1990/cf-agent.git"
Uninstalling does not remove your login. Config and tokens live in
~/.cf-agent/— delete that folder to fully reset (rm -rf ~/.cf-agent), thencf-agent loginagain.
Common errors
| Message | Cause & fix |
|---|---|
Not logged in / 401 |
Token expired → cf-agent login. |
403 Forbidden — You are not allowed to modify this fragment |
The fragment is review-locked (reviewRequired = true) or your Adobe ID lacks write access on this environment. Release the review in AEM, or ask an admin for access. |
… is in review and locked |
Sent to review — complete/cancel the review in AEM, then retry. |
Field 'X' is not editable on update |
Only the editable fields above can change on update. |
Field 'X' cannot be changed after creation |
slug / systems / availability are immutable. |
Slug '…' is already in use |
Choose a unique slug. |
Referenced asset does not exist in AEM |
The logo / a content-guide image isn't in the DAM — upload it with cf-agent asset upload … --logo / --image, or check with cf-agent asset exists …. |
cf-agent: command not found |
The venv isn't active → source ~/.venvs/cf-agent/bin/activate. |
Asset upload requires boto3 / requires 'keyring' |
Your install predates 1.1.0, when these became base dependencies → pip install --upgrade "git+https://github.com/krishnakumar1990/cf-agent.git". |
Could not stage file to S3: access denied |
The AWS key lacks permission on the staging bucket, or none is set → cf-agent asset credentials show. |
The staged file isn't readable via its pre-signed URL |
The AWS key can write but not read the staging bucket — it needs s3:GetObject too. Ask the team for a corrected key. |
Inspect your session
cf-agent whoami # user, org, scopes, token expiry, active environment
cf-agent env current # which environment you're pointed at
whoami is the fastest way to debug 403s — confirm the token's org matches the environment.
Metadata
Release files for cf-agent 1.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| cf_agent-1.2.0.tar.gz | 54.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cf_agent-1.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 112.4 kB
Release files / cf_agent-1.2.0.tar.gz
| Download URL | cf_agent-1.2.0.tar.gz |
|---|---|
| Size | 54.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2e615bf531d61795493996342dcd702a1e2a4bd31593c20dad2f6940bfd8fd5f
|
|
BLAKE2b-256 checksum How to use checksums |
e15c1733909414ae82b8413500a1c2f298d0d015cce8a02f21d27be9e335863c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.15
|
Release files / cf_agent-1.2.0-py3-none-any.whl
| Download URL | cf_agent-1.2.0-py3-none-any.whl |
|---|---|
| Size | 57.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
cf8fbf642c22071ddbde09eb6227afa4e4eeb7a37086e19178824a7a7384d946
|
|
BLAKE2b-256 checksum How to use checksums |
1e6fc847d35325f9fb12b060ab02a8a8f08334eb08aa5bc1776192e909641d5b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.15
|