@playcraft/cli 0.1.54 → 0.1.55-beta
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 +83 -15
- package/SKILLS_REGISTRY.md +149 -0
- package/dist/agent/local-backend.js +1 -1
- package/dist/assets/client.js +157 -0
- package/dist/assets/messages.js +48 -0
- package/dist/auth/account-activation.js +157 -0
- package/dist/auth/messages.js +30 -0
- package/dist/cli-root-help.js +4 -0
- package/dist/commands/assets.js +123 -0
- package/dist/commands/build-all.js +5 -19
- package/dist/commands/build-temp.js +26 -0
- package/dist/commands/build.js +15 -28
- package/dist/commands/create.js +26 -33
- package/dist/commands/image-layer-messages.js +37 -0
- package/dist/commands/image-layer-tools.js +185 -0
- package/dist/commands/image.js +2 -73
- package/dist/commands/library-messages.js +46 -0
- package/dist/commands/library.js +130 -0
- package/dist/commands/login.js +84 -42
- package/dist/commands/platform-skills.generated.js +19 -0
- package/dist/commands/playable.js +53 -0
- package/dist/commands/preview-runtime.js +21 -0
- package/dist/commands/skills.js +4 -0
- package/dist/commands/tools-3d.js +11 -0
- package/dist/commands/tools-batch.js +113 -0
- package/dist/commands/tools-generation.js +6 -0
- package/dist/commands/tools.js +2 -0
- package/dist/commands/video.js +122 -0
- package/dist/commands/workspace-runtime.js +54 -0
- package/dist/commands/workspace.js +40 -0
- package/dist/config.js +59 -4
- package/dist/index.js +47 -3
- package/dist/preview-runtime/distribution.js +77 -0
- package/dist/project-skills/builtin-import.js +225 -0
- package/dist/project-skills/commands.js +77 -0
- package/dist/project-skills/lifecycle.js +28 -0
- package/dist/project-skills/local-run.js +112 -0
- package/dist/project-skills/local-store.js +221 -0
- package/dist/project-skills/messages.js +80 -0
- package/dist/project-skills/reconcile.js +62 -0
- package/dist/project-skills/remote-cache.js +51 -0
- package/dist/project-skills/validation.js +19 -0
- package/dist/recording/messages.js +40 -0
- package/dist/recording/project-digest.js +85 -0
- package/dist/recording/recorder.js +293 -0
- package/dist/recording/scenario.js +50 -0
- package/dist/remix/clone.js +2 -0
- package/dist/remix/init-template.js +2 -0
- package/dist/remix/pull.js +2 -0
- package/dist/remix/push.js +21 -1
- package/dist/skill-registry/client.js +345 -0
- package/dist/skill-registry/commands.js +180 -0
- package/dist/skill-registry/evaluation-client.js +156 -0
- package/dist/skill-registry/evaluation-commands.js +174 -0
- package/dist/skill-registry/messages.js +177 -0
- package/dist/skill-registry/package.js +275 -0
- package/dist/skill-registry/platform-package.js +100 -0
- package/dist/utils/agent-api-client.js +245 -30
- package/dist/utils/environment-endpoints.js +17 -0
- package/dist/utils/error-diagnostics.js +20 -0
- package/dist/utils/playcraft-http-client.js +6 -4
- package/dist/utils/tool-operation-journal.js +124 -0
- package/dist/utils/video-evidence-git.js +81 -0
- package/dist/video-replication/client.js +234 -0
- package/dist/video-replication/comparison-evidence.js +149 -0
- package/dist/video-replication/comparison-files.js +57 -0
- package/dist/video-replication/comparison-log.js +72 -0
- package/dist/video-replication/comparison-messages.js +104 -0
- package/dist/video-replication/comparison-output.js +63 -0
- package/dist/video-replication/comparison-preflight.js +66 -0
- package/dist/video-replication/comparison.js +230 -0
- package/dist/video-replication/diagnostics.js +41 -0
- package/dist/video-replication/evidence.js +165 -0
- package/dist/video-replication/media.js +112 -0
- package/dist/video-replication/messages.js +97 -0
- package/dist/workspace/messages.js +67 -0
- package/dist/workspace/transfer-client.js +237 -0
- package/dist/workspace/transfer-errors.js +48 -0
- package/dist/workspace-runtime/batch/content-changes.js +30 -0
- package/dist/workspace-runtime/batch/control.js +86 -0
- package/dist/workspace-runtime/batch/deliver.js +173 -0
- package/dist/workspace-runtime/batch/freeze.js +101 -0
- package/dist/workspace-runtime/batch/generate.js +249 -0
- package/dist/workspace-runtime/batch/git.js +40 -0
- package/dist/workspace-runtime/batch/occupancy.js +8 -0
- package/dist/workspace-runtime/batch/policy.js +70 -0
- package/dist/workspace-runtime/batch/prepare.js +317 -0
- package/dist/workspace-runtime/batch/processes.js +72 -0
- package/dist/workspace-runtime/batch/records.js +113 -0
- package/dist/workspace-runtime/batch/routes.js +61 -0
- package/dist/workspace-runtime/batch/source-candidate.js +86 -0
- package/dist/workspace-runtime/batch/source-capture.js +146 -0
- package/dist/workspace-runtime/batch/source-failure.js +43 -0
- package/dist/workspace-runtime/batch/source-publish.js +130 -0
- package/dist/workspace-runtime/codex/app-server.js +1065 -0
- package/dist/workspace-runtime/codex/compact.js +46 -0
- package/dist/workspace-runtime/codex/config-toml.js +107 -0
- package/dist/workspace-runtime/codex/delivery-tools.js +144 -0
- package/dist/workspace-runtime/codex/jsonrpc-stdio.js +145 -0
- package/dist/workspace-runtime/codex/launcher-client.js +177 -0
- package/dist/workspace-runtime/codex/loopback.js +146 -0
- package/dist/workspace-runtime/codex/mcp-config.js +39 -0
- package/dist/workspace-runtime/codex/model-broker.js +263 -0
- package/dist/workspace-runtime/codex/native-adapter.js +9 -0
- package/dist/workspace-runtime/codex/permissions.js +79 -0
- package/dist/workspace-runtime/codex/thread-image.js +38 -0
- package/dist/workspace-runtime/codex/tool-response.js +10 -0
- package/dist/workspace-runtime/codex/ui-instructions.js +4 -0
- package/dist/workspace-runtime/codex/ui-tools.js +203 -0
- package/dist/workspace-runtime/codex/work-mode.js +15 -0
- package/dist/workspace-runtime/main.js +10 -0
- package/dist/workspace-runtime/persistence/journal.js +365 -0
- package/dist/workspace-runtime/persistence/native-subagents.js +162 -0
- package/dist/workspace-runtime/persistence/sqlite-client.js +77 -0
- package/dist/workspace-runtime/persistence/sqlite-worker.js +1161 -0
- package/dist/workspace-runtime/persistence/stopped-execution.js +77 -0
- package/dist/workspace-runtime/previews/diagnostics.js +44 -0
- package/dist/workspace-runtime/previews/output.js +36 -0
- package/dist/workspace-runtime/previews/publish.js +283 -0
- package/dist/workspace-runtime/previews/runtime-preview.js +251 -0
- package/dist/workspace-runtime/previews/single-file.js +43 -0
- package/dist/workspace-runtime/processes/managed-writes.js +69 -0
- package/dist/workspace-runtime/serve.js +441 -0
- package/dist/workspace-runtime/server/accept-operation.js +61 -0
- package/dist/workspace-runtime/server/asset-transfers.js +63 -0
- package/dist/workspace-runtime/server/auth.js +21 -0
- package/dist/workspace-runtime/server/connectivity.js +211 -0
- package/dist/workspace-runtime/server/desktop-host.js +429 -0
- package/dist/workspace-runtime/server/desktop-preview-config.js +81 -0
- package/dist/workspace-runtime/server/desktop-preview-service.js +157 -0
- package/dist/workspace-runtime/server/desktop-preview-static.js +53 -0
- package/dist/workspace-runtime/server/desktop-preview.js +189 -0
- package/dist/workspace-runtime/server/dispatch.js +1655 -0
- package/dist/workspace-runtime/server/endpoints.js +21 -0
- package/dist/workspace-runtime/server/execution-group.js +90 -0
- package/dist/workspace-runtime/server/http.js +1363 -0
- package/dist/workspace-runtime/server/lock.js +53 -0
- package/dist/workspace-runtime/server/operation-record.js +33 -0
- package/dist/workspace-runtime/server/rfb-watch-gate.js +168 -0
- package/dist/workspace-runtime/server/session-restore.js +84 -0
- package/dist/workspace-runtime/server/source-uploader.js +138 -0
- package/dist/workspace-runtime/server/submit-operation.js +41 -0
- package/dist/workspace-runtime/server/terminal-host.js +366 -0
- package/dist/workspace-runtime/server/types.js +1 -0
- package/dist/workspace-runtime/server/workspace-transfer.js +168 -0
- package/dist/workspace-runtime/verify-artifact.js +59 -0
- package/dist/workspace-runtime/workspaces/append-only-log.js +62 -0
- package/dist/workspace-runtime/workspaces/archive.js +207 -0
- package/dist/workspace-runtime/workspaces/artifact-events.js +55 -0
- package/dist/workspace-runtime/workspaces/artifact-files.js +199 -0
- package/dist/workspace-runtime/workspaces/artifact-receipts.js +89 -0
- package/dist/workspace-runtime/workspaces/artifacts.js +282 -0
- package/dist/workspace-runtime/workspaces/asset-library-import.js +387 -0
- package/dist/workspace-runtime/workspaces/asset-publication-access.js +25 -0
- package/dist/workspace-runtime/workspaces/asset-transfer-dependencies.js +81 -0
- package/dist/workspace-runtime/workspaces/asset-transfer-publish.js +438 -0
- package/dist/workspace-runtime/workspaces/asset-transfer-storage.js +389 -0
- package/dist/workspace-runtime/workspaces/attachment-content.js +73 -0
- package/dist/workspace-runtime/workspaces/attachments.js +185 -0
- package/dist/workspace-runtime/workspaces/batch-commit.js +142 -0
- package/dist/workspace-runtime/workspaces/binary-content.js +65 -0
- package/dist/workspace-runtime/workspaces/binary-write.js +117 -0
- package/dist/workspace-runtime/workspaces/cli-context.js +23 -0
- package/dist/workspace-runtime/workspaces/cli-credentials.js +115 -0
- package/dist/workspace-runtime/workspaces/composer-context.js +147 -0
- package/dist/workspace-runtime/workspaces/context-error.js +2 -0
- package/dist/workspace-runtime/workspaces/delivery-build.js +331 -0
- package/dist/workspace-runtime/workspaces/delivery-store.js +168 -0
- package/dist/workspace-runtime/workspaces/delivery.js +64 -0
- package/dist/workspace-runtime/workspaces/dependencies.js +279 -0
- package/dist/workspace-runtime/workspaces/developer-command.js +98 -0
- package/dist/workspace-runtime/workspaces/directories.js +135 -0
- package/dist/workspace-runtime/workspaces/directory-access.js +37 -0
- package/dist/workspace-runtime/workspaces/directory-attachments.js +245 -0
- package/dist/workspace-runtime/workspaces/directory-create.js +57 -0
- package/dist/workspace-runtime/workspaces/directory-maintenance.js +111 -0
- package/dist/workspace-runtime/workspaces/directory-mutations.js +238 -0
- package/dist/workspace-runtime/workspaces/directory-revisions.js +56 -0
- package/dist/workspace-runtime/workspaces/directory-stats.js +49 -0
- package/dist/workspace-runtime/workspaces/disk-snapshot.js +31 -0
- package/dist/workspace-runtime/workspaces/empty-template.js +43 -0
- package/dist/workspace-runtime/workspaces/file-content.js +48 -0
- package/dist/workspace-runtime/workspaces/files-search.js +116 -0
- package/dist/workspace-runtime/workspaces/files.js +150 -0
- package/dist/workspace-runtime/workspaces/git-credential-broker.js +312 -0
- package/dist/workspace-runtime/workspaces/git-credential-helper.js +68 -0
- package/dist/workspace-runtime/workspaces/git-direct-store.js +155 -0
- package/dist/workspace-runtime/workspaces/git.js +670 -0
- package/dist/workspace-runtime/workspaces/instructions.js +56 -0
- package/dist/workspace-runtime/workspaces/json5-edit.js +313 -0
- package/dist/workspace-runtime/workspaces/library-attachments.js +268 -0
- package/dist/workspace-runtime/workspaces/operation-log.js +23 -0
- package/dist/workspace-runtime/workspaces/optional-platform-skills.js +14 -0
- package/dist/workspace-runtime/workspaces/parameter-agent-inputs.js +97 -0
- package/dist/workspace-runtime/workspaces/parameter-agent-protocol.js +40 -0
- package/dist/workspace-runtime/workspaces/parameter-agent-store.js +58 -0
- package/dist/workspace-runtime/workspaces/parameter-agent.js +284 -0
- package/dist/workspace-runtime/workspaces/parameter-assets.js +189 -0
- package/dist/workspace-runtime/workspaces/parameters.js +329 -0
- package/dist/workspace-runtime/workspaces/prepare.js +284 -0
- package/dist/workspace-runtime/workspaces/registry.js +251 -0
- package/dist/workspace-runtime/workspaces/remix-references.js +301 -0
- package/dist/workspace-runtime/workspaces/revisions.js +31 -0
- package/dist/workspace-runtime/workspaces/session-image.js +104 -0
- package/dist/workspace-runtime/workspaces/shared-git.js +40 -0
- package/dist/workspace-runtime/workspaces/skill-artifact-loader.js +95 -0
- package/dist/workspace-runtime/workspaces/skills.js +191 -0
- package/dist/workspace-runtime/workspaces/theme-asset-index.js +157 -0
- package/dist/workspace-runtime/workspaces/transfer-quota.js +69 -0
- package/dist/workspace-runtime/workspaces/transfer.js +471 -0
- package/dist/workspace-runtime/workspaces/turn-changes.js +75 -0
- package/dist/workspace-runtime/workspaces/video-attachments.js +49 -0
- package/dist/workspace-runtime/workspaces/worktree-attachments.js +74 -0
- package/dist/workspace-runtime/workspaces/worktree-checkout.js +67 -0
- package/dist/workspace-runtime/workspaces/worktrees.js +73 -0
- package/package.json +16 -5
- package/project-template/.claude/skills/playcraft-audio-generation/SKILL.md +31 -4
- package/project-template/.claude/skills/playcraft-image-generation/SKILL.md +36 -5
- package/project-template/.claude/skills/playcraft-image-generation/reference/build-sprite-sheet.template.mjs +1 -1
- package/project-template/.claude/skills/playcraft-image-generation/reference/compare-style.template.mjs +1 -1
- package/project-template/.claude/skills/playcraft-image-generation/reference/gen-batch-sprite.template.mjs +1 -1
- package/project-template/.claude/skills/playcraft-image-generation/reference/gen-batch.template.mjs +1 -1
- package/project-template/.claude/skills/playcraft-image-generation/reference/gen-edit-variants.template.mjs +1 -1
- package/project-template/.claude/skills/playcraft-image-generation/reference/process-batch.template.mjs +1 -1
- package/project-template/.claude/skills/playcraft-image-generation/reference/ta-image-generation-detail.md +300 -0
- package/project-template/.claude/skills/playcraft-image-generation/reference/validate-sprite-sheet.template.mjs +1 -1
- package/project-template/.claude/skills/playcraft-image-processing/SKILL.md +50 -25
- package/project-template/.claude/skills/playcraft-replica-validation/SKILL.md +25 -0
- package/project-template/.claude/skills/playcraft-replica-validation/references/contracts.md +79 -0
- package/project-template/.claude/skills/playcraft-video-understanding/SKILL.md +29 -0
- package/project-template/.claude/skills/playcraft-video-understanding/references/contracts.md +37 -0
- package/project-template/.claude/skills/playcraft-workspace-assets/SKILL.md +53 -0
- package/project-template/.claude/skills/playcraft-workspace-assets/references/requests.json +32 -0
- package/project-template-v2/.claude/agents/artist.md +82 -0
- package/project-template-v2/.claude/agents/developer.md +153 -0
- package/project-template-v2/.claude/agents/game-designer.md +264 -0
- package/project-template-v2/.claude/agents/refs/artist-art-style-catalog.md +533 -0
- package/project-template-v2/.claude/agents/refs/artist-color-audio-recipes.md +153 -0
- package/project-template-v2/.claude/agents/refs/artist-dimension-axis.md +27 -0
- package/project-template-v2/.claude/agents/refs/artist-master-composite-recipes.md +208 -0
- package/project-template-v2/.claude/agents/refs/atom-skill-library.md +81 -0
- package/project-template-v2/.claude/agents/refs/developer-impl-cookbook.md +432 -0
- package/project-template-v2/.claude/agents/refs/framework-5-component-filter.md +252 -0
- package/project-template-v2/.claude/agents/refs/framework-game-feel-juice.md +266 -0
- package/project-template-v2/.claude/agents/refs/framework-mda.md +147 -0
- package/project-template-v2/.claude/agents/refs/game-designer-gameplay-sufficiency.md +123 -0
- package/project-template-v2/.claude/agents/refs/ta-3d-flip-recipe.md +88 -0
- package/project-template-v2/.claude/agents/refs/ta-atlas-deliverable-standard.md +67 -0
- package/project-template-v2/.claude/agents/refs/ta-batch-pipeline-recipes.md +120 -0
- package/project-template-v2/.claude/agents/refs/ta-image-generation-detail.md +300 -0
- package/project-template-v2/.claude/agents/refs/ta-image-ops-reference.md +495 -0
- package/project-template-v2/.claude/agents/refs/ta-pipeline-cookbook.md +1141 -0
- package/project-template-v2/.claude/agents/refs/ta-tools-reference.md +111 -0
- package/project-template-v2/.claude/agents/refs/ta-vfx-preset-catalog.md +365 -0
- package/project-template-v2/.claude/agents/refs/threejs-cannon-pitfalls.md +412 -0
- package/project-template-v2/.claude/agents/reviewer.md +75 -0
- package/project-template-v2/.claude/agents/technical-artist.md +86 -0
- package/project-template-v2/.claude/hooks/snapshot-milestone.mjs +243 -0
- package/project-template-v2/.claude/hooks/user-prompt.mjs +133 -0
- package/project-template-v2/.claude/settings.json +33 -0
- package/project-template-v2/.claude/skills/brainstorming/SKILL.md +161 -0
- package/project-template-v2/.claude/skills/brainstorming/scripts/frame-template.html +270 -0
- package/project-template-v2/.claude/skills/brainstorming/scripts/helper.js +177 -0
- package/project-template-v2/.claude/skills/brainstorming/scripts/server.cjs +354 -0
- package/project-template-v2/.claude/skills/brainstorming/scripts/start-server.sh +148 -0
- package/project-template-v2/.claude/skills/brainstorming/scripts/stop-server.sh +56 -0
- package/project-template-v2/.claude/skills/brainstorming/scripts/wait-for-selection.sh +62 -0
- package/project-template-v2/.claude/skills/brainstorming/spec-document-reviewer-prompt.md +49 -0
- package/project-template-v2/.claude/skills/brainstorming/visual-companion.md +309 -0
- package/project-template-v2/.claude/skills/playcraft-ad-psychology/SKILL.md +182 -0
- package/project-template-v2/.claude/skills/playcraft-art-style-guide/SKILL.md +123 -0
- package/project-template-v2/.claude/skills/playcraft-asset-state-sheet/SKILL.md +205 -0
- package/project-template-v2/.claude/skills/playcraft-audio-generation/SKILL.md +307 -0
- package/project-template-v2/.claude/skills/playcraft-batch-pipeline/SKILL.md +184 -0
- package/project-template-v2/.claude/skills/playcraft-build-optimizer/SKILL.md +306 -0
- package/project-template-v2/.claude/skills/playcraft-image-generation/SKILL.md +401 -0
- package/project-template-v2/.claude/skills/playcraft-image-generation/reference/build-sprite-sheet.template.mjs +123 -0
- package/project-template-v2/.claude/skills/playcraft-image-generation/reference/compare-style.template.mjs +254 -0
- package/project-template-v2/.claude/skills/playcraft-image-generation/reference/gen-batch-sprite.template.mjs +333 -0
- package/project-template-v2/.claude/skills/playcraft-image-generation/reference/gen-batch.template.mjs +95 -0
- package/project-template-v2/.claude/skills/playcraft-image-generation/reference/gen-edit-variants.template.mjs +118 -0
- package/project-template-v2/.claude/skills/playcraft-image-generation/reference/process-batch.template.mjs +136 -0
- package/project-template-v2/.claude/skills/playcraft-image-generation/reference/prompt-cookbook.md +397 -0
- package/project-template-v2/.claude/skills/playcraft-image-generation/reference/ta-image-generation-detail.md +300 -0
- package/project-template-v2/.claude/skills/playcraft-image-generation/reference/validate-sprite-sheet.template.mjs +296 -0
- package/project-template-v2/.claude/skills/playcraft-image-ops/SKILL.md +122 -0
- package/project-template-v2/.claude/skills/playcraft-image-processing/SKILL.md +244 -0
- package/project-template-v2/.claude/skills/playcraft-masking/SKILL.md +373 -0
- package/project-template-v2/.claude/skills/playcraft-playable-optimization/SKILL.md +161 -0
- package/project-template-v2/.claude/skills/playcraft-research/SKILL.md +215 -0
- package/project-template-v2/.claude/skills/playcraft-skill-recommender/SKILL.md +382 -0
- package/project-template-v2/.claude/skills/playcraft-sprite-generation/SKILL.md +423 -0
- package/project-template-v2/.claude/skills/playcraft-sprite-remix/SKILL.md +158 -0
- package/project-template-v2/.claude/skills/playcraft-sprite-sheet/SKILL.md +100 -0
- package/project-template-v2/.claude/skills/playcraft-storyboard/SKILL.md +167 -0
- package/project-template-v2/.claude/skills/playcraft-style-qa/SKILL.md +270 -0
- package/project-template-v2/.claude/skills/playcraft-text-rendering/SKILL.md +236 -0
- package/project-template-v2/.claude/skills/playcraft-vfx-animation/SKILL.md +130 -0
- package/project-template-v2/.claude/skills/playcraft-workspace-assets/SKILL.md +53 -0
- package/project-template-v2/.claude/skills/playcraft-workspace-assets/references/requests.json +32 -0
- package/project-template-v2/.claude/skills/playwright-cli/SKILL.md +390 -0
- package/project-template-v2/.claude/skills/playwright-cli/references/element-attributes.md +23 -0
- package/project-template-v2/.claude/skills/playwright-cli/references/playwright-tests.md +39 -0
- package/project-template-v2/.claude/skills/playwright-cli/references/request-mocking.md +87 -0
- package/project-template-v2/.claude/skills/playwright-cli/references/running-code.md +240 -0
- package/project-template-v2/.claude/skills/playwright-cli/references/session-management.md +226 -0
- package/project-template-v2/.claude/skills/playwright-cli/references/spec-driven-testing.md +308 -0
- package/project-template-v2/.claude/skills/playwright-cli/references/storage-state.md +275 -0
- package/project-template-v2/.claude/skills/playwright-cli/references/test-generation.md +134 -0
- package/project-template-v2/.claude/skills/playwright-cli/references/tracing.md +142 -0
- package/project-template-v2/.claude/skills/playwright-cli/references/video-recording.md +153 -0
- package/project-template-v2/.claude/skills/session-analyzer/SKILL.md +386 -0
- package/project-template-v2/.claude/skills/session-analyzer/scripts/execution-breakdown.mjs +182 -0
- package/project-template-v2/.claude/skills/session-analyzer/scripts/find-turns.mjs +72 -0
- package/project-template-v2/.claude/skills/session-analyzer/scripts/heavy-output.mjs +121 -0
- package/project-template-v2/.claude/skills/session-analyzer/scripts/resolve-session.mjs +102 -0
- package/project-template-v2/.claude/skills/session-analyzer/scripts/subagent-stats.mjs +127 -0
- package/project-template-v2/.claude/skills/session-analyzer/scripts/subagent-tool-timeline.mjs +106 -0
- package/project-template-v2/.claude/skills/session-analyzer/scripts/time-gaps.mjs +128 -0
- package/project-template-v2/.claude/skills/session-analyzer/scripts/turn-timeline.mjs +67 -0
- package/project-template-v2/.claude/snapshot.mjs +263 -0
- package/project-template-v2/.playcraft/skills.lock.json +152 -0
- package/project-template-v2/CLAUDE.md +146 -0
- 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 +254 -0
- package/project-template-v2/docs/harness/iteration-1/generate-flow.md +91 -0
- package/project-template-v2/docs/harness/iteration-1/ideate-flow.md +214 -0
- package/project-template-v2/docs/harness/iteration-1/optimize-flow.md +75 -0
- package/project-template-v2/docs/harness/iteration-1/wrapup-flow.md +63 -0
- package/project-template-v2/docs/harness/iteration-2/context-flow.md +223 -0
- package/project-template-v2/docs/harness/iteration-2/generate-flow.md +129 -0
- package/project-template-v2/docs/harness/iteration-2/ideate-flow.md +267 -0
- package/project-template-v2/docs/harness/iteration-2/optimize-flow.md +164 -0
- package/project-template-v2/docs/harness/iteration-2/wrapup-flow.md +115 -0
- package/project-template-v2/docs/harness/orchestrator-flow.md +364 -0
- package/project-template-v2/docs/project-state.json +60 -0
- package/project-template-v2/docs/project-state.md +72 -0
- package/project-template-v2/docs/standards/README.md +225 -0
- package/project-template-v2/docs/standards/agent-behavior-standards.md +174 -0
- package/project-template-v2/docs/standards/artifacts/design-brief.md +19 -0
- package/project-template-v2/docs/standards/artifacts/design.md +22 -0
- package/project-template-v2/docs/standards/artifacts/game-code.md +41 -0
- package/project-template-v2/docs/standards/artifacts/todo-list.md +41 -0
- package/project-template-v2/docs/standards/iter1-agent-behavior-standards.md +343 -0
- package/project-template-v2/game/index.ts +18 -0
- package/project-template-v2/globals.d.ts +51 -0
- package/project-template-v2/index.css +34 -0
- package/project-template-v2/index.html +18 -0
- package/project-template-v2/main.ts +9 -0
- package/project-template-v2/package.json +46 -0
- package/project-template-v2/skills/_shared/scripts/dispatch-clear.mjs +31 -0
- package/project-template-v2/skills/_shared/scripts/dispatch-set.mjs +86 -0
- package/project-template-v2/skills/_shared/scripts/dod-check.mjs +153 -0
- package/project-template-v2/skills/_shared/scripts/handoff-append.mjs +70 -0
- package/project-template-v2/skills/_shared/scripts/lib/validator-artifacts.mjs +91 -0
- package/project-template-v2/skills/_shared/scripts/pipeline/dod-config.mjs +131 -0
- package/project-template-v2/skills/_shared/scripts/pipeline/index.mjs +80 -0
- package/project-template-v2/skills/_shared/scripts/pipeline/iteration-1-core.mjs +90 -0
- package/project-template-v2/skills/_shared/scripts/pipeline/iteration-2-wrap.mjs +93 -0
- package/project-template-v2/skills/_shared/scripts/pipeline/iteration-3-visual.mjs +23 -0
- package/project-template-v2/skills/_shared/scripts/render-project-state.mjs +230 -0
- package/project-template-v2/skills/_shared/scripts/state-advance-stage.mjs +58 -0
- package/project-template-v2/skills/_shared/scripts/state-get.mjs +339 -0
- package/project-template-v2/skills/_shared/scripts/state-handoff.mjs +39 -0
- package/project-template-v2/skills/_shared/scripts/state-set.mjs +94 -0
- package/project-template-v2/skills/_shared/scripts/state-store.mjs +783 -0
- package/project-template-v2/skills/_shared/scripts/todo-add.mjs +88 -0
- package/project-template-v2/skills/_shared/scripts/todo-get.mjs +130 -0
- package/project-template-v2/skills/_shared/scripts/todo-remove.mjs +33 -0
- package/project-template-v2/skills/_shared/scripts/todo-set.mjs +47 -0
- package/project-template-v2/skills/_shared/scripts/verify-asset-code-sync.mjs +268 -0
- package/project-template-v2/skills/_shared/scripts/verify-env.mjs +285 -0
- package/project-template-v2/skills/_shared/scripts/verify-placeholders.mjs +161 -0
- package/project-template-v2/skills/playable-autoplay/SKILL.md +176 -0
- package/project-template-v2/skills/playable-autoplay/agents/openai.yaml +4 -0
- package/project-template-v2/skills/playable-debug/SKILL.md +116 -0
- package/project-template-v2/skills/playable-debug/agents/openai.yaml +4 -0
- package/project-template-v2/skills/playable-debug/references/debug-config.md +40 -0
- package/project-template-v2/skills/playable-record/SKILL.md +140 -0
- package/project-template-v2/skills/playable-record/scripts/lib/dev-server.mjs +104 -0
- package/project-template-v2/skills/playable-record/scripts/lib/record-audio-bridge.js +141 -0
- package/project-template-v2/skills/playable-record/scripts/record-playable.mjs +425 -0
- package/project-template-v2/skills/playable-record/scripts/verify-contract.mjs +261 -0
- package/project-template-v2/skills/playable-record/scripts/verify-firstwin.mjs +398 -0
- package/project-template-v2/skills/playable-record/scripts/verify-fusion.mjs +322 -0
- package/project-template-v2/skills/playable-record/scripts/verify-lifecycle.mjs +105 -0
- package/project-template-v2/skills/playable-record/scripts/verify-vlm-video.mjs +230 -0
- package/project-template-v2/skills/playable-validate/SKILL.md +61 -0
- package/project-template-v2/skills/playable-validate/validation-rules.md +14 -0
- package/project-template-v2/skills/playable-verify-ui/SKILL.md +63 -0
- package/project-template-v2/skills/playable-verify-ui/scripts/lib/__init__.py +1 -0
- package/project-template-v2/skills/playable-verify-ui/scripts/lib/config_expr.js +218 -0
- package/project-template-v2/skills/playable-verify-ui/scripts/lib/node_utils.js +51 -0
- package/project-template-v2/skills/playable-verify-ui/scripts/lib/profile_loader.py +59 -0
- package/project-template-v2/skills/playable-verify-ui/scripts/lib/report.py +106 -0
- package/project-template-v2/skills/playable-verify-ui/scripts/lib/runtime_contract.js +116 -0
- package/project-template-v2/skills/playable-verify-ui/scripts/verify_fetch_antipatterns.py +59 -0
- package/project-template-v2/skills/playable-verify-ui/scripts/verify_hardcoded_layout.js +64 -0
- package/project-template-v2/skills/playable-verify-ui/scripts/verify_runtime_contract.js +58 -0
- package/project-template-v2/skills/playable-verify-ui/scripts/verify_text_overlap.py +333 -0
- package/project-template-v2/ta-workspace/scripts/.gitkeep +0 -0
- package/project-template-v2/tsconfig.json +20 -0
- package/project-template-v2/vite.config.ts +27 -0
- package/project-templates/batch-locks/phaser.yaml +922 -0
- package/project-templates/batch-locks/threejs.yaml +978 -0
- package/project-templates/empty/AGENTS.md +53 -0
- package/project-templates/empty/gitignore +33 -0
- package/project-templates/phaser/.claude/skills/playcraft-audio-generation/SKILL.md +31 -4
- package/project-templates/phaser/.claude/skills/playcraft-image-generation/SKILL.md +36 -5
- package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/build-sprite-sheet.template.mjs +1 -1
- package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/compare-style.template.mjs +1 -1
- package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/gen-batch-sprite.template.mjs +1 -1
- package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/gen-batch.template.mjs +1 -1
- package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/gen-edit-variants.template.mjs +1 -1
- package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/process-batch.template.mjs +1 -1
- package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/ta-image-generation-detail.md +300 -0
- package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/validate-sprite-sheet.template.mjs +1 -1
- package/project-templates/phaser/.claude/skills/playcraft-image-processing/SKILL.md +50 -25
- package/project-templates/phaser/.claude/skills/playcraft-workspace-assets/SKILL.md +53 -0
- package/project-templates/phaser/.claude/skills/playcraft-workspace-assets/references/requests.json +32 -0
- package/project-templates/threejs/.claude/skills/playcraft-audio-generation/SKILL.md +31 -4
- package/project-templates/threejs/.claude/skills/playcraft-image-generation/SKILL.md +36 -5
- package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/build-sprite-sheet.template.mjs +1 -1
- package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/compare-style.template.mjs +1 -1
- package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/gen-batch-sprite.template.mjs +1 -1
- package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/gen-batch.template.mjs +1 -1
- package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/gen-edit-variants.template.mjs +1 -1
- package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/process-batch.template.mjs +1 -1
- package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/ta-image-generation-detail.md +300 -0
- package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/validate-sprite-sheet.template.mjs +1 -1
- package/project-templates/threejs/.claude/skills/playcraft-image-processing/SKILL.md +50 -25
- package/project-templates/threejs/.claude/skills/playcraft-workspace-assets/SKILL.md +53 -0
- package/project-templates/threejs/.claude/skills/playcraft-workspace-assets/references/requests.json +32 -0
- package/templates/preview-runtime/v1/README.md +71 -0
- package/templates/preview-runtime/v1/build-preview.mjs +1324 -0
- package/templates/preview-runtime/v1/manifest.json +17 -0
- package/templates/preview-runtime/v1/release-guard.mjs +87 -0
- package/templates/preview-runtime/v1/runtime.d.mts +3 -0
- package/templates/preview-runtime/v1/runtime.d.ts +3 -0
- package/templates/preview-runtime/v1/runtime.mjs +1121 -0
- package/templates/preview-runtime/v1/types/adapters.d.ts +26 -0
- package/templates/preview-runtime/v1/types/preview.d.ts +23 -0
- package/templates/preview-runtime/v1/types/protocol.d.ts +94 -0
- package/templates/preview-runtime/v1/types/runtime.d.ts +14 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const messages = {
|
|
2
|
+
en: {
|
|
3
|
+
decompose: 'Separate an image into a base and editable layers using the cloud workspace pipeline',
|
|
4
|
+
input: 'Input image path or image address', outputDir: 'Output directory',
|
|
5
|
+
engine: 'Layer engine: seedream (workspace) or qwen (legacy)', imageModel: 'Image model reference for workspace layer separation',
|
|
6
|
+
size: 'Workspace layer size: auto, 1K, 1.5K, 2K', prompt: 'Optional layer guidance', team: 'Team used for model configuration',
|
|
7
|
+
numLayers: 'Legacy Qwen layer count, 2–10', safety: 'Disable the legacy Qwen safety checker', format: 'Legacy Qwen output format',
|
|
8
|
+
ninePatch: 'Analyze stretchable nine-patch borders with the cloud workspace vision model',
|
|
9
|
+
output: 'Save the analysis as a file', visionModel: 'Vision model reference',
|
|
10
|
+
invalidFile: 'Input must be a regular image file of at most {maxBytes} bytes', downloadFailed: 'Image download failed ({status})',
|
|
11
|
+
tooLarge: 'Input image exceeds {maxBytes} bytes', unsupportedInput: 'Input must be a single image in a supported format',
|
|
12
|
+
unsupportedOutput: 'Unsupported output image type: {mimeType}', invalidEngine: '--engine must be seedream or qwen',
|
|
13
|
+
legacyOnly: '--num-layers, --no-safety-checker and --output-format require --engine qwen',
|
|
14
|
+
workspaceOnly: '--image-model, --size and --team-id require --engine seedream',
|
|
15
|
+
invalidSize: '--size must be auto, 1K, 1.5K or 2K', invalidCount: '--num-layers must be an integer between 2 and 10',
|
|
16
|
+
saved: 'Saved separated layers and decompose-layers-meta.json to {outputDir}', failed: 'Image command failed: {message}',
|
|
17
|
+
},
|
|
18
|
+
zh: {
|
|
19
|
+
decompose: '使用云工作区同款管线,将图片分解为底图和可编辑图层',
|
|
20
|
+
input: '输入图片路径或图片地址', outputDir: '输出目录',
|
|
21
|
+
engine: '分层引擎:seedream(云工作区)或 qwen(旧版)', imageModel: '云工作区图层分离的图像模型引用',
|
|
22
|
+
size: '云工作区分层尺寸:auto、1K、1.5K、2K', prompt: '可选图层分离提示词', team: '模型配置所属团队',
|
|
23
|
+
numLayers: '旧版 Qwen 图层数量,2–10', safety: '关闭旧版 Qwen 安全检测', format: '旧版 Qwen 输出格式',
|
|
24
|
+
ninePatch: '使用云工作区视觉模型分析点九图可拉伸边界', output: '将分析结果保存到文件', visionModel: '视觉模型引用',
|
|
25
|
+
invalidFile: '输入必须是大小不超过 {maxBytes} 字节的普通图片文件', downloadFailed: '图片下载失败({status})',
|
|
26
|
+
tooLarge: '输入图片超过 {maxBytes} 字节', unsupportedInput: '输入必须是支持格式的单张图片',
|
|
27
|
+
unsupportedOutput: '不支持的输出图片类型:{mimeType}', invalidEngine: '--engine 必须为 seedream 或 qwen',
|
|
28
|
+
legacyOnly: '--num-layers、--no-safety-checker 和 --output-format 需要使用 --engine qwen',
|
|
29
|
+
workspaceOnly: '--image-model、--size 和 --team-id 需要使用 --engine seedream',
|
|
30
|
+
invalidSize: '--size 必须为 auto、1K、1.5K 或 2K', invalidCount: '--num-layers 必须为 2 到 10 之间的整数',
|
|
31
|
+
saved: '已将分离图层与 decompose-layers-meta.json 保存到 {outputDir}', failed: '图片命令执行失败:{message}',
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
export function imageLayerMessage(key, values = {}) {
|
|
35
|
+
const language = (process.env.PLAYCRAFT_LANGUAGE || process.env.LANG || 'en').startsWith('zh') ? 'zh' : 'en';
|
|
36
|
+
return messages[language][key].replace(/\{(\w+)\}/g, (match, name) => String(values[name] ?? match));
|
|
37
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import sharp from 'sharp';
|
|
4
|
+
import { AgentApiClient } from '../utils/agent-api-client.js';
|
|
5
|
+
import { resolveSandboxPath } from '../utils/sandbox-path.js';
|
|
6
|
+
import { imageLayerMessage as t } from './image-layer-messages.js';
|
|
7
|
+
import { captureGeneratedArtifact } from '../workspace-runtime/workspaces/artifact-receipts.js';
|
|
8
|
+
const MAX_IMAGE_BYTES = 30_000_000;
|
|
9
|
+
const isRemote = (input) => /^https?:\/\//i.test(input);
|
|
10
|
+
function localImageBytes(input, maxBytes) {
|
|
11
|
+
const filename = resolveSandboxPath(input);
|
|
12
|
+
const info = statSync(filename);
|
|
13
|
+
if (!info.isFile() || info.size > maxBytes) {
|
|
14
|
+
throw new Error(t('invalidFile', { maxBytes }));
|
|
15
|
+
}
|
|
16
|
+
return readFileSync(filename);
|
|
17
|
+
}
|
|
18
|
+
async function inputImage(input) {
|
|
19
|
+
let bytes;
|
|
20
|
+
if (isRemote(input)) {
|
|
21
|
+
const response = await fetch(input, { signal: AbortSignal.timeout(60_000) });
|
|
22
|
+
if (!response.ok || !response.body)
|
|
23
|
+
throw new Error(t('downloadFailed', { status: response.status }));
|
|
24
|
+
if (Number(response.headers.get('content-length')) > MAX_IMAGE_BYTES) {
|
|
25
|
+
await response.body.cancel();
|
|
26
|
+
throw new Error(t('tooLarge', { maxBytes: MAX_IMAGE_BYTES }));
|
|
27
|
+
}
|
|
28
|
+
const reader = response.body.getReader();
|
|
29
|
+
const chunks = [];
|
|
30
|
+
let total = 0;
|
|
31
|
+
try {
|
|
32
|
+
while (true) {
|
|
33
|
+
const chunk = await reader.read();
|
|
34
|
+
if (chunk.done)
|
|
35
|
+
break;
|
|
36
|
+
total += chunk.value.byteLength;
|
|
37
|
+
if (total > MAX_IMAGE_BYTES)
|
|
38
|
+
throw new Error(t('tooLarge', { maxBytes: MAX_IMAGE_BYTES }));
|
|
39
|
+
chunks.push(chunk.value);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
await reader.cancel();
|
|
44
|
+
}
|
|
45
|
+
bytes = Buffer.concat(chunks);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
bytes = localImageBytes(input, MAX_IMAGE_BYTES);
|
|
49
|
+
}
|
|
50
|
+
const metadata = await sharp(bytes).metadata();
|
|
51
|
+
const inputMimeTypes = { png: 'image/png', jpeg: 'image/jpeg', webp: 'image/webp' };
|
|
52
|
+
const mimeType = inputMimeTypes[metadata.format ?? ''];
|
|
53
|
+
if (!mimeType || (metadata.pages ?? 1) > 1)
|
|
54
|
+
throw new Error(t('unsupportedInput'));
|
|
55
|
+
return { imageBase64: bytes.toString('base64'), mimeType };
|
|
56
|
+
}
|
|
57
|
+
function imageExtension(mimeType) {
|
|
58
|
+
const formats = { 'image/png': 'png', 'image/jpeg': 'jpg', 'image/webp': 'webp' };
|
|
59
|
+
const extension = formats[mimeType];
|
|
60
|
+
if (!extension)
|
|
61
|
+
throw new Error(t('unsupportedOutput', { mimeType }));
|
|
62
|
+
return extension;
|
|
63
|
+
}
|
|
64
|
+
function saveLayer(outputDir, stem, layer) {
|
|
65
|
+
const file = `${stem}.${imageExtension(layer.mimeType)}`;
|
|
66
|
+
writeFileSync(join(outputDir, file), Buffer.from(layer.imageBase64, 'base64'));
|
|
67
|
+
const { imageBase64: _bytes, ...metadata } = layer;
|
|
68
|
+
return { ...metadata, file };
|
|
69
|
+
}
|
|
70
|
+
function fail(error) {
|
|
71
|
+
console.error(t('failed', { message: error instanceof Error ? error.message : String(error) }));
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
export function registerImageLayerTools(image) {
|
|
75
|
+
image.command('decompose-layers')
|
|
76
|
+
.description(t('decompose'))
|
|
77
|
+
.requiredOption('--input <path>', t('input'))
|
|
78
|
+
.option('--output-dir <dir>', t('outputDir'), './decompose-layers-output')
|
|
79
|
+
.option('--engine <name>', t('engine'), 'seedream')
|
|
80
|
+
.option('--image-model <ref>', t('imageModel'))
|
|
81
|
+
.option('--size <size>', t('size'))
|
|
82
|
+
.option('--prompt <text>', t('prompt'), '')
|
|
83
|
+
.option('--team-id <id>', t('team'))
|
|
84
|
+
.option('--num-layers <n>', t('numLayers'), (value) => Number(value), 4)
|
|
85
|
+
.option('--no-safety-checker', t('safety'))
|
|
86
|
+
.option('--output-format <format>', t('format'), 'png')
|
|
87
|
+
.action(async (opts, command) => {
|
|
88
|
+
try {
|
|
89
|
+
const legacyFlags = ['numLayers', 'safetyChecker', 'outputFormat'].some(name => command.getOptionValueSource(name) === 'cli');
|
|
90
|
+
const explicitEngine = command.getOptionValueSource('engine') === 'cli';
|
|
91
|
+
const engine = legacyFlags && !explicitEngine ? 'qwen' : opts.engine;
|
|
92
|
+
if (!['seedream', 'qwen'].includes(engine))
|
|
93
|
+
throw new Error(t('invalidEngine'));
|
|
94
|
+
if (engine === 'seedream' && legacyFlags) {
|
|
95
|
+
throw new Error(t('legacyOnly'));
|
|
96
|
+
}
|
|
97
|
+
if (engine === 'qwen' && (opts.imageModel || opts.size || opts.teamId)) {
|
|
98
|
+
throw new Error(t('workspaceOnly'));
|
|
99
|
+
}
|
|
100
|
+
if (opts.size && !['auto', '1K', '1.5K', '2K'].includes(opts.size)) {
|
|
101
|
+
throw new Error(t('invalidSize'));
|
|
102
|
+
}
|
|
103
|
+
if (engine === 'qwen' && (!Number.isInteger(opts.numLayers) || opts.numLayers < 2 || opts.numLayers > 10)) {
|
|
104
|
+
throw new Error(t('invalidCount'));
|
|
105
|
+
}
|
|
106
|
+
const outputDir = resolveSandboxPath(opts.outputDir);
|
|
107
|
+
const input = opts.input;
|
|
108
|
+
const startedAt = Date.now();
|
|
109
|
+
const client = new AgentApiClient();
|
|
110
|
+
let metadata;
|
|
111
|
+
const savedImages = [];
|
|
112
|
+
if (engine === 'seedream') {
|
|
113
|
+
const result = await client.post('/image/layer-decomposition', {
|
|
114
|
+
...await inputImage(input),
|
|
115
|
+
prompt: opts.prompt || undefined,
|
|
116
|
+
size: opts.size || 'auto',
|
|
117
|
+
imageModelRef: opts.imageModel,
|
|
118
|
+
teamId: opts.teamId,
|
|
119
|
+
});
|
|
120
|
+
mkdirSync(outputDir, { recursive: true });
|
|
121
|
+
const base = saveLayer(outputDir, 'base', result.base);
|
|
122
|
+
const layers = result.layers.map((layer, index) => saveLayer(outputDir, `layer_${String(index).padStart(2, '0')}`, layer));
|
|
123
|
+
savedImages.push(...[base, ...layers].map(layer => join(outputDir, layer.file)));
|
|
124
|
+
metadata = { source: input, engine, base, layers, usage: result.usage };
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
const maxBytes = Number.parseInt(process.env.PLAYCRAFT_IMAGE_MAX_INPUT_BYTES || '', 10);
|
|
128
|
+
const payload = {
|
|
129
|
+
...(isRemote(input) ? { imageUrl: input } : {
|
|
130
|
+
imageBase64: localImageBytes(input, maxBytes > 0 ? maxBytes : 512 * 1024 * 1024).toString('base64'),
|
|
131
|
+
}),
|
|
132
|
+
numLayers: opts.numLayers,
|
|
133
|
+
prompt: opts.prompt,
|
|
134
|
+
enableSafetyChecker: opts.safetyChecker !== false,
|
|
135
|
+
outputFormat: opts.outputFormat,
|
|
136
|
+
};
|
|
137
|
+
const result = await client.post('/image-layered/decompose', payload);
|
|
138
|
+
mkdirSync(outputDir, { recursive: true });
|
|
139
|
+
const layers = result.layers.map((layer) => {
|
|
140
|
+
const file = `layer_${String(layer.index).padStart(2, '0')}.${imageExtension(layer.mimeType)}`;
|
|
141
|
+
writeFileSync(join(outputDir, file), Buffer.from(layer.imageBase64, 'base64'));
|
|
142
|
+
savedImages.push(join(outputDir, file));
|
|
143
|
+
const { imageBase64: _bytes, ...details } = layer;
|
|
144
|
+
return { ...details, file };
|
|
145
|
+
});
|
|
146
|
+
metadata = { source: input, engine, requestId: result.requestId, numLayers: result.numLayers,
|
|
147
|
+
hasNsfwConcepts: result.hasNsfwConcepts, layers };
|
|
148
|
+
}
|
|
149
|
+
const elapsed = `${((Date.now() - startedAt) / 1000).toFixed(1)}s`;
|
|
150
|
+
writeFileSync(join(outputDir, 'decompose-layers-meta.json'), JSON.stringify({ ...metadata, elapsed }, null, 2));
|
|
151
|
+
for (const imagePath of savedImages)
|
|
152
|
+
await captureGeneratedArtifact(imagePath, 'decompose-layers');
|
|
153
|
+
console.log(t('saved', { outputDir }));
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
fail(error);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
image.command('analyze-nine-patch')
|
|
160
|
+
.description(t('ninePatch'))
|
|
161
|
+
.requiredOption('--input <path>', t('input'))
|
|
162
|
+
.option('--output <path>', t('output'))
|
|
163
|
+
.option('--vision-model <ref>', t('visionModel'))
|
|
164
|
+
.option('--team-id <id>', t('team'))
|
|
165
|
+
.action(async (opts) => {
|
|
166
|
+
try {
|
|
167
|
+
const input = await inputImage(opts.input);
|
|
168
|
+
const result = await new AgentApiClient().post('/image/analyze-nine-patch', {
|
|
169
|
+
imageBase64: input.imageBase64,
|
|
170
|
+
visionModelRef: opts.visionModel,
|
|
171
|
+
teamId: opts.teamId,
|
|
172
|
+
});
|
|
173
|
+
const json = JSON.stringify(result, null, 2);
|
|
174
|
+
if (opts.output) {
|
|
175
|
+
const output = resolveSandboxPath(opts.output);
|
|
176
|
+
mkdirSync(dirname(output), { recursive: true });
|
|
177
|
+
writeFileSync(output, json);
|
|
178
|
+
}
|
|
179
|
+
console.log(json);
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
fail(error);
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
}
|
package/dist/commands/image.js
CHANGED
|
@@ -4,6 +4,7 @@ import { fileURLToPath } from 'url';
|
|
|
4
4
|
import sharp from 'sharp';
|
|
5
5
|
import yaml from 'js-yaml';
|
|
6
6
|
import { AgentApiClient } from '../utils/agent-api-client.js';
|
|
7
|
+
import { registerImageLayerTools } from './image-layer-tools.js';
|
|
7
8
|
import { resolveSandboxPath } from '../utils/sandbox-path.js';
|
|
8
9
|
/** 单文件输入上限(字节),防止误处理巨型文件导致 OOM */
|
|
9
10
|
const DEFAULT_MAX_INPUT_BYTES = 512 * 1024 * 1024;
|
|
@@ -1853,79 +1854,7 @@ export function registerImageCommands(program) {
|
|
|
1853
1854
|
handleError(e);
|
|
1854
1855
|
}
|
|
1855
1856
|
});
|
|
1856
|
-
|
|
1857
|
-
img
|
|
1858
|
-
.command('decompose-layers')
|
|
1859
|
-
.description([
|
|
1860
|
-
'302 Qwen-Image-Layered 整图语义分层:将合成图拆成 N 个 RGBA 图层',
|
|
1861
|
-
'',
|
|
1862
|
-
' 示例:',
|
|
1863
|
-
' playcraft image decompose-layers --input composite.png --output-dir ./layers',
|
|
1864
|
-
' playcraft image decompose-layers --input https://example.com/a.png --num-layers 4',
|
|
1865
|
-
'',
|
|
1866
|
-
' 需 Admin 配置 provider=302、modelKind=image 的 API Key;耗时约 1–2 分钟。',
|
|
1867
|
-
' 本地文件需 storage 公网 URL,或 --input 使用 HTTPS 直链。',
|
|
1868
|
-
].join('\n'))
|
|
1869
|
-
.requiredOption('--input <path>', '输入图片路径或 HTTPS URL')
|
|
1870
|
-
.option('--output-dir <dir>', '输出目录', './decompose-layers-output')
|
|
1871
|
-
.option('--num-layers <n>', '分层数量 2–10', cliParseInt, 4)
|
|
1872
|
-
.option('--prompt <text>', '可选分层语义引导', '')
|
|
1873
|
-
.option('--no-safety-checker', '关闭安全检测')
|
|
1874
|
-
.option('--output-format <fmt>', '输出格式', 'png')
|
|
1875
|
-
.action(async (opts) => {
|
|
1876
|
-
try {
|
|
1877
|
-
const payload = {
|
|
1878
|
-
numLayers: opts.numLayers,
|
|
1879
|
-
prompt: opts.prompt || '',
|
|
1880
|
-
enableSafetyChecker: opts.safetyChecker !== false,
|
|
1881
|
-
outputFormat: opts.outputFormat,
|
|
1882
|
-
};
|
|
1883
|
-
const input = opts.input;
|
|
1884
|
-
const isUrl = input.startsWith('http://') || input.startsWith('https://');
|
|
1885
|
-
if (isUrl) {
|
|
1886
|
-
payload.imageUrl = input;
|
|
1887
|
-
console.log(`Using image URL: ${input}`);
|
|
1888
|
-
}
|
|
1889
|
-
else {
|
|
1890
|
-
const resolvedDecomposeInput = assertInputWithinLimit(input);
|
|
1891
|
-
payload.imageBase64 = readFileSync(resolvedDecomposeInput).toString('base64');
|
|
1892
|
-
console.log(`Uploading local image: ${input}`);
|
|
1893
|
-
}
|
|
1894
|
-
console.log(`Decomposing into ${payload.numLayers} layer(s) via 302 Qwen-Image-Layered (may take 1–2 min)…`);
|
|
1895
|
-
const t0 = Date.now();
|
|
1896
|
-
const client = new AgentApiClient();
|
|
1897
|
-
const result = await client.post('/image-layered/decompose', payload);
|
|
1898
|
-
const elapsed = ((Date.now() - t0) / 1000).toFixed(1);
|
|
1899
|
-
const outputDir = opts.outputDir;
|
|
1900
|
-
mkdirSync(outputDir, { recursive: true });
|
|
1901
|
-
for (const layer of result.layers) {
|
|
1902
|
-
const ext = layer.mimeType.includes('png') ? 'png' : 'png';
|
|
1903
|
-
const filename = `layer_${String(layer.index).padStart(2, '0')}.${ext}`;
|
|
1904
|
-
writeFileSync(join(outputDir, filename), Buffer.from(layer.imageBase64, 'base64'));
|
|
1905
|
-
const dim = layer.width && layer.height ? ` ${layer.width}×${layer.height}` : '';
|
|
1906
|
-
console.log(` ${filename}${dim}`);
|
|
1907
|
-
}
|
|
1908
|
-
const meta = {
|
|
1909
|
-
source: input,
|
|
1910
|
-
requestId: result.requestId,
|
|
1911
|
-
numLayers: result.numLayers,
|
|
1912
|
-
elapsed: `${elapsed}s`,
|
|
1913
|
-
hasNsfwConcepts: result.hasNsfwConcepts,
|
|
1914
|
-
layers: result.layers.map((l) => ({
|
|
1915
|
-
index: l.index,
|
|
1916
|
-
url: l.url,
|
|
1917
|
-
width: l.width,
|
|
1918
|
-
height: l.height,
|
|
1919
|
-
seed: l.seed,
|
|
1920
|
-
})),
|
|
1921
|
-
};
|
|
1922
|
-
writeFileSync(join(outputDir, 'decompose-layers-meta.json'), JSON.stringify(meta, null, 2), 'utf-8');
|
|
1923
|
-
console.log(`\nDone in ${elapsed}s — ${result.layers.length} layer(s) → ${outputDir}/`);
|
|
1924
|
-
}
|
|
1925
|
-
catch (e) {
|
|
1926
|
-
handleError(e);
|
|
1927
|
-
}
|
|
1928
|
-
});
|
|
1857
|
+
registerImageLayerTools(img);
|
|
1929
1858
|
// ─── segment ─────────────────────────────────────────────────────────────
|
|
1930
1859
|
img
|
|
1931
1860
|
.command('segment')
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const messages = {
|
|
2
|
+
en: {
|
|
3
|
+
topic: 'Find and download authorized shared asset library content',
|
|
4
|
+
list: 'List library assets with filters and pagination', search: 'Search library names and content metadata',
|
|
5
|
+
get: 'Read asset metadata', files: 'List every file in an asset or bundle', facets: 'Inspect available categories and tags',
|
|
6
|
+
download: 'Download an asset or a complete bundle with relative dependencies',
|
|
7
|
+
query: 'Search text', id: 'Library asset identifier', type: 'Asset type', path: 'Library path prefix',
|
|
8
|
+
category: 'Category', subcategory: 'Subcategory', tags: 'Comma-separated tags',
|
|
9
|
+
visibility: 'public | team', team: 'Authorized team identifier', page: 'Page number, starting at 1',
|
|
10
|
+
limit: 'Results per page (1–100)', sort: 'modifiedAt | createdAt | name | referenceCount | size', order: 'asc | desc',
|
|
11
|
+
expand: 'Return individual files instead of collapsed bundles',
|
|
12
|
+
output: 'New file path, or a new directory for a bundle', outputDir: 'New directory for the downloaded content',
|
|
13
|
+
json: 'Print machine-readable JSON (JavaScript Object Notation, 数据交换格式; always enabled)',
|
|
14
|
+
invalidNumber: 'Expected a positive integer.', invalidId: 'Invalid library asset identifier.',
|
|
15
|
+
invalidManifest: 'The library returned an invalid or unsafe file manifest.',
|
|
16
|
+
outputRequired: 'Specify exactly one of --output or --output-dir.',
|
|
17
|
+
outputExists: 'The output already exists. Choose a new path to preserve existing files.',
|
|
18
|
+
failed: 'Asset library command failed.',
|
|
19
|
+
},
|
|
20
|
+
zh: {
|
|
21
|
+
topic: '检索和下载有权访问的共享素材库内容',
|
|
22
|
+
list: '按条件分页列出素材库内容', search: '检索素材名称和内容元数据',
|
|
23
|
+
get: '读取素材详情', files: '列出素材或素材包内的全部文件', facets: '查看可用分类与标签',
|
|
24
|
+
download: '下载素材或完整素材包,保留依赖文件的相对路径',
|
|
25
|
+
query: '检索文本', id: '素材库中的素材编号', type: '素材类型', path: '素材库路径前缀',
|
|
26
|
+
category: '分类', subcategory: '子分类', tags: '标签,逗号分隔',
|
|
27
|
+
visibility: 'public(公共)或 team(团队)', team: '有权访问的团队编号', page: '页码,从 1 开始',
|
|
28
|
+
limit: '每页数量(1–100)', sort: 'modifiedAt | createdAt | name | referenceCount | size', order: 'asc | desc',
|
|
29
|
+
expand: '返回单独文件,不折叠素材包', output: '新的文件路径;素材包则为新的目录', outputDir: '下载内容的新目录',
|
|
30
|
+
json: '输出机器可读的 JSON (JavaScript Object Notation, 数据交换格式),始终启用',
|
|
31
|
+
invalidNumber: '请输入正整数。', invalidId: '素材库编号无效。',
|
|
32
|
+
invalidManifest: '素材库返回的文件清单无效或包含不安全路径。',
|
|
33
|
+
outputRequired: '请指定 --output 或 --output-dir,且只指定一项。',
|
|
34
|
+
outputExists: '输出路径已存在,请选择新路径以保留已有文件。', failed: '素材库命令执行失败。',
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
export function libraryMessage(key) {
|
|
38
|
+
return messages[(process.env.PLAYCRAFT_LANGUAGE || process.env.LANG || 'en').startsWith('zh') ? 'zh' : 'en'][key];
|
|
39
|
+
}
|
|
40
|
+
export class LibraryError extends Error {
|
|
41
|
+
code;
|
|
42
|
+
constructor(code) {
|
|
43
|
+
super(libraryMessage(code));
|
|
44
|
+
this.code = code;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { InvalidArgumentError, Option } from 'commander';
|
|
2
|
+
import { link, lstat, mkdir, mkdtemp, rename, rm } from 'node:fs/promises';
|
|
3
|
+
import { dirname, join, posix, resolve } from 'node:path';
|
|
4
|
+
import { AgentApiClient } from '../utils/agent-api-client.js';
|
|
5
|
+
import { resolveSandboxPath } from '../utils/sandbox-path.js';
|
|
6
|
+
import { libraryMessage as t, LibraryError } from './library-messages.js';
|
|
7
|
+
function assetId(value) {
|
|
8
|
+
if (!/^(?:\d{1,20}|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})$/i.test(value))
|
|
9
|
+
throw new InvalidArgumentError(t('invalidId'));
|
|
10
|
+
return value;
|
|
11
|
+
}
|
|
12
|
+
function positiveInteger(value) {
|
|
13
|
+
if (!/^[1-9]\d*$/.test(value) || !Number.isSafeInteger(Number(value)))
|
|
14
|
+
throw new InvalidArgumentError(t('invalidNumber'));
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
function filters(command) {
|
|
18
|
+
return command.option('--search <text>', t('query')).option('--type <type>', t('type'))
|
|
19
|
+
.option('--path <path>', t('path')).option('--category <category>', t('category'))
|
|
20
|
+
.option('--subcategory <category>', t('subcategory')).option('--tags <tags>', t('tags'))
|
|
21
|
+
.addOption(new Option('--visibility <scope>', t('visibility')).choices(['public', 'team']))
|
|
22
|
+
.option('--team-id <id>', t('team')).option('--page <number>', t('page'), positiveInteger, '1')
|
|
23
|
+
.option('--limit <number>', t('limit'), value => {
|
|
24
|
+
positiveInteger(value);
|
|
25
|
+
if (Number(value) > 100)
|
|
26
|
+
throw new InvalidArgumentError(t('limit'));
|
|
27
|
+
return value;
|
|
28
|
+
}, '30')
|
|
29
|
+
.addOption(new Option('--sort-by <field>', t('sort')).choices(['modifiedAt', 'createdAt', 'name', 'referenceCount', 'size']))
|
|
30
|
+
.addOption(new Option('--sort-order <order>', t('order')).choices(['asc', 'desc']))
|
|
31
|
+
.option('--expand-prefabs', t('expand')).option('--json', t('json'));
|
|
32
|
+
}
|
|
33
|
+
function query(options) {
|
|
34
|
+
return Object.fromEntries(Object.entries(options)
|
|
35
|
+
.filter(([key, value]) => key !== 'json' && value !== undefined && value !== false)
|
|
36
|
+
.map(([key, value]) => [key, String(value)]));
|
|
37
|
+
}
|
|
38
|
+
async function action(run) {
|
|
39
|
+
try {
|
|
40
|
+
console.log(JSON.stringify({ ok: true, result: await run() }, null, 2));
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
const code = error instanceof LibraryError ? error.code : 'failed';
|
|
44
|
+
console.error(`${t('failed')} ${error instanceof Error ? error.message : String(error)}`);
|
|
45
|
+
console.log(JSON.stringify({ ok: false, error: { code, message: error instanceof Error ? error.message : String(error) } }));
|
|
46
|
+
process.exitCode = 1;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function validatedFiles(manifest) {
|
|
50
|
+
if (!manifest || !['file', 'bundle'].includes(manifest.kind) || !Array.isArray(manifest.files) ||
|
|
51
|
+
!manifest.files.length || (manifest.kind === 'file' && manifest.files.length !== 1))
|
|
52
|
+
throw new LibraryError('invalidManifest');
|
|
53
|
+
const seen = new Set();
|
|
54
|
+
for (const file of manifest.files) {
|
|
55
|
+
try {
|
|
56
|
+
assetId(file.id);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
throw new LibraryError('invalidManifest');
|
|
60
|
+
}
|
|
61
|
+
const path = file.path;
|
|
62
|
+
if (typeof path !== 'string' || !path || path.startsWith('/') || /[\\:\x00-\x1f]/.test(path) ||
|
|
63
|
+
path.split('/').some(part => !part || part === '.' || part === '..') || posix.normalize(path) !== path)
|
|
64
|
+
throw new LibraryError('invalidManifest');
|
|
65
|
+
const key = path.toLowerCase();
|
|
66
|
+
if (seen.has(key))
|
|
67
|
+
throw new LibraryError('invalidManifest');
|
|
68
|
+
seen.add(key);
|
|
69
|
+
}
|
|
70
|
+
for (const key of seen) {
|
|
71
|
+
for (let parent = posix.dirname(key); parent !== '.'; parent = posix.dirname(parent)) {
|
|
72
|
+
if (seen.has(parent))
|
|
73
|
+
throw new LibraryError('invalidManifest');
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return manifest.files;
|
|
77
|
+
}
|
|
78
|
+
export async function downloadLibraryAsset(client, id, options) {
|
|
79
|
+
if (Boolean(options.output) === Boolean(options.outputDir))
|
|
80
|
+
throw new LibraryError('outputRequired');
|
|
81
|
+
assetId(id);
|
|
82
|
+
const manifest = await client.get(`/asset-library/${encodeURIComponent(id)}/files`);
|
|
83
|
+
const files = validatedFiles(manifest);
|
|
84
|
+
const destination = resolve(resolveSandboxPath(options.output ?? options.outputDir));
|
|
85
|
+
const existing = await lstat(destination).catch(error => { if (error.code === 'ENOENT')
|
|
86
|
+
return undefined; throw error; });
|
|
87
|
+
if (existing)
|
|
88
|
+
throw new LibraryError('outputExists');
|
|
89
|
+
await mkdir(dirname(destination), { recursive: true });
|
|
90
|
+
const staging = await mkdtemp(join(dirname(destination), '.playcraft-library-'));
|
|
91
|
+
const directory = manifest.kind === 'bundle' || Boolean(options.outputDir);
|
|
92
|
+
const downloaded = [];
|
|
93
|
+
try {
|
|
94
|
+
for (const file of files) {
|
|
95
|
+
const target = directory ? join(staging, file.path) : join(staging, 'content');
|
|
96
|
+
const { bytes } = await client.download(`/asset-library/${encodeURIComponent(file.id)}/file`, target);
|
|
97
|
+
downloaded.push({ id: file.id, path: directory ? join(destination, file.path) : destination, bytes });
|
|
98
|
+
}
|
|
99
|
+
// Recheck before publishing the complete bundle; failures never expose a partial download.
|
|
100
|
+
if (await lstat(destination).catch(error => { if (error.code === 'ENOENT')
|
|
101
|
+
return undefined; throw error; }))
|
|
102
|
+
throw new LibraryError('outputExists');
|
|
103
|
+
if (directory)
|
|
104
|
+
await rename(staging, destination);
|
|
105
|
+
else
|
|
106
|
+
await link(join(staging, 'content'), destination);
|
|
107
|
+
return { assetId: manifest.assetId, kind: manifest.kind, output: destination, files: downloaded };
|
|
108
|
+
}
|
|
109
|
+
finally {
|
|
110
|
+
await rm(staging, { recursive: true, force: true });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
export function registerLibraryCommands(program) {
|
|
114
|
+
const library = program.command('library').description(t('topic'));
|
|
115
|
+
filters(library.command('list').description(t('list')))
|
|
116
|
+
.action((options) => action(() => new AgentApiClient().get('/asset-library', query(options))));
|
|
117
|
+
filters(library.command('search').description(t('search')).argument('<query>', t('query')))
|
|
118
|
+
.action((search, options) => action(() => new AgentApiClient().get('/asset-library', query({ ...options, search }))));
|
|
119
|
+
library.command('facets').description(t('facets')).option('--path <path>', t('path'))
|
|
120
|
+
.addOption(new Option('--visibility <scope>', t('visibility')).choices(['public', 'team']))
|
|
121
|
+
.option('--team-id <id>', t('team')).option('--json', t('json'))
|
|
122
|
+
.action((options) => action(() => new AgentApiClient().get('/asset-library/facets', query(options))));
|
|
123
|
+
for (const command of ['get', 'files']) {
|
|
124
|
+
library.command(command).description(t(command)).argument('<assetId>', t('id'), assetId).option('--json', t('json'))
|
|
125
|
+
.action((id) => action(() => new AgentApiClient().get(`/asset-library/${encodeURIComponent(id)}${command === 'files' ? '/files' : ''}`)));
|
|
126
|
+
}
|
|
127
|
+
library.command('download').description(t('download')).argument('<assetId>', t('id'), assetId)
|
|
128
|
+
.option('--output <path>', t('output')).option('--output-dir <directory>', t('outputDir')).option('--json', t('json'))
|
|
129
|
+
.action((id, options) => action(() => downloadLibraryAsset(new AgentApiClient(), id, options)));
|
|
130
|
+
}
|