Claude
Skills
Sign in
Back

ops

Included with Lifetime
$97 forever

Execute AEM Edge Delivery Services admin operations - list admins, add/remove users, preview, publish, unpublish content, clear cache, sync code, reindex, generate sitemap, manage snapshots, view logs, manage jobs, list sites, configure org/site settings, manage secrets and API keys. Also supports Document Authoring (DA) operations via admin.da.live - list/get/put content, copy, move, delete, versioning, and DA-specific preview/publish. Use for any Edge Delivery Services administrative task.

Backend & APIs

What this skill does


# Edge Delivery Services Admin Operations

Execute admin operations on AEM Edge Delivery Services projects using natural language commands.

## Quick Reference

| Category | Examples |
|----------|----------|
| **Content** | preview /path, publish /path, unpublish /path, status /path |
| **Cache** | clear cache /path, force clear cache |
| **Code** | sync code, deploy code |
| **Index** | reindex /path, remove from index |
| **Sitemap** | generate sitemap |
| **Snapshots** | create snapshot X, publish snapshot X, approve snapshot X |
| **Logs** | show logs, show logs last hour |
| **Users** | add user@email as author/publish/develop, remove admin user@email, who am i |
| **Jobs** | list jobs, job status X, stop job X |
| **Sites** | list sites, switch to site-X, use branch feature-X |
| **Config** | show org config, show site config, update robots.txt |
| **Secrets** | list secrets, create secret, delete secret |
| **API Keys** | list API keys, create API key, revoke API key |
| **Tokens** | list tokens, create token, revoke token |
| **Profiles** | show profile config, create profile, delete profile |
| **Index Config** | show index config, update index config (query.yaml) |
| **Sitemap Config** | show sitemap config, update sitemap config (sitemap.yaml) |
| **Versioning** | list versions, restore version, rollback config |
| **Pages** | list pages, list all pages, show indexed pages |
| **DA (Document Authoring)** | da list, da source /path, da copy, da move, da delete, da config, da update config, da versions, da create version, da upload media, da auth |

---

## Communication Guidelines

- **NEVER use "EDS"** as an acronym for Edge Delivery Services in any responses
- Always use the full name "Edge Delivery Services" or "AEM Edge Delivery Services"
- Show clear, actionable error messages when operations fail
- Confirm destructive operations before executing

---

## Welcome Message

If user invokes the skill without a specific command (e.g., just `/ops` or "help me with ops"), show:

```
Edge Delivery Services Operations

Quick commands to try:
  list pages       - Show all indexed pages
  who am i         - Check your user profile
  list sites       - Show available sites
  show site config - View site configuration
  preview /path    - Preview a content path
  show logs        - View recent activity

For the full command list: type help, /ops help, or what can you do? (slash commands may be /ops help or /ops what can you do? depending on your client).
```

---

## Cross-Platform Notes

Shell commands in this skill use POSIX-compatible syntax (works on macOS/Linux). On Windows:
- **Git Bash / WSL**: Commands work as-is
- **PowerShell**: Claude Code will translate commands automatically using available shell

The agent executing these commands should adapt syntax to the user's environment.

---

## Intent Router

Analyze user request and load the appropriate resource module.

### Step 0: Get Organization Name (REQUIRED FIRST)

**Before ANY operation**, check `~/.aem/ops-config.json` for a previously stored org:

```bash
ORG=$(node -e "
  const fs = require('fs');
  try {
    const c = JSON.parse(fs.readFileSync(process.env.HOME + '/.aem/ops-config.json', 'utf8'));
    process.stdout.write(c.org || '');
  } catch(e) {}
")
echo "org=${ORG:-NOT SET}"
```

**If `ORG` is set**, confirm with the user:

> "Previously used org: `{ORG}`. Do you want to continue with this org, or use a different one?"

- If user confirms → proceed
- If user provides a different org → save the new value

**If `ORG` is empty**, ask the user:

> "What is your Config Service organization name? This is the `{org}` part of your Edge Delivery Services URLs (e.g., `https://main--site--{org}.aem.page`).
>
> **Note:** The org name may differ from your GitHub organization, especially in repoless multi-site setups."

**Save org to `~/.aem/ops-config.json`:**

```bash
mkdir -p "${HOME}/.aem"
node -e "
  const fs = require('fs');
  const p = process.env.HOME + '/.aem/ops-config.json';
  let c = {};
  try { c = JSON.parse(fs.readFileSync(p, 'utf8')); } catch(e) {}
  c.org = '{ORG_NAME}';
  fs.writeFileSync(p, JSON.stringify(c, null, 2));
"
```

**STRICTLY FORBIDDEN - Do NOT attempt any of these to get org name:**
- `git remote -v` - GitHub org often differs from Config Service org
- Reading `fstab.yaml` - Does not contain org name
- Inferring from folder/repo names - Unreliable
- Any other inference method

**ONLY use the org name from:**
- Saved config (`~/.aem/ops-config.json`)
- Direct user input when prompted

**Do NOT proceed until org is confirmed.**

### Step 1: Authenticate (REQUIRED)

**Before ANY API call**, check if IMS token exists:

```bash
IMS_TOKEN=$(node -e "
  const fs = require('fs');
  try {
    const t = JSON.parse(fs.readFileSync(process.env.HOME + '/.aem/ims-token.json', 'utf8'));
    if (t.imsToken && t.imsTokenExpiry > Math.floor(Date.now()/1000) + 60) {
      process.stdout.write(t.imsToken);
    }
  } catch (e) {}
")
echo "auth=${IMS_TOKEN:+set}"
```

**If `IMS_TOKEN` is empty**, invoke the auth skill BEFORE proceeding:

```
Skill({ skill: "project-management:auth" })
```

**IMPORTANT:** Do NOT skip this step. Do NOT attempt any API calls without a valid token. Use `Authorization: Bearer ${IMS_TOKEN}` header for all API calls.

### Step 2: Load Full Configuration and Validate Role

After auth is confirmed, load full config from `~/.aem/ops-config.json`:

```bash
eval $(node -e "
  const fs = require('fs');
  try {
    const c = JSON.parse(fs.readFileSync(process.env.HOME + '/.aem/ops-config.json', 'utf8'));
    console.log('ORG=' + JSON.stringify(c.org || ''));
    console.log('SITE=' + JSON.stringify(c.site || ''));
    console.log('REF=' + JSON.stringify(c.ref || 'main'));
  } catch(e) {
    console.log('ORG='); console.log('SITE='); console.log('REF=main');
  }
")
IMS_TOKEN=$(node -e "
  const fs = require('fs');
  try {
    const t = JSON.parse(fs.readFileSync(process.env.HOME + '/.aem/ims-token.json', 'utf8'));
    process.stdout.write(t.imsToken || '');
  } catch (e) {}
")
echo "Config: org=$ORG site=$SITE ref=$REF auth=${IMS_TOKEN:+set}"
```

**Fetch profile** to verify auth and record user identity:

```bash
PROFILE_RESPONSE=$(curl -s -w "\n%{http_code}" \
  -H "Authorization: Bearer ${IMS_TOKEN}" \
  "https://admin.hlx.page/profile")
HTTP_CODE=$(echo "$PROFILE_RESPONSE" | tail -n1)
PROFILE=$(echo "$PROFILE_RESPONSE" | sed '$d')

if [ "$HTTP_CODE" = "401" ]; then
  echo "Auth token expired. Clearing cached token..."
  rm -f "${HOME}/.aem/ims-token.json"
  echo "REAUTH_REQUIRED"
  exit 1
elif [ "$HTTP_CODE" != "200" ]; then
  echo "Failed to fetch profile (HTTP $HTTP_CODE). Check network/API status."
  exit 1
fi

# Profile response: {"profile": {"email": "...", "name": "...", "ttl": ...}}
eval $(echo "$PROFILE" | node -e "
  const d = require('fs').readFileSync(0,'utf8');
  try {
    const p = JSON.parse(d).profile || {};
    console.log('USER_EMAIL=' + JSON.stringify(p.email || ''));
    console.log('USER_NAME=' + JSON.stringify(p.name || ''));
  } catch(e) { console.log('USER_EMAIL=\"\"'); console.log('USER_NAME=\"\"'); }
")

echo "Authenticated as: $USER_EMAIL ($USER_NAME)"
```

**Important:** The `/profile` endpoint does **not** return a role. To determine if the user is admin or author on a site, check the site access config:

```bash
# Determine user role on the current site
ACCESS_RESPONSE=$(curl -s \
  -H "Authorization: Bearer ${IMS_TOKEN}" \
  "https://admin.hlx.page/config/${ORG}/sites/${SITE}.json")
# Check which role(s) the user's email appears in within access.admin.role
# Roles: admin, author, publish, basic_author, basic_publish, develop, config, config_admin
```

If an operation returns 403, inform the user which role is required. Key role requirements:
- **Preview** → `basic_author`, `author`, `publish`, or `admin`
- **Publish to live** → `basic_publish`, `publish`, or `admin`
- **Unpublish from live** → `publish` or `admin`
- **Code

Related in Backend & APIs