@weppy/roblox-mcp 2.7.17 → 2.7.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/roblox-plugin/WeppyRobloxMCP.rbxm +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,13 @@ All notable changes to this project will be documented in this file.
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
|
|
9
|
+
|
|
10
|
+
## [2.7.18] - 2026-06-08
|
|
11
|
+
|
|
12
|
+
### Stability
|
|
13
|
+
|
|
14
|
+
- **Clearer sync disk-error troubleshooting** — When Project Sync cannot start because the local sync folder cannot be written, WEPPY now keeps the underlying disk or permission message in the Studio error details and server logs instead of only showing `Disk error`. This makes support logs more actionable without changing the sync workflow.
|
|
15
|
+
|
|
9
16
|
## [2.7.17] - 2026-06-07
|
|
10
17
|
|
|
11
18
|
### Bug Fixes
|
package/dist/index.js
CHANGED
|
@@ -501,7 +501,7 @@ data: ${JSON.stringify(n)}
|
|
|
501
501
|
`)}catch(i){g.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()},Uce),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 Ye.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 Ye.copyFile(a,o)}}async tryRename(e,n){try{return this.onWriteCallback?.(e),this.onWriteCallback?.(n),await Ye.rename(e,n),!0}catch{return!1}}};function hP(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)=>hP(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)&&hP(n[o],i[o]))}function qce(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;hP(a,o)||(n[r]={before:a,after:o})}return n}import qe from"path";import{promises as mo}from"fs";ie();var Cv=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){g.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 y=qe.relative(this.placeRoot,o.fsPath),v={type:"containerCreated",instancePath:p,fsPath:y,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){g.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:Er(o).siblingIndex}isForwardOnlyPath(e){return this.index.isPathUnderClass(e,gc)}logForwardOnlySkip(e,n){g.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:Er(n).siblingIndex}async isContainerDirectory(e){try{let n=await mo.readdir(e,{withFileTypes:!0});for(let i of n)if(i.isFile()&&lv.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 mo.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 mo.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 mo.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 mo.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 mo.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 mo.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:HI.has(n[1])}};ie();import Vce from"path";ie();import ia from"path";import{createHash as iU}from"crypto";import{promises as yP}from"fs";var Bce=500,gP=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`${ia.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=Bce){this.explorerRoot=ia.resolve(e),this.syncIndex=n,this.scanIntervalMs=i}setDirectionChecker(e){this.directionChecker=e}setOnForwardViolation(e){this.onForwardViolation=e}suppressPath(e,n){let i=ia.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"),g.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=>{g.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){g.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),g.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=eU(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&&g.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=ia.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=ia.join(this.explorerRoot,r.relativePath),o=new Date().toISOString();if(await this.isSuppressedPath(a)||r.kind==="file"&&fP(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 yP.readFile(a,"utf-8"),l=iU("md5").update(c,"utf-8").digest("hex");if(s&&l===s){g.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}g.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;g.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,gc)?!0:this.directionChecker?this.directionChecker(e)==="forward":!1}async isSuppressedPath(e){let n=ia.resolve(e);for(let[i,r]of this.suppressedPaths){if(n===i||n.startsWith(`${i}${ia.sep}`))return r.mode==="generic"?!0:r.mode==="missing"?!await Hce(n):await Zce(n)===r.hash;if(i.startsWith(`${n}${ia.sep}`))return r.mode==="generic"}return!1}};async function Hce(t){try{return await yP.access(t),!0}catch{return!1}}async function Zce(t){try{let e=await yP.readFile(t,"utf-8");return iU("md5").update(e,"utf-8").digest("hex")}catch{return null}}var aU=gP;var rt="snapshot-scanner",Iv=class extends aU{placeId;detectorExplorerRoot;lifecycleGeneration=0;constructor(e,n,i={}){super(e,n,i.scanIntervalMs),this.placeId=i.placeId??null,this.detectorExplorerRoot=Vce.resolve(e)}async start(){let e=++this.lifecycleGeneration;g.info("Reverse detector bootstrap scheduled",{placeId:this.placeId,mode:rt,explorerRoot:this.detectorExplorerRoot}),await super.start(),e===this.lifecycleGeneration&&g.info("Reverse detector bootstrap completed",{placeId:this.placeId,mode:rt})}async stop(e){this.lifecycleGeneration+=1,g.info("Reverse detector stopping",{placeId:this.placeId,mode:rt,reason:e??"unspecified"}),await super.stop(e)}beginFullSyncReplacement(){g.info("Reverse detector paused for full sync replacement",{placeId:this.placeId,mode:rt}),super.beginFullSyncReplacement()}async endFullSyncReplacement(){g.info("Reverse detector baseline refresh scheduled",{placeId:this.placeId,mode:rt}),await super.endFullSyncReplacement()}async rescan(){g.debug("Reverse detector manual rescan requested",{placeId:this.placeId,mode:rt});let e=await super.rescan();return g.info("Reverse detector manual rescan completed",{placeId:this.placeId,mode:rt,added:e,pendingCount:this.getPendingCount()}),e}};import Tc from"path";import{promises as oU}from"fs";function ho(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 vP(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=Tc.relative(s,a).split(Tc.sep).filter(l=>l.length>0);return st(["game",...c])}let o=e;if(n){let s=t.getResolvedName(e,n);if(s)o=Jd(o,s);else if(r)throw new Error(`Missing collision mapping for ${e}#${n}`)}return o}function Wce(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 sU(t,e){e=Gce(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=ho(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&&Wce(a,s)){i.add(r);break}return i.size===0?e:e.filter((r,a)=>!i.has(a))}function Gce(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=go(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 go(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 aa(t,e,n,i,r){let a=Tc.join(e,n),o=Tc.join(e,i);try{(await oU.stat(a)).isDirectory()&&(r&&(r.suppressPath(a),r.suppressPath(o)),await oU.rename(a,o),t.renameHashKeysUnder(a,o),t.renameReverseNameMappingsUnder(a,o),t.renameInstanceDirMappingsUnder(a,o))}catch{return}}function vP(t,e,n){let i=Tc.relative(t.getExplorerRoot(),e).split(Tc.sep).filter(r=>r.length>0);return st(["game",...i,n])}async function Pv(t,e){let n=t.index.planCollisionReindexAfterBatchRemovals(e.instancePath,[...e.removedSiblingIndices],e.parentDir);for(let i of n.renames)await aa(t.index,e.parentDir,i.from,i.to,t.fileWatcher),await $c(t.writer,t.index,e.parentDir,i.from,i.to);t.index.applyCollisionReindexAfterBatchRemovals(e.instancePath,e.parentDir,n)}async function $c(t,e,n,i,r){let a=vP(e,n,i),o=vP(e,n,r);await t.renameInTree(a,o,r)}import Ev from"path";var cU=5*60*1e3,lU=100;function bP(t){return t!==null&&typeof t=="object"&&!Array.isArray(t)}function Kd(t){return typeof t=="string"&&t.length>0}function Rv(t,e){let n=Ev.resolve(t),i=Ev.resolve(e),r=Ev.relative(n,i);return r===""||!r.startsWith("..")&&!Ev.isAbsolute(r)}function uU(t){if(!bP(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(!Kd(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"}:Kd(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 Kd(t.serviceName)?Kd(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 dU(t){if(!bP(t))return{success:!1,error:"Body must be an object"};if(!Kd(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(!bP(i)||!e.has(i.type))return{success:!1,error:`changes[${n}]: invalid type '${String(i?.type)}'`}}return{success:!0,data:t}}import sr from"path";import{promises as yo}from"fs";ie();var Tv=class{constructor(e){this.ctx=e}async processChangeForPlace(e,n,i){let r=UL(n.type);if(e.directions[r]==="reverse")return g.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=ho(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):g.warn("propertyChanged: missing both property and properties",{path:o});return null}case"instanceAdded":{let o=hc.has(n.className),s=n.path,c=n.name;{let d=go(e.index,n.path,n.parentSiblingIndex,!0),{resolved:p,retroactiveRename:f}=e.index.registerCollision(n.path,n.siblingIndex,d);f&&(await aa(e.index,d,f.from,f.to,e.fileWatcher),await $c(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(),y=sr.relative(h,d).split(sr.sep).filter(v=>v.length>0);s=st(["game",...y,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=ho(e.index,n.path,n.siblingIndex,n.parentSiblingIndex,!0),s=go(e.index,n.path,n.parentSiblingIndex,!0);g.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);sr.dirname(d)!==e.index.getExplorerRoot()&&(await yo.readdir(d)).length===0&&await yo.rmdir(d)}catch{return null}return null}case"instanceRenamed":{let o=ho(e.index,n.oldPath,n.siblingIndex,n.parentSiblingIndex,!0),s=mt(n.oldPath)===mt(n.newPath),c=s?go(e.index,n.oldPath,n.parentSiblingIndex,!0):null,l=go(e.index,n.newPath,n.parentSiblingIndex,!0),{resolved:u,retroactiveRename:d}=e.index.registerCollision(n.newPath,n.siblingIndex,l);d&&(await aa(e.index,l,d.from,d.to,e.fileWatcher),await $c(e.writer,e.index,l,d.from,d.to));let p=u!==e.index.sanitizeName(n.newName)?Jd(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 aa(e.index,c,m.from,m.to,e.fileWatcher),await $c(e.writer,e.index,c,m.from,m.to);e.index.applyCollisionReindexAfterRemoval(n.oldPath,c,f)}return null}case"instanceMoved":{let o=ho(e.index,n.oldPath,n.siblingIndex,n.parentSiblingIndex,!0),s=go(e.index,n.newPath,n.parentSiblingIndex,!0),{resolved:c,retroactiveRename:l}=e.index.registerCollision(n.newPath,n.siblingIndex,s);l&&(await aa(e.index,s,l.from,l.to,e.fileWatcher),await $c(e.writer,e.index,s,l.from,l.to));let u=nt(n.newPath)||"",d=c!==e.index.sanitizeName(u)?Jd(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=ho(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=sr.join(this.ctx.config.getPlaceRoot(e.placeId),"explorer"),o=sr.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=sr.join(this.ctx.config.getPlaceRoot(e.placeId),"explorer"),a=sr.relative(r,i);e.fileWatcher.injectPending({fsPath:i,relativePath:a,type:"change",detectedAt:new Date().toISOString()})}}catch(i){g.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 yo.readFile(i,"utf-8"),a=JSON.parse(r);if(a.className&&hc.has(a.className))return a.className}catch{return"ModuleScript"}return"ModuleScript"}async cleanupStaleScriptFilesForPlace(e,n,i){let r=sv[i],a=sr.join(this.ctx.config.getPlaceRoot(e.placeId),"explorer");for(let[o,s]of Object.entries(sv)){if(s===r)continue;let c=e.index.resolveScriptPath(n,o,!1);try{await yo.unlink(c),e.index.removeHash(c),g.info("Cleaned up stale script file",{placeId:e.placeId,removed:sr.relative(a,c),currentClassName:i})}catch(l){l.code!=="ENOENT"&&g.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 yo.readFile(r,"utf-8")}catch{return null}return e.index.computeHash(o)===a?null:{fsPath:sr.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 yo.readFile(a,"utf-8")}catch{return null}return e.index.computeHash(s)===o?null:{fsPath:sr.relative(this.ctx.config.getPlaceRoot(e.placeId),a),instancePath:n,fileType:"script",studioContent:i,fileContent:s}}catch{return null}}async applyDeferredCollisionBatchReindexForPlace(e,n){await Pv(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 yo.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 pU from"path";import{promises as $v}from"fs";ie();var Dv=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=pU.join(r,".sync-index.json"),o=!1;try{await $v.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=pU.join(r,"explorer"),c=new ra(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),g.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),g.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=ln(e.query.limit,50,1,200),a=ln(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 yv(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:{...so},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:{...so},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 yv(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{...oo};let i=this.ctx.places.get(n);return i?{...i.directions}:{...oo}}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 $v.readFile(a,"utf-8"),path:a}}catch(s){if(s.code!=="ENOENT")throw s}for(let s of hc){let c=r.index.resolveScriptPath(n,s,!1);try{return{content:await $v.readFile(c,"utf-8"),path:c}}catch{continue}}let o=r.index.resolveValuePath(n);try{return{content:await $v.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 Jce,randomUUID as Kce}from"crypto";import{promises as bn}from"fs";ie();function Xce(t){return Jce("md5").update(t,"utf-8").digest("hex")}function fU(t){return t.pluginProcessToken?`process:${t.pluginProcessToken}`:t.pluginSessionId?`session:${t.pluginSessionId}`:t.pluginClientId?`client:${t.pluginClientId}`:null}var Ov=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&&(g.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=fU(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),g.info("Sync directions received",{placeId:e,directions:r.directions})}else r.directions={...oo};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={...so};r.forwardRestoreQueue=[];let a=Kce();r.activeFullSyncSessionId=a,this.pendingServiceTrees.set(a,new Map),r.syncInitRequestsInFlight=0,r.instanceCount=0,r.scriptCount=0;let o=this.ctx.config.getPlaceRoot(e),s=$e.join(o,`explorer_tmp_${a}`);await bn.mkdir(s,{recursive:!0}),r.tmpIndex=new ra(o,s),r.tmpWriter=new Rc(this.ctx.config,r.tmpIndex,e),r.collisionDirMap=new Map;let c=n.preserveLocalFiles;Array.isArray(c)&&c.length>0&&(this.setPreserveLocalFiles(a,c),g.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 bn.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}`}),g.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=r.tmpIndex,s=r.tmpWriter;this.startTTLTimerForPlace(r,a),this.beginInitRequest(r);try{let c=this.getOrCreatePendingServiceTree(a,n),l=0,u=r.collisionDirMap;for(let p of n.instances){if(this.ctx.config.isForbiddenPath(p.path))continue;let f=o.resolveParentDir(p.path),m=u.get(f)??f,h=r.activeSyncIdentityScope??fU(n),y=this.getPreferredResolvedName(r,h,p),{resolved:v,retroactiveRename:x}=o.registerCollisionWithPreferredName(p.path,p.siblingIndex??void 0,m,y);x&&(await aa(o,m,x.from,x.to),this.rewritePendingEffectivePaths(c,o.getExplorerRoot(),$e.join(m,x.from),$e.join(m,x.to)),o.updateSessionDebugMappingResolvedName(h,x.instancePath,x.siblingIndex,x.to));let S=o.resolveChildrenDir(p.path),w=$e.join(m,v);w!==S&&u.set(S,w);let I=o.sanitizeName(p.name),D=p;if(m!==f||v!==I){let ce=o.getExplorerRoot(),de=$e.relative(ce,m).split($e.sep).filter(De=>De.length>0),B=st(["game",...de,v]);D={...p,path:B}}let F=await s.writeInstance(D);o.setClassName(p.path,p.className,p.siblingIndex),this.recordSessionDebugMapping(r,h,p,v),l++,(F.propsWritten||F.valueWritten)&&r.instanceCount++,F.scriptWritten&&r.scriptCount++,c.instances.push({effectivePath:D.path,originalPath:p.path,className:p.className})}let d=this.isLastChunk(c,n.chunkIndex,n.totalChunks);if(d){let p=this.buildServiceTree(r,c);await s.writeTree(n.serviceName,p);let f=this.pendingServiceTrees.get(a);f?.delete(n.serviceName),f&&f.size===0&&this.pendingServiceTrees.delete(a)}r.syncProgress&&(r.syncProgress.processedInstances+=l,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,d&&r.syncProgress.processedServices++),g.debug("Sync chunk processed",{placeId:e,serviceName:n.serviceName,chunkIndex:n.chunkIndex,totalChunks:n.totalChunks,processed:l}),i.status(200).json({processed:l,service:n.serviceName})}finally{this.endInitRequest(r)}}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;this.startTTLTimerForPlace(r,a),this.beginInitRequest(r);let 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 bn.readFile(m,"utf-8");d.set(f,h)}catch{p.push(f)}}g.info("Backed up local files for preservation",{placeId:e,requested:u.length,backed:d.size,deleted:p.length})}l&&l.beginFullSyncReplacement();try{try{await bn.rm(s,{recursive:!0,force:!0})}catch{}await bn.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(),y=r.index.getExplorerRoot();for(let[v,x]of r.tmpIndex.getAllHashes()){let S=$e.relative(h,v),w=$e.resolve(y,S);r.index.updateHashByValue(w,x)}for(let[v,x]of r.tmpIndex.getAllFileHashes()){let S=$e.relative(h,v),w=$e.resolve(y,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,y]of d){let v=$e.resolve(o,h);try{await bn.mkdir($e.dirname(v),{recursive:!0}),await bn.writeFile(v,y,"utf-8"),l?.suppressPath(v,Xce(y))}catch(x){g.warn("Failed to restore preserved file",{path:h,error:x instanceof Error?x.message:String(x)})}}g.info("Restored preserved local files",{placeId:e,count:d.size})}if(p.length>0){let h=0;for(let y of p){let v=$e.resolve(o,y);try{await bn.unlink(v),r.index.removeHash(v),l?.suppressPath(v,"missing"),h++}catch(x){x.code!=="ENOENT"&&g.warn("Failed to delete preserved-as-deleted file",{path:y,error:x instanceof Error?x.message:String(x)})}}h>0&&(await r.index.saveToDisk(),g.info("Deleted locally-removed files from new sync",{placeId:e,count:h}))}try{let h=this.ctx.config.getSyncRoot();await dv(h,o)}catch(h){g.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(),y={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(y,null,2)+`
|
|
504
|
-
`),await yc(h)}catch(h){g.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=>{g.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}`}),g.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}finally{this.endInitRequest(r)}}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){e.incompleteSyncTimer&&(clearTimeout(e.incompleteSyncTimer),e.incompleteSyncTimer=null);let i=setTimeout(async()=>{if(g.warn("Incomplete sync TTL expired, cleaning up",{placeId:e.placeId,syncId:n}),e.incompleteSyncTimer=null,e.activeFullSyncSessionId===n&&e.syncInitRequestsInFlight>0){g.warn("Incomplete sync TTL expired while sync init request is in flight, extending cleanup",{placeId:e.placeId,syncId:n,syncInitRequestsInFlight:e.syncInitRequestsInFlight}),this.startTTLTimerForPlace(e,n);return}let r=this.ctx.config.getPlaceRoot(e.placeId),a=$e.join(r,`explorer_tmp_${n}`);try{await bn.rm(a,{recursive:!0,force:!0})}catch(o){g.error("Failed to clean up expired temp dir",o instanceof Error?o:new Error(String(o)))}e.activeFullSyncSessionId===n&&(e.activeFullSyncSessionId=null,e.activeClientId=null,e.state="idle",e.tmpIndex=null,e.syncInitRequestsInFlight=0,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))},cU);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)}beginInitRequest(e){e.syncInitRequestsInFlight++}endInitRequest(e){e.syncInitRequestsInFlight=Math.max(0,e.syncInitRequestsInFlight-1)}async cleanupStaleTempDirs(){let e=this.ctx.config.getSyncRoot();try{let n=await bn.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);g.warn("Removing stale temp directory from crashed sync",{dir:i.name});try{await bn.rm(r,{recursive:!0,force:!0})}catch(a){g.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 bn.readdir(r,{withFileTypes:!0});for(let o of a)if(o.isDirectory()&&o.name.startsWith("explorer_tmp_")){let s=$e.join(r,o.name);g.warn("Removing stale temp directory from crashed sync",{dir:`${i.name}/${o.name}`});try{await bn.rm(s,{recursive:!0,force:!0})}catch(c){g.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;g.warn("Failed to scan for stale temp dirs",{error:n instanceof Error?n.message:String(n)})}}};import cr from"path";import{promises as Av}from"fs";ie();var Nv=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=cr.resolve(s,u);if(!Rv(s,d)){l.push({path:u,error:"Path is outside the place root"});continue}try{let p=await Av.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),y=mt(m);y&&h&&await o.writer.removeFromTree(y,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(!Rv(o,cr.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=cr.resolve(c,r);if(!Rv(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 Av.mkdir(cr.dirname(l),{recursive:!0});let p=i.studioContent,f=u.computeHash(p);s.fileWatcher?.suppressPath(l,f),await Av.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 Av.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),g.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=cr.relative(r,n);if(a.startsWith("..")||a===""||cr.isAbsolute(a))return null;let o=a.split(cr.sep).filter(f=>f.length>0);if(o.length<2)return null;let s=cr.basename(n),c=cr.dirname(n),l=e.getOriginalInstance(c,s);if(l)return l.instancePath;let u=s.toLowerCase();if(lv.some(f=>u.endsWith(f))||u==="_tree.json")return null;let d=["game"],p=r;for(let f of o){p=cr.join(p,f);let m=cr.dirname(p);d.push(e.getOriginalNameForDir(m,f))}return st(d)}};ie();function mU(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 Yce(t){return t.type==="instanceAdded"||t.type==="instanceRemoved"||t.type==="instanceRenamed"||t.type==="instanceMoved"||t.type==="scriptSourceChanged"}var Tr=class{config;places;apiHandler;changeProcessor;initHandler;reverseHandler;reverseDetectorFactory;activeFullSyncPlaceId=null;activeRuntimeSyncPlaceId=null;currentConnectedPlaceId=null;currentConnectedPlaceName=null;constructor(e,n={}){this.config=new ov(e,n),this.reverseDetectorFactory=n.reverseDetectorFactory??(i=>new Iv(i.explorerRoot,i.syncIndex,{placeId:i.placeId})),this.apiHandler=new Dv(this),this.changeProcessor=new Tv(this),this.initHandler=new Ov(this),this.reverseHandler=new Nv(this),this.places=new kv({max:3,dispose:(i,r)=>{g.info("Disposing place context (LRU eviction)",{placeId:r}),this.activeFullSyncPlaceId===r&&(this.activeFullSyncPlaceId=null),i.fileWatcher&&(g.info("Disposing place context: stopping reverse detector",{placeId:r,reason:"lru-dispose",mode:rt}),i.fileWatcher.stop("lru-dispose").catch(a=>{g.error("Error stopping reverse detector during dispose",a)}),i.fileWatcher=null),i.writer.stopChangeLogFlusher().catch(a=>{g.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=>{g.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,i.syncInitRequestsInFlight=0}})}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&&(g.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=xP.join(r,"explorer");await Dc.mkdir(r,{recursive:!0}),await Dc.mkdir(a,{recursive:!0});let o=new ra(r,a);await o.loadFromDisk();let s=new Rc(this.config,o,e),c=new Cv(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,syncInitRequestsInFlight:0,changesSinceLastSave:0,directions:{...oo},applyModes:{...so},forwardRestoreQueue:[],syncProgress:null,collisionDirMap:null,activeSyncIdentityScope:null},this.places.set(e,i),g.info("Created new place context",{placeId:e,placeRoot:r})}return i}async handleSyncInit(e,n){try{mU(e.body);let i=uU(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{mU(e.body);let i=dU(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=sU(o,r.changes);g.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++,Yce(m)&&(d=!0))}catch(h){let y="path"in m?m.path:"oldPath"in m?m.oldPath:"unknown";c.push({path:y,error:h instanceof Error?h.message:String(h)})}for(let m of p.values())try{await Pv(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 dv(m,h),await yc(m)}catch(m){g.warn("Failed to refresh sourcemaps after incremental sync update",{placeId:a,error:m instanceof Error?m.message:String(m)})}o.changesSinceLastSave+=u,o.changesSinceLastSave>=lU&&(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:[...gc]};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(g.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){g.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")){g.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&&(g.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=xP.join(l,`explorer_tmp_${o}`);await Dc.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.syncInitRequestsInFlight=0,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),g.info("handleSyncStop responding idle",{placeId:r,previousState:s,activeFullSyncSessionId:o,hadWatcher:c}),g.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(),g.info("SyncController initialized")}catch(e){g.error("SyncController initialization failed",e instanceof Error?e:new Error(String(e)))}}async shutdown(){try{g.info("SyncController shutdown: clearing place contexts",{placeCount:this.places.size}),this.places.clear(),g.info("SyncController shut down")}catch(e){g.error("SyncController shutdown error",e instanceof Error?e:new Error(String(e)))}}async atomicWriteFile(e,n){let i=e+".tmp."+Qce().slice(0,8);try{await Dc.writeFile(i,n,"utf-8"),await Dc.rename(i,e)}catch(r){throw await Dc.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"){g.debug("Skipping reverse detector start - place not syncing",{placeId:e.placeId,state:e.state});return}if(e.fileWatcher){if(e.fileWatcher.isActivelyScanning()){g.info("startFileWatcherForPlace: reusing active reverse detector",{placeId:e.placeId,mode:rt});return}g.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&&g.info("Reverse detector started for reverse sync",{placeId:e.placeId,mode:rt})}).catch(i=>{g.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:xP.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=FL(i);return e.directions[r]}),n.setOnForwardViolation(i=>{e.forwardRestoreQueue.includes(i)||(e.forwardRestoreQueue.push(i),g.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}g.error(`SyncController.${i} failed`,n instanceof Error?n:new Error(r)),e.status(500).json({error:"Internal error",message:`${i}: ${r}`})}};import{randomUUID as ele}from"crypto";function hU(t,e){let n=ele(),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 gU=Et(io(),1);ie();function yU(t){let e=gU.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)=>{g.debug(`${n.method} ${n.path}`,{ip:n.ip}),r()})}qs();import{randomUUID as eb}from"crypto";ie();function tle(){let t=process.env.WEPPY_ROBLOX_MCP_VERSION?.trim();return t||null}var it=tle()??"2.7.17";ep();function Lv(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 wU(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),g.debug("Removed stale MCP instance",{instanceId:i,lastSeen:r.lastSeen}))}function kU(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"){g.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}),g.debug("Selection cache updated",{count:r.count,clientId:a,timestamp:o}),n.json({status:"ok",timestamp:o})}catch(r){g.error("Error handling selection update",r),n.status(500).json({error:"Internal server error"})}}function CU(t,e,n){let i=parseInt(e.query.maxAge)||3e4,r=tp(t,i);r?n.json({cached:!0,...r}):n.json({cached:!1,message:"No cached selection available"})}function tp(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 PP(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)&&tP(t,r.placeId,r.placeName??null),t.analyticsManager&&(e.pluginVersion&&t.analyticsManager.setPluginVersion(e.pluginVersion),t.analyticsManager.trackPluginConnected()),g.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:Lv(t)}}function EP(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"}),Sc(t,{timestamp:new Date().toISOString(),type:"plugin",status:"disconnected",clientId:e,message:`Plugin disconnected \u2014 ${e}`})),g.info("Plugin client unregistered",{clientId:e,existed:i}),i}function Uv(t,e,n){try{let i=e.body;if(!i.clientId){n.status(400).json({error:"Missing clientId"});return}let r=PP(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){g.error("Error registering plugin client",i),n.status(500).json({error:"Internal server error"})}}function Fv(t,e,n){let i=e.body?.clientId;if(!i){n.status(400).json({error:"Missing clientId"});return}let r=EP(t,i);n.json({status:"ok",existed:r})}function IU(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"}),Sc(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}:{}}),g.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){g.error("Error registering MCP instance",i),n.status(500).json({error:"Internal server error"})}}function PU(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"}),Sc(t,{timestamp:new Date().toISOString(),type:"mcp",status:"unregistered",instanceId:i,message:`MCP unregistered \u2014 ${r?.aiClientName??i}`,...r?.aiClientName?{aiClientName:r.aiClientName}:{}})),g.info("MCP instance unregistered",{instanceId:i,existed:a}),n.json({status:"ok",existed:a})}function EU(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 RU(t){let e=3e4,n=Date.now(),i=ir({appDataDir:t.config.appDataDir});for(let[r,a]of t.pluginClients)n-a.lastSeen>e&&(t.pluginClients.delete(r),t.pendingCommands.delete(r));return wU(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 qv(t,e){e.json(RU(t))}function Bv(t,e,n){let i=e.query.instanceId;i&&t.mcpInstances.has(i)&&(t.mcpInstances.get(i).lastSeen=Date.now()),wU(t);let r=Yi(t),s={...{status:"online",connectedClients:r.length,queuedCommands:S6(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:Lv(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 TU(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")){g.warn("Shutdown request rejected from non-localhost",{ip:r}),n.status(403).json({error:"Forbidden: localhost only"});return}g.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(),g.info("Graceful shutdown completed"),process.exit(0)}catch(o){g.error("Error during graceful shutdown",o),process.exit(1)}},100)}async function Hv(t){if(t.isClientMode)try{let e=await fetch(`${t.baseUrl}/connection-info`);if(e.ok)return await e.json()}catch(e){g.warn("Failed to fetch connection info from server",{error:e})}return RU(t)}var ile="system_info_stop",$U=[5e3,15e3];function DU(t){return t===ile}ie();Xd();var Nc={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 ale={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 Zv(t){return ale[t]||"plugin"}var OU=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"]),RP=new Set(["system_info_connection","system_info_ping","system_info_usage"]);function Fn(t){return OU.has(t)?"pro":"basic"}function ole(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 sle=/^Pro action '[^']+' is blocked in Basic mode\. Basic fallback '([^']+)' failed: (.*)$/s,cle=/^Pro action '[^']+' is blocked in Basic mode\.(?! Basic fallback ')/,lle=/instance not found|not found in|parent not found/,ule=/is required|missing required/,dle=/unknown action|unknown command/,ple=/must be|invalid|cannot/;function fle(t){let e=sle.exec(t);return e?{fallbackCommand:e[1],reason:e[2]}:null}function mle(t){return cle.test(t)}function hle(t){let e=t.toLowerCase();return lle.test(e)?"not_found":ule.test(e)?"missing_param":dle.test(e)?"unknown_action":ple.test(e)?"validation":"other"}function TP(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 jc(t){if(t.resultSuccess){let e=ole(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=fle(t.resultError);if(e)return{kind:"blocked_fallback_failed",fallbackCommand:e.fallbackCommand,blockedDetail:hle(e.reason)};if(mle(t.resultError))return{kind:"blocked_unsupported"}}return t.failureClassification?{kind:TP(t.failureClassification)}:{kind:"error"}}function ve(t,e,n,i,r={}){return{errorType:t,errorDetail:e,failureOrigin:n,recoverability:i,...r}}function gle(t){let e=t.toLowerCase();return/^\s*compilation error:/.test(e)?ve("execution_failed","compile_error","agent_request","code_fix",{validationStage:"plugin_handler"}):/^\s*result too large:/.test(e)?ve("execution_failed","result_too_large","agent_request","code_fix",{validationStage:"plugin_handler"}):/^\s*execution timed out after/.test(e)?ve("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)?ve("execution_failed","blocked_source","roblox_policy","code_fix",{validationStage:"plugin_handler"}):null}function AU(t){let e=t.toLowerCase();return/upstream_server_down/.test(e)?ve("bridge_error","upstream_down","bridge","wait_connection"):/^http [45]\d\d/.test(e)?ve("bridge_error","http_error","bridge","retry"):/duplicate request\s*id/.test(e)?ve("bridge_error","duplicate_request_id","bridge","code_fix"):/timeout/.test(e)?ve("command_timeout","timeout","bridge","retry"):/econnrefused|socket hang up|econnreset|epipe|network/.test(e)?ve("bridge_error","connection","bridge","wait_connection"):/abort/.test(e)?ve("command_timeout","aborted","bridge","retry"):/runtime error|traceback|attempt to/.test(e)?ve("plugin_runtime","runtime_error","plugin_runtime","code_fix"):/created instance, but failed to set \d+ propert/.test(e)?ve("execution_failed","property_apply_failed","agent_request","fix_params",{validationStage:"plugin_handler",partialSuccess:"true"}):/failed to set property/.test(e)?ve("execution_failed","property_apply_failed","agent_request","fix_params",{validationStage:"plugin_handler"}):/parent(?: instance)? not found/.test(e)?ve("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)?ve("execution_failed","not_found","user_project_state","fix_params",{validationStage:"plugin_handler"}):/is required|missing required/.test(e)?ve("execution_failed","missing_param","agent_request","fix_params",{validationStage:"schema"}):/invalid classname|invalid class/.test(e)?ve("execution_failed","invalid_class","agent_request","fix_params",{validationStage:"plugin_handler"}):/unknown action|unknown command/.test(e)?ve("execution_failed","unknown_action","agent_request","fix_params",{validationStage:"dispatcher"}):/failed to insert asset/.test(e)?ve("execution_failed","asset_error","roblox_policy","fix_params",{validationStage:"roblox_api"}):/failed to read property|property access denied/.test(e)?ve("execution_failed","property_error","agent_request","fix_params",{validationStage:"plugin_handler"}):/forbidden (?:path|parent path)|exceeds (?:total lines|max(?:imum)?(?: length)?)|is not (?:a|an) /.test(e)?ve("execution_failed","validation","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)?ve("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)?ve("execution_failed","unsupported_context","roblox_policy","no_retry",{validationStage:"preflight"}):/editableimage api is not enabled|allow mesh \/ image apis|allow http requests/.test(e)?ve("execution_failed","feature_disabled","roblox_policy","no_retry",{validationStage:"preflight"}):/must be|invalid|cannot/.test(e)?ve("execution_failed","validation","agent_request","fix_params",{validationStage:"plugin_handler"}):ve("execution_failed","other","unknown","unknown")}function Mc(t,e){if(t==="execute_luau"){let i=gle(e);if(i)return i}let n=AU(e);return t==="execute_luau"&&n.errorDetail==="runtime_error"&&/^\s*runtime error:/i.test(e)?ve("execution_failed","runtime_error","agent_request","code_fix"):n}function NU(t){let n=(t instanceof Error?t.message:String(t)).toLowerCase();return/timeout/.test(n)?ve("command_timeout","timeout","bridge","retry"):/econnrefused|socket hang up|network|econnreset|epipe/.test(n)?ve("bridge_error","connection","bridge","wait_connection"):/eaddrinuse/.test(n)?ve("bridge_error","port_in_use","bridge","no_retry"):ve("exception","unexpected","server_exception","code_fix")}Gv();import{randomBytes as Sle}from"node:crypto";var wle=["manage_ui_create_tree","manage_ui_update","manage_ui_delete"],BU=["manage_ui_preview","manage_ui_check"],kle=["game_genre","ui_role","style_family","layout_family","device_policy","safe_area_policy","existing_ui_strategy"];function jP(t){return t&&typeof t=="object"?t:void 0}function AP(t){return typeof t=="number"&&Number.isFinite(t)?t:0}function Jv(t){let e=jP(t);if(!e)return;let n={};for(let i of kle){let r=e[i];typeof r=="string"&&r.trim().length>0&&(n[i]=r)}return Object.keys(n).length>0?n:void 0}function NP(t){let e=Jv(t.qualityPlanSummary);if(e)return e;let n=jP(t.contextSummary),i=Jv(n?.qualityPlanSummary);if(i)return i;let r=Jv(n?.qualityPlan);if(r)return r;let a=Jv(n?.quality_plan);if(a)return a}function HU(t,e){return e?{...t??{},...e}:t}function Cle(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 ZU(t,e,n,i,r){if(!e)return;let a=AP(e.priority_high),o=AP(e.priority_medium),s=AP(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 VU(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 Ile(t){let e=jP(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 Ple(){return{source:"unavailable",priority_high:0,priority_medium:0,priority_low:0,total:0}}function WU(t){let e=t.now??Date.now,n=null,i=null,r=l=>`${l}_${e()}_${Sle(3).toString("hex")}`,a=()=>{i&&clearTimeout(i),i=setTimeout(()=>{s("idle_timeout")},t.idleMs)},o=async l=>{try{return Cle(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 y=await o(h);d[h]=y.snapshotId,p=VU(p,ZU("after_snapshot",y.designCheckSummary,h,y.snapshotId,y.capturedAt))}let f=p??u.latestDesignCheckSummary??Ple(),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 UU(t.placeDir,m)},c=l=>({requestId:r("req"),contextId:l.contextId,label:Ile(l.contextSummary),startedAtIso:new Date(e()).toISOString(),startedAtMs:e(),affectedPaths:new Set,beforeSnapshots:{},afterSnapshotsPathPolicy:{},childMutationIds:[],mutationCount:0,ok:!0,qualityPlanSummary:NP(l),latestDesignCheckSummary:void 0,pendingMutation:void 0});return{async beforeMutation(l){if(BU.includes(l.command)){if(n){let d=r("mut");n.qualityPlanSummary=HU(n.qualityPlanSummary,NP(l)),n.pendingMutation={mutationId:d,startedMs:e(),args:l}}return}if(!wle.includes(l.command))return;n&&n.contextId!==l.contextId&&await s("context_change"),n||(n=c(l)),n.qualityPlanSummary=HU(n.qualityPlanSummary,NP(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 FU(t.placeDir,p),n.pendingMutation=void 0,BU.includes(d.command)){n.latestDesignCheckSummary=VU(n.latestDesignCheckSummary,ZU("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 pF=100,uue=12e4,due=15e3,fF=new Set(["manage_ui_create_tree","manage_ui_update","manage_ui_delete"]),pue={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"}},fue={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"},mue=Object.entries(Nc).reduce((t,[e,n])=>{for(let i of n.validActions)t[`${e}_${i}`]=e;return t},{});function oF(t){return{toolName:mue[t]||t,commandName:t}}function sF(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 Xv(t,e){if(t!=="manage_sync_status_current_place")return e;let{placeId:n,...i}=e;return i}async function hue(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 gue(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 yue(t,e){let n=e.targetPath;if(typeof n=="string"&&n.length>0)return n;if(t==="manage_ui_create_tree")return gue(e)}function vue(t){let e=pue[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 cF(t,e){return Object.prototype.hasOwnProperty.call(t,e)}function bue(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=cF(a,"before"),c=cF(a,"after");i.set(o,{...s&&{before:a.before},...c&&{after:a.after},hasBefore:s,hasAfter:c})}return i}function mF(t,e){let i=t.changes?.properties;if(!i||typeof i!="object")return;let r=bue(e),a=Object.entries(i).map(([o,s])=>{let c=vue(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 hF(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?`${fue[r]} ${a}\uAC1C`:null}).filter(r=>r!==null);return i.length>0?`${i.join(", ")} \uBCC0\uACBD`:void 0}function lF(t,e){if(t==="manage_ui_update"){let n=e.changes,i=hF(mF(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 dp(t){return t&&typeof t=="object"?t:void 0}function Qv(t,e){let n=t[e];return typeof n=="number"&&Number.isFinite(n)?n:0}function gF(t){let e=dp(t),n=dp(e?.design_check_summary);if(n)return{priority_high:Qv(n,"priority_high"),priority_medium:Qv(n,"priority_medium"),priority_low:Qv(n,"priority_low"),total:Qv(n,"total"),...typeof n.scope=="string"&&{scope:n.scope},...typeof n.target=="string"&&{target:n.target}}}function yF(t){let n=dp(t)?.snapshot_id;return typeof n=="string"?n:null}function uF(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 vF(t){let e=dp(t),n=dp(e?.meta);return uF(n?.captured_at)??uF(n?.evaluated_at)}function xue(t,e){let n=t.uiStudioRecorders.get(e);if(n)return n;let i=WU({placeDir:e,idleMs:due,capturePreview:async r=>{try{let a=await bo(t,"manage_ui_preview",{targetPath:r},eb());if(!a.success||!a.data||typeof a.data!="object")return null;let o=gF(a.data),s=vF(a.data);return{snapshotId:yF(a.data),...o!==void 0&&{designCheckSummary:o},...s!==void 0&&{capturedAt:s}}}catch{return null}}});return t.uiStudioRecorders.set(e,i),i}async function _ue(t,e,n){if(!fF.has(e))return null;let i=await hue(t,n);if(!i)return null;let r=xue(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=yue(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 g.debug("UI Studio recorder beforeMutation \uC2E4\uD328 (\uBB34\uC2DC)",{error:p}),null}}async function dF(t,e){if(t)try{let n=t.command==="manage_ui_update"?mF(t.params,e.data):void 0,i=t.command==="manage_ui_update"?hF(n)??lF(t.command,t.params):lF(t.command,t.params),r=gF(e.data),a=yF(e.data),o=vF(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){g.debug("UI Studio recorder afterMutation \uC2E4\uD328 (\uBB34\uC2DC)",{error:n})}}async function qP(t,e,n,i,r,a){if(!t.historyManager)return;let o=!r.success&&r.error?Mc(n,r.error):void 0,s=jc({resultSuccess:r.success,resultData:r.data,resultError:r.error,tier:Fn(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,Xv(n,i),l,a,n);return}if(s.kind==="blocked_unsupported"){await t.historyManager.recordBlockedOutcome(e,Xv(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,Xv(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,Xv(n,i),r.error||"Unknown error",a,o?.errorType.toUpperCase(),n)}function bF(t,e,n){if(!e)return;let i=t.pluginClients.get(e);i?.inFlightRequestId===n&&(i.inFlightRequestId=void 0)}function Sue(t,e){for(let[n,i]of t.pendingCommands){let r=i.filter(a=>a.data.requestId!==e);r.length!==i.length&&t.pendingCommands.set(n,r)}t.globalPendingCommands=t.globalPendingCommands.filter(n=>n.data.requestId!==e)}function Yv(t,e,n){let i=t.pendingCommands.get(e)||[];i.length>=pF&&i.shift(),i.push(n),t.pendingCommands.set(e,i)}function wue(t,e){t.globalPendingCommands.length>=pF&&t.globalPendingCommands.shift(),t.globalPendingCommands.push(e)}function xF(t,e,n){let i=eb(),r={requestId:i,command:e.command,params:e.params,timestamp:Date.now(),queueTimeoutMs:e.queueTimeoutMs,ackedTimeoutMs:e.ackedTimeoutMs,state:nn.QUEUED,targetClientId:n,resolve:()=>{},reject:a=>{g.debug("Auxiliary playtest stop command ended without awaited response",{requestId:i,targetClientId:n,error:a.message})},timeoutId:null};return t.commandQueue.set(i,r),r.timeoutId=setTimeout(()=>{t.commandQueue.delete(i),bF(t,n,i),Sue(t,i),r.reject(new Error(`Command timeout after ${r.queueTimeoutMs}ms while ${r.state}: ${r.command}`))},r.queueTimeoutMs),{event:"command",id:i,data:{command:r.command,requestId:i,params:r.params}}}function kue(t,e,n,i){if(!DU(e.data.command))return;let r=t.commandQueue.get(e.data.requestId);if(!r)return;Cue(t,r);let a=n.filter(o=>o.clientId!==i);for(let o of a){let s=xF(t,r,o.clientId);Yv(t,o.clientId,s)}a.length>0&&g.info("Fan-out playtest stop command to active plugin clients",{requestId:e.data.requestId,primaryClientId:i,auxiliaryClientIds:a.map(o=>o.clientId)})}function Cue(t,e){for(let n of $U)setTimeout(()=>{let r=[...new Set(Yi(t).map(a=>a.clientId))];for(let a of r){if(!t.pluginClients.get(a))continue;let s=xF(t,e,a);Yv(t,a,s)}g.info("Retry playtest stop command to active plugin clients",{sourceRequestId:e.requestId,delayMs:n,targetClientIds:r}),Zd(t)},n).unref?.()}function Iue(t,e,n){if(g.debug("Broadcasting command",{command:e.data.command,requestId:e.data.requestId,targetClientId:n||"all",activeWebsocketClients:Yi(t).length}),n)Yv(t,n,e);else{let i=Yi(t).sort((o,s)=>s.lastSeen-o.lastSeen),r=e.data.params?.placeId,a;r!==void 0&&i.length>1&&(a=i.find(o=>o.placeId===r),a&&g.debug("Routed command by placeId",{clientId:a.clientId,placeId:r})),a||(a=i[0],i.length>1&&g.warn("Multiple plugin clients connected, routing to most recent",{targetClientId:a?.clientId,targetPlaceId:a?.placeId,totalClients:i.length,command:e.data.command})),a?(Yv(t,a.clientId,e),kue(t,e,i,a.clientId),g.debug("Routed command to client",{clientId:a.clientId,projectName:a.projectName,placeId:a.placeId})):wue(t,e)}Zd(t,n)}async function _F(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=oF(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(g.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=tp(t,m!==void 0?m:3e4),y=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,y,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,y,c.commandName),n.json({requestId:s,success:!0,data:x});return}if(r==="get_connection_info"){let m=await Hv(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(Zv(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),y=Date.now()-i,v=sF(m,h.data);t.historyManager&&await qP(t,c.toolName,c.commandName,v,{success:h.success,data:h.data,error:h.error||"Internal command failed"},y),n.json({requestId:s,...h});return}let d=await bo(t,r,a,s,l),p=Date.now()-i,f={...sF(a,d.data),__sessionId:u};t.historyManager&&await qP(t,c.toolName,c.commandName,f,d,p),n.json(d)}catch(o){let s=Date.now()-i,c=xn(o);if(t.historyManager&&r){let l=oF(r);await t.historyManager.recordFailure(l.toolName,a,c,s,"EXCEPTION",l.commandName)}g.error("Execute request failed",o),n.status(500).json({success:!1,error:c})}}async function SF(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=eb(),{action:l,...u}=r;try{t.serverLastCommandAt=Date.now();let d=await bo(t,s,u,c),p=Date.now()-i;t.historyManager&&await qP(t,"manage_properties",s,u,d,p),n.json(d)}catch(d){let p=Date.now()-i,f=xn(d);t.historyManager&&await t.historyManager.recordFailure("manage_properties",u,f,p,"EXCEPTION",s),g.error("/api/properties \uC694\uCCAD \uCC98\uB9AC \uC2E4\uD328",d),n.status(500).json({success:!1,error:f})}}async function bo(t,e,n,i,r){if(g.debug("Executing command locally",{command:e,requestId:i}),f6(t,i)){let l=`Duplicate requestId '${i}' for command '${e}'. Generate a unique requestId for each command.`;return g.warn("Duplicate execute request rejected",{command:e,requestId:i}),{requestId:i,success:!1,error:l}}let a=fF.has(e)?await _ue(t,e,n):null,o=t.config.requestTimeout,s=r?.timeout??Math.max(t.config.requestTimeout,uue),c=new Promise((l,u)=>{let d={requestId:i,command:e,params:n,timestamp:Date.now(),queueTimeoutMs:o,ackedTimeoutMs:s,state:nn.QUEUED,resolve:l,reject:u,timeoutId:null};t.commandQueue.set(i,d),d.timeoutId=setTimeout(()=>{t.commandQueue.delete(d.requestId),bF(t,d.targetClientId,d.requestId),d.reject(new Error(`Command timeout after ${o}ms while ${d.state}: ${d.command}`))},o)});return Iue(t,{event:"command",id:eb(),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(()=>(FP(),UP)),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){g.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(()=>(FP(),UP)),{processCheckResult:p}=await Promise.resolve().then(()=>(aF(),iF)),f=await d(t,n),m=await p("manage_ui_check",l.data,f,n);u={...u,data:m.data}}catch(d){g.warn("check visual post-processing failed",{error:d instanceof Error?d.message:String(d)})}return u}).then(async l=>(await dF(a,{success:l.success,...l.data!==void 0&&{data:l.data},...l.error!==void 0&&{error:l.error}}),l),async l=>{throw await dF(a,{success:!1,error:xn(l)}),l})}ie();function tb(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 Or(t,e,n,i){t.status(e).json({success:!1,error:{code:n,message:i}})}function Pue(t){if(typeof t!="string")return;let e=t.trim().toLowerCase();return e.length>0?e:void 0}function nb(t,e){if(e==="plugin")return t;let n={...t};return delete n.sessionToken,n}function pp(t,e,n,i){let r=Pue(i??e.query.provider)??"auto";return t.licenseState?.supportsProvider(r)===!1?(Or(n,400,"LICENSE_PROVIDER_UNSUPPORTED",`provider "${r}" is not supported`),null):r}function fp(t,e){return t.licenseState?!0:(Or(e,503,"LICENSE_NOT_INITIALIZED","License system is not initialized"),!1)}function Eue(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 mp(t,e,n,i="plugin"){if(!fp(t,n))return;let r=e.body,a=typeof r?.licenseKey=="string"?r.licenseKey:"";if(!a.trim()){Or(n,400,"LICENSE_KEY_REQUIRED","licenseKey is required");return}let o=pp(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"),tb(t,{provider:u.provider,tier:u.canUsePro?"pro":"basic",status:u.status,canUsePro:u.canUsePro}),n.json(nb(u,i))}).catch(u=>{g.warn("License activate failed",{error:u instanceof Error?u.message:"unknown_error"}),Or(n,502,"LICENSE_ACTIVATE_FAILED",u instanceof Error?u.message:"License activation failed")})}function hp(t,e,n,i="plugin"){if(!fp(t,n))return;let r=pp(t,e,n);if(r)try{let a=t.licenseState.getStatus(r);t.analyticsManager?.setTier(a.canUsePro?"pro":"basic"),n.json(nb(a,i))}catch(a){g.warn("License status check failed",{error:a instanceof Error?a.message:"unknown_error"}),Or(n,502,"LICENSE_STATUS_FAILED",a instanceof Error?a.message:"License status check failed")}}function gp(t,e,n,i="plugin"){if(!fp(t,n))return;let r=e.body,a=pp(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"),tb(t,{provider:s.provider,tier:s.canUsePro?"pro":"basic",status:s.status,canUsePro:s.canUsePro}),n.json(nb(s,i))}).catch(s=>{g.warn("License refresh failed",{error:s instanceof Error?s.message:"unknown_error"}),Or(n,502,"LICENSE_REFRESH_FAILED",s instanceof Error?s.message:"License refresh failed")})}function yp(t,e,n,i="plugin"){if(!fp(t,n))return;let r=e.body,a=pp(t,e,n,r?.provider);a&&t.licenseState.resetGateway({provider:a,clientType:i}).then(o=>{t.analyticsManager?.setTier("basic"),tb(t,{provider:o.provider,tier:"basic",status:o.status,canUsePro:o.canUsePro,cleared:!0}),n.json(nb(o,i))}).catch(o=>{g.warn("License reset failed",{error:o instanceof Error?o.message:"unknown_error"}),Or(n,502,"LICENSE_RESET_FAILED",o instanceof Error?o.message:"License reset failed")})}function BP(t,e,n){if(!fp(t,n))return;let i=e.body,r=pp(t,e,n,i?.provider);if(!r)return;if(i?.clientType!=="plugin"){Or(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=Eue(i?.snapshot)?i.snapshot:void 0;if(!o&&!a){Or(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"),tb(t,{provider:c.provider,tier:c.canUsePro?"pro":"basic",status:c.status,canUsePro:c.canUsePro}),n.json({ok:!0})}).catch(c=>{g.warn("License bootstrap failed",{error:c instanceof Error?c.message:"unknown_error"}),Or(n,502,"LICENSE_BOOTSTRAP_FAILED",c instanceof Error?c.message:"License bootstrap failed")})}function wF(t){t.app.post("/license/bootstrap",(e,n)=>BP(t,e,n)),t.app.post("/license/activate",(e,n)=>mp(t,e,n,"plugin")),t.app.post("/license/refresh",(e,n)=>gp(t,e,n,"plugin")),t.app.post("/license/reset",(e,n)=>yp(t,e,n,"plugin")),t.app.get("/license/status",(e,n)=>hp(t,e,n,"plugin"))}Nt();ie();import{promises as rb}from"fs";import HP from"path";function Rue(t){let e=t.config.appDataDir??un();return HP.join(e,"observability","logs")}async function qc(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)){g.warn("Invalid logs request",{body:r}),n.status(400).json({error:"Invalid request body"});return}let a=Rue(t);await rb.mkdir(a,{recursive:!0});let c=`plugin-${new Date().toISOString().split("T")[0]}.log`,l=HP.join(a,c),u=HP.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 yc(h)}catch(h){g.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=>{g.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}`}),g.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}finally{this.endInitRequest(r)}}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){e.incompleteSyncTimer&&(clearTimeout(e.incompleteSyncTimer),e.incompleteSyncTimer=null);let i=setTimeout(async()=>{if(g.warn("Incomplete sync TTL expired, cleaning up",{placeId:e.placeId,syncId:n}),e.incompleteSyncTimer=null,e.activeFullSyncSessionId===n&&e.syncInitRequestsInFlight>0){g.warn("Incomplete sync TTL expired while sync init request is in flight, extending cleanup",{placeId:e.placeId,syncId:n,syncInitRequestsInFlight:e.syncInitRequestsInFlight}),this.startTTLTimerForPlace(e,n);return}let r=this.ctx.config.getPlaceRoot(e.placeId),a=$e.join(r,`explorer_tmp_${n}`);try{await bn.rm(a,{recursive:!0,force:!0})}catch(o){g.error("Failed to clean up expired temp dir",o instanceof Error?o:new Error(String(o)))}e.activeFullSyncSessionId===n&&(e.activeFullSyncSessionId=null,e.activeClientId=null,e.state="idle",e.tmpIndex=null,e.syncInitRequestsInFlight=0,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))},cU);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)}beginInitRequest(e){e.syncInitRequestsInFlight++}endInitRequest(e){e.syncInitRequestsInFlight=Math.max(0,e.syncInitRequestsInFlight-1)}async cleanupStaleTempDirs(){let e=this.ctx.config.getSyncRoot();try{let n=await bn.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);g.warn("Removing stale temp directory from crashed sync",{dir:i.name});try{await bn.rm(r,{recursive:!0,force:!0})}catch(a){g.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 bn.readdir(r,{withFileTypes:!0});for(let o of a)if(o.isDirectory()&&o.name.startsWith("explorer_tmp_")){let s=$e.join(r,o.name);g.warn("Removing stale temp directory from crashed sync",{dir:`${i.name}/${o.name}`});try{await bn.rm(s,{recursive:!0,force:!0})}catch(c){g.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;g.warn("Failed to scan for stale temp dirs",{error:n instanceof Error?n.message:String(n)})}}};import cr from"path";import{promises as Av}from"fs";ie();var Nv=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=cr.resolve(s,u);if(!Rv(s,d)){l.push({path:u,error:"Path is outside the place root"});continue}try{let p=await Av.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),y=mt(m);y&&h&&await o.writer.removeFromTree(y,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(!Rv(o,cr.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=cr.resolve(c,r);if(!Rv(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 Av.mkdir(cr.dirname(l),{recursive:!0});let p=i.studioContent,f=u.computeHash(p);s.fileWatcher?.suppressPath(l,f),await Av.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 Av.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),g.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=cr.relative(r,n);if(a.startsWith("..")||a===""||cr.isAbsolute(a))return null;let o=a.split(cr.sep).filter(f=>f.length>0);if(o.length<2)return null;let s=cr.basename(n),c=cr.dirname(n),l=e.getOriginalInstance(c,s);if(l)return l.instancePath;let u=s.toLowerCase();if(lv.some(f=>u.endsWith(f))||u==="_tree.json")return null;let d=["game"],p=r;for(let f of o){p=cr.join(p,f);let m=cr.dirname(p);d.push(e.getOriginalNameForDir(m,f))}return st(d)}};ie();function mU(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 Yce(t){return t.type==="instanceAdded"||t.type==="instanceRemoved"||t.type==="instanceRenamed"||t.type==="instanceMoved"||t.type==="scriptSourceChanged"}var Tr=class{config;places;apiHandler;changeProcessor;initHandler;reverseHandler;reverseDetectorFactory;activeFullSyncPlaceId=null;activeRuntimeSyncPlaceId=null;currentConnectedPlaceId=null;currentConnectedPlaceName=null;constructor(e,n={}){this.config=new ov(e,n),this.reverseDetectorFactory=n.reverseDetectorFactory??(i=>new Iv(i.explorerRoot,i.syncIndex,{placeId:i.placeId})),this.apiHandler=new Dv(this),this.changeProcessor=new Tv(this),this.initHandler=new Ov(this),this.reverseHandler=new Nv(this),this.places=new kv({max:3,dispose:(i,r)=>{g.info("Disposing place context (LRU eviction)",{placeId:r}),this.activeFullSyncPlaceId===r&&(this.activeFullSyncPlaceId=null),i.fileWatcher&&(g.info("Disposing place context: stopping reverse detector",{placeId:r,reason:"lru-dispose",mode:rt}),i.fileWatcher.stop("lru-dispose").catch(a=>{g.error("Error stopping reverse detector during dispose",a)}),i.fileWatcher=null),i.writer.stopChangeLogFlusher().catch(a=>{g.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=>{g.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,i.syncInitRequestsInFlight=0}})}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&&(g.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=xP.join(r,"explorer");await Dc.mkdir(r,{recursive:!0}),await Dc.mkdir(a,{recursive:!0});let o=new ra(r,a);await o.loadFromDisk();let s=new Rc(this.config,o,e),c=new Cv(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,syncInitRequestsInFlight:0,changesSinceLastSave:0,directions:{...oo},applyModes:{...so},forwardRestoreQueue:[],syncProgress:null,collisionDirMap:null,activeSyncIdentityScope:null},this.places.set(e,i),g.info("Created new place context",{placeId:e,placeRoot:r})}return i}async handleSyncInit(e,n){try{mU(e.body);let i=uU(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{mU(e.body);let i=dU(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=sU(o,r.changes);g.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++,Yce(m)&&(d=!0))}catch(h){let y="path"in m?m.path:"oldPath"in m?m.oldPath:"unknown";c.push({path:y,error:h instanceof Error?h.message:String(h)})}for(let m of p.values())try{await Pv(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 dv(m,h),await yc(m)}catch(m){g.warn("Failed to refresh sourcemaps after incremental sync update",{placeId:a,error:m instanceof Error?m.message:String(m)})}o.changesSinceLastSave+=u,o.changesSinceLastSave>=lU&&(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:[...gc]};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(g.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){g.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")){g.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&&(g.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=xP.join(l,`explorer_tmp_${o}`);await Dc.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.syncInitRequestsInFlight=0,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),g.info("handleSyncStop responding idle",{placeId:r,previousState:s,activeFullSyncSessionId:o,hadWatcher:c}),g.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(),g.info("SyncController initialized")}catch(e){g.error("SyncController initialization failed",e instanceof Error?e:new Error(String(e)))}}async shutdown(){try{g.info("SyncController shutdown: clearing place contexts",{placeCount:this.places.size}),this.places.clear(),g.info("SyncController shut down")}catch(e){g.error("SyncController shutdown error",e instanceof Error?e:new Error(String(e)))}}async atomicWriteFile(e,n){let i=e+".tmp."+Qce().slice(0,8);try{await Dc.writeFile(i,n,"utf-8"),await Dc.rename(i,e)}catch(r){throw await Dc.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"){g.debug("Skipping reverse detector start - place not syncing",{placeId:e.placeId,state:e.state});return}if(e.fileWatcher){if(e.fileWatcher.isActivelyScanning()){g.info("startFileWatcherForPlace: reusing active reverse detector",{placeId:e.placeId,mode:rt});return}g.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&&g.info("Reverse detector started for reverse sync",{placeId:e.placeId,mode:rt})}).catch(i=>{g.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:xP.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=FL(i);return e.directions[r]}),n.setOnForwardViolation(i=>{e.forwardRestoreQueue.includes(i)||(e.forwardRestoreQueue.push(i),g.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"){g.warn(`SyncController.${i} disk error`,{code:a,message:r}),e.status(500).json({error:"Disk error",message:r,code:a,operation:i});return}g.error(`SyncController.${i} failed`,n instanceof Error?n:new Error(r)),e.status(500).json({error:"Internal error",message:`${i}: ${r}`})}};import{randomUUID as ele}from"crypto";function hU(t,e){let n=ele(),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 gU=Et(io(),1);ie();function yU(t){let e=gU.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)=>{g.debug(`${n.method} ${n.path}`,{ip:n.ip}),r()})}qs();import{randomUUID as eb}from"crypto";ie();function tle(){let t=process.env.WEPPY_ROBLOX_MCP_VERSION?.trim();return t||null}var it=tle()??"2.7.18";ep();function Lv(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 wU(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),g.debug("Removed stale MCP instance",{instanceId:i,lastSeen:r.lastSeen}))}function kU(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"){g.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}),g.debug("Selection cache updated",{count:r.count,clientId:a,timestamp:o}),n.json({status:"ok",timestamp:o})}catch(r){g.error("Error handling selection update",r),n.status(500).json({error:"Internal server error"})}}function CU(t,e,n){let i=parseInt(e.query.maxAge)||3e4,r=tp(t,i);r?n.json({cached:!0,...r}):n.json({cached:!1,message:"No cached selection available"})}function tp(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 PP(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)&&tP(t,r.placeId,r.placeName??null),t.analyticsManager&&(e.pluginVersion&&t.analyticsManager.setPluginVersion(e.pluginVersion),t.analyticsManager.trackPluginConnected()),g.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:Lv(t)}}function EP(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"}),Sc(t,{timestamp:new Date().toISOString(),type:"plugin",status:"disconnected",clientId:e,message:`Plugin disconnected \u2014 ${e}`})),g.info("Plugin client unregistered",{clientId:e,existed:i}),i}function Uv(t,e,n){try{let i=e.body;if(!i.clientId){n.status(400).json({error:"Missing clientId"});return}let r=PP(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){g.error("Error registering plugin client",i),n.status(500).json({error:"Internal server error"})}}function Fv(t,e,n){let i=e.body?.clientId;if(!i){n.status(400).json({error:"Missing clientId"});return}let r=EP(t,i);n.json({status:"ok",existed:r})}function IU(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"}),Sc(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}:{}}),g.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){g.error("Error registering MCP instance",i),n.status(500).json({error:"Internal server error"})}}function PU(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"}),Sc(t,{timestamp:new Date().toISOString(),type:"mcp",status:"unregistered",instanceId:i,message:`MCP unregistered \u2014 ${r?.aiClientName??i}`,...r?.aiClientName?{aiClientName:r.aiClientName}:{}})),g.info("MCP instance unregistered",{instanceId:i,existed:a}),n.json({status:"ok",existed:a})}function EU(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 RU(t){let e=3e4,n=Date.now(),i=ir({appDataDir:t.config.appDataDir});for(let[r,a]of t.pluginClients)n-a.lastSeen>e&&(t.pluginClients.delete(r),t.pendingCommands.delete(r));return wU(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 qv(t,e){e.json(RU(t))}function Bv(t,e,n){let i=e.query.instanceId;i&&t.mcpInstances.has(i)&&(t.mcpInstances.get(i).lastSeen=Date.now()),wU(t);let r=Yi(t),s={...{status:"online",connectedClients:r.length,queuedCommands:S6(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:Lv(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 TU(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")){g.warn("Shutdown request rejected from non-localhost",{ip:r}),n.status(403).json({error:"Forbidden: localhost only"});return}g.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(),g.info("Graceful shutdown completed"),process.exit(0)}catch(o){g.error("Error during graceful shutdown",o),process.exit(1)}},100)}async function Hv(t){if(t.isClientMode)try{let e=await fetch(`${t.baseUrl}/connection-info`);if(e.ok)return await e.json()}catch(e){g.warn("Failed to fetch connection info from server",{error:e})}return RU(t)}var ile="system_info_stop",$U=[5e3,15e3];function DU(t){return t===ile}ie();Xd();var Nc={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 ale={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 Zv(t){return ale[t]||"plugin"}var OU=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"]),RP=new Set(["system_info_connection","system_info_ping","system_info_usage"]);function Fn(t){return OU.has(t)?"pro":"basic"}function ole(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 sle=/^Pro action '[^']+' is blocked in Basic mode\. Basic fallback '([^']+)' failed: (.*)$/s,cle=/^Pro action '[^']+' is blocked in Basic mode\.(?! Basic fallback ')/,lle=/instance not found|not found in|parent not found/,ule=/is required|missing required/,dle=/unknown action|unknown command/,ple=/must be|invalid|cannot/;function fle(t){let e=sle.exec(t);return e?{fallbackCommand:e[1],reason:e[2]}:null}function mle(t){return cle.test(t)}function hle(t){let e=t.toLowerCase();return lle.test(e)?"not_found":ule.test(e)?"missing_param":dle.test(e)?"unknown_action":ple.test(e)?"validation":"other"}function TP(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 jc(t){if(t.resultSuccess){let e=ole(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=fle(t.resultError);if(e)return{kind:"blocked_fallback_failed",fallbackCommand:e.fallbackCommand,blockedDetail:hle(e.reason)};if(mle(t.resultError))return{kind:"blocked_unsupported"}}return t.failureClassification?{kind:TP(t.failureClassification)}:{kind:"error"}}function ve(t,e,n,i,r={}){return{errorType:t,errorDetail:e,failureOrigin:n,recoverability:i,...r}}function gle(t){let e=t.toLowerCase();return/^\s*compilation error:/.test(e)?ve("execution_failed","compile_error","agent_request","code_fix",{validationStage:"plugin_handler"}):/^\s*result too large:/.test(e)?ve("execution_failed","result_too_large","agent_request","code_fix",{validationStage:"plugin_handler"}):/^\s*execution timed out after/.test(e)?ve("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)?ve("execution_failed","blocked_source","roblox_policy","code_fix",{validationStage:"plugin_handler"}):null}function AU(t){let e=t.toLowerCase();return/upstream_server_down/.test(e)?ve("bridge_error","upstream_down","bridge","wait_connection"):/^http [45]\d\d/.test(e)?ve("bridge_error","http_error","bridge","retry"):/duplicate request\s*id/.test(e)?ve("bridge_error","duplicate_request_id","bridge","code_fix"):/timeout/.test(e)?ve("command_timeout","timeout","bridge","retry"):/econnrefused|socket hang up|econnreset|epipe|network/.test(e)?ve("bridge_error","connection","bridge","wait_connection"):/abort/.test(e)?ve("command_timeout","aborted","bridge","retry"):/runtime error|traceback|attempt to/.test(e)?ve("plugin_runtime","runtime_error","plugin_runtime","code_fix"):/created instance, but failed to set \d+ propert/.test(e)?ve("execution_failed","property_apply_failed","agent_request","fix_params",{validationStage:"plugin_handler",partialSuccess:"true"}):/failed to set property/.test(e)?ve("execution_failed","property_apply_failed","agent_request","fix_params",{validationStage:"plugin_handler"}):/parent(?: instance)? not found/.test(e)?ve("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)?ve("execution_failed","not_found","user_project_state","fix_params",{validationStage:"plugin_handler"}):/is required|missing required/.test(e)?ve("execution_failed","missing_param","agent_request","fix_params",{validationStage:"schema"}):/invalid classname|invalid class/.test(e)?ve("execution_failed","invalid_class","agent_request","fix_params",{validationStage:"plugin_handler"}):/unknown action|unknown command/.test(e)?ve("execution_failed","unknown_action","agent_request","fix_params",{validationStage:"dispatcher"}):/failed to insert asset/.test(e)?ve("execution_failed","asset_error","roblox_policy","fix_params",{validationStage:"roblox_api"}):/failed to read property|property access denied/.test(e)?ve("execution_failed","property_error","agent_request","fix_params",{validationStage:"plugin_handler"}):/forbidden (?:path|parent path)|exceeds (?:total lines|max(?:imum)?(?: length)?)|is not (?:a|an) /.test(e)?ve("execution_failed","validation","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)?ve("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)?ve("execution_failed","unsupported_context","roblox_policy","no_retry",{validationStage:"preflight"}):/editableimage api is not enabled|allow mesh \/ image apis|allow http requests/.test(e)?ve("execution_failed","feature_disabled","roblox_policy","no_retry",{validationStage:"preflight"}):/must be|invalid|cannot/.test(e)?ve("execution_failed","validation","agent_request","fix_params",{validationStage:"plugin_handler"}):ve("execution_failed","other","unknown","unknown")}function Mc(t,e){if(t==="execute_luau"){let i=gle(e);if(i)return i}let n=AU(e);return t==="execute_luau"&&n.errorDetail==="runtime_error"&&/^\s*runtime error:/i.test(e)?ve("execution_failed","runtime_error","agent_request","code_fix"):n}function NU(t){let n=(t instanceof Error?t.message:String(t)).toLowerCase();return/timeout/.test(n)?ve("command_timeout","timeout","bridge","retry"):/econnrefused|socket hang up|network|econnreset|epipe/.test(n)?ve("bridge_error","connection","bridge","wait_connection"):/eaddrinuse/.test(n)?ve("bridge_error","port_in_use","bridge","no_retry"):ve("exception","unexpected","server_exception","code_fix")}Gv();import{randomBytes as Sle}from"node:crypto";var wle=["manage_ui_create_tree","manage_ui_update","manage_ui_delete"],BU=["manage_ui_preview","manage_ui_check"],kle=["game_genre","ui_role","style_family","layout_family","device_policy","safe_area_policy","existing_ui_strategy"];function jP(t){return t&&typeof t=="object"?t:void 0}function AP(t){return typeof t=="number"&&Number.isFinite(t)?t:0}function Jv(t){let e=jP(t);if(!e)return;let n={};for(let i of kle){let r=e[i];typeof r=="string"&&r.trim().length>0&&(n[i]=r)}return Object.keys(n).length>0?n:void 0}function NP(t){let e=Jv(t.qualityPlanSummary);if(e)return e;let n=jP(t.contextSummary),i=Jv(n?.qualityPlanSummary);if(i)return i;let r=Jv(n?.qualityPlan);if(r)return r;let a=Jv(n?.quality_plan);if(a)return a}function HU(t,e){return e?{...t??{},...e}:t}function Cle(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 ZU(t,e,n,i,r){if(!e)return;let a=AP(e.priority_high),o=AP(e.priority_medium),s=AP(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 VU(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 Ile(t){let e=jP(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 Ple(){return{source:"unavailable",priority_high:0,priority_medium:0,priority_low:0,total:0}}function WU(t){let e=t.now??Date.now,n=null,i=null,r=l=>`${l}_${e()}_${Sle(3).toString("hex")}`,a=()=>{i&&clearTimeout(i),i=setTimeout(()=>{s("idle_timeout")},t.idleMs)},o=async l=>{try{return Cle(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 y=await o(h);d[h]=y.snapshotId,p=VU(p,ZU("after_snapshot",y.designCheckSummary,h,y.snapshotId,y.capturedAt))}let f=p??u.latestDesignCheckSummary??Ple(),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 UU(t.placeDir,m)},c=l=>({requestId:r("req"),contextId:l.contextId,label:Ile(l.contextSummary),startedAtIso:new Date(e()).toISOString(),startedAtMs:e(),affectedPaths:new Set,beforeSnapshots:{},afterSnapshotsPathPolicy:{},childMutationIds:[],mutationCount:0,ok:!0,qualityPlanSummary:NP(l),latestDesignCheckSummary:void 0,pendingMutation:void 0});return{async beforeMutation(l){if(BU.includes(l.command)){if(n){let d=r("mut");n.qualityPlanSummary=HU(n.qualityPlanSummary,NP(l)),n.pendingMutation={mutationId:d,startedMs:e(),args:l}}return}if(!wle.includes(l.command))return;n&&n.contextId!==l.contextId&&await s("context_change"),n||(n=c(l)),n.qualityPlanSummary=HU(n.qualityPlanSummary,NP(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 FU(t.placeDir,p),n.pendingMutation=void 0,BU.includes(d.command)){n.latestDesignCheckSummary=VU(n.latestDesignCheckSummary,ZU("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 pF=100,uue=12e4,due=15e3,fF=new Set(["manage_ui_create_tree","manage_ui_update","manage_ui_delete"]),pue={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"}},fue={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"},mue=Object.entries(Nc).reduce((t,[e,n])=>{for(let i of n.validActions)t[`${e}_${i}`]=e;return t},{});function oF(t){return{toolName:mue[t]||t,commandName:t}}function sF(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 Xv(t,e){if(t!=="manage_sync_status_current_place")return e;let{placeId:n,...i}=e;return i}async function hue(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 gue(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 yue(t,e){let n=e.targetPath;if(typeof n=="string"&&n.length>0)return n;if(t==="manage_ui_create_tree")return gue(e)}function vue(t){let e=pue[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 cF(t,e){return Object.prototype.hasOwnProperty.call(t,e)}function bue(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=cF(a,"before"),c=cF(a,"after");i.set(o,{...s&&{before:a.before},...c&&{after:a.after},hasBefore:s,hasAfter:c})}return i}function mF(t,e){let i=t.changes?.properties;if(!i||typeof i!="object")return;let r=bue(e),a=Object.entries(i).map(([o,s])=>{let c=vue(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 hF(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?`${fue[r]} ${a}\uAC1C`:null}).filter(r=>r!==null);return i.length>0?`${i.join(", ")} \uBCC0\uACBD`:void 0}function lF(t,e){if(t==="manage_ui_update"){let n=e.changes,i=hF(mF(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 dp(t){return t&&typeof t=="object"?t:void 0}function Qv(t,e){let n=t[e];return typeof n=="number"&&Number.isFinite(n)?n:0}function gF(t){let e=dp(t),n=dp(e?.design_check_summary);if(n)return{priority_high:Qv(n,"priority_high"),priority_medium:Qv(n,"priority_medium"),priority_low:Qv(n,"priority_low"),total:Qv(n,"total"),...typeof n.scope=="string"&&{scope:n.scope},...typeof n.target=="string"&&{target:n.target}}}function yF(t){let n=dp(t)?.snapshot_id;return typeof n=="string"?n:null}function uF(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 vF(t){let e=dp(t),n=dp(e?.meta);return uF(n?.captured_at)??uF(n?.evaluated_at)}function xue(t,e){let n=t.uiStudioRecorders.get(e);if(n)return n;let i=WU({placeDir:e,idleMs:due,capturePreview:async r=>{try{let a=await bo(t,"manage_ui_preview",{targetPath:r},eb());if(!a.success||!a.data||typeof a.data!="object")return null;let o=gF(a.data),s=vF(a.data);return{snapshotId:yF(a.data),...o!==void 0&&{designCheckSummary:o},...s!==void 0&&{capturedAt:s}}}catch{return null}}});return t.uiStudioRecorders.set(e,i),i}async function _ue(t,e,n){if(!fF.has(e))return null;let i=await hue(t,n);if(!i)return null;let r=xue(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=yue(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 g.debug("UI Studio recorder beforeMutation \uC2E4\uD328 (\uBB34\uC2DC)",{error:p}),null}}async function dF(t,e){if(t)try{let n=t.command==="manage_ui_update"?mF(t.params,e.data):void 0,i=t.command==="manage_ui_update"?hF(n)??lF(t.command,t.params):lF(t.command,t.params),r=gF(e.data),a=yF(e.data),o=vF(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){g.debug("UI Studio recorder afterMutation \uC2E4\uD328 (\uBB34\uC2DC)",{error:n})}}async function qP(t,e,n,i,r,a){if(!t.historyManager)return;let o=!r.success&&r.error?Mc(n,r.error):void 0,s=jc({resultSuccess:r.success,resultData:r.data,resultError:r.error,tier:Fn(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,Xv(n,i),l,a,n);return}if(s.kind==="blocked_unsupported"){await t.historyManager.recordBlockedOutcome(e,Xv(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,Xv(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,Xv(n,i),r.error||"Unknown error",a,o?.errorType.toUpperCase(),n)}function bF(t,e,n){if(!e)return;let i=t.pluginClients.get(e);i?.inFlightRequestId===n&&(i.inFlightRequestId=void 0)}function Sue(t,e){for(let[n,i]of t.pendingCommands){let r=i.filter(a=>a.data.requestId!==e);r.length!==i.length&&t.pendingCommands.set(n,r)}t.globalPendingCommands=t.globalPendingCommands.filter(n=>n.data.requestId!==e)}function Yv(t,e,n){let i=t.pendingCommands.get(e)||[];i.length>=pF&&i.shift(),i.push(n),t.pendingCommands.set(e,i)}function wue(t,e){t.globalPendingCommands.length>=pF&&t.globalPendingCommands.shift(),t.globalPendingCommands.push(e)}function xF(t,e,n){let i=eb(),r={requestId:i,command:e.command,params:e.params,timestamp:Date.now(),queueTimeoutMs:e.queueTimeoutMs,ackedTimeoutMs:e.ackedTimeoutMs,state:nn.QUEUED,targetClientId:n,resolve:()=>{},reject:a=>{g.debug("Auxiliary playtest stop command ended without awaited response",{requestId:i,targetClientId:n,error:a.message})},timeoutId:null};return t.commandQueue.set(i,r),r.timeoutId=setTimeout(()=>{t.commandQueue.delete(i),bF(t,n,i),Sue(t,i),r.reject(new Error(`Command timeout after ${r.queueTimeoutMs}ms while ${r.state}: ${r.command}`))},r.queueTimeoutMs),{event:"command",id:i,data:{command:r.command,requestId:i,params:r.params}}}function kue(t,e,n,i){if(!DU(e.data.command))return;let r=t.commandQueue.get(e.data.requestId);if(!r)return;Cue(t,r);let a=n.filter(o=>o.clientId!==i);for(let o of a){let s=xF(t,r,o.clientId);Yv(t,o.clientId,s)}a.length>0&&g.info("Fan-out playtest stop command to active plugin clients",{requestId:e.data.requestId,primaryClientId:i,auxiliaryClientIds:a.map(o=>o.clientId)})}function Cue(t,e){for(let n of $U)setTimeout(()=>{let r=[...new Set(Yi(t).map(a=>a.clientId))];for(let a of r){if(!t.pluginClients.get(a))continue;let s=xF(t,e,a);Yv(t,a,s)}g.info("Retry playtest stop command to active plugin clients",{sourceRequestId:e.requestId,delayMs:n,targetClientIds:r}),Zd(t)},n).unref?.()}function Iue(t,e,n){if(g.debug("Broadcasting command",{command:e.data.command,requestId:e.data.requestId,targetClientId:n||"all",activeWebsocketClients:Yi(t).length}),n)Yv(t,n,e);else{let i=Yi(t).sort((o,s)=>s.lastSeen-o.lastSeen),r=e.data.params?.placeId,a;r!==void 0&&i.length>1&&(a=i.find(o=>o.placeId===r),a&&g.debug("Routed command by placeId",{clientId:a.clientId,placeId:r})),a||(a=i[0],i.length>1&&g.warn("Multiple plugin clients connected, routing to most recent",{targetClientId:a?.clientId,targetPlaceId:a?.placeId,totalClients:i.length,command:e.data.command})),a?(Yv(t,a.clientId,e),kue(t,e,i,a.clientId),g.debug("Routed command to client",{clientId:a.clientId,projectName:a.projectName,placeId:a.placeId})):wue(t,e)}Zd(t,n)}async function _F(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=oF(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(g.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=tp(t,m!==void 0?m:3e4),y=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,y,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,y,c.commandName),n.json({requestId:s,success:!0,data:x});return}if(r==="get_connection_info"){let m=await Hv(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(Zv(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),y=Date.now()-i,v=sF(m,h.data);t.historyManager&&await qP(t,c.toolName,c.commandName,v,{success:h.success,data:h.data,error:h.error||"Internal command failed"},y),n.json({requestId:s,...h});return}let d=await bo(t,r,a,s,l),p=Date.now()-i,f={...sF(a,d.data),__sessionId:u};t.historyManager&&await qP(t,c.toolName,c.commandName,f,d,p),n.json(d)}catch(o){let s=Date.now()-i,c=xn(o);if(t.historyManager&&r){let l=oF(r);await t.historyManager.recordFailure(l.toolName,a,c,s,"EXCEPTION",l.commandName)}g.error("Execute request failed",o),n.status(500).json({success:!1,error:c})}}async function SF(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=eb(),{action:l,...u}=r;try{t.serverLastCommandAt=Date.now();let d=await bo(t,s,u,c),p=Date.now()-i;t.historyManager&&await qP(t,"manage_properties",s,u,d,p),n.json(d)}catch(d){let p=Date.now()-i,f=xn(d);t.historyManager&&await t.historyManager.recordFailure("manage_properties",u,f,p,"EXCEPTION",s),g.error("/api/properties \uC694\uCCAD \uCC98\uB9AC \uC2E4\uD328",d),n.status(500).json({success:!1,error:f})}}async function bo(t,e,n,i,r){if(g.debug("Executing command locally",{command:e,requestId:i}),f6(t,i)){let l=`Duplicate requestId '${i}' for command '${e}'. Generate a unique requestId for each command.`;return g.warn("Duplicate execute request rejected",{command:e,requestId:i}),{requestId:i,success:!1,error:l}}let a=fF.has(e)?await _ue(t,e,n):null,o=t.config.requestTimeout,s=r?.timeout??Math.max(t.config.requestTimeout,uue),c=new Promise((l,u)=>{let d={requestId:i,command:e,params:n,timestamp:Date.now(),queueTimeoutMs:o,ackedTimeoutMs:s,state:nn.QUEUED,resolve:l,reject:u,timeoutId:null};t.commandQueue.set(i,d),d.timeoutId=setTimeout(()=>{t.commandQueue.delete(d.requestId),bF(t,d.targetClientId,d.requestId),d.reject(new Error(`Command timeout after ${o}ms while ${d.state}: ${d.command}`))},o)});return Iue(t,{event:"command",id:eb(),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(()=>(FP(),UP)),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){g.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(()=>(FP(),UP)),{processCheckResult:p}=await Promise.resolve().then(()=>(aF(),iF)),f=await d(t,n),m=await p("manage_ui_check",l.data,f,n);u={...u,data:m.data}}catch(d){g.warn("check visual post-processing failed",{error:d instanceof Error?d.message:String(d)})}return u}).then(async l=>(await dF(a,{success:l.success,...l.data!==void 0&&{data:l.data},...l.error!==void 0&&{error:l.error}}),l),async l=>{throw await dF(a,{success:!1,error:xn(l)}),l})}ie();function tb(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 Or(t,e,n,i){t.status(e).json({success:!1,error:{code:n,message:i}})}function Pue(t){if(typeof t!="string")return;let e=t.trim().toLowerCase();return e.length>0?e:void 0}function nb(t,e){if(e==="plugin")return t;let n={...t};return delete n.sessionToken,n}function pp(t,e,n,i){let r=Pue(i??e.query.provider)??"auto";return t.licenseState?.supportsProvider(r)===!1?(Or(n,400,"LICENSE_PROVIDER_UNSUPPORTED",`provider "${r}" is not supported`),null):r}function fp(t,e){return t.licenseState?!0:(Or(e,503,"LICENSE_NOT_INITIALIZED","License system is not initialized"),!1)}function Eue(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 mp(t,e,n,i="plugin"){if(!fp(t,n))return;let r=e.body,a=typeof r?.licenseKey=="string"?r.licenseKey:"";if(!a.trim()){Or(n,400,"LICENSE_KEY_REQUIRED","licenseKey is required");return}let o=pp(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"),tb(t,{provider:u.provider,tier:u.canUsePro?"pro":"basic",status:u.status,canUsePro:u.canUsePro}),n.json(nb(u,i))}).catch(u=>{g.warn("License activate failed",{error:u instanceof Error?u.message:"unknown_error"}),Or(n,502,"LICENSE_ACTIVATE_FAILED",u instanceof Error?u.message:"License activation failed")})}function hp(t,e,n,i="plugin"){if(!fp(t,n))return;let r=pp(t,e,n);if(r)try{let a=t.licenseState.getStatus(r);t.analyticsManager?.setTier(a.canUsePro?"pro":"basic"),n.json(nb(a,i))}catch(a){g.warn("License status check failed",{error:a instanceof Error?a.message:"unknown_error"}),Or(n,502,"LICENSE_STATUS_FAILED",a instanceof Error?a.message:"License status check failed")}}function gp(t,e,n,i="plugin"){if(!fp(t,n))return;let r=e.body,a=pp(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"),tb(t,{provider:s.provider,tier:s.canUsePro?"pro":"basic",status:s.status,canUsePro:s.canUsePro}),n.json(nb(s,i))}).catch(s=>{g.warn("License refresh failed",{error:s instanceof Error?s.message:"unknown_error"}),Or(n,502,"LICENSE_REFRESH_FAILED",s instanceof Error?s.message:"License refresh failed")})}function yp(t,e,n,i="plugin"){if(!fp(t,n))return;let r=e.body,a=pp(t,e,n,r?.provider);a&&t.licenseState.resetGateway({provider:a,clientType:i}).then(o=>{t.analyticsManager?.setTier("basic"),tb(t,{provider:o.provider,tier:"basic",status:o.status,canUsePro:o.canUsePro,cleared:!0}),n.json(nb(o,i))}).catch(o=>{g.warn("License reset failed",{error:o instanceof Error?o.message:"unknown_error"}),Or(n,502,"LICENSE_RESET_FAILED",o instanceof Error?o.message:"License reset failed")})}function BP(t,e,n){if(!fp(t,n))return;let i=e.body,r=pp(t,e,n,i?.provider);if(!r)return;if(i?.clientType!=="plugin"){Or(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=Eue(i?.snapshot)?i.snapshot:void 0;if(!o&&!a){Or(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"),tb(t,{provider:c.provider,tier:c.canUsePro?"pro":"basic",status:c.status,canUsePro:c.canUsePro}),n.json({ok:!0})}).catch(c=>{g.warn("License bootstrap failed",{error:c instanceof Error?c.message:"unknown_error"}),Or(n,502,"LICENSE_BOOTSTRAP_FAILED",c instanceof Error?c.message:"License bootstrap failed")})}function wF(t){t.app.post("/license/bootstrap",(e,n)=>BP(t,e,n)),t.app.post("/license/activate",(e,n)=>mp(t,e,n,"plugin")),t.app.post("/license/refresh",(e,n)=>gp(t,e,n,"plugin")),t.app.post("/license/reset",(e,n)=>yp(t,e,n,"plugin")),t.app.get("/license/status",(e,n)=>hp(t,e,n,"plugin"))}Nt();ie();import{promises as rb}from"fs";import HP from"path";function Rue(t){let e=t.config.appDataDir??un();return HP.join(e,"observability","logs")}async function qc(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)){g.warn("Invalid logs request",{body:r}),n.status(400).json({error:"Invalid request body"});return}let a=Rue(t);await rb.mkdir(a,{recursive:!0});let c=`plugin-${new Date().toISOString().split("T")[0]}.log`,l=HP.join(a,c),u=HP.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 rb.appendFile(l,d,"utf-8");try{let p="";try{p=await rb.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.
|
|
3
|
+
"version": "2.7.18",
|
|
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
|