aem-mcp-server 1.5.1 → 1.6.1

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 CHANGED
@@ -124,16 +124,19 @@ All tools will get an `instance` parameter to target a specific instance.
124
124
 
125
125
  ## Features
126
126
 
127
- - **AEM Page & Asset Management**: Create, update, delete, activate, deactivate, and replicate pages and assets
127
+ - **57 MCP Tools** covering pages, components, assets, workflows, content fragments, and experience fragments
128
+ - **MCP Resources** — agents discover components, sites, templates, and workflow models upfront via `resources/list`, eliminating discovery roundtrips
129
+ - **Tool Annotations** — every tool tagged with `group`, `readOnly`, and `complexity` so agents can make smarter tool selection decisions
130
+ - **Response Verbosity** — `verbosity` parameter (`summary`/`standard`/`full`) on content-reading tools strips JCR internals and truncates long text
131
+ - **Actionable Errors** — error responses include `suggestion` and `alternatives` fields for self-healing agent workflows
128
132
  - **Component Operations**: Update, scan, add, convert, and bulk-manage AEM components (including Experience Fragments)
133
+ - **Content & Experience Fragments**: Full CRUD + variation management for both CF and XF
129
134
  - **Advanced Search**: QueryBuilder, fulltext, fuzzy, and enhanced page search
130
- - **Replication & Rollout**: Publish/unpublish content, roll out changes to language copies
135
+ - **Replication & Workflows**: Publish/unpublish content, start/advance/delegate workflow stages
131
136
  - **Text & Image Extraction**: Extract all text and images from pages, including fragments
132
137
  - **Template & Structure Discovery**: List templates, analyze page/component structure
133
- - **Workflow and Inbox Operations**: List, start, advance, suspend, and delegate workflow stages
134
- - **JCR Node Access**: Legacy and modern node/content access
135
- - **AI/LLM Integration**: Natural language interface for AEM via any MCP-compatible client
136
- - **Security**: Auth, environment-based config, and safe operation defaults
138
+ - **Multi-instance**: Connect to multiple AEM instances simultaneously; tools and resources are instance-aware
139
+ - **Security**: Basic auth and OAuth S2S, environment-based config, safe operation defaults
137
140
 
138
141
  ---
139
142
 
@@ -145,6 +148,19 @@ Once configured in your AI IDE, just ask in natural language:
145
148
  List all components on MyPage
146
149
  ```
147
150
 
151
+ ## MCP Resources
152
+
153
+ The server exposes read-only MCP resources so agents can discover AEM catalogs without tool calls:
154
+
155
+ | Resource URI | Description |
156
+ |---|---|
157
+ | `aem://{instance}/components` | All components (name, resourceType, title, group) |
158
+ | `aem://{instance}/sites` | Site roots and language structure under /content |
159
+ | `aem://{instance}/templates` | Available page templates (path, title) |
160
+ | `aem://{instance}/workflow-models` | Workflow models (ID, title, description) |
161
+
162
+ Resources return summary data only. In multi-instance mode, each instance gets its own set of resource URIs.
163
+
148
164
  ## API Documentation
149
165
 
150
166
  For detailed API documentation, please refer to the [API Docs](docs/API.md).
@@ -1 +1 @@
1
- "use strict";import{getAEMConfig as B,isValidContentPath as v}from"./aem.config.js";import{AEM_ERROR_CODES as P,createAEMError as $,createSuccessResponse as y,handleAEMHttpError as M,safeExecute as C}from"./aem.errors.js";import{AEMFetch as k}from"./aem.fetch.js";import{ContentFragmentManager as J}from"./aem.content-fragments.js";import{ExperienceFragmentManager as H}from"./aem.experience-fragments.js";import{LOGGER as p}from"../utils/logger.js";export class AEMConnector{isInitialized;isAEMaaCS;config;aemConfig;fetch;contentFragments;experienceFragments;constructor(e){this.isInitialized=!1,this.config=this.loadConfig(e),this.aemConfig=B({}),this.isAEMaaCS=this.isConfigAEMaaCS(),this.fetch=new k({host:this.config.aem.host,auth:this.config.aem.auth,timeout:this.aemConfig.queries.timeoutMs}),this.contentFragments=new J(this.fetch,this.isAEMaaCS),this.experienceFragments=new H(this.fetch,this.config.aem.host)}async init(){await this.fetch.init(),this.isInitialized=!0}isConfigAEMaaCS(){return!!(this.config.aem.auth.clientId&&this.config.aem.auth.clientSecret)}loadConfig(e={}){let t;return e.id&&e.secret?t={clientId:e.id,clientSecret:e.secret}:t={username:e.user||"admin",password:e.pass||"admin"},{aem:{host:e.host||"http://localhost:4502",author:e.host||"http://localhost:4502",publish:"http://localhost:4503",auth:t,endpoints:{content:"/content",dam:"/content/dam",query:"/bin/querybuilder.json",crxde:"/crx/de",jcr:""}},mcp:{name:"NAEM MCP Server",version:"1.0.0"}}}getPreviewUrl(e){return`${this.config.aem.host}${e}.html?wcmmode=disabled`}async testConnection(){const e=await this.testAEMConnection(),t=e?await this.testAuthConnection():!1;return{aem:e,auth:t}}async testAEMConnection(){try{this.isInitialized||await this.init();const e="/libs/granite/core/content/login.html";p.log("Testing AEM connection to:",e);const t=await this.fetch.get(e,void 0,void 0,5e3,!0);return p.log("\u2705 AEM connection successful!"),!0}catch(e){return p.error("\u274C AEM connection failed:",e.message),!1}}async testAuthConnection(){try{this.isInitialized||await this.init();const e="/libs/granite/security/currentuser.json";p.log("Testing AEM authentication connection to:",e);const t=await this.fetch.get(e,void 0,void 0,5e3);return p.log("\u2705 AEM authentication connection successful!"),!0}catch(e){return p.error("\u274C AEM authentication connection failed:",e.message),!1}}validateComponentProps(e,t,n){const r=[],o=[];return t==="text"&&!n.text&&!n.richText&&r.push("Text component should have text or richText property"),t==="image"&&!n.fileReference&&!n.src&&o.push("Image component requires fileReference or src property"),{valid:o.length===0,errors:o,warnings:r,componentType:t,propsValidated:Object.keys(n).length}}async updateComponent(e){return C(async()=>{if(!e.componentPath||typeof e.componentPath!="string")throw $(P.INVALID_PARAMETERS,"Component path is required and must be a string");if(!e.properties||typeof e.properties!="object")throw $(P.INVALID_PARAMETERS,"Properties are required and must be an object");if(!v(e.componentPath,this.aemConfig))throw $(P.INVALID_PATH,`Component path '${e.componentPath}' is not within allowed content roots`,{path:e.componentPath,allowedRoots:Object.values(this.aemConfig.contentPaths)});const t=`${e.componentPath}.json`;let n;try{if(n=await this.fetch.get(t),!n||typeof n=="object"&&Object.keys(n).length===0)throw $(P.COMPONENT_NOT_FOUND,`Component not found at path: ${e.componentPath}`,{componentPath:e.componentPath})}catch(i){throw i.code===P.COMPONENT_NOT_FOUND?i:i.message?.includes("404")||i.response?.status===404?$(P.COMPONENT_NOT_FOUND,`Component not found at path: ${e.componentPath}`,{componentPath:e.componentPath}):M(i,"updateComponent")}const r=n["sling:resourceType"];if(r){p.log(`Validating properties against component definition for: ${r}`);const i=await this.getComponentDefinition(r);if(Object.keys(i.fieldDefinitions).length>0){const l=this.validateComponentProperties(e.properties,i.fieldDefinitions);if(!l.valid){const c=l.errors.join("; ");throw $(P.INVALID_PARAMETERS,`Invalid property values for component ${r}: ${c}`,{resourceType:r,componentPath:i.componentPath,validationErrors:l.errors,invalidFields:l.invalidFields,providedProperties:e.properties})}l.warnings.length>0&&p.warn(`Property validation warnings: ${l.warnings.join("; ")}`),p.log("Property values validated successfully against component dialog definitions")}}const o=new URLSearchParams;r&&o.append("sling:resourceType",r),Object.entries(e.properties).forEach(([i,l])=>{l==null?o.append(`${i}@Delete`,""):Array.isArray(l)?l.forEach(c=>{o.append(`${i}`,c.toString())}):typeof l=="object"?o.append(i,JSON.stringify(l)):o.append(i,l.toString())});const s=await this.fetch.post(e.componentPath,o,{headers:{Accept:"application/json"}}),a=await this.fetch.get(`${e.componentPath}.json`);return y({message:"Component updated successfully",path:e.componentPath,properties:e.properties,updatedProperties:a,response:s,verification:{success:!0,propertiesChanged:Object.keys(e.properties).length,timestamp:new Date().toISOString()}},"updateComponent")},"updateComponent")}async scanPageComponents(e){return C(async()=>{const t=`${e}.infinity.json`,n=await this.fetch.get(t),r=[],o=(s,a)=>{!s||typeof s!="object"||(s["sling:resourceType"]&&r.push({path:a,resourceType:s["sling:resourceType"],properties:{...s}}),Object.entries(s).forEach(([i,l])=>{if(typeof l=="object"&&l!==null&&!i.startsWith("rep:")&&!i.startsWith("oak:")){const c=a?`${a}/${i}`:i;o(l,c)}}))};return n["jcr:content"]?o(n["jcr:content"],"jcr:content"):o(n,e),y({pagePath:e,components:r,totalComponents:r.length},"scanPageComponents")},"scanPageComponents")}async fetchSites(){return C(async()=>{const t=await this.fetch.get("/content.2.json",{":depth":"2"}),n=[];return Object.entries(t).forEach(([r,o])=>{r.startsWith("jcr:")||r.startsWith("sling:")||o&&typeof o=="object"&&o["jcr:content"]&&n.push({name:r,path:`/content/${r}`,title:o["jcr:content"]["jcr:title"]||r,template:o["jcr:content"]["cq:template"],lastModified:o["jcr:content"]["cq:lastModified"]})}),y({sites:n,totalCount:n.length},"fetchSites")},"fetchSites")}async fetchLanguageMasters(e){return C(async()=>{const t=`/content/${e}.2.json`,n=await this.fetch.get(t),r=[];let o=null,s="";return Object.entries(n).forEach(([a,i])=>{(a==="master"||a==="language-masters")&&i&&typeof i=="object"&&(o=i,s=`/content/${e}/${a}`)}),o?(Object.entries(o).forEach(([a,i])=>{a.startsWith("jcr:")||a.startsWith("sling:")||i&&typeof i=="object"&&r.push({name:a,path:`${s}/${a}`,title:i["jcr:content"]?.["jcr:title"]||i["jcr:title"]||a,language:i["jcr:content"]?.["jcr:language"]||i["jcr:language"]||a})}),y({site:e,languageMasters:r},"fetchLanguageMasters")):y({site:e,languageMasters:[],message:"No master or language-masters node found"},"fetchLanguageMasters")},"fetchLanguageMasters")}async fetchAvailableLocales(e){return C(async()=>{const t=`/content/${e}.4.json`,n=await this.fetch.get(t),r={},o=(s,a,i=[])=>{!s||typeof s!="object"||Object.entries(s).forEach(([l,c])=>{if(!(l.startsWith("jcr:")||l.startsWith("sling:")||l.startsWith("cq:")||l.startsWith("rep:")||l.startsWith("oak:")||l==="jcr:content")&&c&&typeof c=="object"){const h=`${a}/${l}`,A=[...i,l],T=c["jcr:content"],m=T&&typeof T=="object",j=T?.["jcr:language"]||null,S=l.length===2||l.length===3,b=i.length>0&&(i[i.length-1].length===2||i[i.length-1].length===3);if(m&&S&&b){const d=i[i.length-1].toUpperCase(),g=l.toLowerCase(),u=`${g}_${d}`;r[u]={path:h,title:T?.["jcr:title"]||l,language:j||`${g}_${d}`,country:d}}o(c,h,A)}})};return o(n,`/content/${e}`,[]),y({site:e,locales:r,totalCount:Object.keys(r).length},"fetchAvailableLocales")},"fetchAvailableLocales")}async getAllTextContent(e){return C(async()=>{let t;try{const o=`${e}.infinity.json`;t=await this.fetch.get(o)}catch(o){if(o.message?.includes("300")||o.message?.includes("redirect")){p.warn(`infinity.json failed for ${e}, trying depth-based approach`);try{t=await this.fetch.get(`${e}.5.json`)}catch{try{t=await this.fetch.get(`${e}/jcr:content.5.json`)}catch{throw M(o,"getAllTextContent")}}}else throw M(o,"getAllTextContent")}const n=[],r=(o,s)=>{!o||typeof o!="object"||((o.text||o["jcr:title"]||o["jcr:description"])&&n.push({path:s,title:o["jcr:title"],text:o.text,description:o["jcr:description"]}),Object.entries(o).forEach(([a,i])=>{if(typeof i=="object"&&i!==null&&!a.startsWith("rep:")&&!a.startsWith("oak:")){const l=s?`${s}/${a}`:a;r(i,l)}}))};return t["jcr:content"]?r(t["jcr:content"],"jcr:content"):r(t,e),y({pagePath:e,textContent:n},"getAllTextContent")},"getAllTextContent")}async getPageTextContent(e){return C(async()=>this.getAllTextContent(e),"getPageTextContent")}async getPageImages(e){return C(async()=>{const t=`${e}.infinity.json`,n=await this.fetch.get(t),r=[],o=(s,a)=>{!s||typeof s!="object"||((s.fileReference||s.src)&&r.push({path:a,fileReference:s.fileReference,src:s.src,alt:s.alt||s.altText,title:s["jcr:title"]||s.title}),Object.entries(s).forEach(([i,l])=>{if(typeof l=="object"&&l!==null&&!i.startsWith("rep:")&&!i.startsWith("oak:")){const c=a?`${a}/${i}`:i;o(l,c)}}))};return n["jcr:content"]?o(n["jcr:content"],"jcr:content"):o(n,e),y({pagePath:e,images:r},"getPageImages")},"getPageImages")}async updateImagePath(e,t){return C(async()=>this.updateComponent({componentPath:e,properties:{fileReference:t}}),"updateImagePath")}async getPageContent(e){return C(async()=>{const t=`${e}.infinity.json`,n=await this.fetch.get(t);return y({pagePath:e,content:n},"getPageContent")},"getPageContent")}async listChildren(e,t=1){return C(async()=>{try{const n=await this.fetch.get(`${e}.${t}.json`),r=[];return n&&typeof n=="object"&&Object.entries(n).forEach(([o,s])=>{if(!(o.startsWith("jcr:")||o.startsWith("sling:")||o.startsWith("cq:")||o.startsWith("rep:")||o.startsWith("oak:")||o==="jcr:content")&&s&&typeof s=="object"){const a=`${e}/${o}`;r.push({name:o,path:a,primaryType:s["jcr:primaryType"]||"nt:unstructured",title:s["jcr:content"]?.["jcr:title"]||s["jcr:title"]||o,lastModified:s["jcr:content"]?.["cq:lastModified"]||s["cq:lastModified"],resourceType:s["jcr:content"]?.["sling:resourceType"]||s["sling:resourceType"]})}}),r}catch(n){if(n.response?.status===404||n.response?.status===403)return((await this.fetch.get("/bin/querybuilder.json",{path:e,type:"cq:Page","p.nodedepth":"1","p.limit":"1000","p.hits":"full"})).hits||[]).map(o=>({name:o.name||o.path?.split("/").pop(),path:o.path,primaryType:o["jcr:primaryType"]||"cq:Page",title:o["jcr:content/jcr:title"]||o.title||o.name,lastModified:o["jcr:content/cq:lastModified"],resourceType:o["jcr:content/sling:resourceType"]}));throw n}},"listChildren")}async listPages(e,t=1,n=20){return C(async()=>{try{const r=await this.fetch.get(`${e}.${t}.json`),o=[],s=(a,i,l)=>{l>t||o.length>=n||Object.entries(a).forEach(([c,h])=>{if(!(o.length>=n)&&!(c.startsWith("jcr:")||c.startsWith("sling:")||c.startsWith("cq:")||c.startsWith("rep:")||c.startsWith("oak:"))&&h&&typeof h=="object"){const A=`${i}/${c}`;h["jcr:primaryType"]==="cq:Page"&&o.push({name:c,path:A,primaryType:"cq:Page",title:h["jcr:content"]?.["jcr:title"]||c,template:h["jcr:content"]?.["cq:template"],lastModified:h["jcr:content"]?.["cq:lastModified"],lastModifiedBy:h["jcr:content"]?.["cq:lastModifiedBy"],resourceType:h["jcr:content"]?.["sling:resourceType"],type:"page"}),l<t&&s(h,A,l+1)}})};return r&&typeof r=="object"&&s(r,e,0),y({siteRoot:e,pages:o,pageCount:o.length,depth:t,limit:n,totalChildrenScanned:o.length},"listPages")}catch(r){if(p.warn("JSON API failed, falling back to QueryBuilder:",r.message),r.response?.status===404||r.response?.status===403){const o=await this.fetch.get("/bin/querybuilder.json",{path:e,type:"cq:Page","p.nodedepth":t.toString(),"p.limit":n.toString(),"p.hits":"full"}),s=(o.hits||[]).map(a=>({name:a.name||a.path?.split("/").pop(),path:a.path,primaryType:"cq:Page",title:a["jcr:content/jcr:title"]||a.title||a.name,template:a["jcr:content/cq:template"],lastModified:a["jcr:content/cq:lastModified"],lastModifiedBy:a["jcr:content/cq:lastModifiedBy"],resourceType:a["jcr:content/sling:resourceType"],type:"page"}));return y({siteRoot:e,pages:s,pageCount:s.length,depth:t,limit:n,totalChildrenScanned:o.total||s.length,fallbackUsed:"QueryBuilder"},"listPages")}throw r}},"listPages")}async executeJCRQuery(e,t=20){return C(async()=>{if(!e||e.trim().length===0)throw new Error("Query is required and must be a non-empty string. Note: Only QueryBuilder fulltext is supported, not JCR SQL2.");const n=e.toLowerCase();if(/drop|delete|update|insert|exec|script|\.|<script/i.test(n)||e.length>1e3)throw new Error("Query contains potentially unsafe patterns or is too long");const r=await this.fetch.get("/bin/querybuilder.json",{path:"/content",type:"cq:Page",fulltext:e,"p.limit":t});return{query:e,results:r.hits||[],total:r.total||0,limit:t}},"executeJCRQuery")}async getPageProperties(e){return C(async()=>{const t=`${e}/jcr:content.json`,n=await this.fetch.get(t),r={title:n["jcr:title"],description:n["jcr:description"],template:n["cq:template"],lastModified:n["cq:lastModified"],lastModifiedBy:n["jcr:createdBy"],created:n["jcr:created"],createdBy:n["jcr:createdBy"],primaryType:n["jcr:primaryType"],resourceType:n["sling:resourceType"],tags:n["cq:tags"]||[],properties:n};return y({pagePath:e,properties:r},"getPageProperties")},"getPageProperties")}async searchContent(e){return C(async()=>{const t=await this.fetch.get(this.config.aem.endpoints.query,e);return y({params:e,results:t.hits||[],total:t.total||0,rawResponse:t},"searchContent")},"searchContent")}async getAssetMetadata(e){return C(async()=>{const t=`${e}.json`,n=await this.fetch.get(t),r=n["jcr:content"]?.metadata||{};return y({assetPath:e,metadata:r,fullData:n},"getAssetMetadata")},"getAssetMetadata")}async createPage(e){return this.createPageWithTemplate(e)}async deletePage(e){return C(async()=>{const{pagePath:t}=e;if(!v(t,this.aemConfig))throw $(P.INVALID_PARAMETERS,`Invalid page path: ${String(t)}`,{pagePath:t});let n=!1;try{await this.fetch.delete(t),n=!0}catch(r){if(r?.status===405||r?.response?.status===405)try{await this.fetch.post("/bin/wcmcommand",{cmd:"deletePage",path:t,force:e.force?"true":"false"}),n=!0}catch{try{await this.fetch.post(t,{":operation":"delete"}),n=!0}catch(s){throw s}}else throw p.error("DELETE failed:",r.response?.status,r.response?.data),r}return y({success:n,deletedPath:t,timestamp:new Date().toISOString()},"deletePage")},"deletePage")}async createComponent(e){return C(async()=>{const{pagePath:t,componentPath:n,componentType:r,resourceType:o,properties:s={},name:a}=e;if(!v(t,this.aemConfig))throw $(P.INVALID_PARAMETERS,`Invalid page path: ${String(t)}`,{pagePath:t});const i=a||`${r}_${Date.now()}`,l=n||`${t}/jcr:content/${i}`;return await this.fetch.post(l,{"jcr:primaryType":"nt:unstructured","sling:resourceType":o,...s,":operation":"import",":contentType":"json",":replace":"true"}),y({success:!0,componentPath:l,componentType:r,resourceType:o,properties:s,timestamp:new Date().toISOString()},"createComponent")},"createComponent")}async getComponentDefinition(e,t=new Set){const n=[],r={};let o="";if(t.has(e))return p.warn(`Circular reference detected for resourceType: ${e}`),{componentPath:o,requiredProperties:n,fieldDefinitions:r};t.add(e);const s=e.replace(/:/g,"/"),a=[`/apps/${s}`,`/libs/${s}`];let i=!1,l=null;for(const c of a)try{const h=await this.fetch.get(`${c}.json`,{":depth":"2"});if(h){o=c,h["sling:resourceSuperType"]&&(l=h["sling:resourceSuperType"],p.log(`Found sling:resourceSuperType: ${l} for component at ${c}`));try{const A=`${c}/_cq_dialog`,T=await this.fetch.get(`${A}.infinity.json`),m=(j,S="")=>{if(!j||typeof j!="object")return;const b=j["sling:resourceType"]||"",d=j.name||j.fieldName,g=d?d.replace(/^\.\//,""):null;if(g&&(b.includes("form/select")||b.includes("form/checkbox")||b.includes("form/textfield")||b.includes("form/textarea")||b.includes("form/numberfield")||b.includes("form/pathfield")||b.includes("form/datepicker")||b.includes("form/colorfield"))){const u=j.required===!0||j.required==="true",f={name:g,type:this.getFieldType(b),required:u,description:j.fieldDescription||j.fieldLabel||"",defaultValue:j.value!==void 0?j.value:j.checked!==void 0?j.checked:void 0};if(b.includes("form/select")&&j.items){const w=[],I=j.items;typeof I=="object"&&Object.values(I).forEach(E=>{E&&typeof E=="object"&&E.value!==void 0&&w.push(String(E.value))}),w.length>0&&(f.options=w)}b.includes("form/checkbox"),r[g]=f,p.log(`Found field definition: ${g} (type: ${f.type}, options: ${f.options?f.options.join(", "):"N/A"})`),u&&!n.includes(g)&&n.push(g)}j.items&&(Array.isArray(j.items)?j.items.forEach((u,f)=>{m(u,S?`${S}/items[${f}]`:`items[${f}]`)}):typeof j.items=="object"&&Object.entries(j.items).forEach(([u,f])=>{m(f,S?`${S}/items/${u}`:`items/${u}`)})),Object.entries(j).forEach(([u,f])=>{typeof f=="object"&&f!==null&&!u.startsWith("jcr:")&&!u.startsWith("sling:")&&u!=="items"&&m(f,S?`${S}/${u}`:u)})};m(T),i=!0,p.log(`Found component definition at ${o}, required properties: ${n.join(", ")}, field definitions: ${Object.keys(r).join(", ")}`);break}catch(A){p.warn(`Could not fetch dialog for ${c}: ${A.message}`)}}}catch{continue}if(!i&&l){p.log(`No dialog found for ${e}, checking super type: ${l}`);const c=await this.getComponentDefinition(l,t);c.requiredProperties.forEach(h=>{n.includes(h)||n.push(h)}),Object.entries(c.fieldDefinitions).forEach(([h,A])=>{r[h]||(r[h]=A)}),!o&&c.componentPath&&(o=c.componentPath)}return{componentPath:o,requiredProperties:n,fieldDefinitions:r}}getFieldType(e){return e.includes("form/select")?"select":e.includes("form/checkbox")?"checkbox":e.includes("form/textfield")?"textfield":e.includes("form/textarea")?"textarea":e.includes("form/numberfield")?"numberfield":e.includes("form/pathfield")?"pathfield":e.includes("form/datepicker")?"datepicker":e.includes("form/colorfield")?"colorfield":"unknown"}validateComponentProperties(e,t){const n=[],r=[],o={};for(const[s,a]of Object.entries(t)){const i=e[s];if(i!=null){if(a.type==="select"&&a.options){const l=String(i);if(!a.options.includes(l)){const c=`Invalid value '${i}' for field '${s}'. Allowed values: ${a.options.join(", ")}`;n.push(c),o[s]={provided:i,expected:a.options,message:c}}}if(a.type==="checkbox"&&!(typeof i=="boolean"||i==="true"||i==="false"||i===!0||i===!1)){const c=`Invalid value '${i}' for checkbox field '${s}'. Must be boolean or 'true'/'false'`;n.push(c),o[s]={provided:i,expected:'boolean or "true"/"false"',message:c}}if(a.type==="numberfield"&&isNaN(Number(i))){const l=`Invalid value '${i}' for number field '${s}'. Must be a number`;n.push(l),o[s]={provided:i,expected:"number",message:l}}}}return{valid:n.length===0,errors:n,warnings:r,invalidFields:o}}async getComponentTemplate(e){if(!e)return null;try{const t=`${e}/cq:template.infinity.json`;p.log(`Checking for cq:template at: ${t}`);const n=await this.fetch.get(t);if(n&&typeof n=="object"&&Object.keys(n).length>0){p.log(`\u2705 Found cq:template with ${Object.keys(n).length} top-level keys`);const r=Object.keys(n).filter(o=>!o.startsWith("jcr:")&&!o.startsWith("sling:"));return r.length>0&&p.log(`Template child nodes: ${r.join(", ")}`),n}return null}catch(t){const n=t.response?.status||t.statusCode||"unknown";return n===404?p.log(`No cq:template found at ${e}/cq:template`):p.warn(`Error checking for cq:template: ${t.message} (status: ${n})`),null}}async applyTemplateChildNodes(e,t){if(!(!t||Object.keys(t).length===0)){p.log(`Applying ${Object.keys(t).length} child nodes from cq:template to: ${e}`);for(const[n,r]of Object.entries(t))try{const o=`${e}/${n}`,s={...r};s["jcr:primaryType"]||(s["jcr:primaryType"]="nt:unstructured"),await this.fetch.post(o,{...s,":operation":"import",":contentType":"json",":replace":"true"}),p.log(`Created template child node: ${o}`)}catch(o){p.warn(`Failed to create template child node ${n}: ${o.message}`)}}}async addComponent(e){return C(async()=>{const{pagePath:t,resourceType:n,containerPath:r,name:o,properties:s={}}=e;if(!t||typeof t!="string")throw $(P.INVALID_PARAMETERS,"Page path is required and must be a string",{pagePath:t});if(!n||typeof n!="string")throw $(P.INVALID_PARAMETERS,"Resource type is required and must be a string",{resourceType:n});if(!v(t,this.aemConfig))throw $(P.INVALID_PATH,`Invalid page path: ${String(t)}`,{pagePath:t});p.log(`Fetching component definition for resourceType: ${n}`);const a=await this.getComponentDefinition(n);if(a.componentPath?p.log(`Component definition found at: ${a.componentPath}`):p.warn(`Component definition not found for ${n}, skipping required property validation`),a.requiredProperties.length>0){const d=[];if(a.requiredProperties.forEach(g=>{(!(g in s)||s[g]===null||s[g]===void 0||s[g]==="")&&d.push(g)}),d.length>0)throw $(P.INVALID_PARAMETERS,`Missing required properties for component ${n}: ${d.join(", ")}`,{resourceType:n,componentPath:a.componentPath,requiredProperties:a.requiredProperties,missingProperties:d,providedProperties:Object.keys(s)});p.log(`All required properties validated: ${a.requiredProperties.join(", ")}`)}if(p.log(`Checking field definitions: ${Object.keys(a.fieldDefinitions).length} fields found`),Object.keys(a.fieldDefinitions).length>0){p.log(`Field definitions: ${JSON.stringify(Object.keys(a.fieldDefinitions))}`),p.log(`Validating properties: ${JSON.stringify(Object.keys(s))}`);const d=this.validateComponentProperties(s,a.fieldDefinitions);if(p.log(`Validation result: valid=${d.valid}, errors=${d.errors.length}`),!d.valid){const g=d.errors.join("; ");throw p.error(`Property validation failed: ${g}`),$(P.INVALID_PARAMETERS,`Invalid property values for component ${n}: ${g}`,{resourceType:n,componentPath:a.componentPath,validationErrors:d.errors,invalidFields:d.invalidFields,providedProperties:s})}d.warnings.length>0&&p.warn(`Property validation warnings: ${d.warnings.join("; ")}`),p.log("Property values validated successfully against component dialog definitions")}else p.log(`No field definitions found for component ${n}, skipping property value validation`);try{const d=await this.fetch.get(`${t}.json`);if(!d||typeof d=="object"&&Object.keys(d).length===0)throw $(P.INVALID_PATH,`Page not found or invalid at: ${t}`,{pagePath:t});p.log(`Page verified at: ${t}`)}catch(d){throw d.message?.includes("404")||d.message?.includes("Page not found")||d.response?.status===404?$(P.INVALID_PATH,`Page not found at: ${t}`,{pagePath:t}):M(d,"addComponent")}let i;if(r)r.startsWith("/")?i=r:r.includes("jcr:content")?i=`${t}/${r}`:i=`${t}/jcr:content/${r}`;else{const d=`${t}/jcr:content`;let g=!1;try{const u=await this.fetch.get(`${d}.5.json`);u&&u.root?u.root.container?(i=`${d}/root/container`,g=!0,p.log(`\u2705 Found root/container at: ${i}`)):(i=`${d}/root/container`,g=!1,p.log(`Found root but no container, will create container at: ${i}`)):(i=`${d}/root/container`,g=!1,p.log(`No root found, will create root/container at: ${i}`))}catch{i=`${d}/root/container`,g=!1,p.warn(`Could not fetch jcr:content structure, will create root/container at: ${i}`)}}try{await this.fetch.get(`${i}.json`),p.log(`\u2705 Container exists at: ${i}`)}catch(d){if(d.message?.includes("404")||d.response?.status===404){p.warn(`Container not found at ${i}, attempting to create root/container structure`);const u=`${`${t}/jcr:content`}/root`,f=`${u}/container`;try{try{await this.fetch.get(`${u}.json`),p.log(`\u2705 Root already exists at: ${u}`)}catch{p.log(`Creating root at: ${u}`),await this.fetch.post(u,{"jcr:primaryType":"nt:unstructured","sling:resourceType":"aemmcp/base/components/aemmcp-container/v1/aemmcp-container"}),p.log(`\u2705 Created root at: ${u}`)}i===f&&(p.log(`Creating container at: ${f}`),await this.fetch.post(f,{"jcr:primaryType":"nt:unstructured","sling:resourceType":"aemmcp/base/components/aemmcp-container/v1/aemmcp-container"}),p.log(`\u2705 Created container at: ${f}`))}catch(w){throw $(P.INVALID_PATH,`Could not create root/container structure at: ${i}`,{targetContainerPath:i,error:w.message})}}else throw M(d,"addComponent")}const l=o||`component_${Date.now()}`,c=`${i}/${l}`;let h=null,A={},T={};if(a.componentPath&&(h=await this.getComponentTemplate(a.componentPath),h)){p.log("Component has cq:template, processing structure...");const d=["jcr:created","jcr:createdBy","jcr:lastModified","jcr:lastModifiedBy","jcr:mixinTypes"],g=["jcr:primaryType","sling:resourceType","layout","showSeparator","columns","separator"];for(const[u,f]of Object.entries(h))d.includes(u)||(g.includes(u)?A[u]=f:typeof f=="object"&&f!==null&&!Array.isArray(f)&&(f["jcr:primaryType"]!==void 0||f["sling:resourceType"]!==void 0)?T[u]=f:(typeof f!="object"||Array.isArray(f))&&(A[u]=f));p.log(`Template: ${Object.keys(A).length} properties, ${Object.keys(T).length} child nodes`)}const m=new URLSearchParams;if(Object.entries(A).forEach(([d,g])=>{g!=null&&(Array.isArray(g)?g.forEach(u=>m.append(d,u.toString())):typeof g=="object"?m.append(d,JSON.stringify(g)):m.append(d,g.toString()))}),Object.entries(s).forEach(([d,g])=>{g!=null&&(Array.isArray(g)?g.forEach(u=>m.append(d,u.toString())):typeof g=="object"?m.append(d,JSON.stringify(g)):m.append(d,g.toString()))}),m.has("jcr:primaryType")||m.append("jcr:primaryType","nt:unstructured"),m.has("sling:resourceType")||m.append("sling:resourceType",n),await this.fetch.post(c,m),Object.keys(T).length>0)try{await this.applyTemplateChildNodes(c,T),p.log(`\u2705 Applied ${Object.keys(T).length} child nodes from cq:template`)}catch(d){p.warn(`Failed to apply cq:template child nodes: ${d.message}`)}let j=await this.fetch.get(`${c}.json`),S=!1,b=null;try{if(a.componentPath){const d=await this.fetch.get(`${a.componentPath}.json`,{":depth":"2"});if(d&&(d["cq:isContainer"]===!0||d["cq:isContainer"]==="true")){S=!0,p.log(`Component ${n} is a container component (cq:isContainer=true)`),await new Promise(f=>setTimeout(f,1e3)),j=await this.fetch.get(`${c}.json`,{":depth":"5"}),Object.keys(j).some(f=>!f.startsWith("jcr:")&&!f.startsWith("sling:")&&!f.startsWith("cq:")&&f!=="layout"&&f!=="showSeparator"&&f!=="columns"&&f!=="separator")||(p.log("No default structure detected immediately, waiting longer..."),await new Promise(f=>setTimeout(f,1500)),j=await this.fetch.get(`${c}.json`,{":depth":"5"})),b={};const u=(f,w,I=0)=>{I>3||Object.keys(f).forEach(E=>{if(!E.startsWith("jcr:")&&!E.startsWith("sling:")&&!E.startsWith("cq:")&&E!=="layout"&&E!=="showSeparator"&&E!=="columns"&&E!=="separator"){const R=f[E];if(R&&typeof R=="object"&&!Array.isArray(R)){const W=w?`${w}/${E}`:E;if(R["sling:resourceType"]){const D=R["sling:resourceType"];D.includes("aemmcp-cc-container")||D.includes("columncontrol")||E.startsWith("col_")||E.match(/^col_\d+$/)?b[E]={path:`${c}/${W}`,resourceType:D,type:"column",isContainer:R["cq:isContainer"]===!0||R["cq:isContainer"]==="true"}:b[E]={path:`${c}/${W}`,resourceType:D,isContainer:R["cq:isContainer"]===!0||R["cq:isContainer"]==="true"},u(R,W,I+1)}else(E.startsWith("col_")||E.match(/^col_\d+$/)||E.startsWith("column")||E.match(/^column\d+$/)||E.includes("container")||E.includes("parsys")||E.includes("Column")||E.includes("Container"))&&(b[E]={path:`${c}/${W}`,type:"container",children:Object.keys(R).filter(D=>!D.startsWith("jcr:")&&!D.startsWith("sling:")&&!D.startsWith("cq:"))},u(R,W,I+1))}}})};u(j,""),Object.keys(b).length>0?p.log(`Container structure detected: ${Object.keys(b).join(", ")}`):p.log("Container component created but no default structure detected yet")}}}catch(d){p.warn(`Could not check container status: ${d.message}`)}return y({success:!0,pagePath:t,previewUrl:this.getPreviewUrl(t),componentPath:c,resourceType:n,isContainer:S,containerStructure:b||void 0,componentDefinition:{path:a.componentPath||"Not found",requiredProperties:a.requiredProperties,validationPassed:a.requiredProperties.length===0||a.requiredProperties.every(d=>d in s&&s[d]!==null&&s[d]!==void 0&&s[d]!==""),hasTemplate:h!==null,templatePath:h?`${a.componentPath}/cq:template`:void 0,templateChildNodesCount:Object.keys(T).length,fieldDefinitions:Object.keys(a.fieldDefinitions).length>0?a.fieldDefinitions:void 0},containerPath:i,componentName:l,properties:s,verification:j,timestamp:new Date().toISOString()},"addComponent")},"addComponent")}async deleteComponent(e){return C(async()=>{const{componentPath:t}=e;if(!v(t,this.aemConfig))throw $(P.INVALID_PARAMETERS,`Invalid component path: ${String(t)}`,{componentPath:t});let n=!1;try{const r=new URLSearchParams;r.append(":operation","delete"),await this.fetch.post(t,r),n=!0}catch(r){if(r?.status===405||r?.response?.status===405||r?.status===403||r?.response?.status===403)try{await this.fetch.delete(t),n=!0}catch(o){throw p.error("Both POST and DELETE failed:",r.response?.status,o.response?.status),r}else throw p.error("DELETE failed:",r.response?.status,r.response?.data),r}return y({success:n,deletedPath:t,timestamp:new Date().toISOString()},"deleteComponent")},"deleteComponent")}async unpublishContent(e){return C(async()=>{const{contentPaths:t,unpublishTree:n=!1}=e;if(!t||Array.isArray(t)&&t.length===0)throw $(P.INVALID_PARAMETERS,"Content paths array is required and cannot be empty",{contentPaths:t});const r=[];for(const o of Array.isArray(t)?t:[t])try{const s=new URLSearchParams;s.append("cmd","Deactivate"),s.append("path",o),s.append("ignoredeactivated","false"),s.append("onlymodified","false"),n&&s.append("deep","true");const a=await this.fetch.post("/bin/replicate.json",s);r.push({path:o,success:!0,response:a})}catch(s){r.push({path:o,success:!1,error:s.message})}return y({success:r.every(o=>o.success),results:r,unpublishedPaths:t,unpublishTree:n,timestamp:new Date().toISOString()},"unpublishContent")},"unpublishContent")}async activatePage(e){return C(async()=>{const{pagePath:t,activateTree:n=!1}=e;if(!v(t,this.aemConfig))throw $(P.INVALID_PARAMETERS,`Invalid page path: ${String(t)}`,{pagePath:t});try{const r=new URLSearchParams;r.append("cmd","Activate"),r.append("path",t);let o;return n?(r.append("ignoredeactivated","false"),r.append("onlymodified","false"),r.append("deep","true"),o=await this.fetch.post("/libs/replication/treeactivation.html",r)):o=await this.fetch.post("/bin/replicate.json",r),y({success:!0,activatedPath:t,activateTree:n,response:o,timestamp:new Date().toISOString()},"activatePage")}catch(r){try{const o=await this.fetch.post("/bin/wcmcommand",{cmd:"activate",path:t,ignoredeactivated:!1,onlymodified:!1});return y({success:!0,activatedPath:t,activateTree:n,response:o,fallbackUsed:"WCM Command",timestamp:new Date().toISOString()},"activatePage")}catch{throw M(r,"activatePage")}}},"activatePage")}async deactivatePage(e){return C(async()=>{const{pagePath:t,deactivateTree:n=!1}=e;if(!v(t,this.aemConfig))throw $(P.INVALID_PARAMETERS,`Invalid page path: ${String(t)}`,{pagePath:t});try{const r=new URLSearchParams;r.append("cmd","Deactivate"),r.append("path",t),r.append("ignoredeactivated","false"),r.append("onlymodified","false"),n&&r.append("deep","true");const o=await this.fetch.post("/bin/replicate.json",r);return y({success:!0,deactivatedPath:t,deactivateTree:n,response:o,timestamp:new Date().toISOString()},"deactivatePage")}catch(r){try{const o=await this.fetch.post("/bin/wcmcommand",{cmd:"deactivate",path:t,ignoredeactivated:!1,onlymodified:!1});return y({success:!0,deactivatedPath:t,deactivateTree:n,response:o,fallbackUsed:"WCM Command",timestamp:new Date().toISOString()},"deactivatePage")}catch{throw M(r,"deactivatePage")}}},"deactivatePage")}async updateAsset(e){return C(async()=>{const{assetPath:t,metadata:n,fileContent:r,mimeType:o}=e;if(!v(t,this.aemConfig))throw $(P.INVALID_PARAMETERS,`Invalid asset path: ${String(t)}`,{assetPath:t});const s=new URLSearchParams;r&&(s.append("file",r),o&&s.append("jcr:content/jcr:mimeType",o)),n&&typeof n=="object"&&Object.entries(n).forEach(([a,i])=>{s.append(`jcr:content/metadata/${a}`,String(i))});try{const a=await this.fetch.post(t,s),i=await this.fetch.get(`${t}.json`);return y({success:!0,assetPath:t,updatedMetadata:n,updateResponse:a,assetData:i,timestamp:new Date().toISOString()},"updateAsset")}catch(a){throw M(a,"updateAsset")}},"updateAsset")}async deleteAsset(e){return C(async()=>{const{assetPath:t,force:n=!1}=e;if(!v(t,this.aemConfig))throw $(P.INVALID_PARAMETERS,`Invalid asset path: ${String(t)}`,{assetPath:t});return await this.fetch.delete(t),y({success:!0,deletedPath:t,force:n,timestamp:new Date().toISOString()},"deleteAsset")},"deleteAsset")}getTemplatesPath(e){if(!e||e.trim().length===0)return"";let t=e.trim(),n="/conf",r="/settings/wcm/templates";return t=t.replace(/\/+$/,""),t.startsWith("/content/")&&(t=t.replace("/content","")),t.startsWith(n)||(t=`${n}/${t.replace(/^\//,"")}`),t.endsWith(r)||(t+=r),t}async getTemplates(e){return C(async()=>{if(e)try{const t=this.getTemplatesPath(e);if(!t)throw $(P.INVALID_PARAMETERS,`Cannot determine configuration path for site: ${e}`,{sitePath:e});p.log("Looking for site-specific templates at:",t);const n=await this.fetch.get(`${t}.2.json`),r=[];return n&&typeof n=="object"&&Object.entries(n).forEach(([o,s])=>{o.startsWith("jcr:")||o.startsWith("sling:")||s&&typeof s=="object"&&s["jcr:content"]&&r.push({name:o,path:`${t}/${o}`,title:s["jcr:content"]["jcr:title"]||o,description:s["jcr:content"]["jcr:description"],allowedPaths:s["jcr:content"].allowedPaths,ranking:s["jcr:content"].ranking||0})}),y({sitePath:e,templates:r,totalCount:r.length,source:"site-specific"},"getTemplates")}catch{}try{const t=["/apps/wcm/core/content/sites/templates","/libs/wcm/core/content/sites/templates"],n=[];for(const r of t)try{const o=await this.fetch.get(`${r}.json`,{":depth":"2"});o&&typeof o=="object"&&Object.entries(o).forEach(([s,a])=>{s.startsWith("jcr:")||s.startsWith("sling:")||a&&typeof a=="object"&&n.push({name:s,path:`${r}/${s}`,title:a["jcr:content"]?.["jcr:title"]||s,description:a["jcr:content"]?.["jcr:description"],allowedPaths:a["jcr:content"]?.allowedPaths,ranking:a["jcr:content"]?.ranking||0,source:r.includes("/apps/")?"apps":"libs"})})}catch{}return y({sitePath:e||"global",templates:n,totalCount:n.length,source:"global"},"getTemplates")}catch(t){throw M(t,"getTemplates")}},"getTemplates")}async getTemplateStructure(e){return C(async()=>{try{const t=await this.fetch.get(`${e}.infinity.json`),n={path:e,properties:t["jcr:content"]||{},policies:t["jcr:content"]?.policies||{},structure:t["jcr:content"]?.structure||{},initialContent:t["jcr:content"]?.initial||{},allowedComponents:[],allowedPaths:t["jcr:content"]?.allowedPaths||[]},r=(o,s="")=>{if(!(!o||typeof o!="object")){if(o.components){const a=Object.keys(o.components);n.allowedComponents.push(...a)}Object.entries(o).forEach(([a,i])=>{typeof i=="object"&&i!==null&&!a.startsWith("jcr:")&&r(i,s?`${s}/${a}`:a)})}};return r(n.policies),n.allowedComponents=[...new Set(n.allowedComponents)],y({templatePath:e,structure:n,fullData:t},"getTemplateStructure")}catch(t){throw M(t,"getTemplateStructure")}},"getTemplateStructure")}async bulkUpdateComponents(e){return C(async()=>{const{updates:t,validateFirst:n=!0,continueOnError:r=!1}=e;if(!Array.isArray(t)||t.length===0)throw $(P.INVALID_PARAMETERS,"Updates array is required and cannot be empty");const o=[];if(n)for(const a of t)try{await this.fetch.get(`${a.componentPath}.json`)}catch(i){if(i.response?.status===404&&(o.push({componentPath:a.componentPath,success:!1,error:`Component not found: ${a.componentPath}`,phase:"validation"}),!r))return y({success:!1,message:"Bulk update failed during validation phase",results:o,totalUpdates:t.length,successfulUpdates:0},"bulkUpdateComponents")}let s=0;for(const a of t)try{const i=await this.updateComponent({componentPath:a.componentPath,properties:a.properties});o.push({componentPath:a.componentPath,success:!0,result:i,phase:"update"}),s++}catch(i){if(o.push({componentPath:a.componentPath,success:!1,error:i.message,phase:"update"}),!r)break}return y({success:s===t.length,message:`Bulk update completed: ${s}/${t.length} successful`,results:o,totalUpdates:t.length,successfulUpdates:s,failedUpdates:t.length-s},"bulkUpdateComponents")},"bulkUpdateComponents")}async convertComponents(e){return C(async()=>{const{pagePath:t,sourceResourceType:n,targetResourceType:r,requiredProperties:o={},continueOnError:s=!0}=e;if(!t||typeof t!="string")throw $(P.INVALID_PARAMETERS,"Page path is required and must be a string");if(!n||typeof n!="string")throw $(P.INVALID_PARAMETERS,"Source resource type is required and must be a string");if(!r||typeof r!="string")throw $(P.INVALID_PARAMETERS,"Target resource type is required and must be a string");if(!v(t,this.aemConfig))throw $(P.INVALID_PATH,`Page path '${t}' is not within allowed content roots`);p.log(`Scanning page ${t} for components with resourceType: ${n}`);const i=await this.scanPageComponents(t),c=(i.data?.components||i.components||[]).filter(b=>b.resourceType===n);if(c.length===0)return y({message:`No components found with resourceType: ${n} on page ${t}`,pagePath:t,sourceResourceType:n,targetResourceType:r,componentsFound:0,componentsConverted:0},"convertComponents");p.log(`Found ${c.length} components with resourceType: ${n}`);const A=(await this.getComponentDefinition(r)).requiredProperties||[],T=[];if(A.length>0&&(A.forEach(b=>{(!(b in o)||o[b]===null||o[b]===void 0||o[b]==="")&&T.push(b)}),T.length>0))return y({message:"Target component requires properties that are not provided",pagePath:t,sourceResourceType:n,targetResourceType:r,componentsFound:c.length,componentsConverted:0,targetComponentRequiredProperties:A,missingRequiredProperties:T,providedProperties:Object.keys(o),note:"Please provide the missing required properties in the requiredProperties parameter and retry"},"convertComponents");const m=[];let j=0,S=0;for(const b of c)try{let d;b.path.startsWith("/")?d=b.path:b.path.startsWith("jcr:content")?d=`${t}/${b.path}`:d=`${t}/${b.path}`,p.log(`Processing component at: ${d}`),p.log(`Deleting source component at: ${d}`),await this.deleteComponent({componentPath:d}),p.log(`Creating target component at: ${d}`);const g=new URLSearchParams;g.append("jcr:primaryType","nt:unstructured"),g.append("sling:resourceType",r),Object.entries(o).forEach(([f,w])=>{if(w!=null){const I=f.startsWith("./")?f.substring(2):f;Array.isArray(w)?w.forEach(E=>{g.append(I,E.toString())}):typeof w=="object"?g.append(I,JSON.stringify(w)):g.append(I,w.toString())}}),await this.fetch.post(d,g,{headers:{Accept:"application/json"}});const u=await this.fetch.get(`${d}.json`);if(!u||!u["sling:resourceType"]||u["sling:resourceType"]!==r)throw new Error("Component creation verification failed: resourceType mismatch");m.push({sourceComponentPath:d,targetComponentPath:d,success:!0,message:`Successfully converted component from ${n} to ${r}`}),j++,p.log(`\u2705 Successfully converted component at: ${d}`)}catch(d){const g=d.message||String(d);if(p.error(`Failed to convert component at ${b.path}: ${g}`),m.push({sourceComponentPath:b.path,success:!1,error:g}),S++,!s)return y({message:"Conversion stopped due to error (continueOnError=false)",pagePath:t,sourceResourceType:n,targetResourceType:r,componentsFound:c.length,componentsConverted:j,componentsFailed:S,results:m},"convertComponents")}return y({message:`Converted ${j} of ${c.length} components from ${n} to ${r}`,pagePath:t,sourceResourceType:n,targetResourceType:r,componentsFound:c.length,componentsConverted:j,componentsFailed:S,results:m},"convertComponents")},"convertComponents")}async bulkConvertComponents(e){return C(async()=>{const{pagePaths:t,searchPath:n,depth:r=2,limit:o=50,sourceResourceType:s,targetResourceType:a,requiredProperties:i={},continueOnError:l=!0}=e;if(!s||typeof s!="string")throw $(P.INVALID_PARAMETERS,"Source resource type is required and must be a string");if(!a||typeof a!="string")throw $(P.INVALID_PARAMETERS,"Target resource type is required and must be a string");let c=[];if(t&&Array.isArray(t)&&t.length>0)c=t,p.log(`Processing ${c.length} specified pages`);else if(n){if(!v(n,this.aemConfig))throw $(P.INVALID_PATH,`Search path '${n}' is not within allowed content roots`);p.log(`Searching for pages under ${n} with depth ${r}`);const d=await this.listPages(n,r,o),g=d.data?.pages||d.pages||[];if(g.length===0)return y({message:`No pages found under ${n}`,searchPath:n,sourceResourceType:s,targetResourceType:a,pagesProcessed:0,pagesSucceeded:0,pagesFailed:0,totalComponentsConverted:0},"bulkConvertComponents");c=g.map(u=>u.path||u["@path"]).filter(u=>u),p.log(`Found ${c.length} pages to process`)}else throw $(P.INVALID_PARAMETERS,"Either pagePaths array or searchPath must be provided");if(c.length===0)return y({message:"No pages to process",sourceResourceType:s,targetResourceType:a,pagesProcessed:0,pagesSucceeded:0,pagesFailed:0,totalComponentsConverted:0},"bulkConvertComponents");const h=[];let A=0,T=0,m=0,j=0,S=0;for(const b of c)try{p.log(`Processing page: ${b}`);const g=await this.convertComponents({pagePath:b,sourceResourceType:s,targetResourceType:a,requiredProperties:i,continueOnError:l}),u=g.data||g;if(g.success!==!1){const f=u.componentsFound||0,w=u.componentsConverted||0,I=u.componentsFailed||0;m+=f,j+=w,S+=I,f>0&&A++,h.push({pagePath:b,success:!0,componentsFound:f,componentsConverted:w,componentsFailed:I,message:u.message||"Processed successfully"})}else T++,h.push({pagePath:b,success:!1,error:u.message||"Conversion failed",componentsFound:0,componentsConverted:0,componentsFailed:0})}catch(d){const g=d.message||String(d);if(p.error(`Failed to process page ${b}: ${g}`),T++,h.push({pagePath:b,success:!1,error:g,componentsFound:0,componentsConverted:0,componentsFailed:0}),!l)return y({message:"Bulk conversion stopped due to error (continueOnError=false)",sourceResourceType:s,targetResourceType:a,pagesProcessed:c.length,pagesSucceeded:A,pagesFailed:T,totalComponentsFound:m,totalComponentsConverted:j,totalComponentsFailed:S,pageResults:h},"bulkConvertComponents")}return y({message:`Bulk conversion completed: ${A} pages succeeded, ${T} pages failed. Total components converted: ${j}`,sourceResourceType:s,targetResourceType:a,pagesProcessed:c.length,pagesSucceeded:A,pagesFailed:T,totalComponentsFound:m,totalComponentsConverted:j,totalComponentsFailed:S,pageResults:h},"bulkConvertComponents")},"bulkConvertComponents")}async getNodeContent(e,t=1){return C(async()=>{const n=`${e}.json`,r=await this.fetch.get(n,{":depth":t.toString()});return{path:e,depth:t,content:r,timestamp:new Date().toISOString()}},"getNodeContent")}async getAvailableTemplates(e){return C(async()=>{console.log("getAvailableTemplates for parentPath:",e);let t="/conf";const n=e.split("/");n.length>=3&&n[1]==="content"&&(t=`/conf/${n[2]}`);const r=`${t}/settings/wcm/templates`;try{const o=await this.fetch.get(`${r}.3.json`),s=[];return o&&typeof o=="object"&&Object.entries(o).forEach(([a,i])=>{if(!(a.startsWith("jcr:")||a.startsWith("sling:"))&&i&&typeof i=="object"&&i["jcr:content"]){const l=`${r}/${a}`,c=i["jcr:content"],h=i?.structure?.["jcr:content"]||{};s.push({name:a,path:l,title:c["jcr:title"]||a,description:c["jcr:description"]||"",thumbnail:c.thumbnail||"",allowedPaths:c.allowedPaths||[],status:c.status||"enabled",ranking:c.ranking||0,templateType:c.templateType||"page",resourceType:h["sling:resourceType"]||"",lastModified:c["cq:lastModified"],createdBy:c["jcr:createdBy"]})}}),s.sort((a,i)=>a.ranking!==i.ranking?i.ranking-a.ranking:a.name.localeCompare(i.name)),y({parentPath:e,templatesPath:r,templates:s,totalCount:s.length,availableTemplates:s.filter(a=>a.status==="enabled")},"getAvailableTemplates")}catch(o){if(o.response?.status===404){const s="/libs/wcm/foundation/templates",a=await this.fetch.get(`${s}.json`,{":depth":"2"}),i=[];return a&&typeof a=="object"&&Object.entries(a).forEach(([l,c])=>{l.startsWith("jcr:")||l.startsWith("sling:")||c&&typeof c=="object"&&i.push({name:l,path:`${s}/${l}`,title:c["jcr:title"]||l,description:c["jcr:description"]||"Global template",status:"enabled",ranking:0,templateType:"page",isGlobal:!0})}),y({parentPath:e,templatesPath:s,templates:i,totalCount:i.length,availableTemplates:i,fallbackUsed:!0},"getAvailableTemplates")}throw o}},"getAvailableTemplates")}async createPageWithTemplate(e){return C(async()=>{const{parentPath:t,title:n,template:r,name:o,properties:s={},resourceType:a=""}=e;if(!v(t,this.aemConfig))throw $(P.INVALID_PARAMETERS,`Invalid parent path: ${String(t)}`,{parentPath:t});if(!o&&!n)throw $(P.INVALID_PARAMETERS,'Either "name" or "title" must be provided to create a page. Please provide at least one of these parameters.',{parentPath:t,providedParams:{name:o,title:n}});const i=n||(o?o.replace(/-/g," ").replace(/\b\w/g,f=>f.toUpperCase()):"");let l=r,c=a;if(!l){const w=(await this.getAvailableTemplates(t)).data.availableTemplates;if(w.length===0)throw $(P.INVALID_PARAMETERS,"No templates available for this path",{parentPath:t});const I=w[0];l=I.path,!c&&I.resourceType&&(c=I.resourceType),p.log(`\u{1F3AF} Auto-selected template: ${l} (${w[0].title})`,c)}let h=null;try{const f=await this.fetch.get(`${l}.json`);p.log(`\u2705 Template verified: ${l}`,f);try{const w=await this.fetch.get(`${l}.infinity.json`);if(!c&&w){const I=w.structure?.["jcr:content"];if(I&&I["sling:resourceType"])c=I["sling:resourceType"],p.log(`\u{1F4CB} Extracted resourceType from template structure: ${c}`);else{const E=w["jcr:content"];E&&E["sling:resourceType"]&&(c=E["sling:resourceType"],p.log(`\u{1F4CB} Extracted resourceType from template jcr:content: ${c}`))}}w&&w["jcr:content"]&&w.initial?(h=w.initial,p.log(`\u{1F4CB} Found initial content node at template/initial: ${l}`)):p.warn(`\u26A0\uFE0F No initial content found in template at /initial: ${l}`)}catch(w){p.warn(`Could not fetch template structure: ${w.message}`)}}catch(f){throw p.error("Template verification failed:",f.message,f),f?.response?.status===404?$(P.INVALID_PARAMETERS,`Template not found: ${l}`,{template:l}):M(f,"createPageWithTemplate")}try{await this.validateTemplate(l,t),p.log(`\u2705 Template validation passed for ${l} at ${t}`)}catch(f){throw f}const A=o||(i?i.replace(/\s+/g,"-").toLowerCase():""),T=`${t}/${A}`;try{const f=await this.fetch.get(`${T}.json`);if(f&&(f["jcr:primaryType"]==="cq:Page"||f["jcr:content"]))throw $(P.INVALID_PARAMETERS,`Page already exists at path: ${T}. Cannot overwrite existing page. Please use a different name or delete the existing page first.`,{pagePath:T,parentPath:t,pageName:A,existingPage:!0})}catch(f){if(f.code===P.INVALID_PARAMETERS&&f.message?.includes("already exists"))throw f;f.response?.status!==404&&!f.message?.includes("404")&&p.warn(`Could not check if page exists at ${T}: ${f.message}`)}const m={"jcr:primaryType":"cq:Page","jcr:content":{"jcr:primaryType":"cq:PageContent","jcr:title":i,"cq:template":l,"sling:resourceType":c||"foundation/components/page","cq:lastModified":new Date().toISOString(),"jcr:createdBy":"admin","jcr:created":new Date().toISOString(),"cq:lastModifiedBy":"admin",...s}};if(h&&h["jcr:content"]){const f=h["jcr:content"];Object.entries(f).forEach(([w,I])=>{w==="jcr:created"||w==="jcr:createdBy"||w==="jcr:title"||w==="cq:template"||w.startsWith("jcr:")&&w!=="jcr:primaryType"||typeof I=="object"&&!Array.isArray(I)&&!w.startsWith("jcr:")&&!w.startsWith("sling:")&&!w.startsWith("cq:")||m["jcr:content"][w]||(m["jcr:content"][w]=I)}),p.log("\u{1F4E6} Merged initial content properties from template")}const j=new URLSearchParams;j.append("jcr:primaryType","cq:Page"),await this.fetch.post(T,j);const S=new URLSearchParams;if(Object.entries(m["jcr:content"]).forEach(([f,w])=>{if(!(f==="jcr:created"||f==="jcr:createdBy"))if(typeof w=="object"&&w!==null&&!Array.isArray(w)){if(!f.startsWith("jcr:")&&!f.startsWith("sling:")&&!f.startsWith("cq:"))return;S.append(f,JSON.stringify(w))}else S.append(f,String(w))}),await this.fetch.post(`${T}/jcr:content`,S),h&&h["jcr:content"]){const f=`${T}/jcr:content`,I=h["jcr:content"],E=async(R,W,D=10)=>{if(!(D<=0)){for(const[x,L]of Object.entries(W))if(!(x.startsWith("jcr:")||x.startsWith("sling:")||x.startsWith("cq:")||x.startsWith("rep:")||x.startsWith("oak:"))&&L&&typeof L=="object"&&!Array.isArray(L)){const q=`${R}/${x}`,O=L;try{try{await this.fetch.get(`${q}.json`),O&&typeof O=="object"&&await E(q,O,D-1)}catch{const _=new URLSearchParams;O["jcr:primaryType"]?_.append("jcr:primaryType",O["jcr:primaryType"]):_.append("jcr:primaryType","nt:unstructured"),Object.entries(O).forEach(([F,N])=>{F!=="jcr:primaryType"&&(F.startsWith("jcr:")&&F!=="jcr:primaryType"||typeof N=="object"&&!Array.isArray(N)||N!=null&&(Array.isArray(N)?N.forEach(U=>{_.append(F,String(U))}):_.append(F,String(N))))}),await this.fetch.post(q,_),p.log(`\u2705 Created initial node: ${q}`),await E(q,O,D-1)}}catch(V){p.warn(`Could not create initial node ${q}: ${V.message}`)}}}};await E(f,I),p.log("\u2705 Created initial content structure from template under jcr:content")}const b=await this.fetch.get(`${T}.json`),d=b["jcr:content"]!==void 0;let g=!1;try{g=(await this.fetch.get(`${T}.html`)).status===200}catch{g=!1}const u={hasErrors:!1,errors:[]};return y({success:!0,pagePath:T,previewUrl:this.getPreviewUrl(T),title:n,templateUsed:l,jcrContentCreated:d,pageAccessible:g,errorLogCheck:u,creationDetails:{timestamp:new Date().toISOString(),steps:["Template validation completed","Template initial content fetched","Page node created","jcr:content node created",h?"Initial content structure created from template":"No initial content in template","Page structure verified","Accessibility check completed"],initialContentCreated:h!==null},pageStructure:b.data},"createPageWithTemplate")},"createPageWithTemplate")}async validateTemplate(e,t){return C(async()=>{try{let n;try{n=await this.fetch.get(`${e}.2.json`)}catch(m){try{n=await this.fetch.get(`${e}.infinity.json`)}catch(j){throw $(P.INVALID_PARAMETERS,`Template not found or inaccessible: ${e}`,{templatePath:e,fetchError:m.message,infinityError:j.message})}}if(!n||typeof n!="object")throw $(P.INVALID_PARAMETERS,"Invalid template structure: template data is not an object",{templatePath:e});const r=n["jcr:content"];if(!r||typeof r!="object")throw $(P.INVALID_PARAMETERS,"Invalid template structure: jcr:content not found",{templatePath:e,templateDataKeys:Object.keys(n)});const o=r.allowedPaths,s=Array.isArray(o)?o:o?[o]:[],a=r.allowedParents,i=Array.isArray(a)?a:a?[a]:[];let l=s.length===0;s.length>0&&(l=s.some(m=>{if(m.includes("(")||m.includes("*")||m.includes("?"))try{return new RegExp(m).test(t)}catch{return t.startsWith(m.replace(/[()*?]/g,""))}return t.startsWith(m)}));let c=!0,h=null,A=null;if(i.length>0)try{const m=await this.fetch.get(`${t}.2.json`);m&&m["jcr:content"]?(h=m["jcr:content"]["cq:template"]||null,h?(c=i.includes(h),c||(A=`Template '${e}' cannot be used under parent page with template '${h}'. Allowed parent templates: ${i.join(", ")}`)):(c=!1,A=`Template '${e}' requires a parent page with one of the allowed templates, but parent page at '${t}' has no template. Allowed parent templates: ${i.join(", ")}`)):(c=!1,A=`Template '${e}' requires a parent page with one of the allowed templates, but '${t}' is not a page. Allowed parent templates: ${i.join(", ")}`)}catch(m){m.response?.status===404?(c=!1,A=`Template '${e}' requires a parent page with one of the allowed templates, but parent path '${t}' does not exist. Allowed parent templates: ${i.join(", ")}`):(p.warn(`Could not validate parent path ${t}: ${m.message}`),c=!1,A=`Could not validate parent path '${t}': ${m.message}. Template '${e}' requires a parent page with one of the allowed templates: ${i.join(", ")}`)}if(!(l&&c)){const m=[];throw l||m.push(`Path '${t}' is not allowed. Allowed paths: ${s.join(", ")}`),!c&&A&&m.push(A),$(P.INVALID_PARAMETERS,`Template '${e}' cannot be used at '${t}'. ${m.join(" ")}`,{templatePath:e,targetPath:t,allowedPaths:s,allowedParents:i,parentTemplate:h,pathAllowed:l,parentAllowed:c})}return y({templatePath:e,targetPath:t,isValid:!0,templateTitle:r["jcr:title"]||"Untitled Template",templateDescription:r["jcr:description"]||"",allowedPaths:s,allowedParents:i,parentTemplate:h,restrictions:{hasPathRestrictions:s.length>0,hasParentRestrictions:i.length>0,allowedPaths:s,allowedParents:i,pathAllowed:l,parentAllowed:c}},"validateTemplate")}catch(n){throw n.code===P.INVALID_PARAMETERS?n:n.response?.status===404?$(P.INVALID_PARAMETERS,`Template not found: ${e}`,{templatePath:e}):M(n,"validateTemplate")}},"validateTemplate")}templateCache=new Map;templateCacheExpiry=new Map;TEMPLATE_CACHE_TTL=300*1e3;async getTemplateMetadata(e,t=!0){return C(async()=>{if(t&&this.templateCache.has(e)){const s=this.templateCacheExpiry.get(e)||0;if(Date.now()<s)return y({...this.templateCache.get(e),fromCache:!0},"getTemplateMetadata")}const n=await this.fetch.get(`${e}.json`);if(!n||!n["jcr:content"])throw $(P.INVALID_PARAMETERS,"Invalid template structure",{templatePath:e});const r=n["jcr:content"],o={templatePath:e,title:r["jcr:title"]||"Untitled Template",description:r["jcr:description"]||"",thumbnail:r.thumbnail||"",allowedPaths:r.allowedPaths||[],status:r.status||"enabled",ranking:r.ranking||0,templateType:r.templateType||"page",lastModified:r["cq:lastModified"],createdBy:r["jcr:createdBy"],policies:r.policies||{},structure:r.structure||{},initialContent:r.initial||{}};return t&&(this.templateCache.set(e,o),this.templateCacheExpiry.set(e,Date.now()+this.TEMPLATE_CACHE_TTL)),y(o,"getTemplateMetadata")},"getTemplateMetadata")}clearTemplateCache(){this.templateCache.clear(),this.templateCacheExpiry.clear(),console.log("\u{1F5D1}\uFE0F Template cache cleared")}async getComponents(e){return C(async()=>{const t=e||this.aemConfig.components.componentPaths?.projectRoot1||"/apps/aemmcp/base/components";p.log(`Fetching components from root path: ${t}`);const n=[],r=async(o,s=5)=>{if(!(s<=0))try{const a=await this.fetch.get(`${o}.${Math.min(s,3)}.json`);if(a&&typeof a=="object")for(const[i,l]of Object.entries(a)){const c=l;if(!(i.startsWith("jcr:")||i.startsWith("sling:")||i.startsWith("rep:")||i.startsWith("oak:"))&&c&&typeof c=="object"){const h=`${o}/${i}`,A=c["jcr:primaryType"],T=c._cq_dialog!==void 0,m=c[".content.xml"]!==void 0||c["component.html"]!==void 0||c["component.js"]!==void 0||c["component.css"]!==void 0;if(T||m||A==="cq:Component"){let j=i,S="",b="",d="";if(c["jcr:title"]?j=c["jcr:title"]:c["jcr:content"]?.["jcr:title"]&&(j=c["jcr:content"]["jcr:title"]),c["jcr:description"]?S=c["jcr:description"]:c["jcr:content"]?.["jcr:description"]&&(S=c["jcr:content"]["jcr:description"]),c.componentGroup&&(b=c.componentGroup),d=h.replace("/apps/","").replace("/libs/",""),c._cq_dialog)try{const g=await this.fetch.get(`${h}/_cq_dialog.json`,{":depth":"2"});g&&g["jcr:title"]&&(j=g["jcr:title"]||j),g&&g["jcr:description"]&&(S=g["jcr:description"]||S)}catch{}n.push({name:i,title:j,description:S,path:h,resourceType:d,componentGroup:b||"General",primaryType:A,hasDialog:T})}else await r(h,s-1)}}}catch(a){a.message?.includes("404")?p.warn(`Path not found: ${o}`):p.warn(`Error fetching components from ${o}: ${a.message}`)}};return await r(t,5),n.sort((o,s)=>o.name.localeCompare(s.name)),p.log(`Found ${n.length} components from ${t}`),y({rootPath:t,components:n,totalCount:n.length},"getComponents")},"getComponents")}async listWorkflowModels(){return C(async()=>{const e=await this.fetch.get("/etc/workflow/models.json"),t=Array.isArray(e)?e:[],n={request_for_activation:"Publish/activate pages",request_for_deactivation:"Unpublish/deactivate pages",request_for_deletion:"Delete pages",request_for_deletion_without_deactivation:"Delete pages without unpublishing first","dam/update_asset":"Update DAM assets","dam/dam-update-language-copy":"Update language copies of assets","dam/dam-create-language-copy":"Create language copies of assets","wcm-translation/translate-language-copy":"Translate language copies","wcm-translation/create_language_copy":"Create language copies","wcm-translation/prepare_translation_project":"Prepare translation project","wcm-translation/sync_translation_job":"Sync translation job","wcm-translation/update_language_copy":"Update language copy",activationmodel:"Activation workflow model",scheduled_activation:"Scheduled activation",scheduled_deactivation:"Scheduled deactivation"},r=t.map(o=>{const s=o.uri||"",a=s.replace("/var/workflow/models/",""),i=n[a]||"Custom workflow model";return{uri:s,modelId:a,description:i,...o}});return y({models:r,totalCount:r.length,commonWorkflows:Object.entries(n).map(([o,s])=>({modelId:o,uri:`/var/workflow/models/${o}`,description:s}))},"listWorkflowModels")},"listWorkflowModels")}async startWorkflow(e,t,n="JCR_PATH"){return C(async()=>{const r=e.startsWith("/var/workflow/models/")?e:`/var/workflow/models/${e}`,o=new URLSearchParams;o.append("model",r),o.append("payloadType",n),o.append("payload",t);const s=await this.fetch.postWithHeaders("/etc/workflow/instances",o,{headers:{Accept:"application/json"}});if(!s.ok&&s.status!==201){const c=await s.text();throw $(P.INVALID_PARAMETERS,`Failed to start workflow: ${s.status} - ${c}`)}let a=null,i=null;const l=s.headers.get("Location");if(l){a=l.startsWith("http")?new URL(l).pathname:l;const c=a.split("/").filter(h=>h);c.length>0&&(i=c[c.length-1])}else try{const c=await s.text();if(c){const h=JSON.parse(c);if(h&&h.uri){a=h.uri;const A=a.split("/").filter(T=>T);A.length>0&&(i=A[A.length-1])}}}catch{p.log("Workflow created (201), but response is not JSON. Location header:",l||"not found")}return y({modelId:e,modelUri:r,payload:t,payloadType:n,instanceId:i,instancePath:a,locationHeader:l,status:s.status,message:"Workflow instance started successfully"},"startWorkflow")},"startWorkflow")}async listWorkflowInstances(e){return C(async()=>{const t=e?`/etc/workflow/instances.${e}.json`:"/etc/workflow/instances.json",n=await this.fetch.get(t),r=Array.isArray(n)?n:n?.instances||[];return y({instances:r,totalCount:r.length,state:e||"all",timestamp:new Date().toISOString()},"listWorkflowInstances")},"listWorkflowInstances")}async getWorkflowInstance(e){return C(async()=>{const t=e.startsWith("/var/workflow/instances/")?e:`/var/workflow/instances/${e}`,n=await this.fetch.get(`${t}.json`);return y({instanceId:e,instancePath:t,instance:n,timestamp:new Date().toISOString()},"getWorkflowInstance")},"getWorkflowInstance")}async updateWorkflowInstanceState(e,t){return C(async()=>{if(!["RUNNING","SUSPENDED","ABORTED"].includes(t))throw $(P.INVALID_PARAMETERS,`Invalid state: ${t}. Must be RUNNING, SUSPENDED, or ABORTED`);const n=e.startsWith("/var/workflow/instances/")?e:`/var/workflow/instances/${e}`,r=new URLSearchParams;r.append("state",t);const o=await this.fetch.post(n,r,{headers:{Accept:"application/json"}});return y({instanceId:e,instancePath:n,newState:t,response:o,message:`Workflow instance state updated to ${t}`},"updateWorkflowInstanceState")},"updateWorkflowInstanceState")}async getInboxItems(){return C(async()=>{const e=await this.fetch.get("/bin/workflow/inbox.json"),t=e?.items||e||[];return y({items:t,totalCount:Array.isArray(t)?t.length:0,timestamp:new Date().toISOString()},"getInboxItems")},"getInboxItems")}async completeWorkItem(e,t,n){return C(async()=>{let r=t||"";if(!r){const i=(await this.fetch.get(`${e}.routes.json`))?.routes||[];if(i.length===0)throw $(P.INVALID_PARAMETERS,"No routes available for this work item");const l=i[0];if(!l?.rid)throw $(P.INVALID_PARAMETERS,"No valid route ID found in available routes");r=l.rid,p.log(`No route specified, using first available route: ${l.label||"Unknown"} (${r})`)}const o=new URLSearchParams;o.append("item",e),o.append("route",r),n&&o.append("comment",n);const s=await this.fetch.post("/bin/workflow/inbox",o,{headers:{Accept:"application/json"}});return y({workItemPath:e,routeId:r,comment:n,response:s,message:"Work item completed successfully"},"completeWorkItem")},"completeWorkItem")}async delegateWorkItem(e,t){return C(async()=>{const n=new URLSearchParams;n.append("item",e),n.append("delegatee",t);const r=await this.fetch.post("/bin/workflow/inbox",n,{headers:{Accept:"application/json"}});return y({workItemPath:e,delegatee:t,response:r,message:`Work item delegated to ${t}`},"delegateWorkItem")},"delegateWorkItem")}async getWorkItemRoutes(e){return C(async()=>{const n=(await this.fetch.get(`${e}.routes.json`))?.routes||[];return y({workItemPath:e,routes:n,totalCount:n.length,timestamp:new Date().toISOString()},"getWorkItemRoutes")},"getWorkItemRoutes")}async getContentFragment(e){return this.contentFragments.getContentFragment(e)}async listContentFragments(e){return this.contentFragments.listContentFragments(e)}async manageContentFragment(e){return this.contentFragments.manageContentFragment(e)}async manageContentFragmentVariation(e){return this.contentFragments.manageContentFragmentVariation(e)}async getExperienceFragment(e){return this.experienceFragments.getExperienceFragment(e)}async listExperienceFragments(e){return this.experienceFragments.listExperienceFragments(e)}async manageExperienceFragment(e){return this.experienceFragments.manageExperienceFragment(e)}async manageExperienceFragmentVariation(e){return this.experienceFragments.manageExperienceFragmentVariation(e)}}
1
+ "use strict";import{getAEMConfig as k,isValidContentPath as v}from"./aem.config.js";import{AEM_ERROR_CODES as P,createAEMError as $,createSuccessResponse as y,handleAEMHttpError as M,safeExecute as C}from"./aem.errors.js";import{AEMFetch as J}from"./aem.fetch.js";import{filterNodeTree as U,filterProperties as H}from"./aem.filter.js";import{ContentFragmentManager as G}from"./aem.content-fragments.js";import{ExperienceFragmentManager as Q}from"./aem.experience-fragments.js";import{LOGGER as p}from"../utils/logger.js";export class AEMConnector{isInitialized;isAEMaaCS;config;aemConfig;fetch;contentFragments;experienceFragments;constructor(e){this.isInitialized=!1,this.config=this.loadConfig(e),this.aemConfig=k({}),this.isAEMaaCS=this.isConfigAEMaaCS(),this.fetch=new J({host:this.config.aem.host,auth:this.config.aem.auth,timeout:this.aemConfig.queries.timeoutMs}),this.contentFragments=new G(this.fetch,this.isAEMaaCS),this.experienceFragments=new Q(this.fetch,this.config.aem.host)}async init(){await this.fetch.init(),this.isInitialized=!0}isConfigAEMaaCS(){return!!(this.config.aem.auth.clientId&&this.config.aem.auth.clientSecret)}loadConfig(e={}){let t;return e.id&&e.secret?t={clientId:e.id,clientSecret:e.secret}:t={username:e.user||"admin",password:e.pass||"admin"},{aem:{host:e.host||"http://localhost:4502",author:e.host||"http://localhost:4502",publish:"http://localhost:4503",auth:t,endpoints:{content:"/content",dam:"/content/dam",query:"/bin/querybuilder.json",crxde:"/crx/de",jcr:""}},mcp:{name:"NAEM MCP Server",version:"1.0.0"}}}getPreviewUrl(e){return`${this.config.aem.host}${e}.html?wcmmode=disabled`}async testConnection(){const e=await this.testAEMConnection(),t=e?await this.testAuthConnection():!1;return{aem:e,auth:t}}async testAEMConnection(){try{this.isInitialized||await this.init();const e="/libs/granite/core/content/login.html";p.log("Testing AEM connection to:",e);const t=await this.fetch.get(e,void 0,void 0,5e3,!0);return p.log("\u2705 AEM connection successful!"),!0}catch(e){return p.error("\u274C AEM connection failed:",e.message),!1}}async testAuthConnection(){try{this.isInitialized||await this.init();const e="/libs/granite/security/currentuser.json";p.log("Testing AEM authentication connection to:",e);const t=await this.fetch.get(e,void 0,void 0,5e3);return p.log("\u2705 AEM authentication connection successful!"),!0}catch(e){return p.error("\u274C AEM authentication connection failed:",e.message),!1}}validateComponentProps(e,t,n){const o=[],r=[];return t==="text"&&!n.text&&!n.richText&&o.push("Text component should have text or richText property"),t==="image"&&!n.fileReference&&!n.src&&r.push("Image component requires fileReference or src property"),{valid:r.length===0,errors:r,warnings:o,componentType:t,propsValidated:Object.keys(n).length}}async updateComponent(e){return C(async()=>{if(!e.componentPath||typeof e.componentPath!="string")throw $(P.INVALID_PARAMETERS,"Component path is required and must be a string");if(!e.properties||typeof e.properties!="object")throw $(P.INVALID_PARAMETERS,"Properties are required and must be an object");if(!v(e.componentPath,this.aemConfig))throw $(P.INVALID_PATH,`Component path '${e.componentPath}' is not within allowed content roots`,{path:e.componentPath,allowedRoots:Object.values(this.aemConfig.contentPaths)});const t=`${e.componentPath}.json`;let n;try{if(n=await this.fetch.get(t),!n||typeof n=="object"&&Object.keys(n).length===0)throw $(P.COMPONENT_NOT_FOUND,`Component not found at path: ${e.componentPath}`,{componentPath:e.componentPath})}catch(i){throw i.code===P.COMPONENT_NOT_FOUND?i:i.message?.includes("404")||i.response?.status===404?$(P.COMPONENT_NOT_FOUND,`Component not found at path: ${e.componentPath}`,{componentPath:e.componentPath}):M(i,"updateComponent")}const o=n["sling:resourceType"];if(o){p.log(`Validating properties against component definition for: ${o}`);const i=await this.getComponentDefinition(o);if(Object.keys(i.fieldDefinitions).length>0){const l=this.validateComponentProperties(e.properties,i.fieldDefinitions);if(!l.valid){const c=l.errors.join("; ");throw $(P.INVALID_PARAMETERS,`Invalid property values for component ${o}: ${c}`,{resourceType:o,componentPath:i.componentPath,validationErrors:l.errors,invalidFields:l.invalidFields,providedProperties:e.properties})}l.warnings.length>0&&p.warn(`Property validation warnings: ${l.warnings.join("; ")}`),p.log("Property values validated successfully against component dialog definitions")}}const r=new URLSearchParams;o&&r.append("sling:resourceType",o),Object.entries(e.properties).forEach(([i,l])=>{l==null?r.append(`${i}@Delete`,""):Array.isArray(l)?l.forEach(c=>{r.append(`${i}`,c.toString())}):typeof l=="object"?r.append(i,JSON.stringify(l)):r.append(i,l.toString())});const s=await this.fetch.post(e.componentPath,r,{headers:{Accept:"application/json"}}),a=await this.fetch.get(`${e.componentPath}.json`);return y({message:"Component updated successfully",path:e.componentPath,properties:e.properties,updatedProperties:a,response:s,verification:{success:!0,propertiesChanged:Object.keys(e.properties).length,timestamp:new Date().toISOString()}},"updateComponent")},"updateComponent")}async scanPageComponents(e,t="standard"){return C(async()=>{const n=`${e}.infinity.json`,o=await this.fetch.get(n),r=[],s=(a,i)=>{!a||typeof a!="object"||(a["sling:resourceType"]&&r.push({path:i,resourceType:a["sling:resourceType"],properties:H({...a},t)}),Object.entries(a).forEach(([l,c])=>{if(typeof c=="object"&&c!==null&&!l.startsWith("rep:")&&!l.startsWith("oak:")){const h=i?`${i}/${l}`:l;s(c,h)}}))};return o["jcr:content"]?s(o["jcr:content"],"jcr:content"):s(o,e),y({pagePath:e,verbosity:t,components:r,totalComponents:r.length},"scanPageComponents")},"scanPageComponents")}async fetchSites(){return C(async()=>{const t=await this.fetch.get("/content.2.json",{":depth":"2"}),n=[];return Object.entries(t).forEach(([o,r])=>{o.startsWith("jcr:")||o.startsWith("sling:")||r&&typeof r=="object"&&r["jcr:content"]&&n.push({name:o,path:`/content/${o}`,title:r["jcr:content"]["jcr:title"]||o,template:r["jcr:content"]["cq:template"],lastModified:r["jcr:content"]["cq:lastModified"]})}),y({sites:n,totalCount:n.length},"fetchSites")},"fetchSites")}async fetchLanguageMasters(e){return C(async()=>{const t=`/content/${e}.2.json`,n=await this.fetch.get(t),o=[];let r=null,s="";return Object.entries(n).forEach(([a,i])=>{(a==="master"||a==="language-masters")&&i&&typeof i=="object"&&(r=i,s=`/content/${e}/${a}`)}),r?(Object.entries(r).forEach(([a,i])=>{a.startsWith("jcr:")||a.startsWith("sling:")||i&&typeof i=="object"&&o.push({name:a,path:`${s}/${a}`,title:i["jcr:content"]?.["jcr:title"]||i["jcr:title"]||a,language:i["jcr:content"]?.["jcr:language"]||i["jcr:language"]||a})}),y({site:e,languageMasters:o},"fetchLanguageMasters")):y({site:e,languageMasters:[],message:"No master or language-masters node found"},"fetchLanguageMasters")},"fetchLanguageMasters")}async fetchAvailableLocales(e){return C(async()=>{const t=`/content/${e}.4.json`,n=await this.fetch.get(t),o={},r=(s,a,i=[])=>{!s||typeof s!="object"||Object.entries(s).forEach(([l,c])=>{if(!(l.startsWith("jcr:")||l.startsWith("sling:")||l.startsWith("cq:")||l.startsWith("rep:")||l.startsWith("oak:")||l==="jcr:content")&&c&&typeof c=="object"){const h=`${a}/${l}`,A=[...i,l],T=c["jcr:content"],m=T&&typeof T=="object",j=T?.["jcr:language"]||null,S=l.length===2||l.length===3,b=i.length>0&&(i[i.length-1].length===2||i[i.length-1].length===3);if(m&&S&&b){const d=i[i.length-1].toUpperCase(),g=l.toLowerCase(),u=`${g}_${d}`;o[u]={path:h,title:T?.["jcr:title"]||l,language:j||`${g}_${d}`,country:d}}r(c,h,A)}})};return r(n,`/content/${e}`,[]),y({site:e,locales:o,totalCount:Object.keys(o).length},"fetchAvailableLocales")},"fetchAvailableLocales")}async getAllTextContent(e){return C(async()=>{let t;try{const r=`${e}.infinity.json`;t=await this.fetch.get(r)}catch(r){if(r.message?.includes("300")||r.message?.includes("redirect")){p.warn(`infinity.json failed for ${e}, trying depth-based approach`);try{t=await this.fetch.get(`${e}.5.json`)}catch{try{t=await this.fetch.get(`${e}/jcr:content.5.json`)}catch{throw M(r,"getAllTextContent")}}}else throw M(r,"getAllTextContent")}const n=[],o=(r,s)=>{!r||typeof r!="object"||((r.text||r["jcr:title"]||r["jcr:description"])&&n.push({path:s,title:r["jcr:title"],text:r.text,description:r["jcr:description"]}),Object.entries(r).forEach(([a,i])=>{if(typeof i=="object"&&i!==null&&!a.startsWith("rep:")&&!a.startsWith("oak:")){const l=s?`${s}/${a}`:a;o(i,l)}}))};return t["jcr:content"]?o(t["jcr:content"],"jcr:content"):o(t,e),y({pagePath:e,textContent:n},"getAllTextContent")},"getAllTextContent")}async getPageTextContent(e){return C(async()=>this.getAllTextContent(e),"getPageTextContent")}async getPageImages(e){return C(async()=>{const t=`${e}.infinity.json`,n=await this.fetch.get(t),o=[],r=(s,a)=>{!s||typeof s!="object"||((s.fileReference||s.src)&&o.push({path:a,fileReference:s.fileReference,src:s.src,alt:s.alt||s.altText,title:s["jcr:title"]||s.title}),Object.entries(s).forEach(([i,l])=>{if(typeof l=="object"&&l!==null&&!i.startsWith("rep:")&&!i.startsWith("oak:")){const c=a?`${a}/${i}`:i;r(l,c)}}))};return n["jcr:content"]?r(n["jcr:content"],"jcr:content"):r(n,e),y({pagePath:e,images:o},"getPageImages")},"getPageImages")}async updateImagePath(e,t){return C(async()=>this.updateComponent({componentPath:e,properties:{fileReference:t}}),"updateImagePath")}async getPageContent(e,t="standard"){return C(async()=>{const n=`${e}.infinity.json`,o=await this.fetch.get(n),r=U(o,t);return y({pagePath:e,verbosity:t,content:r},"getPageContent")},"getPageContent")}async listChildren(e,t=1){return C(async()=>{try{const n=await this.fetch.get(`${e}.${t}.json`),o=[];return n&&typeof n=="object"&&Object.entries(n).forEach(([r,s])=>{if(!(r.startsWith("jcr:")||r.startsWith("sling:")||r.startsWith("cq:")||r.startsWith("rep:")||r.startsWith("oak:")||r==="jcr:content")&&s&&typeof s=="object"){const a=`${e}/${r}`;o.push({name:r,path:a,primaryType:s["jcr:primaryType"]||"nt:unstructured",title:s["jcr:content"]?.["jcr:title"]||s["jcr:title"]||r,lastModified:s["jcr:content"]?.["cq:lastModified"]||s["cq:lastModified"],resourceType:s["jcr:content"]?.["sling:resourceType"]||s["sling:resourceType"]})}}),o}catch(n){if(n.response?.status===404||n.response?.status===403)return((await this.fetch.get("/bin/querybuilder.json",{path:e,type:"cq:Page","p.nodedepth":"1","p.limit":"1000","p.hits":"full"})).hits||[]).map(r=>({name:r.name||r.path?.split("/").pop(),path:r.path,primaryType:r["jcr:primaryType"]||"cq:Page",title:r["jcr:content/jcr:title"]||r.title||r.name,lastModified:r["jcr:content/cq:lastModified"],resourceType:r["jcr:content/sling:resourceType"]}));throw n}},"listChildren")}async listPages(e,t=1,n=20){return C(async()=>{try{const o=await this.fetch.get(`${e}.${t}.json`),r=[],s=(a,i,l)=>{l>t||r.length>=n||Object.entries(a).forEach(([c,h])=>{if(!(r.length>=n)&&!(c.startsWith("jcr:")||c.startsWith("sling:")||c.startsWith("cq:")||c.startsWith("rep:")||c.startsWith("oak:"))&&h&&typeof h=="object"){const A=`${i}/${c}`;h["jcr:primaryType"]==="cq:Page"&&r.push({name:c,path:A,primaryType:"cq:Page",title:h["jcr:content"]?.["jcr:title"]||c,template:h["jcr:content"]?.["cq:template"],lastModified:h["jcr:content"]?.["cq:lastModified"],lastModifiedBy:h["jcr:content"]?.["cq:lastModifiedBy"],resourceType:h["jcr:content"]?.["sling:resourceType"],type:"page"}),l<t&&s(h,A,l+1)}})};return o&&typeof o=="object"&&s(o,e,0),y({siteRoot:e,pages:r,pageCount:r.length,depth:t,limit:n,totalChildrenScanned:r.length},"listPages")}catch(o){if(p.warn("JSON API failed, falling back to QueryBuilder:",o.message),o.response?.status===404||o.response?.status===403){const r=await this.fetch.get("/bin/querybuilder.json",{path:e,type:"cq:Page","p.nodedepth":t.toString(),"p.limit":n.toString(),"p.hits":"full"}),s=(r.hits||[]).map(a=>({name:a.name||a.path?.split("/").pop(),path:a.path,primaryType:"cq:Page",title:a["jcr:content/jcr:title"]||a.title||a.name,template:a["jcr:content/cq:template"],lastModified:a["jcr:content/cq:lastModified"],lastModifiedBy:a["jcr:content/cq:lastModifiedBy"],resourceType:a["jcr:content/sling:resourceType"],type:"page"}));return y({siteRoot:e,pages:s,pageCount:s.length,depth:t,limit:n,totalChildrenScanned:r.total||s.length,fallbackUsed:"QueryBuilder"},"listPages")}throw o}},"listPages")}async executeJCRQuery(e,t=20){return C(async()=>{if(!e||e.trim().length===0)throw new Error("Query is required and must be a non-empty string. Note: Only QueryBuilder fulltext is supported, not JCR SQL2.");const n=e.toLowerCase();if(/drop|delete|update|insert|exec|script|\.|<script/i.test(n)||e.length>1e3)throw new Error("Query contains potentially unsafe patterns or is too long");const o=await this.fetch.get("/bin/querybuilder.json",{path:"/content",type:"cq:Page",fulltext:e,"p.limit":t});return{query:e,results:o.hits||[],total:o.total||0,limit:t}},"executeJCRQuery")}async getPageProperties(e){return C(async()=>{const t=`${e}/jcr:content.json`,n=await this.fetch.get(t),o={title:n["jcr:title"],description:n["jcr:description"],template:n["cq:template"],lastModified:n["cq:lastModified"],lastModifiedBy:n["jcr:createdBy"],created:n["jcr:created"],createdBy:n["jcr:createdBy"],primaryType:n["jcr:primaryType"],resourceType:n["sling:resourceType"],tags:n["cq:tags"]||[],properties:n};return y({pagePath:e,properties:o},"getPageProperties")},"getPageProperties")}async searchContent(e){return C(async()=>{const t=await this.fetch.get(this.config.aem.endpoints.query,e);return y({params:e,results:t.hits||[],total:t.total||0,rawResponse:t},"searchContent")},"searchContent")}async getAssetMetadata(e){return C(async()=>{const t=`${e}.json`,n=await this.fetch.get(t),o=n["jcr:content"]?.metadata||{};return y({assetPath:e,metadata:o,fullData:n},"getAssetMetadata")},"getAssetMetadata")}async createPage(e){return this.createPageWithTemplate(e)}async deletePage(e){return C(async()=>{const{pagePath:t}=e;if(!v(t,this.aemConfig))throw $(P.INVALID_PARAMETERS,`Invalid page path: ${String(t)}`,{pagePath:t});let n=!1;try{await this.fetch.delete(t),n=!0}catch(o){if(o?.status===405||o?.response?.status===405)try{await this.fetch.post("/bin/wcmcommand",{cmd:"deletePage",path:t,force:e.force?"true":"false"}),n=!0}catch{try{await this.fetch.post(t,{":operation":"delete"}),n=!0}catch(s){throw s}}else throw p.error("DELETE failed:",o.response?.status,o.response?.data),o}return y({success:n,deletedPath:t,timestamp:new Date().toISOString()},"deletePage")},"deletePage")}async createComponent(e){return C(async()=>{const{pagePath:t,componentPath:n,componentType:o,resourceType:r,properties:s={},name:a}=e;if(!v(t,this.aemConfig))throw $(P.INVALID_PARAMETERS,`Invalid page path: ${String(t)}`,{pagePath:t});const i=a||`${o}_${Date.now()}`,l=n||`${t}/jcr:content/${i}`;return await this.fetch.post(l,{"jcr:primaryType":"nt:unstructured","sling:resourceType":r,...s,":operation":"import",":contentType":"json",":replace":"true"}),y({success:!0,componentPath:l,componentType:o,resourceType:r,properties:s,timestamp:new Date().toISOString()},"createComponent")},"createComponent")}async getComponentDefinition(e,t=new Set){const n=[],o={};let r="";if(t.has(e))return p.warn(`Circular reference detected for resourceType: ${e}`),{componentPath:r,requiredProperties:n,fieldDefinitions:o};t.add(e);const s=e.replace(/:/g,"/"),a=[`/apps/${s}`,`/libs/${s}`];let i=!1,l=null;for(const c of a)try{const h=await this.fetch.get(`${c}.json`,{":depth":"2"});if(h){r=c,h["sling:resourceSuperType"]&&(l=h["sling:resourceSuperType"],p.log(`Found sling:resourceSuperType: ${l} for component at ${c}`));try{const A=`${c}/_cq_dialog`,T=await this.fetch.get(`${A}.infinity.json`),m=(j,S="")=>{if(!j||typeof j!="object")return;const b=j["sling:resourceType"]||"",d=j.name||j.fieldName,g=d?d.replace(/^\.\//,""):null;if(g&&(b.includes("form/select")||b.includes("form/checkbox")||b.includes("form/textfield")||b.includes("form/textarea")||b.includes("form/numberfield")||b.includes("form/pathfield")||b.includes("form/datepicker")||b.includes("form/colorfield"))){const u=j.required===!0||j.required==="true",f={name:g,type:this.getFieldType(b),required:u,description:j.fieldDescription||j.fieldLabel||"",defaultValue:j.value!==void 0?j.value:j.checked!==void 0?j.checked:void 0};if(b.includes("form/select")&&j.items){const w=[],I=j.items;typeof I=="object"&&Object.values(I).forEach(E=>{E&&typeof E=="object"&&E.value!==void 0&&w.push(String(E.value))}),w.length>0&&(f.options=w)}b.includes("form/checkbox"),o[g]=f,p.log(`Found field definition: ${g} (type: ${f.type}, options: ${f.options?f.options.join(", "):"N/A"})`),u&&!n.includes(g)&&n.push(g)}j.items&&(Array.isArray(j.items)?j.items.forEach((u,f)=>{m(u,S?`${S}/items[${f}]`:`items[${f}]`)}):typeof j.items=="object"&&Object.entries(j.items).forEach(([u,f])=>{m(f,S?`${S}/items/${u}`:`items/${u}`)})),Object.entries(j).forEach(([u,f])=>{typeof f=="object"&&f!==null&&!u.startsWith("jcr:")&&!u.startsWith("sling:")&&u!=="items"&&m(f,S?`${S}/${u}`:u)})};m(T),i=!0,p.log(`Found component definition at ${r}, required properties: ${n.join(", ")}, field definitions: ${Object.keys(o).join(", ")}`);break}catch(A){p.warn(`Could not fetch dialog for ${c}: ${A.message}`)}}}catch{continue}if(!i&&l){p.log(`No dialog found for ${e}, checking super type: ${l}`);const c=await this.getComponentDefinition(l,t);c.requiredProperties.forEach(h=>{n.includes(h)||n.push(h)}),Object.entries(c.fieldDefinitions).forEach(([h,A])=>{o[h]||(o[h]=A)}),!r&&c.componentPath&&(r=c.componentPath)}return{componentPath:r,requiredProperties:n,fieldDefinitions:o}}getFieldType(e){return e.includes("form/select")?"select":e.includes("form/checkbox")?"checkbox":e.includes("form/textfield")?"textfield":e.includes("form/textarea")?"textarea":e.includes("form/numberfield")?"numberfield":e.includes("form/pathfield")?"pathfield":e.includes("form/datepicker")?"datepicker":e.includes("form/colorfield")?"colorfield":"unknown"}validateComponentProperties(e,t){const n=[],o=[],r={};for(const[s,a]of Object.entries(t)){const i=e[s];if(i!=null){if(a.type==="select"&&a.options){const l=String(i);if(!a.options.includes(l)){const c=`Invalid value '${i}' for field '${s}'. Allowed values: ${a.options.join(", ")}`;n.push(c),r[s]={provided:i,expected:a.options,message:c}}}if(a.type==="checkbox"&&!(typeof i=="boolean"||i==="true"||i==="false"||i===!0||i===!1)){const c=`Invalid value '${i}' for checkbox field '${s}'. Must be boolean or 'true'/'false'`;n.push(c),r[s]={provided:i,expected:'boolean or "true"/"false"',message:c}}if(a.type==="numberfield"&&isNaN(Number(i))){const l=`Invalid value '${i}' for number field '${s}'. Must be a number`;n.push(l),r[s]={provided:i,expected:"number",message:l}}}}return{valid:n.length===0,errors:n,warnings:o,invalidFields:r}}async getComponentTemplate(e){if(!e)return null;try{const t=`${e}/cq:template.infinity.json`;p.log(`Checking for cq:template at: ${t}`);const n=await this.fetch.get(t);if(n&&typeof n=="object"&&Object.keys(n).length>0){p.log(`\u2705 Found cq:template with ${Object.keys(n).length} top-level keys`);const o=Object.keys(n).filter(r=>!r.startsWith("jcr:")&&!r.startsWith("sling:"));return o.length>0&&p.log(`Template child nodes: ${o.join(", ")}`),n}return null}catch(t){const n=t.response?.status||t.statusCode||"unknown";return n===404?p.log(`No cq:template found at ${e}/cq:template`):p.warn(`Error checking for cq:template: ${t.message} (status: ${n})`),null}}async applyTemplateChildNodes(e,t){if(!(!t||Object.keys(t).length===0)){p.log(`Applying ${Object.keys(t).length} child nodes from cq:template to: ${e}`);for(const[n,o]of Object.entries(t))try{const r=`${e}/${n}`,s={...o};s["jcr:primaryType"]||(s["jcr:primaryType"]="nt:unstructured"),await this.fetch.post(r,{...s,":operation":"import",":contentType":"json",":replace":"true"}),p.log(`Created template child node: ${r}`)}catch(r){p.warn(`Failed to create template child node ${n}: ${r.message}`)}}}async addComponent(e){return C(async()=>{const{pagePath:t,resourceType:n,containerPath:o,name:r,properties:s={}}=e;if(!t||typeof t!="string")throw $(P.INVALID_PARAMETERS,"Page path is required and must be a string",{pagePath:t});if(!n||typeof n!="string")throw $(P.INVALID_PARAMETERS,"Resource type is required and must be a string",{resourceType:n});if(!v(t,this.aemConfig))throw $(P.INVALID_PATH,`Invalid page path: ${String(t)}`,{pagePath:t});p.log(`Fetching component definition for resourceType: ${n}`);const a=await this.getComponentDefinition(n);if(a.componentPath?p.log(`Component definition found at: ${a.componentPath}`):p.warn(`Component definition not found for ${n}, skipping required property validation`),a.requiredProperties.length>0){const d=[];if(a.requiredProperties.forEach(g=>{(!(g in s)||s[g]===null||s[g]===void 0||s[g]==="")&&d.push(g)}),d.length>0)throw $(P.INVALID_PARAMETERS,`Missing required properties for component ${n}: ${d.join(", ")}`,{resourceType:n,componentPath:a.componentPath,requiredProperties:a.requiredProperties,missingProperties:d,providedProperties:Object.keys(s)});p.log(`All required properties validated: ${a.requiredProperties.join(", ")}`)}if(p.log(`Checking field definitions: ${Object.keys(a.fieldDefinitions).length} fields found`),Object.keys(a.fieldDefinitions).length>0){p.log(`Field definitions: ${JSON.stringify(Object.keys(a.fieldDefinitions))}`),p.log(`Validating properties: ${JSON.stringify(Object.keys(s))}`);const d=this.validateComponentProperties(s,a.fieldDefinitions);if(p.log(`Validation result: valid=${d.valid}, errors=${d.errors.length}`),!d.valid){const g=d.errors.join("; ");throw p.error(`Property validation failed: ${g}`),$(P.INVALID_PARAMETERS,`Invalid property values for component ${n}: ${g}`,{resourceType:n,componentPath:a.componentPath,validationErrors:d.errors,invalidFields:d.invalidFields,providedProperties:s})}d.warnings.length>0&&p.warn(`Property validation warnings: ${d.warnings.join("; ")}`),p.log("Property values validated successfully against component dialog definitions")}else p.log(`No field definitions found for component ${n}, skipping property value validation`);try{const d=await this.fetch.get(`${t}.json`);if(!d||typeof d=="object"&&Object.keys(d).length===0)throw $(P.INVALID_PATH,`Page not found or invalid at: ${t}`,{pagePath:t});p.log(`Page verified at: ${t}`)}catch(d){throw d.message?.includes("404")||d.message?.includes("Page not found")||d.response?.status===404?$(P.INVALID_PATH,`Page not found at: ${t}`,{pagePath:t}):M(d,"addComponent")}let i;if(o)o.startsWith("/")?i=o:o.includes("jcr:content")?i=`${t}/${o}`:i=`${t}/jcr:content/${o}`;else{const d=`${t}/jcr:content`;let g=!1;try{const u=await this.fetch.get(`${d}.5.json`);u&&u.root?u.root.container?(i=`${d}/root/container`,g=!0,p.log(`\u2705 Found root/container at: ${i}`)):(i=`${d}/root/container`,g=!1,p.log(`Found root but no container, will create container at: ${i}`)):(i=`${d}/root/container`,g=!1,p.log(`No root found, will create root/container at: ${i}`))}catch{i=`${d}/root/container`,g=!1,p.warn(`Could not fetch jcr:content structure, will create root/container at: ${i}`)}}try{await this.fetch.get(`${i}.json`),p.log(`\u2705 Container exists at: ${i}`)}catch(d){if(d.message?.includes("404")||d.response?.status===404){p.warn(`Container not found at ${i}, attempting to create root/container structure`);const u=`${`${t}/jcr:content`}/root`,f=`${u}/container`;try{try{await this.fetch.get(`${u}.json`),p.log(`\u2705 Root already exists at: ${u}`)}catch{p.log(`Creating root at: ${u}`),await this.fetch.post(u,{"jcr:primaryType":"nt:unstructured","sling:resourceType":"aemmcp/base/components/aemmcp-container/v1/aemmcp-container"}),p.log(`\u2705 Created root at: ${u}`)}i===f&&(p.log(`Creating container at: ${f}`),await this.fetch.post(f,{"jcr:primaryType":"nt:unstructured","sling:resourceType":"aemmcp/base/components/aemmcp-container/v1/aemmcp-container"}),p.log(`\u2705 Created container at: ${f}`))}catch(w){throw $(P.INVALID_PATH,`Could not create root/container structure at: ${i}`,{targetContainerPath:i,error:w.message})}}else throw M(d,"addComponent")}const l=r||`component_${Date.now()}`,c=`${i}/${l}`;let h=null,A={},T={};if(a.componentPath&&(h=await this.getComponentTemplate(a.componentPath),h)){p.log("Component has cq:template, processing structure...");const d=["jcr:created","jcr:createdBy","jcr:lastModified","jcr:lastModifiedBy","jcr:mixinTypes"],g=["jcr:primaryType","sling:resourceType","layout","showSeparator","columns","separator"];for(const[u,f]of Object.entries(h))d.includes(u)||(g.includes(u)?A[u]=f:typeof f=="object"&&f!==null&&!Array.isArray(f)&&(f["jcr:primaryType"]!==void 0||f["sling:resourceType"]!==void 0)?T[u]=f:(typeof f!="object"||Array.isArray(f))&&(A[u]=f));p.log(`Template: ${Object.keys(A).length} properties, ${Object.keys(T).length} child nodes`)}const m=new URLSearchParams;if(Object.entries(A).forEach(([d,g])=>{g!=null&&(Array.isArray(g)?g.forEach(u=>m.append(d,u.toString())):typeof g=="object"?m.append(d,JSON.stringify(g)):m.append(d,g.toString()))}),Object.entries(s).forEach(([d,g])=>{g!=null&&(Array.isArray(g)?g.forEach(u=>m.append(d,u.toString())):typeof g=="object"?m.append(d,JSON.stringify(g)):m.append(d,g.toString()))}),m.has("jcr:primaryType")||m.append("jcr:primaryType","nt:unstructured"),m.has("sling:resourceType")||m.append("sling:resourceType",n),await this.fetch.post(c,m),Object.keys(T).length>0)try{await this.applyTemplateChildNodes(c,T),p.log(`\u2705 Applied ${Object.keys(T).length} child nodes from cq:template`)}catch(d){p.warn(`Failed to apply cq:template child nodes: ${d.message}`)}let j=await this.fetch.get(`${c}.json`),S=!1,b=null;try{if(a.componentPath){const d=await this.fetch.get(`${a.componentPath}.json`,{":depth":"2"});if(d&&(d["cq:isContainer"]===!0||d["cq:isContainer"]==="true")){S=!0,p.log(`Component ${n} is a container component (cq:isContainer=true)`),await new Promise(f=>setTimeout(f,1e3)),j=await this.fetch.get(`${c}.json`,{":depth":"5"}),Object.keys(j).some(f=>!f.startsWith("jcr:")&&!f.startsWith("sling:")&&!f.startsWith("cq:")&&f!=="layout"&&f!=="showSeparator"&&f!=="columns"&&f!=="separator")||(p.log("No default structure detected immediately, waiting longer..."),await new Promise(f=>setTimeout(f,1500)),j=await this.fetch.get(`${c}.json`,{":depth":"5"})),b={};const u=(f,w,I=0)=>{I>3||Object.keys(f).forEach(E=>{if(!E.startsWith("jcr:")&&!E.startsWith("sling:")&&!E.startsWith("cq:")&&E!=="layout"&&E!=="showSeparator"&&E!=="columns"&&E!=="separator"){const R=f[E];if(R&&typeof R=="object"&&!Array.isArray(R)){const W=w?`${w}/${E}`:E;if(R["sling:resourceType"]){const D=R["sling:resourceType"];D.includes("aemmcp-cc-container")||D.includes("columncontrol")||E.startsWith("col_")||E.match(/^col_\d+$/)?b[E]={path:`${c}/${W}`,resourceType:D,type:"column",isContainer:R["cq:isContainer"]===!0||R["cq:isContainer"]==="true"}:b[E]={path:`${c}/${W}`,resourceType:D,isContainer:R["cq:isContainer"]===!0||R["cq:isContainer"]==="true"},u(R,W,I+1)}else(E.startsWith("col_")||E.match(/^col_\d+$/)||E.startsWith("column")||E.match(/^column\d+$/)||E.includes("container")||E.includes("parsys")||E.includes("Column")||E.includes("Container"))&&(b[E]={path:`${c}/${W}`,type:"container",children:Object.keys(R).filter(D=>!D.startsWith("jcr:")&&!D.startsWith("sling:")&&!D.startsWith("cq:"))},u(R,W,I+1))}}})};u(j,""),Object.keys(b).length>0?p.log(`Container structure detected: ${Object.keys(b).join(", ")}`):p.log("Container component created but no default structure detected yet")}}}catch(d){p.warn(`Could not check container status: ${d.message}`)}return y({success:!0,pagePath:t,previewUrl:this.getPreviewUrl(t),componentPath:c,resourceType:n,isContainer:S,containerStructure:b||void 0,componentDefinition:{path:a.componentPath||"Not found",requiredProperties:a.requiredProperties,validationPassed:a.requiredProperties.length===0||a.requiredProperties.every(d=>d in s&&s[d]!==null&&s[d]!==void 0&&s[d]!==""),hasTemplate:h!==null,templatePath:h?`${a.componentPath}/cq:template`:void 0,templateChildNodesCount:Object.keys(T).length,fieldDefinitions:Object.keys(a.fieldDefinitions).length>0?a.fieldDefinitions:void 0},containerPath:i,componentName:l,properties:s,verification:j,timestamp:new Date().toISOString()},"addComponent")},"addComponent")}async deleteComponent(e){return C(async()=>{const{componentPath:t}=e;if(!v(t,this.aemConfig))throw $(P.INVALID_PARAMETERS,`Invalid component path: ${String(t)}`,{componentPath:t});let n=!1;try{const o=new URLSearchParams;o.append(":operation","delete"),await this.fetch.post(t,o),n=!0}catch(o){if(o?.status===405||o?.response?.status===405||o?.status===403||o?.response?.status===403)try{await this.fetch.delete(t),n=!0}catch(r){throw p.error("Both POST and DELETE failed:",o.response?.status,r.response?.status),o}else throw p.error("DELETE failed:",o.response?.status,o.response?.data),o}return y({success:n,deletedPath:t,timestamp:new Date().toISOString()},"deleteComponent")},"deleteComponent")}async unpublishContent(e){return C(async()=>{const{contentPaths:t,unpublishTree:n=!1}=e;if(!t||Array.isArray(t)&&t.length===0)throw $(P.INVALID_PARAMETERS,"Content paths array is required and cannot be empty",{contentPaths:t});const o=[];for(const r of Array.isArray(t)?t:[t])try{const s=new URLSearchParams;s.append("cmd","Deactivate"),s.append("path",r),s.append("ignoredeactivated","false"),s.append("onlymodified","false"),n&&s.append("deep","true");const a=await this.fetch.post("/bin/replicate.json",s);o.push({path:r,success:!0,response:a})}catch(s){o.push({path:r,success:!1,error:s.message})}return y({success:o.every(r=>r.success),results:o,unpublishedPaths:t,unpublishTree:n,timestamp:new Date().toISOString()},"unpublishContent")},"unpublishContent")}async activatePage(e){return C(async()=>{const{pagePath:t,activateTree:n=!1}=e;if(!v(t,this.aemConfig))throw $(P.INVALID_PARAMETERS,`Invalid page path: ${String(t)}`,{pagePath:t});try{const o=new URLSearchParams;o.append("cmd","Activate"),o.append("path",t);let r;return n?(o.append("ignoredeactivated","false"),o.append("onlymodified","false"),o.append("deep","true"),r=await this.fetch.post("/libs/replication/treeactivation.html",o)):r=await this.fetch.post("/bin/replicate.json",o),y({success:!0,activatedPath:t,activateTree:n,response:r,timestamp:new Date().toISOString()},"activatePage")}catch(o){try{const r=await this.fetch.post("/bin/wcmcommand",{cmd:"activate",path:t,ignoredeactivated:!1,onlymodified:!1});return y({success:!0,activatedPath:t,activateTree:n,response:r,fallbackUsed:"WCM Command",timestamp:new Date().toISOString()},"activatePage")}catch{throw M(o,"activatePage")}}},"activatePage")}async deactivatePage(e){return C(async()=>{const{pagePath:t,deactivateTree:n=!1}=e;if(!v(t,this.aemConfig))throw $(P.INVALID_PARAMETERS,`Invalid page path: ${String(t)}`,{pagePath:t});try{const o=new URLSearchParams;o.append("cmd","Deactivate"),o.append("path",t),o.append("ignoredeactivated","false"),o.append("onlymodified","false"),n&&o.append("deep","true");const r=await this.fetch.post("/bin/replicate.json",o);return y({success:!0,deactivatedPath:t,deactivateTree:n,response:r,timestamp:new Date().toISOString()},"deactivatePage")}catch(o){try{const r=await this.fetch.post("/bin/wcmcommand",{cmd:"deactivate",path:t,ignoredeactivated:!1,onlymodified:!1});return y({success:!0,deactivatedPath:t,deactivateTree:n,response:r,fallbackUsed:"WCM Command",timestamp:new Date().toISOString()},"deactivatePage")}catch{throw M(o,"deactivatePage")}}},"deactivatePage")}async updateAsset(e){return C(async()=>{const{assetPath:t,metadata:n,fileContent:o,mimeType:r}=e;if(!v(t,this.aemConfig))throw $(P.INVALID_PARAMETERS,`Invalid asset path: ${String(t)}`,{assetPath:t});const s=new URLSearchParams;o&&(s.append("file",o),r&&s.append("jcr:content/jcr:mimeType",r)),n&&typeof n=="object"&&Object.entries(n).forEach(([a,i])=>{s.append(`jcr:content/metadata/${a}`,String(i))});try{const a=await this.fetch.post(t,s),i=await this.fetch.get(`${t}.json`);return y({success:!0,assetPath:t,updatedMetadata:n,updateResponse:a,assetData:i,timestamp:new Date().toISOString()},"updateAsset")}catch(a){throw M(a,"updateAsset")}},"updateAsset")}async deleteAsset(e){return C(async()=>{const{assetPath:t,force:n=!1}=e;if(!v(t,this.aemConfig))throw $(P.INVALID_PARAMETERS,`Invalid asset path: ${String(t)}`,{assetPath:t});return await this.fetch.delete(t),y({success:!0,deletedPath:t,force:n,timestamp:new Date().toISOString()},"deleteAsset")},"deleteAsset")}getTemplatesPath(e){if(!e||e.trim().length===0)return"";let t=e.trim(),n="/conf",o="/settings/wcm/templates";return t=t.replace(/\/+$/,""),t.startsWith("/content/")&&(t=t.replace("/content","")),t.startsWith(n)||(t=`${n}/${t.replace(/^\//,"")}`),t.endsWith(o)||(t+=o),t}async getTemplates(e){return C(async()=>{if(e)try{const t=this.getTemplatesPath(e);if(!t)throw $(P.INVALID_PARAMETERS,`Cannot determine configuration path for site: ${e}`,{sitePath:e});p.log("Looking for site-specific templates at:",t);const n=await this.fetch.get(`${t}.2.json`),o=[];return n&&typeof n=="object"&&Object.entries(n).forEach(([r,s])=>{r.startsWith("jcr:")||r.startsWith("sling:")||s&&typeof s=="object"&&s["jcr:content"]&&o.push({name:r,path:`${t}/${r}`,title:s["jcr:content"]["jcr:title"]||r,description:s["jcr:content"]["jcr:description"],allowedPaths:s["jcr:content"].allowedPaths,ranking:s["jcr:content"].ranking||0})}),y({sitePath:e,templates:o,totalCount:o.length,source:"site-specific"},"getTemplates")}catch{}try{const t=["/apps/wcm/core/content/sites/templates","/libs/wcm/core/content/sites/templates"],n=[];for(const o of t)try{const r=await this.fetch.get(`${o}.json`,{":depth":"2"});r&&typeof r=="object"&&Object.entries(r).forEach(([s,a])=>{s.startsWith("jcr:")||s.startsWith("sling:")||a&&typeof a=="object"&&n.push({name:s,path:`${o}/${s}`,title:a["jcr:content"]?.["jcr:title"]||s,description:a["jcr:content"]?.["jcr:description"],allowedPaths:a["jcr:content"]?.allowedPaths,ranking:a["jcr:content"]?.ranking||0,source:o.includes("/apps/")?"apps":"libs"})})}catch{}return y({sitePath:e||"global",templates:n,totalCount:n.length,source:"global"},"getTemplates")}catch(t){throw M(t,"getTemplates")}},"getTemplates")}async getTemplateStructure(e){return C(async()=>{try{const t=await this.fetch.get(`${e}.infinity.json`),n={path:e,properties:t["jcr:content"]||{},policies:t["jcr:content"]?.policies||{},structure:t["jcr:content"]?.structure||{},initialContent:t["jcr:content"]?.initial||{},allowedComponents:[],allowedPaths:t["jcr:content"]?.allowedPaths||[]},o=(r,s="")=>{if(!(!r||typeof r!="object")){if(r.components){const a=Object.keys(r.components);n.allowedComponents.push(...a)}Object.entries(r).forEach(([a,i])=>{typeof i=="object"&&i!==null&&!a.startsWith("jcr:")&&o(i,s?`${s}/${a}`:a)})}};return o(n.policies),n.allowedComponents=[...new Set(n.allowedComponents)],y({templatePath:e,structure:n,fullData:t},"getTemplateStructure")}catch(t){throw M(t,"getTemplateStructure")}},"getTemplateStructure")}async bulkUpdateComponents(e){return C(async()=>{const{updates:t,validateFirst:n=!0,continueOnError:o=!1}=e;if(!Array.isArray(t)||t.length===0)throw $(P.INVALID_PARAMETERS,"Updates array is required and cannot be empty");const r=[];if(n)for(const a of t)try{await this.fetch.get(`${a.componentPath}.json`)}catch(i){if(i.response?.status===404&&(r.push({componentPath:a.componentPath,success:!1,error:`Component not found: ${a.componentPath}`,phase:"validation"}),!o))return y({success:!1,message:"Bulk update failed during validation phase",results:r,totalUpdates:t.length,successfulUpdates:0},"bulkUpdateComponents")}let s=0;for(const a of t)try{const i=await this.updateComponent({componentPath:a.componentPath,properties:a.properties});r.push({componentPath:a.componentPath,success:!0,result:i,phase:"update"}),s++}catch(i){if(r.push({componentPath:a.componentPath,success:!1,error:i.message,phase:"update"}),!o)break}return y({success:s===t.length,message:`Bulk update completed: ${s}/${t.length} successful`,results:r,totalUpdates:t.length,successfulUpdates:s,failedUpdates:t.length-s},"bulkUpdateComponents")},"bulkUpdateComponents")}async convertComponents(e){return C(async()=>{const{pagePath:t,sourceResourceType:n,targetResourceType:o,requiredProperties:r={},continueOnError:s=!0}=e;if(!t||typeof t!="string")throw $(P.INVALID_PARAMETERS,"Page path is required and must be a string");if(!n||typeof n!="string")throw $(P.INVALID_PARAMETERS,"Source resource type is required and must be a string");if(!o||typeof o!="string")throw $(P.INVALID_PARAMETERS,"Target resource type is required and must be a string");if(!v(t,this.aemConfig))throw $(P.INVALID_PATH,`Page path '${t}' is not within allowed content roots`);p.log(`Scanning page ${t} for components with resourceType: ${n}`);const i=await this.scanPageComponents(t),c=(i.data?.components||i.components||[]).filter(b=>b.resourceType===n);if(c.length===0)return y({message:`No components found with resourceType: ${n} on page ${t}`,pagePath:t,sourceResourceType:n,targetResourceType:o,componentsFound:0,componentsConverted:0},"convertComponents");p.log(`Found ${c.length} components with resourceType: ${n}`);const A=(await this.getComponentDefinition(o)).requiredProperties||[],T=[];if(A.length>0&&(A.forEach(b=>{(!(b in r)||r[b]===null||r[b]===void 0||r[b]==="")&&T.push(b)}),T.length>0))return y({message:"Target component requires properties that are not provided",pagePath:t,sourceResourceType:n,targetResourceType:o,componentsFound:c.length,componentsConverted:0,targetComponentRequiredProperties:A,missingRequiredProperties:T,providedProperties:Object.keys(r),note:"Please provide the missing required properties in the requiredProperties parameter and retry"},"convertComponents");const m=[];let j=0,S=0;for(const b of c)try{let d;b.path.startsWith("/")?d=b.path:b.path.startsWith("jcr:content")?d=`${t}/${b.path}`:d=`${t}/${b.path}`,p.log(`Processing component at: ${d}`),p.log(`Deleting source component at: ${d}`),await this.deleteComponent({componentPath:d}),p.log(`Creating target component at: ${d}`);const g=new URLSearchParams;g.append("jcr:primaryType","nt:unstructured"),g.append("sling:resourceType",o),Object.entries(r).forEach(([f,w])=>{if(w!=null){const I=f.startsWith("./")?f.substring(2):f;Array.isArray(w)?w.forEach(E=>{g.append(I,E.toString())}):typeof w=="object"?g.append(I,JSON.stringify(w)):g.append(I,w.toString())}}),await this.fetch.post(d,g,{headers:{Accept:"application/json"}});const u=await this.fetch.get(`${d}.json`);if(!u||!u["sling:resourceType"]||u["sling:resourceType"]!==o)throw new Error("Component creation verification failed: resourceType mismatch");m.push({sourceComponentPath:d,targetComponentPath:d,success:!0,message:`Successfully converted component from ${n} to ${o}`}),j++,p.log(`\u2705 Successfully converted component at: ${d}`)}catch(d){const g=d.message||String(d);if(p.error(`Failed to convert component at ${b.path}: ${g}`),m.push({sourceComponentPath:b.path,success:!1,error:g}),S++,!s)return y({message:"Conversion stopped due to error (continueOnError=false)",pagePath:t,sourceResourceType:n,targetResourceType:o,componentsFound:c.length,componentsConverted:j,componentsFailed:S,results:m},"convertComponents")}return y({message:`Converted ${j} of ${c.length} components from ${n} to ${o}`,pagePath:t,sourceResourceType:n,targetResourceType:o,componentsFound:c.length,componentsConverted:j,componentsFailed:S,results:m},"convertComponents")},"convertComponents")}async bulkConvertComponents(e){return C(async()=>{const{pagePaths:t,searchPath:n,depth:o=2,limit:r=50,sourceResourceType:s,targetResourceType:a,requiredProperties:i={},continueOnError:l=!0}=e;if(!s||typeof s!="string")throw $(P.INVALID_PARAMETERS,"Source resource type is required and must be a string");if(!a||typeof a!="string")throw $(P.INVALID_PARAMETERS,"Target resource type is required and must be a string");let c=[];if(t&&Array.isArray(t)&&t.length>0)c=t,p.log(`Processing ${c.length} specified pages`);else if(n){if(!v(n,this.aemConfig))throw $(P.INVALID_PATH,`Search path '${n}' is not within allowed content roots`);p.log(`Searching for pages under ${n} with depth ${o}`);const d=await this.listPages(n,o,r),g=d.data?.pages||d.pages||[];if(g.length===0)return y({message:`No pages found under ${n}`,searchPath:n,sourceResourceType:s,targetResourceType:a,pagesProcessed:0,pagesSucceeded:0,pagesFailed:0,totalComponentsConverted:0},"bulkConvertComponents");c=g.map(u=>u.path||u["@path"]).filter(u=>u),p.log(`Found ${c.length} pages to process`)}else throw $(P.INVALID_PARAMETERS,"Either pagePaths array or searchPath must be provided");if(c.length===0)return y({message:"No pages to process",sourceResourceType:s,targetResourceType:a,pagesProcessed:0,pagesSucceeded:0,pagesFailed:0,totalComponentsConverted:0},"bulkConvertComponents");const h=[];let A=0,T=0,m=0,j=0,S=0;for(const b of c)try{p.log(`Processing page: ${b}`);const g=await this.convertComponents({pagePath:b,sourceResourceType:s,targetResourceType:a,requiredProperties:i,continueOnError:l}),u=g.data||g;if(g.success!==!1){const f=u.componentsFound||0,w=u.componentsConverted||0,I=u.componentsFailed||0;m+=f,j+=w,S+=I,f>0&&A++,h.push({pagePath:b,success:!0,componentsFound:f,componentsConverted:w,componentsFailed:I,message:u.message||"Processed successfully"})}else T++,h.push({pagePath:b,success:!1,error:u.message||"Conversion failed",componentsFound:0,componentsConverted:0,componentsFailed:0})}catch(d){const g=d.message||String(d);if(p.error(`Failed to process page ${b}: ${g}`),T++,h.push({pagePath:b,success:!1,error:g,componentsFound:0,componentsConverted:0,componentsFailed:0}),!l)return y({message:"Bulk conversion stopped due to error (continueOnError=false)",sourceResourceType:s,targetResourceType:a,pagesProcessed:c.length,pagesSucceeded:A,pagesFailed:T,totalComponentsFound:m,totalComponentsConverted:j,totalComponentsFailed:S,pageResults:h},"bulkConvertComponents")}return y({message:`Bulk conversion completed: ${A} pages succeeded, ${T} pages failed. Total components converted: ${j}`,sourceResourceType:s,targetResourceType:a,pagesProcessed:c.length,pagesSucceeded:A,pagesFailed:T,totalComponentsFound:m,totalComponentsConverted:j,totalComponentsFailed:S,pageResults:h},"bulkConvertComponents")},"bulkConvertComponents")}async getNodeContent(e,t=1,n="standard"){return C(async()=>{const o=`${e}.json`,r=await this.fetch.get(o,{":depth":t.toString()}),s=U(r,n);return{path:e,depth:t,verbosity:n,content:s,timestamp:new Date().toISOString()}},"getNodeContent")}async getAvailableTemplates(e){return C(async()=>{console.log("getAvailableTemplates for parentPath:",e);let t="/conf";const n=e.split("/");n.length>=3&&n[1]==="content"&&(t=`/conf/${n[2]}`);const o=`${t}/settings/wcm/templates`;try{const r=await this.fetch.get(`${o}.3.json`),s=[];return r&&typeof r=="object"&&Object.entries(r).forEach(([a,i])=>{if(!(a.startsWith("jcr:")||a.startsWith("sling:"))&&i&&typeof i=="object"&&i["jcr:content"]){const l=`${o}/${a}`,c=i["jcr:content"],h=i?.structure?.["jcr:content"]||{};s.push({name:a,path:l,title:c["jcr:title"]||a,description:c["jcr:description"]||"",thumbnail:c.thumbnail||"",allowedPaths:c.allowedPaths||[],status:c.status||"enabled",ranking:c.ranking||0,templateType:c.templateType||"page",resourceType:h["sling:resourceType"]||"",lastModified:c["cq:lastModified"],createdBy:c["jcr:createdBy"]})}}),s.sort((a,i)=>a.ranking!==i.ranking?i.ranking-a.ranking:a.name.localeCompare(i.name)),y({parentPath:e,templatesPath:o,templates:s,totalCount:s.length,availableTemplates:s.filter(a=>a.status==="enabled")},"getAvailableTemplates")}catch(r){if(r.response?.status===404){const s="/libs/wcm/foundation/templates",a=await this.fetch.get(`${s}.json`,{":depth":"2"}),i=[];return a&&typeof a=="object"&&Object.entries(a).forEach(([l,c])=>{l.startsWith("jcr:")||l.startsWith("sling:")||c&&typeof c=="object"&&i.push({name:l,path:`${s}/${l}`,title:c["jcr:title"]||l,description:c["jcr:description"]||"Global template",status:"enabled",ranking:0,templateType:"page",isGlobal:!0})}),y({parentPath:e,templatesPath:s,templates:i,totalCount:i.length,availableTemplates:i,fallbackUsed:!0},"getAvailableTemplates")}throw r}},"getAvailableTemplates")}async createPageWithTemplate(e){return C(async()=>{const{parentPath:t,title:n,template:o,name:r,properties:s={},resourceType:a=""}=e;if(!v(t,this.aemConfig))throw $(P.INVALID_PARAMETERS,`Invalid parent path: ${String(t)}`,{parentPath:t});if(!r&&!n)throw $(P.INVALID_PARAMETERS,'Either "name" or "title" must be provided to create a page. Please provide at least one of these parameters.',{parentPath:t,providedParams:{name:r,title:n}});const i=n||(r?r.replace(/-/g," ").replace(/\b\w/g,f=>f.toUpperCase()):"");let l=o,c=a;if(!l){const w=(await this.getAvailableTemplates(t)).data.availableTemplates;if(w.length===0)throw $(P.INVALID_PARAMETERS,"No templates available for this path",{parentPath:t});const I=w[0];l=I.path,!c&&I.resourceType&&(c=I.resourceType),p.log(`\u{1F3AF} Auto-selected template: ${l} (${w[0].title})`,c)}let h=null;try{const f=await this.fetch.get(`${l}.json`);p.log(`\u2705 Template verified: ${l}`,f);try{const w=await this.fetch.get(`${l}.infinity.json`);if(!c&&w){const I=w.structure?.["jcr:content"];if(I&&I["sling:resourceType"])c=I["sling:resourceType"],p.log(`\u{1F4CB} Extracted resourceType from template structure: ${c}`);else{const E=w["jcr:content"];E&&E["sling:resourceType"]&&(c=E["sling:resourceType"],p.log(`\u{1F4CB} Extracted resourceType from template jcr:content: ${c}`))}}w&&w["jcr:content"]&&w.initial?(h=w.initial,p.log(`\u{1F4CB} Found initial content node at template/initial: ${l}`)):p.warn(`\u26A0\uFE0F No initial content found in template at /initial: ${l}`)}catch(w){p.warn(`Could not fetch template structure: ${w.message}`)}}catch(f){throw p.error("Template verification failed:",f.message,f),f?.response?.status===404?$(P.INVALID_PARAMETERS,`Template not found: ${l}`,{template:l}):M(f,"createPageWithTemplate")}try{await this.validateTemplate(l,t),p.log(`\u2705 Template validation passed for ${l} at ${t}`)}catch(f){throw f}const A=r||(i?i.replace(/\s+/g,"-").toLowerCase():""),T=`${t}/${A}`;try{const f=await this.fetch.get(`${T}.json`);if(f&&(f["jcr:primaryType"]==="cq:Page"||f["jcr:content"]))throw $(P.INVALID_PARAMETERS,`Page already exists at path: ${T}. Cannot overwrite existing page. Please use a different name or delete the existing page first.`,{pagePath:T,parentPath:t,pageName:A,existingPage:!0})}catch(f){if(f.code===P.INVALID_PARAMETERS&&f.message?.includes("already exists"))throw f;f.response?.status!==404&&!f.message?.includes("404")&&p.warn(`Could not check if page exists at ${T}: ${f.message}`)}const m={"jcr:primaryType":"cq:Page","jcr:content":{"jcr:primaryType":"cq:PageContent","jcr:title":i,"cq:template":l,"sling:resourceType":c||"foundation/components/page","cq:lastModified":new Date().toISOString(),"jcr:createdBy":"admin","jcr:created":new Date().toISOString(),"cq:lastModifiedBy":"admin",...s}};if(h&&h["jcr:content"]){const f=h["jcr:content"];Object.entries(f).forEach(([w,I])=>{w==="jcr:created"||w==="jcr:createdBy"||w==="jcr:title"||w==="cq:template"||w.startsWith("jcr:")&&w!=="jcr:primaryType"||typeof I=="object"&&!Array.isArray(I)&&!w.startsWith("jcr:")&&!w.startsWith("sling:")&&!w.startsWith("cq:")||m["jcr:content"][w]||(m["jcr:content"][w]=I)}),p.log("\u{1F4E6} Merged initial content properties from template")}const j=new URLSearchParams;j.append("jcr:primaryType","cq:Page"),await this.fetch.post(T,j);const S=new URLSearchParams;if(Object.entries(m["jcr:content"]).forEach(([f,w])=>{if(!(f==="jcr:created"||f==="jcr:createdBy"))if(typeof w=="object"&&w!==null&&!Array.isArray(w)){if(!f.startsWith("jcr:")&&!f.startsWith("sling:")&&!f.startsWith("cq:"))return;S.append(f,JSON.stringify(w))}else S.append(f,String(w))}),await this.fetch.post(`${T}/jcr:content`,S),h&&h["jcr:content"]){const f=`${T}/jcr:content`,I=h["jcr:content"],E=async(R,W,D=10)=>{if(!(D<=0)){for(const[x,L]of Object.entries(W))if(!(x.startsWith("jcr:")||x.startsWith("sling:")||x.startsWith("cq:")||x.startsWith("rep:")||x.startsWith("oak:"))&&L&&typeof L=="object"&&!Array.isArray(L)){const q=`${R}/${x}`,N=L;try{try{await this.fetch.get(`${q}.json`),N&&typeof N=="object"&&await E(q,N,D-1)}catch{const _=new URLSearchParams;N["jcr:primaryType"]?_.append("jcr:primaryType",N["jcr:primaryType"]):_.append("jcr:primaryType","nt:unstructured"),Object.entries(N).forEach(([F,O])=>{F!=="jcr:primaryType"&&(F.startsWith("jcr:")&&F!=="jcr:primaryType"||typeof O=="object"&&!Array.isArray(O)||O!=null&&(Array.isArray(O)?O.forEach(B=>{_.append(F,String(B))}):_.append(F,String(O))))}),await this.fetch.post(q,_),p.log(`\u2705 Created initial node: ${q}`),await E(q,N,D-1)}}catch(V){p.warn(`Could not create initial node ${q}: ${V.message}`)}}}};await E(f,I),p.log("\u2705 Created initial content structure from template under jcr:content")}const b=await this.fetch.get(`${T}.json`),d=b["jcr:content"]!==void 0;let g=!1;try{g=(await this.fetch.get(`${T}.html`)).status===200}catch{g=!1}const u={hasErrors:!1,errors:[]};return y({success:!0,pagePath:T,previewUrl:this.getPreviewUrl(T),title:n,templateUsed:l,jcrContentCreated:d,pageAccessible:g,errorLogCheck:u,creationDetails:{timestamp:new Date().toISOString(),steps:["Template validation completed","Template initial content fetched","Page node created","jcr:content node created",h?"Initial content structure created from template":"No initial content in template","Page structure verified","Accessibility check completed"],initialContentCreated:h!==null},pageStructure:b.data},"createPageWithTemplate")},"createPageWithTemplate")}async validateTemplate(e,t){return C(async()=>{try{let n;try{n=await this.fetch.get(`${e}.2.json`)}catch(m){try{n=await this.fetch.get(`${e}.infinity.json`)}catch(j){throw $(P.INVALID_PARAMETERS,`Template not found or inaccessible: ${e}`,{templatePath:e,fetchError:m.message,infinityError:j.message})}}if(!n||typeof n!="object")throw $(P.INVALID_PARAMETERS,"Invalid template structure: template data is not an object",{templatePath:e});const o=n["jcr:content"];if(!o||typeof o!="object")throw $(P.INVALID_PARAMETERS,"Invalid template structure: jcr:content not found",{templatePath:e,templateDataKeys:Object.keys(n)});const r=o.allowedPaths,s=Array.isArray(r)?r:r?[r]:[],a=o.allowedParents,i=Array.isArray(a)?a:a?[a]:[];let l=s.length===0;s.length>0&&(l=s.some(m=>{if(m.includes("(")||m.includes("*")||m.includes("?"))try{return new RegExp(m).test(t)}catch{return t.startsWith(m.replace(/[()*?]/g,""))}return t.startsWith(m)}));let c=!0,h=null,A=null;if(i.length>0)try{const m=await this.fetch.get(`${t}.2.json`);m&&m["jcr:content"]?(h=m["jcr:content"]["cq:template"]||null,h?(c=i.includes(h),c||(A=`Template '${e}' cannot be used under parent page with template '${h}'. Allowed parent templates: ${i.join(", ")}`)):(c=!1,A=`Template '${e}' requires a parent page with one of the allowed templates, but parent page at '${t}' has no template. Allowed parent templates: ${i.join(", ")}`)):(c=!1,A=`Template '${e}' requires a parent page with one of the allowed templates, but '${t}' is not a page. Allowed parent templates: ${i.join(", ")}`)}catch(m){m.response?.status===404?(c=!1,A=`Template '${e}' requires a parent page with one of the allowed templates, but parent path '${t}' does not exist. Allowed parent templates: ${i.join(", ")}`):(p.warn(`Could not validate parent path ${t}: ${m.message}`),c=!1,A=`Could not validate parent path '${t}': ${m.message}. Template '${e}' requires a parent page with one of the allowed templates: ${i.join(", ")}`)}if(!(l&&c)){const m=[];throw l||m.push(`Path '${t}' is not allowed. Allowed paths: ${s.join(", ")}`),!c&&A&&m.push(A),$(P.INVALID_PARAMETERS,`Template '${e}' cannot be used at '${t}'. ${m.join(" ")}`,{templatePath:e,targetPath:t,allowedPaths:s,allowedParents:i,parentTemplate:h,pathAllowed:l,parentAllowed:c})}return y({templatePath:e,targetPath:t,isValid:!0,templateTitle:o["jcr:title"]||"Untitled Template",templateDescription:o["jcr:description"]||"",allowedPaths:s,allowedParents:i,parentTemplate:h,restrictions:{hasPathRestrictions:s.length>0,hasParentRestrictions:i.length>0,allowedPaths:s,allowedParents:i,pathAllowed:l,parentAllowed:c}},"validateTemplate")}catch(n){throw n.code===P.INVALID_PARAMETERS?n:n.response?.status===404?$(P.INVALID_PARAMETERS,`Template not found: ${e}`,{templatePath:e}):M(n,"validateTemplate")}},"validateTemplate")}templateCache=new Map;templateCacheExpiry=new Map;TEMPLATE_CACHE_TTL=300*1e3;async getTemplateMetadata(e,t=!0){return C(async()=>{if(t&&this.templateCache.has(e)){const s=this.templateCacheExpiry.get(e)||0;if(Date.now()<s)return y({...this.templateCache.get(e),fromCache:!0},"getTemplateMetadata")}const n=await this.fetch.get(`${e}.json`);if(!n||!n["jcr:content"])throw $(P.INVALID_PARAMETERS,"Invalid template structure",{templatePath:e});const o=n["jcr:content"],r={templatePath:e,title:o["jcr:title"]||"Untitled Template",description:o["jcr:description"]||"",thumbnail:o.thumbnail||"",allowedPaths:o.allowedPaths||[],status:o.status||"enabled",ranking:o.ranking||0,templateType:o.templateType||"page",lastModified:o["cq:lastModified"],createdBy:o["jcr:createdBy"],policies:o.policies||{},structure:o.structure||{},initialContent:o.initial||{}};return t&&(this.templateCache.set(e,r),this.templateCacheExpiry.set(e,Date.now()+this.TEMPLATE_CACHE_TTL)),y(r,"getTemplateMetadata")},"getTemplateMetadata")}clearTemplateCache(){this.templateCache.clear(),this.templateCacheExpiry.clear(),console.log("\u{1F5D1}\uFE0F Template cache cleared")}async getComponents(e){return C(async()=>{const t=e||this.aemConfig.components.componentPaths?.projectRoot1||"/apps/aemmcp/base/components";p.log(`Fetching components from root path: ${t}`);const n=[],o=async(r,s=5)=>{if(!(s<=0))try{const a=await this.fetch.get(`${r}.${Math.min(s,3)}.json`);if(a&&typeof a=="object")for(const[i,l]of Object.entries(a)){const c=l;if(!(i.startsWith("jcr:")||i.startsWith("sling:")||i.startsWith("rep:")||i.startsWith("oak:"))&&c&&typeof c=="object"){const h=`${r}/${i}`,A=c["jcr:primaryType"],T=c._cq_dialog!==void 0,m=c[".content.xml"]!==void 0||c["component.html"]!==void 0||c["component.js"]!==void 0||c["component.css"]!==void 0;if(T||m||A==="cq:Component"){let j=i,S="",b="",d="";if(c["jcr:title"]?j=c["jcr:title"]:c["jcr:content"]?.["jcr:title"]&&(j=c["jcr:content"]["jcr:title"]),c["jcr:description"]?S=c["jcr:description"]:c["jcr:content"]?.["jcr:description"]&&(S=c["jcr:content"]["jcr:description"]),c.componentGroup&&(b=c.componentGroup),d=h.replace("/apps/","").replace("/libs/",""),c._cq_dialog)try{const g=await this.fetch.get(`${h}/_cq_dialog.json`,{":depth":"2"});g&&g["jcr:title"]&&(j=g["jcr:title"]||j),g&&g["jcr:description"]&&(S=g["jcr:description"]||S)}catch{}n.push({name:i,title:j,description:S,path:h,resourceType:d,componentGroup:b||"General",primaryType:A,hasDialog:T})}else await o(h,s-1)}}}catch(a){a.message?.includes("404")?p.warn(`Path not found: ${r}`):p.warn(`Error fetching components from ${r}: ${a.message}`)}};return await o(t,5),n.sort((r,s)=>r.name.localeCompare(s.name)),p.log(`Found ${n.length} components from ${t}`),y({rootPath:t,components:n,totalCount:n.length},"getComponents")},"getComponents")}async listWorkflowModels(){return C(async()=>{const e=await this.fetch.get("/etc/workflow/models.json"),t=Array.isArray(e)?e:[],n={request_for_activation:"Publish/activate pages",request_for_deactivation:"Unpublish/deactivate pages",request_for_deletion:"Delete pages",request_for_deletion_without_deactivation:"Delete pages without unpublishing first","dam/update_asset":"Update DAM assets","dam/dam-update-language-copy":"Update language copies of assets","dam/dam-create-language-copy":"Create language copies of assets","wcm-translation/translate-language-copy":"Translate language copies","wcm-translation/create_language_copy":"Create language copies","wcm-translation/prepare_translation_project":"Prepare translation project","wcm-translation/sync_translation_job":"Sync translation job","wcm-translation/update_language_copy":"Update language copy",activationmodel:"Activation workflow model",scheduled_activation:"Scheduled activation",scheduled_deactivation:"Scheduled deactivation"},o=t.map(r=>{const s=r.uri||"",a=s.replace("/var/workflow/models/",""),i=n[a]||"Custom workflow model";return{uri:s,modelId:a,description:i,...r}});return y({models:o,totalCount:o.length,commonWorkflows:Object.entries(n).map(([r,s])=>({modelId:r,uri:`/var/workflow/models/${r}`,description:s}))},"listWorkflowModels")},"listWorkflowModels")}async startWorkflow(e,t,n="JCR_PATH"){return C(async()=>{const o=e.startsWith("/var/workflow/models/")?e:`/var/workflow/models/${e}`,r=new URLSearchParams;r.append("model",o),r.append("payloadType",n),r.append("payload",t);const s=await this.fetch.postWithHeaders("/etc/workflow/instances",r,{headers:{Accept:"application/json"}});if(!s.ok&&s.status!==201){const c=await s.text();throw $(P.INVALID_PARAMETERS,`Failed to start workflow: ${s.status} - ${c}`)}let a=null,i=null;const l=s.headers.get("Location");if(l){a=l.startsWith("http")?new URL(l).pathname:l;const c=a.split("/").filter(h=>h);c.length>0&&(i=c[c.length-1])}else try{const c=await s.text();if(c){const h=JSON.parse(c);if(h&&h.uri){a=h.uri;const A=a.split("/").filter(T=>T);A.length>0&&(i=A[A.length-1])}}}catch{p.log("Workflow created (201), but response is not JSON. Location header:",l||"not found")}return y({modelId:e,modelUri:o,payload:t,payloadType:n,instanceId:i,instancePath:a,locationHeader:l,status:s.status,message:"Workflow instance started successfully"},"startWorkflow")},"startWorkflow")}async listWorkflowInstances(e){return C(async()=>{const t=e?`/etc/workflow/instances.${e}.json`:"/etc/workflow/instances.json",n=await this.fetch.get(t),o=Array.isArray(n)?n:n?.instances||[];return y({instances:o,totalCount:o.length,state:e||"all",timestamp:new Date().toISOString()},"listWorkflowInstances")},"listWorkflowInstances")}async getWorkflowInstance(e){return C(async()=>{const t=e.startsWith("/var/workflow/instances/")?e:`/var/workflow/instances/${e}`,n=await this.fetch.get(`${t}.json`);return y({instanceId:e,instancePath:t,instance:n,timestamp:new Date().toISOString()},"getWorkflowInstance")},"getWorkflowInstance")}async updateWorkflowInstanceState(e,t){return C(async()=>{if(!["RUNNING","SUSPENDED","ABORTED"].includes(t))throw $(P.INVALID_PARAMETERS,`Invalid state: ${t}. Must be RUNNING, SUSPENDED, or ABORTED`);const n=e.startsWith("/var/workflow/instances/")?e:`/var/workflow/instances/${e}`,o=new URLSearchParams;o.append("state",t);const r=await this.fetch.post(n,o,{headers:{Accept:"application/json"}});return y({instanceId:e,instancePath:n,newState:t,response:r,message:`Workflow instance state updated to ${t}`},"updateWorkflowInstanceState")},"updateWorkflowInstanceState")}async getInboxItems(){return C(async()=>{const e=await this.fetch.get("/bin/workflow/inbox.json"),t=e?.items||e||[];return y({items:t,totalCount:Array.isArray(t)?t.length:0,timestamp:new Date().toISOString()},"getInboxItems")},"getInboxItems")}async completeWorkItem(e,t,n){return C(async()=>{let o=t||"";if(!o){const i=(await this.fetch.get(`${e}.routes.json`))?.routes||[];if(i.length===0)throw $(P.INVALID_PARAMETERS,"No routes available for this work item");const l=i[0];if(!l?.rid)throw $(P.INVALID_PARAMETERS,"No valid route ID found in available routes");o=l.rid,p.log(`No route specified, using first available route: ${l.label||"Unknown"} (${o})`)}const r=new URLSearchParams;r.append("item",e),r.append("route",o),n&&r.append("comment",n);const s=await this.fetch.post("/bin/workflow/inbox",r,{headers:{Accept:"application/json"}});return y({workItemPath:e,routeId:o,comment:n,response:s,message:"Work item completed successfully"},"completeWorkItem")},"completeWorkItem")}async delegateWorkItem(e,t){return C(async()=>{const n=new URLSearchParams;n.append("item",e),n.append("delegatee",t);const o=await this.fetch.post("/bin/workflow/inbox",n,{headers:{Accept:"application/json"}});return y({workItemPath:e,delegatee:t,response:o,message:`Work item delegated to ${t}`},"delegateWorkItem")},"delegateWorkItem")}async getWorkItemRoutes(e){return C(async()=>{const n=(await this.fetch.get(`${e}.routes.json`))?.routes||[];return y({workItemPath:e,routes:n,totalCount:n.length,timestamp:new Date().toISOString()},"getWorkItemRoutes")},"getWorkItemRoutes")}async getContentFragment(e){return this.contentFragments.getContentFragment(e)}async listContentFragments(e){return this.contentFragments.listContentFragments(e)}async manageContentFragment(e){return this.contentFragments.manageContentFragment(e)}async manageContentFragmentVariation(e){return this.contentFragments.manageContentFragmentVariation(e)}async getExperienceFragment(e){return this.experienceFragments.getExperienceFragment(e)}async listExperienceFragments(e){return this.experienceFragments.listExperienceFragments(e)}async manageExperienceFragment(e){return this.experienceFragments.manageExperienceFragment(e)}async manageExperienceFragmentVariation(e){return this.experienceFragments.manageExperienceFragmentVariation(e)}}
@@ -1 +1 @@
1
- "use strict";import{LOGGER as i}from"../utils/logger.js";export class AEMOperationError extends Error{code;details;recoverable;retryAfter;constructor(r){super(r.message),this.name="AEMOperationError",this.code=r.code,this.details=r.details,this.recoverable=r.recoverable,this.retryAfter=r.retryAfter}}export const AEM_ERROR_CODES={CONNECTION_FAILED:"CONNECTION_FAILED",TIMEOUT:"TIMEOUT",AUTHENTICATION_FAILED:"AUTHENTICATION_FAILED",UNAUTHORIZED:"UNAUTHORIZED",INVALID_PATH:"INVALID_PATH",INVALID_COMPONENT_TYPE:"INVALID_COMPONENT_TYPE",INVALID_LOCALE:"INVALID_LOCALE",INVALID_PARAMETERS:"INVALID_PARAMETERS",RESOURCE_NOT_FOUND:"RESOURCE_NOT_FOUND",COMPONENT_NOT_FOUND:"COMPONENT_NOT_FOUND",PAGE_NOT_FOUND:"PAGE_NOT_FOUND",UPDATE_FAILED:"UPDATE_FAILED",VALIDATION_FAILED:"VALIDATION_FAILED",REPLICATION_FAILED:"REPLICATION_FAILED",QUERY_FAILED:"QUERY_FAILED",INSUFFICIENT_PERMISSIONS:"INSUFFICIENT_PERMISSIONS",SYSTEM_ERROR:"SYSTEM_ERROR",RATE_LIMITED:"RATE_LIMITED"};export function createAEMError(t,r,s,e=!1,E){return new AEMOperationError({code:t,message:r,details:s,recoverable:e,retryAfter:E})}export function handleAEMHttpError(t,r){if(t.response){const s=t.response.status,e=t.response.data;switch(s){case 401:return createAEMError(AEM_ERROR_CODES.AUTHENTICATION_FAILED,"Authentication failed. Check AEM credentials.",{status:s,data:e});case 403:return createAEMError(AEM_ERROR_CODES.INSUFFICIENT_PERMISSIONS,"Insufficient permissions for this operation.",{status:s,data:e,operation:r});case 404:return createAEMError(AEM_ERROR_CODES.RESOURCE_NOT_FOUND,"Resource not found in AEM.",{status:s,data:e,operation:r});case 429:const E=t.response.headers["retry-after"];return createAEMError(AEM_ERROR_CODES.RATE_LIMITED,"Rate limit exceeded. Please try again later.",{status:s,data:e},!0,E?parseInt(E)*1e3:6e4);case 500:case 502:case 503:return createAEMError(AEM_ERROR_CODES.SYSTEM_ERROR,"AEM system error. Please try again later.",{status:s,data:e},!0,3e4);default:let n="Unknown error";if(typeof e=="string"&&e.trim().length>0)try{const a=JSON.parse(e);n=a.message||JSON.stringify(a)}catch{n=e}else e&&typeof e=="object"&&e.message?n=e.message:e&&typeof e=="object"&&(n=JSON.stringify(e));return createAEMError(AEM_ERROR_CODES.SYSTEM_ERROR,`HTTP ${s}: ${n}`,{status:s,data:e,operation:r})}}else return t.code==="ECONNREFUSED"||t.code==="ENOTFOUND"?createAEMError(AEM_ERROR_CODES.CONNECTION_FAILED,"Cannot connect to AEM instance. Check host and network.",{originalError:t.message},!0,5e3):t.code==="ETIMEDOUT"?createAEMError(AEM_ERROR_CODES.TIMEOUT,"Request to AEM timed out.",{originalError:t.message},!0,1e4):createAEMError(AEM_ERROR_CODES.SYSTEM_ERROR,`Unexpected error during ${r}: ${t.message}`,{originalError:t.message})}export async function safeExecute(t,r,s=3){let e;for(let E=1;E<=s;E++)try{return await t()}catch(n){if(e=n instanceof AEMOperationError?n:handleAEMHttpError(n,r),!e.recoverable||E===s)break;const a=e.retryAfter||Math.pow(2,E)*1e3;i.warn(`[${r}] Attempt ${E} failed, retrying in ${a}ms:`,e.message),await new Promise(o=>setTimeout(o,a))}throw e}export function createSuccessResponse(t,r){return{success:!0,operation:r,timestamp:new Date().toISOString(),data:t}}export function createErrorResponse(t,r){return{success:!1,operation:r,timestamp:new Date().toISOString(),error:{code:t.code,message:t.message,details:t.details,recoverable:t.recoverable,retryAfter:t.retryAfter}}}
1
+ "use strict";import{LOGGER as E}from"../utils/logger.js";export class AEMOperationError extends Error{code;details;recoverable;retryAfter;constructor(s){super(s.message),this.name="AEMOperationError",this.code=s.code,this.details=s.details,this.recoverable=s.recoverable,this.retryAfter=s.retryAfter}}export const AEM_ERROR_CODES={CONNECTION_FAILED:"CONNECTION_FAILED",TIMEOUT:"TIMEOUT",AUTHENTICATION_FAILED:"AUTHENTICATION_FAILED",UNAUTHORIZED:"UNAUTHORIZED",INVALID_PATH:"INVALID_PATH",INVALID_COMPONENT_TYPE:"INVALID_COMPONENT_TYPE",INVALID_LOCALE:"INVALID_LOCALE",INVALID_PARAMETERS:"INVALID_PARAMETERS",RESOURCE_NOT_FOUND:"RESOURCE_NOT_FOUND",COMPONENT_NOT_FOUND:"COMPONENT_NOT_FOUND",PAGE_NOT_FOUND:"PAGE_NOT_FOUND",UPDATE_FAILED:"UPDATE_FAILED",VALIDATION_FAILED:"VALIDATION_FAILED",REPLICATION_FAILED:"REPLICATION_FAILED",QUERY_FAILED:"QUERY_FAILED",INSUFFICIENT_PERMISSIONS:"INSUFFICIENT_PERMISSIONS",SYSTEM_ERROR:"SYSTEM_ERROR",RATE_LIMITED:"RATE_LIMITED"};export function createAEMError(e,s,r,t=!1,n){return new AEMOperationError({code:e,message:s,details:r,recoverable:t,retryAfter:n})}export function handleAEMHttpError(e,s){if(e.response){const r=e.response.status,t=e.response.data;switch(r){case 401:return createAEMError(AEM_ERROR_CODES.AUTHENTICATION_FAILED,"Authentication failed. Check AEM credentials.",{status:r,data:t});case 403:return createAEMError(AEM_ERROR_CODES.INSUFFICIENT_PERMISSIONS,"Insufficient permissions for this operation.",{status:r,data:t,operation:s});case 404:return createAEMError(AEM_ERROR_CODES.RESOURCE_NOT_FOUND,"Resource not found in AEM.",{status:r,data:t,operation:s});case 429:const n=e.response.headers["retry-after"];return createAEMError(AEM_ERROR_CODES.RATE_LIMITED,"Rate limit exceeded. Please try again later.",{status:r,data:t},!0,n?parseInt(n)*1e3:6e4);case 500:case 502:case 503:return createAEMError(AEM_ERROR_CODES.SYSTEM_ERROR,"AEM system error. Please try again later.",{status:r,data:t},!0,3e4);default:let a="Unknown error";if(typeof t=="string"&&t.trim().length>0)try{const o=JSON.parse(t);a=o.message||JSON.stringify(o)}catch{a=t}else t&&typeof t=="object"&&t.message?a=t.message:t&&typeof t=="object"&&(a=JSON.stringify(t));return createAEMError(AEM_ERROR_CODES.SYSTEM_ERROR,`HTTP ${r}: ${a}`,{status:r,data:t,operation:s})}}else return e.code==="ECONNREFUSED"||e.code==="ENOTFOUND"?createAEMError(AEM_ERROR_CODES.CONNECTION_FAILED,"Cannot connect to AEM instance. Check host and network.",{originalError:e.message},!0,5e3):e.code==="ETIMEDOUT"?createAEMError(AEM_ERROR_CODES.TIMEOUT,"Request to AEM timed out.",{originalError:e.message},!0,1e4):createAEMError(AEM_ERROR_CODES.SYSTEM_ERROR,`Unexpected error during ${s}: ${e.message}`,{originalError:e.message})}export async function safeExecute(e,s,r=3){let t;for(let n=1;n<=r;n++)try{return await e()}catch(a){if(t=a instanceof AEMOperationError?a:handleAEMHttpError(a,s),!t.recoverable||n===r)break;const o=t.retryAfter||Math.pow(2,n)*1e3;E.warn(`[${s}] Attempt ${n} failed, retrying in ${o}ms:`,t.message),await new Promise(i=>setTimeout(i,o))}throw t}const c={COMPONENT_NOT_FOUND:{suggestion:"Check the component resourceType spelling. Use getComponents to list all available components.",alternatives:["scanPageComponents to find components on a specific page","searchContent to search by partial name"]},PAGE_NOT_FOUND:{suggestion:"Verify the page path exists. Use listPages or enhancedPageSearch to find pages.",alternatives:["listPages to list children of a parent path","enhancedPageSearch for fulltext page search"]},RESOURCE_NOT_FOUND:{suggestion:"The path may not exist or may have a different structure. Use getNodeContent with depth:1 to explore.",alternatives:["listChildren to see what exists at the parent path"]},AUTHENTICATION_FAILED:{suggestion:"Check credentials. For Basic Auth: verify username/password. For OAuth: verify client ID/secret and IMS endpoint.",alternatives:[]},INVALID_PATH:{suggestion:"Paths must start with /content, /content/dam, /conf, or /content/experience-fragments.",alternatives:["fetchSites to discover valid site roots"]},INVALID_COMPONENT_TYPE:{suggestion:"The resourceType may be incorrect. Use getComponents to list valid component types.",alternatives:["scanPageComponents to see what components exist on a page"]},TIMEOUT:{suggestion:"AEM may be under load or restarting. Retry after a few seconds.",alternatives:[]},CONNECTION_FAILED:{suggestion:"AEM instance may not be running. Check the configured URL and port.",alternatives:[]}};export function createSuccessResponse(e,s){return{success:!0,operation:s,timestamp:new Date().toISOString(),data:e}}export function createErrorResponse(e,s){const r=c[e.code]||{};return{success:!1,operation:s,timestamp:new Date().toISOString(),error:{code:e.code,message:e.message,details:e.details,recoverable:e.recoverable,retryAfter:e.retryAfter,...r.suggestion&&{suggestion:r.suggestion},...r.alternatives?.length&&{alternatives:r.alternatives}}}}
@@ -0,0 +1 @@
1
+ "use strict";const o=new Set(["jcr:created","jcr:createdBy","jcr:lastModified","jcr:lastModifiedBy","jcr:uuid","jcr:baseVersion","jcr:predecessors","jcr:versionHistory","jcr:isCheckedOut","jcr:mixinTypes","cq:lastRolledout","cq:lastRolledoutBy","cq:lastReplicatedBy","cq:lastReplicationAction","cq:lastModified","cq:lastModifiedBy","rep:policy"]),l=new Set(["jcr:primaryType","jcr:title","jcr:description"]),a=new Set(["jcr:primaryType","sling:resourceType","jcr:title","cq:template"]);export function filterProperties(i,c="standard"){if(c==="full")return i;const t={};for(const[r,e]of Object.entries(i))l.has(r)?t[r]=e:c==="summary"?a.has(r)&&(t[r]=e):o.has(r)||(t[r]=typeof e=="string"&&e.length>500?e.substring(0,500)+"...[truncated, use getNodeContent for full]":e);return t}export function filterNodeTree(i,c="standard",t=0,r=3){if(c==="full")return i;const e=filterProperties(i,c);for(const[s,n]of Object.entries(e))n&&typeof n=="object"&&!Array.isArray(n)&&t<r&&(e[s]=filterNodeTree(n,c,t+1,r));return e}
@@ -1 +1 @@
1
- "use strict";import{AEMConnector as i}from"../aem/aem.connector.js";import{handleAEMHttpError as c}from"../aem/aem.errors.js";import{toolSchemas as s}from"./mcp.tools.js";import{LOGGER as a}from"../utils/logger.js";export class MCPRequestHandler{aemConnector;config;constructor(n){this.config=n,this.aemConnector=new i(n)}async init(){this.aemConnector.isInitialized?a.log("AEM Connector already initialized."):(await this.aemConnector.init(),a.log("AEM Connector initialized."))}async handleRequest(n,e){const o=s[n];if(o){const t=o.safeParse(e);if(!t.success)throw new Error(`Invalid input for ${n}: ${t.error.issues.map(r=>`${r.path.join(".")}: ${r.message}`).join(", ")}`);e=t.data}try{await this.init()}catch(t){throw a.error("ERROR initializing MCP Server",t.message),c(t,"MCP Server Initialization")}try{switch(n){case"updateComponent":return await this.aemConnector.updateComponent(e);case"scanPageComponents":return await this.aemConnector.scanPageComponents(e.pagePath);case"fetchSites":return await this.aemConnector.fetchSites();case"fetchLanguageMasters":return await this.aemConnector.fetchLanguageMasters(e.site);case"fetchAvailableLocales":return await this.aemConnector.fetchAvailableLocales(e.site);case"getAllTextContent":return await this.aemConnector.getAllTextContent(e.pagePath);case"getPageTextContent":return await this.aemConnector.getPageTextContent(e.pagePath);case"getPageImages":return await this.aemConnector.getPageImages(e.pagePath);case"updateImagePath":return await this.aemConnector.updateImagePath(e.componentPath,e.newImagePath);case"getPageContent":return await this.aemConnector.getPageContent(e.pagePath);case"listPages":return await this.aemConnector.listPages(e.siteRoot||e.path||"/content",e.depth||1,e.limit||20);case"getNodeContent":return await this.aemConnector.getNodeContent(e.path,e.depth||1);case"listChildren":return await this.aemConnector.listChildren(e.path);case"getPageProperties":return await this.aemConnector.getPageProperties(e.pagePath);case"searchContent":return await this.aemConnector.searchContent(e);case"executeJCRQuery":return await this.aemConnector.executeJCRQuery(e.query,e.limit);case"getAssetMetadata":return await this.aemConnector.getAssetMetadata(e.assetPath);case"enhancedPageSearch":return await this.aemConnector.searchContent({fulltext:e.searchTerm,path:e.basePath,type:"cq:Page",limit:20});case"createPage":return await this.aemConnector.createPage(e);case"deletePage":return await this.aemConnector.deletePage(e);case"createComponent":return await this.aemConnector.createComponent(e);case"addComponent":return await this.aemConnector.addComponent(e);case"deleteComponent":return await this.aemConnector.deleteComponent(e);case"unpublishContent":return await this.aemConnector.unpublishContent(e);case"activatePage":return await this.aemConnector.activatePage(e);case"deactivatePage":return await this.aemConnector.deactivatePage(e);case"updateAsset":return await this.aemConnector.updateAsset(e);case"deleteAsset":return await this.aemConnector.deleteAsset(e);case"getTemplates":return await this.aemConnector.getTemplates(e.sitePath);case"getTemplateStructure":return await this.aemConnector.getTemplateStructure(e.templatePath);case"getComponents":return await this.aemConnector.getComponents(e.path);case"bulkUpdateComponents":return await this.aemConnector.bulkUpdateComponents(e);case"convertComponents":return await this.aemConnector.convertComponents(e);case"bulkConvertComponents":return await this.aemConnector.bulkConvertComponents(e);case"listWorkflowModels":return await this.aemConnector.listWorkflowModels();case"startWorkflow":return await this.aemConnector.startWorkflow(e.modelId,e.payload,e.payloadType);case"listWorkflowInstances":return await this.aemConnector.listWorkflowInstances(e.state);case"getWorkflowInstance":return await this.aemConnector.getWorkflowInstance(e.instanceId);case"updateWorkflowInstanceState":return await this.aemConnector.updateWorkflowInstanceState(e.instanceId,e.state);case"getInboxItems":return await this.aemConnector.getInboxItems();case"completeWorkItem":return await this.aemConnector.completeWorkItem(e.workItemPath,e.routeId,e.comment);case"delegateWorkItem":return await this.aemConnector.delegateWorkItem(e.workItemPath,e.delegatee);case"getWorkItemRoutes":return await this.aemConnector.getWorkItemRoutes(e.workItemPath);case"getContentFragment":return await this.aemConnector.getContentFragment(e.path);case"listContentFragments":return await this.aemConnector.listContentFragments(e);case"manageContentFragment":return await this.aemConnector.manageContentFragment(e);case"manageContentFragmentVariation":return await this.aemConnector.manageContentFragmentVariation(e);case"getExperienceFragment":return await this.aemConnector.getExperienceFragment(e.path);case"listExperienceFragments":return await this.aemConnector.listExperienceFragments(e);case"manageExperienceFragment":return await this.aemConnector.manageExperienceFragment(e);case"manageExperienceFragmentVariation":return await this.aemConnector.manageExperienceFragmentVariation(e);default:throw new Error(`Unknown method: ${n}`)}}catch(t){throw a.error(`Error in tool ${n}:`,t.message),t}}}
1
+ "use strict";import{AEMConnector as i}from"../aem/aem.connector.js";import{handleAEMHttpError as c}from"../aem/aem.errors.js";import{toolSchemas as s}from"./mcp.tools.js";import{LOGGER as a}from"../utils/logger.js";export class MCPRequestHandler{aemConnector;config;constructor(n){this.config=n,this.aemConnector=new i(n)}async init(){this.aemConnector.isInitialized?a.log("AEM Connector already initialized."):(await this.aemConnector.init(),a.log("AEM Connector initialized."))}async handleRequest(n,e){const o=s[n];if(o){const t=o.safeParse(e);if(!t.success)throw new Error(`Invalid input for ${n}: ${t.error.issues.map(r=>`${r.path.join(".")}: ${r.message}`).join(", ")}`);e=t.data}try{await this.init()}catch(t){throw a.error("ERROR initializing MCP Server",t.message),c(t,"MCP Server Initialization")}try{switch(n){case"updateComponent":return await this.aemConnector.updateComponent(e);case"scanPageComponents":return await this.aemConnector.scanPageComponents(e.pagePath,e.verbosity);case"fetchSites":return await this.aemConnector.fetchSites();case"fetchLanguageMasters":return await this.aemConnector.fetchLanguageMasters(e.site);case"fetchAvailableLocales":return await this.aemConnector.fetchAvailableLocales(e.site);case"getAllTextContent":return await this.aemConnector.getAllTextContent(e.pagePath);case"getPageTextContent":return await this.aemConnector.getPageTextContent(e.pagePath);case"getPageImages":return await this.aemConnector.getPageImages(e.pagePath);case"updateImagePath":return await this.aemConnector.updateImagePath(e.componentPath,e.newImagePath);case"getPageContent":return await this.aemConnector.getPageContent(e.pagePath,e.verbosity);case"listPages":return await this.aemConnector.listPages(e.siteRoot||e.path||"/content",e.depth||1,e.limit||20);case"getNodeContent":return await this.aemConnector.getNodeContent(e.path,e.depth||1,e.verbosity);case"listChildren":return await this.aemConnector.listChildren(e.path);case"getPageProperties":return await this.aemConnector.getPageProperties(e.pagePath);case"searchContent":return await this.aemConnector.searchContent(e);case"executeJCRQuery":return await this.aemConnector.executeJCRQuery(e.query,e.limit);case"getAssetMetadata":return await this.aemConnector.getAssetMetadata(e.assetPath);case"enhancedPageSearch":return await this.aemConnector.searchContent({fulltext:e.searchTerm,path:e.basePath,type:"cq:Page",limit:20});case"createPage":return await this.aemConnector.createPage(e);case"deletePage":return await this.aemConnector.deletePage(e);case"createComponent":return await this.aemConnector.createComponent(e);case"addComponent":return await this.aemConnector.addComponent(e);case"deleteComponent":return await this.aemConnector.deleteComponent(e);case"unpublishContent":return await this.aemConnector.unpublishContent(e);case"activatePage":return await this.aemConnector.activatePage(e);case"deactivatePage":return await this.aemConnector.deactivatePage(e);case"updateAsset":return await this.aemConnector.updateAsset(e);case"deleteAsset":return await this.aemConnector.deleteAsset(e);case"getTemplates":return await this.aemConnector.getTemplates(e.sitePath);case"getTemplateStructure":return await this.aemConnector.getTemplateStructure(e.templatePath);case"getComponents":return await this.aemConnector.getComponents(e.path);case"bulkUpdateComponents":return await this.aemConnector.bulkUpdateComponents(e);case"convertComponents":return await this.aemConnector.convertComponents(e);case"bulkConvertComponents":return await this.aemConnector.bulkConvertComponents(e);case"listWorkflowModels":return await this.aemConnector.listWorkflowModels();case"startWorkflow":return await this.aemConnector.startWorkflow(e.modelId,e.payload,e.payloadType);case"listWorkflowInstances":return await this.aemConnector.listWorkflowInstances(e.state);case"getWorkflowInstance":return await this.aemConnector.getWorkflowInstance(e.instanceId);case"updateWorkflowInstanceState":return await this.aemConnector.updateWorkflowInstanceState(e.instanceId,e.state);case"getInboxItems":return await this.aemConnector.getInboxItems();case"completeWorkItem":return await this.aemConnector.completeWorkItem(e.workItemPath,e.routeId,e.comment);case"delegateWorkItem":return await this.aemConnector.delegateWorkItem(e.workItemPath,e.delegatee);case"getWorkItemRoutes":return await this.aemConnector.getWorkItemRoutes(e.workItemPath);case"getContentFragment":return await this.aemConnector.getContentFragment(e.path);case"listContentFragments":return await this.aemConnector.listContentFragments(e);case"manageContentFragment":return await this.aemConnector.manageContentFragment(e);case"manageContentFragmentVariation":return await this.aemConnector.manageContentFragmentVariation(e);case"getExperienceFragment":return await this.aemConnector.getExperienceFragment(e.path);case"listExperienceFragments":return await this.aemConnector.listExperienceFragments(e);case"manageExperienceFragment":return await this.aemConnector.manageExperienceFragment(e);case"manageExperienceFragmentVariation":return await this.aemConnector.manageExperienceFragmentVariation(e);default:throw new Error(`Unknown method: ${n}`)}}catch(t){throw a.error(`Error in tool ${n}:`,t.message),t}}}
@@ -1 +1 @@
1
- "use strict";import{MCPRequestHandler as h}from"./mcp.aem-handler.js";import{LOGGER as c}from"../utils/logger.js";export class InstanceRegistry{handlers=new Map;defaultInstance;constructor(e){const a=this.parseInstances(e);for(const s of a){const n={host:s.host,user:s.user,pass:s.pass,id:s.id,secret:s.secret};this.handlers.set(s.name,new h(n)),c.log(`Registered AEM instance: "${s.name}" \u2192 ${s.host}`)}this.defaultInstance=a[0].name,c.log(`Default instance: "${this.defaultInstance}"`)}parseInstances(e){if(e.instances){const a=e.instances.split(",").map(n=>n.trim()).filter(Boolean),s=[];for(const n of a){const t=n.split(":");if(t.length<4){c.error(`Invalid instance format: "${n}" \u2014 expected "name:host:user:pass" or "name:https://host:user:pass"`);continue}let i,r,o,l;t[1]?.startsWith("//")||t[2]?.startsWith("//")?(i=t[0],r=t[1]+":"+t[2],o=t[3],l=t.slice(4).join(":")):(i=t[0],r=t[1],o=t[2],l=t.slice(3).join(":"),r.startsWith("http")||(r=`http://${r}`)),s.push({name:i,host:r,user:o||"admin",pass:l||"admin"})}if(s.length===0)throw new Error("No valid instances parsed from --instances flag");return s}return[{name:"default",host:e.host||"http://localhost:4502",user:e.user||"admin",pass:e.pass||"admin",id:e.id,secret:e.secret}]}getHandler(e){const a=e||this.defaultInstance,s=this.handlers.get(a);if(!s){const n=Array.from(this.handlers.keys()).join(", ");throw new Error(`Unknown AEM instance: "${a}". Available: ${n}`)}return s}getDefaultName(){return this.defaultInstance}getInstanceNames(){return Array.from(this.handlers.keys())}}
1
+ "use strict";import{MCPRequestHandler as d}from"./mcp.aem-handler.js";import{LOGGER as h}from"../utils/logger.js";export class InstanceRegistry{handlers=new Map;defaultInstance;constructor(e){const a=this.parseInstances(e);for(const t of a){const n={host:t.host,user:t.user,pass:t.pass,id:t.id,secret:t.secret};this.handlers.set(t.name,new d(n)),h.log(`Registered AEM instance: "${t.name}" \u2192 ${t.host}`)}this.defaultInstance=a[0].name,h.log(`Default instance: "${this.defaultInstance}"`)}parseInstances(e){if(e.instances){const a=e.instances.split(",").map(n=>n.trim()).filter(Boolean),t=[];for(const n of a){const s=n.split(":");if(s.length<4){h.error(`Invalid instance format: "${n}" \u2014 expected "name:host:user:pass" or "name:https://host:user:pass"`);continue}let o,r,l,c;if(s[1]?.startsWith("//")||s[2]?.startsWith("//")){o=s[0],r=s[1]+":"+s[2];let i=3;s[i]&&/^\d+$/.test(s[i])&&(r+=":"+s[i],i++),l=s[i]||"admin",c=s.slice(i+1).join(":")||"admin"}else o=s[0],r=s[1],l=s[2],c=s.slice(3).join(":"),r.startsWith("http")||(r=`http://${r}`);t.push({name:o,host:r,user:l||"admin",pass:c||"admin"})}if(t.length===0)throw new Error("No valid instances parsed from --instances flag");return t}return[{name:"default",host:e.host||"http://localhost:4502",user:e.user||"admin",pass:e.pass||"admin",id:e.id,secret:e.secret}]}getHandler(e){const a=e||this.defaultInstance,t=this.handlers.get(a);if(!t){const n=Array.from(this.handlers.keys()).join(", ");throw new Error(`Unknown AEM instance: "${a}". Available: ${n}`)}return t}getDefaultName(){return this.defaultInstance}getInstanceNames(){return Array.from(this.handlers.keys())}}
@@ -0,0 +1 @@
1
+ "use strict";import{LOGGER as a}from"../utils/logger.js";const i=[{key:"components",name:"AEM Components",description:"All components (name, resourceType, title, group)"},{key:"sites",name:"AEM Sites",description:"Site roots and language structure under /content"},{key:"templates",name:"AEM Templates",description:"Available page templates (path, title)"},{key:"workflow-models",name:"AEM Workflow Models",description:"Workflow models (ID, title, description)"}];export function getResourceDefinitions(n){return i.map(o=>({uri:`aem://${n}/${o.key}`,name:`${o.name} [${n}]`,description:o.description,mimeType:"application/json"}))}function m(n,o){const s=n?.data??n;switch(o){case"components":{const t=s?.components??[];return{totalCount:t.length,components:t.map(e=>({name:e.name,title:e.title,resourceType:e.resourceType,group:e.componentGroup||e.group}))}}case"sites":{const t=s?.sites??[];return{totalCount:t.length,sites:t.map(e=>({name:e.name,path:e.path,title:e.title}))}}case"templates":{const t=s?.templates??[];return{totalCount:t.length,templates:t.map(e=>({name:e.name,path:e.path,title:e.title}))}}case"workflow-models":{const t=s?.models??s?.commonWorkflows??[];return{totalCount:t.length,models:t.map(e=>({modelId:e.modelId,uri:e.uri,description:e.description}))}}default:return s}}export async function readResource(n,o){const s=n.match(/^aem:\/\/([^/]+)\/(.+)$/);if(!s)return{uri:n,mimeType:"application/json",text:JSON.stringify({error:`Invalid resource URI: ${n}`})};const[,,t]=s;try{let e;switch(t){case"components":e=await o.getComponents();break;case"sites":e=await o.fetchSites();break;case"templates":e=await o.getTemplates();break;case"workflow-models":e=await o.listWorkflowModels();break;default:return{uri:n,mimeType:"application/json",text:JSON.stringify({error:`Unknown resource: ${t}`})}}const r=m(e,t);return{uri:n,mimeType:"application/json",text:JSON.stringify(r,null,2)}}catch(e){return a.error(`Resource read failed for ${n}:`,e.message),{uri:n,mimeType:"application/json",text:JSON.stringify({error:e.message,uri:n})}}}
@@ -1 +1 @@
1
- "use strict";import{CallToolRequestSchema as d,ListToolsRequestSchema as f,InitializeRequestSchema as y,LATEST_PROTOCOL_VERSION as E,SUPPORTED_PROTOCOL_VERSIONS as O}from"@modelcontextprotocol/sdk/types.js";import{Server as S}from"@modelcontextprotocol/sdk/server/index.js";import{tools as p,injectInstanceParam as U}from"./mcp.tools.js";import{InstanceRegistry as v}from"./mcp.instances.js";import{LOGGER as c}from"../utils/logger.js";export const createMCPServer=g=>{const n=new v(g),s=n.getInstanceNames(),l=s.length>1,m={name:"aem-mcp-server",version:"1.3.9"},h={capabilities:{resources:{},tools:{}},instructions:l?`AEM MCP server with ${s.length} instances: ${s.join(", ")}. Use the "instance" parameter to target a specific instance (default: "${n.getDefaultName()}").`:"This is an AEM MCP server that provides tools for managing AEM components and content."},a=new S(m,h);return a.setRequestHandler(y,e=>{const i=e.params.protocolVersion,o=O.includes(i)?i:E;return c.log("1. Received InitializeRequest",e,"response:",{protocolVersion:o}),{protocolVersion:o,...h,serverInfo:m}}),a.setRequestHandler(f,async()=>{const e=l?U(p,s,n.getDefaultName()):p;return c.log("2. Received ListToolsRequest",e),{tools:e}}),a.setRequestHandler(d,async e=>{const{name:i,arguments:o}=e.params;if(c.log("3. Received CallToolRequestSchema",e.params),!o)return{content:[{type:"text",text:"Error: No arguments provided"}],isError:!0};try{const{instance:t,...u}=o,r=await n.getHandler(t).handleRequest(i,u);if(r&&typeof r=="object"&&"error"in r&&r.error?.code==="OAUTH_REQUIRED"){const R={error:"OAuth authentication required",message:r.error.message,authUrl:r.error.authUrl,redirectUri:r.error.redirectUri,instructions:"Please open the authUrl in your browser to authorize the application. After authorization, you can retry this operation."};return{content:[{type:"text",text:JSON.stringify(R,null,2)}],isError:!0}}return{content:[{type:"text",text:JSON.stringify(r,null,2)}]}}catch(t){if(c.error("ERROR CallToolRequestSchema",t.message),t.code==="OAUTH_REQUIRED"&&t.authUrl){const u={error:"OAuth authentication required",message:t.message,authUrl:t.authUrl,instructions:"Please open the authUrl in your browser to authorize the application."};return{content:[{type:"text",text:JSON.stringify(u,null,2)}],isError:!0}}return{content:[{type:"text",text:`Error: ${t.message}`}],isError:!0}}}),a};
1
+ "use strict";import{CallToolRequestSchema as y,ListToolsRequestSchema as E,ListResourcesRequestSchema as q,ReadResourceRequestSchema as O,InitializeRequestSchema as S,LATEST_PROTOCOL_VERSION as v,SUPPORTED_PROTOCOL_VERSIONS as U}from"@modelcontextprotocol/sdk/types.js";import{Server as T}from"@modelcontextprotocol/sdk/server/index.js";import{tools as p,injectInstanceParam as I,toolAnnotations as d}from"./mcp.tools.js";import{getResourceDefinitions as x,readResource as P}from"./mcp.resources.js";import{InstanceRegistry as A}from"./mcp.instances.js";import{LOGGER as a}from"../utils/logger.js";export const createMCPServer=g=>{const i=new A(g),c=i.getInstanceNames(),l=c.length>1,m={name:"aem-mcp-server",version:"1.3.9"},R={capabilities:{resources:{},tools:{}},instructions:l?`AEM MCP server with ${c.length} instances: ${c.join(", ")}. Use the "instance" parameter to target a specific instance (default: "${i.getDefaultName()}").`:"This is an AEM MCP server that provides tools for managing AEM components and content."},s=new T(m,R);return s.setRequestHandler(S,t=>{const e=t.params.protocolVersion,o=U.includes(e)?e:v;return a.log("1. Received InitializeRequest",t,"response:",{protocolVersion:o}),{protocolVersion:o,...R,serverInfo:m}}),s.setRequestHandler(E,async()=>{const t=l?I(p,c,i.getDefaultName()):p;return a.log("2. Received ListToolsRequest",t),{tools:t.map(e=>({...e,...d[e.name]&&{annotations:d[e.name]}}))}}),s.setRequestHandler(q,async()=>{const t=c.flatMap(e=>x(e));return a.log("Received ListResourcesRequest",t),{resources:t}}),s.setRequestHandler(O,async t=>{const{uri:e}=t.params;a.log("Received ReadResourceRequest",e);const r=e.match(/^aem:\/\/([^/]+)\//)?.[1],h=i.getHandler(r).aemConnector;return{contents:[await P(e,h)]}}),s.setRequestHandler(y,async t=>{const{name:e,arguments:o}=t.params;if(a.log("3. Received CallToolRequestSchema",t.params),!o)return{content:[{type:"text",text:"Error: No arguments provided"}],isError:!0};try{const{instance:r,...u}=o,n=await i.getHandler(r).handleRequest(e,u);if(n&&typeof n=="object"&&"error"in n&&n.error?.code==="OAUTH_REQUIRED"){const f={error:"OAuth authentication required",message:n.error.message,authUrl:n.error.authUrl,redirectUri:n.error.redirectUri,instructions:"Please open the authUrl in your browser to authorize the application. After authorization, you can retry this operation."};return{content:[{type:"text",text:JSON.stringify(f,null,2)}],isError:!0}}return{content:[{type:"text",text:JSON.stringify(n,null,2)}]}}catch(r){if(a.error("ERROR CallToolRequestSchema",r.message),r.code==="OAUTH_REQUIRED"&&r.authUrl){const u={error:"OAuth authentication required",message:r.message,authUrl:r.authUrl,instructions:"Please open the authUrl in your browser to authorize the application."};return{content:[{type:"text",text:JSON.stringify(u,null,2)}],isError:!0}}return{content:[{type:"text",text:`Error: ${r.message}`}],isError:!0}}}),s};
@@ -1 +1 @@
1
- "use strict";import{z as e}from"zod";import{zodToJsonSchema as i}from"zod-to-json-schema";const s={getAllTextContent:e.object({pagePath:e.string().describe("Path to the page")}).passthrough(),getPageTextContent:e.object({pagePath:e.string().describe("Path to the page")}).passthrough(),getPageImages:e.object({pagePath:e.string().describe("Path to the page")}).passthrough(),updateImagePath:e.object({componentPath:e.string().describe("Path to the image component"),newImagePath:e.string().describe("New image path")}).passthrough(),getPageContent:e.object({pagePath:e.string().describe("Path to the page")}).passthrough(),getNodeContent:e.object({path:e.string().describe("JCR node path"),depth:e.number().optional().describe("Depth to traverse")}).passthrough(),listChildren:e.object({path:e.string().describe("Parent node path")}).passthrough(),getPageProperties:e.object({pagePath:e.string().describe("Path to the page")}).passthrough()},c={fetchSites:e.object({}).passthrough(),fetchLanguageMasters:e.object({site:e.string().describe("Site name")}).passthrough(),fetchAvailableLocales:e.object({site:e.string().describe("Site name")}).passthrough()},p={listPages:e.object({siteRoot:e.string().optional().describe("Site root path"),depth:e.number().optional().describe("Depth to traverse"),limit:e.number().optional().describe("Maximum number of results")}).passthrough(),createPage:e.object({parentPath:e.string().describe("Parent path"),title:e.string().describe("Page title"),template:e.string().describe("Template path"),resourceType:e.string().optional().describe("Optional: Will be extracted from template if not provided"),name:e.string().optional().describe("Page name"),properties:e.record(e.unknown()).optional().describe("Additional properties")}).passthrough(),deletePage:e.object({pagePath:e.string().describe("Path to the page to delete"),force:e.boolean().optional().describe("Force deletion")}).passthrough(),activatePage:e.object({pagePath:e.string().describe("Path to the page"),activateTree:e.boolean().optional().describe("Activate entire tree")}).passthrough(),deactivatePage:e.object({pagePath:e.string().describe("Path to the page"),deactivateTree:e.boolean().optional().describe("Deactivate entire tree")}).passthrough(),unpublishContent:e.object({contentPaths:e.array(e.string()).describe("Paths to unpublish"),unpublishTree:e.boolean().optional().describe("Unpublish tree")}).passthrough()},l={updateComponent:e.object({componentPath:e.string().describe("Path to the component"),properties:e.record(e.unknown()).describe("Properties to update")}).passthrough(),scanPageComponents:e.object({pagePath:e.string().describe("Path to the page to scan")}).passthrough(),createComponent:e.object({pagePath:e.string().describe("Path to the page"),componentType:e.string().describe("Component type"),resourceType:e.string().describe("Resource type"),properties:e.record(e.unknown()).optional().describe("Component properties"),name:e.string().optional().describe("Component name")}).passthrough(),addComponent:e.object({pagePath:e.string().describe("Path to the existing page (e.g., /content/site/en/page)"),resourceType:e.string().describe("Sling resource type of the component (required)"),containerPath:e.string().optional().describe("Optional: specific container path (defaults to root/container)"),name:e.string().optional().describe("Optional: component node name (auto-generated if not provided)"),properties:e.record(e.unknown()).optional().describe("Optional: component properties to set")}).passthrough(),deleteComponent:e.object({componentPath:e.string().describe("Path to the component to delete"),force:e.boolean().optional().describe("Force deletion")}).passthrough(),getComponents:e.object({path:e.string().optional().describe("Optional: Component root path to fetch components from. If not provided, uses the configured default path.")}).passthrough(),bulkUpdateComponents:e.object({updates:e.array(e.object({componentPath:e.string(),properties:e.record(e.unknown())})).describe("Array of component updates"),validateFirst:e.boolean().optional().describe("Validate before updating"),continueOnError:e.boolean().optional().describe("Continue on individual failures")}).passthrough(),convertComponents:e.object({pagePath:e.string().describe("Path to the page containing components to convert"),sourceResourceType:e.string().describe("The resource type to search for and convert"),targetResourceType:e.string().describe("The resource type to convert to"),requiredProperties:e.record(e.unknown()).optional().describe("Optional: Required property values for the target component"),continueOnError:e.boolean().optional().describe("Optional: Continue converting even if some fail (default: true)")}).passthrough(),bulkConvertComponents:e.object({pagePaths:e.array(e.string()).optional().describe("Array of page paths to process"),searchPath:e.string().optional().describe("Optional: Base path to search for pages"),depth:e.number().optional().describe("Optional: Depth to search when using searchPath (default: 2)"),limit:e.number().optional().describe("Optional: Maximum number of pages to process (default: 50)"),sourceResourceType:e.string().describe("The resource type to search for and convert"),targetResourceType:e.string().describe("The resource type to convert to"),requiredProperties:e.record(e.unknown()).optional().describe("Optional: Required property values for the target component"),continueOnError:e.boolean().optional().describe("Optional: Continue processing pages even if some fail (default: true)")}).passthrough()},d={getAssetMetadata:e.object({assetPath:e.string().describe("Path to the asset")}).passthrough(),updateAsset:e.object({assetPath:e.string().describe("Path to the asset"),metadata:e.record(e.unknown()).optional().describe("Metadata to update"),fileContent:e.string().optional().describe("File content"),mimeType:e.string().optional().describe("MIME type")}).passthrough(),deleteAsset:e.object({assetPath:e.string().describe("Path to the asset to delete"),force:e.boolean().optional().describe("Force deletion")}).passthrough()},g={searchContent:e.object({type:e.string().optional().describe("Content type"),fulltext:e.string().optional().describe("Fulltext search term"),path:e.string().optional().describe("Search path"),limit:e.number().optional().describe("Maximum number of results")}).passthrough(),executeJCRQuery:e.object({query:e.string().describe("JCR query"),limit:e.number().optional().describe("Maximum number of results")}).passthrough(),enhancedPageSearch:e.object({searchTerm:e.string().describe("Search term"),basePath:e.string().describe("Base path for search"),includeAlternateLocales:e.boolean().optional().describe("Include alternate locales")}).passthrough()},h={getTemplates:e.object({sitePath:e.string().optional().describe("Site path")}).passthrough(),getTemplateStructure:e.object({templatePath:e.string().describe("Template path")}).passthrough()},m={listWorkflowModels:e.object({}).passthrough(),startWorkflow:e.object({modelId:e.string().describe('Workflow model ID (e.g., "request_for_activation")'),payload:e.string().describe("JCR path or URL to process"),payloadType:e.string().optional().describe('Type of payload (default: "JCR_PATH")')}).passthrough(),listWorkflowInstances:e.object({state:e.string().optional().describe("Optional: Filter by state (RUNNING, SUSPENDED, ABORTED, COMPLETED)")}).passthrough(),getWorkflowInstance:e.object({instanceId:e.string().describe("Workflow instance ID or full path")}).passthrough(),updateWorkflowInstanceState:e.object({instanceId:e.string().describe("Workflow instance ID or full path"),state:e.enum(["RUNNING","SUSPENDED","ABORTED"]).describe("New state for the workflow instance")}).passthrough(),getInboxItems:e.object({}).passthrough(),completeWorkItem:e.object({workItemPath:e.string().describe("Path to the work item"),routeId:e.string().optional().describe("Optional: Route ID to advance to"),comment:e.string().optional().describe("Optional: Comment for the completion")}).passthrough(),delegateWorkItem:e.object({workItemPath:e.string().describe("Path to the work item"),delegatee:e.string().describe("User or group to delegate to")}).passthrough(),getWorkItemRoutes:e.object({workItemPath:e.string().describe("Path to the work item")}).passthrough()},u={getContentFragment:e.object({path:e.string().describe("Path to the content fragment in DAM (e.g., /content/dam/site/cf/my-article)")}).passthrough(),listContentFragments:e.object({path:e.string().describe("Parent path to search under (e.g., /content/dam/site/cf)"),model:e.string().optional().describe("Filter by CF model path"),limit:e.number().optional().describe("Max results (default: 20)"),offset:e.number().optional().describe("Pagination offset (default: 0)")}).passthrough(),manageContentFragment:e.object({action:e.enum(["create","update","delete"]).describe("Action to perform"),fragmentPath:e.string().optional().describe("Path to existing CF (required for update/delete)"),parentPath:e.string().optional().describe("Parent folder in DAM (required for create)"),title:e.string().optional().describe("Fragment title (required for create)"),name:e.string().optional().describe("Node name (auto-generated from title if omitted)"),model:e.string().optional().describe("CF model path (required for create)"),fields:e.record(e.unknown()).optional().describe("Field values as { fieldName: value }"),description:e.string().optional().describe("Fragment description"),force:e.boolean().optional().describe("Force delete even if referenced")}).passthrough(),manageContentFragmentVariation:e.object({action:e.enum(["create","update","delete"]).describe("Action to perform"),fragmentPath:e.string().describe("Path to the parent content fragment"),variationName:e.string().describe("Variation identifier"),title:e.string().optional().describe("Variation title (required for create)"),fields:e.record(e.unknown()).optional().describe("Field overrides as { fieldName: value }")}).passthrough()},b={getExperienceFragment:e.object({path:e.string().describe("Path to the experience fragment page")}).passthrough(),listExperienceFragments:e.object({path:e.string().optional().describe("Root path (default: /content/experience-fragments)"),template:e.string().optional().describe("Filter by template path"),limit:e.number().optional().describe("Max results (default: 20)"),offset:e.number().optional().describe("Pagination offset (default: 0)")}).passthrough(),manageExperienceFragment:e.object({action:e.enum(["create","update","delete"]).describe("Action to perform"),xfPath:e.string().optional().describe("Existing XF path (required for update/delete)"),parentPath:e.string().optional().describe("Parent path for new XF (required for create)"),name:e.string().optional().describe("Node name (auto-generated from title if omitted)"),title:e.string().optional().describe("XF title (required for create)"),template:e.string().optional().describe("XF template path (required for create)"),description:e.string().optional().describe("XF description"),tags:e.array(e.string()).optional().describe("Tags to apply"),force:e.boolean().optional().describe("Force delete even if referenced")}).passthrough(),manageExperienceFragmentVariation:e.object({action:e.enum(["create","update","delete"]).describe("Action to perform"),xfPath:e.string().describe("Parent experience fragment path"),variationName:e.string().describe("Variation identifier"),variationType:e.enum(["web","email","social","custom"]).optional().describe("Variation type (default: web)"),title:e.string().optional().describe("Variation title (required for create)"),template:e.string().optional().describe("Template for the variation"),force:e.boolean().optional().describe("Force deletion")}).passthrough()};export const toolSchemas={...s,...c,...p,...l,...d,...g,...h,...m,...u,...b},toolDescriptions={updateComponent:"Update component properties in AEM",scanPageComponents:"Scan a page to discover all components and their properties",fetchSites:"Get all available sites in AEM",fetchLanguageMasters:"Get language masters for a specific site",fetchAvailableLocales:"Get available locales for a site",getAllTextContent:"Get all text content from a page including titles, text components, and descriptions",getPageTextContent:"Get text content from a specific page",getPageImages:"Get all images from a page, including those within Experience Fragments",updateImagePath:"Update the image path for an image component and verify the update",getPageContent:"Get all content from a page including Experience Fragments and Content Fragments",listPages:"List all pages under a site root",getNodeContent:"Legacy: Get JCR node content",listChildren:"Legacy: List child nodes",getPageProperties:"Get page properties",searchContent:"Search content using Query Builder",executeJCRQuery:"Execute JCR query",getAssetMetadata:"Get asset metadata",enhancedPageSearch:"Intelligent page search with comprehensive fallback strategies and cross-section search",createPage:"Create a new page in AEM. The resourceType will be automatically extracted from the template structure if not provided.",deletePage:"Delete a page from AEM",createComponent:"Create a new component on a page",addComponent:"Add a component to an existing page. Automatically finds the appropriate container (root/container) and adds the component there.",deleteComponent:"Delete a component from AEM",unpublishContent:"Unpublish content from the publish environment",activatePage:"Activate (publish) a single page",deactivatePage:"Deactivate (unpublish) a single page",updateAsset:"Update an existing asset in AEM DAM",deleteAsset:"Delete an asset from AEM DAM",getTemplates:"Get available page templates",getTemplateStructure:"Get detailed structure of a specific template",getComponents:"Get all components from the configured component root path (projectRoot1) or a specified path. Shows component name, title, description, resource type, and other metadata.",bulkUpdateComponents:"Update multiple components in a single operation with validation and rollback support",convertComponents:"Find all components of a specific resource type on a page, delete them, and create new components of another type at the same location. Returns required properties if target component needs them.",bulkConvertComponents:"Convert components across multiple pages. Find all components of a specific resource type on multiple pages, delete them, and create new components of another type at the same location.",listWorkflowModels:"List all available workflow models in AEM. Returns common workflows like request_for_activation (publish), request_for_deactivation (unpublish), request_for_deletion (delete pages), and others.",startWorkflow:"Start a workflow instance. Common workflows: request_for_activation (publish pages), request_for_deactivation (unpublish pages), request_for_deletion (delete pages).",listWorkflowInstances:"List workflow instances, optionally filtered by state",getWorkflowInstance:"Get details of a specific workflow instance by ID",updateWorkflowInstanceState:"Update workflow instance state (RUNNING, SUSPENDED, ABORTED)",getInboxItems:"Get all work items in the inbox (work items assigned to current user)",completeWorkItem:"Complete or advance a work item to the next step in the workflow",delegateWorkItem:"Delegate a work item to another user or group",getWorkItemRoutes:"Get available routes for a work item (to see what steps are available)",getContentFragment:"Get a content fragment with all fields, variations, and metadata",listContentFragments:"List content fragments under a path with optional model filter",manageContentFragment:"Create, update, or delete a content fragment. Use action param to specify operation.",manageContentFragmentVariation:"Create, update, or delete a variation within a content fragment",getExperienceFragment:"Get an experience fragment with all variations, components, and metadata",listExperienceFragments:"List experience fragments under a path with optional template filter",manageExperienceFragment:"Create, update, or delete an experience fragment. Auto-creates master variation on create.",manageExperienceFragmentVariation:"Create, update, or delete a variation within an experience fragment"};function f(){return Object.keys(toolSchemas).map(t=>{const{$schema:o,...a}=i(toolSchemas[t],{target:"jsonSchema7"});return{name:t,description:toolDescriptions[t],inputSchema:a}})}export const tools=f();export function injectInstanceParam(t,o,a){return t.map(n=>{const r=n.inputSchema;return{...n,inputSchema:{...r,properties:{...r.properties||{},instance:{type:"string",description:`Target AEM instance. Available: ${o.join(", ")}. Default: "${a}"`,enum:o}}}}})}
1
+ "use strict";import{z as e}from"zod";import{zodToJsonSchema as s}from"zod-to-json-schema";const t=e.enum(["summary","standard","full"]).default("standard").optional().describe("Response detail level: summary (paths/names only), standard (default, minus JCR internals), full (everything)"),p={getAllTextContent:e.object({pagePath:e.string().describe("Path to the page")}).passthrough(),getPageTextContent:e.object({pagePath:e.string().describe("Path to the page")}).passthrough(),getPageImages:e.object({pagePath:e.string().describe("Path to the page")}).passthrough(),updateImagePath:e.object({componentPath:e.string().describe("Path to the image component"),newImagePath:e.string().describe("New image path")}).passthrough(),getPageContent:e.object({pagePath:e.string().describe("Path to the page"),verbosity:t}).passthrough(),getNodeContent:e.object({path:e.string().describe("JCR node path"),depth:e.number().optional().describe("Depth to traverse"),verbosity:t}).passthrough(),listChildren:e.object({path:e.string().describe("Parent node path"),verbosity:t}).passthrough(),getPageProperties:e.object({pagePath:e.string().describe("Path to the page")}).passthrough()},c={fetchSites:e.object({}).passthrough(),fetchLanguageMasters:e.object({site:e.string().describe("Site name")}).passthrough(),fetchAvailableLocales:e.object({site:e.string().describe("Site name")}).passthrough()},l={listPages:e.object({siteRoot:e.string().optional().describe("Site root path"),depth:e.number().optional().describe("Depth to traverse"),limit:e.number().optional().describe("Maximum number of results"),verbosity:t}).passthrough(),createPage:e.object({parentPath:e.string().describe("Parent path"),title:e.string().describe("Page title"),template:e.string().describe("Template path"),resourceType:e.string().optional().describe("Optional: Will be extracted from template if not provided"),name:e.string().optional().describe("Page name"),properties:e.record(e.unknown()).optional().describe("Additional properties")}).passthrough(),deletePage:e.object({pagePath:e.string().describe("Path to the page to delete"),force:e.boolean().optional().describe("Force deletion")}).passthrough(),activatePage:e.object({pagePath:e.string().describe("Path to the page"),activateTree:e.boolean().optional().describe("Activate entire tree")}).passthrough(),deactivatePage:e.object({pagePath:e.string().describe("Path to the page"),deactivateTree:e.boolean().optional().describe("Deactivate entire tree")}).passthrough(),unpublishContent:e.object({contentPaths:e.array(e.string()).describe("Paths to unpublish"),unpublishTree:e.boolean().optional().describe("Unpublish tree")}).passthrough()},d={updateComponent:e.object({componentPath:e.string().describe("Path to the component"),properties:e.record(e.unknown()).describe("Properties to update")}).passthrough(),scanPageComponents:e.object({pagePath:e.string().describe("Path to the page to scan"),verbosity:t}).passthrough(),createComponent:e.object({pagePath:e.string().describe("Path to the page"),componentType:e.string().describe("Component type"),resourceType:e.string().describe("Resource type"),properties:e.record(e.unknown()).optional().describe("Component properties"),name:e.string().optional().describe("Component name")}).passthrough(),addComponent:e.object({pagePath:e.string().describe("Path to the existing page (e.g., /content/site/en/page)"),resourceType:e.string().describe("Sling resource type of the component (required)"),containerPath:e.string().optional().describe("Optional: specific container path (defaults to root/container)"),name:e.string().optional().describe("Optional: component node name (auto-generated if not provided)"),properties:e.record(e.unknown()).optional().describe("Optional: component properties to set")}).passthrough(),deleteComponent:e.object({componentPath:e.string().describe("Path to the component to delete"),force:e.boolean().optional().describe("Force deletion")}).passthrough(),getComponents:e.object({path:e.string().optional().describe("Optional: Component root path to fetch components from. If not provided, uses the configured default path."),verbosity:t}).passthrough(),bulkUpdateComponents:e.object({updates:e.array(e.object({componentPath:e.string(),properties:e.record(e.unknown())})).describe("Array of component updates"),validateFirst:e.boolean().optional().describe("Validate before updating"),continueOnError:e.boolean().optional().describe("Continue on individual failures")}).passthrough(),convertComponents:e.object({pagePath:e.string().describe("Path to the page containing components to convert"),sourceResourceType:e.string().describe("The resource type to search for and convert"),targetResourceType:e.string().describe("The resource type to convert to"),requiredProperties:e.record(e.unknown()).optional().describe("Optional: Required property values for the target component"),continueOnError:e.boolean().optional().describe("Optional: Continue converting even if some fail (default: true)")}).passthrough(),bulkConvertComponents:e.object({pagePaths:e.array(e.string()).optional().describe("Array of page paths to process"),searchPath:e.string().optional().describe("Optional: Base path to search for pages"),depth:e.number().optional().describe("Optional: Depth to search when using searchPath (default: 2)"),limit:e.number().optional().describe("Optional: Maximum number of pages to process (default: 50)"),sourceResourceType:e.string().describe("The resource type to search for and convert"),targetResourceType:e.string().describe("The resource type to convert to"),requiredProperties:e.record(e.unknown()).optional().describe("Optional: Required property values for the target component"),continueOnError:e.boolean().optional().describe("Optional: Continue processing pages even if some fail (default: true)")}).passthrough()},g={getAssetMetadata:e.object({assetPath:e.string().describe("Path to the asset")}).passthrough(),updateAsset:e.object({assetPath:e.string().describe("Path to the asset"),metadata:e.record(e.unknown()).optional().describe("Metadata to update"),fileContent:e.string().optional().describe("File content"),mimeType:e.string().optional().describe("MIME type")}).passthrough(),deleteAsset:e.object({assetPath:e.string().describe("Path to the asset to delete"),force:e.boolean().optional().describe("Force deletion")}).passthrough()},m={searchContent:e.object({type:e.string().optional().describe("Content type"),fulltext:e.string().optional().describe("Fulltext search term"),path:e.string().optional().describe("Search path"),limit:e.number().optional().describe("Maximum number of results")}).passthrough(),executeJCRQuery:e.object({query:e.string().describe("JCR query"),limit:e.number().optional().describe("Maximum number of results")}).passthrough(),enhancedPageSearch:e.object({searchTerm:e.string().describe("Search term"),basePath:e.string().describe("Base path for search"),includeAlternateLocales:e.boolean().optional().describe("Include alternate locales")}).passthrough()},u={getTemplates:e.object({sitePath:e.string().optional().describe("Site path")}).passthrough(),getTemplateStructure:e.object({templatePath:e.string().describe("Template path")}).passthrough()},h={listWorkflowModels:e.object({}).passthrough(),startWorkflow:e.object({modelId:e.string().describe('Workflow model ID (e.g., "request_for_activation")'),payload:e.string().describe("JCR path or URL to process"),payloadType:e.string().optional().describe('Type of payload (default: "JCR_PATH")')}).passthrough(),listWorkflowInstances:e.object({state:e.string().optional().describe("Optional: Filter by state (RUNNING, SUSPENDED, ABORTED, COMPLETED)")}).passthrough(),getWorkflowInstance:e.object({instanceId:e.string().describe("Workflow instance ID or full path")}).passthrough(),updateWorkflowInstanceState:e.object({instanceId:e.string().describe("Workflow instance ID or full path"),state:e.enum(["RUNNING","SUSPENDED","ABORTED"]).describe("New state for the workflow instance")}).passthrough(),getInboxItems:e.object({}).passthrough(),completeWorkItem:e.object({workItemPath:e.string().describe("Path to the work item"),routeId:e.string().optional().describe("Optional: Route ID to advance to"),comment:e.string().optional().describe("Optional: Comment for the completion")}).passthrough(),delegateWorkItem:e.object({workItemPath:e.string().describe("Path to the work item"),delegatee:e.string().describe("User or group to delegate to")}).passthrough(),getWorkItemRoutes:e.object({workItemPath:e.string().describe("Path to the work item")}).passthrough()},b={getContentFragment:e.object({path:e.string().describe("Path to the content fragment in DAM (e.g., /content/dam/site/cf/my-article)")}).passthrough(),listContentFragments:e.object({path:e.string().describe("Parent path to search under (e.g., /content/dam/site/cf)"),model:e.string().optional().describe("Filter by CF model path"),limit:e.number().optional().describe("Max results (default: 20)"),offset:e.number().optional().describe("Pagination offset (default: 0)")}).passthrough(),manageContentFragment:e.object({action:e.enum(["create","update","delete"]).describe("Action to perform"),fragmentPath:e.string().optional().describe("Path to existing CF (required for update/delete)"),parentPath:e.string().optional().describe("Parent folder in DAM (required for create)"),title:e.string().optional().describe("Fragment title (required for create)"),name:e.string().optional().describe("Node name (auto-generated from title if omitted)"),model:e.string().optional().describe("CF model path (required for create)"),fields:e.record(e.unknown()).optional().describe("Field values as { fieldName: value }"),description:e.string().optional().describe("Fragment description"),force:e.boolean().optional().describe("Force delete even if referenced")}).passthrough(),manageContentFragmentVariation:e.object({action:e.enum(["create","update","delete"]).describe("Action to perform"),fragmentPath:e.string().describe("Path to the parent content fragment"),variationName:e.string().describe("Variation identifier"),title:e.string().optional().describe("Variation title (required for create)"),fields:e.record(e.unknown()).optional().describe("Field overrides as { fieldName: value }")}).passthrough()},f={getExperienceFragment:e.object({path:e.string().describe("Path to the experience fragment page")}).passthrough(),listExperienceFragments:e.object({path:e.string().optional().describe("Root path (default: /content/experience-fragments)"),template:e.string().optional().describe("Filter by template path"),limit:e.number().optional().describe("Max results (default: 20)"),offset:e.number().optional().describe("Pagination offset (default: 0)")}).passthrough(),manageExperienceFragment:e.object({action:e.enum(["create","update","delete"]).describe("Action to perform"),xfPath:e.string().optional().describe("Existing XF path (required for update/delete)"),parentPath:e.string().optional().describe("Parent path for new XF (required for create)"),name:e.string().optional().describe("Node name (auto-generated from title if omitted)"),title:e.string().optional().describe("XF title (required for create)"),template:e.string().optional().describe("XF template path (required for create)"),description:e.string().optional().describe("XF description"),tags:e.array(e.string()).optional().describe("Tags to apply"),force:e.boolean().optional().describe("Force delete even if referenced")}).passthrough(),manageExperienceFragmentVariation:e.object({action:e.enum(["create","update","delete"]).describe("Action to perform"),xfPath:e.string().describe("Parent experience fragment path"),variationName:e.string().describe("Variation identifier"),variationType:e.enum(["web","email","social","custom"]).optional().describe("Variation type (default: web)"),title:e.string().optional().describe("Variation title (required for create)"),template:e.string().optional().describe("Template for the variation"),force:e.boolean().optional().describe("Force deletion")}).passthrough()};export const toolSchemas={...p,...c,...l,...d,...g,...m,...u,...h,...b,...f},toolDescriptions={updateComponent:"Update component properties in AEM",scanPageComponents:"Scan a page to discover all components and their properties",fetchSites:"Get all top-level AEM site roots under /content. Returns site name, path, and language root structure.",fetchLanguageMasters:"Get language masters for a specific site",fetchAvailableLocales:"Get available locales for a site",getAllTextContent:"Get all text content from a page including titles, text components, and descriptions",getPageTextContent:"Get text content from a specific page",getPageImages:"Get all images from a page, including those within Experience Fragments",updateImagePath:"Update the image path for an image component and verify the update",getPageContent:"Get complete page content including resolved Experience Fragments and Content Fragments. Returns full content tree. For text-only extraction, use getPageTextContent. For raw JCR nodes, use getNodeContent.",listPages:"List child pages directly under a path (non-recursive, structural). For finding pages by content or name, use enhancedPageSearch instead.",getNodeContent:"Get raw JCR node properties at a specific path and depth. Low-level tool \u2014 use getPageContent for pages or scanPageComponents for component discovery.",listChildren:"Legacy: List child nodes",getPageProperties:"Get page properties",searchContent:"Structured content search with filters (type, property values, path scope, fulltext). More flexible than executeJCRQuery. Use for finding nodes by property values or content type.",executeJCRQuery:"Execute fulltext search on cq:Page nodes under /content. Uses QueryBuilder internally, NOT raw JCR-SQL2. For structured property-based queries, use searchContent instead.",getAssetMetadata:"Get DAM asset metadata including title, description, dimensions, format, tags, and custom properties. Path must be under /content/dam.",enhancedPageSearch:"Intelligent page search with comprehensive fallback strategies and cross-section search",createPage:"Create a new page in AEM. The resourceType will be automatically extracted from the template structure if not provided.",deletePage:"Delete a page from AEM",createComponent:"Create a component at a specific JCR path (you must know the exact container path). For automatic container detection and cq:template application, use addComponent instead.",addComponent:"Add a component to a page with automatic parsys/container detection and cq:template application. Preferred over createComponent for most use cases.",deleteComponent:"Delete a component from AEM",unpublishContent:"Unpublish content from the publish environment",activatePage:"Publish a page immediately via direct replication (synchronous). For approval-based publishing workflows, use startWorkflow with the request_for_activation model.",deactivatePage:"Deactivate (unpublish) a single page",updateAsset:"Update an existing asset in AEM DAM",deleteAsset:"Delete an asset from AEM DAM",getTemplates:"Get available page templates",getTemplateStructure:"Get detailed structure of a specific template",getComponents:"Get all components from the configured component root path (projectRoot1) or a specified path. Shows component name, title, description, resource type, and other metadata.",bulkUpdateComponents:"Update multiple components in a single operation with validation and rollback support",convertComponents:"Find all components of a specific resource type on a page, delete them, and create new components of another type at the same location. Returns required properties if target component needs them.",bulkConvertComponents:"Convert components across multiple pages. Find all components of a specific resource type on multiple pages, delete them, and create new components of another type at the same location.",listWorkflowModels:"List all available workflow models in AEM. Returns common workflows like request_for_activation (publish), request_for_deactivation (unpublish), request_for_deletion (delete pages), and others.",startWorkflow:"Start a workflow instance. Common workflows: request_for_activation (publish pages), request_for_deactivation (unpublish pages), request_for_deletion (delete pages).",listWorkflowInstances:"List workflow instances, optionally filtered by state",getWorkflowInstance:"Get details of a specific workflow instance by ID",updateWorkflowInstanceState:"Update workflow instance state (RUNNING, SUSPENDED, ABORTED)",getInboxItems:"Get all work items in the inbox (work items assigned to current user)",completeWorkItem:"Complete or advance a work item to the next step in the workflow",delegateWorkItem:"Delegate a work item to another user or group",getWorkItemRoutes:"Get available routes for a work item (to see what steps are available)",getContentFragment:"Get a content fragment with all fields, variations, and metadata",listContentFragments:"List content fragments under a path with optional model filter",manageContentFragment:"Create, update, or delete a content fragment. Use action param to specify operation.",manageContentFragmentVariation:"Create, update, or delete a variation within a content fragment",getExperienceFragment:"Get an experience fragment with all variations, components, and metadata",listExperienceFragments:"List experience fragments under a path with optional template filter",manageExperienceFragment:"Create, update, or delete an experience fragment. Auto-creates master variation on create.",manageExperienceFragmentVariation:"Create, update, or delete a variation within an experience fragment"};function y(){return Object.keys(toolSchemas).map(o=>{const{$schema:a,...r}=s(toolSchemas[o],{target:"jsonSchema7"});return{name:o,description:toolDescriptions[o],inputSchema:r}})}export const tools=y();export function injectInstanceParam(o,a,r){return o.map(n=>{const i=n.inputSchema;return{...n,inputSchema:{...i,properties:{...i.properties||{},instance:{type:"string",description:`Target AEM instance. Available: ${a.join(", ")}. Default: "${r}"`,enum:a}}}}})}export const toolAnnotations={getAllTextContent:{group:"content",readOnly:!0,complexity:"low"},getPageTextContent:{group:"content",readOnly:!0,complexity:"low"},getPageImages:{group:"content",readOnly:!0,complexity:"low"},updateImagePath:{group:"content",readOnly:!1,complexity:"medium"},getPageContent:{group:"content",readOnly:!0,complexity:"low"},getPageProperties:{group:"content",readOnly:!0,complexity:"low"},fetchSites:{group:"sites",readOnly:!0,complexity:"low"},fetchLanguageMasters:{group:"sites",readOnly:!0,complexity:"low"},fetchAvailableLocales:{group:"sites",readOnly:!0,complexity:"low"},listPages:{group:"pages",readOnly:!0,complexity:"low"},createPage:{group:"pages",readOnly:!1,complexity:"medium"},deletePage:{group:"pages",readOnly:!1,complexity:"high"},activatePage:{group:"pages",readOnly:!1,complexity:"medium"},deactivatePage:{group:"pages",readOnly:!1,complexity:"medium"},unpublishContent:{group:"pages",readOnly:!1,complexity:"medium"},enhancedPageSearch:{group:"search",readOnly:!0,complexity:"low"},getNodeContent:{group:"content",readOnly:!0,complexity:"low"},listChildren:{group:"content",readOnly:!0,complexity:"low"},updateComponent:{group:"components",readOnly:!1,complexity:"medium"},scanPageComponents:{group:"components",readOnly:!0,complexity:"low"},createComponent:{group:"components",readOnly:!1,complexity:"high"},addComponent:{group:"components",readOnly:!1,complexity:"medium"},deleteComponent:{group:"components",readOnly:!1,complexity:"high"},getComponents:{group:"components",readOnly:!0,complexity:"low"},bulkUpdateComponents:{group:"components",readOnly:!1,complexity:"high"},convertComponents:{group:"components",readOnly:!1,complexity:"high"},bulkConvertComponents:{group:"components",readOnly:!1,complexity:"high"},getAssetMetadata:{group:"assets",readOnly:!0,complexity:"low"},updateAsset:{group:"assets",readOnly:!1,complexity:"medium"},deleteAsset:{group:"assets",readOnly:!1,complexity:"high"},searchContent:{group:"search",readOnly:!0,complexity:"low"},executeJCRQuery:{group:"search",readOnly:!0,complexity:"medium"},getTemplates:{group:"templates",readOnly:!0,complexity:"low"},getTemplateStructure:{group:"templates",readOnly:!0,complexity:"low"},listWorkflowModels:{group:"workflows",readOnly:!0,complexity:"low"},startWorkflow:{group:"workflows",readOnly:!1,complexity:"medium"},listWorkflowInstances:{group:"workflows",readOnly:!0,complexity:"low"},getWorkflowInstance:{group:"workflows",readOnly:!0,complexity:"low"},updateWorkflowInstanceState:{group:"workflows",readOnly:!1,complexity:"medium"},getInboxItems:{group:"workflows",readOnly:!0,complexity:"low"},completeWorkItem:{group:"workflows",readOnly:!1,complexity:"medium"},delegateWorkItem:{group:"workflows",readOnly:!1,complexity:"medium"},getWorkItemRoutes:{group:"workflows",readOnly:!0,complexity:"low"},getContentFragment:{group:"fragments-content",readOnly:!0,complexity:"low"},listContentFragments:{group:"fragments-content",readOnly:!0,complexity:"low"},manageContentFragment:{group:"fragments-content",readOnly:!1,complexity:"medium"},manageContentFragmentVariation:{group:"fragments-content",readOnly:!1,complexity:"medium"},getExperienceFragment:{group:"fragments-experience",readOnly:!0,complexity:"low"},listExperienceFragments:{group:"fragments-experience",readOnly:!0,complexity:"low"},manageExperienceFragment:{group:"fragments-experience",readOnly:!1,complexity:"medium"},manageExperienceFragmentVariation:{group:"fragments-experience",readOnly:!1,complexity:"medium"}};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aem-mcp-server",
3
- "version": "1.5.1",
3
+ "version": "1.6.1",
4
4
  "description": "AEM MCP server. Chat with your AEM instance for content, component, and asset operations.",
5
5
  "license": "AGPL-3.0-only",
6
6
  "private": false,