@playcraft/cli 0.1.50-beta → 0.1.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -62
- package/dist/agent/local-backend.js +1 -1
- package/dist/cli-root-help.js +0 -2
- package/dist/commands/build-all.js +19 -5
- package/dist/commands/build.js +28 -15
- package/dist/commands/create.js +33 -26
- package/dist/commands/image.js +6 -2
- package/dist/commands/login.js +42 -84
- package/dist/commands/skills.js +0 -4
- package/dist/commands/tools-3d.js +0 -11
- package/dist/commands/tools-generation.js +137 -69
- package/dist/commands/tools-image-context.js +29 -0
- package/dist/commands/tools-utils.js +12 -0
- package/dist/commands/tools.js +2 -2
- package/dist/config.js +4 -59
- package/dist/index.js +3 -40
- package/dist/remix/clone.js +0 -2
- package/dist/remix/init-template.js +0 -2
- package/dist/remix/pull.js +0 -2
- package/dist/remix/push.js +1 -21
- package/dist/utils/agent-api-client.js +30 -210
- package/dist/utils/image-asset-context.js +228 -0
- package/dist/utils/playcraft-http-client.js +4 -6
- package/package.json +5 -15
- package/project-template/.claude/agents/refs/ta-image-generation-detail.md +67 -123
- package/project-template/.claude/skills/playcraft-image-generation/SKILL.md +135 -63
- package/project-template/.claude/skills/playcraft-image-generation/reference/gen-batch-sprite.template.mjs +42 -33
- package/project-template/.claude/skills/playcraft-image-generation/reference/gen-batch.template.mjs +18 -20
- package/project-template/.claude/skills/playcraft-image-generation/reference/process-batch.template.mjs +6 -7
- package/project-template/.claude/skills/playcraft-image-generation/reference/prompt-cookbook.md +39 -39
- package/project-templates/phaser/.claude/agents/refs/ta-image-generation-detail.md +59 -59
- package/project-templates/phaser/.claude/skills/playcraft-image-generation/SKILL.md +135 -63
- package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/gen-batch-sprite.template.mjs +42 -33
- package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/gen-batch.template.mjs +18 -20
- package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/process-batch.template.mjs +6 -7
- package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/prompt-cookbook.md +39 -39
- package/project-templates/threejs/.claude/agents/refs/ta-image-generation-detail.md +59 -59
- package/project-templates/threejs/.claude/skills/playcraft-image-generation/SKILL.md +135 -63
- package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/gen-batch-sprite.template.mjs +42 -33
- package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/gen-batch.template.mjs +18 -20
- package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/process-batch.template.mjs +6 -7
- package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/prompt-cookbook.md +39 -39
- package/SKILLS_REGISTRY.md +0 -149
- package/dist/auth/account-activation.js +0 -157
- package/dist/auth/messages.js +0 -30
- package/dist/commands/build-temp.js +0 -26
- package/dist/commands/platform-skills.generated.js +0 -19
- package/dist/commands/playable.js +0 -53
- package/dist/commands/tools-batch.js +0 -113
- package/dist/commands/video.js +0 -122
- package/dist/commands/workspace-runtime.js +0 -54
- package/dist/commands/workspace.js +0 -40
- package/dist/project-skills/builtin-import.js +0 -225
- package/dist/project-skills/commands.js +0 -77
- package/dist/project-skills/lifecycle.js +0 -28
- package/dist/project-skills/local-run.js +0 -112
- package/dist/project-skills/local-store.js +0 -221
- package/dist/project-skills/messages.js +0 -80
- package/dist/project-skills/reconcile.js +0 -62
- package/dist/project-skills/remote-cache.js +0 -51
- package/dist/project-skills/validation.js +0 -19
- package/dist/recording/messages.js +0 -40
- package/dist/recording/project-digest.js +0 -85
- package/dist/recording/recorder.js +0 -293
- package/dist/recording/scenario.js +0 -50
- package/dist/skill-registry/client.js +0 -345
- package/dist/skill-registry/commands.js +0 -180
- package/dist/skill-registry/evaluation-client.js +0 -156
- package/dist/skill-registry/evaluation-commands.js +0 -174
- package/dist/skill-registry/messages.js +0 -177
- package/dist/skill-registry/package.js +0 -275
- package/dist/skill-registry/platform-package.js +0 -100
- package/dist/utils/environment-endpoints.js +0 -17
- package/dist/utils/error-diagnostics.js +0 -20
- package/dist/utils/tool-operation-journal.js +0 -124
- package/dist/utils/video-evidence-git.js +0 -81
- package/dist/video-replication/client.js +0 -234
- package/dist/video-replication/comparison-evidence.js +0 -149
- package/dist/video-replication/comparison-files.js +0 -57
- package/dist/video-replication/comparison-log.js +0 -72
- package/dist/video-replication/comparison-messages.js +0 -104
- package/dist/video-replication/comparison-output.js +0 -63
- package/dist/video-replication/comparison-preflight.js +0 -66
- package/dist/video-replication/comparison.js +0 -230
- package/dist/video-replication/diagnostics.js +0 -41
- package/dist/video-replication/evidence.js +0 -165
- package/dist/video-replication/media.js +0 -112
- package/dist/video-replication/messages.js +0 -97
- package/dist/workspace/messages.js +0 -67
- package/dist/workspace/transfer-client.js +0 -233
- package/dist/workspace/transfer-errors.js +0 -48
- package/dist/workspace-runtime/batch/content-changes.js +0 -30
- package/dist/workspace-runtime/batch/control.js +0 -86
- package/dist/workspace-runtime/batch/deliver.js +0 -173
- package/dist/workspace-runtime/batch/freeze.js +0 -101
- package/dist/workspace-runtime/batch/generate.js +0 -249
- package/dist/workspace-runtime/batch/git.js +0 -40
- package/dist/workspace-runtime/batch/occupancy.js +0 -8
- package/dist/workspace-runtime/batch/policy.js +0 -70
- package/dist/workspace-runtime/batch/prepare.js +0 -317
- package/dist/workspace-runtime/batch/processes.js +0 -72
- package/dist/workspace-runtime/batch/records.js +0 -113
- package/dist/workspace-runtime/batch/routes.js +0 -61
- package/dist/workspace-runtime/batch/source-candidate.js +0 -86
- package/dist/workspace-runtime/batch/source-capture.js +0 -146
- package/dist/workspace-runtime/batch/source-failure.js +0 -43
- package/dist/workspace-runtime/batch/source-publish.js +0 -130
- package/dist/workspace-runtime/codex/app-server.js +0 -1065
- package/dist/workspace-runtime/codex/compact.js +0 -46
- package/dist/workspace-runtime/codex/config-toml.js +0 -107
- package/dist/workspace-runtime/codex/delivery-tools.js +0 -144
- package/dist/workspace-runtime/codex/jsonrpc-stdio.js +0 -145
- package/dist/workspace-runtime/codex/launcher-client.js +0 -177
- package/dist/workspace-runtime/codex/loopback.js +0 -146
- package/dist/workspace-runtime/codex/mcp-config.js +0 -39
- package/dist/workspace-runtime/codex/model-broker.js +0 -263
- package/dist/workspace-runtime/codex/native-adapter.js +0 -9
- package/dist/workspace-runtime/codex/permissions.js +0 -79
- package/dist/workspace-runtime/codex/thread-image.js +0 -38
- package/dist/workspace-runtime/codex/tool-response.js +0 -10
- package/dist/workspace-runtime/codex/ui-instructions.js +0 -4
- package/dist/workspace-runtime/codex/ui-tools.js +0 -203
- package/dist/workspace-runtime/codex/work-mode.js +0 -15
- package/dist/workspace-runtime/main.js +0 -10
- package/dist/workspace-runtime/persistence/journal.js +0 -365
- package/dist/workspace-runtime/persistence/native-subagents.js +0 -162
- package/dist/workspace-runtime/persistence/sqlite-client.js +0 -77
- package/dist/workspace-runtime/persistence/sqlite-worker.js +0 -1161
- package/dist/workspace-runtime/persistence/stopped-execution.js +0 -77
- package/dist/workspace-runtime/previews/diagnostics.js +0 -44
- package/dist/workspace-runtime/previews/output.js +0 -36
- package/dist/workspace-runtime/previews/publish.js +0 -283
- package/dist/workspace-runtime/previews/runtime-preview.js +0 -177
- package/dist/workspace-runtime/previews/single-file.js +0 -43
- package/dist/workspace-runtime/processes/managed-writes.js +0 -69
- package/dist/workspace-runtime/serve.js +0 -441
- package/dist/workspace-runtime/server/accept-operation.js +0 -61
- package/dist/workspace-runtime/server/asset-transfers.js +0 -63
- package/dist/workspace-runtime/server/auth.js +0 -21
- package/dist/workspace-runtime/server/connectivity.js +0 -209
- package/dist/workspace-runtime/server/desktop-host.js +0 -429
- package/dist/workspace-runtime/server/desktop-preview-config.js +0 -81
- package/dist/workspace-runtime/server/desktop-preview-service.js +0 -157
- package/dist/workspace-runtime/server/desktop-preview-static.js +0 -53
- package/dist/workspace-runtime/server/desktop-preview.js +0 -189
- package/dist/workspace-runtime/server/dispatch.js +0 -1632
- package/dist/workspace-runtime/server/endpoints.js +0 -21
- package/dist/workspace-runtime/server/execution-group.js +0 -90
- package/dist/workspace-runtime/server/http.js +0 -1340
- package/dist/workspace-runtime/server/lock.js +0 -53
- package/dist/workspace-runtime/server/operation-record.js +0 -33
- package/dist/workspace-runtime/server/rfb-watch-gate.js +0 -168
- package/dist/workspace-runtime/server/session-restore.js +0 -84
- package/dist/workspace-runtime/server/source-uploader.js +0 -138
- package/dist/workspace-runtime/server/submit-operation.js +0 -41
- package/dist/workspace-runtime/server/terminal-host.js +0 -366
- package/dist/workspace-runtime/server/types.js +0 -1
- package/dist/workspace-runtime/server/workspace-transfer.js +0 -168
- package/dist/workspace-runtime/verify-artifact.js +0 -59
- package/dist/workspace-runtime/workspaces/append-only-log.js +0 -62
- package/dist/workspace-runtime/workspaces/archive.js +0 -207
- package/dist/workspace-runtime/workspaces/artifact-events.js +0 -55
- package/dist/workspace-runtime/workspaces/artifact-files.js +0 -199
- package/dist/workspace-runtime/workspaces/artifact-receipts.js +0 -89
- package/dist/workspace-runtime/workspaces/artifacts.js +0 -282
- package/dist/workspace-runtime/workspaces/asset-library-import.js +0 -366
- package/dist/workspace-runtime/workspaces/asset-transfer-dependencies.js +0 -81
- package/dist/workspace-runtime/workspaces/asset-transfer-publish.js +0 -434
- package/dist/workspace-runtime/workspaces/asset-transfer-storage.js +0 -389
- package/dist/workspace-runtime/workspaces/attachment-content.js +0 -73
- package/dist/workspace-runtime/workspaces/attachments.js +0 -185
- package/dist/workspace-runtime/workspaces/batch-commit.js +0 -142
- package/dist/workspace-runtime/workspaces/binary-content.js +0 -65
- package/dist/workspace-runtime/workspaces/binary-write.js +0 -112
- package/dist/workspace-runtime/workspaces/cli-context.js +0 -23
- package/dist/workspace-runtime/workspaces/cli-credentials.js +0 -115
- package/dist/workspace-runtime/workspaces/composer-context.js +0 -147
- package/dist/workspace-runtime/workspaces/context-error.js +0 -2
- package/dist/workspace-runtime/workspaces/delivery-build.js +0 -331
- package/dist/workspace-runtime/workspaces/delivery-store.js +0 -168
- package/dist/workspace-runtime/workspaces/delivery.js +0 -64
- package/dist/workspace-runtime/workspaces/dependencies.js +0 -279
- package/dist/workspace-runtime/workspaces/developer-command.js +0 -98
- package/dist/workspace-runtime/workspaces/directories.js +0 -132
- package/dist/workspace-runtime/workspaces/directory-access.js +0 -37
- package/dist/workspace-runtime/workspaces/directory-attachments.js +0 -245
- package/dist/workspace-runtime/workspaces/directory-create.js +0 -57
- package/dist/workspace-runtime/workspaces/directory-maintenance.js +0 -111
- package/dist/workspace-runtime/workspaces/directory-mutations.js +0 -238
- package/dist/workspace-runtime/workspaces/directory-revisions.js +0 -56
- package/dist/workspace-runtime/workspaces/directory-stats.js +0 -49
- package/dist/workspace-runtime/workspaces/disk-snapshot.js +0 -31
- package/dist/workspace-runtime/workspaces/empty-template.js +0 -42
- package/dist/workspace-runtime/workspaces/file-content.js +0 -48
- package/dist/workspace-runtime/workspaces/files-search.js +0 -116
- package/dist/workspace-runtime/workspaces/files.js +0 -150
- package/dist/workspace-runtime/workspaces/git-credential-broker.js +0 -312
- package/dist/workspace-runtime/workspaces/git-credential-helper.js +0 -68
- package/dist/workspace-runtime/workspaces/git-direct-store.js +0 -155
- package/dist/workspace-runtime/workspaces/git.js +0 -670
- package/dist/workspace-runtime/workspaces/instructions.js +0 -56
- package/dist/workspace-runtime/workspaces/json5-edit.js +0 -313
- package/dist/workspace-runtime/workspaces/library-attachments.js +0 -268
- package/dist/workspace-runtime/workspaces/operation-log.js +0 -23
- package/dist/workspace-runtime/workspaces/optional-platform-skills.js +0 -14
- package/dist/workspace-runtime/workspaces/parameter-agent-inputs.js +0 -87
- package/dist/workspace-runtime/workspaces/parameter-agent-protocol.js +0 -29
- package/dist/workspace-runtime/workspaces/parameter-agent-store.js +0 -58
- package/dist/workspace-runtime/workspaces/parameter-agent.js +0 -282
- package/dist/workspace-runtime/workspaces/parameters.js +0 -295
- package/dist/workspace-runtime/workspaces/prepare.js +0 -278
- package/dist/workspace-runtime/workspaces/registry.js +0 -251
- package/dist/workspace-runtime/workspaces/remix-references.js +0 -301
- package/dist/workspace-runtime/workspaces/revisions.js +0 -31
- package/dist/workspace-runtime/workspaces/session-image.js +0 -104
- package/dist/workspace-runtime/workspaces/shared-git.js +0 -40
- package/dist/workspace-runtime/workspaces/skill-artifact-loader.js +0 -95
- package/dist/workspace-runtime/workspaces/skills.js +0 -191
- package/dist/workspace-runtime/workspaces/transfer-quota.js +0 -69
- package/dist/workspace-runtime/workspaces/transfer.js +0 -471
- package/dist/workspace-runtime/workspaces/turn-changes.js +0 -75
- package/dist/workspace-runtime/workspaces/video-attachments.js +0 -49
- package/project-template/.claude/skills/playcraft-replica-validation/SKILL.md +0 -25
- package/project-template/.claude/skills/playcraft-replica-validation/references/contracts.md +0 -79
- package/project-template/.claude/skills/playcraft-video-understanding/SKILL.md +0 -29
- package/project-template/.claude/skills/playcraft-video-understanding/references/contracts.md +0 -37
- package/project-template-v2/.claude/agents/artist.md +0 -82
- package/project-template-v2/.claude/agents/developer.md +0 -153
- package/project-template-v2/.claude/agents/game-designer.md +0 -264
- package/project-template-v2/.claude/agents/refs/artist-art-style-catalog.md +0 -533
- package/project-template-v2/.claude/agents/refs/artist-color-audio-recipes.md +0 -153
- package/project-template-v2/.claude/agents/refs/artist-dimension-axis.md +0 -27
- package/project-template-v2/.claude/agents/refs/artist-master-composite-recipes.md +0 -208
- package/project-template-v2/.claude/agents/refs/atom-skill-library.md +0 -81
- package/project-template-v2/.claude/agents/refs/developer-impl-cookbook.md +0 -432
- package/project-template-v2/.claude/agents/refs/framework-5-component-filter.md +0 -252
- package/project-template-v2/.claude/agents/refs/framework-game-feel-juice.md +0 -266
- package/project-template-v2/.claude/agents/refs/framework-mda.md +0 -147
- package/project-template-v2/.claude/agents/refs/game-designer-gameplay-sufficiency.md +0 -123
- package/project-template-v2/.claude/agents/refs/ta-3d-flip-recipe.md +0 -88
- package/project-template-v2/.claude/agents/refs/ta-atlas-deliverable-standard.md +0 -67
- package/project-template-v2/.claude/agents/refs/ta-batch-pipeline-recipes.md +0 -120
- package/project-template-v2/.claude/agents/refs/ta-image-generation-detail.md +0 -300
- package/project-template-v2/.claude/agents/refs/ta-image-ops-reference.md +0 -495
- package/project-template-v2/.claude/agents/refs/ta-pipeline-cookbook.md +0 -1141
- package/project-template-v2/.claude/agents/refs/ta-tools-reference.md +0 -111
- package/project-template-v2/.claude/agents/refs/ta-vfx-preset-catalog.md +0 -365
- package/project-template-v2/.claude/agents/refs/threejs-cannon-pitfalls.md +0 -412
- package/project-template-v2/.claude/agents/reviewer.md +0 -75
- package/project-template-v2/.claude/agents/technical-artist.md +0 -86
- package/project-template-v2/.claude/hooks/snapshot-milestone.mjs +0 -243
- package/project-template-v2/.claude/hooks/user-prompt.mjs +0 -133
- package/project-template-v2/.claude/settings.json +0 -33
- package/project-template-v2/.claude/skills/brainstorming/SKILL.md +0 -161
- package/project-template-v2/.claude/skills/brainstorming/scripts/frame-template.html +0 -270
- package/project-template-v2/.claude/skills/brainstorming/scripts/helper.js +0 -177
- package/project-template-v2/.claude/skills/brainstorming/scripts/server.cjs +0 -354
- package/project-template-v2/.claude/skills/brainstorming/scripts/start-server.sh +0 -148
- package/project-template-v2/.claude/skills/brainstorming/scripts/stop-server.sh +0 -56
- package/project-template-v2/.claude/skills/brainstorming/scripts/wait-for-selection.sh +0 -62
- package/project-template-v2/.claude/skills/brainstorming/spec-document-reviewer-prompt.md +0 -49
- package/project-template-v2/.claude/skills/brainstorming/visual-companion.md +0 -309
- package/project-template-v2/.claude/skills/playcraft-ad-psychology/SKILL.md +0 -182
- package/project-template-v2/.claude/skills/playcraft-art-style-guide/SKILL.md +0 -123
- package/project-template-v2/.claude/skills/playcraft-asset-state-sheet/SKILL.md +0 -205
- package/project-template-v2/.claude/skills/playcraft-audio-generation/SKILL.md +0 -280
- package/project-template-v2/.claude/skills/playcraft-batch-pipeline/SKILL.md +0 -184
- package/project-template-v2/.claude/skills/playcraft-build-optimizer/SKILL.md +0 -306
- package/project-template-v2/.claude/skills/playcraft-image-generation/SKILL.md +0 -298
- package/project-template-v2/.claude/skills/playcraft-image-generation/reference/build-sprite-sheet.template.mjs +0 -123
- package/project-template-v2/.claude/skills/playcraft-image-generation/reference/compare-style.template.mjs +0 -254
- package/project-template-v2/.claude/skills/playcraft-image-generation/reference/gen-batch-sprite.template.mjs +0 -324
- package/project-template-v2/.claude/skills/playcraft-image-generation/reference/gen-batch.template.mjs +0 -97
- package/project-template-v2/.claude/skills/playcraft-image-generation/reference/gen-edit-variants.template.mjs +0 -118
- package/project-template-v2/.claude/skills/playcraft-image-generation/reference/process-batch.template.mjs +0 -137
- package/project-template-v2/.claude/skills/playcraft-image-generation/reference/prompt-cookbook.md +0 -397
- package/project-template-v2/.claude/skills/playcraft-image-generation/reference/validate-sprite-sheet.template.mjs +0 -296
- package/project-template-v2/.claude/skills/playcraft-image-ops/SKILL.md +0 -122
- package/project-template-v2/.claude/skills/playcraft-image-processing/SKILL.md +0 -219
- package/project-template-v2/.claude/skills/playcraft-masking/SKILL.md +0 -373
- package/project-template-v2/.claude/skills/playcraft-playable-optimization/SKILL.md +0 -161
- package/project-template-v2/.claude/skills/playcraft-research/SKILL.md +0 -215
- package/project-template-v2/.claude/skills/playcraft-skill-recommender/SKILL.md +0 -382
- package/project-template-v2/.claude/skills/playcraft-sprite-generation/SKILL.md +0 -423
- package/project-template-v2/.claude/skills/playcraft-sprite-remix/SKILL.md +0 -158
- package/project-template-v2/.claude/skills/playcraft-sprite-sheet/SKILL.md +0 -100
- package/project-template-v2/.claude/skills/playcraft-storyboard/SKILL.md +0 -167
- package/project-template-v2/.claude/skills/playcraft-style-qa/SKILL.md +0 -270
- package/project-template-v2/.claude/skills/playcraft-text-rendering/SKILL.md +0 -236
- package/project-template-v2/.claude/skills/playcraft-vfx-animation/SKILL.md +0 -130
- package/project-template-v2/.claude/skills/playwright-cli/SKILL.md +0 -390
- package/project-template-v2/.claude/skills/playwright-cli/references/element-attributes.md +0 -23
- package/project-template-v2/.claude/skills/playwright-cli/references/playwright-tests.md +0 -39
- package/project-template-v2/.claude/skills/playwright-cli/references/request-mocking.md +0 -87
- package/project-template-v2/.claude/skills/playwright-cli/references/running-code.md +0 -240
- package/project-template-v2/.claude/skills/playwright-cli/references/session-management.md +0 -226
- package/project-template-v2/.claude/skills/playwright-cli/references/spec-driven-testing.md +0 -308
- package/project-template-v2/.claude/skills/playwright-cli/references/storage-state.md +0 -275
- package/project-template-v2/.claude/skills/playwright-cli/references/test-generation.md +0 -134
- package/project-template-v2/.claude/skills/playwright-cli/references/tracing.md +0 -142
- package/project-template-v2/.claude/skills/playwright-cli/references/video-recording.md +0 -153
- package/project-template-v2/.claude/skills/session-analyzer/SKILL.md +0 -386
- package/project-template-v2/.claude/skills/session-analyzer/scripts/execution-breakdown.mjs +0 -182
- package/project-template-v2/.claude/skills/session-analyzer/scripts/find-turns.mjs +0 -72
- package/project-template-v2/.claude/skills/session-analyzer/scripts/heavy-output.mjs +0 -121
- package/project-template-v2/.claude/skills/session-analyzer/scripts/resolve-session.mjs +0 -102
- package/project-template-v2/.claude/skills/session-analyzer/scripts/subagent-stats.mjs +0 -127
- package/project-template-v2/.claude/skills/session-analyzer/scripts/subagent-tool-timeline.mjs +0 -106
- package/project-template-v2/.claude/skills/session-analyzer/scripts/time-gaps.mjs +0 -128
- package/project-template-v2/.claude/skills/session-analyzer/scripts/turn-timeline.mjs +0 -67
- package/project-template-v2/.claude/snapshot.mjs +0 -263
- package/project-template-v2/.playcraft/skills.lock.json +0 -152
- package/project-template-v2/CLAUDE.md +0 -146
- package/project-template-v2/assets/audio/bgm/.gitkeep +0 -0
- package/project-template-v2/assets/audio/sfx/.gitkeep +0 -0
- package/project-template-v2/assets/bundles/.gitkeep +0 -0
- package/project-template-v2/assets/images/bg/.gitkeep +0 -0
- package/project-template-v2/assets/images/reference/.gitkeep +0 -0
- package/project-template-v2/assets/images/storyboard/.gitkeep +0 -0
- package/project-template-v2/assets/images/tiles/.gitkeep +0 -0
- package/project-template-v2/assets/images/ui/.gitkeep +0 -0
- package/project-template-v2/assets/images/vfx/.gitkeep +0 -0
- package/project-template-v2/assets/models/.gitkeep +0 -0
- package/project-template-v2/docs/harness/iteration-1/context-flow.md +0 -254
- package/project-template-v2/docs/harness/iteration-1/generate-flow.md +0 -91
- package/project-template-v2/docs/harness/iteration-1/ideate-flow.md +0 -214
- package/project-template-v2/docs/harness/iteration-1/optimize-flow.md +0 -75
- package/project-template-v2/docs/harness/iteration-1/wrapup-flow.md +0 -63
- package/project-template-v2/docs/harness/iteration-2/context-flow.md +0 -223
- package/project-template-v2/docs/harness/iteration-2/generate-flow.md +0 -129
- package/project-template-v2/docs/harness/iteration-2/ideate-flow.md +0 -267
- package/project-template-v2/docs/harness/iteration-2/optimize-flow.md +0 -164
- package/project-template-v2/docs/harness/iteration-2/wrapup-flow.md +0 -115
- package/project-template-v2/docs/harness/orchestrator-flow.md +0 -364
- package/project-template-v2/docs/project-state.json +0 -60
- package/project-template-v2/docs/project-state.md +0 -72
- package/project-template-v2/docs/standards/README.md +0 -225
- package/project-template-v2/docs/standards/agent-behavior-standards.md +0 -174
- package/project-template-v2/docs/standards/artifacts/design-brief.md +0 -19
- package/project-template-v2/docs/standards/artifacts/design.md +0 -22
- package/project-template-v2/docs/standards/artifacts/game-code.md +0 -41
- package/project-template-v2/docs/standards/artifacts/todo-list.md +0 -41
- package/project-template-v2/docs/standards/iter1-agent-behavior-standards.md +0 -343
- package/project-template-v2/game/index.ts +0 -18
- package/project-template-v2/globals.d.ts +0 -51
- package/project-template-v2/index.css +0 -34
- package/project-template-v2/index.html +0 -18
- package/project-template-v2/main.ts +0 -9
- package/project-template-v2/package.json +0 -46
- package/project-template-v2/skills/_shared/scripts/dispatch-clear.mjs +0 -31
- package/project-template-v2/skills/_shared/scripts/dispatch-set.mjs +0 -86
- package/project-template-v2/skills/_shared/scripts/dod-check.mjs +0 -153
- package/project-template-v2/skills/_shared/scripts/handoff-append.mjs +0 -70
- package/project-template-v2/skills/_shared/scripts/lib/validator-artifacts.mjs +0 -91
- package/project-template-v2/skills/_shared/scripts/pipeline/dod-config.mjs +0 -131
- package/project-template-v2/skills/_shared/scripts/pipeline/index.mjs +0 -80
- package/project-template-v2/skills/_shared/scripts/pipeline/iteration-1-core.mjs +0 -90
- package/project-template-v2/skills/_shared/scripts/pipeline/iteration-2-wrap.mjs +0 -93
- package/project-template-v2/skills/_shared/scripts/pipeline/iteration-3-visual.mjs +0 -23
- package/project-template-v2/skills/_shared/scripts/render-project-state.mjs +0 -230
- package/project-template-v2/skills/_shared/scripts/state-advance-stage.mjs +0 -58
- package/project-template-v2/skills/_shared/scripts/state-get.mjs +0 -339
- package/project-template-v2/skills/_shared/scripts/state-handoff.mjs +0 -39
- package/project-template-v2/skills/_shared/scripts/state-set.mjs +0 -94
- package/project-template-v2/skills/_shared/scripts/state-store.mjs +0 -783
- package/project-template-v2/skills/_shared/scripts/todo-add.mjs +0 -88
- package/project-template-v2/skills/_shared/scripts/todo-get.mjs +0 -130
- package/project-template-v2/skills/_shared/scripts/todo-remove.mjs +0 -33
- package/project-template-v2/skills/_shared/scripts/todo-set.mjs +0 -47
- package/project-template-v2/skills/_shared/scripts/verify-asset-code-sync.mjs +0 -268
- package/project-template-v2/skills/_shared/scripts/verify-env.mjs +0 -285
- package/project-template-v2/skills/_shared/scripts/verify-placeholders.mjs +0 -161
- package/project-template-v2/skills/playable-autoplay/SKILL.md +0 -176
- package/project-template-v2/skills/playable-autoplay/agents/openai.yaml +0 -4
- package/project-template-v2/skills/playable-debug/SKILL.md +0 -116
- package/project-template-v2/skills/playable-debug/agents/openai.yaml +0 -4
- package/project-template-v2/skills/playable-debug/references/debug-config.md +0 -40
- package/project-template-v2/skills/playable-record/SKILL.md +0 -140
- package/project-template-v2/skills/playable-record/scripts/lib/dev-server.mjs +0 -104
- package/project-template-v2/skills/playable-record/scripts/lib/record-audio-bridge.js +0 -141
- package/project-template-v2/skills/playable-record/scripts/record-playable.mjs +0 -425
- package/project-template-v2/skills/playable-record/scripts/verify-contract.mjs +0 -261
- package/project-template-v2/skills/playable-record/scripts/verify-firstwin.mjs +0 -398
- package/project-template-v2/skills/playable-record/scripts/verify-fusion.mjs +0 -322
- package/project-template-v2/skills/playable-record/scripts/verify-lifecycle.mjs +0 -105
- package/project-template-v2/skills/playable-record/scripts/verify-vlm-video.mjs +0 -230
- package/project-template-v2/skills/playable-validate/SKILL.md +0 -61
- package/project-template-v2/skills/playable-validate/validation-rules.md +0 -14
- package/project-template-v2/skills/playable-verify-ui/SKILL.md +0 -63
- package/project-template-v2/skills/playable-verify-ui/scripts/lib/__init__.py +0 -1
- package/project-template-v2/skills/playable-verify-ui/scripts/lib/config_expr.js +0 -218
- package/project-template-v2/skills/playable-verify-ui/scripts/lib/node_utils.js +0 -51
- package/project-template-v2/skills/playable-verify-ui/scripts/lib/profile_loader.py +0 -59
- package/project-template-v2/skills/playable-verify-ui/scripts/lib/report.py +0 -106
- package/project-template-v2/skills/playable-verify-ui/scripts/lib/runtime_contract.js +0 -116
- package/project-template-v2/skills/playable-verify-ui/scripts/verify_fetch_antipatterns.py +0 -59
- package/project-template-v2/skills/playable-verify-ui/scripts/verify_hardcoded_layout.js +0 -64
- package/project-template-v2/skills/playable-verify-ui/scripts/verify_runtime_contract.js +0 -58
- package/project-template-v2/skills/playable-verify-ui/scripts/verify_text_overlap.py +0 -333
- package/project-template-v2/ta-workspace/scripts/.gitkeep +0 -0
- package/project-template-v2/tsconfig.json +0 -20
- package/project-template-v2/vite.config.ts +0 -27
- package/project-templates/batch-locks/phaser.yaml +0 -922
- package/project-templates/batch-locks/threejs.yaml +0 -978
- package/project-templates/empty/AGENTS.md +0 -41
- package/project-templates/empty/gitignore +0 -33
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import { constants } from 'node:fs';
|
|
2
|
-
import { open } from 'node:fs/promises';
|
|
3
|
-
import { extname, join, posix } from 'node:path';
|
|
4
|
-
import { AssetTransferError, safeAssetTransferDirectory } from './asset-transfer-storage.js';
|
|
5
|
-
const METADATA_MAX_BYTES = 8 * 1024 * 1024;
|
|
6
|
-
/** Parse only bounded model metadata; all external dependencies must be explicit package members. */
|
|
7
|
-
export async function validateAssetTransferDependencies(transfer) {
|
|
8
|
-
const paths = new Set(transfer.record.manifest.files.map(file => file.path));
|
|
9
|
-
for (const [index, file] of transfer.record.manifest.files.entries()) {
|
|
10
|
-
const extension = extname(file.path).toLowerCase();
|
|
11
|
-
if (extension !== '.gltf' && extension !== '.glb')
|
|
12
|
-
continue;
|
|
13
|
-
await safeAssetTransferDirectory(transfer.directory, 'incoming');
|
|
14
|
-
const handle = await open(join(transfer.directory, 'incoming', String(index)), constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
15
|
-
try {
|
|
16
|
-
let start = 0, size = file.size;
|
|
17
|
-
if (extension === '.glb') {
|
|
18
|
-
const header = Buffer.alloc(20);
|
|
19
|
-
const read = await handle.read(header, 0, header.length, 0);
|
|
20
|
-
if (read.bytesRead !== 20 || header.readUInt32LE(0) !== 0x46546c67 || header.readUInt32LE(4) !== 2
|
|
21
|
-
|| header.readUInt32LE(8) !== file.size || header.readUInt32LE(16) !== 0x4e4f534a)
|
|
22
|
-
throw new AssetTransferError('asset_transfer_invalid_model');
|
|
23
|
-
size = header.readUInt32LE(12);
|
|
24
|
-
start = 20;
|
|
25
|
-
}
|
|
26
|
-
if (size > METADATA_MAX_BYTES)
|
|
27
|
-
throw new AssetTransferError('asset_transfer_metadata_too_large', 413);
|
|
28
|
-
if (size < 2 || size + start > file.size)
|
|
29
|
-
throw new AssetTransferError('asset_transfer_invalid_model');
|
|
30
|
-
const bytes = Buffer.alloc(size);
|
|
31
|
-
let offset = 0;
|
|
32
|
-
while (offset < size) {
|
|
33
|
-
const read = await handle.read(bytes, offset, size - offset, start + offset);
|
|
34
|
-
if (!read.bytesRead)
|
|
35
|
-
throw new AssetTransferError('asset_transfer_invalid_model');
|
|
36
|
-
offset += read.bytesRead;
|
|
37
|
-
}
|
|
38
|
-
let body;
|
|
39
|
-
try {
|
|
40
|
-
body = JSON.parse(bytes.toString('utf8'));
|
|
41
|
-
}
|
|
42
|
-
catch {
|
|
43
|
-
throw new AssetTransferError('asset_transfer_invalid_model');
|
|
44
|
-
}
|
|
45
|
-
if (!body || typeof body !== 'object' || Array.isArray(body)
|
|
46
|
-
|| !Array.isArray(body.buffers ?? []) || !Array.isArray(body.images ?? []))
|
|
47
|
-
throw new AssetTransferError('asset_transfer_invalid_model');
|
|
48
|
-
for (const resource of [...(body.buffers ?? []), ...(body.images ?? [])]) {
|
|
49
|
-
if (!resource || typeof resource !== 'object' || Array.isArray(resource))
|
|
50
|
-
throw new AssetTransferError('asset_transfer_invalid_model');
|
|
51
|
-
if (resource.uri === undefined)
|
|
52
|
-
continue;
|
|
53
|
-
if (typeof resource.uri !== 'string')
|
|
54
|
-
throw new AssetTransferError('asset_transfer_invalid_model');
|
|
55
|
-
const target = assetTransferDependencyPath(file, resource.uri);
|
|
56
|
-
if (target && !paths.has(target))
|
|
57
|
-
throw new AssetTransferError('asset_transfer_missing_dependency');
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
finally {
|
|
61
|
-
await handle.close();
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
function assetTransferDependencyPath(file, uri) {
|
|
66
|
-
if (/^data:/i.test(uri))
|
|
67
|
-
return undefined;
|
|
68
|
-
let decoded;
|
|
69
|
-
try {
|
|
70
|
-
decoded = decodeURIComponent(uri);
|
|
71
|
-
}
|
|
72
|
-
catch {
|
|
73
|
-
throw new AssetTransferError('asset_transfer_invalid_dependency');
|
|
74
|
-
}
|
|
75
|
-
if (!decoded || /^[a-z][a-z\d+.-]*:/i.test(decoded) || /^[\/\\]/.test(decoded) || /[\\:\x00-\x1f\x7f?#]/.test(decoded))
|
|
76
|
-
throw new AssetTransferError('asset_transfer_invalid_dependency');
|
|
77
|
-
const target = posix.normalize(posix.join(posix.dirname(file.path), decoded));
|
|
78
|
-
if (target === '..' || target.startsWith('../'))
|
|
79
|
-
throw new AssetTransferError('asset_transfer_invalid_dependency');
|
|
80
|
-
return target;
|
|
81
|
-
}
|
|
@@ -1,434 +0,0 @@
|
|
|
1
|
-
import { createHash } from 'node:crypto';
|
|
2
|
-
import { constants } from 'node:fs';
|
|
3
|
-
import { link, lstat, mkdir, open, readdir, realpath, rename, rm, rmdir, unlink } from 'node:fs/promises';
|
|
4
|
-
import { dirname, join } from 'node:path';
|
|
5
|
-
import { ASSET_TRANSFER_CHUNK_BYTES, assetBundleSavePayloadSchema, assetBundleReceiptSchema, isAssetBundleReceiptForManifest, } from '@playcraft/common/cloud-workspace/runtime';
|
|
6
|
-
import { AssetTransferError, assetTransferMissing, hashAssetTransferFile, safeAssetTransferDirectory, saveAssetTransfer, syncAssetTransferDirectory, withAssetTransfer, } from './asset-transfer-storage.js';
|
|
7
|
-
import { validateAssetTransferDependencies } from './asset-transfer-dependencies.js';
|
|
8
|
-
const same = (left, right) => left.dev === right.dev && left.ino === right.ino
|
|
9
|
-
&& left.size === right.size && left.contentHash === right.contentHash;
|
|
10
|
-
async function targetFile(transfer, path) {
|
|
11
|
-
try {
|
|
12
|
-
await safeAssetTransferDirectory(transfer.root, dirname(path) === '.' ? '' : dirname(path));
|
|
13
|
-
return await hashAssetTransferFile(join(transfer.root, path));
|
|
14
|
-
}
|
|
15
|
-
catch (error) {
|
|
16
|
-
if (assetTransferMissing(error))
|
|
17
|
-
return undefined;
|
|
18
|
-
throw error;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
async function privateFile(transfer, directory, index) {
|
|
22
|
-
try {
|
|
23
|
-
await safeAssetTransferDirectory(transfer.directory, directory);
|
|
24
|
-
return await hashAssetTransferFile(join(transfer.directory, directory, String(index)), undefined, true);
|
|
25
|
-
}
|
|
26
|
-
catch (error) {
|
|
27
|
-
if (assetTransferMissing(error))
|
|
28
|
-
return undefined;
|
|
29
|
-
throw error;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
async function assertSource(transfer, file) {
|
|
33
|
-
if (file.source && (await targetFile(transfer, file.source.path))?.contentHash !== file.source.contentHash)
|
|
34
|
-
throw new AssetTransferError('source_content_conflict');
|
|
35
|
-
}
|
|
36
|
-
async function assemble(transfer) {
|
|
37
|
-
await safeAssetTransferDirectory(transfer.directory, 'chunks', true);
|
|
38
|
-
await safeAssetTransferDirectory(transfer.directory, 'incoming', true);
|
|
39
|
-
const staged = [];
|
|
40
|
-
for (const [index, file] of transfer.record.manifest.files.entries()) {
|
|
41
|
-
const destination = join(transfer.directory, 'incoming', String(index));
|
|
42
|
-
const output = await open(destination, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 0o600);
|
|
43
|
-
const hash = createHash('sha256');
|
|
44
|
-
try {
|
|
45
|
-
for (const [chunkIndex, expected] of file.chunkHashes.entries()) {
|
|
46
|
-
let input;
|
|
47
|
-
try {
|
|
48
|
-
input = await open(join(transfer.directory, 'chunks', `${index}-${chunkIndex}`), constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
49
|
-
}
|
|
50
|
-
catch (error) {
|
|
51
|
-
if (assetTransferMissing(error))
|
|
52
|
-
throw new AssetTransferError('asset_transfer_incomplete');
|
|
53
|
-
throw error;
|
|
54
|
-
}
|
|
55
|
-
try {
|
|
56
|
-
const stat = await input.stat();
|
|
57
|
-
const size = Math.min(ASSET_TRANSFER_CHUNK_BYTES, file.size - chunkIndex * ASSET_TRANSFER_CHUNK_BYTES);
|
|
58
|
-
if (!stat.isFile() || stat.nlink !== 1 || stat.size !== size)
|
|
59
|
-
throw new AssetTransferError('asset_transfer_chunk_mismatch');
|
|
60
|
-
const bytes = await input.readFile();
|
|
61
|
-
if (bytes.length !== size || createHash('sha256').update(bytes).digest('hex') !== expected)
|
|
62
|
-
throw new AssetTransferError('asset_transfer_chunk_mismatch');
|
|
63
|
-
hash.update(bytes);
|
|
64
|
-
await output.writeFile(bytes);
|
|
65
|
-
}
|
|
66
|
-
finally {
|
|
67
|
-
await input.close();
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
await output.sync();
|
|
71
|
-
}
|
|
72
|
-
finally {
|
|
73
|
-
await output.close();
|
|
74
|
-
}
|
|
75
|
-
const contentHash = hash.digest('hex');
|
|
76
|
-
if (file.contentHash && file.contentHash !== contentHash)
|
|
77
|
-
throw new AssetTransferError('asset_transfer_content_mismatch');
|
|
78
|
-
const sampled = await hashAssetTransferFile(destination);
|
|
79
|
-
if (sampled.size !== file.size || sampled.contentHash !== contentHash)
|
|
80
|
-
throw new AssetTransferError('asset_transfer_content_mismatch');
|
|
81
|
-
staged.push(sampled);
|
|
82
|
-
}
|
|
83
|
-
return staged;
|
|
84
|
-
}
|
|
85
|
-
function validatePublication(transfer) {
|
|
86
|
-
const publication = transfer.record.publication;
|
|
87
|
-
if (!publication || publication.manifestHash !== transfer.record.manifestHash
|
|
88
|
-
|| typeof publication.operationId !== 'string' || !publication.operationId || publication.operationId.length > 128
|
|
89
|
-
|| !Array.isArray(publication.files) || publication.files.length !== transfer.record.manifest.files.length
|
|
90
|
-
|| !Array.isArray(publication.directories))
|
|
91
|
-
throw new AssetTransferError('asset_transfer_recovery_required');
|
|
92
|
-
for (const file of publication.files)
|
|
93
|
-
for (const identity of [file.staged, file.previous].filter(Boolean)) {
|
|
94
|
-
if (!identity || !Number.isSafeInteger(identity.dev) || !Number.isSafeInteger(identity.ino) || !Number.isSafeInteger(identity.size)
|
|
95
|
-
|| !/^[a-f0-9]{64}$/.test(identity.contentHash))
|
|
96
|
-
throw new AssetTransferError('asset_transfer_recovery_required');
|
|
97
|
-
}
|
|
98
|
-
for (const directory of publication.directories) {
|
|
99
|
-
if (typeof directory.path !== 'string' || !Number.isSafeInteger(directory.dev) || !Number.isSafeInteger(directory.ino)
|
|
100
|
-
|| ![...transfer.record.manifest.files.map(file => file.path), ...(transfer.record.manifest.directories ?? [])]
|
|
101
|
-
.some(path => path === directory.path || path.startsWith(directory.path + '/')))
|
|
102
|
-
throw new AssetTransferError('asset_transfer_recovery_required');
|
|
103
|
-
}
|
|
104
|
-
if (publication.markers?.some(marker => !Number.isSafeInteger(marker.dev) || !Number.isSafeInteger(marker.ino)
|
|
105
|
-
|| !transfer.record.manifest.directories?.includes(marker.path)))
|
|
106
|
-
throw new AssetTransferError('asset_transfer_recovery_required');
|
|
107
|
-
return publication;
|
|
108
|
-
}
|
|
109
|
-
async function createDirectories(transfer, path) {
|
|
110
|
-
const publication = validatePublication(transfer);
|
|
111
|
-
let directory = '';
|
|
112
|
-
for (const part of path.split('/').filter(Boolean)) {
|
|
113
|
-
directory = [directory, part].filter(Boolean).join('/');
|
|
114
|
-
try {
|
|
115
|
-
await safeAssetTransferDirectory(transfer.root, directory);
|
|
116
|
-
}
|
|
117
|
-
catch (error) {
|
|
118
|
-
if (!assetTransferMissing(error))
|
|
119
|
-
throw error;
|
|
120
|
-
await safeAssetTransferDirectory(transfer.root, dirname(directory) === '.' ? '' : dirname(directory));
|
|
121
|
-
await mkdir(join(transfer.root, directory), { mode: 0o700 });
|
|
122
|
-
const stat = await lstat(await safeAssetTransferDirectory(transfer.root, directory));
|
|
123
|
-
publication.directories.push({ path: directory, dev: stat.dev, ino: stat.ino });
|
|
124
|
-
await saveAssetTransfer(transfer);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
async function cleanPublicationFiles(transfer, completed = false) {
|
|
129
|
-
for (const name of completed ? ['chunks', 'incoming', 'previous'] : ['incoming', 'previous']) {
|
|
130
|
-
try {
|
|
131
|
-
await safeAssetTransferDirectory(transfer.directory, name);
|
|
132
|
-
await rm(join(transfer.directory, name), { recursive: true });
|
|
133
|
-
}
|
|
134
|
-
catch (error) {
|
|
135
|
-
if (!assetTransferMissing(error))
|
|
136
|
-
throw error;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
async function ensureDirectoryMarkers(transfer) {
|
|
141
|
-
const publication = validatePublication(transfer);
|
|
142
|
-
for (const path of transfer.record.manifest.directories ?? []) {
|
|
143
|
-
const parent = await safeAssetTransferDirectory(transfer.root, path);
|
|
144
|
-
const marker = join(parent, '.gitkeep');
|
|
145
|
-
try {
|
|
146
|
-
const handle = await open(marker, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 0o600);
|
|
147
|
-
try {
|
|
148
|
-
await handle.sync();
|
|
149
|
-
const stat = await handle.stat();
|
|
150
|
-
(publication.markers ??= []).push({ path, dev: stat.dev, ino: stat.ino });
|
|
151
|
-
await saveAssetTransfer(transfer);
|
|
152
|
-
}
|
|
153
|
-
finally {
|
|
154
|
-
await handle.close();
|
|
155
|
-
}
|
|
156
|
-
await syncAssetTransferDirectory(parent);
|
|
157
|
-
}
|
|
158
|
-
catch (error) {
|
|
159
|
-
if (error.code !== 'EEXIST')
|
|
160
|
-
throw error;
|
|
161
|
-
const stat = await lstat(marker);
|
|
162
|
-
if (!stat.isFile() || stat.isSymbolicLink() || stat.nlink !== 1)
|
|
163
|
-
throw new AssetTransferError('content_conflict');
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
async function assertCompletedTargets(transfer) {
|
|
168
|
-
if (!isAssetBundleReceiptForManifest(transfer.record.manifest, transfer.record.manifestHash, transfer.record.receipt))
|
|
169
|
-
throw new AssetTransferError('asset_transfer_content_mismatch');
|
|
170
|
-
for (const [index, file] of transfer.record.manifest.files.entries()) {
|
|
171
|
-
await safeAssetTransferDirectory(transfer.root, dirname(file.path) === '.' ? '' : dirname(file.path));
|
|
172
|
-
const current = await hashAssetTransferFile(join(transfer.root, file.path), undefined, false, file.chunkHashes);
|
|
173
|
-
const saved = transfer.record.receipt.files[index];
|
|
174
|
-
if (saved.path !== file.path || current.size !== file.size || current.contentHash !== saved.contentHash)
|
|
175
|
-
throw new AssetTransferError('asset_transfer_content_mismatch');
|
|
176
|
-
}
|
|
177
|
-
for (const directory of transfer.record.manifest.directories ?? [])
|
|
178
|
-
await safeAssetTransferDirectory(transfer.root, directory);
|
|
179
|
-
}
|
|
180
|
-
async function finishPublication(transfer) {
|
|
181
|
-
const publication = validatePublication(transfer);
|
|
182
|
-
const files = [];
|
|
183
|
-
for (const [index, file] of transfer.record.manifest.files.entries()) {
|
|
184
|
-
await safeAssetTransferDirectory(transfer.root, dirname(file.path) === '.' ? '' : dirname(file.path));
|
|
185
|
-
const current = await hashAssetTransferFile(join(transfer.root, file.path), undefined, false, file.chunkHashes);
|
|
186
|
-
if (!current || !same(current, publication.files[index].staged))
|
|
187
|
-
throw new AssetTransferError('content_conflict');
|
|
188
|
-
files.push({ path: file.path, size: current.size, contentHash: current.contentHash });
|
|
189
|
-
}
|
|
190
|
-
await ensureDirectoryMarkers(transfer);
|
|
191
|
-
const receipt = assetBundleReceiptSchema.parse({ transferId: transfer.record.manifest.transferId,
|
|
192
|
-
manifestHash: transfer.record.manifestHash, files,
|
|
193
|
-
...(transfer.record.manifest.directories ? { directories: transfer.record.manifest.directories } : {}) });
|
|
194
|
-
if (!isAssetBundleReceiptForManifest(transfer.record.manifest, transfer.record.manifestHash, receipt))
|
|
195
|
-
throw new AssetTransferError('asset_transfer_content_mismatch');
|
|
196
|
-
transfer.record.status = 'completed';
|
|
197
|
-
transfer.record.receipt = receipt;
|
|
198
|
-
await saveAssetTransfer(transfer);
|
|
199
|
-
await cleanPublicationFiles(transfer, true).catch(() => undefined);
|
|
200
|
-
return receipt;
|
|
201
|
-
}
|
|
202
|
-
/** Complete only this journal's exclusive link/unlink pairs left behind by a crash. */
|
|
203
|
-
async function normalizePublicationAliases(transfer) {
|
|
204
|
-
const publication = validatePublication(transfer);
|
|
205
|
-
for (const [index, file] of transfer.record.manifest.files.entries()) {
|
|
206
|
-
const parent = dirname(file.path) === '.' ? '' : dirname(file.path);
|
|
207
|
-
try {
|
|
208
|
-
await safeAssetTransferDirectory(transfer.root, parent);
|
|
209
|
-
}
|
|
210
|
-
catch (error) {
|
|
211
|
-
if (assetTransferMissing(error))
|
|
212
|
-
continue;
|
|
213
|
-
throw error;
|
|
214
|
-
}
|
|
215
|
-
const target = join(transfer.root, file.path);
|
|
216
|
-
const targetStat = await lstat(target).catch(error => { if (assetTransferMissing(error))
|
|
217
|
-
return undefined; throw error; });
|
|
218
|
-
if (!targetStat || targetStat.nlink === 1)
|
|
219
|
-
continue;
|
|
220
|
-
if (!targetStat.isFile() || targetStat.isSymbolicLink() || targetStat.nlink !== 2)
|
|
221
|
-
throw new AssetTransferError('asset_transfer_recovery_required');
|
|
222
|
-
let normalized = false;
|
|
223
|
-
for (const [directory, expected] of [['incoming', publication.files[index].staged], ['previous', publication.files[index].previous]]) {
|
|
224
|
-
if (!expected || targetStat.dev !== expected.dev || targetStat.ino !== expected.ino)
|
|
225
|
-
continue;
|
|
226
|
-
try {
|
|
227
|
-
await safeAssetTransferDirectory(transfer.directory, directory);
|
|
228
|
-
}
|
|
229
|
-
catch (error) {
|
|
230
|
-
if (assetTransferMissing(error))
|
|
231
|
-
continue;
|
|
232
|
-
throw error;
|
|
233
|
-
}
|
|
234
|
-
const alias = join(transfer.directory, directory, String(index));
|
|
235
|
-
const aliasStat = await lstat(alias).catch(error => { if (assetTransferMissing(error))
|
|
236
|
-
return undefined; throw error; });
|
|
237
|
-
if (!aliasStat || !aliasStat.isFile() || aliasStat.isSymbolicLink() || aliasStat.nlink !== 2
|
|
238
|
-
|| aliasStat.dev !== expected.dev || aliasStat.ino !== expected.ino)
|
|
239
|
-
continue;
|
|
240
|
-
const observed = await hashAssetTransferFile(target, undefined, true);
|
|
241
|
-
if (!same(observed, expected))
|
|
242
|
-
throw new AssetTransferError('asset_transfer_recovery_required');
|
|
243
|
-
// Recheck both names immediately before unlinking the known private alias.
|
|
244
|
-
const [current, privateAlias] = await Promise.all([lstat(target), lstat(alias)]);
|
|
245
|
-
if (current.dev !== expected.dev || current.ino !== expected.ino || current.nlink !== 2
|
|
246
|
-
|| privateAlias.dev !== expected.dev || privateAlias.ino !== expected.ino || privateAlias.nlink !== 2)
|
|
247
|
-
throw new AssetTransferError('asset_transfer_recovery_required');
|
|
248
|
-
await unlink(alias);
|
|
249
|
-
await syncAssetTransferDirectory(join(transfer.directory, directory));
|
|
250
|
-
normalized = true;
|
|
251
|
-
break;
|
|
252
|
-
}
|
|
253
|
-
if (!normalized)
|
|
254
|
-
throw new AssetTransferError('asset_transfer_recovery_required');
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
/** Roll back only files still owned by this publication; preserve concurrent external edits and backups. */
|
|
258
|
-
async function rollbackPublication(transfer) {
|
|
259
|
-
await normalizePublicationAliases(transfer);
|
|
260
|
-
const publication = validatePublication(transfer);
|
|
261
|
-
let conflict = false;
|
|
262
|
-
for (let index = publication.files.length - 1; index >= 0; index--) {
|
|
263
|
-
const file = transfer.record.manifest.files[index], expected = publication.files[index];
|
|
264
|
-
try {
|
|
265
|
-
const current = await targetFile(transfer, file.path);
|
|
266
|
-
const backup = await privateFile(transfer, 'previous', index);
|
|
267
|
-
if (backup) {
|
|
268
|
-
if (!expected.previous || !same(backup, expected.previous)) {
|
|
269
|
-
conflict = true;
|
|
270
|
-
continue;
|
|
271
|
-
}
|
|
272
|
-
if (current) {
|
|
273
|
-
if (!same(current, expected.staged)) {
|
|
274
|
-
conflict = true;
|
|
275
|
-
continue;
|
|
276
|
-
}
|
|
277
|
-
await unlink(join(transfer.root, file.path));
|
|
278
|
-
}
|
|
279
|
-
await safeAssetTransferDirectory(transfer.root, dirname(file.path) === '.' ? '' : dirname(file.path));
|
|
280
|
-
await link(join(transfer.directory, 'previous', String(index)), join(transfer.root, file.path));
|
|
281
|
-
await unlink(join(transfer.directory, 'previous', String(index)));
|
|
282
|
-
await syncAssetTransferDirectory(join(transfer.root, dirname(file.path)));
|
|
283
|
-
}
|
|
284
|
-
else if (current && same(current, expected.staged)) {
|
|
285
|
-
if (expected.previous) {
|
|
286
|
-
conflict = true;
|
|
287
|
-
continue;
|
|
288
|
-
}
|
|
289
|
-
await unlink(join(transfer.root, file.path));
|
|
290
|
-
}
|
|
291
|
-
else if (expected.previous && (!current || !same(current, expected.previous)))
|
|
292
|
-
conflict = true;
|
|
293
|
-
}
|
|
294
|
-
catch {
|
|
295
|
-
conflict = true;
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
if (conflict)
|
|
299
|
-
throw new AssetTransferError('asset_transfer_recovery_required');
|
|
300
|
-
for (const marker of publication.markers ?? []) {
|
|
301
|
-
try {
|
|
302
|
-
const parent = await safeAssetTransferDirectory(transfer.root, marker.path);
|
|
303
|
-
const path = join(parent, '.gitkeep'), stat = await lstat(path);
|
|
304
|
-
if (stat.dev === marker.dev && stat.ino === marker.ino && stat.isFile() && stat.nlink === 1 && stat.size === 0)
|
|
305
|
-
await unlink(path);
|
|
306
|
-
}
|
|
307
|
-
catch (error) {
|
|
308
|
-
if (!assetTransferMissing(error))
|
|
309
|
-
throw error;
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
for (const directory of [...publication.directories].reverse()) {
|
|
313
|
-
try {
|
|
314
|
-
const target = await safeAssetTransferDirectory(transfer.root, directory.path);
|
|
315
|
-
const stat = await lstat(target);
|
|
316
|
-
if (stat.dev === directory.dev && stat.ino === directory.ino)
|
|
317
|
-
await rmdir(target);
|
|
318
|
-
}
|
|
319
|
-
catch (error) {
|
|
320
|
-
if (!assetTransferMissing(error) && !['ENOTEMPTY', 'EEXIST'].includes(String(error.code)))
|
|
321
|
-
throw error;
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
transfer.record.status = 'uploading';
|
|
325
|
-
delete transfer.record.publication;
|
|
326
|
-
delete transfer.record.receipt;
|
|
327
|
-
await saveAssetTransfer(transfer);
|
|
328
|
-
await cleanPublicationFiles(transfer);
|
|
329
|
-
}
|
|
330
|
-
export async function recoverAssetTransfer(transfer) {
|
|
331
|
-
if (transfer.record.status === 'completed') {
|
|
332
|
-
await assertCompletedTargets(transfer);
|
|
333
|
-
await cleanPublicationFiles(transfer, true);
|
|
334
|
-
return;
|
|
335
|
-
}
|
|
336
|
-
if (transfer.record.status !== 'publishing')
|
|
337
|
-
return;
|
|
338
|
-
await normalizePublicationAliases(transfer);
|
|
339
|
-
try {
|
|
340
|
-
await finishPublication(transfer);
|
|
341
|
-
}
|
|
342
|
-
catch {
|
|
343
|
-
await rollbackPublication(transfer);
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
export async function publishAssetTransfer(root, scope, input, options) {
|
|
347
|
-
const payload = assetBundleSavePayloadSchema.parse(input);
|
|
348
|
-
return withAssetTransfer(root, scope, payload.transferId, async (transfer) => {
|
|
349
|
-
if (transfer.record.manifestHash !== payload.manifestHash)
|
|
350
|
-
throw new AssetTransferError('asset_transfer_manifest_conflict');
|
|
351
|
-
options.onManifest?.(transfer.record.manifest);
|
|
352
|
-
await recoverAssetTransfer(transfer);
|
|
353
|
-
if (transfer.record.status === 'completed')
|
|
354
|
-
return transfer.record.receipt;
|
|
355
|
-
await options.assertCurrent?.();
|
|
356
|
-
await cleanPublicationFiles(transfer);
|
|
357
|
-
let staged;
|
|
358
|
-
try {
|
|
359
|
-
staged = await assemble(transfer);
|
|
360
|
-
await validateAssetTransferDependencies(transfer);
|
|
361
|
-
}
|
|
362
|
-
catch (error) {
|
|
363
|
-
await cleanPublicationFiles(transfer);
|
|
364
|
-
throw error;
|
|
365
|
-
}
|
|
366
|
-
const previous = [];
|
|
367
|
-
for (const file of transfer.record.manifest.files) {
|
|
368
|
-
const observed = await targetFile(transfer, file.path);
|
|
369
|
-
if ((observed?.contentHash ?? null) !== file.expectedContentHash)
|
|
370
|
-
throw new AssetTransferError('content_conflict');
|
|
371
|
-
await assertSource(transfer, file);
|
|
372
|
-
await options.assertRevision?.(file, observed);
|
|
373
|
-
previous.push(observed);
|
|
374
|
-
}
|
|
375
|
-
transfer.record.status = 'publishing';
|
|
376
|
-
transfer.record.publication = { operationId: options.operationId, manifestHash: payload.manifestHash,
|
|
377
|
-
files: staged.map((identity, index) => ({ staged: identity, ...(previous[index] ? { previous: previous[index] } : {}) })), directories: [] };
|
|
378
|
-
await saveAssetTransfer(transfer);
|
|
379
|
-
try {
|
|
380
|
-
await safeAssetTransferDirectory(transfer.directory, 'previous', true);
|
|
381
|
-
for (const directory of transfer.record.manifest.directories ?? [])
|
|
382
|
-
await createDirectories(transfer, directory);
|
|
383
|
-
for (const [index, file] of transfer.record.manifest.files.entries()) {
|
|
384
|
-
await options.assertCurrent?.();
|
|
385
|
-
await createDirectories(transfer, dirname(file.path) === '.' ? '' : dirname(file.path));
|
|
386
|
-
const current = await targetFile(transfer, file.path);
|
|
387
|
-
if (previous[index] ? !current || !same(current, previous[index]) : current)
|
|
388
|
-
throw new AssetTransferError('content_conflict');
|
|
389
|
-
await assertSource(transfer, file);
|
|
390
|
-
if (current) {
|
|
391
|
-
await rename(join(transfer.root, file.path), join(transfer.directory, 'previous', String(index)));
|
|
392
|
-
const backup = await privateFile(transfer, 'previous', index);
|
|
393
|
-
if (!backup || !same(backup, current))
|
|
394
|
-
throw new AssetTransferError('content_conflict');
|
|
395
|
-
await syncAssetTransferDirectory(join(transfer.directory, 'previous'));
|
|
396
|
-
}
|
|
397
|
-
await safeAssetTransferDirectory(transfer.root, dirname(file.path) === '.' ? '' : dirname(file.path));
|
|
398
|
-
// Creating the destination with link refuses a concurrently-created file instead of overwriting it.
|
|
399
|
-
await link(join(transfer.directory, 'incoming', String(index)), join(transfer.root, file.path));
|
|
400
|
-
await unlink(join(transfer.directory, 'incoming', String(index)));
|
|
401
|
-
await syncAssetTransferDirectory(join(transfer.root, dirname(file.path)));
|
|
402
|
-
await options.onPublishedFile?.(index);
|
|
403
|
-
}
|
|
404
|
-
await options.assertCurrent?.();
|
|
405
|
-
return await finishPublication(transfer);
|
|
406
|
-
}
|
|
407
|
-
catch (error) {
|
|
408
|
-
await rollbackPublication(transfer);
|
|
409
|
-
throw error;
|
|
410
|
-
}
|
|
411
|
-
});
|
|
412
|
-
}
|
|
413
|
-
export async function recoverAssetTransfers(root, scope) {
|
|
414
|
-
const recovered = [];
|
|
415
|
-
let directory;
|
|
416
|
-
try {
|
|
417
|
-
directory = await safeAssetTransferDirectory(await realpath(root), '.playcraft-temp/asset-transfers');
|
|
418
|
-
}
|
|
419
|
-
catch (error) {
|
|
420
|
-
if (assetTransferMissing(error))
|
|
421
|
-
return recovered;
|
|
422
|
-
throw error;
|
|
423
|
-
}
|
|
424
|
-
for (const item of await readdir(directory, { withFileTypes: true })) {
|
|
425
|
-
if (!item.isDirectory() || !/^[a-f0-9-]{36}$/i.test(item.name))
|
|
426
|
-
continue;
|
|
427
|
-
await withAssetTransfer(root, scope, item.name, async (transfer) => {
|
|
428
|
-
await recoverAssetTransfer(transfer);
|
|
429
|
-
if (transfer.record.status === 'completed' && transfer.record.publication?.operationId)
|
|
430
|
-
recovered.push({ operationId: transfer.record.publication.operationId, receipt: transfer.record.receipt, manifest: transfer.record.manifest });
|
|
431
|
-
}).catch(() => undefined);
|
|
432
|
-
}
|
|
433
|
-
return recovered;
|
|
434
|
-
}
|