aem-mcp-server 1.3.0 → 1.3.2

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
@@ -29,7 +29,7 @@ This project is designed for non-technical persons who want to manage AEM via na
29
29
  - Access to an AEM instance (local or remote)
30
30
 
31
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=AEM3&config=JTdCJTIyY29tbWFuZCUyMiUzQSUyMm5weCUyMC0teWVzJTIwLXAlMjBhZW0tbWNwLXNlcnZlciUyMC0tJTIwYWVtLW1jcCUyMC1IJTNEaHR0cCUzQSUyRiUyRmxvY2FsaG9zdCUzQTQ1MDIlMjIlMkMlMjJ0eXBlJTIyJTNBJTIyc3RyZWFtYWJsZS1odHRwJTIyJTJDJTIyc3RyZWFtYWJsZSUyMiUzQXRydWUlMkMlMjJ1cmwlMjIlM0ElMjJodHRwJTNBJTJGJTJGMTI3LjAuMC4xJTNBODUwMiUyRm1jcCUyMiU3RA%3D%3D)
32
+ [![Install MCP Server](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en/install-mcp?name=AEM&config=eyJ1cmwiOiJodHRwOi8vMTI3LjAuMC4xOjg1MDIvbWNwIn0%3D)
33
33
 
34
34
  OR:
35
35
 
@@ -51,7 +51,7 @@ Options:
51
51
  -P, --publisher Publisher instance URL [string] [default: "http://localhost:4503"]
52
52
  -u, --user Username for authentication [string] [default: "admin"]
53
53
  -p, --pass Password for authentication [string] [default: "admin"]
54
- -m, --mcpPort Port for MCP server [number] [default: 3000]
54
+ -m, --mcpPort Port for MCP server [number] [default: 8502]
55
55
  -h, --help Show help [boolean]
56
56
 
57
57
  ```
@@ -82,7 +82,7 @@ AEM MCP Server is compatible with modern AI IDEs and code editors that support M
82
82
  - Open your IDE's MCP server settings.
83
83
  - Add a new server with:
84
84
  - **Type:** Custom MCP
85
- - **url:** `http://127.0.0.1:3000/mcp`
85
+ - **url:** `http://127.0.0.1:8502/mcp`
86
86
 
87
87
  3. **Restart your IDE** if needed. The IDE will now be able to:
88
88
  - List, search, and manage AEM content
@@ -94,7 +94,7 @@ Sample for AI-based code editors or custom clients:
94
94
  {
95
95
  "mcpServers": {
96
96
  "AEM": {
97
- "url": "http://127.0.0.1:3000/mcp"
97
+ "url": "http://127.0.0.1:8502/mcp"
98
98
  }
99
99
  }
100
100
  }
@@ -1 +1 @@
1
- "use strict";import{getAEMConfig as P,isValidContentPath as u,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={}){return{aem:{host:e.host||"http://localhost:4502",author:e.host||"http://localhost:4502",publish:"http://localhost:4503",auth:{username:e.user||"admin",password:e.pass||"admin"},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(!u(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(!u(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 m=r?`${r}/${c}`:c;a(l,m)}}))};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 m=r?`${r}/${c}`:c;a(l,m)}}))};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 m=r?`${r}/${c}`:c;a(l,m)}}))};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(([m,f])=>{if(!(a.length>=n)&&!(m.startsWith("jcr:")||m.startsWith("sling:")||m.startsWith("cq:")||m.startsWith("rep:")||m.startsWith("oak:"))&&f&&typeof f=="object"){const j=`${c}/${m}`;f["jcr:primaryType"]==="cq:Page"&&a.push({name:m,path:j,primaryType:"cq:Page",title:f["jcr:content"]?.["jcr:title"]||m,template:f["jcr:content"]?.["cq:template"],lastModified:f["jcr:content"]?.["cq:lastModified"],lastModifiedBy:f["jcr:content"]?.["cq:lastModifiedBy"],resourceType:f["jcr:content"]?.["sling:resourceType"],type:"page"}),l<t&&o(f,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(!u(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(!u(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(!u(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(!u(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(!u(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(!u(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(!u(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(([f,j])=>{c.append(`jcr:content/metadata/${f}`,String(j))});const l=await this.fetch.post(r,c),m=await this.fetch.get(`${r}.json`);return i({success:!0,assetPath:r,fileName:n,mimeType:a,metadata:o,uploadResponse:l,assetData:m,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(!u(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(!u(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/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")}}
package/dist/cli.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- "use strict";import r from"yargs";import{hideBin as a}from"yargs/helpers";import{startServer as t}from"./index.js";const s=r(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"},mcpPort:{type:"number",default:8502,alias:"m"}}).help().alias("h","help").parseSync();s.help&&process.exit(0);const{host:e,user:i,pass:o,mcpPort:p}=s;t({host:e,user:i,pass:o,mcpPort:p});
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});
@@ -1,2 +1,2 @@
1
- "use strict";function o(){const c=(new Error().stack?.split(`
2
- `)||[])[3]||"",e=c.match(/\(([^)]+)\)/);return e?e[1]:c.trim()}const r=process.env.ENABLE_LOGGER==="true";export const LOGGER={log:(...n)=>{r&&console.log(`[${o()}]`,...n)},info:(...n)=>{r&&console.info(`[${o()}]`,...n)},warn:(...n)=>{r&&console.warn(`[${o()}]`,...n)},error:(...n)=>{r&&console.error(`[${o()}]`,...n)}};
1
+ "use strict";const a=(n,t)=>`\x1B]8;;${t}\x07${n}\x1B]8;;\x07`;function o(){const s=(new Error().stack?.split(`
2
+ `)||[])[3]||"",c=s.match(/\(([^)]+)\)/),e=c?c[1]:s.trim(),i=e.split("/").pop()||"unknown";return a(`${i}`,`${e}`)}const r=!!process.env.MCP_LOGGER;export const LOGGER={log:(...n)=>{r&&console.log(`[${o()}]`,...n)},info:(...n)=>{r&&console.info(`[${o()}]`,...n)},warn:(...n)=>{r&&console.warn(`[${o()}]`,...n)},error:(...n)=>{r&&console.error(`[${o()}]`,...n)}};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aem-mcp-server",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
4
4
  "description": "AEM Model Context Protocol (MCP) server",
5
5
  "private": false,
6
6
  "publishConfig": {
@@ -25,8 +25,6 @@
25
25
  },
26
26
  "files": [
27
27
  "dist/**/*.js",
28
- "build/**/*.d.ts",
29
- "docs/",
30
28
  "README.md",
31
29
  "LICENSE"
32
30
  ],
package/docs/API.md DELETED
@@ -1,286 +0,0 @@
1
- ```json
2
- {
3
- "methods": [
4
- {
5
- "name": "validateComponent",
6
- "description": "Validate component changes before applying them",
7
- "parameters": [
8
- "locale",
9
- "page_path",
10
- "component",
11
- "props"
12
- ]
13
- },
14
- {
15
- "name": "updateComponent",
16
- "description": "Update component properties in AEM",
17
- "parameters": [
18
- "componentPath",
19
- "properties"
20
- ]
21
- },
22
- {
23
- "name": "undoChanges",
24
- "description": "Undo the last component changes",
25
- "parameters": [
26
- "job_id"
27
- ]
28
- },
29
- {
30
- "name": "scanPageComponents",
31
- "description": "Scan a page to discover all components and their properties",
32
- "parameters": [
33
- "pagePath"
34
- ]
35
- },
36
- {
37
- "name": "fetchSites",
38
- "description": "Get all available sites in AEM",
39
- "parameters": []
40
- },
41
- {
42
- "name": "fetchLanguageMasters",
43
- "description": "Get language masters for a specific site",
44
- "parameters": [
45
- "site"
46
- ]
47
- },
48
- {
49
- "name": "fetchAvailableLocales",
50
- "description": "Get available locales for a site and language master",
51
- "parameters": [
52
- "site",
53
- "languageMasterPath"
54
- ]
55
- },
56
- {
57
- "name": "replicateAndPublish",
58
- "description": "Replicate and publish content to selected locales",
59
- "parameters": [
60
- "selectedLocales",
61
- "componentData",
62
- "localizedOverrides"
63
- ]
64
- },
65
- {
66
- "name": "getAllTextContent",
67
- "description": "Get all text content from a page including titles, text components, and descriptions",
68
- "parameters": [
69
- "pagePath"
70
- ]
71
- },
72
- {
73
- "name": "getPageTextContent",
74
- "description": "Get text content from a specific page",
75
- "parameters": [
76
- "pagePath"
77
- ]
78
- },
79
- {
80
- "name": "getPageImages",
81
- "description": "Get all images from a page, including those within Experience Fragments",
82
- "parameters": [
83
- "pagePath"
84
- ]
85
- },
86
- {
87
- "name": "updateImagePath",
88
- "description": "Update the image path for an image component and verify the update",
89
- "parameters": [
90
- "componentPath",
91
- "newImagePath"
92
- ]
93
- },
94
- {
95
- "name": "getPageContent",
96
- "description": "Get all content from a page including Experience Fragments and Content Fragments",
97
- "parameters": [
98
- "pagePath"
99
- ]
100
- },
101
- {
102
- "name": "listPages",
103
- "description": "List all pages under a site root",
104
- "parameters": [
105
- "siteRoot",
106
- "depth",
107
- "limit"
108
- ]
109
- },
110
- {
111
- "name": "getNodeContent",
112
- "description": "Legacy: Get JCR node content",
113
- "parameters": [
114
- "path",
115
- "depth"
116
- ]
117
- },
118
- {
119
- "name": "listChildren",
120
- "description": "Legacy: List child nodes",
121
- "parameters": [
122
- "path"
123
- ]
124
- },
125
- {
126
- "name": "getPageProperties",
127
- "description": "Get page properties",
128
- "parameters": [
129
- "pagePath"
130
- ]
131
- },
132
- {
133
- "name": "searchContent",
134
- "description": "Search content using Query Builder",
135
- "parameters": [
136
- "type",
137
- "fulltext",
138
- "path",
139
- "limit"
140
- ]
141
- },
142
- {
143
- "name": "executeJCRQuery",
144
- "description": "Execute JCR query",
145
- "parameters": [
146
- "query",
147
- "limit"
148
- ]
149
- },
150
- {
151
- "name": "getAssetMetadata",
152
- "description": "Get asset metadata",
153
- "parameters": [
154
- "assetPath"
155
- ]
156
- },
157
- {
158
- "name": "getStatus",
159
- "description": "Get workflow status by ID",
160
- "parameters": [
161
- "workflowId"
162
- ]
163
- },
164
- {
165
- "name": "listMethods",
166
- "description": "Get list of available MCP methods",
167
- "parameters": []
168
- },
169
- {
170
- "name": "enhancedPageSearch",
171
- "description": "Intelligent page search with comprehensive fallback strategies and cross-section search",
172
- "parameters": [
173
- "searchTerm",
174
- "basePath",
175
- "includeAlternateLocales"
176
- ]
177
- },
178
- {
179
- "name": "createPage",
180
- "description": "Create a new page in AEM",
181
- "parameters": [
182
- "parentPath",
183
- "title",
184
- "template",
185
- "name",
186
- "properties"
187
- ]
188
- },
189
- {
190
- "name": "deletePage",
191
- "description": "Delete a page from AEM",
192
- "parameters": [
193
- "pagePath",
194
- "force"
195
- ]
196
- },
197
- {
198
- "name": "createComponent",
199
- "description": "Create a new component on a page",
200
- "parameters": [
201
- "pagePath",
202
- "componentType",
203
- "resourceType",
204
- "properties",
205
- "name"
206
- ]
207
- },
208
- {
209
- "name": "deleteComponent",
210
- "description": "Delete a component from AEM",
211
- "parameters": [
212
- "componentPath",
213
- "force"
214
- ]
215
- },
216
- {
217
- "name": "unpublishContent",
218
- "description": "Unpublish content from the publish environment",
219
- "parameters": [
220
- "contentPaths",
221
- "unpublishTree"
222
- ]
223
- },
224
- {
225
- "name": "activatePage",
226
- "description": "Activate (publish) a single page",
227
- "parameters": [
228
- "pagePath",
229
- "activateTree"
230
- ]
231
- },
232
- {
233
- "name": "deactivatePage",
234
- "description": "Deactivate (unpublish) a single page",
235
- "parameters": [
236
- "pagePath",
237
- "deactivateTree"
238
- ]
239
- },
240
- {
241
- "name": "uploadAsset",
242
- "description": "Upload a new asset to AEM DAM",
243
- "parameters": [
244
- "parentPath",
245
- "fileName",
246
- "fileContent",
247
- "mimeType",
248
- "metadata"
249
- ]
250
- },
251
- {
252
- "name": "updateAsset",
253
- "description": "Update an existing asset in AEM DAM",
254
- "parameters": [
255
- "assetPath",
256
- "metadata",
257
- "fileContent",
258
- "mimeType"
259
- ]
260
- },
261
- {
262
- "name": "deleteAsset",
263
- "description": "Delete an asset from AEM DAM",
264
- "parameters": [
265
- "assetPath",
266
- "force"
267
- ]
268
- },
269
- {
270
- "name": "getTemplates",
271
- "description": "Get available page templates",
272
- "parameters": [
273
- "sitePath"
274
- ]
275
- },
276
- {
277
- "name": "getTemplateStructure",
278
- "description": "Get detailed structure of a specific template",
279
- "parameters": [
280
- "templatePath"
281
- ]
282
- }
283
- ],
284
- "total": 35
285
- }
286
- ```
package/docs/CHANGELOG.md DELETED
@@ -1,147 +0,0 @@
1
- # [1.3.0](https://github.com/easingthemes/aem-mcp-server/compare/v1.2.0...v1.3.0) (2025-08-10)
2
-
3
-
4
- ### Features
5
-
6
- * add logger, change port ([f4ee48b](https://github.com/easingthemes/aem-mcp-server/commit/f4ee48b32fde2034b53f29fb2efa3aef9ba20f39))
7
-
8
- # [1.2.0](https://github.com/easingthemes/aem-mcp-server/compare/v1.1.5...v1.2.0) (2025-08-10)
9
-
10
-
11
- ### Features
12
-
13
- * add AEMaaCS OAuth support, add fetch retry on 401 ([0f53d1a](https://github.com/easingthemes/aem-mcp-server/commit/0f53d1a4bc8bc622f018f68c021d95e4a0791e78))
14
-
15
- ## [1.1.5](https://github.com/easingthemes/aem-mcp-server/compare/v1.1.4...v1.1.5) (2025-08-08)
16
-
17
-
18
- ### Bug Fixes
19
-
20
- * remove invalid empty `prompts` ([92c64d3](https://github.com/easingthemes/aem-mcp-server/commit/92c64d3d2db3ff32ce0e2b651b22697940761dd0))
21
-
22
- ## [1.1.4](https://github.com/easingthemes/aem-mcp-server/compare/v1.1.3...v1.1.4) (2025-07-19)
23
-
24
-
25
- ### Bug Fixes
26
-
27
- * [docs] simplify Readme ([6e00bef](https://github.com/easingthemes/aem-mcp-server/commit/6e00bef7a498d872573549cc7d35206affe98bba))
28
-
29
- ## [1.1.3](https://github.com/easingthemes/aem-mcp-server/compare/v1.1.2...v1.1.3) (2025-07-19)
30
-
31
-
32
- ### Bug Fixes
33
-
34
- * [docs] simplify Readme ([12d6753](https://github.com/easingthemes/aem-mcp-server/commit/12d6753d214de6eb49451595025de95df170f66b))
35
-
36
- ## [1.1.2](https://github.com/easingthemes/aem-mcp-server/compare/v1.1.1...v1.1.2) (2025-07-19)
37
-
38
-
39
- ### Bug Fixes
40
-
41
- * [docs] simplify Readme ([c9af9e6](https://github.com/easingthemes/aem-mcp-server/commit/c9af9e67043ced9e54a6bbf83cd1527026ae0c69))
42
-
43
- ## [1.1.1](https://github.com/easingthemes/aem-mcp-server/compare/v1.1.0...v1.1.1) (2025-07-19)
44
-
45
-
46
- ### Bug Fixes
47
-
48
- * [docs] simplify Readme ([3d70ee2](https://github.com/easingthemes/aem-mcp-server/commit/3d70ee2acbdc7cdca291adc326bacf804fd7700c))
49
-
50
- # [1.1.0](https://github.com/easingthemes/aem-mcp-server/compare/v1.0.12...v1.1.0) (2025-07-19)
51
-
52
-
53
- ### Features
54
-
55
- * [deps] remove swagger ([6cfa01a](https://github.com/easingthemes/aem-mcp-server/commit/6cfa01ac1d8336fb2a5de300cedcc5581726512f))
56
-
57
- ## [1.0.12](https://github.com/easingthemes/aem-mcp-server/compare/v1.0.11...v1.0.12) (2025-07-18)
58
-
59
-
60
- ### Bug Fixes
61
-
62
- * [aem] remove component type check ([6dbf89c](https://github.com/easingthemes/aem-mcp-server/commit/6dbf89c39f78b24e33b3e34992f886c761e6f572))
63
-
64
- ## [1.0.11](https://github.com/easingthemes/aem-mcp-server/compare/v1.0.10...v1.0.11) (2025-07-18)
65
-
66
-
67
- ### Bug Fixes
68
-
69
- * [build] minify ([4760bdc](https://github.com/easingthemes/aem-mcp-server/commit/4760bdcccf6c38b10df4f472c6900279bc09cacb))
70
-
71
- ## [1.0.10](https://github.com/easingthemes/aem-mcp-server/compare/v1.0.9...v1.0.10) (2025-07-18)
72
-
73
-
74
- ### Bug Fixes
75
-
76
- * [docs] add docs dir ([d8e6f04](https://github.com/easingthemes/aem-mcp-server/commit/d8e6f04dc8963d559d258d1491d5ea81e4b9d9e0))
77
-
78
- ## [1.0.9](https://github.com/easingthemes/aem-mcp-server/compare/v1.0.8...v1.0.9) (2025-07-18)
79
-
80
-
81
- ### Bug Fixes
82
-
83
- * [docs] add params info ([b4f128d](https://github.com/easingthemes/aem-mcp-server/commit/b4f128db6aed5d159cd18aa93692e2948c99ffb0))
84
-
85
- ## [1.0.8](https://github.com/easingthemes/aem-mcp-server/compare/v1.0.7...v1.0.8) (2025-07-18)
86
-
87
-
88
- ### Bug Fixes
89
-
90
- * [config] remove dotenv ([2c0ddcd](https://github.com/easingthemes/aem-mcp-server/commit/2c0ddcdb9b5b2c60bb27bfad8dcfeebe39275510))
91
-
92
- ## [1.0.7](https://github.com/easingthemes/aem-mcp-server/compare/v1.0.6...v1.0.7) (2025-07-18)
93
-
94
-
95
- ### Bug Fixes
96
-
97
- * [cli] npm pkg fix ([34dc243](https://github.com/easingthemes/aem-mcp-server/commit/34dc243d1064120b595bc0eb242e3c446bb4c54c))
98
-
99
- ## [1.0.6](https://github.com/easingthemes/aem-mcp-server/compare/v1.0.5...v1.0.6) (2025-07-18)
100
-
101
-
102
- ### Bug Fixes
103
-
104
- * [cli] add node bin ([50d73f4](https://github.com/easingthemes/aem-mcp-server/commit/50d73f4a1ee1ee037366aa8a0d42639c8602a2fe))
105
-
106
- ## [1.0.5](https://github.com/easingthemes/aem-mcp-server/compare/v1.0.4...v1.0.5) (2025-07-18)
107
-
108
-
109
- ### Bug Fixes
110
-
111
- * [cli] remove test script ([e9d2325](https://github.com/easingthemes/aem-mcp-server/commit/e9d2325e46976965df6fa4e04bfda2047a0927d5))
112
- * [cli] use es6 ([ef3327c](https://github.com/easingthemes/aem-mcp-server/commit/ef3327c9c6f78e459e756c0f41c13b6002c19193))
113
-
114
- ## [1.0.4](https://github.com/easingthemes/aem-mcp-server/compare/v1.0.3...v1.0.4) (2025-07-18)
115
-
116
-
117
- ### Bug Fixes
118
-
119
- * [cli] fix cli params ([6405d05](https://github.com/easingthemes/aem-mcp-server/commit/6405d0584586bf2e06c6333757ccee292dceac38))
120
-
121
- ## [1.0.3](https://github.com/easingthemes/aem-mcp-server/compare/v1.0.2...v1.0.3) (2025-07-18)
122
-
123
-
124
- ### Bug Fixes
125
-
126
- * [build] fix build path ([683b87f](https://github.com/easingthemes/aem-mcp-server/commit/683b87fb3cad42867baa851cce766ec956c1c637))
127
-
128
- ## [1.0.2](https://github.com/easingthemes/aem-mcp-server/compare/v1.0.1...v1.0.2) (2025-07-18)
129
-
130
-
131
- ### Bug Fixes
132
-
133
- * [cli] add cli params ([59c6a5e](https://github.com/easingthemes/aem-mcp-server/commit/59c6a5ef72ea2fe5f16278655af433b3a17843fc))
134
-
135
- ## [1.0.1](https://github.com/easingthemes/aem-mcp-server/compare/v1.0.0...v1.0.1) (2025-07-18)
136
-
137
-
138
- ### Bug Fixes
139
-
140
- * [cli] simplify usage ([3e5981c](https://github.com/easingthemes/aem-mcp-server/commit/3e5981c49a8a843eda16482f041a2c291051d68e))
141
-
142
- # 1.0.0 (2025-07-17)
143
-
144
-
145
- ### Bug Fixes
146
-
147
- * [npm] enable npm release ([3165378](https://github.com/easingthemes/aem-mcp-server/commit/3165378659357d060c03e0798ad50eb44328d429))
@@ -1,74 +0,0 @@
1
- # Netcentric Code of Conduct
2
-
3
- ## Our Pledge
4
-
5
- In the interest of fostering an open and welcoming environment, we as
6
- contributors and maintainers pledge to making participation in our project and
7
- our community a harassment-free experience for everyone, regardless of age, body
8
- size, disability, ethnicity, gender identity and expression, level of experience,
9
- nationality, personal appearance, race, religion, or sexual identity and
10
- orientation.
11
-
12
- ## Our Standards
13
-
14
- Examples of behavior that contributes to creating a positive environment
15
- include:
16
-
17
- * Using welcoming and inclusive language
18
- * Being respectful of differing viewpoints and experiences
19
- * Gracefully accepting constructive criticism
20
- * Focusing on what is best for the community
21
- * Showing empathy towards other community members
22
-
23
- Examples of unacceptable behavior by participants include:
24
-
25
- * The use of sexualized language or imagery and unwelcome sexual attention or
26
- advances
27
- * Trolling, insulting/derogatory comments, and personal or political attacks
28
- * Public or private harassment
29
- * Publishing others' private information, such as a physical or electronic
30
- address, without explicit permission
31
- * Other conduct which could reasonably be considered inappropriate in a
32
- professional setting
33
-
34
- ## Our Responsibilities
35
-
36
- Project maintainers are responsible for clarifying the standards of acceptable
37
- behavior and are expected to take appropriate and fair corrective action in
38
- response to any instances of unacceptable behavior.
39
-
40
- Project maintainers have the right and responsibility to remove, edit, or
41
- reject comments, commits, code, wiki edits, issues, and other contributions
42
- that are not aligned to this Code of Conduct, or to ban temporarily or
43
- permanently any contributor for other behaviors that they deem inappropriate,
44
- threatening, offensive, or harmful.
45
-
46
- ## Scope
47
-
48
- This Code of Conduct applies both within project spaces and in public spaces
49
- when an individual is representing the project or its community. Examples of
50
- representing a project or community include using an official project e-mail
51
- address, posting via an official social media account, or acting as an appointed
52
- representative at an online or offline event. Representation of a project may be
53
- further defined and clarified by project maintainers.
54
-
55
- ## Enforcement
56
-
57
- Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
- reported by contacting the project team at Grp-opensourceoffice@adobe.com. All
59
- complaints will be reviewed and investigated and will result in a response that
60
- is deemed necessary and appropriate to the circumstances. The project team is
61
- obligated to maintain confidentiality with regard to the reporter of an incident.
62
- Further details of specific enforcement policies may be posted separately.
63
-
64
- Project maintainers who do not follow or enforce the Code of Conduct in good
65
- faith may face temporary or permanent repercussions as determined by other
66
- members of the project's leadership.
67
-
68
- ## Attribution
69
-
70
- This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
- available at [http://contributor-covenant.org/version/1/4][version]
72
-
73
- [homepage]: https://contributor-covenant.org
74
- [version]: https://contributor-covenant.org/version/1/4/
@@ -1,25 +0,0 @@
1
- # Contributing
2
-
3
- Thanks for choosing to contribute!
4
-
5
- The following are a set of guidelines to follow when contributing to this project.
6
-
7
- ## Code Of Conduct
8
-
9
- This project adheres to the <COMPANY> [code of conduct](CODE_OF_CONDUCT.md). By participating,
10
- you are expected to uphold this code.
11
-
12
- ## Have A Question?
13
-
14
- Start by filing an issue. The existing committers on this project work to reach
15
- consensus around project direction and issue solutions within issue threads
16
- (when appropriate).
17
-
18
- ## Code Reviews
19
-
20
- All submissions should come in the form of pull requests and need to be reviewed
21
- by project committers. Read [GitHub's pull request documentation](https://help.github.com/articles/about-pull-requests/)
22
- for more information on sending pull requests.
23
-
24
- Lastly, please follow the [pull request template](../.github/PULL_REQUEST_TEMPLATE.md) when
25
- submitting a pull request!
package/docs/cursor.png DELETED
Binary file