@alpsckr/unitycli 0.4.3 → 0.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (425) hide show
  1. package/README.md +5 -5
  2. package/contracts/command-contract.json +109 -217
  3. package/contracts/package-files.json +374 -294
  4. package/contracts/stable-session/direct-call-semantics.json +8 -2
  5. package/contracts/tool-completion-contract.json +148 -672
  6. package/dist/bridge/admission.d.ts +17 -1
  7. package/dist/bridge/admission.js +2 -0
  8. package/dist/bridge/admission.js.map +1 -1
  9. package/dist/bridge/domains/asset.js +3 -1
  10. package/dist/bridge/domains/asset.js.map +1 -1
  11. package/dist/bridge/domains/camera.js +39 -7
  12. package/dist/bridge/domains/camera.js.map +1 -1
  13. package/dist/bridge/domains/console.js +2 -1
  14. package/dist/bridge/domains/console.js.map +1 -1
  15. package/dist/bridge/domains/doctor.d.ts +3 -0
  16. package/dist/bridge/domains/doctor.js +8 -0
  17. package/dist/bridge/domains/doctor.js.map +1 -0
  18. package/dist/bridge/domains/editor.js +0 -4
  19. package/dist/bridge/domains/editor.js.map +1 -1
  20. package/dist/bridge/domains/game-object.js +4 -12
  21. package/dist/bridge/domains/game-object.js.map +1 -1
  22. package/dist/bridge/domains/graphics.js +1 -16
  23. package/dist/bridge/domains/graphics.js.map +1 -1
  24. package/dist/bridge/domains/object.js +3 -0
  25. package/dist/bridge/domains/object.js.map +1 -1
  26. package/dist/bridge/domains/physics.js +1 -2
  27. package/dist/bridge/domains/physics.js.map +1 -1
  28. package/dist/bridge/domains/profiler.js +18 -7
  29. package/dist/bridge/domains/profiler.js.map +1 -1
  30. package/dist/bridge/domains/project-tool.js +4 -0
  31. package/dist/bridge/domains/project-tool.js.map +1 -1
  32. package/dist/bridge/domains/reflection.js +10 -2
  33. package/dist/bridge/domains/reflection.js.map +1 -1
  34. package/dist/bridge/domains/runtime-tool.js +3 -0
  35. package/dist/bridge/domains/runtime-tool.js.map +1 -1
  36. package/dist/bridge/domains/scene.js +3 -8
  37. package/dist/bridge/domains/scene.js.map +1 -1
  38. package/dist/bridge/domains/screenshot.js +30 -6
  39. package/dist/bridge/domains/screenshot.js.map +1 -1
  40. package/dist/bridge/domains/ui.js +3 -1
  41. package/dist/bridge/domains/ui.js.map +1 -1
  42. package/dist/bridge/index.d.ts +5 -5
  43. package/dist/bridge/index.js +6 -9
  44. package/dist/bridge/index.js.map +1 -1
  45. package/dist/bridge/registry.js +3 -5
  46. package/dist/bridge/registry.js.map +1 -1
  47. package/dist/bridge/session-supervisor.d.ts +6 -3
  48. package/dist/bridge/session-supervisor.js +145 -39
  49. package/dist/bridge/session-supervisor.js.map +1 -1
  50. package/dist/bridge/start-diagnostics.d.ts +19 -0
  51. package/dist/bridge/start-diagnostics.js +96 -0
  52. package/dist/bridge/start-diagnostics.js.map +1 -0
  53. package/dist/bridge/types.d.ts +6 -5
  54. package/dist/bridge/types.js +16 -2
  55. package/dist/bridge/types.js.map +1 -1
  56. package/dist/catalog-protocol.d.ts +64 -0
  57. package/dist/catalog-protocol.js +460 -0
  58. package/dist/catalog-protocol.js.map +1 -0
  59. package/dist/cli-commands/command.js +84 -61
  60. package/dist/cli-commands/command.js.map +1 -1
  61. package/dist/cli-commands/extensions.js +70 -50
  62. package/dist/cli-commands/extensions.js.map +1 -1
  63. package/dist/cli-commands/instances.js +1 -1
  64. package/dist/cli-commands/instances.js.map +1 -1
  65. package/dist/cli-commands/package.js +10 -2
  66. package/dist/cli-commands/package.js.map +1 -1
  67. package/dist/cli-process-owners.d.ts +6 -0
  68. package/dist/cli-process-owners.js +27 -0
  69. package/dist/cli-process-owners.js.map +1 -0
  70. package/dist/cli.d.ts +1 -1
  71. package/dist/cli.js +20 -5
  72. package/dist/cli.js.map +1 -1
  73. package/dist/contracts/command-contract.js +14 -4
  74. package/dist/contracts/command-contract.js.map +1 -1
  75. package/dist/doctor.d.ts +4 -6
  76. package/dist/doctor.js +451 -398
  77. package/dist/doctor.js.map +1 -1
  78. package/dist/editor-blocker-observer.d.ts +2 -1
  79. package/dist/editor-blocker-observer.js +40 -13
  80. package/dist/editor-blocker-observer.js.map +1 -1
  81. package/dist/extension-availability.d.ts +27 -9
  82. package/dist/extension-availability.js +96 -56
  83. package/dist/extension-availability.js.map +1 -1
  84. package/dist/extensions/descriptor-loader.d.ts +2 -2
  85. package/dist/extensions/descriptor-loader.js +3 -4
  86. package/dist/extensions/descriptor-loader.js.map +1 -1
  87. package/dist/extensions/index.d.ts +1 -1
  88. package/dist/extensions/index.js +1 -1
  89. package/dist/extensions/index.js.map +1 -1
  90. package/dist/extensions/installer.d.ts +57 -5
  91. package/dist/extensions/installer.js +263 -64
  92. package/dist/extensions/installer.js.map +1 -1
  93. package/dist/extensions/registry.js +3 -5
  94. package/dist/extensions/registry.js.map +1 -1
  95. package/dist/extensions/state-contract.d.ts +11 -0
  96. package/dist/extensions/state-contract.js +50 -0
  97. package/dist/extensions/state-contract.js.map +1 -0
  98. package/dist/instances.d.ts +19 -1
  99. package/dist/instances.js +39 -0
  100. package/dist/instances.js.map +1 -1
  101. package/dist/package-manager.d.ts +0 -24
  102. package/dist/package-manager.js +5 -58
  103. package/dist/package-manager.js.map +1 -1
  104. package/dist/project-tool-discovery.d.ts +5 -2
  105. package/dist/project-tool-discovery.js +22 -18
  106. package/dist/project-tool-discovery.js.map +1 -1
  107. package/dist/runtime-code.js +1 -1
  108. package/dist/runtime-code.js.map +1 -1
  109. package/dist/tool-name.d.ts +4 -0
  110. package/dist/tool-name.js +15 -0
  111. package/dist/tool-name.js.map +1 -0
  112. package/dist/tools/.generated-completion-registry.d.ts +2 -0
  113. package/dist/tools/.generated-completion-registry.js +763 -325
  114. package/dist/tools/.generated-completion-registry.js.map +1 -1
  115. package/dist/tools/.generated-registry.d.ts +6 -11
  116. package/dist/tools/.generated-registry.js +6 -11
  117. package/dist/tools/.generated-registry.js.map +1 -1
  118. package/dist/tools/.generated-schemas.js +11 -16
  119. package/dist/tools/.generated-schemas.js.map +1 -1
  120. package/dist/tools/asset/refresh-status.d.ts +3 -0
  121. package/dist/tools/asset/refresh-status.js +12 -0
  122. package/dist/tools/asset/refresh-status.js.map +1 -0
  123. package/dist/tools/asset/refresh.js +7 -3
  124. package/dist/tools/asset/refresh.js.map +1 -1
  125. package/dist/tools/build/player-status.d.ts +3 -0
  126. package/dist/tools/build/player-status.js +12 -0
  127. package/dist/tools/build/player-status.js.map +1 -0
  128. package/dist/tools/build/run.js +1 -1
  129. package/dist/tools/build/run.js.map +1 -1
  130. package/dist/tools/catalog-control.d.ts +22 -0
  131. package/dist/tools/catalog-control.js +335 -0
  132. package/dist/tools/catalog-control.js.map +1 -0
  133. package/dist/tools/catalog.d.ts +7 -9
  134. package/dist/tools/catalog.js +66 -45
  135. package/dist/tools/catalog.js.map +1 -1
  136. package/dist/tools/commands.d.ts +1 -1
  137. package/dist/tools/commands.js +44 -11
  138. package/dist/tools/commands.js.map +1 -1
  139. package/dist/tools/define.js +3 -2
  140. package/dist/tools/define.js.map +1 -1
  141. package/dist/tools/dispatcher.d.ts +53 -6
  142. package/dist/tools/dispatcher.js +277 -108
  143. package/dist/tools/dispatcher.js.map +1 -1
  144. package/dist/tools/editor/compile.js +1 -1
  145. package/dist/tools/editor/compile.js.map +1 -1
  146. package/dist/tools/editor/play-mode-status.d.ts +3 -0
  147. package/dist/tools/editor/play-mode-status.js +12 -0
  148. package/dist/tools/editor/play-mode-status.js.map +1 -0
  149. package/dist/tools/editor/readiness.d.ts +0 -1
  150. package/dist/tools/editor/readiness.js +0 -1
  151. package/dist/tools/editor/readiness.js.map +1 -1
  152. package/dist/tools/editor/refresh.d.ts +7 -3
  153. package/dist/tools/editor/refresh.js +20 -15
  154. package/dist/tools/editor/refresh.js.map +1 -1
  155. package/dist/tools/editor/state.d.ts +2 -2
  156. package/dist/tools/editor/state.js +13 -12
  157. package/dist/tools/editor/state.js.map +1 -1
  158. package/dist/tools/metadata/derived.js +1 -7
  159. package/dist/tools/metadata/derived.js.map +1 -1
  160. package/dist/tools/metadata/status.js +1 -12
  161. package/dist/tools/metadata/status.js.map +1 -1
  162. package/dist/tools/metadata/types.d.ts +0 -2
  163. package/dist/tools/navmesh/live.js +1 -1
  164. package/dist/tools/navmesh/live.js.map +1 -1
  165. package/dist/tools/occlusion/live.js +1 -1
  166. package/dist/tools/occlusion/live.js.map +1 -1
  167. package/dist/tools/package/deploy.js +2 -2
  168. package/dist/tools/package/deploy.js.map +1 -1
  169. package/dist/tools/package/embed.js +1 -1
  170. package/dist/tools/package/embed.js.map +1 -1
  171. package/dist/tools/package/install-status.d.ts +3 -0
  172. package/dist/tools/package/install-status.js +12 -0
  173. package/dist/tools/package/install-status.js.map +1 -0
  174. package/dist/tools/package/install.js +3 -3
  175. package/dist/tools/package/install.js.map +1 -1
  176. package/dist/tools/package/live-resolve.js +45 -12
  177. package/dist/tools/package/live-resolve.js.map +1 -1
  178. package/dist/tools/package/registry-clear.js.map +1 -1
  179. package/dist/tools/package/registry-set.js.map +1 -1
  180. package/dist/tools/package/remove-status.d.ts +3 -0
  181. package/dist/tools/package/remove-status.js +12 -0
  182. package/dist/tools/package/remove-status.js.map +1 -0
  183. package/dist/tools/package/remove.js +3 -3
  184. package/dist/tools/package/remove.js.map +1 -1
  185. package/dist/tools/package/resolve.js +3 -3
  186. package/dist/tools/package/resolve.js.map +1 -1
  187. package/dist/tools/package/restore.js +2 -2
  188. package/dist/tools/package/restore.js.map +1 -1
  189. package/dist/tools/package/scoped-registry-add.js.map +1 -1
  190. package/dist/tools/status/support.d.ts +13 -0
  191. package/dist/tools/status/support.js +28 -0
  192. package/dist/tools/status/support.js.map +1 -0
  193. package/dist/tools/test/cancel.js +2 -2
  194. package/dist/tools/test/cancel.js.map +1 -1
  195. package/dist/tools/test/run-status.d.ts +3 -0
  196. package/dist/tools/test/run-status.js +12 -0
  197. package/dist/tools/test/run-status.js.map +1 -0
  198. package/dist/tools/test/run.js +7 -4
  199. package/dist/tools/test/run.js.map +1 -1
  200. package/dist/tools/tools/group-summaries.js +10 -14
  201. package/dist/tools/tools/group-summaries.js.map +1 -1
  202. package/docs/architecture.md +4 -4
  203. package/docs/availability-differences.md +4 -4
  204. package/docs/command-execution.md +4 -3
  205. package/docs/error-codes.md +13 -5
  206. package/docs/extensions.md +1 -7
  207. package/docs/risk-and-side-effects.md +4 -3
  208. package/docs/safety-boundaries.md +3 -1
  209. package/docs/target-and-instance.md +1 -1
  210. package/docs/tool-discovery.md +17 -10
  211. package/docs/tool-execution.md +18 -7
  212. package/extensions/cinemachine/unitypkg/Editor/UnityCliExtensionAssembly.cs +3 -0
  213. package/extensions/hdrp/unitypkg/Editor/UnityCliExtensionAssembly.cs +3 -0
  214. package/extensions/probuilder/unitypkg/Editor/UnityCliExtensionAssembly.cs +3 -0
  215. package/extensions/urp/unitypkg/Editor/UnityCliExtensionAssembly.cs +3 -0
  216. package/extensions/vfx-graph/unitypkg/Editor/UnityCliExtensionAssembly.cs +3 -0
  217. package/package.json +2 -4
  218. package/skills/unitycli/SKILL.md +11 -2
  219. package/skills/unitycli/manifest.json +1 -1
  220. package/skills/unitycli/references/operation-protocol.md +3 -3
  221. package/unitypkg/Editor/AssemblyInfo.cs +5 -0
  222. package/unitypkg/Editor/Bridge/BridgeCommandRegistry.cs +49 -14
  223. package/unitypkg/Editor/Bridge/BridgeHttpServer.cs +9 -35
  224. package/unitypkg/Editor/Bridge/BridgeQueueState.cs +672 -0
  225. package/unitypkg/Editor/Bridge/BridgeWorkQueue.cs +970 -120
  226. package/unitypkg/Editor/BridgeStartupStateMachine.cs +59 -1
  227. package/unitypkg/Editor/Commands/ProjectToolCommands.cs +4 -0
  228. package/unitypkg/Editor/Commands/RuntimeToolCommands.cs +3 -0
  229. package/unitypkg/Editor/Generated/UnityCliToolCompletionRegistry.g.cs +379 -14
  230. package/unitypkg/Editor/Infrastructure/CodeExecSupport.cs +28 -13
  231. package/unitypkg/Editor/Infrastructure/RegistryOwnershipResolver.cs +132 -105
  232. package/unitypkg/Editor/Infrastructure/UnityCliActiveOperation.cs +138 -29
  233. package/unitypkg/Editor/Infrastructure/UnityCliAdmission.cs +1856 -237
  234. package/unitypkg/Editor/Infrastructure/UnityCliDoctorDiagnostics.cs +220 -0
  235. package/unitypkg/Editor/Infrastructure/UnityCliDurableStatusCoordinator.cs +118 -0
  236. package/unitypkg/Editor/Infrastructure/UnityCliEditorStateSnapshot.cs +2 -1
  237. package/unitypkg/Editor/Infrastructure/UnityCliHierarchyPager.cs +203 -0
  238. package/unitypkg/Editor/Infrastructure/UnityCliInstanceCleanup.cs +2 -1
  239. package/unitypkg/Editor/Infrastructure/UnityCliInstanceRegistry.cs +201 -63
  240. package/unitypkg/Editor/Infrastructure/UnityCliJson.cs +2 -3
  241. package/unitypkg/Editor/Infrastructure/UnityCliLiveBuildCoordinator.cs +289 -46
  242. package/unitypkg/Editor/Infrastructure/UnityCliLiveRefreshCoordinator.cs +87 -53
  243. package/unitypkg/Editor/Infrastructure/UnityCliNavMeshBakeCoordinator.cs +4 -4
  244. package/unitypkg/Editor/Infrastructure/UnityCliObjectRef.cs +128 -25
  245. package/unitypkg/Editor/Infrastructure/UnityCliObjectSerializer.cs +435 -327
  246. package/unitypkg/Editor/Infrastructure/UnityCliOcclusionBakeCoordinator.cs +4 -4
  247. package/unitypkg/Editor/Infrastructure/UnityCliOperationCoordinator.cs +351 -53
  248. package/unitypkg/Editor/Infrastructure/UnityCliPackageResolveCoordinator.cs +118 -18
  249. package/unitypkg/Editor/Infrastructure/UnityCliPackageResolveState.cs +20 -1
  250. package/unitypkg/Editor/Infrastructure/UnityCliReadGeneration.cs +97 -0
  251. package/unitypkg/Editor/Infrastructure/UnityCliReflection.cs +644 -133
  252. package/unitypkg/Editor/Infrastructure/UnityCliReloadOperation.cs +128 -41
  253. package/unitypkg/Editor/Infrastructure/UnityCliRuntimeExecutor.cs +14 -84
  254. package/unitypkg/Editor/Infrastructure/UnityCliSerializer.cs +220 -0
  255. package/unitypkg/Editor/Infrastructure/UnityCliSessionMutationLease.cs +69 -29
  256. package/unitypkg/Editor/Infrastructure/UnityCliTestRunCoordinator.cs +440 -54
  257. package/unitypkg/Editor/Infrastructure/UnityCliWaiter.cs +128 -35
  258. package/unitypkg/Editor/ProjectTools/IUnityCliTool.cs +9 -1
  259. package/unitypkg/Editor/ProjectTools/OfficialAttributeToolRegistry.cs +32 -31
  260. package/unitypkg/Editor/ProjectTools/ProjectToolRegistry.cs +607 -458
  261. package/unitypkg/Editor/ProjectTools/ProjectToolSchemaValidator.cs +35 -7
  262. package/unitypkg/Editor/ProjectTools/RegisteredTool.cs +501 -60
  263. package/unitypkg/Editor/ProjectTools/UnityCliProjectToolContext.cs +21 -0
  264. package/unitypkg/Editor/ProjectTools/UnityCliSchemaDeriver.cs +36 -7
  265. package/unitypkg/Editor/ProjectTools/UnityCliToolName.cs +30 -0
  266. package/unitypkg/Editor/Properties/AssemblyInfo.cs +3 -0
  267. package/unitypkg/Editor/Tools/asset/get-data.cs +16 -7
  268. package/unitypkg/Editor/Tools/build/BuildSceneStateTool.cs +23 -3
  269. package/unitypkg/Editor/Tools/build/BuildToolDtos.cs +2 -1
  270. package/unitypkg/Editor/Tools/build/scene/add.cs +6 -3
  271. package/unitypkg/Editor/Tools/build/scene/disable.cs +1 -1
  272. package/unitypkg/Editor/Tools/build/scene/enable.cs +1 -1
  273. package/unitypkg/Editor/Tools/build/scene/list.cs +1 -1
  274. package/unitypkg/Editor/Tools/build/scene/remove.cs +13 -3
  275. package/unitypkg/Editor/Tools/camera/CameraToolSupport.cs +38 -38
  276. package/unitypkg/Editor/Tools/camera/multiview.cs +13 -6
  277. package/unitypkg/Editor/Tools/camera/screenshot.cs +14 -7
  278. package/unitypkg/Editor/Tools/component/get.cs +16 -9
  279. package/unitypkg/Editor/Tools/component/modify.cs +6 -4
  280. package/unitypkg/Editor/Tools/console/clear.cs +7 -6
  281. package/unitypkg/Editor/Tools/console/get.cs +48 -16
  282. package/unitypkg/Editor/Tools/editor/EditorToolSupport.cs +143 -28
  283. package/unitypkg/Editor/Tools/editor/menu-items.cs +1 -1
  284. package/unitypkg/Editor/Tools/editor/play.cs +2 -11
  285. package/unitypkg/Editor/Tools/editor/selection.cs +1 -1
  286. package/unitypkg/Editor/Tools/editor/stop.cs +2 -11
  287. package/unitypkg/Editor/Tools/editor/windows.cs +1 -1
  288. package/unitypkg/Editor/Tools/go/GameObjectToolSupport.cs +54 -4
  289. package/unitypkg/Editor/Tools/go/delete.cs +1 -1
  290. package/unitypkg/Editor/Tools/go/find.cs +167 -70
  291. package/unitypkg/Editor/Tools/graphics/GraphicsToolSupport.cs +25 -0
  292. package/unitypkg/Editor/Tools/graphics/light-probe-group.cs +9 -3
  293. package/unitypkg/Editor/Tools/graphics/pipeline-info.cs +1 -1
  294. package/unitypkg/Editor/Tools/graphics/pipeline-settings.cs +1 -1
  295. package/unitypkg/Editor/Tools/graphics/quality.cs +2 -2
  296. package/unitypkg/Editor/Tools/graphics/reflection-probe.cs +3 -3
  297. package/unitypkg/Editor/Tools/graphics/stats.cs +1 -1
  298. package/unitypkg/Editor/Tools/object/ObjectToolSupport.cs +4 -4
  299. package/unitypkg/Editor/Tools/object/get-data.cs +9 -7
  300. package/unitypkg/Editor/Tools/object/modify.cs +7 -5
  301. package/unitypkg/Editor/Tools/physics/raycast.cs +24 -6
  302. package/unitypkg/Editor/Tools/prefab/stage.cs +1 -1
  303. package/unitypkg/Editor/Tools/profiler/ProfilerFrameDebuggerSupport.cs +94 -21
  304. package/unitypkg/Editor/Tools/profiler/ProfilerMemorySnapshotSupport.cs +397 -10
  305. package/unitypkg/Editor/Tools/profiler/ProfilerToolSupport.cs +18 -4
  306. package/unitypkg/Editor/Tools/profiler/frame-debugger/enable.cs +1 -1
  307. package/unitypkg/Editor/Tools/profiler/frame-debugger/events.cs +88 -87
  308. package/unitypkg/Editor/Tools/profiler/hierarchy.cs +278 -112
  309. package/unitypkg/Editor/Tools/profiler/memory-snapshot/compare.cs +9 -5
  310. package/unitypkg/Editor/Tools/profiler/memory-snapshot/list.cs +37 -35
  311. package/unitypkg/Editor/Tools/profiler/memory-snapshot/take.cs +99 -97
  312. package/unitypkg/Editor/Tools/project/info.cs +1 -1
  313. package/unitypkg/Editor/Tools/project/layers.cs +1 -1
  314. package/unitypkg/Editor/Tools/project/tags.cs +1 -1
  315. package/unitypkg/Editor/Tools/reflection/method/call.cs +9 -7
  316. package/unitypkg/Editor/Tools/reflection/method/find.cs +27 -37
  317. package/unitypkg/Editor/Tools/reflection/type/schema.cs +24 -28
  318. package/unitypkg/Editor/Tools/scene/gameobject/component.cs +8 -5
  319. package/unitypkg/Editor/Tools/scene/gameobject/components.cs +10 -7
  320. package/unitypkg/Editor/Tools/scene/gameobject-support.cs +104 -8
  321. package/unitypkg/Editor/Tools/scene/gameobject.cs +37 -16
  322. package/unitypkg/Editor/Tools/scene/hierarchy.cs +146 -142
  323. package/unitypkg/Editor/Tools/screenshot/EditorElementCaptureToolSupport.cs +77 -25
  324. package/unitypkg/Editor/Tools/screenshot/ScreenshotToolSupport.cs +315 -95
  325. package/unitypkg/Editor/Tools/screenshot/editor-element.cs +7 -5
  326. package/unitypkg/Editor/Tools/screenshot/game-view.cs +35 -21
  327. package/unitypkg/Editor/Tools/screenshot/isolated-object.cs +29 -23
  328. package/unitypkg/Editor/Tools/screenshot/orbit.cs +37 -25
  329. package/unitypkg/Editor/Tools/screenshot/scene-view.cs +25 -18
  330. package/unitypkg/Editor/Tools/screenshot/screen.cs +177 -17
  331. package/unitypkg/Editor/Tools/screenshot/surround.cs +83 -79
  332. package/unitypkg/Editor/Tools/ui/render-preview.cs +16 -11
  333. package/unitypkg/Editor/UnityCliBridge.cs +97 -34
  334. package/unitypkg/Runtime/Infrastructure/UnityCliRuntimeBridge.cs +753 -122
  335. package/unitypkg/Runtime/Properties/AssemblyInfo.cs +5 -0
  336. package/unitypkg/Runtime/UnityCli.Runtime.asmdef +1 -1
  337. package/unitypkg/Shared/AssemblyInfo.cs +4 -0
  338. package/unitypkg/Shared/UnityCliAdmissionPool.cs +265 -75
  339. package/unitypkg/Shared/UnityCliBridgeQueuePolicy.cs +410 -0
  340. package/unitypkg/Shared/UnityCliBridgeWorkItemState.cs +47 -0
  341. package/unitypkg/Shared/UnityCliExceptionProjection.cs +151 -0
  342. package/unitypkg/Shared/UnityCliQueuePublication.cs +30 -0
  343. package/unitypkg/Shared/UnityCliRequestBudget.cs +352 -0
  344. package/unitypkg/Shared/UnityCliRuntimeRequestPublication.cs +107 -0
  345. package/unitypkg/Tests/Editor/UnityCli.Editor.Tests.asmdef +2 -2
  346. package/unitypkg/Tests/Editor/UnityCliAdmissionCoordinatorIntegrationTests.cs +1329 -0
  347. package/unitypkg/Tests/Editor/UnityCliAdmissionPoolTests.cs +311 -114
  348. package/unitypkg/Tests/Editor/UnityCliAdmissionRepairStateTests.cs +94 -0
  349. package/unitypkg/Tests/Editor/UnityCliBridgeAdmissionIntegrationTests.cs +1229 -0
  350. package/unitypkg/Tests/Editor/UnityCliBridgeQueuePolicyTests.cs +412 -0
  351. package/unitypkg/Tests/Editor/UnityCliContinuationIntegrationTests.cs +292 -0
  352. package/unitypkg/Tests/Editor/UnityCliDiagnosticBoundaryTests.cs +120 -0
  353. package/unitypkg/Tests/Editor/UnityCliDurableStatusTests.cs +84 -0
  354. package/unitypkg/Tests/Editor/UnityCliExceptionProjectionTests.cs +72 -0
  355. package/unitypkg/Tests/Editor/UnityCliInstancePublicationRetryTests.cs +180 -97
  356. package/unitypkg/Tests/Editor/UnityCliMemoryReflectionIntegrationTests.cs +434 -0
  357. package/unitypkg/Tests/Editor/UnityCliRequestBudgetTests.cs +615 -0
  358. package/unitypkg/Tests/Editor/UnityCliRuntimeBridgeIntegrationTests.cs +520 -0
  359. package/unitypkg/Tests/Editor/UnityCliToolCatalogTests.cs +562 -0
  360. package/unitypkg/Tests/Runtime/UnityCli.Runtime.Tests.asmdef +23 -0
  361. package/unitypkg/Tests/Runtime/UnityCliRuntimeCatalogValidationTests.cs +29 -0
  362. package/unitypkg/package.json +1 -1
  363. package/contracts/static-capability-catalog.json +0 -48226
  364. package/dist/bridge/domains/diagnose.d.ts +0 -2
  365. package/dist/bridge/domains/diagnose.js +0 -11
  366. package/dist/bridge/domains/diagnose.js.map +0 -1
  367. package/dist/bridge/domains/extension.d.ts +0 -8
  368. package/dist/bridge/domains/extension.js +0 -20
  369. package/dist/bridge/domains/extension.js.map +0 -1
  370. package/dist/capabilities.d.ts +0 -5
  371. package/dist/capabilities.js +0 -84
  372. package/dist/capabilities.js.map +0 -1
  373. package/dist/static-capability-catalog.d.ts +0 -13
  374. package/dist/static-capability-catalog.js +0 -41
  375. package/dist/static-capability-catalog.js.map +0 -1
  376. package/dist/tools/diagnose/capabilities.d.ts +0 -13
  377. package/dist/tools/diagnose/capabilities.js +0 -34
  378. package/dist/tools/diagnose/capabilities.js.map +0 -1
  379. package/dist/tools/package/info.d.ts +0 -19
  380. package/dist/tools/package/info.js +0 -27
  381. package/dist/tools/package/info.js.map +0 -1
  382. package/dist/tools/package/list.d.ts +0 -27
  383. package/dist/tools/package/list.js +0 -21
  384. package/dist/tools/package/list.js.map +0 -1
  385. package/dist/tools/package/registry-get.d.ts +0 -10
  386. package/dist/tools/package/registry-get.js +0 -22
  387. package/dist/tools/package/registry-get.js.map +0 -1
  388. package/dist/tools/package/scoped-registry-list.d.ts +0 -14
  389. package/dist/tools/package/scoped-registry-list.js +0 -22
  390. package/dist/tools/package/scoped-registry-list.js.map +0 -1
  391. package/dist/tools/package/search.d.ts +0 -17
  392. package/dist/tools/package/search.js +0 -39
  393. package/dist/tools/package/search.js.map +0 -1
  394. package/dist/tools/package/status.d.ts +0 -21
  395. package/dist/tools/package/status.js +0 -37
  396. package/dist/tools/package/status.js.map +0 -1
  397. package/dist/tools/tools/describe.d.ts +0 -27
  398. package/dist/tools/tools/describe.js +0 -35
  399. package/dist/tools/tools/describe.js.map +0 -1
  400. package/dist/tools/tools/group-list.d.ts +0 -16
  401. package/dist/tools/tools/group-list.js +0 -38
  402. package/dist/tools/tools/group-list.js.map +0 -1
  403. package/dist/tools/tools/group.d.ts +0 -13
  404. package/dist/tools/tools/group.js +0 -34
  405. package/dist/tools/tools/group.js.map +0 -1
  406. package/dist/tools/tools/pagination.d.ts +0 -17
  407. package/dist/tools/tools/pagination.js +0 -28
  408. package/dist/tools/tools/pagination.js.map +0 -1
  409. package/dist/tools/tools/search.d.ts +0 -15
  410. package/dist/tools/tools/search.js +0 -42
  411. package/dist/tools/tools/search.js.map +0 -1
  412. package/unitypkg/Editor/Tools/diagnose/preflight.cs +0 -269
  413. package/unitypkg/Editor/Tools/editor/console.cs +0 -26
  414. package/unitypkg/Editor/Tools/extension/status.cs +0 -87
  415. package/unitypkg/Editor/Tools/go/inspect.cs +0 -49
  416. package/unitypkg/Editor/Tools/graphics/pipeline-quality.cs +0 -24
  417. package/unitypkg/Editor/Tools/graphics/probe-positions.cs +0 -58
  418. package/unitypkg/Editor/Tools/graphics/settings.cs +0 -104
  419. package/unitypkg/Editor/Tools/graphics/stats-counters.cs +0 -28
  420. package/unitypkg/Editor/Tools/graphics/stats-memory.cs +0 -21
  421. package/unitypkg/Editor/Tools/instances/list.cs +0 -71
  422. package/unitypkg/Editor/Tools/physics/raycast-all.cs +0 -67
  423. package/unitypkg/Editor/Tools/scene/build-settings.cs +0 -69
  424. package/unitypkg/Editor/Tools/scene/data.cs +0 -125
  425. package/unitypkg/Editor/Tools/tests/list.cs +0 -98
@@ -1,42 +1,39 @@
1
1
  #if UNITY_EDITOR
2
2
  using System;
3
3
  using System.Collections.Generic;
4
- using System.Linq;
4
+ using System.Globalization;
5
+ using UnityEditor;
5
6
  using UnityEngine;
6
7
  using UnityEngine.SceneManagement;
7
8
 
8
9
  namespace UnityCli
9
10
  {
10
- /// <summary>
11
- /// scene.hierarchy — 读取激活场景的轻量分页层级:parent/深度/名称过滤/组件预览。
12
- /// </summary>
11
+ /// <summary>scene.hierarchy — 以 next raw GameObject 为游标的有界前序层级读取。</summary>
13
12
  public class SceneHierarchyTool : IUnityCliTool<SceneHierarchyTool.Request, SceneHierarchyTool.Result>
14
13
  {
15
14
  public sealed class Request
16
15
  {
17
- public int? PageSize { get; set; }
18
- public int? Cursor { get; set; }
16
+ [UnityCliRange(1L, 500L)] public int? PageSize { get; set; }
17
+ public string Cursor { get; set; }
19
18
  public string Parent { get; set; }
20
- public int? MaxDepth { get; set; }
19
+ [UnityCliRange(0L, 16L)] public int? MaxDepth { get; set; }
21
20
  public string Query { get; set; }
22
21
  public string NameFilter { get; set; }
23
22
  public string ComponentType { get; set; }
24
23
  public bool IncludeComponents { get; set; }
25
24
  public bool IncludeInactive { get; set; }
25
+ public UnityCliReadBudgetInput Budget { get; set; }
26
26
  }
27
27
 
28
28
  public sealed class Result
29
29
  {
30
30
  public SceneSummary Scene { get; set; }
31
- public List<HierarchyItem> Items { get; set; }
31
+ [UnityCliItemsRange(0, 500)] public List<HierarchyItem> Items { get; set; }
32
32
  public PageInfo PageInfo { get; set; }
33
+ public UnityCliRequestBudgetUsage Budget { get; set; }
33
34
  }
34
35
 
35
- public sealed class SceneSummary
36
- {
37
- public string Name { get; set; }
38
- public string Path { get; set; }
39
- }
36
+ public sealed class SceneSummary { public string Name { get; set; } public string Path { get; set; } }
40
37
 
41
38
  public sealed class HierarchyItem
42
39
  {
@@ -50,28 +47,28 @@ namespace UnityCli
50
47
  public long Layer { get; set; }
51
48
  public long ChildCount { get; set; }
52
49
  public bool ChildrenTruncated { get; set; }
53
- public long ComponentCount { get; set; }
54
- public List<string> ComponentTypes { get; set; }
55
- public List<string> Components { get; set; }
50
+ public long? ComponentCount { get; set; }
51
+ [UnityCliItemsRange(0, UnityCliBridge.ObjectRefResolver.AbsoluteMaxBatchItems)] public List<string> ComponentTypes { get; set; }
52
+ [UnityCliItemsRange(0, UnityCliBridge.ObjectRefResolver.AbsoluteMaxBatchItems)] public List<string> Components { get; set; }
56
53
  }
57
54
 
58
55
  public sealed class PageInfo
59
56
  {
60
- public long Total { get; set; }
61
- public long TotalCount { get; set; }
62
57
  public long ReturnedCount { get; set; }
63
58
  public long PageSize { get; set; }
64
- public long Cursor { get; set; }
65
- public long? NextCursor { get; set; }
59
+ public string Cursor { get; set; }
60
+ public string NextCursor { get; set; }
66
61
  public bool HasMore { get; set; }
62
+ public bool Complete { get; set; }
67
63
  public bool Truncated { get; set; }
64
+ public string Generation { get; set; }
68
65
  }
69
66
 
70
67
  public ToolDescriptor Describe() => new()
71
68
  {
72
69
  Name = "scene.hierarchy",
73
- Summary = "Read a lightweight, paged hierarchy of active scene GameObjects.",
74
- Description = "Read a lightweight, paged hierarchy of active scene GameObjects. Use parent to list a subtree, maxDepth to bound recursion, nameFilter/filter to narrow results, and includeComponents only when component previews are needed. For deep object or component fields, use go.inspect, object.get-data, or component.get after selecting a stable path/id.",
70
+ Summary = "Read a lightweight, paged active scene hierarchy from stable raw GameObject positions.",
71
+ Description = "Read a lightweight active-scene hierarchy in Edit or Play mode. Use parent for one subtree, maxDepth to limit recursion, query/name/component filters to narrow results, and includeComponents only when component previews are needed. Read one selected object with scene.gameobject; use object.get-data or component.get for deep fields. pageSize is 1..500 (default 50), maxDepth is 0..16 (default 1), each page scans only forward from nextCursor, pageInfo never fabricates a filtered total, and old generations return E_CONTINUATION_STALE. All pages share scan/node/getter/reflection/time/UTF-8 budgets.",
75
72
  Group = "scene",
76
73
  DestructiveHint = false,
77
74
  Target = "editor",
@@ -80,169 +77,176 @@ namespace UnityCli
80
77
  MaxTimeoutMs = 600000,
81
78
  Examples = new[]
82
79
  {
83
- new ToolExample { Description = "List a bounded hierarchy page", Input = new Request { PageSize = 25, MaxDepth = 1 } },
80
+ new ToolExample { Description = "List one hierarchy page", Input = new Request { PageSize = 25, MaxDepth = 1 } },
84
81
  new ToolExample { Description = "Find camera nodes by component type", Input = new Request { ComponentType = "Camera", PageSize = 10 } }
85
82
  }
86
83
  };
87
84
 
88
85
  public System.Threading.Tasks.ValueTask<Result> ExecuteAsync(Request request, UnityCliProjectToolContext ctx, System.Threading.CancellationToken ct = default)
89
86
  {
87
+ request = request ?? new Request();
88
+ var budget = ctx.UseReadBudget(request.Budget);
89
+ if (!budget.TryConsumeGetter()) throw new UnityCliRequestBudgetExceededException(budget.ExhaustedDimension);
90
90
  var scene = SceneManager.GetActiveScene();
91
- var parent = request.Parent;
92
- var includeInactive = request.IncludeInactive;
93
- var includeComponents = request.IncludeComponents;
94
- var query = request.Query ?? request.NameFilter;
95
- var componentType = request.ComponentType;
96
- var maxDepth = Math.Max(0, Math.Min(request.MaxDepth ?? 1, 16));
97
- var cursor = Math.Max(0, request.Cursor ?? 0);
98
- var pageSize = Math.Max(1, Math.Min(request.PageSize ?? 50, 500));
99
-
100
- var roots = new List<GameObject>();
101
- if (!string.IsNullOrEmpty(parent))
91
+ if (!budget.TryConsumeGetter() || !budget.TryConsumeGetter()) throw new UnityCliRequestBudgetExceededException(budget.ExhaustedDimension);
92
+ var sceneName = scene.name;
93
+ var scenePath = scene.path;
94
+ if (!budget.TryReserveSerializedUtf8(sceneName) || !budget.TryReserveSerializedUtf8(scenePath)) throw new UnityCliRequestBudgetExceededException(budget.ExhaustedDimension);
95
+ var parent = ResolveParent(request.Parent, budget);
96
+ if (parent != null)
102
97
  {
103
- var parentObject = SceneToolSupport.ResolveGameObjectByPath(parent);
104
- if (parentObject == null)
105
- {
106
- throw new UnityCliBridge.BridgeCommandException("E_SCENE_HIERARCHY_PARENT_NOT_FOUND", "Parent GameObject not found: " + parent, "Run go.find or scene.hierarchy without parent, then pass a stable hierarchy path or instance id.");
107
- }
108
- for (var i = 0; i < parentObject.transform.childCount; i++)
109
- {
110
- roots.Add(parentObject.transform.GetChild(i).gameObject);
111
- }
98
+ if (!budget.TryConsumeGetters(2)) throw new UnityCliRequestBudgetExceededException(budget.ExhaustedDimension);
99
+ if (parent.scene.handle != scene.handle)
100
+ throw new UnityCliBridge.BridgeCommandException("E_SCENE_HIERARCHY_PARENT_NOT_FOUND", "Parent is not in the active scene: " + request.Parent, "Pass an exact active-scene hierarchy path or instance id.");
112
101
  }
102
+ var maxDepth = request.MaxDepth ?? 1;
103
+ var pageSize = request.PageSize ?? 50;
104
+ var query = request.Query ?? request.NameFilter;
105
+ if (parent != null && !budget.TryConsumeGetter()) throw new UnityCliRequestBudgetExceededException(budget.ExhaustedDimension);
106
+ var selector = (parent == null ? "root" : parent.GetInstanceID().ToString(CultureInfo.InvariantCulture)) + "\0" + maxDepth.ToString(CultureInfo.InvariantCulture)
107
+ + "\0" + query + "\0" + request.ComponentType + "\0" + request.IncludeInactive + "\0" + request.IncludeComponents;
108
+ var generation = UnityCliContinuationToken.Fingerprint(UnityCliReadGeneration.Hierarchy(scene) + "\0" + selector);
109
+ var traversal = new UnityCliHierarchyPager.Traversal(scene, parent, maxDepth, budget);
110
+ GameObject current;
111
+ if (string.IsNullOrEmpty(request.Cursor)) current = traversal.First(budget);
113
112
  else
114
113
  {
115
- roots.AddRange(scene.GetRootGameObjects());
114
+ current = UnityCliHierarchyPager.ResolveCursor(request.Cursor, "scene.hierarchy", generation, budget, out var phase);
115
+ if (!budget.TryConsumeGetters(parent == null ? 2 : 3)) throw new UnityCliRequestBudgetExceededException(budget.ExhaustedDimension);
116
+ if (phase != "scene" || current.scene.handle != scene.handle || !WithinBoundary(current.transform, parent == null ? null : parent.transform, maxDepth, budget))
117
+ throw UnityCliHierarchyPager.Stale();
116
118
  }
117
119
 
118
- var allItems = new List<HierarchyItem>();
119
- foreach (var root in roots.OrderBy(g => HierarchyPath(g), StringComparer.Ordinal))
120
+ var items = new List<HierarchyItem>();
121
+ while (current != null && items.Count < pageSize)
120
122
  {
121
- CollectHierarchy(root.transform, allItems, 0, maxDepth, query, includeInactive, includeComponents, componentType);
123
+ var candidate = current;
124
+ if (!budget.TryConsumeScannedRecord()) break;
125
+ try
126
+ {
127
+ var next = traversal.Next(candidate, budget);
128
+ if (Matches(candidate, query, request.ComponentType, request.IncludeInactive, budget))
129
+ items.Add(BuildItem(candidate.transform, parent, request.IncludeComponents, budget));
130
+ current = next;
131
+ }
132
+ catch (UnityCliRequestBudgetExceededException)
133
+ {
134
+ current = candidate;
135
+ break;
136
+ }
122
137
  }
123
138
 
124
- var total = allItems.Count;
125
- var page = allItems.Skip(cursor).Take(pageSize).ToList();
139
+ if (items.Count == 0 && current != null) throw new UnityCliRequestBudgetExceededException(budget.ExhaustedDimension ?? "scannedRecords");
140
+ var nextCursor = UnityCliHierarchyPager.Cursor("scene.hierarchy", generation, "scene", current, budget);
126
141
  return new System.Threading.Tasks.ValueTask<Result>(new Result
127
142
  {
128
- Scene = new SceneSummary { Name = scene.name, Path = scene.path },
129
- Items = page,
130
- PageInfo = BuildPageInfo(pageSize, cursor, total, page.Count)
143
+ Scene = new SceneSummary { Name = sceneName, Path = scenePath },
144
+ Items = items,
145
+ PageInfo = new PageInfo
146
+ {
147
+ ReturnedCount = items.Count,
148
+ PageSize = pageSize,
149
+ Cursor = request.Cursor,
150
+ NextCursor = nextCursor,
151
+ HasMore = nextCursor != null,
152
+ Complete = nextCursor == null,
153
+ Truncated = nextCursor != null,
154
+ Generation = generation
155
+ },
156
+ Budget = budget.Usage()
131
157
  });
132
158
  }
133
159
 
134
- // 按深度/名称/inactive 策略收集层级项;分页在调用方统一处理。
135
- private static void CollectHierarchy(Transform transform, List<HierarchyItem> items, int depth, int maxDepth, string query, bool includeInactive, bool includeComponents, string componentType)
160
+ private static GameObject ResolveParent(string parent, UnityCliRequestBudget budget)
136
161
  {
137
- var gameObject = transform.gameObject;
138
- if (includeInactive || gameObject.activeInHierarchy)
139
- {
140
- if (TextMatches(query, gameObject.name, HierarchyPath(gameObject)) && ComponentMatches(gameObject, componentType))
141
- {
142
- items.Add(BuildHierarchyItem(transform, depth, includeComponents));
143
- }
144
- }
145
- if (depth >= maxDepth)
146
- {
147
- return;
148
- }
149
- foreach (Transform child in transform)
162
+ if (string.IsNullOrEmpty(parent)) return null;
163
+ int instanceId;
164
+ GameObject value = null;
165
+ if (int.TryParse(parent, NumberStyles.Integer, CultureInfo.InvariantCulture, out instanceId))
150
166
  {
151
- CollectHierarchy(child, items, depth + 1, maxDepth, query, includeInactive, includeComponents, componentType);
167
+ if (!budget.TryConsumeGetter()) throw new UnityCliRequestBudgetExceededException(budget.ExhaustedDimension);
168
+ value = EditorUtility.InstanceIDToObject(instanceId) as GameObject;
152
169
  }
170
+ else value = UnityCliBridge.ObjectRefResolver.FindGameObjectByExactPath(parent, budget);
171
+ if (value == null) throw new UnityCliBridge.BridgeCommandException("E_SCENE_HIERARCHY_PARENT_NOT_FOUND", "Parent GameObject not found: " + parent, "Pass an exact hierarchy path or instance id from a fresh scene.hierarchy page.");
172
+ return value;
153
173
  }
154
174
 
155
- // 生成轻量 GameObject 层级项;componentTypes 始终返回,components 仅在 includeComponents 时返回。
156
- private static HierarchyItem BuildHierarchyItem(Transform transform, int depth, bool includeComponents)
175
+ private static bool WithinBoundary(Transform value, Transform boundary, int maxDepth, UnityCliRequestBudget budget)
157
176
  {
158
- var componentNames = new List<string>();
159
- foreach (var component in transform.GetComponents<Component>())
177
+ if (value == null) return false;
178
+ var current = value;
179
+ var depth = 0;
180
+ while (current != null && current != boundary)
160
181
  {
161
- componentNames.Add(component != null ? component.GetType().Name : "MissingScript");
162
- }
163
- var gameObject = transform.gameObject;
164
- var item = new HierarchyItem
165
- {
166
- Id = gameObject.GetInstanceID(),
167
- Name = gameObject.name,
168
- Path = HierarchyPath(gameObject),
169
- Depth = depth,
170
- Active = gameObject.activeSelf,
171
- ActiveInHierarchy = gameObject.activeInHierarchy,
172
- Tag = gameObject.tag,
173
- Layer = gameObject.layer,
174
- ChildCount = transform.childCount,
175
- ChildrenTruncated = transform.childCount > 0,
176
- ComponentCount = componentNames.Count,
177
- ComponentTypes = componentNames
178
- };
179
- if (includeComponents)
180
- {
181
- item.Components = componentNames;
182
+ if (depth > maxDepth) return false;
183
+ if (!budget.TryConsumeGetter()) throw new UnityCliRequestBudgetExceededException(budget.ExhaustedDimension);
184
+ current = current.parent;
185
+ depth++;
182
186
  }
183
- return item;
187
+ return (boundary == null || current == boundary) && depth - 1 <= maxDepth;
184
188
  }
185
189
 
186
- private static string HierarchyPath(GameObject gameObject)
190
+ private static bool Matches(GameObject gameObject, string query, string componentType, bool includeInactive, UnityCliRequestBudget budget)
187
191
  {
188
- var names = new Stack<string>();
189
- var current = gameObject.transform;
190
- while (current != null)
192
+ if (!budget.TryConsumeGetter()) throw new UnityCliRequestBudgetExceededException(budget.ExhaustedDimension);
193
+ if (!includeInactive && !gameObject.activeInHierarchy) return false;
194
+ if (!budget.TryConsumeGetter()) throw new UnityCliRequestBudgetExceededException(budget.ExhaustedDimension);
195
+ var name = gameObject.name;
196
+ if (!string.IsNullOrEmpty(query))
191
197
  {
192
- names.Push(current.name);
193
- current = current.parent;
198
+ var path = UnityCliHierarchyPager.Path(gameObject, budget);
199
+ if (name.IndexOf(query, StringComparison.OrdinalIgnoreCase) < 0 && path.IndexOf(query, StringComparison.OrdinalIgnoreCase) < 0) return false;
194
200
  }
195
- return string.Join("/", names.ToArray());
196
- }
197
-
198
- // 通用文本筛选;query 为空时视为匹配。
199
- private static bool TextMatches(string query, params string[] values)
200
- {
201
- if (string.IsNullOrEmpty(query)) return true;
202
- foreach (var value in values ?? new string[0])
201
+ if (string.IsNullOrEmpty(componentType)) return true;
202
+ foreach (var component in UnityCliBridge.ObjectRefResolver.BoundedComponents(gameObject, budget))
203
203
  {
204
- if (!string.IsNullOrEmpty(value) && value.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0)
205
- {
206
- return true;
207
- }
204
+ if (component == null && string.Equals(componentType, "MissingScript", StringComparison.OrdinalIgnoreCase)) return true;
205
+ if (component == null) continue;
206
+ if (!budget.TryConsumeReflectionCall()) throw new UnityCliRequestBudgetExceededException(budget.ExhaustedDimension);
207
+ var type = component.GetType();
208
+ if (string.Equals(type.Name, componentType, StringComparison.OrdinalIgnoreCase)
209
+ || string.Equals(type.FullName, componentType, StringComparison.OrdinalIgnoreCase)) return true;
208
210
  }
209
211
  return false;
210
212
  }
211
213
 
212
- // 判断 GameObject 是否拥有指定 Component 类型;空 componentType 表示不过滤(字符串匹配,不解析程序集)
213
- private static bool ComponentMatches(GameObject gameObject, string componentType)
214
+ private static HierarchyItem BuildItem(Transform transform, GameObject parent, bool includeComponents, UnityCliRequestBudget budget)
214
215
  {
215
- if (gameObject == null || string.IsNullOrEmpty(componentType)) return true;
216
- foreach (var component in gameObject.GetComponents<Component>())
216
+ if (!budget.TryConsumeNode() || !budget.TryConsumeGetter()) throw new UnityCliRequestBudgetExceededException(budget.ExhaustedDimension);
217
+ var gameObject = transform.gameObject;
218
+ var path = UnityCliHierarchyPager.Path(gameObject, budget);
219
+ if (!budget.TryConsumeGetter()) throw new UnityCliRequestBudgetExceededException(budget.ExhaustedDimension);
220
+ var name = gameObject.name;
221
+ if (!budget.TryReserveSerializedUtf8(name) || !budget.TryReserveSerializedUtf8(path)) throw new UnityCliRequestBudgetExceededException(budget.ExhaustedDimension);
222
+ List<string> componentNames = null;
223
+ if (includeComponents)
217
224
  {
218
- if (ComponentTypeMatches(component, componentType)) return true;
225
+ componentNames = new List<string>();
226
+ foreach (var component in UnityCliBridge.ObjectRefResolver.BoundedComponents(gameObject, budget))
227
+ {
228
+ if (!budget.TryConsumeReflectionCall()) throw new UnityCliRequestBudgetExceededException(budget.ExhaustedDimension);
229
+ var componentName = component == null ? "MissingScript" : component.GetType().FullName;
230
+ if (!budget.TryReserveSerializedUtf8(componentName)) throw new UnityCliRequestBudgetExceededException(budget.ExhaustedDimension);
231
+ componentNames.Add(componentName);
232
+ }
219
233
  }
220
- return false;
221
- }
222
-
223
- private static bool ComponentTypeMatches(Component component, string componentType)
224
- {
225
- if (string.IsNullOrEmpty(componentType)) return true;
226
- if (component == null) return string.Equals(componentType, "MissingScript", StringComparison.OrdinalIgnoreCase);
227
- var type = component.GetType();
228
- return string.Equals(type.Name, componentType, StringComparison.OrdinalIgnoreCase)
229
- || string.Equals(type.FullName, componentType, StringComparison.OrdinalIgnoreCase)
230
- || type.FullName.IndexOf(componentType, StringComparison.OrdinalIgnoreCase) >= 0;
231
- }
232
-
233
- private static PageInfo BuildPageInfo(int pageSize, int cursor, int totalCount, int returnedCount)
234
- {
235
- long? next = cursor + returnedCount < totalCount ? (long?)(cursor + returnedCount) : null;
236
- return new PageInfo
234
+ if (!budget.TryConsumeGetter() || !budget.TryConsumeGetter() || !budget.TryConsumeGetter()
235
+ || !budget.TryConsumeGetter() || !budget.TryConsumeGetter() || !budget.TryConsumeGetter())
236
+ throw new UnityCliRequestBudgetExceededException(budget.ExhaustedDimension);
237
+ var childCount = transform.childCount;
238
+ var tag = gameObject.tag;
239
+ if (!budget.TryReserveSerializedUtf8(tag)) throw new UnityCliRequestBudgetExceededException(budget.ExhaustedDimension);
240
+ return new HierarchyItem
237
241
  {
238
- Total = totalCount,
239
- TotalCount = totalCount,
240
- ReturnedCount = returnedCount,
241
- PageSize = pageSize,
242
- Cursor = cursor,
243
- NextCursor = next,
244
- HasMore = next != null,
245
- Truncated = next != null
242
+ Id = gameObject.GetInstanceID(), Name = name, Path = path,
243
+ Depth = UnityCliHierarchyPager.Depth(transform, parent == null ? null : parent.transform, budget),
244
+ Active = gameObject.activeSelf, ActiveInHierarchy = gameObject.activeInHierarchy,
245
+ Tag = tag, Layer = gameObject.layer, ChildCount = childCount,
246
+ ChildrenTruncated = childCount > 0,
247
+ ComponentCount = componentNames == null ? (long?)null : componentNames.Count,
248
+ ComponentTypes = componentNames,
249
+ Components = componentNames
246
250
  };
247
251
  }
248
252
  }
@@ -2,7 +2,6 @@
2
2
  using System;
3
3
  using System.Collections.Generic;
4
4
  using System.IO;
5
- using System.Linq;
6
5
  using UnityEditor;
7
6
  using UnityEngine;
8
7
  #if UNITY_6000_7_OR_NEWER
@@ -26,9 +25,10 @@ namespace UnityCli
26
25
  internal List<string> ElementClasses;
27
26
  internal long WindowInstanceId, MaxBytes;
28
27
  internal int Width, Height;
28
+ internal ScreenshotCaptureBudget CaptureBudget;
29
29
  }
30
30
 
31
- internal static Plan Prepare(EditorElementCaptureRequest request)
31
+ internal static Plan Prepare(EditorElementCaptureRequest request, UnityCliRequestBudget requestBudget)
32
32
  {
33
33
  #if !UNITY_6000_7_OR_NEWER
34
34
  throw Unavailable();
@@ -41,20 +41,29 @@ namespace UnityCli
41
41
  var maxBytes = request.MaxBytes ?? (8L * 1024L * 1024L);
42
42
  if (maxWidth < 1 || maxWidth > MaxDimension || maxHeight < 1 || maxHeight > MaxDimension || maxBytes < 1024 || maxBytes > MaxArtifactBytes)
43
43
  throw Error("E_EDITOR_ELEMENT_LIMIT_INVALID", "maxWidth/maxHeight must be 1..4096 and maxBytes must be 1024..33554432.");
44
- var candidates = Resources.FindObjectsOfTypeAll<EditorWindow>()
45
- .Where(value => value != null && value.rootVisualElement != null && value.rootVisualElement.panel != null)
46
- .Where(value => string.Equals(value.GetType().Name, request.Window, StringComparison.Ordinal) || string.Equals(value.titleContent == null ? null : value.titleContent.text, request.Window, StringComparison.Ordinal))
47
- .OrderBy(value => value.GetInstanceID()).Take(257).ToList();
44
+ var candidates = new List<EditorWindow>();
45
+ foreach (var value in Resources.FindObjectsOfTypeAll<EditorWindow>())
46
+ {
47
+ if (!requestBudget.TryConsumeScannedRecord()) throw new UnityCliRequestBudgetExceededException(requestBudget.ExhaustedDimension);
48
+ if (value == null || value.rootVisualElement == null || value.rootVisualElement.panel == null) continue;
49
+ if (!string.Equals(value.GetType().Name, request.Window, StringComparison.Ordinal)
50
+ && !string.Equals(value.titleContent == null ? null : value.titleContent.text, request.Window, StringComparison.Ordinal)) continue;
51
+ candidates.Add(value);
52
+ if (candidates.Count > 256) break;
53
+ }
54
+ candidates.Sort((left, right) => left.GetInstanceID().CompareTo(right.GetInstanceID()));
48
55
  if (candidates.Count == 0) throw Error("E_EDITOR_ELEMENT_WINDOW_NOT_FOUND", "No attached EditorWindow exactly matches '" + request.Window + "'.");
49
56
  if (candidates.Count > 256) throw Error("E_EDITOR_ELEMENT_LIMIT_EXCEEDED", "More than 256 EditorWindow candidates matched the bounded scan.");
50
57
  var window = candidates[0];
51
- var element = Resolve(window.rootVisualElement, request.Selector);
58
+ var element = Resolve(window.rootVisualElement, request.Selector, requestBudget);
52
59
  if (element == null) throw Error("E_EDITOR_ELEMENT_NOT_FOUND", "No UI Toolkit element matched selector '" + request.Selector + "'.");
53
60
  var width = Mathf.CeilToInt(element.worldBound.width);
54
61
  var height = Mathf.CeilToInt(element.worldBound.height);
55
62
  if (width < 1 || height < 1 || width > maxWidth || height > maxHeight || (long)width * height > 16777216L)
56
63
  throw Error("E_EDITOR_ELEMENT_LIMIT_EXCEEDED", "Resolved element dimensions " + width + "x" + height + " exceed the requested capture bounds.");
57
- return new Plan { Element = element, WindowType = window.GetType().Name, WindowTitle = window.titleContent == null ? null : window.titleContent.text, WindowInstanceId = window.GetInstanceID(), Selector = request.Selector, ElementType = element.GetType().Name, ElementName = element.name, ElementClasses = element.GetClasses().Take(128).Select(Bound).ToList(), Width = width, Height = height, MaxBytes = maxBytes };
64
+ var limits = new ScreenshotBudgetInput { MaxArtifactBytes = maxBytes };
65
+ var captureBudget = ScreenshotToolSupport.PrepareCaptureBudget(width, height, 1, false, limits, requestBudget);
66
+ return new Plan { Element = element, WindowType = window.GetType().Name, WindowTitle = window.titleContent == null ? null : window.titleContent.text, WindowInstanceId = window.GetInstanceID(), Selector = request.Selector, ElementType = element.GetType().Name, ElementName = element.name, ElementClasses = BoundClasses(element, requestBudget), Width = width, Height = height, MaxBytes = maxBytes, CaptureBudget = captureBudget };
58
67
  #endif
59
68
  }
60
69
 
@@ -65,6 +74,7 @@ namespace UnityCli
65
74
  throw Unavailable();
66
75
  #else
67
76
  if (plan == null || plan.Element == null || plan.Element.panel == null) throw Error("E_EDITOR_ELEMENT_TARGET_CHANGED", "The frozen UI Toolkit element is no longer attached.");
77
+ plan.CaptureBudget.BeginCapture(plan.Width, plan.Height);
68
78
  RenderTexture texture = null;
69
79
  Texture2D readable = null;
70
80
  var previous = RenderTexture.active;
@@ -91,7 +101,7 @@ namespace UnityCli
91
101
  if (string.IsNullOrWhiteSpace(ctx.ArtifactDir)) throw Error("E_EDITOR_ELEMENT_ARTIFACT_UNAVAILABLE", "The request artifact directory is unavailable.");
92
102
  Directory.CreateDirectory(ctx.ArtifactDir);
93
103
  var output = Path.Combine(ctx.ArtifactDir, "editor-element.png");
94
- var artifact = ScreenshotToolSupport.WriteScreenshotBytes(output, png);
104
+ var artifact = ScreenshotToolSupport.WriteScreenshotBytes(output, png, plan.CaptureBudget);
95
105
  if (artifact.SizeBytes > plan.MaxBytes || artifact.SizeBytes != png.LongLength)
96
106
  {
97
107
  try { File.Delete(output); } catch { }
@@ -105,15 +115,16 @@ namespace UnityCli
105
115
 
106
116
  private static EditorElementCaptureResult Result(Plan plan, bool dryRun, ScreenshotArtifactProof artifact)
107
117
  {
108
- return new EditorElementCaptureResult { Available = true, Element = new EditorElementIdentity { Selector = plan.Selector, Type = plan.ElementType, Name = plan.ElementName, Classes = plan.ElementClasses }, Width = plan.Width, Height = plan.Height, Artifact = artifact, DryRun = dryRun, PlannedEffects = new List<EditorElementCapturePlannedEffect> { new EditorElementCapturePlannedEffect { Type = "screenshot.editor-element", WindowInstanceId = plan.WindowInstanceId, Width = plan.Width, Height = plan.Height, MaxBytes = plan.MaxBytes } } };
118
+ return new EditorElementCaptureResult { Available = true, Element = new EditorElementIdentity { Selector = plan.Selector, Type = plan.ElementType, Name = plan.ElementName, Classes = plan.ElementClasses }, Width = plan.Width, Height = plan.Height, Artifact = artifact, DryRun = dryRun, PlannedEffects = new List<EditorElementCapturePlannedEffect> { new EditorElementCapturePlannedEffect { Type = "screenshot.editor-element", WindowInstanceId = plan.WindowInstanceId, Width = plan.Width, Height = plan.Height, MaxBytes = plan.MaxBytes, CapturePlan = plan.CaptureBudget.Plan } } };
109
119
  }
110
120
 
111
121
  #if UNITY_6000_7_OR_NEWER
112
122
  /// <summary>实现type/#id/.class与descendant/child链;全查询最多16段、10000候选。</summary>
113
- private static VisualElement Resolve(VisualElement root, string selector)
123
+ private static VisualElement Resolve(VisualElement root, string selector, UnityCliRequestBudget budget)
114
124
  {
115
125
  var tokens = selector.Replace(">", " > ").Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
116
- var segments = tokens.Count(token => token != ">");
126
+ var segments = 0;
127
+ foreach (var token in tokens) if (token != ">") segments++;
117
128
  if (segments < 1 || segments > 16) throw Error("E_EDITOR_ELEMENT_SELECTOR_INVALID", "selector must contain 1..16 simple segments.");
118
129
  var current = new List<VisualElement> { root };
119
130
  var direct = false;
@@ -124,11 +135,10 @@ namespace UnityCli
124
135
  var next = new List<VisualElement>();
125
136
  foreach (var parent in current)
126
137
  {
127
- var values = direct ? parent.Children() : Descendants(parent, ref visited);
128
- foreach (var value in values)
138
+ foreach (var value in TraverseChildren(parent, !direct, budget))
129
139
  {
130
140
  if (++visited > 10000) throw Error("E_EDITOR_ELEMENT_LIMIT_EXCEEDED", "selector scan exceeded 10000 UI elements.");
131
- if (Matches(value, token)) next.Add(value);
141
+ if (Matches(value, token, budget)) next.Add(value);
132
142
  if (next.Count > 256) throw Error("E_EDITOR_ELEMENT_LIMIT_EXCEEDED", "selector produced more than 256 intermediate matches.");
133
143
  }
134
144
  }
@@ -136,27 +146,55 @@ namespace UnityCli
136
146
  direct = false;
137
147
  if (current.Count == 0) return null;
138
148
  }
139
- return current.FirstOrDefault();
149
+ return current.Count == 0 ? null : current[0];
140
150
  }
141
151
 
142
- private static IEnumerable<VisualElement> Descendants(VisualElement root, ref int visited)
152
+ private static List<string> BoundClasses(VisualElement element, UnityCliRequestBudget budget)
143
153
  {
144
- var result = new List<VisualElement>();
145
- var stack = new Stack<VisualElement>(root.Children().Reverse());
146
- while (stack.Count > 0)
154
+ var result = new List<string>();
155
+ foreach (var value in element.GetClasses())
147
156
  {
148
- var value = stack.Pop();
149
- result.Add(value);
150
- if (result.Count + visited > 10000) break;
151
- foreach (var child in value.Children().Reverse()) stack.Push(child);
157
+ if (result.Count >= 128) break;
158
+ if (!budget.TryConsumeScannedRecord()) throw new UnityCliRequestBudgetExceededException(budget.ExhaustedDimension);
159
+ var bounded = Bound(value);
160
+ if (!budget.TryReserveSerializedUtf8(bounded)) throw new UnityCliRequestBudgetExceededException(budget.ExhaustedDimension);
161
+ result.Add(bounded);
152
162
  }
153
163
  return result;
154
164
  }
155
165
 
156
- private static bool Matches(VisualElement value, string simple)
166
+ /// <summary>用(parent,nextChildIndex)逐子节点DFS,任何childCount和索引读取前先消费同一请求预算。</summary>
167
+ private static IEnumerable<VisualElement> TraverseChildren(VisualElement root, bool recursive, UnityCliRequestBudget budget)
168
+ {
169
+ var stack = new Stack<VisualElementTraversalFrame>();
170
+ stack.Push(CreateTraversalFrame(root, budget));
171
+ while (stack.Count > 0)
172
+ {
173
+ var frame = stack.Peek();
174
+ if (frame.NextChildIndex >= frame.ChildCount)
175
+ {
176
+ stack.Pop();
177
+ continue;
178
+ }
179
+ if (!budget.TryCheckpoint() || !budget.TryConsumeScannedRecord() || !budget.TryConsumeNode() || !budget.TryConsumeGetter())
180
+ throw new UnityCliRequestBudgetExceededException(budget.ExhaustedDimension);
181
+ var value = frame.Parent[frame.NextChildIndex++];
182
+ yield return value;
183
+ if (recursive) stack.Push(CreateTraversalFrame(value, budget));
184
+ }
185
+ }
186
+
187
+ private static VisualElementTraversalFrame CreateTraversalFrame(VisualElement parent, UnityCliRequestBudget budget)
188
+ {
189
+ if (!budget.TryCheckpoint() || !budget.TryConsumeGetter()) throw new UnityCliRequestBudgetExceededException(budget.ExhaustedDimension);
190
+ return new VisualElementTraversalFrame(parent, parent.childCount);
191
+ }
192
+
193
+ private static bool Matches(VisualElement value, string simple, UnityCliRequestBudget budget)
157
194
  {
158
195
  var typeEnd = simple.IndexOfAny(new[] { '#', '.' });
159
196
  var type = typeEnd < 0 ? simple : simple.Substring(0, typeEnd);
197
+ if (!budget.TryConsumeReflectionCall()) throw new UnityCliRequestBudgetExceededException(budget.ExhaustedDimension);
160
198
  if (!string.IsNullOrEmpty(type) && type != "*" && !string.Equals(value.GetType().Name, type, StringComparison.Ordinal)) return false;
161
199
  var position = Math.Max(0, typeEnd);
162
200
  while (position < simple.Length)
@@ -165,12 +203,26 @@ namespace UnityCli
165
203
  var next = simple.IndexOfAny(new[] { '#', '.' }, position);
166
204
  var name = simple.Substring(position, (next < 0 ? simple.Length : next) - position);
167
205
  if (string.IsNullOrEmpty(name)) return false;
206
+ if (!budget.TryConsumeGetter()) throw new UnityCliRequestBudgetExceededException(budget.ExhaustedDimension);
168
207
  if (marker == '#' && !string.Equals(value.name, name, StringComparison.Ordinal)) return false;
169
208
  if (marker == '.' && !value.ClassListContains(name)) return false;
170
209
  position = next < 0 ? simple.Length : next;
171
210
  }
172
211
  return true;
173
212
  }
213
+
214
+ private sealed class VisualElementTraversalFrame
215
+ {
216
+ internal readonly VisualElement Parent;
217
+ internal readonly int ChildCount;
218
+ internal int NextChildIndex;
219
+
220
+ internal VisualElementTraversalFrame(VisualElement parent, int childCount)
221
+ {
222
+ Parent = parent;
223
+ ChildCount = childCount;
224
+ }
225
+ }
174
226
  #endif
175
227
 
176
228
  private static string Bound(string value) => value == null || value.Length <= 1024 ? value : value.Substring(0, 1024);