@playdrop/playdrop-cli 0.16.2 → 0.16.11

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 (383) hide show
  1. package/bin/playdrop +1 -2
  2. package/config/client-meta.json +4 -4
  3. package/dist/apiClient.d.ts +2 -2
  4. package/dist/apiClient.js +16 -20
  5. package/dist/appUrls.d.ts +2 -2
  6. package/dist/appUrls.js +31 -33
  7. package/dist/apps/build.d.ts +2 -2
  8. package/dist/apps/build.js +83 -83
  9. package/dist/apps/index.d.ts +7 -7
  10. package/dist/apps/index.js +8 -8
  11. package/dist/apps/loadCheck.d.ts +28 -16
  12. package/dist/apps/loadCheck.js +411 -279
  13. package/dist/apps/registration.d.ts +3 -3
  14. package/dist/apps/registration.js +1 -1
  15. package/dist/apps/staticHtml.d.ts +3 -3
  16. package/dist/apps/staticHtml.js +90 -91
  17. package/dist/apps/surfaceProfiles.d.ts +2 -2
  18. package/dist/apps/surfaceProfiles.js +6 -6
  19. package/dist/apps/upload.d.ts +5 -5
  20. package/dist/apps/upload.js +75 -81
  21. package/dist/apps/validate.d.ts +2 -2
  22. package/dist/apps/validate.js +18 -22
  23. package/dist/assetSpecs.d.ts +2 -2
  24. package/dist/assetSpecs.js +35 -35
  25. package/dist/assets/model-artifacts.d.ts +1 -1
  26. package/dist/assets/model-artifacts.js +81 -77
  27. package/dist/assets/model-worker.d.ts +1 -1
  28. package/dist/assets/model-worker.js +6 -6
  29. package/dist/browser.js +9 -9
  30. package/dist/catalogue-utils.d.ts +1 -1
  31. package/dist/catalogue-utils.js +26 -26
  32. package/dist/catalogue.d.ts +21 -16
  33. package/dist/catalogue.js +509 -413
  34. package/dist/clientInfo.d.ts +1 -1
  35. package/dist/clientInfo.js +28 -28
  36. package/dist/commandContext.d.ts +5 -5
  37. package/dist/commandContext.js +19 -23
  38. package/dist/commands/accounts.js +6 -6
  39. package/dist/commands/ads.js +16 -16
  40. package/dist/commands/agents.d.ts +2 -2
  41. package/dist/commands/agents.js +131 -127
  42. package/dist/commands/boosts.js +41 -31
  43. package/dist/commands/browse.d.ts +2 -2
  44. package/dist/commands/browse.js +168 -128
  45. package/dist/commands/build.js +9 -9
  46. package/dist/commands/captureListing.d.ts +8 -8
  47. package/dist/commands/captureListing.js +292 -332
  48. package/dist/commands/changelog.js +1 -1
  49. package/dist/commands/chat.js +51 -53
  50. package/dist/commands/check.d.ts +1 -1
  51. package/dist/commands/check.js +44 -44
  52. package/dist/commands/clients.d.ts +3 -3
  53. package/dist/commands/clients.js +67 -64
  54. package/dist/commands/comments.js +36 -29
  55. package/dist/commands/create.js +305 -312
  56. package/dist/commands/createRemixContent.d.ts +1 -1
  57. package/dist/commands/createRemixContent.js +65 -60
  58. package/dist/commands/creations.js +205 -135
  59. package/dist/commands/credits.js +37 -27
  60. package/dist/commands/detail.js +40 -34
  61. package/dist/commands/dev.js +103 -126
  62. package/dist/commands/devBrowser.js +74 -65
  63. package/dist/commands/devRuntimeAssets.d.ts +4 -4
  64. package/dist/commands/devRuntimeAssets.js +38 -40
  65. package/dist/commands/devServer.d.ts +4 -4
  66. package/dist/commands/devServer.js +183 -174
  67. package/dist/commands/devShared.d.ts +3 -3
  68. package/dist/commands/devShared.js +31 -31
  69. package/dist/commands/doctor.d.ts +5 -5
  70. package/dist/commands/doctor.js +63 -57
  71. package/dist/commands/documentation.js +42 -32
  72. package/dist/commands/feedback.js +59 -42
  73. package/dist/commands/format.js +9 -9
  74. package/dist/commands/generation.d.ts +2 -2
  75. package/dist/commands/generation.js +344 -326
  76. package/dist/commands/init.d.ts +4 -4
  77. package/dist/commands/init.js +64 -61
  78. package/dist/commands/login.d.ts +2 -2
  79. package/dist/commands/login.js +42 -62
  80. package/dist/commands/logout.js +4 -4
  81. package/dist/commands/marketing.d.ts +8 -8
  82. package/dist/commands/marketing.js +246 -297
  83. package/dist/commands/notes.js +19 -19
  84. package/dist/commands/notifications.js +34 -22
  85. package/dist/commands/npmProcess.js +3 -3
  86. package/dist/commands/ownedRuntimeImageValidation.d.ts +1 -1
  87. package/dist/commands/ownedRuntimeImageValidation.js +19 -17
  88. package/dist/commands/play.js +12 -21
  89. package/dist/commands/publicPages.js +8 -4
  90. package/dist/commands/review.js +123 -110
  91. package/dist/commands/search.js +107 -88
  92. package/dist/commands/tags.js +10 -12
  93. package/dist/commands/taskCaptureSession.d.ts +3 -3
  94. package/dist/commands/taskCaptureSession.js +3 -3
  95. package/dist/commands/tweaks.js +25 -23
  96. package/dist/commands/upgrade.js +40 -31
  97. package/dist/commands/upload-content.d.ts +5 -5
  98. package/dist/commands/upload-content.js +130 -122
  99. package/dist/commands/upload-graph.d.ts +1 -1
  100. package/dist/commands/upload-graph.js +7 -11
  101. package/dist/commands/upload.d.ts +7 -7
  102. package/dist/commands/upload.js +196 -217
  103. package/dist/commands/validate.js +33 -32
  104. package/dist/commands/versionsBrowse.js +61 -51
  105. package/dist/commands/whoami.js +11 -14
  106. package/dist/commands/worker/archive-staging.d.ts +9 -0
  107. package/dist/commands/worker/archive-staging.js +257 -0
  108. package/dist/commands/worker/instrument-evidence.d.ts +1 -1
  109. package/dist/commands/worker/instrument-evidence.js +41 -43
  110. package/dist/commands/worker/provider-telemetry.d.ts +2 -1
  111. package/dist/commands/worker/provider-telemetry.js +181 -64
  112. package/dist/commands/worker/runtime.d.ts +18 -3
  113. package/dist/commands/worker/runtime.js +291 -239
  114. package/dist/commands/worker.d.ts +83 -22
  115. package/dist/commands/worker.js +1980 -1411
  116. package/dist/commands/workspaces.d.ts +2 -2
  117. package/dist/commands/workspaces.js +57 -57
  118. package/dist/config.js +23 -20
  119. package/dist/devAuth.d.ts +3 -3
  120. package/dist/devAuth.js +20 -17
  121. package/dist/environment.d.ts +1 -1
  122. package/dist/environment.js +34 -34
  123. package/dist/errors.d.ts +1 -1
  124. package/dist/errors.js +7 -7
  125. package/dist/externalAssetPackValidation.d.ts +1 -1
  126. package/dist/externalAssetPackValidation.js +9 -9
  127. package/dist/http.d.ts +1 -1
  128. package/dist/http.js +9 -9
  129. package/dist/index.js +657 -664
  130. package/dist/listingCaptureLease.js +23 -23
  131. package/dist/listingPreflight.d.ts +2 -2
  132. package/dist/listingPreflight.js +33 -33
  133. package/dist/messages.js +6 -14
  134. package/dist/output.js +9 -9
  135. package/dist/playwright.d.ts +1 -1
  136. package/dist/playwright.js +35 -40
  137. package/dist/proxyFetch.js +30 -27
  138. package/dist/recorderRelease.js +8 -8
  139. package/dist/refs.d.ts +5 -5
  140. package/dist/refs.js +40 -37
  141. package/dist/sessionCookie.js +15 -21
  142. package/dist/shellProbe.js +5 -5
  143. package/dist/taskSelection.d.ts +3 -3
  144. package/dist/taskSelection.js +67 -62
  145. package/dist/taskUtils.d.ts +1 -1
  146. package/dist/taskUtils.js +4 -4
  147. package/dist/tweakStaleness.d.ts +3 -3
  148. package/dist/tweakStaleness.js +8 -8
  149. package/dist/uploadLog.d.ts +3 -3
  150. package/dist/uploadLog.js +51 -51
  151. package/dist/versionCompare.js +3 -3
  152. package/dist/workerAppProject.d.ts +1 -1
  153. package/dist/workerAppProject.js +10 -17
  154. package/dist/workspaceAuth.d.ts +1 -1
  155. package/dist/workspaceAuth.js +28 -22
  156. package/node_modules/@playdrop/ai-client/dist/index.d.ts +35 -35
  157. package/node_modules/@playdrop/ai-client/dist/index.d.ts.map +1 -1
  158. package/node_modules/@playdrop/api-client/dist/client.d.ts +34 -33
  159. package/node_modules/@playdrop/api-client/dist/client.d.ts.map +1 -1
  160. package/node_modules/@playdrop/api-client/dist/core/errors.d.ts +1 -1
  161. package/node_modules/@playdrop/api-client/dist/core/errors.d.ts.map +1 -1
  162. package/node_modules/@playdrop/api-client/dist/core/request.d.ts +2 -2
  163. package/node_modules/@playdrop/api-client/dist/core/request.d.ts.map +1 -1
  164. package/node_modules/@playdrop/api-client/dist/domains/admin.d.ts +2 -2
  165. package/node_modules/@playdrop/api-client/dist/domains/admin.d.ts.map +1 -1
  166. package/node_modules/@playdrop/api-client/dist/domains/admin.js +4 -1
  167. package/node_modules/@playdrop/api-client/dist/domains/agent-tasks.d.ts +4 -3
  168. package/node_modules/@playdrop/api-client/dist/domains/agent-tasks.d.ts.map +1 -1
  169. package/node_modules/@playdrop/api-client/dist/domains/agent-tasks.js +12 -0
  170. package/node_modules/@playdrop/api-client/dist/domains/ai.d.ts +2 -2
  171. package/node_modules/@playdrop/api-client/dist/domains/ai.d.ts.map +1 -1
  172. package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts +4 -4
  173. package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts.map +1 -1
  174. package/node_modules/@playdrop/api-client/dist/domains/asset-packs.d.ts +4 -4
  175. package/node_modules/@playdrop/api-client/dist/domains/asset-packs.d.ts.map +1 -1
  176. package/node_modules/@playdrop/api-client/dist/domains/assets.d.ts +4 -4
  177. package/node_modules/@playdrop/api-client/dist/domains/assets.d.ts.map +1 -1
  178. package/node_modules/@playdrop/api-client/dist/domains/auth.d.ts +2 -2
  179. package/node_modules/@playdrop/api-client/dist/domains/auth.d.ts.map +1 -1
  180. package/node_modules/@playdrop/api-client/dist/domains/chat.d.ts +2 -2
  181. package/node_modules/@playdrop/api-client/dist/domains/chat.d.ts.map +1 -1
  182. package/node_modules/@playdrop/api-client/dist/domains/comments.d.ts +3 -3
  183. package/node_modules/@playdrop/api-client/dist/domains/comments.d.ts.map +1 -1
  184. package/node_modules/@playdrop/api-client/dist/domains/free-credits.d.ts +2 -2
  185. package/node_modules/@playdrop/api-client/dist/domains/free-credits.d.ts.map +1 -1
  186. package/node_modules/@playdrop/api-client/dist/domains/me.d.ts +2 -2
  187. package/node_modules/@playdrop/api-client/dist/domains/me.d.ts.map +1 -1
  188. package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts +2 -2
  189. package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts.map +1 -1
  190. package/node_modules/@playdrop/api-client/dist/domains/player-meta.d.ts +2 -2
  191. package/node_modules/@playdrop/api-client/dist/domains/player-meta.d.ts.map +1 -1
  192. package/node_modules/@playdrop/api-client/dist/domains/playtrade.d.ts +3 -3
  193. package/node_modules/@playdrop/api-client/dist/domains/playtrade.d.ts.map +1 -1
  194. package/node_modules/@playdrop/api-client/dist/domains/playtrade.js +2 -2
  195. package/node_modules/@playdrop/api-client/dist/domains/search.d.ts +2 -2
  196. package/node_modules/@playdrop/api-client/dist/domains/search.d.ts.map +1 -1
  197. package/node_modules/@playdrop/api-client/dist/domains/tags.d.ts +2 -2
  198. package/node_modules/@playdrop/api-client/dist/domains/tags.d.ts.map +1 -1
  199. package/node_modules/@playdrop/api-client/dist/index.d.ts +19 -18
  200. package/node_modules/@playdrop/api-client/dist/index.d.ts.map +1 -1
  201. package/node_modules/@playdrop/api-client/dist/index.js +7 -2
  202. package/node_modules/@playdrop/boxel-core/dist/src/entity-cleaner.d.ts +1 -1
  203. package/node_modules/@playdrop/boxel-core/dist/src/entity-cleaner.js +47 -47
  204. package/node_modules/@playdrop/boxel-core/dist/src/entity-cleaner.js.map +1 -1
  205. package/node_modules/@playdrop/boxel-core/dist/src/entity-utils.d.ts +3 -3
  206. package/node_modules/@playdrop/boxel-core/dist/src/entity-utils.js +32 -47
  207. package/node_modules/@playdrop/boxel-core/dist/src/entity-utils.js.map +1 -1
  208. package/node_modules/@playdrop/boxel-core/dist/src/format-utils.js +16 -18
  209. package/node_modules/@playdrop/boxel-core/dist/src/format-utils.js.map +1 -1
  210. package/node_modules/@playdrop/boxel-core/dist/src/humanoid/geometry.d.ts +2 -2
  211. package/node_modules/@playdrop/boxel-core/dist/src/humanoid/index.d.ts +4 -4
  212. package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/common.d.ts +1 -1
  213. package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/index.d.ts +3 -3
  214. package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/textured-builder.d.ts +2 -2
  215. package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/textured-builder.js +44 -45
  216. package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/textured-builder.js.map +1 -1
  217. package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/types.d.ts +1 -1
  218. package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/voxel-builder.d.ts +2 -2
  219. package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/voxel-builder.js +40 -44
  220. package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/voxel-builder.js.map +1 -1
  221. package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/builder.d.ts +2 -2
  222. package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/builder.js +30 -34
  223. package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/builder.js.map +1 -1
  224. package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/index.d.ts +4 -4
  225. package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/index.js.map +1 -1
  226. package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/scanner.d.ts +3 -3
  227. package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/scanner.js +23 -20
  228. package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/scanner.js.map +1 -1
  229. package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/textures/artifacts.d.ts +3 -3
  230. package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/textures/artifacts.js +3 -3
  231. package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/textures/face-map.d.ts +1 -1
  232. package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/textures/face-map.js +21 -21
  233. package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/textures/face-map.js.map +1 -1
  234. package/node_modules/@playdrop/boxel-core/dist/src/humanoid/templates/index.d.ts +1 -1
  235. package/node_modules/@playdrop/boxel-core/dist/src/humanoid/templates/index.js +2 -2
  236. package/node_modules/@playdrop/boxel-core/dist/src/index.d.ts +19 -19
  237. package/node_modules/@playdrop/boxel-core/dist/src/materials.d.ts +1 -1
  238. package/node_modules/@playdrop/boxel-core/dist/src/materials.js +12 -16
  239. package/node_modules/@playdrop/boxel-core/dist/src/materials.js.map +1 -1
  240. package/node_modules/@playdrop/boxel-core/dist/src/palette_tools.d.ts +1 -1
  241. package/node_modules/@playdrop/boxel-core/dist/src/palette_tools.js +38 -41
  242. package/node_modules/@playdrop/boxel-core/dist/src/palette_tools.js.map +1 -1
  243. package/node_modules/@playdrop/boxel-core/dist/src/serialization.d.ts +1 -1
  244. package/node_modules/@playdrop/boxel-core/dist/src/serialization.js +54 -56
  245. package/node_modules/@playdrop/boxel-core/dist/src/serialization.js.map +1 -1
  246. package/node_modules/@playdrop/boxel-core/dist/src/textures.d.ts +3 -3
  247. package/node_modules/@playdrop/boxel-core/dist/src/textures.js +39 -45
  248. package/node_modules/@playdrop/boxel-core/dist/src/textures.js.map +1 -1
  249. package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/infer-face.d.ts +2 -2
  250. package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/infer-face.js +39 -43
  251. package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/infer-face.js.map +1 -1
  252. package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/layer-mode.d.ts +3 -3
  253. package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/layer-mode.js +35 -35
  254. package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/layer-mode.js.map +1 -1
  255. package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/merge-overlay.d.ts +1 -1
  256. package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/merge-overlay.js +18 -18
  257. package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/merge-overlay.js.map +1 -1
  258. package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/slice.d.ts +3 -3
  259. package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/slice.js +50 -48
  260. package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/slice.js.map +1 -1
  261. package/node_modules/@playdrop/boxel-core/dist/src/transforms/upscale.d.ts +1 -1
  262. package/node_modules/@playdrop/boxel-core/dist/src/transforms/upscale.js +22 -26
  263. package/node_modules/@playdrop/boxel-core/dist/src/transforms/upscale.js.map +1 -1
  264. package/node_modules/@playdrop/boxel-core/dist/src/transforms/voxels/slice.d.ts +2 -2
  265. package/node_modules/@playdrop/boxel-core/dist/src/transforms/voxels/slice.js +8 -8
  266. package/node_modules/@playdrop/boxel-core/dist/src/transforms/voxels/slice.js.map +1 -1
  267. package/node_modules/@playdrop/boxel-core/dist/src/transforms/voxels/textured-to-voxel.d.ts +2 -2
  268. package/node_modules/@playdrop/boxel-core/dist/src/transforms/voxels/textured-to-voxel.js +23 -28
  269. package/node_modules/@playdrop/boxel-core/dist/src/transforms/voxels/textured-to-voxel.js.map +1 -1
  270. package/node_modules/@playdrop/boxel-core/dist/src/types.d.ts +8 -8
  271. package/node_modules/@playdrop/boxel-core/dist/src/validation.d.ts +1 -1
  272. package/node_modules/@playdrop/boxel-core/dist/src/validation.js +93 -95
  273. package/node_modules/@playdrop/boxel-core/dist/src/validation.js.map +1 -1
  274. package/node_modules/@playdrop/boxel-core/dist/src/voxels/greedy-mesher.d.ts +1 -1
  275. package/node_modules/@playdrop/boxel-core/dist/src/voxels/greedy-mesher.js +2 -2
  276. package/node_modules/@playdrop/boxel-core/dist/src/voxels/greedy-mesher.js.map +1 -1
  277. package/node_modules/@playdrop/boxel-core/dist/src/voxels.d.ts +1 -1
  278. package/node_modules/@playdrop/boxel-core/dist/src/voxels.js +2 -7
  279. package/node_modules/@playdrop/boxel-core/dist/src/voxels.js.map +1 -1
  280. package/node_modules/@playdrop/boxel-three/dist/src/animations.d.ts +3 -3
  281. package/node_modules/@playdrop/boxel-three/dist/src/builders.d.ts +4 -4
  282. package/node_modules/@playdrop/boxel-three/dist/src/context.d.ts +3 -3
  283. package/node_modules/@playdrop/boxel-three/dist/src/exporters/glb.d.ts +6 -6
  284. package/node_modules/@playdrop/boxel-three/dist/src/exporters/glb.js +9 -1
  285. package/node_modules/@playdrop/boxel-three/dist/src/exporters/image.d.ts +3 -3
  286. package/node_modules/@playdrop/boxel-three/dist/src/index.d.ts +17 -17
  287. package/node_modules/@playdrop/boxel-three/dist/src/instantiate.d.ts +9 -9
  288. package/node_modules/@playdrop/boxel-three/dist/src/nodes.d.ts +3 -3
  289. package/node_modules/@playdrop/boxel-three/dist/src/nodes.js +1 -1
  290. package/node_modules/@playdrop/boxel-three/dist/src/overlays.d.ts +2 -2
  291. package/node_modules/@playdrop/boxel-three/dist/src/primitives.d.ts +1 -1
  292. package/node_modules/@playdrop/boxel-three/dist/src/scene.d.ts +1 -1
  293. package/node_modules/@playdrop/boxel-three/dist/src/skinned-mesh.d.ts +2 -2
  294. package/node_modules/@playdrop/boxel-three/dist/src/texture-atlas.d.ts +1 -1
  295. package/node_modules/@playdrop/boxel-three/dist/src/textures.d.ts +3 -3
  296. package/node_modules/@playdrop/boxel-three/dist/src/types.d.ts +4 -4
  297. package/node_modules/@playdrop/boxel-three/dist/src/voxels/faces.d.ts +2 -2
  298. package/node_modules/@playdrop/boxel-three/dist/src/voxels/mesher.d.ts +1 -1
  299. package/node_modules/@playdrop/boxel-three/dist/test/fixtures/render-worker.d.ts +1 -1
  300. package/node_modules/@playdrop/config/client-meta.json +4 -4
  301. package/node_modules/@playdrop/config/dist/src/client-meta.d.ts +1 -1
  302. package/node_modules/@playdrop/config/dist/src/client-meta.d.ts.map +1 -1
  303. package/node_modules/@playdrop/config/dist/src/client-meta.js +10 -10
  304. package/node_modules/@playdrop/config/dist/src/constants.js +10 -10
  305. package/node_modules/@playdrop/config/dist/src/creator-plans.d.ts +1 -1
  306. package/node_modules/@playdrop/config/dist/src/creator-plans.d.ts.map +1 -1
  307. package/node_modules/@playdrop/config/dist/src/creator-plans.js +27 -28
  308. package/node_modules/@playdrop/config/dist/src/deployment.d.ts +4 -4
  309. package/node_modules/@playdrop/config/dist/src/deployment.d.ts.map +1 -1
  310. package/node_modules/@playdrop/config/dist/src/deployment.js +42 -41
  311. package/node_modules/@playdrop/config/dist/src/game-arena.js +12 -12
  312. package/node_modules/@playdrop/config/dist/src/index.d.ts +9 -9
  313. package/node_modules/@playdrop/config/dist/src/index.d.ts.map +1 -1
  314. package/node_modules/@playdrop/config/dist/src/index.js +24 -25
  315. package/node_modules/@playdrop/config/dist/src/public-deployment.d.ts +4 -4
  316. package/node_modules/@playdrop/config/dist/src/public-deployment.d.ts.map +1 -1
  317. package/node_modules/@playdrop/config/dist/src/public-deployment.js +41 -39
  318. package/node_modules/@playdrop/config/dist/src/runtime-client-meta.js +1 -3
  319. package/node_modules/@playdrop/config/dist/src/runtime-service-origins.d.ts.map +1 -1
  320. package/node_modules/@playdrop/config/dist/src/runtime-service-origins.js +23 -25
  321. package/node_modules/@playdrop/config/dist/src/server/fastify.d.ts +5 -5
  322. package/node_modules/@playdrop/config/dist/src/server/fastify.d.ts.map +1 -1
  323. package/node_modules/@playdrop/config/dist/src/server/fastify.js +44 -46
  324. package/node_modules/@playdrop/config/dist/src/server/logging.d.ts +1 -1
  325. package/node_modules/@playdrop/config/dist/src/server/logging.d.ts.map +1 -1
  326. package/node_modules/@playdrop/config/dist/src/server/logging.js +6 -8
  327. package/node_modules/@playdrop/config/dist/src/types.d.ts +2 -2
  328. package/node_modules/@playdrop/config/dist/test/creator-plans.test.js +12 -12
  329. package/node_modules/@playdrop/config/dist/test/deployment.test.js +59 -59
  330. package/node_modules/@playdrop/config/dist/test/fastify.test.js +4 -4
  331. package/node_modules/@playdrop/config/dist/test/game-arena.test.js +8 -8
  332. package/node_modules/@playdrop/config/dist/test/logging.test.js +6 -6
  333. package/node_modules/@playdrop/config/dist/test/validateClientEnvironment.test.js +57 -57
  334. package/node_modules/@playdrop/config/dist/tsconfig.tsbuildinfo +1 -1
  335. package/node_modules/@playdrop/types/dist/agent-task-playtest.d.ts.map +1 -1
  336. package/node_modules/@playdrop/types/dist/agent-task-terminal.d.ts +2 -2
  337. package/node_modules/@playdrop/types/dist/agent-task-terminal.d.ts.map +1 -1
  338. package/node_modules/@playdrop/types/dist/api.d.ts +314 -250
  339. package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
  340. package/node_modules/@playdrop/types/dist/api.js +40 -59
  341. package/node_modules/@playdrop/types/dist/app-capability-filters.d.ts +2 -2
  342. package/node_modules/@playdrop/types/dist/app-playtest-tape.d.ts +6 -6
  343. package/node_modules/@playdrop/types/dist/app-playtest-tape.d.ts.map +1 -1
  344. package/node_modules/@playdrop/types/dist/app-review.d.ts +2 -2
  345. package/node_modules/@playdrop/types/dist/app-review.d.ts.map +1 -1
  346. package/node_modules/@playdrop/types/dist/app-review.js +1 -6
  347. package/node_modules/@playdrop/types/dist/app.d.ts.map +1 -1
  348. package/node_modules/@playdrop/types/dist/asset-pack.d.ts +5 -5
  349. package/node_modules/@playdrop/types/dist/asset-spec.d.ts +4 -4
  350. package/node_modules/@playdrop/types/dist/asset.d.ts +11 -11
  351. package/node_modules/@playdrop/types/dist/attachment-policy.d.ts.map +1 -1
  352. package/node_modules/@playdrop/types/dist/chat.d.ts +3 -3
  353. package/node_modules/@playdrop/types/dist/creator-public-image.d.ts.map +1 -1
  354. package/node_modules/@playdrop/types/dist/graph.d.ts +4 -4
  355. package/node_modules/@playdrop/types/dist/index.d.ts +25 -25
  356. package/node_modules/@playdrop/types/dist/instrument-evidence.d.ts +2 -2
  357. package/node_modules/@playdrop/types/dist/instrument-evidence.d.ts.map +1 -1
  358. package/node_modules/@playdrop/types/dist/instrument-evidence.js +2 -11
  359. package/node_modules/@playdrop/types/dist/locale.d.ts +2 -2
  360. package/node_modules/@playdrop/types/dist/locale.d.ts.map +1 -1
  361. package/node_modules/@playdrop/types/dist/locale.js +2 -17
  362. package/node_modules/@playdrop/types/dist/owned-assets.d.ts +2 -2
  363. package/node_modules/@playdrop/types/dist/owned-assets.d.ts.map +1 -1
  364. package/node_modules/@playdrop/types/dist/player-meta.d.ts +3 -3
  365. package/node_modules/@playdrop/types/dist/player-meta.d.ts.map +1 -1
  366. package/node_modules/@playdrop/types/dist/playtest-notes.d.ts +11 -11
  367. package/node_modules/@playdrop/types/dist/playtest-notes.d.ts.map +1 -1
  368. package/node_modules/@playdrop/types/dist/playtrade.d.ts +8 -5
  369. package/node_modules/@playdrop/types/dist/playtrade.d.ts.map +1 -1
  370. package/node_modules/@playdrop/types/dist/realtime.d.ts +1 -1
  371. package/node_modules/@playdrop/types/dist/tweaks.d.ts +4 -4
  372. package/node_modules/@playdrop/types/dist/tweaks.d.ts.map +1 -1
  373. package/node_modules/@playdrop/types/dist/tweaks.js +12 -2
  374. package/node_modules/@playdrop/types/dist/version.d.ts +14 -14
  375. package/node_modules/@playdrop/types/dist/version.d.ts.map +1 -1
  376. package/node_modules/@playdrop/types/dist/version.js +4 -1
  377. package/node_modules/@playdrop/vox-three/dist/src/index.d.ts +1 -1
  378. package/node_modules/@playdrop/vox-three/dist/src/vox.d.ts +2 -2
  379. package/node_modules/@playdrop/vox-three/dist/src/vox.js +20 -18
  380. package/node_modules/@playdrop/vox-three/dist/src/vox.js.map +1 -1
  381. package/node_modules/@playdrop/vox-three/dist/test/vox.test.js +11 -14
  382. package/node_modules/@playdrop/vox-three/dist/test/vox.test.js.map +1 -1
  383. package/package.json +1 -1
@@ -6,14 +6,21 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.PLAYDROP_SURFACE_CONTEXT_OPTIONS = void 0;
7
7
  exports.formatConsoleValue = formatConsoleValue;
8
8
  exports.formatBrowserConsoleMessage = formatBrowserConsoleMessage;
9
+ exports.isDuplicateBrowserResourceConsoleError = isDuplicateBrowserResourceConsoleError;
10
+ exports.isExpectedAnonymousSessionProbe = isExpectedAnonymousSessionProbe;
11
+ exports.waitForHostedLaunchState = waitForHostedLaunchState;
9
12
  exports.resolveLoadCheckSurface = resolveLoadCheckSurface;
10
13
  exports.cloneLoadCheckContextOptions = cloneLoadCheckContextOptions;
11
14
  exports.isSoftwareWebGlRenderer = isSoftwareWebGlRenderer;
12
15
  exports.dispatchPlaytestTapeToFrame = dispatchPlaytestTapeToFrame;
16
+ exports.findGameFrame = findGameFrame;
17
+ exports.describeHostedLaunchTimeout = describeHostedLaunchTimeout;
13
18
  exports.verifyHostedEditorPhaseRoundTrip = verifyHostedEditorPhaseRoundTrip;
14
19
  exports.formatHostedLoadCheckFailure = formatHostedLoadCheckFailure;
15
20
  exports.redactHostedLoadCheckSecrets = redactHostedLoadCheckSecrets;
16
21
  exports.runLocalHostedLoadCheck = runLocalHostedLoadCheck;
22
+ exports.buildUploadedHostedLoadCheckUrl = buildUploadedHostedLoadCheckUrl;
23
+ exports.buildUploadedHostedLoadCheckScreenshotPath = buildUploadedHostedLoadCheckScreenshotPath;
17
24
  exports.runUploadedHostedLoadCheck = runUploadedHostedLoadCheck;
18
25
  exports.comparePlaytestFrames = comparePlaytestFrames;
19
26
  exports.runUploadedHostedPlaytestSmokeCheck = runUploadedHostedPlaytestSmokeCheck;
@@ -37,15 +44,16 @@ var surfaceProfiles_2 = require("./surfaceProfiles");
37
44
  Object.defineProperty(exports, "PLAYDROP_SURFACE_CONTEXT_OPTIONS", { enumerable: true, get: function () { return surfaceProfiles_2.PLAYDROP_SURFACE_CONTEXT_OPTIONS; } });
38
45
  const FRAME_SELECTOR = 'iframe[title="Game"]';
39
46
  const DEFAULT_HOSTED_LOAD_TIMEOUT_MS = 15000;
47
+ const HOSTED_LAUNCH_DIAGNOSTIC_TIMEOUT_MS = 1000;
40
48
  const PLAYTEST_INTERACTIVE_MEAN_DELTA_MIN = 0.002;
41
49
  const PLAYTEST_INTERACTIVE_CHANGED_PIXEL_RATIO_MIN = 0.01;
42
- const LOAD_CHECK_SURFACE_ORDER = ['DESKTOP', 'MOBILE_LANDSCAPE', 'MOBILE_PORTRAIT'];
50
+ const LOAD_CHECK_SURFACE_ORDER = ["DESKTOP", "MOBILE_LANDSCAPE", "MOBILE_PORTRAIT"];
43
51
  const GOOGLE_TELEMETRY_HOSTS = [
44
- 'analytics.google.com',
45
- 'googletagmanager.com',
46
- 'google-analytics.com',
47
- 'googleadservices.com',
48
- 'doubleclick.net',
52
+ "analytics.google.com",
53
+ "googletagmanager.com",
54
+ "google-analytics.com",
55
+ "googleadservices.com",
56
+ "doubleclick.net",
49
57
  ];
50
58
  const SOFTWARE_WEBGL_RENDERER_PATTERNS = [
51
59
  /\bswiftshader\b/i,
@@ -68,7 +76,7 @@ function isKnownGoogleTelemetryUrl(rawUrl) {
68
76
  return false;
69
77
  }
70
78
  const hostname = parsed.hostname.toLowerCase();
71
- if (hostname === 'accounts.google.com' || hostname.endsWith('.accounts.google.com')) {
79
+ if (hostname === "accounts.google.com" || hostname.endsWith(".accounts.google.com")) {
72
80
  return false;
73
81
  }
74
82
  return GOOGLE_TELEMETRY_HOSTS.some((domain) => isHostOrSubdomain(hostname, domain));
@@ -77,24 +85,24 @@ function formatConsoleValue(value) {
77
85
  if (value instanceof Error) {
78
86
  return `${value.name}: ${value.message}`;
79
87
  }
80
- if (typeof value === 'string')
88
+ if (typeof value === "string")
81
89
  return value;
82
- if (typeof value === 'number' || typeof value === 'boolean' || value === null) {
90
+ if (typeof value === "number" || typeof value === "boolean" || value === null) {
83
91
  return String(value);
84
92
  }
85
- if (typeof value === 'undefined') {
86
- return 'undefined';
93
+ if (typeof value === "undefined") {
94
+ return "undefined";
87
95
  }
88
- if (typeof value === 'object') {
96
+ if (typeof value === "object") {
89
97
  const namedError = value;
90
- if (typeof namedError.message === 'string') {
91
- return `${typeof namedError.name === 'string' ? `${namedError.name}: ` : ''}${namedError.message}`;
98
+ if (typeof namedError.message === "string") {
99
+ return `${typeof namedError.name === "string" ? `${namedError.name}: ` : ""}${namedError.message}`;
92
100
  }
93
101
  try {
94
102
  return JSON.stringify(value);
95
103
  }
96
104
  catch {
97
- return '[object]';
105
+ return "[object]";
98
106
  }
99
107
  }
100
108
  return String(value);
@@ -103,14 +111,29 @@ function formatBrowserConsoleMessage(values, fallbackText) {
103
111
  if (values.length === 0) {
104
112
  return fallbackText;
105
113
  }
106
- const rendered = values.map(formatConsoleValue).join(' ');
107
- return rendered === '{}' || rendered === '[object]' ? fallbackText : rendered;
114
+ const rendered = values.map(formatConsoleValue).join(" ");
115
+ return rendered === "{}" || rendered === "[object]" ? fallbackText : rendered;
116
+ }
117
+ function isDuplicateBrowserResourceConsoleError(type, message) {
118
+ return (type === "error" &&
119
+ /^Failed to load resource: the server responded with a status of \d{3}(?: \([^)]*\))?$/i.test(message.trim()));
120
+ }
121
+ function isExpectedAnonymousSessionProbe(input) {
122
+ if (input.hasExplicitToken || input.status !== 401 || input.method.toUpperCase() !== "GET") {
123
+ return false;
124
+ }
125
+ try {
126
+ return new URL(input.url).pathname === "/me";
127
+ }
128
+ catch {
129
+ return false;
130
+ }
108
131
  }
109
132
  function serializePayload(payload) {
110
133
  if (payload === undefined) {
111
- return '';
134
+ return "";
112
135
  }
113
- if (typeof payload === 'string') {
136
+ if (typeof payload === "string") {
114
137
  return payload;
115
138
  }
116
139
  try {
@@ -121,75 +144,74 @@ function serializePayload(payload) {
121
144
  }
122
145
  }
123
146
  function normalizeHostedLaunchStatePayload(payload) {
124
- if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
147
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
125
148
  return null;
126
149
  }
127
- const state = typeof payload.state === 'string'
150
+ const state = typeof payload.state === "string"
128
151
  ? payload.state.trim().toLowerCase()
129
- : '';
130
- if (state === 'ready') {
131
- return { state: 'ready' };
152
+ : "";
153
+ if (state === "ready") {
154
+ return { state: "ready" };
132
155
  }
133
- if (state === 'login_required') {
134
- return { state: 'login_required' };
156
+ if (state === "login_required") {
157
+ return { state: "login_required" };
135
158
  }
136
- if (state === 'controller_required') {
137
- return { state: 'controller_required' };
159
+ if (state === "controller_required") {
160
+ return { state: "controller_required" };
138
161
  }
139
- if (state === 'surface_unsupported') {
140
- return { state: 'surface_unsupported' };
162
+ if (state === "surface_unsupported") {
163
+ return { state: "surface_unsupported" };
141
164
  }
142
- if (state !== 'error') {
165
+ if (state !== "error") {
143
166
  return null;
144
167
  }
145
- const errorCode = typeof payload.errorCode === 'string'
168
+ const errorCode = typeof payload.errorCode === "string"
146
169
  ? payload.errorCode.trim() || null
147
170
  : null;
148
- const message = typeof payload.message === 'string'
171
+ const message = typeof payload.message === "string"
149
172
  ? payload.message.trim() || null
150
173
  : null;
151
174
  return {
152
- state: 'error',
175
+ state: "error",
153
176
  errorCode,
154
177
  message,
155
178
  };
156
179
  }
157
180
  function formatHostedLaunchError(state) {
158
- const parts = ['Hosted app failed to launch.'];
181
+ const parts = ["Hosted app failed to launch."];
159
182
  if (state.errorCode) {
160
183
  parts.push(`errorCode=${state.errorCode}.`);
161
184
  }
162
185
  if (state.message) {
163
186
  parts.push(state.message);
164
187
  }
165
- return parts.join(' ');
166
- }
167
- function formatHostedLaunchStateMismatch(actualState, expectedState) {
168
- if (expectedState === 'login_required') {
169
- return `Hosted app reported ${actualState} instead of login_required.`;
170
- }
171
- if (expectedState === 'controller_required') {
172
- return `Hosted app reported ${actualState} instead of controller_required.`;
173
- }
174
- if (expectedState === 'surface_unsupported') {
175
- return `Hosted app reported ${actualState} instead of surface_unsupported.`;
176
- }
177
- return `Hosted app reported ${actualState} instead of ready.`;
188
+ return parts.join(" ");
178
189
  }
179
190
  function shouldSettleHostedLaunchWaiter(state, expectedState) {
180
- if (state.state === 'error' || !expectedState) {
181
- return true;
182
- }
183
- return state.state === expectedState;
191
+ return state.state === "error" || state.state === expectedState;
192
+ }
193
+ function waitForHostedLaunchState(waiter, expectedState, timeoutMs) {
194
+ return new Promise((resolve, reject) => {
195
+ const timeout = setTimeout(() => {
196
+ reject(new Error(expectedState === "ready"
197
+ ? "hosted_app_missing_ready: instrument_error:game_ready_timeout: Hosted app did not report sdk.host.ready() before the instrument timeout."
198
+ : `hosted_app_gate_missing: Hosted app did not report ${expectedState} before the load-check timeout.`));
199
+ }, timeoutMs);
200
+ waiter.then((state) => {
201
+ clearTimeout(timeout);
202
+ resolve(state);
203
+ }, (error) => {
204
+ clearTimeout(timeout);
205
+ reject(error);
206
+ });
207
+ });
184
208
  }
185
209
  function normalizeLoadCheckSurface(value) {
186
- if (typeof value !== 'string') {
210
+ if (typeof value !== "string") {
187
211
  return null;
188
212
  }
189
213
  const normalized = value.trim().toUpperCase();
190
- return LOAD_CHECK_SURFACE_ORDER.includes(normalized)
191
- ? normalized
192
- : null;
214
+ return LOAD_CHECK_SURFACE_ORDER.includes(normalized) ? normalized : null;
193
215
  }
194
216
  function normalizeLoadCheckSurfaceTargets(value) {
195
217
  if (!Array.isArray(value)) {
@@ -208,31 +230,31 @@ function resolveLoadCheckSurface(surfaceTargets, primarySurface) {
208
230
  return surface;
209
231
  }
210
232
  }
211
- return 'DESKTOP';
233
+ return "DESKTOP";
212
234
  }
213
235
  function cloneLoadCheckContextOptions(surface) {
214
236
  return (0, surfaceProfiles_1.clonePlaydropSurfaceContextOptions)(surface);
215
237
  }
216
238
  function resolvePostAuthHostedLaunchState(controllerMode) {
217
- return controllerMode === 'REQUIRED' ? 'controller_required' : 'ready';
239
+ return controllerMode === "REQUIRED" ? "controller_required" : "ready";
218
240
  }
219
241
  function resolveAnonymousHostedLaunchState(input) {
220
242
  const surfaceTargets = normalizeLoadCheckSurfaceTargets(input.surfaceTargets);
221
243
  if (!surfaceTargets.includes(input.captureSurface)) {
222
- return 'surface_unsupported';
244
+ return "surface_unsupported";
223
245
  }
224
- if (input.authMode === 'REQUIRED') {
225
- return 'login_required';
246
+ if (input.authMode === "REQUIRED") {
247
+ return "login_required";
226
248
  }
227
249
  return resolvePostAuthHostedLaunchState(input.controllerMode);
228
250
  }
229
251
  function normalizeLoadCheckMessage(message) {
230
- const normalized = typeof message === 'string' ? message.trim() : '';
231
- return normalized || 'Hosted app failed to launch.';
252
+ const normalized = typeof message === "string" ? message.trim() : "";
253
+ return normalized || "Hosted app failed to launch.";
232
254
  }
233
255
  function buildPassedLoadCheckResult(input) {
234
256
  return {
235
- status: 'PASSED',
257
+ status: "PASSED",
236
258
  errorCode: null,
237
259
  message: null,
238
260
  checkedAt: new Date().toISOString(),
@@ -246,7 +268,7 @@ function buildPassedLoadCheckResult(input) {
246
268
  }
247
269
  function buildFailedLoadCheckResult(errorCode, message, artifactFingerprint, extra = {}) {
248
270
  return {
249
- status: 'FAILED',
271
+ status: "FAILED",
250
272
  errorCode,
251
273
  message,
252
274
  checkedAt: new Date().toISOString(),
@@ -260,20 +282,20 @@ function buildFailedLoadCheckResult(errorCode, message, artifactFingerprint, ext
260
282
  }
261
283
  function formatLoadCheckErrors(errors) {
262
284
  if (!Array.isArray(errors) || errors.length === 0) {
263
- return '';
285
+ return "";
264
286
  }
265
287
  const normalized = errors
266
- .map((entry) => entry.replace(/\s+/g, ' ').trim())
288
+ .map((entry) => entry.replace(/\s+/g, " ").trim())
267
289
  .filter(Boolean)
268
290
  .slice(0, 3)
269
- .map((entry) => entry.length > 240 ? `${entry.slice(0, 237)}...` : entry);
291
+ .map((entry) => (entry.length > 240 ? `${entry.slice(0, 237)}...` : entry));
270
292
  if (normalized.length === 0) {
271
- return '';
293
+ return "";
272
294
  }
273
- return ` First error${normalized.length === 1 ? '' : 's'}: ${normalized.join(' | ')}`;
295
+ return ` First error${normalized.length === 1 ? "" : "s"}: ${normalized.join(" | ")}`;
274
296
  }
275
297
  function isSoftwareWebGlRenderer(renderer) {
276
- const normalized = typeof renderer === 'string' ? renderer.trim() : '';
298
+ const normalized = typeof renderer === "string" ? renderer.trim() : "";
277
299
  return normalized.length > 0 && SOFTWARE_WEBGL_RENDERER_PATTERNS.some((pattern) => pattern.test(normalized));
278
300
  }
279
301
  async function prepareProjectCheckGameFrame(frame) {
@@ -282,16 +304,18 @@ async function prepareProjectCheckGameFrame(frame) {
282
304
  await element.scrollIntoViewIfNeeded().catch(() => { });
283
305
  const box = await element.boundingBox();
284
306
  if (!box) {
285
- throw new Error('project_check_game_frame_not_visible');
307
+ throw new Error("project_check_game_frame_not_visible");
286
308
  }
287
309
  await element.focus().catch(() => { });
288
- await frame.evaluate(() => {
310
+ await frame
311
+ .evaluate(() => {
289
312
  window.focus();
290
313
  if (document.body) {
291
314
  document.body.tabIndex = document.body.tabIndex >= 0 ? document.body.tabIndex : -1;
292
315
  document.body.focus();
293
316
  }
294
- }).catch(() => { });
317
+ })
318
+ .catch(() => { });
295
319
  return box;
296
320
  }
297
321
  finally {
@@ -300,12 +324,12 @@ async function prepareProjectCheckGameFrame(frame) {
300
324
  }
301
325
  function resolveTapePoint(box, event) {
302
326
  return {
303
- x: box.x + (event.x * box.width),
304
- y: box.y + (event.y * box.height),
327
+ x: box.x + event.x * box.width,
328
+ y: box.y + event.y * box.height,
305
329
  };
306
330
  }
307
331
  async function dispatchTouchPoints(session, type, points) {
308
- await session.send('Input.dispatchTouchEvent', {
332
+ await session.send("Input.dispatchTouchEvent", {
309
333
  type,
310
334
  touchPoints: Array.from(points.entries()).map(([id, point]) => ({
311
335
  id,
@@ -319,7 +343,7 @@ async function dispatchTouchPoints(session, type, points) {
319
343
  }
320
344
  async function dispatchPlaytestTapeToFrame(page, frame, surface, tape, hooks = {}) {
321
345
  const box = await prepareProjectCheckGameFrame(frame);
322
- const mobile = surface !== 'DESKTOP';
346
+ const mobile = surface !== "DESKTOP";
323
347
  const session = mobile ? await page.context().newCDPSession(page) : null;
324
348
  const touchPoints = new Map();
325
349
  const startedAt = Date.now();
@@ -331,71 +355,71 @@ async function dispatchPlaytestTapeToFrame(page, frame, surface, tape, hooks = {
331
355
  if (waitMs > 0) {
332
356
  await page.waitForTimeout(waitMs);
333
357
  }
334
- if (event.type === 'keyDown') {
358
+ if (event.type === "keyDown") {
335
359
  await page.keyboard.down(event.key);
336
360
  continue;
337
361
  }
338
- if (event.type === 'keyUp') {
362
+ if (event.type === "keyUp") {
339
363
  await page.keyboard.up(event.key);
340
364
  continue;
341
365
  }
342
- if (!('x' in event) || !('y' in event)) {
366
+ if (!("x" in event) || !("y" in event)) {
343
367
  throw new Error(`project_check_playtest_event_unsupported:${event.type}`);
344
368
  }
345
369
  const point = resolveTapePoint(box, event);
346
- const pointerId = 'pointerId' in event ? event.pointerId ?? 0 : 0;
347
- if (event.type === 'tap') {
370
+ const pointerId = "pointerId" in event ? (event.pointerId ?? 0) : 0;
371
+ if (event.type === "tap") {
348
372
  if (mobile && session) {
349
373
  touchPoints.set(pointerId, point);
350
- await dispatchTouchPoints(session, 'touchStart', touchPoints);
374
+ await dispatchTouchPoints(session, "touchStart", touchPoints);
351
375
  await page.waitForTimeout(event.durationMs ?? types_1.APP_PLAYTEST_DEFAULT_TAP_DURATION_MS);
352
376
  touchPoints.delete(pointerId);
353
- await dispatchTouchPoints(session, 'touchEnd', touchPoints);
377
+ await dispatchTouchPoints(session, "touchEnd", touchPoints);
354
378
  }
355
379
  else {
356
380
  await page.mouse.move(point.x, point.y);
357
- await page.mouse.down({ button: 'left' });
381
+ await page.mouse.down({ button: "left" });
358
382
  await page.waitForTimeout(event.durationMs ?? types_1.APP_PLAYTEST_DEFAULT_TAP_DURATION_MS);
359
- await page.mouse.up({ button: 'left' });
383
+ await page.mouse.up({ button: "left" });
360
384
  }
361
385
  continue;
362
386
  }
363
- if (event.type === 'pointerDown') {
387
+ if (event.type === "pointerDown") {
364
388
  if (mobile && session) {
365
389
  touchPoints.set(pointerId, point);
366
- await dispatchTouchPoints(session, 'touchStart', touchPoints);
390
+ await dispatchTouchPoints(session, "touchStart", touchPoints);
367
391
  }
368
392
  else {
369
393
  await page.mouse.move(point.x, point.y);
370
- await page.mouse.down({ button: 'left' });
394
+ await page.mouse.down({ button: "left" });
371
395
  }
372
396
  continue;
373
397
  }
374
- if (event.type === 'pointerMove') {
398
+ if (event.type === "pointerMove") {
375
399
  if (mobile && session) {
376
400
  touchPoints.set(pointerId, point);
377
- await dispatchTouchPoints(session, 'touchMove', touchPoints);
401
+ await dispatchTouchPoints(session, "touchMove", touchPoints);
378
402
  }
379
403
  else {
380
404
  await page.mouse.move(point.x, point.y);
381
405
  }
382
406
  continue;
383
407
  }
384
- if (event.type === 'pointerUp') {
408
+ if (event.type === "pointerUp") {
385
409
  if (mobile && session) {
386
410
  touchPoints.delete(pointerId);
387
- await dispatchTouchPoints(session, 'touchEnd', touchPoints);
411
+ await dispatchTouchPoints(session, "touchEnd", touchPoints);
388
412
  }
389
413
  else {
390
414
  await page.mouse.move(point.x, point.y);
391
- await page.mouse.up({ button: 'left' });
415
+ await page.mouse.up({ button: "left" });
392
416
  }
393
417
  }
394
418
  }
395
419
  catch (error) {
396
420
  const detail = error instanceof Error ? error.message : String(error);
397
- throw new Error(`project_check_playtest_action_failed: action ${eventIndex + 1} of ${tape.events.length} `
398
- + `(${event.type} at ${event.atMs} ms) failed: ${detail}`);
421
+ throw new Error(`project_check_playtest_action_failed: action ${eventIndex + 1} of ${tape.events.length} ` +
422
+ `(${event.type} at ${event.atMs} ms) failed: ${detail}`);
399
423
  }
400
424
  }
401
425
  const remainingMs = tape.durationMs - (Date.now() - startedAt);
@@ -410,14 +434,47 @@ async function dispatchPlaytestTapeToFrame(page, frame, surface, tape, hooks = {
410
434
  }
411
435
  }
412
436
  async function findGameFrame(page, timeoutMs) {
413
- const handle = await page.waitForSelector(FRAME_SELECTOR, { timeout: timeoutMs });
437
+ const handle = await page.waitForSelector(FRAME_SELECTOR, {
438
+ state: "attached",
439
+ timeout: timeoutMs,
440
+ });
414
441
  const frame = await handle.contentFrame();
415
442
  await handle.dispose().catch(() => { });
416
443
  if (!frame) {
417
- throw new Error('project_check_game_frame_missing');
444
+ throw new Error("project_check_game_frame_missing");
418
445
  }
419
446
  return frame;
420
447
  }
448
+ async function describeHostedLaunchTimeout(page, lastCapturedState = null, timeoutMs = HOSTED_LAUNCH_DIAGNOSTIC_TIMEOUT_MS) {
449
+ let diagnosticTimeout = null;
450
+ const lastCapturedStateLabel = lastCapturedState?.state ?? "none";
451
+ try {
452
+ const state = await Promise.race([
453
+ page.evaluate(({ frameSelector }) => {
454
+ const hostedState = window.__playdropCaptureHostedLaunchState;
455
+ return {
456
+ documentReadyState: document.readyState,
457
+ visibilityState: document.visibilityState,
458
+ gameFrameCount: document.querySelectorAll(frameSelector).length,
459
+ hostedState: typeof hostedState?.state === "string" ? hostedState.state : null,
460
+ };
461
+ }, { frameSelector: FRAME_SELECTOR }),
462
+ new Promise((_resolve, reject) => {
463
+ diagnosticTimeout = setTimeout(() => reject(new Error("diagnostic_timeout")), timeoutMs);
464
+ }),
465
+ ]);
466
+ return `[check][timeout] lastCapturedState=${lastCapturedStateLabel} documentReadyState=${state.documentReadyState} visibilityState=${state.visibilityState} gameFrameCount=${state.gameFrameCount} hostedState=${state.hostedState ?? "none"}`;
467
+ }
468
+ catch (error) {
469
+ const detail = error instanceof Error ? error.message : String(error);
470
+ return `[check][timeout] lastCapturedState=${lastCapturedStateLabel} pageStateUnavailable=${detail}`;
471
+ }
472
+ finally {
473
+ if (diagnosticTimeout) {
474
+ clearTimeout(diagnosticTimeout);
475
+ }
476
+ }
477
+ }
421
478
  async function verifyHostedEditorPhaseRoundTrip(page, frame, timeoutMs) {
422
479
  const armReadySignal = async () => {
423
480
  await frame.evaluate(() => {
@@ -432,9 +489,9 @@ async function verifyHostedEditorPhaseRoundTrip(page, frame, timeoutMs) {
432
489
  return;
433
490
  }
434
491
  state.installed = true;
435
- window.addEventListener('playdrop:loading-state', (event) => {
492
+ window.addEventListener("playdrop:loading-state", (event) => {
436
493
  const detail = event.detail;
437
- if (detail?.status === 'ready') {
494
+ if (detail?.status === "ready") {
438
495
  state.signals.push({ editor: detail.editor === true });
439
496
  }
440
497
  });
@@ -443,48 +500,50 @@ async function verifyHostedEditorPhaseRoundTrip(page, frame, timeoutMs) {
443
500
  const changePhaseAndWait = async (phase, editor) => {
444
501
  await armReadySignal();
445
502
  await page.evaluate((nextPhase) => {
446
- window.postMessage({ type: 'playdrop-studio-runtime-state', active: true, phase: nextPhase }, window.location.origin);
503
+ window.postMessage({ type: "playdrop-studio-runtime-state", active: true, phase: nextPhase }, window.location.origin);
447
504
  }, phase);
448
505
  await frame.waitForFunction((expectedEditor) => {
449
506
  const runtimeWindow = window;
450
- return runtimeWindow.__playdropEditorPhaseCheck?.signals?.some((signal) => signal.editor === expectedEditor) === true;
507
+ return (runtimeWindow.__playdropEditorPhaseCheck?.signals?.some((signal) => signal.editor === expectedEditor) === true);
451
508
  }, editor, { timeout: timeoutMs });
452
509
  };
453
510
  try {
454
- await changePhaseAndWait('play', false);
455
- await changePhaseAndWait('editor', true);
511
+ await changePhaseAndWait("play", false);
512
+ await changePhaseAndWait("editor", true);
456
513
  }
457
514
  catch (error) {
458
515
  const detail = error instanceof Error ? error.message : String(error);
459
- throw new Error('project_check_editor_phase_switch_failed: The hosted bundle must switch Editor to Playtest and back '
460
- + `inside the existing runtime frame. ${detail}`);
516
+ throw new Error("project_check_editor_phase_switch_failed: The hosted bundle must switch Editor to Playtest and back " +
517
+ `inside the existing runtime frame. ${detail}`);
461
518
  }
462
519
  }
463
520
  async function readWebGlRenderer(frame) {
464
521
  const renderer = await frame.evaluate(() => {
465
- const canvas = document.createElement('canvas');
466
- const gl = (canvas.getContext('webgl2') || canvas.getContext('webgl') || canvas.getContext('experimental-webgl'));
522
+ const canvas = document.createElement("canvas");
523
+ const gl = (canvas.getContext("webgl2") ||
524
+ canvas.getContext("webgl") ||
525
+ canvas.getContext("experimental-webgl"));
467
526
  if (!gl) {
468
- return '';
527
+ return "";
469
528
  }
470
- const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
529
+ const debugInfo = gl.getExtension("WEBGL_debug_renderer_info");
471
530
  if (debugInfo) {
472
531
  const unmaskedRenderer = gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL);
473
- if (typeof unmaskedRenderer === 'string' && unmaskedRenderer.trim()) {
532
+ if (typeof unmaskedRenderer === "string" && unmaskedRenderer.trim()) {
474
533
  return unmaskedRenderer;
475
534
  }
476
535
  }
477
536
  const rendererValue = gl.getParameter(gl.RENDERER);
478
- return typeof rendererValue === 'string' ? rendererValue : String(rendererValue ?? '');
537
+ return typeof rendererValue === "string" ? rendererValue : String(rendererValue ?? "");
479
538
  });
480
- const normalized = typeof renderer === 'string' ? renderer.trim() : '';
539
+ const normalized = typeof renderer === "string" ? renderer.trim() : "";
481
540
  if (!normalized) {
482
- throw new Error('project_check_webgl_renderer_missing');
541
+ throw new Error("project_check_webgl_renderer_missing");
483
542
  }
484
543
  return normalized;
485
544
  }
486
545
  async function saveScreenshot(page, screenshotPath) {
487
- const normalizedPath = typeof screenshotPath === 'string' ? screenshotPath.trim() : '';
546
+ const normalizedPath = typeof screenshotPath === "string" ? screenshotPath.trim() : "";
488
547
  if (!normalizedPath) {
489
548
  return null;
490
549
  }
@@ -493,11 +552,11 @@ async function saveScreenshot(page, screenshotPath) {
493
552
  return normalizedPath;
494
553
  }
495
554
  async function saveGameFrameScreenshot(page, screenshotPath) {
496
- const normalizedPath = typeof screenshotPath === 'string' ? screenshotPath.trim() : '';
555
+ const normalizedPath = typeof screenshotPath === "string" ? screenshotPath.trim() : "";
497
556
  if (!normalizedPath)
498
557
  return null;
499
558
  await (0, promises_1.mkdir)((0, node_path_1.dirname)(normalizedPath), { recursive: true });
500
- await page.locator(FRAME_SELECTOR).screenshot({ path: normalizedPath, type: 'png' });
559
+ await page.locator(FRAME_SELECTOR).screenshot({ path: normalizedPath, type: "png" });
501
560
  return normalizedPath;
502
561
  }
503
562
  function normalizeLoadCheckFailure(error, artifactFingerprint) {
@@ -508,44 +567,45 @@ function normalizeLoadCheckFailure(error, artifactFingerprint) {
508
567
  return buildFailedLoadCheckResult(errorCodeMatch[1], message, artifactFingerprint);
509
568
  }
510
569
  const prefixedCodeMatch = message.match(/^([a-z0-9_]+):/i);
511
- if (prefixedCodeMatch?.[1]
512
- && (prefixedCodeMatch[1].startsWith('hosted_app_')
513
- || prefixedCodeMatch[1].startsWith('local_dev_')
514
- || prefixedCodeMatch[1].startsWith('dev_auth_')
515
- || prefixedCodeMatch[1].startsWith('project_check_'))) {
570
+ if (prefixedCodeMatch?.[1] &&
571
+ (prefixedCodeMatch[1].startsWith("hosted_app_") ||
572
+ prefixedCodeMatch[1].startsWith("local_dev_") ||
573
+ prefixedCodeMatch[1].startsWith("dev_auth_") ||
574
+ prefixedCodeMatch[1].startsWith("project_check_"))) {
516
575
  return buildFailedLoadCheckResult(prefixedCodeMatch[1], message, artifactFingerprint);
517
576
  }
518
577
  if (/missing_init\b/i.test(message) || /hosted_app_missing_client_init:/i.test(message)) {
519
- return buildFailedLoadCheckResult('missing_init', message, artifactFingerprint);
578
+ return buildFailedLoadCheckResult("missing_init", message, artifactFingerprint);
520
579
  }
521
580
  if (/missing_ready\b/i.test(message) || /hosted_app_missing_ready:/i.test(message)) {
522
- return buildFailedLoadCheckResult('missing_ready', message, artifactFingerprint);
581
+ return buildFailedLoadCheckResult("missing_ready", message, artifactFingerprint);
523
582
  }
524
583
  if (/sdk_mismatch\b/i.test(message) || /hosted_app_sdk_version_mismatch:/i.test(message)) {
525
- return buildFailedLoadCheckResult('sdk_mismatch', message, artifactFingerprint);
584
+ return buildFailedLoadCheckResult("sdk_mismatch", message, artifactFingerprint);
526
585
  }
527
586
  if (/software renderer|swiftshader|webgl_renderer_software/i.test(message)) {
528
- return buildFailedLoadCheckResult('webgl_renderer_software', message, artifactFingerprint);
587
+ return buildFailedLoadCheckResult("webgl_renderer_software", message, artifactFingerprint);
529
588
  }
530
- return buildFailedLoadCheckResult('startup_failure', message, artifactFingerprint);
589
+ return buildFailedLoadCheckResult("startup_failure", message, artifactFingerprint);
531
590
  }
532
- function formatHostedLoadCheckFailure(taskName, result, scope = 'local') {
533
- const scopeLabel = scope === 'final'
534
- ? 'final owner play route'
535
- : scope === 'editor'
536
- ? 'staged Editor phase'
537
- : scope === 'staged'
538
- ? 'staged upload preview'
539
- : 'local hosted preview';
540
- const errorCode = result.errorCode?.trim() || 'startup_failure';
591
+ function formatHostedLoadCheckFailure(taskName, result, scope = "local") {
592
+ const scopeLabel = scope === "final"
593
+ ? "final owner play route"
594
+ : scope === "editor"
595
+ ? "staged Editor phase"
596
+ : scope === "staged"
597
+ ? "staged upload preview"
598
+ : "local hosted preview";
599
+ const errorCode = result.errorCode?.trim() || "startup_failure";
541
600
  const message = normalizeLoadCheckMessage(result.message);
542
- return `hosted_load_check_failed: ${taskName} failed the ${scopeLabel} check (${errorCode}). ${message}`;
601
+ const screenshot = result.screenshotPath ? ` Screenshot: ${result.screenshotPath}.` : "";
602
+ return `hosted_load_check_failed: ${taskName} failed the ${scopeLabel} check (${errorCode}). ${message}${screenshot}`;
543
603
  }
544
604
  function redactHostedLoadCheckSecrets(text, sourceUrl) {
545
- return (0, appUrls_1.redactTaskCaptureToken)(text, sourceUrl).replace(/("(?:authToken|appAccessToken|captureTaskToken|reviewToken)"\s*:\s*")[^"]+(")/g, '$1<redacted>$2');
605
+ return (0, appUrls_1.redactTaskCaptureToken)(text, sourceUrl).replace(/("(?:authToken|appAccessToken|captureTaskToken|reviewToken)"\s*:\s*")[^"]+(")/g, "$1<redacted>$2");
546
606
  }
547
607
  async function runHostedLoadCheck(options) {
548
- const expectedHostedLaunchState = options.expectedHostedLaunchState ?? 'ready';
608
+ const expectedHostedLaunchState = options.expectedHostedLaunchState ?? "ready";
549
609
  const artifactFingerprint = options.artifactFingerprint ?? null;
550
610
  const timeoutMs = options.timeoutMs ?? DEFAULT_HOSTED_LOAD_TIMEOUT_MS;
551
611
  const errors = [];
@@ -560,15 +620,17 @@ async function runHostedLoadCheck(options) {
560
620
  const hasExplicitToken = Boolean(options.token?.trim());
561
621
  const shouldBootstrapSavedSession = options.savedSessionBootstrap !== false && hasExplicitToken;
562
622
  const bootstrapToken = shouldBootstrapSavedSession ? options.token?.trim() || null : null;
563
- const bootstrapUser = shouldBootstrapSavedSession ? options.user ?? null : null;
564
- let currentTapeAction = '';
623
+ const bootstrapUser = shouldBootstrapSavedSession ? (options.user ?? null) : null;
624
+ let currentTapeAction = "";
565
625
  const withTapeAction = (line) => (currentTapeAction ? `${line} after ${currentTapeAction}` : line);
566
626
  let hostedLaunchWaiterSettled = false;
627
+ let lastHostedLaunchState = null;
567
628
  let resolveHostedLaunchWaiter = null;
568
629
  const hostedLaunchWaiter = new Promise((resolve) => {
569
630
  resolveHostedLaunchWaiter = resolve;
570
631
  });
571
632
  const settleHostedLaunchWaiter = (state) => {
633
+ lastHostedLaunchState = state;
572
634
  if (hostedLaunchWaiterSettled) {
573
635
  return;
574
636
  }
@@ -585,7 +647,7 @@ async function runHostedLoadCheck(options) {
585
647
  await page.addInitScript(({ token, user }) => {
586
648
  try {
587
649
  if (token) {
588
- window.localStorage.setItem('playdrop.accessToken', token);
650
+ window.localStorage.setItem("playdrop.accessToken", token);
589
651
  }
590
652
  }
591
653
  catch {
@@ -593,60 +655,60 @@ async function runHostedLoadCheck(options) {
593
655
  }
594
656
  try {
595
657
  if (user) {
596
- window.localStorage.setItem('playdrop.user', JSON.stringify(user));
658
+ window.localStorage.setItem("playdrop.user", JSON.stringify(user));
597
659
  }
598
660
  }
599
661
  catch {
600
662
  // Storage can be blocked by browser policy; the cookie remains authoritative.
601
663
  }
602
664
  try {
603
- window.sessionStorage.removeItem('playdrop.logoutReason');
665
+ window.sessionStorage.removeItem("playdrop.logoutReason");
604
666
  }
605
667
  catch {
606
668
  // Ignore session storage failures.
607
669
  }
608
670
  }, { token: bootstrapToken, user: bootstrapUser });
609
671
  }
610
- await page.exposeBinding('__playdropCaptureLog', async (_source, type, payload) => {
611
- if (type === 'hosted-launch-state') {
672
+ await page.exposeBinding("__playdropCaptureLog", async (_source, type, payload) => {
673
+ if (type === "hosted-launch-state") {
612
674
  const normalizedHostedState = normalizeHostedLaunchStatePayload(payload);
613
675
  if (normalizedHostedState) {
614
676
  settleHostedLaunchWaiter(normalizedHostedState);
615
677
  }
616
678
  }
617
- const normalizedType = typeof type === 'string' ? type.toLowerCase() : 'info';
679
+ const normalizedType = typeof type === "string" ? type.toLowerCase() : "info";
618
680
  if (/error|failed|denied|unreachable/.test(normalizedType)) {
619
681
  const serialized = serializePayload(payload);
620
- errors.push(redactReportText(['[check][custom]', normalizedType, serialized].filter(Boolean).join(' ')));
682
+ errors.push(redactReportText(["[check][custom]", normalizedType, serialized].filter(Boolean).join(" ")));
621
683
  }
622
684
  });
623
685
  await page.addInitScript(() => {
624
- window.addEventListener('unhandledrejection', (event) => {
686
+ window.addEventListener("unhandledrejection", (event) => {
625
687
  try {
626
- console.error('[check][unhandledrejection]', event.reason);
688
+ console.error("[check][unhandledrejection]", event.reason);
627
689
  }
628
690
  catch {
629
- console.error('[check][unhandledrejection]');
691
+ console.error("[check][unhandledrejection]");
630
692
  }
631
693
  });
632
694
  const bridgeWindow = window;
633
695
  bridgeWindow.playdrop = bridgeWindow.playdrop || {};
634
696
  bridgeWindow.__playdropCaptureHostedLaunchState = null;
635
697
  const bridge = (type, payload) => {
636
- if (type === 'hosted-launch-state') {
698
+ if (type === "hosted-launch-state") {
637
699
  bridgeWindow.__playdropCaptureHostedLaunchState = payload;
638
700
  }
639
701
  try {
640
702
  const binding = bridgeWindow.__playdropCaptureLog;
641
- if (typeof binding === 'function') {
703
+ if (typeof binding === "function") {
642
704
  binding(type, payload);
643
705
  }
644
706
  }
645
707
  catch (error) {
646
- console.error('[check][bridge-error]', error);
708
+ console.error("[check][bridge-error]", error);
647
709
  }
648
710
  };
649
- Object.defineProperty(bridgeWindow.playdrop, 'capture', {
711
+ Object.defineProperty(bridgeWindow.playdrop, "capture", {
650
712
  value: bridge,
651
713
  configurable: true,
652
714
  writable: true,
@@ -656,26 +718,58 @@ async function runHostedLoadCheck(options) {
656
718
  const type = message.type();
657
719
  const args = await Promise.all(message.args().map((arg) => arg.jsonValue().catch(() => arg.toString())));
658
720
  const rendered = formatBrowserConsoleMessage(args, message.text());
721
+ if (isDuplicateBrowserResourceConsoleError(type, rendered)) {
722
+ return;
723
+ }
659
724
  const line = redactReportText(withTapeAction(`[check][console:${type}] ${rendered}`));
660
- if (type === 'error' || type === 'assert' || type === 'trace') {
725
+ if (type === "error" || type === "assert" || type === "trace") {
661
726
  errors.push(line);
662
727
  }
663
728
  };
664
- page.on('console', (message) => {
665
- void handleConsoleMessage(message).catch((error) => {
729
+ const pendingConsoleMessages = new Set();
730
+ const drainConsoleMessages = async () => {
731
+ if (pendingConsoleMessages.size === 0)
732
+ return;
733
+ let timeout = null;
734
+ const settled = await Promise.race([
735
+ Promise.allSettled([...pendingConsoleMessages]).then(() => true),
736
+ new Promise((resolve) => {
737
+ timeout = setTimeout(() => resolve(false), HOSTED_LAUNCH_DIAGNOSTIC_TIMEOUT_MS);
738
+ }),
739
+ ]);
740
+ if (timeout)
741
+ clearTimeout(timeout);
742
+ if (!settled) {
743
+ warnings.push("[check][console-handler] Timed out while collecting browser console diagnostics.");
744
+ }
745
+ };
746
+ page.on("console", (message) => {
747
+ const pending = handleConsoleMessage(message)
748
+ .catch((error) => {
666
749
  const text = redactReportText(error instanceof Error ? error.message : String(error));
667
- errors.push(`[check][console-handler] ${text}`);
668
- });
750
+ warnings.push(`[check][console-handler] ${text}`);
751
+ })
752
+ .finally(() => pendingConsoleMessages.delete(pending));
753
+ pendingConsoleMessages.add(pending);
669
754
  });
670
- page.on('pageerror', (error) => {
755
+ page.on("pageerror", (error) => {
671
756
  const text = redactReportText(error?.message ?? String(error));
672
757
  const line = withTapeAction(`[check][pageerror] ${text}`);
673
758
  errors.push(line);
674
759
  });
675
- page.on('requestfailed', (request) => {
760
+ page.on("crash", () => {
761
+ const line = withTapeAction("[check][pagecrash] Hosted validator page crashed.");
762
+ errors.push(line);
763
+ settleHostedLaunchWaiter({
764
+ state: "error",
765
+ errorCode: "browser_page_crashed",
766
+ message: "The hosted validator page crashed before launch completed.",
767
+ });
768
+ });
769
+ page.on("requestfailed", (request) => {
676
770
  const failure = request.failure();
677
- const errorText = failure?.errorText ?? '';
678
- const text = redactReportText(`${request.method()} ${request.url()} - ${errorText || 'failed'}`);
771
+ const errorText = failure?.errorText ?? "";
772
+ const text = redactReportText(`${request.method()} ${request.url()} - ${errorText || "failed"}`);
679
773
  if (isKnownGoogleTelemetryUrl(request.url())) {
680
774
  warnings.push(`[check][requestfailed] ${text}`);
681
775
  return;
@@ -686,12 +780,21 @@ async function runHostedLoadCheck(options) {
686
780
  const line = withTapeAction(`[check][requestfailed] ${text}`);
687
781
  errors.push(line);
688
782
  });
689
- page.on('response', (response) => {
783
+ page.on("response", (response) => {
690
784
  const status = response.status();
691
785
  if (status < 400) {
692
786
  return;
693
787
  }
694
788
  const text = redactReportText(`${status} ${response.statusText()} ${response.url()}`);
789
+ if (isExpectedAnonymousSessionProbe({
790
+ url: response.url(),
791
+ status,
792
+ method: response.request().method(),
793
+ hasExplicitToken,
794
+ })) {
795
+ warnings.push(`[check][response] ${text}`);
796
+ return;
797
+ }
695
798
  if (isKnownGoogleTelemetryUrl(response.url())) {
696
799
  warnings.push(`[check][response] ${text}`);
697
800
  return;
@@ -699,65 +802,83 @@ async function runHostedLoadCheck(options) {
699
802
  const line = withTapeAction(`[check][response] ${text}`);
700
803
  errors.push(line);
701
804
  });
702
- const response = await page.goto(options.targetUrl, { waitUntil: 'domcontentloaded', timeout: 30000 });
703
- if (response && !response.ok()) {
704
- errors.push(`[check][navigation] ${response.status()} ${response.statusText()} ${reportTargetUrl}`);
705
- }
706
- const readinessTimeoutMs = Math.min(Math.max(timeoutMs, 1000), 30000);
707
- await page.waitForFunction(({ frameSelector }) => {
708
- const frame = document.querySelector(frameSelector);
709
- const state = window.__playdropCaptureHostedLaunchState;
710
- if (frame) {
711
- return 'frame';
805
+ const captureFailureScreenshot = async () => {
806
+ try {
807
+ screenshotPath = await saveScreenshot(page, options.failureScreenshotPath ?? options.screenshotPath);
712
808
  }
713
- if (state && typeof state.state === 'string' && state.state.trim().length > 0) {
714
- return `hosted:${state.state.trim().toLowerCase()}`;
809
+ catch (screenshotError) {
810
+ const detail = redactReportText(screenshotError instanceof Error ? screenshotError.message : String(screenshotError));
811
+ errors.push(`[check][screenshot] Failed to capture staged validation evidence: ${detail}`);
812
+ }
813
+ };
814
+ const captureCompletionScreenshot = async () => {
815
+ if (errors.length > 0) {
816
+ await captureFailureScreenshot();
817
+ return;
715
818
  }
716
- return null;
717
- }, { frameSelector: FRAME_SELECTOR }, { timeout: readinessTimeoutMs });
718
- const launchState = await Promise.race([
719
- hostedLaunchWaiter,
720
- page.waitForTimeout(timeoutMs).then(() => {
721
- throw new Error(expectedHostedLaunchState === 'ready'
722
- ? 'hosted_app_missing_ready: instrument_error:game_ready_timeout: Hosted app did not report sdk.host.ready() before the instrument timeout.'
723
- : `hosted_app_gate_missing: Hosted app did not report ${expectedHostedLaunchState} before the load-check timeout.`);
724
- }),
725
- ]);
726
- if (launchState.state === 'error') {
727
- throw new Error(formatHostedLaunchError(launchState));
728
- }
729
- if (launchState.state !== expectedHostedLaunchState) {
730
- throw new Error(formatHostedLaunchStateMismatch(launchState.state, expectedHostedLaunchState));
731
- }
732
- finalUrl = (0, appUrls_1.redactTaskCaptureSession)(page.url());
733
- if (expectedHostedLaunchState !== 'ready') {
734
819
  screenshotPath = await saveScreenshot(page, options.screenshotPath);
735
- return;
736
- }
737
- const frame = await findGameFrame(page, readinessTimeoutMs);
738
- webglRenderer = await readWebGlRenderer(frame);
739
- if (isSoftwareWebGlRenderer(webglRenderer)) {
740
- throw new Error(`project_check_webgl_renderer_software: WebGL renderer is "${webglRenderer}". Real hardware acceleration is required.`);
741
- }
742
- if (options.verifyEditorPhaseRoundTrip) {
743
- await verifyHostedEditorPhaseRoundTrip(page, frame, readinessTimeoutMs);
744
- }
745
- await saveGameFrameScreenshot(page, options.readyGameFrameScreenshotPath);
746
- if (options.playtestTape) {
747
- if (!options.playtestSurface) {
748
- throw new Error('project_check_playtest_surface_required');
820
+ };
821
+ try {
822
+ const response = await page.goto(options.targetUrl, { waitUntil: "domcontentloaded", timeout: 30000 });
823
+ if (response && !response.ok()) {
824
+ errors.push(`[check][navigation] ${response.status()} ${response.statusText()} ${reportTargetUrl}`);
749
825
  }
750
- await dispatchPlaytestTapeToFrame(page, frame, options.playtestSurface, options.playtestTape, {
751
- onBeforeEvent: (eventIndex, event) => {
752
- currentTapeAction = `action ${eventIndex + 1} of ${options.playtestTape.events.length} (${event.type} at ${event.atMs} ms)`;
753
- },
754
- });
826
+ const readinessTimeoutMs = Math.min(Math.max(timeoutMs, 1000), 30000);
827
+ let launchState;
828
+ try {
829
+ launchState = await waitForHostedLaunchState(hostedLaunchWaiter, expectedHostedLaunchState, timeoutMs);
830
+ }
831
+ catch (error) {
832
+ const timeoutContext = await describeHostedLaunchTimeout(page, lastHostedLaunchState);
833
+ const detail = error instanceof Error ? error.message : String(error);
834
+ throw new Error(`${detail} ${timeoutContext}`);
835
+ }
836
+ if (launchState.state === "error") {
837
+ throw new Error(formatHostedLaunchError(launchState));
838
+ }
839
+ finalUrl = (0, appUrls_1.redactTaskCaptureSession)(page.url());
840
+ if (expectedHostedLaunchState !== "ready") {
841
+ await drainConsoleMessages();
842
+ await captureCompletionScreenshot();
843
+ return;
844
+ }
845
+ const frame = await findGameFrame(page, readinessTimeoutMs);
846
+ webglRenderer = await readWebGlRenderer(frame);
847
+ if (isSoftwareWebGlRenderer(webglRenderer)) {
848
+ throw new Error(`project_check_webgl_renderer_software: WebGL renderer is "${webglRenderer}". Real hardware acceleration is required.`);
849
+ }
850
+ if (options.verifyEditorPhaseRoundTrip) {
851
+ await verifyHostedEditorPhaseRoundTrip(page, frame, readinessTimeoutMs);
852
+ }
853
+ await saveGameFrameScreenshot(page, options.readyGameFrameScreenshotPath);
854
+ if (options.playtestTape) {
855
+ if (!options.playtestSurface) {
856
+ throw new Error("project_check_playtest_surface_required");
857
+ }
858
+ await dispatchPlaytestTapeToFrame(page, frame, options.playtestSurface, options.playtestTape, {
859
+ onBeforeEvent: (eventIndex, event) => {
860
+ currentTapeAction = `action ${eventIndex + 1} of ${options.playtestTape.events.length} (${event.type} at ${event.atMs} ms)`;
861
+ },
862
+ });
863
+ }
864
+ else if (options.postReadyWaitMs && options.postReadyWaitMs > 0) {
865
+ await page.waitForTimeout(options.postReadyWaitMs);
866
+ }
867
+ await saveGameFrameScreenshot(page, options.gameFrameScreenshotPath);
868
+ await drainConsoleMessages();
869
+ await captureCompletionScreenshot();
755
870
  }
756
- else if (options.postReadyWaitMs && options.postReadyWaitMs > 0) {
757
- await page.waitForTimeout(options.postReadyWaitMs);
871
+ catch (error) {
872
+ await drainConsoleMessages();
873
+ try {
874
+ finalUrl = (0, appUrls_1.redactTaskCaptureSession)(page.url());
875
+ }
876
+ catch {
877
+ // Keep the last known URL when the renderer is unavailable.
878
+ }
879
+ await captureFailureScreenshot();
880
+ throw error;
758
881
  }
759
- await saveGameFrameScreenshot(page, options.gameFrameScreenshotPath);
760
- screenshotPath = await saveScreenshot(page, options.screenshotPath);
761
882
  }, {
762
883
  ...options.contextOptions,
763
884
  automationOrigin: new URL(options.targetUrl).origin,
@@ -780,7 +901,7 @@ async function runHostedLoadCheck(options) {
780
901
  };
781
902
  }
782
903
  if (errors.length > 0) {
783
- return buildFailedLoadCheckResult('startup_failure', `Hosted app emitted ${errors.length} console, page, or network error(s) during startup.${formatLoadCheckErrors(errors)}`, artifactFingerprint, {
904
+ return buildFailedLoadCheckResult("startup_failure", `Hosted app emitted ${errors.length} console, page, or network error(s) during startup.${formatLoadCheckErrors(errors)}`, artifactFingerprint, {
784
905
  screenshotPath,
785
906
  webglRenderer,
786
907
  targetUrl: finalUrl,
@@ -797,9 +918,9 @@ async function runHostedLoadCheck(options) {
797
918
  });
798
919
  }
799
920
  async function startOrReuseMountedDevRuntime(input) {
800
- const appTypeSlug = (0, appUrls_1.getAppTypeSlug)(input.task.type ?? 'GAME');
921
+ const appTypeSlug = (0, appUrls_1.getAppTypeSlug)(input.task.type ?? "GAME");
801
922
  const devRouterPort = input.devRouterPort ?? devServer_1.DEV_ROUTER_PORT;
802
- const runtimeAssetManifest = input.task.hostingMode === 'EXTERNAL' || input.task.externalUrl
923
+ const runtimeAssetManifest = input.task.hostingMode === "EXTERNAL" || input.task.externalUrl
803
924
  ? (0, devRuntimeAssets_1.createEmptyDevRuntimeAssetManifest)()
804
925
  : await (0, devRuntimeAssets_1.buildDevRuntimeAssetManifest)({
805
926
  client: input.client,
@@ -838,7 +959,7 @@ async function startOrReuseMountedDevRuntime(input) {
838
959
  }
839
960
  catch (error) {
840
961
  const formatted = (0, dev_1.formatDevRuntimeAssetManifestFailure)(error);
841
- const mountConflict = (0, devServer_1.parseMountConflictError)(error instanceof Error ? error.message : '');
962
+ const mountConflict = (0, devServer_1.parseMountConflictError)(error instanceof Error ? error.message : "");
842
963
  if (mountConflict) {
843
964
  throw new Error(`A different dev session already owns ${mountConflict.ref}. Active owner pid: ${mountConflict.ownerPid}.`);
844
965
  }
@@ -855,14 +976,12 @@ async function runLocalHostedLoadCheck(input) {
855
976
  task: input.task,
856
977
  devRouterPort: input.devRouterPort,
857
978
  });
858
- const localDevPort = input.devRouterPort === undefined || input.devRouterPort === devServer_1.DEV_ROUTER_PORT
859
- ? null
860
- : input.devRouterPort;
979
+ const localDevPort = input.devRouterPort === undefined || input.devRouterPort === devServer_1.DEV_ROUTER_PORT ? null : input.devRouterPort;
861
980
  const localAppMetadata = mountedRuntime.runtimeAssetManifest.response.localAppMetadata;
862
981
  const declaredSurfaces = normalizeLoadCheckSurfaceTargets(localAppMetadata.surfaceTargets);
863
982
  const captureSurface = input.surface ?? resolveLoadCheckSurface(localAppMetadata.surfaceTargets, input.task.primarySurface);
864
983
  if (!declaredSurfaces.includes(captureSurface)) {
865
- return buildFailedLoadCheckResult('surface_unsupported', `Surface ${captureSurface} is not declared in surfaceTargets for "${input.task.name}".`, null);
984
+ return buildFailedLoadCheckResult("surface_unsupported", `Surface ${captureSurface} is not declared in surfaceTargets for "${input.task.name}".`, null);
866
985
  }
867
986
  const contextOptions = cloneLoadCheckContextOptions(captureSurface);
868
987
  const anonymousExpectedState = resolveAnonymousHostedLaunchState({
@@ -874,7 +993,7 @@ async function runLocalHostedLoadCheck(input) {
874
993
  targetUrl: (0, appUrls_1.buildPlatformDevUrl)(input.webBase, {
875
994
  creatorUsername: input.creatorUsername,
876
995
  appName: input.task.name,
877
- appType: input.task.type ?? 'GAME',
996
+ appType: input.task.type ?? "GAME",
878
997
  launchCheck: true,
879
998
  localDevPort,
880
999
  captureSession: input.captureSession,
@@ -888,20 +1007,18 @@ async function runLocalHostedLoadCheck(input) {
888
1007
  postReadyWaitMs: input.postReadyWaitMs,
889
1008
  });
890
1009
  }
891
- if (anonymousExpectedState === 'login_required') {
1010
+ if (anonymousExpectedState === "login_required") {
892
1011
  const registeredApp = await (0, registration_1.fetchRegisteredAppShell)(input.client, input.creatorUsername, input.task.name);
893
- const canLaunchUnregisteredViewer = input.allowUnregisteredViewerLaunch === true
894
- && Boolean(input.token)
895
- && Boolean(input.currentUser);
1012
+ const canLaunchUnregisteredViewer = input.allowUnregisteredViewerLaunch === true && Boolean(input.token) && Boolean(input.currentUser);
896
1013
  if (!registeredApp?.id && !canLaunchUnregisteredViewer) {
897
- return buildFailedLoadCheckResult('app_registration_required_for_auth_validation', `Auth-required hosted app "${input.task.name}" must be registered on PlayDrop before viewer load-check can run. Run "playdrop project create app ${input.task.name}" and try again.`, null);
1014
+ return buildFailedLoadCheckResult("app_registration_required_for_auth_validation", `Auth-required hosted app "${input.task.name}" must be registered on PlayDrop before viewer load-check can run. Run "playdrop project create app ${input.task.name}" and try again.`, null);
898
1015
  }
899
1016
  const anonymousGateResult = await runHostedLoadCheck({
900
1017
  targetUrl: (0, appUrls_1.buildPlatformDevUrl)(input.webBase, {
901
1018
  creatorUsername: input.creatorUsername,
902
1019
  appName: input.task.name,
903
- appType: input.task.type ?? 'GAME',
904
- devAuth: 'anonymous',
1020
+ appType: input.task.type ?? "GAME",
1021
+ devAuth: "anonymous",
905
1022
  launchCheck: true,
906
1023
  localDevPort,
907
1024
  }),
@@ -909,15 +1026,15 @@ async function runLocalHostedLoadCheck(input) {
909
1026
  expectedHostedLaunchState: anonymousExpectedState,
910
1027
  contextOptions,
911
1028
  });
912
- if (anonymousGateResult.status !== 'PASSED') {
1029
+ if (anonymousGateResult.status !== "PASSED") {
913
1030
  return anonymousGateResult;
914
1031
  }
915
1032
  return await runHostedLoadCheck({
916
1033
  targetUrl: (0, appUrls_1.buildPlatformDevUrl)(input.webBase, {
917
1034
  creatorUsername: input.creatorUsername,
918
1035
  appName: input.task.name,
919
- appType: input.task.type ?? 'GAME',
920
- devAuth: 'viewer',
1036
+ appType: input.task.type ?? "GAME",
1037
+ devAuth: "viewer",
921
1038
  launchCheck: true,
922
1039
  localDevPort,
923
1040
  }),
@@ -937,8 +1054,8 @@ async function runLocalHostedLoadCheck(input) {
937
1054
  targetUrl: (0, appUrls_1.buildPlatformDevUrl)(input.webBase, {
938
1055
  creatorUsername: input.creatorUsername,
939
1056
  appName: input.task.name,
940
- appType: input.task.type ?? 'GAME',
941
- devAuth: 'anonymous',
1057
+ appType: input.task.type ?? "GAME",
1058
+ devAuth: "anonymous",
942
1059
  launchCheck: true,
943
1060
  localDevPort,
944
1061
  }),
@@ -963,14 +1080,26 @@ function appendLoadCheckParams(targetUrl, devAuth) {
963
1080
  player: null,
964
1081
  });
965
1082
  }
1083
+ function buildUploadedHostedLoadCheckUrl(rawUrl, phase) {
1084
+ const url = new URL(rawUrl);
1085
+ url.searchParams.set("launchCheck", "1");
1086
+ if (phase === "editor") {
1087
+ url.searchParams.set("hostPhase", "editor");
1088
+ }
1089
+ return url.toString();
1090
+ }
1091
+ function buildUploadedHostedLoadCheckScreenshotPath(task, sessionId, phase) {
1092
+ const projectDir = typeof task.projectDir === "string" ? task.projectDir.trim() : "";
1093
+ if (!projectDir)
1094
+ return null;
1095
+ const safeName = task.name.replace(/[^a-zA-Z0-9._-]+/g, "-");
1096
+ const safeSessionId = sessionId.replace(/[^a-zA-Z0-9._-]+/g, "-");
1097
+ return (0, node_path_1.join)(projectDir, ".playdrop", "validation-evidence", `staged-${safeName}-${safeSessionId}-${phase ?? "play"}.png`);
1098
+ }
966
1099
  async function runUploadedHostedLoadCheck(input) {
967
1100
  const preview = await input.client.fetchAppUploadLaunchCheckPreview(input.creatorUsername, input.task.name, input.sessionId);
968
- const withRequestedPhase = (rawUrl) => {
969
- const url = new URL(rawUrl);
970
- if (input.phase === 'editor')
971
- url.searchParams.set('hostPhase', 'editor');
972
- return url.toString();
973
- };
1101
+ const withRequestedPhase = (rawUrl) => buildUploadedHostedLoadCheckUrl(rawUrl, input.phase);
1102
+ const failureScreenshotPath = input.screenshotPath ?? buildUploadedHostedLoadCheckScreenshotPath(input.task, input.sessionId, input.phase);
974
1103
  const captureSurface = resolveLoadCheckSurface(preview.localAppMetadata.surfaceTargets, input.task.primarySurface);
975
1104
  const contextOptions = cloneLoadCheckContextOptions(captureSurface);
976
1105
  const anonymousExpectedState = resolveAnonymousHostedLaunchState({
@@ -988,6 +1117,7 @@ async function runUploadedHostedLoadCheck(input) {
988
1117
  expectedHostedLaunchState: resolvePostAuthHostedLaunchState(preview.localAppMetadata.controllerMode),
989
1118
  contextOptions,
990
1119
  screenshotPath: input.screenshotPath,
1120
+ failureScreenshotPath,
991
1121
  readyGameFrameScreenshotPath: input.readyGameFrameScreenshotPath,
992
1122
  gameFrameScreenshotPath: input.gameFrameScreenshotPath,
993
1123
  playtestTape: input.playtestTape,
@@ -996,24 +1126,26 @@ async function runUploadedHostedLoadCheck(input) {
996
1126
  verifyEditorPhaseRoundTrip: input.verifyEditorPhaseRoundTrip,
997
1127
  });
998
1128
  }
999
- else if (anonymousExpectedState === 'login_required') {
1129
+ else if (anonymousExpectedState === "login_required") {
1000
1130
  if (!preview.app?.id) {
1001
- result = buildFailedLoadCheckResult('app_registration_required_for_auth_validation', `Auth-required hosted app "${input.task.name}" must be registered on PlayDrop before viewer load-check can run.`, preview.session.manifestHash);
1131
+ result = buildFailedLoadCheckResult("app_registration_required_for_auth_validation", `Auth-required hosted app "${input.task.name}" must be registered on PlayDrop before viewer load-check can run.`, preview.session.manifestHash);
1002
1132
  }
1003
1133
  else {
1004
1134
  const anonymousGateResult = await runHostedLoadCheck({
1005
- targetUrl: withRequestedPhase(appendLoadCheckParams(preview.launchUrl, 'anonymous')),
1135
+ targetUrl: withRequestedPhase(appendLoadCheckParams(preview.launchUrl, "anonymous")),
1006
1136
  artifactFingerprint: preview.session.manifestHash,
1007
1137
  timeoutMs: input.timeoutMs,
1008
1138
  expectedHostedLaunchState: anonymousExpectedState,
1009
1139
  contextOptions,
1140
+ screenshotPath: input.screenshotPath,
1141
+ failureScreenshotPath,
1010
1142
  });
1011
- if (anonymousGateResult.status !== 'PASSED') {
1143
+ if (anonymousGateResult.status !== "PASSED") {
1012
1144
  result = anonymousGateResult;
1013
1145
  }
1014
1146
  else {
1015
1147
  result = await runHostedLoadCheck({
1016
- targetUrl: withRequestedPhase(appendLoadCheckParams(preview.launchUrl, 'viewer')),
1148
+ targetUrl: withRequestedPhase(appendLoadCheckParams(preview.launchUrl, "viewer")),
1017
1149
  artifactFingerprint: preview.session.manifestHash,
1018
1150
  timeoutMs: input.timeoutMs,
1019
1151
  expectedHostedLaunchState: resolvePostAuthHostedLaunchState(preview.localAppMetadata.controllerMode),
@@ -1022,6 +1154,7 @@ async function runUploadedHostedLoadCheck(input) {
1022
1154
  savedSessionBootstrap: true,
1023
1155
  contextOptions,
1024
1156
  screenshotPath: input.screenshotPath,
1157
+ failureScreenshotPath,
1025
1158
  readyGameFrameScreenshotPath: input.readyGameFrameScreenshotPath,
1026
1159
  gameFrameScreenshotPath: input.gameFrameScreenshotPath,
1027
1160
  playtestTape: input.playtestTape,
@@ -1034,12 +1167,13 @@ async function runUploadedHostedLoadCheck(input) {
1034
1167
  }
1035
1168
  else {
1036
1169
  result = await runHostedLoadCheck({
1037
- targetUrl: withRequestedPhase(appendLoadCheckParams(preview.launchUrl, 'anonymous')),
1170
+ targetUrl: withRequestedPhase(appendLoadCheckParams(preview.launchUrl, "anonymous")),
1038
1171
  artifactFingerprint: preview.session.manifestHash,
1039
1172
  timeoutMs: input.timeoutMs,
1040
1173
  expectedHostedLaunchState: anonymousExpectedState,
1041
1174
  contextOptions,
1042
1175
  screenshotPath: input.screenshotPath,
1176
+ failureScreenshotPath,
1043
1177
  readyGameFrameScreenshotPath: input.readyGameFrameScreenshotPath,
1044
1178
  gameFrameScreenshotPath: input.gameFrameScreenshotPath,
1045
1179
  playtestTape: input.playtestTape,
@@ -1055,8 +1189,8 @@ async function runUploadedHostedLoadCheck(input) {
1055
1189
  message: result.message,
1056
1190
  });
1057
1191
  const recordedLaunchCheck = recorded.session.launchCheck;
1058
- if (recordedLaunchCheck.status !== 'PASSED' && recordedLaunchCheck.status !== 'FAILED') {
1059
- return buildFailedLoadCheckResult('startup_failure', 'Upload load-check was not recorded on the staged session.', preview.session.manifestHash);
1192
+ if (recordedLaunchCheck.status !== "PASSED" && recordedLaunchCheck.status !== "FAILED") {
1193
+ return buildFailedLoadCheckResult("startup_failure", "Upload load-check was not recorded on the staged session.", preview.session.manifestHash);
1060
1194
  }
1061
1195
  return {
1062
1196
  ...result,
@@ -1068,22 +1202,20 @@ async function runUploadedHostedLoadCheck(input) {
1068
1202
  };
1069
1203
  }
1070
1204
  async function hashFile(path) {
1071
- return (0, node_crypto_1.createHash)('sha256').update(await (0, promises_1.readFile)(path)).digest('hex');
1205
+ return (0, node_crypto_1.createHash)("sha256")
1206
+ .update(await (0, promises_1.readFile)(path))
1207
+ .digest("hex");
1072
1208
  }
1073
1209
  async function comparePlaytestFrames(firstPath, secondPath, thresholds = {
1074
1210
  meanDeltaMin: PLAYTEST_INTERACTIVE_MEAN_DELTA_MIN,
1075
1211
  changedPixelRatioMin: PLAYTEST_INTERACTIVE_CHANGED_PIXEL_RATIO_MIN,
1076
1212
  }) {
1077
1213
  const readPixels = async (path) => {
1078
- return await (0, sharp_1.default)(path)
1079
- .resize(64, 64, { fit: 'fill' })
1080
- .greyscale()
1081
- .raw()
1082
- .toBuffer();
1214
+ return await (0, sharp_1.default)(path).resize(64, 64, { fit: "fill" }).greyscale().raw().toBuffer();
1083
1215
  };
1084
1216
  const [first, second] = await Promise.all([readPixels(firstPath), readPixels(secondPath)]);
1085
1217
  if (first.length !== second.length || first.length === 0) {
1086
- throw new Error('playtest_smoke_check_frame_shape_mismatch: idle and tape screenshots have different dimensions');
1218
+ throw new Error("playtest_smoke_check_frame_shape_mismatch: idle and tape screenshots have different dimensions");
1087
1219
  }
1088
1220
  let deltaTotal = 0;
1089
1221
  let changedPixels = 0;
@@ -1105,12 +1237,12 @@ async function runUploadedHostedPlaytestSmokeCheck(input) {
1105
1237
  const primarySurface = input.task.primarySurface;
1106
1238
  const fullTape = primarySurface ? input.task.playtestTapes?.[primarySurface] : null;
1107
1239
  if (!primarySurface || !fullTape) {
1108
- throw new Error('agent_task_playtest_smoke_check_primary_tape_required');
1240
+ throw new Error("agent_task_playtest_smoke_check_primary_tape_required");
1109
1241
  }
1110
- const tempDir = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'playdrop-playtest-smoke-'));
1242
+ const tempDir = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "playdrop-playtest-smoke-"));
1111
1243
  const paths = {
1112
- zeroInput: (0, node_path_1.join)(tempDir, 'zero-input.png'),
1113
- fullTape: (0, node_path_1.join)(tempDir, 'full-tape.png'),
1244
+ zeroInput: (0, node_path_1.join)(tempDir, "zero-input.png"),
1245
+ fullTape: (0, node_path_1.join)(tempDir, "full-tape.png"),
1114
1246
  };
1115
1247
  const run = async (label, options) => {
1116
1248
  console.log(`[upload] Playtest smoke check: ${label} from sdk.host.ready().`);
@@ -1125,38 +1257,38 @@ async function runUploadedHostedPlaytestSmokeCheck(input) {
1125
1257
  captureSession: input.captureSession,
1126
1258
  ...options,
1127
1259
  });
1128
- if (result.status !== 'PASSED') {
1129
- throw new Error(`agent_task_playtest_smoke_check_runtime_failed: ${label} failed. `
1130
- + formatHostedLoadCheckFailure(input.task.name, result, 'staged'));
1260
+ if (result.status !== "PASSED") {
1261
+ throw new Error(`agent_task_playtest_smoke_check_runtime_failed: ${label} failed. ` +
1262
+ formatHostedLoadCheckFailure(input.task.name, result, "staged"));
1131
1263
  }
1132
1264
  if (!result.artifactFingerprint) {
1133
- throw new Error('agent_task_playtest_smoke_check_artifact_fingerprint_missing');
1265
+ throw new Error("agent_task_playtest_smoke_check_artifact_fingerprint_missing");
1134
1266
  }
1135
1267
  return result;
1136
1268
  };
1137
1269
  try {
1138
- const zeroInput = await run('zero-input', {
1270
+ const zeroInput = await run("zero-input", {
1139
1271
  postReadyWaitMs: fullTape.durationMs,
1140
1272
  gameFrameScreenshotPath: paths.zeroInput,
1141
1273
  });
1142
- const full = await run('full-tape', {
1274
+ const full = await run("full-tape", {
1143
1275
  playtestTape: fullTape,
1144
1276
  gameFrameScreenshotPath: paths.fullTape,
1145
1277
  });
1146
1278
  const artifactFingerprint = full.artifactFingerprint;
1147
1279
  if (zeroInput.artifactFingerprint !== artifactFingerprint) {
1148
- throw new Error('agent_task_playtest_smoke_check_artifact_changed_during_check');
1280
+ throw new Error("agent_task_playtest_smoke_check_artifact_changed_during_check");
1149
1281
  }
1150
1282
  const comparison = await comparePlaytestFrames(paths.zeroInput, paths.fullTape);
1151
1283
  if (!comparison.passed) {
1152
- throw new Error(`agent_task_playtest_smoke_check_not_interactive: The validator completed all ${fullTape.events.length} tape actions `
1153
- + `without a crash, but the result looked the same as running the game for ${fullTape.durationMs} ms without input `
1154
- + `(mean pixel change ${comparison.meanDelta.toFixed(4)}, changed pixels ${(comparison.changedPixelRatio * 100).toFixed(2)}%). `
1155
- + 'Make sure the tape performs the core gameplay action and produces a visible response, then rerun the tape check.');
1284
+ throw new Error(`agent_task_playtest_smoke_check_not_interactive: The validator completed all ${fullTape.events.length} tape actions ` +
1285
+ `without a crash, but the result looked the same as running the game for ${fullTape.durationMs} ms without input ` +
1286
+ `(mean pixel change ${comparison.meanDelta.toFixed(4)}, changed pixels ${(comparison.changedPixelRatio * 100).toFixed(2)}%). ` +
1287
+ "Make sure the tape performs the core gameplay action and produces a visible response, then rerun the tape check.");
1156
1288
  }
1157
1289
  const evidence = {
1158
1290
  version: 2,
1159
- source: 'CLI_MACHINE',
1291
+ source: "CLI_MACHINE",
1160
1292
  artifactFingerprint,
1161
1293
  primarySurface,
1162
1294
  primaryVerb: fullTape.primaryVerb,
@@ -1167,8 +1299,8 @@ async function runUploadedHostedPlaytestSmokeCheck(input) {
1167
1299
  comparison,
1168
1300
  };
1169
1301
  const recorded = await input.client.recordAppUploadCausalCheck(input.creatorUsername, input.task.name, input.sessionId, { evidence });
1170
- if (recorded.session.causalCheck.status !== 'PASSED') {
1171
- throw new Error('agent_task_playtest_smoke_check_not_recorded');
1302
+ if (recorded.session.causalCheck.status !== "PASSED") {
1303
+ throw new Error("agent_task_playtest_smoke_check_not_recorded");
1172
1304
  }
1173
1305
  console.log(`[upload] Playtest smoke check passed for ${input.creatorUsername}/${input.task.name}.`);
1174
1306
  return evidence;