@playcraft/cli 0.1.42 → 0.1.44-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 +62 -3
- package/SKILLS_REGISTRY.md +149 -0
- package/dist/agent/local-backend.js +1 -1
- package/dist/auth/account-activation.js +157 -0
- package/dist/auth/messages.js +30 -0
- package/dist/cli-root-help.js +2 -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.js +2 -6
- 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/skills.js +4 -0
- package/dist/commands/tools-3d.js +11 -0
- package/dist/commands/tools-batch.js +105 -0
- package/dist/commands/tools-generation.js +69 -137
- package/dist/commands/tools-utils.js +0 -12
- package/dist/commands/tools.js +2 -2
- 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 +40 -3
- 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 +210 -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 +233 -0
- package/dist/workspace/transfer-errors.js +48 -0
- package/dist/workspace-runtime/batch/control.js +80 -0
- package/dist/workspace-runtime/batch/deliver.js +150 -0
- package/dist/workspace-runtime/batch/freeze.js +102 -0
- package/dist/workspace-runtime/batch/generate.js +205 -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 +202 -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 +58 -0
- package/dist/workspace-runtime/batch/source-capture.js +159 -0
- package/dist/workspace-runtime/codex/app-server.js +1040 -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/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/ui-instructions.js +4 -0
- package/dist/workspace-runtime/codex/ui-tools.js +186 -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/output.js +36 -0
- package/dist/workspace-runtime/previews/publish.js +262 -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/production/content-changes.js +36 -0
- package/dist/workspace-runtime/production/contracts.js +19 -0
- package/dist/workspace-runtime/production/controller.js +228 -0
- package/dist/workspace-runtime/production/dependencies.js +60 -0
- package/dist/workspace-runtime/production/native.js +239 -0
- package/dist/workspace-runtime/production/processes.js +102 -0
- package/dist/workspace-runtime/production/save-command.js +35 -0
- package/dist/workspace-runtime/production/source-archive.js +137 -0
- package/dist/workspace-runtime/production/source-copy.js +92 -0
- package/dist/workspace-runtime/production/stages.js +354 -0
- package/dist/workspace-runtime/production/storage.js +75 -0
- package/dist/workspace-runtime/serve.js +283 -0
- package/dist/workspace-runtime/server/auth.js +21 -0
- package/dist/workspace-runtime/server/connectivity.js +209 -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 +1339 -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 +1297 -0
- package/dist/workspace-runtime/server/lock.js +53 -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/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/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 +366 -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 +112 -0
- package/dist/workspace-runtime/workspaces/cli-context.js +23 -0
- package/dist/workspace-runtime/workspaces/cli-credentials.js +111 -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/dependencies.js +264 -0
- package/dist/workspace-runtime/workspaces/developer-command.js +64 -0
- package/dist/workspace-runtime/workspaces/directories.js +137 -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-stats.js +49 -0
- package/dist/workspace-runtime/workspaces/disk-snapshot.js +31 -0
- package/dist/workspace-runtime/workspaces/empty-template.js +42 -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 +170 -0
- package/dist/workspace-runtime/workspaces/library-attachments.js +268 -0
- package/dist/workspace-runtime/workspaces/optional-platform-skills.js +14 -0
- package/dist/workspace-runtime/workspaces/parameters.js +272 -0
- package/dist/workspace-runtime/workspaces/prepare.js +278 -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/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/package.json +15 -5
- package/project-template/.claude/agents/refs/ta-image-generation-detail.md +123 -67
- package/project-template/.claude/skills/playcraft-image-generation/SKILL.md +63 -135
- package/project-template/.claude/skills/playcraft-image-generation/reference/gen-batch-sprite.template.mjs +33 -42
- package/project-template/.claude/skills/playcraft-image-generation/reference/gen-batch.template.mjs +20 -18
- package/project-template/.claude/skills/playcraft-image-generation/reference/process-batch.template.mjs +7 -6
- package/project-template/.claude/skills/playcraft-image-generation/reference/prompt-cookbook.md +39 -39
- 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-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 +280 -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 +298 -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 +324 -0
- package/project-template-v2/.claude/skills/playcraft-image-generation/reference/gen-batch.template.mjs +97 -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 +137 -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/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 +219 -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/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 +41 -0
- package/project-templates/empty/gitignore +33 -0
- 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 +63 -135
- package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/gen-batch-sprite.template.mjs +33 -42
- package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/gen-batch.template.mjs +20 -18
- package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/process-batch.template.mjs +7 -6
- 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 +63 -135
- package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/gen-batch-sprite.template.mjs +33 -42
- package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/gen-batch.template.mjs +20 -18
- package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/process-batch.template.mjs +7 -6
- package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/prompt-cookbook.md +39 -39
- package/dist/commands/tools-image-context.js +0 -29
- package/dist/utils/image-asset-context.js +0 -228
package/dist/commands/build.js
CHANGED
|
@@ -4,6 +4,7 @@ import pc from 'picocolors';
|
|
|
4
4
|
import ora from 'ora';
|
|
5
5
|
import { BaseBuilder, ViteBuilder, PlayableBuilder, PlayableAnalyzer, EngineDetector, detectAmmoUsage } from '@playcraft/build';
|
|
6
6
|
import { loadBuildConfig } from '../build-config.js';
|
|
7
|
+
import { cleanupBuildTempDirectory, createBuildTempDirectory } from './build-temp.js';
|
|
7
8
|
import inquirer from 'inquirer';
|
|
8
9
|
/**
|
|
9
10
|
* 类型守卫:检查对象是否符合 PlayableScriptsConfig 类型
|
|
@@ -250,6 +251,7 @@ export async function buildCommand(projectPath, options) {
|
|
|
250
251
|
const spinner = ora(pc.cyan('🔨 开始打包 Playable Ad...')).start();
|
|
251
252
|
// 清理选项(默认为 false,使用覆盖模式)
|
|
252
253
|
const shouldClean = options.clean === true;
|
|
254
|
+
let tempBuildDir;
|
|
253
255
|
try {
|
|
254
256
|
// ====== 引擎 + 构建工具检测 ======
|
|
255
257
|
spinner.text = pc.cyan('检测项目引擎类型...');
|
|
@@ -450,6 +452,7 @@ export async function buildCommand(projectPath, options) {
|
|
|
450
452
|
const shouldRunPlayableOnly = options.mode === 'playable';
|
|
451
453
|
// 验证平台
|
|
452
454
|
const validPlatforms = [
|
|
455
|
+
'playcraft',
|
|
453
456
|
'facebook',
|
|
454
457
|
'snapchat',
|
|
455
458
|
'ironsource',
|
|
@@ -473,6 +476,7 @@ export async function buildCommand(projectPath, options) {
|
|
|
473
476
|
// 注:Facebook 在 Phaser(playable-scripts devkit)下的产物格式由其 devkit 输入配置决定(恒为 ZIP),
|
|
474
477
|
// 平台侧不在此处做引擎判断。
|
|
475
478
|
const platformFormatConfig = {
|
|
479
|
+
playcraft: { formats: ['html'], default: 'html' },
|
|
476
480
|
facebook: { formats: ['html', 'zip'], default: 'html' },
|
|
477
481
|
applovin: { formats: ['html'], default: 'html' },
|
|
478
482
|
ironsource: { formats: ['html'], default: 'html' },
|
|
@@ -584,8 +588,11 @@ export async function buildCommand(projectPath, options) {
|
|
|
584
588
|
console.log(pc.dim(' 无主题目录,跳过主题选择'));
|
|
585
589
|
}
|
|
586
590
|
}
|
|
587
|
-
// 输入商店跳转地址(CTA)-
|
|
588
|
-
if (!options.storeUrls) {
|
|
591
|
+
// 输入商店跳转地址(CTA)- 如果配置文件中没有则必填;通用预览(playcraft)不面向渠道,可不填
|
|
592
|
+
if (selectedPlatform === 'playcraft' && !options.storeUrls) {
|
|
593
|
+
console.log(pc.dim('\nℹ️ 通用预览不需要商店地址'));
|
|
594
|
+
}
|
|
595
|
+
else if (!options.storeUrls) {
|
|
589
596
|
console.log(pc.cyan('\n🔗 商店跳转地址(CTA 按钮目标)'));
|
|
590
597
|
console.log(pc.dim(' iOS 和 Android 地址均为必填'));
|
|
591
598
|
const storeUrlAnswer = await inquirer.prompt([
|
|
@@ -636,8 +643,8 @@ export async function buildCommand(projectPath, options) {
|
|
|
636
643
|
// 未指定格式,使用默认值
|
|
637
644
|
options.format = formatConfig.default;
|
|
638
645
|
}
|
|
639
|
-
// 如果命令行未传入 storeUrls
|
|
640
|
-
if (!options.storeUrls) {
|
|
646
|
+
// 如果命令行未传入 storeUrls,交互式输入(必填);通用预览(playcraft)不面向渠道,可不填
|
|
647
|
+
if (!options.storeUrls && options.platform !== 'playcraft') {
|
|
641
648
|
spinner.stop();
|
|
642
649
|
console.log(pc.cyan('\n🔗 商店跳转地址(CTA 按钮目标)'));
|
|
643
650
|
console.log(pc.dim(' iOS 和 Android 地址均为必填'));
|
|
@@ -742,7 +749,7 @@ export async function buildCommand(projectPath, options) {
|
|
|
742
749
|
// 输入是源代码或官方构建,先执行阶段1
|
|
743
750
|
spinner.text = pc.cyan('执行阶段1: 基础构建...');
|
|
744
751
|
// 将临时目录放在项目所在目录下,避免跨盘符路径问题(Windows 上 C:\Temp 和 D:\project 会导致 Vite 路径计算错误)
|
|
745
|
-
|
|
752
|
+
tempBuildDir = await createBuildTempDirectory(resolvedProjectPath);
|
|
746
753
|
// 构建 playableScriptsConfig,合并 CLI 选择的参数
|
|
747
754
|
const playableScriptsConfig = buildPlayableScriptsConfig(fileConfig?.playableScripts, {
|
|
748
755
|
platform: options.platform,
|
|
@@ -750,7 +757,7 @@ export async function buildCommand(projectPath, options) {
|
|
|
750
757
|
selectedThemes,
|
|
751
758
|
});
|
|
752
759
|
const baseBuilder = new BaseBuilder(resolvedProjectPath, {
|
|
753
|
-
outputDir:
|
|
760
|
+
outputDir: tempBuildDir,
|
|
754
761
|
selectedScenes,
|
|
755
762
|
analyze: buildOptions.analyze,
|
|
756
763
|
analyzeReportPath: buildOptions.analyze ? 'base-bundle-report.html' : undefined,
|
|
@@ -795,16 +802,7 @@ export async function buildCommand(projectPath, options) {
|
|
|
795
802
|
}
|
|
796
803
|
console.log('\n' + pc.green(`输出目录: ${finalOutputDir}`));
|
|
797
804
|
console.log(pc.dim('💡 产物已包含渠道适配、MRAID 注入、代码混淆和 fflate 压缩'));
|
|
798
|
-
|
|
799
|
-
try {
|
|
800
|
-
const tempBaseDir = path.join(resolvedProjectPath, '.playcraft-temp');
|
|
801
|
-
await fs.rm(tempBaseDir, { recursive: true, force: true });
|
|
802
|
-
console.log(pc.dim(`\n🗑️ 已清理临时目录`));
|
|
803
|
-
}
|
|
804
|
-
catch (error) {
|
|
805
|
-
// 记录清理错误信息
|
|
806
|
-
console.log(pc.dim(`\n⚠️ 清理临时目录失败: ${error instanceof Error ? error.message : String(error)}`));
|
|
807
|
-
}
|
|
805
|
+
await cleanupBuildTempDirectory(tempBuildDir);
|
|
808
806
|
return;
|
|
809
807
|
}
|
|
810
808
|
spinner.text = pc.cyan('✅ 阶段1完成,开始阶段2...');
|
|
@@ -976,18 +974,7 @@ export async function buildCommand(projectPath, options) {
|
|
|
976
974
|
console.log(pc.yellow(`\n⚠️ 检测到 Ammo.js,建议替换为 ${analysis.suggestedEngine}`));
|
|
977
975
|
}
|
|
978
976
|
}
|
|
979
|
-
|
|
980
|
-
if (!isBaseBuild && baseBuildDir.includes('.playcraft-temp')) {
|
|
981
|
-
// 调试:暂时保留临时目录
|
|
982
|
-
console.log(pc.yellow(`[DEBUG] 临时目录保留在: ${baseBuildDir}`));
|
|
983
|
-
// try {
|
|
984
|
-
// // 清理整个 .playcraft-temp 目录
|
|
985
|
-
// const tempBaseDir = path.join(resolvedProjectPath, '.playcraft-temp');
|
|
986
|
-
// await fs.rm(tempBaseDir, { recursive: true, force: true });
|
|
987
|
-
// } catch (error) {
|
|
988
|
-
// // 忽略清理错误
|
|
989
|
-
// }
|
|
990
|
-
}
|
|
977
|
+
await cleanupBuildTempDirectory(tempBuildDir);
|
|
991
978
|
}
|
|
992
979
|
catch (error) {
|
|
993
980
|
spinner.fail(pc.red('打包失败'));
|
package/dist/commands/create.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { prepareLocalSkillLifecycle } from '../project-skills/lifecycle.js';
|
|
1
2
|
import { execFileSync, execSync } from 'child_process';
|
|
2
3
|
import inquirer from 'inquirer';
|
|
3
4
|
import fs from 'fs/promises';
|
|
@@ -6,8 +7,11 @@ import { fileURLToPath } from 'url';
|
|
|
6
7
|
import pc from 'picocolors';
|
|
7
8
|
import ora from 'ora';
|
|
8
9
|
import { resolveCreateTemplate, resolveCreateTemplateDir, } from '../scaffold/templates.js';
|
|
10
|
+
import { PLATFORM_SKILL_DIRS } from './platform-skills.generated.js';
|
|
9
11
|
const __filename = fileURLToPath(import.meta.url);
|
|
10
12
|
const __dirname = path.dirname(__filename);
|
|
13
|
+
/** `@playcraft/skills` 包内 Platform Skill 的目录名(与 Atom Skill 的 `skills/` 区分)。 */
|
|
14
|
+
const PLATFORM_SKILLS_DIRNAME = 'playcraft-skills';
|
|
11
15
|
/** 生成一个本地占位 Project ID(99xxxxxx),后续可通过 playcraft config set 更新为真实 ID */
|
|
12
16
|
function generateMockProjectId() {
|
|
13
17
|
const suffix = Math.floor(Math.random() * 90_000_000) + 10_000_000;
|
|
@@ -849,7 +853,7 @@ function extractSkillBodyForCursor(content) {
|
|
|
849
853
|
* @param cliCommandsDir CLI 命令所在目录(用于解析 skills 包位置)
|
|
850
854
|
* @param configDir 配置目录名(如 '.claude' 或 '.codebuddy')
|
|
851
855
|
*/
|
|
852
|
-
async function setupPlatformSkillLinksInner(targetDir, cliCommandsDir, configDir) {
|
|
856
|
+
export async function setupPlatformSkillLinksInner(targetDir, cliCommandsDir, configDir) {
|
|
853
857
|
const skillsLinkDir = path.join(targetDir, configDir, 'skills');
|
|
854
858
|
await fs.mkdir(skillsLinkDir, { recursive: true });
|
|
855
859
|
// 以下 Skill 在项目模板中以静态目录形式存在,不创建软连接覆盖它们。
|
|
@@ -868,11 +872,12 @@ async function setupPlatformSkillLinksInner(targetDir, cliCommandsDir, configDir
|
|
|
868
872
|
'playcraft-image-generation',
|
|
869
873
|
'playcraft-audio-generation',
|
|
870
874
|
]);
|
|
871
|
-
//
|
|
875
|
+
// Platform Skill 住在 playcraft-skills/,与 Atom Skill 的 skills/ 是两个目录。
|
|
876
|
+
// 尝试从 CLI 安装包的位置定位它(monorepo 内或与 CLI 平级安装时可解析)。
|
|
872
877
|
const candidateDirs = [
|
|
873
|
-
path.resolve(cliCommandsDir, '..', '..', '..', '..', 'skills',
|
|
874
|
-
path.resolve(cliCommandsDir, '..', '..', '..', 'skills',
|
|
875
|
-
path.resolve(cliCommandsDir, '..', '..', 'skills',
|
|
878
|
+
path.resolve(cliCommandsDir, '..', '..', '..', '..', 'skills', PLATFORM_SKILLS_DIRNAME),
|
|
879
|
+
path.resolve(cliCommandsDir, '..', '..', '..', 'skills', PLATFORM_SKILLS_DIRNAME),
|
|
880
|
+
path.resolve(cliCommandsDir, '..', '..', 'skills', PLATFORM_SKILLS_DIRNAME),
|
|
876
881
|
];
|
|
877
882
|
let resolvedSkillsDir = null;
|
|
878
883
|
for (const dir of candidateDirs) {
|
|
@@ -885,18 +890,24 @@ async function setupPlatformSkillLinksInner(targetDir, cliCommandsDir, configDir
|
|
|
885
890
|
// 继续下一个候选
|
|
886
891
|
}
|
|
887
892
|
}
|
|
888
|
-
// 相对路径(指向 npm install 后的 node_modules
|
|
889
|
-
const relativeSkillsBase = path.join('..', '..', 'node_modules', '@playcraft', 'skills',
|
|
893
|
+
// 相对路径(指向 npm install 后的 node_modules)作为安装后激活的备用
|
|
894
|
+
const relativeSkillsBase = path.join('..', '..', 'node_modules', '@playcraft', 'skills', PLATFORM_SKILLS_DIRNAME);
|
|
890
895
|
if (resolvedSkillsDir) {
|
|
891
|
-
// 能立即解析 →
|
|
896
|
+
// 能立即解析 → 以真实目录为准创建绝对路径软连接,当下即可使用。
|
|
897
|
+
// 按「是否含 SKILL.md」判定而非 playcraft- 前缀:sound-effects 无前缀但同样是平台技能。
|
|
892
898
|
const entries = await fs.readdir(resolvedSkillsDir, { withFileTypes: true });
|
|
893
899
|
for (const entry of entries) {
|
|
894
|
-
if (!entry.
|
|
900
|
+
if (!entry.isDirectory() || staticSkills.has(entry.name))
|
|
895
901
|
continue;
|
|
896
|
-
const linkPath = path.join(skillsLinkDir, entry.name);
|
|
897
902
|
const targetPath = path.join(resolvedSkillsDir, entry.name);
|
|
898
903
|
try {
|
|
899
|
-
await fs.
|
|
904
|
+
await fs.access(path.join(targetPath, 'SKILL.md'));
|
|
905
|
+
}
|
|
906
|
+
catch {
|
|
907
|
+
continue;
|
|
908
|
+
}
|
|
909
|
+
try {
|
|
910
|
+
await fs.symlink(targetPath, path.join(skillsLinkDir, entry.name), 'dir');
|
|
900
911
|
}
|
|
901
912
|
catch {
|
|
902
913
|
// 已存在则跳过
|
|
@@ -904,28 +915,9 @@ async function setupPlatformSkillLinksInner(targetDir, cliCommandsDir, configDir
|
|
|
904
915
|
}
|
|
905
916
|
}
|
|
906
917
|
else {
|
|
907
|
-
// 无法立即解析 →
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
'playcraft-audio-generation',
|
|
911
|
-
'playcraft-build',
|
|
912
|
-
'playcraft-code-editor',
|
|
913
|
-
'playcraft-create-remix',
|
|
914
|
-
'playcraft-deploy',
|
|
915
|
-
'playcraft-image-generation',
|
|
916
|
-
'playcraft-image-processing',
|
|
917
|
-
'playcraft-platform-intro',
|
|
918
|
-
'playcraft-prefab',
|
|
919
|
-
'playcraft-project-management',
|
|
920
|
-
'playcraft-remix-workflow',
|
|
921
|
-
'playcraft-save',
|
|
922
|
-
'playcraft-skill-recommender',
|
|
923
|
-
'playcraft-sprite-generation',
|
|
924
|
-
'playcraft-sprite-remix',
|
|
925
|
-
'playcraft-sprite-sheet',
|
|
926
|
-
'playcraft-3d-flip-sprite',
|
|
927
|
-
];
|
|
928
|
-
for (const name of knownPlatformTools) {
|
|
918
|
+
// 无法立即解析 → 指向 node_modules 的相对路径软连接,npm install 后自动激活。
|
|
919
|
+
// 清单由 scripts/generate-platform-skills.mjs 从真实目录生成并由 CI 校验,不要手写。
|
|
920
|
+
for (const name of PLATFORM_SKILL_DIRS) {
|
|
929
921
|
if (staticSkills.has(name))
|
|
930
922
|
continue;
|
|
931
923
|
const linkPath = path.join(skillsLinkDir, name);
|
|
@@ -1289,6 +1281,7 @@ export async function createCommand(projectName, options) {
|
|
|
1289
1281
|
smokeSpinner.warn(`Harness 自检跳过: ${message}`);
|
|
1290
1282
|
}
|
|
1291
1283
|
}
|
|
1284
|
+
await prepareLocalSkillLifecycle(targetDir, 'create');
|
|
1292
1285
|
const gitSpinner = ora(isWorktreeMode ? '提交项目分支...' : '初始化 Git 仓库...').start();
|
|
1293
1286
|
try {
|
|
1294
1287
|
if (isWorktreeMode) {
|
package/dist/commands/image.js
CHANGED
|
@@ -677,9 +677,7 @@ async function runBatchStep(cmd, args, inputPath, outputPath) {
|
|
|
677
677
|
const meta = await sharp(inputPath).metadata();
|
|
678
678
|
const targetW = scale ? Math.round((meta.width ?? 64) * scale) : w;
|
|
679
679
|
const targetH = scale ? Math.round((meta.height ?? 64) * scale) : h;
|
|
680
|
-
await safeToFile(sharp(inputPath).resize(targetW, targetH, {
|
|
681
|
-
fit, background: { r: 0, g: 0, b: 0, alpha: 0 },
|
|
682
|
-
}), outputPath, inputPath);
|
|
680
|
+
await safeToFile(sharp(inputPath).resize(targetW, targetH, { fit }), outputPath, inputPath);
|
|
683
681
|
break;
|
|
684
682
|
}
|
|
685
683
|
case 'crop': {
|
|
@@ -838,9 +836,7 @@ export function registerImageCommands(program) {
|
|
|
838
836
|
process.exit(1);
|
|
839
837
|
}
|
|
840
838
|
const fit = parseResizeFitOption(opts.fit);
|
|
841
|
-
await safeToFile(sharp(opts.input).resize(targetW, targetH, {
|
|
842
|
-
fit, background: { r: 0, g: 0, b: 0, alpha: 0 },
|
|
843
|
-
}), opts.output, opts.input);
|
|
839
|
+
await safeToFile(sharp(opts.input).resize(targetW, targetH, { fit }), opts.output, opts.input);
|
|
844
840
|
const outSz = statSync(resolveSandboxPath(opts.output)).size;
|
|
845
841
|
console.log(`Resized ${w}x${h} → ${targetW ?? '?'}x${targetH ?? '?'} — ${opts.output} (${sizeLabelBytes(outSz)})`);
|
|
846
842
|
}
|
package/dist/commands/login.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import http from 'http';
|
|
2
|
-
import {
|
|
2
|
+
import { execFile } from 'child_process';
|
|
3
3
|
import crypto from 'crypto';
|
|
4
|
-
import {
|
|
4
|
+
import { loadGlobalConfig, normalizeEnvironmentBaseUrl, readEnvironmentProfile, saveEnvironmentProfile } from '../config.js';
|
|
5
|
+
import { activateAccount } from '../auth/account-activation.js';
|
|
5
6
|
/** 默认云端 Portal(Next.js `/cli-auth` 与 API 同源时的基址;可通过 --url 或 PLAYCRAFT_URL 覆盖) */
|
|
6
7
|
export const DEFAULT_PLAYCRAFT_CLOUD_BASE = 'https://playcraft.aix.intlgame.com';
|
|
7
8
|
const CALLBACK_PORT = 3456;
|
|
@@ -18,10 +19,11 @@ function openBrowser(url) {
|
|
|
18
19
|
const cmd = process.platform === 'darwin'
|
|
19
20
|
? 'open'
|
|
20
21
|
: process.platform === 'win32'
|
|
21
|
-
? '
|
|
22
|
+
? 'rundll32'
|
|
22
23
|
: 'xdg-open';
|
|
23
24
|
try {
|
|
24
|
-
|
|
25
|
+
const arguments_ = process.platform === 'win32' ? ['url.dll,FileProtocolHandler', url] : [url];
|
|
26
|
+
execFile(cmd, arguments_, (err, _stdout, stderr) => {
|
|
25
27
|
if (err) {
|
|
26
28
|
console.error('Failed to open browser automatically:', err.message);
|
|
27
29
|
if (stderr?.trim()) {
|
|
@@ -44,21 +46,18 @@ function resolveLoginBaseUrl(options) {
|
|
|
44
46
|
globalConfig.url ||
|
|
45
47
|
globalConfig.backendUrl ||
|
|
46
48
|
DEFAULT_PLAYCRAFT_CLOUD_BASE;
|
|
47
|
-
|
|
49
|
+
const base = normalizeEnvironmentBaseUrl(raw);
|
|
50
|
+
if (!base)
|
|
51
|
+
throw new Error('Invalid environment address');
|
|
52
|
+
return base;
|
|
48
53
|
}
|
|
49
54
|
function normalizeLoginEnvironmentUrl(raw) {
|
|
50
|
-
|
|
51
|
-
const url = new URL(raw);
|
|
52
|
-
url.hash = '';
|
|
53
|
-
url.search = '';
|
|
54
|
-
url.pathname = url.pathname.replace(/\/+$/, '');
|
|
55
|
-
return url.toString().replace(/\/+$/, '');
|
|
56
|
-
}
|
|
57
|
-
catch {
|
|
58
|
-
return undefined;
|
|
59
|
-
}
|
|
55
|
+
return normalizeEnvironmentBaseUrl(raw);
|
|
60
56
|
}
|
|
61
57
|
export function resolveExistingTokenForLoginTarget(globalConfig, backendUrl) {
|
|
58
|
+
const profile = readEnvironmentProfile(globalConfig, backendUrl);
|
|
59
|
+
if (profile)
|
|
60
|
+
return profile.token;
|
|
62
61
|
const token = typeof globalConfig.token === 'string' ? globalConfig.token : undefined;
|
|
63
62
|
const storedBackendUrl = typeof globalConfig.backendUrl === 'string'
|
|
64
63
|
? globalConfig.backendUrl
|
|
@@ -74,9 +73,17 @@ export function resolveExistingTokenForLoginTarget(globalConfig, backendUrl) {
|
|
|
74
73
|
: undefined;
|
|
75
74
|
}
|
|
76
75
|
export function buildCliAuthUrl(options) {
|
|
77
|
-
const
|
|
76
|
+
const base = normalizeEnvironmentBaseUrl(options.backendUrl);
|
|
77
|
+
if (!base)
|
|
78
|
+
throw new Error('Invalid environment address');
|
|
79
|
+
const url = new URL(`${base}/cli-auth`);
|
|
80
|
+
url.searchParams.set('callbackUrl', options.callbackUrl);
|
|
78
81
|
url.searchParams.set('callback', options.callbackUrl);
|
|
79
82
|
url.searchParams.set('state', options.state);
|
|
83
|
+
if (options.codeChallenge) {
|
|
84
|
+
url.searchParams.set('codeChallenge', options.codeChallenge);
|
|
85
|
+
url.searchParams.set('codeChallengeMethod', 'S256');
|
|
86
|
+
}
|
|
80
87
|
return url.toString();
|
|
81
88
|
}
|
|
82
89
|
export async function renewExistingToken(options) {
|
|
@@ -87,12 +94,20 @@ export async function renewExistingToken(options) {
|
|
|
87
94
|
try {
|
|
88
95
|
const res = await fetchFn(`${options.backendUrl}/api/settings/personal-access-tokens/current/renew`, {
|
|
89
96
|
method: 'PATCH',
|
|
97
|
+
redirect: 'error',
|
|
98
|
+
signal: AbortSignal.timeout(10_000),
|
|
90
99
|
headers: {
|
|
91
100
|
Authorization: `Bearer ${options.token}`,
|
|
92
101
|
'Content-Type': 'application/json',
|
|
93
102
|
},
|
|
94
103
|
body: JSON.stringify({ expiresIn: '365d' }),
|
|
95
104
|
});
|
|
105
|
+
if (res.ok && res.json && options.onRenewed) {
|
|
106
|
+
const result = await res.json();
|
|
107
|
+
if (typeof result.expiresAt === 'string' && Date.parse(result.expiresAt) > Date.now()) {
|
|
108
|
+
options.onRenewed(result.expiresAt);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
96
111
|
return res.ok;
|
|
97
112
|
}
|
|
98
113
|
catch {
|
|
@@ -100,29 +115,43 @@ export async function renewExistingToken(options) {
|
|
|
100
115
|
}
|
|
101
116
|
}
|
|
102
117
|
export async function loginCommand(options) {
|
|
118
|
+
if (options.activate) {
|
|
119
|
+
await activateAccount(options, DEFAULT_PLAYCRAFT_CLOUD_BASE);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
103
122
|
const backendUrl = resolveLoginBaseUrl(options);
|
|
104
123
|
const callbackPort = options.callbackPort ?? CALLBACK_PORT;
|
|
105
124
|
const globalConfig = loadGlobalConfig();
|
|
106
125
|
const existingToken = resolveExistingTokenForLoginTarget(globalConfig, backendUrl);
|
|
107
126
|
if (existingToken) {
|
|
108
|
-
|
|
127
|
+
let expiresAt;
|
|
128
|
+
const renewed = await renewExistingToken({ backendUrl, token: existingToken,
|
|
129
|
+
onRenewed: (value) => { expiresAt = value; } });
|
|
109
130
|
if (renewed) {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
});
|
|
131
|
+
const previous = readEnvironmentProfile(globalConfig, backendUrl);
|
|
132
|
+
saveEnvironmentProfile(backendUrl, { token: existingToken,
|
|
133
|
+
...(previous?.identity ? { identity: previous.identity } : {}),
|
|
134
|
+
...(expiresAt ? { expiresAt } : {}) });
|
|
115
135
|
console.log('Existing CLI token renewed. Token saved to ~/.playcraft/config.json');
|
|
116
136
|
return;
|
|
117
137
|
}
|
|
118
138
|
}
|
|
119
139
|
const state = crypto.randomBytes(16).toString('hex');
|
|
140
|
+
const codeVerifier = crypto.randomBytes(32).toString('base64url');
|
|
141
|
+
const codeChallenge = crypto.createHash('sha256').update(codeVerifier).digest('base64url');
|
|
142
|
+
let callbackUrl = '';
|
|
143
|
+
let exchanging = false;
|
|
120
144
|
return new Promise((resolve, reject) => {
|
|
121
145
|
let timeout;
|
|
122
146
|
const server = http.createServer((req, res) => {
|
|
123
|
-
res.setHeader('Access-Control-Allow-Origin',
|
|
147
|
+
res.setHeader('Access-Control-Allow-Origin', new URL(backendUrl).origin);
|
|
124
148
|
res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
|
|
125
149
|
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
150
|
+
if (req.headers.origin && req.headers.origin !== new URL(backendUrl).origin) {
|
|
151
|
+
res.writeHead(403);
|
|
152
|
+
res.end();
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
126
155
|
if (req.method === 'OPTIONS') {
|
|
127
156
|
res.writeHead(204);
|
|
128
157
|
res.end();
|
|
@@ -130,8 +159,9 @@ export async function loginCommand(options) {
|
|
|
130
159
|
}
|
|
131
160
|
if (req.url === '/callback' && req.method === 'POST') {
|
|
132
161
|
let body = '';
|
|
133
|
-
req.on('data', (chunk) =>
|
|
134
|
-
|
|
162
|
+
req.on('data', (chunk) => { body += chunk; if (body.length > 16_384)
|
|
163
|
+
req.destroy(); });
|
|
164
|
+
req.on('end', async () => {
|
|
135
165
|
try {
|
|
136
166
|
const data = JSON.parse(body);
|
|
137
167
|
if (data.state !== state) {
|
|
@@ -139,22 +169,36 @@ export async function loginCommand(options) {
|
|
|
139
169
|
'Content-Type': 'text/html',
|
|
140
170
|
Connection: 'close',
|
|
141
171
|
});
|
|
142
|
-
res.end('<html><body><h1>Invalid state</h1></body></html>'
|
|
143
|
-
closeServer(() => {
|
|
144
|
-
reject(new Error('State mismatch - possible CSRF attack'));
|
|
145
|
-
});
|
|
146
|
-
});
|
|
172
|
+
res.end('<html><body><h1>Invalid state</h1></body></html>');
|
|
147
173
|
return;
|
|
148
174
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
175
|
+
if (exchanging) {
|
|
176
|
+
res.writeHead(409);
|
|
177
|
+
res.end();
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
if (typeof data.code !== 'string' || !/^[A-Za-z0-9_-]{43}$/.test(data.code)) {
|
|
181
|
+
throw new Error('Login callback did not include an authorization code');
|
|
152
182
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
183
|
+
exchanging = true;
|
|
184
|
+
const base = normalizeEnvironmentBaseUrl(backendUrl);
|
|
185
|
+
if (!base)
|
|
186
|
+
throw new Error('Invalid environment address');
|
|
187
|
+
const exchange = await fetch(`${base}/api/cloud-environments/exchange`, {
|
|
188
|
+
method: 'POST', redirect: 'error', signal: AbortSignal.timeout(10_000),
|
|
189
|
+
headers: { 'Content-Type': 'application/json' },
|
|
190
|
+
body: JSON.stringify({ code: data.code, state, callbackUrl, codeVerifier }),
|
|
157
191
|
});
|
|
192
|
+
if (!exchange.ok)
|
|
193
|
+
throw new Error('Authorization code exchange failed');
|
|
194
|
+
const credential = await exchange.json();
|
|
195
|
+
if (!credential.token || !/^playcraft_pat_[A-Za-z0-9_-]{32}$/.test(credential.token)
|
|
196
|
+
|| !credential.identity?.userId || !Array.isArray(credential.identity.identities)
|
|
197
|
+
|| !credential.expiresAt || Date.parse(credential.expiresAt) <= Date.now()) {
|
|
198
|
+
throw new Error('Invalid environment credential response');
|
|
199
|
+
}
|
|
200
|
+
saveEnvironmentProfile(base, { token: credential.token,
|
|
201
|
+
identity: credential.identity, expiresAt: credential.expiresAt });
|
|
158
202
|
res.writeHead(200, {
|
|
159
203
|
'Content-Type': 'text/html',
|
|
160
204
|
Connection: 'close',
|
|
@@ -174,9 +218,6 @@ export async function loginCommand(options) {
|
|
|
174
218
|
catch (err) {
|
|
175
219
|
const cause = err instanceof Error ? err : new Error(String(err));
|
|
176
220
|
console.error('[playcraft login] Callback handling failed:', cause.message);
|
|
177
|
-
if (cause.stack) {
|
|
178
|
-
console.error(cause.stack);
|
|
179
|
-
}
|
|
180
221
|
const userFacing = cause instanceof SyntaxError
|
|
181
222
|
? 'Invalid JSON in login callback.'
|
|
182
223
|
: `Login callback failed: ${cause.message}`;
|
|
@@ -221,14 +262,15 @@ export async function loginCommand(options) {
|
|
|
221
262
|
});
|
|
222
263
|
});
|
|
223
264
|
try {
|
|
224
|
-
server.listen(callbackPort, () => {
|
|
265
|
+
server.listen(callbackPort, '127.0.0.1', () => {
|
|
225
266
|
const address = server.address();
|
|
226
267
|
const listeningPort = typeof address === 'object' && address ? address.port : callbackPort;
|
|
227
|
-
|
|
268
|
+
callbackUrl = `http://127.0.0.1:${listeningPort}/callback`;
|
|
228
269
|
const authUrl = buildCliAuthUrl({
|
|
229
270
|
backendUrl,
|
|
230
271
|
callbackUrl,
|
|
231
272
|
state,
|
|
273
|
+
codeChallenge,
|
|
232
274
|
});
|
|
233
275
|
console.log('Opening browser for login...');
|
|
234
276
|
console.log(`If the browser does not open, visit this URL manually:\n ${authUrl}\n`);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// 此文件由 scripts/generate-platform-skills.mjs 生成,请勿手改。
|
|
2
|
+
// 重新生成:pnpm --filter @playcraft/cli generate:platform-skills
|
|
3
|
+
/** `@playcraft/skills/playcraft-skills/` 下的 Platform Skill 目录名。 */
|
|
4
|
+
export const PLATFORM_SKILL_DIRS = [
|
|
5
|
+
'playcraft-3d-flip-sprite',
|
|
6
|
+
'playcraft-asset-management',
|
|
7
|
+
'playcraft-build',
|
|
8
|
+
'playcraft-code-editor',
|
|
9
|
+
'playcraft-create-remix',
|
|
10
|
+
'playcraft-deploy',
|
|
11
|
+
'playcraft-iteration-methodology',
|
|
12
|
+
'playcraft-platform-intro',
|
|
13
|
+
'playcraft-prefab',
|
|
14
|
+
'playcraft-project-management',
|
|
15
|
+
'playcraft-remix-workflow',
|
|
16
|
+
'playcraft-save',
|
|
17
|
+
'playcraft-template-upgrade-merge',
|
|
18
|
+
'sound-effects',
|
|
19
|
+
];
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { recordPlayable } from '../recording/recorder.js';
|
|
2
|
+
import { RecordingError, recordingMessage as t } from '../recording/messages.js';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
import { captureGeneratedArtifact } from '../workspace-runtime/workspaces/artifact-receipts.js';
|
|
5
|
+
export async function runPlayableRecord(options) {
|
|
6
|
+
const interrupted = new AbortController();
|
|
7
|
+
const deadline = new AbortController();
|
|
8
|
+
const onInterrupt = () => interrupted.abort(new DOMException('Interrupted', 'AbortError'));
|
|
9
|
+
process.once('SIGINT', onInterrupt);
|
|
10
|
+
let timer;
|
|
11
|
+
try {
|
|
12
|
+
const seconds = options.timeout === undefined ? 1200 : Number(options.timeout);
|
|
13
|
+
if (!Number.isFinite(seconds) || seconds <= 0 || seconds > 1800)
|
|
14
|
+
throw new RecordingError('invalidArguments', 64);
|
|
15
|
+
timer = setTimeout(() => deadline.abort(new DOMException('Timed out', 'TimeoutError')), seconds * 1000);
|
|
16
|
+
const result = await recordPlayable(options, AbortSignal.any([interrupted.signal, deadline.signal]));
|
|
17
|
+
const metadata = result.envelope.result;
|
|
18
|
+
const output = result.envelope.artifacts;
|
|
19
|
+
if (result.envelope.ok && metadata && output?.candidate) {
|
|
20
|
+
const artifacts = [metadata.candidate, ...metadata.controls];
|
|
21
|
+
const paths = [output.candidate, ...(output.controls ?? [])];
|
|
22
|
+
for (let index = 0; index < artifacts.length; index++) {
|
|
23
|
+
const artifact = artifacts[index], path = paths[index];
|
|
24
|
+
if (!path)
|
|
25
|
+
continue;
|
|
26
|
+
const thumbnail = artifact.screenshots?.[0]?.path;
|
|
27
|
+
await captureGeneratedArtifact(path, 'playable record', {
|
|
28
|
+
width: artifact.video.source.width, height: artifact.video.source.height,
|
|
29
|
+
duration: artifact.video.source.durationMs / 1000,
|
|
30
|
+
...(thumbnail ? { thumbnailPath: join(dirname(output.metadata), thumbnail) } : {}),
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
process.stdout.write(`${JSON.stringify(result.envelope)}\n`);
|
|
35
|
+
process.exitCode = result.exitCode;
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
const code = error instanceof RecordingError ? error.code : 'recordingFailed';
|
|
39
|
+
process.stdout.write(`${JSON.stringify({ schemaVersion: 'playcraft.video-cli.v1', command: 'playable record', state: 'failed', ok: false, error: { code, message: t(code) } })}\n`);
|
|
40
|
+
process.exitCode = error instanceof RecordingError ? error.exitCode : 4;
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
if (timer)
|
|
44
|
+
clearTimeout(timer);
|
|
45
|
+
process.removeListener('SIGINT', onInterrupt);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export function registerPlayableCommands(program) {
|
|
49
|
+
program.command('playable').description(t('topic')).command('record').description(t('record'))
|
|
50
|
+
.option('--url <url>', t('url')).option('--scenario <file>', t('scenario')).option('--out <directory>', t('out'))
|
|
51
|
+
.option('--browser-path <path>', t('browser')).option('--timeout <seconds>', t('timeout'))
|
|
52
|
+
.action(runPlayableRecord);
|
|
53
|
+
}
|
package/dist/commands/skills.js
CHANGED
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
* 3. <cwd>/node_modules/@playcraft/skills/playcraft-skills(兼容旧路径 skills)
|
|
19
19
|
* 4. <CLI 自身>/../../skills/playcraft-skills(monorepo 开发时,兼容旧路径 skills)
|
|
20
20
|
*/
|
|
21
|
+
import { registerProjectSkillsCommands } from '../project-skills/commands.js';
|
|
22
|
+
import { registerRegistryCommands } from '../skill-registry/commands.js';
|
|
21
23
|
import * as fs from 'node:fs';
|
|
22
24
|
import * as path from 'node:path';
|
|
23
25
|
import { getFusionHintsForGameplayAtoms, } from '../atom-tree/fusion-hints.js';
|
|
@@ -1430,6 +1432,8 @@ export function registerSkillsCommands(program) {
|
|
|
1430
1432
|
const skills = program
|
|
1431
1433
|
.command('skills')
|
|
1432
1434
|
.description('开发时 Skill 发现与脚手架工具(按 engine/intent 推荐 skill 集合,或将 ref 文件复制到项目)');
|
|
1435
|
+
registerProjectSkillsCommands(skills);
|
|
1436
|
+
registerRegistryCommands(skills);
|
|
1433
1437
|
// 公共选项:skills 目录
|
|
1434
1438
|
const addSkillsDirOption = (cmd) => cmd.option('--skills-dir <path>', '指定 skills 目录路径(默认自动发现)');
|
|
1435
1439
|
// ── list ──────────────────────────────────────────────────────────────────
|
|
@@ -2,6 +2,7 @@ import { writeFileSync, mkdirSync, readFileSync } from 'fs';
|
|
|
2
2
|
import { dirname } from 'path';
|
|
3
3
|
import { AgentApiClient } from '../utils/agent-api-client.js';
|
|
4
4
|
import { mimeTypeForImagePath, handleError } from './tools-utils.js';
|
|
5
|
+
import { captureGeneratedArtifact } from '../workspace-runtime/workspaces/artifact-receipts.js';
|
|
5
6
|
const AVATAR_TEMPLATES = [
|
|
6
7
|
'basketball', 'badminton', 'pingpong', 'gymnastics', 'pilidance',
|
|
7
8
|
'tennis', 'athletics', 'footballboykicking1', 'footballboykicking2',
|
|
@@ -148,6 +149,9 @@ export function register3DCommands(tools) {
|
|
|
148
149
|
writeFileSync(opts.savePreview, previewBuf);
|
|
149
150
|
console.log(` Preview saved: ${opts.savePreview}`);
|
|
150
151
|
}
|
|
152
|
+
await captureGeneratedArtifact(opts.output, 'generate-3d', {
|
|
153
|
+
...(opts.savePreview && result.previewImageBase64 ? { thumbnailPath: opts.savePreview } : {}),
|
|
154
|
+
});
|
|
151
155
|
console.log(`\nNext steps:`);
|
|
152
156
|
console.log(` Inspect: playcraft 3d info ${opts.output}`);
|
|
153
157
|
console.log(` Apply texture: playcraft tools generate-texture --model-url <url> --reference-image <img> --output out.glb`);
|
|
@@ -209,6 +213,7 @@ export function register3DCommands(tools) {
|
|
|
209
213
|
const outDir = dirname(opts.output);
|
|
210
214
|
mkdirSync(outDir, { recursive: true });
|
|
211
215
|
writeFileSync(opts.output, buf);
|
|
216
|
+
await captureGeneratedArtifact(opts.output, 'generate-texture');
|
|
212
217
|
const sizeMB = (buf.length / 1024 / 1024).toFixed(2);
|
|
213
218
|
console.log(`\nTextured model saved: ${opts.output} (${sizeMB}MB, ${elapsed}s)`);
|
|
214
219
|
if (result.resultUrl)
|
|
@@ -253,6 +258,7 @@ export function register3DCommands(tools) {
|
|
|
253
258
|
const outDir = dirname(opts.output);
|
|
254
259
|
mkdirSync(outDir, { recursive: true });
|
|
255
260
|
writeFileSync(opts.output, buf);
|
|
261
|
+
await captureGeneratedArtifact(opts.output, 'retopology');
|
|
256
262
|
const sizeMB = (buf.length / 1024 / 1024).toFixed(2);
|
|
257
263
|
console.log(`\nRetopologized model saved: ${opts.output} (${sizeMB}MB, ${elapsed}s)`);
|
|
258
264
|
}
|
|
@@ -289,6 +295,7 @@ export function register3DCommands(tools) {
|
|
|
289
295
|
const outDir = dirname(opts.output);
|
|
290
296
|
mkdirSync(outDir, { recursive: true });
|
|
291
297
|
writeFileSync(opts.output, buf);
|
|
298
|
+
await captureGeneratedArtifact(opts.output, 'uv-unwrap');
|
|
292
299
|
const sizeMB = (buf.length / 1024 / 1024).toFixed(2);
|
|
293
300
|
console.log(`\nUV-unwrapped model saved: ${opts.output} (${sizeMB}MB, ${elapsed}s)`);
|
|
294
301
|
}
|
|
@@ -324,6 +331,7 @@ export function register3DCommands(tools) {
|
|
|
324
331
|
const outDir = dirname(opts.output);
|
|
325
332
|
mkdirSync(outDir, { recursive: true });
|
|
326
333
|
writeFileSync(opts.output, buf);
|
|
334
|
+
await captureGeneratedArtifact(opts.output, 'convert-3d');
|
|
327
335
|
const sizeMB = (buf.length / 1024 / 1024).toFixed(2);
|
|
328
336
|
console.log(`\nConverted model saved: ${opts.output} (${sizeMB}MB, ${elapsed}s)`);
|
|
329
337
|
console.log(` Format: ${result.format.toUpperCase()}`);
|
|
@@ -359,6 +367,7 @@ export function register3DCommands(tools) {
|
|
|
359
367
|
const outDir = dirname(opts.output);
|
|
360
368
|
mkdirSync(outDir, { recursive: true });
|
|
361
369
|
writeFileSync(opts.output, buf);
|
|
370
|
+
await captureGeneratedArtifact(opts.output, 'generate-3d-parts');
|
|
362
371
|
const sizeMB = (buf.length / 1024 / 1024).toFixed(2);
|
|
363
372
|
console.log(`\n3D parts model saved: ${opts.output} (${sizeMB}MB, ${elapsed}s)`);
|
|
364
373
|
}
|
|
@@ -402,6 +411,7 @@ export function register3DCommands(tools) {
|
|
|
402
411
|
const outDir = dirname(opts.output);
|
|
403
412
|
mkdirSync(outDir, { recursive: true });
|
|
404
413
|
writeFileSync(opts.output, buf);
|
|
414
|
+
await captureGeneratedArtifact(opts.output, 'generate-motion');
|
|
405
415
|
const sizeMB = (buf.length / 1024 / 1024).toFixed(2);
|
|
406
416
|
console.log(`\nMotion FBX saved: ${opts.output} (${sizeMB}MB, ${elapsed}s)`);
|
|
407
417
|
}
|
|
@@ -463,6 +473,7 @@ export function register3DCommands(tools) {
|
|
|
463
473
|
const outDir = dirname(opts.output);
|
|
464
474
|
mkdirSync(outDir, { recursive: true });
|
|
465
475
|
writeFileSync(opts.output, buf);
|
|
476
|
+
await captureGeneratedArtifact(opts.output, 'generate-avatar');
|
|
466
477
|
const sizeMB = (buf.length / 1024 / 1024).toFixed(2);
|
|
467
478
|
console.log(`\n3D avatar saved: ${opts.output} (${sizeMB}MB, ${elapsed}s)`);
|
|
468
479
|
}
|