@weppy/roblox-mcp 2.1.0 → 2.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +16 -0
  3. package/docs/assets/screenshots/plugin/sync/sync-main.png +0 -0
  4. package/install.ps1 +164 -22
  5. package/install.sh +82 -9
  6. package/package.json +1 -1
  7. package/plugins/weppy-roblox-mcp/.claude-plugin/plugin.json +1 -1
  8. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{ChangelogDetailPage-lzDR6yjY.js → ChangelogDetailPage-2ZPwh_Le.js} +1 -1
  9. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/ChangelogPage-DW2_STiE.js +1 -0
  10. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{ConfirmModal-BIJpNntn.js → ConfirmModal-CInBBLZW.js} +1 -1
  11. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{ConnectionPage-CdLqNY4r.js → ConnectionPage-nNwPIEZw.js} +1 -1
  12. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{InfoLabel-CBAqTqRy.js → InfoLabel-B_Ys60mi.js} +1 -1
  13. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/OverviewPage-BMBzDR4R.js +1 -0
  14. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/PlaytestPage-BDgsWgto.js +11 -0
  15. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/PlaytestPage-CYSu0pfO.css +1 -0
  16. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{PropertyDiff-D34Apw3G.js → PropertyDiff-BbsF9z4D.js} +1 -1
  17. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{SettingsPage-yWzPPZIB.js → SettingsPage-BPSAA3lu.js} +1 -1
  18. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{StatusBadge-B-pYMpUG.js → StatusBadge-FYpJOX7r.js} +1 -1
  19. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{SyncPage-E4XZhZn0.js → SyncPage-BcXYv0GI.js} +2 -2
  20. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/TierComparison-D4uVUGcZ.js +1 -0
  21. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/TierComparison-DGh9vLz0.css +1 -0
  22. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/TierPromo-D5n9OoEm.css +1 -0
  23. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/TierPromo.module-CAoUYgIx.js +1 -0
  24. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/TierPromoProgress-Cuz1dgcs.js +1 -0
  25. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/ToolsPage-YGBoDwbQ.js +1 -0
  26. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{index-E1cuNPsJ.js → index-BNdn6WpU.js} +18 -18
  27. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{useLiveUptime-qJDofPOo.js → useLiveUptime-D5Ux8dA5.js} +1 -1
  28. package/plugins/weppy-roblox-mcp/dashboard/dist/index.html +1 -1
  29. package/plugins/weppy-roblox-mcp/dist/index.js +4 -4
  30. package/plugins/weppy-roblox-mcp/roblox-plugin/WeppyRobloxMCP.rbxm +0 -0
  31. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/ChangelogPage-BscI67Aj.js +0 -1
  32. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/OverviewPage-BOm0Ai9y.js +0 -1
  33. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/PlaytestPage-DaDbQ6Qa.js +0 -11
  34. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/PlaytestPage-Dw8nj399.css +0 -1
  35. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/TierPromo-CcQarFkP.css +0 -1
  36. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/TierPromo.module-BBX3CVXn.js +0 -1
  37. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/TierPromoProgress-CqxKoPOJ.js +0 -1
  38. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/ToolsPage-yaahWgTq.js +0 -1
  39. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/tier-promo-config-BuGSENUv.js +0 -1
  40. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/tier-promo-config-C-Xy3iYW.css +0 -1
@@ -123,7 +123,7 @@ data: ${JSON.stringify(n)}
123
123
  `).filter(g=>g.length>0)}catch(h){if(h.code==="ENOENT"){n.status(200).json({entries:[],total:0,hasMore:!1});return}throw h}let p=[];for(let h=u.length-1;h>=0;h--)try{let g=JSON.parse(u[h]);if(o&&g.direction!==o||s&&g.type!==s)continue;p.push(g)}catch{continue}let d=p.length,m={entries:p.slice(a,a+i),total:d,hasMore:a+i<d};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 i=this.ctx.getDefaultRuntimePlaceId();i!=null&&(n=this.ctx.places.get(i))}if(!n)return"forward";let r=e;return n.directions[r]??"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:{...Yi}};let r=this.ctx.places.get(n);if(!r)return{state:"idle",instanceCount:0,scriptCount:0,lastFullSync:null,lastIncrementalSync:null,syncRoot:this.ctx.config.getPlaceRoot(n),activeClientId:null,reverseSyncAvailable:!1,modifiedFileCount:0,applyModes:{...Yi}};let i=r.fileWatcher?.getPendingCount()??0;return{state:r.state,instanceCount:r.instanceCount,scriptCount:r.scriptCount,lastFullSync:r.lastFullSync,lastIncrementalSync:r.lastIncrementalSync,syncRoot:this.ctx.config.getPlaceRoot(n),activeClientId:r.activeClientId,reverseSyncAvailable:i>0,modifiedFileCount:i,applyModes:{...r.applyModes}}}getConfigDirect(){return this.ctx.config.getConfig()}async getHistoryDirect(e,n){let r=parseInt(e,10),i=this.ctx.places.get(r);i&&await i.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(r),c=[];try{c=(await Zo.readFile(s,"utf-8")).split(`
124
124
  `).filter(f=>f.length>0)}catch(d){if(d.code==="ENOENT")return{entries:[],total:0,hasMore:!1};throw d}let l=[];for(let d=c.length-1;d>=0;d--)try{l.push(JSON.parse(c[d]))}catch{continue}let 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{...Xi};let r=this.ctx.places.get(n);return r?{...r.directions}:{...Xi}}getProgressDirect(e){let n=e!=null?parseInt(e,10):this.ctx.getDefaultRuntimePlaceId();if(n==null)return{state:"idle",isSyncing:!1};let r=this.ctx.places.get(n);if(!r)return{state:"idle",isSyncing:!1};if(!r.syncProgress)return{state:r.state,isSyncing:!1,lastSync:{instanceCount:r.instanceCount,scriptCount:r.scriptCount,completedAt:r.lastFullSync}};let i=r.syncProgress,a=Date.now()-i.syncStartTime,o=i.totalInstances>0?Math.min(100,Math.round(i.processedInstances/i.totalInstances*100)):0,s;if(i.processedInstances>0&&o<100){let l=a/i.processedInstances,u=i.totalInstances-i.processedInstances;s=Math.round(l*u)}let c=a>0?Math.round(i.bytesReceived/a*1e3):0;return{state:r.state,isSyncing:!0,progressPercent:o,currentService:i.currentService,currentChunk:{index:i.currentChunkIndex,total:i.currentTotalChunks},instances:{processed:i.processedInstances,total:i.totalInstances},services:{processed:i.processedServices,total:i.totalServices},elapsedMs:a,estimatedRemainingMs:s,bytesReceived:i.bytesReceived,bytesPerSecond:c}}async readSyncedFile(e,n){let r=parseInt(e,10),i=this.ctx.places.get(r);if(!i)throw new Error(`Place ${e} not found in sync cache`);let a=i.index.resolvePropsPath(n);try{return{content:await Zo.readFile(a,"utf-8"),path:a}}catch(s){if(s.code!=="ENOENT")throw s}for(let s of Oo){let c=i.index.resolveScriptPath(n,s,!1);try{return{content:await Zo.readFile(c,"utf-8"),path:c}}catch{continue}}let o=i.index.resolveValuePath(n);try{return{content:await Zo.readFile(o,"utf-8"),path:o}}catch(s){if(s.code!=="ENOENT")throw s}throw new Error(`No synced file found for instance: ${n}`)}async writeSyncedFile(e,n,r){let i=parseInt(e,10),a=this.ctx.places.get(i);if(!a)throw new Error(`Place ${e} not found in sync cache`);await a.writer.writeScript(n,"Script",r,!1)}async executeViaDisk(e,n){let r=this.ctx.getDefaultRuntimePlaceId();if(r==null)throw new Error("No active sync place for disk execution");let i=this.ctx.places.get(r);if(!i)throw new Error(`Place ${r} not found in sync cache`);switch(e){case"set_script_source":{let a=n.scriptType||n.className||"Script";return await i.writer.writeScript(n.path,a,n.source,!1),{success:!0,path:n.path}}case"set_property":return await i.writer.writeProps(n.path,{className:n.className||"Instance",name:Rt(n.path),properties:{[n.property]:n.value}}),{success:!0,path:n.path};default:throw new Error(`Disk execution not supported for action: ${e}`)}}};import Ce from"path";import{randomUUID as dee}from"crypto";import{promises as nn}from"fs";pe();var Of=class{constructor(e){this.ctx=e}preserveLocalFilesMap=new Map;pendingServiceTrees=new Map;async handleInitStart(e,n,r){if(n.previousPlaceId!==void 0&&n.previousPlaceId!==e&&(y.info("Place promotion detected",{from:n.previousPlaceId,to:e}),await this.ctx.config.promotePlaceRoot(n.previousPlaceId,e,n.placeName),this.ctx.places.get(n.previousPlaceId)&&this.ctx.places.delete(n.previousPlaceId),this.ctx.activeFullSyncPlaceId===n.previousPlaceId&&(this.ctx.activeFullSyncPlaceId=null),this.ctx.clearRuntimePlaceIfMatch(n.previousPlaceId)),this.ctx.activeFullSyncPlaceId!==null&&this.ctx.activeFullSyncPlaceId!==void 0&&this.ctx.activeFullSyncPlaceId!==e){r.status(409).json({error:"Conflict",message:`Place ${this.ctx.activeFullSyncPlaceId} is currently syncing. Only one place can sync at a time.`});return}let i=await this.ctx.getOrCreatePlaceContext(e,n.placeName);if(i.activeClientId&&i.activeClientId!==n.clientId&&i.activeFullSyncSessionId!==null){r.status(409).json({error:"Conflict",message:`Another client (${i.activeClientId}) is currently syncing this place`});return}if(this.ctx.activeFullSyncPlaceId=e,this.ctx.touchRuntimePlace(e),i.activeClientId=n.clientId,i.placeName=n.placeName,n.directions&&typeof n.directions=="object"){let p=n.directions,d=f=>f==="forward"||f==="reverse"||f==="bidirectional";d(p.scripts)&&(i.directions.scripts=p.scripts),d(p.values)&&(i.directions.values=p.values),d(p.containers)&&(i.directions.containers=p.containers),d(p.data)&&(i.directions.data=p.data),d(p.services)&&(i.directions.services=p.services),y.info("Sync directions received",{placeId:e,directions:i.directions})}else i.directions={...Xi};if(n.applyModes&&typeof n.applyModes=="object"){let p=n.applyModes,d=f=>f==="auto"||f==="manual";d(p.scripts)&&(i.applyModes.scripts=p.scripts),d(p.values)&&(i.applyModes.values=p.values),d(p.containers)&&(i.applyModes.containers=p.containers),d(p.data)&&(i.applyModes.data=p.data),d(p.services)&&(i.applyModes.services=p.services)}else i.applyModes={...Yi};i.forwardRestoreQueue=[];let a=dee();i.activeFullSyncSessionId=a,this.pendingServiceTrees.set(a,new Map),i.instanceCount=0,i.scriptCount=0;let o=this.ctx.config.getPlaceRoot(e),s=Ce.join(o,`explorer_tmp_${a}`);await nn.mkdir(s,{recursive:!0}),i.tmpIndex=new ai(o,s),i.tmpWriter=new Mo(this.ctx.config,i.tmpIndex,e),i.collisionDirMap=new Map;let c=n.preserveLocalFiles;Array.isArray(c)&&c.length>0&&(this.setPreserveLocalFiles(a,c),y.info("PreserveLocalFiles set for sync",{syncId:a,fileCount:c.length}));let l={version:1,placeId:n.placeId,placeName:n.placeName,lastFullSync:null,lastIncrementalSync:null,instanceCount:0,scriptCount:0,syncMode:"mirror"},u=Ce.join(o,".sync-meta.json");await nn.mkdir(Ce.dirname(u),{recursive:!0}),await this.ctx.atomicWriteFile(u,JSON.stringify(l,null,2)+`
125
125
  `),this.startTTLTimerForPlace(i,a),i.state="initializing",i.index.resetNameCounters(),i.syncProgress={syncStartTime:Date.now(),totalInstances:n.totalInstances,totalServices:n.totalServices,processedInstances:0,processedServices:0,currentService:null,currentChunkIndex:0,currentTotalChunks:0,bytesReceived:0,processedChunks:0},i.writer.appendChangeLog(`FULL_SYNC_START clientId=${n.clientId} placeId=${n.placeId}`),i.writer.appendHistory({timestamp:new Date().toISOString(),type:"fullSyncStart",direction:"forward",path:`place_${n.placeId}`,details:`services:${n.totalServices} instances:${n.totalInstances}`}),y.info("Full sync started",{syncId:a,clientId:n.clientId,placeId:n.placeId,placeName:n.placeName,totalServices:n.totalServices,totalInstances:n.totalInstances}),r.status(200).json({status:"started",syncId:i.activeFullSyncSessionId})}async handleInitChunk(e,n,r){let i=this.ctx.places.get(e);if(!i||!i.activeFullSyncSessionId||!i.tmpIndex||!i.tmpWriter){r.status(400).json({error:"No active sync session",message:"Call sync/init with phase=start first"});return}if(i.activeClientId&&n.clientId&&i.activeClientId!==n.clientId){r.status(409).json({error:"Conflict",message:`This sync session belongs to client ${i.activeClientId}`});return}let a=i.activeFullSyncSessionId,o=this.getOrCreatePendingServiceTree(a,n),s=0,c=i.collisionDirMap;for(let u of n.instances){if(this.ctx.config.isForbiddenPath(u.path))continue;let p=i.tmpIndex.resolveParentDir(u.path),d=c.get(p)??p,{resolved:f,retroactiveRename:m}=i.tmpIndex.registerCollision(u.path,u.siblingIndex??void 0,d);m&&(await oi(i.tmpIndex,d,m.from,m.to),this.rewritePendingEffectivePaths(o,i.tmpIndex.getExplorerRoot(),Ce.join(d,m.from),Ce.join(d,m.to)));let h=i.tmpIndex.resolveChildrenDir(u.path),g=Ce.join(d,f);g!==h&&c.set(h,g);let x=i.tmpIndex.sanitizeName(u.name),w=u;if(d!==p||f!==x){let k=i.tmpIndex.getExplorerRoot(),I=Ce.relative(k,d).split(Ce.sep).filter(H=>H.length>0),D=Ye(["game",...I,f]);w={...u,path:D}}let _=await i.tmpWriter.writeInstance(w);i.tmpIndex.setClassName(u.path,u.className,u.siblingIndex),s++,(_.propsWritten||_.valueWritten)&&i.instanceCount++,_.scriptWritten&&i.scriptCount++,o.instances.push({effectivePath:w.path,originalPath:u.path,className:u.className})}let l=this.isLastChunk(o,n.chunkIndex,n.totalChunks);if(l){let u=this.buildServiceTree(i,o);await i.tmpWriter.writeTree(n.serviceName,u);let p=this.pendingServiceTrees.get(a);p?.delete(n.serviceName),p&&p.size===0&&this.pendingServiceTrees.delete(a)}i.syncProgress&&(i.syncProgress.processedInstances+=s,i.syncProgress.currentService=n.serviceName,i.syncProgress.currentChunkIndex=n.chunkIndex,i.syncProgress.currentTotalChunks=n.totalChunks,i.syncProgress.processedChunks++,i.syncProgress.bytesReceived+=JSON.stringify(n).length,l&&i.syncProgress.processedServices++),y.debug("Sync chunk processed",{placeId:e,serviceName:n.serviceName,chunkIndex:n.chunkIndex,totalChunks:n.totalChunks,processed:s}),r.status(200).json({processed:s,service:n.serviceName})}async handleInitComplete(e,n,r){let i=this.ctx.places.get(e);if(!i||!i.activeFullSyncSessionId){r.status(400).json({error:"No active sync session",message:"Call sync/init with phase=start first"});return}if(i.activeClientId&&n.clientId&&i.activeClientId!==n.clientId){r.status(409).json({error:"Conflict",message:`This sync session belongs to client ${i.activeClientId}`});return}let a=i.activeFullSyncSessionId,o=this.ctx.config.getPlaceRoot(e),s=Ce.join(o,"explorer"),c=Ce.join(o,`explorer_tmp_${a}`),l=this.getAndClearPreserveLocalFiles(a),u=new Map,p=[];if(l.length>0){for(let m of l){let h=Ce.resolve(o,m);try{let g=await nn.readFile(h,"utf-8");u.set(m,g)}catch{p.push(m)}}y.info("Backed up local files for preservation",{placeId:e,requested:l.length,backed:u.size,deleted:p.length})}try{await nn.rm(s,{recursive:!0,force:!0})}catch{}await nn.rename(c,s),i.instanceCount=n.instanceCount,i.scriptCount=n.scriptCount,i.lastFullSync=new Date().toISOString();let d={version:1,placeId:i.placeId,placeName:i.placeName,lastFullSync:i.lastFullSync,lastIncrementalSync:i.lastIncrementalSync,instanceCount:i.instanceCount,scriptCount:i.scriptCount,syncMode:"mirror"},f=Ce.join(o,".sync-meta.json");if(await this.ctx.atomicWriteFile(f,JSON.stringify(d,null,2)+`
126
- `),this.clearTTLTimerForPlace(i,a),i.index.clearAllHashes(),i.index.clearClassMappings(),i.tmpIndex){let m=i.tmpIndex.getExplorerRoot(),h=i.index.getExplorerRoot();for(let[g,x]of i.tmpIndex.getAllHashes()){let w=Ce.relative(m,g),_=Ce.resolve(h,w);i.index.updateHashByValue(_,x)}for(let[g,x]of i.tmpIndex.getAllFileHashes()){let w=Ce.relative(m,g),_=Ce.resolve(h,w);i.index.updateFileHashByValue(_,x)}i.index.resetNameCounters(),i.index.mergeNameMappingsFrom(i.tmpIndex)}if(i.tmpIndex=null,i.tmpWriter&&(i.tmpWriter.stopChangeLogFlusher(),i.tmpWriter=null),this.pendingServiceTrees.delete(a),i.collisionDirMap=null,await i.index.saveToDisk(),i.state="syncing",i.activeFullSyncSessionId=null,i.syncProgress=null,this.ctx.touchRuntimePlace(e),this.ctx.activeFullSyncPlaceId=null,await this.ctx.startFileWatcherForPlace(i),i.fileWatcher&&await i.fileWatcher.waitUntilReady(),u.size>0){for(let[m,h]of u){let g=Ce.resolve(o,m);try{await nn.mkdir(Ce.dirname(g),{recursive:!0}),await nn.writeFile(g,h,"utf-8")}catch(x){y.warn("Failed to restore preserved file",{path:m,error:x instanceof Error?x.message:String(x)})}}y.info("Restored preserved local files",{placeId:e,count:u.size})}if(p.length>0){let m=0;for(let h of p){let g=Ce.resolve(o,h);try{await nn.unlink(g),i.index.removeHash(g),m++}catch(x){x.code!=="ENOENT"&&y.warn("Failed to delete preserved-as-deleted file",{path:h,error:x instanceof Error?x.message:String(x)})}}m>0&&(await i.index.saveToDisk(),y.info("Deleted locally-removed files from new sync",{placeId:e,count:m}))}i.writer.appendChangeLog(`FULL_SYNC_COMPLETE instances=${i.instanceCount} scripts=${i.scriptCount}`),i.writer.appendHistory({timestamp:new Date().toISOString(),type:"fullSyncComplete",direction:"forward",path:`place_${e}`,details:`instances:${i.instanceCount} scripts:${i.scriptCount}`}),y.info("Full sync completed",{placeId:e,instanceCount:i.instanceCount,scriptCount:i.scriptCount}),r.status(200).json({status:"completed",instanceCount:i.instanceCount,scriptCount:i.scriptCount,syncRoot:this.ctx.config.getPlaceRoot(e)})}setPreserveLocalFiles(e,n){this.preserveLocalFilesMap.set(e,n)}getAndClearPreserveLocalFiles(e){let n=this.preserveLocalFilesMap.get(e)||[];return this.preserveLocalFilesMap.delete(e),n}clearPreserveLocalFiles(e){this.preserveLocalFilesMap.delete(e)}clearPendingServiceTrees(e){this.pendingServiceTrees.delete(e)}startTTLTimerForPlace(e,n){let r=setTimeout(async()=>{y.warn("Incomplete sync TTL expired, cleaning up",{placeId:e.placeId,syncId:n});let i=this.ctx.config.getPlaceRoot(e.placeId),a=Ce.join(i,`explorer_tmp_${n}`);try{await nn.rm(a,{recursive:!0,force:!0})}catch(o){y.error("Failed to clean up expired temp dir",o instanceof Error?o:new Error(String(o)))}e.incompleteSyncTimer=null,e.activeFullSyncSessionId===n&&(e.activeFullSyncSessionId=null,e.activeClientId=null,e.state="idle",e.tmpIndex=null,this.pendingServiceTrees.delete(n),e.collisionDirMap=null,e.tmpWriter&&(e.tmpWriter.stopChangeLogFlusher(),e.tmpWriter=null),this.ctx.activeFullSyncPlaceId===e.placeId&&(this.ctx.activeFullSyncPlaceId=null),this.ctx.clearRuntimePlaceIfMatch(e.placeId))},zO);r&&typeof r=="object"&&"unref"in r&&r.unref(),e.incompleteSyncTimer=r}getOrCreatePendingServiceTree(e,n){let r=this.pendingServiceTrees.get(e);r||(r=new Map,this.pendingServiceTrees.set(e,r));let i=r.get(n.serviceName);if(i)return i;let a={serviceName:n.tree?.name??n.serviceName,serviceClassName:n.tree?.className??n.serviceClassName,zeroBasedChunkIndex:n.chunkIndex===0,instances:[]};return r.set(n.serviceName,a),a}isLastChunk(e,n,r){return r<=1?!0:e.zeroBasedChunkIndex?n>=r-1:n>=r}buildServiceTree(e,n){let r={name:n.serviceName,className:n.serviceClassName,childCount:0,children:[],syncedAt:new Date().toISOString()};for(let i of n.instances){let a=this.resolveEffectiveSegments(e,i.effectivePath),o=gt(i.originalPath);this.upsertTreeNode(r,a,o,i.className)}return this.recomputeTreeChildCounts(r),r.syncedAt=new Date().toISOString(),r}resolveEffectiveSegments(e,n){return e.tmpIndex?gt(n).map(r=>e.tmpIndex.sanitizeName(r)):gt(n)}rewritePendingEffectivePaths(e,n,r,i){let a=Ce.relative(n,r).split(Ce.sep).filter(l=>l.length>0),o=Ce.relative(n,i).split(Ce.sep).filter(l=>l.length>0);if(a.length===0||o.length===0)return;let s=Ye(["game",...a]),c=Ye(["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,r,i){if(n.length<=1)return;let a=e.children;for(let o=1;o<n.length;o++){let s=n[o],c=r[o],l=o===n.length-1,u=a.find(p=>p.name===s);u?l&&(u.className=i,c!==void 0&&c!==s&&(u.originalName=c)):(u={name:s,className:l?i:"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=r=>{let i=r.children??[];r.children=i;for(let a of i)n(a);r.childCount=i.length};for(let r of e.children)n(r);e.childCount=e.children.length}clearTTLTimerForPlace(e,n){e.incompleteSyncTimer&&e.activeFullSyncSessionId===n&&(clearTimeout(e.incompleteSyncTimer),e.incompleteSyncTimer=null)}async cleanupStaleTempDirs(){let e=this.ctx.config.getSyncRoot();try{let n=await nn.readdir(e,{withFileTypes:!0});for(let r of n)if(r.isDirectory()){if(r.name.startsWith("explorer_tmp_")){let i=Ce.join(e,r.name);y.warn("Removing stale temp directory from crashed sync",{dir:r.name});try{await nn.rm(i,{recursive:!0,force:!0})}catch(a){y.error(`Failed to remove stale temp dir: ${r.name}`,a instanceof Error?a:new Error(String(a)))}}if(r.name.startsWith("place_")){let i=Ce.join(e,r.name);try{let a=await nn.readdir(i,{withFileTypes:!0});for(let o of a)if(o.isDirectory()&&o.name.startsWith("explorer_tmp_")){let s=Ce.join(i,o.name);y.warn("Removing stale temp directory from crashed sync",{dir:`${r.name}/${o.name}`});try{await nn.rm(s,{recursive:!0,force:!0})}catch(c){y.error(`Failed to remove stale temp dir: ${r.name}/${o.name}`,c instanceof Error?c:new Error(String(c)))}}}catch{continue}}}}catch(n){if(n.code==="ENOENT")return;y.warn("Failed to scan for stale temp dirs",{error:n instanceof Error?n.message:String(n)})}}};import tr from"path";import{promises as BO}from"fs";pe();var Df=class{constructor(e){this.ctx=e}async handleReversePending(e,n){let r=this.ctx.resolveQueryPlaceId(e);if(r==null){n.status(200).json({pending:0,hasConflicts:!1,lastDetected:null});return}let i=this.ctx.places.get(r);if(!i){n.status(404).json({error:"Place not found",message:`No sync context for place ${r}`});return}let a={pending:i.fileWatcher?.getPendingCount()??0,hasConflicts:!1,lastDetected:i.fileWatcher?.getLastDetected()??null,forwardRestoreNeeded:i.forwardRestoreQueue.length};this.ctx.touchRuntimePlace(r),n.status(200).json(a)}async handleReverseSyncChanges(e,n){let r=this.ctx.resolveQueryPlaceId(e);if(r==null){n.status(200).json({changes:[],count:0});return}let i=this.ctx.places.get(r);if(!i){n.status(404).json({error:"Place not found",message:`No sync context for place ${r}`});return}if(i.state!=="syncing"){n.status(400).json({error:"Not syncing",message:"Reverse sync is only available when sync is active"});return}let a=i.fileWatcher?.drainPendingChanges()??[],o=a.length>0?await i.reader.buildChangesFromPending(a):[];this.ctx.touchRuntimePlace(r),n.status(200).json({changes:o,count:o.length})}async handleReverseSyncResult(e,n){let r=e.body,i=r.placeId??this.ctx.getDefaultRuntimePlaceId();if(i==null){n.status(400).json({error:"Validation error",message:"placeId is required (in body or via active sync session)"});return}let a=r.appliedFiles??r.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(i);if(!o){n.status(404).json({error:"Place not found",message:`No sync context for place ${i}`});return}this.ctx.touchRuntimePlace(i);let s=this.ctx.config.getPlaceRoot(i),c=0,l=[];for(let u of a){let p=tr.resolve(s,u);if(!jf(s,p)){l.push({path:u,error:"Path is outside the place root"});continue}try{let d=await BO.readFile(p,"utf-8"),f=o.index.computeHash(d);o.index.updateHashByValue(p,f),o.index.updateFileHashByValue(p,f),c++}catch(d){let f=d.code;if(f==="ENOENT"){o.index.removeHash(p),o.index.removeHashesUnder(p);let m=this.resolveInstancePathForAppliedPath(o.index,p);if(m){let h=Rt(m),g=jt(m);g&&h&&await o.writer.removeFromTree(g,h)}c++}else f==="EISDIR"?c++:l.push({path:u,error:d instanceof Error?d.message:String(d)})}}c>0&&await o.index.saveToDisk(),c>0&&o.writer.appendHistory({timestamp:new Date().toISOString(),type:"reverseApply",direction:"reverse",path:`place_${i}`,details:`applied:${c} failed:${l.length}`}),n.status(200).json({updated:c,failed:l.length,errors:l})}async handleResolveConflict(e,n){let r=e.body;if(!r.fsPath||!r.resolution){n.status(400).json({error:"Validation error",message:"fsPath and resolution are required"});return}let{fsPath:i,resolution:a}=r,o=this.ctx.config.getSyncRoot();if(!jf(o,tr.resolve(o,i))){n.status(403).json({error:"Forbidden",message:"Path is outside the sync root"});return}if(a==="skip"){n.status(200).json({status:"skipped",fsPath:i});return}let s;if(r.placeId&&(s=this.ctx.places.get(r.placeId)),!s){let d=i.match(/^place_(\d+)(?:_[^/]+)?\//);if(d){let f=parseInt(d[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=tr.resolve(c,i);if(!jf(c,l)){n.status(403).json({error:"Forbidden",message:"Path is outside the place root"});return}let u=s.index,p=s.reader;if(a==="apply-studio"){u.resolveFile(l,"apply-studio"),await u.saveToDisk(),n.status(200).json({status:"resolved",resolution:"apply-studio",fsPath:i});return}if(a==="apply-file"){let d=await BO.readFile(l,"utf-8"),f=u.computeHash(d);u.resolveFile(l,"apply-file",f),await u.saveToDisk();let m=p.getFileType(l),h=p.resolveInstancePathFromFile(l);n.status(200).json({status:"resolved",resolution:"apply-file",fsPath:i,instancePath:h,fileType:m,content:d});return}n.status(400).json({error:"Invalid resolution",message:`Unknown resolution: ${a}`})}async handleReverseRescan(e,n){let r=this.ctx.resolveQueryPlaceId(e);if(r==null){n.status(200).json({added:0});return}let i=this.ctx.places.get(r);if(!i){n.status(404).json({error:"Place not found",message:`No sync context for place ${r}`});return}if(!i.fileWatcher){n.status(200).json({added:0});return}let a=await i.fileWatcher.rescan();this.ctx.touchRuntimePlace(r),y.info("Reverse rescan completed",{placeId:r,added:a}),n.status(200).json({added:a})}resolveInstancePathForAppliedPath(e,n){let r=e.resolveInstancePathFromFsPath(n);if(r)return r;let i=e.getExplorerRoot(),a=tr.relative(i,n);if(a.startsWith("..")||a===""||tr.isAbsolute(a))return null;let o=a.split(tr.sep).filter(f=>f.length>0);if(o.length<2)return null;let s=tr.basename(n),c=tr.dirname(n),l=e.getOriginalInstance(c,s);if(l)return l.instancePath;let u=s.toLowerCase();if(No.some(f=>u.endsWith(f))||u==="_tree.json")return null;let p=["game"],d=i;for(let f of o){d=tr.join(d,f);let m=tr.dirname(d);p.push(e.getOriginalNameForDir(m,f))}return Ye(p)}};pe();function ZO(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={})}var si=class{config;places;apiHandler;changeProcessor;initHandler;reverseHandler;activeFullSyncPlaceId=null;activeRuntimeSyncPlaceId=null;constructor(e,n={}){this.config=new gf(e,n),this.apiHandler=new Nf(this),this.changeProcessor=new Af(this),this.initHandler=new Of(this),this.reverseHandler=new Df(this),this.places=new hf({max:3,dispose:(r,i)=>{y.info("Disposing place context (LRU eviction)",{placeId:i}),this.activeFullSyncPlaceId===i&&(this.activeFullSyncPlaceId=null),this.activeRuntimeSyncPlaceId===i&&(this.activeRuntimeSyncPlaceId=null),r.fileWatcher&&(r.fileWatcher.stop().catch(a=>{y.error("Error stopping file watcher during dispose",a)}),r.fileWatcher=null),r.writer.stopChangeLogFlusher(),r.incompleteSyncTimer&&(clearTimeout(r.incompleteSyncTimer),r.incompleteSyncTimer=null),r.index.saveToDisk().catch(a=>{y.error("Error saving index during dispose",a)}),r.activeFullSyncSessionId&&this.initHandler.clearPendingServiceTrees(r.activeFullSyncSessionId),r.tmpWriter&&(r.tmpWriter.stopChangeLogFlusher(),r.tmpWriter=null),r.tmpIndex=null,r.collisionDirMap=null}})}getSyncRoot(){return this.config.getSyncRoot()}async getOrCreatePlaceContext(e,n){let r=this.places.get(e);if(r&&n){let i=this.config.getPlaceRoot(e),a=await this.config.resolvePlaceRoot(e,n);a!==i&&(y.info("Place root migrated, recreating context",{placeId:e,from:i,to:a}),this.places.delete(e),r=void 0)}if(!r){let i=await this.config.resolvePlaceRoot(e,n),a=pS.join(i,"explorer");await Ho.mkdir(i,{recursive:!0}),await Ho.mkdir(a,{recursive:!0});let o=new ai(i,a);await o.loadFromDisk();let s=new Mo(this.config,o,e),c=new xf(this.config,o,i);s.startChangeLogFlusher(),r={placeId:e,placeName:"",index:o,writer:s,reader:c,fileWatcher:null,state:"idle",activeClientId:null,activeFullSyncSessionId:null,instanceCount:0,scriptCount:0,lastFullSync:null,lastIncrementalSync:null,tmpIndex:null,tmpWriter:null,incompleteSyncTimer:null,changesSinceLastSave:0,directions:{...Xi},applyModes:{...Yi},forwardRestoreQueue:[],syncProgress:null,collisionDirMap:null},this.places.set(e,r),y.info("Created new place context",{placeId:e,placeRoot:i})}return r}async handleSyncInit(e,n){try{ZO(e.body);let r=LO(e.body);if(!r.success){n.status(400).json({error:"Validation error",message:r.error});return}let i=r.data,a=i.phase==="start"?i.placeId??null:i.phase==="chunk"||i.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(i.phase){case"start":await this.initHandler.handleInitStart(a,i,n);break;case"chunk":await this.initHandler.handleInitChunk(a,i,n);break;case"complete":await this.initHandler.handleInitComplete(a,i,n);break}}catch(r){this.sendError(n,r,"handleSyncInit")}}async handleSyncUpdate(e,n){try{ZO(e.body);let r=UO(e.body);if(!r.success){n.status(400).json({error:"Validation error",message:r.error});return}let i=r.data,a=i.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(o.activeFullSyncSessionId!==null||o.state==="initializing"){n.status(409).json({error:"Conflict",message:`Full sync in progress for place ${a}`});return}o.activeClientId=i.clientId,this.touchRuntimePlace(a);let s=DO(o,i.changes);y.info("Sync update received",{placeId:a,clientId:i.clientId,changeCount:s.length,receivedCount:i.changes.length,types:s.map(f=>f.type)});let c=[],l=[],u=0,p=new Map;for(let f of s)try{let m=await this.changeProcessor.processChangeForPlace(o,f,p);m?l.push(m):u++}catch(m){let h="path"in f?f.path:"oldPath"in f?f.oldPath:"unknown";c.push({path:h,error:m instanceof Error?m.message:String(m)})}for(let f of p.values())try{await Ef(o,f)}catch(m){c.push({path:f.instancePath,error:m instanceof Error?m.message:String(m)})}o.changesSinceLastSave+=u,o.changesSinceLastSave>=MO&&(await o.index.saveToDisk(),o.changesSinceLastSave=0),o.lastIncrementalSync=new Date().toISOString();let d={processed:u,failed:c.length,errors:c,syncedAt:o.lastIncrementalSync};l.length>0&&(d.conflicts=l),n.status(200).json(d)}catch(r){this.sendError(n,r,"handleSyncUpdate")}}getDefaultRuntimePlaceId(){if(this.activeRuntimeSyncPlaceId!==null&&this.activeRuntimeSyncPlaceId!==void 0){if(this.places.has(this.activeRuntimeSyncPlaceId))return this.activeRuntimeSyncPlaceId;this.activeRuntimeSyncPlaceId=null}if(this.activeFullSyncPlaceId!==null&&this.activeFullSyncPlaceId!==void 0&&this.places.has(this.activeFullSyncPlaceId))return this.activeRuntimeSyncPlaceId=this.activeFullSyncPlaceId,this.activeRuntimeSyncPlaceId;for(let[e,n]of this.places.entries())if(n.state==="syncing"||n.state==="initializing")return this.activeRuntimeSyncPlaceId=e,e;return this.activeRuntimeSyncPlaceId=null,null}touchRuntimePlace(e){this.activeRuntimeSyncPlaceId=e}clearRuntimePlaceIfMatch(e){this.activeRuntimeSyncPlaceId===e&&(this.activeRuntimeSyncPlaceId=null)}resolveQueryPlaceId(e,n="runtime"){let r=e.query.placeId;if(r){let i=parseInt(r,10);if(!isNaN(i))return i}return n==="full"?this.activeFullSyncPlaceId:this.getDefaultRuntimePlaceId()}async handleSyncStatus(e,n){try{let r=this.resolveQueryPlaceId(e);if(r==null){let s={state:"idle",instanceCount:0,scriptCount:0,lastFullSync:null,lastIncrementalSync:null,syncRoot:this.config.getSyncRoot(),activeClientId:null,reverseSyncAvailable:!1,modifiedFileCount:0};n.status(200).json(s);return}let i=this.places.get(r);if(!i){let s={state:"idle",instanceCount:0,scriptCount:0,lastFullSync:null,lastIncrementalSync:null,syncRoot:this.config.getPlaceRoot(r),activeClientId:null,reverseSyncAvailable:!1,modifiedFileCount:0};n.status(200).json(s);return}let a=i.fileWatcher?.getPendingCount()??0;this.touchRuntimePlace(r);let o={state:i.state,instanceCount:i.instanceCount,scriptCount:i.scriptCount,lastFullSync:i.lastFullSync,lastIncrementalSync:i.lastIncrementalSync,syncRoot:this.config.getPlaceRoot(r),activeClientId:i.activeClientId,reverseSyncAvailable:a>0,modifiedFileCount:a,applyModes:i.applyModes,directions:i.directions,fileWatcherActive:i.fileWatcher!==null,forwardOnlyClasses:[...Do]};n.status(200).json(o)}catch(r){this.sendError(n,r,"handleSyncStatus")}}async handleSyncStop(e,n){try{let r=e.body,i=r.placeId??this.getDefaultRuntimePlaceId();if(i==null){n.status(200).json({status:"idle",state:"idle",placeId:null,message:"No active sync place"});return}let a=this.places.get(i);if(!a){n.status(200).json({status:"idle",state:"idle",placeId:i,message:`No sync context for place ${i}`});return}if(r.clientId&&a.activeClientId&&r.clientId!==a.activeClientId&&(a.activeFullSyncSessionId!==null||a.state==="syncing"||a.state==="initializing")){n.status(409).json({error:"Conflict",message:`This sync session belongs to client ${a.activeClientId}`});return}let o=a.activeFullSyncSessionId;if(o&&(this.initHandler.clearPreserveLocalFiles(o),this.initHandler.clearPendingServiceTrees(o)),a.fileWatcher&&(await a.fileWatcher.stop(),a.fileWatcher=null),a.incompleteSyncTimer&&(clearTimeout(a.incompleteSyncTimer),a.incompleteSyncTimer=null),o){let s=this.config.getPlaceRoot(i),c=pS.join(s,`explorer_tmp_${o}`);await Ho.rm(c,{recursive:!0,force:!0}).catch(()=>{})}a.tmpWriter&&(a.tmpWriter.stopChangeLogFlusher(),a.tmpWriter=null),a.tmpIndex=null,a.collisionDirMap=null,a.activeFullSyncSessionId=null,a.syncProgress=null,a.state="idle",a.activeClientId=null,a.instanceCount=0,a.scriptCount=0,this.activeFullSyncPlaceId===i&&(this.activeFullSyncPlaceId=null),this.clearRuntimePlaceIfMatch(i),y.info("Sync stopped",{placeId:i,reason:r.reason??"requested"}),n.status(200).json({status:"stopped",state:"idle",placeId:i})}catch(r){this.sendError(n,r,"handleSyncStop")}}async handleSyncConfig(e,n){try{if(e.method==="GET"){n.status(200).json(this.config.getConfig());return}let r=FO(e.body);if(!r.success){n.status(400).json({error:"Validation error",message:r.error});return}let i=r.data;i.maxDepth!==void 0&&this.config.updateConfig({maxDepth:i.maxDepth}),i.maxInstances!==void 0&&this.config.updateConfig({maxInstances:i.maxInstances}),n.status(200).json({status:"updated",config:this.config.getConfig()})}catch(r){this.sendError(n,r,"handleSyncConfig")}}async initialize(){try{await this.config.loadFromMeta(),await this.initHandler.cleanupStaleTempDirs(),y.info("SyncController initialized")}catch(e){y.error("SyncController initialization failed",e instanceof Error?e:new Error(String(e)))}}async shutdown(){try{this.places.clear(),y.info("SyncController shut down")}catch(e){y.error("SyncController shutdown error",e instanceof Error?e:new Error(String(e)))}}async atomicWriteFile(e,n){let r=e+".tmp."+fee().slice(0,8);try{await Ho.writeFile(r,n,"utf-8"),await Ho.rename(r,e)}catch(i){throw await Ho.unlink(r).catch(()=>{}),i}}async handlePreCheck(e,n){try{await this.apiHandler.handlePreCheck(e,n)}catch(r){this.sendError(n,r,"handlePreCheck")}}async handleSyncDirections(e,n){try{await this.apiHandler.handleSyncDirections(e,n)}catch(r){this.sendError(n,r,"handleSyncDirections")}}async handleForwardRestoreList(e,n){try{await this.apiHandler.handleForwardRestoreList(e,n)}catch(r){this.sendError(n,r,"handleForwardRestoreList")}}async handleReversePending(e,n){try{await this.reverseHandler.handleReversePending(e,n)}catch(r){this.sendError(n,r,"handleReversePending")}}async handleReverseSyncChanges(e,n){try{await this.reverseHandler.handleReverseSyncChanges(e,n)}catch(r){this.sendError(n,r,"handleReverseSyncChanges")}}async handleReverseSyncResult(e,n){try{await this.reverseHandler.handleReverseSyncResult(e,n)}catch(r){this.sendError(n,r,"handleReverseSyncResult")}}async handleResolveConflict(e,n){try{await this.reverseHandler.handleResolveConflict(e,n)}catch(r){this.sendError(n,r,"handleResolveConflict")}}async handleReverseRescan(e,n){try{await this.reverseHandler.handleReverseRescan(e,n)}catch(r){this.sendError(n,r,"handleReverseRescan")}}async handleSyncHistory(e,n){try{await this.apiHandler.handleSyncHistory(e,n)}catch(r){this.sendError(n,r,"handleSyncHistory")}}async startFileWatcherForPlace(e){if(e.state!=="syncing"){y.debug("Skipping file watcher start - place not syncing",{placeId:e.placeId,state:e.state});return}e.fileWatcher&&await e.fileWatcher.stop();let n=pS.join(this.config.getPlaceRoot(e.placeId),"explorer");e.fileWatcher=new $f(n,e.index),e.writer.setOnWriteCallback(r=>{e.fileWatcher?.suppressPath(r)}),e.fileWatcher.setDirectionChecker(r=>{let i=oO(r);return e.directions[i]}),e.fileWatcher.setOnForwardViolation(r=>{e.forwardRestoreQueue.includes(r)||(e.forwardRestoreQueue.push(r),y.info("Forward violation queued for restore",{placeId:e.placeId,relativePath:r,queueSize:e.forwardRestoreQueue.length}))}),await e.fileWatcher.start(),y.info("File watcher started for reverse sync",{placeId:e.placeId})}getStatusSummary(){return this.apiHandler.getStatusSummary()}getDirectionForCategory(e){return this.apiHandler.getDirectionForCategory(e)}getStatusDirect(e){return this.apiHandler.getStatusDirect(e)}getConfigDirect(){return this.apiHandler.getConfigDirect()}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,r){await this.apiHandler.writeSyncedFile(e,n,r)}async executeViaDisk(e,n){return this.apiHandler.executeViaDisk(e,n)}sendError(e,n,r){let i=n instanceof Error?n.message:String(n);if(i.includes("Path traversal detected")){e.status(403).json({error:"Forbidden",message:i});return}let a=n.code;if(a==="ENOSPC"||a==="EPERM"||a==="EACCES"){e.status(500).json({error:"Disk error",message:i});return}y.error(`SyncController.${r} failed`,n instanceof Error?n:new Error(i)),e.status(500).json({error:"Internal error",message:`${r}: ${i}`})}};import{randomUUID as mee}from"crypto";function HO(t,e){let n=mee(),r=n.replace(/-/g,"").substring(0,8).toUpperCase(),i=`${r.substring(0,4)}-${r.substring(4,8)}`;return{config:t,app:e,instanceId:n,sessionId:i,startTime:Date.now(),baseUrl:`http://${t.httpHost}:${t.httpPort}`,commandQueue:new Map,pendingCommands:new Map,globalPendingCommands:[],totalCommandsProcessed:0,pluginClients:new Map,mcpInstances:new Map,sseClients:new Set,cachedSelectionMap:new Map,isClientMode:!1,clientModeHealthTimer:null,clientModeConsecutiveHealthFailures:0,clientModeUpstreamReachable:!0,clientModeUpstreamContextCaptureEnabled:!0,clientModeLastHealthSuccessAt:null,clientModeLastHealthFailureAt:null,clientModeLastHealthError:null,historyManager:null,analyticsManager:null,executionContextManager:null,licenseState:null,syncController:null,internalActionExecutor:null,dashboardFolderPicker:null,activeSyncOwnerInstanceId:null,activeProjectRoot:null,pendingDashboardSyncRootPin:null,playtestControlCommand:null,dashboardSyncRootSwitchInFlight:!1,aiClientName:"",pluginVersion:"",syncedSessionToken:null,serverLastCommandAt:null}}var VO=fi(Po(),1);pe();function WO(t){let e=VO.default.json({limit:"5mb"});t.app.use((n,r,i)=>{if(n.path.startsWith("/sync/")){i();return}e(n,r,i)}),t.app.use((n,r,i)=>{r.setHeader("Access-Control-Allow-Origin","http://localhost:3002"),r.setHeader("Access-Control-Allow-Methods","GET, POST, OPTIONS"),r.setHeader("Access-Control-Allow-Headers","Content-Type"),i()}),t.app.use((n,r,i)=>{y.debug(`${n.method} ${n.path}`,{ip:n.ip}),i()})}import{randomUUID as Tl}from"crypto";pe();function hee(){let t=process.env.WEPPY_ROBLOX_MCP_VERSION?.trim();return t||null}var We=hee()??"2.1.0";Lf();function YO(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 yee(t){let n=Date.now();return Array.from(t.pluginClients.values()).filter(r=>n-r.lastSeen<1e4)}function QO(t){let n=Date.now();for(let[r,i]of t.mcpInstances)i.lastSeen&&n-i.lastSeen>15e3&&(t.mcpInstances.delete(r),i.sessionId&&t.executionContextManager?.endSession(i.sessionId),y.debug("Removed stale MCP instance",{instanceId:r,lastSeen:i.lastSeen}))}function eD(t,e,n){let r=e.query.clientId;if(r&&t.pluginClients.has(r)){let i=t.pluginClients.get(r);i.lastSeen=Date.now()}try{let i=e.body;if(!i||!Array.isArray(i.selection)||typeof i.count!="number"){y.warn("Invalid selection update request",{body:i}),n.status(400).json({error:"Invalid request body"});return}let a=r||"unknown",o=Date.now();t.cachedSelectionMap.set(a,{selection:i.selection,count:i.count,timestamp:o,clientId:a}),y.debug("Selection cache updated",{count:i.count,clientId:a,timestamp:o}),n.json({status:"ok",timestamp:o})}catch(i){y.error("Error handling selection update",i),n.status(500).json({error:"Internal server error"})}}function tD(t,e,n){let r=parseInt(e.query.maxAge)||3e4,i=El(t,r);i?n.json({cached:!0,...i}):n.json({cached:!1,message:"No cached selection available"})}function El(t,e=3e4,n){if(t.cachedSelectionMap.size===0)return null;let r;if(n)r=t.cachedSelectionMap.get(n);else for(let a of t.cachedSelectionMap.values())(!r||a.timestamp>r.timestamp)&&(r=a);if(!r)return null;let i=Date.now()-r.timestamp;return e===0||i<=e?r:null}function nD(t,e,n){try{let r=e.body;if(!r.clientId){n.status(400).json({error:"Missing clientId"});return}let i=t.pluginClients.get(r.clientId),a=Date.now(),o={clientId:r.clientId,projectName:r.projectName,placeName:r.placeName,placeId:r.placeId,pluginVersion:r.pluginVersion,connectedAt:i?.connectedAt||a,lastSeen:a,commandsProcessed:i?.commandsProcessed||0,connectionType:"polling"};t.pluginClients.set(r.clientId,o),t.pendingCommands.has(r.clientId)||t.pendingCommands.set(r.clientId,[]),r.pluginVersion&&(t.pluginVersion=r.pluginVersion),$t(t,"connection",{clientId:r.clientId,placeId:o.projectName,placeName:o.placeName,status:"connected"}),bl(t,{timestamp:new Date().toISOString(),type:"plugin",status:"connected",clientId:r.clientId,message:`Plugin connected \u2014 ${r.clientId}`,...o.placeId!==void 0?{placeId:o.placeId}:{},...o.placeName?{placeName:o.placeName}:{}}),typeof o.placeId=="number"&&Number.isFinite(o.placeId)&&Nw(t,o.placeId,o.placeName??null),t.analyticsManager&&(r.pluginVersion&&t.analyticsManager.setPluginVersion(r.pluginVersion),t.analyticsManager.trackPluginConnected()),y.info("Plugin client registered",{clientId:r.clientId,projectName:r.projectName,placeName:r.placeName,isReconnect:!!i}),n.json({status:"ok",clientId:r.clientId,serverInstanceId:t.instanceId,sessionId:t.sessionId,mcpVersion:We,connectedAt:o.connectedAt,aiClientNames:YO(t),serverStartTime:t.startTime,mcpInstanceCount:t.mcpInstances.size+1})}catch(r){y.error("Error registering plugin client",r),n.status(500).json({error:"Internal server error"})}}function rD(t,e,n){let r=e.body?.clientId;if(!r){n.status(400).json({error:"Missing clientId"});return}let i=t.pluginClients.has(r);t.pluginClients.delete(r),t.pendingCommands.delete(r),i&&($t(t,"connection",{clientId:r,status:"disconnected"}),bl(t,{timestamp:new Date().toISOString(),type:"plugin",status:"disconnected",clientId:r,message:`Plugin disconnected \u2014 ${r}`})),y.info("Plugin client unregistered",{clientId:r,existed:i}),n.json({status:"ok",existed:i})}function iD(t,e,n){try{let r=e.body;if(!r.instanceId){n.status(400).json({error:"Missing instanceId"});return}let i=Date.now(),a={instanceId:r.instanceId,...typeof r.sessionId=="string"?{sessionId:r.sessionId}:{},pid:r.pid,connectedAt:i,isServer:!1,lastSeen:i};r.aiClientName&&(a.aiClientName=r.aiClientName),r.cwd&&(a.cwd=r.cwd),"projectRoot"in r&&(a.projectRoot=r.projectRoot),t.mcpInstances.set(r.instanceId,a),$t(t,"mcp_status",{aiClientName:a.aiClientName??"Unknown",instanceId:r.instanceId,status:"registered"}),bl(t,{timestamp:new Date().toISOString(),type:"mcp",status:"registered",instanceId:r.instanceId,message:`MCP registered \u2014 ${a.aiClientName??r.instanceId}`,...a.aiClientName?{aiClientName:a.aiClientName}:{}}),y.info("MCP instance registered (client mode)",{instanceId:r.instanceId,pid:r.pid,cwd:r.cwd}),n.json({status:"ok",instanceId:r.instanceId,serverInstanceId:t.instanceId,sessionId:t.sessionId,mcpVersion:We,mcpInstanceCount:t.mcpInstances.size+1})}catch(r){y.error("Error registering MCP instance",r),n.status(500).json({error:"Internal server error"})}}function aD(t,e,n){let r=e.body?.instanceId;if(!r){n.status(400).json({error:"Missing instanceId"});return}let i=t.mcpInstances.get(r),a=!!i;t.mcpInstances.delete(r),i?.sessionId&&t.executionContextManager?.endSession(i.sessionId),a&&($t(t,"mcp_status",{aiClientName:i?.aiClientName??"Unknown",instanceId:r,status:"unregistered"}),bl(t,{timestamp:new Date().toISOString(),type:"mcp",status:"unregistered",instanceId:r,message:`MCP unregistered \u2014 ${i?.aiClientName??r}`,...i?.aiClientName?{aiClientName:i.aiClientName}:{}})),y.info("MCP instance unregistered",{instanceId:r,existed:a}),n.json({status:"ok",existed:a})}function oD(t,e,n){let{instanceId:r,aiClientName:i}=e.body;if(r&&i){let a=t.mcpInstances.get(r);a&&(a.aiClientName=i)}n.json({status:"ok"})}function sD(t){let n=Date.now(),r=Tn({appDataDir:t.config.appDataDir});for(let[i,a]of t.pluginClients)n-a.lastSeen>3e4&&(t.pluginClients.delete(i),t.pendingCommands.delete(i));return QO(t),{serverInstanceId:t.instanceId,sessionId:t.sessionId,mcpVersion:We,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:r,...t.aiClientName?{aiClientName:t.aiClientName}:{}},...Array.from(t.mcpInstances.values())],mcpInstanceCount:t.mcpInstances.size+1}}function cD(t,e){e.json(sD(t))}function lD(t,e,n){let r=e.query.instanceId;r&&t.mcpInstances.has(r)&&(t.mcpInstances.get(r).lastSeen=Date.now()),QO(t);let i=yee(t),a=t.sseClients.size,c={...{status:"online",connectedClients:a+i.length,queuedCommands:t.commandQueue.size,uptime:Date.now()-t.startTime,version:We,enableContextCapture:t.executionContextManager?.isEnabled()??t.config.enableContextCapture??!0,isClientMode:t.isClientMode,pid:process.pid,sessionId:t.sessionId},instanceId:t.instanceId,mcpInstanceCount:t.mcpInstances.size+1,aiClientNames:YO(t),pluginVersion:t.pluginVersion||void 0,sseClients:a,dashboardSseClients:t.dashboardSseClients?.size??0,pollingClients:i.length,pluginClients:i.map(l=>({clientId:l.clientId,projectName:l.projectName,placeName:l.placeName,pluginVersion:l.pluginVersion,lastSeen:Date.now()-l.lastSeen})),...t.isClientMode?{upstream:{reachable:t.clientModeUpstreamReachable,consecutiveFailures:t.clientModeConsecutiveHealthFailures,lastSuccessAt:t.clientModeLastHealthSuccessAt,lastFailureAt:t.clientModeLastHealthFailureAt,lastError:t.clientModeLastHealthError,baseUrl:t.baseUrl}}:{}};n.json(c)}function uD(t,e,n,r){let i=e.ip||e.socket.remoteAddress||"";if(!(i==="127.0.0.1"||i==="::1"||i==="::ffff:127.0.0.1"||i==="localhost")){y.warn("Shutdown request rejected from non-localhost",{ip:i}),n.status(403).json({error:"Forbidden: localhost only"});return}y.info("Shutdown request received, initiating graceful shutdown",{requestedBy:i,uptime:Date.now()-t.startTime}),n.json({status:"shutting_down",message:"Server will shutdown gracefully",pid:process.pid}),setTimeout(async()=>{try{await r(),y.info("Graceful shutdown completed"),process.exit(0)}catch(o){y.error("Error during graceful shutdown",o),process.exit(1)}},100)}async function Uf(t){if(t.isClientMode)try{let e=await fetch(`${t.baseUrl}/connection-info`);if(e.ok)return await e.json()}catch(e){y.warn("Failed to fetch connection info from server",{error:e})}return sD(t)}pe();var Ff={query_instances:{discriminator:"action",mapping:{get:"get_instance",children:"get_instance_children",find_child:"find_first_child",find_descendant:"find_first_descendant",wait_for_child:"wait_for_child",class_info:"get_class_info",search_name:"search_by_name",search_class:"search_by_class",search_property:"search_by_property",search_tag:"search_by_tag",file_tree:"get_file_tree",project_structure:"get_project_structure",descendants:"get_descendants",ancestors:"get_ancestors"},paramAliases:{search_by_name:{query:"pattern"},search_by_property:{root:"rootPath"},search_by_tag:{root:"rootPath"},get_project_structure:{root:"rootPath"}}},mutate_instances:{discriminator:"action",mapping:{create:"create_instance",create_with_props:"create_instance_with_properties",delete:"delete_instance",clone:"clone_instance",move:"move_instance",rename:"rename_instance",pivot:"pivot_to",create_tree:"create_instance_tree",mass_create:"mass_create_instances",mass_delete:"mass_delete_instances",mass_duplicate:"mass_duplicate",smart_duplicate:"smart_duplicate"},paramAliases:{clone_instance:{path:"sourcePath"}}},manage_properties:{discriminator:"action",mapping:{get:"get_property",set:"set_property",get_all:"get_all_properties",set_multiple:"set_multiple_properties",get_attr:"get_attribute",set_attr:"set_attribute",get_all_attrs:"get_all_attributes",delete_attr:"delete_attribute",add_tag:"add_tag",remove_tag:"remove_tag",check_tag:"has_tag",get_tags:"get_tags",get_tagged:"get_tagged",set_calculated:"set_calculated_property",set_relative:"set_relative_property",mass_set:"mass_set_property",mass_get:"mass_get_property",modify_children:"modify_children"},paramAliases:{get_tagged:{tagName:"tag",root:"rootPath"},set_relative_property:{amount:"value"}}},manage_scripts:{discriminator:"action",mapping:{get_source:"get_script_source",set_source:"set_script_source",create:"create_script",delete:"delete_script",edit_replace:"edit_script_lines",edit_insert:"insert_script_lines",edit_delete:"delete_script_lines",search:"search_in_scripts",replace:"replace_in_scripts",get_dependencies:"get_script_dependencies"},paramAliases:{edit_script_lines:{newLines:"newContent"},insert_script_lines:{lines:"content"},replace_in_scripts:{pattern:"searchPattern"}}},manage_lighting:{discriminator:"action",mapping:{lighting:"set_lighting",atmosphere:"set_atmosphere",sky:"set_sky",terrain_props:"set_terrain",time:"set_time_of_day"}},manage_selection:{discriminator:"action",mapping:{get:"get_selection",set:"set_selection",clear:"clear_selection",cached:"get_cached_selection",context:"get_selection_context",details:"get_selection_details",add:"add_to_selection",remove:"remove_from_selection",watch:"watch_selection"}},manage_camera:{discriminator:"action",mapping:{info:"get_camera_info",focus_path:"focus_camera_path",focus_position:"focus_camera_position",suggest:"get_suggested_camera_view"},paramAliases:{get_suggested_camera_view:{path:"targetPath"}}},manage_tween:{discriminator:"action",mapping:{create:"create_tween",play:"play_tween",pause:"pause_tween",cancel:"cancel_tween"}},manage_audio:{discriminator:"action",mapping:{play:"play_sound",stop:"stop_sound",pause:"pause_sound",resume:"resume_sound",set_listener:"set_listener"}},manage_animation:{discriminator:"action",mapping:{load:"load_animation",play:"play_animation",stop:"stop_animation",get_tracks:"get_animation_tracks"}},manage_physics:{discriminator:"action",mapping:{register_group:"register_collision_group",set_collidable:"set_collidable",get_groups:"get_collision_groups"}},manage_effects:{discriminator:"action",mapping:{emit:"emit_particles",clear:"clear_particles",toggle:"toggle_effect"}},manage_terrain:{discriminator:"action",mapping:{fill_block:"terrain_fill_block",fill_ball:"terrain_fill_ball",fill_cylinder:"terrain_fill_cylinder",fill_wedge:"terrain_fill_wedge",clear_region:"terrain_clear",clear_bounds:"terrain_clear_region",replace_material:"terrain_replace_material",colors_get:"terrain_get_material_color",colors_set:"terrain_set_material_color",read_voxel:"terrain_read_voxel",read_voxels:"terrain_read_voxels",write_voxels:"terrain_write_voxels",generate:"terrain_generate",smooth:"terrain_smooth"}},spatial_query:{discriminator:"action",mapping:{raycast:"raycast",find_ground:"find_ground",check_placement:"check_placement",multi_raycast:"multi_raycast",scan_area:"scan_area",find_flat:"find_flat_areas",find_spawn:"find_spawn_positions",analyze_walkable:"analyze_walkable_area",spatial_map:"get_spatial_map",find_space:"find_empty_space",bounds:"get_bounds",snap_grid:"snap_to_grid",collision:"check_collision"},paramAliases:{get_spatial_map:{path:"rootPath"}}},manage_assets:{discriminator:"action",mapping:{insert:"insert_model",info:"get_asset_info",search:"search_creator_store",search_insert:"search_and_insert_model",insert_free:"insert_free_model",insert_package:"insert_package",export:"export_selection"},paramAliases:{search_creator_store:{maxResults:"limit"}}},manage_sync:{discriminator:"action",mapping:{status:"sync_status",config:"sync_config",history:"sync_history",directions:"sync_directions",read_file:"sync_read_file",write_file:"sync_write_file",progress:"sync_progress"}},workspace_state:{discriminator:"action",mapping:{sync:"sync_workspace_state",snapshot:"get_workspace_snapshot",changes:"get_recent_changes",viewport:"get_viewport_info",clear_history:"clear_change_history",metadata:"get_workspace_metadata",scripts:"get_script_list",selection_info:"get_selection_info",clear_cache:"clear_state_cache"}},manage_logs:{discriminator:"action",mapping:{get:"get_output_logs",clear:"clear_output_logs",errors:"get_recent_errors"},paramAliases:{get_output_logs:{level:"type"}}},system_info:{discriminator:"action",mapping:{ping:"ping",connection:"get_connection_info",usage:"get_usage_status",place_info:"get_place_info",services:"get_services",studio_settings:"get_studio_settings",play:"start_playtest",stop:"stop_playtest",pause:"pause_playtest",resume:"resume_playtest",play_status:"get_play_status",run_test:"run_test"}}};var vee={get_cached_selection:"internal",sync_status:"internal",sync_config:"internal",sync_history:"internal",sync_directions:"internal",sync_read_file:"internal",sync_write_file:"internal",sync_progress:"internal",get_connection_info:"internal",run_test:"internal"};function qf(t){return vee[t]||"plugin"}var Bf=100,xee=Object.entries(Ff).reduce((t,[e,n])=>{for(let r of Object.values(n.mapping))t[r]=e;return t},{});function pD(t){return{toolName:xee[t]||t,actionName:t}}function dD(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 fD(t,e,n){y.info("Plugin connected via SSE"),n.setHeader("Content-Type","text/event-stream"),n.setHeader("Cache-Control","no-cache"),n.setHeader("Connection","keep-alive"),t.sseClients.add(n),xS(n,{event:"command",id:Tl(),data:{action:"connected",requestId:Tl(),params:{serverVersion:We,timestamp:Date.now()}}});let r=setInterval(()=>{xS(n,{event:"command",id:Tl(),data:{action:"keepalive",requestId:Tl(),params:{timestamp:Date.now()}}})},3e4);n.on("close",()=>{y.info("Plugin disconnected from SSE"),clearInterval(r),t.sseClients.delete(n)})}function xS(t,e){let n=JSON.stringify(e.data);t.write(`event: ${e.event}
126
+ `),this.clearTTLTimerForPlace(i,a),i.index.clearAllHashes(),i.index.clearClassMappings(),i.tmpIndex){let m=i.tmpIndex.getExplorerRoot(),h=i.index.getExplorerRoot();for(let[g,x]of i.tmpIndex.getAllHashes()){let w=Ce.relative(m,g),_=Ce.resolve(h,w);i.index.updateHashByValue(_,x)}for(let[g,x]of i.tmpIndex.getAllFileHashes()){let w=Ce.relative(m,g),_=Ce.resolve(h,w);i.index.updateFileHashByValue(_,x)}i.index.resetNameCounters(),i.index.mergeNameMappingsFrom(i.tmpIndex)}if(i.tmpIndex=null,i.tmpWriter&&(i.tmpWriter.stopChangeLogFlusher(),i.tmpWriter=null),this.pendingServiceTrees.delete(a),i.collisionDirMap=null,await i.index.saveToDisk(),i.state="syncing",i.activeFullSyncSessionId=null,i.syncProgress=null,this.ctx.touchRuntimePlace(e),this.ctx.activeFullSyncPlaceId=null,await this.ctx.startFileWatcherForPlace(i),i.fileWatcher&&await i.fileWatcher.waitUntilReady(),u.size>0){for(let[m,h]of u){let g=Ce.resolve(o,m);try{await nn.mkdir(Ce.dirname(g),{recursive:!0}),await nn.writeFile(g,h,"utf-8")}catch(x){y.warn("Failed to restore preserved file",{path:m,error:x instanceof Error?x.message:String(x)})}}y.info("Restored preserved local files",{placeId:e,count:u.size})}if(p.length>0){let m=0;for(let h of p){let g=Ce.resolve(o,h);try{await nn.unlink(g),i.index.removeHash(g),m++}catch(x){x.code!=="ENOENT"&&y.warn("Failed to delete preserved-as-deleted file",{path:h,error:x instanceof Error?x.message:String(x)})}}m>0&&(await i.index.saveToDisk(),y.info("Deleted locally-removed files from new sync",{placeId:e,count:m}))}i.writer.appendChangeLog(`FULL_SYNC_COMPLETE instances=${i.instanceCount} scripts=${i.scriptCount}`),i.writer.appendHistory({timestamp:new Date().toISOString(),type:"fullSyncComplete",direction:"forward",path:`place_${e}`,details:`instances:${i.instanceCount} scripts:${i.scriptCount}`}),y.info("Full sync completed",{placeId:e,instanceCount:i.instanceCount,scriptCount:i.scriptCount}),r.status(200).json({status:"completed",instanceCount:i.instanceCount,scriptCount:i.scriptCount,syncRoot:this.ctx.config.getPlaceRoot(e)})}setPreserveLocalFiles(e,n){this.preserveLocalFilesMap.set(e,n)}getAndClearPreserveLocalFiles(e){let n=this.preserveLocalFilesMap.get(e)||[];return this.preserveLocalFilesMap.delete(e),n}clearPreserveLocalFiles(e){this.preserveLocalFilesMap.delete(e)}clearPendingServiceTrees(e){this.pendingServiceTrees.delete(e)}startTTLTimerForPlace(e,n){let r=setTimeout(async()=>{y.warn("Incomplete sync TTL expired, cleaning up",{placeId:e.placeId,syncId:n});let i=this.ctx.config.getPlaceRoot(e.placeId),a=Ce.join(i,`explorer_tmp_${n}`);try{await nn.rm(a,{recursive:!0,force:!0})}catch(o){y.error("Failed to clean up expired temp dir",o instanceof Error?o:new Error(String(o)))}e.incompleteSyncTimer=null,e.activeFullSyncSessionId===n&&(e.activeFullSyncSessionId=null,e.activeClientId=null,e.state="idle",e.tmpIndex=null,this.pendingServiceTrees.delete(n),e.collisionDirMap=null,e.tmpWriter&&(e.tmpWriter.stopChangeLogFlusher(),e.tmpWriter=null),this.ctx.activeFullSyncPlaceId===e.placeId&&(this.ctx.activeFullSyncPlaceId=null),this.ctx.clearRuntimePlaceIfMatch(e.placeId))},zO);r&&typeof r=="object"&&"unref"in r&&r.unref(),e.incompleteSyncTimer=r}getOrCreatePendingServiceTree(e,n){let r=this.pendingServiceTrees.get(e);r||(r=new Map,this.pendingServiceTrees.set(e,r));let i=r.get(n.serviceName);if(i)return i;let a={serviceName:n.tree?.name??n.serviceName,serviceClassName:n.tree?.className??n.serviceClassName,zeroBasedChunkIndex:n.chunkIndex===0,instances:[]};return r.set(n.serviceName,a),a}isLastChunk(e,n,r){return r<=1?!0:e.zeroBasedChunkIndex?n>=r-1:n>=r}buildServiceTree(e,n){let r={name:n.serviceName,className:n.serviceClassName,childCount:0,children:[],syncedAt:new Date().toISOString()};for(let i of n.instances){let a=this.resolveEffectiveSegments(e,i.effectivePath),o=gt(i.originalPath);this.upsertTreeNode(r,a,o,i.className)}return this.recomputeTreeChildCounts(r),r.syncedAt=new Date().toISOString(),r}resolveEffectiveSegments(e,n){return e.tmpIndex?gt(n).map(r=>e.tmpIndex.sanitizeName(r)):gt(n)}rewritePendingEffectivePaths(e,n,r,i){let a=Ce.relative(n,r).split(Ce.sep).filter(l=>l.length>0),o=Ce.relative(n,i).split(Ce.sep).filter(l=>l.length>0);if(a.length===0||o.length===0)return;let s=Ye(["game",...a]),c=Ye(["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,r,i){if(n.length<=1)return;let a=e.children;for(let o=1;o<n.length;o++){let s=n[o],c=r[o],l=o===n.length-1,u=a.find(p=>p.name===s);u?l&&(u.className=i,c!==void 0&&c!==s&&(u.originalName=c)):(u={name:s,className:l?i:"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=r=>{let i=r.children??[];r.children=i;for(let a of i)n(a);r.childCount=i.length};for(let r of e.children)n(r);e.childCount=e.children.length}clearTTLTimerForPlace(e,n){e.incompleteSyncTimer&&e.activeFullSyncSessionId===n&&(clearTimeout(e.incompleteSyncTimer),e.incompleteSyncTimer=null)}async cleanupStaleTempDirs(){let e=this.ctx.config.getSyncRoot();try{let n=await nn.readdir(e,{withFileTypes:!0});for(let r of n)if(r.isDirectory()){if(r.name.startsWith("explorer_tmp_")){let i=Ce.join(e,r.name);y.warn("Removing stale temp directory from crashed sync",{dir:r.name});try{await nn.rm(i,{recursive:!0,force:!0})}catch(a){y.error(`Failed to remove stale temp dir: ${r.name}`,a instanceof Error?a:new Error(String(a)))}}if(r.name.startsWith("place_")){let i=Ce.join(e,r.name);try{let a=await nn.readdir(i,{withFileTypes:!0});for(let o of a)if(o.isDirectory()&&o.name.startsWith("explorer_tmp_")){let s=Ce.join(i,o.name);y.warn("Removing stale temp directory from crashed sync",{dir:`${r.name}/${o.name}`});try{await nn.rm(s,{recursive:!0,force:!0})}catch(c){y.error(`Failed to remove stale temp dir: ${r.name}/${o.name}`,c instanceof Error?c:new Error(String(c)))}}}catch{continue}}}}catch(n){if(n.code==="ENOENT")return;y.warn("Failed to scan for stale temp dirs",{error:n instanceof Error?n.message:String(n)})}}};import tr from"path";import{promises as BO}from"fs";pe();var Df=class{constructor(e){this.ctx=e}async handleReversePending(e,n){let r=this.ctx.resolveQueryPlaceId(e);if(r==null){n.status(200).json({pending:0,hasConflicts:!1,lastDetected:null});return}let i=this.ctx.places.get(r);if(!i){n.status(404).json({error:"Place not found",message:`No sync context for place ${r}`});return}let a={pending:i.fileWatcher?.getPendingCount()??0,hasConflicts:!1,lastDetected:i.fileWatcher?.getLastDetected()??null,forwardRestoreNeeded:i.forwardRestoreQueue.length};this.ctx.touchRuntimePlace(r),n.status(200).json(a)}async handleReverseSyncChanges(e,n){let r=this.ctx.resolveQueryPlaceId(e);if(r==null){n.status(200).json({changes:[],count:0});return}let i=this.ctx.places.get(r);if(!i){n.status(404).json({error:"Place not found",message:`No sync context for place ${r}`});return}if(i.state!=="syncing"){n.status(400).json({error:"Not syncing",message:"Reverse sync is only available when sync is active"});return}let a=i.fileWatcher?.drainPendingChanges()??[],o=a.length>0?await i.reader.buildChangesFromPending(a):[];this.ctx.touchRuntimePlace(r),n.status(200).json({changes:o,count:o.length})}async handleReverseSyncResult(e,n){let r=e.body,i=r.placeId??this.ctx.getDefaultRuntimePlaceId();if(i==null){n.status(400).json({error:"Validation error",message:"placeId is required (in body or via active sync session)"});return}let a=r.appliedFiles??r.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(i);if(!o){n.status(404).json({error:"Place not found",message:`No sync context for place ${i}`});return}this.ctx.touchRuntimePlace(i);let s=this.ctx.config.getPlaceRoot(i),c=0,l=[];for(let u of a){let p=tr.resolve(s,u);if(!jf(s,p)){l.push({path:u,error:"Path is outside the place root"});continue}try{let d=await BO.readFile(p,"utf-8"),f=o.index.computeHash(d);o.index.updateHashByValue(p,f),o.index.updateFileHashByValue(p,f),c++}catch(d){let f=d.code;if(f==="ENOENT"){o.index.removeHash(p),o.index.removeHashesUnder(p);let m=this.resolveInstancePathForAppliedPath(o.index,p);if(m){let h=Rt(m),g=jt(m);g&&h&&await o.writer.removeFromTree(g,h)}c++}else f==="EISDIR"?c++:l.push({path:u,error:d instanceof Error?d.message:String(d)})}}c>0&&await o.index.saveToDisk(),c>0&&o.writer.appendHistory({timestamp:new Date().toISOString(),type:"reverseApply",direction:"reverse",path:`place_${i}`,details:`applied:${c} failed:${l.length}`}),n.status(200).json({updated:c,failed:l.length,errors:l})}async handleResolveConflict(e,n){let r=e.body;if(!r.fsPath||!r.resolution){n.status(400).json({error:"Validation error",message:"fsPath and resolution are required"});return}let{fsPath:i,resolution:a}=r,o=this.ctx.config.getSyncRoot();if(!jf(o,tr.resolve(o,i))){n.status(403).json({error:"Forbidden",message:"Path is outside the sync root"});return}if(a==="skip"){n.status(200).json({status:"skipped",fsPath:i});return}let s;if(r.placeId&&(s=this.ctx.places.get(r.placeId)),!s){let d=i.match(/^place_(\d+)(?:_[^/]+)?\//);if(d){let f=parseInt(d[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=tr.resolve(c,i);if(!jf(c,l)){n.status(403).json({error:"Forbidden",message:"Path is outside the place root"});return}let u=s.index,p=s.reader;if(a==="apply-studio"){u.resolveFile(l,"apply-studio"),await u.saveToDisk(),n.status(200).json({status:"resolved",resolution:"apply-studio",fsPath:i});return}if(a==="apply-file"){let d=await BO.readFile(l,"utf-8"),f=u.computeHash(d);u.resolveFile(l,"apply-file",f),await u.saveToDisk();let m=p.getFileType(l),h=p.resolveInstancePathFromFile(l);n.status(200).json({status:"resolved",resolution:"apply-file",fsPath:i,instancePath:h,fileType:m,content:d});return}n.status(400).json({error:"Invalid resolution",message:`Unknown resolution: ${a}`})}async handleReverseRescan(e,n){let r=this.ctx.resolveQueryPlaceId(e);if(r==null){n.status(200).json({added:0});return}let i=this.ctx.places.get(r);if(!i){n.status(404).json({error:"Place not found",message:`No sync context for place ${r}`});return}if(!i.fileWatcher){n.status(200).json({added:0});return}let a=await i.fileWatcher.rescan();this.ctx.touchRuntimePlace(r),y.info("Reverse rescan completed",{placeId:r,added:a}),n.status(200).json({added:a})}resolveInstancePathForAppliedPath(e,n){let r=e.resolveInstancePathFromFsPath(n);if(r)return r;let i=e.getExplorerRoot(),a=tr.relative(i,n);if(a.startsWith("..")||a===""||tr.isAbsolute(a))return null;let o=a.split(tr.sep).filter(f=>f.length>0);if(o.length<2)return null;let s=tr.basename(n),c=tr.dirname(n),l=e.getOriginalInstance(c,s);if(l)return l.instancePath;let u=s.toLowerCase();if(No.some(f=>u.endsWith(f))||u==="_tree.json")return null;let p=["game"],d=i;for(let f of o){d=tr.join(d,f);let m=tr.dirname(d);p.push(e.getOriginalNameForDir(m,f))}return Ye(p)}};pe();function ZO(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={})}var si=class{config;places;apiHandler;changeProcessor;initHandler;reverseHandler;activeFullSyncPlaceId=null;activeRuntimeSyncPlaceId=null;constructor(e,n={}){this.config=new gf(e,n),this.apiHandler=new Nf(this),this.changeProcessor=new Af(this),this.initHandler=new Of(this),this.reverseHandler=new Df(this),this.places=new hf({max:3,dispose:(r,i)=>{y.info("Disposing place context (LRU eviction)",{placeId:i}),this.activeFullSyncPlaceId===i&&(this.activeFullSyncPlaceId=null),this.activeRuntimeSyncPlaceId===i&&(this.activeRuntimeSyncPlaceId=null),r.fileWatcher&&(r.fileWatcher.stop().catch(a=>{y.error("Error stopping file watcher during dispose",a)}),r.fileWatcher=null),r.writer.stopChangeLogFlusher(),r.incompleteSyncTimer&&(clearTimeout(r.incompleteSyncTimer),r.incompleteSyncTimer=null),r.index.saveToDisk().catch(a=>{y.error("Error saving index during dispose",a)}),r.activeFullSyncSessionId&&this.initHandler.clearPendingServiceTrees(r.activeFullSyncSessionId),r.tmpWriter&&(r.tmpWriter.stopChangeLogFlusher(),r.tmpWriter=null),r.tmpIndex=null,r.collisionDirMap=null}})}getSyncRoot(){return this.config.getSyncRoot()}async getOrCreatePlaceContext(e,n){let r=this.places.get(e);if(r&&n){let i=this.config.getPlaceRoot(e),a=await this.config.resolvePlaceRoot(e,n);a!==i&&(y.info("Place root migrated, recreating context",{placeId:e,from:i,to:a}),this.places.delete(e),r=void 0)}if(!r){let i=await this.config.resolvePlaceRoot(e,n),a=pS.join(i,"explorer");await Ho.mkdir(i,{recursive:!0}),await Ho.mkdir(a,{recursive:!0});let o=new ai(i,a);await o.loadFromDisk();let s=new Mo(this.config,o,e),c=new xf(this.config,o,i);s.startChangeLogFlusher(),r={placeId:e,placeName:"",index:o,writer:s,reader:c,fileWatcher:null,state:"idle",activeClientId:null,activeFullSyncSessionId:null,instanceCount:0,scriptCount:0,lastFullSync:null,lastIncrementalSync:null,tmpIndex:null,tmpWriter:null,incompleteSyncTimer:null,changesSinceLastSave:0,directions:{...Xi},applyModes:{...Yi},forwardRestoreQueue:[],syncProgress:null,collisionDirMap:null},this.places.set(e,r),y.info("Created new place context",{placeId:e,placeRoot:i})}return r}async handleSyncInit(e,n){try{ZO(e.body);let r=LO(e.body);if(!r.success){n.status(400).json({error:"Validation error",message:r.error});return}let i=r.data,a=i.phase==="start"?i.placeId??null:i.phase==="chunk"||i.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(i.phase){case"start":await this.initHandler.handleInitStart(a,i,n);break;case"chunk":await this.initHandler.handleInitChunk(a,i,n);break;case"complete":await this.initHandler.handleInitComplete(a,i,n);break}}catch(r){this.sendError(n,r,"handleSyncInit")}}async handleSyncUpdate(e,n){try{ZO(e.body);let r=UO(e.body);if(!r.success){n.status(400).json({error:"Validation error",message:r.error});return}let i=r.data,a=i.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(o.activeFullSyncSessionId!==null||o.state==="initializing"){n.status(409).json({error:"Conflict",message:`Full sync in progress for place ${a}`});return}o.activeClientId=i.clientId,this.touchRuntimePlace(a);let s=DO(o,i.changes);y.info("Sync update received",{placeId:a,clientId:i.clientId,changeCount:s.length,receivedCount:i.changes.length,types:s.map(f=>f.type)});let c=[],l=[],u=0,p=new Map;for(let f of s)try{let m=await this.changeProcessor.processChangeForPlace(o,f,p);m?l.push(m):u++}catch(m){let h="path"in f?f.path:"oldPath"in f?f.oldPath:"unknown";c.push({path:h,error:m instanceof Error?m.message:String(m)})}for(let f of p.values())try{await Ef(o,f)}catch(m){c.push({path:f.instancePath,error:m instanceof Error?m.message:String(m)})}o.changesSinceLastSave+=u,o.changesSinceLastSave>=MO&&(await o.index.saveToDisk(),o.changesSinceLastSave=0),o.lastIncrementalSync=new Date().toISOString();let d={processed:u,failed:c.length,errors:c,syncedAt:o.lastIncrementalSync};l.length>0&&(d.conflicts=l),n.status(200).json(d)}catch(r){this.sendError(n,r,"handleSyncUpdate")}}getDefaultRuntimePlaceId(){if(this.activeRuntimeSyncPlaceId!==null&&this.activeRuntimeSyncPlaceId!==void 0){if(this.places.has(this.activeRuntimeSyncPlaceId))return this.activeRuntimeSyncPlaceId;this.activeRuntimeSyncPlaceId=null}if(this.activeFullSyncPlaceId!==null&&this.activeFullSyncPlaceId!==void 0&&this.places.has(this.activeFullSyncPlaceId))return this.activeRuntimeSyncPlaceId=this.activeFullSyncPlaceId,this.activeRuntimeSyncPlaceId;for(let[e,n]of this.places.entries())if(n.state==="syncing"||n.state==="initializing")return this.activeRuntimeSyncPlaceId=e,e;return this.activeRuntimeSyncPlaceId=null,null}touchRuntimePlace(e){this.activeRuntimeSyncPlaceId=e}clearRuntimePlaceIfMatch(e){this.activeRuntimeSyncPlaceId===e&&(this.activeRuntimeSyncPlaceId=null)}resolveQueryPlaceId(e,n="runtime"){let r=e.query.placeId;if(r){let i=parseInt(r,10);if(!isNaN(i))return i}return n==="full"?this.activeFullSyncPlaceId:this.getDefaultRuntimePlaceId()}async handleSyncStatus(e,n){try{let r=this.resolveQueryPlaceId(e);if(r==null){let s={state:"idle",instanceCount:0,scriptCount:0,lastFullSync:null,lastIncrementalSync:null,syncRoot:this.config.getSyncRoot(),activeClientId:null,reverseSyncAvailable:!1,modifiedFileCount:0};n.status(200).json(s);return}let i=this.places.get(r);if(!i){let s={state:"idle",instanceCount:0,scriptCount:0,lastFullSync:null,lastIncrementalSync:null,syncRoot:this.config.getPlaceRoot(r),activeClientId:null,reverseSyncAvailable:!1,modifiedFileCount:0};n.status(200).json(s);return}let a=i.fileWatcher?.getPendingCount()??0;this.touchRuntimePlace(r);let o={state:i.state,instanceCount:i.instanceCount,scriptCount:i.scriptCount,lastFullSync:i.lastFullSync,lastIncrementalSync:i.lastIncrementalSync,syncRoot:this.config.getPlaceRoot(r),activeClientId:i.activeClientId,reverseSyncAvailable:a>0,modifiedFileCount:a,applyModes:i.applyModes,directions:i.directions,fileWatcherActive:i.fileWatcher!==null,forwardOnlyClasses:[...Do]};n.status(200).json(o)}catch(r){this.sendError(n,r,"handleSyncStatus")}}async handleSyncStop(e,n){try{let r=e.body,i=r.placeId??this.getDefaultRuntimePlaceId();if(i==null){n.status(200).json({status:"idle",state:"idle",placeId:null,message:"No active sync place"});return}let a=this.places.get(i);if(!a){n.status(200).json({status:"idle",state:"idle",placeId:i,message:`No sync context for place ${i}`});return}if(r.clientId&&a.activeClientId&&r.clientId!==a.activeClientId&&(a.activeFullSyncSessionId!==null||a.state==="syncing"||a.state==="initializing")){n.status(409).json({error:"Conflict",message:`This sync session belongs to client ${a.activeClientId}`});return}let o=a.activeFullSyncSessionId;if(o&&(this.initHandler.clearPreserveLocalFiles(o),this.initHandler.clearPendingServiceTrees(o)),a.fileWatcher&&(await a.fileWatcher.stop(),a.fileWatcher=null),a.incompleteSyncTimer&&(clearTimeout(a.incompleteSyncTimer),a.incompleteSyncTimer=null),o){let s=this.config.getPlaceRoot(i),c=pS.join(s,`explorer_tmp_${o}`);await Ho.rm(c,{recursive:!0,force:!0}).catch(()=>{})}a.tmpWriter&&(a.tmpWriter.stopChangeLogFlusher(),a.tmpWriter=null),a.tmpIndex=null,a.collisionDirMap=null,a.activeFullSyncSessionId=null,a.syncProgress=null,a.state="idle",a.activeClientId=null,a.instanceCount=0,a.scriptCount=0,this.activeFullSyncPlaceId===i&&(this.activeFullSyncPlaceId=null),this.clearRuntimePlaceIfMatch(i),y.info("Sync stopped",{placeId:i,reason:r.reason??"requested"}),n.status(200).json({status:"stopped",state:"idle",placeId:i})}catch(r){this.sendError(n,r,"handleSyncStop")}}async handleSyncConfig(e,n){try{if(e.method==="GET"){n.status(200).json(this.config.getConfig());return}let r=FO(e.body);if(!r.success){n.status(400).json({error:"Validation error",message:r.error});return}let i=r.data;i.maxDepth!==void 0&&this.config.updateConfig({maxDepth:i.maxDepth}),i.maxInstances!==void 0&&this.config.updateConfig({maxInstances:i.maxInstances}),n.status(200).json({status:"updated",config:this.config.getConfig()})}catch(r){this.sendError(n,r,"handleSyncConfig")}}async initialize(){try{await this.config.loadFromMeta(),await this.initHandler.cleanupStaleTempDirs(),y.info("SyncController initialized")}catch(e){y.error("SyncController initialization failed",e instanceof Error?e:new Error(String(e)))}}async shutdown(){try{this.places.clear(),y.info("SyncController shut down")}catch(e){y.error("SyncController shutdown error",e instanceof Error?e:new Error(String(e)))}}async atomicWriteFile(e,n){let r=e+".tmp."+fee().slice(0,8);try{await Ho.writeFile(r,n,"utf-8"),await Ho.rename(r,e)}catch(i){throw await Ho.unlink(r).catch(()=>{}),i}}async handlePreCheck(e,n){try{await this.apiHandler.handlePreCheck(e,n)}catch(r){this.sendError(n,r,"handlePreCheck")}}async handleSyncDirections(e,n){try{await this.apiHandler.handleSyncDirections(e,n)}catch(r){this.sendError(n,r,"handleSyncDirections")}}async handleForwardRestoreList(e,n){try{await this.apiHandler.handleForwardRestoreList(e,n)}catch(r){this.sendError(n,r,"handleForwardRestoreList")}}async handleReversePending(e,n){try{await this.reverseHandler.handleReversePending(e,n)}catch(r){this.sendError(n,r,"handleReversePending")}}async handleReverseSyncChanges(e,n){try{await this.reverseHandler.handleReverseSyncChanges(e,n)}catch(r){this.sendError(n,r,"handleReverseSyncChanges")}}async handleReverseSyncResult(e,n){try{await this.reverseHandler.handleReverseSyncResult(e,n)}catch(r){this.sendError(n,r,"handleReverseSyncResult")}}async handleResolveConflict(e,n){try{await this.reverseHandler.handleResolveConflict(e,n)}catch(r){this.sendError(n,r,"handleResolveConflict")}}async handleReverseRescan(e,n){try{await this.reverseHandler.handleReverseRescan(e,n)}catch(r){this.sendError(n,r,"handleReverseRescan")}}async handleSyncHistory(e,n){try{await this.apiHandler.handleSyncHistory(e,n)}catch(r){this.sendError(n,r,"handleSyncHistory")}}async startFileWatcherForPlace(e){if(e.state!=="syncing"){y.debug("Skipping file watcher start - place not syncing",{placeId:e.placeId,state:e.state});return}e.fileWatcher&&await e.fileWatcher.stop();let n=pS.join(this.config.getPlaceRoot(e.placeId),"explorer");e.fileWatcher=new $f(n,e.index),e.writer.setOnWriteCallback(r=>{e.fileWatcher?.suppressPath(r)}),e.fileWatcher.setDirectionChecker(r=>{let i=oO(r);return e.directions[i]}),e.fileWatcher.setOnForwardViolation(r=>{e.forwardRestoreQueue.includes(r)||(e.forwardRestoreQueue.push(r),y.info("Forward violation queued for restore",{placeId:e.placeId,relativePath:r,queueSize:e.forwardRestoreQueue.length}))}),await e.fileWatcher.start(),y.info("File watcher started for reverse sync",{placeId:e.placeId})}getStatusSummary(){return this.apiHandler.getStatusSummary()}getDirectionForCategory(e){return this.apiHandler.getDirectionForCategory(e)}getStatusDirect(e){return this.apiHandler.getStatusDirect(e)}getConfigDirect(){return this.apiHandler.getConfigDirect()}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,r){await this.apiHandler.writeSyncedFile(e,n,r)}async executeViaDisk(e,n){return this.apiHandler.executeViaDisk(e,n)}sendError(e,n,r){let i=n instanceof Error?n.message:String(n);if(i.includes("Path traversal detected")){e.status(403).json({error:"Forbidden",message:i});return}let a=n.code;if(a==="ENOSPC"||a==="EPERM"||a==="EACCES"){e.status(500).json({error:"Disk error",message:i});return}y.error(`SyncController.${r} failed`,n instanceof Error?n:new Error(i)),e.status(500).json({error:"Internal error",message:`${r}: ${i}`})}};import{randomUUID as mee}from"crypto";function HO(t,e){let n=mee(),r=n.replace(/-/g,"").substring(0,8).toUpperCase(),i=`${r.substring(0,4)}-${r.substring(4,8)}`;return{config:t,app:e,instanceId:n,sessionId:i,startTime:Date.now(),baseUrl:`http://${t.httpHost}:${t.httpPort}`,commandQueue:new Map,pendingCommands:new Map,globalPendingCommands:[],totalCommandsProcessed:0,pluginClients:new Map,mcpInstances:new Map,sseClients:new Set,cachedSelectionMap:new Map,isClientMode:!1,clientModeHealthTimer:null,clientModeConsecutiveHealthFailures:0,clientModeUpstreamReachable:!0,clientModeUpstreamContextCaptureEnabled:!0,clientModeLastHealthSuccessAt:null,clientModeLastHealthFailureAt:null,clientModeLastHealthError:null,historyManager:null,analyticsManager:null,executionContextManager:null,licenseState:null,syncController:null,internalActionExecutor:null,dashboardFolderPicker:null,activeSyncOwnerInstanceId:null,activeProjectRoot:null,pendingDashboardSyncRootPin:null,playtestControlCommand:null,dashboardSyncRootSwitchInFlight:!1,aiClientName:"",pluginVersion:"",syncedSessionToken:null,serverLastCommandAt:null}}var VO=fi(Po(),1);pe();function WO(t){let e=VO.default.json({limit:"5mb"});t.app.use((n,r,i)=>{if(n.path.startsWith("/sync/")){i();return}e(n,r,i)}),t.app.use((n,r,i)=>{r.setHeader("Access-Control-Allow-Origin","http://localhost:3002"),r.setHeader("Access-Control-Allow-Methods","GET, POST, OPTIONS"),r.setHeader("Access-Control-Allow-Headers","Content-Type"),i()}),t.app.use((n,r,i)=>{y.debug(`${n.method} ${n.path}`,{ip:n.ip}),i()})}import{randomUUID as Tl}from"crypto";pe();function hee(){let t=process.env.WEPPY_ROBLOX_MCP_VERSION?.trim();return t||null}var We=hee()??"2.1.2";Lf();function YO(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 yee(t){let n=Date.now();return Array.from(t.pluginClients.values()).filter(r=>n-r.lastSeen<1e4)}function QO(t){let n=Date.now();for(let[r,i]of t.mcpInstances)i.lastSeen&&n-i.lastSeen>15e3&&(t.mcpInstances.delete(r),i.sessionId&&t.executionContextManager?.endSession(i.sessionId),y.debug("Removed stale MCP instance",{instanceId:r,lastSeen:i.lastSeen}))}function eD(t,e,n){let r=e.query.clientId;if(r&&t.pluginClients.has(r)){let i=t.pluginClients.get(r);i.lastSeen=Date.now()}try{let i=e.body;if(!i||!Array.isArray(i.selection)||typeof i.count!="number"){y.warn("Invalid selection update request",{body:i}),n.status(400).json({error:"Invalid request body"});return}let a=r||"unknown",o=Date.now();t.cachedSelectionMap.set(a,{selection:i.selection,count:i.count,timestamp:o,clientId:a}),y.debug("Selection cache updated",{count:i.count,clientId:a,timestamp:o}),n.json({status:"ok",timestamp:o})}catch(i){y.error("Error handling selection update",i),n.status(500).json({error:"Internal server error"})}}function tD(t,e,n){let r=parseInt(e.query.maxAge)||3e4,i=El(t,r);i?n.json({cached:!0,...i}):n.json({cached:!1,message:"No cached selection available"})}function El(t,e=3e4,n){if(t.cachedSelectionMap.size===0)return null;let r;if(n)r=t.cachedSelectionMap.get(n);else for(let a of t.cachedSelectionMap.values())(!r||a.timestamp>r.timestamp)&&(r=a);if(!r)return null;let i=Date.now()-r.timestamp;return e===0||i<=e?r:null}function nD(t,e,n){try{let r=e.body;if(!r.clientId){n.status(400).json({error:"Missing clientId"});return}let i=t.pluginClients.get(r.clientId),a=Date.now(),o={clientId:r.clientId,projectName:r.projectName,placeName:r.placeName,placeId:r.placeId,pluginVersion:r.pluginVersion,connectedAt:i?.connectedAt||a,lastSeen:a,commandsProcessed:i?.commandsProcessed||0,connectionType:"polling"};t.pluginClients.set(r.clientId,o),t.pendingCommands.has(r.clientId)||t.pendingCommands.set(r.clientId,[]),r.pluginVersion&&(t.pluginVersion=r.pluginVersion),$t(t,"connection",{clientId:r.clientId,placeId:o.projectName,placeName:o.placeName,status:"connected"}),bl(t,{timestamp:new Date().toISOString(),type:"plugin",status:"connected",clientId:r.clientId,message:`Plugin connected \u2014 ${r.clientId}`,...o.placeId!==void 0?{placeId:o.placeId}:{},...o.placeName?{placeName:o.placeName}:{}}),typeof o.placeId=="number"&&Number.isFinite(o.placeId)&&Nw(t,o.placeId,o.placeName??null),t.analyticsManager&&(r.pluginVersion&&t.analyticsManager.setPluginVersion(r.pluginVersion),t.analyticsManager.trackPluginConnected()),y.info("Plugin client registered",{clientId:r.clientId,projectName:r.projectName,placeName:r.placeName,isReconnect:!!i}),n.json({status:"ok",clientId:r.clientId,serverInstanceId:t.instanceId,sessionId:t.sessionId,mcpVersion:We,connectedAt:o.connectedAt,aiClientNames:YO(t),serverStartTime:t.startTime,mcpInstanceCount:t.mcpInstances.size+1})}catch(r){y.error("Error registering plugin client",r),n.status(500).json({error:"Internal server error"})}}function rD(t,e,n){let r=e.body?.clientId;if(!r){n.status(400).json({error:"Missing clientId"});return}let i=t.pluginClients.has(r);t.pluginClients.delete(r),t.pendingCommands.delete(r),i&&($t(t,"connection",{clientId:r,status:"disconnected"}),bl(t,{timestamp:new Date().toISOString(),type:"plugin",status:"disconnected",clientId:r,message:`Plugin disconnected \u2014 ${r}`})),y.info("Plugin client unregistered",{clientId:r,existed:i}),n.json({status:"ok",existed:i})}function iD(t,e,n){try{let r=e.body;if(!r.instanceId){n.status(400).json({error:"Missing instanceId"});return}let i=Date.now(),a={instanceId:r.instanceId,...typeof r.sessionId=="string"?{sessionId:r.sessionId}:{},pid:r.pid,connectedAt:i,isServer:!1,lastSeen:i};r.aiClientName&&(a.aiClientName=r.aiClientName),r.cwd&&(a.cwd=r.cwd),"projectRoot"in r&&(a.projectRoot=r.projectRoot),t.mcpInstances.set(r.instanceId,a),$t(t,"mcp_status",{aiClientName:a.aiClientName??"Unknown",instanceId:r.instanceId,status:"registered"}),bl(t,{timestamp:new Date().toISOString(),type:"mcp",status:"registered",instanceId:r.instanceId,message:`MCP registered \u2014 ${a.aiClientName??r.instanceId}`,...a.aiClientName?{aiClientName:a.aiClientName}:{}}),y.info("MCP instance registered (client mode)",{instanceId:r.instanceId,pid:r.pid,cwd:r.cwd}),n.json({status:"ok",instanceId:r.instanceId,serverInstanceId:t.instanceId,sessionId:t.sessionId,mcpVersion:We,mcpInstanceCount:t.mcpInstances.size+1})}catch(r){y.error("Error registering MCP instance",r),n.status(500).json({error:"Internal server error"})}}function aD(t,e,n){let r=e.body?.instanceId;if(!r){n.status(400).json({error:"Missing instanceId"});return}let i=t.mcpInstances.get(r),a=!!i;t.mcpInstances.delete(r),i?.sessionId&&t.executionContextManager?.endSession(i.sessionId),a&&($t(t,"mcp_status",{aiClientName:i?.aiClientName??"Unknown",instanceId:r,status:"unregistered"}),bl(t,{timestamp:new Date().toISOString(),type:"mcp",status:"unregistered",instanceId:r,message:`MCP unregistered \u2014 ${i?.aiClientName??r}`,...i?.aiClientName?{aiClientName:i.aiClientName}:{}})),y.info("MCP instance unregistered",{instanceId:r,existed:a}),n.json({status:"ok",existed:a})}function oD(t,e,n){let{instanceId:r,aiClientName:i}=e.body;if(r&&i){let a=t.mcpInstances.get(r);a&&(a.aiClientName=i)}n.json({status:"ok"})}function sD(t){let n=Date.now(),r=Tn({appDataDir:t.config.appDataDir});for(let[i,a]of t.pluginClients)n-a.lastSeen>3e4&&(t.pluginClients.delete(i),t.pendingCommands.delete(i));return QO(t),{serverInstanceId:t.instanceId,sessionId:t.sessionId,mcpVersion:We,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:r,...t.aiClientName?{aiClientName:t.aiClientName}:{}},...Array.from(t.mcpInstances.values())],mcpInstanceCount:t.mcpInstances.size+1}}function cD(t,e){e.json(sD(t))}function lD(t,e,n){let r=e.query.instanceId;r&&t.mcpInstances.has(r)&&(t.mcpInstances.get(r).lastSeen=Date.now()),QO(t);let i=yee(t),a=t.sseClients.size,c={...{status:"online",connectedClients:a+i.length,queuedCommands:t.commandQueue.size,uptime:Date.now()-t.startTime,version:We,enableContextCapture:t.executionContextManager?.isEnabled()??t.config.enableContextCapture??!0,isClientMode:t.isClientMode,pid:process.pid,sessionId:t.sessionId},instanceId:t.instanceId,mcpInstanceCount:t.mcpInstances.size+1,aiClientNames:YO(t),pluginVersion:t.pluginVersion||void 0,sseClients:a,dashboardSseClients:t.dashboardSseClients?.size??0,pollingClients:i.length,pluginClients:i.map(l=>({clientId:l.clientId,projectName:l.projectName,placeName:l.placeName,pluginVersion:l.pluginVersion,lastSeen:Date.now()-l.lastSeen})),...t.isClientMode?{upstream:{reachable:t.clientModeUpstreamReachable,consecutiveFailures:t.clientModeConsecutiveHealthFailures,lastSuccessAt:t.clientModeLastHealthSuccessAt,lastFailureAt:t.clientModeLastHealthFailureAt,lastError:t.clientModeLastHealthError,baseUrl:t.baseUrl}}:{}};n.json(c)}function uD(t,e,n,r){let i=e.ip||e.socket.remoteAddress||"";if(!(i==="127.0.0.1"||i==="::1"||i==="::ffff:127.0.0.1"||i==="localhost")){y.warn("Shutdown request rejected from non-localhost",{ip:i}),n.status(403).json({error:"Forbidden: localhost only"});return}y.info("Shutdown request received, initiating graceful shutdown",{requestedBy:i,uptime:Date.now()-t.startTime}),n.json({status:"shutting_down",message:"Server will shutdown gracefully",pid:process.pid}),setTimeout(async()=>{try{await r(),y.info("Graceful shutdown completed"),process.exit(0)}catch(o){y.error("Error during graceful shutdown",o),process.exit(1)}},100)}async function Uf(t){if(t.isClientMode)try{let e=await fetch(`${t.baseUrl}/connection-info`);if(e.ok)return await e.json()}catch(e){y.warn("Failed to fetch connection info from server",{error:e})}return sD(t)}pe();var Ff={query_instances:{discriminator:"action",mapping:{get:"get_instance",children:"get_instance_children",find_child:"find_first_child",find_descendant:"find_first_descendant",wait_for_child:"wait_for_child",class_info:"get_class_info",search_name:"search_by_name",search_class:"search_by_class",search_property:"search_by_property",search_tag:"search_by_tag",file_tree:"get_file_tree",project_structure:"get_project_structure",descendants:"get_descendants",ancestors:"get_ancestors"},paramAliases:{search_by_name:{query:"pattern"},search_by_property:{root:"rootPath"},search_by_tag:{root:"rootPath"},get_project_structure:{root:"rootPath"}}},mutate_instances:{discriminator:"action",mapping:{create:"create_instance",create_with_props:"create_instance_with_properties",delete:"delete_instance",clone:"clone_instance",move:"move_instance",rename:"rename_instance",pivot:"pivot_to",create_tree:"create_instance_tree",mass_create:"mass_create_instances",mass_delete:"mass_delete_instances",mass_duplicate:"mass_duplicate",smart_duplicate:"smart_duplicate"},paramAliases:{clone_instance:{path:"sourcePath"}}},manage_properties:{discriminator:"action",mapping:{get:"get_property",set:"set_property",get_all:"get_all_properties",set_multiple:"set_multiple_properties",get_attr:"get_attribute",set_attr:"set_attribute",get_all_attrs:"get_all_attributes",delete_attr:"delete_attribute",add_tag:"add_tag",remove_tag:"remove_tag",check_tag:"has_tag",get_tags:"get_tags",get_tagged:"get_tagged",set_calculated:"set_calculated_property",set_relative:"set_relative_property",mass_set:"mass_set_property",mass_get:"mass_get_property",modify_children:"modify_children"},paramAliases:{get_tagged:{tagName:"tag",root:"rootPath"},set_relative_property:{amount:"value"}}},manage_scripts:{discriminator:"action",mapping:{get_source:"get_script_source",set_source:"set_script_source",create:"create_script",delete:"delete_script",edit_replace:"edit_script_lines",edit_insert:"insert_script_lines",edit_delete:"delete_script_lines",search:"search_in_scripts",replace:"replace_in_scripts",get_dependencies:"get_script_dependencies"},paramAliases:{edit_script_lines:{newLines:"newContent"},insert_script_lines:{lines:"content"},replace_in_scripts:{pattern:"searchPattern"}}},manage_lighting:{discriminator:"action",mapping:{lighting:"set_lighting",atmosphere:"set_atmosphere",sky:"set_sky",terrain_props:"set_terrain",time:"set_time_of_day"}},manage_selection:{discriminator:"action",mapping:{get:"get_selection",set:"set_selection",clear:"clear_selection",cached:"get_cached_selection",context:"get_selection_context",details:"get_selection_details",add:"add_to_selection",remove:"remove_from_selection",watch:"watch_selection"}},manage_camera:{discriminator:"action",mapping:{info:"get_camera_info",focus_path:"focus_camera_path",focus_position:"focus_camera_position",suggest:"get_suggested_camera_view"},paramAliases:{get_suggested_camera_view:{path:"targetPath"}}},manage_tween:{discriminator:"action",mapping:{create:"create_tween",play:"play_tween",pause:"pause_tween",cancel:"cancel_tween"}},manage_audio:{discriminator:"action",mapping:{play:"play_sound",stop:"stop_sound",pause:"pause_sound",resume:"resume_sound",set_listener:"set_listener"}},manage_animation:{discriminator:"action",mapping:{load:"load_animation",play:"play_animation",stop:"stop_animation",get_tracks:"get_animation_tracks"}},manage_physics:{discriminator:"action",mapping:{register_group:"register_collision_group",set_collidable:"set_collidable",get_groups:"get_collision_groups"}},manage_effects:{discriminator:"action",mapping:{emit:"emit_particles",clear:"clear_particles",toggle:"toggle_effect"}},manage_terrain:{discriminator:"action",mapping:{fill_block:"terrain_fill_block",fill_ball:"terrain_fill_ball",fill_cylinder:"terrain_fill_cylinder",fill_wedge:"terrain_fill_wedge",clear_region:"terrain_clear",clear_bounds:"terrain_clear_region",replace_material:"terrain_replace_material",colors_get:"terrain_get_material_color",colors_set:"terrain_set_material_color",read_voxel:"terrain_read_voxel",read_voxels:"terrain_read_voxels",write_voxels:"terrain_write_voxels",generate:"terrain_generate",smooth:"terrain_smooth"}},spatial_query:{discriminator:"action",mapping:{raycast:"raycast",find_ground:"find_ground",check_placement:"check_placement",multi_raycast:"multi_raycast",scan_area:"scan_area",find_flat:"find_flat_areas",find_spawn:"find_spawn_positions",analyze_walkable:"analyze_walkable_area",spatial_map:"get_spatial_map",find_space:"find_empty_space",bounds:"get_bounds",snap_grid:"snap_to_grid",collision:"check_collision"},paramAliases:{get_spatial_map:{path:"rootPath"}}},manage_assets:{discriminator:"action",mapping:{insert:"insert_model",info:"get_asset_info",search:"search_creator_store",search_insert:"search_and_insert_model",insert_free:"insert_free_model",insert_package:"insert_package",export:"export_selection"},paramAliases:{search_creator_store:{maxResults:"limit"}}},manage_sync:{discriminator:"action",mapping:{status:"sync_status",config:"sync_config",history:"sync_history",directions:"sync_directions",read_file:"sync_read_file",write_file:"sync_write_file",progress:"sync_progress"}},workspace_state:{discriminator:"action",mapping:{sync:"sync_workspace_state",snapshot:"get_workspace_snapshot",changes:"get_recent_changes",viewport:"get_viewport_info",clear_history:"clear_change_history",metadata:"get_workspace_metadata",scripts:"get_script_list",selection_info:"get_selection_info",clear_cache:"clear_state_cache"}},manage_logs:{discriminator:"action",mapping:{get:"get_output_logs",clear:"clear_output_logs",errors:"get_recent_errors"},paramAliases:{get_output_logs:{level:"type"}}},system_info:{discriminator:"action",mapping:{ping:"ping",connection:"get_connection_info",usage:"get_usage_status",place_info:"get_place_info",services:"get_services",studio_settings:"get_studio_settings",play:"start_playtest",stop:"stop_playtest",pause:"pause_playtest",resume:"resume_playtest",play_status:"get_play_status",run_test:"run_test"}}};var vee={get_cached_selection:"internal",sync_status:"internal",sync_config:"internal",sync_history:"internal",sync_directions:"internal",sync_read_file:"internal",sync_write_file:"internal",sync_progress:"internal",get_connection_info:"internal",run_test:"internal"};function qf(t){return vee[t]||"plugin"}var Bf=100,xee=Object.entries(Ff).reduce((t,[e,n])=>{for(let r of Object.values(n.mapping))t[r]=e;return t},{});function pD(t){return{toolName:xee[t]||t,actionName:t}}function dD(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 fD(t,e,n){y.info("Plugin connected via SSE"),n.setHeader("Content-Type","text/event-stream"),n.setHeader("Cache-Control","no-cache"),n.setHeader("Connection","keep-alive"),t.sseClients.add(n),xS(n,{event:"command",id:Tl(),data:{action:"connected",requestId:Tl(),params:{serverVersion:We,timestamp:Date.now()}}});let r=setInterval(()=>{xS(n,{event:"command",id:Tl(),data:{action:"keepalive",requestId:Tl(),params:{timestamp:Date.now()}}})},3e4);n.on("close",()=>{y.info("Plugin disconnected from SSE"),clearInterval(r),t.sseClients.delete(n)})}function xS(t,e){let n=JSON.stringify(e.data);t.write(`event: ${e.event}
127
127
  `),t.write(`id: ${e.id}
128
128
  `),t.write(`data: ${n}
129
129
 
@@ -131,17 +131,17 @@ data: ${JSON.stringify(n)}
131
131
  `)+`
132
132
  `;await Vf.appendFile(l,p,"utf-8");try{let d="";try{d=await Vf.readFile(u,"utf-8")}catch{}let m=(d+p).split(`
133
133
  `).slice(-500).join(`
134
- `);await Vf.writeFile(u,m,"utf-8")}catch(d){y.warn("Failed to update current.log",{error:d})}y.debug("Logs received from plugin",{count:i.logs.length,clientId:r||"legacy",logFile:c}),n.json({status:"ok",logsReceived:i.logs.length,logFile:c})}catch(i){y.error("Error handling logs",i),n.status(500).json({error:"Internal server error"})}}function xD(t,e){let{app:n}=t;n.get("/events",(r,i)=>fD(t,r,i)),n.get("/commands",(r,i)=>mD(t,r,i)),n.post("/result",(r,i)=>hD(t,r,i)),n.post("/selection-update",(r,i)=>eD(t,r,i)),n.post("/execute",(r,i)=>gD(t,r,i)),n.get("/cached-selection",(r,i)=>tD(t,r,i)),n.post("/register-plugin",(r,i)=>nD(t,r,i)),n.post("/unregister-plugin",(r,i)=>rD(t,r,i)),n.post("/register-mcp",(r,i)=>iD(t,r,i)),n.post("/unregister-mcp",(r,i)=>aD(t,r,i)),n.post("/set-ai-client-name",(r,i)=>oD(t,r,i)),n.get("/connection-info",(r,i)=>cD(t,i)),n.post("/logs",(r,i)=>vD(t,r,i)),yD(t),n.get("/status",(r,i)=>lD(t,r,i)),n.post("/shutdown",(r,i)=>uD(t,r,i,e)),n.post("/playtest-control",(r,i)=>{let a=r.body?.command;if(!a||!["stop","pause","resume"].includes(a)){i.status(400).json({error:"Invalid command. Use: stop, pause, resume"});return}t.playtestControlCommand=a,i.json({success:!0,command:a})}),n.get("/playtest-control",(r,i)=>{let a=t.playtestControlCommand;t.playtestControlCommand=null,i.json({command:a??""})}),n.options("*",(r,i)=>{i.sendStatus(200)})}var bD=fi(Po(),1),Iee=new Set(["explorer_session_start","explorer_session_end","explorer_session_heartbeat","explorer_view_open","explorer_command"]),Pee=new Set(["event_source","explorer_version","platform","locale","timezone","has_sync_root","place_count","mcp_online","session_id","engagement_time_msec","timestamp_micros","view_name","command_name","result_count_bucket"]),Cee=new Set(["refresh","search","open_file","copy_instance_path","reveal_in_explorer","collapse_all"]),$ee=new Set(["robloxInstanceTree"]),Eee=new Set(["0","1_9","10_49","50_299","300_plus"]),Tee=["session_id","engagement_time_msec","timestamp_micros"],Ree=25;function jee(t){return typeof t=="string"||typeof t=="number"&&Number.isFinite(t)}function Aee(t,e){return jee(e)?t==="command_name"?typeof e=="string"&&Cee.has(e):t==="view_name"?typeof e=="string"&&$ee.has(e):t==="result_count_bucket"?typeof e=="string"&&Eee.has(e):!0:!1}function Nee(t){let e=[];for(let n of t){if(!n||typeof n!="object")continue;let{name:r,params:i}=n;if(typeof r!="string"||!Iee.has(r))continue;let a={event_source:"explorer"};if(i&&typeof i=="object")for(let[o,s]of Object.entries(i))Pee.has(o)&&Aee(o,s)&&(a[o]=s);Tee.some(o=>typeof a[o]!="number")||(a.event_source="explorer",e.push({name:r,params:a}))}return e}function _D(t){let e=(0,bD.Router)();return e.post("/api/explorer/analytics",(n,r)=>{let i=n.body;if(!t.analyticsManager||!t.analyticsManager.isEnabled()){r.status(202).json({ok:!0,accepted:0,telemetryEnabled:!1});return}if(typeof i?.client_id!="string"||i.client_id.trim().length===0||!Array.isArray(i.events)||i.events.length===0){r.status(400).json({error:"client_id string and non-empty events array are required"});return}if(i.events.length>Ree){r.status(400).json({error:"events array must contain at most 25 items"});return}let a=Nee(i.events);if(a.length===0){r.status(400).json({error:"no valid explorer analytics events found"});return}t.analyticsManager.trackExplorerEvents(i.client_id,a),r.status(202).json({ok:!0,accepted:a.length,telemetryEnabled:!0})}),e}var $D=fi(Po(),1);Et();pe();var wD=new WeakSet,Wf=new WeakSet,SD=new WeakMap;function ED(t){let e=SD.get(t);return e||(e=new Map,SD.set(t,e)),e}function kD(t){let e=t.activeSyncOwnerInstanceId;return!e||e===t.instanceId?t.sessionId:t.mcpInstances.get(e)?.sessionId??t.sessionId}function TD(t,e,n){if(n.length===0)return;let r=ED(t),i=r.get(e)??new Set;for(let a of n)i.add(_l(a));r.set(e,i)}function ID(t,e){let{writer:n,tmpWriter:r,placeId:i}=e;n.setHistoryContextIdResolver(()=>t.executionContextManager?.findActiveContextSync?.(kD(t),i)?.contextId??null),Wf.has(n)||(n.setOnHistoryFlushCallback(()=>{PD(t,i)}),Wf.add(n)),r&&r.setHistoryContextIdResolver(()=>t.executionContextManager?.findActiveContextSync?.(kD(t),i)?.contextId??null),r&&!Wf.has(r)&&(r.setOnHistoryFlushCallback(()=>{PD(t,i)}),Wf.add(r))}function kr(t){let e=t.syncController;if(e){if(!wD.has(e)){let n=e.getOrCreatePlaceContext.bind(e);e.getOrCreatePlaceContext=async(r,i)=>{let a=await n(r,i);return ID(t,a),a},wD.add(e)}for(let n of e.places.values())ID(t,n)}}async function PD(t,e){let n=await ni(t,_r),r=Ki(n,{placeId:e});if(!r)return;let i=t.syncController?.places.get(e)?.placeName,a=r.entryId,o=ED(t),s=o.get(a)??new Set,c=r.changes,l=c.filter(u=>!s.has(_l(u)));if(l.length===0){o.set(a,new Set(c.map(u=>_l(u))));return}for(let u of l)await Dw(t,e,i,{entryId:r.entryId,sessionId:r.sessionId,placeId:e,timestamp:u.timestamp,change:u}),$t(t,"game_change",u);o.set(a,new Set(c.map(u=>_l(u))))}function CD(t){if(!t.syncController)return;let e=t.syncController.getDefaultRuntimePlaceId();if(e==null){$t(t,"sync",{placeId:null,status:"idle"});return}let n=t.syncController.places.get(e);n&&$t(t,"sync",{placeId:String(e),status:n.state,detail:n.placeName||void 0})}function RD(t){let e=$D.default.json({limit:"5mb"});t.app.post("/sync/init",e,async(n,r)=>{if(n.body.phase==="start"){let a=IS(t);if(!a.success){r.status(400).json({error:"PROJECT_ROOT_UNRESOLVED",message:"\uBAA8\uB4E0 MCP \uC778\uC2A4\uD134\uC2A4\uC758 projectRoot\uAC00 unresolved\uC785\uB2C8\uB2E4. \uC720\uD6A8\uD55C \uD504\uB85C\uC81D\uD2B8 \uB514\uB809\uD1A0\uB9AC\uC5D0\uC11C MCP\uB97C \uC2E4\uD589\uD558\uC138\uC694."});return}let o=Bt(a.projectRoot),s=t.syncController?.getSyncRoot?.();if(t.activeSyncOwnerInstanceId=a.instanceId,t.activeProjectRoot=a.projectRoot,t.pendingDashboardSyncRootPin===a.projectRoot&&(t.pendingDashboardSyncRootPin=null),!(t.syncController?.activeFullSyncPlaceId!==null&&t.syncController?.activeFullSyncPlaceId!==void 0)&&(!t.syncController||s!==o))try{t.syncController&&await t.syncController.shutdown(),t.syncController=new si(a.projectRoot,{appDataDir:t.config.appDataDir}),await t.syncController.initialize(),kr(t),y.info("SyncController \uC7AC\uC0DD\uC131 (projectRoot \uBCC0\uACBD)",{owner:a.instanceId,projectRoot:a.projectRoot,syncRoot:o})}catch(l){y.error("SyncController \uC7AC\uC0DD\uC131 \uC2E4\uD328",l instanceof Error?l:new Error(String(l))),r.status(500).json({error:"Failed to initialize sync controller"});return}}if(!t.syncController){r.json({active:!1});return}t.syncController.handleSyncInit(n,r).then(()=>{kr(t),CD(t)}).catch(()=>{})}),t.app.post("/sync/update",e,(n,r)=>{if(!t.syncController){r.json({active:!1});return}t.syncController.handleSyncUpdate(n,r).then(()=>{kr(t)}).catch(()=>{})}),t.app.post("/sync/stop",e,(n,r)=>{if(!t.syncController){r.json({active:!1});return}t.syncController.handleSyncStop(n,r).then(()=>{kr(t),CD(t)}).catch(()=>{})}),t.app.get("/sync/status",(n,r)=>{if(!t.syncController){r.json({active:!1});return}t.syncController.handleSyncStatus(n,r)}),t.app.get("/sync/config",(n,r)=>{if(!t.syncController){r.json({active:!1});return}t.syncController.handleSyncConfig(n,r)}),t.app.post("/sync/config",e,(n,r)=>{if(!t.syncController){r.json({active:!1});return}t.syncController.handleSyncConfig(n,r)}),t.app.get("/sync/pre-check",(n,r)=>{if(!IS(t).success){r.status(200).json({hasPreviousSync:!1,changes:[],summary:{modified:0,deleted:0,added:0,total:0},ownershipUnresolved:!0,ownershipError:"PROJECT_ROOT_UNRESOLVED"});return}if(!t.syncController){r.json({active:!1});return}t.syncController.handlePreCheck(n,r)}),t.app.get("/sync/history",(n,r)=>{if(!t.syncController){r.json({active:!1});return}t.syncController.handleSyncHistory(n,r)}),t.app.get("/sync/reverse-pending",(n,r)=>{if(!t.syncController){r.json({active:!1});return}t.syncController.handleReversePending(n,r)}),t.app.get("/sync/reverse-changes",(n,r)=>{if(!t.syncController){r.json({active:!1});return}t.syncController.handleReverseSyncChanges(n,r)}),t.app.post("/sync/reverse-result",e,(n,r)=>{if(!t.syncController){r.json({active:!1});return}t.syncController.handleReverseSyncResult(n,r).then(()=>{kr(t)}).catch(()=>{})}),t.app.post("/sync/reverse-rescan",e,(n,r)=>{if(!t.syncController){r.status(503).json({error:"Sync not initialized"});return}t.syncController.handleReverseRescan(n,r)}),t.app.post("/sync/resolve-conflict",e,(n,r)=>{if(!t.syncController){r.status(503).json({error:"Sync not initialized"});return}t.syncController.handleResolveConflict(n,r)}),t.app.post("/sync/directions",e,(n,r)=>{if(!t.syncController){r.status(503).json({error:"Sync not initialized"});return}t.syncController.handleSyncDirections(n,r)}),t.app.get("/sync/forward-restore-list",(n,r)=>{if(!t.syncController){r.status(503).json({error:"Sync not initialized"});return}t.syncController.handleForwardRestoreList(n,r)})}var sz=fi(Po(),1);pe();import*as Vo from"fs";import*as PS from"path";var na={LOG_LEVEL:"info",ENABLE_LOCAL_HISTORY:!0,ENABLE_LOCAL_STATISTICS:!0,ENABLE_CONTEXT_CAPTURE:!0,LICENSE_PROVIDER:"gumroad",REQUEST_TIMEOUT:3e4},jD="dashboard-settings.json";function Gf(t){let e=PS.join(t,jD);try{let n=Vo.readFileSync(e,"utf-8"),r=JSON.parse(n);return{...na,...r}}catch{return{...na}}}function AD(t,e){let n=PS.join(t,jD);try{Vo.mkdirSync(t,{recursive:!0}),Vo.writeFileSync(n,JSON.stringify(e,null,2))}catch(r){y.error("\uB300\uC2DC\uBCF4\uB4DC \uC124\uC815 \uC800\uC7A5 \uC2E4\uD328",r)}}function ND(t,e){let r={...{LOG_LEVEL:y.getLevel(),ENABLE_LOCAL_HISTORY:e.historyManager?.isLocalHistoryEnabled()??na.ENABLE_LOCAL_HISTORY,ENABLE_LOCAL_STATISTICS:e.historyManager?.isLocalStatisticsEnabled()??na.ENABLE_LOCAL_STATISTICS,ENABLE_CONTEXT_CAPTURE:e.executionContextManager?.isEnabled()??e.config.enableContextCapture??na.ENABLE_CONTEXT_CAPTURE,LICENSE_PROVIDER:e.config.licenseProvider??na.LICENSE_PROVIDER,REQUEST_TIMEOUT:e.config.requestTimeout??na.REQUEST_TIMEOUT},...t};if(t.LOG_LEVEL!==void 0){let i=t.LOG_LEVEL;y.setLevel(i),y.info("\uB85C\uADF8 \uB808\uBCA8 \uBCC0\uACBD",{level:i})}return t.ENABLE_LOCAL_HISTORY!==void 0&&e.historyManager&&(e.historyManager.setLocalHistoryEnabled(t.ENABLE_LOCAL_HISTORY),y.info("\uB85C\uCEEC \uD788\uC2A4\uD1A0\uB9AC \uC124\uC815 \uBCC0\uACBD",{enabled:t.ENABLE_LOCAL_HISTORY})),t.ENABLE_LOCAL_STATISTICS!==void 0&&e.historyManager&&(e.historyManager.setLocalStatisticsEnabled(t.ENABLE_LOCAL_STATISTICS),y.info("\uB85C\uCEEC \uD1B5\uACC4 \uC124\uC815 \uBCC0\uACBD",{enabled:t.ENABLE_LOCAL_STATISTICS})),t.ENABLE_CONTEXT_CAPTURE!==void 0&&(e.config.enableContextCapture=t.ENABLE_CONTEXT_CAPTURE,e.executionContextManager?.setEnabled(t.ENABLE_CONTEXT_CAPTURE),y.info("\uCEE8\uD14D\uC2A4\uD2B8 \uCEA1\uCC98 \uC124\uC815 \uBCC0\uACBD",{enabled:t.ENABLE_CONTEXT_CAPTURE})),t.REQUEST_TIMEOUT!==void 0&&(e.config.requestTimeout=t.REQUEST_TIMEOUT,y.info("\uC694\uCCAD \uD0C0\uC784\uC544\uC6C3 \uBCC0\uACBD",{timeout:t.REQUEST_TIMEOUT})),t.LICENSE_PROVIDER!==void 0&&(e.config.licenseProvider=t.LICENSE_PROVIDER,y.info("\uB77C\uC774\uC120\uC2A4 \uD504\uB85C\uBC14\uC774\uB354 \uBCC0\uACBD",{provider:t.LICENSE_PROVIDER})),r}pe();import*as Nl from"fs";import*as Wo from"path";function Oee(){return{version:2,lastUpdated:new Date().toISOString(),totalSessions:0,totalCalls:0,totalSuccessCalls:0,totalFailedCalls:0,tools:{},tierSummary:{basic:{totalCalls:0,successCount:0,failureCount:0},pro:{totalCalls:0,successCount:0,failureCount:0}}}}function ra(t){let e=vn(t);return{placeId:e,placeName:Tt(t,e)}}function OD(t){return async(e,n)=>{try{n.json(await NN(t))}catch(r){y.error("place summary \uC870\uD68C \uC2E4\uD328",r),n.status(500).json({error:"Failed to get place summary"})}}}function DD(t){let e=[];for(let n of t.split(`
134
+ `);await Vf.writeFile(u,m,"utf-8")}catch(d){y.warn("Failed to update current.log",{error:d})}y.debug("Logs received from plugin",{count:i.logs.length,clientId:r||"legacy",logFile:c}),n.json({status:"ok",logsReceived:i.logs.length,logFile:c})}catch(i){y.error("Error handling logs",i),n.status(500).json({error:"Internal server error"})}}function xD(t,e){let{app:n}=t;n.get("/events",(r,i)=>fD(t,r,i)),n.get("/commands",(r,i)=>mD(t,r,i)),n.post("/result",(r,i)=>hD(t,r,i)),n.post("/selection-update",(r,i)=>eD(t,r,i)),n.post("/execute",(r,i)=>gD(t,r,i)),n.get("/cached-selection",(r,i)=>tD(t,r,i)),n.post("/register-plugin",(r,i)=>nD(t,r,i)),n.post("/unregister-plugin",(r,i)=>rD(t,r,i)),n.post("/register-mcp",(r,i)=>iD(t,r,i)),n.post("/unregister-mcp",(r,i)=>aD(t,r,i)),n.post("/set-ai-client-name",(r,i)=>oD(t,r,i)),n.get("/connection-info",(r,i)=>cD(t,i)),n.post("/logs",(r,i)=>vD(t,r,i)),yD(t),n.get("/status",(r,i)=>lD(t,r,i)),n.post("/shutdown",(r,i)=>uD(t,r,i,e)),n.post("/playtest-control",(r,i)=>{let a=r.body?.command;if(!a||!["stop","pause","resume"].includes(a)){i.status(400).json({error:"Invalid command. Use: stop, pause, resume"});return}t.playtestControlCommand=a,i.json({success:!0,command:a})}),n.get("/playtest-control",(r,i)=>{let a=t.playtestControlCommand;t.playtestControlCommand=null,i.json({command:a??""})}),n.options("*",(r,i)=>{i.sendStatus(200)})}var bD=fi(Po(),1),Iee=new Set(["explorer_session_start","explorer_session_end","explorer_session_heartbeat","explorer_view_open","explorer_command"]),Pee=new Set(["event_source","explorer_version","platform","locale","timezone","has_sync_root","place_count","mcp_online","session_id","engagement_time_msec","timestamp_micros","view_name","command_name","result_count_bucket"]),Cee=new Set(["refresh","search","open_file","copy_instance_path","reveal_in_explorer","collapse_all"]),$ee=new Set(["robloxInstanceTree"]),Eee=new Set(["0","1_9","10_49","50_299","300_plus"]),Tee=["session_id","engagement_time_msec","timestamp_micros"],Ree=25;function jee(t){return typeof t=="string"||typeof t=="number"&&Number.isFinite(t)}function Aee(t,e){return jee(e)?t==="command_name"?typeof e=="string"&&Cee.has(e):t==="view_name"?typeof e=="string"&&$ee.has(e):t==="result_count_bucket"?typeof e=="string"&&Eee.has(e):!0:!1}function Nee(t){let e=[];for(let n of t){if(!n||typeof n!="object")continue;let{name:r,params:i}=n;if(typeof r!="string"||!Iee.has(r))continue;let a={event_source:"explorer"};if(i&&typeof i=="object")for(let[o,s]of Object.entries(i))Pee.has(o)&&Aee(o,s)&&(a[o]=s);Tee.some(o=>typeof a[o]!="number")||(a.event_source="explorer",e.push({name:r,params:a}))}return e}function _D(t){let e=(0,bD.Router)();return e.post("/api/explorer/analytics",(n,r)=>{let i=n.body;if(!t.analyticsManager||!t.analyticsManager.isEnabled()){r.status(202).json({ok:!0,accepted:0,telemetryEnabled:!1});return}if(!Array.isArray(i.events)||i.events.length===0){r.status(400).json({error:"non-empty events array is required"});return}if(i.events.length>Ree){r.status(400).json({error:"events array must contain at most 25 items"});return}let a=Nee(i.events);if(a.length===0){r.status(400).json({error:"no valid explorer analytics events found"});return}t.analyticsManager.trackExplorerEvents(a),r.status(202).json({ok:!0,accepted:a.length,telemetryEnabled:!0})}),e}var $D=fi(Po(),1);Et();pe();var wD=new WeakSet,Wf=new WeakSet,SD=new WeakMap;function ED(t){let e=SD.get(t);return e||(e=new Map,SD.set(t,e)),e}function kD(t){let e=t.activeSyncOwnerInstanceId;return!e||e===t.instanceId?t.sessionId:t.mcpInstances.get(e)?.sessionId??t.sessionId}function TD(t,e,n){if(n.length===0)return;let r=ED(t),i=r.get(e)??new Set;for(let a of n)i.add(_l(a));r.set(e,i)}function ID(t,e){let{writer:n,tmpWriter:r,placeId:i}=e;n.setHistoryContextIdResolver(()=>t.executionContextManager?.findActiveContextSync?.(kD(t),i)?.contextId??null),Wf.has(n)||(n.setOnHistoryFlushCallback(()=>{PD(t,i)}),Wf.add(n)),r&&r.setHistoryContextIdResolver(()=>t.executionContextManager?.findActiveContextSync?.(kD(t),i)?.contextId??null),r&&!Wf.has(r)&&(r.setOnHistoryFlushCallback(()=>{PD(t,i)}),Wf.add(r))}function kr(t){let e=t.syncController;if(e){if(!wD.has(e)){let n=e.getOrCreatePlaceContext.bind(e);e.getOrCreatePlaceContext=async(r,i)=>{let a=await n(r,i);return ID(t,a),a},wD.add(e)}for(let n of e.places.values())ID(t,n)}}async function PD(t,e){let n=await ni(t,_r),r=Ki(n,{placeId:e});if(!r)return;let i=t.syncController?.places.get(e)?.placeName,a=r.entryId,o=ED(t),s=o.get(a)??new Set,c=r.changes,l=c.filter(u=>!s.has(_l(u)));if(l.length===0){o.set(a,new Set(c.map(u=>_l(u))));return}for(let u of l)await Dw(t,e,i,{entryId:r.entryId,sessionId:r.sessionId,placeId:e,timestamp:u.timestamp,change:u}),$t(t,"game_change",u);o.set(a,new Set(c.map(u=>_l(u))))}function CD(t){if(!t.syncController)return;let e=t.syncController.getDefaultRuntimePlaceId();if(e==null){$t(t,"sync",{placeId:null,status:"idle"});return}let n=t.syncController.places.get(e);n&&$t(t,"sync",{placeId:String(e),status:n.state,detail:n.placeName||void 0})}function RD(t){let e=$D.default.json({limit:"5mb"});t.app.post("/sync/init",e,async(n,r)=>{if(n.body.phase==="start"){let a=IS(t);if(!a.success){r.status(400).json({error:"PROJECT_ROOT_UNRESOLVED",message:"\uBAA8\uB4E0 MCP \uC778\uC2A4\uD134\uC2A4\uC758 projectRoot\uAC00 unresolved\uC785\uB2C8\uB2E4. \uC720\uD6A8\uD55C \uD504\uB85C\uC81D\uD2B8 \uB514\uB809\uD1A0\uB9AC\uC5D0\uC11C MCP\uB97C \uC2E4\uD589\uD558\uC138\uC694."});return}let o=Bt(a.projectRoot),s=t.syncController?.getSyncRoot?.();if(t.activeSyncOwnerInstanceId=a.instanceId,t.activeProjectRoot=a.projectRoot,t.pendingDashboardSyncRootPin===a.projectRoot&&(t.pendingDashboardSyncRootPin=null),!(t.syncController?.activeFullSyncPlaceId!==null&&t.syncController?.activeFullSyncPlaceId!==void 0)&&(!t.syncController||s!==o))try{t.syncController&&await t.syncController.shutdown(),t.syncController=new si(a.projectRoot,{appDataDir:t.config.appDataDir}),await t.syncController.initialize(),kr(t),y.info("SyncController \uC7AC\uC0DD\uC131 (projectRoot \uBCC0\uACBD)",{owner:a.instanceId,projectRoot:a.projectRoot,syncRoot:o})}catch(l){y.error("SyncController \uC7AC\uC0DD\uC131 \uC2E4\uD328",l instanceof Error?l:new Error(String(l))),r.status(500).json({error:"Failed to initialize sync controller"});return}}if(!t.syncController){r.json({active:!1});return}t.syncController.handleSyncInit(n,r).then(()=>{kr(t),CD(t)}).catch(()=>{})}),t.app.post("/sync/update",e,(n,r)=>{if(!t.syncController){r.json({active:!1});return}t.syncController.handleSyncUpdate(n,r).then(()=>{kr(t)}).catch(()=>{})}),t.app.post("/sync/stop",e,(n,r)=>{if(!t.syncController){r.json({active:!1});return}t.syncController.handleSyncStop(n,r).then(()=>{kr(t),CD(t)}).catch(()=>{})}),t.app.get("/sync/status",(n,r)=>{if(!t.syncController){r.json({active:!1});return}t.syncController.handleSyncStatus(n,r)}),t.app.get("/sync/config",(n,r)=>{if(!t.syncController){r.json({active:!1});return}t.syncController.handleSyncConfig(n,r)}),t.app.post("/sync/config",e,(n,r)=>{if(!t.syncController){r.json({active:!1});return}t.syncController.handleSyncConfig(n,r)}),t.app.get("/sync/pre-check",(n,r)=>{if(!IS(t).success){r.status(200).json({hasPreviousSync:!1,changes:[],summary:{modified:0,deleted:0,added:0,total:0},ownershipUnresolved:!0,ownershipError:"PROJECT_ROOT_UNRESOLVED"});return}if(!t.syncController){r.json({active:!1});return}t.syncController.handlePreCheck(n,r)}),t.app.get("/sync/history",(n,r)=>{if(!t.syncController){r.json({active:!1});return}t.syncController.handleSyncHistory(n,r)}),t.app.get("/sync/reverse-pending",(n,r)=>{if(!t.syncController){r.json({active:!1});return}t.syncController.handleReversePending(n,r)}),t.app.get("/sync/reverse-changes",(n,r)=>{if(!t.syncController){r.json({active:!1});return}t.syncController.handleReverseSyncChanges(n,r)}),t.app.post("/sync/reverse-result",e,(n,r)=>{if(!t.syncController){r.json({active:!1});return}t.syncController.handleReverseSyncResult(n,r).then(()=>{kr(t)}).catch(()=>{})}),t.app.post("/sync/reverse-rescan",e,(n,r)=>{if(!t.syncController){r.status(503).json({error:"Sync not initialized"});return}t.syncController.handleReverseRescan(n,r)}),t.app.post("/sync/resolve-conflict",e,(n,r)=>{if(!t.syncController){r.status(503).json({error:"Sync not initialized"});return}t.syncController.handleResolveConflict(n,r)}),t.app.post("/sync/directions",e,(n,r)=>{if(!t.syncController){r.status(503).json({error:"Sync not initialized"});return}t.syncController.handleSyncDirections(n,r)}),t.app.get("/sync/forward-restore-list",(n,r)=>{if(!t.syncController){r.status(503).json({error:"Sync not initialized"});return}t.syncController.handleForwardRestoreList(n,r)})}var sz=fi(Po(),1);pe();import*as Vo from"fs";import*as PS from"path";var na={LOG_LEVEL:"info",ENABLE_LOCAL_HISTORY:!0,ENABLE_LOCAL_STATISTICS:!0,ENABLE_CONTEXT_CAPTURE:!0,LICENSE_PROVIDER:"gumroad",REQUEST_TIMEOUT:3e4},jD="dashboard-settings.json";function Gf(t){let e=PS.join(t,jD);try{let n=Vo.readFileSync(e,"utf-8"),r=JSON.parse(n);return{...na,...r}}catch{return{...na}}}function AD(t,e){let n=PS.join(t,jD);try{Vo.mkdirSync(t,{recursive:!0}),Vo.writeFileSync(n,JSON.stringify(e,null,2))}catch(r){y.error("\uB300\uC2DC\uBCF4\uB4DC \uC124\uC815 \uC800\uC7A5 \uC2E4\uD328",r)}}function ND(t,e){let r={...{LOG_LEVEL:y.getLevel(),ENABLE_LOCAL_HISTORY:e.historyManager?.isLocalHistoryEnabled()??na.ENABLE_LOCAL_HISTORY,ENABLE_LOCAL_STATISTICS:e.historyManager?.isLocalStatisticsEnabled()??na.ENABLE_LOCAL_STATISTICS,ENABLE_CONTEXT_CAPTURE:e.executionContextManager?.isEnabled()??e.config.enableContextCapture??na.ENABLE_CONTEXT_CAPTURE,LICENSE_PROVIDER:e.config.licenseProvider??na.LICENSE_PROVIDER,REQUEST_TIMEOUT:e.config.requestTimeout??na.REQUEST_TIMEOUT},...t};if(t.LOG_LEVEL!==void 0){let i=t.LOG_LEVEL;y.setLevel(i),y.info("\uB85C\uADF8 \uB808\uBCA8 \uBCC0\uACBD",{level:i})}return t.ENABLE_LOCAL_HISTORY!==void 0&&e.historyManager&&(e.historyManager.setLocalHistoryEnabled(t.ENABLE_LOCAL_HISTORY),y.info("\uB85C\uCEEC \uD788\uC2A4\uD1A0\uB9AC \uC124\uC815 \uBCC0\uACBD",{enabled:t.ENABLE_LOCAL_HISTORY})),t.ENABLE_LOCAL_STATISTICS!==void 0&&e.historyManager&&(e.historyManager.setLocalStatisticsEnabled(t.ENABLE_LOCAL_STATISTICS),y.info("\uB85C\uCEEC \uD1B5\uACC4 \uC124\uC815 \uBCC0\uACBD",{enabled:t.ENABLE_LOCAL_STATISTICS})),t.ENABLE_CONTEXT_CAPTURE!==void 0&&(e.config.enableContextCapture=t.ENABLE_CONTEXT_CAPTURE,e.executionContextManager?.setEnabled(t.ENABLE_CONTEXT_CAPTURE),y.info("\uCEE8\uD14D\uC2A4\uD2B8 \uCEA1\uCC98 \uC124\uC815 \uBCC0\uACBD",{enabled:t.ENABLE_CONTEXT_CAPTURE})),t.REQUEST_TIMEOUT!==void 0&&(e.config.requestTimeout=t.REQUEST_TIMEOUT,y.info("\uC694\uCCAD \uD0C0\uC784\uC544\uC6C3 \uBCC0\uACBD",{timeout:t.REQUEST_TIMEOUT})),t.LICENSE_PROVIDER!==void 0&&(e.config.licenseProvider=t.LICENSE_PROVIDER,y.info("\uB77C\uC774\uC120\uC2A4 \uD504\uB85C\uBC14\uC774\uB354 \uBCC0\uACBD",{provider:t.LICENSE_PROVIDER})),r}pe();import*as Nl from"fs";import*as Wo from"path";function Oee(){return{version:2,lastUpdated:new Date().toISOString(),totalSessions:0,totalCalls:0,totalSuccessCalls:0,totalFailedCalls:0,tools:{},tierSummary:{basic:{totalCalls:0,successCount:0,failureCount:0},pro:{totalCalls:0,successCount:0,failureCount:0}}}}function ra(t){let e=vn(t);return{placeId:e,placeName:Tt(t,e)}}function OD(t){return async(e,n)=>{try{n.json(await NN(t))}catch(r){y.error("place summary \uC870\uD68C \uC2E4\uD328",r),n.status(500).json({error:"Failed to get place summary"})}}}function DD(t){let e=[];for(let n of t.split(`
135
135
  `)){let r=n.trim();if(!(!r||r.startsWith("#")))try{e.push(JSON.parse(r))}catch{}}return e}function zD(t){return async(e,n)=>{try{if(!t.historyManager){n.json(Oee());return}let{placeId:r,placeName:i}=ra(t);n.json(await t.historyManager.getStatisticsForPlace(r,i))}catch(r){y.error("\uB3C4\uAD6C \uD1B5\uACC4 \uC870\uD68C \uC2E4\uD328",r),n.status(500).json({error:"Failed to get tool statistics"})}}}function MD(t){return async(e,n)=>{try{let r=Math.min(parseInt(e.query.limit)||50,200),i=parseInt(e.query.offset)||0,a=e.query.tool;if(!t.historyManager){n.json({entries:[],total:0,hasMore:!1});return}let{placeId:o,placeName:s}=ra(t),c=await t.historyManager.getHistoryDir(o,s),l;try{l=await Nl.promises.readdir(c)}catch{n.json({entries:[],total:0,hasMore:!1});return}let u=l.filter(g=>g.endsWith(".jsonl")).sort().reverse();if(u.length===0){n.json({entries:[],total:0,hasMore:!1});return}let p=(await Promise.all(u.map(async g=>{let x=Wo.join(c,g),w=await Nl.promises.readFile(x,"utf-8");return DD(w).map(_=>({entry:_,fileName:g}))}))).flat();a&&p.splice(0,p.length,...p.filter(({entry:g})=>g.toolName===a)),p.sort((g,x)=>{let w=x.entry.timestamp.localeCompare(g.entry.timestamp);if(w!==0)return w;let _=x.entry.sequenceNumber-g.entry.sequenceNumber;return _!==0?_:x.fileName.localeCompare(g.fileName)});let d=p.map(({entry:g})=>g),f=d.length,m=d.slice(i,i+r),h=i+r<f;n.json({entries:m,total:f,hasMore:h})}catch(r){y.error("\uD788\uC2A4\uD1A0\uB9AC \uC870\uD68C \uC2E4\uD328",r),n.status(500).json({error:"Failed to get history"})}}}function LD(t){return async(e,n)=>{try{let r=Math.min(parseInt(e.query.limit)||50,200),i=parseInt(e.query.offset)||0;if(!t.historyManager){n.json({entries:[],total:0,hasMore:!1});return}let{placeId:a,placeName:o}=ra(t),s=await t.historyManager.getStatisticsDir(a,o),c=Wo.join(s,"failures.jsonl"),l;try{l=await Nl.promises.readFile(c,"utf-8")}catch{n.json({entries:[],total:0,hasMore:!1});return}let u=DD(l).reverse(),p=u.length,d=u.slice(i,i+r),f=i+r<p;n.json({entries:d,total:p,hasMore:f})}catch(r){y.error("\uC2E4\uD328 \uB85C\uADF8 \uC870\uD68C \uC2E4\uD328",r),n.status(500).json({error:"Failed to get failures"})}}}function UD(t){return async(e,n)=>{try{let r=await zN(t);n.json({entries:r})}catch(r){y.error("\uC5F0\uACB0 \uB85C\uADF8 \uC870\uD68C \uC2E4\uD328",r),n.status(500).json({error:"Failed to get connection log"})}}}function FD(t){return async(e,n)=>{try{let r=await MN(t);n.json({ok:!0,target:"connection-log",placeId:null,clearedCount:r})}catch(r){y.error("\uC5F0\uACB0 \uB85C\uADF8 \uC0AD\uC81C \uC2E4\uD328",r),n.status(500).json({error:"Failed to clear connection log"})}}}function qD(t){return async(e,n)=>{try{if(!t.historyManager){n.json({ok:!0,target:"tools-history",placeId:null,clearedCount:0});return}let{placeId:r,placeName:i}=ra(t),a=await t.historyManager.clearHistoryForPlace(r,i);n.json({ok:!0,target:"tools-history",placeId:r,clearedCount:a})}catch(r){y.error("\uB3C4\uAD6C \uD788\uC2A4\uD1A0\uB9AC \uC0AD\uC81C \uC2E4\uD328",r),n.status(500).json({error:"Failed to clear tools history"})}}}function BD(t){return async(e,n)=>{try{if(!t.historyManager){n.json({ok:!0,target:"tools-statistics",placeId:null,clearedCount:0});return}let{placeId:r,placeName:i}=ra(t);await t.historyManager.clearStatisticsForPlace(r,i),n.json({ok:!0,target:"tools-statistics",placeId:r,clearedCount:1})}catch(r){y.error("\uB3C4\uAD6C \uD1B5\uACC4 \uC0AD\uC81C \uC2E4\uD328",r),n.status(500).json({error:"Failed to clear tool statistics"})}}}function ZD(t){return async(e,n)=>{try{let{placeId:r,placeName:i}=ra(t),a=await UN(t,r,i),o=await xl(Wo.join(a,"sync-history.jsonl")),s=await xl(Wo.join(a,"sync-changes.log"));n.json({ok:!0,target:"sync-history",placeId:r,clearedCount:o+s})}catch(r){y.error("\uC2F1\uD06C \uD788\uC2A4\uD1A0\uB9AC \uC0AD\uC81C \uC2E4\uD328",r),n.status(500).json({error:"Failed to clear sync history"})}}}function HD(t){return async(e,n)=>{try{let r=Math.min(parseInt(e.query.lines)||100,500),i=t.config.appDataDir;if(!i){n.json({lines:[]});return}let a=Wo.join(i,"observability","logs","current.log"),o;try{o=await Nl.promises.readFile(a,"utf-8")}catch{n.json({lines:[]});return}let c=o.split(`
136
136
  `).filter(l=>l.trim()).slice(-r);n.json({lines:c})}catch(r){y.error("\uD50C\uB7EC\uADF8\uC778 \uB85C\uADF8 \uC870\uD68C \uC2E4\uD328",r),n.status(500).json({error:"Failed to get plugin logs"})}}}function VD(t){return async(e,n)=>{try{let{placeId:i,placeName:a}=ra(t),o=(await $o(t,i,a)).sort((c,l)=>l.timestamp.localeCompare(c.timestamp)).map(c=>c.change);if(o.length===0&&t.historyManager){let c=await ni(t,_r);o=Ki(c,{placeId:i})?.changes??[]}let s=await Eo(t,i,a);n.json({...Zw(o),recentChanges:o.slice(0,20).map(c=>Gi(c,s))})}catch(r){y.error("\uD65C\uC131 \uBCC0\uACBD\uB85C\uADF8 \uC870\uD68C \uC2E4\uD328",r),n.status(500).json({error:"Failed to get active changelog"})}}}pe();import*as ci from"fs";import*as ia from"path";async function WD(t){let e=vn(t);if(e==null)return null;let n=Tt(t,e);return zw(t,e,n)}function GD(t){return async(e,n)=>{try{if(!(t.pluginClients.size>0)){n.json({state:"NotRunning"});return}if(t.internalActionExecutor)try{let i=await t.internalActionExecutor("system_info.play_status",{});if(i.success&&i.data){let a=i.data,s={state:a.state??"NotRunning"};a.mode&&(s.mode=a.mode),a.placeId!=null&&(s.placeId=a.placeId),a.placeName&&(s.placeName=a.placeName),a.elapsedMs!=null&&(s.elapsedMs=a.elapsedMs),n.json(s);return}}catch{}n.json({state:"NotRunning"})}catch(r){y.error("\uD50C\uB808\uC774\uD14C\uC2A4\uD2B8 \uC0C1\uD0DC \uC870\uD68C \uC2E4\uD328",r),n.status(500).json({error:"Failed to get playtest status"})}}}function JD(t){return async(e,n)=>{try{if(!t.config.dataDir){n.json({entries:[]});return}let i=await WD(t);if(!i){n.json({entries:[]});return}let a=[],o;try{o=await ci.promises.readdir(i)}catch{n.json({entries:[]});return}for(let s of o){let c=ia.join(i,s);try{if(!(await ci.promises.stat(c)).isDirectory())continue}catch{continue}let l=ia.join(c,"test-report.md"),u=ia.join(c,"test-log.txt"),p=!1,d=!1;try{await ci.promises.access(l),p=!0}catch{}try{await ci.promises.access(u),d=!0}catch{}if(!p&&!d)continue;let f=await Dee(l,u,c,s,p);a.push(f)}a.sort((s,c)=>c.timestamp.localeCompare(s.timestamp)),n.json({entries:a})}catch(r){y.error("\uD50C\uB808\uC774\uD14C\uC2A4\uD2B8 \uD788\uC2A4\uD1A0\uB9AC \uC870\uD68C \uC2E4\uD328",r),n.status(500).json({error:"Failed to get playtest history"})}}}async function Dee(t,e,n,r,i){let a={timestamp:r,testName:"Unknown Test",mode:"Play",status:"unknown",durationMs:0};if(i)try{let s=await ci.promises.readFile(t,"utf-8"),c=s.match(/^- Test Name:\s*(.+)$/m);if(c)a.testName=c[1].trim();else{let p=s.match(/^#\s+(.+)$/m);p&&(a.testName=p[1].trim())}if(/\bpassed\b/i.test(s)||/\bsuccess\b/i.test(s))a.status="passed";else if(/\bfailed\b/i.test(s)||/\berror\b/i.test(s)){a.status="failed";let p=s.match(/(?:Error|Failed):\s*(.+)$/m);p&&(a.errorMessage=p[1].trim())}let l=s.match(/\bMode:\s*(Play|Run|Server)\b/i);l&&(a.mode=l[1]);let u=s.match(/\bDuration \(ms\):\s*(\d+(?:\.\d+)?)/i);if(u)a.durationMs=parseFloat(u[1]);else{let p=s.match(/(\d+(?:\.\d+)?)\s*(?:ms|s)/);if(p){let d=parseFloat(p[1]);a.durationMs=p[0].includes("s")&&!p[0].includes("ms")?d*1e3:d}}}catch{}let o=await Lw(n);return o&&(typeof o.contextId=="string"&&(a.contextId=o.contextId),o.contextSummary&&(a.contextSummary=o.contextSummary),o.replayMetadata&&(a.replayMetadata=o.replayMetadata)),a}function KD(t){return async(e,n)=>{try{let r=e.params.timestamp;if(!r){n.status(400).json({error:"Timestamp is required"});return}if(!t.config.dataDir){n.status(404).json({error:"Data directory not configured"});return}let a=await WD(t);if(!a){n.status(404).json({error:"Active place not found"});return}let o=ia.join(a,r),s=ia.join(o,"test-report.md"),c=ia.join(o,"test-log.txt"),l="",u="";try{l=await ci.promises.readFile(s,"utf-8")}catch{}try{u=await ci.promises.readFile(c,"utf-8")}catch{}if(l||u){let p=await Lw(o);n.json({markdown:l,logs:u,...typeof p?.contextId=="string"?{contextId:p.contextId}:{},...p?.contextSummary?{contextSummary:p.contextSummary}:{},...p?.replayMetadata?{replayMetadata:p.replayMetadata}:{}});return}n.status(404).json({error:"Report not found"})}catch(r){y.error("\uD50C\uB808\uC774\uD14C\uC2A4\uD2B8 \uB9AC\uD3EC\uD2B8 \uC870\uD68C \uC2E4\uD328",r),n.status(500).json({error:"Failed to get playtest report"})}}}function XD(t){return async(e,n)=>{try{let r=e.body?.action,i=["play","stop","pause","resume"];if(!r||!i.includes(r)){n.status(400).json({error:`Invalid action. Use: ${i.join(", ")}`});return}if(r==="play"){if(t.internalActionExecutor)try{let a=await t.internalActionExecutor("system_info.play",{});n.json({success:a.success,action:r});return}catch{n.status(500).json({error:"Failed to execute play command"});return}n.status(503).json({error:"Plugin not available"});return}if(t.playtestControlCommand=r,t.internalActionExecutor)try{let o={stop:"system_info.stop",pause:"system_info.pause",resume:"system_info.resume"}[r];o&&await t.internalActionExecutor(o,{})}catch{}n.json({success:!0,action:r})}catch(r){y.error("\uD50C\uB808\uC774\uD14C\uC2A4\uD2B8 \uC81C\uC5B4 \uBA85\uB839 \uC2E4\uD328",r),n.status(500).json({error:"Failed to execute playtest control"})}}}function YD(t){return async(e,n)=>{try{let r=vn(t),i=Tt(t,r),a=await zw(t,r,i),o=await DN(a);n.json({ok:!0,target:"playtest-history",placeId:r,clearedCount:o})}catch(r){y.error("\uD50C\uB808\uC774\uD14C\uC2A4\uD2B8 \uD788\uC2A4\uD1A0\uB9AC \uC0AD\uC81C \uC2E4\uD328",r),n.status(500).json({error:"Failed to clear playtest history"})}}}import{execFile as zee}from"node:child_process";var Mee="macos-osascript",Lee="windows-powershell",Uee="linux-zenity",Fee="linux-kdialog",li=class extends Error{code;constructor(e,n){super(n),this.name="NativeFolderPickerError",this.code=e}};function qee(t,e){return new Promise((n,r)=>{zee(t,e,(i,a,o)=>{if(i){r(i);return}n({stdout:a,stderr:o})})})}function Bee(){return[{command:"zenity",args:["--file-selection","--directory","--title=Select a Roblox project root"],strategy:Uee},{command:"kdialog",args:["--getexistingdirectory","","--title","Select a Roblox project root"],strategy:Fee}]}function Zee(t,e){if(t==="darwin")return[{command:"osascript",args:["-e",'POSIX path of (choose folder with prompt "Select a Roblox project root")'],strategy:Mee}];if(t==="win32")return[{command:"powershell",args:["-NoProfile","-STA","-Command",["Add-Type -AssemblyName System.Windows.Forms;","$dialog = New-Object System.Windows.Forms.FolderBrowserDialog;",'$dialog.Description = "Select a Roblox project root";',"$dialog.UseDescriptionForTitle = $true;","if ($dialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {"," [Console]::Out.Write($dialog.SelectedPath)","}"].join(" ")],strategy:Lee}];if(t==="linux"){if(!e.DISPLAY&&!e.WAYLAND_DISPLAY)throw new li("FOLDER_PICKER_HEADLESS","Native folder picker requires a desktop session. Set DISPLAY or WAYLAND_DISPLAY, or apply the path manually.");return Bee()}throw new li("FOLDER_PICKER_UNSUPPORTED",`Native folder picker is not supported on platform: ${t}`)}function Hee(t,e){let n=e.message.toLowerCase();return t==="darwin"?/user canceled|cancelled|canceled/.test(n):t==="linux"?String(e.code??"")==="1":t==="win32"?/cancelled|canceled/.test(n):!1}async function Jf(t={}){let e=t.platform??process.platform,n=t.env??process.env,r=t.execFile??qee,i=Zee(e,n),a=null;for(let o of i)try{let{stdout:s}=await r(o.command,o.args),c=s.trim();return{cancelled:c.length===0,projectRoot:c.length===0?null:c,strategy:o.strategy}}catch(s){let c=s;if(c.code==="ENOENT"){a=c;continue}if(Hee(e,c))return{cancelled:!0,projectRoot:null,strategy:o.strategy};throw new li("FOLDER_PICKER_FAILED",c.message||"Failed to open native folder picker.")}throw new li("FOLDER_PICKER_UNAVAILABLE",a?"No supported native folder picker command is available. Install zenity or kdialog, or apply the path manually.":"Failed to open native folder picker.")}import{randomUUID as Vee}from"node:crypto";import{promises as Wee}from"node:fs";Et();Ew();var Gee=new Set(["initializing","syncing"]),Jee="dashboard_sync_stop",Kee="dashboard_sync_start",Xee="Studio plugin could not restart full sync for the selected project root.",Yee="Command timeout after ",Ir=class extends Error{code;statusCode;constructor(e,n,r){super(n),this.name="DashboardSyncRootError",this.code=e,this.statusCode=r}};function Ol(t){return t.config.appDataDir??qt()}function Qee(t){return t.startsWith("/")||/^[A-Z]:\\/i.test(t)}function ete(t){return t==="/"||t==="\\"||/^[A-Z]:\\?$/i.test(t)}function tte(t){if(!t||!Qee(t)||ete(t))throw new Ir("INVALID_PROJECT_ROOT","projectRoot must be an absolute non-root filesystem path.",400)}function ez(t){return t&&typeof t=="object"&&typeof t.getSyncRoot=="function"&&typeof t.retargetSyncRoot=="function"?t:null}function CS(t){return ez(t.historyManager)}function $S(t){return ez(t.executionContextManager)}function tz(t){let e=t.syncController;if(!e)return{state:"idle",placeId:null,activeClientId:null,modifiedFileCount:0};let n=e.getDefaultRuntimePlaceId();if(n==null)return{state:"idle",placeId:null,activeClientId:null,modifiedFileCount:0};let r=e.places.get(n);return r?{state:r.state,placeId:n,activeClientId:r.activeClientId??null,modifiedFileCount:r.fileWatcher?.getPendingCount()??0}:{state:"idle",placeId:n,activeClientId:null,modifiedFileCount:0}}function nte(t,e){if(e.activeClientId&&t.pluginClients.has(e.activeClientId)){let r=t.pluginClients.get(e.activeClientId);if(typeof r.placeId=="number")return{clientId:r.clientId,placeId:r.placeId,placeName:r.placeName}}if(e.placeId!==null){let r=[...t.pluginClients.values()].find(i=>i.placeId===e.placeId);if(r)return{clientId:r.clientId,placeId:r.placeId,placeName:r.placeName}}let n=[...t.pluginClients.values()].filter(r=>typeof r.placeId=="number").sort((r,i)=>i.lastSeen-r.lastSeen)[0];return n?{clientId:n.clientId,placeId:n.placeId,placeName:n.placeName}:null}async function nz(t,e,n){let r=Xee;try{let i=await Rl(t,e,n,Vee());if(!i.success)throw new Ir("SYNC_ROOT_PRECONDITION_FAILED",QD(i.error,r),412)}catch(i){throw i instanceof Ir?i:new Ir("SYNC_ROOT_PRECONDITION_FAILED",QD(i instanceof Error?i.message:null,r),412)}}function QD(t,e){if(typeof t!="string")return e;let n=t.trim();return!n||n.startsWith(Yee)?e:n}async function rte(t,e,n){Gee.has(n.state)&&await nz(t,Jee,{clientId:e.clientId,placeId:e.placeId,reason:"switch_sync_root"})}async function ite(t,e,n){await nz(t,Kee,{clientId:e.clientId,placeId:e.placeId,placeName:e.placeName,projectRoot:n,projectSyncRoot:Bt(n)})}async function ate(t,e){let n=new si(e,{appDataDir:t});return await n.initialize(),n}function ote(t){return{overrideProjectRoot:gl(Ol(t)),configDataDir:t.config.dataDir,syncController:t.syncController,activeProjectRoot:t.activeProjectRoot,activeSyncOwnerInstanceId:t.activeSyncOwnerInstanceId,pendingDashboardSyncRootPin:t.pendingDashboardSyncRootPin,historyManagerSyncRoot:CS(t)?.getSyncRoot()??null,executionContextManagerSyncRoot:$S(t)?.getSyncRoot()??null}}async function rz(t,e,n){e.configDataDir===void 0?delete t.config.dataDir:t.config.dataDir=e.configDataDir,t.syncController=e.syncController,t.activeProjectRoot=e.activeProjectRoot,t.activeSyncOwnerInstanceId=e.activeSyncOwnerInstanceId,t.pendingDashboardSyncRootPin=e.pendingDashboardSyncRootPin;let r=CS(t);r&&e.historyManagerSyncRoot&&await r.retargetSyncRoot(e.historyManagerSyncRoot);let i=$S(t);i&&e.executionContextManagerSyncRoot&&await i.retargetSyncRoot(e.executionContextManagerSyncRoot),e.overrideProjectRoot?$w(Ol(t),e.overrideProjectRoot):EN(Ol(t)),n&&await n.shutdown()}async function ste(t,e,n,r,i){let a=Ol(t);try{let o=CS(t);o&&await o.retargetSyncRoot(r);let s=$S(t);s&&await s.retargetSyncRoot(r),$w(a,n),t.config.dataDir=r,t.syncController=i,kr(t),t.activeProjectRoot=n,t.activeSyncOwnerInstanceId=null,t.pendingDashboardSyncRootPin=n}catch(o){throw await rz(t,e,i),o}}function ES(t){let e=Ol(t),n=Tn({appDataDir:e}),r=gl(e)!==null;return{projectRoot:n,projectSyncRoot:Bt(n),hasOverride:r,sync:tz(t)}}async function iz(t){let n=await(t.dashboardFolderPicker??Jf)();return{cancelled:n.cancelled,projectRoot:n.projectRoot}}async function az(t){if(tte(t.projectRoot),t.ctx.dashboardSyncRootSwitchInFlight)throw new Ir("SYNC_ROOT_SWITCH_IN_PROGRESS","Another sync-root switch is already in progress.",409);t.ctx.dashboardSyncRootSwitchInFlight=!0;try{let e=tz(t.ctx),n=nte(t.ctx,e),r=ote(t.ctx),i=Bt(t.projectRoot);n&&await rte(t.ctx,n,e),await Wee.mkdir(i,{recursive:!0});let a=await ate(t.ctx.config.appDataDir,t.projectRoot);if(await ste(t.ctx,r,t.projectRoot,i,a),n)try{await ite(t.ctx,n,t.projectRoot)}catch(o){throw await rz(t.ctx,r,a),o}return r.syncController&&typeof r.syncController.shutdown=="function"&&await r.syncController.shutdown(),ES(t.ctx)}finally{t.ctx.dashboardSyncRootSwitchInFlight=!1}}pe();Et();function oz(t){return t.config.appDataDir?t.config.appDataDir:qt()}function cz(t){let e=(0,sz.Router)();e.get("/api/dashboard/events",(r,i)=>{i.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive"}),i.write(`data: {"type":"connected"}
137
137
 
138
- `),t.dashboardSseClients??=new Set,t.dashboardSseClients.add(i),r.on("close",()=>{t.dashboardSseClients?.delete(i)})}),e.get("/api/dashboard/settings",(r,i)=>{try{let a=oz(t),o=Gf(a);o.ENABLE_CONTEXT_CAPTURE=t.executionContextManager?.isEnabled()??t.config.enableContextCapture??o.ENABLE_CONTEXT_CAPTURE;let s={HTTP_PORT:t.config.httpPort,HTTP_HOST:t.config.httpHost,DASHBOARD_AUTO_OPEN:!1,ENABLE_TELEMETRY:t.config.enableTelemetry??!1};i.json({hot:o,cold:s})}catch(a){y.error("\uB300\uC2DC\uBCF4\uB4DC \uC124\uC815 \uC870\uD68C \uC2E4\uD328",a),i.status(500).json({error:"Failed to load settings"})}}),e.patch("/api/dashboard/settings",(r,i)=>{try{let a=oz(t),o=r.body,s=ND(o,t);AD(a,s);let c={HTTP_PORT:t.config.httpPort,HTTP_HOST:t.config.httpHost,DASHBOARD_AUTO_OPEN:!1,ENABLE_TELEMETRY:t.config.enableTelemetry??!1};i.json({hot:s,cold:c})}catch(a){y.error("\uB300\uC2DC\uBCF4\uB4DC \uC124\uC815 \uBCC0\uACBD \uC2E4\uD328",a),i.status(500).json({error:"Failed to save settings"})}}),e.get("/api/dashboard/sync-root",(r,i)=>{try{i.json(ES(t))}catch(a){y.error("\uB300\uC2DC\uBCF4\uB4DC sync-root \uC0C1\uD0DC \uC870\uD68C \uC2E4\uD328",a),i.status(500).json({error:"Failed to load dashboard sync root state"})}}),e.get("/api/dashboard/place-summary",OD(t)),e.post("/api/dashboard/sync-root/pick",async(r,i)=>{try{i.json(await iz(t))}catch(a){if(a instanceof li){i.status(503).json({error:a.code,message:a.message});return}y.error("\uB300\uC2DC\uBCF4\uB4DC sync-root picker \uD638\uCD9C \uC2E4\uD328",a),i.status(500).json({error:"Failed to open native folder picker"})}}),e.patch("/api/dashboard/sync-root",async(r,i)=>{try{let a=r.body?.projectRoot;i.json(await az({ctx:t,projectRoot:a??""}))}catch(a){if(a instanceof Ir){i.status(a.statusCode).json({error:a.code,message:a.message});return}y.error("\uB300\uC2DC\uBCF4\uB4DC sync-root \uC804\uD658 \uC2E4\uD328",a),i.status(500).json({error:"Failed to apply dashboard sync root"})}}),e.get("/api/dashboard/license/status",(r,i)=>_S(t,r,i,"dashboard")),e.post("/api/dashboard/license/activate",(r,i)=>bS(t,r,i,"dashboard")),e.post("/api/dashboard/license/refresh",(r,i)=>wS(t,r,i,"dashboard")),e.post("/api/dashboard/license/reset",(r,i)=>SS(t,r,i,"dashboard")),e.get("/api/dashboard/tool-stats",zD(t)),e.get("/api/dashboard/history",MD(t)),e.get("/api/dashboard/failures",LD(t)),e.get("/api/dashboard/connection-log",UD(t)),e.post("/api/dashboard/connection-log/clear",FD(t)),e.get("/api/dashboard/plugin-logs",HD(t)),e.post("/api/dashboard/tools/history/clear",qD(t)),e.post("/api/dashboard/tools/statistics/clear",BD(t)),e.post("/api/dashboard/sync/history/clear",ZD(t)),e.get("/api/dashboard/changelog/active",VD(t)),e.get("/api/dashboard/changelog",JN(t)),e.get("/api/dashboard/changelog/:id",KN(t)),e.get("/api/dashboard/changelog/:id/changes",XN(t)),e.post("/api/dashboard/changelog/clear",YN(t)),e.get("/api/dashboard/playtest/status",GD(t)),e.get("/api/dashboard/playtest/history",JD(t)),e.get("/api/dashboard/playtest/report/:timestamp",KD(t)),e.post("/api/dashboard/playtest/control",XD(t)),e.post("/api/dashboard/playtest/history/clear",YD(t));let n=new Set(["dashboard_page_view","dashboard_tier_toggle","dashboard_upgrade_click"]);return e.post("/api/dashboard/analytics",(r,i)=>{let a=r.body;if(!Array.isArray(a?.events)||a.events.length===0){i.status(400).json({error:"events array is required"});return}let o=a.events.filter(s=>typeof s.name=="string"&&n.has(s.name));o.length>0&&t.analyticsManager&&t.analyticsManager.trackDashboardEvents(o),i.status(202).json({ok:!0})}),e}import cte from"fs";import lte from"path";var ute=["../dashboard/dist","../../mcp-dashboard/dist"];function Kf(t){for(let e of ute){let n=lte.resolve(t,e);if(cte.existsSync(n))return n}return null}Lf();function mte(t,e){let n=t.historyManager?.getCurrentHistoryFile(e.placeId??null);if(!n)return e.sessionId;let r=n.split(/[\\/]/).pop();return r?r.endsWith(".jsonl")?r.slice(0,-6):r:e.sessionId}function hte(t,e){let n=_r([e]);if(n.length===0)return;let r=mte(t,e);TD(t,r,n),(async()=>{let i=new Map;if(t.executionContextManager&&e.contextId){t.executionContextManager.appendLinks({sessionId:e.sessionId,contextId:e.contextId,...e.placeId!==void 0?{placeId:e.placeId}:{},...e.placeName!==void 0?{placeName:e.placeName}:{},links:{changelogEntryIds:[r]}}).catch(()=>{});try{let a=await t.executionContextManager.getLatestSnapshot(e.contextId);a&&(i=new Map([[a.contextId,a]]))}catch{i=new Map}}for(let a of n){let o=i.size>0?Gi(a,i):a;$t(t,"game_change",o)}})()}function gte(t){return!!(t==null||t===""||t==="/"||t==="\\"||/^[A-Z]:\\?$/i.test(t)||!t.startsWith("/")&&!/^[A-Z]:\\/i.test(t))}function IS(t){if(t.pendingDashboardSyncRootPin)return{success:!0,instanceId:t.instanceId,projectRoot:t.pendingDashboardSyncRootPin};let e=[],n=Tn({appDataDir:t.config.appDataDir});e.push({instanceId:t.instanceId,projectRoot:n,activity:t.serverLastCommandAt??t.startTime});for(let i of t.mcpInstances.values())gte(i.projectRoot)||e.push({instanceId:i.instanceId,projectRoot:i.projectRoot,activity:i.lastCommandAt??i.connectedAt});if(e.length===0)return{success:!1,error:"PROJECT_ROOT_UNRESOLVED"};e.sort((i,a)=>a.activity-i.activity);let r=e[0];return{success:!0,instanceId:r.instanceId,projectRoot:r.projectRoot}}var RS=class{ctx;server=null;stopPromise=null;constructor(e){let n=(0,TS.default)();this.ctx=HO(e,n),this.ctx.dashboardFolderPicker=Jf,WO(this.ctx),xD(this.ctx,()=>this.stop()),RD(this.ctx),n.use(_D(this.ctx)),n.use(cz(this.ctx));let r=pte(import.meta.url),i=Xf.dirname(r),a=Kf(i);a&&(n.use("/dashboard",TS.default.static(a)),n.get("/dashboard/*",(o,s)=>{s.sendFile(Xf.join(a,"index.html"))}))}setHistoryManager(e){this.ctx.historyManager=e,e.setOnRecord(n=>{$t(this.ctx,"command",n)}),e.setOnEntryRecord(n=>{this.ctx.executionContextManager&&n.contextId&&this.ctx.executionContextManager.appendLinks({sessionId:n.sessionId,contextId:n.contextId,...n.placeId!==void 0?{placeId:n.placeId}:{},...n.placeName!==void 0?{placeName:n.placeName}:{},links:{toolHistoryIds:[n.id]}}).catch(()=>{}),hte(this.ctx,n)})}setAnalyticsManager(e){this.ctx.analyticsManager=e}setExecutionContextManager(e){this.ctx.executionContextManager=e}setLicenseStateManager(e){this.ctx.licenseState=e}setInternalActionExecutor(e){this.ctx.internalActionExecutor=e}setAiClientInfo(e,n){this.ctx.aiClientName=e,this.ctx.analyticsManager?.setAiClient(e),this.ctx.isClientMode&&vS(this.ctx,e)}getInstanceId(){return this.ctx.instanceId}getSessionId(){return this.ctx.sessionId}getContext(){return this.ctx}noteLocalCommandActivity(e=Date.now()){this.ctx.serverLastCommandAt=e}getSyncController(){return this.ctx.syncController}getIsClientMode(){return this.ctx.isClientMode}getDashboardSseClientCount(){return this.ctx.dashboardSseClients?.size??0}getBridgeContext(){return this.ctx}getCachedSelection(e=3e4,n){return El(this.ctx,e,n)}async getConnectionInfo(){return Uf(this.ctx)}async executeCommand(e,n,r){return this.ctx.isClientMode?yS(this.ctx,e,n,r):Rl(this.ctx,e,n,dte(),r)}async start(){let e=await hS(this.ctx);if(e)if(y.info("Existing MCP server found, switching to CLIENT MODE",{existingPid:e.pid,existingUptime:e.uptime,existingConnectedClients:e.connectedClients}),this.ctx.isClientMode=!0,await Mf(this.ctx)){zf(this.ctx),y.info("HTTP Bridge started in CLIENT MODE",{serverUrl:this.ctx.baseUrl,myPid:process.pid,myInstanceId:this.ctx.instanceId});return}else y.warn("Failed to register with server, will try to start as server");return new Promise((n,r)=>{this.server=fte(this.ctx.app),this.server.on("error",async i=>{i.code==="EADDRINUSE"?(y.info("Port in use, switching to CLIENT MODE",{port:this.ctx.config.httpPort}),this.ctx.isClientMode=!0,await Mf(this.ctx)?(zf(this.ctx),y.info("HTTP Bridge started in CLIENT MODE (after EADDRINUSE)",{serverUrl:this.ctx.baseUrl,myPid:process.pid}),n()):r(new Error(`Port ${this.ctx.config.httpPort} is in use but failed to connect as client. Check if the existing server is responsive: curl ${this.ctx.baseUrl}/status`))):r(i)}),this.server.listen(this.ctx.config.httpPort,this.ctx.config.httpHost,async()=>{this.ctx.isClientMode=!1,$l(this.ctx),this.ctx.clientModeUpstreamReachable=!0,this.ctx.clientModeConsecutiveHealthFailures=0,this.ctx.clientModeLastHealthError=null,this.ctx.clientModeLastHealthSuccessAt=null,this.ctx.clientModeLastHealthFailureAt=null,this.ctx.config.httpHost!=="127.0.0.1"&&this.ctx.config.httpHost!=="localhost"&&y.warn("HTTP_HOST is not localhost -- sync data may be exposed",{host:this.ctx.config.httpHost});try{this.ctx.syncController=new si(void 0,{appDataDir:this.ctx.config.appDataDir}),await this.ctx.syncController.initialize(),kr(this.ctx),y.info("SyncController initialized")}catch(i){y.error("Failed to initialize SyncController",i instanceof Error?i:new Error(String(i)))}y.info("HTTP Bridge started in SERVER MODE",{host:this.ctx.config.httpHost,port:this.ctx.config.httpPort,pid:process.pid,instanceId:this.ctx.instanceId}),n()})})}async stop(){return this.stopPromise?this.stopPromise:(this.stopPromise=this.stopInternal(),this.stopPromise)}async stopInternal(){if(y.info("Stopping HTTP Bridge",{isClientMode:this.ctx.isClientMode}),this.ctx.isClientMode){$l(this.ctx);try{await fetch(`${this.ctx.baseUrl}/unregister-mcp`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({instanceId:this.ctx.instanceId})}),y.info("Unregistered from server")}catch{}y.info("Client mode - stopped");return}if(this.ctx.syncController){try{await this.ctx.syncController.shutdown()}catch(n){y.error("Error shutting down SyncController",n instanceof Error?n:new Error(String(n)))}this.ctx.syncController=null}for(let n of this.ctx.sseClients)n.end();this.ctx.sseClients.clear();for(let[n,r]of this.ctx.commandQueue)clearTimeout(r.timeoutId),r.reject(new Error("Server shutting down")),this.ctx.commandQueue.delete(n);let e=this.server;this.server=null,e&&await new Promise(n=>{e.close(r=>{r?y.warn("HTTP server close returned during shutdown",{error:r.message}):y.info("HTTP server closed"),n()})})}},lz=RS;pe();Et();var uz=new Set(["add_to_selection","analyze_walkable_area","batch_execute","cancel_tween","check_collision","check_placement","clear_change_history","clear_particles","clear_state_cache","create_instance_tree","create_tween","emit_particles","execute_luau","export_selection","extended_call_method","extended_call_methods","extended_get_properties","extended_get_property","extended_set_properties","extended_set_property","find_empty_space","find_flat_areas","find_ground","find_spawn_positions","get_ancestors","get_animation_tracks","get_asset_info","get_bounds","get_collision_groups","get_descendants","get_file_tree","get_place_info","get_play_status","get_project_structure","get_recent_changes","get_script_list","get_selection_context","get_selection_details","get_selection_info","get_services","get_spatial_map","get_studio_settings","get_viewport_info","get_workspace_metadata","get_workspace_snapshot","insert_free_model","insert_model","insert_package","load_animation","mass_create_instances","mass_delete_instances","mass_duplicate","mass_get_property","mass_set_property","modify_children","multi_raycast","pause_playtest","pause_sound","pause_tween","play_animation","play_sound","play_tween","raycast","register_collision_group","remove_from_selection","replace_in_scripts","resume_playtest","resume_sound","run_test","scan_area","search_and_insert_model","search_by_property","search_by_tag","search_creator_store","set_atmosphere","set_calculated_property","set_collidable","set_lighting","set_listener","set_relative_property","set_sky","set_terrain","set_time_of_day","smart_duplicate","snap_to_grid","start_playtest","stop_animation","stop_playtest","stop_sound","sync_config","sync_directions","sync_history","sync_progress","sync_read_file","sync_status","sync_workspace_state","sync_write_file","terrain_clear","terrain_clear_region","terrain_fill_ball","terrain_fill_block","terrain_fill_cylinder","terrain_fill_wedge","terrain_generate","terrain_get_material_color","terrain_read_voxel","terrain_read_voxels","terrain_replace_material","terrain_set_material_color","terrain_smooth","terrain_write_voxels","toggle_effect","watch_selection"]),jS=new Set(["get_connection_info","get_usage_status","ping"]);function Go(t){return uz.has(t)?"pro":"basic"}var pz=new Set(["execute_luau","batch_execute"]);function AS(t,e){let n=Ff[t];if(!n){if(t==="batch_execute"&&Array.isArray(e.commands)){let c=e.commands.map(l=>{if(pz.has(l.tool))throw new Error(`Tool "${l.tool}" is not allowed inside batch_execute. Denied tools: ${[...pz].join(", ")}`);return{toolName:l.tool,...AS(l.tool,l.args||{})}});return{action:t,params:{...e,commands:c}}}return{action:t,params:e}}let r=e[n.discriminator],i=n.mapping[r];if(!i){let c=Object.keys(n.mapping).join(", ");throw new Error(`Invalid ${n.discriminator} "${r}" for tool "${t}". Valid values: ${c}`)}let{[n.discriminator]:a,...o}=e,s=n.paramAliases?.[i];if(s)for(let[c,l]of Object.entries(s))c in o&&!(l in o)&&(o[l]=o[c],delete o[c]);return{action:i,params:o}}var yte={manage_scripts:["scripts"],manage_properties:["values","data"],mutate_instances:["containers"],manage_lighting:["services"]};function NS(t,e,n){let r=qf(e);if(r!=="plugin")return{target:r};let i=yte[t];if(!i||!n)return{target:"plugin"};try{let a=n.getStatusSummary();if(!a||!a.active)return{target:"plugin"};for(let o of i)if(n.getDirectionForCategory(o)==="reverse")return{target:"disk"}}catch{}return{target:"plugin"}}pe();async function dz(t,e,n){if(!n)return{success:!1,error:"Sync controller not available. Project sync is not initialized."};try{switch(t){case"sync_status":{let r=e.placeId;return{success:!0,data:n.getStatusDirect(r)}}case"sync_config":return{success:!0,data:n.getConfigDirect()};case"sync_history":{let r=e.placeId,i=e.query;return r==null?{success:!1,error:"placeId is required for sync_history"}:{success:!0,data:await n.getHistoryDirect(r,i)}}case"sync_directions":{let r=e.placeId;return{success:!0,data:n.getDirectionsDirect(r)}}case"sync_progress":{let r=e.placeId;return{success:!0,data:n.getProgressDirect(r)}}case"sync_read_file":{let r=e.placeId,i=e.instancePath;return r==null||!i?{success:!1,error:"placeId and instancePath are required"}:{success:!0,data:await n.readSyncedFile(r,i)}}case"sync_write_file":{let r=e.placeId,i=e.instancePath,a=e.content;return r==null||!i||a===void 0?{success:!1,error:"placeId, instancePath, and content are required"}:(await n.writeSyncedFile(r,i,a),{success:!0,data:{written:!0,placeId:r,instancePath:i}})}default:return{success:!1,error:`Unknown sync action: ${t}`}}}catch(r){let i=r instanceof Error?r.message:"Unknown error";return y.error("Sync action failed",{action:t,error:i}),{success:!1,error:i}}}async function OS(t,e,n){if(!n)return{success:!1,error:"Sync controller not available for disk operations."};try{return{success:!0,data:await n.executeViaDisk(t,e)}}catch(r){let i=r instanceof Error?r.message:"Unknown error";return y.error("Disk action failed",{action:t,error:i}),{success:!1,error:i}}}Et();import*as yt from"fs";import*as aa from"path";import{randomUUID as DS}from"crypto";pe();var vte={enableLocalHistory:!0,enableLocalStatistics:!0,dataDir:br(),maxHistoryFileSize:10*1024*1024,maxHistoryFiles:100};function Yf(){return{version:2,lastUpdated:new Date().toISOString(),totalSessions:0,totalCalls:0,totalSuccessCalls:0,totalFailedCalls:0,tools:{},tierSummary:{basic:{totalCalls:0,successCount:0,failureCount:0},pro:{totalCalls:0,successCount:0,failureCount:0}}}}function xte(t){let e={};for(let[n,r]of Object.entries(t.tools??{})){let i={},a=!1,o=!1;for(let[c,l]of Object.entries(r.actions??{})){let u={action:l.action??l.actionName??c,tier:l.tier,totalCalls:l.totalCalls,successCount:l.successCount,failureCount:l.failureCount,totalExecutionTimeMs:l.totalExecutionTimeMs,avgExecutionTimeMs:l.avgExecutionTimeMs,firstUsed:l.firstUsed,lastUsed:l.lastUsed};i[c]=u,u.tier==="basic"?a=!0:o=!0}let s=a&&o?"mixed":o?"pro":"basic";e[n]={...r,tier:s,actions:i}}return{...t,version:2,tools:e}}function bte(){let t=new Date,e=String(t.getFullYear()).slice(2),n=String(t.getMonth()+1).padStart(2,"0"),r=String(t.getDate()).padStart(2,"0"),i=String(t.getHours()).padStart(2,"0"),a=String(t.getMinutes()).padStart(2,"0"),o=String(t.getSeconds()).padStart(2,"0"),s=DS().replace(/-/g,"").slice(0,8).toUpperCase();return`${e}${n}${r}-${i}${a}${o}-${s}.jsonl`}function fz(t){let e=new Set(Object.values(t.actions??{}).map(n=>n.tier));return e.has("basic")&&e.has("pro")?"mixed":e.has("pro")?"pro":"basic"}async function _te(t){try{let e=await yt.promises.readFile(t,"utf-8"),n=JSON.parse(e);return n.version===1?Yf():xte(n)}catch{return Yf()}}var Qf=class{config;syncRoot;sessionId="";initialized=!1;buckets=new Map;onRecordCallback=null;onEntryRecordCallback=null;placeResolver=null;constructor(e={}){this.config={...vte,...e},this.syncRoot=Qn(this.config.dataDir)}async initialize(e){if(this.sessionId=e,this.initialized=!0,!this.config.enableLocalHistory&&!this.config.enableLocalStatistics){y.info("Tool history manager initialized (all features disabled)",{sessionId:e});return}await yt.promises.mkdir(this.syncRoot,{recursive:!0}),await this.ensureBucket({}),y.info("Tool history manager initialized",{sessionId:e,localHistoryEnabled:this.config.enableLocalHistory,localStatisticsEnabled:this.config.enableLocalStatistics,dataDir:this.syncRoot})}setPlaceResolver(e){this.placeResolver=e}getSyncRoot(){return this.syncRoot}buildBucketKey(e){return this.placeResolver===null&&e===void 0?"legacy-root":e==null||!Number.isFinite(e)?"place_unassigned":`place_${e}`}resolvePlaceContext(e){return this.placeResolver?.(e)??{}}toPlaceContext(e,n){return{...e!==void 0?{placeId:e}:{},...n!==void 0?{placeName:n}:{}}}async ensureBucket(e){let n=this.buildBucketKey(e.placeId),r=this.buckets.get(n);if(r)return!r.placeName&&e.placeName&&(r.placeName=e.placeName),r;let i=n==="legacy-root"?aa.join(this.syncRoot,"observability"):await Co(this.syncRoot,e.placeId,e.placeName),a=aa.join(i,"tool-history"),o=aa.join(i,"tool-stats.json"),s=aa.join(i,"failures.jsonl");await yt.promises.mkdir(i,{recursive:!0}),await yt.promises.mkdir(a,{recursive:!0});let c=await _te(o);c.totalSessions++;let l={key:n,placeId:e.placeId??void 0,placeName:e.placeName??void 0,observabilityDir:i,historyDir:a,statisticsFilePath:o,failureLogPath:s,historyFilePath:null,statistics:c,sequenceNumber:0,sessionOpened:!1,saveTimer:null,pendingSave:!1};return this.buckets.set(n,l),await this.openBucketSession(l),await this.saveStatistics(l),l}async openBucketSession(e){if(!e.sessionOpened){if(this.config.enableLocalHistory){e.historyFilePath=aa.join(e.historyDir,bte());let n={sessionId:this.sessionId,startTime:new Date().toISOString(),totalCommands:0,pid:process.pid,version:We,...e.placeId!==void 0?{placeId:e.placeId}:{},...e.placeName?{placeName:e.placeName}:{}};await yt.promises.appendFile(e.historyFilePath,`# SESSION_START ${JSON.stringify(n)}
138
+ `),t.dashboardSseClients??=new Set,t.dashboardSseClients.add(i),r.on("close",()=>{t.dashboardSseClients?.delete(i)})}),e.get("/api/dashboard/settings",(r,i)=>{try{let a=oz(t),o=Gf(a);o.ENABLE_CONTEXT_CAPTURE=t.executionContextManager?.isEnabled()??t.config.enableContextCapture??o.ENABLE_CONTEXT_CAPTURE;let s={HTTP_PORT:t.config.httpPort,HTTP_HOST:t.config.httpHost,DASHBOARD_AUTO_OPEN:!1,ENABLE_TELEMETRY:t.config.enableTelemetry??!1};i.json({hot:o,cold:s})}catch(a){y.error("\uB300\uC2DC\uBCF4\uB4DC \uC124\uC815 \uC870\uD68C \uC2E4\uD328",a),i.status(500).json({error:"Failed to load settings"})}}),e.patch("/api/dashboard/settings",(r,i)=>{try{let a=oz(t),o=r.body,s=ND(o,t);AD(a,s);let c={HTTP_PORT:t.config.httpPort,HTTP_HOST:t.config.httpHost,DASHBOARD_AUTO_OPEN:!1,ENABLE_TELEMETRY:t.config.enableTelemetry??!1};i.json({hot:s,cold:c})}catch(a){y.error("\uB300\uC2DC\uBCF4\uB4DC \uC124\uC815 \uBCC0\uACBD \uC2E4\uD328",a),i.status(500).json({error:"Failed to save settings"})}}),e.get("/api/dashboard/sync-root",(r,i)=>{try{i.json(ES(t))}catch(a){y.error("\uB300\uC2DC\uBCF4\uB4DC sync-root \uC0C1\uD0DC \uC870\uD68C \uC2E4\uD328",a),i.status(500).json({error:"Failed to load dashboard sync root state"})}}),e.get("/api/dashboard/place-summary",OD(t)),e.post("/api/dashboard/sync-root/pick",async(r,i)=>{try{i.json(await iz(t))}catch(a){if(a instanceof li){i.status(503).json({error:a.code,message:a.message});return}y.error("\uB300\uC2DC\uBCF4\uB4DC sync-root picker \uD638\uCD9C \uC2E4\uD328",a),i.status(500).json({error:"Failed to open native folder picker"})}}),e.patch("/api/dashboard/sync-root",async(r,i)=>{try{let a=r.body?.projectRoot,o=await az({ctx:t,projectRoot:a??""});t.analyticsManager?.trackDashboardEvents([{name:"dashboard_project_root_changed",params:{has_override:o.hasOverride?1:0}}]),i.json(o)}catch(a){if(a instanceof Ir){i.status(a.statusCode).json({error:a.code,message:a.message});return}y.error("\uB300\uC2DC\uBCF4\uB4DC sync-root \uC804\uD658 \uC2E4\uD328",a),i.status(500).json({error:"Failed to apply dashboard sync root"})}}),e.get("/api/dashboard/license/status",(r,i)=>_S(t,r,i,"dashboard")),e.post("/api/dashboard/license/activate",(r,i)=>bS(t,r,i,"dashboard")),e.post("/api/dashboard/license/refresh",(r,i)=>wS(t,r,i,"dashboard")),e.post("/api/dashboard/license/reset",(r,i)=>SS(t,r,i,"dashboard")),e.get("/api/dashboard/tool-stats",zD(t)),e.get("/api/dashboard/history",MD(t)),e.get("/api/dashboard/failures",LD(t)),e.get("/api/dashboard/connection-log",UD(t)),e.post("/api/dashboard/connection-log/clear",FD(t)),e.get("/api/dashboard/plugin-logs",HD(t)),e.post("/api/dashboard/tools/history/clear",qD(t)),e.post("/api/dashboard/tools/statistics/clear",BD(t)),e.post("/api/dashboard/sync/history/clear",ZD(t)),e.get("/api/dashboard/changelog/active",VD(t)),e.get("/api/dashboard/changelog",JN(t)),e.get("/api/dashboard/changelog/:id",KN(t)),e.get("/api/dashboard/changelog/:id/changes",XN(t)),e.post("/api/dashboard/changelog/clear",YN(t)),e.get("/api/dashboard/playtest/status",GD(t)),e.get("/api/dashboard/playtest/history",JD(t)),e.get("/api/dashboard/playtest/report/:timestamp",KD(t)),e.post("/api/dashboard/playtest/control",XD(t)),e.post("/api/dashboard/playtest/history/clear",YD(t));let n=new Set(["dashboard_page_view","dashboard_tier_toggle","dashboard_upgrade_click"]);return e.post("/api/dashboard/analytics",(r,i)=>{let a=r.body;if(!Array.isArray(a?.events)||a.events.length===0){i.status(400).json({error:"events array is required"});return}let o=a.events.filter(s=>typeof s.name=="string"&&n.has(s.name));o.length>0&&t.analyticsManager&&t.analyticsManager.trackDashboardEvents(o),i.status(202).json({ok:!0})}),e}import cte from"fs";import lte from"path";var ute=["../dashboard/dist","../../mcp-dashboard/dist"];function Kf(t){for(let e of ute){let n=lte.resolve(t,e);if(cte.existsSync(n))return n}return null}Lf();function mte(t,e){let n=t.historyManager?.getCurrentHistoryFile(e.placeId??null);if(!n)return e.sessionId;let r=n.split(/[\\/]/).pop();return r?r.endsWith(".jsonl")?r.slice(0,-6):r:e.sessionId}function hte(t,e){let n=_r([e]);if(n.length===0)return;let r=mte(t,e);TD(t,r,n),(async()=>{let i=new Map;if(t.executionContextManager&&e.contextId){t.executionContextManager.appendLinks({sessionId:e.sessionId,contextId:e.contextId,...e.placeId!==void 0?{placeId:e.placeId}:{},...e.placeName!==void 0?{placeName:e.placeName}:{},links:{changelogEntryIds:[r]}}).catch(()=>{});try{let a=await t.executionContextManager.getLatestSnapshot(e.contextId);a&&(i=new Map([[a.contextId,a]]))}catch{i=new Map}}for(let a of n){let o=i.size>0?Gi(a,i):a;$t(t,"game_change",o)}})()}function gte(t){return!!(t==null||t===""||t==="/"||t==="\\"||/^[A-Z]:\\?$/i.test(t)||!t.startsWith("/")&&!/^[A-Z]:\\/i.test(t))}function IS(t){if(t.pendingDashboardSyncRootPin)return{success:!0,instanceId:t.instanceId,projectRoot:t.pendingDashboardSyncRootPin};let e=[],n=Tn({appDataDir:t.config.appDataDir});e.push({instanceId:t.instanceId,projectRoot:n,activity:t.serverLastCommandAt??t.startTime});for(let i of t.mcpInstances.values())gte(i.projectRoot)||e.push({instanceId:i.instanceId,projectRoot:i.projectRoot,activity:i.lastCommandAt??i.connectedAt});if(e.length===0)return{success:!1,error:"PROJECT_ROOT_UNRESOLVED"};e.sort((i,a)=>a.activity-i.activity);let r=e[0];return{success:!0,instanceId:r.instanceId,projectRoot:r.projectRoot}}var RS=class{ctx;server=null;stopPromise=null;constructor(e){let n=(0,TS.default)();this.ctx=HO(e,n),this.ctx.dashboardFolderPicker=Jf,WO(this.ctx),xD(this.ctx,()=>this.stop()),RD(this.ctx),n.use(_D(this.ctx)),n.use(cz(this.ctx));let r=pte(import.meta.url),i=Xf.dirname(r),a=Kf(i);a&&(n.use("/dashboard",TS.default.static(a)),n.get("/dashboard/*",(o,s)=>{s.sendFile(Xf.join(a,"index.html"))}))}setHistoryManager(e){this.ctx.historyManager=e,e.setOnRecord(n=>{$t(this.ctx,"command",n)}),e.setOnEntryRecord(n=>{this.ctx.executionContextManager&&n.contextId&&this.ctx.executionContextManager.appendLinks({sessionId:n.sessionId,contextId:n.contextId,...n.placeId!==void 0?{placeId:n.placeId}:{},...n.placeName!==void 0?{placeName:n.placeName}:{},links:{toolHistoryIds:[n.id]}}).catch(()=>{}),hte(this.ctx,n)})}setAnalyticsManager(e){this.ctx.analyticsManager=e}setExecutionContextManager(e){this.ctx.executionContextManager=e}setLicenseStateManager(e){this.ctx.licenseState=e}setInternalActionExecutor(e){this.ctx.internalActionExecutor=e}setAiClientInfo(e,n){this.ctx.aiClientName=e,this.ctx.analyticsManager?.setAiClient(e),this.ctx.isClientMode&&vS(this.ctx,e)}getInstanceId(){return this.ctx.instanceId}getSessionId(){return this.ctx.sessionId}getContext(){return this.ctx}noteLocalCommandActivity(e=Date.now()){this.ctx.serverLastCommandAt=e}getSyncController(){return this.ctx.syncController}getIsClientMode(){return this.ctx.isClientMode}getDashboardSseClientCount(){return this.ctx.dashboardSseClients?.size??0}getBridgeContext(){return this.ctx}getCachedSelection(e=3e4,n){return El(this.ctx,e,n)}async getConnectionInfo(){return Uf(this.ctx)}async executeCommand(e,n,r){return this.ctx.isClientMode?yS(this.ctx,e,n,r):Rl(this.ctx,e,n,dte(),r)}async start(){let e=await hS(this.ctx);if(e)if(y.info("Existing MCP server found, switching to CLIENT MODE",{existingPid:e.pid,existingUptime:e.uptime,existingConnectedClients:e.connectedClients}),this.ctx.isClientMode=!0,await Mf(this.ctx)){zf(this.ctx),y.info("HTTP Bridge started in CLIENT MODE",{serverUrl:this.ctx.baseUrl,myPid:process.pid,myInstanceId:this.ctx.instanceId});return}else y.warn("Failed to register with server, will try to start as server");return new Promise((n,r)=>{this.server=fte(this.ctx.app),this.server.on("error",async i=>{i.code==="EADDRINUSE"?(y.info("Port in use, switching to CLIENT MODE",{port:this.ctx.config.httpPort}),this.ctx.isClientMode=!0,await Mf(this.ctx)?(zf(this.ctx),y.info("HTTP Bridge started in CLIENT MODE (after EADDRINUSE)",{serverUrl:this.ctx.baseUrl,myPid:process.pid}),n()):r(new Error(`Port ${this.ctx.config.httpPort} is in use but failed to connect as client. Check if the existing server is responsive: curl ${this.ctx.baseUrl}/status`))):r(i)}),this.server.listen(this.ctx.config.httpPort,this.ctx.config.httpHost,async()=>{this.ctx.isClientMode=!1,$l(this.ctx),this.ctx.clientModeUpstreamReachable=!0,this.ctx.clientModeConsecutiveHealthFailures=0,this.ctx.clientModeLastHealthError=null,this.ctx.clientModeLastHealthSuccessAt=null,this.ctx.clientModeLastHealthFailureAt=null,this.ctx.config.httpHost!=="127.0.0.1"&&this.ctx.config.httpHost!=="localhost"&&y.warn("HTTP_HOST is not localhost -- sync data may be exposed",{host:this.ctx.config.httpHost});try{this.ctx.syncController=new si(void 0,{appDataDir:this.ctx.config.appDataDir}),await this.ctx.syncController.initialize(),kr(this.ctx),y.info("SyncController initialized")}catch(i){y.error("Failed to initialize SyncController",i instanceof Error?i:new Error(String(i)))}y.info("HTTP Bridge started in SERVER MODE",{host:this.ctx.config.httpHost,port:this.ctx.config.httpPort,pid:process.pid,instanceId:this.ctx.instanceId}),n()})})}async stop(){return this.stopPromise?this.stopPromise:(this.stopPromise=this.stopInternal(),this.stopPromise)}async stopInternal(){if(y.info("Stopping HTTP Bridge",{isClientMode:this.ctx.isClientMode}),this.ctx.isClientMode){$l(this.ctx);try{await fetch(`${this.ctx.baseUrl}/unregister-mcp`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({instanceId:this.ctx.instanceId})}),y.info("Unregistered from server")}catch{}y.info("Client mode - stopped");return}if(this.ctx.syncController){try{await this.ctx.syncController.shutdown()}catch(n){y.error("Error shutting down SyncController",n instanceof Error?n:new Error(String(n)))}this.ctx.syncController=null}for(let n of this.ctx.sseClients)n.end();this.ctx.sseClients.clear();for(let[n,r]of this.ctx.commandQueue)clearTimeout(r.timeoutId),r.reject(new Error("Server shutting down")),this.ctx.commandQueue.delete(n);let e=this.server;this.server=null,e&&await new Promise(n=>{e.close(r=>{r?y.warn("HTTP server close returned during shutdown",{error:r.message}):y.info("HTTP server closed"),n()})})}},lz=RS;pe();Et();var uz=new Set(["add_to_selection","analyze_walkable_area","batch_execute","cancel_tween","check_collision","check_placement","clear_change_history","clear_particles","clear_state_cache","create_instance_tree","create_tween","emit_particles","execute_luau","export_selection","extended_call_method","extended_call_methods","extended_get_properties","extended_get_property","extended_set_properties","extended_set_property","find_empty_space","find_flat_areas","find_ground","find_spawn_positions","get_ancestors","get_animation_tracks","get_asset_info","get_bounds","get_collision_groups","get_descendants","get_file_tree","get_place_info","get_play_status","get_project_structure","get_recent_changes","get_script_list","get_selection_context","get_selection_details","get_selection_info","get_services","get_spatial_map","get_studio_settings","get_viewport_info","get_workspace_metadata","get_workspace_snapshot","insert_free_model","insert_model","insert_package","load_animation","mass_create_instances","mass_delete_instances","mass_duplicate","mass_get_property","mass_set_property","modify_children","multi_raycast","pause_playtest","pause_sound","pause_tween","play_animation","play_sound","play_tween","raycast","register_collision_group","remove_from_selection","replace_in_scripts","resume_playtest","resume_sound","run_test","scan_area","search_and_insert_model","search_by_property","search_by_tag","search_creator_store","set_atmosphere","set_calculated_property","set_collidable","set_lighting","set_listener","set_relative_property","set_sky","set_terrain","set_time_of_day","smart_duplicate","snap_to_grid","start_playtest","stop_animation","stop_playtest","stop_sound","sync_config","sync_directions","sync_history","sync_progress","sync_read_file","sync_status","sync_workspace_state","sync_write_file","terrain_clear","terrain_clear_region","terrain_fill_ball","terrain_fill_block","terrain_fill_cylinder","terrain_fill_wedge","terrain_generate","terrain_get_material_color","terrain_read_voxel","terrain_read_voxels","terrain_replace_material","terrain_set_material_color","terrain_smooth","terrain_write_voxels","toggle_effect","watch_selection"]),jS=new Set(["get_connection_info","get_usage_status","ping"]);function Go(t){return uz.has(t)?"pro":"basic"}var pz=new Set(["execute_luau","batch_execute"]);function AS(t,e){let n=Ff[t];if(!n){if(t==="batch_execute"&&Array.isArray(e.commands)){let c=e.commands.map(l=>{if(pz.has(l.tool))throw new Error(`Tool "${l.tool}" is not allowed inside batch_execute. Denied tools: ${[...pz].join(", ")}`);return{toolName:l.tool,...AS(l.tool,l.args||{})}});return{action:t,params:{...e,commands:c}}}return{action:t,params:e}}let r=e[n.discriminator],i=n.mapping[r];if(!i){let c=Object.keys(n.mapping).join(", ");throw new Error(`Invalid ${n.discriminator} "${r}" for tool "${t}". Valid values: ${c}`)}let{[n.discriminator]:a,...o}=e,s=n.paramAliases?.[i];if(s)for(let[c,l]of Object.entries(s))c in o&&!(l in o)&&(o[l]=o[c],delete o[c]);return{action:i,params:o}}var yte={manage_scripts:["scripts"],manage_properties:["values","data"],mutate_instances:["containers"],manage_lighting:["services"]};function NS(t,e,n){let r=qf(e);if(r!=="plugin")return{target:r};let i=yte[t];if(!i||!n)return{target:"plugin"};try{let a=n.getStatusSummary();if(!a||!a.active)return{target:"plugin"};for(let o of i)if(n.getDirectionForCategory(o)==="reverse")return{target:"disk"}}catch{}return{target:"plugin"}}pe();async function dz(t,e,n){if(!n)return{success:!1,error:"Sync controller not available. Project sync is not initialized."};try{switch(t){case"sync_status":{let r=e.placeId;return{success:!0,data:n.getStatusDirect(r)}}case"sync_config":return{success:!0,data:n.getConfigDirect()};case"sync_history":{let r=e.placeId,i=e.query;return r==null?{success:!1,error:"placeId is required for sync_history"}:{success:!0,data:await n.getHistoryDirect(r,i)}}case"sync_directions":{let r=e.placeId;return{success:!0,data:n.getDirectionsDirect(r)}}case"sync_progress":{let r=e.placeId;return{success:!0,data:n.getProgressDirect(r)}}case"sync_read_file":{let r=e.placeId,i=e.instancePath;return r==null||!i?{success:!1,error:"placeId and instancePath are required"}:{success:!0,data:await n.readSyncedFile(r,i)}}case"sync_write_file":{let r=e.placeId,i=e.instancePath,a=e.content;return r==null||!i||a===void 0?{success:!1,error:"placeId, instancePath, and content are required"}:(await n.writeSyncedFile(r,i,a),{success:!0,data:{written:!0,placeId:r,instancePath:i}})}default:return{success:!1,error:`Unknown sync action: ${t}`}}}catch(r){let i=r instanceof Error?r.message:"Unknown error";return y.error("Sync action failed",{action:t,error:i}),{success:!1,error:i}}}async function OS(t,e,n){if(!n)return{success:!1,error:"Sync controller not available for disk operations."};try{return{success:!0,data:await n.executeViaDisk(t,e)}}catch(r){let i=r instanceof Error?r.message:"Unknown error";return y.error("Disk action failed",{action:t,error:i}),{success:!1,error:i}}}Et();import*as yt from"fs";import*as aa from"path";import{randomUUID as DS}from"crypto";pe();var vte={enableLocalHistory:!0,enableLocalStatistics:!0,dataDir:br(),maxHistoryFileSize:10*1024*1024,maxHistoryFiles:100};function Yf(){return{version:2,lastUpdated:new Date().toISOString(),totalSessions:0,totalCalls:0,totalSuccessCalls:0,totalFailedCalls:0,tools:{},tierSummary:{basic:{totalCalls:0,successCount:0,failureCount:0},pro:{totalCalls:0,successCount:0,failureCount:0}}}}function xte(t){let e={};for(let[n,r]of Object.entries(t.tools??{})){let i={},a=!1,o=!1;for(let[c,l]of Object.entries(r.actions??{})){let u={action:l.action??l.actionName??c,tier:l.tier,totalCalls:l.totalCalls,successCount:l.successCount,failureCount:l.failureCount,totalExecutionTimeMs:l.totalExecutionTimeMs,avgExecutionTimeMs:l.avgExecutionTimeMs,firstUsed:l.firstUsed,lastUsed:l.lastUsed};i[c]=u,u.tier==="basic"?a=!0:o=!0}let s=a&&o?"mixed":o?"pro":"basic";e[n]={...r,tier:s,actions:i}}return{...t,version:2,tools:e}}function bte(){let t=new Date,e=String(t.getFullYear()).slice(2),n=String(t.getMonth()+1).padStart(2,"0"),r=String(t.getDate()).padStart(2,"0"),i=String(t.getHours()).padStart(2,"0"),a=String(t.getMinutes()).padStart(2,"0"),o=String(t.getSeconds()).padStart(2,"0"),s=DS().replace(/-/g,"").slice(0,8).toUpperCase();return`${e}${n}${r}-${i}${a}${o}-${s}.jsonl`}function fz(t){let e=new Set(Object.values(t.actions??{}).map(n=>n.tier));return e.has("basic")&&e.has("pro")?"mixed":e.has("pro")?"pro":"basic"}async function _te(t){try{let e=await yt.promises.readFile(t,"utf-8"),n=JSON.parse(e);return n.version===1?Yf():xte(n)}catch{return Yf()}}var Qf=class{config;syncRoot;sessionId="";initialized=!1;buckets=new Map;onRecordCallback=null;onEntryRecordCallback=null;placeResolver=null;constructor(e={}){this.config={...vte,...e},this.syncRoot=Qn(this.config.dataDir)}async initialize(e){if(this.sessionId=e,this.initialized=!0,!this.config.enableLocalHistory&&!this.config.enableLocalStatistics){y.info("Tool history manager initialized (all features disabled)",{sessionId:e});return}await yt.promises.mkdir(this.syncRoot,{recursive:!0}),await this.ensureBucket({}),y.info("Tool history manager initialized",{sessionId:e,localHistoryEnabled:this.config.enableLocalHistory,localStatisticsEnabled:this.config.enableLocalStatistics,dataDir:this.syncRoot})}setPlaceResolver(e){this.placeResolver=e}getSyncRoot(){return this.syncRoot}buildBucketKey(e){return this.placeResolver===null&&e===void 0?"legacy-root":e==null||!Number.isFinite(e)?"place_unassigned":`place_${e}`}resolvePlaceContext(e){return this.placeResolver?.(e)??{}}toPlaceContext(e,n){return{...e!==void 0?{placeId:e}:{},...n!==void 0?{placeName:n}:{}}}async ensureBucket(e){let n=this.buildBucketKey(e.placeId),r=this.buckets.get(n);if(r)return!r.placeName&&e.placeName&&(r.placeName=e.placeName),r;let i=n==="legacy-root"?aa.join(this.syncRoot,"observability"):await Co(this.syncRoot,e.placeId,e.placeName),a=aa.join(i,"tool-history"),o=aa.join(i,"tool-stats.json"),s=aa.join(i,"failures.jsonl");await yt.promises.mkdir(i,{recursive:!0}),await yt.promises.mkdir(a,{recursive:!0});let c=await _te(o);c.totalSessions++;let l={key:n,placeId:e.placeId??void 0,placeName:e.placeName??void 0,observabilityDir:i,historyDir:a,statisticsFilePath:o,failureLogPath:s,historyFilePath:null,statistics:c,sequenceNumber:0,sessionOpened:!1,saveTimer:null,pendingSave:!1};return this.buckets.set(n,l),await this.openBucketSession(l),await this.saveStatistics(l),l}async openBucketSession(e){if(!e.sessionOpened){if(this.config.enableLocalHistory){e.historyFilePath=aa.join(e.historyDir,bte());let n={sessionId:this.sessionId,startTime:new Date().toISOString(),totalCommands:0,pid:process.pid,version:We,...e.placeId!==void 0?{placeId:e.placeId}:{},...e.placeName?{placeName:e.placeName}:{}};await yt.promises.appendFile(e.historyFilePath,`# SESSION_START ${JSON.stringify(n)}
139
139
  `)}e.sessionOpened=!0}}async saveStatistics(e){if(this.config.enableLocalStatistics){if(e.saveTimer){e.pendingSave=!0;return}e.statistics.lastUpdated=new Date().toISOString(),await yt.promises.writeFile(e.statisticsFilePath,JSON.stringify(e.statistics,null,2)),e.saveTimer=setTimeout(async()=>{e.saveTimer=null,e.pendingSave&&(e.pendingSave=!1,await this.saveStatistics(e))},1e3)}}updateToolStats(e,n,r,i,a,o,s){e.totalCalls++,i?e.totalSuccessCalls++:e.totalFailedCalls++;let c=s??n;jS.has(c)||(e.tierSummary[r].totalCalls++,i?e.tierSummary[r].successCount++:e.tierSummary[r].failureCount++),e.tools[n]||(e.tools[n]={toolName:n,tier:r,actions:{},totalCalls:0,successCount:0,failureCount:0,totalExecutionTimeMs:0,avgExecutionTimeMs:0,firstUsed:o,lastUsed:o});let l=e.tools[n],u=c;l.actions[u]||(l.actions[u]={action:u,tier:r,totalCalls:0,successCount:0,failureCount:0,totalExecutionTimeMs:0,avgExecutionTimeMs:0,firstUsed:o,lastUsed:o});let p=l.actions[u];p.totalCalls++,p.totalExecutionTimeMs+=a,p.avgExecutionTimeMs=Math.round(p.totalExecutionTimeMs/p.totalCalls),p.lastUsed=o,i?p.successCount++:p.failureCount++,l.totalCalls++,l.totalExecutionTimeMs+=a,l.avgExecutionTimeMs=Math.round(l.totalExecutionTimeMs/l.totalCalls),l.lastUsed=o,l.tier=fz(l),i?l.successCount++:l.failureCount++}async recordSuccess(e,n,r,i,a,o){if(!this.initialized){y.warn("Tool history manager not initialized, skipping record");return}let s=this.resolvePlaceContext(n),c=await this.ensureBucket(s),l=Go(a??e),u=new Date().toISOString(),p=a??e,{__sessionId:d,...f}=n,m=a&&a!==e?{...f,action:p}:f;this.config.enableLocalStatistics&&(this.updateToolStats(c.statistics,e,l,!0,i,u,a),await this.saveStatistics(c)),c.sequenceNumber++;let h={id:DS(),sessionId:typeof d=="string"?d:this.sessionId,placeId:c.placeId,placeName:c.placeName,timestamp:u,sequenceNumber:c.sequenceNumber,toolName:e,contextId:typeof m.contextId=="string"?m.contextId:void 0,tier:l,parameters:m,result:r,executionTimeMs:i,clientId:o};this.config.enableLocalHistory&&c.historyFilePath&&await yt.promises.appendFile(c.historyFilePath,JSON.stringify(h)+`
140
140
  `),this.onRecordCallback?.({tool:e,action:p,success:!0,durationMs:i}),this.onEntryRecordCallback?.(h)}async recordFailure(e,n,r,i,a,o){if(!this.initialized){y.warn("Tool history manager not initialized, skipping record");return}let s=this.resolvePlaceContext(n),c=await this.ensureBucket(s),l=Go(o??e),u=new Date().toISOString(),p=o??e,{__sessionId:d,...f}=n,m=o&&o!==e?{...f,action:p}:f;this.config.enableLocalStatistics&&(this.updateToolStats(c.statistics,e,l,!1,i||0,u,o),await this.saveStatistics(c));let h={id:DS(),sessionId:typeof d=="string"?d:this.sessionId,placeId:c.placeId,placeName:c.placeName,timestamp:u,toolName:e,contextId:typeof m.contextId=="string"?m.contextId:void 0,tier:l,parameters:m,errorMessage:r,errorCode:a,executionTimeMs:i};await yt.promises.appendFile(c.failureLogPath,JSON.stringify(h)+`
141
141
  `),this.onRecordCallback?.({tool:e,action:p,success:!1,durationMs:i||0})}async getStatisticsForPlace(e,n){return{...(await this.ensureBucket(this.toPlaceContext(e,n))).statistics}}getStatistics(){let e=Yf();for(let n of this.buckets.values()){e.totalSessions+=n.statistics.totalSessions,e.totalCalls+=n.statistics.totalCalls,e.totalSuccessCalls+=n.statistics.totalSuccessCalls,e.totalFailedCalls+=n.statistics.totalFailedCalls,e.tierSummary.basic.totalCalls+=n.statistics.tierSummary.basic.totalCalls,e.tierSummary.basic.successCount+=n.statistics.tierSummary.basic.successCount,e.tierSummary.basic.failureCount+=n.statistics.tierSummary.basic.failureCount,e.tierSummary.pro.totalCalls+=n.statistics.tierSummary.pro.totalCalls,e.tierSummary.pro.successCount+=n.statistics.tierSummary.pro.successCount,e.tierSummary.pro.failureCount+=n.statistics.tierSummary.pro.failureCount;for(let[r,i]of Object.entries(n.statistics.tools)){if(!e.tools[r]){e.tools[r]=structuredClone(i);continue}let a=e.tools[r];a.totalCalls+=i.totalCalls,a.successCount+=i.successCount,a.failureCount+=i.failureCount,a.totalExecutionTimeMs+=i.totalExecutionTimeMs,a.avgExecutionTimeMs=Math.round(a.totalExecutionTimeMs/Math.max(1,a.totalCalls)),a.lastUsed=a.lastUsed>i.lastUsed?a.lastUsed:i.lastUsed,a.firstUsed=a.firstUsed<i.firstUsed?a.firstUsed:i.firstUsed;for(let[o,s]of Object.entries(i.actions??{})){if(!a.actions[o]){a.actions[o]=structuredClone(s);continue}let c=a.actions[o];c.totalCalls+=s.totalCalls,c.successCount+=s.successCount,c.failureCount+=s.failureCount,c.totalExecutionTimeMs+=s.totalExecutionTimeMs,c.avgExecutionTimeMs=Math.round(c.totalExecutionTimeMs/Math.max(1,c.totalCalls)),c.lastUsed=c.lastUsed>s.lastUsed?c.lastUsed:s.lastUsed,c.firstUsed=c.firstUsed<s.firstUsed?c.firstUsed:s.firstUsed}a.tier=fz(a)}}return e}async getHistoryDir(e,n){return(await this.ensureBucket(this.toPlaceContext(e,n))).historyDir}async getStatisticsDir(e,n){return(await this.ensureBucket(this.toPlaceContext(e,n))).observabilityDir}getCurrentHistoryFile(e){let n=this.buildBucketKey(e);return this.buckets.get(n)?.historyFilePath??null}async clearHistoryForPlace(e,n){let r=await this.ensureBucket(this.toPlaceContext(e,n)),i=0;try{let o=(await yt.promises.readdir(r.historyDir)).filter(s=>s.endsWith(".jsonl"));for(let s of o)await yt.promises.rm(aa.join(r.historyDir,s),{force:!0}),i++}catch{}return await yt.promises.writeFile(r.failureLogPath,""),i}async clearStatisticsForPlace(e,n){let r=await this.ensureBucket(this.toPlaceContext(e,n));r.statistics=Yf(),await yt.promises.writeFile(r.statisticsFilePath,JSON.stringify(r.statistics,null,2))}getTopTools(e=10){return Object.values(this.getStatistics().tools).sort((n,r)=>r.totalCalls-n.totalCalls).slice(0,e)}async shutdown(){this.initialized&&(await this.flushAndCloseBuckets(),this.initialized=!1)}async retargetSyncRoot(e){let n=this.syncRoot,r=Qn(e);return r===n||(this.initialized&&(await this.flushAndCloseBuckets(),this.buckets.clear()),this.syncRoot=r,this.config.dataDir=r,this.initialized&&(this.config.enableLocalHistory||this.config.enableLocalStatistics)&&(await yt.promises.mkdir(this.syncRoot,{recursive:!0}),await this.ensureBucket({}))),n}async flushAndCloseBuckets(){for(let e of this.buckets.values())if(e.saveTimer&&(clearTimeout(e.saveTimer),e.saveTimer=null),this.config.enableLocalStatistics&&(e.statistics.lastUpdated=new Date().toISOString(),await yt.promises.writeFile(e.statisticsFilePath,JSON.stringify(e.statistics,null,2))),this.config.enableLocalHistory&&e.historyFilePath){let n={sessionId:this.sessionId,startTime:"",endTime:new Date().toISOString(),totalCommands:e.sequenceNumber,pid:process.pid,version:We,...e.placeId!==void 0?{placeId:e.placeId}:{},...e.placeName?{placeName:e.placeName}:{}};await yt.promises.appendFile(e.historyFilePath,`# SESSION_END ${JSON.stringify(n)}
142
142
  `)}}setLocalHistoryEnabled(e){this.config.enableLocalHistory=e}setLocalStatisticsEnabled(e){this.config.enableLocalStatistics=e}setOnRecord(e){this.onRecordCallback=e}setOnEntryRecord(e){this.onEntryRecordCallback=e}isLocalHistoryEnabled(){return this.config.enableLocalHistory}isLocalStatisticsEnabled(){return this.config.enableLocalStatistics}};import*as Jo from"fs";import*as tm from"path";import{randomUUID as wte}from"crypto";var mz=1,Ste=new Set(["script","instance","property","system","gameplay","ui","terrain","lighting","asset","other"]);function kte(){return{intent:null,affectedAreas:[],testScenario:null,expectedBehavior:null,observedBehavior:null}}function Ite(){return{mode:null,timeout:null,seed:null,inputRefs:[],setupRefs:[],assertionRefs:[],artifactRefs:[]}}function hz(){return{toolHistoryIds:[],changelogEntryIds:[],playtestTimestamps:[]}}function em(t){return t!==null&&typeof t=="object"}function xz(t){return Array.isArray(t)?t.flatMap(e=>{if(!e||typeof e!="object")return[];let n=e;return typeof n.kind!="string"||!Ste.has(n.kind)||typeof n.target!="string"||typeof n.label!="string"?[]:[{kind:n.kind,target:n.target,label:n.label}]}):[]}function gz(t){return{intent:typeof t?.intent=="string"?t.intent:null,affectedAreas:xz(t?.affectedAreas),testScenario:typeof t?.testScenario=="string"?t.testScenario:null,expectedBehavior:typeof t?.expectedBehavior=="string"?t.expectedBehavior:null,observedBehavior:typeof t?.observedBehavior=="string"?t.observedBehavior:null}}function yz(t){let e=n=>Array.isArray(n)?n.filter(r=>typeof r=="string"):[];return{mode:typeof t?.mode=="string"?t.mode:null,timeout:typeof t?.timeout=="number"&&Number.isFinite(t.timeout)?t.timeout:null,seed:typeof t?.seed=="number"&&Number.isFinite(t.seed)?t.seed:null,inputRefs:e(t?.inputRefs),setupRefs:e(t?.setupRefs),assertionRefs:e(t?.assertionRefs),artifactRefs:e(t?.artifactRefs)}}function Pte(t,e){return e===void 0?t:e===null?kte():{intent:Object.prototype.hasOwnProperty.call(e,"intent")?typeof e.intent=="string"?e.intent:null:t.intent,affectedAreas:Object.prototype.hasOwnProperty.call(e,"affectedAreas")?xz(e.affectedAreas):t.affectedAreas,testScenario:Object.prototype.hasOwnProperty.call(e,"testScenario")?typeof e.testScenario=="string"?e.testScenario:null:t.testScenario,expectedBehavior:Object.prototype.hasOwnProperty.call(e,"expectedBehavior")?typeof e.expectedBehavior=="string"?e.expectedBehavior:null:t.expectedBehavior,observedBehavior:Object.prototype.hasOwnProperty.call(e,"observedBehavior")?typeof e.observedBehavior=="string"?e.observedBehavior:null:t.observedBehavior}}function Cte(t,e){if(e===void 0)return t;if(e===null)return Ite();let n=r=>{let i=e[r];return i===null?[]:Array.isArray(i)?i.filter(a=>typeof a=="string"):t[r]};return{mode:Object.prototype.hasOwnProperty.call(e,"mode")?typeof e.mode=="string"?e.mode:null:t.mode,timeout:Object.prototype.hasOwnProperty.call(e,"timeout")?typeof e.timeout=="number"&&Number.isFinite(e.timeout)?e.timeout:null:t.timeout,seed:Object.prototype.hasOwnProperty.call(e,"seed")?typeof e.seed=="number"&&Number.isFinite(e.seed)?e.seed:null:t.seed,inputRefs:Object.prototype.hasOwnProperty.call(e,"inputRefs")?n("inputRefs"):t.inputRefs,setupRefs:Object.prototype.hasOwnProperty.call(e,"setupRefs")?n("setupRefs"):t.setupRefs,assertionRefs:Object.prototype.hasOwnProperty.call(e,"assertionRefs")?n("assertionRefs"):t.assertionRefs,artifactRefs:Object.prototype.hasOwnProperty.call(e,"artifactRefs")?n("artifactRefs"):t.artifactRefs}}function vz(t,e){let n={toolHistoryIds:[...t.toolHistoryIds],changelogEntryIds:[...t.changelogEntryIds],playtestTimestamps:[...t.playtestTimestamps]};for(let r of Object.keys(n)){let i=e[r];if(!Array.isArray(i)||i.length===0)continue;let a=new Set(n[r]);for(let o of i)typeof o=="string"&&o.length>0&&a.add(o);n[r]=[...a]}return n}var zS=class{syncRoot;enabled;runtimePlaceIdResolver;runtimePlaceNameResolver;activeByScope=new Map;latestByContextId=new Map;lastRuntimePlaceBySession=new Map;ready;constructor(e){this.syncRoot=Qn(e.dataDir),this.enabled=e.enabled,this.runtimePlaceIdResolver=e.runtimePlaceIdResolver??null,this.runtimePlaceNameResolver=e.runtimePlaceNameResolver??null,this.ready=this.loadActiveSnapshots()}getSyncRoot(){return this.syncRoot}async begin(e){if(await this.ready,!this.enabled)return{enabled:!1,applied:!1,skipped:!0};await this.syncRuntimePlace(e.sessionId,e.placeId);let n=this.resolveScope(e.sessionId,e.placeId,e.placeName),r=this.activeByScope.get(n.key);r&&await this.finishSnapshot(r,new Date().toISOString());let i=new Date().toISOString(),a={version:mz,contextId:`ctx_${wte().replace(/-/g,"")}`,source:e.source==="implicit"?"implicit":"explicit",sessionId:e.sessionId,placeId:n.placeId,placeName:n.placeName,status:"active",startedAt:i,lastActivityAt:i,endedAt:null,contextSummary:gz(e.contextSummary),replayMetadata:yz(e.replayMetadata),links:hz()};return await this.persistSnapshot(a),this.activeByScope.set(n.key,a),{enabled:!0,applied:!0,contextId:a.contextId,placeId:a.placeId,status:a.status}}async update(e){if(await this.ready,!this.enabled)return{enabled:!1,applied:!1,skipped:!0,...e.contextId?{contextId:e.contextId}:{}};await this.syncRuntimePlace(e.sessionId,e.placeId);let{scope:n,snapshot:r}=this.requireActiveSnapshot(e.sessionId,e.placeId,e.placeName,e.contextId),i=new Date().toISOString(),a={...r,source:e.patch.source==="explicit"?"explicit":r.source,placeName:Object.prototype.hasOwnProperty.call(e.patch,"placeName")?typeof e.patch.placeName=="string"?e.patch.placeName:null:r.placeName,lastActivityAt:i,contextSummary:Pte(r.contextSummary,e.patch.contextSummary),replayMetadata:Cte(r.replayMetadata,e.patch.replayMetadata),links:r.links};return await this.persistSnapshot(a),this.activeByScope.set(n.key,a),{enabled:!0,applied:!0,contextId:a.contextId,placeId:a.placeId,status:a.status}}async appendLinks(e){if(await this.ready,!this.enabled)return{enabled:!1,applied:!1,skipped:!0,...e.contextId?{contextId:e.contextId}:{}};await this.syncRuntimePlace(e.sessionId,e.placeId);let{scope:n,snapshot:r}=this.requireActiveSnapshot(e.sessionId,e.placeId,e.placeName,e.contextId),i={...r,lastActivityAt:new Date().toISOString(),links:vz(r.links,e.links)};return await this.persistSnapshot(i),this.activeByScope.set(n.key,i),{enabled:!0,applied:!0,contextId:i.contextId,placeId:i.placeId,status:i.status}}async end(e){if(await this.ready,!this.enabled)return{enabled:!1,applied:!1,skipped:!0,...e.contextId?{contextId:e.contextId}:{}};await this.syncRuntimePlace(e.sessionId,e.placeId);let{scope:n,snapshot:r}=this.requireActiveSnapshot(e.sessionId,e.placeId,e.placeName,e.contextId),i=await this.finishSnapshot(r,new Date().toISOString());return this.activeByScope.delete(n.key),{enabled:!0,applied:!0,contextId:i.contextId,placeId:i.placeId,status:i.status}}async getActiveContext(e,n){return await this.ready,this.activeByScope.get(this.buildScopeKey(e,n))??null}async getLatestSnapshot(e){return await this.ready,this.latestByContextId.get(e)??null}findActiveContextSync(e,n){try{return this.syncRuntimePlaceSync(e,n),this.resolveScope(e,n,void 0).snapshot}catch{return null}}async findActiveContext(e,n){await this.ready;try{return await this.syncRuntimePlace(e,n),this.resolveScope(e,n,void 0).snapshot}catch{return null}}async setEnabled(e){if(await this.ready,this.enabled!==e){if(!e){this.enabled=!1;let n=new Date().toISOString(),r=[...this.activeByScope.values()];this.activeByScope.clear(),this.lastRuntimePlaceBySession.clear(),await Promise.all(r.map(i=>this.finishSnapshot(i,n)));return}this.enabled=!0}}isEnabled(){return this.enabled}async retargetSyncRoot(e){await this.ready;let n=this.syncRoot,r=Qn(e);return r===n||(this.syncRoot=r,await this.reloadSnapshotsFromCurrentRoot()),n}async shutdown(){await this.ready;let e=new Date().toISOString(),n=[...this.activeByScope.values()];this.activeByScope.clear(),this.lastRuntimePlaceBySession.clear(),await Promise.all(n.map(r=>this.finishSnapshot(r,e)))}async endSession(e){await this.ready;let n=new Date().toISOString(),r=[...this.activeByScope.entries()].filter(([,i])=>i.sessionId===e);for(let[i]of r)this.activeByScope.delete(i);this.lastRuntimePlaceBySession.delete(e),await Promise.all(r.map(([,i])=>this.finishSnapshot(i,n)))}async syncRuntimePlace(e,n){if(typeof n=="number"&&Number.isFinite(n))return;let r=this.runtimePlaceIdResolver?.();if(typeof r!="number"||!Number.isFinite(r))return;let i=this.lastRuntimePlaceBySession.get(e);if(i!==void 0&&i!==r){let a=this.buildScopeKey(e,i),o=this.activeByScope.get(a);o&&(this.activeByScope.delete(a),await this.finishSnapshot(o,new Date().toISOString()))}this.lastRuntimePlaceBySession.set(e,r)}syncRuntimePlaceSync(e,n){if(typeof n=="number"&&Number.isFinite(n))return;let r=this.runtimePlaceIdResolver?.();if(typeof r!="number"||!Number.isFinite(r))return;let i=this.lastRuntimePlaceBySession.get(e);if(i!==void 0&&i!==r){let a=this.buildScopeKey(e,i),o=this.activeByScope.get(a);o&&(this.activeByScope.delete(a),this.finishSnapshot(o,new Date().toISOString()))}this.lastRuntimePlaceBySession.set(e,r)}resolveScope(e,n,r,i){let a=this.resolvePlaceId(n,i,e),o=this.buildScopeKey(e,a),s=this.activeByScope.get(o)??null,c=r===void 0?s?.placeName??this.runtimePlaceNameResolver?.(a)??null:r;return{key:o,placeId:a,placeName:c,snapshot:s}}requireActiveSnapshot(e,n,r,i){let a=this.resolveScope(e,n,r,i),o=a.snapshot;if(!o)throw new Error("Active execution context not found");if(i&&o.contextId!==i)throw new Error(`Active execution context mismatch: expected ${o.contextId}, received ${i}`);return{scope:a,snapshot:o}}resolvePlaceId(e,n,r){if(typeof e=="number"&&Number.isFinite(e))return e;let i=this.runtimePlaceIdResolver?.();if(typeof i=="number"&&Number.isFinite(i))return i;if(typeof n=="string"&&n.length>0){let a=this.latestByContextId.get(n);if(a&&(r===void 0||a.sessionId===r))return a.placeId}throw new Error("placeId is required")}buildScopeKey(e,n){return`${e}:${n}`}async finishSnapshot(e,n){let r={...e,status:"ended",lastActivityAt:n,endedAt:n};return await this.persistSnapshot(r),r}async persistSnapshot(e){let n=await lf(this.syncRoot,e.placeId,e.placeName);await Jo.promises.mkdir(tm.dirname(n),{recursive:!0}),await Jo.promises.appendFile(n,JSON.stringify(e)+`
143
143
  `),this.latestByContextId.set(e.contextId,e)}async reloadSnapshotsFromCurrentRoot(){this.latestByContextId.clear(),await this.loadActiveSnapshots();for(let e of this.activeByScope.values())this.latestByContextId.set(e.contextId,e)}normalizeSnapshot(e){if(!em(e))return null;let n=typeof e.contextId=="string"?e.contextId:null,r=typeof e.sessionId=="string"?e.sessionId:null,i=typeof e.placeId=="number"&&Number.isFinite(e.placeId)?e.placeId:null,a=e.status==="active"||e.status==="ended"?e.status:null,o=typeof e.startedAt=="string"?e.startedAt:null,s=typeof e.lastActivityAt=="string"?e.lastActivityAt:null,c=typeof e.endedAt=="string"?e.endedAt:(e.endedAt===null,null);return!n||!r||i===null||!a||!o||!s?null:{version:typeof e.version=="number"&&Number.isFinite(e.version)?e.version:mz,contextId:n,source:e.source==="implicit"?"implicit":"explicit",sessionId:r,placeId:i,placeName:typeof e.placeName=="string"?e.placeName:null,status:a,startedAt:o,lastActivityAt:s,endedAt:c,contextSummary:gz(em(e.contextSummary)?e.contextSummary:null),replayMetadata:yz(em(e.replayMetadata)?e.replayMetadata:null),links:vz(hz(),em(e.links)?e.links:{})}}async loadActiveSnapshots(){try{await Jo.promises.mkdir(this.syncRoot,{recursive:!0})}catch{return}let e;try{e=await Jo.promises.readdir(this.syncRoot,{withFileTypes:!0})}catch{return}for(let n of e){if(!n.isDirectory())continue;let r=tm.join(this.syncRoot,n.name,"observability","execution-context-history.jsonl"),i;try{i=await Jo.promises.readFile(r,"utf-8")}catch{continue}for(let a of i.split(`
144
- `)){let o=a.trim();if(o.length!==0)try{let s=this.normalizeSnapshot(JSON.parse(o));if(!s)continue;this.latestByContextId.set(s.contextId,s)}catch{continue}}}}};function bz(t){return new zS(t)}pe();import{createHash as $te,randomUUID as Ete}from"crypto";import*as Ko from"fs";import*as wz from"path";var MS={"claude-code":"Claude Code",claude:"Claude",cursor:"Cursor","codex-cli":"Codex CLI","openai-codex":"Codex CLI","gemini-cli":"Gemini CLI",windsurf:"Windsurf",cline:"Cline",continue:"Continue",zed:"Zed",vscode:"VS Code"};function _z(t){let e=t.toLowerCase();if(MS[e])return MS[e];for(let[n,r]of Object.entries(MS))if(e.includes(n))return r;return t}function nm(){return process.env.CLAUDECODE||process.env.CLAUDE_CODE_ENTRYPOINT?{name:"Claude Code",version:""}:process.env.CURSOR_EDITOR?{name:"Cursor",version:""}:process.env.WINDSURF_EDITOR?{name:"Windsurf",version:""}:process.env.CONTINUE_GLOBAL_DIR?{name:"Continue",version:""}:process.env.CLINE_CONTEXT?{name:"Cline",version:""}:null}Et();var Tte="https://www.google-analytics.com/mp/collect",Rte="G-87FMYC2KLT",jte="ANCUVw5rTZ6ZRMOBd_RLMw",Ate=6e4,Nte=20,LS=25,Ote=5e3,Dte="device-id.json",zte="https://api.ipify.org",Mte=3e3,rm=class{enabled;clientId;deviceKey;sessionId="";sessionStartTime=0;gaSessionId=0;lastEventTime=0;toolCallsTotal=0;toolCallsSuccess=0;toolCallsFailed=0;eventQueue=[];explorerPayloadQueue=[];flushTimer=null;aiClient="unknown";pluginVersion="";tier="basic";userPropertiesDirty=!0;pendingSessionStart=!1;sessionStartTimer=null;publicIp;constructor(e){this.enabled=this.resolveEnabled(e.enabled),this.clientId=this.loadOrCreateDeviceId(),this.deviceKey=this.createDeviceKey(this.clientId),this.enabled||y.debug("Telemetry disabled")}getAnalyticsDirPath(){return qt()}getDeviceIdPath(){return wz.join(this.getAnalyticsDirPath(),Dte)}loadOrCreateDeviceId(){let e=this.getDeviceIdPath();try{let r=Ko.readFileSync(e,"utf-8"),i=JSON.parse(r);if(typeof i.deviceId=="string"&&i.deviceId.length>0)return i.deviceId}catch{}let n=Ete();try{Ko.mkdirSync(this.getAnalyticsDirPath(),{recursive:!0}),Ko.writeFileSync(e,JSON.stringify({deviceId:n},null,2),"utf-8")}catch(r){y.warn("Failed to persist telemetry device ID",{error:r})}return n}createDeviceKey(e){return $te("sha256").update(e).digest("hex").slice(0,32)}resolveEnabled(e){let n=process.env.ENABLE_TELEMETRY;return n!==void 0&&n!==""?n.toLowerCase()==="true"||n==="1":e!==void 0?e:!We.startsWith("0.0.")}initialize(e){if(!this.enabled)return;let n=Date.now();this.sessionId=e,this.sessionStartTime=n,this.gaSessionId=Math.floor(n/1e3),this.lastEventTime=n;let r=nm();r&&(this.aiClient=r.name),this.fetchPublicIp(),this.flushTimer=setInterval(()=>this.flush(),Ate),this.flushTimer.unref(),y.debug("Telemetry initialized",{sessionId:e})}fetchPublicIp(){let e=new AbortController,n=setTimeout(()=>e.abort(),Mte);fetch(zte,{signal:e.signal}).then(r=>r.text()).then(r=>{clearTimeout(n);let i=r.trim();/^[\d.:a-fA-F]+$/.test(i)&&(this.publicIp=i,y.debug("Public IP resolved for geo tracking"))}).catch(()=>{clearTimeout(n)})}getActivityEventParams(e){let n=this.gaSessionId||Math.floor((this.sessionStartTime||e)/1e3),r=this.lastEventTime||this.sessionStartTime||e,i=Math.max(1,e-r);return this.gaSessionId=n,this.lastEventTime=e,{session_id:n,engagement_time_msec:i,timestamp_micros:e*1e3}}createSharedEventParams(){let e=Intl.DateTimeFormat().resolvedOptions();return{mcp_version:We,plugin_version:this.pluginVersion||"unknown",platform:process.platform,user_tier:this.tier,ai_client:this.aiClient,timezone:e.timeZone||"unknown",locale:e.locale||"unknown"}}createEventParams(e={}){let n=Date.now();return{device_key:this.deviceKey,...this.createSharedEventParams(),...this.getActivityEventParams(n),...e}}trackSessionStart(){if(this.enabled){if(this.aiClient!=="unknown"){this.emitSessionStart();return}this.pendingSessionStart=!0,this.sessionStartTimer=setTimeout(()=>{this.pendingSessionStart&&(this.pendingSessionStart=!1,this.emitSessionStart(),y.debug("session_start sent with unknown ai_client (timeout)"))},1e4),this.sessionStartTimer.unref()}}emitSessionStart(){this.enqueue({name:"mcp_session_start",params:this.createEventParams({server_session_id:this.sessionId})})}trackSessionEnd(){if(!this.enabled)return;let e=Math.round((Date.now()-this.sessionStartTime)/1e3);this.enqueue({name:"mcp_session_end",params:this.createEventParams({server_session_id:this.sessionId,duration_sec:e,tool_calls_total:this.toolCallsTotal,tool_calls_success:this.toolCallsSuccess,tool_calls_failed:this.toolCallsFailed})})}trackToolCall(e,n,r,i,a,o){if(!this.enabled)return;this.toolCallsTotal++,r?this.toolCallsSuccess++:this.toolCallsFailed++;let s=this.createEventParams({tool_name:e,required_tier:n,success:r?1:0,tool_result:r?"success":"error"});i&&(s.action=i),!r&&a&&(s.error_type=a),!r&&o&&(s.error_detail=o),this.enqueue({name:"mcp_tool_call",params:s})}trackToolError(e,n,r,i,a){if(!this.enabled)return;let o=this.createEventParams({tool_name:e,required_tier:n});r&&(o.action=r),i&&(o.error_type=i),a&&(o.error_detail=a),this.enqueue({name:"mcp_tool_error",params:o})}trackPluginConnected(){this.enabled&&this.enqueue({name:"mcp_plugin_connected",params:this.createEventParams({server_session_id:this.sessionId})})}setAiClient(e){this.aiClient=e,this.userPropertiesDirty=!0,this.pendingSessionStart&&(this.pendingSessionStart=!1,this.sessionStartTimer&&(clearTimeout(this.sessionStartTimer),this.sessionStartTimer=null),this.emitSessionStart())}setPluginVersion(e){this.pluginVersion=e,this.userPropertiesDirty=!0}setTier(e){this.tier=e,this.userPropertiesDirty=!0}isEnabled(){return this.enabled}trackDashboardEvents(e){if(this.enabled)for(let n of e)this.enqueue({name:n.name,params:this.createEventParams({event_source:"dashboard",...n.params})})}trackExplorerEvents(e,n){if(!this.enabled||n.length===0)return;let r=this.createSharedEventParams(),i=this.createDeviceKey(e),a=n.map(o=>({name:o.name,params:{...o.params,...r,device_key:i,event_source:"explorer"}}));this.enqueueExplorerPayload({client_id:e,events:a})}enqueue(e){this.eventQueue.push(e),this.flushIfThresholdReached()}enqueueExplorerPayload(e){for(let n=0;n<e.events.length;n+=LS)this.explorerPayloadQueue.push({client_id:e.client_id,events:e.events.slice(n,n+LS)});this.flushIfThresholdReached()}flushIfThresholdReached(){let e=this.explorerPayloadQueue.reduce((n,r)=>n+r.events.length,0);this.eventQueue.length+e>=Nte&&this.flush()}flush(){this.flushDefaultEvents(),this.flushExplorerPayloads()}flushDefaultEvents(){if(this.eventQueue.length!==0)for(;this.eventQueue.length>0;){let e=this.eventQueue.splice(0,LS),n={client_id:this.clientId,events:e,...this.publicIp&&{ip_override:this.publicIp}};this.userPropertiesDirty&&(n.user_properties={mcp_version:{value:We},plugin_version:{value:this.pluginVersion||"unknown"},device_key:{value:this.deviceKey},platform:{value:process.platform},user_tier:{value:this.tier},ai_client:{value:this.aiClient}},this.userPropertiesDirty=!1),this.sendToGA4(n)}}flushExplorerPayloads(){for(;this.explorerPayloadQueue.length>0;){let e=this.explorerPayloadQueue.shift();if(!e)break;this.publicIp&&(e.ip_override=this.publicIp),this.sendToGA4(e)}}sendToGA4(e){let n=`${Tte}?measurement_id=${Rte}&api_secret=${jte}`,r=new AbortController,i=setTimeout(()=>r.abort(),Ote);fetch(n,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e),signal:r.signal}).then(()=>{clearTimeout(i),y.debug("Telemetry events sent",{count:e.events.length})}).catch(()=>{clearTimeout(i)})}async shutdown(){this.enabled&&(this.pendingSessionStart&&(this.pendingSessionStart=!1,this.sessionStartTimer&&(clearTimeout(this.sessionStartTimer),this.sessionStartTimer=null),this.emitSessionStart()),this.flushTimer&&(clearInterval(this.flushTimer),this.flushTimer=null),(this.eventQueue.length>0||this.explorerPayloadQueue.length>0)&&this.flush(),await new Promise(e=>setTimeout(e,500)),y.debug("Telemetry shutdown complete"))}};var im=class extends Error{statusCode;constructor(e,n){super(e),this.name="LicenseApiError",this.statusCode=n}};function Lte(t){return new Promise(e=>setTimeout(e,t))}function Sz(t){let e={canUsePro:!1,status:"unlicensed",checkedAt:Math.floor(Date.now()/1e3),source:"cached"};if(!t||typeof t!="object")return e;let n=t,r=typeof n.status=="string"?n.status:"unlicensed",i=new Set(["active","unknown","grace","revoked","invalid","unlicensed"]),a=n.source==="live"||n.source==="cached"||n.source==="offline_snapshot"?n.source:"cached",o={canUsePro:n.canUsePro===!0,status:i.has(r)?r:"unlicensed",checkedAt:typeof n.checkedAt=="number"?n.checkedAt:e.checkedAt,source:a};if(typeof n.maskedKey=="string"&&(o.maskedKey=n.maskedKey),(n.billingState==="ok"||n.billingState==="cancelled"||n.billingState==="expired"||n.billingState==="payment_failed"||n.billingState==="refunded"||n.billingState==="chargeback"||n.billingState==="unknown")&&(o.billingState=n.billingState),typeof n.reason=="string"&&(o.reason=n.reason),(n.decisionSource==="provider_live"||n.decisionSource==="provider_webhook"||n.decisionSource==="license_server_cache")&&(o.decisionSource=n.decisionSource),n.abuse&&typeof n.abuse=="object"){let s=n.abuse;(s.blocked===!0||s.blocked===!1)&&(o.abuse={blocked:s.blocked===!0},typeof s.blockedUntil=="number"&&(o.abuse.blockedUntil=s.blockedUntil),typeof s.blockReason=="string"&&(o.abuse.blockReason=s.blockReason))}return typeof n.sessionToken=="string"&&n.sessionToken.length>0&&(o.sessionToken=n.sessionToken),typeof n.tokenStatus=="string"&&(o.tokenStatus=n.tokenStatus),typeof n.tokenReason=="string"&&(o.tokenReason=n.tokenReason),typeof n.statusDetail=="string"&&(o.statusDetail=n.statusDetail),typeof n.lastLicenseServerSuccessAt=="number"&&(o.lastLicenseServerSuccessAt=n.lastLicenseServerSuccessAt,o.lastSuccessfulCheckAt=n.lastLicenseServerSuccessAt),typeof n.graceUntil=="number"&&(o.graceUntil=n.graceUntil,o.fallbackUntil=n.graceUntil),typeof n.lastSuccessfulCheckAt=="number"&&(o.lastSuccessfulCheckAt=n.lastSuccessfulCheckAt),typeof n.nextRecheckAt=="number"&&(o.nextRecheckAt=n.nextRecheckAt),typeof n.fallbackUntil=="number"&&(o.fallbackUntil=n.fallbackUntil),typeof o.lastSuccessfulCheckAt=="number"&&o.lastLicenseServerSuccessAt===void 0&&(o.lastLicenseServerSuccessAt=o.lastSuccessfulCheckAt),typeof o.fallbackUntil=="number"&&o.graceUntil===void 0&&(o.graceUntil=o.fallbackUntil),o}var am=class{config;constructor(e){this.config=e}isConfigured(){return this.config.baseUrl.length>0&&this.config.projectId.length>0&&this.config.provider.length>0}supportsProvider(e){return e.trim().toLowerCase()===this.config.provider.trim().toLowerCase()}getConfiguredProvider(){return this.config.provider.trim().toLowerCase()}getV2ActionPath(e,n){let r=encodeURIComponent(this.config.projectId),i=encodeURIComponent(e);return`/v2/projects/${r}/providers/${i}/license/${n}`}async bearerRequest(e,n,r,i){let a=JSON.stringify(r),o=new URL(n,this.config.baseUrl),s={"content-type":"application/json"};i&&(s.authorization=`Bearer ${i}`);let c=0,l=null;for(;c<=this.config.retryCount;){let u=new AbortController,p=setTimeout(()=>u.abort(),this.config.timeoutMs);try{let d=await fetch(o.toString(),{method:e,headers:s,body:a,signal:u.signal});clearTimeout(p);let f=await d.text(),m=f.length>0?JSON.parse(f):null;if(!d.ok){let h=m&&typeof m=="object"&&typeof m.error=="string"?m.error:`License API error ${d.status}`;throw new im(h,d.status)}return m}catch(d){if(clearTimeout(p),d instanceof im&&d.statusCode<500)throw d;l=d instanceof Error?d:new Error("Unknown license API error"),c+=1,c<=this.config.retryCount&&await Lte(this.config.retryDelayMs)}}throw l??new Error("License API request failed")}async activate(e){let n=e.provider.trim().toLowerCase();if(!n)throw new Error("provider is required");let r={licenseKey:e.licenseKey,mcpInstanceId:e.mcpInstanceId},i=e.productRef??e.productPermalink;i&&(r.productRef=i),e.pluginClientId&&(r.pluginClientId=e.pluginClientId),e.deviceId&&(r.deviceId=e.deviceId);let a=await this.bearerRequest("POST",this.getV2ActionPath(n,"activate"),r),o=a?.state??a,s=Sz(o);return a?.sessionToken&&(s.sessionToken=a.sessionToken),s}async refresh(e){let n=e.provider.trim().toLowerCase();if(!n)throw new Error("provider is required");let r=e.sessionToken;if(!r)throw new Error("sessionToken is required for refresh");let i={};e.pluginClientId&&(i.pluginClientId=e.pluginClientId),e.mcpInstanceId&&(i.mcpInstanceId=e.mcpInstanceId),e.deviceId&&(i.deviceId=e.deviceId);let a=await this.bearerRequest("POST",this.getV2ActionPath(n,"refresh"),i,r),o=a?.state??a,s=Sz(o);return a?.sessionToken&&(s.sessionToken=a.sessionToken),s}};Et();import kz from"path";import{promises as US}from"fs";var Ute="license-state.json",qS=5;function Fte(){return kz.join(qt(),Ute)}var qte=new Set(["active","unknown","grace","revoked","invalid","unlicensed"]);function Bte(){return{canUsePro:!1,status:"unlicensed",checkedAt:Math.floor(Date.now()/1e3),source:"cached"}}function om(){return{version:qS,updatedAt:Date.now(),hasStoredState:!1,state:Bte()}}function Zte(){return Math.floor(Date.now()/1e3)}function FS(t){let e=typeof t?.status=="string"&&qte.has(t.status)?t.status:"unlicensed",n={canUsePro:t?.canUsePro===!0,status:e,checkedAt:typeof t?.checkedAt=="number"?t.checkedAt:Zte(),source:t?.source==="live"||t?.source==="cached"||t?.source==="offline_snapshot"?t.source:"cached"};return typeof t?.maskedKey=="string"&&(n.maskedKey=t.maskedKey),typeof t?.billingState=="string"&&(n.billingState=t.billingState),typeof t?.reason=="string"&&(n.reason=t.reason),(t?.decisionSource==="provider_live"||t?.decisionSource==="provider_webhook"||t?.decisionSource==="license_server_cache")&&(n.decisionSource=t.decisionSource),t?.abuse&&typeof t.abuse=="object"&&t.abuse.blocked!==void 0&&(n.abuse={blocked:t.abuse.blocked===!0},typeof t.abuse.blockedUntil=="number"&&(n.abuse.blockedUntil=t.abuse.blockedUntil),typeof t.abuse.blockReason=="string"&&(n.abuse.blockReason=t.abuse.blockReason)),typeof t?.tokenStatus=="string"&&(n.tokenStatus=t.tokenStatus),typeof t?.statusDetail=="string"&&(n.statusDetail=t.statusDetail),typeof t?.lastLicenseServerSuccessAt=="number"&&(n.lastLicenseServerSuccessAt=t.lastLicenseServerSuccessAt),typeof t?.graceUntil=="number"&&(n.graceUntil=t.graceUntil),typeof t?.lastSuccessfulCheckAt=="number"&&(n.lastSuccessfulCheckAt=t.lastSuccessfulCheckAt),typeof t?.nextRecheckAt=="number"&&(n.nextRecheckAt=t.nextRecheckAt),typeof t?.fallbackUntil=="number"&&(n.fallbackUntil=t.fallbackUntil),typeof n.lastLicenseServerSuccessAt=="number"&&n.lastSuccessfulCheckAt===void 0&&(n.lastSuccessfulCheckAt=n.lastLicenseServerSuccessAt),typeof n.lastSuccessfulCheckAt=="number"&&n.lastLicenseServerSuccessAt===void 0&&(n.lastLicenseServerSuccessAt=n.lastSuccessfulCheckAt),typeof n.graceUntil=="number"&&n.fallbackUntil===void 0&&(n.fallbackUntil=n.graceUntil),typeof n.fallbackUntil=="number"&&n.graceUntil===void 0&&(n.graceUntil=n.fallbackUntil),n}var sm=class{cacheFilePath;cache=om();storedState=!1;constructor(e){this.cacheFilePath=e??Fte()}getState(){return{...this.cache.state}}getProvider(){return this.cache.provider}getSessionToken(){return this.cache.sessionToken}hasStoredState(){return this.storedState}async initialize(){try{let e=await US.readFile(this.cacheFilePath,"utf8"),n=JSON.parse(e);if(!n||typeof n!="object"){this.cache=om();return}let r=n,i=r.state&&typeof r.state=="object"?r.state:void 0,a=FS(i),o={version:qS,updatedAt:typeof r.updatedAt=="number"?r.updatedAt:Date.now(),state:a};this.storedState=typeof r.hasStoredState=="boolean"?r.hasStoredState:!!(r.state&&typeof r.state=="object"),typeof r.provider=="string"&&r.provider.trim()!==""&&(o.provider=r.provider.trim().toLowerCase()),typeof r.sessionToken=="string"&&r.sessionToken.length>0&&(o.sessionToken=r.sessionToken),o.hasStoredState=this.storedState,this.cache=o}catch{this.cache=om(),this.storedState=!1}}async persistState(e,n,r){let i={version:qS,updatedAt:Date.now(),state:FS(e),hasStoredState:!0},a=n??this.cache.provider;typeof a=="string"&&a.trim()!==""&&(i.provider=a.trim().toLowerCase());let o=r??e.sessionToken??this.cache.sessionToken;typeof o=="string"&&o.length>0&&(i.sessionToken=o),this.storedState=!0,this.cache=i,await this.write()}async persistCredentials(e,n){let r={...this.cache,updatedAt:Date.now(),state:FS(this.cache.state),hasStoredState:this.storedState},i=e??this.cache.provider;typeof i=="string"&&i.trim()!==""?r.provider=i.trim().toLowerCase():delete r.provider,typeof n=="string"&&(n.length>0?r.sessionToken=n:delete r.sessionToken),this.cache=r,await this.write()}async clear(){this.cache=om(),this.storedState=!1,await this.write()}async write(){await US.mkdir(kz.dirname(this.cacheFilePath),{recursive:!0}),await US.writeFile(this.cacheFilePath,JSON.stringify(this.cache,null,2),{mode:384})}};pe();var Iz=10*60*60,Hte=14*60*60,Vte=2147483647;function nr(){return Math.floor(Date.now()/1e3)}function Wte(t){return{...t,checkedAt:t.checkedAt||nr(),source:t.source??"cached"}}var cm=class{client;cache;mcpInstanceId;fallbackMaxSeconds;random;refreshTimer=null;refreshInFlight=!1;constructor(e,n,r,i={}){this.client=e,this.cache=n,this.mcpInstanceId=r,this.fallbackMaxSeconds=Math.max(1,i.fallbackMaxHours??48)*60*60,this.random=i.random??Math.random}async initialize(){await this.cache.initialize(),this.scheduleFromCurrentState()}async shutdown(){this.clearScheduledRefresh()}isConfigured(){return this.client.isConfigured()}clearScheduledRefresh(){this.refreshTimer&&(clearTimeout(this.refreshTimer),this.refreshTimer=null)}computeNextRefreshAt(e=nr()){let n=Hte-Iz,r=Iz+Math.floor(this.random()*(n+1));return e+r}scheduleRefreshAt(e){this.clearScheduledRefresh();let n=nr(),r=Math.min(Vte,Math.max(0,(e-n)*1e3));this.refreshTimer=setTimeout(()=>{this.runScheduledRefresh()},r),this.refreshTimer.unref?.()}scheduleFromCurrentState(){if(!this.client.isConfigured()){this.clearScheduledRefresh();return}let e=!!this.cache.getSessionToken(),n=this.cache.getProvider();if(!e||!n){this.clearScheduledRefresh();return}let r=this.cache.getState(),i=typeof r.nextRecheckAt=="number"?r.nextRecheckAt:this.computeNextRefreshAt();this.scheduleRefreshAt(i)}getLastSuccessAt(e){return e.lastLicenseServerSuccessAt??e.lastSuccessfulCheckAt}getGraceUntil(e){return e.graceUntil??e.fallbackUntil}withCompatibilityFields(e){let n={...e},r=this.getLastSuccessAt(n);typeof r=="number"&&(n.lastLicenseServerSuccessAt=r,n.lastSuccessfulCheckAt=r);let i=this.getGraceUntil(n);return typeof i=="number"&&(n.graceUntil=i,n.fallbackUntil=i),n}async runScheduledRefresh(){if(!this.refreshInFlight){this.refreshInFlight=!0;try{await this.refresh({})}catch(e){y.warn("Scheduled license refresh failed",{error:e instanceof Error?e.message:"unknown_error"})}finally{this.refreshInFlight=!1,this.scheduleFromCurrentState()}}}withLocalTransitionDiagnostics(e,n,r){let{tokenStatus:i,tokenReason:a,statusDetail:o,...s}=e;return{...s,...n,statusDetail:r}}buildSuccessfulRemoteState(e,n,r){let i={...e,checkedAt:n,nextRecheckAt:r,source:e.source??"live"},a=this.getLastSuccessAt(e),o=this.getGraceUntil(e);return i.lastLicenseServerSuccessAt=typeof a=="number"?a:n,i.lastSuccessfulCheckAt=i.lastLicenseServerSuccessAt,i.graceUntil=typeof o=="number"?o:n+this.fallbackMaxSeconds,i.fallbackUntil=i.graceUntil,this.withCompatibilityFields(i)}evaluateEffectiveState(e){let n=Wte(e),r=nr();if(n.status==="revoked"||n.status==="invalid"||n.status==="unlicensed")return{...n,canUsePro:!1};if(n.status==="active"){if(n.canUsePro){let{reason:i,...a}=n;return a}return this.withLocalTransitionDiagnostics(n,{canUsePro:!1,status:"invalid",reason:n.reason??"invalid_active_state"},"invalid_local_evaluation")}if(n.status==="grace"){let i=this.getGraceUntil(n);return n.canUsePro&&(i===void 0||r<=i)?{...n,canUsePro:!0}:this.withLocalTransitionDiagnostics(n,{canUsePro:!1,status:"unlicensed",reason:"network_unavailable_fallback_window_exceeded"},"unlicensed_local_evaluation")}if(n.status==="unknown"){let i=this.getLastSuccessAt(n);return typeof i=="number"&&r<=i+this.fallbackMaxSeconds&&n.canUsePro?this.withLocalTransitionDiagnostics(n,{status:"grace",canUsePro:!0,graceUntil:i+this.fallbackMaxSeconds,fallbackUntil:i+this.fallbackMaxSeconds,reason:"network_unavailable_fallback_to_last_success"},"grace_local_evaluation"):this.withLocalTransitionDiagnostics(n,{canUsePro:!1,status:"unlicensed",reason:n.reason??"license_check_unavailable"},"unlicensed_local_evaluation")}return this.withLocalTransitionDiagnostics(n,{canUsePro:!1,status:"unlicensed",reason:n.reason??"license_unknown_state"},"unlicensed_local_evaluation")}buildPersistedState(e){let n=e.checkedAt||nr(),r=this.computeNextRefreshAt(n),i={...e,checkedAt:n,nextRecheckAt:r,source:e.source??"live"};return e.canUsePro&&(e.status==="active"||e.status==="grace")?this.buildSuccessfulRemoteState(e,n,r):this.withCompatibilityFields(i)}buildFallbackState(e){let n=nr(),r=this.computeNextRefreshAt(n);if(e.canUsePro&&typeof this.getLastSuccessAt(e)=="number"&&n<=this.getLastSuccessAt(e)+this.fallbackMaxSeconds){let i=this.getLastSuccessAt(e);return this.withLocalTransitionDiagnostics(e,{canUsePro:!0,status:"grace",checkedAt:n,source:"cached",lastLicenseServerSuccessAt:i,lastSuccessfulCheckAt:i,graceUntil:i+this.fallbackMaxSeconds,fallbackUntil:i+this.fallbackMaxSeconds,nextRecheckAt:r,reason:"network_unavailable_fallback_to_last_success"},"grace_network_fallback")}return this.withCompatibilityFields(this.withLocalTransitionDiagnostics(e,{canUsePro:!1,status:"unlicensed",checkedAt:n,source:"cached",nextRecheckAt:r,reason:"network_unavailable_fallback_window_exceeded"},"unlicensed_fallback_expired"))}getResponseProvider(e){return e?.trim().toLowerCase()||this.cache.getProvider()||this.client.getConfiguredProvider()}getRefreshBlockedReason(e){if(e&&!this.cache.getSessionToken())return"missing_session_token"}isRefreshRequired(e){return!!(e.status==="grace"&&e.decisionSource==="license_server_cache"||e.canUsePro&&typeof e.nextRecheckAt=="number"&&nr()>=e.nextRecheckAt)}toNormalizedState(e,n){let r=this.withCompatibilityFields(this.evaluateEffectiveState(e)),i=this.isRefreshRequired(r),a=this.getRefreshBlockedReason(i);return{...r,provider:this.getResponseProvider(n),source:r.source??"cached",refreshRequired:i,...a?{refreshBlockedReason:a}:{}}}toGatewayEnvelope(e,n){let i={...this.toNormalizedState(e,n.provider),ok:n.ok,recoverable:n.recoverable};return n.reasonCode&&(i.reasonCode=n.reasonCode),n.message&&(i.message=n.message),n.clientType!=="plugin"&&delete i.sessionToken,i}buildBootstrapState(e){return this.withCompatibilityFields({...e,nextRecheckAt:e.nextRecheckAt??this.computeNextRefreshAt(e.checkedAt),source:"offline_snapshot"})}supportsProvider(e){return this.client.supportsProvider(e)}getStatus(e){let n=this.cache.getState(),r=this.evaluateEffectiveState(n),i=!!this.cache.getSessionToken();return this.client.isConfigured()&&i&&this.cache.getProvider()&&typeof n.nextRecheckAt=="number"&&nr()>=n.nextRecheckAt&&!this.refreshInFlight&&this.runScheduledRefresh(),this.toNormalizedState(r,e)}async activate(e){if(!this.client.isConfigured())throw new Error("License API is not configured. Set LICENSE_API_BASE_URL, LICENSE_PROJECT_ID, LICENSE_PROVIDER.");let n={licenseKey:e.licenseKey,provider:e.provider.trim().toLowerCase(),mcpInstanceId:this.mcpInstanceId};if(!n.provider)throw new Error("provider is required");e.pluginClientId&&(n.pluginClientId=e.pluginClientId),e.deviceId&&(n.deviceId=e.deviceId);let r=await this.client.activate(n),i=this.buildPersistedState(r);await this.cache.persistState(i,n.provider,r.sessionToken??""),this.scheduleFromCurrentState();let a=this.withCompatibilityFields(this.evaluateEffectiveState(i));return r.sessionToken&&(a.sessionToken=r.sessionToken),a}async refresh(e){if(!this.client.isConfigured())return this.getStatus(e.provider);let n=(e.provider??this.cache.getProvider())?.trim().toLowerCase();if(!n){let a={canUsePro:!1,status:"unlicensed",checkedAt:nr(),reason:"license_provider_missing",source:"cached"};return await this.cache.persistState(a),this.clearScheduledRefresh(),a}let r=e.sessionToken??this.cache.getSessionToken();if(r)try{let a={sessionToken:r,provider:n,mcpInstanceId:this.mcpInstanceId};e.pluginClientId&&(a.pluginClientId=e.pluginClientId),e.deviceId&&(a.deviceId=e.deviceId);let o=await this.client.refresh(a),s=this.buildPersistedState(o);return await this.cache.persistState(s,n,o.sessionToken),this.scheduleFromCurrentState(),this.withCompatibilityFields(this.evaluateEffectiveState(s))}catch(a){y.warn("License refresh failed, falling back",{error:a instanceof Error?a.message:"unknown_error"});let o=this.buildFallbackState(this.cache.getState());return await this.cache.persistState(o,n),this.scheduleFromCurrentState(),this.withCompatibilityFields(this.evaluateEffectiveState(o))}let i=this.evaluateEffectiveState(this.cache.getState());return await this.cache.persistState(i,n),this.scheduleFromCurrentState(),this.withCompatibilityFields(i)}async evaluateProAccess(){let e=this.getStatus();if(e.canUsePro)return{allowed:!0,state:e};if(e.reason==="device_temporarily_blocked")return{allowed:!1,state:e,reason:"This device is temporarily blocked due to multi-device activity."};let n="License required";return e.status==="revoked"?n="License revoked. Pro tools are disabled.":e.status==="invalid"?n="License invalid. Please re-enter a valid license key.":(e.status==="unknown"||e.status==="grace")&&(n=e.reason??"License check is temporarily unavailable."),{allowed:!1,state:e,reason:n}}async bootstrap(e){let n=e.provider.trim().toLowerCase(),r=e.sessionToken?.trim()||void 0,i=this.cache.getState(),a=!1;if(e.snapshot){let o=this.buildBootstrapState(e.snapshot);(!this.cache.hasStoredState()||o.checkedAt>i.checkedAt)&&(await this.cache.persistState(o,n,r),a=!0)}return!a&&(r!==void 0||n!==this.cache.getProvider())&&await this.cache.persistCredentials(n,r),this.scheduleFromCurrentState(),this.getStatus(n)}async activateGateway(e){try{let n=await this.activate(e);return this.toGatewayEnvelope(n,{provider:e.provider,clientType:e.clientType,ok:!0,recoverable:!1})}catch(n){let r=n instanceof Error?n.message:"License activation failed";return this.toGatewayEnvelope(this.cache.getState(),{provider:e.provider,clientType:e.clientType,ok:!1,recoverable:!0,reasonCode:"license_activate_failed",message:r})}}async refreshGateway(e){let n=this.getResponseProvider(e.provider),r=e.sessionToken??this.cache.getSessionToken();if(!r)return this.toGatewayEnvelope(this.cache.getState(),{provider:n,clientType:e.clientType,ok:!1,recoverable:!0,reasonCode:"missing_session_token",message:"sessionToken is required for refresh"});try{let i={provider:n,sessionToken:r};e.pluginClientId&&(i.pluginClientId=e.pluginClientId),e.deviceId&&(i.deviceId=e.deviceId);let a=await this.refresh(i);return this.toGatewayEnvelope(a,{provider:n,clientType:e.clientType,ok:!0,recoverable:!1})}catch(i){let a=i instanceof Error?i.message:"License refresh failed";return this.toGatewayEnvelope(this.cache.getState(),{provider:n,clientType:e.clientType,ok:!1,recoverable:!0,reasonCode:"license_refresh_failed",message:a})}}async resetGateway(e){let n=this.getResponseProvider(e.provider),r={canUsePro:!1,status:"unlicensed",checkedAt:nr(),source:"offline_snapshot"};return await this.cache.persistState(r,n,""),this.clearScheduledRefresh(),this.toGatewayEnvelope(r,{provider:n,clientType:e.clientType,ok:!0,recoverable:!1})}async clearCredentials(){await this.cache.clear(),this.clearScheduledRefresh()}};pe();Et();import{mkdir as Gte,rename as Jte,writeFile as Kte}from"fs/promises";import ui from"path";import{randomUUID as Xte}from"crypto";var Yte="test-report.md",Qte="test-log.txt",ene="test-context.json",tne="roblox-project-sync";function Dl(t){return String(t).padStart(2,"0")}function nne(t){return[t.getFullYear(),Dl(t.getMonth()+1),Dl(t.getDate()),"-",Dl(t.getHours()),Dl(t.getMinutes()),Dl(t.getSeconds())].join("")}function ZS(t){return typeof t!="number"?"-":new Date(t).toISOString()}async function BS(t,e){let n=`${t}.${Xte()}.tmp`;await Kte(n,e,"utf8"),await Jte(n,t)}function rne(t){return typeof t.contextId=="string"||t.contextSummary!==void 0||t.replayMetadata!==void 0}function ine(t){return`${JSON.stringify({...typeof t.contextId=="string"?{contextId:t.contextId}:{},...t.contextSummary!==void 0?{contextSummary:t.contextSummary}:{},...t.replayMetadata!==void 0?{replayMetadata:t.replayMetadata}:{}},null,2)}
144
+ `)){let o=a.trim();if(o.length!==0)try{let s=this.normalizeSnapshot(JSON.parse(o));if(!s)continue;this.latestByContextId.set(s.contextId,s)}catch{continue}}}}};function bz(t){return new zS(t)}pe();import{createHash as $te,randomUUID as Ete}from"crypto";import*as Ko from"fs";import*as wz from"path";var MS={"claude-code":"Claude Code",claude:"Claude",cursor:"Cursor","codex-cli":"Codex CLI","openai-codex":"Codex CLI","gemini-cli":"Gemini CLI",windsurf:"Windsurf",cline:"Cline",continue:"Continue",zed:"Zed",vscode:"VS Code"};function _z(t){let e=t.toLowerCase();if(MS[e])return MS[e];for(let[n,r]of Object.entries(MS))if(e.includes(n))return r;return t}function nm(){return process.env.CLAUDECODE||process.env.CLAUDE_CODE_ENTRYPOINT?{name:"Claude Code",version:""}:process.env.CURSOR_EDITOR?{name:"Cursor",version:""}:process.env.WINDSURF_EDITOR?{name:"Windsurf",version:""}:process.env.CONTINUE_GLOBAL_DIR?{name:"Continue",version:""}:process.env.CLINE_CONTEXT?{name:"Cline",version:""}:null}Et();var Tte="https://www.google-analytics.com/mp/collect",Rte="G-87FMYC2KLT",jte="ANCUVw5rTZ6ZRMOBd_RLMw",Ate=6e4,Nte=20,LS=25,Ote=5e3,Dte="device-id.json",zte="https://api.ipify.org",Mte=3e3,rm=class{enabled;clientId;deviceKey;sessionId="";sessionStartTime=0;gaSessionId=0;lastEventTime=0;toolCallsTotal=0;toolCallsSuccess=0;toolCallsFailed=0;eventQueue=[];explorerPayloadQueue=[];flushTimer=null;aiClient="unknown";pluginVersion="";tier="basic";userPropertiesDirty=!0;pendingSessionStart=!1;sessionStartTimer=null;publicIp;constructor(e){this.enabled=this.resolveEnabled(e.enabled),this.clientId=this.loadOrCreateDeviceId(),this.deviceKey=this.createDeviceKey(this.clientId),this.enabled||y.debug("Telemetry disabled")}getAnalyticsDirPath(){return qt()}getDeviceIdPath(){return wz.join(this.getAnalyticsDirPath(),Dte)}loadOrCreateDeviceId(){let e=this.getDeviceIdPath();try{let r=Ko.readFileSync(e,"utf-8"),i=JSON.parse(r);if(typeof i.deviceId=="string"&&i.deviceId.length>0)return i.deviceId}catch{}let n=Ete();try{Ko.mkdirSync(this.getAnalyticsDirPath(),{recursive:!0}),Ko.writeFileSync(e,JSON.stringify({deviceId:n},null,2),"utf-8")}catch(r){y.warn("Failed to persist telemetry device ID",{error:r})}return n}createDeviceKey(e){return $te("sha256").update(e).digest("hex").slice(0,32)}resolveEnabled(e){let n=process.env.ENABLE_TELEMETRY;return n!==void 0&&n!==""?n.toLowerCase()==="true"||n==="1":e!==void 0?e:!We.startsWith("0.0.")}initialize(e){if(!this.enabled)return;let n=Date.now();this.sessionId=e,this.sessionStartTime=n,this.gaSessionId=Math.floor(n/1e3),this.lastEventTime=n;let r=nm();r&&(this.aiClient=r.name),this.fetchPublicIp(),this.flushTimer=setInterval(()=>this.flush(),Ate),this.flushTimer.unref(),y.debug("Telemetry initialized",{sessionId:e})}fetchPublicIp(){let e=new AbortController,n=setTimeout(()=>e.abort(),Mte);fetch(zte,{signal:e.signal}).then(r=>r.text()).then(r=>{clearTimeout(n);let i=r.trim();/^[\d.:a-fA-F]+$/.test(i)&&(this.publicIp=i,y.debug("Public IP resolved for geo tracking"))}).catch(()=>{clearTimeout(n)})}getActivityEventParams(e){let n=this.gaSessionId||Math.floor((this.sessionStartTime||e)/1e3),r=this.lastEventTime||this.sessionStartTime||e,i=Math.max(1,e-r);return this.gaSessionId=n,this.lastEventTime=e,{session_id:n,engagement_time_msec:i,timestamp_micros:e*1e3}}createSharedEventParams(){let e=Intl.DateTimeFormat().resolvedOptions();return{mcp_version:We,plugin_version:this.pluginVersion||"unknown",platform:process.platform,user_tier:this.tier,ai_client:this.aiClient,timezone:e.timeZone||"unknown",locale:e.locale||"unknown"}}createEventParams(e={}){let n=Date.now();return{device_key:this.deviceKey,event_source:"mcp",...this.createSharedEventParams(),...this.getActivityEventParams(n),...e}}trackSessionStart(){if(this.enabled){if(this.aiClient!=="unknown"){this.emitSessionStart();return}this.pendingSessionStart=!0,this.sessionStartTimer=setTimeout(()=>{this.pendingSessionStart&&(this.pendingSessionStart=!1,this.emitSessionStart(),y.debug("session_start sent with unknown ai_client (timeout)"))},1e4),this.sessionStartTimer.unref()}}emitSessionStart(){this.enqueue({name:"mcp_session_start",params:this.createEventParams({server_session_id:this.sessionId})})}trackSessionEnd(){if(!this.enabled)return;let e=Math.round((Date.now()-this.sessionStartTime)/1e3);this.enqueue({name:"mcp_session_end",params:this.createEventParams({server_session_id:this.sessionId,duration_sec:e,tool_calls_total:this.toolCallsTotal,tool_calls_success:this.toolCallsSuccess,tool_calls_failed:this.toolCallsFailed})})}trackToolCall(e,n,r,i,a,o){if(!this.enabled)return;this.toolCallsTotal++,r?this.toolCallsSuccess++:this.toolCallsFailed++;let s=this.createEventParams({tool_name:e,required_tier:n,success:r?1:0,tool_result:r?"success":"error"});i&&(s.action=i),!r&&a&&(s.error_type=a),!r&&o&&(s.error_detail=o),this.enqueue({name:"mcp_tool_call",params:s})}trackToolError(e,n,r,i,a){if(!this.enabled)return;let o=this.createEventParams({tool_name:e,required_tier:n});r&&(o.action=r),i&&(o.error_type=i),a&&(o.error_detail=a),this.enqueue({name:"mcp_tool_error",params:o})}trackPluginConnected(){this.enabled&&this.enqueue({name:"mcp_plugin_connected",params:this.createEventParams({server_session_id:this.sessionId})})}setAiClient(e){this.aiClient=e,this.userPropertiesDirty=!0,this.pendingSessionStart&&(this.pendingSessionStart=!1,this.sessionStartTimer&&(clearTimeout(this.sessionStartTimer),this.sessionStartTimer=null),this.emitSessionStart())}setPluginVersion(e){this.pluginVersion=e,this.userPropertiesDirty=!0}setTier(e){this.tier=e,this.userPropertiesDirty=!0}isEnabled(){return this.enabled}trackDashboardEvents(e){if(this.enabled)for(let n of e)this.enqueue({name:n.name,params:this.createEventParams({event_source:"dashboard",...n.params})})}trackExplorerEvents(e){if(!this.enabled||e.length===0)return;let n=this.createSharedEventParams(),r=e.map(i=>({name:i.name,params:{...i.params,...n,device_key:this.deviceKey,event_source:"explorer"}}));this.enqueueExplorerPayload({client_id:this.clientId,events:r})}enqueue(e){this.eventQueue.push(e),this.flushIfThresholdReached()}enqueueExplorerPayload(e){for(let n=0;n<e.events.length;n+=LS)this.explorerPayloadQueue.push({client_id:e.client_id,events:e.events.slice(n,n+LS)});this.flushIfThresholdReached()}flushIfThresholdReached(){let e=this.explorerPayloadQueue.reduce((n,r)=>n+r.events.length,0);this.eventQueue.length+e>=Nte&&this.flush()}flush(){this.flushDefaultEvents(),this.flushExplorerPayloads()}flushDefaultEvents(){if(this.eventQueue.length!==0)for(;this.eventQueue.length>0;){let e=this.eventQueue.splice(0,LS),n={client_id:this.clientId,events:e,...this.publicIp&&{ip_override:this.publicIp}};this.userPropertiesDirty&&(n.user_properties={mcp_version:{value:We},plugin_version:{value:this.pluginVersion||"unknown"},device_key:{value:this.deviceKey},platform:{value:process.platform},user_tier:{value:this.tier},ai_client:{value:this.aiClient}},this.userPropertiesDirty=!1),this.sendToGA4(n)}}flushExplorerPayloads(){for(;this.explorerPayloadQueue.length>0;){let e=this.explorerPayloadQueue.shift();if(!e)break;this.publicIp&&(e.ip_override=this.publicIp),this.sendToGA4(e)}}sendToGA4(e){let n=`${Tte}?measurement_id=${Rte}&api_secret=${jte}`,r=new AbortController,i=setTimeout(()=>r.abort(),Ote);fetch(n,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e),signal:r.signal}).then(()=>{clearTimeout(i),y.debug("Telemetry events sent",{count:e.events.length})}).catch(()=>{clearTimeout(i)})}async shutdown(){this.enabled&&(this.pendingSessionStart&&(this.pendingSessionStart=!1,this.sessionStartTimer&&(clearTimeout(this.sessionStartTimer),this.sessionStartTimer=null),this.emitSessionStart()),this.flushTimer&&(clearInterval(this.flushTimer),this.flushTimer=null),(this.eventQueue.length>0||this.explorerPayloadQueue.length>0)&&this.flush(),await new Promise(e=>setTimeout(e,500)),y.debug("Telemetry shutdown complete"))}};var im=class extends Error{statusCode;constructor(e,n){super(e),this.name="LicenseApiError",this.statusCode=n}};function Lte(t){return new Promise(e=>setTimeout(e,t))}function Sz(t){let e={canUsePro:!1,status:"unlicensed",checkedAt:Math.floor(Date.now()/1e3),source:"cached"};if(!t||typeof t!="object")return e;let n=t,r=typeof n.status=="string"?n.status:"unlicensed",i=new Set(["active","unknown","grace","revoked","invalid","unlicensed"]),a=n.source==="live"||n.source==="cached"||n.source==="offline_snapshot"?n.source:"cached",o={canUsePro:n.canUsePro===!0,status:i.has(r)?r:"unlicensed",checkedAt:typeof n.checkedAt=="number"?n.checkedAt:e.checkedAt,source:a};if(typeof n.maskedKey=="string"&&(o.maskedKey=n.maskedKey),(n.billingState==="ok"||n.billingState==="cancelled"||n.billingState==="expired"||n.billingState==="payment_failed"||n.billingState==="refunded"||n.billingState==="chargeback"||n.billingState==="unknown")&&(o.billingState=n.billingState),typeof n.reason=="string"&&(o.reason=n.reason),(n.decisionSource==="provider_live"||n.decisionSource==="provider_webhook"||n.decisionSource==="license_server_cache")&&(o.decisionSource=n.decisionSource),n.abuse&&typeof n.abuse=="object"){let s=n.abuse;(s.blocked===!0||s.blocked===!1)&&(o.abuse={blocked:s.blocked===!0},typeof s.blockedUntil=="number"&&(o.abuse.blockedUntil=s.blockedUntil),typeof s.blockReason=="string"&&(o.abuse.blockReason=s.blockReason))}return typeof n.sessionToken=="string"&&n.sessionToken.length>0&&(o.sessionToken=n.sessionToken),typeof n.tokenStatus=="string"&&(o.tokenStatus=n.tokenStatus),typeof n.tokenReason=="string"&&(o.tokenReason=n.tokenReason),typeof n.statusDetail=="string"&&(o.statusDetail=n.statusDetail),typeof n.lastLicenseServerSuccessAt=="number"&&(o.lastLicenseServerSuccessAt=n.lastLicenseServerSuccessAt,o.lastSuccessfulCheckAt=n.lastLicenseServerSuccessAt),typeof n.graceUntil=="number"&&(o.graceUntil=n.graceUntil,o.fallbackUntil=n.graceUntil),typeof n.lastSuccessfulCheckAt=="number"&&(o.lastSuccessfulCheckAt=n.lastSuccessfulCheckAt),typeof n.nextRecheckAt=="number"&&(o.nextRecheckAt=n.nextRecheckAt),typeof n.fallbackUntil=="number"&&(o.fallbackUntil=n.fallbackUntil),typeof o.lastSuccessfulCheckAt=="number"&&o.lastLicenseServerSuccessAt===void 0&&(o.lastLicenseServerSuccessAt=o.lastSuccessfulCheckAt),typeof o.fallbackUntil=="number"&&o.graceUntil===void 0&&(o.graceUntil=o.fallbackUntil),o}var am=class{config;constructor(e){this.config=e}isConfigured(){return this.config.baseUrl.length>0&&this.config.projectId.length>0&&this.config.provider.length>0}supportsProvider(e){return e.trim().toLowerCase()===this.config.provider.trim().toLowerCase()}getConfiguredProvider(){return this.config.provider.trim().toLowerCase()}getV2ActionPath(e,n){let r=encodeURIComponent(this.config.projectId),i=encodeURIComponent(e);return`/v2/projects/${r}/providers/${i}/license/${n}`}async bearerRequest(e,n,r,i){let a=JSON.stringify(r),o=new URL(n,this.config.baseUrl),s={"content-type":"application/json"};i&&(s.authorization=`Bearer ${i}`);let c=0,l=null;for(;c<=this.config.retryCount;){let u=new AbortController,p=setTimeout(()=>u.abort(),this.config.timeoutMs);try{let d=await fetch(o.toString(),{method:e,headers:s,body:a,signal:u.signal});clearTimeout(p);let f=await d.text(),m=f.length>0?JSON.parse(f):null;if(!d.ok){let h=m&&typeof m=="object"&&typeof m.error=="string"?m.error:`License API error ${d.status}`;throw new im(h,d.status)}return m}catch(d){if(clearTimeout(p),d instanceof im&&d.statusCode<500)throw d;l=d instanceof Error?d:new Error("Unknown license API error"),c+=1,c<=this.config.retryCount&&await Lte(this.config.retryDelayMs)}}throw l??new Error("License API request failed")}async activate(e){let n=e.provider.trim().toLowerCase();if(!n)throw new Error("provider is required");let r={licenseKey:e.licenseKey,mcpInstanceId:e.mcpInstanceId},i=e.productRef??e.productPermalink;i&&(r.productRef=i),e.pluginClientId&&(r.pluginClientId=e.pluginClientId),e.deviceId&&(r.deviceId=e.deviceId);let a=await this.bearerRequest("POST",this.getV2ActionPath(n,"activate"),r),o=a?.state??a,s=Sz(o);return a?.sessionToken&&(s.sessionToken=a.sessionToken),s}async refresh(e){let n=e.provider.trim().toLowerCase();if(!n)throw new Error("provider is required");let r=e.sessionToken;if(!r)throw new Error("sessionToken is required for refresh");let i={};e.pluginClientId&&(i.pluginClientId=e.pluginClientId),e.mcpInstanceId&&(i.mcpInstanceId=e.mcpInstanceId),e.deviceId&&(i.deviceId=e.deviceId);let a=await this.bearerRequest("POST",this.getV2ActionPath(n,"refresh"),i,r),o=a?.state??a,s=Sz(o);return a?.sessionToken&&(s.sessionToken=a.sessionToken),s}};Et();import kz from"path";import{promises as US}from"fs";var Ute="license-state.json",qS=5;function Fte(){return kz.join(qt(),Ute)}var qte=new Set(["active","unknown","grace","revoked","invalid","unlicensed"]);function Bte(){return{canUsePro:!1,status:"unlicensed",checkedAt:Math.floor(Date.now()/1e3),source:"cached"}}function om(){return{version:qS,updatedAt:Date.now(),hasStoredState:!1,state:Bte()}}function Zte(){return Math.floor(Date.now()/1e3)}function FS(t){let e=typeof t?.status=="string"&&qte.has(t.status)?t.status:"unlicensed",n={canUsePro:t?.canUsePro===!0,status:e,checkedAt:typeof t?.checkedAt=="number"?t.checkedAt:Zte(),source:t?.source==="live"||t?.source==="cached"||t?.source==="offline_snapshot"?t.source:"cached"};return typeof t?.maskedKey=="string"&&(n.maskedKey=t.maskedKey),typeof t?.billingState=="string"&&(n.billingState=t.billingState),typeof t?.reason=="string"&&(n.reason=t.reason),(t?.decisionSource==="provider_live"||t?.decisionSource==="provider_webhook"||t?.decisionSource==="license_server_cache")&&(n.decisionSource=t.decisionSource),t?.abuse&&typeof t.abuse=="object"&&t.abuse.blocked!==void 0&&(n.abuse={blocked:t.abuse.blocked===!0},typeof t.abuse.blockedUntil=="number"&&(n.abuse.blockedUntil=t.abuse.blockedUntil),typeof t.abuse.blockReason=="string"&&(n.abuse.blockReason=t.abuse.blockReason)),typeof t?.tokenStatus=="string"&&(n.tokenStatus=t.tokenStatus),typeof t?.statusDetail=="string"&&(n.statusDetail=t.statusDetail),typeof t?.lastLicenseServerSuccessAt=="number"&&(n.lastLicenseServerSuccessAt=t.lastLicenseServerSuccessAt),typeof t?.graceUntil=="number"&&(n.graceUntil=t.graceUntil),typeof t?.lastSuccessfulCheckAt=="number"&&(n.lastSuccessfulCheckAt=t.lastSuccessfulCheckAt),typeof t?.nextRecheckAt=="number"&&(n.nextRecheckAt=t.nextRecheckAt),typeof t?.fallbackUntil=="number"&&(n.fallbackUntil=t.fallbackUntil),typeof n.lastLicenseServerSuccessAt=="number"&&n.lastSuccessfulCheckAt===void 0&&(n.lastSuccessfulCheckAt=n.lastLicenseServerSuccessAt),typeof n.lastSuccessfulCheckAt=="number"&&n.lastLicenseServerSuccessAt===void 0&&(n.lastLicenseServerSuccessAt=n.lastSuccessfulCheckAt),typeof n.graceUntil=="number"&&n.fallbackUntil===void 0&&(n.fallbackUntil=n.graceUntil),typeof n.fallbackUntil=="number"&&n.graceUntil===void 0&&(n.graceUntil=n.fallbackUntil),n}var sm=class{cacheFilePath;cache=om();storedState=!1;constructor(e){this.cacheFilePath=e??Fte()}getState(){return{...this.cache.state}}getProvider(){return this.cache.provider}getSessionToken(){return this.cache.sessionToken}hasStoredState(){return this.storedState}async initialize(){try{let e=await US.readFile(this.cacheFilePath,"utf8"),n=JSON.parse(e);if(!n||typeof n!="object"){this.cache=om();return}let r=n,i=r.state&&typeof r.state=="object"?r.state:void 0,a=FS(i),o={version:qS,updatedAt:typeof r.updatedAt=="number"?r.updatedAt:Date.now(),state:a};this.storedState=typeof r.hasStoredState=="boolean"?r.hasStoredState:!!(r.state&&typeof r.state=="object"),typeof r.provider=="string"&&r.provider.trim()!==""&&(o.provider=r.provider.trim().toLowerCase()),typeof r.sessionToken=="string"&&r.sessionToken.length>0&&(o.sessionToken=r.sessionToken),o.hasStoredState=this.storedState,this.cache=o}catch{this.cache=om(),this.storedState=!1}}async persistState(e,n,r){let i={version:qS,updatedAt:Date.now(),state:FS(e),hasStoredState:!0},a=n??this.cache.provider;typeof a=="string"&&a.trim()!==""&&(i.provider=a.trim().toLowerCase());let o=r??e.sessionToken??this.cache.sessionToken;typeof o=="string"&&o.length>0&&(i.sessionToken=o),this.storedState=!0,this.cache=i,await this.write()}async persistCredentials(e,n){let r={...this.cache,updatedAt:Date.now(),state:FS(this.cache.state),hasStoredState:this.storedState},i=e??this.cache.provider;typeof i=="string"&&i.trim()!==""?r.provider=i.trim().toLowerCase():delete r.provider,typeof n=="string"&&(n.length>0?r.sessionToken=n:delete r.sessionToken),this.cache=r,await this.write()}async clear(){this.cache=om(),this.storedState=!1,await this.write()}async write(){await US.mkdir(kz.dirname(this.cacheFilePath),{recursive:!0}),await US.writeFile(this.cacheFilePath,JSON.stringify(this.cache,null,2),{mode:384})}};pe();var Iz=10*60*60,Hte=14*60*60,Vte=2147483647;function nr(){return Math.floor(Date.now()/1e3)}function Wte(t){return{...t,checkedAt:t.checkedAt||nr(),source:t.source??"cached"}}var cm=class{client;cache;mcpInstanceId;fallbackMaxSeconds;random;refreshTimer=null;refreshInFlight=!1;constructor(e,n,r,i={}){this.client=e,this.cache=n,this.mcpInstanceId=r,this.fallbackMaxSeconds=Math.max(1,i.fallbackMaxHours??48)*60*60,this.random=i.random??Math.random}async initialize(){await this.cache.initialize(),this.scheduleFromCurrentState()}async shutdown(){this.clearScheduledRefresh()}isConfigured(){return this.client.isConfigured()}clearScheduledRefresh(){this.refreshTimer&&(clearTimeout(this.refreshTimer),this.refreshTimer=null)}computeNextRefreshAt(e=nr()){let n=Hte-Iz,r=Iz+Math.floor(this.random()*(n+1));return e+r}scheduleRefreshAt(e){this.clearScheduledRefresh();let n=nr(),r=Math.min(Vte,Math.max(0,(e-n)*1e3));this.refreshTimer=setTimeout(()=>{this.runScheduledRefresh()},r),this.refreshTimer.unref?.()}scheduleFromCurrentState(){if(!this.client.isConfigured()){this.clearScheduledRefresh();return}let e=!!this.cache.getSessionToken(),n=this.cache.getProvider();if(!e||!n){this.clearScheduledRefresh();return}let r=this.cache.getState(),i=typeof r.nextRecheckAt=="number"?r.nextRecheckAt:this.computeNextRefreshAt();this.scheduleRefreshAt(i)}getLastSuccessAt(e){return e.lastLicenseServerSuccessAt??e.lastSuccessfulCheckAt}getGraceUntil(e){return e.graceUntil??e.fallbackUntil}withCompatibilityFields(e){let n={...e},r=this.getLastSuccessAt(n);typeof r=="number"&&(n.lastLicenseServerSuccessAt=r,n.lastSuccessfulCheckAt=r);let i=this.getGraceUntil(n);return typeof i=="number"&&(n.graceUntil=i,n.fallbackUntil=i),n}async runScheduledRefresh(){if(!this.refreshInFlight){this.refreshInFlight=!0;try{await this.refresh({})}catch(e){y.warn("Scheduled license refresh failed",{error:e instanceof Error?e.message:"unknown_error"})}finally{this.refreshInFlight=!1,this.scheduleFromCurrentState()}}}withLocalTransitionDiagnostics(e,n,r){let{tokenStatus:i,tokenReason:a,statusDetail:o,...s}=e;return{...s,...n,statusDetail:r}}buildSuccessfulRemoteState(e,n,r){let i={...e,checkedAt:n,nextRecheckAt:r,source:e.source??"live"},a=this.getLastSuccessAt(e),o=this.getGraceUntil(e);return i.lastLicenseServerSuccessAt=typeof a=="number"?a:n,i.lastSuccessfulCheckAt=i.lastLicenseServerSuccessAt,i.graceUntil=typeof o=="number"?o:n+this.fallbackMaxSeconds,i.fallbackUntil=i.graceUntil,this.withCompatibilityFields(i)}evaluateEffectiveState(e){let n=Wte(e),r=nr();if(n.status==="revoked"||n.status==="invalid"||n.status==="unlicensed")return{...n,canUsePro:!1};if(n.status==="active"){if(n.canUsePro){let{reason:i,...a}=n;return a}return this.withLocalTransitionDiagnostics(n,{canUsePro:!1,status:"invalid",reason:n.reason??"invalid_active_state"},"invalid_local_evaluation")}if(n.status==="grace"){let i=this.getGraceUntil(n);return n.canUsePro&&(i===void 0||r<=i)?{...n,canUsePro:!0}:this.withLocalTransitionDiagnostics(n,{canUsePro:!1,status:"unlicensed",reason:"network_unavailable_fallback_window_exceeded"},"unlicensed_local_evaluation")}if(n.status==="unknown"){let i=this.getLastSuccessAt(n);return typeof i=="number"&&r<=i+this.fallbackMaxSeconds&&n.canUsePro?this.withLocalTransitionDiagnostics(n,{status:"grace",canUsePro:!0,graceUntil:i+this.fallbackMaxSeconds,fallbackUntil:i+this.fallbackMaxSeconds,reason:"network_unavailable_fallback_to_last_success"},"grace_local_evaluation"):this.withLocalTransitionDiagnostics(n,{canUsePro:!1,status:"unlicensed",reason:n.reason??"license_check_unavailable"},"unlicensed_local_evaluation")}return this.withLocalTransitionDiagnostics(n,{canUsePro:!1,status:"unlicensed",reason:n.reason??"license_unknown_state"},"unlicensed_local_evaluation")}buildPersistedState(e){let n=e.checkedAt||nr(),r=this.computeNextRefreshAt(n),i={...e,checkedAt:n,nextRecheckAt:r,source:e.source??"live"};return e.canUsePro&&(e.status==="active"||e.status==="grace")?this.buildSuccessfulRemoteState(e,n,r):this.withCompatibilityFields(i)}buildFallbackState(e){let n=nr(),r=this.computeNextRefreshAt(n);if(e.canUsePro&&typeof this.getLastSuccessAt(e)=="number"&&n<=this.getLastSuccessAt(e)+this.fallbackMaxSeconds){let i=this.getLastSuccessAt(e);return this.withLocalTransitionDiagnostics(e,{canUsePro:!0,status:"grace",checkedAt:n,source:"cached",lastLicenseServerSuccessAt:i,lastSuccessfulCheckAt:i,graceUntil:i+this.fallbackMaxSeconds,fallbackUntil:i+this.fallbackMaxSeconds,nextRecheckAt:r,reason:"network_unavailable_fallback_to_last_success"},"grace_network_fallback")}return this.withCompatibilityFields(this.withLocalTransitionDiagnostics(e,{canUsePro:!1,status:"unlicensed",checkedAt:n,source:"cached",nextRecheckAt:r,reason:"network_unavailable_fallback_window_exceeded"},"unlicensed_fallback_expired"))}getResponseProvider(e){return e?.trim().toLowerCase()||this.cache.getProvider()||this.client.getConfiguredProvider()}getRefreshBlockedReason(e){if(e&&!this.cache.getSessionToken())return"missing_session_token"}isRefreshRequired(e){return!!(e.status==="grace"&&e.decisionSource==="license_server_cache"||e.canUsePro&&typeof e.nextRecheckAt=="number"&&nr()>=e.nextRecheckAt)}toNormalizedState(e,n){let r=this.withCompatibilityFields(this.evaluateEffectiveState(e)),i=this.isRefreshRequired(r),a=this.getRefreshBlockedReason(i);return{...r,provider:this.getResponseProvider(n),source:r.source??"cached",refreshRequired:i,...a?{refreshBlockedReason:a}:{}}}toGatewayEnvelope(e,n){let i={...this.toNormalizedState(e,n.provider),ok:n.ok,recoverable:n.recoverable};return n.reasonCode&&(i.reasonCode=n.reasonCode),n.message&&(i.message=n.message),n.clientType!=="plugin"&&delete i.sessionToken,i}buildBootstrapState(e){return this.withCompatibilityFields({...e,nextRecheckAt:e.nextRecheckAt??this.computeNextRefreshAt(e.checkedAt),source:"offline_snapshot"})}supportsProvider(e){return this.client.supportsProvider(e)}getStatus(e){let n=this.cache.getState(),r=this.evaluateEffectiveState(n),i=!!this.cache.getSessionToken();return this.client.isConfigured()&&i&&this.cache.getProvider()&&typeof n.nextRecheckAt=="number"&&nr()>=n.nextRecheckAt&&!this.refreshInFlight&&this.runScheduledRefresh(),this.toNormalizedState(r,e)}async activate(e){if(!this.client.isConfigured())throw new Error("License API is not configured. Set LICENSE_API_BASE_URL, LICENSE_PROJECT_ID, LICENSE_PROVIDER.");let n={licenseKey:e.licenseKey,provider:e.provider.trim().toLowerCase(),mcpInstanceId:this.mcpInstanceId};if(!n.provider)throw new Error("provider is required");e.pluginClientId&&(n.pluginClientId=e.pluginClientId),e.deviceId&&(n.deviceId=e.deviceId);let r=await this.client.activate(n),i=this.buildPersistedState(r);await this.cache.persistState(i,n.provider,r.sessionToken??""),this.scheduleFromCurrentState();let a=this.withCompatibilityFields(this.evaluateEffectiveState(i));return r.sessionToken&&(a.sessionToken=r.sessionToken),a}async refresh(e){if(!this.client.isConfigured())return this.getStatus(e.provider);let n=(e.provider??this.cache.getProvider())?.trim().toLowerCase();if(!n){let a={canUsePro:!1,status:"unlicensed",checkedAt:nr(),reason:"license_provider_missing",source:"cached"};return await this.cache.persistState(a),this.clearScheduledRefresh(),a}let r=e.sessionToken??this.cache.getSessionToken();if(r)try{let a={sessionToken:r,provider:n,mcpInstanceId:this.mcpInstanceId};e.pluginClientId&&(a.pluginClientId=e.pluginClientId),e.deviceId&&(a.deviceId=e.deviceId);let o=await this.client.refresh(a),s=this.buildPersistedState(o);return await this.cache.persistState(s,n,o.sessionToken),this.scheduleFromCurrentState(),this.withCompatibilityFields(this.evaluateEffectiveState(s))}catch(a){y.warn("License refresh failed, falling back",{error:a instanceof Error?a.message:"unknown_error"});let o=this.buildFallbackState(this.cache.getState());return await this.cache.persistState(o,n),this.scheduleFromCurrentState(),this.withCompatibilityFields(this.evaluateEffectiveState(o))}let i=this.evaluateEffectiveState(this.cache.getState());return await this.cache.persistState(i,n),this.scheduleFromCurrentState(),this.withCompatibilityFields(i)}async evaluateProAccess(){let e=this.getStatus();if(e.canUsePro)return{allowed:!0,state:e};if(e.reason==="device_temporarily_blocked")return{allowed:!1,state:e,reason:"This device is temporarily blocked due to multi-device activity."};let n="License required";return e.status==="revoked"?n="License revoked. Pro tools are disabled.":e.status==="invalid"?n="License invalid. Please re-enter a valid license key.":(e.status==="unknown"||e.status==="grace")&&(n=e.reason??"License check is temporarily unavailable."),{allowed:!1,state:e,reason:n}}async bootstrap(e){let n=e.provider.trim().toLowerCase(),r=e.sessionToken?.trim()||void 0,i=this.cache.getState(),a=!1;if(e.snapshot){let o=this.buildBootstrapState(e.snapshot);(!this.cache.hasStoredState()||o.checkedAt>i.checkedAt)&&(await this.cache.persistState(o,n,r),a=!0)}return!a&&(r!==void 0||n!==this.cache.getProvider())&&await this.cache.persistCredentials(n,r),this.scheduleFromCurrentState(),this.getStatus(n)}async activateGateway(e){try{let n=await this.activate(e);return this.toGatewayEnvelope(n,{provider:e.provider,clientType:e.clientType,ok:!0,recoverable:!1})}catch(n){let r=n instanceof Error?n.message:"License activation failed";return this.toGatewayEnvelope(this.cache.getState(),{provider:e.provider,clientType:e.clientType,ok:!1,recoverable:!0,reasonCode:"license_activate_failed",message:r})}}async refreshGateway(e){let n=this.getResponseProvider(e.provider),r=e.sessionToken??this.cache.getSessionToken();if(!r)return this.toGatewayEnvelope(this.cache.getState(),{provider:n,clientType:e.clientType,ok:!1,recoverable:!0,reasonCode:"missing_session_token",message:"sessionToken is required for refresh"});try{let i={provider:n,sessionToken:r};e.pluginClientId&&(i.pluginClientId=e.pluginClientId),e.deviceId&&(i.deviceId=e.deviceId);let a=await this.refresh(i);return this.toGatewayEnvelope(a,{provider:n,clientType:e.clientType,ok:!0,recoverable:!1})}catch(i){let a=i instanceof Error?i.message:"License refresh failed";return this.toGatewayEnvelope(this.cache.getState(),{provider:n,clientType:e.clientType,ok:!1,recoverable:!0,reasonCode:"license_refresh_failed",message:a})}}async resetGateway(e){let n=this.getResponseProvider(e.provider),r={canUsePro:!1,status:"unlicensed",checkedAt:nr(),source:"offline_snapshot"};return await this.cache.persistState(r,n,""),this.clearScheduledRefresh(),this.toGatewayEnvelope(r,{provider:n,clientType:e.clientType,ok:!0,recoverable:!1})}async clearCredentials(){await this.cache.clear(),this.clearScheduledRefresh()}};pe();Et();import{mkdir as Gte,rename as Jte,writeFile as Kte}from"fs/promises";import ui from"path";import{randomUUID as Xte}from"crypto";var Yte="test-report.md",Qte="test-log.txt",ene="test-context.json",tne="roblox-project-sync";function Dl(t){return String(t).padStart(2,"0")}function nne(t){return[t.getFullYear(),Dl(t.getMonth()+1),Dl(t.getDate()),"-",Dl(t.getHours()),Dl(t.getMinutes()),Dl(t.getSeconds())].join("")}function ZS(t){return typeof t!="number"?"-":new Date(t).toISOString()}async function BS(t,e){let n=`${t}.${Xte()}.tmp`;await Kte(n,e,"utf8"),await Jte(n,t)}function rne(t){return typeof t.contextId=="string"||t.contextSummary!==void 0||t.replayMetadata!==void 0}function ine(t){return`${JSON.stringify({...typeof t.contextId=="string"?{contextId:t.contextId}:{},...t.contextSummary!==void 0?{contextSummary:t.contextSummary}:{},...t.replayMetadata!==void 0?{replayMetadata:t.replayMetadata}:{}},null,2)}
145
145
  `}function ane(t){let e=t.timedOut?"timed_out":t.passed?"passed":"failed",n=t.signals.find(a=>a.kind==="START"),r=t.signals.find(a=>a.kind==="FINISHED"),i=["# Test Report","",`- Status: ${e}`,`- Test Name: ${t.testName}`,`- Mode: ${t.mode}`,`- Place ID: ${t.placeId}`,`- Place Name: ${t.placeName||"(unknown)"}`,`- Duration (ms): ${t.durationMs}`,`- Total Logs: ${t.logs.length}`,`- Signal Count: ${t.signals.length}`,"","## Signals","",`- START: ${n?ZS(n.timestampMs):"missing"}`,`- FINISHED: ${r?ZS(r.timestampMs):"missing"}`];return t.errorMessage&&i.push("","## Error","","```text",t.errorMessage,"```"),`${i.join(`
146
146
  `)}
147
147
  `}function one(t){return`${t.map(e=>{let n=ZS(e.timestampMs),r=typeof e.seq=="number"?`seq=${e.seq}`:"seq=-",i=e.type||"info";return`[${n}] [${r}] [${i}] ${e.message}`}).join(`