@weppy/roblox-mcp 2.7.6 → 2.7.8

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.
Files changed (29) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +8 -8
  3. package/dashboard/dist/assets/{ChangelogDetailPage-DglsIYkW.js → ChangelogDetailPage-B7ugySeq.js} +1 -1
  4. package/dashboard/dist/assets/{ChangelogPage-65B3_w0_.js → ChangelogPage-B7-iV5Ir.js} +1 -1
  5. package/dashboard/dist/assets/{ConfirmModal-Cpk7SbKb.js → ConfirmModal-Dg8_uxRE.js} +1 -1
  6. package/dashboard/dist/assets/{ConnectionPage-B-IN5LsC.js → ConnectionPage-COgl_4EK.js} +1 -1
  7. package/dashboard/dist/assets/{GameChangeDetail-DM3mWsFX.js → GameChangeDetail-D31gfmkK.js} +1 -1
  8. package/dashboard/dist/assets/{InfoLabel-B_fEbHa7.js → InfoLabel-Bp3j_i44.js} +1 -1
  9. package/dashboard/dist/assets/{OverviewPage-B4O0bv4R.js → OverviewPage-Bl67HNuU.js} +1 -1
  10. package/dashboard/dist/assets/{PlaytestPage-BHLRKn8U.js → PlaytestPage-D-gmWEk9.js} +1 -1
  11. package/dashboard/dist/assets/{SettingsPage-DmIKC_O1.js → SettingsPage-BdUpzUq9.js} +1 -1
  12. package/dashboard/dist/assets/{StatusBadge-DRdnq30k.js → StatusBadge-4qs9buFz.js} +1 -1
  13. package/dashboard/dist/assets/{SyncPage-CW_0kNpZ.js → SyncPage-BaOvmPdn.js} +1 -1
  14. package/dashboard/dist/assets/{Tabs-BsTVkBUh.js → Tabs-BCiSXsKz.js} +1 -1
  15. package/dashboard/dist/assets/{TierComparison-poRtDe46.js → TierComparison-Lkag7n54.js} +1 -1
  16. package/dashboard/dist/assets/{ToolsPage-D77yJ9jZ.js → ToolsPage-BHGtm0IX.js} +1 -1
  17. package/dashboard/dist/assets/{TooltipText-DX5jnyNF.js → TooltipText-Bgk-NYKr.js} +1 -1
  18. package/dashboard/dist/assets/{UiStudioPage-YtdlkQzT.js → UiStudioPage-CvpsOKcP.js} +1 -1
  19. package/dashboard/dist/assets/WhatsNewPage-D5OncQnl.css +1 -0
  20. package/dashboard/dist/assets/WhatsNewPage-g_24s3hx.js +1 -0
  21. package/dashboard/dist/assets/{index-BPIBy2lU.js → index-C9gmLH8z.js} +102 -44
  22. package/dashboard/dist/assets/{sample-requests-CwDMfktX.js → sample-requests-BSEgt_Th.js} +1 -1
  23. package/dashboard/dist/assets/{useLiveUptime-ElD9lDzh.js → useLiveUptime-gW3LP94r.js} +1 -1
  24. package/dashboard/dist/index.html +1 -1
  25. package/dist/index.js +1 -1
  26. package/package.json +1 -1
  27. package/roblox-plugin/WeppyRobloxMCP.rbxm +0 -0
  28. package/dashboard/dist/assets/WhatsNewPage--uCu0xCm.js +0 -1
  29. package/dashboard/dist/assets/WhatsNewPage-Lxgj0StO.css +0 -1
package/dist/index.js CHANGED
@@ -501,7 +501,7 @@ data: ${JSON.stringify(n)}
501
501
  `)}catch(i){y.warn("Failed to flush change log",{error:i instanceof Error?i.message:String(i),entries:e.length})}}startChangeLogFlusher(){this.flushTimer||(this.flushTimer=setInterval(()=>{this.flushChangeLog(),this.flushHistory()},Ace),this.flushTimer&&typeof this.flushTimer=="object"&&"unref"in this.flushTimer&&this.flushTimer.unref())}async stopChangeLogFlusher(){this.flushTimer&&(clearInterval(this.flushTimer),this.flushTimer=null),await this.flushChangeLog(),await this.flushHistory()}async updateSyncIndex(e,n){this.index.updateBothHashes(e,n)}relativePath(e){return ht.relative(this.config.getSyncRoot(),e)}extractServiceName(e){return jt(e)[0]??""}findTreeNode(e,n){let i=jt(n);if(i.length<=1)return null;let r=e.children,a;for(let o=1;o<i.length;o++){if(!r||(a=r.find(s=>s.name===i[o]),!a))return null;r=a.children}return a??null}detachTreeNode(e,n){let i=this.getParentPath(n),r=this.getLeafName(n);if(!i||!r)return null;let a=this.findTreeNode(e,i),o=a?.children??e.children,s=o.findIndex(l=>l.name===r);if(s<0)return null;let[c]=o.splice(s,1);return a?a.childCount=o.length:e.childCount=o.length,c??null}insertTreeNode(e,n,i){let r=this.findTreeNode(e,n);if(r){r.children||(r.children=[]);let o=r.children.findIndex(s=>s.name===i.name);o>=0?r.children[o]=i:r.children.push(i),r.childCount=r.children.length;return}let a=e.children.findIndex(o=>o.name===i.name);a>=0?e.children[a]=i:e.children.push(i),e.childCount=e.children.length}getLeafName(e){let n=jt(e);return n.length===0?null:n[n.length-1]??null}getParentPath(e){let n=jt(e);return n.length<=1?null:`game.${n.slice(0,-1).join(".")}`}async copyDirectory(e,n){await this.ensureDir(n);let i=await Qe.readdir(e,{withFileTypes:!0});for(let r of i){let a=ht.join(e,r.name),o=ht.join(n,r.name);r.isDirectory()?await this.copyDirectory(a,o):await Qe.copyFile(a,o)}}async tryRename(e,n){try{return this.onWriteCallback?.(e),this.onWriteCallback?.(n),await Qe.rename(e,n),!0}catch{return!1}}};function pP(t,e){if(t===e)return!0;if(t===null||e===null||typeof t!=typeof e||typeof t!="object")return!1;if(Array.isArray(t))return!Array.isArray(e)||t.length!==e.length?!1:t.every((o,s)=>pP(o,e[s]));let n=t,i=e,r=Object.keys(n),a=Object.keys(i);return r.length!==a.length?!1:r.every(o=>Object.hasOwn(i,o)&&pP(n[o],i[o]))}function jce(t,e){let n={},i=new Set([...Object.keys(t??{}),...Object.keys(e)]);for(let r of i){let a=t?.[r]??null,o=e[r]??null;pP(a,o)||(n[r]={before:a,after:o})}return n}import qe from"path";import{promises as fo}from"fs";ie();var wv=class{index;placeRoot;constructor(e,n,i){this.index=n,this.placeRoot=i??e.getSyncRoot()}async buildReverseChanges(){let{modified:e,deleted:n}=await this.index.scanForFileModifications(),i=[];for(let a of e){if(this.isForwardOnlyPath(a.fsPath)){this.logForwardOnlySkip(a.fsPath,"buildReverseChanges:modified");continue}try{let o;a.fileType==="script"?o=await this.readModifiedScript(a.fsPath,a.instancePath):a.fileType==="value"?o=await this.readModifiedValue(a.fsPath,a.instancePath):o=await this.readModifiedProps(a.fsPath,a.instancePath),i.push(o)}catch(o){y.warn("Failed to read modified file for reverse sync",{path:a.fsPath,error:o instanceof Error?o.message:String(o)})}}let r=new Set;for(let a of n){if(this.isForwardOnlyPath(a.fsPath)){this.logForwardOnlySkip(a.fsPath,"buildReverseChanges:deleted");continue}if(r.has(a.instancePath))continue;r.add(a.instancePath);let o=qe.relative(this.placeRoot,a.fsPath);i.push({type:"instanceRemoved",instancePath:a.instancePath,fsPath:o,fileType:a.fileType,content:null})}return i}async buildChangesFromPending(e){let n=[],i=new Set,r=new Set,a=new Set;for(let o of e)if(o.type==="add"||o.type==="change"){let s=qe.dirname(o.fsPath),c=this.getFileType(o.fsPath);(c==="props"||c==="script"||c==="value")&&a.add(s)}for(let o of e)try{if(this.isForwardOnlyPath(o.fsPath)){this.logForwardOnlySkip(o.fsPath,`buildChangesFromPending:${o.type}`);continue}if(o.type==="addDir"||o.type==="unlinkDir"){let p=this.resolveInstancePathFromDirectory(o.fsPath);if(!p)continue;let f=this.getSiblingIndexForDirectory(o.fsPath),m=this.buildPendingDedupeKey(p,f);if(o.type==="unlinkDir"){if(i.has(m)||(i.add(m),this.isServiceInstance(p)))continue;let x=qe.relative(this.placeRoot,o.fsPath),S={type:"instanceRemoved",instancePath:p,fsPath:x,fileType:"directory",content:null};f&&(S.siblingIndex=f),n.push(S);continue}if(r.has(m)||this.isServiceInstance(p)||a.has(o.fsPath)||!await this.isContainerDirectory(o.fsPath))continue;let g=qe.relative(this.placeRoot,o.fsPath),v={type:"containerCreated",instancePath:p,fsPath:g,fileType:"directory",content:null,className:"Folder",parentPath:this.extractParentPath(p)};f&&(v.siblingIndex=f),r.add(m),n.push(v);continue}let s=this.resolveInstancePathFromFile(o.fsPath);if(!s)continue;let c=this.getSiblingIndexForFile(o.fsPath);if(o.type==="unlink"){let p=this.buildPendingDedupeKey(s,c);if(i.has(p)||(i.add(p),this.isServiceInstance(s)))continue;let f=qe.relative(this.placeRoot,o.fsPath),m=this.getFileType(o.fsPath);if(m!=="script"&&m!=="props"&&m!=="value")continue;let h={type:"instanceRemoved",instancePath:s,fsPath:f,fileType:m,content:null};c&&(h.siblingIndex=c),n.push(h);continue}let l=this.getFileType(o.fsPath),u=!this.index.hasEntry(o.fsPath)&&!this.index.hasInstancePath(s),d;if(u&&o.type==="add"){if(this.isServiceInstance(s))continue;if(l==="script")d=await this.readNewScript(o.fsPath,s);else if(l==="value")d=await this.readNewValue(o.fsPath,s);else if(l==="props")d=await this.readNewInstance(o.fsPath,s);else continue}else if(l==="script")d=await this.readModifiedScript(o.fsPath,s);else if(l==="value")d=await this.readModifiedValue(o.fsPath,s);else if(l==="props")d=await this.readModifiedProps(o.fsPath,s);else continue;c&&(d.siblingIndex=c),n.push(d)}catch(s){y.warn("Failed to build change from pending entry",{path:o.fsPath,type:o.type,error:s instanceof Error?s.message:String(s)})}return n}resolveInstancePathFromFile(e){return this.index.resolveInstancePathFromFsPath(e)}getSiblingIndexForFile(e){let n=this.index.getExplorerRoot();if(qe.basename(e)==="_tree.json")return;let r=qe.relative(n,e);if(r.startsWith(".."))return;let a=r.split(qe.sep).filter(d=>d.length>0);if(a.length<2)return;let o=a[a.length-2],s=qe.dirname(e),c=qe.dirname(s),l=this.index.getOriginalInstance(c,o);return l?l.siblingIndex:Rr(o).siblingIndex}isForwardOnlyPath(e){return this.index.isPathUnderClass(e,mc)}logForwardOnlySkip(e,n){y.info("Skipped reverse change for forward-only path",{source:n,path:qe.relative(this.placeRoot,e),className:this.index.getClassNameForFsPath(e)??"unknown"})}getFileType(e){return this.index.getFileTypeFromPath(e)}resolveInstancePathFromDirectory(e){let n=this.index.getExplorerRoot(),i=qe.relative(n,e);if(i.startsWith("..")||i===""||qe.isAbsolute(i))return null;let r=i.split(qe.sep).filter(s=>s.length>0);if(r.length===0)return null;let a=["game"],o=n;for(let s of r){o=qe.join(o,s);let c=qe.dirname(o);a.push(this.index.getOriginalNameForDir(c,s))}return st(a)}getSiblingIndexForDirectory(e){let n=qe.basename(e),i=qe.dirname(e),r=this.index.getOriginalInstance(i,n);return r?r.siblingIndex:Rr(n).siblingIndex}async isContainerDirectory(e){try{let n=await fo.readdir(e,{withFileTypes:!0});for(let i of n)if(i.isFile()&&sv.some(r=>i.name.endsWith(r)))return!1;return!0}catch(n){if(n.code==="ENOENT")return!1;throw n}}buildPendingDedupeKey(e,n){return n?`${e}#${n}`:e}async readModifiedScript(e,n){let i=await fo.readFile(e,"utf-8"),r=qe.relative(this.placeRoot,e);return{type:"scriptSource",instancePath:n,fsPath:r,fileType:"script",content:i}}async readModifiedProps(e,n){let i=await fo.readFile(e,"utf-8"),r=JSON.parse(i),a=qe.relative(this.placeRoot,e);return{type:"properties",instancePath:n,fsPath:a,fileType:"props",content:r}}async readModifiedValue(e,n){let i=await fo.readFile(e,"utf-8"),r=JSON.parse(i),a=qe.relative(this.placeRoot,e);return{type:"valueChanged",instancePath:n,fsPath:a,fileType:"value",content:r}}async readNewScript(e,n){let i=await fo.readFile(e,"utf-8"),r=qe.relative(this.placeRoot,e),a=qe.basename(e),o="ModuleScript";a.endsWith(".server.luau")?o="Script":a.endsWith(".client.luau")&&(o="LocalScript");let s=this.extractParentPath(n);return{type:"scriptCreated",instancePath:n,fsPath:r,fileType:"script",content:i,className:o,parentPath:s}}async readNewValue(e,n){let i=await fo.readFile(e,"utf-8"),r=JSON.parse(i),a=qe.relative(this.placeRoot,e),o=this.extractParentPath(n);return{type:"valueCreated",instancePath:n,fsPath:a,fileType:"value",content:r,className:r.className,parentPath:o}}async readNewInstance(e,n){let i=await fo.readFile(e,"utf-8"),r=JSON.parse(i),a=qe.relative(this.placeRoot,e),o=this.extractParentPath(n);return{type:"instanceCreated",instancePath:n,fsPath:a,fileType:"props",content:r,className:r.className,parentPath:o}}extractParentPath(e){return mt(e)}isServiceInstance(e){let n=ni(e);return n.length!==2||n[0]!=="game"?!1:UI.has(n[1])}};ie();import Uce from"path";ie();import na from"path";import{createHash as nU}from"crypto";import{promises as mP}from"fs";var Mce=500,fP=class{explorerRoot;syncIndex;scanIntervalMs;previousSnapshot=new Map;pendingChanges=new Map;scanTimer=null;readyResolvers=[];isReady=!1;isPaused=!1;isScanning=!1;refreshSnapshotOnNextScan=!1;scanEpoch=0;pendingSequence=0;suppressedPaths=new Map;directionChecker=null;onForwardViolation=null;getPendingKey(e,n){return`${na.resolve(e)}\0${n}`}getPendingSlot(e){return e==="addDir"||e==="unlinkDir"?"dir":"file"}getPendingRank(e){return e==="change"?1:2}isStaleScan(e){return this.isPaused||e!==this.scanEpoch}resolveReadyWaiters(){for(let e of this.readyResolvers)e();this.readyResolvers=[]}queuePendingChange(e){let n=this.getPendingSlot(e.type),i=this.getPendingKey(e.fsPath,n),r=this.getPendingRank(e.type),a=this.pendingChanges.get(i);a&&a.rank>r||this.pendingChanges.set(i,{change:e,rank:r,sequence:++this.pendingSequence})}constructor(e,n,i=Mce){this.explorerRoot=na.resolve(e),this.syncIndex=n,this.scanIntervalMs=i}setDirectionChecker(e){this.directionChecker=e}setOnForwardViolation(e){this.onForwardViolation=e}suppressPath(e,n){let i=na.resolve(e);if(n===void 0){this.suppressedPaths.set(i,{mode:"generic"});return}if(n==="missing"){this.suppressedPaths.set(i,{mode:"missing"});return}this.suppressedPaths.set(i,{mode:"hash",hash:n})}async start(){this.scanTimer&&await this.stop("restart"),y.info("SnapshotChangeScanner: starting",{explorerRoot:this.explorerRoot,scanIntervalMs:this.scanIntervalMs}),this.isPaused=!1,this.refreshSnapshotOnNextScan=!1,this.suppressedPaths.clear(),this.scanEpoch+=1;let e=this.scanEpoch;this.previousSnapshot=await this.syncIndex.buildSnapshotSeed(),!this.isStaleScan(e)&&(this.isReady=!0,this.resolveReadyWaiters(),this.scanTimer=setInterval(()=>{this.scanSnapshotDiffs().catch(n=>{y.warn("SnapshotChangeScanner: background scan failed",{error:n instanceof Error?n.message:String(n)})})},this.scanIntervalMs),typeof this.scanTimer=="object"&&this.scanTimer&&"unref"in this.scanTimer&&this.scanTimer.unref())}async stop(e){y.info("SnapshotChangeScanner: stopping",{reason:e??"unspecified",explorerRoot:this.explorerRoot,isReady:this.isReady,isPaused:this.isPaused,pendingChanges:this.pendingChanges.size}),this.scanTimer&&(clearInterval(this.scanTimer),this.scanTimer=null),this.scanEpoch+=1,this.clearTransientState(),this.previousSnapshot=new Map,this.refreshSnapshotOnNextScan=!1,this.isPaused=!1,this.suppressedPaths.clear(),this.resolveReadyWaiters(),this.isReady=!1,this.isScanning=!1}beginFullSyncReplacement(){this.scanEpoch+=1,this.isPaused=!0,this.isReady=!1,this.refreshSnapshotOnNextScan=!1,this.suppressedPaths.clear(),this.clearTransientState()}async endFullSyncReplacement(){if(this.clearTransientState(),this.scanTimer===null){await this.start();return}let e=++this.scanEpoch;this.isReady=!1,this.finalizeResumedBaseline(e)}isActivelyScanning(){return this.scanTimer!==null&&!this.isPaused}injectPending(e){this.isPaused||(this.queuePendingChange(e),y.debug("SnapshotChangeScanner: injected pending change",{relativePath:e.relativePath,type:e.type}))}drainPendingChanges(){let e=Array.from(this.pendingChanges.values()).sort((n,i)=>n.sequence-i.sequence).map(n=>n.change);return this.pendingChanges.clear(),e}getPendingCount(){return this.pendingChanges.size}getLastDetected(){let e=null;for(let n of this.pendingChanges.values()){let i=n.change;(!e||i.detectedAt>e)&&(e=i.detectedAt)}return e}waitUntilReady(){return this.isReady?Promise.resolve():new Promise(e=>{this.readyResolvers.push(e)})}async rescan(){let e=await this.scanSnapshotDiffs(),n=await this.scanStoredModifications();return e+n}async scanSnapshotDiffs(){if(this.isPaused||this.isScanning)return 0;let e=this.scanEpoch;this.isScanning=!0;try{let n=await this.syncIndex.buildSnapshotSeed();if(this.isStaleScan(e))return 0;if(this.refreshSnapshotOnNextScan)return this.previousSnapshot=n,this.refreshSnapshotOnNextScan=!1,this.suppressedPaths.clear(),0;let i=Y6(this.previousSnapshot,n),r=await this.processCandidates(i,e);if(this.isStaleScan(e))return 0;let a=this.commitPendingChanges(r);return this.previousSnapshot=n,this.suppressedPaths.clear(),a>0&&y.info("SnapshotChangeScanner: scan found pending changes",{added:a}),a}finally{this.isScanning=!1}}async scanStoredModifications(){if(this.isPaused)return 0;let e=this.scanEpoch,n=await this.scanStoredModificationsInternal(e);if(!n||this.isStaleScan(e))return 0;for(let i of n.forwardViolations)this.onForwardViolation?.(i);return this.commitPendingChanges(n.pending)}async scanStoredModificationsInternal(e){let n=await this.syncIndex.getModifiedFiles(),i={pending:[],forwardViolations:[]};for(let r of n){if(e!==this.scanEpoch)return null;let a=r.fsPath,o=na.relative(this.explorerRoot,a);if(await this.isSuppressedPath(a))continue;if(this.shouldRedirectToForwardRestore(o,a)){i.forwardViolations.push(o);continue}let s;switch(r.changeType){case"modified":s="change";break;case"added":s="add";break;case"deleted":s="unlink";break;default:continue}i.pending.push({fsPath:a,relativePath:o,type:s,detectedAt:new Date().toISOString()})}return i}async processCandidates(e,n){let i=[];for(let r of e){if(this.isStaleScan(n))return[];let a=na.join(this.explorerRoot,r.relativePath),o=new Date().toISOString();if(await this.isSuppressedPath(a)||r.kind==="file"&&uP(r.relativePath))continue;if(this.shouldRedirectToForwardRestore(r.relativePath,a)){this.onForwardViolation?.(r.relativePath);continue}if(r.kind==="dir"){i.push({fsPath:a,relativePath:r.relativePath,type:r.changeType,detectedAt:o});continue}if(r.changeType==="unlink"){i.push({fsPath:a,relativePath:r.relativePath,type:"unlink",detectedAt:o});continue}let s=this.syncIndex.getStoredFileHash(a);try{let c=await mP.readFile(a,"utf-8"),l=nU("md5").update(c,"utf-8").digest("hex");if(s&&l===s){y.debug("SnapshotChangeScanner: echo skipped",{relativePath:r.relativePath,changeType:r.changeType});continue}i.push({fsPath:a,relativePath:r.relativePath,type:r.changeType,detectedAt:o})}catch(c){if(c.code==="ENOENT"){i.push({fsPath:a,relativePath:r.relativePath,type:"unlink",detectedAt:o});continue}y.warn("SnapshotChangeScanner: failed to read candidate file",{path:r.relativePath,changeType:r.changeType,error:c instanceof Error?c.message:String(c)})}}return i}commitPendingChanges(e){let n=0;for(let i of e){let r=this.getPendingSlot(i.type),a=this.getPendingKey(i.fsPath,r),o=this.getPendingRank(i.type),s=this.pendingChanges.get(a);s&&s.rank>o||(s||n++,this.pendingChanges.set(a,{change:i,rank:o,sequence:++this.pendingSequence}))}return n}clearTransientState(){this.pendingChanges.clear()}async finalizeResumedBaseline(e){try{let n=await this.syncIndex.buildSnapshotSeed();if(e!==this.scanEpoch)return;this.previousSnapshot=n,this.refreshSnapshotOnNextScan=!1;let i=await this.scanStoredModificationsInternal(e);if(e!==this.scanEpoch)return;if(i){for(let r of i.forwardViolations)this.onForwardViolation?.(r);this.commitPendingChanges(i.pending)}this.suppressedPaths.clear(),this.isPaused=!1,this.isReady=!0,this.resolveReadyWaiters()}catch(n){if(e!==this.scanEpoch)return;y.warn("SnapshotChangeScanner: resume baseline refresh failed",{error:n instanceof Error?n.message:String(n)}),this.refreshSnapshotOnNextScan=!0,this.isPaused=!1,this.isReady=!0,this.resolveReadyWaiters()}}shouldRedirectToForwardRestore(e,n){return this.syncIndex.isPathUnderClass(n,mc)?!0:this.directionChecker?this.directionChecker(e)==="forward":!1}async isSuppressedPath(e){let n=na.resolve(e);for(let[i,r]of this.suppressedPaths){if(n===i||n.startsWith(`${i}${na.sep}`))return r.mode==="generic"?!0:r.mode==="missing"?!await zce(n):await Lce(n)===r.hash;if(i.startsWith(`${n}${na.sep}`))return r.mode==="generic"}return!1}};async function zce(t){try{return await mP.access(t),!0}catch{return!1}}async function Lce(t){try{let e=await mP.readFile(t,"utf-8");return nU("md5").update(e,"utf-8").digest("hex")}catch{return null}}var rU=fP;var rt="snapshot-scanner",kv=class extends rU{placeId;detectorExplorerRoot;lifecycleGeneration=0;constructor(e,n,i={}){super(e,n,i.scanIntervalMs),this.placeId=i.placeId??null,this.detectorExplorerRoot=Uce.resolve(e)}async start(){let e=++this.lifecycleGeneration;y.info("Reverse detector bootstrap scheduled",{placeId:this.placeId,mode:rt,explorerRoot:this.detectorExplorerRoot}),await super.start(),e===this.lifecycleGeneration&&y.info("Reverse detector bootstrap completed",{placeId:this.placeId,mode:rt})}async stop(e){this.lifecycleGeneration+=1,y.info("Reverse detector stopping",{placeId:this.placeId,mode:rt,reason:e??"unspecified"}),await super.stop(e)}beginFullSyncReplacement(){y.info("Reverse detector paused for full sync replacement",{placeId:this.placeId,mode:rt}),super.beginFullSyncReplacement()}async endFullSyncReplacement(){y.info("Reverse detector baseline refresh scheduled",{placeId:this.placeId,mode:rt}),await super.endFullSyncReplacement()}async rescan(){y.debug("Reverse detector manual rescan requested",{placeId:this.placeId,mode:rt});let e=await super.rescan();return y.info("Reverse detector manual rescan completed",{placeId:this.placeId,mode:rt,added:e,pendingCount:this.getPendingCount()}),e}};import Ec from"path";import{promises as iU}from"fs";function mo(t,e,n,i,r=!1){if(i&&i>=1){let s=mt(e);if(s){let c=t.getEffectiveDir(s,i);if(c){let l=nt(e);if(n){let u=t.getResolvedName(e,n);if(u)l=u;else if(r)throw new Error(`Missing collision mapping for ${e}#${n}`)}return hP(t,c,l)}if(r)throw new Error(`Missing parent collision mapping for ${s}#${i}`)}}let a=t.getEffectiveDir(e,n??1);if(a){let s=t.getExplorerRoot(),c=Ec.relative(s,a).split(Ec.sep).filter(l=>l.length>0);return st(["game",...c])}let o=e;if(n){let s=t.getResolvedName(e,n);if(s)o=Wd(o,s);else if(r)throw new Error(`Missing collision mapping for ${e}#${n}`)}return o}function Fce(t,e){let n=ni(t),i=ni(e);if(n.length<=i.length)return!1;for(let r=0;r<i.length;r++)if(n[r]!==i[r])return!1;return!0}function aU(t,e){e=qce(t,e);let n=new Map;for(let r=0;r<e.length;r++){let a=e[r];if(a?.type==="instanceRemoved")try{let o=mo(t.index,a.path,a.siblingIndex,a.parentSiblingIndex,!0);n.set(r,o)}catch{continue}}if(n.size<=1)return e;let i=new Set;for(let[r,a]of n)for(let[o,s]of n)if(r!==o&&Fce(a,s)){i.add(r);break}return i.size===0?e:e.filter((r,a)=>!i.has(a))}function qce(t,e){let n=new Map;for(let a=0;a<e.length;a++){let o=e[a];if(o?.type!=="instanceRemoved"||o.siblingIndex===void 0)continue;let s=n.get(o.path);s||(s=[],n.set(o.path,s)),s.push(a)}let i=!1;for(let a of n.values()){if(a.length<2)continue;let o=new Set;for(let s of a){let c=e[s].siblingIndex;if(o.has(c)){i=!0;break}o.add(c)}if(i)break}if(!i)return e;let r=[...e];for(let[a,o]of n){if(o.length<2)continue;let s=ho(t.index,a,void 0,!1),c=new Set(t.index.getRegisteredSiblingIndices(a,s));if(c.size===0)continue;let l=new Set;for(let u of o){let d=e[u];if(d.type!=="instanceRemoved")continue;let p=d.siblingIndex;if(!l.has(p)&&c.has(p))l.add(p);else{let f;for(let m of c)if(!l.has(m)){f=m;break}f!==void 0&&(l.add(f),r[u]={...d,siblingIndex:f})}}}return r}function ho(t,e,n,i=!1){if(n&&n>=1){let r=mt(e);if(r){let a=t.getEffectiveDir(r,n);if(a)return a;if(i)throw new Error(`Missing parent collision mapping for ${r}#${n}`)}}return t.resolveParentDir(e)}async function ra(t,e,n,i,r){let a=Ec.join(e,n),o=Ec.join(e,i);try{(await iU.stat(a)).isDirectory()&&(r&&(r.suppressPath(a),r.suppressPath(o)),await iU.rename(a,o),t.renameHashKeysUnder(a,o),t.renameReverseNameMappingsUnder(a,o),t.renameInstanceDirMappingsUnder(a,o))}catch{return}}function hP(t,e,n){let i=Ec.relative(t.getExplorerRoot(),e).split(Ec.sep).filter(r=>r.length>0);return st(["game",...i,n])}async function Cv(t,e){let n=t.index.planCollisionReindexAfterBatchRemovals(e.instancePath,[...e.removedSiblingIndices],e.parentDir);for(let i of n.renames)await ra(t.index,e.parentDir,i.from,i.to,t.fileWatcher),await Rc(t.writer,t.index,e.parentDir,i.from,i.to);t.index.applyCollisionReindexAfterBatchRemovals(e.instancePath,e.parentDir,n)}async function Rc(t,e,n,i,r){let a=hP(e,n,i),o=hP(e,n,r);await t.renameInTree(a,o,r)}import Iv from"path";var oU=5*60*1e3,sU=100;function gP(t){return t!==null&&typeof t=="object"&&!Array.isArray(t)}function Gd(t){return typeof t=="string"&&t.length>0}function Pv(t,e){let n=Iv.resolve(t),i=Iv.resolve(e),r=Iv.relative(n,i);return r===""||!r.startsWith("..")&&!Iv.isAbsolute(r)}function cU(t){if(!gP(t))return{success:!1,error:"Body must be an object"};let e=t.phase;if(e!=="start"&&e!=="chunk"&&e!=="complete")return{success:!1,error:`Invalid phase: ${String(e)}`};if(!Gd(t.clientId))return{success:!1,error:"clientId is required"};switch(e){case"start":return typeof t.placeId!="number"?{success:!1,error:"placeId must be a number"}:Gd(t.placeName)?typeof t.totalServices!="number"?{success:!1,error:"totalServices must be a number"}:typeof t.totalInstances!="number"?{success:!1,error:"totalInstances must be a number"}:t.previousPlaceId!==void 0&&typeof t.previousPlaceId!="number"?{success:!1,error:"previousPlaceId must be a number"}:{success:!0,data:t}:{success:!1,error:"placeName is required"};case"chunk":return Gd(t.serviceName)?Gd(t.serviceClassName)?Array.isArray(t.instances)?typeof t.chunkIndex!="number"?{success:!1,error:"chunkIndex must be a number"}:typeof t.totalChunks!="number"?{success:!1,error:"totalChunks must be a number"}:{success:!0,data:t}:{success:!1,error:"instances must be an array"}:{success:!1,error:"serviceClassName is required"}:{success:!1,error:"serviceName is required"};case"complete":return typeof t.instanceCount!="number"?{success:!1,error:"instanceCount must be a number"}:typeof t.scriptCount!="number"?{success:!1,error:"scriptCount must be a number"}:{success:!0,data:t}}}function lU(t){if(!gP(t))return{success:!1,error:"Body must be an object"};if(!Gd(t.clientId))return{success:!1,error:"clientId is required"};if(t.placeId!==void 0&&typeof t.placeId!="number")return{success:!1,error:"placeId must be a number"};if(typeof t.timestamp!="number")return{success:!1,error:"timestamp must be a number"};if(!Array.isArray(t.changes)||t.changes.length===0)return{success:!1,error:"changes must be a non-empty array"};let e=new Set(["propertyChanged","instanceAdded","instanceRemoved","instanceRenamed","instanceMoved","scriptSourceChanged"]);for(let n=0;n<t.changes.length;n++){let i=t.changes[n];if(!gP(i)||!e.has(i.type))return{success:!1,error:`changes[${n}]: invalid type '${String(i?.type)}'`}}return{success:!0,data:t}}import or from"path";import{promises as go}from"fs";ie();var Ev=class{constructor(e){this.ctx=e}async processChangeForPlace(e,n,i){let r=LL(n.type);if(e.directions[r]==="reverse")return y.info("Reverse enforcement: rejecting Studio change on reverse-only path",{placeId:e.placeId,changeType:n.type,dirKey:r,path:"path"in n?n.path:void 0}),(n.type==="scriptSourceChanged"||n.type==="propertyChanged")&&await this.injectLocalAsReverse(e,n),null;switch(n.type){case"propertyChanged":{let o=mo(e.index,n.path,n.siblingIndex,n.parentSiblingIndex,!0),s=await this.checkPropertyConflictForPlace(e,o,n);if(s)return s;let c=n;if(c.attributes!==void 0||c.tags!==void 0){let l=e.index.resolvePropsPath(o),u=await this.buildPropertyChangedPropsData(l,o,n);await e.writer.writeProps(o,u)}else c.properties&&typeof c.properties=="object"&&!Array.isArray(c.properties)?await e.writer.updateProperties(o,c.properties):n.property!==void 0?await e.writer.updateProperty(o,n.property,n.value):y.warn("propertyChanged: missing both property and properties",{path:o});return null}case"instanceAdded":{let o=fc.has(n.className),s=n.path,c=n.name;{let d=ho(e.index,n.path,n.parentSiblingIndex,!0),{resolved:p,retroactiveRename:f}=e.index.registerCollision(n.path,n.siblingIndex,d);f&&(await ra(e.index,d,f.from,f.to,e.fileWatcher),await Rc(e.writer,e.index,d,f.from,f.to));let m=e.index.sanitizeName(n.name);if(p!==m||d!==e.index.resolveParentDir(n.path)){let h=e.index.getExplorerRoot(),g=or.relative(h,d).split(or.sep).filter(v=>v.length>0);s=st(["game",...g,p]),c=p}}await e.writer.writeInstance({path:s,name:n.name,className:n.className,properties:n.properties,attributes:n.attributes,tags:n.tags,scriptSource:n.scriptSource,hasChildren:!1,childCount:0,siblingIndex:n.siblingIndex}),this.setOriginalClassMapping(e,n.path,n.className,n.siblingIndex);let l={name:c,className:n.className,childCount:0,...c!==n.name?{originalName:n.name}:{}},u=mt(s)||n.parentPath;return await e.writer.addToTree(u,l),e.instanceCount++,o&&n.scriptSource!==void 0&&e.scriptCount++,null}case"instanceRemoved":{let o=mo(e.index,n.path,n.siblingIndex,n.parentSiblingIndex,!0),s=ho(e.index,n.path,n.parentSiblingIndex,!0);y.info("Processing instanceRemoved",{path:o}),await e.writer.removeInstance(o),this.removeOriginalClassMapping(e,n.path,o);let c=nt(o),l=mt(o);if(l&&c&&await e.writer.removeFromTree(l,c),n.siblingIndex!==void 0){let d=`${s}\0${n.path}`,p=i.get(d);p||(p={instancePath:n.path,parentDir:s,removedSiblingIndices:new Set},i.set(d,p)),p.removedSiblingIndices.add(n.siblingIndex)}let u=mt(o);if(u)try{let d=e.index.resolveChildrenDir(u);or.dirname(d)!==e.index.getExplorerRoot()&&(await go.readdir(d)).length===0&&await go.rmdir(d)}catch{return null}return null}case"instanceRenamed":{let o=mo(e.index,n.oldPath,n.siblingIndex,n.parentSiblingIndex,!0),s=mt(n.oldPath)===mt(n.newPath),c=s?ho(e.index,n.oldPath,n.parentSiblingIndex,!0):null,l=ho(e.index,n.newPath,n.parentSiblingIndex,!0),{resolved:u,retroactiveRename:d}=e.index.registerCollision(n.newPath,n.siblingIndex,l);d&&(await ra(e.index,l,d.from,d.to,e.fileWatcher),await Rc(e.writer,e.index,l,d.from,d.to));let p=u!==e.index.sanitizeName(n.newName)?Wd(n.newPath,u):n.newPath;if(await e.writer.renameInstance(o,p,n.newName),this.renameOriginalClassMapping(e,n.oldPath,n.newPath),await e.writer.renameInTree(o,p,u,n.newName),s&&c&&n.siblingIndex!==void 0){let f=e.index.planCollisionReindexAfterRemoval(n.oldPath,n.siblingIndex,c);for(let m of f.renames)await ra(e.index,c,m.from,m.to,e.fileWatcher),await Rc(e.writer,e.index,c,m.from,m.to);e.index.applyCollisionReindexAfterRemoval(n.oldPath,c,f)}return null}case"instanceMoved":{let o=mo(e.index,n.oldPath,n.siblingIndex,n.parentSiblingIndex,!0),s=ho(e.index,n.newPath,n.parentSiblingIndex,!0),{resolved:c,retroactiveRename:l}=e.index.registerCollision(n.newPath,n.siblingIndex,s);l&&(await ra(e.index,s,l.from,l.to,e.fileWatcher),await Rc(e.writer,e.index,s,l.from,l.to));let u=nt(n.newPath)||"",d=c!==e.index.sanitizeName(u)?Wd(n.newPath,c):n.newPath;return await e.writer.moveInstance(o,d),this.renameOriginalClassMapping(e,n.oldPath,n.newPath),await e.writer.moveInTree(o,d),null}case"scriptSourceChanged":{let o=mo(e.index,n.path,n.siblingIndex,n.parentSiblingIndex,!0),s=await this.checkScriptConflictForPlace(e,o,n.source);if(s)return s;let c=await this.resolveScriptClassForPlace(e,o);return await this.cleanupStaleScriptFilesForPlace(e,o,c),await e.writer.writeScript(o,c,n.source,!1),null}}}async injectLocalAsReverse(e,n){if(e.fileWatcher)try{if(n.type==="scriptSourceChanged"){let i=await this.resolveScriptClassForPlace(e,n.path),r=e.index.resolveScriptPath(n.path,i,!1),a=or.join(this.ctx.config.getPlaceRoot(e.placeId),"explorer"),o=or.relative(a,r);e.fileWatcher.injectPending({fsPath:r,relativePath:o,type:"change",detectedAt:new Date().toISOString()})}else if(n.type==="propertyChanged"){let i=e.index.resolvePropsPath(n.path),r=or.join(this.ctx.config.getPlaceRoot(e.placeId),"explorer"),a=or.relative(r,i);e.fileWatcher.injectPending({fsPath:i,relativePath:a,type:"change",detectedAt:new Date().toISOString()})}}catch(i){y.debug("injectLocalAsReverse: could not inject",{changeType:n.type,error:i instanceof Error?i.message:String(i)})}}async resolveScriptClassForPlace(e,n){try{let i=e.index.resolvePropsPath(n),r=await go.readFile(i,"utf-8"),a=JSON.parse(r);if(a.className&&fc.has(a.className))return a.className}catch{return"ModuleScript"}return"ModuleScript"}async cleanupStaleScriptFilesForPlace(e,n,i){let r=av[i],a=or.join(this.ctx.config.getPlaceRoot(e.placeId),"explorer");for(let[o,s]of Object.entries(av)){if(s===r)continue;let c=e.index.resolveScriptPath(n,o,!1);try{await go.unlink(c),e.index.removeHash(c),y.info("Cleaned up stale script file",{placeId:e.placeId,removed:or.relative(a,c),currentClassName:i})}catch(l){l.code!=="ENOENT"&&y.warn("Failed to clean up stale script file",{path:c,error:l instanceof Error?l.message:String(l)})}}}async checkPropertyConflictForPlace(e,n,i){try{let r=e.index.resolvePropsPath(n),a=e.index.getFileHash(r);if(!a)return null;let o;try{o=await go.readFile(r,"utf-8")}catch{return null}return e.index.computeHash(o)===a?null:{fsPath:or.relative(this.ctx.config.getPlaceRoot(e.placeId),r),instancePath:n,fileType:"props",studioContent:await this.buildStudioPropsConflictContent(r,n,i),fileContent:o}}catch{return null}}async checkScriptConflictForPlace(e,n,i){try{let r=await this.resolveScriptClassForPlace(e,n),a=e.index.resolveScriptPath(n,r,!1),o=e.index.getFileHash(a);if(!o)return null;let s;try{s=await go.readFile(a,"utf-8")}catch{return null}return e.index.computeHash(s)===o?null:{fsPath:or.relative(this.ctx.config.getPlaceRoot(e.placeId),a),instancePath:n,fileType:"script",studioContent:i,fileContent:s}}catch{return null}}async applyDeferredCollisionBatchReindexForPlace(e,n){await Cv(e,n)}async buildStudioPropsConflictContent(e,n,i){let r=await this.buildPropertyChangedPropsData(e,n,i);return JSON.stringify(r,null,2)}async buildPropertyChangedPropsData(e,n,i){let r=null;try{let s=await go.readFile(e,"utf-8");r=JSON.parse(s)}catch{r=null}let a=i.properties?{...i.properties}:{...r?.properties??{}};i.property&&(a[i.property]=i.value);let o={name:r?.name??nt(n)??"",className:i.className??r?.className??"Folder",properties:a};return i.attributes!==void 0?Object.keys(i.attributes).length>0&&(o.attributes={...i.attributes}):r?.attributes&&(o.attributes={...r.attributes}),i.tags!==void 0?i.tags.length>0&&(o.tags=[...i.tags]):r?.tags&&(o.tags=[...r.tags]),o}setOriginalClassMapping(e,n,i,r){e.index.setClassName(n,i,r)}removeOriginalClassMapping(e,n,i){n!==i&&e.index.removeClassMappingsUnderInstancePath(n)}renameOriginalClassMapping(e,n,i){n!==i&&e.index.renameClassMappingKeys(n,i)}};import uU from"path";import{promises as Rv}from"fs";ie();var Tv=class{constructor(e){this.ctx=e}async handlePreCheck(e,n){let i=this.ctx.resolveQueryPlaceId(e);if(i==null){n.status(200).json({hasPreviousSync:!1,changes:[],summary:{modified:0,deleted:0,added:0,total:0}});return}let r=this.ctx.config.getPlaceRoot(i),a=uU.join(r,".sync-index.json"),o=!1;try{await Rv.access(a),o=!0}catch{o=!1}if(!o){n.status(200).json({hasPreviousSync:!1,changes:[],summary:{modified:0,deleted:0,added:0,total:0}});return}let s=uU.join(r,"explorer"),c=new ta(r,s);await c.loadFromDisk();let l=await c.getModifiedFiles(),u=0,d=0,p=0;for(let m of l)switch(m.changeType){case"modified":u++;break;case"deleted":d++;break;case"added":p++;break}let f=l.map(m=>({relPath:m.relPath,instancePath:m.instancePath,changeType:m.changeType,fileType:m.fileType}));n.status(200).json({hasPreviousSync:!0,changes:f,summary:{modified:u,deleted:d,added:p,total:f.length}})}async handleSyncDirections(e,n){let i=e.body,r=i.placeId??this.ctx.getDefaultRuntimePlaceId();if(r==null){n.status(400).json({error:"Missing placeId"});return}let a=this.ctx.places.get(r);if(!a){n.status(404).json({error:"Place not found",message:`No sync context for place ${r}`});return}if(i.directions&&typeof i.directions=="object"){let o=c=>c==="forward"||c==="reverse"||c==="bidirectional",s=i.directions;o(s.scripts)&&(a.directions.scripts=s.scripts),o(s.values)&&(a.directions.values=s.values),o(s.containers)&&(a.directions.containers=s.containers),o(s.data)&&(a.directions.data=s.data),o(s.services)&&(a.directions.services=s.services),y.info("Sync directions updated",{placeId:r,directions:a.directions})}if(i.applyModes&&typeof i.applyModes=="object"){let o=c=>c==="auto"||c==="manual",s=i.applyModes;o(s.scripts)&&(a.applyModes.scripts=s.scripts),o(s.values)&&(a.applyModes.values=s.values),o(s.containers)&&(a.applyModes.containers=s.containers),o(s.data)&&(a.applyModes.data=s.data),o(s.services)&&(a.applyModes.services=s.services)}this.ctx.touchRuntimePlace(r),n.status(200).json({status:"updated",directions:a.directions,applyModes:a.applyModes})}async handleForwardRestoreList(e,n){let i=this.ctx.resolveQueryPlaceId(e);if(i==null){n.status(200).json({paths:[]});return}let r=this.ctx.places.get(i);if(!r){n.status(404).json({error:"Place not found",message:`No sync context for place ${i}`});return}let a=r.forwardRestoreQueue.splice(0);this.ctx.touchRuntimePlace(i),y.info("Forward restore list drained",{placeId:i,count:a.length}),n.status(200).json({paths:a})}async handleSyncHistory(e,n){let i=this.ctx.resolveQueryPlaceId(e);if(i==null){n.status(200).json({entries:[],total:0,hasMore:!1});return}let r=sn(e.query.limit,50,1,200),a=sn(e.query.offset,0,0),o=e.query.direction,s=e.query.type,c=this.ctx.places.get(i);c&&(await c.writer.flushHistory(),this.ctx.touchRuntimePlace(i));let l=this.ctx.config.getHistoryPath(i),d=(await hv(l,1e4)).reverse().filter(h=>!(o&&h.direction!==o||s&&h.type!==s)),p=d.length,m={entries:d.slice(a,a+r),total:p,hasMore:a+r<p};n.status(200).json(m)}getStatusSummary(){if(this.ctx.activeFullSyncPlaceId!==null&&this.ctx.activeFullSyncPlaceId!==void 0)return{active:!0,placeId:this.ctx.activeFullSyncPlaceId};for(let[e,n]of this.ctx.places.entries())if(n.state==="syncing")return{active:!0,placeId:e};return{active:!1}}getDirectionForCategory(e){let n;if(this.ctx.activeFullSyncPlaceId!==null&&this.ctx.activeFullSyncPlaceId!==void 0&&(n=this.ctx.places.get(this.ctx.activeFullSyncPlaceId)),!n){let r=this.ctx.getDefaultRuntimePlaceId();r!=null&&(n=this.ctx.places.get(r))}if(!n)return"forward";let i=e;return n.directions[i]??"forward"}getStatusDirect(e){let n=e!=null?parseInt(e,10):this.ctx.getDefaultRuntimePlaceId();if(n==null)return{state:"idle",instanceCount:0,scriptCount:0,lastFullSync:null,lastIncrementalSync:null,syncRoot:this.ctx.config.getSyncRoot(),activeClientId:null,reverseSyncAvailable:!1,modifiedFileCount:0,applyModes:{...ao},reverseDetectorActive:!1,reverseDetectorMode:rt,fileWatcherActive:!1};let i=this.ctx.places.get(n);if(!i)return{state:"idle",instanceCount:0,scriptCount:0,lastFullSync:null,lastIncrementalSync:null,syncRoot:this.ctx.config.getPlaceRoot(n),activeClientId:null,reverseSyncAvailable:!1,modifiedFileCount:0,applyModes:{...ao},reverseDetectorActive:!1,reverseDetectorMode:rt,fileWatcherActive:!1};let r=i.fileWatcher?.getPendingCount()??0,a=i.fileWatcher?.isActivelyScanning()??!1,o=i.fileWatcher!==null;return{state:i.state,instanceCount:i.instanceCount,scriptCount:i.scriptCount,lastFullSync:i.lastFullSync,lastIncrementalSync:i.lastIncrementalSync,syncRoot:this.ctx.config.getPlaceRoot(n),activeClientId:i.activeClientId,reverseSyncAvailable:r>0,modifiedFileCount:r,applyModes:{...i.applyModes},reverseDetectorActive:a,reverseDetectorMode:rt,fileWatcherActive:o}}async getHistoryDirect(e,n){let i=e!=null?parseInt(e,10):this.ctx.getDefaultRuntimePlaceId();if(i==null||!Number.isFinite(i))return{entries:[],total:0,hasMore:!1};let r=this.ctx.places.get(i);r&&await r.writer.flushHistory();let a=Math.min(Math.max(n?.limit??50,1),200),o=Math.max(n?.offset??0,0),s=this.ctx.config.getHistoryPath(i),l=(await hv(s,1e4)).reverse(),u=l.length;return{entries:l.slice(o,o+a),total:u,hasMore:o+a<u}}getDirectionsDirect(e){let n=e!=null?parseInt(e,10):this.ctx.getDefaultRuntimePlaceId();if(n==null)return{...io};let i=this.ctx.places.get(n);return i?{...i.directions}:{...io}}getProgressDirect(e){let n=e!=null?parseInt(e,10):this.ctx.getDefaultRuntimePlaceId();if(n==null)return{state:"idle",isSyncing:!1};let i=this.ctx.places.get(n);if(!i)return{state:"idle",isSyncing:!1};if(!i.syncProgress)return{state:i.state,isSyncing:!1,lastSync:{instanceCount:i.instanceCount,scriptCount:i.scriptCount,completedAt:i.lastFullSync}};let r=i.syncProgress,a=Date.now()-r.syncStartTime,o=r.totalInstances>0?Math.min(100,Math.round(r.processedInstances/r.totalInstances*100)):0,s;if(r.processedInstances>0&&o<100){let l=a/r.processedInstances,u=r.totalInstances-r.processedInstances;s=Math.round(l*u)}let c=a>0?Math.round(r.bytesReceived/a*1e3):0;return{state:i.state,isSyncing:!0,progressPercent:o,currentService:r.currentService,currentChunk:{index:r.currentChunkIndex,total:r.currentTotalChunks},instances:{processed:r.processedInstances,total:r.totalInstances},services:{processed:r.processedServices,total:r.totalServices},elapsedMs:a,estimatedRemainingMs:s,bytesReceived:r.bytesReceived,bytesPerSecond:c}}async readSyncedFile(e,n){let i=parseInt(e,10),r=this.ctx.places.get(i);if(!r)throw new Error(`Place ${e} is not loaded in the sync cache. Start sync for this place before reading synced files.`);let a=r.index.resolvePropsPath(n);try{return{content:await Rv.readFile(a,"utf-8"),path:a}}catch(s){if(s.code!=="ENOENT")throw s}for(let s of fc){let c=r.index.resolveScriptPath(n,s,!1);try{return{content:await Rv.readFile(c,"utf-8"),path:c}}catch{continue}}let o=r.index.resolveValuePath(n);try{return{content:await Rv.readFile(o,"utf-8"),path:o}}catch(s){if(s.code!=="ENOENT")throw s}throw new Error(`No synced file found for instance: ${n}. Ensure the instance has been synced to disk and that the canonical instance path is correct.`)}async writeSyncedFile(e,n,i){let r=parseInt(e,10),a=this.ctx.places.get(r);if(!a)throw new Error(`Place ${e} is not loaded in the sync cache. Start sync for this place before writing synced files.`);await a.writer.writeScript(n,"Script",i,!1)}async executeViaDisk(e,n){let i=this.ctx.getDefaultRuntimePlaceId();if(i==null)throw new Error("No active sync place for disk execution. Start sync for the current place before using reverse-sync disk execution.");let r=this.ctx.places.get(i);if(!r)throw new Error(`Place ${i} is not loaded in the sync cache. Restart sync before retrying disk execution.`);switch(e){case"manage_scripts_set_source":{let a=n.scriptType||n.className||"Script";return await r.writer.writeScript(n.path,a,n.source,!1),{success:!0,path:n.path}}case"manage_properties_set":return await r.writer.writeProps(n.path,{className:n.className||"Instance",name:nt(n.path),properties:{[n.property]:n.value}}),{success:!0,path:n.path};default:throw new Error(`Disk execution is not supported for command: ${e}. This command must run through the plugin route.`)}}};import $e from"path";import{createHash as Bce,randomUUID as Hce}from"crypto";import{promises as vn}from"fs";ie();function Zce(t){return Bce("md5").update(t,"utf-8").digest("hex")}function dU(t){return t.pluginProcessToken?`process:${t.pluginProcessToken}`:t.pluginSessionId?`session:${t.pluginSessionId}`:t.pluginClientId?`client:${t.pluginClientId}`:null}var $v=class{constructor(e){this.ctx=e}preserveLocalFilesMap=new Map;pendingServiceTrees=new Map;async handleInitStart(e,n,i){if(n.previousPlaceId!==void 0&&n.previousPlaceId!==e&&(y.info("Place promotion detected",{from:n.previousPlaceId,to:e}),await this.ctx.config.promotePlaceRoot(n.previousPlaceId,e,n.placeName),this.ctx.places.get(n.previousPlaceId)&&this.ctx.places.delete(n.previousPlaceId),this.ctx.activeFullSyncPlaceId===n.previousPlaceId&&(this.ctx.activeFullSyncPlaceId=null),this.ctx.clearRuntimePlaceIfMatch(n.previousPlaceId)),this.ctx.activeFullSyncPlaceId!==null&&this.ctx.activeFullSyncPlaceId!==void 0&&this.ctx.activeFullSyncPlaceId!==e){i.status(409).json({error:"Conflict",message:`Place ${this.ctx.activeFullSyncPlaceId} is currently syncing. Only one place can sync at a time.`});return}let r=await this.ctx.getOrCreatePlaceContext(e,n.placeName);if(r.activeClientId&&r.activeClientId!==n.clientId&&r.activeFullSyncSessionId!==null){i.status(409).json({error:"Conflict",message:`Another client (${r.activeClientId}) is currently syncing this place`});return}if(this.ctx.activeFullSyncPlaceId=e,this.ctx.touchRuntimePlace(e),r.activeClientId=n.clientId,r.activeSyncIdentityScope=dU(n),r.placeName=n.placeName,n.directions&&typeof n.directions=="object"){let d=n.directions,p=f=>f==="forward"||f==="reverse"||f==="bidirectional";p(d.scripts)&&(r.directions.scripts=d.scripts),p(d.values)&&(r.directions.values=d.values),p(d.containers)&&(r.directions.containers=d.containers),p(d.data)&&(r.directions.data=d.data),p(d.services)&&(r.directions.services=d.services),y.info("Sync directions received",{placeId:e,directions:r.directions})}else r.directions={...io};if(n.applyModes&&typeof n.applyModes=="object"){let d=n.applyModes,p=f=>f==="auto"||f==="manual";p(d.scripts)&&(r.applyModes.scripts=d.scripts),p(d.values)&&(r.applyModes.values=d.values),p(d.containers)&&(r.applyModes.containers=d.containers),p(d.data)&&(r.applyModes.data=d.data),p(d.services)&&(r.applyModes.services=d.services)}else r.applyModes={...ao};r.forwardRestoreQueue=[];let a=Hce();r.activeFullSyncSessionId=a,this.pendingServiceTrees.set(a,new Map),r.instanceCount=0,r.scriptCount=0;let o=this.ctx.config.getPlaceRoot(e),s=$e.join(o,`explorer_tmp_${a}`);await vn.mkdir(s,{recursive:!0}),r.tmpIndex=new ta(o,s),r.tmpWriter=new Pc(this.ctx.config,r.tmpIndex,e),r.collisionDirMap=new Map;let c=n.preserveLocalFiles;Array.isArray(c)&&c.length>0&&(this.setPreserveLocalFiles(a,c),y.info("PreserveLocalFiles set for sync",{syncId:a,fileCount:c.length}));let l={version:1,placeId:n.placeId,placeName:n.placeName,lastFullSync:null,lastIncrementalSync:null,instanceCount:0,scriptCount:0,syncMode:"mirror"},u=$e.join(o,".sync-meta.json");await vn.mkdir($e.dirname(u),{recursive:!0}),await this.ctx.atomicWriteFile(u,JSON.stringify(l,null,2)+`
502
502
  `),this.startTTLTimerForPlace(r,a),r.state="initializing",r.index.resetNameCounters(),r.syncProgress={syncStartTime:Date.now(),totalInstances:n.totalInstances,totalServices:n.totalServices,processedInstances:0,processedServices:0,currentService:null,currentChunkIndex:0,currentTotalChunks:0,bytesReceived:0,processedChunks:0},r.writer.appendChangeLog(`FULL_SYNC_START clientId=${n.clientId} placeId=${n.placeId}`),r.writer.setBootstrapMode(!0),r.writer.appendHistory({timestamp:new Date().toISOString(),type:"fullSyncStart",direction:"forward",source:"studio",path:`place_${n.placeId}`,details:`services:${n.totalServices} instances:${n.totalInstances}`}),y.info("Full sync started",{syncId:a,clientId:n.clientId,identityScope:r.activeSyncIdentityScope,placeId:n.placeId,placeName:n.placeName,totalServices:n.totalServices,totalInstances:n.totalInstances}),i.status(200).json({status:"started",syncId:r.activeFullSyncSessionId})}async handleInitChunk(e,n,i){let r=this.ctx.places.get(e);if(!r||!r.activeFullSyncSessionId||!r.tmpIndex||!r.tmpWriter){i.status(400).json({error:"No active sync session",message:"Call sync/init with phase=start first"});return}if(r.activeClientId&&n.clientId&&r.activeClientId!==n.clientId){i.status(409).json({error:"Conflict",message:`This sync session belongs to client ${r.activeClientId}`});return}let a=r.activeFullSyncSessionId,o=this.getOrCreatePendingServiceTree(a,n),s=0,c=r.collisionDirMap;for(let u of n.instances){if(this.ctx.config.isForbiddenPath(u.path))continue;let d=r.tmpIndex.resolveParentDir(u.path),p=c.get(d)??d,f=r.activeSyncIdentityScope??dU(n),m=this.getPreferredResolvedName(r,f,u),{resolved:h,retroactiveRename:g}=r.tmpIndex.registerCollisionWithPreferredName(u.path,u.siblingIndex??void 0,p,m);g&&(await ra(r.tmpIndex,p,g.from,g.to),this.rewritePendingEffectivePaths(o,r.tmpIndex.getExplorerRoot(),$e.join(p,g.from),$e.join(p,g.to)),r.tmpIndex.updateSessionDebugMappingResolvedName(f,g.instancePath,g.siblingIndex,g.to));let v=r.tmpIndex.resolveChildrenDir(u.path),x=$e.join(p,h);x!==v&&c.set(v,x);let S=r.tmpIndex.sanitizeName(u.name),w=u;if(p!==d||h!==S){let D=r.tmpIndex.getExplorerRoot(),q=$e.relative(D,p).split($e.sep).filter(de=>de.length>0),ce=st(["game",...q,h]);w={...u,path:ce}}let I=await r.tmpWriter.writeInstance(w);r.tmpIndex.setClassName(u.path,u.className,u.siblingIndex),this.recordSessionDebugMapping(r,f,u,h),s++,(I.propsWritten||I.valueWritten)&&r.instanceCount++,I.scriptWritten&&r.scriptCount++,o.instances.push({effectivePath:w.path,originalPath:u.path,className:u.className})}let l=this.isLastChunk(o,n.chunkIndex,n.totalChunks);if(l){let u=this.buildServiceTree(r,o);await r.tmpWriter.writeTree(n.serviceName,u);let d=this.pendingServiceTrees.get(a);d?.delete(n.serviceName),d&&d.size===0&&this.pendingServiceTrees.delete(a)}r.syncProgress&&(r.syncProgress.processedInstances+=s,r.syncProgress.currentService=n.serviceName,r.syncProgress.currentChunkIndex=n.chunkIndex,r.syncProgress.currentTotalChunks=n.totalChunks,r.syncProgress.processedChunks++,r.syncProgress.bytesReceived+=JSON.stringify(n).length,l&&r.syncProgress.processedServices++),y.debug("Sync chunk processed",{placeId:e,serviceName:n.serviceName,chunkIndex:n.chunkIndex,totalChunks:n.totalChunks,processed:s}),i.status(200).json({processed:s,service:n.serviceName})}async handleInitComplete(e,n,i){let r=this.ctx.places.get(e);if(!r||!r.activeFullSyncSessionId){i.status(400).json({error:"No active sync session",message:"Call sync/init with phase=start first"});return}if(r.activeClientId&&n.clientId&&r.activeClientId!==n.clientId){i.status(409).json({error:"Conflict",message:`This sync session belongs to client ${r.activeClientId}`});return}let a=r.activeFullSyncSessionId,o=this.ctx.config.getPlaceRoot(e),s=$e.join(o,"explorer"),c=$e.join(o,`explorer_tmp_${a}`),l=r.fileWatcher,u=this.getAndClearPreserveLocalFiles(a),d=new Map,p=[];if(u.length>0){for(let f of u){let m=$e.resolve(o,f);try{let h=await vn.readFile(m,"utf-8");d.set(f,h)}catch{p.push(f)}}y.info("Backed up local files for preservation",{placeId:e,requested:u.length,backed:d.size,deleted:p.length})}l&&l.beginFullSyncReplacement();try{try{await vn.rm(s,{recursive:!0,force:!0})}catch{}await vn.rename(c,s),r.instanceCount=n.instanceCount,r.scriptCount=n.scriptCount,r.lastFullSync=new Date().toISOString();let f={version:1,placeId:r.placeId,placeName:r.placeName,lastFullSync:r.lastFullSync,lastIncrementalSync:r.lastIncrementalSync,instanceCount:r.instanceCount,scriptCount:r.scriptCount,syncMode:"mirror"},m=$e.join(o,".sync-meta.json");if(await this.ctx.atomicWriteFile(m,JSON.stringify(f,null,2)+`
503
503
  `),this.clearTTLTimerForPlace(r,a),r.index.clearAllHashes(),r.index.clearClassMappings(),r.tmpIndex){let h=r.tmpIndex.getExplorerRoot(),g=r.index.getExplorerRoot();for(let[v,x]of r.tmpIndex.getAllHashes()){let S=$e.relative(h,v),w=$e.resolve(g,S);r.index.updateHashByValue(w,x)}for(let[v,x]of r.tmpIndex.getAllFileHashes()){let S=$e.relative(h,v),w=$e.resolve(g,S);r.index.updateFileHashByValue(w,x)}r.index.resetNameCounters(),r.index.mergeNameMappingsFrom(r.tmpIndex),r.index.mergeSessionDebugMappingsFrom(r.tmpIndex)}if(r.tmpIndex=null,r.tmpWriter&&(r.tmpWriter.stopChangeLogFlusher(),r.tmpWriter=null),this.pendingServiceTrees.delete(a),r.collisionDirMap=null,r.activeSyncIdentityScope=null,await r.index.saveToDisk(),r.state="syncing",r.activeFullSyncSessionId=null,r.syncProgress=null,this.ctx.touchRuntimePlace(e),this.ctx.activeFullSyncPlaceId=null,d.size>0){for(let[h,g]of d){let v=$e.resolve(o,h);try{await vn.mkdir($e.dirname(v),{recursive:!0}),await vn.writeFile(v,g,"utf-8"),l?.suppressPath(v,Zce(g))}catch(x){y.warn("Failed to restore preserved file",{path:h,error:x instanceof Error?x.message:String(x)})}}y.info("Restored preserved local files",{placeId:e,count:d.size})}if(p.length>0){let h=0;for(let g of p){let v=$e.resolve(o,g);try{await vn.unlink(v),r.index.removeHash(v),l?.suppressPath(v,"missing"),h++}catch(x){x.code!=="ENOENT"&&y.warn("Failed to delete preserved-as-deleted file",{path:g,error:x instanceof Error?x.message:String(x)})}}h>0&&(await r.index.saveToDisk(),y.info("Deleted locally-removed files from new sync",{placeId:e,count:h}))}try{let h=this.ctx.config.getSyncRoot();await lv(h,o)}catch(h){y.warn("Failed to generate place sourcemap after full sync",{placeId:e,error:h instanceof Error?h.message:String(h)})}try{let h=this.ctx.config.getSyncRoot(),g={version:1,lastActivePlaceId:r.placeId,lastActivePlaceName:r.placeName??null,lastActiveAt:new Date().toISOString()},v=$e.join(h,".project-meta.json");await this.ctx.atomicWriteFile(v,JSON.stringify(g,null,2)+`
504
- `),await hc(h)}catch(h){y.warn("Failed to refresh root representative sourcemap after full sync",{placeId:e,error:h instanceof Error?h.message:String(h)})}this.ctx.startFileWatcherForPlace(r).catch(h=>{y.warn("Failed to start reverse detector after full sync",{placeId:e,error:h instanceof Error?h.message:String(h)}),r.fileWatcher=null}),r.writer.setBootstrapMode(!1),r.writer.appendChangeLog(`FULL_SYNC_COMPLETE instances=${r.instanceCount} scripts=${r.scriptCount}`),r.writer.appendHistory({timestamp:new Date().toISOString(),type:"fullSyncComplete",direction:"forward",source:"studio",path:`place_${e}`,details:`instances:${r.instanceCount} scripts:${r.scriptCount}`}),y.info("Full sync completed",{placeId:e,instanceCount:r.instanceCount,scriptCount:r.scriptCount}),i.status(200).json({status:"completed",instanceCount:r.instanceCount,scriptCount:r.scriptCount,syncRoot:this.ctx.config.getPlaceRoot(e)})}catch(f){throw l&&l.endFullSyncReplacement(),f}}setPreserveLocalFiles(e,n){this.preserveLocalFilesMap.set(e,n)}getAndClearPreserveLocalFiles(e){let n=this.preserveLocalFilesMap.get(e)||[];return this.preserveLocalFilesMap.delete(e),n}clearPreserveLocalFiles(e){this.preserveLocalFilesMap.delete(e)}clearPendingServiceTrees(e){this.pendingServiceTrees.delete(e)}startTTLTimerForPlace(e,n){let i=setTimeout(async()=>{y.warn("Incomplete sync TTL expired, cleaning up",{placeId:e.placeId,syncId:n});let r=this.ctx.config.getPlaceRoot(e.placeId),a=$e.join(r,`explorer_tmp_${n}`);try{await vn.rm(a,{recursive:!0,force:!0})}catch(o){y.error("Failed to clean up expired temp dir",o instanceof Error?o:new Error(String(o)))}e.incompleteSyncTimer=null,e.activeFullSyncSessionId===n&&(e.activeFullSyncSessionId=null,e.activeClientId=null,e.state="idle",e.tmpIndex=null,this.pendingServiceTrees.delete(n),e.collisionDirMap=null,e.activeSyncIdentityScope=null,e.tmpWriter&&(e.tmpWriter.stopChangeLogFlusher(),e.tmpWriter=null),this.ctx.activeFullSyncPlaceId===e.placeId&&(this.ctx.activeFullSyncPlaceId=null),this.ctx.clearRuntimePlaceIfMatch(e.placeId))},oU);i&&typeof i=="object"&&"unref"in i&&i.unref(),e.incompleteSyncTimer=i}getOrCreatePendingServiceTree(e,n){let i=this.pendingServiceTrees.get(e);i||(i=new Map,this.pendingServiceTrees.set(e,i));let r=i.get(n.serviceName);if(r)return r;let a={serviceName:n.tree?.name??n.serviceName,serviceClassName:n.tree?.className??n.serviceClassName,zeroBasedChunkIndex:n.chunkIndex===0,instances:[]};return i.set(n.serviceName,a),a}isLastChunk(e,n,i){return i<=1?!0:e.zeroBasedChunkIndex?n>=i-1:n>=i}getPreferredResolvedName(e,n,i){let r=typeof i.sessionDebugId=="string"?i.sessionDebugId:"";if(!n||r==="")return;let a=e.index.getSessionDebugMapping(n,r);if(!a)return;let o=mt(i.path),s=nt(i.path)||i.name;if(!(a.parentPath!==o||a.originalName!==s))return a.resolvedName}recordSessionDebugMapping(e,n,i,r){let a=typeof i.sessionDebugId=="string"?i.sessionDebugId:"";!n||a===""||!e.tmpIndex||e.tmpIndex.setSessionDebugMapping({scope:n,sessionDebugId:a,instancePath:i.path,parentPath:mt(i.path),originalName:nt(i.path)||i.name,siblingIndex:Math.max(1,i.siblingIndex??1),resolvedName:r,className:i.className,updatedAt:new Date().toISOString()})}buildServiceTree(e,n){let i={name:n.serviceName,className:n.serviceClassName,childCount:0,children:[],syncedAt:new Date().toISOString()};for(let r of n.instances){let a=this.resolveEffectiveSegments(e,r.effectivePath),o=jt(r.originalPath);this.upsertTreeNode(i,a,o,r.className)}return this.recomputeTreeChildCounts(i),i.syncedAt=new Date().toISOString(),i}resolveEffectiveSegments(e,n){return e.tmpIndex?jt(n).map(i=>e.tmpIndex.sanitizeName(i)):jt(n)}rewritePendingEffectivePaths(e,n,i,r){let a=$e.relative(n,i).split($e.sep).filter(l=>l.length>0),o=$e.relative(n,r).split($e.sep).filter(l=>l.length>0);if(a.length===0||o.length===0)return;let s=st(["game",...a]),c=st(["game",...o]);for(let l of e.instances){if(l.effectivePath===s){l.effectivePath=c;continue}(l.effectivePath.startsWith(`${s}.`)||l.effectivePath.startsWith(`${s}[`))&&(l.effectivePath=`${c}${l.effectivePath.slice(s.length)}`)}}upsertTreeNode(e,n,i,r){if(n.length<=1)return;let a=e.children;for(let o=1;o<n.length;o++){let s=n[o],c=i[o],l=o===n.length-1,u=a.find(d=>d.name===s);u?l&&(u.className=r,c!==void 0&&c!==s&&(u.originalName=c)):(u={name:s,className:l?r:"Folder",childCount:0,children:[]},c!==void 0&&c!==s&&(u.originalName=c),a.push(u)),u.children||(u.children=[]),a=u.children}}recomputeTreeChildCounts(e){let n=i=>{let r=i.children??[];i.children=r;for(let a of r)n(a);i.childCount=r.length};for(let i of e.children)n(i);e.childCount=e.children.length}clearTTLTimerForPlace(e,n){e.incompleteSyncTimer&&e.activeFullSyncSessionId===n&&(clearTimeout(e.incompleteSyncTimer),e.incompleteSyncTimer=null)}async cleanupStaleTempDirs(){let e=this.ctx.config.getSyncRoot();try{let n=await vn.readdir(e,{withFileTypes:!0});for(let i of n)if(i.isDirectory()){if(i.name.startsWith("explorer_tmp_")){let r=$e.join(e,i.name);y.warn("Removing stale temp directory from crashed sync",{dir:i.name});try{await vn.rm(r,{recursive:!0,force:!0})}catch(a){y.error(`Failed to remove stale temp dir: ${i.name}`,a instanceof Error?a:new Error(String(a)))}}if(i.name.startsWith("place_")){let r=$e.join(e,i.name);try{let a=await vn.readdir(r,{withFileTypes:!0});for(let o of a)if(o.isDirectory()&&o.name.startsWith("explorer_tmp_")){let s=$e.join(r,o.name);y.warn("Removing stale temp directory from crashed sync",{dir:`${i.name}/${o.name}`});try{await vn.rm(s,{recursive:!0,force:!0})}catch(c){y.error(`Failed to remove stale temp dir: ${i.name}/${o.name}`,c instanceof Error?c:new Error(String(c)))}}}catch{continue}}}}catch(n){if(n.code==="ENOENT")return;y.warn("Failed to scan for stale temp dirs",{error:n instanceof Error?n.message:String(n)})}}};import sr from"path";import{promises as Dv}from"fs";ie();var Ov=class{constructor(e){this.ctx=e}async handleReversePending(e,n){let i=this.ctx.resolveQueryPlaceId(e);if(i==null){n.status(200).json({pending:0,hasConflicts:!1,lastDetected:null});return}let r=this.ctx.places.get(i);if(!r){n.status(404).json({error:"Place not found",message:`No sync context for place ${i}`});return}let a={pending:r.fileWatcher?.getPendingCount()??0,hasConflicts:!1,lastDetected:r.fileWatcher?.getLastDetected()??null,forwardRestoreNeeded:r.forwardRestoreQueue.length};this.ctx.touchRuntimePlace(i),n.status(200).json(a)}async handleReverseSyncChanges(e,n){let i=this.ctx.resolveQueryPlaceId(e);if(i==null){n.status(200).json({changes:[],count:0});return}let r=this.ctx.places.get(i);if(!r){n.status(404).json({error:"Place not found",message:`No sync context for place ${i}`});return}if(r.state!=="syncing"){n.status(400).json({error:"Not syncing",message:"Reverse sync is only available when sync is active"});return}let a=r.fileWatcher?.drainPendingChanges()??[],o=a.length>0?await r.reader.buildChangesFromPending(a):[];this.ctx.touchRuntimePlace(i),n.status(200).json({changes:o,count:o.length})}async handleReverseSyncResult(e,n){let i=e.body,r=i.placeId??this.ctx.getDefaultRuntimePlaceId();if(r==null){n.status(400).json({error:"Validation error",message:"placeId is required (in body or via active sync session)"});return}let a=i.appliedFiles??i.appliedPaths;if(!a||!Array.isArray(a)){n.status(400).json({error:"Validation error",message:"appliedFiles (or appliedPaths) must be an array of relative file paths"});return}let o=this.ctx.places.get(r);if(!o){n.status(404).json({error:"Place not found",message:`No sync context for place ${r}`});return}this.ctx.touchRuntimePlace(r);let s=this.ctx.config.getPlaceRoot(r),c=0,l=[];for(let u of a){let d=sr.resolve(s,u);if(!Pv(s,d)){l.push({path:u,error:"Path is outside the place root"});continue}try{let p=await Dv.readFile(d,"utf-8"),f=o.index.computeHash(p);o.index.updateHashByValue(d,f),o.index.updateFileHashByValue(d,f),c++}catch(p){let f=p.code;if(f==="ENOENT"){o.index.removeHash(d),o.index.removeHashesUnder(d);let m=this.resolveInstancePathForAppliedPath(o.index,d);if(m){let h=nt(m),g=mt(m);g&&h&&await o.writer.removeFromTree(g,h)}c++}else f==="EISDIR"?c++:l.push({path:u,error:p instanceof Error?p.message:String(p)})}}c>0&&await o.index.saveToDisk(),c>0&&o.writer.appendHistory({timestamp:new Date().toISOString(),type:"reverseApply",direction:"reverse",source:"local",path:`place_${r}`,details:`applied:${c} failed:${l.length}`}),n.status(200).json({updated:c,failed:l.length,errors:l})}async handleResolveConflict(e,n){let i=e.body;if(!i.fsPath||!i.resolution){n.status(400).json({error:"Validation error",message:"fsPath and resolution are required"});return}let{fsPath:r,resolution:a}=i,o=this.ctx.config.getSyncRoot();if(!Pv(o,sr.resolve(o,r))){n.status(403).json({error:"Forbidden",message:"Path is outside the sync root"});return}if(a==="skip"){n.status(200).json({status:"skipped",fsPath:r});return}let s;if(i.placeId&&(s=this.ctx.places.get(i.placeId)),!s){let p=r.match(/^place_(\d+)(?:_[^/]+)?\//);if(p){let f=parseInt(p[1],10);s=this.ctx.places.get(f)}else s=Array.from(this.ctx.places.values())[0]}if(!s){n.status(404).json({error:"No active place context",message:"No sync session is active. Start a sync first."});return}let c=this.ctx.config.getPlaceRoot(s.placeId),l=sr.resolve(c,r);if(!Pv(c,l)){n.status(403).json({error:"Forbidden",message:"Path is outside the place root"});return}let u=s.index,d=s.reader;if(a==="apply-studio"){if(typeof i.studioContent!="string"){n.status(400).json({error:"Validation error",message:"studioContent is required for apply-studio resolution"});return}await Dv.mkdir(sr.dirname(l),{recursive:!0});let p=i.studioContent,f=u.computeHash(p);s.fileWatcher?.suppressPath(l,f),await Dv.writeFile(l,p,"utf-8"),u.updateHashByValue(l,f),u.updateFileHashByValue(l,f),await u.saveToDisk(),n.status(200).json({status:"resolved",resolution:"apply-studio",fsPath:r});return}if(a==="apply-file"){let p=await Dv.readFile(l,"utf-8"),f=u.computeHash(p);u.resolveFile(l,"apply-file",f),await u.saveToDisk();let m=d.getFileType(l),h=d.resolveInstancePathFromFile(l);n.status(200).json({status:"resolved",resolution:"apply-file",fsPath:r,instancePath:h,fileType:m,content:p});return}n.status(400).json({error:"Invalid resolution",message:`Unknown resolution: ${a}`})}async handleReverseRescan(e,n){let i=this.ctx.resolveQueryPlaceId(e);if(i==null){n.status(200).json({added:0});return}let r=this.ctx.places.get(i);if(!r){n.status(404).json({error:"Place not found",message:`No sync context for place ${i}`});return}if(!r.fileWatcher){n.status(200).json({added:0});return}let a=await r.fileWatcher.rescan();this.ctx.touchRuntimePlace(i),y.info("Reverse rescan completed",{placeId:i,added:a}),n.status(200).json({added:a})}resolveInstancePathForAppliedPath(e,n){let i=e.resolveInstancePathFromFsPath(n);if(i)return i;let r=e.getExplorerRoot(),a=sr.relative(r,n);if(a.startsWith("..")||a===""||sr.isAbsolute(a))return null;let o=a.split(sr.sep).filter(f=>f.length>0);if(o.length<2)return null;let s=sr.basename(n),c=sr.dirname(n),l=e.getOriginalInstance(c,s);if(l)return l.instancePath;let u=s.toLowerCase();if(sv.some(f=>u.endsWith(f))||u==="_tree.json")return null;let d=["game"],p=r;for(let f of o){p=sr.join(p,f);let m=sr.dirname(p);d.push(e.getOriginalNameForDir(m,f))}return st(d)}};ie();function pU(t){if(!t||typeof t!="object")return;let e=t;if(Array.isArray(e.instances))for(let n of e.instances)Array.isArray(n.properties)&&n.properties.length===0&&(n.properties={}),Array.isArray(n.attributes)&&n.attributes.length===0&&(n.attributes={});if(Array.isArray(e.changes))for(let n of e.changes)Array.isArray(n.properties)&&n.properties.length===0&&(n.properties={}),Array.isArray(n.attributes)&&n.attributes.length===0&&(n.attributes={})}function Wce(t){return t.type==="instanceAdded"||t.type==="instanceRemoved"||t.type==="instanceRenamed"||t.type==="instanceMoved"||t.type==="scriptSourceChanged"}var $r=class{config;places;apiHandler;changeProcessor;initHandler;reverseHandler;reverseDetectorFactory;activeFullSyncPlaceId=null;activeRuntimeSyncPlaceId=null;currentConnectedPlaceId=null;currentConnectedPlaceName=null;constructor(e,n={}){this.config=new iv(e,n),this.reverseDetectorFactory=n.reverseDetectorFactory??(i=>new kv(i.explorerRoot,i.syncIndex,{placeId:i.placeId})),this.apiHandler=new Tv(this),this.changeProcessor=new Ev(this),this.initHandler=new $v(this),this.reverseHandler=new Ov(this),this.places=new Sv({max:3,dispose:(i,r)=>{y.info("Disposing place context (LRU eviction)",{placeId:r}),this.activeFullSyncPlaceId===r&&(this.activeFullSyncPlaceId=null),i.fileWatcher&&(y.info("Disposing place context: stopping reverse detector",{placeId:r,reason:"lru-dispose",mode:rt}),i.fileWatcher.stop("lru-dispose").catch(a=>{y.error("Error stopping reverse detector during dispose",a)}),i.fileWatcher=null),i.writer.stopChangeLogFlusher().catch(a=>{y.error("LRU dispose \uC911 changelog flush \uC2E4\uD328",a)}),i.incompleteSyncTimer&&(clearTimeout(i.incompleteSyncTimer),i.incompleteSyncTimer=null),i.index.clearAllMaps(),i.index.saveToDisk().catch(a=>{y.error("Error saving index during dispose",a)}),i.activeFullSyncSessionId&&this.initHandler.clearPendingServiceTrees(i.activeFullSyncSessionId),i.tmpWriter&&(i.tmpWriter.stopChangeLogFlusher(),i.tmpWriter=null),i.tmpIndex=null,i.collisionDirMap=null,i.activeSyncIdentityScope=null}})}getSyncRoot(){return this.config.getSyncRoot()}async getOrCreatePlaceContext(e,n){let i=this.places.get(e);if(i&&n){let r=this.config.getPlaceRoot(e),a=await this.config.resolvePlaceRoot(e,n);a!==r&&(y.info("Place root migrated, recreating context",{placeId:e,from:r,to:a}),this.places.delete(e),i=void 0)}if(!i){let r=await this.config.resolvePlaceRoot(e,n),a=yP.join(r,"explorer");await Tc.mkdir(r,{recursive:!0}),await Tc.mkdir(a,{recursive:!0});let o=new ta(r,a);await o.loadFromDisk();let s=new Pc(this.config,o,e),c=new wv(this.config,o,r);s.startChangeLogFlusher(),i={placeId:e,placeName:"",index:o,writer:s,reader:c,fileWatcher:null,state:"idle",activeClientId:null,activeFullSyncSessionId:null,instanceCount:0,scriptCount:0,lastFullSync:null,lastIncrementalSync:null,tmpIndex:null,tmpWriter:null,incompleteSyncTimer:null,changesSinceLastSave:0,directions:{...io},applyModes:{...ao},forwardRestoreQueue:[],syncProgress:null,collisionDirMap:null,activeSyncIdentityScope:null},this.places.set(e,i),y.info("Created new place context",{placeId:e,placeRoot:r})}return i}async handleSyncInit(e,n){try{pU(e.body);let i=cU(e.body);if(!i.success){n.status(400).json({error:"Validation error",message:i.error});return}let r=i.data,a=r.phase==="start"?r.placeId??null:r.phase==="chunk"||r.phase==="complete"?this.activeFullSyncPlaceId:null;if(a==null){n.status(400).json({error:"Missing placeId",message:"placeId is required in start phase or must be set by previous start"});return}switch(r.phase){case"start":this.setCurrentConnectedPlace(a,r.placeName),await this.initHandler.handleInitStart(a,r,n);break;case"chunk":await this.initHandler.handleInitChunk(a,r,n);break;case"complete":await this.initHandler.handleInitComplete(a,r,n);break}}catch(i){this.sendError(n,i,"handleSyncInit")}}async handleSyncUpdate(e,n){try{pU(e.body);let i=lU(e.body);if(!i.success){n.status(400).json({error:"Validation error",message:i.error});return}let r=i.data,a=r.placeId??this.getDefaultRuntimePlaceId();if(a==null){n.status(400).json({error:"Missing placeId",message:"placeId is required in body or must have an active sync session"});return}let o=await this.getOrCreatePlaceContext(a);if(this.setCurrentConnectedPlace(a,o.placeName||this.currentConnectedPlaceName),o.activeFullSyncSessionId!==null||o.state==="initializing"){n.status(409).json({error:"Conflict",message:`Full sync in progress for place ${a}`});return}o.activeClientId=r.clientId,this.touchRuntimePlace(a);let s=aU(o,r.changes);y.info("Sync update received",{placeId:a,clientId:r.clientId,changeCount:s.length,receivedCount:r.changes.length,types:s.map(m=>m.type)});let c=[],l=[],u=0,d=!1,p=new Map;for(let m of s)try{let h=await this.changeProcessor.processChangeForPlace(o,m,p);h?l.push(h):(u++,Wce(m)&&(d=!0))}catch(h){let g="path"in m?m.path:"oldPath"in m?m.oldPath:"unknown";c.push({path:g,error:h instanceof Error?h.message:String(h)})}for(let m of p.values())try{await Cv(o,m)}catch(h){c.push({path:m.instancePath,error:h instanceof Error?h.message:String(h)})}if(d)try{let m=this.config.getSyncRoot(),h=this.config.getPlaceRoot(a);await lv(m,h),await hc(m)}catch(m){y.warn("Failed to refresh sourcemaps after incremental sync update",{placeId:a,error:m instanceof Error?m.message:String(m)})}o.changesSinceLastSave+=u,o.changesSinceLastSave>=sU&&(await o.index.saveToDisk(),o.changesSinceLastSave=0),o.lastIncrementalSync=new Date().toISOString();let f={processed:u,failed:c.length,errors:c,syncedAt:o.lastIncrementalSync};l.length>0&&(f.conflicts=l),n.status(200).json(f)}catch(i){this.sendError(n,i,"handleSyncUpdate")}}getDefaultRuntimePlaceId(){return this.currentConnectedPlaceId}getDefaultRuntimePlaceName(){if(this.currentConnectedPlaceName)return this.currentConnectedPlaceName;if(this.currentConnectedPlaceId!==null&&this.currentConnectedPlaceId!==void 0){let e=this.places.get(this.currentConnectedPlaceId);if(e?.placeName)return e.placeName}return null}setCurrentConnectedPlace(e,n){if(this.currentConnectedPlaceId=e,e==null){this.currentConnectedPlaceName=null;return}if(typeof n=="string"&&n.length>0){this.currentConnectedPlaceName=n;let r=this.places.get(e);r&&(r.placeName=n);return}let i=this.places.get(e);i?.placeName&&(this.currentConnectedPlaceName=i.placeName)}clearCurrentConnectedPlaceIfMatch(e){this.currentConnectedPlaceId===e&&(this.currentConnectedPlaceId=null,this.currentConnectedPlaceName=null)}touchRuntimePlace(e){this.activeRuntimeSyncPlaceId=e}clearRuntimePlaceIfMatch(e){this.activeRuntimeSyncPlaceId===e&&(this.activeRuntimeSyncPlaceId=null)}resolveQueryPlaceId(e,n="runtime"){let i=e.query.placeId;if(i){let r=parseInt(i,10);if(!isNaN(r))return r}return n==="full"?this.activeFullSyncPlaceId:this.getDefaultRuntimePlaceId()}async handleSyncStatus(e,n){try{let i=u=>({state:"idle",instanceCount:0,scriptCount:0,lastFullSync:null,lastIncrementalSync:null,syncRoot:u,activeClientId:null,reverseSyncAvailable:!1,modifiedFileCount:0,reverseDetectorActive:!1,reverseDetectorMode:rt,fileWatcherActive:!1}),r=this.resolveQueryPlaceId(e);if(r==null){let u=i(this.config.getSyncRoot());n.status(200).json(u);return}let a=this.places.get(r);if(!a){let u=i(this.config.getPlaceRoot(r));n.status(200).json(u);return}let o=a.fileWatcher?.getPendingCount()??0,s=a.fileWatcher?.isActivelyScanning()??!1,c=a.fileWatcher!==null;this.touchRuntimePlace(r);let l={state:a.state,instanceCount:a.instanceCount,scriptCount:a.scriptCount,lastFullSync:a.lastFullSync,lastIncrementalSync:a.lastIncrementalSync,syncRoot:this.config.getPlaceRoot(r),activeClientId:a.activeClientId,reverseSyncAvailable:o>0,modifiedFileCount:o,applyModes:a.applyModes,directions:a.directions,reverseDetectorActive:s,reverseDetectorMode:rt,fileWatcherActive:c,forwardOnlyClasses:[...mc]};n.status(200).json(l)}catch(i){this.sendError(n,i,"handleSyncStatus")}}async handleSyncStop(e,n){try{let i=e.body,r=i.placeId??this.getDefaultRuntimePlaceId();if(y.info("handleSyncStop requested",{placeId:r,clientId:i.clientId??null,reason:i.reason??"requested"}),r==null){n.status(200).json({status:"idle",state:"idle",placeId:null,message:"No active sync place"});return}let a=this.places.get(r);if(!a){y.info("handleSyncStop no place context",{placeId:r,clientId:i.clientId??null}),n.status(200).json({status:"idle",state:"idle",placeId:r,message:`No sync context for place ${r}`});return}if(i.clientId&&a.activeClientId&&i.clientId!==a.activeClientId&&(a.activeFullSyncSessionId!==null||a.state==="syncing"||a.state==="initializing")){y.warn("handleSyncStop rejected due to active client mismatch",{placeId:r,requestedClientId:i.clientId,activeClientId:a.activeClientId,state:a.state,activeFullSyncSessionId:a.activeFullSyncSessionId}),n.status(409).json({error:"Conflict",message:`This sync session belongs to client ${a.activeClientId}`});return}let o=a.activeFullSyncSessionId,s=a.state,c=a.fileWatcher!==null;if(o&&(this.initHandler.clearPreserveLocalFiles(o),this.initHandler.clearPendingServiceTrees(o)),a.fileWatcher&&(y.info("handleSyncStop suspending reverse detector",{placeId:r,state:a.state,activeClientId:a.activeClientId,mode:rt}),a.fileWatcher.beginFullSyncReplacement()),a.incompleteSyncTimer&&(clearTimeout(a.incompleteSyncTimer),a.incompleteSyncTimer=null),o){let l=this.config.getPlaceRoot(r),u=yP.join(l,`explorer_tmp_${o}`);await Tc.rm(u,{recursive:!0,force:!0}).catch(()=>{})}a.tmpWriter&&(a.tmpWriter.stopChangeLogFlusher(),a.tmpWriter=null),a.tmpIndex=null,a.collisionDirMap=null,a.activeSyncIdentityScope=null,a.activeFullSyncSessionId=null,a.syncProgress=null,a.state="idle",a.activeClientId=null,a.instanceCount=0,a.scriptCount=0,a.forwardRestoreQueue=[],this.activeFullSyncPlaceId===r&&(this.activeFullSyncPlaceId=null),this.clearRuntimePlaceIfMatch(r),y.info("handleSyncStop responding idle",{placeId:r,previousState:s,activeFullSyncSessionId:o,hadWatcher:c}),y.info("Sync stopped",{placeId:r,reason:i.reason??"requested"}),n.status(200).json({status:"stopped",state:"idle",placeId:r})}catch(i){this.sendError(n,i,"handleSyncStop")}}async initialize(){try{await this.config.loadFromMeta(),await this.initHandler.cleanupStaleTempDirs(),y.info("SyncController initialized")}catch(e){y.error("SyncController initialization failed",e instanceof Error?e:new Error(String(e)))}}async shutdown(){try{y.info("SyncController shutdown: clearing place contexts",{placeCount:this.places.size}),this.places.clear(),y.info("SyncController shut down")}catch(e){y.error("SyncController shutdown error",e instanceof Error?e:new Error(String(e)))}}async atomicWriteFile(e,n){let i=e+".tmp."+Vce().slice(0,8);try{await Tc.writeFile(i,n,"utf-8"),await Tc.rename(i,e)}catch(r){throw await Tc.unlink(i).catch(()=>{}),r}}async handlePreCheck(e,n){try{await this.apiHandler.handlePreCheck(e,n)}catch(i){this.sendError(n,i,"handlePreCheck")}}async handleSyncDirections(e,n){try{await this.apiHandler.handleSyncDirections(e,n)}catch(i){this.sendError(n,i,"handleSyncDirections")}}async handleForwardRestoreList(e,n){try{await this.apiHandler.handleForwardRestoreList(e,n)}catch(i){this.sendError(n,i,"handleForwardRestoreList")}}async handleReversePending(e,n){try{await this.reverseHandler.handleReversePending(e,n)}catch(i){this.sendError(n,i,"handleReversePending")}}async handleReverseSyncChanges(e,n){try{await this.reverseHandler.handleReverseSyncChanges(e,n)}catch(i){this.sendError(n,i,"handleReverseSyncChanges")}}async handleReverseSyncResult(e,n){try{await this.reverseHandler.handleReverseSyncResult(e,n)}catch(i){this.sendError(n,i,"handleReverseSyncResult")}}async handleResolveConflict(e,n){try{await this.reverseHandler.handleResolveConflict(e,n)}catch(i){this.sendError(n,i,"handleResolveConflict")}}async handleReverseRescan(e,n){try{await this.reverseHandler.handleReverseRescan(e,n)}catch(i){this.sendError(n,i,"handleReverseRescan")}}async handleSyncHistory(e,n){try{await this.apiHandler.handleSyncHistory(e,n)}catch(i){this.sendError(n,i,"handleSyncHistory")}}async startFileWatcherForPlace(e){if(e.state!=="syncing"){y.debug("Skipping reverse detector start - place not syncing",{placeId:e.placeId,state:e.state});return}if(e.fileWatcher){if(e.fileWatcher.isActivelyScanning()){y.info("startFileWatcherForPlace: reusing active reverse detector",{placeId:e.placeId,mode:rt});return}y.info("startFileWatcherForPlace: resuming paused reverse detector",{placeId:e.placeId,mode:rt}),e.fileWatcher.endFullSyncReplacement();return}let n=this.createReverseChangeDetector(e);this.configureReverseChangeDetector(e,n),e.fileWatcher=n,n.start().then(()=>{e.fileWatcher===n&&y.info("Reverse detector started for reverse sync",{placeId:e.placeId,mode:rt})}).catch(i=>{y.warn("Failed to start reverse detector",{placeId:e.placeId,mode:rt,error:i instanceof Error?i.message:String(i)}),e.fileWatcher===n&&(e.fileWatcher=null)})}createReverseChangeDetector(e){return this.reverseDetectorFactory({explorerRoot:yP.join(this.config.getPlaceRoot(e.placeId),"explorer"),syncIndex:e.index,placeId:e.placeId})}configureReverseChangeDetector(e,n){e.writer.setOnWriteCallback(i=>{n.suppressPath(i)}),n.setDirectionChecker(i=>{let r=UL(i);return e.directions[r]}),n.setOnForwardViolation(i=>{e.forwardRestoreQueue.includes(i)||(e.forwardRestoreQueue.push(i),y.info("Forward violation queued for restore",{placeId:e.placeId,relativePath:i,queueSize:e.forwardRestoreQueue.length}))})}getStatusSummary(){return this.apiHandler.getStatusSummary()}getDirectionForCategory(e){return this.apiHandler.getDirectionForCategory(e)}getStatusDirect(e){return this.apiHandler.getStatusDirect(e)}async getHistoryDirect(e,n){return this.apiHandler.getHistoryDirect(e,n)}getDirectionsDirect(e){return this.apiHandler.getDirectionsDirect(e)}getProgressDirect(e){return this.apiHandler.getProgressDirect(e)}async readSyncedFile(e,n){return this.apiHandler.readSyncedFile(e,n)}async writeSyncedFile(e,n,i){await this.apiHandler.writeSyncedFile(e,n,i)}async executeViaDisk(e,n){return this.apiHandler.executeViaDisk(e,n)}sendError(e,n,i){let r=n instanceof Error?n.message:String(n);if(r.includes("Path traversal detected")){e.status(403).json({error:"Forbidden",message:r});return}let a=n.code;if(a==="ENOSPC"||a==="EPERM"||a==="EACCES"){e.status(500).json({error:"Disk error",message:r});return}y.error(`SyncController.${i} failed`,n instanceof Error?n:new Error(r)),e.status(500).json({error:"Internal error",message:`${i}: ${r}`})}};import{randomUUID as Gce}from"crypto";function fU(t,e){let n=Gce(),i=n.replace(/-/g,"").substring(0,8).toUpperCase(),r=`${i.substring(0,4)}-${i.substring(4,8)}`;return{config:t,app:e,instanceId:n,sessionId:r,startTime:Date.now(),baseUrl:`http://${t.httpHost}:${t.httpPort}`,commandQueue:new Map,pendingCommands:new Map,globalPendingCommands:[],totalCommandsProcessed:0,pluginClients:new Map,pluginCommandSessionsByClientId:new Map,pluginCommandClientIdsByProcessToken:new Map,mcpInstances:new Map,cachedSelectionMap:new Map,isClientMode:!1,clientModeHealthTimer:null,clientModeConsecutiveHealthFailures:0,clientModeUpstreamReachable:!0,clientModeUpstreamContextCaptureEnabled:!0,clientModeLastHealthSuccessAt:null,clientModeLastHealthFailureAt:null,clientModeLastHealthError:null,shuttingDown:!1,onUpstreamPermanentlyDown:null,clientModeIdleTimeoutMs:t.clientModeIdleTimeoutMs??36e5,clientModeIdleWatchdogTimer:null,shutdownFn:null,historyManager:null,analyticsManager:null,executionContextManager:null,licenseState:null,syncController:null,internalCommandExecutor:null,dashboardFolderPicker:null,activeSyncOwnerInstanceId:null,activeProjectRoot:null,pendingDashboardSyncRootPin:null,playtestControlCommand:null,dashboardSyncRootSwitchInFlight:!1,uiStudioRecorders:new Map,aiClientName:"",pluginVersion:"",syncedSessionToken:null,serverLastCommandAt:null}}var mU=Et(no(),1);ie();function hU(t){let e=mU.default.json({limit:"5mb"});t.app.use((n,i,r)=>{if(n.path.startsWith("/sync/")){r();return}e(n,i,r)}),t.app.use((n,i,r)=>{i.setHeader("Access-Control-Allow-Origin","http://localhost:3002"),i.setHeader("Access-Control-Allow-Methods","GET, POST, OPTIONS"),i.setHeader("Access-Control-Allow-Headers","Content-Type"),r()}),t.app.use((n,i,r)=>{y.debug(`${n.method} ${n.path}`,{ip:n.ip}),r()})}zd();import{randomUUID as FP}from"crypto";ie();function Jce(){let t=process.env.WEPPY_ROBLOX_MCP_VERSION?.trim();return t||null}var it=Jce()??"2.7.6";Yd();function Mv(t){let e=new Set;t.aiClientName&&e.add(t.aiClientName);for(let n of t.mcpInstances.values())n.aiClientName&&e.add(n.aiClientName);return Array.from(e)}function _U(t){let n=Date.now();for(let[i,r]of t.mcpInstances)r.lastSeen&&n-r.lastSeen>15e3&&(t.mcpInstances.delete(i),r.sessionId&&t.executionContextManager?.endSession(r.sessionId),y.debug("Removed stale MCP instance",{instanceId:i,lastSeen:r.lastSeen}))}function SU(t,e,n){let i=e.query.clientId;if(i&&t.pluginClients.has(i)){let r=t.pluginClients.get(i);r.lastSeen=Date.now()}try{let r=e.body;if(!r||!Array.isArray(r.selection)||typeof r.count!="number"){y.warn("Invalid selection update request",{body:r}),n.status(400).json({error:"Invalid request body"});return}let a=i||"unknown",o=Date.now();t.cachedSelectionMap.set(a,{selection:r.selection,count:r.count,timestamp:o,clientId:a}),y.debug("Selection cache updated",{count:r.count,clientId:a,timestamp:o}),n.json({status:"ok",timestamp:o})}catch(r){y.error("Error handling selection update",r),n.status(500).json({error:"Internal server error"})}}function wU(t,e,n){let i=parseInt(e.query.maxAge)||3e4,r=Qd(t,i);r?n.json({cached:!0,...r}):n.json({cached:!1,message:"No cached selection available"})}function Qd(t,e=3e4,n){if(t.cachedSelectionMap.size===0)return null;let i;if(n)i=t.cachedSelectionMap.get(n);else for(let a of t.cachedSelectionMap.values())(!i||a.timestamp>i.timestamp)&&(i=a);if(!i)return null;let r=Date.now()-i.timestamp;return e===0||r<=e?i:null}function kP(t,e){let n=t.pluginClients.get(e.clientId),i=Date.now(),r={clientId:e.clientId,projectName:e.projectName,placeName:e.placeName,placeId:e.placeId,pluginVersion:e.pluginVersion,connectedAt:n?.connectedAt||i,lastSeen:i,commandsProcessed:n?.commandsProcessed||0,connectionType:n?.connectionType||"polling",inFlightRequestId:n?.inFlightRequestId,...n?.commandSessionState?{commandSessionState:n.commandSessionState}:{},...n?.processToken?{processToken:n.processToken}:{}};return t.pluginClients.set(e.clientId,r),t.syncController&&typeof t.syncController.setCurrentConnectedPlace=="function"&&typeof e.placeId=="number"&&Number.isFinite(e.placeId)&&t.syncController.setCurrentConnectedPlace(e.placeId,e.placeName??null),t.pendingCommands.has(e.clientId)||t.pendingCommands.set(e.clientId,[]),e.pluginVersion&&(t.pluginVersion=e.pluginVersion),typeof r.placeId=="number"&&Number.isFinite(r.placeId)&&YI(t,r.placeId,r.placeName??null),t.analyticsManager&&(e.pluginVersion&&t.analyticsManager.setPluginVersion(e.pluginVersion),t.analyticsManager.trackPluginConnected()),y.info("Plugin client registered",{clientId:e.clientId,projectName:e.projectName,placeName:e.placeName,isReconnect:!!n}),{clientInfo:r,sessionId:t.sessionId,mcpVersion:it,mcpInstanceCount:t.mcpInstances.size+1,aiClientNames:Mv(t)}}function CP(t,e){let n=t.pluginClients.get(e),i=t.pluginClients.has(e);if(t.pluginClients.delete(e),t.pendingCommands.delete(e),i&&t.syncController&&typeof t.syncController.clearCurrentConnectedPlaceIfMatch=="function"&&typeof n?.placeId=="number"&&Number.isFinite(n.placeId)){let r=[...t.pluginClients.values()].filter(a=>typeof a.placeId=="number"&&Number.isFinite(a.placeId)).sort((a,o)=>o.lastSeen-a.lastSeen)[0];r?t.syncController.setCurrentConnectedPlace?.(r.placeId??null,r.placeName??null):t.syncController.clearCurrentConnectedPlaceIfMatch(n.placeId)}return i&&(ot(t,"connection",{clientId:e,status:"disconnected"}),xc(t,{timestamp:new Date().toISOString(),type:"plugin",status:"disconnected",clientId:e,message:`Plugin disconnected \u2014 ${e}`})),y.info("Plugin client unregistered",{clientId:e,existed:i}),i}function zv(t,e,n){try{let i=e.body;if(!i.clientId){n.status(400).json({error:"Missing clientId"});return}let r=kP(t,{clientId:i.clientId,projectName:i.projectName,placeName:i.placeName,placeId:i.placeId,pluginVersion:i.pluginVersion});n.json({status:"ok",clientId:i.clientId,serverInstanceId:t.instanceId,sessionId:r.sessionId,mcpVersion:r.mcpVersion,connectedAt:r.clientInfo.connectedAt,aiClientNames:r.aiClientNames,serverStartTime:t.startTime,mcpInstanceCount:r.mcpInstanceCount})}catch(i){y.error("Error registering plugin client",i),n.status(500).json({error:"Internal server error"})}}function Lv(t,e,n){let i=e.body?.clientId;if(!i){n.status(400).json({error:"Missing clientId"});return}let r=CP(t,i);n.json({status:"ok",existed:r})}function kU(t,e,n){try{let i=e.body;if(!i.instanceId){n.status(400).json({error:"Missing instanceId"});return}let r=Date.now(),a={instanceId:i.instanceId,...typeof i.sessionId=="string"?{sessionId:i.sessionId}:{},pid:i.pid,connectedAt:r,isServer:!1,lastSeen:r};i.aiClientName&&(a.aiClientName=i.aiClientName),i.cwd&&(a.cwd=i.cwd),"projectRoot"in i&&(a.projectRoot=i.projectRoot),t.mcpInstances.set(i.instanceId,a),ot(t,"mcp_status",{aiClientName:a.aiClientName??"Unknown",instanceId:i.instanceId,status:"registered"}),xc(t,{timestamp:new Date().toISOString(),type:"mcp",status:"registered",instanceId:i.instanceId,message:`MCP registered \u2014 ${a.aiClientName??i.instanceId}`,...a.aiClientName?{aiClientName:a.aiClientName}:{}}),y.info("MCP instance registered (client mode)",{instanceId:i.instanceId,pid:i.pid,cwd:i.cwd}),n.json({status:"ok",instanceId:i.instanceId,serverInstanceId:t.instanceId,sessionId:t.sessionId,mcpVersion:it,mcpInstanceCount:t.mcpInstances.size+1})}catch(i){y.error("Error registering MCP instance",i),n.status(500).json({error:"Internal server error"})}}function CU(t,e,n){let i=e.body?.instanceId;if(!i){n.status(400).json({error:"Missing instanceId"});return}let r=t.mcpInstances.get(i),a=!!r;t.mcpInstances.delete(i),r?.sessionId&&t.executionContextManager?.endSession(r.sessionId),a&&(ot(t,"mcp_status",{aiClientName:r?.aiClientName??"Unknown",instanceId:i,status:"unregistered"}),xc(t,{timestamp:new Date().toISOString(),type:"mcp",status:"unregistered",instanceId:i,message:`MCP unregistered \u2014 ${r?.aiClientName??i}`,...r?.aiClientName?{aiClientName:r.aiClientName}:{}})),y.info("MCP instance unregistered",{instanceId:i,existed:a}),n.json({status:"ok",existed:a})}function IU(t,e,n){let{instanceId:i,aiClientName:r}=e.body;if(i&&r){let a=t.mcpInstances.get(i);a&&(a.aiClientName=r)}n.json({status:"ok"})}function PU(t){let e=3e4,n=Date.now(),i=rr({appDataDir:t.config.appDataDir});for(let[r,a]of t.pluginClients)n-a.lastSeen>e&&(t.pluginClients.delete(r),t.pendingCommands.delete(r));return _U(t),{serverInstanceId:t.instanceId,sessionId:t.sessionId,mcpVersion:it,uptime:n-t.startTime,serverStartTime:t.startTime,serverExecutable:process.execPath,serverHost:t.config.httpHost,serverPort:t.config.httpPort,serverPid:process.pid,mcpInstances:[{instanceId:t.instanceId,pid:process.pid,connectedAt:t.startTime,isServer:!0,cwd:process.cwd(),projectRoot:i,lastSeen:n,...t.aiClientName?{aiClientName:t.aiClientName}:{}},...Array.from(t.mcpInstances.values())],mcpInstanceCount:t.mcpInstances.size+1}}function Uv(t,e){e.json(PU(t))}function Fv(t,e,n){let i=e.query.instanceId;i&&t.mcpInstances.has(i)&&(t.mcpInstances.get(i).lastSeen=Date.now()),_U(t);let r=co(t),s={...{status:"online",connectedClients:r.length,queuedCommands:x6(t),uptime:Date.now()-t.startTime,version:it,enableContextCapture:t.executionContextManager?.isEnabled()??t.config.enableContextCapture??!0,isClientMode:t.isClientMode,shuttingDown:t.shuttingDown,pid:process.pid,sessionId:t.sessionId},instanceId:t.instanceId,mcpInstanceCount:t.mcpInstances.size+1,aiClientNames:Mv(t),pluginVersion:t.pluginVersion||void 0,dashboardSseClients:t.dashboardSseClients?.size??0,websocketClients:r.length,pluginClients:r.map(c=>({clientId:c.clientId,projectName:c.projectName,placeName:c.placeName,pluginVersion:c.pluginVersion,connectionType:c.connectionType,commandSessionState:c.commandSessionState,lastSeen:c.lastSeen})),...t.isClientMode?{upstream:{reachable:t.clientModeUpstreamReachable,consecutiveFailures:t.clientModeConsecutiveHealthFailures,lastSuccessAt:t.clientModeLastHealthSuccessAt,lastFailureAt:t.clientModeLastHealthFailureAt,lastError:t.clientModeLastHealthError,baseUrl:t.baseUrl}}:{}};n.json(s)}function EU(t,e,n,i){let r=e.ip||e.socket.remoteAddress||"";if(!(r==="127.0.0.1"||r==="::1"||r==="::ffff:127.0.0.1"||r==="localhost")){y.warn("Shutdown request rejected from non-localhost",{ip:r}),n.status(403).json({error:"Forbidden: localhost only"});return}y.info("Shutdown request received, initiating graceful shutdown",{requestedBy:r,uptime:Date.now()-t.startTime}),n.json({status:"shutting_down",message:"Server will shutdown gracefully",pid:process.pid}),setTimeout(async()=>{try{await i(),y.info("Graceful shutdown completed"),process.exit(0)}catch(o){y.error("Error during graceful shutdown",o),process.exit(1)}},100)}async function qv(t){if(t.isClientMode)try{let e=await fetch(`${t.baseUrl}/connection-info`);if(e.ok)return await e.json()}catch(e){y.warn("Failed to fetch connection info from server",{error:e})}return PU(t)}ie();Jd();var Oc={query_instances:{discriminator:"action",validActions:new Set(["get","children","find_child","find_descendant","wait_for_child","class_info","search_name","search_class","search_property","search_tag","file_tree","project_structure","descendants","ancestors"]),paramAliases:{query_instances_search_name:{query:"pattern"},query_instances_search_property:{root:"rootPath"},query_instances_search_tag:{root:"rootPath"},query_instances_project_structure:{root:"rootPath"}}},mutate_instances:{discriminator:"action",validActions:new Set(["create","create_with_props","delete","clone","move","rename","pivot","create_tree","mass_create","mass_delete","mass_duplicate","smart_duplicate"]),paramAliases:{mutate_instances_clone:{path:"sourcePath"},mutate_instances_smart_duplicate:{spacing:"offset"}}},manage_properties:{discriminator:"action",validActions:new Set(["get","set","get_all","set_multiple","get_attr","set_attr","get_all_attrs","delete_attr","add_tag","remove_tag","check_tag","get_tags","get_tagged","set_calculated","set_relative","mass_set","mass_get","modify_children"]),paramAliases:{manage_properties_get_tagged:{tagName:"tag",root:"rootPath"},manage_properties_set_relative:{amount:"value"}}},manage_scripts:{discriminator:"action",validActions:new Set(["get_source","set_source","create","delete","edit_replace","edit_insert","edit_delete","search","replace","get_dependencies"]),paramAliases:{manage_scripts_edit_replace:{newLines:"newContent"},manage_scripts_edit_insert:{lines:"content"},manage_scripts_replace:{pattern:"searchPattern"}}},manage_ui:{discriminator:"action",validActions:new Set(["design_brief","create_tree","update","delete","preview","check"])},manage_lighting:{discriminator:"action",validActions:new Set(["lighting","atmosphere","sky","terrain_props","time"])},manage_selection:{discriminator:"action",validActions:new Set(["get","set","clear","cached","context","details","add","remove","watch"])},manage_camera:{discriminator:"action",validActions:new Set(["info","focus_path","focus_position","suggest","screenshot"]),paramAliases:{manage_camera_suggest:{path:"targetPath"}}},manage_tween:{discriminator:"action",validActions:new Set(["create","play","pause","cancel"])},manage_audio:{discriminator:"action",validActions:new Set(["play","stop","pause","resume","set_listener"])},manage_animation:{discriminator:"action",validActions:new Set(["load","play","stop","get_tracks"])},manage_physics:{discriminator:"action",validActions:new Set(["register_group","set_collidable","get_groups"])},manage_effects:{discriminator:"action",validActions:new Set(["emit","clear","toggle"])},manage_terrain:{discriminator:"action",validActions:new Set(["fill_block","fill_ball","fill_cylinder","fill_wedge","clear_region","clear_bounds","replace_material","colors_get","colors_set","read_voxel","read_voxels","write_voxels","generate","smooth"])},spatial_query:{discriminator:"action",validActions:new Set(["raycast","find_ground","check_placement","multi_raycast","scan_area","find_flat","find_spawn","analyze_walkable","spatial_map","find_space","bounds","snap_grid","collision"]),paramAliases:{spatial_query_spatial_map:{path:"rootPath"}}},manage_assets:{discriminator:"action",validActions:new Set(["insert","info","search","search_insert","insert_free","insert_package","export"]),paramAliases:{manage_assets_search:{maxResults:"limit"}}},manage_sync:{discriminator:"action",validActions:new Set(["status_current_place","history","directions","read_file","write_file","progress"])},workspace_state:{discriminator:"action",validActions:new Set(["sync","snapshot","changes","viewport","clear_history","metadata","scripts","selection_info","clear_cache"])},manage_logs:{discriminator:"action",validActions:new Set(["get","clear","errors"]),paramAliases:{manage_logs_get:{level:"type"}}},system_info:{discriminator:"action",validActions:new Set(["ping","connection","usage","place_info","services","studio_settings","play","stop","pause","resume","play_status","run_test"])},manage_studio:{discriminator:"action",validActions:new Set(["toggle_ui_preview"])}};var Yce={manage_selection_cached:"internal",manage_sync_status_current_place:"internal",manage_sync_history:"internal",manage_sync_directions:"internal",manage_sync_read_file:"internal",manage_sync_write_file:"internal",manage_sync_progress:"internal",system_info_connection:"internal",system_info_run_test:"internal"};function Bv(t){return Yce[t]||"plugin"}var RU=new Set(["batch_execute","execute_luau","extended_call_method","extended_call_methods","extended_get_properties","extended_get_property","extended_set_properties","extended_set_property","manage_animation_get_tracks","manage_animation_load","manage_animation_play","manage_animation_stop","manage_assets_export","manage_assets_info","manage_assets_insert","manage_assets_insert_free","manage_assets_insert_package","manage_assets_search","manage_assets_search_insert","manage_audio_pause","manage_audio_play","manage_audio_resume","manage_audio_set_listener","manage_audio_stop","manage_camera_screenshot","manage_effects_clear","manage_effects_emit","manage_effects_toggle","manage_lighting_atmosphere","manage_lighting_lighting","manage_lighting_sky","manage_lighting_terrain_props","manage_lighting_time","manage_physics_get_groups","manage_physics_register_group","manage_physics_set_collidable","manage_properties_mass_get","manage_properties_mass_set","manage_properties_modify_children","manage_properties_set_calculated","manage_properties_set_relative","manage_scripts_replace","manage_selection_add","manage_selection_context","manage_selection_details","manage_selection_remove","manage_selection_watch","manage_studio_toggle_ui_preview","manage_sync_directions","manage_sync_history","manage_sync_progress","manage_sync_read_file","manage_sync_status_current_place","manage_sync_write_file","manage_terrain_clear_bounds","manage_terrain_clear_region","manage_terrain_colors_get","manage_terrain_colors_set","manage_terrain_fill_ball","manage_terrain_fill_block","manage_terrain_fill_cylinder","manage_terrain_fill_wedge","manage_terrain_generate","manage_terrain_read_voxel","manage_terrain_read_voxels","manage_terrain_replace_material","manage_terrain_smooth","manage_terrain_write_voxels","manage_tween_cancel","manage_tween_create","manage_tween_pause","manage_tween_play","manage_ui_check","manage_ui_create_tree","manage_ui_delete","manage_ui_design_brief","manage_ui_preview","manage_ui_update","mutate_instances_create_tree","mutate_instances_mass_create","mutate_instances_mass_delete","mutate_instances_mass_duplicate","mutate_instances_smart_duplicate","query_instances_ancestors","query_instances_descendants","query_instances_file_tree","query_instances_project_structure","query_instances_search_property","query_instances_search_tag","spatial_query_analyze_walkable","spatial_query_bounds","spatial_query_check_placement","spatial_query_collision","spatial_query_find_flat","spatial_query_find_ground","spatial_query_find_space","spatial_query_find_spawn","spatial_query_multi_raycast","spatial_query_raycast","spatial_query_scan_area","spatial_query_snap_grid","spatial_query_spatial_map","system_info_pause","system_info_place_info","system_info_play","system_info_play_status","system_info_resume","system_info_run_test","system_info_services","system_info_stop","system_info_studio_settings","workspace_state_changes","workspace_state_clear_cache","workspace_state_clear_history","workspace_state_metadata","workspace_state_scripts","workspace_state_selection_info","workspace_state_snapshot","workspace_state_sync","workspace_state_viewport"]),IP=new Set(["system_info_connection","system_info_ping","system_info_usage"]);function Un(t){return RU.has(t)?"pro":"basic"}function Qce(t){if(t===null||typeof t!="object")return;let e=t.proFallback;if(e===null||typeof e!="object")return;let n=e;if(typeof n.executedCommand!="string")return;let i=Array.isArray(n.alternatives)?n.alternatives.filter(r=>typeof r=="string"):void 0;return{executedCommand:n.executedCommand,...typeof n.requestedCommand=="string"?{requestedCommand:n.requestedCommand}:{},...i?{alternatives:i}:{}}}var ele=/^Pro action '[^']+' is blocked in Basic mode\. Basic fallback '([^']+)' failed: (.*)$/s,tle=/^Pro action '[^']+' is blocked in Basic mode\.(?! Basic fallback ')/,nle=/instance not found|not found in|parent not found/,rle=/is required|missing required/,ile=/unknown action|unknown command/,ale=/must be|invalid|cannot/;function ole(t){let e=ele.exec(t);return e?{fallbackCommand:e[1],reason:e[2]}:null}function sle(t){return tle.test(t)}function cle(t){let e=t.toLowerCase();return nle.test(e)?"not_found":rle.test(e)?"missing_param":ile.test(e)?"unknown_action":ale.test(e)?"validation":"other"}function PP(t){return t.errorType==="bridge_error"||t.errorType==="command_timeout"||t.errorType==="plugin_runtime"||t.errorType==="exception"||t.errorType==="device_blocked"||t.failureOrigin==="bridge"||t.failureOrigin==="plugin_runtime"||t.failureOrigin==="server_exception"||t.failureOrigin==="product_bug"||t.failureOrigin==="unknown"||t.recoverability==="retry"||t.recoverability==="wait_connection"||t.recoverability==="unknown"?"error":"warning"}function Ac(t){if(t.resultSuccess){let e=Qce(t.resultData);return e?{kind:"success_fallback",fallbackCommand:e.executedCommand,...e.alternatives?{alternatives:e.alternatives}:{}}:{kind:"success_ok"}}if(!t.resultError)return{kind:"error"};if(t.tier==="pro"){let e=ole(t.resultError);if(e)return{kind:"blocked_fallback_failed",fallbackCommand:e.fallbackCommand,blockedDetail:cle(e.reason)};if(sle(t.resultError))return{kind:"blocked_unsupported"}}return t.failureClassification?{kind:PP(t.failureClassification)}:{kind:"error"}}function Ce(t,e,n,i,r={}){return{errorType:t,errorDetail:e,failureOrigin:n,recoverability:i,...r}}function lle(t){let e=t.toLowerCase();return/^\s*compilation error:/.test(e)?Ce("execution_failed","compile_error","agent_request","code_fix",{validationStage:"plugin_handler"}):/^\s*result too large:/.test(e)?Ce("execution_failed","result_too_large","agent_request","code_fix",{validationStage:"plugin_handler"}):/^\s*execution timed out after/.test(e)?Ce("execution_failed","execution_timeout","agent_request","code_fix",{validationStage:"plugin_handler"}):/potentially dangerous infinite loop|blocked in sandbox|access to .* is blocked|forbidden|not allowed|cannot access/.test(e)?Ce("execution_failed","blocked_source","roblox_policy","code_fix",{validationStage:"plugin_handler"}):null}function TU(t){let e=t.toLowerCase();return/upstream_server_down/.test(e)?Ce("bridge_error","upstream_down","bridge","wait_connection"):/^http [45]\d\d/.test(e)?Ce("bridge_error","http_error","bridge","retry"):/timeout/.test(e)?Ce("command_timeout","timeout","bridge","retry"):/econnrefused|socket hang up|econnreset|epipe|network/.test(e)?Ce("bridge_error","connection","bridge","wait_connection"):/abort/.test(e)?Ce("command_timeout","aborted","bridge","retry"):/runtime error|traceback|attempt to/.test(e)?Ce("plugin_runtime","runtime_error","plugin_runtime","code_fix"):/created instance, but failed to set \d+ propert/.test(e)?Ce("execution_failed","property_apply_failed","agent_request","fix_params",{validationStage:"plugin_handler",partialSuccess:"true"}):/failed to set property/.test(e)?Ce("execution_failed","property_apply_failed","agent_request","fix_params",{validationStage:"plugin_handler"}):/parent(?: instance)? not found/.test(e)?Ce("execution_failed","parent_not_found","agent_request","fix_params",{validationStage:"plugin_handler"}):/(?:instance|path|root|source instance|ancestor instance) not found|target\s+(?:path\s+)?(?:['"`][^'"`]+['"`]\s+)?not found|not found in|no synced file found for instance/.test(e)?Ce("execution_failed","not_found","user_project_state","fix_params",{validationStage:"plugin_handler"}):/is required|missing required/.test(e)?Ce("execution_failed","missing_param","agent_request","fix_params",{validationStage:"schema"}):/invalid classname|invalid class/.test(e)?Ce("execution_failed","invalid_class","agent_request","fix_params",{validationStage:"plugin_handler"}):/unknown action|unknown command/.test(e)?Ce("execution_failed","unknown_action","agent_request","fix_params",{validationStage:"dispatcher"}):/failed to insert asset/.test(e)?Ce("execution_failed","asset_error","roblox_policy","fix_params",{validationStage:"roblox_api"}):/failed to read property|property access denied/.test(e)?Ce("execution_failed","property_error","agent_request","fix_params",{validationStage:"plugin_handler"}):/sync controller not available|project sync is not initialized|no active sync place|not found in sync cache/.test(e)?Ce("execution_failed","sync_not_ready","user_project_state","wait_connection",{validationStage:"preflight"}):/cannot capture edit-mode screenshot while a playtest is active|play-mode screenshot capture is not supported/.test(e)?Ce("execution_failed","unsupported_context","roblox_policy","no_retry",{validationStage:"preflight"}):/editableimage api is not enabled|allow mesh \/ image apis|allow http requests/.test(e)?Ce("execution_failed","feature_disabled","roblox_policy","no_retry",{validationStage:"preflight"}):/must be|invalid|cannot/.test(e)?Ce("execution_failed","validation","agent_request","fix_params",{validationStage:"plugin_handler"}):Ce("execution_failed","other","unknown","unknown")}function Nc(t,e){if(t==="execute_luau"){let i=lle(e);if(i)return i}let n=TU(e);return t==="execute_luau"&&n.errorDetail==="runtime_error"&&/^\s*runtime error:/i.test(e)?Ce("execution_failed","runtime_error","agent_request","code_fix"):n}function $U(t){let n=(t instanceof Error?t.message:String(t)).toLowerCase();return/timeout/.test(n)?Ce("command_timeout","timeout","bridge","retry"):/econnrefused|socket hang up|network|econnreset|epipe/.test(n)?Ce("bridge_error","connection","bridge","wait_connection"):/eaddrinuse/.test(n)?Ce("bridge_error","port_in_use","bridge","no_retry"):Ce("exception","unexpected","server_exception","code_fix")}Vv();import{randomBytes as hle}from"node:crypto";var gle=["manage_ui_create_tree","manage_ui_update","manage_ui_delete"],LU=["manage_ui_preview","manage_ui_check"],yle=["game_genre","ui_role","style_family","layout_family","device_policy","safe_area_policy","existing_ui_strategy"];function OP(t){return t&&typeof t=="object"?t:void 0}function $P(t){return typeof t=="number"&&Number.isFinite(t)?t:0}function Wv(t){let e=OP(t);if(!e)return;let n={};for(let i of yle){let r=e[i];typeof r=="string"&&r.trim().length>0&&(n[i]=r)}return Object.keys(n).length>0?n:void 0}function DP(t){let e=Wv(t.qualityPlanSummary);if(e)return e;let n=OP(t.contextSummary),i=Wv(n?.qualityPlanSummary);if(i)return i;let r=Wv(n?.qualityPlan);if(r)return r;let a=Wv(n?.quality_plan);if(a)return a}function UU(t,e){return e?{...t??{},...e}:t}function vle(t){return typeof t=="string"?{snapshotId:t}:t&&typeof t=="object"?{snapshotId:typeof t.snapshotId=="string"?t.snapshotId:null,...t.designCheckSummary!==void 0&&{designCheckSummary:t.designCheckSummary},...t.capturedAt!==void 0&&{capturedAt:t.capturedAt}}:{snapshotId:null}}function FU(t,e,n,i,r){if(!e)return;let a=$P(e.priority_high),o=$P(e.priority_medium),s=$P(e.priority_low),c=typeof e.total=="number"&&Number.isFinite(e.total)?e.total:a+o+s,l=n??(typeof e.target=="string"?e.target:void 0);return{source:t,...l!==void 0&&{targetPath:l},...typeof i=="string"&&{snapshotId:i},...r!==void 0&&{capturedAt:r},priority_high:a,priority_medium:o,priority_low:s,total:c}}function qU(t,e){if(!e)return t;if(!t)return e;let n=t.targetPath===e.targetPath?t.targetPath:void 0,i=t.snapshotId===e.snapshotId?t.snapshotId:void 0;return{source:e.source,...n!==void 0&&{targetPath:n},...i!==void 0&&{snapshotId:i},...e.capturedAt!==void 0&&{capturedAt:e.capturedAt},priority_high:t.priority_high+e.priority_high,priority_medium:t.priority_medium+e.priority_medium,priority_low:t.priority_low+e.priority_low,total:t.total+e.total}}function ble(t){let e=OP(t);if(e)for(let n of["userMessage","intent","expectedBehavior"]){let i=e[n];if(typeof i=="string"&&i.trim().length>0)return i.trim().slice(0,80)}}function xle(){return{source:"unavailable",priority_high:0,priority_medium:0,priority_low:0,total:0}}function BU(t){let e=t.now??Date.now,n=null,i=null,r=l=>`${l}_${e()}_${hle(3).toString("hex")}`,a=()=>{i&&clearTimeout(i),i=setTimeout(()=>{s("idle_timeout")},t.idleMs)},o=async l=>{try{return vle(await t.capturePreview(l))}catch{return{snapshotId:null}}},s=async l=>{if(!n)return;i&&(clearTimeout(i),i=null);let u=n;n=null;let d={},p;for(let h of u.affectedPaths)if(u.afterSnapshotsPathPolicy[h]==="skip")d[h]=null;else{let g=await o(h);d[h]=g.snapshotId,p=qU(p,FU("after_snapshot",g.designCheckSummary,h,g.snapshotId,g.capturedAt))}let f=p??u.latestDesignCheckSummary??xle(),m={requestId:u.requestId,...u.contextId!==void 0&&{contextId:u.contextId},...u.label!==void 0&&{label:u.label},startedAt:u.startedAtIso,closedAt:new Date(e()).toISOString(),closeReason:l,ok:u.ok,affectedPaths:Array.from(u.affectedPaths).sort(),mutationCount:u.mutationCount,beforeSnapshots:u.beforeSnapshots,afterSnapshots:d,childMutationIds:u.childMutationIds,...u.qualityPlanSummary!==void 0&&{qualityPlanSummary:u.qualityPlanSummary},postChangeDesignCheckSummary:f};await jU(t.placeDir,m)},c=l=>({requestId:r("req"),contextId:l.contextId,label:ble(l.contextSummary),startedAtIso:new Date(e()).toISOString(),startedAtMs:e(),affectedPaths:new Set,beforeSnapshots:{},afterSnapshotsPathPolicy:{},childMutationIds:[],mutationCount:0,ok:!0,qualityPlanSummary:DP(l),latestDesignCheckSummary:void 0,pendingMutation:void 0});return{async beforeMutation(l){if(LU.includes(l.command)){if(n){let d=r("mut");n.qualityPlanSummary=UU(n.qualityPlanSummary,DP(l)),n.pendingMutation={mutationId:d,startedMs:e(),args:l}}return}if(!gle.includes(l.command))return;n&&n.contextId!==l.contextId&&await s("context_change"),n||(n=c(l)),n.qualityPlanSummary=UU(n.qualityPlanSummary,DP(l)),l.targetPath&&!(l.targetPath in n.beforeSnapshots)&&(n.affectedPaths.add(l.targetPath),l.command==="manage_ui_create_tree"?n.beforeSnapshots[l.targetPath]=null:n.beforeSnapshots[l.targetPath]=(await o(l.targetPath)).snapshotId,l.command==="manage_ui_delete"?n.afterSnapshotsPathPolicy[l.targetPath]="skip":l.targetPath in n.afterSnapshotsPathPolicy||(n.afterSnapshotsPathPolicy[l.targetPath]="capture"));let u=r("mut");n.pendingMutation={mutationId:u,startedMs:e(),args:l}},async afterMutation(l){if(!n?.pendingMutation)return;let{mutationId:u,args:d}=n.pendingMutation,p={mutationId:u,requestId:n.requestId,ts:new Date(e()).toISOString(),command:d.command,origin:d.origin,...d.targetPath!==void 0&&{targetPath:d.targetPath},...d.briefId!==void 0&&{briefId:d.briefId},...l.diffSummary!==void 0&&{diffSummary:l.diffSummary},...l.changeDetails!==void 0&&{changeDetails:l.changeDetails},ok:l.ok,...l.error!==void 0&&{error:l.error},durationMs:l.durationMs};if(await MU(t.placeDir,p),n.pendingMutation=void 0,LU.includes(d.command)){n.latestDesignCheckSummary=qU(n.latestDesignCheckSummary,FU("check_results",l.designCheckSummary,d.targetPath,l.snapshotId,l.capturedAt));return}if(n.mutationCount++,n.childMutationIds.push(u),l.ok||(n.ok=!1),!n.contextId){await s("legacy_single");return}a()},async flushNow(){n&&await s("explicit")}}}var LP=100,rue=12e4,iue=15e3,cF=new Set(["manage_ui_create_tree","manage_ui_update","manage_ui_delete"]),aue={Text:{category:"text",label:"\uD14D\uC2A4\uD2B8"},PlaceholderText:{category:"text",label:"\uD50C\uB808\uC774\uC2A4\uD640\uB354"},TextColor3:{category:"color",label:"\uAE00\uC790\uC0C9"},BackgroundColor3:{category:"color",label:"\uBC30\uACBD\uC0C9"},BorderColor3:{category:"color",label:"\uD14C\uB450\uB9AC\uC0C9"},ImageColor3:{category:"color",label:"\uC774\uBBF8\uC9C0\uC0C9"},Size:{category:"size",label:"\uC0AC\uC774\uC988"},TextSize:{category:"size",label:"\uAE00\uC790 \uD06C\uAE30"},Position:{category:"layout",label:"\uC704\uCE58"},AnchorPoint:{category:"layout",label:"\uC575\uCEE4"},Rotation:{category:"layout",label:"\uD68C\uC804"},LayoutOrder:{category:"layout",label:"\uB808\uC774\uC544\uC6C3 \uC21C\uC11C"},Visible:{category:"state",label:"\uD45C\uC2DC \uC0C1\uD0DC"},Active:{category:"state",label:"\uD65C\uC131 \uC0C1\uD0DC"},Interactable:{category:"state",label:"\uC0C1\uD638\uC791\uC6A9 \uC0C1\uD0DC"},Selectable:{category:"state",label:"\uC120\uD0DD \uAC00\uB2A5"},Enabled:{category:"state",label:"\uD65C\uC131\uD654"},Image:{category:"asset",label:"\uC774\uBBF8\uC9C0"},Texture:{category:"asset",label:"\uD14D\uC2A4\uCC98"},SoundId:{category:"asset",label:"\uC0AC\uC6B4\uB4DC"}},oue={text:"\uD14D\uC2A4\uD2B8",color:"\uCEEC\uB7EC",size:"\uC0AC\uC774\uC988",layout:"\uB808\uC774\uC544\uC6C3",state:"\uC0C1\uD0DC",asset:"\uC5D0\uC14B",property:"\uC18D\uC131"},sue=Object.entries(Oc).reduce((t,[e,n])=>{for(let i of n.validActions)t[`${e}_${i}`]=e;return t},{});function nF(t){return{toolName:sue[t]||t,commandName:t}}function rF(t,e){if(typeof t.contextId=="string"||!e||typeof e!="object")return t;let n=e.contextId;return typeof n=="string"?{...t,contextId:n}:t}function Jv(t,e){if(t!=="manage_sync_status_current_place")return e;let{placeId:n,...i}=e;return i}async function cue(t,e){let n=t.syncController;if(!n)return null;try{let i=e.placeId,r=n.getDefaultRuntimePlaceId(),a=null;if(typeof i=="number"&&Number.isFinite(i))a=i;else if(typeof i=="string"&&i.trim().length>0){let s=Number.parseInt(i,10);Number.isFinite(s)&&(a=s)}if(a===null&&(a=r),a===null||!Number.isFinite(a))return null;let o=a===r?n.getDefaultRuntimePlaceName():null;return typeof o=="string"&&o.length>0?await n.config.resolvePlaceRoot(a,o):n.config.getPlaceRoot(a)}catch{return null}}function lue(t){let e=t.tree;if(!e||typeof e!="object")return;let n=e,i=typeof n.name=="string"&&n.name.length>0?n.name:void 0;return i?`${typeof n.parent=="string"&&n.parent.length>0?n.parent:"StarterGui"}.${i}`:void 0}function uue(t,e){let n=e.targetPath;if(typeof n=="string"&&n.length>0)return n;if(t==="manage_ui_create_tree")return lue(e)}function due(t){let e=aue[t];if(e)return e;let n=t.toLowerCase();return n.includes("color")?{category:"color",label:"\uC0C9\uC0C1"}:t==="Size"||n.endsWith("size")||n.includes("scale")?{category:"size",label:"\uC0AC\uC774\uC988"}:n.includes("position")||n.includes("anchor")||n.includes("rotation")||n.includes("layout")?{category:"layout",label:"\uB808\uC774\uC544\uC6C3"}:n.includes("visible")||n.includes("active")||n.includes("enabled")||n.includes("selectable")||n.includes("interactable")?{category:"state",label:"\uC0C1\uD0DC"}:n.includes("image")||n.includes("texture")||n.includes("sound")||n.includes("asset")?{category:"asset",label:"\uC5D0\uC14B"}:n.includes("text")||n.includes("placeholder")?{category:"text",label:"\uD14D\uC2A4\uD2B8"}:{category:"property",label:t}}function iF(t,e){return Object.prototype.hasOwnProperty.call(t,e)}function pue(t){if(!t||typeof t!="object")return new Map;let e=t,n=e.changed_properties??e.changedProperties;if(!Array.isArray(n))return new Map;let i=new Map;for(let r of n){if(!r||typeof r!="object")continue;let a=r,o=a.property;if(typeof o!="string"||o.length===0)continue;let s=iF(a,"before"),c=iF(a,"after");i.set(o,{...s&&{before:a.before},...c&&{after:a.after},hasBefore:s,hasAfter:c})}return i}function lF(t,e){let i=t.changes?.properties;if(!i||typeof i!="object")return;let r=pue(e),a=Object.entries(i).map(([o,s])=>{let c=due(o),l=r.get(o),u={property:o,category:c.category,label:c.label,after:l?.hasAfter?l.after:s};return l?.hasBefore&&(u.before=l.before),u});return a.length>0?a:void 0}function uF(t){if(!t||t.length===0)return;let e=new Map;for(let r of t)e.set(r.category,(e.get(r.category)??0)+1);let i=["text","color","size","layout","state","asset","property"].map(r=>{let a=e.get(r)??0;return a>0?`${oue[r]} ${a}\uAC1C`:null}).filter(r=>r!==null);return i.length>0?`${i.join(", ")} \uBCC0\uACBD`:void 0}function aF(t,e){if(t==="manage_ui_update"){let n=e.changes,i=uF(lF(e));if(i)return i;let r=n?.properties?Object.keys(n.properties).length:0,a=Array.isArray(n?.addChildren)?n.addChildren.length:0,o=Array.isArray(n?.removeChildren)?n.removeChildren.length:0;return`${r} props, +${a}, -${o}`}if(t==="manage_ui_create_tree")return"tree \uC0DD\uC131";if(t==="manage_ui_delete")return"\uC81C\uAC70\uB428"}function lp(t){return t&&typeof t=="object"?t:void 0}function Kv(t,e){let n=t[e];return typeof n=="number"&&Number.isFinite(n)?n:0}function dF(t){let e=lp(t),n=lp(e?.design_check_summary);if(n)return{priority_high:Kv(n,"priority_high"),priority_medium:Kv(n,"priority_medium"),priority_low:Kv(n,"priority_low"),total:Kv(n,"total"),...typeof n.scope=="string"&&{scope:n.scope},...typeof n.target=="string"&&{target:n.target}}}function pF(t){let n=lp(t)?.snapshot_id;return typeof n=="string"?n:null}function oF(t){if(typeof t=="string"&&t.trim().length>0)return t;if(typeof t=="number"&&Number.isFinite(t)){let e=t<1e10?t*1e3:t;return new Date(e).toISOString()}}function fF(t){let e=lp(t),n=lp(e?.meta);return oF(n?.captured_at)??oF(n?.evaluated_at)}function fue(t,e){let n=t.uiStudioRecorders.get(e);if(n)return n;let i=BU({placeDir:e,idleMs:iue,capturePreview:async r=>{try{let a=await vo(t,"manage_ui_preview",{targetPath:r},FP());if(!a.success||!a.data||typeof a.data!="object")return null;let o=dF(a.data),s=fF(a.data);return{snapshotId:pF(a.data),...o!==void 0&&{designCheckSummary:o},...s!==void 0&&{capturedAt:s}}}catch{return null}}});return t.uiStudioRecorders.set(e,i),i}async function mue(t,e,n){if(!cF.has(e))return null;let i=await cue(t,n);if(!i)return null;let r=fue(t,i),a=e,o=typeof n.contextId=="string"?n.contextId:void 0,s=n.contextSummary,c=s!==void 0&&typeof s=="object"?s:void 0,l=uue(a,n),u=typeof n.briefId=="string"?n.briefId:void 0,d={command:a,...o!==void 0&&{contextId:o},...c!==void 0&&{contextSummary:c},...l!==void 0&&{targetPath:l},...u!==void 0&&{briefId:u},origin:n.__origin==="dashboard"?"dashboard":"ai"};try{return await r.beforeMutation(d),{recorder:r,command:a,params:n,startedAt:Date.now()}}catch(p){return y.debug("UI Studio recorder beforeMutation \uC2E4\uD328 (\uBB34\uC2DC)",{error:p}),null}}async function sF(t,e){if(t)try{let n=t.command==="manage_ui_update"?lF(t.params,e.data):void 0,i=t.command==="manage_ui_update"?uF(n)??aF(t.command,t.params):aF(t.command,t.params),r=dF(e.data),a=pF(e.data),o=fF(e.data);await t.recorder.afterMutation({ok:e.success,durationMs:Date.now()-t.startedAt,...i!==void 0&&{diffSummary:i},...n!==void 0&&{changeDetails:n},...e.error!==void 0&&{error:e.error},...r!==void 0&&{designCheckSummary:r},...a!==null&&{snapshotId:a},...o!==void 0&&{capturedAt:o}})}catch(n){y.debug("UI Studio recorder afterMutation \uC2E4\uD328 (\uBB34\uC2DC)",{error:n})}}async function UP(t,e,n,i,r,a){if(!t.historyManager)return;let o=!r.success&&r.error?Nc(n,r.error):void 0,s=Ac({resultSuccess:r.success,resultData:r.data,resultError:r.error,tier:Un(n),...o?{failureClassification:o}:{}});if(r.success){let l=s.kind==="success_fallback"&&r.data&&typeof r.data=="object"?{...r.data,outcomeStatus:"fallback",requestedCommand:n,executedCommand:s.fallbackCommand,...s.alternatives?{alternatives:s.alternatives}:{}}:r.data;await t.historyManager.recordSuccess(e,Jv(n,i),l,a,n);return}if(s.kind==="blocked_unsupported"){await t.historyManager.recordBlockedOutcome(e,Jv(n,i),r.error||"Pro action blocked in Basic mode","blocked_unsupported",{executionTimeMs:a,command:n});return}if(s.kind==="blocked_fallback_failed"){await t.historyManager.recordBlockedOutcome(e,Jv(n,i),r.error||"Pro action blocked in Basic mode","blocked_fallback_failed",{executionTimeMs:a,command:n,...s.fallbackCommand?{fallbackCommand:s.fallbackCommand}:{},...s.blockedDetail?{blockedDetail:s.blockedDetail}:{}});return}await(s.kind==="warning"?t.historyManager.recordWarning.bind(t.historyManager):t.historyManager.recordFailure.bind(t.historyManager))(e,Jv(n,i),r.error||"Unknown error",a,o?.errorType.toUpperCase(),n)}function hue(t,e,n){if(!e)return;let i=t.pluginClients.get(e);i?.inFlightRequestId===n&&(i.inFlightRequestId=void 0)}function gue(t,e,n){if(y.debug("Broadcasting command",{command:e.data.command,requestId:e.data.requestId,targetClientId:n||"all",activeWebsocketClients:co(t).length}),n){let i=t.pendingCommands.get(n)||[];i.length>=LP&&i.shift(),i.push(e),t.pendingCommands.set(n,i)}else{let i=co(t).sort((o,s)=>s.lastSeen-o.lastSeen),r=e.data.params?.placeId,a;if(r!==void 0&&i.length>1&&(a=i.find(o=>o.placeId===r),a&&y.debug("Routed command by placeId",{clientId:a.clientId,placeId:r})),a||(a=i[0],i.length>1&&y.warn("Multiple plugin clients connected, routing to most recent",{targetClientId:a?.clientId,targetPlaceId:a?.placeId,totalClients:i.length,command:e.data.command})),a){let o=t.pendingCommands.get(a.clientId)||[];o.length>=LP&&o.shift(),o.push(e),t.pendingCommands.set(a.clientId,o),y.debug("Routed command to client",{clientId:a.clientId,projectName:a.projectName,placeId:a.placeId})}else t.globalPendingCommands.length>=LP&&t.globalPendingCommands.shift(),t.globalPendingCommands.push(e)}mv(t,n)}async function mF(t,e,n){let i=Date.now(),r="",a={};try{let o=e.body;r=o.command??"",a=o.params||{};let s=o.requestId;if(o.instanceId||(t.serverLastCommandAt=Date.now()),o.instanceId){let m=t.mcpInstances.get(o.instanceId);m&&(m.lastCommandAt=Date.now(),m.lastSeen=m.lastCommandAt)}let c=nF(r),l=typeof o.timeout=="number"&&Number.isFinite(o.timeout)&&o.timeout>0?{timeout:o.timeout}:void 0,u=o.instanceId?t.mcpInstances.get(o.instanceId)?.sessionId??t.sessionId:t.sessionId;if(!r){n.status(400).json({error:"Missing command"});return}if(y.debug("Received execute request",{command:r,requestId:s}),r==="manage_camera_play_screenshot"){let m="manage_camera.play_screenshot is not supported. Screenshot capture is available only in Edit mode via manage_camera.screenshot.",h=Date.now()-i;t.historyManager&&await t.historyManager.recordFailure("manage_camera",a,m,h,"UNSUPPORTED_COMMAND",r),n.json({requestId:s,success:!1,error:m});return}if(r==="get_cached_selection"){let m=a.maxAge,h=Qd(t,m!==void 0?m:3e4),g=Date.now()-i;if(!h){let S={requestId:s,success:!0,data:{cached:!1,message:"No cached selection data available. The plugin may not be connected or no selection changes have occurred yet."}};t.historyManager&&await t.historyManager.recordSuccess(c.toolName,a,S.data,g,c.commandName),n.json(S);return}let v=Date.now()-h.timestamp,x={cached:!0,selection:h.selection,count:h.count,timestamp:h.timestamp,age:v};t.historyManager&&await t.historyManager.recordSuccess(c.toolName,a,x,g,c.commandName),n.json({requestId:s,success:!0,data:x});return}if(r==="get_connection_info"){let m=await qv(t),h=Date.now()-i;t.historyManager&&await t.historyManager.recordSuccess(c.toolName,a,m,h,c.commandName),n.json({requestId:s,success:!0,data:m});return}if(Bv(r)==="internal"){if(!t.internalCommandExecutor){n.status(503).json({requestId:s,success:!1,error:`Internal command executor is not initialized for command: ${r}`});return}let m={...a,__sessionId:u},h=await t.internalCommandExecutor(r,m),g=Date.now()-i,v=rF(m,h.data);t.historyManager&&await UP(t,c.toolName,c.commandName,v,{success:h.success,data:h.data,error:h.error||"Internal command failed"},g),n.json({requestId:s,...h});return}let d=await vo(t,r,a,s,l),p=Date.now()-i,f={...rF(a,d.data),__sessionId:u};t.historyManager&&await UP(t,c.toolName,c.commandName,f,d,p),n.json(d)}catch(o){let s=Date.now()-i,c=bn(o);if(t.historyManager&&r){let l=nF(r);await t.historyManager.recordFailure(l.toolName,a,c,s,"EXCEPTION",l.commandName)}y.error("Execute request failed",o),n.status(500).json({success:!1,error:c})}}async function hF(t,e,n){let i=Date.now(),r=e.body,a=r.action;if(!a||typeof a!="string"){n.status(400).json({success:!1,error:"Missing required field: action"});return}if(!new Set(["get","set","get_all","set_multiple","get_attr","set_attr","get_all_attrs","delete_attr","add_tag","remove_tag","check_tag","get_tags","get_tagged"]).has(a)){n.status(400).json({success:!1,error:`Unsupported action: ${a}`});return}let s=`manage_properties_${a}`,c=FP(),{action:l,...u}=r;try{t.serverLastCommandAt=Date.now();let d=await vo(t,s,u,c),p=Date.now()-i;t.historyManager&&await UP(t,"manage_properties",s,u,d,p),n.json(d)}catch(d){let p=Date.now()-i,f=bn(d);t.historyManager&&await t.historyManager.recordFailure("manage_properties",u,f,p,"EXCEPTION",s),y.error("/api/properties \uC694\uCCAD \uCC98\uB9AC \uC2E4\uD328",d),n.status(500).json({success:!1,error:f})}}async function vo(t,e,n,i,r){y.debug("Executing command locally",{command:e,requestId:i});let a=cF.has(e)?await mue(t,e,n):null,o=t.config.requestTimeout,s=r?.timeout??Math.max(t.config.requestTimeout,rue),c=new Promise((l,u)=>{let d={requestId:i,command:e,params:n,timestamp:Date.now(),queueTimeoutMs:o,ackedTimeoutMs:s,state:gn.QUEUED,resolve:l,reject:u,timeoutId:null};t.commandQueue.set(i,d),d.timeoutId=setTimeout(()=>{t.commandQueue.delete(d.requestId),hue(t,d.targetClientId,d.requestId),d.reject(new Error(`Command timeout after ${o}ms while ${d.state}: ${d.command}`))},o)});return gue(t,{event:"command",id:FP(),data:{command:e,requestId:i,params:n}}),c.then(async l=>{let u=l;if(e==="manage_ui_preview"&&l.success&&l.data)try{let{processPreviewResult:d,resolvePreviewPlaceDirFromCtx:p}=await Promise.resolve().then(()=>(zP(),MP)),f=await p(t,n),m=await d("manage_ui_preview",l.data,f);u={...l,data:m.data,...m.mcpContent?{mcpContent:m.mcpContent}:{}}}catch(d){y.warn("preview post-processing failed",{error:d instanceof Error?d.message:String(d)})}if(e==="manage_ui_check"&&l.success&&l.data&&n.includeVisualAnalysis===!0)try{let{resolvePreviewPlaceDirFromCtx:d}=await Promise.resolve().then(()=>(zP(),MP)),{processCheckResult:p}=await Promise.resolve().then(()=>(tF(),eF)),f=await d(t,n),m=await p("manage_ui_check",l.data,f,n);u={...u,data:m.data}}catch(d){y.warn("check visual post-processing failed",{error:d instanceof Error?d.message:String(d)})}return u}).then(async l=>(await sF(a,{success:l.success,...l.data!==void 0&&{data:l.data},...l.error!==void 0&&{error:l.error}}),l),async l=>{throw await sF(a,{success:!1,error:bn(l)}),l})}ie();function Xv(t,e){let n={};e.provider!==void 0&&(n.provider=e.provider),e.tier!==void 0&&(n.tier=e.tier),e.status!==void 0&&(n.status=e.status),e.canUsePro!==void 0&&(n.canUsePro=e.canUsePro),e.cleared===!0&&(n.cleared=!0),ot(t,"license",n)}function lr(t,e,n,i){t.status(e).json({success:!1,error:{code:n,message:i}})}function yue(t){if(typeof t!="string")return;let e=t.trim().toLowerCase();return e.length>0?e:void 0}function Yv(t,e){if(e==="plugin")return t;let n={...t};return delete n.sessionToken,n}function up(t,e,n,i){let r=yue(i??e.query.provider);return r?t.licenseState?.supportsProvider(r)===!1?(lr(n,400,"LICENSE_PROVIDER_UNSUPPORTED",`provider "${r}" is not supported`),null):r:(lr(n,400,"LICENSE_PROVIDER_REQUIRED","provider is required"),null)}function dp(t,e){return t.licenseState?!0:(lr(e,503,"LICENSE_NOT_INITIALIZED","License system is not initialized"),!1)}function vue(t){if(!t||typeof t!="object")return!1;let e=t;return(e.canUsePro===!0||e.canUsePro===!1)&&typeof e.status=="string"&&typeof e.checkedAt=="number"}function pp(t,e,n,i="plugin"){if(!dp(t,n))return;let r=e.body,a=typeof r?.licenseKey=="string"?r.licenseKey:"";if(!a.trim()){lr(n,400,"LICENSE_KEY_REQUIRED","licenseKey is required");return}let o=up(t,e,n,r?.provider);if(!o)return;let s=typeof r?.pluginClientId=="string"?r.pluginClientId:typeof e.query.clientId=="string"?e.query.clientId:void 0,c=typeof r?.deviceId=="string"?r.deviceId:void 0,l={licenseKey:a,provider:o,clientType:i};s&&(l.pluginClientId=s),c&&(l.deviceId=c),t.licenseState.activateGateway(l).then(u=>{t.analyticsManager?.setTier(u.canUsePro?"pro":"basic"),Xv(t,{provider:o,tier:u.canUsePro?"pro":"basic",status:u.status,canUsePro:u.canUsePro}),n.json(Yv(u,i))}).catch(u=>{y.warn("License activate failed",{error:u instanceof Error?u.message:"unknown_error"}),lr(n,502,"LICENSE_ACTIVATE_FAILED",u instanceof Error?u.message:"License activation failed")})}function fp(t,e,n,i="plugin"){if(!dp(t,n))return;let r=up(t,e,n);if(r)try{let a=t.licenseState.getStatus(r);t.analyticsManager?.setTier(a.canUsePro?"pro":"basic"),n.json(Yv(a,i))}catch(a){y.warn("License status check failed",{error:a instanceof Error?a.message:"unknown_error"}),lr(n,502,"LICENSE_STATUS_FAILED",a instanceof Error?a.message:"License status check failed")}}function mp(t,e,n,i="plugin"){if(!dp(t,n))return;let r=e.body,a=up(t,e,n,r?.provider);if(!a)return;let o={provider:a,clientType:i};i==="plugin"&&typeof r?.sessionToken=="string"&&r.sessionToken.trim()&&(o.sessionToken=r.sessionToken.trim()),typeof r?.pluginClientId=="string"&&(o.pluginClientId=r.pluginClientId),typeof r?.deviceId=="string"&&(o.deviceId=r.deviceId),t.licenseState.refreshGateway(o).then(s=>{t.analyticsManager?.setTier(s.canUsePro?"pro":"basic"),Xv(t,{provider:a,tier:s.canUsePro?"pro":"basic",status:s.status,canUsePro:s.canUsePro}),n.json(Yv(s,i))}).catch(s=>{y.warn("License refresh failed",{error:s instanceof Error?s.message:"unknown_error"}),lr(n,502,"LICENSE_REFRESH_FAILED",s instanceof Error?s.message:"License refresh failed")})}function hp(t,e,n,i="plugin"){if(!dp(t,n))return;let r=e.body,a=up(t,e,n,r?.provider);a&&t.licenseState.resetGateway({provider:a,clientType:i}).then(o=>{t.analyticsManager?.setTier("basic"),Xv(t,{provider:a,tier:"basic",status:o.status,canUsePro:o.canUsePro,cleared:!0}),n.json(Yv(o,i))}).catch(o=>{y.warn("License reset failed",{error:o instanceof Error?o.message:"unknown_error"}),lr(n,502,"LICENSE_RESET_FAILED",o instanceof Error?o.message:"License reset failed")})}function qP(t,e,n){if(!dp(t,n))return;let i=e.body,r=up(t,e,n,i?.provider);if(!r)return;if(i?.clientType!=="plugin"){lr(n,400,"LICENSE_CLIENT_TYPE_INVALID","clientType must be plugin");return}let a=typeof i?.sessionToken=="string"&&i.sessionToken.trim()?i.sessionToken.trim():void 0,o=vue(i?.snapshot)?i.snapshot:void 0;if(!o&&!a){lr(n,400,"LICENSE_BOOTSTRAP_REQUIRED","snapshot or sessionToken is required");return}let s={provider:r,clientType:"plugin"};o&&(s.snapshot=o),a&&(s.sessionToken=a),t.licenseState.bootstrap(s).then(c=>{t.analyticsManager?.setTier(c.canUsePro?"pro":"basic"),Xv(t,{provider:r,tier:c.canUsePro?"pro":"basic",status:c.status,canUsePro:c.canUsePro}),n.json({ok:!0})}).catch(c=>{y.warn("License bootstrap failed",{error:c instanceof Error?c.message:"unknown_error"}),lr(n,502,"LICENSE_BOOTSTRAP_FAILED",c instanceof Error?c.message:"License bootstrap failed")})}function gF(t){t.app.post("/license/bootstrap",(e,n)=>qP(t,e,n)),t.app.post("/license/activate",(e,n)=>pp(t,e,n,"plugin")),t.app.post("/license/refresh",(e,n)=>mp(t,e,n,"plugin")),t.app.post("/license/reset",(e,n)=>hp(t,e,n,"plugin")),t.app.get("/license/status",(e,n)=>fp(t,e,n,"plugin"))}Nt();ie();import{promises as Qv}from"fs";import BP from"path";function bue(t){let e=t.config.appDataDir??cn();return BP.join(e,"observability","logs")}async function Uc(t,e,n){let i=e.query.clientId;if(i&&t.pluginClients.has(i)){let r=t.pluginClients.get(i);r.lastSeen=Date.now()}try{let r=e.body;if(!r||!Array.isArray(r.logs)){y.warn("Invalid logs request",{body:r}),n.status(400).json({error:"Invalid request body"});return}let a=bue(t);await Qv.mkdir(a,{recursive:!0});let c=`plugin-${new Date().toISOString().split("T")[0]}.log`,l=BP.join(a,c),u=BP.join(a,"current.log"),d=r.logs.map(p=>`[${new Date(p.timestamp*1e3).toISOString().replace("T"," ").substring(0,19)}] [${p.level}] ${p.message}`).join(`
504
+ `),await hc(h)}catch(h){y.warn("Failed to refresh root representative sourcemap after full sync",{placeId:e,error:h instanceof Error?h.message:String(h)})}this.ctx.startFileWatcherForPlace(r).catch(h=>{y.warn("Failed to start reverse detector after full sync",{placeId:e,error:h instanceof Error?h.message:String(h)}),r.fileWatcher=null}),r.writer.setBootstrapMode(!1),r.writer.appendChangeLog(`FULL_SYNC_COMPLETE instances=${r.instanceCount} scripts=${r.scriptCount}`),r.writer.appendHistory({timestamp:new Date().toISOString(),type:"fullSyncComplete",direction:"forward",source:"studio",path:`place_${e}`,details:`instances:${r.instanceCount} scripts:${r.scriptCount}`}),y.info("Full sync completed",{placeId:e,instanceCount:r.instanceCount,scriptCount:r.scriptCount}),i.status(200).json({status:"completed",instanceCount:r.instanceCount,scriptCount:r.scriptCount,syncRoot:this.ctx.config.getPlaceRoot(e)})}catch(f){throw l&&l.endFullSyncReplacement(),f}}setPreserveLocalFiles(e,n){this.preserveLocalFilesMap.set(e,n)}getAndClearPreserveLocalFiles(e){let n=this.preserveLocalFilesMap.get(e)||[];return this.preserveLocalFilesMap.delete(e),n}clearPreserveLocalFiles(e){this.preserveLocalFilesMap.delete(e)}clearPendingServiceTrees(e){this.pendingServiceTrees.delete(e)}startTTLTimerForPlace(e,n){let i=setTimeout(async()=>{y.warn("Incomplete sync TTL expired, cleaning up",{placeId:e.placeId,syncId:n});let r=this.ctx.config.getPlaceRoot(e.placeId),a=$e.join(r,`explorer_tmp_${n}`);try{await vn.rm(a,{recursive:!0,force:!0})}catch(o){y.error("Failed to clean up expired temp dir",o instanceof Error?o:new Error(String(o)))}e.incompleteSyncTimer=null,e.activeFullSyncSessionId===n&&(e.activeFullSyncSessionId=null,e.activeClientId=null,e.state="idle",e.tmpIndex=null,this.pendingServiceTrees.delete(n),e.collisionDirMap=null,e.activeSyncIdentityScope=null,e.tmpWriter&&(e.tmpWriter.stopChangeLogFlusher(),e.tmpWriter=null),this.ctx.activeFullSyncPlaceId===e.placeId&&(this.ctx.activeFullSyncPlaceId=null),this.ctx.clearRuntimePlaceIfMatch(e.placeId))},oU);i&&typeof i=="object"&&"unref"in i&&i.unref(),e.incompleteSyncTimer=i}getOrCreatePendingServiceTree(e,n){let i=this.pendingServiceTrees.get(e);i||(i=new Map,this.pendingServiceTrees.set(e,i));let r=i.get(n.serviceName);if(r)return r;let a={serviceName:n.tree?.name??n.serviceName,serviceClassName:n.tree?.className??n.serviceClassName,zeroBasedChunkIndex:n.chunkIndex===0,instances:[]};return i.set(n.serviceName,a),a}isLastChunk(e,n,i){return i<=1?!0:e.zeroBasedChunkIndex?n>=i-1:n>=i}getPreferredResolvedName(e,n,i){let r=typeof i.sessionDebugId=="string"?i.sessionDebugId:"";if(!n||r==="")return;let a=e.index.getSessionDebugMapping(n,r);if(!a)return;let o=mt(i.path),s=nt(i.path)||i.name;if(!(a.parentPath!==o||a.originalName!==s))return a.resolvedName}recordSessionDebugMapping(e,n,i,r){let a=typeof i.sessionDebugId=="string"?i.sessionDebugId:"";!n||a===""||!e.tmpIndex||e.tmpIndex.setSessionDebugMapping({scope:n,sessionDebugId:a,instancePath:i.path,parentPath:mt(i.path),originalName:nt(i.path)||i.name,siblingIndex:Math.max(1,i.siblingIndex??1),resolvedName:r,className:i.className,updatedAt:new Date().toISOString()})}buildServiceTree(e,n){let i={name:n.serviceName,className:n.serviceClassName,childCount:0,children:[],syncedAt:new Date().toISOString()};for(let r of n.instances){let a=this.resolveEffectiveSegments(e,r.effectivePath),o=jt(r.originalPath);this.upsertTreeNode(i,a,o,r.className)}return this.recomputeTreeChildCounts(i),i.syncedAt=new Date().toISOString(),i}resolveEffectiveSegments(e,n){return e.tmpIndex?jt(n).map(i=>e.tmpIndex.sanitizeName(i)):jt(n)}rewritePendingEffectivePaths(e,n,i,r){let a=$e.relative(n,i).split($e.sep).filter(l=>l.length>0),o=$e.relative(n,r).split($e.sep).filter(l=>l.length>0);if(a.length===0||o.length===0)return;let s=st(["game",...a]),c=st(["game",...o]);for(let l of e.instances){if(l.effectivePath===s){l.effectivePath=c;continue}(l.effectivePath.startsWith(`${s}.`)||l.effectivePath.startsWith(`${s}[`))&&(l.effectivePath=`${c}${l.effectivePath.slice(s.length)}`)}}upsertTreeNode(e,n,i,r){if(n.length<=1)return;let a=e.children;for(let o=1;o<n.length;o++){let s=n[o],c=i[o],l=o===n.length-1,u=a.find(d=>d.name===s);u?l&&(u.className=r,c!==void 0&&c!==s&&(u.originalName=c)):(u={name:s,className:l?r:"Folder",childCount:0,children:[]},c!==void 0&&c!==s&&(u.originalName=c),a.push(u)),u.children||(u.children=[]),a=u.children}}recomputeTreeChildCounts(e){let n=i=>{let r=i.children??[];i.children=r;for(let a of r)n(a);i.childCount=r.length};for(let i of e.children)n(i);e.childCount=e.children.length}clearTTLTimerForPlace(e,n){e.incompleteSyncTimer&&e.activeFullSyncSessionId===n&&(clearTimeout(e.incompleteSyncTimer),e.incompleteSyncTimer=null)}async cleanupStaleTempDirs(){let e=this.ctx.config.getSyncRoot();try{let n=await vn.readdir(e,{withFileTypes:!0});for(let i of n)if(i.isDirectory()){if(i.name.startsWith("explorer_tmp_")){let r=$e.join(e,i.name);y.warn("Removing stale temp directory from crashed sync",{dir:i.name});try{await vn.rm(r,{recursive:!0,force:!0})}catch(a){y.error(`Failed to remove stale temp dir: ${i.name}`,a instanceof Error?a:new Error(String(a)))}}if(i.name.startsWith("place_")){let r=$e.join(e,i.name);try{let a=await vn.readdir(r,{withFileTypes:!0});for(let o of a)if(o.isDirectory()&&o.name.startsWith("explorer_tmp_")){let s=$e.join(r,o.name);y.warn("Removing stale temp directory from crashed sync",{dir:`${i.name}/${o.name}`});try{await vn.rm(s,{recursive:!0,force:!0})}catch(c){y.error(`Failed to remove stale temp dir: ${i.name}/${o.name}`,c instanceof Error?c:new Error(String(c)))}}}catch{continue}}}}catch(n){if(n.code==="ENOENT")return;y.warn("Failed to scan for stale temp dirs",{error:n instanceof Error?n.message:String(n)})}}};import sr from"path";import{promises as Dv}from"fs";ie();var Ov=class{constructor(e){this.ctx=e}async handleReversePending(e,n){let i=this.ctx.resolveQueryPlaceId(e);if(i==null){n.status(200).json({pending:0,hasConflicts:!1,lastDetected:null});return}let r=this.ctx.places.get(i);if(!r){n.status(404).json({error:"Place not found",message:`No sync context for place ${i}`});return}let a={pending:r.fileWatcher?.getPendingCount()??0,hasConflicts:!1,lastDetected:r.fileWatcher?.getLastDetected()??null,forwardRestoreNeeded:r.forwardRestoreQueue.length};this.ctx.touchRuntimePlace(i),n.status(200).json(a)}async handleReverseSyncChanges(e,n){let i=this.ctx.resolveQueryPlaceId(e);if(i==null){n.status(200).json({changes:[],count:0});return}let r=this.ctx.places.get(i);if(!r){n.status(404).json({error:"Place not found",message:`No sync context for place ${i}`});return}if(r.state!=="syncing"){n.status(400).json({error:"Not syncing",message:"Reverse sync is only available when sync is active"});return}let a=r.fileWatcher?.drainPendingChanges()??[],o=a.length>0?await r.reader.buildChangesFromPending(a):[];this.ctx.touchRuntimePlace(i),n.status(200).json({changes:o,count:o.length})}async handleReverseSyncResult(e,n){let i=e.body,r=i.placeId??this.ctx.getDefaultRuntimePlaceId();if(r==null){n.status(400).json({error:"Validation error",message:"placeId is required (in body or via active sync session)"});return}let a=i.appliedFiles??i.appliedPaths;if(!a||!Array.isArray(a)){n.status(400).json({error:"Validation error",message:"appliedFiles (or appliedPaths) must be an array of relative file paths"});return}let o=this.ctx.places.get(r);if(!o){n.status(404).json({error:"Place not found",message:`No sync context for place ${r}`});return}this.ctx.touchRuntimePlace(r);let s=this.ctx.config.getPlaceRoot(r),c=0,l=[];for(let u of a){let d=sr.resolve(s,u);if(!Pv(s,d)){l.push({path:u,error:"Path is outside the place root"});continue}try{let p=await Dv.readFile(d,"utf-8"),f=o.index.computeHash(p);o.index.updateHashByValue(d,f),o.index.updateFileHashByValue(d,f),c++}catch(p){let f=p.code;if(f==="ENOENT"){o.index.removeHash(d),o.index.removeHashesUnder(d);let m=this.resolveInstancePathForAppliedPath(o.index,d);if(m){let h=nt(m),g=mt(m);g&&h&&await o.writer.removeFromTree(g,h)}c++}else f==="EISDIR"?c++:l.push({path:u,error:p instanceof Error?p.message:String(p)})}}c>0&&await o.index.saveToDisk(),c>0&&o.writer.appendHistory({timestamp:new Date().toISOString(),type:"reverseApply",direction:"reverse",source:"local",path:`place_${r}`,details:`applied:${c} failed:${l.length}`}),n.status(200).json({updated:c,failed:l.length,errors:l})}async handleResolveConflict(e,n){let i=e.body;if(!i.fsPath||!i.resolution){n.status(400).json({error:"Validation error",message:"fsPath and resolution are required"});return}let{fsPath:r,resolution:a}=i,o=this.ctx.config.getSyncRoot();if(!Pv(o,sr.resolve(o,r))){n.status(403).json({error:"Forbidden",message:"Path is outside the sync root"});return}if(a==="skip"){n.status(200).json({status:"skipped",fsPath:r});return}let s;if(i.placeId&&(s=this.ctx.places.get(i.placeId)),!s){let p=r.match(/^place_(\d+)(?:_[^/]+)?\//);if(p){let f=parseInt(p[1],10);s=this.ctx.places.get(f)}else s=Array.from(this.ctx.places.values())[0]}if(!s){n.status(404).json({error:"No active place context",message:"No sync session is active. Start a sync first."});return}let c=this.ctx.config.getPlaceRoot(s.placeId),l=sr.resolve(c,r);if(!Pv(c,l)){n.status(403).json({error:"Forbidden",message:"Path is outside the place root"});return}let u=s.index,d=s.reader;if(a==="apply-studio"){if(typeof i.studioContent!="string"){n.status(400).json({error:"Validation error",message:"studioContent is required for apply-studio resolution"});return}await Dv.mkdir(sr.dirname(l),{recursive:!0});let p=i.studioContent,f=u.computeHash(p);s.fileWatcher?.suppressPath(l,f),await Dv.writeFile(l,p,"utf-8"),u.updateHashByValue(l,f),u.updateFileHashByValue(l,f),await u.saveToDisk(),n.status(200).json({status:"resolved",resolution:"apply-studio",fsPath:r});return}if(a==="apply-file"){let p=await Dv.readFile(l,"utf-8"),f=u.computeHash(p);u.resolveFile(l,"apply-file",f),await u.saveToDisk();let m=d.getFileType(l),h=d.resolveInstancePathFromFile(l);n.status(200).json({status:"resolved",resolution:"apply-file",fsPath:r,instancePath:h,fileType:m,content:p});return}n.status(400).json({error:"Invalid resolution",message:`Unknown resolution: ${a}`})}async handleReverseRescan(e,n){let i=this.ctx.resolveQueryPlaceId(e);if(i==null){n.status(200).json({added:0});return}let r=this.ctx.places.get(i);if(!r){n.status(404).json({error:"Place not found",message:`No sync context for place ${i}`});return}if(!r.fileWatcher){n.status(200).json({added:0});return}let a=await r.fileWatcher.rescan();this.ctx.touchRuntimePlace(i),y.info("Reverse rescan completed",{placeId:i,added:a}),n.status(200).json({added:a})}resolveInstancePathForAppliedPath(e,n){let i=e.resolveInstancePathFromFsPath(n);if(i)return i;let r=e.getExplorerRoot(),a=sr.relative(r,n);if(a.startsWith("..")||a===""||sr.isAbsolute(a))return null;let o=a.split(sr.sep).filter(f=>f.length>0);if(o.length<2)return null;let s=sr.basename(n),c=sr.dirname(n),l=e.getOriginalInstance(c,s);if(l)return l.instancePath;let u=s.toLowerCase();if(sv.some(f=>u.endsWith(f))||u==="_tree.json")return null;let d=["game"],p=r;for(let f of o){p=sr.join(p,f);let m=sr.dirname(p);d.push(e.getOriginalNameForDir(m,f))}return st(d)}};ie();function pU(t){if(!t||typeof t!="object")return;let e=t;if(Array.isArray(e.instances))for(let n of e.instances)Array.isArray(n.properties)&&n.properties.length===0&&(n.properties={}),Array.isArray(n.attributes)&&n.attributes.length===0&&(n.attributes={});if(Array.isArray(e.changes))for(let n of e.changes)Array.isArray(n.properties)&&n.properties.length===0&&(n.properties={}),Array.isArray(n.attributes)&&n.attributes.length===0&&(n.attributes={})}function Wce(t){return t.type==="instanceAdded"||t.type==="instanceRemoved"||t.type==="instanceRenamed"||t.type==="instanceMoved"||t.type==="scriptSourceChanged"}var $r=class{config;places;apiHandler;changeProcessor;initHandler;reverseHandler;reverseDetectorFactory;activeFullSyncPlaceId=null;activeRuntimeSyncPlaceId=null;currentConnectedPlaceId=null;currentConnectedPlaceName=null;constructor(e,n={}){this.config=new iv(e,n),this.reverseDetectorFactory=n.reverseDetectorFactory??(i=>new kv(i.explorerRoot,i.syncIndex,{placeId:i.placeId})),this.apiHandler=new Tv(this),this.changeProcessor=new Ev(this),this.initHandler=new $v(this),this.reverseHandler=new Ov(this),this.places=new Sv({max:3,dispose:(i,r)=>{y.info("Disposing place context (LRU eviction)",{placeId:r}),this.activeFullSyncPlaceId===r&&(this.activeFullSyncPlaceId=null),i.fileWatcher&&(y.info("Disposing place context: stopping reverse detector",{placeId:r,reason:"lru-dispose",mode:rt}),i.fileWatcher.stop("lru-dispose").catch(a=>{y.error("Error stopping reverse detector during dispose",a)}),i.fileWatcher=null),i.writer.stopChangeLogFlusher().catch(a=>{y.error("LRU dispose \uC911 changelog flush \uC2E4\uD328",a)}),i.incompleteSyncTimer&&(clearTimeout(i.incompleteSyncTimer),i.incompleteSyncTimer=null),i.index.clearAllMaps(),i.index.saveToDisk().catch(a=>{y.error("Error saving index during dispose",a)}),i.activeFullSyncSessionId&&this.initHandler.clearPendingServiceTrees(i.activeFullSyncSessionId),i.tmpWriter&&(i.tmpWriter.stopChangeLogFlusher(),i.tmpWriter=null),i.tmpIndex=null,i.collisionDirMap=null,i.activeSyncIdentityScope=null}})}getSyncRoot(){return this.config.getSyncRoot()}async getOrCreatePlaceContext(e,n){let i=this.places.get(e);if(i&&n){let r=this.config.getPlaceRoot(e),a=await this.config.resolvePlaceRoot(e,n);a!==r&&(y.info("Place root migrated, recreating context",{placeId:e,from:r,to:a}),this.places.delete(e),i=void 0)}if(!i){let r=await this.config.resolvePlaceRoot(e,n),a=yP.join(r,"explorer");await Tc.mkdir(r,{recursive:!0}),await Tc.mkdir(a,{recursive:!0});let o=new ta(r,a);await o.loadFromDisk();let s=new Pc(this.config,o,e),c=new wv(this.config,o,r);s.startChangeLogFlusher(),i={placeId:e,placeName:"",index:o,writer:s,reader:c,fileWatcher:null,state:"idle",activeClientId:null,activeFullSyncSessionId:null,instanceCount:0,scriptCount:0,lastFullSync:null,lastIncrementalSync:null,tmpIndex:null,tmpWriter:null,incompleteSyncTimer:null,changesSinceLastSave:0,directions:{...io},applyModes:{...ao},forwardRestoreQueue:[],syncProgress:null,collisionDirMap:null,activeSyncIdentityScope:null},this.places.set(e,i),y.info("Created new place context",{placeId:e,placeRoot:r})}return i}async handleSyncInit(e,n){try{pU(e.body);let i=cU(e.body);if(!i.success){n.status(400).json({error:"Validation error",message:i.error});return}let r=i.data,a=r.phase==="start"?r.placeId??null:r.phase==="chunk"||r.phase==="complete"?this.activeFullSyncPlaceId:null;if(a==null){n.status(400).json({error:"Missing placeId",message:"placeId is required in start phase or must be set by previous start"});return}switch(r.phase){case"start":this.setCurrentConnectedPlace(a,r.placeName),await this.initHandler.handleInitStart(a,r,n);break;case"chunk":await this.initHandler.handleInitChunk(a,r,n);break;case"complete":await this.initHandler.handleInitComplete(a,r,n);break}}catch(i){this.sendError(n,i,"handleSyncInit")}}async handleSyncUpdate(e,n){try{pU(e.body);let i=lU(e.body);if(!i.success){n.status(400).json({error:"Validation error",message:i.error});return}let r=i.data,a=r.placeId??this.getDefaultRuntimePlaceId();if(a==null){n.status(400).json({error:"Missing placeId",message:"placeId is required in body or must have an active sync session"});return}let o=await this.getOrCreatePlaceContext(a);if(this.setCurrentConnectedPlace(a,o.placeName||this.currentConnectedPlaceName),o.activeFullSyncSessionId!==null||o.state==="initializing"){n.status(409).json({error:"Conflict",message:`Full sync in progress for place ${a}`});return}o.activeClientId=r.clientId,this.touchRuntimePlace(a);let s=aU(o,r.changes);y.info("Sync update received",{placeId:a,clientId:r.clientId,changeCount:s.length,receivedCount:r.changes.length,types:s.map(m=>m.type)});let c=[],l=[],u=0,d=!1,p=new Map;for(let m of s)try{let h=await this.changeProcessor.processChangeForPlace(o,m,p);h?l.push(h):(u++,Wce(m)&&(d=!0))}catch(h){let g="path"in m?m.path:"oldPath"in m?m.oldPath:"unknown";c.push({path:g,error:h instanceof Error?h.message:String(h)})}for(let m of p.values())try{await Cv(o,m)}catch(h){c.push({path:m.instancePath,error:h instanceof Error?h.message:String(h)})}if(d)try{let m=this.config.getSyncRoot(),h=this.config.getPlaceRoot(a);await lv(m,h),await hc(m)}catch(m){y.warn("Failed to refresh sourcemaps after incremental sync update",{placeId:a,error:m instanceof Error?m.message:String(m)})}o.changesSinceLastSave+=u,o.changesSinceLastSave>=sU&&(await o.index.saveToDisk(),o.changesSinceLastSave=0),o.lastIncrementalSync=new Date().toISOString();let f={processed:u,failed:c.length,errors:c,syncedAt:o.lastIncrementalSync};l.length>0&&(f.conflicts=l),n.status(200).json(f)}catch(i){this.sendError(n,i,"handleSyncUpdate")}}getDefaultRuntimePlaceId(){return this.currentConnectedPlaceId}getDefaultRuntimePlaceName(){if(this.currentConnectedPlaceName)return this.currentConnectedPlaceName;if(this.currentConnectedPlaceId!==null&&this.currentConnectedPlaceId!==void 0){let e=this.places.get(this.currentConnectedPlaceId);if(e?.placeName)return e.placeName}return null}setCurrentConnectedPlace(e,n){if(this.currentConnectedPlaceId=e,e==null){this.currentConnectedPlaceName=null;return}if(typeof n=="string"&&n.length>0){this.currentConnectedPlaceName=n;let r=this.places.get(e);r&&(r.placeName=n);return}let i=this.places.get(e);i?.placeName&&(this.currentConnectedPlaceName=i.placeName)}clearCurrentConnectedPlaceIfMatch(e){this.currentConnectedPlaceId===e&&(this.currentConnectedPlaceId=null,this.currentConnectedPlaceName=null)}touchRuntimePlace(e){this.activeRuntimeSyncPlaceId=e}clearRuntimePlaceIfMatch(e){this.activeRuntimeSyncPlaceId===e&&(this.activeRuntimeSyncPlaceId=null)}resolveQueryPlaceId(e,n="runtime"){let i=e.query.placeId;if(i){let r=parseInt(i,10);if(!isNaN(r))return r}return n==="full"?this.activeFullSyncPlaceId:this.getDefaultRuntimePlaceId()}async handleSyncStatus(e,n){try{let i=u=>({state:"idle",instanceCount:0,scriptCount:0,lastFullSync:null,lastIncrementalSync:null,syncRoot:u,activeClientId:null,reverseSyncAvailable:!1,modifiedFileCount:0,reverseDetectorActive:!1,reverseDetectorMode:rt,fileWatcherActive:!1}),r=this.resolveQueryPlaceId(e);if(r==null){let u=i(this.config.getSyncRoot());n.status(200).json(u);return}let a=this.places.get(r);if(!a){let u=i(this.config.getPlaceRoot(r));n.status(200).json(u);return}let o=a.fileWatcher?.getPendingCount()??0,s=a.fileWatcher?.isActivelyScanning()??!1,c=a.fileWatcher!==null;this.touchRuntimePlace(r);let l={state:a.state,instanceCount:a.instanceCount,scriptCount:a.scriptCount,lastFullSync:a.lastFullSync,lastIncrementalSync:a.lastIncrementalSync,syncRoot:this.config.getPlaceRoot(r),activeClientId:a.activeClientId,reverseSyncAvailable:o>0,modifiedFileCount:o,applyModes:a.applyModes,directions:a.directions,reverseDetectorActive:s,reverseDetectorMode:rt,fileWatcherActive:c,forwardOnlyClasses:[...mc]};n.status(200).json(l)}catch(i){this.sendError(n,i,"handleSyncStatus")}}async handleSyncStop(e,n){try{let i=e.body,r=i.placeId??this.getDefaultRuntimePlaceId();if(y.info("handleSyncStop requested",{placeId:r,clientId:i.clientId??null,reason:i.reason??"requested"}),r==null){n.status(200).json({status:"idle",state:"idle",placeId:null,message:"No active sync place"});return}let a=this.places.get(r);if(!a){y.info("handleSyncStop no place context",{placeId:r,clientId:i.clientId??null}),n.status(200).json({status:"idle",state:"idle",placeId:r,message:`No sync context for place ${r}`});return}if(i.clientId&&a.activeClientId&&i.clientId!==a.activeClientId&&(a.activeFullSyncSessionId!==null||a.state==="syncing"||a.state==="initializing")){y.warn("handleSyncStop rejected due to active client mismatch",{placeId:r,requestedClientId:i.clientId,activeClientId:a.activeClientId,state:a.state,activeFullSyncSessionId:a.activeFullSyncSessionId}),n.status(409).json({error:"Conflict",message:`This sync session belongs to client ${a.activeClientId}`});return}let o=a.activeFullSyncSessionId,s=a.state,c=a.fileWatcher!==null;if(o&&(this.initHandler.clearPreserveLocalFiles(o),this.initHandler.clearPendingServiceTrees(o)),a.fileWatcher&&(y.info("handleSyncStop suspending reverse detector",{placeId:r,state:a.state,activeClientId:a.activeClientId,mode:rt}),a.fileWatcher.beginFullSyncReplacement()),a.incompleteSyncTimer&&(clearTimeout(a.incompleteSyncTimer),a.incompleteSyncTimer=null),o){let l=this.config.getPlaceRoot(r),u=yP.join(l,`explorer_tmp_${o}`);await Tc.rm(u,{recursive:!0,force:!0}).catch(()=>{})}a.tmpWriter&&(a.tmpWriter.stopChangeLogFlusher(),a.tmpWriter=null),a.tmpIndex=null,a.collisionDirMap=null,a.activeSyncIdentityScope=null,a.activeFullSyncSessionId=null,a.syncProgress=null,a.state="idle",a.activeClientId=null,a.instanceCount=0,a.scriptCount=0,a.forwardRestoreQueue=[],this.activeFullSyncPlaceId===r&&(this.activeFullSyncPlaceId=null),this.clearRuntimePlaceIfMatch(r),y.info("handleSyncStop responding idle",{placeId:r,previousState:s,activeFullSyncSessionId:o,hadWatcher:c}),y.info("Sync stopped",{placeId:r,reason:i.reason??"requested"}),n.status(200).json({status:"stopped",state:"idle",placeId:r})}catch(i){this.sendError(n,i,"handleSyncStop")}}async initialize(){try{await this.config.loadFromMeta(),await this.initHandler.cleanupStaleTempDirs(),y.info("SyncController initialized")}catch(e){y.error("SyncController initialization failed",e instanceof Error?e:new Error(String(e)))}}async shutdown(){try{y.info("SyncController shutdown: clearing place contexts",{placeCount:this.places.size}),this.places.clear(),y.info("SyncController shut down")}catch(e){y.error("SyncController shutdown error",e instanceof Error?e:new Error(String(e)))}}async atomicWriteFile(e,n){let i=e+".tmp."+Vce().slice(0,8);try{await Tc.writeFile(i,n,"utf-8"),await Tc.rename(i,e)}catch(r){throw await Tc.unlink(i).catch(()=>{}),r}}async handlePreCheck(e,n){try{await this.apiHandler.handlePreCheck(e,n)}catch(i){this.sendError(n,i,"handlePreCheck")}}async handleSyncDirections(e,n){try{await this.apiHandler.handleSyncDirections(e,n)}catch(i){this.sendError(n,i,"handleSyncDirections")}}async handleForwardRestoreList(e,n){try{await this.apiHandler.handleForwardRestoreList(e,n)}catch(i){this.sendError(n,i,"handleForwardRestoreList")}}async handleReversePending(e,n){try{await this.reverseHandler.handleReversePending(e,n)}catch(i){this.sendError(n,i,"handleReversePending")}}async handleReverseSyncChanges(e,n){try{await this.reverseHandler.handleReverseSyncChanges(e,n)}catch(i){this.sendError(n,i,"handleReverseSyncChanges")}}async handleReverseSyncResult(e,n){try{await this.reverseHandler.handleReverseSyncResult(e,n)}catch(i){this.sendError(n,i,"handleReverseSyncResult")}}async handleResolveConflict(e,n){try{await this.reverseHandler.handleResolveConflict(e,n)}catch(i){this.sendError(n,i,"handleResolveConflict")}}async handleReverseRescan(e,n){try{await this.reverseHandler.handleReverseRescan(e,n)}catch(i){this.sendError(n,i,"handleReverseRescan")}}async handleSyncHistory(e,n){try{await this.apiHandler.handleSyncHistory(e,n)}catch(i){this.sendError(n,i,"handleSyncHistory")}}async startFileWatcherForPlace(e){if(e.state!=="syncing"){y.debug("Skipping reverse detector start - place not syncing",{placeId:e.placeId,state:e.state});return}if(e.fileWatcher){if(e.fileWatcher.isActivelyScanning()){y.info("startFileWatcherForPlace: reusing active reverse detector",{placeId:e.placeId,mode:rt});return}y.info("startFileWatcherForPlace: resuming paused reverse detector",{placeId:e.placeId,mode:rt}),e.fileWatcher.endFullSyncReplacement();return}let n=this.createReverseChangeDetector(e);this.configureReverseChangeDetector(e,n),e.fileWatcher=n,n.start().then(()=>{e.fileWatcher===n&&y.info("Reverse detector started for reverse sync",{placeId:e.placeId,mode:rt})}).catch(i=>{y.warn("Failed to start reverse detector",{placeId:e.placeId,mode:rt,error:i instanceof Error?i.message:String(i)}),e.fileWatcher===n&&(e.fileWatcher=null)})}createReverseChangeDetector(e){return this.reverseDetectorFactory({explorerRoot:yP.join(this.config.getPlaceRoot(e.placeId),"explorer"),syncIndex:e.index,placeId:e.placeId})}configureReverseChangeDetector(e,n){e.writer.setOnWriteCallback(i=>{n.suppressPath(i)}),n.setDirectionChecker(i=>{let r=UL(i);return e.directions[r]}),n.setOnForwardViolation(i=>{e.forwardRestoreQueue.includes(i)||(e.forwardRestoreQueue.push(i),y.info("Forward violation queued for restore",{placeId:e.placeId,relativePath:i,queueSize:e.forwardRestoreQueue.length}))})}getStatusSummary(){return this.apiHandler.getStatusSummary()}getDirectionForCategory(e){return this.apiHandler.getDirectionForCategory(e)}getStatusDirect(e){return this.apiHandler.getStatusDirect(e)}async getHistoryDirect(e,n){return this.apiHandler.getHistoryDirect(e,n)}getDirectionsDirect(e){return this.apiHandler.getDirectionsDirect(e)}getProgressDirect(e){return this.apiHandler.getProgressDirect(e)}async readSyncedFile(e,n){return this.apiHandler.readSyncedFile(e,n)}async writeSyncedFile(e,n,i){await this.apiHandler.writeSyncedFile(e,n,i)}async executeViaDisk(e,n){return this.apiHandler.executeViaDisk(e,n)}sendError(e,n,i){let r=n instanceof Error?n.message:String(n);if(r.includes("Path traversal detected")){e.status(403).json({error:"Forbidden",message:r});return}let a=n.code;if(a==="ENOSPC"||a==="EPERM"||a==="EACCES"){e.status(500).json({error:"Disk error",message:r});return}y.error(`SyncController.${i} failed`,n instanceof Error?n:new Error(r)),e.status(500).json({error:"Internal error",message:`${i}: ${r}`})}};import{randomUUID as Gce}from"crypto";function fU(t,e){let n=Gce(),i=n.replace(/-/g,"").substring(0,8).toUpperCase(),r=`${i.substring(0,4)}-${i.substring(4,8)}`;return{config:t,app:e,instanceId:n,sessionId:r,startTime:Date.now(),baseUrl:`http://${t.httpHost}:${t.httpPort}`,commandQueue:new Map,pendingCommands:new Map,globalPendingCommands:[],totalCommandsProcessed:0,pluginClients:new Map,pluginCommandSessionsByClientId:new Map,pluginCommandClientIdsByProcessToken:new Map,mcpInstances:new Map,cachedSelectionMap:new Map,isClientMode:!1,clientModeHealthTimer:null,clientModeConsecutiveHealthFailures:0,clientModeUpstreamReachable:!0,clientModeUpstreamContextCaptureEnabled:!0,clientModeLastHealthSuccessAt:null,clientModeLastHealthFailureAt:null,clientModeLastHealthError:null,shuttingDown:!1,onUpstreamPermanentlyDown:null,clientModeIdleTimeoutMs:t.clientModeIdleTimeoutMs??36e5,clientModeIdleWatchdogTimer:null,shutdownFn:null,historyManager:null,analyticsManager:null,executionContextManager:null,licenseState:null,syncController:null,internalCommandExecutor:null,dashboardFolderPicker:null,activeSyncOwnerInstanceId:null,activeProjectRoot:null,pendingDashboardSyncRootPin:null,playtestControlCommand:null,dashboardSyncRootSwitchInFlight:!1,uiStudioRecorders:new Map,aiClientName:"",pluginVersion:"",syncedSessionToken:null,serverLastCommandAt:null}}var mU=Et(no(),1);ie();function hU(t){let e=mU.default.json({limit:"5mb"});t.app.use((n,i,r)=>{if(n.path.startsWith("/sync/")){r();return}e(n,i,r)}),t.app.use((n,i,r)=>{i.setHeader("Access-Control-Allow-Origin","http://localhost:3002"),i.setHeader("Access-Control-Allow-Methods","GET, POST, OPTIONS"),i.setHeader("Access-Control-Allow-Headers","Content-Type"),r()}),t.app.use((n,i,r)=>{y.debug(`${n.method} ${n.path}`,{ip:n.ip}),r()})}zd();import{randomUUID as FP}from"crypto";ie();function Jce(){let t=process.env.WEPPY_ROBLOX_MCP_VERSION?.trim();return t||null}var it=Jce()??"2.7.8";Yd();function Mv(t){let e=new Set;t.aiClientName&&e.add(t.aiClientName);for(let n of t.mcpInstances.values())n.aiClientName&&e.add(n.aiClientName);return Array.from(e)}function _U(t){let n=Date.now();for(let[i,r]of t.mcpInstances)r.lastSeen&&n-r.lastSeen>15e3&&(t.mcpInstances.delete(i),r.sessionId&&t.executionContextManager?.endSession(r.sessionId),y.debug("Removed stale MCP instance",{instanceId:i,lastSeen:r.lastSeen}))}function SU(t,e,n){let i=e.query.clientId;if(i&&t.pluginClients.has(i)){let r=t.pluginClients.get(i);r.lastSeen=Date.now()}try{let r=e.body;if(!r||!Array.isArray(r.selection)||typeof r.count!="number"){y.warn("Invalid selection update request",{body:r}),n.status(400).json({error:"Invalid request body"});return}let a=i||"unknown",o=Date.now();t.cachedSelectionMap.set(a,{selection:r.selection,count:r.count,timestamp:o,clientId:a}),y.debug("Selection cache updated",{count:r.count,clientId:a,timestamp:o}),n.json({status:"ok",timestamp:o})}catch(r){y.error("Error handling selection update",r),n.status(500).json({error:"Internal server error"})}}function wU(t,e,n){let i=parseInt(e.query.maxAge)||3e4,r=Qd(t,i);r?n.json({cached:!0,...r}):n.json({cached:!1,message:"No cached selection available"})}function Qd(t,e=3e4,n){if(t.cachedSelectionMap.size===0)return null;let i;if(n)i=t.cachedSelectionMap.get(n);else for(let a of t.cachedSelectionMap.values())(!i||a.timestamp>i.timestamp)&&(i=a);if(!i)return null;let r=Date.now()-i.timestamp;return e===0||r<=e?i:null}function kP(t,e){let n=t.pluginClients.get(e.clientId),i=Date.now(),r={clientId:e.clientId,projectName:e.projectName,placeName:e.placeName,placeId:e.placeId,pluginVersion:e.pluginVersion,connectedAt:n?.connectedAt||i,lastSeen:i,commandsProcessed:n?.commandsProcessed||0,connectionType:n?.connectionType||"polling",inFlightRequestId:n?.inFlightRequestId,...n?.commandSessionState?{commandSessionState:n.commandSessionState}:{},...n?.processToken?{processToken:n.processToken}:{}};return t.pluginClients.set(e.clientId,r),t.syncController&&typeof t.syncController.setCurrentConnectedPlace=="function"&&typeof e.placeId=="number"&&Number.isFinite(e.placeId)&&t.syncController.setCurrentConnectedPlace(e.placeId,e.placeName??null),t.pendingCommands.has(e.clientId)||t.pendingCommands.set(e.clientId,[]),e.pluginVersion&&(t.pluginVersion=e.pluginVersion),typeof r.placeId=="number"&&Number.isFinite(r.placeId)&&YI(t,r.placeId,r.placeName??null),t.analyticsManager&&(e.pluginVersion&&t.analyticsManager.setPluginVersion(e.pluginVersion),t.analyticsManager.trackPluginConnected()),y.info("Plugin client registered",{clientId:e.clientId,projectName:e.projectName,placeName:e.placeName,isReconnect:!!n}),{clientInfo:r,sessionId:t.sessionId,mcpVersion:it,mcpInstanceCount:t.mcpInstances.size+1,aiClientNames:Mv(t)}}function CP(t,e){let n=t.pluginClients.get(e),i=t.pluginClients.has(e);if(t.pluginClients.delete(e),t.pendingCommands.delete(e),i&&t.syncController&&typeof t.syncController.clearCurrentConnectedPlaceIfMatch=="function"&&typeof n?.placeId=="number"&&Number.isFinite(n.placeId)){let r=[...t.pluginClients.values()].filter(a=>typeof a.placeId=="number"&&Number.isFinite(a.placeId)).sort((a,o)=>o.lastSeen-a.lastSeen)[0];r?t.syncController.setCurrentConnectedPlace?.(r.placeId??null,r.placeName??null):t.syncController.clearCurrentConnectedPlaceIfMatch(n.placeId)}return i&&(ot(t,"connection",{clientId:e,status:"disconnected"}),xc(t,{timestamp:new Date().toISOString(),type:"plugin",status:"disconnected",clientId:e,message:`Plugin disconnected \u2014 ${e}`})),y.info("Plugin client unregistered",{clientId:e,existed:i}),i}function zv(t,e,n){try{let i=e.body;if(!i.clientId){n.status(400).json({error:"Missing clientId"});return}let r=kP(t,{clientId:i.clientId,projectName:i.projectName,placeName:i.placeName,placeId:i.placeId,pluginVersion:i.pluginVersion});n.json({status:"ok",clientId:i.clientId,serverInstanceId:t.instanceId,sessionId:r.sessionId,mcpVersion:r.mcpVersion,connectedAt:r.clientInfo.connectedAt,aiClientNames:r.aiClientNames,serverStartTime:t.startTime,mcpInstanceCount:r.mcpInstanceCount})}catch(i){y.error("Error registering plugin client",i),n.status(500).json({error:"Internal server error"})}}function Lv(t,e,n){let i=e.body?.clientId;if(!i){n.status(400).json({error:"Missing clientId"});return}let r=CP(t,i);n.json({status:"ok",existed:r})}function kU(t,e,n){try{let i=e.body;if(!i.instanceId){n.status(400).json({error:"Missing instanceId"});return}let r=Date.now(),a={instanceId:i.instanceId,...typeof i.sessionId=="string"?{sessionId:i.sessionId}:{},pid:i.pid,connectedAt:r,isServer:!1,lastSeen:r};i.aiClientName&&(a.aiClientName=i.aiClientName),i.cwd&&(a.cwd=i.cwd),"projectRoot"in i&&(a.projectRoot=i.projectRoot),t.mcpInstances.set(i.instanceId,a),ot(t,"mcp_status",{aiClientName:a.aiClientName??"Unknown",instanceId:i.instanceId,status:"registered"}),xc(t,{timestamp:new Date().toISOString(),type:"mcp",status:"registered",instanceId:i.instanceId,message:`MCP registered \u2014 ${a.aiClientName??i.instanceId}`,...a.aiClientName?{aiClientName:a.aiClientName}:{}}),y.info("MCP instance registered (client mode)",{instanceId:i.instanceId,pid:i.pid,cwd:i.cwd}),n.json({status:"ok",instanceId:i.instanceId,serverInstanceId:t.instanceId,sessionId:t.sessionId,mcpVersion:it,mcpInstanceCount:t.mcpInstances.size+1})}catch(i){y.error("Error registering MCP instance",i),n.status(500).json({error:"Internal server error"})}}function CU(t,e,n){let i=e.body?.instanceId;if(!i){n.status(400).json({error:"Missing instanceId"});return}let r=t.mcpInstances.get(i),a=!!r;t.mcpInstances.delete(i),r?.sessionId&&t.executionContextManager?.endSession(r.sessionId),a&&(ot(t,"mcp_status",{aiClientName:r?.aiClientName??"Unknown",instanceId:i,status:"unregistered"}),xc(t,{timestamp:new Date().toISOString(),type:"mcp",status:"unregistered",instanceId:i,message:`MCP unregistered \u2014 ${r?.aiClientName??i}`,...r?.aiClientName?{aiClientName:r.aiClientName}:{}})),y.info("MCP instance unregistered",{instanceId:i,existed:a}),n.json({status:"ok",existed:a})}function IU(t,e,n){let{instanceId:i,aiClientName:r}=e.body;if(i&&r){let a=t.mcpInstances.get(i);a&&(a.aiClientName=r)}n.json({status:"ok"})}function PU(t){let e=3e4,n=Date.now(),i=rr({appDataDir:t.config.appDataDir});for(let[r,a]of t.pluginClients)n-a.lastSeen>e&&(t.pluginClients.delete(r),t.pendingCommands.delete(r));return _U(t),{serverInstanceId:t.instanceId,sessionId:t.sessionId,mcpVersion:it,uptime:n-t.startTime,serverStartTime:t.startTime,serverExecutable:process.execPath,serverHost:t.config.httpHost,serverPort:t.config.httpPort,serverPid:process.pid,mcpInstances:[{instanceId:t.instanceId,pid:process.pid,connectedAt:t.startTime,isServer:!0,cwd:process.cwd(),projectRoot:i,lastSeen:n,...t.aiClientName?{aiClientName:t.aiClientName}:{}},...Array.from(t.mcpInstances.values())],mcpInstanceCount:t.mcpInstances.size+1}}function Uv(t,e){e.json(PU(t))}function Fv(t,e,n){let i=e.query.instanceId;i&&t.mcpInstances.has(i)&&(t.mcpInstances.get(i).lastSeen=Date.now()),_U(t);let r=co(t),s={...{status:"online",connectedClients:r.length,queuedCommands:x6(t),uptime:Date.now()-t.startTime,version:it,enableContextCapture:t.executionContextManager?.isEnabled()??t.config.enableContextCapture??!0,isClientMode:t.isClientMode,shuttingDown:t.shuttingDown,pid:process.pid,sessionId:t.sessionId},instanceId:t.instanceId,mcpInstanceCount:t.mcpInstances.size+1,aiClientNames:Mv(t),pluginVersion:t.pluginVersion||void 0,dashboardSseClients:t.dashboardSseClients?.size??0,websocketClients:r.length,pluginClients:r.map(c=>({clientId:c.clientId,projectName:c.projectName,placeName:c.placeName,pluginVersion:c.pluginVersion,connectionType:c.connectionType,commandSessionState:c.commandSessionState,lastSeen:c.lastSeen})),...t.isClientMode?{upstream:{reachable:t.clientModeUpstreamReachable,consecutiveFailures:t.clientModeConsecutiveHealthFailures,lastSuccessAt:t.clientModeLastHealthSuccessAt,lastFailureAt:t.clientModeLastHealthFailureAt,lastError:t.clientModeLastHealthError,baseUrl:t.baseUrl}}:{}};n.json(s)}function EU(t,e,n,i){let r=e.ip||e.socket.remoteAddress||"";if(!(r==="127.0.0.1"||r==="::1"||r==="::ffff:127.0.0.1"||r==="localhost")){y.warn("Shutdown request rejected from non-localhost",{ip:r}),n.status(403).json({error:"Forbidden: localhost only"});return}y.info("Shutdown request received, initiating graceful shutdown",{requestedBy:r,uptime:Date.now()-t.startTime}),n.json({status:"shutting_down",message:"Server will shutdown gracefully",pid:process.pid}),setTimeout(async()=>{try{await i(),y.info("Graceful shutdown completed"),process.exit(0)}catch(o){y.error("Error during graceful shutdown",o),process.exit(1)}},100)}async function qv(t){if(t.isClientMode)try{let e=await fetch(`${t.baseUrl}/connection-info`);if(e.ok)return await e.json()}catch(e){y.warn("Failed to fetch connection info from server",{error:e})}return PU(t)}ie();Jd();var Oc={query_instances:{discriminator:"action",validActions:new Set(["get","children","find_child","find_descendant","wait_for_child","class_info","search_name","search_class","search_property","search_tag","file_tree","project_structure","descendants","ancestors"]),paramAliases:{query_instances_search_name:{query:"pattern"},query_instances_search_property:{root:"rootPath"},query_instances_search_tag:{root:"rootPath"},query_instances_project_structure:{root:"rootPath"}}},mutate_instances:{discriminator:"action",validActions:new Set(["create","create_with_props","delete","clone","move","rename","pivot","create_tree","mass_create","mass_delete","mass_duplicate","smart_duplicate"]),paramAliases:{mutate_instances_clone:{path:"sourcePath"},mutate_instances_smart_duplicate:{spacing:"offset"}}},manage_properties:{discriminator:"action",validActions:new Set(["get","set","get_all","set_multiple","get_attr","set_attr","get_all_attrs","delete_attr","add_tag","remove_tag","check_tag","get_tags","get_tagged","set_calculated","set_relative","mass_set","mass_get","modify_children"]),paramAliases:{manage_properties_get_tagged:{tagName:"tag",root:"rootPath"},manage_properties_set_relative:{amount:"value"}}},manage_scripts:{discriminator:"action",validActions:new Set(["get_source","set_source","create","delete","edit_replace","edit_insert","edit_delete","search","replace","get_dependencies"]),paramAliases:{manage_scripts_edit_replace:{newLines:"newContent"},manage_scripts_edit_insert:{lines:"content"},manage_scripts_replace:{pattern:"searchPattern"}}},manage_ui:{discriminator:"action",validActions:new Set(["design_brief","create_tree","update","delete","preview","check"])},manage_lighting:{discriminator:"action",validActions:new Set(["lighting","atmosphere","sky","terrain_props","time"])},manage_selection:{discriminator:"action",validActions:new Set(["get","set","clear","cached","context","details","add","remove","watch"])},manage_camera:{discriminator:"action",validActions:new Set(["info","focus_path","focus_position","suggest","screenshot"]),paramAliases:{manage_camera_suggest:{path:"targetPath"}}},manage_tween:{discriminator:"action",validActions:new Set(["create","play","pause","cancel"])},manage_audio:{discriminator:"action",validActions:new Set(["play","stop","pause","resume","set_listener"])},manage_animation:{discriminator:"action",validActions:new Set(["load","play","stop","get_tracks"])},manage_physics:{discriminator:"action",validActions:new Set(["register_group","set_collidable","get_groups"])},manage_effects:{discriminator:"action",validActions:new Set(["emit","clear","toggle"])},manage_terrain:{discriminator:"action",validActions:new Set(["fill_block","fill_ball","fill_cylinder","fill_wedge","clear_region","clear_bounds","replace_material","colors_get","colors_set","read_voxel","read_voxels","write_voxels","generate","smooth"])},spatial_query:{discriminator:"action",validActions:new Set(["raycast","find_ground","check_placement","multi_raycast","scan_area","find_flat","find_spawn","analyze_walkable","spatial_map","find_space","bounds","snap_grid","collision"]),paramAliases:{spatial_query_spatial_map:{path:"rootPath"}}},manage_assets:{discriminator:"action",validActions:new Set(["insert","info","search","search_insert","insert_free","insert_package","export"]),paramAliases:{manage_assets_search:{maxResults:"limit"}}},manage_sync:{discriminator:"action",validActions:new Set(["status_current_place","history","directions","read_file","write_file","progress"])},workspace_state:{discriminator:"action",validActions:new Set(["sync","snapshot","changes","viewport","clear_history","metadata","scripts","selection_info","clear_cache"])},manage_logs:{discriminator:"action",validActions:new Set(["get","clear","errors"]),paramAliases:{manage_logs_get:{level:"type"}}},system_info:{discriminator:"action",validActions:new Set(["ping","connection","usage","place_info","services","studio_settings","play","stop","pause","resume","play_status","run_test"])},manage_studio:{discriminator:"action",validActions:new Set(["toggle_ui_preview"])}};var Yce={manage_selection_cached:"internal",manage_sync_status_current_place:"internal",manage_sync_history:"internal",manage_sync_directions:"internal",manage_sync_read_file:"internal",manage_sync_write_file:"internal",manage_sync_progress:"internal",system_info_connection:"internal",system_info_run_test:"internal"};function Bv(t){return Yce[t]||"plugin"}var RU=new Set(["batch_execute","execute_luau","extended_call_method","extended_call_methods","extended_get_properties","extended_get_property","extended_set_properties","extended_set_property","manage_animation_get_tracks","manage_animation_load","manage_animation_play","manage_animation_stop","manage_assets_export","manage_assets_info","manage_assets_insert","manage_assets_insert_free","manage_assets_insert_package","manage_assets_search","manage_assets_search_insert","manage_audio_pause","manage_audio_play","manage_audio_resume","manage_audio_set_listener","manage_audio_stop","manage_camera_screenshot","manage_effects_clear","manage_effects_emit","manage_effects_toggle","manage_lighting_atmosphere","manage_lighting_lighting","manage_lighting_sky","manage_lighting_terrain_props","manage_lighting_time","manage_physics_get_groups","manage_physics_register_group","manage_physics_set_collidable","manage_properties_mass_get","manage_properties_mass_set","manage_properties_modify_children","manage_properties_set_calculated","manage_properties_set_relative","manage_scripts_replace","manage_selection_add","manage_selection_context","manage_selection_details","manage_selection_remove","manage_selection_watch","manage_studio_toggle_ui_preview","manage_sync_directions","manage_sync_history","manage_sync_progress","manage_sync_read_file","manage_sync_status_current_place","manage_sync_write_file","manage_terrain_clear_bounds","manage_terrain_clear_region","manage_terrain_colors_get","manage_terrain_colors_set","manage_terrain_fill_ball","manage_terrain_fill_block","manage_terrain_fill_cylinder","manage_terrain_fill_wedge","manage_terrain_generate","manage_terrain_read_voxel","manage_terrain_read_voxels","manage_terrain_replace_material","manage_terrain_smooth","manage_terrain_write_voxels","manage_tween_cancel","manage_tween_create","manage_tween_pause","manage_tween_play","manage_ui_check","manage_ui_create_tree","manage_ui_delete","manage_ui_design_brief","manage_ui_preview","manage_ui_update","mutate_instances_create_tree","mutate_instances_mass_create","mutate_instances_mass_delete","mutate_instances_mass_duplicate","mutate_instances_smart_duplicate","query_instances_ancestors","query_instances_descendants","query_instances_file_tree","query_instances_project_structure","query_instances_search_property","query_instances_search_tag","spatial_query_analyze_walkable","spatial_query_bounds","spatial_query_check_placement","spatial_query_collision","spatial_query_find_flat","spatial_query_find_ground","spatial_query_find_space","spatial_query_find_spawn","spatial_query_multi_raycast","spatial_query_raycast","spatial_query_scan_area","spatial_query_snap_grid","spatial_query_spatial_map","system_info_pause","system_info_place_info","system_info_play","system_info_play_status","system_info_resume","system_info_run_test","system_info_services","system_info_stop","system_info_studio_settings","workspace_state_changes","workspace_state_clear_cache","workspace_state_clear_history","workspace_state_metadata","workspace_state_scripts","workspace_state_selection_info","workspace_state_snapshot","workspace_state_sync","workspace_state_viewport"]),IP=new Set(["system_info_connection","system_info_ping","system_info_usage"]);function Un(t){return RU.has(t)?"pro":"basic"}function Qce(t){if(t===null||typeof t!="object")return;let e=t.proFallback;if(e===null||typeof e!="object")return;let n=e;if(typeof n.executedCommand!="string")return;let i=Array.isArray(n.alternatives)?n.alternatives.filter(r=>typeof r=="string"):void 0;return{executedCommand:n.executedCommand,...typeof n.requestedCommand=="string"?{requestedCommand:n.requestedCommand}:{},...i?{alternatives:i}:{}}}var ele=/^Pro action '[^']+' is blocked in Basic mode\. Basic fallback '([^']+)' failed: (.*)$/s,tle=/^Pro action '[^']+' is blocked in Basic mode\.(?! Basic fallback ')/,nle=/instance not found|not found in|parent not found/,rle=/is required|missing required/,ile=/unknown action|unknown command/,ale=/must be|invalid|cannot/;function ole(t){let e=ele.exec(t);return e?{fallbackCommand:e[1],reason:e[2]}:null}function sle(t){return tle.test(t)}function cle(t){let e=t.toLowerCase();return nle.test(e)?"not_found":rle.test(e)?"missing_param":ile.test(e)?"unknown_action":ale.test(e)?"validation":"other"}function PP(t){return t.errorType==="bridge_error"||t.errorType==="command_timeout"||t.errorType==="plugin_runtime"||t.errorType==="exception"||t.errorType==="device_blocked"||t.failureOrigin==="bridge"||t.failureOrigin==="plugin_runtime"||t.failureOrigin==="server_exception"||t.failureOrigin==="product_bug"||t.failureOrigin==="unknown"||t.recoverability==="retry"||t.recoverability==="wait_connection"||t.recoverability==="unknown"?"error":"warning"}function Ac(t){if(t.resultSuccess){let e=Qce(t.resultData);return e?{kind:"success_fallback",fallbackCommand:e.executedCommand,...e.alternatives?{alternatives:e.alternatives}:{}}:{kind:"success_ok"}}if(!t.resultError)return{kind:"error"};if(t.tier==="pro"){let e=ole(t.resultError);if(e)return{kind:"blocked_fallback_failed",fallbackCommand:e.fallbackCommand,blockedDetail:cle(e.reason)};if(sle(t.resultError))return{kind:"blocked_unsupported"}}return t.failureClassification?{kind:PP(t.failureClassification)}:{kind:"error"}}function Ce(t,e,n,i,r={}){return{errorType:t,errorDetail:e,failureOrigin:n,recoverability:i,...r}}function lle(t){let e=t.toLowerCase();return/^\s*compilation error:/.test(e)?Ce("execution_failed","compile_error","agent_request","code_fix",{validationStage:"plugin_handler"}):/^\s*result too large:/.test(e)?Ce("execution_failed","result_too_large","agent_request","code_fix",{validationStage:"plugin_handler"}):/^\s*execution timed out after/.test(e)?Ce("execution_failed","execution_timeout","agent_request","code_fix",{validationStage:"plugin_handler"}):/potentially dangerous infinite loop|blocked in sandbox|access to .* is blocked|forbidden|not allowed|cannot access/.test(e)?Ce("execution_failed","blocked_source","roblox_policy","code_fix",{validationStage:"plugin_handler"}):null}function TU(t){let e=t.toLowerCase();return/upstream_server_down/.test(e)?Ce("bridge_error","upstream_down","bridge","wait_connection"):/^http [45]\d\d/.test(e)?Ce("bridge_error","http_error","bridge","retry"):/timeout/.test(e)?Ce("command_timeout","timeout","bridge","retry"):/econnrefused|socket hang up|econnreset|epipe|network/.test(e)?Ce("bridge_error","connection","bridge","wait_connection"):/abort/.test(e)?Ce("command_timeout","aborted","bridge","retry"):/runtime error|traceback|attempt to/.test(e)?Ce("plugin_runtime","runtime_error","plugin_runtime","code_fix"):/created instance, but failed to set \d+ propert/.test(e)?Ce("execution_failed","property_apply_failed","agent_request","fix_params",{validationStage:"plugin_handler",partialSuccess:"true"}):/failed to set property/.test(e)?Ce("execution_failed","property_apply_failed","agent_request","fix_params",{validationStage:"plugin_handler"}):/parent(?: instance)? not found/.test(e)?Ce("execution_failed","parent_not_found","agent_request","fix_params",{validationStage:"plugin_handler"}):/(?:instance|path|root|source instance|ancestor instance) not found|target\s+(?:path\s+)?(?:['"`][^'"`]+['"`]\s+)?not found|not found in|no synced file found for instance/.test(e)?Ce("execution_failed","not_found","user_project_state","fix_params",{validationStage:"plugin_handler"}):/is required|missing required/.test(e)?Ce("execution_failed","missing_param","agent_request","fix_params",{validationStage:"schema"}):/invalid classname|invalid class/.test(e)?Ce("execution_failed","invalid_class","agent_request","fix_params",{validationStage:"plugin_handler"}):/unknown action|unknown command/.test(e)?Ce("execution_failed","unknown_action","agent_request","fix_params",{validationStage:"dispatcher"}):/failed to insert asset/.test(e)?Ce("execution_failed","asset_error","roblox_policy","fix_params",{validationStage:"roblox_api"}):/failed to read property|property access denied/.test(e)?Ce("execution_failed","property_error","agent_request","fix_params",{validationStage:"plugin_handler"}):/sync controller not available|project sync is not initialized|no active sync place|not found in sync cache/.test(e)?Ce("execution_failed","sync_not_ready","user_project_state","wait_connection",{validationStage:"preflight"}):/cannot capture edit-mode screenshot while a playtest is active|play-mode screenshot capture is not supported/.test(e)?Ce("execution_failed","unsupported_context","roblox_policy","no_retry",{validationStage:"preflight"}):/editableimage api is not enabled|allow mesh \/ image apis|allow http requests/.test(e)?Ce("execution_failed","feature_disabled","roblox_policy","no_retry",{validationStage:"preflight"}):/must be|invalid|cannot/.test(e)?Ce("execution_failed","validation","agent_request","fix_params",{validationStage:"plugin_handler"}):Ce("execution_failed","other","unknown","unknown")}function Nc(t,e){if(t==="execute_luau"){let i=lle(e);if(i)return i}let n=TU(e);return t==="execute_luau"&&n.errorDetail==="runtime_error"&&/^\s*runtime error:/i.test(e)?Ce("execution_failed","runtime_error","agent_request","code_fix"):n}function $U(t){let n=(t instanceof Error?t.message:String(t)).toLowerCase();return/timeout/.test(n)?Ce("command_timeout","timeout","bridge","retry"):/econnrefused|socket hang up|network|econnreset|epipe/.test(n)?Ce("bridge_error","connection","bridge","wait_connection"):/eaddrinuse/.test(n)?Ce("bridge_error","port_in_use","bridge","no_retry"):Ce("exception","unexpected","server_exception","code_fix")}Vv();import{randomBytes as hle}from"node:crypto";var gle=["manage_ui_create_tree","manage_ui_update","manage_ui_delete"],LU=["manage_ui_preview","manage_ui_check"],yle=["game_genre","ui_role","style_family","layout_family","device_policy","safe_area_policy","existing_ui_strategy"];function OP(t){return t&&typeof t=="object"?t:void 0}function $P(t){return typeof t=="number"&&Number.isFinite(t)?t:0}function Wv(t){let e=OP(t);if(!e)return;let n={};for(let i of yle){let r=e[i];typeof r=="string"&&r.trim().length>0&&(n[i]=r)}return Object.keys(n).length>0?n:void 0}function DP(t){let e=Wv(t.qualityPlanSummary);if(e)return e;let n=OP(t.contextSummary),i=Wv(n?.qualityPlanSummary);if(i)return i;let r=Wv(n?.qualityPlan);if(r)return r;let a=Wv(n?.quality_plan);if(a)return a}function UU(t,e){return e?{...t??{},...e}:t}function vle(t){return typeof t=="string"?{snapshotId:t}:t&&typeof t=="object"?{snapshotId:typeof t.snapshotId=="string"?t.snapshotId:null,...t.designCheckSummary!==void 0&&{designCheckSummary:t.designCheckSummary},...t.capturedAt!==void 0&&{capturedAt:t.capturedAt}}:{snapshotId:null}}function FU(t,e,n,i,r){if(!e)return;let a=$P(e.priority_high),o=$P(e.priority_medium),s=$P(e.priority_low),c=typeof e.total=="number"&&Number.isFinite(e.total)?e.total:a+o+s,l=n??(typeof e.target=="string"?e.target:void 0);return{source:t,...l!==void 0&&{targetPath:l},...typeof i=="string"&&{snapshotId:i},...r!==void 0&&{capturedAt:r},priority_high:a,priority_medium:o,priority_low:s,total:c}}function qU(t,e){if(!e)return t;if(!t)return e;let n=t.targetPath===e.targetPath?t.targetPath:void 0,i=t.snapshotId===e.snapshotId?t.snapshotId:void 0;return{source:e.source,...n!==void 0&&{targetPath:n},...i!==void 0&&{snapshotId:i},...e.capturedAt!==void 0&&{capturedAt:e.capturedAt},priority_high:t.priority_high+e.priority_high,priority_medium:t.priority_medium+e.priority_medium,priority_low:t.priority_low+e.priority_low,total:t.total+e.total}}function ble(t){let e=OP(t);if(e)for(let n of["userMessage","intent","expectedBehavior"]){let i=e[n];if(typeof i=="string"&&i.trim().length>0)return i.trim().slice(0,80)}}function xle(){return{source:"unavailable",priority_high:0,priority_medium:0,priority_low:0,total:0}}function BU(t){let e=t.now??Date.now,n=null,i=null,r=l=>`${l}_${e()}_${hle(3).toString("hex")}`,a=()=>{i&&clearTimeout(i),i=setTimeout(()=>{s("idle_timeout")},t.idleMs)},o=async l=>{try{return vle(await t.capturePreview(l))}catch{return{snapshotId:null}}},s=async l=>{if(!n)return;i&&(clearTimeout(i),i=null);let u=n;n=null;let d={},p;for(let h of u.affectedPaths)if(u.afterSnapshotsPathPolicy[h]==="skip")d[h]=null;else{let g=await o(h);d[h]=g.snapshotId,p=qU(p,FU("after_snapshot",g.designCheckSummary,h,g.snapshotId,g.capturedAt))}let f=p??u.latestDesignCheckSummary??xle(),m={requestId:u.requestId,...u.contextId!==void 0&&{contextId:u.contextId},...u.label!==void 0&&{label:u.label},startedAt:u.startedAtIso,closedAt:new Date(e()).toISOString(),closeReason:l,ok:u.ok,affectedPaths:Array.from(u.affectedPaths).sort(),mutationCount:u.mutationCount,beforeSnapshots:u.beforeSnapshots,afterSnapshots:d,childMutationIds:u.childMutationIds,...u.qualityPlanSummary!==void 0&&{qualityPlanSummary:u.qualityPlanSummary},postChangeDesignCheckSummary:f};await jU(t.placeDir,m)},c=l=>({requestId:r("req"),contextId:l.contextId,label:ble(l.contextSummary),startedAtIso:new Date(e()).toISOString(),startedAtMs:e(),affectedPaths:new Set,beforeSnapshots:{},afterSnapshotsPathPolicy:{},childMutationIds:[],mutationCount:0,ok:!0,qualityPlanSummary:DP(l),latestDesignCheckSummary:void 0,pendingMutation:void 0});return{async beforeMutation(l){if(LU.includes(l.command)){if(n){let d=r("mut");n.qualityPlanSummary=UU(n.qualityPlanSummary,DP(l)),n.pendingMutation={mutationId:d,startedMs:e(),args:l}}return}if(!gle.includes(l.command))return;n&&n.contextId!==l.contextId&&await s("context_change"),n||(n=c(l)),n.qualityPlanSummary=UU(n.qualityPlanSummary,DP(l)),l.targetPath&&!(l.targetPath in n.beforeSnapshots)&&(n.affectedPaths.add(l.targetPath),l.command==="manage_ui_create_tree"?n.beforeSnapshots[l.targetPath]=null:n.beforeSnapshots[l.targetPath]=(await o(l.targetPath)).snapshotId,l.command==="manage_ui_delete"?n.afterSnapshotsPathPolicy[l.targetPath]="skip":l.targetPath in n.afterSnapshotsPathPolicy||(n.afterSnapshotsPathPolicy[l.targetPath]="capture"));let u=r("mut");n.pendingMutation={mutationId:u,startedMs:e(),args:l}},async afterMutation(l){if(!n?.pendingMutation)return;let{mutationId:u,args:d}=n.pendingMutation,p={mutationId:u,requestId:n.requestId,ts:new Date(e()).toISOString(),command:d.command,origin:d.origin,...d.targetPath!==void 0&&{targetPath:d.targetPath},...d.briefId!==void 0&&{briefId:d.briefId},...l.diffSummary!==void 0&&{diffSummary:l.diffSummary},...l.changeDetails!==void 0&&{changeDetails:l.changeDetails},ok:l.ok,...l.error!==void 0&&{error:l.error},durationMs:l.durationMs};if(await MU(t.placeDir,p),n.pendingMutation=void 0,LU.includes(d.command)){n.latestDesignCheckSummary=qU(n.latestDesignCheckSummary,FU("check_results",l.designCheckSummary,d.targetPath,l.snapshotId,l.capturedAt));return}if(n.mutationCount++,n.childMutationIds.push(u),l.ok||(n.ok=!1),!n.contextId){await s("legacy_single");return}a()},async flushNow(){n&&await s("explicit")}}}var LP=100,rue=12e4,iue=15e3,cF=new Set(["manage_ui_create_tree","manage_ui_update","manage_ui_delete"]),aue={Text:{category:"text",label:"\uD14D\uC2A4\uD2B8"},PlaceholderText:{category:"text",label:"\uD50C\uB808\uC774\uC2A4\uD640\uB354"},TextColor3:{category:"color",label:"\uAE00\uC790\uC0C9"},BackgroundColor3:{category:"color",label:"\uBC30\uACBD\uC0C9"},BorderColor3:{category:"color",label:"\uD14C\uB450\uB9AC\uC0C9"},ImageColor3:{category:"color",label:"\uC774\uBBF8\uC9C0\uC0C9"},Size:{category:"size",label:"\uC0AC\uC774\uC988"},TextSize:{category:"size",label:"\uAE00\uC790 \uD06C\uAE30"},Position:{category:"layout",label:"\uC704\uCE58"},AnchorPoint:{category:"layout",label:"\uC575\uCEE4"},Rotation:{category:"layout",label:"\uD68C\uC804"},LayoutOrder:{category:"layout",label:"\uB808\uC774\uC544\uC6C3 \uC21C\uC11C"},Visible:{category:"state",label:"\uD45C\uC2DC \uC0C1\uD0DC"},Active:{category:"state",label:"\uD65C\uC131 \uC0C1\uD0DC"},Interactable:{category:"state",label:"\uC0C1\uD638\uC791\uC6A9 \uC0C1\uD0DC"},Selectable:{category:"state",label:"\uC120\uD0DD \uAC00\uB2A5"},Enabled:{category:"state",label:"\uD65C\uC131\uD654"},Image:{category:"asset",label:"\uC774\uBBF8\uC9C0"},Texture:{category:"asset",label:"\uD14D\uC2A4\uCC98"},SoundId:{category:"asset",label:"\uC0AC\uC6B4\uB4DC"}},oue={text:"\uD14D\uC2A4\uD2B8",color:"\uCEEC\uB7EC",size:"\uC0AC\uC774\uC988",layout:"\uB808\uC774\uC544\uC6C3",state:"\uC0C1\uD0DC",asset:"\uC5D0\uC14B",property:"\uC18D\uC131"},sue=Object.entries(Oc).reduce((t,[e,n])=>{for(let i of n.validActions)t[`${e}_${i}`]=e;return t},{});function nF(t){return{toolName:sue[t]||t,commandName:t}}function rF(t,e){if(typeof t.contextId=="string"||!e||typeof e!="object")return t;let n=e.contextId;return typeof n=="string"?{...t,contextId:n}:t}function Jv(t,e){if(t!=="manage_sync_status_current_place")return e;let{placeId:n,...i}=e;return i}async function cue(t,e){let n=t.syncController;if(!n)return null;try{let i=e.placeId,r=n.getDefaultRuntimePlaceId(),a=null;if(typeof i=="number"&&Number.isFinite(i))a=i;else if(typeof i=="string"&&i.trim().length>0){let s=Number.parseInt(i,10);Number.isFinite(s)&&(a=s)}if(a===null&&(a=r),a===null||!Number.isFinite(a))return null;let o=a===r?n.getDefaultRuntimePlaceName():null;return typeof o=="string"&&o.length>0?await n.config.resolvePlaceRoot(a,o):n.config.getPlaceRoot(a)}catch{return null}}function lue(t){let e=t.tree;if(!e||typeof e!="object")return;let n=e,i=typeof n.name=="string"&&n.name.length>0?n.name:void 0;return i?`${typeof n.parent=="string"&&n.parent.length>0?n.parent:"StarterGui"}.${i}`:void 0}function uue(t,e){let n=e.targetPath;if(typeof n=="string"&&n.length>0)return n;if(t==="manage_ui_create_tree")return lue(e)}function due(t){let e=aue[t];if(e)return e;let n=t.toLowerCase();return n.includes("color")?{category:"color",label:"\uC0C9\uC0C1"}:t==="Size"||n.endsWith("size")||n.includes("scale")?{category:"size",label:"\uC0AC\uC774\uC988"}:n.includes("position")||n.includes("anchor")||n.includes("rotation")||n.includes("layout")?{category:"layout",label:"\uB808\uC774\uC544\uC6C3"}:n.includes("visible")||n.includes("active")||n.includes("enabled")||n.includes("selectable")||n.includes("interactable")?{category:"state",label:"\uC0C1\uD0DC"}:n.includes("image")||n.includes("texture")||n.includes("sound")||n.includes("asset")?{category:"asset",label:"\uC5D0\uC14B"}:n.includes("text")||n.includes("placeholder")?{category:"text",label:"\uD14D\uC2A4\uD2B8"}:{category:"property",label:t}}function iF(t,e){return Object.prototype.hasOwnProperty.call(t,e)}function pue(t){if(!t||typeof t!="object")return new Map;let e=t,n=e.changed_properties??e.changedProperties;if(!Array.isArray(n))return new Map;let i=new Map;for(let r of n){if(!r||typeof r!="object")continue;let a=r,o=a.property;if(typeof o!="string"||o.length===0)continue;let s=iF(a,"before"),c=iF(a,"after");i.set(o,{...s&&{before:a.before},...c&&{after:a.after},hasBefore:s,hasAfter:c})}return i}function lF(t,e){let i=t.changes?.properties;if(!i||typeof i!="object")return;let r=pue(e),a=Object.entries(i).map(([o,s])=>{let c=due(o),l=r.get(o),u={property:o,category:c.category,label:c.label,after:l?.hasAfter?l.after:s};return l?.hasBefore&&(u.before=l.before),u});return a.length>0?a:void 0}function uF(t){if(!t||t.length===0)return;let e=new Map;for(let r of t)e.set(r.category,(e.get(r.category)??0)+1);let i=["text","color","size","layout","state","asset","property"].map(r=>{let a=e.get(r)??0;return a>0?`${oue[r]} ${a}\uAC1C`:null}).filter(r=>r!==null);return i.length>0?`${i.join(", ")} \uBCC0\uACBD`:void 0}function aF(t,e){if(t==="manage_ui_update"){let n=e.changes,i=uF(lF(e));if(i)return i;let r=n?.properties?Object.keys(n.properties).length:0,a=Array.isArray(n?.addChildren)?n.addChildren.length:0,o=Array.isArray(n?.removeChildren)?n.removeChildren.length:0;return`${r} props, +${a}, -${o}`}if(t==="manage_ui_create_tree")return"tree \uC0DD\uC131";if(t==="manage_ui_delete")return"\uC81C\uAC70\uB428"}function lp(t){return t&&typeof t=="object"?t:void 0}function Kv(t,e){let n=t[e];return typeof n=="number"&&Number.isFinite(n)?n:0}function dF(t){let e=lp(t),n=lp(e?.design_check_summary);if(n)return{priority_high:Kv(n,"priority_high"),priority_medium:Kv(n,"priority_medium"),priority_low:Kv(n,"priority_low"),total:Kv(n,"total"),...typeof n.scope=="string"&&{scope:n.scope},...typeof n.target=="string"&&{target:n.target}}}function pF(t){let n=lp(t)?.snapshot_id;return typeof n=="string"?n:null}function oF(t){if(typeof t=="string"&&t.trim().length>0)return t;if(typeof t=="number"&&Number.isFinite(t)){let e=t<1e10?t*1e3:t;return new Date(e).toISOString()}}function fF(t){let e=lp(t),n=lp(e?.meta);return oF(n?.captured_at)??oF(n?.evaluated_at)}function fue(t,e){let n=t.uiStudioRecorders.get(e);if(n)return n;let i=BU({placeDir:e,idleMs:iue,capturePreview:async r=>{try{let a=await vo(t,"manage_ui_preview",{targetPath:r},FP());if(!a.success||!a.data||typeof a.data!="object")return null;let o=dF(a.data),s=fF(a.data);return{snapshotId:pF(a.data),...o!==void 0&&{designCheckSummary:o},...s!==void 0&&{capturedAt:s}}}catch{return null}}});return t.uiStudioRecorders.set(e,i),i}async function mue(t,e,n){if(!cF.has(e))return null;let i=await cue(t,n);if(!i)return null;let r=fue(t,i),a=e,o=typeof n.contextId=="string"?n.contextId:void 0,s=n.contextSummary,c=s!==void 0&&typeof s=="object"?s:void 0,l=uue(a,n),u=typeof n.briefId=="string"?n.briefId:void 0,d={command:a,...o!==void 0&&{contextId:o},...c!==void 0&&{contextSummary:c},...l!==void 0&&{targetPath:l},...u!==void 0&&{briefId:u},origin:n.__origin==="dashboard"?"dashboard":"ai"};try{return await r.beforeMutation(d),{recorder:r,command:a,params:n,startedAt:Date.now()}}catch(p){return y.debug("UI Studio recorder beforeMutation \uC2E4\uD328 (\uBB34\uC2DC)",{error:p}),null}}async function sF(t,e){if(t)try{let n=t.command==="manage_ui_update"?lF(t.params,e.data):void 0,i=t.command==="manage_ui_update"?uF(n)??aF(t.command,t.params):aF(t.command,t.params),r=dF(e.data),a=pF(e.data),o=fF(e.data);await t.recorder.afterMutation({ok:e.success,durationMs:Date.now()-t.startedAt,...i!==void 0&&{diffSummary:i},...n!==void 0&&{changeDetails:n},...e.error!==void 0&&{error:e.error},...r!==void 0&&{designCheckSummary:r},...a!==null&&{snapshotId:a},...o!==void 0&&{capturedAt:o}})}catch(n){y.debug("UI Studio recorder afterMutation \uC2E4\uD328 (\uBB34\uC2DC)",{error:n})}}async function UP(t,e,n,i,r,a){if(!t.historyManager)return;let o=!r.success&&r.error?Nc(n,r.error):void 0,s=Ac({resultSuccess:r.success,resultData:r.data,resultError:r.error,tier:Un(n),...o?{failureClassification:o}:{}});if(r.success){let l=s.kind==="success_fallback"&&r.data&&typeof r.data=="object"?{...r.data,outcomeStatus:"fallback",requestedCommand:n,executedCommand:s.fallbackCommand,...s.alternatives?{alternatives:s.alternatives}:{}}:r.data;await t.historyManager.recordSuccess(e,Jv(n,i),l,a,n);return}if(s.kind==="blocked_unsupported"){await t.historyManager.recordBlockedOutcome(e,Jv(n,i),r.error||"Pro action blocked in Basic mode","blocked_unsupported",{executionTimeMs:a,command:n});return}if(s.kind==="blocked_fallback_failed"){await t.historyManager.recordBlockedOutcome(e,Jv(n,i),r.error||"Pro action blocked in Basic mode","blocked_fallback_failed",{executionTimeMs:a,command:n,...s.fallbackCommand?{fallbackCommand:s.fallbackCommand}:{},...s.blockedDetail?{blockedDetail:s.blockedDetail}:{}});return}await(s.kind==="warning"?t.historyManager.recordWarning.bind(t.historyManager):t.historyManager.recordFailure.bind(t.historyManager))(e,Jv(n,i),r.error||"Unknown error",a,o?.errorType.toUpperCase(),n)}function hue(t,e,n){if(!e)return;let i=t.pluginClients.get(e);i?.inFlightRequestId===n&&(i.inFlightRequestId=void 0)}function gue(t,e,n){if(y.debug("Broadcasting command",{command:e.data.command,requestId:e.data.requestId,targetClientId:n||"all",activeWebsocketClients:co(t).length}),n){let i=t.pendingCommands.get(n)||[];i.length>=LP&&i.shift(),i.push(e),t.pendingCommands.set(n,i)}else{let i=co(t).sort((o,s)=>s.lastSeen-o.lastSeen),r=e.data.params?.placeId,a;if(r!==void 0&&i.length>1&&(a=i.find(o=>o.placeId===r),a&&y.debug("Routed command by placeId",{clientId:a.clientId,placeId:r})),a||(a=i[0],i.length>1&&y.warn("Multiple plugin clients connected, routing to most recent",{targetClientId:a?.clientId,targetPlaceId:a?.placeId,totalClients:i.length,command:e.data.command})),a){let o=t.pendingCommands.get(a.clientId)||[];o.length>=LP&&o.shift(),o.push(e),t.pendingCommands.set(a.clientId,o),y.debug("Routed command to client",{clientId:a.clientId,projectName:a.projectName,placeId:a.placeId})}else t.globalPendingCommands.length>=LP&&t.globalPendingCommands.shift(),t.globalPendingCommands.push(e)}mv(t,n)}async function mF(t,e,n){let i=Date.now(),r="",a={};try{let o=e.body;r=o.command??"",a=o.params||{};let s=o.requestId;if(o.instanceId||(t.serverLastCommandAt=Date.now()),o.instanceId){let m=t.mcpInstances.get(o.instanceId);m&&(m.lastCommandAt=Date.now(),m.lastSeen=m.lastCommandAt)}let c=nF(r),l=typeof o.timeout=="number"&&Number.isFinite(o.timeout)&&o.timeout>0?{timeout:o.timeout}:void 0,u=o.instanceId?t.mcpInstances.get(o.instanceId)?.sessionId??t.sessionId:t.sessionId;if(!r){n.status(400).json({error:"Missing command"});return}if(y.debug("Received execute request",{command:r,requestId:s}),r==="manage_camera_play_screenshot"){let m="manage_camera.play_screenshot is not supported. Screenshot capture is available only in Edit mode via manage_camera.screenshot.",h=Date.now()-i;t.historyManager&&await t.historyManager.recordFailure("manage_camera",a,m,h,"UNSUPPORTED_COMMAND",r),n.json({requestId:s,success:!1,error:m});return}if(r==="get_cached_selection"){let m=a.maxAge,h=Qd(t,m!==void 0?m:3e4),g=Date.now()-i;if(!h){let S={requestId:s,success:!0,data:{cached:!1,message:"No cached selection data available. The plugin may not be connected or no selection changes have occurred yet."}};t.historyManager&&await t.historyManager.recordSuccess(c.toolName,a,S.data,g,c.commandName),n.json(S);return}let v=Date.now()-h.timestamp,x={cached:!0,selection:h.selection,count:h.count,timestamp:h.timestamp,age:v};t.historyManager&&await t.historyManager.recordSuccess(c.toolName,a,x,g,c.commandName),n.json({requestId:s,success:!0,data:x});return}if(r==="get_connection_info"){let m=await qv(t),h=Date.now()-i;t.historyManager&&await t.historyManager.recordSuccess(c.toolName,a,m,h,c.commandName),n.json({requestId:s,success:!0,data:m});return}if(Bv(r)==="internal"){if(!t.internalCommandExecutor){n.status(503).json({requestId:s,success:!1,error:`Internal command executor is not initialized for command: ${r}`});return}let m={...a,__sessionId:u},h=await t.internalCommandExecutor(r,m),g=Date.now()-i,v=rF(m,h.data);t.historyManager&&await UP(t,c.toolName,c.commandName,v,{success:h.success,data:h.data,error:h.error||"Internal command failed"},g),n.json({requestId:s,...h});return}let d=await vo(t,r,a,s,l),p=Date.now()-i,f={...rF(a,d.data),__sessionId:u};t.historyManager&&await UP(t,c.toolName,c.commandName,f,d,p),n.json(d)}catch(o){let s=Date.now()-i,c=bn(o);if(t.historyManager&&r){let l=nF(r);await t.historyManager.recordFailure(l.toolName,a,c,s,"EXCEPTION",l.commandName)}y.error("Execute request failed",o),n.status(500).json({success:!1,error:c})}}async function hF(t,e,n){let i=Date.now(),r=e.body,a=r.action;if(!a||typeof a!="string"){n.status(400).json({success:!1,error:"Missing required field: action"});return}if(!new Set(["get","set","get_all","set_multiple","get_attr","set_attr","get_all_attrs","delete_attr","add_tag","remove_tag","check_tag","get_tags","get_tagged"]).has(a)){n.status(400).json({success:!1,error:`Unsupported action: ${a}`});return}let s=`manage_properties_${a}`,c=FP(),{action:l,...u}=r;try{t.serverLastCommandAt=Date.now();let d=await vo(t,s,u,c),p=Date.now()-i;t.historyManager&&await UP(t,"manage_properties",s,u,d,p),n.json(d)}catch(d){let p=Date.now()-i,f=bn(d);t.historyManager&&await t.historyManager.recordFailure("manage_properties",u,f,p,"EXCEPTION",s),y.error("/api/properties \uC694\uCCAD \uCC98\uB9AC \uC2E4\uD328",d),n.status(500).json({success:!1,error:f})}}async function vo(t,e,n,i,r){y.debug("Executing command locally",{command:e,requestId:i});let a=cF.has(e)?await mue(t,e,n):null,o=t.config.requestTimeout,s=r?.timeout??Math.max(t.config.requestTimeout,rue),c=new Promise((l,u)=>{let d={requestId:i,command:e,params:n,timestamp:Date.now(),queueTimeoutMs:o,ackedTimeoutMs:s,state:gn.QUEUED,resolve:l,reject:u,timeoutId:null};t.commandQueue.set(i,d),d.timeoutId=setTimeout(()=>{t.commandQueue.delete(d.requestId),hue(t,d.targetClientId,d.requestId),d.reject(new Error(`Command timeout after ${o}ms while ${d.state}: ${d.command}`))},o)});return gue(t,{event:"command",id:FP(),data:{command:e,requestId:i,params:n}}),c.then(async l=>{let u=l;if(e==="manage_ui_preview"&&l.success&&l.data)try{let{processPreviewResult:d,resolvePreviewPlaceDirFromCtx:p}=await Promise.resolve().then(()=>(zP(),MP)),f=await p(t,n),m=await d("manage_ui_preview",l.data,f);u={...l,data:m.data,...m.mcpContent?{mcpContent:m.mcpContent}:{}}}catch(d){y.warn("preview post-processing failed",{error:d instanceof Error?d.message:String(d)})}if(e==="manage_ui_check"&&l.success&&l.data&&n.includeVisualAnalysis===!0)try{let{resolvePreviewPlaceDirFromCtx:d}=await Promise.resolve().then(()=>(zP(),MP)),{processCheckResult:p}=await Promise.resolve().then(()=>(tF(),eF)),f=await d(t,n),m=await p("manage_ui_check",l.data,f,n);u={...u,data:m.data}}catch(d){y.warn("check visual post-processing failed",{error:d instanceof Error?d.message:String(d)})}return u}).then(async l=>(await sF(a,{success:l.success,...l.data!==void 0&&{data:l.data},...l.error!==void 0&&{error:l.error}}),l),async l=>{throw await sF(a,{success:!1,error:bn(l)}),l})}ie();function Xv(t,e){let n={};e.provider!==void 0&&(n.provider=e.provider),e.tier!==void 0&&(n.tier=e.tier),e.status!==void 0&&(n.status=e.status),e.canUsePro!==void 0&&(n.canUsePro=e.canUsePro),e.cleared===!0&&(n.cleared=!0),ot(t,"license",n)}function lr(t,e,n,i){t.status(e).json({success:!1,error:{code:n,message:i}})}function yue(t){if(typeof t!="string")return;let e=t.trim().toLowerCase();return e.length>0?e:void 0}function Yv(t,e){if(e==="plugin")return t;let n={...t};return delete n.sessionToken,n}function up(t,e,n,i){let r=yue(i??e.query.provider);return r?t.licenseState?.supportsProvider(r)===!1?(lr(n,400,"LICENSE_PROVIDER_UNSUPPORTED",`provider "${r}" is not supported`),null):r:(lr(n,400,"LICENSE_PROVIDER_REQUIRED","provider is required"),null)}function dp(t,e){return t.licenseState?!0:(lr(e,503,"LICENSE_NOT_INITIALIZED","License system is not initialized"),!1)}function vue(t){if(!t||typeof t!="object")return!1;let e=t;return(e.canUsePro===!0||e.canUsePro===!1)&&typeof e.status=="string"&&typeof e.checkedAt=="number"}function pp(t,e,n,i="plugin"){if(!dp(t,n))return;let r=e.body,a=typeof r?.licenseKey=="string"?r.licenseKey:"";if(!a.trim()){lr(n,400,"LICENSE_KEY_REQUIRED","licenseKey is required");return}let o=up(t,e,n,r?.provider);if(!o)return;let s=typeof r?.pluginClientId=="string"?r.pluginClientId:typeof e.query.clientId=="string"?e.query.clientId:void 0,c=typeof r?.deviceId=="string"?r.deviceId:void 0,l={licenseKey:a,provider:o,clientType:i};s&&(l.pluginClientId=s),c&&(l.deviceId=c),t.licenseState.activateGateway(l).then(u=>{t.analyticsManager?.setTier(u.canUsePro?"pro":"basic"),Xv(t,{provider:o,tier:u.canUsePro?"pro":"basic",status:u.status,canUsePro:u.canUsePro}),n.json(Yv(u,i))}).catch(u=>{y.warn("License activate failed",{error:u instanceof Error?u.message:"unknown_error"}),lr(n,502,"LICENSE_ACTIVATE_FAILED",u instanceof Error?u.message:"License activation failed")})}function fp(t,e,n,i="plugin"){if(!dp(t,n))return;let r=up(t,e,n);if(r)try{let a=t.licenseState.getStatus(r);t.analyticsManager?.setTier(a.canUsePro?"pro":"basic"),n.json(Yv(a,i))}catch(a){y.warn("License status check failed",{error:a instanceof Error?a.message:"unknown_error"}),lr(n,502,"LICENSE_STATUS_FAILED",a instanceof Error?a.message:"License status check failed")}}function mp(t,e,n,i="plugin"){if(!dp(t,n))return;let r=e.body,a=up(t,e,n,r?.provider);if(!a)return;let o={provider:a,clientType:i};i==="plugin"&&typeof r?.sessionToken=="string"&&r.sessionToken.trim()&&(o.sessionToken=r.sessionToken.trim()),typeof r?.pluginClientId=="string"&&(o.pluginClientId=r.pluginClientId),typeof r?.deviceId=="string"&&(o.deviceId=r.deviceId),t.licenseState.refreshGateway(o).then(s=>{t.analyticsManager?.setTier(s.canUsePro?"pro":"basic"),Xv(t,{provider:a,tier:s.canUsePro?"pro":"basic",status:s.status,canUsePro:s.canUsePro}),n.json(Yv(s,i))}).catch(s=>{y.warn("License refresh failed",{error:s instanceof Error?s.message:"unknown_error"}),lr(n,502,"LICENSE_REFRESH_FAILED",s instanceof Error?s.message:"License refresh failed")})}function hp(t,e,n,i="plugin"){if(!dp(t,n))return;let r=e.body,a=up(t,e,n,r?.provider);a&&t.licenseState.resetGateway({provider:a,clientType:i}).then(o=>{t.analyticsManager?.setTier("basic"),Xv(t,{provider:a,tier:"basic",status:o.status,canUsePro:o.canUsePro,cleared:!0}),n.json(Yv(o,i))}).catch(o=>{y.warn("License reset failed",{error:o instanceof Error?o.message:"unknown_error"}),lr(n,502,"LICENSE_RESET_FAILED",o instanceof Error?o.message:"License reset failed")})}function qP(t,e,n){if(!dp(t,n))return;let i=e.body,r=up(t,e,n,i?.provider);if(!r)return;if(i?.clientType!=="plugin"){lr(n,400,"LICENSE_CLIENT_TYPE_INVALID","clientType must be plugin");return}let a=typeof i?.sessionToken=="string"&&i.sessionToken.trim()?i.sessionToken.trim():void 0,o=vue(i?.snapshot)?i.snapshot:void 0;if(!o&&!a){lr(n,400,"LICENSE_BOOTSTRAP_REQUIRED","snapshot or sessionToken is required");return}let s={provider:r,clientType:"plugin"};o&&(s.snapshot=o),a&&(s.sessionToken=a),t.licenseState.bootstrap(s).then(c=>{t.analyticsManager?.setTier(c.canUsePro?"pro":"basic"),Xv(t,{provider:r,tier:c.canUsePro?"pro":"basic",status:c.status,canUsePro:c.canUsePro}),n.json({ok:!0})}).catch(c=>{y.warn("License bootstrap failed",{error:c instanceof Error?c.message:"unknown_error"}),lr(n,502,"LICENSE_BOOTSTRAP_FAILED",c instanceof Error?c.message:"License bootstrap failed")})}function gF(t){t.app.post("/license/bootstrap",(e,n)=>qP(t,e,n)),t.app.post("/license/activate",(e,n)=>pp(t,e,n,"plugin")),t.app.post("/license/refresh",(e,n)=>mp(t,e,n,"plugin")),t.app.post("/license/reset",(e,n)=>hp(t,e,n,"plugin")),t.app.get("/license/status",(e,n)=>fp(t,e,n,"plugin"))}Nt();ie();import{promises as Qv}from"fs";import BP from"path";function bue(t){let e=t.config.appDataDir??cn();return BP.join(e,"observability","logs")}async function Uc(t,e,n){let i=e.query.clientId;if(i&&t.pluginClients.has(i)){let r=t.pluginClients.get(i);r.lastSeen=Date.now()}try{let r=e.body;if(!r||!Array.isArray(r.logs)){y.warn("Invalid logs request",{body:r}),n.status(400).json({error:"Invalid request body"});return}let a=bue(t);await Qv.mkdir(a,{recursive:!0});let c=`plugin-${new Date().toISOString().split("T")[0]}.log`,l=BP.join(a,c),u=BP.join(a,"current.log"),d=r.logs.map(p=>`[${new Date(p.timestamp*1e3).toISOString().replace("T"," ").substring(0,19)}] [${p.level}] ${p.message}`).join(`
505
505
  `)+`
506
506
  `;await Qv.appendFile(l,d,"utf-8");try{let p="";try{p=await Qv.readFile(u,"utf-8")}catch{}let m=(p+d).split(`
507
507
  `).slice(-500).join(`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weppy/roblox-mcp",
3
- "version": "2.7.6",
3
+ "version": "2.7.8",
4
4
  "description": "MCP (Model Context Protocol) server for Roblox Studio integration - enables AI coding agents to interact with Roblox Studio in real-time",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
Binary file
@@ -1 +0,0 @@
1
- import{u as w,j as e,y as m,r as p,z as h,T as j,L as v}from"./index-BPIBy2lU.js";const T="_card_1vaoq_2",b="_cardUnread_1vaoq_15",C="_cardRead_1vaoq_20",f="_topRow_1vaoq_25",S="_categoryChip_1vaoq_32",R="_versionChip_1vaoq_45",k="_date_1vaoq_57",q="_title_1vaoq_65",H="_body_1vaoq_74",E="_link_1vaoq_83",A="_newBadge_1vaoq_98",n={card:T,cardUnread:b,cardRead:C,topRow:f,categoryChip:S,versionChip:R,date:k,title:q,body:H,link:E,newBadge:A},U={release:"whatsNew.category.release",notice:"whatsNew.category.notice",deprecation:"whatsNew.category.deprecation",tip:"whatsNew.category.tip"};function x({announcement:s,isNew:d}){const{locale:i,t:c}=w(),o=d?n.cardUnread:n.cardRead,_=s.title[i]??s.title.en,l=s.body[i]??s.body.en,r=s.link?s.link.label[i]??s.link.label.en:null;return e.jsxs("div",{className:`${n.card} ${o}`,children:[e.jsxs("div",{className:n.topRow,children:[e.jsx("span",{className:n.categoryChip,children:c(U[s.category])}),s.version&&e.jsx("span",{className:n.versionChip,children:s.version}),d&&e.jsx("span",{className:n.newBadge,children:c("whatsNew.newBadge","NEW")}),e.jsx("span",{className:n.date,children:s.date})]}),e.jsx("h3",{className:n.title,children:_}),e.jsx("p",{className:n.body,children:l}),s.link&&r&&e.jsxs("a",{className:n.link,href:s.link.url,target:"_blank",rel:"noopener noreferrer",children:["↗"," ",r]})]})}const B="_page_19yl3_2",I="_pageHeader_19yl3_10",$="_pageHeaderText_19yl3_17",L="_pageTitle_19yl3_24",M="_pageSubtitle_19yl3_31",z="_settingsIcon_19yl3_38",W="_section_19yl3_67",O="_sectionTitle_19yl3_73",Y="_unreadCount_19yl3_85",G="_empty_19yl3_100",K="_divider_19yl3_108",t={page:B,pageHeader:I,pageHeaderText:$,pageTitle:L,pageSubtitle:M,settingsIcon:z,section:W,sectionTitle:O,unreadCount:Y,empty:G,divider:K};function D(){const{t:s}=w(),{readSet:d,markRead:i}=m(),c=p.useMemo(()=>{const a=[];for(const u of h)d.has(u.id)||a.push(u.id);return a},[]),o=p.useRef(c);o.current=c,p.useEffect(()=>()=>{o.current.length>0&&i(o.current)},[]);const _=p.useMemo(()=>new Set(c),[c]),l=h.filter(a=>_.has(a.id)),r=h.filter(a=>!_.has(a.id)),g=l.length>0,N=r.length>0,y=e.jsxs("div",{className:t.pageHeader,children:[e.jsxs("div",{className:t.pageHeaderText,children:[e.jsx("h1",{className:t.pageTitle,children:s("whatsNew.pageTitle","What's New")}),e.jsx("p",{className:t.pageSubtitle,children:s("whatsNew.pageSubtitle","Stay up to date with MCP changes")})]}),e.jsx(j,{text:s("sidebar.settings","Settings"),children:e.jsx(v,{to:"/settings","aria-label":s("sidebar.settings","Settings"),className:t.settingsIcon,children:"⚙️"})})]});return h.length===0?e.jsxs("div",{className:t.page,children:[y,e.jsx("p",{className:t.empty,children:s("whatsNew.empty","No announcements yet")})]}):e.jsxs("div",{className:t.page,children:[y,g&&e.jsxs("section",{className:t.section,children:[e.jsxs("h2",{className:t.sectionTitle,children:[s("whatsNew.unreadSection","Unread"),e.jsx("span",{className:t.unreadCount,children:l.length})]}),l.map(a=>e.jsx(x,{announcement:a,isNew:!0},`unread-${a.id}`))]}),g&&N&&e.jsx("hr",{className:t.divider}),N&&e.jsxs("section",{className:t.section,children:[e.jsx("h2",{className:t.sectionTitle,children:s("whatsNew.allSection","All Announcements")}),r.map(a=>e.jsx(x,{announcement:a,isNew:!1},`all-${a.id}`))]})]})}export{D as Component};
@@ -1 +0,0 @@
1
- ._card_1vaoq_2{position:relative;display:flex;flex-direction:column;gap:8px;padding:14px 16px;border:1px solid var(--border);border-radius:var(--radius);border-left-width:3px;background:var(--bg-card)}._cardUnread_1vaoq_15{border-left-color:var(--accent)}._cardRead_1vaoq_20{border-left-color:var(--text-muted)}._topRow_1vaoq_25{display:flex;align-items:center;gap:8px;flex-wrap:wrap}._categoryChip_1vaoq_32{display:inline-flex;align-items:center;padding:2px 8px;border-radius:999px;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;background:var(--accent-dim);color:var(--accent)}._versionChip_1vaoq_45{display:inline-flex;align-items:center;padding:2px 8px;border-radius:999px;font-size:11px;font-weight:600;background:#7f8daa24;color:var(--text-secondary);font-family:var(--font-code)}._date_1vaoq_57{margin-left:auto;font-size:12px;color:var(--text-muted);white-space:nowrap}._title_1vaoq_65{margin:0;font-size:15px;font-weight:700;color:var(--text-primary);line-height:1.3}._body_1vaoq_74{font-size:13px;color:var(--text-secondary);line-height:1.5;white-space:pre-wrap;word-break:break-word}._link_1vaoq_83{display:inline-flex;align-items:center;gap:4px;font-size:13px;color:var(--accent);text-decoration:none;font-weight:600}._link_1vaoq_83:hover{text-decoration:underline}._newBadge_1vaoq_98{display:inline-flex;align-items:center;padding:1px 6px;border-radius:999px;font-size:10px;font-weight:800;letter-spacing:.06em;background:var(--error);color:#fff}._page_19yl3_2{display:flex;flex-direction:column;gap:24px;max-width:720px}._pageHeader_19yl3_10{display:flex;align-items:flex-start;justify-content:space-between;gap:16px}._pageHeaderText_19yl3_17{display:flex;flex-direction:column;gap:4px;min-width:0}._pageTitle_19yl3_24{margin:0;font-size:22px;font-weight:700;color:var(--text-primary)}._pageSubtitle_19yl3_31{margin:0;font-size:13px;color:var(--text-secondary)}._settingsIcon_19yl3_38{display:inline-flex;align-items:center;justify-content:center;width:40px;height:40px;flex-shrink:0;border:1px solid var(--border);border-radius:12px;background:var(--bg-card);color:var(--text-primary);font-size:18px;text-decoration:none;transition:border-color var(--transition),background var(--transition),color var(--transition),transform var(--transition)}._settingsIcon_19yl3_38:hover{border-color:var(--border-highlight);background:var(--accent-dim);color:var(--accent);text-decoration:none;transform:translateY(-1px)}._section_19yl3_67{display:flex;flex-direction:column;gap:12px}._sectionTitle_19yl3_73{display:flex;align-items:center;gap:8px;margin:0;font-size:14px;font-weight:700;color:var(--text-primary);text-transform:uppercase;letter-spacing:.06em}._unreadCount_19yl3_85{display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:20px;padding:0 6px;border-radius:999px;font-size:11px;font-weight:700;background:var(--error);color:#fff}._empty_19yl3_100{padding:32px 0;text-align:center;font-size:14px;color:var(--text-muted)}._divider_19yl3_108{height:1px;background:var(--border);border:none;margin:0}