aem-mcp-server 1.3.3 → 1.3.5

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
@@ -16,7 +16,7 @@ This project is designed for non-technical persons who want to manage AEM via na
16
16
 
17
17
  - **Chat with your AEM instance** for content, component, and asset operations.
18
18
  - **AI IDEs integration** (Cursor, Copilot, Webstorm, VS Code, etc.)
19
- - **Production-ready, modular, and configurable**
19
+ - **Supports both AEMaaCS and self-hosted instances**
20
20
  - **Modern, TypeScript-based AEM MCP server**
21
21
  - **REST/JSON-RPC API** with latest MCP features.
22
22
 
@@ -28,17 +28,13 @@ This project is designed for non-technical persons who want to manage AEM via na
28
28
  - Node.js 18+
29
29
  - Access to an AEM instance (local or remote)
30
30
 
31
- ### Add AEM MCP to AI IDE
32
- [![Install MCP Server](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en/install-mcp?name=AEM&config=eyJ1cmwiOiJodHRwOi8vMTI3LjAuMC4xOjg1MDIvbWNwIn0%3D)
33
-
34
- OR:
35
-
36
31
  ### Installation
37
32
  ```sh
38
33
  npm install aem-mcp-server -g
39
34
  ```
40
35
 
41
36
  ### Start the Server
37
+ With default settings (admin:admin credentials for http://localhost:4502):
42
38
  ```sh
43
39
  aem-mcp
44
40
  ```
@@ -46,16 +42,27 @@ aem-mcp
46
42
  ### Configuration
47
43
  ```
48
44
  Options:
49
- --version Show version number [boolean]
50
- -H, --host Author instance URL [string] [default: "http://localhost:4502"]
51
- -P, --publisher Publisher instance URL [string] [default: "http://localhost:4503"]
52
- -u, --user Username for authentication [string] [default: "admin"]
53
- -p, --pass Password for authentication [string] [default: "admin"]
54
- -m, --mcpPort Port for MCP server [number] [default: 8502]
55
- -h, --help Show help [boolean]
45
+ --version Show version number [boolean]
46
+ -H, --host [string] [default: "http://localhost:4502"]
47
+ -u, --user [string] [default: "admin"]
48
+ -p, --pass [string] [default: "admin"]
49
+ -i, --id clientId [string] [default: ""]
50
+ -s, --secret clientSecret [string] [default: ""]
51
+ -m, --mcpPort [number] [default: 8502]
52
+ -h, --help Show help [boolean]
53
+ ```
56
54
 
55
+ For AEMaaCS, use the `clientId` and `clientSecret` for authentication. [More info](https://developer.adobe.com/developer-console/docs/guides/authentication/ServerToServerAuthentication/implementation).
56
+ For self-hosted AEM use user/pass. The default credentials are `admin:admin`.
57
+
58
+ ### Example Command
59
+ ```sh
60
+ aem-mcp -u=user@domain.com -p=mypass -H=https://author-qa.adobeaemcloud.com
57
61
  ```
58
62
 
63
+ ### Add AEM MCP to AI IDE
64
+ [![Install MCP Server](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en/install-mcp?name=AEM&config=eyJ1cmwiOiJodHRwOi8vMTI3LjAuMC4xOjg1MDIvbWNwIn0%3D)
65
+
59
66
  ---
60
67
 
61
68
  ## Features
@@ -1 +1 @@
1
- "use strict";import{getAEMConfig as P,isValidContentPath as m,isValidLocale as b}from"./aem.config.js";import{AEM_ERROR_CODES as h,createAEMError as d,createSuccessResponse as i,handleAEMHttpError as y,safeExecute as p,validateComponentOperation as w}from"./aem.errors.js";import{AEMFetch as C}from"./aem.fetch.js";import{LOGGER as g}from"../utils/logger.js";export class AEMConnector{isInitialized;isAEMaaCS;config;aemConfig;fetch;constructor(e){this.isInitialized=!1,this.config=this.loadConfig(e),this.aemConfig=P({}),this.isAEMaaCS=this.isConfigAEMaaCS(),this.fetch=new C({host:this.config.aem.host,auth:this.config.aem.auth,timeout:this.aemConfig.queries.timeoutMs})}async init(){try{await this.fetch.init(),this.isInitialized=!0}catch{this.isInitialized=!1}}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"}}}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(),g.log("Testing AEM connection to:",this.config.aem.host);const e=`${this.config.aem.host}/libs/granite/core/content/login.html`,t=await this.fetch.get(e,{timeout:5e3});return g.log("\u2705 AEM connection successful! Status:",t.status),!0}catch(e){return g.error("\u274C AEM connection failed:",e.message),!1}}async testAuthConnection(){try{this.isInitialized||await this.init();const e=`${this.config.aem.host}/libs/granite/security/content/userinfo.json`;g.log("Testing AEM authentication connection to:",this.config.aem.host);const t=await this.fetch.get(e,{timeout:5e3});return g.log("\u2705 AEM authentication connection successful! Status:",t.status),!0}catch(e){return g.error("\u274C AEM authentication connection failed:",e.message),!1}}async validateComponent(e){return p(async()=>{const t=e.pagePath||e.page_path,{locale:n,component:s,props:a}=e;if(w(n,t,s,a),!b(n,this.aemConfig))throw d(h.INVALID_LOCALE,`Locale '${n}' is not supported`,{locale:n,allowedLocales:this.aemConfig.validation.allowedLocales});if(!m(t,this.aemConfig))throw d(h.INVALID_PATH,`Path '${t}' is not within allowed content roots`,{path:t,allowedRoots:Object.values(this.aemConfig.contentPaths)});const o=`${t}.json`,r=await this.fetch.get(o,{params:{":depth":"2"},timeout:this.aemConfig.queries.timeoutMs}),c=this.validateComponentProps(r.data,s,a);return i({message:"Component validation completed successfully",pageData:r.data,component:s,locale:n,validation:c,configUsed:{allowedLocales:this.aemConfig.validation.allowedLocales}},"validateComponent")},"validateComponent")}validateComponentProps(e,t,n){const s=[],a=[];return t==="text"&&!n.text&&!n.richText&&s.push("Text component should have text or richText property"),t==="image"&&!n.fileReference&&!n.src&&a.push("Image component requires fileReference or src property"),{valid:a.length===0,errors:a,warnings:s,componentType:t,propsValidated:Object.keys(n).length}}async updateComponent(e){return p(async()=>{if(!e.componentPath||typeof e.componentPath!="string")throw d(h.INVALID_PARAMETERS,"Component path is required and must be a string");if(!e.properties||typeof e.properties!="object")throw d(h.INVALID_PARAMETERS,"Properties are required and must be an object");if(!m(e.componentPath,this.aemConfig))throw d(h.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`,n=await this.fetch.get(t);try{await n.json()}catch(r){throw r.response?.status===404?d(h.COMPONENT_NOT_FOUND,`Component not found at path: ${e.componentPath}`,{componentPath:e.componentPath}):y(r,"updateComponent")}const s=new URLSearchParams;Object.entries(e.properties).forEach(([r,c])=>{c==null?s.append(`${r}@Delete`,""):Array.isArray(c)?c.forEach(l=>{s.append(`${r}`,l.toString())}):typeof c=="object"?s.append(r,JSON.stringify(c)):s.append(r,c.toString())});const a=await this.fetch.post(e.componentPath,s),o=await this.fetch.get(`${e.componentPath}.json`);return i({message:"Component updated successfully",path:e.componentPath,properties:e.properties,updatedProperties:o.data,response:a.data,verification:{success:!0,propertiesChanged:Object.keys(e.properties).length,timestamp:new Date().toISOString()}},"updateComponent")},"updateComponent")}async undoChanges(e){return i({message:"undoChanges is not implemented. Please use AEM version history for undo/rollback.",request:e,timestamp:new Date().toISOString()},"undoChanges")}async scanPageComponents(e){return p(async()=>{const t=`${e}.infinity.json`,n=await this.fetch.get(t),s=[],a=(o,r)=>{!o||typeof o!="object"||(o["sling:resourceType"]&&s.push({path:r,resourceType:o["sling:resourceType"],properties:{...o}}),Object.entries(o).forEach(([c,l])=>{if(typeof l=="object"&&l!==null&&!c.startsWith("rep:")&&!c.startsWith("oak:")){const f=r?`${r}/${c}`:c;a(l,f)}}))};return n.data["jcr:content"]?a(n.data["jcr:content"],"jcr:content"):a(n.data,e),i({pagePath:e,components:s,totalComponents:s.length},"scanPageComponents")},"scanPageComponents")}async fetchSites(){return p(async()=>{const t=await this.fetch.get("/content.json",{":depth":"2"}),n=[];return Object.entries(t).forEach(([s,a])=>{s.startsWith("jcr:")||s.startsWith("sling:")||a&&typeof a=="object"&&a["jcr:content"]&&n.push({name:s,path:`/content/${s}`,title:a["jcr:content"]["jcr:title"]||s,template:a["jcr:content"]["cq:template"],lastModified:a["jcr:content"]["cq:lastModified"]})}),i({sites:n,totalCount:n.length},"fetchSites")},"fetchSites")}async fetchLanguageMasters(e){return p(async()=>{const t=`/content/${e}.json`,n=await this.fetch.get(t,{":depth":"3"}),s=[];return Object.entries(n).forEach(([a,o])=>{a.startsWith("jcr:")||a.startsWith("sling:")||o&&typeof o=="object"&&o["jcr:content"]&&s.push({name:a,path:`/content/${a}`,title:o["jcr:content"]["jcr:title"]||a,language:o["jcr:content"]["jcr:language"]||"en"})}),i({site:e,languageMasters:s},"fetchLanguageMasters")},"fetchLanguageMasters")}async fetchAvailableLocales(e,t){return p(async()=>{const n=`${t}.json`,s=await this.fetch.get(n,{":depth":"2"}),a=[];return Object.entries(s).forEach(([o,r])=>{o.startsWith("jcr:")||o.startsWith("sling:")||r&&typeof r=="object"&&a.push({name:o,title:r["jcr:content"]?.["jcr:title"]||o,language:r["jcr:content"]?.["jcr:language"]||o})}),i({site:e,languageMasterPath:t,availableLocales:a},"fetchAvailableLocales")},"fetchAvailableLocales")}async replicateAndPublish(e,t,n){return p(async()=>i({message:"Replication simulated",selectedLocales:e,componentData:t,localizedOverrides:n},"replicateAndPublish"),"replicateAndPublish")}async getAllTextContent(e){return p(async()=>{const t=`${e}.infinity.json`,n=await this.fetch.get(t),s=[],a=(o,r)=>{!o||typeof o!="object"||((o.text||o["jcr:title"]||o["jcr:description"])&&s.push({path:r,title:o["jcr:title"],text:o.text,description:o["jcr:description"]}),Object.entries(o).forEach(([c,l])=>{if(typeof l=="object"&&l!==null&&!c.startsWith("rep:")&&!c.startsWith("oak:")){const f=r?`${r}/${c}`:c;a(l,f)}}))};return n["jcr:content"]?a(n["jcr:content"],"jcr:content"):a(n,e),i({pagePath:e,textContent:s},"getAllTextContent")},"getAllTextContent")}async getPageTextContent(e){return p(async()=>this.getAllTextContent(e),"getPageTextContent")}async getPageImages(e){return p(async()=>{const t=`${e}.infinity.json`,n=await this.fetch.get(t),s=[],a=(o,r)=>{!o||typeof o!="object"||((o.fileReference||o.src)&&s.push({path:r,fileReference:o.fileReference,src:o.src,alt:o.alt||o.altText,title:o["jcr:title"]||o.title}),Object.entries(o).forEach(([c,l])=>{if(typeof l=="object"&&l!==null&&!c.startsWith("rep:")&&!c.startsWith("oak:")){const f=r?`${r}/${c}`:c;a(l,f)}}))};return n["jcr:content"]?a(n["jcr:content"],"jcr:content"):a(n,e),i({pagePath:e,images:s},"getPageImages")},"getPageImages")}async updateImagePath(e,t){return p(async()=>this.updateComponent({componentPath:e,properties:{fileReference:t}}),"updateImagePath")}async getPageContent(e){return p(async()=>{const t=`${e}.infinity.json`,n=await this.fetch.get(t);return i({pagePath:e,content:n},"getPageContent")},"getPageContent")}async listChildren(e,t=1){return p(async()=>{try{const n=await this.fetch.get(`${e}.${t}.json`),s=[];return n&&typeof n=="object"&&Object.entries(n).forEach(([a,o])=>{if(!(a.startsWith("jcr:")||a.startsWith("sling:")||a.startsWith("cq:")||a.startsWith("rep:")||a.startsWith("oak:")||a==="jcr:content")&&o&&typeof o=="object"){const r=`${e}/${a}`;s.push({name:a,path:r,primaryType:o["jcr:primaryType"]||"nt:unstructured",title:o["jcr:content"]?.["jcr:title"]||o["jcr:title"]||a,lastModified:o["jcr:content"]?.["cq:lastModified"]||o["cq:lastModified"],resourceType:o["jcr:content"]?.["sling:resourceType"]||o["sling:resourceType"]})}}),s}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(a=>({name:a.name||a.path?.split("/").pop(),path:a.path,primaryType:a["jcr:primaryType"]||"cq:Page",title:a["jcr:content/jcr:title"]||a.title||a.name,lastModified:a["jcr:content/cq:lastModified"],resourceType:a["jcr:content/sling:resourceType"]}));throw n}},"listChildren")}async listPages(e,t=1,n=20){return p(async()=>{try{const s=await this.fetch.get(`${e}.${t}.json`),a=[],o=(r,c,l)=>{l>t||a.length>=n||Object.entries(r).forEach(([f,u])=>{if(!(a.length>=n)&&!(f.startsWith("jcr:")||f.startsWith("sling:")||f.startsWith("cq:")||f.startsWith("rep:")||f.startsWith("oak:"))&&u&&typeof u=="object"){const j=`${c}/${f}`;u["jcr:primaryType"]==="cq:Page"&&a.push({name:f,path:j,primaryType:"cq:Page",title:u["jcr:content"]?.["jcr:title"]||f,template:u["jcr:content"]?.["cq:template"],lastModified:u["jcr:content"]?.["cq:lastModified"],lastModifiedBy:u["jcr:content"]?.["cq:lastModifiedBy"],resourceType:u["jcr:content"]?.["sling:resourceType"],type:"page"}),l<t&&o(u,j,l+1)}})};return s&&typeof s=="object"&&o(s,e,0),i({siteRoot:e,pages:a,pageCount:a.length,depth:t,limit:n,totalChildrenScanned:a.length},"listPages")}catch(s){if(g.warn("JSON API failed, falling back to QueryBuilder:",s.message),s.response?.status===404||s.response?.status===403){const a=await this.fetch.get("/bin/querybuilder.json",{path:e,type:"cq:Page","p.nodedepth":t.toString(),"p.limit":n.toString(),"p.hits":"full"}),o=(a.hits||[]).map(r=>({name:r.name||r.path?.split("/").pop(),path:r.path,primaryType:"cq:Page",title:r["jcr:content/jcr:title"]||r.title||r.name,template:r["jcr:content/cq:template"],lastModified:r["jcr:content/cq:lastModified"],lastModifiedBy:r["jcr:content/cq:lastModifiedBy"],resourceType:r["jcr:content/sling:resourceType"],type:"page"}));return i({siteRoot:e,pages:o,pageCount:o.length,depth:t,limit:n,totalChildrenScanned:a.total||o.length,fallbackUsed:"QueryBuilder"},"listPages")}throw s}},"listPages")}async executeJCRQuery(e,t=20){return p(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 s=await this.fetch.get("/bin/querybuilder.json",{path:"/content",type:"cq:Page",fulltext:e,"p.limit":t});return{query:e,results:s.hits||[],total:s.total||0,limit:t}},"executeJCRQuery")}async getPageProperties(e){return p(async()=>{const t=`${e}/jcr:content.json`,n=await this.fetch.get(t),s={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 i({pagePath:e,properties:s},"getPageProperties")},"getPageProperties")}async searchContent(e){return p(async()=>{const t=await this.fetch.get(this.config.aem.endpoints.query,e);return i({params:e,results:t.hits||[],total:t.total||0,rawResponse:t},"searchContent")},"searchContent")}async getAssetMetadata(e){return p(async()=>{const t=`${e}.json`,n=await this.fetch.get(t),s=n["jcr:content"]?.metadata||{};return i({assetPath:e,metadata:s,fullData:n},"getAssetMetadata")},"getAssetMetadata")}async createPage(e){return p(async()=>{const{parentPath:t,title:n,template:s,name:a,properties:o}=e;if(!m(t,this.aemConfig))throw d(h.INVALID_PARAMETERS,`Invalid parent path: ${String(t)}`,{parentPath:t});const r=a||n.replace(/\s+/g,"-").toLowerCase(),c=`${t}/${r}`;return await this.fetch.post(c,{"jcr:primaryType":"cq:Page","jcr:title":n,"cq:template":s,...o}),i({success:!0,pagePath:c,title:n,template:s,properties:o,timestamp:new Date().toISOString()},"createPage")},"createPage")}async deletePage(e){return p(async()=>{const{pagePath:t}=e;if(!m(t,this.aemConfig))throw d(h.INVALID_PARAMETERS,`Invalid page path: ${String(t)}`,{pagePath:t});let n=!1;try{await this.fetch.delete(t),n=!0}catch{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(o){throw o}}}return i({success:n,deletedPath:t,timestamp:new Date().toISOString()},"deletePage")},"deletePage")}async createComponent(e){return p(async()=>{const{pagePath:t,componentPath:n,componentType:s,resourceType:a,properties:o={},name:r}=e;if(!m(t,this.aemConfig))throw d(h.INVALID_PARAMETERS,`Invalid page path: ${String(t)}`,{pagePath:t});const c=r||`${s}_${Date.now()}`,l=n||`${t}/jcr:content/${c}`;return await this.fetch.post(l,{"jcr:primaryType":"nt:unstructured","sling:resourceType":a,...o,":operation":"import",":contentType":"json",":replace":"true"}),i({success:!0,componentPath:l,componentType:s,resourceType:a,properties:o,timestamp:new Date().toISOString()},"createComponent")},"createComponent")}async deleteComponent(e){return p(async()=>{const{componentPath:t}=e;if(!m(t,this.aemConfig))throw d(h.INVALID_PARAMETERS,`Invalid component path: ${String(t)}`,{componentPath:t});let n=!1;try{await this.fetch.delete(t),n=!0}catch{try{await this.fetch.post(t,{":operation":"delete"}),n=!0}catch(a){throw a}}return i({success:n,deletedPath:t,timestamp:new Date().toISOString()},"deleteComponent")},"deleteComponent")}async unpublishContent(e){return p(async()=>{const{contentPaths:t,unpublishTree:n=!1}=e;if(!t||Array.isArray(t)&&t.length===0)throw d(h.INVALID_PARAMETERS,"Content paths array is required and cannot be empty",{contentPaths:t});const s=[];for(const a of Array.isArray(t)?t:[t])try{const o=new URLSearchParams;o.append("cmd","Deactivate"),o.append("path",a),o.append("ignoredeactivated","false"),o.append("onlymodified","false"),n&&o.append("deep","true");const r=await this.fetch.post("/bin/replicate.json",o);s.push({path:a,success:!0,response:r})}catch(o){s.push({path:a,success:!1,error:o.message})}return i({success:s.every(a=>a.success),results:s,unpublishedPaths:t,unpublishTree:n,timestamp:new Date().toISOString()},"unpublishContent")},"unpublishContent")}async activatePage(e){return p(async()=>{const{pagePath:t,activateTree:n=!1}=e;if(!m(t,this.aemConfig))throw d(h.INVALID_PARAMETERS,`Invalid page path: ${String(t)}`,{pagePath:t});try{const s=new URLSearchParams;s.append("cmd","Activate"),s.append("path",t),s.append("ignoredeactivated","false"),s.append("onlymodified","false"),n&&s.append("deep","true");const a=await this.fetch.post("/bin/replicate.json",s);return i({success:!0,activatedPath:t,activateTree:n,response:a,timestamp:new Date().toISOString()},"activatePage")}catch(s){try{const a=await this.fetch.post("/bin/wcmcommand",{cmd:"activate",path:t,ignoredeactivated:!1,onlymodified:!1});return i({success:!0,activatedPath:t,activateTree:n,response:a,fallbackUsed:"WCM Command",timestamp:new Date().toISOString()},"activatePage")}catch{throw y(s,"activatePage")}}},"activatePage")}async deactivatePage(e){return p(async()=>{const{pagePath:t,deactivateTree:n=!1}=e;if(!m(t,this.aemConfig))throw d(h.INVALID_PARAMETERS,`Invalid page path: ${String(t)}`,{pagePath:t});try{const s=new URLSearchParams;s.append("cmd","Deactivate"),s.append("path",t),s.append("ignoredeactivated","false"),s.append("onlymodified","false"),n&&s.append("deep","true");const a=await this.fetch.post("/bin/replicate.json",s);return i({success:!0,deactivatedPath:t,deactivateTree:n,response:a,timestamp:new Date().toISOString()},"deactivatePage")}catch(s){try{const a=await this.fetch.post("/bin/wcmcommand",{cmd:"deactivate",path:t,ignoredeactivated:!1,onlymodified:!1});return i({success:!0,deactivatedPath:t,deactivateTree:n,response:a,fallbackUsed:"WCM Command",timestamp:new Date().toISOString()},"deactivatePage")}catch{throw y(s,"deactivatePage")}}},"deactivatePage")}async uploadAsset(e){return p(async()=>{const{parentPath:t,fileName:n,fileContent:s,mimeType:a,metadata:o={}}=e;if(!m(t,this.aemConfig))throw d(h.INVALID_PARAMETERS,`Invalid parent path: ${String(t)}`,{parentPath:t});const r=`${t}/${n}`;try{const c=new URLSearchParams;typeof s=="string"?c.append("file",s):c.append("file",s.toString()),c.append("fileName",n),c.append(":operation","import"),c.append(":contentType","json"),c.append(":replace","true"),c.append("jcr:primaryType","dam:Asset"),a&&c.append("jcr:content/jcr:mimeType",a),Object.entries(o).forEach(([u,j])=>{c.append(`jcr:content/metadata/${u}`,String(j))});const l=await this.fetch.post(r,c),f=await this.fetch.get(`${r}.json`);return i({success:!0,assetPath:r,fileName:n,mimeType:a,metadata:o,uploadResponse:l,assetData:f,timestamp:new Date().toISOString()},"uploadAsset")}catch(c){try{const l=await this.fetch.post("/api/assets"+t,{fileName:n,fileContent:s,mimeType:a,metadata:o});return i({success:!0,assetPath:r,fileName:n,mimeType:a,metadata:o,uploadResponse:l,fallbackUsed:"DAM API",timestamp:new Date().toISOString()},"uploadAsset")}catch{throw y(c,"uploadAsset")}}},"uploadAsset")}async updateAsset(e){return p(async()=>{const{assetPath:t,metadata:n,fileContent:s,mimeType:a}=e;if(!m(t,this.aemConfig))throw d(h.INVALID_PARAMETERS,`Invalid asset path: ${String(t)}`,{assetPath:t});const o=new URLSearchParams;s&&(o.append("file",s),a&&o.append("jcr:content/jcr:mimeType",a)),n&&typeof n=="object"&&Object.entries(n).forEach(([r,c])=>{o.append(`jcr:content/metadata/${r}`,String(c))});try{const r=await this.fetch.post(t,o),c=await this.fetch.get(`${t}.json`);return i({success:!0,assetPath:t,updatedMetadata:n,updateResponse:r,assetData:c,timestamp:new Date().toISOString()},"updateAsset")}catch(r){throw y(r,"updateAsset")}},"updateAsset")}async deleteAsset(e){return p(async()=>{const{assetPath:t,force:n=!1}=e;if(!m(t,this.aemConfig))throw d(h.INVALID_PARAMETERS,`Invalid asset path: ${String(t)}`,{assetPath:t});return await this.fetch.delete(t),i({success:!0,deletedPath:t,force:n,timestamp:new Date().toISOString()},"deleteAsset")},"deleteAsset")}async getTemplateStructure(e){return p(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||[]},s=(a,o="")=>{if(!(!a||typeof a!="object")){if(a.components){const r=Object.keys(a.components);n.allowedComponents.push(...r)}Object.entries(a).forEach(([r,c])=>{typeof c=="object"&&c!==null&&!r.startsWith("jcr:")&&s(c,o?`${o}/${r}`:r)})}};return s(n.policies),n.allowedComponents=[...new Set(n.allowedComponents)],i({templatePath:e,structure:n,fullData:t},"getTemplateStructure")}catch(t){throw y(t,"getTemplateStructure")}},"getTemplateStructure")}async bulkUpdateComponents(e){return p(async()=>{const{updates:t,validateFirst:n=!0,continueOnError:s=!1}=e;if(!Array.isArray(t)||t.length===0)throw d(h.INVALID_PARAMETERS,"Updates array is required and cannot be empty");const a=[];if(n)for(const r of t)try{await this.fetch.get(`${r.componentPath}.json`)}catch(c){if(c.response?.status===404&&(a.push({componentPath:r.componentPath,success:!1,error:`Component not found: ${r.componentPath}`,phase:"validation"}),!s))return i({success:!1,message:"Bulk update failed during validation phase",results:a,totalUpdates:t.length,successfulUpdates:0},"bulkUpdateComponents")}let o=0;for(const r of t)try{const c=await this.updateComponent({componentPath:r.componentPath,properties:r.properties});a.push({componentPath:r.componentPath,success:!0,result:c,phase:"update"}),o++}catch(c){if(a.push({componentPath:r.componentPath,success:!1,error:c.message,phase:"update"}),!s)break}return i({success:o===t.length,message:`Bulk update completed: ${o}/${t.length} successful`,results:a,totalUpdates:t.length,successfulUpdates:o,failedUpdates:t.length-o},"bulkUpdateComponents")},"bulkUpdateComponents")}async getNodeContent(e,t=1){return p(async()=>{const n=`${e}.json`,s=await this.fetch.get(n,{":depth":t.toString()});return{path:e,depth:t,content:s,timestamp:new Date().toISOString()}},"getNodeContent")}}
1
+ "use strict";import{getAEMConfig as P,isValidContentPath as m,isValidLocale as b}from"./aem.config.js";import{AEM_ERROR_CODES as h,createAEMError as d,createSuccessResponse as i,handleAEMHttpError as y,safeExecute as p,validateComponentOperation as w}from"./aem.errors.js";import{AEMFetch as C}from"./aem.fetch.js";import{LOGGER as g}from"../utils/logger.js";export class AEMConnector{isInitialized;isAEMaaCS;config;aemConfig;fetch;constructor(e){this.isInitialized=!1,this.config=this.loadConfig(e),this.aemConfig=P({}),this.isAEMaaCS=this.isConfigAEMaaCS(),this.fetch=new C({host:this.config.aem.host,auth:this.config.aem.auth,timeout:this.aemConfig.queries.timeoutMs})}async init(){try{await this.fetch.init(),this.isInitialized=!0}catch{this.isInitialized=!1}}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"}}}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(),g.log("Testing AEM connection to:",this.config.aem.host);const e=`${this.config.aem.host}/libs/granite/core/content/login.html`,t=await this.fetch.get(e,{timeout:5e3});return g.log("\u2705 AEM connection successful! Status:",t.status),!0}catch(e){return g.error("\u274C AEM connection failed:",e.message),!1}}async testAuthConnection(){try{this.isInitialized||await this.init();const e=`${this.config.aem.host}/libs/granite/security/currentuser.json`;g.log("Testing AEM authentication connection to:",this.config.aem.host);const t=await this.fetch.get(e,{timeout:5e3});return g.log("\u2705 AEM authentication connection successful! Status:",t.status),!0}catch(e){return g.error("\u274C AEM authentication connection failed:",e.message),!1}}async validateComponent(e){return p(async()=>{const t=e.pagePath||e.page_path,{locale:n,component:s,props:a}=e;if(w(n,t,s,a),!b(n,this.aemConfig))throw d(h.INVALID_LOCALE,`Locale '${n}' is not supported`,{locale:n,allowedLocales:this.aemConfig.validation.allowedLocales});if(!m(t,this.aemConfig))throw d(h.INVALID_PATH,`Path '${t}' is not within allowed content roots`,{path:t,allowedRoots:Object.values(this.aemConfig.contentPaths)});const o=`${t}.json`,r=await this.fetch.get(o,{params:{":depth":"2"},timeout:this.aemConfig.queries.timeoutMs}),c=this.validateComponentProps(r.data,s,a);return i({message:"Component validation completed successfully",pageData:r.data,component:s,locale:n,validation:c,configUsed:{allowedLocales:this.aemConfig.validation.allowedLocales}},"validateComponent")},"validateComponent")}validateComponentProps(e,t,n){const s=[],a=[];return t==="text"&&!n.text&&!n.richText&&s.push("Text component should have text or richText property"),t==="image"&&!n.fileReference&&!n.src&&a.push("Image component requires fileReference or src property"),{valid:a.length===0,errors:a,warnings:s,componentType:t,propsValidated:Object.keys(n).length}}async updateComponent(e){return p(async()=>{if(!e.componentPath||typeof e.componentPath!="string")throw d(h.INVALID_PARAMETERS,"Component path is required and must be a string");if(!e.properties||typeof e.properties!="object")throw d(h.INVALID_PARAMETERS,"Properties are required and must be an object");if(!m(e.componentPath,this.aemConfig))throw d(h.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`,n=await this.fetch.get(t);try{await n.json()}catch(r){throw r.response?.status===404?d(h.COMPONENT_NOT_FOUND,`Component not found at path: ${e.componentPath}`,{componentPath:e.componentPath}):y(r,"updateComponent")}const s=new URLSearchParams;Object.entries(e.properties).forEach(([r,c])=>{c==null?s.append(`${r}@Delete`,""):Array.isArray(c)?c.forEach(l=>{s.append(`${r}`,l.toString())}):typeof c=="object"?s.append(r,JSON.stringify(c)):s.append(r,c.toString())});const a=await this.fetch.post(e.componentPath,s),o=await this.fetch.get(`${e.componentPath}.json`);return i({message:"Component updated successfully",path:e.componentPath,properties:e.properties,updatedProperties:o.data,response:a.data,verification:{success:!0,propertiesChanged:Object.keys(e.properties).length,timestamp:new Date().toISOString()}},"updateComponent")},"updateComponent")}async undoChanges(e){return i({message:"undoChanges is not implemented. Please use AEM version history for undo/rollback.",request:e,timestamp:new Date().toISOString()},"undoChanges")}async scanPageComponents(e){return p(async()=>{const t=`${e}.infinity.json`,n=await this.fetch.get(t),s=[],a=(o,r)=>{!o||typeof o!="object"||(o["sling:resourceType"]&&s.push({path:r,resourceType:o["sling:resourceType"],properties:{...o}}),Object.entries(o).forEach(([c,l])=>{if(typeof l=="object"&&l!==null&&!c.startsWith("rep:")&&!c.startsWith("oak:")){const f=r?`${r}/${c}`:c;a(l,f)}}))};return n.data["jcr:content"]?a(n.data["jcr:content"],"jcr:content"):a(n.data,e),i({pagePath:e,components:s,totalComponents:s.length},"scanPageComponents")},"scanPageComponents")}async fetchSites(){return p(async()=>{const t=await this.fetch.get("/content.json",{":depth":"2"}),n=[];return Object.entries(t).forEach(([s,a])=>{s.startsWith("jcr:")||s.startsWith("sling:")||a&&typeof a=="object"&&a["jcr:content"]&&n.push({name:s,path:`/content/${s}`,title:a["jcr:content"]["jcr:title"]||s,template:a["jcr:content"]["cq:template"],lastModified:a["jcr:content"]["cq:lastModified"]})}),i({sites:n,totalCount:n.length},"fetchSites")},"fetchSites")}async fetchLanguageMasters(e){return p(async()=>{const t=`/content/${e}.json`,n=await this.fetch.get(t,{":depth":"3"}),s=[];return Object.entries(n).forEach(([a,o])=>{a.startsWith("jcr:")||a.startsWith("sling:")||o&&typeof o=="object"&&o["jcr:content"]&&s.push({name:a,path:`/content/${a}`,title:o["jcr:content"]["jcr:title"]||a,language:o["jcr:content"]["jcr:language"]||"en"})}),i({site:e,languageMasters:s},"fetchLanguageMasters")},"fetchLanguageMasters")}async fetchAvailableLocales(e,t){return p(async()=>{const n=`${t}.json`,s=await this.fetch.get(n,{":depth":"2"}),a=[];return Object.entries(s).forEach(([o,r])=>{o.startsWith("jcr:")||o.startsWith("sling:")||r&&typeof r=="object"&&a.push({name:o,title:r["jcr:content"]?.["jcr:title"]||o,language:r["jcr:content"]?.["jcr:language"]||o})}),i({site:e,languageMasterPath:t,availableLocales:a},"fetchAvailableLocales")},"fetchAvailableLocales")}async replicateAndPublish(e,t,n){return p(async()=>i({message:"Replication simulated",selectedLocales:e,componentData:t,localizedOverrides:n},"replicateAndPublish"),"replicateAndPublish")}async getAllTextContent(e){return p(async()=>{const t=`${e}.infinity.json`,n=await this.fetch.get(t),s=[],a=(o,r)=>{!o||typeof o!="object"||((o.text||o["jcr:title"]||o["jcr:description"])&&s.push({path:r,title:o["jcr:title"],text:o.text,description:o["jcr:description"]}),Object.entries(o).forEach(([c,l])=>{if(typeof l=="object"&&l!==null&&!c.startsWith("rep:")&&!c.startsWith("oak:")){const f=r?`${r}/${c}`:c;a(l,f)}}))};return n["jcr:content"]?a(n["jcr:content"],"jcr:content"):a(n,e),i({pagePath:e,textContent:s},"getAllTextContent")},"getAllTextContent")}async getPageTextContent(e){return p(async()=>this.getAllTextContent(e),"getPageTextContent")}async getPageImages(e){return p(async()=>{const t=`${e}.infinity.json`,n=await this.fetch.get(t),s=[],a=(o,r)=>{!o||typeof o!="object"||((o.fileReference||o.src)&&s.push({path:r,fileReference:o.fileReference,src:o.src,alt:o.alt||o.altText,title:o["jcr:title"]||o.title}),Object.entries(o).forEach(([c,l])=>{if(typeof l=="object"&&l!==null&&!c.startsWith("rep:")&&!c.startsWith("oak:")){const f=r?`${r}/${c}`:c;a(l,f)}}))};return n["jcr:content"]?a(n["jcr:content"],"jcr:content"):a(n,e),i({pagePath:e,images:s},"getPageImages")},"getPageImages")}async updateImagePath(e,t){return p(async()=>this.updateComponent({componentPath:e,properties:{fileReference:t}}),"updateImagePath")}async getPageContent(e){return p(async()=>{const t=`${e}.infinity.json`,n=await this.fetch.get(t);return i({pagePath:e,content:n},"getPageContent")},"getPageContent")}async listChildren(e,t=1){return p(async()=>{try{const n=await this.fetch.get(`${e}.${t}.json`),s=[];return n&&typeof n=="object"&&Object.entries(n).forEach(([a,o])=>{if(!(a.startsWith("jcr:")||a.startsWith("sling:")||a.startsWith("cq:")||a.startsWith("rep:")||a.startsWith("oak:")||a==="jcr:content")&&o&&typeof o=="object"){const r=`${e}/${a}`;s.push({name:a,path:r,primaryType:o["jcr:primaryType"]||"nt:unstructured",title:o["jcr:content"]?.["jcr:title"]||o["jcr:title"]||a,lastModified:o["jcr:content"]?.["cq:lastModified"]||o["cq:lastModified"],resourceType:o["jcr:content"]?.["sling:resourceType"]||o["sling:resourceType"]})}}),s}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(a=>({name:a.name||a.path?.split("/").pop(),path:a.path,primaryType:a["jcr:primaryType"]||"cq:Page",title:a["jcr:content/jcr:title"]||a.title||a.name,lastModified:a["jcr:content/cq:lastModified"],resourceType:a["jcr:content/sling:resourceType"]}));throw n}},"listChildren")}async listPages(e,t=1,n=20){return p(async()=>{try{const s=await this.fetch.get(`${e}.${t}.json`),a=[],o=(r,c,l)=>{l>t||a.length>=n||Object.entries(r).forEach(([f,u])=>{if(!(a.length>=n)&&!(f.startsWith("jcr:")||f.startsWith("sling:")||f.startsWith("cq:")||f.startsWith("rep:")||f.startsWith("oak:"))&&u&&typeof u=="object"){const j=`${c}/${f}`;u["jcr:primaryType"]==="cq:Page"&&a.push({name:f,path:j,primaryType:"cq:Page",title:u["jcr:content"]?.["jcr:title"]||f,template:u["jcr:content"]?.["cq:template"],lastModified:u["jcr:content"]?.["cq:lastModified"],lastModifiedBy:u["jcr:content"]?.["cq:lastModifiedBy"],resourceType:u["jcr:content"]?.["sling:resourceType"],type:"page"}),l<t&&o(u,j,l+1)}})};return s&&typeof s=="object"&&o(s,e,0),i({siteRoot:e,pages:a,pageCount:a.length,depth:t,limit:n,totalChildrenScanned:a.length},"listPages")}catch(s){if(g.warn("JSON API failed, falling back to QueryBuilder:",s.message),s.response?.status===404||s.response?.status===403){const a=await this.fetch.get("/bin/querybuilder.json",{path:e,type:"cq:Page","p.nodedepth":t.toString(),"p.limit":n.toString(),"p.hits":"full"}),o=(a.hits||[]).map(r=>({name:r.name||r.path?.split("/").pop(),path:r.path,primaryType:"cq:Page",title:r["jcr:content/jcr:title"]||r.title||r.name,template:r["jcr:content/cq:template"],lastModified:r["jcr:content/cq:lastModified"],lastModifiedBy:r["jcr:content/cq:lastModifiedBy"],resourceType:r["jcr:content/sling:resourceType"],type:"page"}));return i({siteRoot:e,pages:o,pageCount:o.length,depth:t,limit:n,totalChildrenScanned:a.total||o.length,fallbackUsed:"QueryBuilder"},"listPages")}throw s}},"listPages")}async executeJCRQuery(e,t=20){return p(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 s=await this.fetch.get("/bin/querybuilder.json",{path:"/content",type:"cq:Page",fulltext:e,"p.limit":t});return{query:e,results:s.hits||[],total:s.total||0,limit:t}},"executeJCRQuery")}async getPageProperties(e){return p(async()=>{const t=`${e}/jcr:content.json`,n=await this.fetch.get(t),s={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 i({pagePath:e,properties:s},"getPageProperties")},"getPageProperties")}async searchContent(e){return p(async()=>{const t=await this.fetch.get(this.config.aem.endpoints.query,e);return i({params:e,results:t.hits||[],total:t.total||0,rawResponse:t},"searchContent")},"searchContent")}async getAssetMetadata(e){return p(async()=>{const t=`${e}.json`,n=await this.fetch.get(t),s=n["jcr:content"]?.metadata||{};return i({assetPath:e,metadata:s,fullData:n},"getAssetMetadata")},"getAssetMetadata")}async createPage(e){return p(async()=>{const{parentPath:t,title:n,template:s,name:a,properties:o}=e;if(!m(t,this.aemConfig))throw d(h.INVALID_PARAMETERS,`Invalid parent path: ${String(t)}`,{parentPath:t});const r=a||n.replace(/\s+/g,"-").toLowerCase(),c=`${t}/${r}`;return await this.fetch.post(c,{"jcr:primaryType":"cq:Page","jcr:title":n,"cq:template":s,...o}),i({success:!0,pagePath:c,title:n,template:s,properties:o,timestamp:new Date().toISOString()},"createPage")},"createPage")}async deletePage(e){return p(async()=>{const{pagePath:t}=e;if(!m(t,this.aemConfig))throw d(h.INVALID_PARAMETERS,`Invalid page path: ${String(t)}`,{pagePath:t});let n=!1;try{await this.fetch.delete(t),n=!0}catch{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(o){throw o}}}return i({success:n,deletedPath:t,timestamp:new Date().toISOString()},"deletePage")},"deletePage")}async createComponent(e){return p(async()=>{const{pagePath:t,componentPath:n,componentType:s,resourceType:a,properties:o={},name:r}=e;if(!m(t,this.aemConfig))throw d(h.INVALID_PARAMETERS,`Invalid page path: ${String(t)}`,{pagePath:t});const c=r||`${s}_${Date.now()}`,l=n||`${t}/jcr:content/${c}`;return await this.fetch.post(l,{"jcr:primaryType":"nt:unstructured","sling:resourceType":a,...o,":operation":"import",":contentType":"json",":replace":"true"}),i({success:!0,componentPath:l,componentType:s,resourceType:a,properties:o,timestamp:new Date().toISOString()},"createComponent")},"createComponent")}async deleteComponent(e){return p(async()=>{const{componentPath:t}=e;if(!m(t,this.aemConfig))throw d(h.INVALID_PARAMETERS,`Invalid component path: ${String(t)}`,{componentPath:t});let n=!1;try{await this.fetch.delete(t),n=!0}catch{try{await this.fetch.post(t,{":operation":"delete"}),n=!0}catch(a){throw a}}return i({success:n,deletedPath:t,timestamp:new Date().toISOString()},"deleteComponent")},"deleteComponent")}async unpublishContent(e){return p(async()=>{const{contentPaths:t,unpublishTree:n=!1}=e;if(!t||Array.isArray(t)&&t.length===0)throw d(h.INVALID_PARAMETERS,"Content paths array is required and cannot be empty",{contentPaths:t});const s=[];for(const a of Array.isArray(t)?t:[t])try{const o=new URLSearchParams;o.append("cmd","Deactivate"),o.append("path",a),o.append("ignoredeactivated","false"),o.append("onlymodified","false"),n&&o.append("deep","true");const r=await this.fetch.post("/bin/replicate.json",o);s.push({path:a,success:!0,response:r})}catch(o){s.push({path:a,success:!1,error:o.message})}return i({success:s.every(a=>a.success),results:s,unpublishedPaths:t,unpublishTree:n,timestamp:new Date().toISOString()},"unpublishContent")},"unpublishContent")}async activatePage(e){return p(async()=>{const{pagePath:t,activateTree:n=!1}=e;if(!m(t,this.aemConfig))throw d(h.INVALID_PARAMETERS,`Invalid page path: ${String(t)}`,{pagePath:t});try{const s=new URLSearchParams;s.append("cmd","Activate"),s.append("path",t),s.append("ignoredeactivated","false"),s.append("onlymodified","false"),n&&s.append("deep","true");const a=await this.fetch.post("/bin/replicate.json",s);return i({success:!0,activatedPath:t,activateTree:n,response:a,timestamp:new Date().toISOString()},"activatePage")}catch(s){try{const a=await this.fetch.post("/bin/wcmcommand",{cmd:"activate",path:t,ignoredeactivated:!1,onlymodified:!1});return i({success:!0,activatedPath:t,activateTree:n,response:a,fallbackUsed:"WCM Command",timestamp:new Date().toISOString()},"activatePage")}catch{throw y(s,"activatePage")}}},"activatePage")}async deactivatePage(e){return p(async()=>{const{pagePath:t,deactivateTree:n=!1}=e;if(!m(t,this.aemConfig))throw d(h.INVALID_PARAMETERS,`Invalid page path: ${String(t)}`,{pagePath:t});try{const s=new URLSearchParams;s.append("cmd","Deactivate"),s.append("path",t),s.append("ignoredeactivated","false"),s.append("onlymodified","false"),n&&s.append("deep","true");const a=await this.fetch.post("/bin/replicate.json",s);return i({success:!0,deactivatedPath:t,deactivateTree:n,response:a,timestamp:new Date().toISOString()},"deactivatePage")}catch(s){try{const a=await this.fetch.post("/bin/wcmcommand",{cmd:"deactivate",path:t,ignoredeactivated:!1,onlymodified:!1});return i({success:!0,deactivatedPath:t,deactivateTree:n,response:a,fallbackUsed:"WCM Command",timestamp:new Date().toISOString()},"deactivatePage")}catch{throw y(s,"deactivatePage")}}},"deactivatePage")}async uploadAsset(e){return p(async()=>{const{parentPath:t,fileName:n,fileContent:s,mimeType:a,metadata:o={}}=e;if(!m(t,this.aemConfig))throw d(h.INVALID_PARAMETERS,`Invalid parent path: ${String(t)}`,{parentPath:t});const r=`${t}/${n}`;try{const c=new URLSearchParams;typeof s=="string"?c.append("file",s):c.append("file",s.toString()),c.append("fileName",n),c.append(":operation","import"),c.append(":contentType","json"),c.append(":replace","true"),c.append("jcr:primaryType","dam:Asset"),a&&c.append("jcr:content/jcr:mimeType",a),Object.entries(o).forEach(([u,j])=>{c.append(`jcr:content/metadata/${u}`,String(j))});const l=await this.fetch.post(r,c),f=await this.fetch.get(`${r}.json`);return i({success:!0,assetPath:r,fileName:n,mimeType:a,metadata:o,uploadResponse:l,assetData:f,timestamp:new Date().toISOString()},"uploadAsset")}catch(c){try{const l=await this.fetch.post("/api/assets"+t,{fileName:n,fileContent:s,mimeType:a,metadata:o});return i({success:!0,assetPath:r,fileName:n,mimeType:a,metadata:o,uploadResponse:l,fallbackUsed:"DAM API",timestamp:new Date().toISOString()},"uploadAsset")}catch{throw y(c,"uploadAsset")}}},"uploadAsset")}async updateAsset(e){return p(async()=>{const{assetPath:t,metadata:n,fileContent:s,mimeType:a}=e;if(!m(t,this.aemConfig))throw d(h.INVALID_PARAMETERS,`Invalid asset path: ${String(t)}`,{assetPath:t});const o=new URLSearchParams;s&&(o.append("file",s),a&&o.append("jcr:content/jcr:mimeType",a)),n&&typeof n=="object"&&Object.entries(n).forEach(([r,c])=>{o.append(`jcr:content/metadata/${r}`,String(c))});try{const r=await this.fetch.post(t,o),c=await this.fetch.get(`${t}.json`);return i({success:!0,assetPath:t,updatedMetadata:n,updateResponse:r,assetData:c,timestamp:new Date().toISOString()},"updateAsset")}catch(r){throw y(r,"updateAsset")}},"updateAsset")}async deleteAsset(e){return p(async()=>{const{assetPath:t,force:n=!1}=e;if(!m(t,this.aemConfig))throw d(h.INVALID_PARAMETERS,`Invalid asset path: ${String(t)}`,{assetPath:t});return await this.fetch.delete(t),i({success:!0,deletedPath:t,force:n,timestamp:new Date().toISOString()},"deleteAsset")},"deleteAsset")}async getTemplateStructure(e){return p(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||[]},s=(a,o="")=>{if(!(!a||typeof a!="object")){if(a.components){const r=Object.keys(a.components);n.allowedComponents.push(...r)}Object.entries(a).forEach(([r,c])=>{typeof c=="object"&&c!==null&&!r.startsWith("jcr:")&&s(c,o?`${o}/${r}`:r)})}};return s(n.policies),n.allowedComponents=[...new Set(n.allowedComponents)],i({templatePath:e,structure:n,fullData:t},"getTemplateStructure")}catch(t){throw y(t,"getTemplateStructure")}},"getTemplateStructure")}async bulkUpdateComponents(e){return p(async()=>{const{updates:t,validateFirst:n=!0,continueOnError:s=!1}=e;if(!Array.isArray(t)||t.length===0)throw d(h.INVALID_PARAMETERS,"Updates array is required and cannot be empty");const a=[];if(n)for(const r of t)try{await this.fetch.get(`${r.componentPath}.json`)}catch(c){if(c.response?.status===404&&(a.push({componentPath:r.componentPath,success:!1,error:`Component not found: ${r.componentPath}`,phase:"validation"}),!s))return i({success:!1,message:"Bulk update failed during validation phase",results:a,totalUpdates:t.length,successfulUpdates:0},"bulkUpdateComponents")}let o=0;for(const r of t)try{const c=await this.updateComponent({componentPath:r.componentPath,properties:r.properties});a.push({componentPath:r.componentPath,success:!0,result:c,phase:"update"}),o++}catch(c){if(a.push({componentPath:r.componentPath,success:!1,error:c.message,phase:"update"}),!s)break}return i({success:o===t.length,message:`Bulk update completed: ${o}/${t.length} successful`,results:a,totalUpdates:t.length,successfulUpdates:o,failedUpdates:t.length-o},"bulkUpdateComponents")},"bulkUpdateComponents")}async getNodeContent(e,t=1){return p(async()=>{const n=`${e}.json`,s=await this.fetch.get(n,{":depth":t.toString()});return{path:e,depth:t,content:s,timestamp:new Date().toISOString()}},"getNodeContent")}}
package/dist/cli.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- "use strict";import t from"yargs";import{hideBin as a}from"yargs/helpers";import{startServer as e}from"./index.js";const s=t(a(process.argv)).options({host:{type:"string",default:"http://localhost:4502",alias:"H"},user:{type:"string",default:"admin",alias:"u"},pass:{type:"string",default:"admin",alias:"p"},id:{type:"string",default:"",alias:"i"},secret:{type:"string",default:"",alias:"s"},mcpPort:{type:"number",default:8502,alias:"m"}}).help().alias("h","help").parseSync();s.help&&process.exit(0);const{host:r,user:i,pass:p,mcpPort:l,id:o,secret:n}=s;e({host:r,user:i,pass:p,mcpPort:l,id:o,secret:n});
2
+ "use strict";import s from"yargs";import{hideBin as t}from"yargs/helpers";import{startServer as r}from"./index.js";const e=s(t(process.argv)).options({host:{type:"string",default:"http://localhost:4502",alias:"H"},user:{type:"string",default:"admin",alias:"u"},pass:{type:"string",default:"admin",alias:"p"},id:{type:"string",default:"",alias:"i",describe:"clientId"},secret:{type:"string",default:"",alias:"s",describe:"clientSecret"},mcpPort:{type:"number",default:8502,alias:"m"}}).help().alias("h","help").parseSync();e.help&&process.exit(0);const{host:a,user:i,pass:l,mcpPort:p,id:o,secret:n}=e;r({host:a,user:i,pass:l,mcpPort:p,id:o,secret:n});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aem-mcp-server",
3
- "version": "1.3.3",
3
+ "version": "1.3.5",
4
4
  "description": "AEM Model Context Protocol (MCP) server",
5
5
  "private": false,
6
6
  "publishConfig": {