@playcraft/cli 0.1.24 → 0.1.25
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 +38 -3
- 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.js +9 -4
- 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 +2 -0
- package/dist/commands/tools-3d.js +11 -0
- package/dist/commands/tools-batch.js +87 -0
- package/dist/commands/tools-generation.js +8 -4
- package/dist/commands/tools.js +2 -0
- package/dist/commands/video.js +111 -0
- package/dist/commands/workspace-runtime.js +54 -0
- package/dist/config.js +59 -4
- package/dist/index.js +34 -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/utils/agent-api-client.js +185 -30
- package/dist/utils/environment-endpoints.js +17 -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 +222 -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 +216 -0
- package/dist/video-replication/evidence.js +165 -0
- package/dist/video-replication/media.js +111 -0
- package/dist/video-replication/messages.js +89 -0
- package/dist/workspace-runtime/codex/app-server.js +627 -0
- package/dist/workspace-runtime/codex/config-toml.js +109 -0
- package/dist/workspace-runtime/codex/jsonrpc-stdio.js +123 -0
- package/dist/workspace-runtime/codex/launcher-client.js +163 -0
- package/dist/workspace-runtime/codex/loopback.js +143 -0
- package/dist/workspace-runtime/codex/model-broker.js +239 -0
- package/dist/workspace-runtime/codex/native-adapter.js +9 -0
- package/dist/workspace-runtime/main.js +10 -0
- package/dist/workspace-runtime/persistence/journal.js +355 -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 +1057 -0
- package/dist/workspace-runtime/previews/publish.js +259 -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/contracts.js +17 -0
- package/dist/workspace-runtime/production/controller.js +207 -0
- package/dist/workspace-runtime/production/dependencies.js +60 -0
- package/dist/workspace-runtime/production/native.js +90 -0
- package/dist/workspace-runtime/production/processes.js +86 -0
- package/dist/workspace-runtime/production/save-command.js +24 -0
- package/dist/workspace-runtime/production/source-copy.js +91 -0
- package/dist/workspace-runtime/production/stages.js +202 -0
- package/dist/workspace-runtime/production/storage.js +32 -0
- package/dist/workspace-runtime/serve.js +202 -0
- package/dist/workspace-runtime/server/auth.js +21 -0
- package/dist/workspace-runtime/server/connectivity.js +201 -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 +876 -0
- package/dist/workspace-runtime/server/endpoints.js +19 -0
- package/dist/workspace-runtime/server/execution-group.js +85 -0
- package/dist/workspace-runtime/server/http.js +976 -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/terminal-host.js +294 -0
- package/dist/workspace-runtime/server/types.js +1 -0
- package/dist/workspace-runtime/workspaces/artifact-events.js +55 -0
- package/dist/workspace-runtime/workspaces/artifact-files.js +195 -0
- package/dist/workspace-runtime/workspaces/artifact-receipts.js +89 -0
- package/dist/workspace-runtime/workspaces/artifacts.js +260 -0
- package/dist/workspace-runtime/workspaces/attachments.js +175 -0
- package/dist/workspace-runtime/workspaces/batch-commit.js +142 -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/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 +113 -0
- package/dist/workspace-runtime/workspaces/directory-access.js +29 -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 +40 -0
- package/dist/workspace-runtime/workspaces/files.js +120 -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 +471 -0
- package/dist/workspace-runtime/workspaces/json5-edit.js +170 -0
- package/dist/workspace-runtime/workspaces/parameters.js +272 -0
- package/dist/workspace-runtime/workspaces/prepare.js +264 -0
- package/dist/workspace-runtime/workspaces/registry.js +200 -0
- package/dist/workspace-runtime/workspaces/revisions.js +31 -0
- package/dist/workspace-runtime/workspaces/skill-artifact-loader.js +91 -0
- package/dist/workspace-runtime/workspaces/skills.js +189 -0
- package/dist/workspace-runtime/workspaces/video-attachments.js +49 -0
- package/package.json +14 -5
- package/project-template/.claude/skills/playcraft-image-generation/SKILL.md +23 -61
- 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 +25 -0
- package/project-templates/empty/gitignore +33 -0
- package/project-templates/phaser/.claude/skills/playcraft-image-generation/SKILL.md +23 -61
- package/project-templates/threejs/.claude/skills/playcraft-image-generation/SKILL.md +23 -61
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,7 @@
|
|
|
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';
|
|
21
22
|
import * as fs from 'node:fs';
|
|
22
23
|
import * as path from 'node:path';
|
|
23
24
|
import { getFusionHintsForGameplayAtoms, } from '../atom-tree/fusion-hints.js';
|
|
@@ -1430,6 +1431,7 @@ export function registerSkillsCommands(program) {
|
|
|
1430
1431
|
const skills = program
|
|
1431
1432
|
.command('skills')
|
|
1432
1433
|
.description('开发时 Skill 发现与脚手架工具(按 engine/intent 推荐 skill 集合,或将 ref 文件复制到项目)');
|
|
1434
|
+
registerProjectSkillsCommands(skills);
|
|
1433
1435
|
// 公共选项:skills 目录
|
|
1434
1436
|
const addSkillsDirOption = (cmd) => cmd.option('--skills-dir <path>', '指定 skills 目录路径(默认自动发现)');
|
|
1435
1437
|
// ── 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
|
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { batchCreateSchema, batchPromptCreateSchema } from '@playcraft/common/cloud-workspace/runtime';
|
|
3
|
+
import { AgentApiClient } from '../utils/agent-api-client.js';
|
|
4
|
+
import { handleError } from './tools-utils.js';
|
|
5
|
+
const messages = {
|
|
6
|
+
en: {
|
|
7
|
+
create: 'Create and automatically run independent Remix variants from one prompt',
|
|
8
|
+
input: 'Path to the batch draft JSON (JavaScript Object Notation, 数据交换格式) file',
|
|
9
|
+
status: 'Read a batch and its generation status',
|
|
10
|
+
id: 'Batch identifier',
|
|
11
|
+
created: 'Batch created',
|
|
12
|
+
prompt: 'The complete batch generation request', workspace: 'Source workspace identifier',
|
|
13
|
+
directory: 'Source engineering directory; defaults to basic', request: 'Stable batch creation identity for retries',
|
|
14
|
+
team: 'Team identifier for a standalone workspace', invalidMode: 'Provide either --prompt with --workspace-id or --input, exclusively.',
|
|
15
|
+
requestRequired: 'Prompt creation requires a stable --client-request-id; reuse it when retrying the same request.',
|
|
16
|
+
help: '\nUse --prompt with --workspace-id and a stable --client-request-id. The platform plans and generates independent Remix directories while preserving the source. An omitted count defaults to 3; maximum 10. --input also accepts existing structured drafts.\n',
|
|
17
|
+
confirmation: 'Generation is queued. Read the batch status to track its results.',
|
|
18
|
+
review: 'This existing draft requires baseline approval in the browser.',
|
|
19
|
+
},
|
|
20
|
+
zh: {
|
|
21
|
+
create: '用一个 prompt 创建并自动生成独立 Remix 变体',
|
|
22
|
+
input: '批次草案 JSON (JavaScript Object Notation, 数据交换格式) 文件路径',
|
|
23
|
+
status: '查询批次及生成状态',
|
|
24
|
+
id: '批次标识',
|
|
25
|
+
created: '批次已创建',
|
|
26
|
+
prompt: '完整的批量生成需求', workspace: '来源 workspace 标识',
|
|
27
|
+
directory: '来源工程目录,默认 basic', request: '用于重试的稳定批次创建标识',
|
|
28
|
+
team: '独立工作区所属团队标识', invalidMode: '请选择 --prompt 配合 --workspace-id,或 --input,两种方式不能同时使用。',
|
|
29
|
+
requestRequired: '使用 prompt 创建时必须提供稳定的 --client-request-id,重试同一请求时复用该标识。',
|
|
30
|
+
help: '\n使用 --prompt、--workspace-id 和稳定的 --client-request-id。平台自动规划,在副本中生成独立 Remix,保持来源不变。未指定数量默认 3 个,最多 10 个。--input 也兼容现有结构化草案。\n',
|
|
31
|
+
confirmation: '生成已排队,可查询批次状态追踪各项成果。',
|
|
32
|
+
review: '此历史草案需要在浏览器确认基准。',
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
const language = () => /^zh/i.test(process.env.PLAYCRAFT_LANG ?? process.env.LANG ?? '') ? messages.zh : messages.en;
|
|
36
|
+
export async function createBatchDraft(path, client = new AgentApiClient()) {
|
|
37
|
+
const draft = batchCreateSchema.parse(JSON.parse(await readFile(path, 'utf8')));
|
|
38
|
+
return client.post('/create-batch', draft);
|
|
39
|
+
}
|
|
40
|
+
export async function createPromptBatch(input, client = new AgentApiClient()) {
|
|
41
|
+
return client.post('/create-batch', batchPromptCreateSchema.parse(input));
|
|
42
|
+
}
|
|
43
|
+
export function registerBatchCommands(tools) {
|
|
44
|
+
const t = language();
|
|
45
|
+
tools.command('create-batch')
|
|
46
|
+
.description(t.create)
|
|
47
|
+
.option('--input <path>', t.input)
|
|
48
|
+
.option('--prompt <text>', t.prompt)
|
|
49
|
+
.option('--workspace-id <id>', t.workspace)
|
|
50
|
+
.option('--source-directory <name>', t.directory, 'basic')
|
|
51
|
+
.option('--client-request-id <id>', t.request)
|
|
52
|
+
.option('--team-id <id>', t.team)
|
|
53
|
+
.option('--json', 'JSON (JavaScript Object Notation, 数据交换格式)')
|
|
54
|
+
.addHelpText('after', t.help)
|
|
55
|
+
.action(async (opts) => {
|
|
56
|
+
try {
|
|
57
|
+
if (Boolean(opts.input) === Boolean(opts.prompt))
|
|
58
|
+
throw new Error(t.invalidMode);
|
|
59
|
+
if (opts.prompt && !opts.workspaceId)
|
|
60
|
+
throw new Error(t.invalidMode);
|
|
61
|
+
if (opts.prompt && !opts.clientRequestId)
|
|
62
|
+
throw new Error(t.requestRequired);
|
|
63
|
+
const result = opts.input ? await createBatchDraft(opts.input) : await createPromptBatch({
|
|
64
|
+
clientRequestId: opts.clientRequestId, prompt: opts.prompt,
|
|
65
|
+
sourceWorkspaceId: opts.workspaceId, sourceDirectoryName: opts.sourceDirectory,
|
|
66
|
+
...(opts.teamId ? { teamId: opts.teamId } : {}),
|
|
67
|
+
});
|
|
68
|
+
console.log(opts.json ? JSON.stringify(result, null, 2) : `${t.created}: ${result.batchId}\n${result.manifest.autoStart ? t.confirmation : t.review}`);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
handleError(error);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
tools.command('get-batch')
|
|
75
|
+
.description(t.status)
|
|
76
|
+
.requiredOption('--batch-id <id>', t.id)
|
|
77
|
+
.action(async (opts) => {
|
|
78
|
+
try {
|
|
79
|
+
const client = new AgentApiClient();
|
|
80
|
+
const result = await client.get('/get-batch', { batchId: opts.batchId });
|
|
81
|
+
console.log(JSON.stringify(result, null, 2));
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
handleError(error);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { writeFileSync, mkdirSync } from 'fs';
|
|
2
2
|
import { dirname, parse, join } from 'path';
|
|
3
3
|
import sharp from 'sharp';
|
|
4
|
+
import { captureGeneratedArtifact } from '../workspace-runtime/workspaces/artifact-receipts.js';
|
|
4
5
|
import { AgentApiClient } from '../utils/agent-api-client.js';
|
|
5
6
|
import { MAX_REFERENCE_IMAGES, collectReferenceImagePaths, collectReferenceImagePayloads, sniffImageExtension, extensionForImageMime, resolveImageOutputPath, handleError, raceWithTimeout, } from './tools-utils.js';
|
|
6
7
|
function isMasterCompositeStripRequest(opts) {
|
|
@@ -258,7 +259,7 @@ export function registerGenerationCommands(tools) {
|
|
|
258
259
|
...(result.additionalImages ?? []),
|
|
259
260
|
];
|
|
260
261
|
const isBatch = allImages.length > 1;
|
|
261
|
-
const modelPart =
|
|
262
|
+
const modelPart = opts.imageModel ? `model: ${opts.imageModel}` : '';
|
|
262
263
|
const providerPart = result.provider ? `provider: ${result.provider}` : '';
|
|
263
264
|
const labelParts = [modelPart, providerPart].filter(Boolean);
|
|
264
265
|
const modelLabel = labelParts.length > 0 ? ` (${labelParts.join(', ')})` : '';
|
|
@@ -311,10 +312,8 @@ export function registerGenerationCommands(tools) {
|
|
|
311
312
|
savedPaths.push(resolvedPath);
|
|
312
313
|
const sidecar = {
|
|
313
314
|
prompt: opts.prompt,
|
|
314
|
-
model:
|
|
315
|
-
requestedModel: opts.imageModel ?? null,
|
|
315
|
+
model: opts.imageModel ?? 'default',
|
|
316
316
|
provider: result.provider ?? null,
|
|
317
|
-
transparentBackground: result.transparentBackground ?? null,
|
|
318
317
|
aspectRatio: opts.aspectRatio,
|
|
319
318
|
imageSize: opts.imageSize ?? null,
|
|
320
319
|
requestedWidth: opts.width ?? stripTarget?.width ?? null,
|
|
@@ -332,6 +331,9 @@ export function registerGenerationCommands(tools) {
|
|
|
332
331
|
};
|
|
333
332
|
const sidecarPath = resolvedPath.replace(/\.[^.]+$/, '.json');
|
|
334
333
|
writeFileSync(sidecarPath, JSON.stringify(sidecar, null, 2));
|
|
334
|
+
await captureGeneratedArtifact(resolvedPath, 'generate-image', {
|
|
335
|
+
...(actualWidth ? { width: actualWidth } : {}), ...(actualHeight ? { height: actualHeight } : {}),
|
|
336
|
+
});
|
|
335
337
|
console.log(` └─ Generation metadata: ${sidecarPath}`);
|
|
336
338
|
}
|
|
337
339
|
if (!isBatch && opts.output !== savedPaths[0] && savedPaths[0]) {
|
|
@@ -379,6 +381,7 @@ export function registerGenerationCommands(tools) {
|
|
|
379
381
|
output: opts.output, generatedAt: new Date().toISOString(),
|
|
380
382
|
};
|
|
381
383
|
writeFileSync(opts.output.replace(/\.[^.]+$/, '.json'), JSON.stringify(sfxSidecar, null, 2));
|
|
384
|
+
await captureGeneratedArtifact(opts.output, 'generate-sfx', { duration: result.duration });
|
|
382
385
|
console.log(` └─ Generation metadata: ${opts.output.replace(/\.[^.]+$/, '.json')}`);
|
|
383
386
|
}
|
|
384
387
|
catch (e) {
|
|
@@ -411,6 +414,7 @@ export function registerGenerationCommands(tools) {
|
|
|
411
414
|
output: opts.output, generatedAt: new Date().toISOString(),
|
|
412
415
|
};
|
|
413
416
|
writeFileSync(opts.output.replace(/\.[^.]+$/, '.json'), JSON.stringify(bgmSidecar, null, 2));
|
|
417
|
+
await captureGeneratedArtifact(opts.output, 'generate-bgm', { duration: result.duration });
|
|
414
418
|
console.log(` └─ Generation metadata: ${opts.output.replace(/\.[^.]+$/, '.json')}`);
|
|
415
419
|
}
|
|
416
420
|
catch (e) {
|
package/dist/commands/tools.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { registerGenerationCommands } from './tools-generation.js';
|
|
2
|
+
import { registerBatchCommands } from './tools-batch.js';
|
|
2
3
|
import { registerProjectCommands } from './tools-project.js';
|
|
3
4
|
import { register3DCommands } from './tools-3d.js';
|
|
4
5
|
import { registerDetectCommand } from './tools-detect.js';
|
|
@@ -9,6 +10,7 @@ export function registerToolsCommands(program) {
|
|
|
9
10
|
.description('后端 /api/agent/tools;运行 playcraft login 后即可直接使用,也支持 .playcraft.json 或 PLAYCRAFT_SANDBOX_TOKEN 环境变量');
|
|
10
11
|
registerGenerationCommands(tools);
|
|
11
12
|
registerProjectCommands(tools);
|
|
13
|
+
registerBatchCommands(tools);
|
|
12
14
|
register3DCommands(tools);
|
|
13
15
|
registerDetectCommand(tools);
|
|
14
16
|
registerVideoReviewCommand(tools);
|