@playcraft/cli 0.1.40-beta → 0.1.41-prod

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 (368) hide show
  1. package/README.md +3 -58
  2. package/dist/agent/local-backend.js +1 -1
  3. package/dist/cli-root-help.js +0 -2
  4. package/dist/commands/build-all.js +19 -5
  5. package/dist/commands/build.js +28 -15
  6. package/dist/commands/create.js +33 -26
  7. package/dist/commands/image.js +6 -2
  8. package/dist/commands/login.js +42 -84
  9. package/dist/commands/skills.js +0 -4
  10. package/dist/commands/tools-3d.js +0 -11
  11. package/dist/commands/tools-generation.js +137 -69
  12. package/dist/commands/tools-image-context.js +29 -0
  13. package/dist/commands/tools-utils.js +12 -0
  14. package/dist/commands/tools.js +2 -2
  15. package/dist/config.js +4 -59
  16. package/dist/index.js +3 -40
  17. package/dist/remix/clone.js +0 -2
  18. package/dist/remix/init-template.js +0 -2
  19. package/dist/remix/pull.js +0 -2
  20. package/dist/remix/push.js +1 -21
  21. package/dist/utils/agent-api-client.js +30 -185
  22. package/dist/utils/image-asset-context.js +228 -0
  23. package/dist/utils/playcraft-http-client.js +4 -6
  24. package/package.json +3 -13
  25. package/project-template/.claude/agents/refs/ta-image-generation-detail.md +67 -123
  26. package/project-template/.claude/skills/playcraft-image-generation/SKILL.md +135 -63
  27. package/project-template/.claude/skills/playcraft-image-generation/reference/gen-batch-sprite.template.mjs +42 -33
  28. package/project-template/.claude/skills/playcraft-image-generation/reference/gen-batch.template.mjs +18 -20
  29. package/project-template/.claude/skills/playcraft-image-generation/reference/process-batch.template.mjs +6 -7
  30. package/project-template/.claude/skills/playcraft-image-generation/reference/prompt-cookbook.md +39 -39
  31. package/project-templates/phaser/.claude/agents/refs/ta-image-generation-detail.md +59 -59
  32. package/project-templates/phaser/.claude/skills/playcraft-image-generation/SKILL.md +135 -63
  33. package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/gen-batch-sprite.template.mjs +42 -33
  34. package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/gen-batch.template.mjs +18 -20
  35. package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/process-batch.template.mjs +6 -7
  36. package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/prompt-cookbook.md +39 -39
  37. package/project-templates/threejs/.claude/agents/refs/ta-image-generation-detail.md +59 -59
  38. package/project-templates/threejs/.claude/skills/playcraft-image-generation/SKILL.md +135 -63
  39. package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/gen-batch-sprite.template.mjs +42 -33
  40. package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/gen-batch.template.mjs +18 -20
  41. package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/process-batch.template.mjs +6 -7
  42. package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/prompt-cookbook.md +39 -39
  43. package/SKILLS_REGISTRY.md +0 -149
  44. package/dist/auth/account-activation.js +0 -157
  45. package/dist/auth/messages.js +0 -30
  46. package/dist/commands/build-temp.js +0 -26
  47. package/dist/commands/platform-skills.generated.js +0 -19
  48. package/dist/commands/playable.js +0 -53
  49. package/dist/commands/tools-batch.js +0 -87
  50. package/dist/commands/video.js +0 -111
  51. package/dist/commands/workspace-runtime.js +0 -54
  52. package/dist/commands/workspace.js +0 -35
  53. package/dist/project-skills/builtin-import.js +0 -225
  54. package/dist/project-skills/commands.js +0 -77
  55. package/dist/project-skills/lifecycle.js +0 -28
  56. package/dist/project-skills/local-run.js +0 -112
  57. package/dist/project-skills/local-store.js +0 -221
  58. package/dist/project-skills/messages.js +0 -80
  59. package/dist/project-skills/reconcile.js +0 -62
  60. package/dist/project-skills/remote-cache.js +0 -51
  61. package/dist/project-skills/validation.js +0 -19
  62. package/dist/recording/messages.js +0 -40
  63. package/dist/recording/project-digest.js +0 -85
  64. package/dist/recording/recorder.js +0 -293
  65. package/dist/recording/scenario.js +0 -50
  66. package/dist/skill-registry/client.js +0 -345
  67. package/dist/skill-registry/commands.js +0 -180
  68. package/dist/skill-registry/evaluation-client.js +0 -156
  69. package/dist/skill-registry/evaluation-commands.js +0 -174
  70. package/dist/skill-registry/messages.js +0 -177
  71. package/dist/skill-registry/package.js +0 -275
  72. package/dist/skill-registry/platform-package.js +0 -100
  73. package/dist/utils/environment-endpoints.js +0 -17
  74. package/dist/utils/tool-operation-journal.js +0 -124
  75. package/dist/utils/video-evidence-git.js +0 -81
  76. package/dist/video-replication/client.js +0 -222
  77. package/dist/video-replication/comparison-evidence.js +0 -149
  78. package/dist/video-replication/comparison-files.js +0 -57
  79. package/dist/video-replication/comparison-log.js +0 -72
  80. package/dist/video-replication/comparison-messages.js +0 -104
  81. package/dist/video-replication/comparison-output.js +0 -63
  82. package/dist/video-replication/comparison-preflight.js +0 -66
  83. package/dist/video-replication/comparison.js +0 -216
  84. package/dist/video-replication/evidence.js +0 -165
  85. package/dist/video-replication/media.js +0 -111
  86. package/dist/video-replication/messages.js +0 -89
  87. package/dist/workspace/messages.js +0 -35
  88. package/dist/workspace/transfer-client.js +0 -174
  89. package/dist/workspace-runtime/codex/app-server.js +0 -1040
  90. package/dist/workspace-runtime/codex/compact.js +0 -46
  91. package/dist/workspace-runtime/codex/config-toml.js +0 -107
  92. package/dist/workspace-runtime/codex/jsonrpc-stdio.js +0 -145
  93. package/dist/workspace-runtime/codex/launcher-client.js +0 -177
  94. package/dist/workspace-runtime/codex/loopback.js +0 -146
  95. package/dist/workspace-runtime/codex/mcp-config.js +0 -39
  96. package/dist/workspace-runtime/codex/model-broker.js +0 -263
  97. package/dist/workspace-runtime/codex/native-adapter.js +0 -9
  98. package/dist/workspace-runtime/codex/permissions.js +0 -70
  99. package/dist/workspace-runtime/codex/thread-image.js +0 -38
  100. package/dist/workspace-runtime/codex/ui-instructions.js +0 -4
  101. package/dist/workspace-runtime/codex/ui-tools.js +0 -183
  102. package/dist/workspace-runtime/codex/work-mode.js +0 -15
  103. package/dist/workspace-runtime/main.js +0 -10
  104. package/dist/workspace-runtime/persistence/journal.js +0 -365
  105. package/dist/workspace-runtime/persistence/native-subagents.js +0 -162
  106. package/dist/workspace-runtime/persistence/sqlite-client.js +0 -77
  107. package/dist/workspace-runtime/persistence/sqlite-worker.js +0 -1161
  108. package/dist/workspace-runtime/persistence/stopped-execution.js +0 -77
  109. package/dist/workspace-runtime/previews/output.js +0 -36
  110. package/dist/workspace-runtime/previews/publish.js +0 -262
  111. package/dist/workspace-runtime/previews/single-file.js +0 -43
  112. package/dist/workspace-runtime/processes/managed-writes.js +0 -69
  113. package/dist/workspace-runtime/production/content-changes.js +0 -36
  114. package/dist/workspace-runtime/production/contracts.js +0 -19
  115. package/dist/workspace-runtime/production/controller.js +0 -218
  116. package/dist/workspace-runtime/production/dependencies.js +0 -60
  117. package/dist/workspace-runtime/production/native.js +0 -212
  118. package/dist/workspace-runtime/production/processes.js +0 -102
  119. package/dist/workspace-runtime/production/save-command.js +0 -35
  120. package/dist/workspace-runtime/production/source-archive.js +0 -137
  121. package/dist/workspace-runtime/production/source-copy.js +0 -91
  122. package/dist/workspace-runtime/production/stages.js +0 -334
  123. package/dist/workspace-runtime/production/storage.js +0 -75
  124. package/dist/workspace-runtime/serve.js +0 -214
  125. package/dist/workspace-runtime/server/auth.js +0 -21
  126. package/dist/workspace-runtime/server/connectivity.js +0 -201
  127. package/dist/workspace-runtime/server/desktop-host.js +0 -429
  128. package/dist/workspace-runtime/server/desktop-preview-config.js +0 -81
  129. package/dist/workspace-runtime/server/desktop-preview-service.js +0 -157
  130. package/dist/workspace-runtime/server/desktop-preview-static.js +0 -53
  131. package/dist/workspace-runtime/server/desktop-preview.js +0 -189
  132. package/dist/workspace-runtime/server/dispatch.js +0 -1217
  133. package/dist/workspace-runtime/server/endpoints.js +0 -21
  134. package/dist/workspace-runtime/server/execution-group.js +0 -90
  135. package/dist/workspace-runtime/server/http.js +0 -1243
  136. package/dist/workspace-runtime/server/lock.js +0 -53
  137. package/dist/workspace-runtime/server/rfb-watch-gate.js +0 -168
  138. package/dist/workspace-runtime/server/source-uploader.js +0 -138
  139. package/dist/workspace-runtime/server/terminal-host.js +0 -366
  140. package/dist/workspace-runtime/server/types.js +0 -1
  141. package/dist/workspace-runtime/server/workspace-transfer.js +0 -99
  142. package/dist/workspace-runtime/workspaces/archive.js +0 -207
  143. package/dist/workspace-runtime/workspaces/artifact-events.js +0 -55
  144. package/dist/workspace-runtime/workspaces/artifact-files.js +0 -197
  145. package/dist/workspace-runtime/workspaces/artifact-receipts.js +0 -89
  146. package/dist/workspace-runtime/workspaces/artifacts.js +0 -282
  147. package/dist/workspace-runtime/workspaces/attachment-content.js +0 -123
  148. package/dist/workspace-runtime/workspaces/attachments.js +0 -185
  149. package/dist/workspace-runtime/workspaces/batch-commit.js +0 -142
  150. package/dist/workspace-runtime/workspaces/cli-context.js +0 -23
  151. package/dist/workspace-runtime/workspaces/cli-credentials.js +0 -111
  152. package/dist/workspace-runtime/workspaces/composer-context.js +0 -147
  153. package/dist/workspace-runtime/workspaces/context-error.js +0 -2
  154. package/dist/workspace-runtime/workspaces/dependencies.js +0 -264
  155. package/dist/workspace-runtime/workspaces/developer-command.js +0 -64
  156. package/dist/workspace-runtime/workspaces/directories.js +0 -133
  157. package/dist/workspace-runtime/workspaces/directory-access.js +0 -37
  158. package/dist/workspace-runtime/workspaces/directory-attachments.js +0 -245
  159. package/dist/workspace-runtime/workspaces/directory-maintenance.js +0 -111
  160. package/dist/workspace-runtime/workspaces/directory-stats.js +0 -49
  161. package/dist/workspace-runtime/workspaces/disk-snapshot.js +0 -31
  162. package/dist/workspace-runtime/workspaces/empty-template.js +0 -42
  163. package/dist/workspace-runtime/workspaces/files-search.js +0 -116
  164. package/dist/workspace-runtime/workspaces/files.js +0 -138
  165. package/dist/workspace-runtime/workspaces/git-credential-broker.js +0 -312
  166. package/dist/workspace-runtime/workspaces/git-credential-helper.js +0 -68
  167. package/dist/workspace-runtime/workspaces/git-direct-store.js +0 -155
  168. package/dist/workspace-runtime/workspaces/git.js +0 -670
  169. package/dist/workspace-runtime/workspaces/instructions.js +0 -56
  170. package/dist/workspace-runtime/workspaces/json5-edit.js +0 -170
  171. package/dist/workspace-runtime/workspaces/library-attachments.js +0 -268
  172. package/dist/workspace-runtime/workspaces/optional-platform-skills.js +0 -14
  173. package/dist/workspace-runtime/workspaces/parameters.js +0 -272
  174. package/dist/workspace-runtime/workspaces/prepare.js +0 -278
  175. package/dist/workspace-runtime/workspaces/registry.js +0 -251
  176. package/dist/workspace-runtime/workspaces/remix-references.js +0 -301
  177. package/dist/workspace-runtime/workspaces/revisions.js +0 -31
  178. package/dist/workspace-runtime/workspaces/session-image.js +0 -104
  179. package/dist/workspace-runtime/workspaces/shared-git.js +0 -40
  180. package/dist/workspace-runtime/workspaces/skill-artifact-loader.js +0 -95
  181. package/dist/workspace-runtime/workspaces/skills.js +0 -191
  182. package/dist/workspace-runtime/workspaces/transfer.js +0 -288
  183. package/dist/workspace-runtime/workspaces/turn-changes.js +0 -75
  184. package/dist/workspace-runtime/workspaces/video-attachments.js +0 -49
  185. package/project-template/.claude/skills/playcraft-replica-validation/SKILL.md +0 -25
  186. package/project-template/.claude/skills/playcraft-replica-validation/references/contracts.md +0 -79
  187. package/project-template/.claude/skills/playcraft-video-understanding/SKILL.md +0 -29
  188. package/project-template/.claude/skills/playcraft-video-understanding/references/contracts.md +0 -37
  189. package/project-template-v2/.claude/agents/artist.md +0 -82
  190. package/project-template-v2/.claude/agents/developer.md +0 -153
  191. package/project-template-v2/.claude/agents/game-designer.md +0 -264
  192. package/project-template-v2/.claude/agents/refs/artist-art-style-catalog.md +0 -533
  193. package/project-template-v2/.claude/agents/refs/artist-color-audio-recipes.md +0 -153
  194. package/project-template-v2/.claude/agents/refs/artist-dimension-axis.md +0 -27
  195. package/project-template-v2/.claude/agents/refs/artist-master-composite-recipes.md +0 -208
  196. package/project-template-v2/.claude/agents/refs/atom-skill-library.md +0 -81
  197. package/project-template-v2/.claude/agents/refs/developer-impl-cookbook.md +0 -432
  198. package/project-template-v2/.claude/agents/refs/framework-5-component-filter.md +0 -252
  199. package/project-template-v2/.claude/agents/refs/framework-game-feel-juice.md +0 -266
  200. package/project-template-v2/.claude/agents/refs/framework-mda.md +0 -147
  201. package/project-template-v2/.claude/agents/refs/game-designer-gameplay-sufficiency.md +0 -123
  202. package/project-template-v2/.claude/agents/refs/ta-3d-flip-recipe.md +0 -88
  203. package/project-template-v2/.claude/agents/refs/ta-atlas-deliverable-standard.md +0 -67
  204. package/project-template-v2/.claude/agents/refs/ta-batch-pipeline-recipes.md +0 -120
  205. package/project-template-v2/.claude/agents/refs/ta-image-generation-detail.md +0 -300
  206. package/project-template-v2/.claude/agents/refs/ta-image-ops-reference.md +0 -495
  207. package/project-template-v2/.claude/agents/refs/ta-pipeline-cookbook.md +0 -1141
  208. package/project-template-v2/.claude/agents/refs/ta-tools-reference.md +0 -111
  209. package/project-template-v2/.claude/agents/refs/ta-vfx-preset-catalog.md +0 -365
  210. package/project-template-v2/.claude/agents/refs/threejs-cannon-pitfalls.md +0 -412
  211. package/project-template-v2/.claude/agents/reviewer.md +0 -75
  212. package/project-template-v2/.claude/agents/technical-artist.md +0 -86
  213. package/project-template-v2/.claude/hooks/snapshot-milestone.mjs +0 -243
  214. package/project-template-v2/.claude/hooks/user-prompt.mjs +0 -133
  215. package/project-template-v2/.claude/settings.json +0 -33
  216. package/project-template-v2/.claude/skills/brainstorming/SKILL.md +0 -161
  217. package/project-template-v2/.claude/skills/brainstorming/scripts/frame-template.html +0 -270
  218. package/project-template-v2/.claude/skills/brainstorming/scripts/helper.js +0 -177
  219. package/project-template-v2/.claude/skills/brainstorming/scripts/server.cjs +0 -354
  220. package/project-template-v2/.claude/skills/brainstorming/scripts/start-server.sh +0 -148
  221. package/project-template-v2/.claude/skills/brainstorming/scripts/stop-server.sh +0 -56
  222. package/project-template-v2/.claude/skills/brainstorming/scripts/wait-for-selection.sh +0 -62
  223. package/project-template-v2/.claude/skills/brainstorming/spec-document-reviewer-prompt.md +0 -49
  224. package/project-template-v2/.claude/skills/brainstorming/visual-companion.md +0 -309
  225. package/project-template-v2/.claude/skills/playcraft-ad-psychology/SKILL.md +0 -182
  226. package/project-template-v2/.claude/skills/playcraft-art-style-guide/SKILL.md +0 -123
  227. package/project-template-v2/.claude/skills/playcraft-asset-state-sheet/SKILL.md +0 -205
  228. package/project-template-v2/.claude/skills/playcraft-audio-generation/SKILL.md +0 -280
  229. package/project-template-v2/.claude/skills/playcraft-batch-pipeline/SKILL.md +0 -184
  230. package/project-template-v2/.claude/skills/playcraft-build-optimizer/SKILL.md +0 -306
  231. package/project-template-v2/.claude/skills/playcraft-image-generation/SKILL.md +0 -298
  232. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/build-sprite-sheet.template.mjs +0 -123
  233. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/compare-style.template.mjs +0 -254
  234. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/gen-batch-sprite.template.mjs +0 -324
  235. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/gen-batch.template.mjs +0 -97
  236. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/gen-edit-variants.template.mjs +0 -118
  237. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/process-batch.template.mjs +0 -137
  238. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/prompt-cookbook.md +0 -397
  239. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/validate-sprite-sheet.template.mjs +0 -296
  240. package/project-template-v2/.claude/skills/playcraft-image-ops/SKILL.md +0 -122
  241. package/project-template-v2/.claude/skills/playcraft-image-processing/SKILL.md +0 -219
  242. package/project-template-v2/.claude/skills/playcraft-masking/SKILL.md +0 -373
  243. package/project-template-v2/.claude/skills/playcraft-playable-optimization/SKILL.md +0 -161
  244. package/project-template-v2/.claude/skills/playcraft-research/SKILL.md +0 -215
  245. package/project-template-v2/.claude/skills/playcraft-skill-recommender/SKILL.md +0 -382
  246. package/project-template-v2/.claude/skills/playcraft-sprite-generation/SKILL.md +0 -423
  247. package/project-template-v2/.claude/skills/playcraft-sprite-remix/SKILL.md +0 -158
  248. package/project-template-v2/.claude/skills/playcraft-sprite-sheet/SKILL.md +0 -100
  249. package/project-template-v2/.claude/skills/playcraft-storyboard/SKILL.md +0 -167
  250. package/project-template-v2/.claude/skills/playcraft-style-qa/SKILL.md +0 -270
  251. package/project-template-v2/.claude/skills/playcraft-text-rendering/SKILL.md +0 -236
  252. package/project-template-v2/.claude/skills/playcraft-vfx-animation/SKILL.md +0 -130
  253. package/project-template-v2/.claude/skills/playwright-cli/SKILL.md +0 -390
  254. package/project-template-v2/.claude/skills/playwright-cli/references/element-attributes.md +0 -23
  255. package/project-template-v2/.claude/skills/playwright-cli/references/playwright-tests.md +0 -39
  256. package/project-template-v2/.claude/skills/playwright-cli/references/request-mocking.md +0 -87
  257. package/project-template-v2/.claude/skills/playwright-cli/references/running-code.md +0 -240
  258. package/project-template-v2/.claude/skills/playwright-cli/references/session-management.md +0 -226
  259. package/project-template-v2/.claude/skills/playwright-cli/references/spec-driven-testing.md +0 -308
  260. package/project-template-v2/.claude/skills/playwright-cli/references/storage-state.md +0 -275
  261. package/project-template-v2/.claude/skills/playwright-cli/references/test-generation.md +0 -134
  262. package/project-template-v2/.claude/skills/playwright-cli/references/tracing.md +0 -142
  263. package/project-template-v2/.claude/skills/playwright-cli/references/video-recording.md +0 -153
  264. package/project-template-v2/.claude/skills/session-analyzer/SKILL.md +0 -386
  265. package/project-template-v2/.claude/skills/session-analyzer/scripts/execution-breakdown.mjs +0 -182
  266. package/project-template-v2/.claude/skills/session-analyzer/scripts/find-turns.mjs +0 -72
  267. package/project-template-v2/.claude/skills/session-analyzer/scripts/heavy-output.mjs +0 -121
  268. package/project-template-v2/.claude/skills/session-analyzer/scripts/resolve-session.mjs +0 -102
  269. package/project-template-v2/.claude/skills/session-analyzer/scripts/subagent-stats.mjs +0 -127
  270. package/project-template-v2/.claude/skills/session-analyzer/scripts/subagent-tool-timeline.mjs +0 -106
  271. package/project-template-v2/.claude/skills/session-analyzer/scripts/time-gaps.mjs +0 -128
  272. package/project-template-v2/.claude/skills/session-analyzer/scripts/turn-timeline.mjs +0 -67
  273. package/project-template-v2/.claude/snapshot.mjs +0 -263
  274. package/project-template-v2/.playcraft/skills.lock.json +0 -152
  275. package/project-template-v2/CLAUDE.md +0 -146
  276. package/project-template-v2/assets/audio/bgm/.gitkeep +0 -0
  277. package/project-template-v2/assets/audio/sfx/.gitkeep +0 -0
  278. package/project-template-v2/assets/bundles/.gitkeep +0 -0
  279. package/project-template-v2/assets/images/bg/.gitkeep +0 -0
  280. package/project-template-v2/assets/images/reference/.gitkeep +0 -0
  281. package/project-template-v2/assets/images/storyboard/.gitkeep +0 -0
  282. package/project-template-v2/assets/images/tiles/.gitkeep +0 -0
  283. package/project-template-v2/assets/images/ui/.gitkeep +0 -0
  284. package/project-template-v2/assets/images/vfx/.gitkeep +0 -0
  285. package/project-template-v2/assets/models/.gitkeep +0 -0
  286. package/project-template-v2/docs/harness/iteration-1/context-flow.md +0 -254
  287. package/project-template-v2/docs/harness/iteration-1/generate-flow.md +0 -91
  288. package/project-template-v2/docs/harness/iteration-1/ideate-flow.md +0 -214
  289. package/project-template-v2/docs/harness/iteration-1/optimize-flow.md +0 -75
  290. package/project-template-v2/docs/harness/iteration-1/wrapup-flow.md +0 -63
  291. package/project-template-v2/docs/harness/iteration-2/context-flow.md +0 -223
  292. package/project-template-v2/docs/harness/iteration-2/generate-flow.md +0 -129
  293. package/project-template-v2/docs/harness/iteration-2/ideate-flow.md +0 -267
  294. package/project-template-v2/docs/harness/iteration-2/optimize-flow.md +0 -164
  295. package/project-template-v2/docs/harness/iteration-2/wrapup-flow.md +0 -115
  296. package/project-template-v2/docs/harness/orchestrator-flow.md +0 -364
  297. package/project-template-v2/docs/project-state.json +0 -60
  298. package/project-template-v2/docs/project-state.md +0 -72
  299. package/project-template-v2/docs/standards/README.md +0 -225
  300. package/project-template-v2/docs/standards/agent-behavior-standards.md +0 -174
  301. package/project-template-v2/docs/standards/artifacts/design-brief.md +0 -19
  302. package/project-template-v2/docs/standards/artifacts/design.md +0 -22
  303. package/project-template-v2/docs/standards/artifacts/game-code.md +0 -41
  304. package/project-template-v2/docs/standards/artifacts/todo-list.md +0 -41
  305. package/project-template-v2/docs/standards/iter1-agent-behavior-standards.md +0 -343
  306. package/project-template-v2/game/index.ts +0 -18
  307. package/project-template-v2/globals.d.ts +0 -51
  308. package/project-template-v2/index.css +0 -34
  309. package/project-template-v2/index.html +0 -18
  310. package/project-template-v2/main.ts +0 -9
  311. package/project-template-v2/package.json +0 -46
  312. package/project-template-v2/skills/_shared/scripts/dispatch-clear.mjs +0 -31
  313. package/project-template-v2/skills/_shared/scripts/dispatch-set.mjs +0 -86
  314. package/project-template-v2/skills/_shared/scripts/dod-check.mjs +0 -153
  315. package/project-template-v2/skills/_shared/scripts/handoff-append.mjs +0 -70
  316. package/project-template-v2/skills/_shared/scripts/lib/validator-artifacts.mjs +0 -91
  317. package/project-template-v2/skills/_shared/scripts/pipeline/dod-config.mjs +0 -131
  318. package/project-template-v2/skills/_shared/scripts/pipeline/index.mjs +0 -80
  319. package/project-template-v2/skills/_shared/scripts/pipeline/iteration-1-core.mjs +0 -90
  320. package/project-template-v2/skills/_shared/scripts/pipeline/iteration-2-wrap.mjs +0 -93
  321. package/project-template-v2/skills/_shared/scripts/pipeline/iteration-3-visual.mjs +0 -23
  322. package/project-template-v2/skills/_shared/scripts/render-project-state.mjs +0 -230
  323. package/project-template-v2/skills/_shared/scripts/state-advance-stage.mjs +0 -58
  324. package/project-template-v2/skills/_shared/scripts/state-get.mjs +0 -339
  325. package/project-template-v2/skills/_shared/scripts/state-handoff.mjs +0 -39
  326. package/project-template-v2/skills/_shared/scripts/state-set.mjs +0 -94
  327. package/project-template-v2/skills/_shared/scripts/state-store.mjs +0 -783
  328. package/project-template-v2/skills/_shared/scripts/todo-add.mjs +0 -88
  329. package/project-template-v2/skills/_shared/scripts/todo-get.mjs +0 -130
  330. package/project-template-v2/skills/_shared/scripts/todo-remove.mjs +0 -33
  331. package/project-template-v2/skills/_shared/scripts/todo-set.mjs +0 -47
  332. package/project-template-v2/skills/_shared/scripts/verify-asset-code-sync.mjs +0 -268
  333. package/project-template-v2/skills/_shared/scripts/verify-env.mjs +0 -285
  334. package/project-template-v2/skills/_shared/scripts/verify-placeholders.mjs +0 -161
  335. package/project-template-v2/skills/playable-autoplay/SKILL.md +0 -176
  336. package/project-template-v2/skills/playable-autoplay/agents/openai.yaml +0 -4
  337. package/project-template-v2/skills/playable-debug/SKILL.md +0 -116
  338. package/project-template-v2/skills/playable-debug/agents/openai.yaml +0 -4
  339. package/project-template-v2/skills/playable-debug/references/debug-config.md +0 -40
  340. package/project-template-v2/skills/playable-record/SKILL.md +0 -140
  341. package/project-template-v2/skills/playable-record/scripts/lib/dev-server.mjs +0 -104
  342. package/project-template-v2/skills/playable-record/scripts/lib/record-audio-bridge.js +0 -141
  343. package/project-template-v2/skills/playable-record/scripts/record-playable.mjs +0 -425
  344. package/project-template-v2/skills/playable-record/scripts/verify-contract.mjs +0 -261
  345. package/project-template-v2/skills/playable-record/scripts/verify-firstwin.mjs +0 -398
  346. package/project-template-v2/skills/playable-record/scripts/verify-fusion.mjs +0 -322
  347. package/project-template-v2/skills/playable-record/scripts/verify-lifecycle.mjs +0 -105
  348. package/project-template-v2/skills/playable-record/scripts/verify-vlm-video.mjs +0 -230
  349. package/project-template-v2/skills/playable-validate/SKILL.md +0 -61
  350. package/project-template-v2/skills/playable-validate/validation-rules.md +0 -14
  351. package/project-template-v2/skills/playable-verify-ui/SKILL.md +0 -63
  352. package/project-template-v2/skills/playable-verify-ui/scripts/lib/__init__.py +0 -1
  353. package/project-template-v2/skills/playable-verify-ui/scripts/lib/config_expr.js +0 -218
  354. package/project-template-v2/skills/playable-verify-ui/scripts/lib/node_utils.js +0 -51
  355. package/project-template-v2/skills/playable-verify-ui/scripts/lib/profile_loader.py +0 -59
  356. package/project-template-v2/skills/playable-verify-ui/scripts/lib/report.py +0 -106
  357. package/project-template-v2/skills/playable-verify-ui/scripts/lib/runtime_contract.js +0 -116
  358. package/project-template-v2/skills/playable-verify-ui/scripts/verify_fetch_antipatterns.py +0 -59
  359. package/project-template-v2/skills/playable-verify-ui/scripts/verify_hardcoded_layout.js +0 -64
  360. package/project-template-v2/skills/playable-verify-ui/scripts/verify_runtime_contract.js +0 -58
  361. package/project-template-v2/skills/playable-verify-ui/scripts/verify_text_overlap.py +0 -333
  362. package/project-template-v2/ta-workspace/scripts/.gitkeep +0 -0
  363. package/project-template-v2/tsconfig.json +0 -20
  364. package/project-template-v2/vite.config.ts +0 -27
  365. package/project-templates/batch-locks/phaser.yaml +0 -922
  366. package/project-templates/batch-locks/threejs.yaml +0 -978
  367. package/project-templates/empty/AGENTS.md +0 -41
  368. package/project-templates/empty/gitignore +0 -33
@@ -1,137 +0,0 @@
1
- import { execFile } from 'node:child_process';
2
- import { createHash } from 'node:crypto';
3
- import { lstat, mkdir, mkdtemp, readFile, readdir, rename, rm, writeFile } from 'node:fs/promises';
4
- import { dirname, join } from 'node:path';
5
- import { promisify } from 'node:util';
6
- import { gzip, gunzip } from 'node:zlib';
7
- import { z } from 'zod';
8
- import { batchSourceSnapshotSchema } from '@playcraft/common/cloud-workspace/runtime';
9
- import { copyProductionSource } from './source-copy.js';
10
- const execute = promisify(execFile);
11
- const compress = promisify(gzip), decompress = promisify(gunzip);
12
- const hash = (body) => createHash('sha256').update(body).digest('hex');
13
- const base64 = z.string().refine(value => Buffer.from(value, 'base64').toString('base64') === value);
14
- const archiveSchema = z.object({
15
- version: z.literal(1),
16
- source: z.object({ batchId: z.string().uuid(), sourceWorkspaceId: z.string().uuid(), sourceDirectoryName: z.string(),
17
- snapshotId: z.string(), commitHash: z.string(), checksum: z.string(), frozenAt: z.string() }).strict(),
18
- bundle: base64,
19
- artifacts: z.array(z.object({ path: z.string().regex(/^\.playcraft\/skill-artifacts\/[a-f0-9]{64}\.tar\.gz$/),
20
- body: base64, mode: z.number().int().min(0).max(0o777) }).strict()),
21
- }).strict();
22
- async function git(root, ...args) {
23
- return (await execute('git', ['-c', 'core.hooksPath=/dev/null', '-c', 'init.templateDir=', '-c', 'credential.helper=',
24
- '-c', 'core.fsmonitor=false', '-C', root, ...args], { encoding: 'buffer', timeout: 90_000, maxBuffer: 64 * 1024 * 1024,
25
- env: { PATH: process.env.PATH, HOME: dirname(root), GIT_CONFIG_GLOBAL: '/dev/null', GIT_CONFIG_NOSYSTEM: '1', GIT_TERMINAL_PROMPT: '0' } })).stdout;
26
- }
27
- function snapshotFromArchive(source, body) {
28
- const archiveDigest = hash(body);
29
- return batchSourceSnapshotSchema.parse({ ...source, archiveDigest,
30
- objectKey: `workspaces/${source.sourceWorkspaceId}/batches/${source.batchId}/source/${archiveDigest}.json.gz` });
31
- }
32
- async function readArchive(body, expected) {
33
- if (expected && hash(body) !== expected.archiveDigest)
34
- throw new Error('production_source_archive_mismatch');
35
- const archive = archiveSchema.parse(JSON.parse((await decompress(body, { maxOutputLength: 512 * 1024 * 1024 })).toString()));
36
- const snapshot = snapshotFromArchive(archive.source, body);
37
- if (expected && JSON.stringify(snapshot) !== JSON.stringify(batchSourceSnapshotSchema.parse(expected)))
38
- throw new Error('production_source_archive_mismatch');
39
- const paths = new Set();
40
- for (const artifact of archive.artifacts) {
41
- if (paths.has(artifact.path) || hash(Buffer.from(artifact.body, 'base64')) !== artifact.path.split('/').at(-1).slice(0, -7))
42
- throw new Error('production_source_archive_mismatch');
43
- paths.add(artifact.path);
44
- }
45
- return { archive, snapshot };
46
- }
47
- export async function cachedProductionSourceArchive(input) {
48
- z.string().uuid().parse(input.batchId);
49
- z.string().uuid().parse(input.sourceWorkspaceId);
50
- const cache = join(input.cacheRoot, `${input.batchId}.json.gz`);
51
- const cached = await readFile(cache).catch(error => { if (error.code !== 'ENOENT')
52
- throw error; return undefined; });
53
- if (cached) {
54
- const { snapshot } = await readArchive(cached);
55
- if (snapshot.batchId !== input.batchId || snapshot.sourceWorkspaceId !== input.sourceWorkspaceId
56
- || snapshot.sourceDirectoryName !== input.sourceDirectoryName)
57
- throw new Error('production_source_archive_mismatch');
58
- return { snapshot, body: cached };
59
- }
60
- return undefined;
61
- }
62
- /** A source archive is raw input, so package and dependency locks are validated by each result later. */
63
- export async function captureProductionSourceArchive(input) {
64
- const cached = await cachedProductionSourceArchive(input);
65
- if (cached)
66
- return cached;
67
- await mkdir(input.cacheRoot, { recursive: true, mode: 0o700 });
68
- const cache = join(input.cacheRoot, `${input.batchId}.json.gz`);
69
- const temporary = await mkdtemp(join(input.cacheRoot, '.capture-'));
70
- try {
71
- const root = join(temporary, 'source');
72
- const captured = await copyProductionSource({ source: input.source, destination: root,
73
- branchName: 'source', repositoryUrl: 'https://snapshot.invalid/source.git' });
74
- const bundle = join(temporary, 'source.bundle');
75
- await git(root, 'bundle', 'create', bundle, 'HEAD');
76
- const artifacts = [];
77
- for (const name of (await readdir(join(root, '.playcraft/skill-artifacts')).catch(error => {
78
- if (error.code !== 'ENOENT')
79
- throw error;
80
- return [];
81
- })).sort()) {
82
- if (!/^[a-f0-9]{64}\.tar\.gz$/.test(name))
83
- continue;
84
- const path = `.playcraft/skill-artifacts/${name}`, stat = await lstat(join(root, path));
85
- if (!stat.isFile() || stat.isSymbolicLink())
86
- throw new Error('production_source_path_invalid');
87
- const body = await readFile(join(root, path));
88
- if (hash(body) !== name.slice(0, -7))
89
- throw new Error('production_source_archive_mismatch');
90
- artifacts.push({ path, mode: stat.mode & 0o777, body: body.toString('base64') });
91
- }
92
- const archive = { version: 1, source: { batchId: input.batchId, sourceWorkspaceId: input.sourceWorkspaceId,
93
- sourceDirectoryName: input.sourceDirectoryName, snapshotId: `${input.batchId}-source`, commitHash: captured.commitHash,
94
- checksum: hash(await git(root, 'ls-tree', '-r', '--full-tree', '-z', captured.commitHash)),
95
- frozenAt: new Date((await git(root, 'show', '-s', '--format=%cI', captured.commitHash)).toString().trim()).toISOString() },
96
- bundle: (await readFile(bundle)).toString('base64'), artifacts };
97
- const body = await compress(Buffer.from(JSON.stringify(archive)));
98
- const snapshot = snapshotFromArchive(archive.source, body);
99
- const staged = join(temporary, 'archive.json.gz');
100
- await writeFile(staged, body, { mode: 0o600 });
101
- await rename(staged, cache);
102
- return { snapshot, body };
103
- }
104
- finally {
105
- await rm(temporary, { recursive: true, force: true });
106
- }
107
- }
108
- /** Restore only the captured root commit and its exact skill archives, independently of the live source. */
109
- export async function checkoutProductionSourceArchive(input) {
110
- const { archive } = await readArchive(input.body, input.snapshot);
111
- if (await lstat(input.destination).catch(() => undefined))
112
- throw new Error('batch_snapshot_destination_exists');
113
- await mkdir(dirname(input.destination), { recursive: true });
114
- const temporary = await mkdtemp(join(dirname(input.destination), '.source-bundle-'));
115
- try {
116
- const bundle = join(temporary, 'source.bundle');
117
- await writeFile(bundle, Buffer.from(archive.bundle, 'base64'), { mode: 0o600 });
118
- await execute('git', ['-c', 'core.hooksPath=/dev/null', '-c', 'init.templateDir=', 'clone', '--no-checkout', '--', bundle, input.destination], {
119
- timeout: 90_000, maxBuffer: 1024 * 1024,
120
- env: { PATH: process.env.PATH, HOME: temporary, GIT_CONFIG_GLOBAL: '/dev/null', GIT_CONFIG_NOSYSTEM: '1', GIT_TERMINAL_PROMPT: '0' },
121
- });
122
- if (hash(await git(input.destination, 'ls-tree', '-r', '--full-tree', '-z', input.snapshot.commitHash)) !== input.snapshot.checksum)
123
- throw new Error('production_source_archive_mismatch');
124
- await git(input.destination, 'checkout', '--detach', input.snapshot.commitHash);
125
- for (const artifact of archive.artifacts) {
126
- await mkdir(dirname(join(input.destination, artifact.path)), { recursive: true });
127
- await writeFile(join(input.destination, artifact.path), Buffer.from(artifact.body, 'base64'), { flag: 'wx', mode: artifact.mode });
128
- }
129
- }
130
- catch (error) {
131
- await rm(input.destination, { recursive: true, force: true });
132
- throw error;
133
- }
134
- finally {
135
- await rm(temporary, { recursive: true, force: true });
136
- }
137
- }
@@ -1,91 +0,0 @@
1
- import { execFile } from 'node:child_process';
2
- import { createHash, randomUUID } from 'node:crypto';
3
- import { constants } from 'node:fs';
4
- import { lstat, mkdir, open, readdir, realpath, rename, rm, writeFile } from 'node:fs/promises';
5
- import { dirname, isAbsolute, join, relative } from 'node:path';
6
- import { promisify } from 'node:util';
7
- import { excludeFromGit } from '../workspaces/dependencies.js';
8
- const execute = promisify(execFile);
9
- const privateNames = new Set(['.git', 'node_modules', '.ssh', '.aws', '.azure', '.gnupg', '.codex', '.npm', '.pnpm-store',
10
- '.config', '.npmrc', '.yarnrc', '.yarnrc.yml', '.netrc', '.git-credentials', 'credentials.json', 'credentials', 'secrets',
11
- 'id_rsa', 'id_ed25519', 'settings.local.json', '.playcraft.json', 'playcraft.config.json', '.playcraft-temp', '.playcraft-preview-out']);
12
- const engineering = (path) => !path.split('/').some(name => privateNames.has(name)
13
- || /^\.env(?:$|\.)/.test(name) || /\.(?:pem|key|p12|pfx)$/.test(name)) && !path.startsWith('.playcraft/skills/');
14
- const inside = (root, path) => { const rel = relative(root, path); return rel !== '..' && !rel.startsWith('../') && !isAbsolute(rel); };
15
- const git = async (root, ...args) => (await execute('git', ['-c', 'core.hooksPath=/dev/null', '-c', 'credential.helper=',
16
- '-c', 'core.fsmonitor=false', '-C', root, ...args], { timeout: 90_000, maxBuffer: 64 * 1024 * 1024,
17
- env: { PATH: process.env.PATH, HOME: dirname(root), GIT_OPTIONAL_LOCKS: '0', GIT_CONFIG_GLOBAL: '/dev/null', GIT_CONFIG_NOSYSTEM: '1', GIT_TERMINAL_PROMPT: '0' } })).stdout;
18
- async function sourceFiles(root) {
19
- const paths = new Set((await git(root, 'ls-files', '--cached', '--others', '--exclude-standard', '-z')).split('\0').filter(Boolean));
20
- const artifacts = await readdir(join(root, '.playcraft/skill-artifacts')).catch(error => {
21
- if (error.code === 'ENOENT')
22
- return [];
23
- throw error;
24
- });
25
- for (const name of artifacts)
26
- if (/^[a-f0-9]{64}\.tar\.gz$/.test(name))
27
- paths.add(`.playcraft/skill-artifacts/${name}`);
28
- const files = new Map();
29
- for (const path of [...paths].sort()) {
30
- if (!engineering(path))
31
- continue;
32
- const full = join(root, path), stat = await lstat(full).catch(error => {
33
- if (error.code === 'ENOENT')
34
- return undefined;
35
- throw error;
36
- });
37
- if (!stat)
38
- continue;
39
- if (!stat.isFile() || stat.isSymbolicLink() || !inside(root, await realpath(full)))
40
- throw new Error('production_source_path_invalid');
41
- const handle = await open(full, constants.O_RDONLY | constants.O_NOFOLLOW);
42
- try {
43
- files.set(path, { body: await handle.readFile(), mode: stat.mode & 0o777 });
44
- }
45
- finally {
46
- await handle.close();
47
- }
48
- }
49
- return files;
50
- }
51
- function digest(files) {
52
- const hash = createHash('sha256');
53
- for (const [path, file] of files)
54
- hash.update(path).update('\0').update(String(file.mode)).update('\0').update(file.body).update('\0');
55
- return hash.digest('hex');
56
- }
57
- /** Capture the selected working tree into a private branch; the source index, head and configuration are read only. */
58
- export async function copyProductionSource(input) {
59
- const source = await realpath(input.source);
60
- if (await lstat(input.destination).catch(() => undefined))
61
- throw new Error('production_source_destination_exists');
62
- const sourceHead = await git(source, 'rev-parse', '--verify', 'HEAD').catch(() => '');
63
- const files = await sourceFiles(source), sourceDigest = digest(files);
64
- const staging = join(dirname(input.destination), `.production-source-${randomUUID()}`);
65
- try {
66
- await mkdir(staging);
67
- // An independent root commit also excludes credentials that existed only in source history.
68
- await git(staging, '-c', 'init.templateDir=', 'init', '--initial-branch', input.branchName);
69
- for (const [path, file] of files) {
70
- await mkdir(dirname(join(staging, path)), { recursive: true });
71
- await writeFile(join(staging, path), file.body, { mode: file.mode, flag: 'wx' });
72
- }
73
- if (sourceHead !== await git(source, 'rev-parse', '--verify', 'HEAD').catch(() => '') || sourceDigest !== digest(await sourceFiles(source)))
74
- throw new Error('production_source_changed');
75
- await git(staging, 'remote', 'add', 'origin', input.repositoryUrl);
76
- await git(staging, 'config', 'user.name', 'PlayCraft');
77
- await git(staging, 'config', 'user.email', 'playcraft@localhost');
78
- await excludeFromGit(staging);
79
- await excludeFromGit(staging, '/.env*');
80
- for (const entry of ['/.playcraft.json', '/playcraft.config.json', '/.playcraft/skill-artifacts/', '/.playcraft/skills/', '/.playcraft-temp/'])
81
- await excludeFromGit(staging, entry);
82
- await git(staging, 'add', '-A', '.');
83
- await git(staging, 'commit', '--allow-empty', '-m', 'Capture batch source in independent remix');
84
- const commitHash = (await git(staging, 'rev-parse', 'HEAD')).trim();
85
- await rename(staging, input.destination);
86
- return { commitHash, sourceDigest };
87
- }
88
- finally {
89
- await rm(staging, { recursive: true, force: true });
90
- }
91
- }
@@ -1,334 +0,0 @@
1
- import { execFile } from 'node:child_process';
2
- import { createHash } from 'node:crypto';
3
- import { lstat, mkdir, readFile, readdir, rm, writeFile } from 'node:fs/promises';
4
- import { dirname, join } from 'node:path';
5
- import { promisify } from 'node:util';
6
- import { batchBaselineSnapshotSchema, batchSavedEvidenceSchema, batchSourceSnapshotSchema } from '@playcraft/common/cloud-workspace/runtime';
7
- import { checkoutBatchCommit, freezeBatchCommit } from '../workspaces/batch-commit.js';
8
- import { grantWorkspaceTraversal, openDirectoryToDevelopment } from '../workspaces/directory-access.js';
9
- import { registerWorkspaceDirectory } from '../workspaces/directories.js';
10
- import { workspaceContainerRoot, workspaceRoot, workspaceSessions } from '../workspaces/registry.js';
11
- import { excludeFromGit, settleDependencyInstalls } from '../workspaces/dependencies.js';
12
- import { resolveProductionDependencies, verifyProductionDependencyLocks } from './dependencies.js';
13
- import { productionLocalCommitCommand } from './save-command.js';
14
- import { productionOutputStore, verifyProductionLease } from './storage.js';
15
- import { runProductionCommand } from './processes.js';
16
- import { runProductionNative } from './native.js';
17
- import { classifyPreviewOutput, findExternalLocalReferences } from '../previews/single-file.js';
18
- import { copyProductionSource } from './source-copy.js';
19
- import { workspaceDirectoryOccupied } from '../workspaces/directory-maintenance.js';
20
- import { ensureWorkspaceSkills } from '../workspaces/skills.js';
21
- import { cachedProductionSourceArchive, captureProductionSourceArchive } from './source-archive.js';
22
- import { prepareWorkspaceCheckout } from '../workspaces/prepare.js';
23
- import { snapshotWorkspaceTree } from '../workspaces/git.js';
24
- import { requireProductionContentChanges } from './content-changes.js';
25
- const execute = promisify(execFile);
26
- const quote = (value) => `'${value.replaceAll("'", "'\\''")}'`;
27
- const hash = (body) => createHash('sha256').update(body).digest('hex');
28
- const record = (value) => value && typeof value === 'object' ? value : {};
29
- export async function executeProductionStage(state, input, processRecord, track) {
30
- const stage = input.stage;
31
- const manifest = record(input.task.payload.manifest);
32
- const directSource = manifest.executionMode === 'direct-source';
33
- let cancelled = false;
34
- let releaseWait;
35
- if (stage === 'capture-source' && directSource)
36
- track(async () => { cancelled = true; releaseWait?.(); });
37
- await verifyProductionLease(input);
38
- const store = productionOutputStore(input);
39
- const workspaceId = input.task.workspaceId;
40
- const resultId = String(input.payload.resultVersionId ?? input.task.payload.resultVersionId ?? input.task.attemptId);
41
- const sourceDirectoryName = String(input.payload.sourceDirectoryName ?? input.task.payload.sourceDirectoryName ?? 'basic');
42
- if (input.task.payload.sourceDirectoryName !== undefined && input.payload.sourceDirectoryName !== undefined
43
- && input.task.payload.sourceDirectoryName !== input.payload.sourceDirectoryName)
44
- throw new Error('production_source_directory_mismatch');
45
- const previous = (name) => input.stages?.find(value => value.stage === name && value.status === 'completed')?.result;
46
- const run = async (cwd, command, env, trusted = false) => {
47
- const outcome = await runProductionCommand(state, { processId: `production-${input.task.attemptId}-${stage}`, cwd, command, env, trusted,
48
- timeoutMs: 10 * 60_000, record: processRecord, track });
49
- if (!outcome.stopped || typeof outcome.code !== 'number')
50
- throw new Error('production_process_unknown');
51
- if (outcome.code !== 0)
52
- throw new Error(`production_${stage}_failed`);
53
- return outcome.output;
54
- };
55
- const git = async (root, ...args) => (await execute('git', ['-c', 'core.hooksPath=/dev/null', '-c', 'core.fsmonitor=false', '-C', root, ...args], {
56
- timeout: 20_000, maxBuffer: 1024 * 1024, env: { PATH: process.env.PATH, HOME: state.config.stateDir, GIT_CONFIG_GLOBAL: '/dev/null', GIT_CONFIG_NOSYSTEM: '1' },
57
- })).stdout.trim();
58
- if (stage === 'capture-source' && directSource) {
59
- if (input.directoryName !== sourceDirectoryName
60
- || String(input.payload.sourceWorkspaceId ?? input.task.payload.sourceWorkspaceId ?? workspaceId) !== workspaceId)
61
- throw new Error('production_source_directory_mismatch');
62
- const scope = { cacheRoot: join(state.config.stateDir, 'production-source-archives'), batchId: input.task.batchId,
63
- sourceWorkspaceId: workspaceId, sourceDirectoryName };
64
- const deadline = Date.now() + 30 * 60_000;
65
- await settleDependencyInstalls(state);
66
- let captured;
67
- while (!captured) {
68
- if (cancelled)
69
- throw new Error('production_execution_stopped');
70
- await verifyProductionLease(input);
71
- captured = await state.journal.exclusive(`directory:${workspaceId}:${sourceDirectoryName}`, async () => {
72
- const cached = await cachedProductionSourceArchive(scope);
73
- if (cached)
74
- return cached;
75
- if (await workspaceDirectoryOccupied(state, workspaceId, sourceDirectoryName, input.task.attemptId, true))
76
- return undefined;
77
- if (cancelled)
78
- throw new Error('production_execution_stopped');
79
- await verifyProductionLease(input);
80
- return captureProductionSourceArchive({ ...scope, source: await workspaceRoot(state, workspaceId, sourceDirectoryName) });
81
- });
82
- if (captured)
83
- break;
84
- if (Date.now() >= deadline)
85
- throw new Error('production_source_busy');
86
- await new Promise(resolve => {
87
- const timer = setTimeout(resolve, 1000);
88
- releaseWait = () => { clearTimeout(timer); resolve(); };
89
- if (cancelled)
90
- releaseWait();
91
- });
92
- releaseWait = undefined;
93
- }
94
- if (cancelled)
95
- throw new Error('production_execution_stopped');
96
- await verifyProductionLease(input);
97
- await store.put(captured.snapshot.objectKey, captured.body);
98
- return { sourceSnapshot: captured.snapshot };
99
- }
100
- if (stage === 'capture-source') {
101
- if (input.directoryName === 'basic' || input.directoryName === sourceDirectoryName)
102
- throw new Error('production_source_directory_mismatch');
103
- const authority = record(input.payload.workspace);
104
- if (authority.directoryName !== input.directoryName || authority.workspaceId !== workspaceId
105
- || typeof authority.remixId !== 'string' || typeof authority.branchName !== 'string' || typeof authority.repositoryUrl !== 'string')
106
- throw new Error('production_source_directory_mismatch');
107
- const { remixId, branchName, repositoryUrl } = authority;
108
- await settleDependencyInstalls(state);
109
- return state.journal.exclusive(`directory:${workspaceId}:${sourceDirectoryName}`, async () => {
110
- if (await workspaceDirectoryOccupied(state, workspaceId, sourceDirectoryName)
111
- || workspaceSessions(state, workspaceId, sourceDirectoryName).some(session => session.turnBlock === 'uncertain' || ['starting', 'running', 'waiting_for_user'].includes(session.turnStatus ?? '')))
112
- throw new Error('production_source_busy');
113
- const source = await workspaceRoot(state, workspaceId, sourceDirectoryName);
114
- const container = await workspaceContainerRoot(state, workspaceId);
115
- const destination = join(container, input.directoryName);
116
- const developmentGroup = (await lstat(state.config.workspaceRoot)).gid;
117
- const snapshot = await copyProductionSource({ source, destination, branchName, repositoryUrl });
118
- await ensureWorkspaceSkills(destination, join(state.config.stateDir, 'skill-artifacts'));
119
- await openDirectoryToDevelopment(destination, developmentGroup);
120
- await grantWorkspaceTraversal(container, developmentGroup);
121
- await state.journal.exclusive(`workspace:${workspaceId}`, async () => {
122
- const binding = state.journal.getWorkspace(workspaceId);
123
- await state.journal.putWorkspace({ ...binding, directories: [...(binding.directories ?? []).filter(value => value.directoryName !== input.directoryName),
124
- { workspaceId, directoryName: input.directoryName, remixId, branchName, sourceCommit: snapshot.commitHash, status: 'ready' }] });
125
- });
126
- return { sourceSnapshot: snapshot, commitHash: snapshot.commitHash };
127
- });
128
- }
129
- if (stage === 'prepare' && directSource) {
130
- const snapshot = batchSourceSnapshotSchema.parse(input.payload.sourceSnapshot ?? manifest.sourceSnapshot);
131
- const authority = record(input.payload.workspace);
132
- if (input.directoryName === 'basic' || authority.directoryName !== input.directoryName || authority.workspaceId !== workspaceId
133
- || typeof authority.remixId !== 'string' || typeof authority.branchName !== 'string'
134
- || snapshot.batchId !== input.task.batchId || snapshot.sourceWorkspaceId !== workspaceId || snapshot.sourceDirectoryName !== sourceDirectoryName)
135
- throw new Error('production_source_directory_mismatch');
136
- const directory = { workspaceId, directoryName: input.directoryName,
137
- remixId: authority.remixId, branchName: authority.branchName, sourceCommit: snapshot.commitHash };
138
- const root = join(await workspaceContainerRoot(state, workspaceId), input.directoryName);
139
- let exists = await lstat(root).catch(error => { if (error.code !== 'ENOENT')
140
- throw error; return undefined; });
141
- const binding = state.journal.getWorkspace(workspaceId);
142
- const existing = binding.directories?.find(value => value.directoryName === input.directoryName);
143
- const repair = (input.payload.repair ?? input.task.payload.repair) === true;
144
- const repairCommitHash = input.payload.repairCommitHash;
145
- if (!exists && repair && typeof repairCommitHash === 'string') {
146
- if (!/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(repairCommitHash)
147
- || !input.repairPreparation || binding.targetKind === 'standalone')
148
- throw new Error('workspace_recovery_required');
149
- const { pendingFork: _fork, directories: _directories, ...base } = binding;
150
- await state.journal.exclusive(`directory:${workspaceId}:${input.directoryName}`, async () => {
151
- await prepareWorkspaceCheckout(state, { ...base, targetKind: 'remix', remixId: authority.remixId,
152
- branchName: authority.branchName }, input.repairPreparation, state.acceptedLeaseFence, input.directoryName, true);
153
- if (await git(root, 'rev-parse', 'HEAD') !== repairCommitHash) {
154
- await rm(root, { recursive: true, force: true });
155
- throw new Error('production_repair_commit_mismatch');
156
- }
157
- });
158
- exists = await lstat(root);
159
- }
160
- else if (!exists && existing && existing.status !== 'pending')
161
- throw new Error('workspace_recovery_required');
162
- await registerWorkspaceDirectory(state, directory, state.acceptedLeaseFence, undefined, { snapshot, body: exists ? undefined : await store.get(snapshot.objectKey) });
163
- }
164
- if (stage === 'prepare' && input.directoryName !== 'basic' && (input.payload.baseline ?? input.task.payload.baseline)) {
165
- const baseline = record(input.payload.baseline ?? input.task.payload.baseline);
166
- const authority = record(input.payload.workspace);
167
- const sourceCommit = baseline.commitHash;
168
- if (typeof sourceCommit !== 'string')
169
- throw new Error('production_approved_commit_required');
170
- const directory = { workspaceId, directoryName: input.directoryName,
171
- remixId: String(authority.remixId), branchName: String(authority.branchName), sourceCommit };
172
- await registerWorkspaceDirectory(state, directory, state.acceptedLeaseFence, sourceDirectoryName);
173
- }
174
- const root = await workspaceRoot(state, workspaceId, input.directoryName);
175
- const variantChanges = async (commitHash) => {
176
- if (!directSource || resultId === input.task.attemptId)
177
- return undefined;
178
- const source = batchSourceSnapshotSchema.parse(manifest.sourceSnapshot);
179
- return requireProductionContentChanges(root, source.commitHash, commitHash);
180
- };
181
- if (stage === 'prepare') {
182
- if (input.payload.repairCommitHash !== undefined && await git(root, 'rev-parse', 'HEAD') !== input.payload.repairCommitHash)
183
- throw new Error('production_repair_commit_mismatch');
184
- await settleDependencyInstalls(state);
185
- if (!(await lstat(join(root, 'package.json')).catch(() => undefined)))
186
- return { prepared: true, directoryName: input.directoryName, emptySource: true };
187
- const policy = await resolveProductionDependencies(root).catch(error => {
188
- const repairable = error instanceof SyntaxError || (error instanceof Error && [
189
- 'production_dependency_lock_required', 'production_dependency_locks_conflict',
190
- 'production_package_manager_declaration_invalid', 'production_package_manager_lock_mismatch',
191
- ].includes(error.message));
192
- if (directSource && repairable)
193
- return undefined;
194
- if (!(input.payload.baseline ?? input.task.payload.baseline) && error instanceof Error
195
- && error.message === 'production_dependency_lock_required')
196
- return undefined;
197
- throw error;
198
- });
199
- if (!policy)
200
- return { prepared: true, directoryName: input.directoryName, dependencyRepairRequired: true };
201
- try {
202
- await run(root, process.env.PLAYCRAFT_PRODUCTION_INSTALL_COMMAND ?? 'pnpm install --frozen-lockfile --store-dir /var/cache/playcraft/pnpm-store --ignore-scripts');
203
- }
204
- catch (error) {
205
- if (directSource && error instanceof Error && error.message === 'production_prepare_failed')
206
- return { prepared: true, directoryName: input.directoryName, dependencyRepairRequired: true };
207
- throw error;
208
- }
209
- await verifyProductionDependencyLocks(root, policy);
210
- return { prepared: true, directoryName: input.directoryName };
211
- }
212
- if (stage === 'generate') {
213
- const beforeTree = await snapshotWorkspaceTree(root);
214
- await runProductionNative(state, input, root, processRecord, track);
215
- await resolveProductionDependencies(root);
216
- const afterTree = await snapshotWorkspaceTree(root);
217
- const changedPaths = await requireProductionContentChanges(root, beforeTree, afterTree);
218
- await variantChanges(afterTree);
219
- return { generated: true, changes: { beforeTree, afterTree, changedPaths } };
220
- }
221
- if (stage === 'save') {
222
- const binding = state.journal.getWorkspace(workspaceId);
223
- const branch = input.directoryName === 'basic' ? binding.branchName : binding.directories?.find(value => value.directoryName === input.directoryName)?.branchName;
224
- if ((input.git && input.git.branchName !== branch) || await git(root, 'symbolic-ref', '--short', 'HEAD') !== branch)
225
- throw new Error('production_git_binding_mismatch');
226
- if (input.payload.saved !== undefined) {
227
- const saved = batchSavedEvidenceSchema.parse(input.payload.saved);
228
- if (input.task.kind !== 'retry' || saved.attemptId === input.task.attemptId || saved.workspaceId !== workspaceId
229
- || saved.resultVersionId !== resultId || saved.commitHash !== saved.pushedCommitHash)
230
- throw new Error('production_recovery_saved_identity_mismatch');
231
- if (await git(root, 'rev-parse', 'HEAD') !== saved.commitHash)
232
- throw new Error('production_repair_commit_mismatch');
233
- const snapshot = await freezeBatchCommit({ source: { workspaceId, root }, commitHash: saved.commitHash, snapshotId: `${input.task.attemptId}-save` });
234
- if (snapshot.checksum !== saved.sourceDigest)
235
- throw new Error('production_recovery_source_mismatch');
236
- if (await snapshotWorkspaceTree(root) !== await git(root, 'rev-parse', `${saved.commitHash}^{tree}`))
237
- throw new Error('production_recovery_worktree_changed');
238
- await variantChanges(snapshot.commitHash);
239
- return { snapshot, saved: { ...saved, attemptId: input.task.attemptId } };
240
- }
241
- await run(root, 'chmod -R u+rwX,g+rwX .').catch(() => undefined);
242
- await excludeFromGit(root);
243
- await excludeFromGit(root, '/.env*');
244
- await excludeFromGit(root, '/.playcraft-temp/');
245
- await excludeFromGit(root, '/.playcraft-preview-out/');
246
- await git(root, 'config', 'user.name', 'PlayCraft');
247
- await git(root, 'config', 'user.email', 'playcraft@localhost');
248
- const env = { GIT_TERMINAL_PROMPT: '0', GIT_CONFIG_GLOBAL: '/dev/null', GIT_CONFIG_NOSYSTEM: '1' };
249
- await run(root, productionLocalCommitCommand(), env, true);
250
- const commitHash = await git(root, 'rev-parse', 'HEAD');
251
- const snapshot = await freezeBatchCommit({ source: { workspaceId, root }, commitHash, snapshotId: `${input.task.attemptId}-save` });
252
- await variantChanges(snapshot.commitHash);
253
- return { snapshot, saved: { attemptId: input.task.attemptId, resultVersionId: resultId, workspaceId, sourceDigest: snapshot.checksum, commitHash, pushedCommitHash: commitHash } };
254
- }
255
- if (stage === 'freeze') {
256
- const saved = record(previous('save')?.saved);
257
- if (typeof saved.commitHash !== 'string')
258
- throw new Error('production_saved_commit_required');
259
- const snapshot = await freezeBatchCommit({ source: { workspaceId, root }, commitHash: saved.commitHash, snapshotId: `${input.task.attemptId}-freeze` });
260
- const baseline = batchBaselineSnapshotSchema.parse({ batchId: input.task.batchId, projectId: String(input.payload.projectId ?? input.task.payload.projectId),
261
- snapshotId: snapshot.snapshotId, sourceWorkspaceId: snapshot.sourceWorkspaceId, commitHash: snapshot.commitHash, checksum: snapshot.checksum,
262
- sourceVersion: snapshot.sourceVersion, engine: String(input.payload.engine ?? input.task.payload.engine), skillLockDigest: snapshot.skillLockDigest, frozenAt: snapshot.frozenAt });
263
- return { snapshot, baseline };
264
- }
265
- if (stage === 'build') {
266
- const saved = record(input.payload.saved ?? previous('save')?.saved);
267
- const snapshot = (previous('freeze')?.snapshot ?? previous('save')?.snapshot);
268
- if (!snapshot || snapshot.commitHash !== saved.commitHash || snapshot.checksum !== saved.sourceDigest)
269
- throw new Error('production_saved_snapshot_mismatch');
270
- await variantChanges(snapshot.commitHash);
271
- const destination = join(root, '.playcraft-temp', `production-${input.task.attemptId}`);
272
- const removeDestination = async () => {
273
- try {
274
- await rm(destination, { recursive: true, force: true });
275
- }
276
- catch {
277
- await run(destination, 'chmod -R u+rwX,g+rwX .').catch(() => undefined);
278
- await rm(destination, { recursive: true, force: true });
279
- }
280
- };
281
- await excludeFromGit(root, '/.playcraft-temp/');
282
- await removeDestination();
283
- try {
284
- await checkoutBatchCommit({ source: { workspaceId, root }, frozen: snapshot, destination });
285
- await openDirectoryToDevelopment(destination, (await lstat(state.config.workspaceRoot)).gid);
286
- const policy = await resolveProductionDependencies(destination);
287
- await run(destination, process.env.PLAYCRAFT_PRODUCTION_INSTALL_COMMAND ?? 'pnpm install --frozen-lockfile --store-dir /var/cache/playcraft/pnpm-store --ignore-scripts');
288
- await verifyProductionDependencyLocks(destination, policy);
289
- const output = join(destination, '.production-html');
290
- const command = process.env.PLAYCRAFT_PRODUCTION_BUILD_COMMAND ?? 'playcraft build --mode full --platform playcraft --format html';
291
- await run(destination, `${command} --output ${quote(output)} .`);
292
- await verifyProductionDependencyLocks(destination, policy);
293
- const entries = await readdir(output, { withFileTypes: true });
294
- const single = classifyPreviewOutput(entries.map(entry => ({ name: entry.name, kind: entry.isFile() ? 'file' : entry.isDirectory() ? 'dir' : 'symlink' })));
295
- if (!single.ok)
296
- throw new Error('production_preview_not_single_file');
297
- const path = join(output, single.entry);
298
- if (!(await lstat(path)).isFile())
299
- throw new Error('production_preview_invalid');
300
- const body = await readFile(path);
301
- if (findExternalLocalReferences(body.toString()).length)
302
- throw new Error('production_preview_external_reference');
303
- const outputDigest = hash(body), outputKey = `workspaces/${workspaceId}/production/${input.task.attemptId}/${outputDigest}.html`;
304
- await store.put(outputKey, body);
305
- return { outputKey, outputDigest, build: { attemptId: input.task.attemptId, resultVersionId: resultId, workspaceId,
306
- sourceDigest: snapshot.checksum, commitHash: snapshot.commitHash, buildId: `${input.task.attemptId}-build`, succeeded: true } };
307
- }
308
- finally {
309
- await removeDestination();
310
- }
311
- }
312
- if (stage === 'preview') {
313
- const built = previous('build');
314
- if (typeof built?.outputKey !== 'string' || typeof built.outputDigest !== 'string')
315
- throw new Error('production_build_required');
316
- const body = await store.get(built.outputKey);
317
- if (hash(body) !== built.outputDigest)
318
- throw new Error('production_preview_digest_mismatch');
319
- const previewPath = join(root, '.playcraft-temp', `${input.task.attemptId}.html`);
320
- await mkdir(dirname(previewPath), { recursive: true });
321
- await writeFile(previewPath, body);
322
- try {
323
- const browser = process.env.PLAYCRAFT_PRODUCTION_PREVIEW_COMMAND ?? 'chromium --headless --no-sandbox --disable-gpu --virtual-time-budget=1000 --dump-dom';
324
- const opened = await run(root, `${browser} ${quote(`file://${previewPath}`)}`);
325
- if (!/<\/?html[\s>]/i.test(opened))
326
- throw new Error('production_preview_not_opened');
327
- return { opened: true, outputDigest: built.outputDigest };
328
- }
329
- finally {
330
- await rm(previewPath, { force: true });
331
- }
332
- }
333
- throw new Error('invalid_production_stage');
334
- }
@@ -1,75 +0,0 @@
1
- const taskUrl = (input) => `${input.callbackUrl.replace(/\/+$/, '')}/internal/cloud-production/tasks/${input.task.taskId}`;
2
- async function objectFailure(response, input, operation) {
3
- const value = await response.json().catch(() => undefined);
4
- const body = value && typeof value === 'object' && !Array.isArray(value) ? value : {};
5
- const reason = [body.reason, body.code, body.message, body.error]
6
- .find((value) => typeof value === 'string' && /^[a-z0-9_]{1,128}$/.test(value)
7
- && !value.includes(input.runnerToken));
8
- // Keep callback status and machine codes; never record its URL, headers, or response body.
9
- try {
10
- console.error(JSON.stringify({ event: 'production.object_transfer_failed', taskId: input.task.taskId,
11
- attemptId: input.task.attemptId, workspaceId: input.task.workspaceId, directoryName: input.directoryName,
12
- stage: input.stage, leaseFence: input.lease.fence, operation, status: response.status, reason }));
13
- }
14
- catch { /* Diagnostics must preserve the original failure. */ }
15
- return new Error(operation === 'get'
16
- ? response.status === 404 ? 'object_missing' : 'object_get_failed'
17
- : response.status === 409 ? 'object_conflict' : 'object_put_failed');
18
- }
19
- /** Build output travels through the Runtime that leased the task; engineering files travel through Git. */
20
- export function productionOutputStore(input) {
21
- const object = (key) => `${taskUrl(input)}/objects/${key.split('/').map(encodeURIComponent).join('/')}`;
22
- const headers = { authorization: `Bearer ${input.runnerToken}`, 'x-playcraft-object-client': 'cli' };
23
- return {
24
- async get(key) {
25
- const response = await fetch(object(key), { headers, redirect: 'error', signal: AbortSignal.timeout(60_000) });
26
- if (!response.ok)
27
- throw await objectFailure(response, input, 'get');
28
- return Buffer.from(await response.arrayBuffer());
29
- },
30
- async put(key, body) {
31
- const response = await fetch(object(key), { method: 'PUT', body: new Uint8Array(body), redirect: 'error', signal: AbortSignal.timeout(60_000),
32
- headers: { ...headers, 'content-type': 'application/octet-stream' } });
33
- if (!response.ok)
34
- throw await objectFailure(response, input, 'put');
35
- },
36
- };
37
- }
38
- export async function verifyProductionLease(input) {
39
- let status;
40
- let reason;
41
- try {
42
- const response = await fetch(`${taskUrl(input)}/runner`, {
43
- headers: { authorization: `Bearer ${input.runnerToken}` }, redirect: 'error', signal: AbortSignal.timeout(10_000),
44
- });
45
- status = response.status;
46
- if (!response.ok) {
47
- const value = await response.json().catch(() => undefined);
48
- const body = value && typeof value === 'object' && !Array.isArray(value) ? value : {};
49
- reason = [body.reason, body.code, body.message, body.error]
50
- .find((value) => typeof value === 'string' && /^[a-z0-9_]{1,128}$/.test(value)
51
- && !value.includes(input.runnerToken));
52
- throw new Error('production_lease_unavailable');
53
- }
54
- reason = 'production_lease_invalid_response';
55
- const { task } = await response.json();
56
- if (!task || task.taskId !== input.task.taskId || task.attemptId !== input.task.attemptId || task.leaseOwner !== input.lease.owner || task.leaseFence !== input.lease.fence || task.status !== 'running' || task.cancelRequested) {
57
- reason = 'production_lease_mismatch';
58
- throw new Error('production_lease_unavailable');
59
- }
60
- }
61
- catch (error) {
62
- const cause = error instanceof Error ? error.cause : undefined;
63
- const causeCode = cause && typeof cause === 'object' && 'code' in cause && typeof cause.code === 'string'
64
- && /^[A-Za-z0-9_]{1,64}$/.test(cause.code) ? cause.code : undefined;
65
- // Callback diagnostics retain machine codes only; responses and credentials stay private.
66
- try {
67
- console.error(JSON.stringify({ event: 'production.lease_verification_failed', taskId: input.task.taskId,
68
- attemptId: input.task.attemptId, workspaceId: input.task.workspaceId, directoryName: input.directoryName,
69
- stage: input.stage, leaseFence: input.lease.fence, status, reason,
70
- errorType: error instanceof Error && /^[A-Za-z0-9_]{1,64}$/.test(error.name) ? error.name : 'UnknownError', causeCode }));
71
- }
72
- catch { /* Diagnostics must preserve the original failure. */ }
73
- throw error;
74
- }
75
- }