@playcraft/cli 0.1.29 → 0.1.30
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 +68 -109
- package/dist/commands/tools.js +2 -2
- 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 +965 -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 +70 -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 +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 +1096 -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 +196 -0
- package/dist/workspace-runtime/production/processes.js +86 -0
- package/dist/workspace-runtime/production/save-command.js +35 -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 +207 -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 +1116 -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 +1038 -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 +366 -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/attachment-content.js +111 -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/composer-context.js +125 -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-search.js +116 -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 +517 -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 +226 -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 -81
- 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 -81
- package/project-templates/threejs/.claude/skills/playcraft-image-generation/SKILL.md +23 -81
- package/dist/commands/tools-image-context.js +0 -23
- package/dist/utils/image-asset-context.js +0 -105
|
@@ -1,8 +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 {
|
|
5
|
-
import { readImageAssetContext } from '../utils/image-asset-context.js';
|
|
4
|
+
import { captureGeneratedArtifact } from '../workspace-runtime/workspaces/artifact-receipts.js';
|
|
6
5
|
import { AgentApiClient } from '../utils/agent-api-client.js';
|
|
7
6
|
import { MAX_REFERENCE_IMAGES, collectReferenceImagePaths, collectReferenceImagePayloads, sniffImageExtension, extensionForImageMime, resolveImageOutputPath, handleError, raceWithTimeout, } from './tools-utils.js';
|
|
8
7
|
function isMasterCompositeStripRequest(opts) {
|
|
@@ -152,120 +151,86 @@ export function registerGenerationCommands(tools) {
|
|
|
152
151
|
.requiredOption('--prompt <text>', '图片描述')
|
|
153
152
|
.option('--aspect-ratio <ratio>', '宽高比 (1:1|45:16|16:9|9:16|3:4|4:3|2:3|3:2|21:9)', '1:1')
|
|
154
153
|
.requiredOption('--output <path>', '保存路径(--count>1 时自动追加 _1/_2 等后缀)')
|
|
155
|
-
.option('--target-asset <path>', 'Existing asset to replace; read its schema references and delivery constraints')
|
|
156
|
-
.option('--no-target-reference', 'Generate without the existing asset reference only when explicitly requested')
|
|
157
|
-
.option('--output-format <format>', 'Final format: png, webp or jpeg')
|
|
158
|
-
.option('--fit-mode <mode>', 'Canvas fitting: contain, cover or fill')
|
|
159
|
-
.option('--padding-color <color>', 'Canvas padding color (transparent by default)')
|
|
160
|
-
.option('--remove-background', 'Require a verified transparent image')
|
|
161
|
-
.option('--no-remove-background', 'Keep the generated background')
|
|
162
154
|
.option('--image-size <size>', '图片尺寸 (1K|2K|4K)')
|
|
163
155
|
.option('--width <n>', '精确宽度(像素;MC 横排 5×9:16 推荐 3600 或 4096)', (v) => parseInt(v, 10))
|
|
164
156
|
.option('--height <n>', '精确高度(像素;MC 推荐 1280 或 1455)', (v) => parseInt(v, 10))
|
|
165
157
|
.option('--reference-image <path>', '参考图路径(可重复多次,最多 8 张),支持 PNG/JPG/WEBP', collectReferenceImagePaths, [])
|
|
166
158
|
.option('--image-model <model>', '模型名(如 gpt-image-2)或 provider/model(如 mulerouter/gpt-image-2)。传模型名时后端按优先级自动 fallback;传 provider/model 则直连指定 provider')
|
|
167
|
-
.option('--retry <n>', '
|
|
159
|
+
.option('--retry <n>', '全部 provider 都失败时的重试轮数(默认 1;超时不计为可重试错误)', (v) => parseInt(v, 10), 1)
|
|
168
160
|
.option('--timeout <seconds>', '请求超时秒数(默认 1800,即 30 分钟;异步生图模型耗时较长,仅在后端明确报错时提前终止)', (v) => parseInt(v, 10), 1800)
|
|
169
161
|
.option('--count <n>', '一次 API 调用生成多少张图片(默认 1,最多 10;仅 gpt-image-2 实际支持批量)', (v) => parseInt(v, 10), 1)
|
|
170
162
|
.action(async (opts) => {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
schemaRemoveBackground: assetContext?.removeBackgroundFromSchema ? assetContext.removeBackground : undefined,
|
|
183
|
-
removeBackground: opts.removeBackground, fallbackRemoveBackground: assetContext?.removeBackground }),
|
|
184
|
-
paddingColor: assetContext?.paddingColor ?? opts.paddingColor,
|
|
185
|
-
imageModel: opts.imageModel || assetContext?.imageModelRef,
|
|
186
|
-
};
|
|
187
|
-
const invalidDelivery = validateImageDeliveryOptions(opts);
|
|
188
|
-
if (invalidDelivery)
|
|
189
|
-
throw new Error(invalidDelivery);
|
|
190
|
-
const paths = opts.referenceImage?.length ? opts.referenceImage : assetContext?.referenceImagePaths ?? [];
|
|
191
|
-
if (paths.length > MAX_REFERENCE_IMAGES) {
|
|
192
|
-
console.error(`Error: Too many reference images: ${paths.length} paths given; maximum is ${MAX_REFERENCE_IMAGES}.`);
|
|
193
|
-
process.exit(1);
|
|
194
|
-
return;
|
|
163
|
+
const paths = opts.referenceImage ?? [];
|
|
164
|
+
if (paths.length > MAX_REFERENCE_IMAGES) {
|
|
165
|
+
console.error(`Error: Too many reference images: ${paths.length} paths given; maximum is ${MAX_REFERENCE_IMAGES}.`);
|
|
166
|
+
process.exit(1);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const hasReferenceImages = paths.length > 0;
|
|
170
|
+
const attemptGenerate = async (modelRef, attempt) => {
|
|
171
|
+
const label = modelRef ? `[${modelRef}]` : '[default model]';
|
|
172
|
+
if (attempt > 0) {
|
|
173
|
+
console.log(`Retry ${attempt}/${opts.retry} ${label}...`);
|
|
195
174
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
return raceWithTimeout(requestPromise, opts.timeout * 1000, `Request timed out after ${opts.timeout}s. The backend task may still be running — check admin panel before re-running.`);
|
|
223
|
-
};
|
|
224
|
-
const enrichError = (err, modelRef) => {
|
|
225
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
226
|
-
const lines = [`Error: ${msg}`];
|
|
227
|
-
if (/source_transparency_invalid|output_transparency_invalid|transparency_unsupported/.test(msg)) {
|
|
228
|
-
lines.push('Hint: Transparent output was not delivered. Preserve the requested transparency, references and model; use the request id and stage to investigate.');
|
|
229
|
-
}
|
|
230
|
-
else if (/API 500\b|Internal server error/.test(msg)) {
|
|
231
|
-
lines.push('');
|
|
232
|
-
lines.push('Hint: API 500 error — the model may be temporarily unavailable.');
|
|
233
|
-
lines.push(' → Inspect the request id and backend diagnostics before another generation attempt.');
|
|
234
|
-
if (modelRef?.includes('/') && opts.removeBackground !== true) {
|
|
235
|
-
lines.push(' → Or try without provider prefix to auto-fallback providers: --image-model gpt-image-2');
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
else if (msg.includes('timed out')) {
|
|
239
|
-
lines.push('');
|
|
240
|
-
lines.push('Hint: Request timed out. The backend task may still be running — retries were skipped to avoid creating duplicate tasks.');
|
|
241
|
-
lines.push(' → Check the backend/admin panel for task status before re-running');
|
|
242
|
-
lines.push(' → Try --image-size 1K to reduce generation time');
|
|
243
|
-
lines.push(' → Or increase timeout with --timeout <seconds> (current default: 1800s)');
|
|
244
|
-
}
|
|
245
|
-
else if (hasReferenceImages && (msg.includes('reference') || msg.includes('image input'))) {
|
|
246
|
-
lines.push('');
|
|
247
|
-
lines.push('Hint: Reference image error — the selected model may not support --reference-image.');
|
|
248
|
-
lines.push(' → Run: playcraft tools list-image-models');
|
|
249
|
-
lines.push(' → Models with CAPABILITY=text+image support --reference-image');
|
|
175
|
+
else {
|
|
176
|
+
console.log(`Generating image ${label}...`);
|
|
177
|
+
}
|
|
178
|
+
const referenceImages = await collectReferenceImagePayloads(paths);
|
|
179
|
+
const client = new AgentApiClient();
|
|
180
|
+
const imageCount = Math.min(Math.max(1, opts.count ?? 1), 10);
|
|
181
|
+
const requestPromise = client.post('/generate-image', {
|
|
182
|
+
prompt: opts.prompt,
|
|
183
|
+
aspectRatio: opts.aspectRatio,
|
|
184
|
+
imageSize: opts.imageSize,
|
|
185
|
+
...(opts.width && opts.height ? { width: opts.width, height: opts.height } : {}),
|
|
186
|
+
referenceImages,
|
|
187
|
+
...(modelRef ? { imageModelRef: modelRef } : {}),
|
|
188
|
+
...(imageCount > 1 ? { imageCount } : {}),
|
|
189
|
+
});
|
|
190
|
+
return raceWithTimeout(requestPromise, opts.timeout * 1000, `Request timed out after ${opts.timeout}s. The backend task may still be running — check admin panel before re-running.`);
|
|
191
|
+
};
|
|
192
|
+
const enrichError = (err, modelRef) => {
|
|
193
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
194
|
+
const lines = [`Error: ${msg}`];
|
|
195
|
+
if (msg.includes('500') || msg.includes('Internal server error')) {
|
|
196
|
+
lines.push('');
|
|
197
|
+
lines.push('Hint: API 500 error — the model may be temporarily unavailable.');
|
|
198
|
+
lines.push(' → If using a bare model name (e.g. gpt-image-2), all providers failed — try a different model');
|
|
199
|
+
if (modelRef?.includes('/')) {
|
|
200
|
+
lines.push(' → Or try without provider prefix to auto-fallback providers: --image-model gpt-image-2');
|
|
250
201
|
}
|
|
251
|
-
|
|
252
|
-
|
|
202
|
+
}
|
|
203
|
+
else if (msg.includes('timed out')) {
|
|
204
|
+
lines.push('');
|
|
205
|
+
lines.push('Hint: Request timed out. The backend task may still be running — retries were skipped to avoid creating duplicate tasks.');
|
|
206
|
+
lines.push(' → Check the backend/admin panel for task status before re-running');
|
|
207
|
+
lines.push(' → Try --image-size 1K to reduce generation time');
|
|
208
|
+
lines.push(' → Or increase timeout with --timeout <seconds> (current default: 1800s)');
|
|
209
|
+
}
|
|
210
|
+
else if (hasReferenceImages && (msg.includes('reference') || msg.includes('image input'))) {
|
|
211
|
+
lines.push('');
|
|
212
|
+
lines.push('Hint: Reference image error — the selected model may not support --reference-image.');
|
|
213
|
+
lines.push(' → Run: playcraft tools list-image-models');
|
|
214
|
+
lines.push(' → Models with CAPABILITY=text+image support --reference-image');
|
|
215
|
+
}
|
|
216
|
+
return lines.join('\n');
|
|
217
|
+
};
|
|
218
|
+
try {
|
|
253
219
|
let result = null;
|
|
254
220
|
let lastError = null;
|
|
255
221
|
// 重试逻辑:超时不重试(后端任务可能仍在运行,重试会导致重复提交)
|
|
256
|
-
const maxAttempts = 1 + (opts.retry ??
|
|
222
|
+
const maxAttempts = 1 + (opts.retry ?? 1);
|
|
257
223
|
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
258
224
|
try {
|
|
259
|
-
result = await attemptGenerate(opts.imageModel
|
|
225
|
+
result = await attemptGenerate(opts.imageModel, attempt);
|
|
260
226
|
break;
|
|
261
227
|
}
|
|
262
228
|
catch (e) {
|
|
263
229
|
lastError = e;
|
|
264
230
|
const msg = e instanceof Error ? e.message : String(e);
|
|
265
231
|
const isTimeout = msg.includes('timed out');
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
console.error(`Attempt ${attempt + 1} stopped: ${msg}`);
|
|
232
|
+
if (isTimeout) {
|
|
233
|
+
console.error(`Attempt ${attempt + 1} timed out — not retrying (backend task may still be running).`);
|
|
269
234
|
break;
|
|
270
235
|
}
|
|
271
236
|
if (attempt < maxAttempts - 1) {
|
|
@@ -294,14 +259,12 @@ export function registerGenerationCommands(tools) {
|
|
|
294
259
|
...(result.additionalImages ?? []),
|
|
295
260
|
];
|
|
296
261
|
const isBatch = allImages.length > 1;
|
|
297
|
-
const modelPart =
|
|
262
|
+
const modelPart = opts.imageModel ? `model: ${opts.imageModel}` : '';
|
|
298
263
|
const providerPart = result.provider ? `provider: ${result.provider}` : '';
|
|
299
264
|
const labelParts = [modelPart, providerPart].filter(Boolean);
|
|
300
265
|
const modelLabel = labelParts.length > 0 ? ` (${labelParts.join(', ')})` : '';
|
|
301
266
|
const savedPaths = [];
|
|
302
|
-
|
|
303
|
-
console.log(`Transparency source: ${result.transparencySource}`);
|
|
304
|
-
const stripTarget = opts.targetAsset || result.transparentBackground ? null : resolveStripTargetDimensions({
|
|
267
|
+
const stripTarget = resolveStripTargetDimensions({
|
|
305
268
|
aspectRatio: opts.aspectRatio,
|
|
306
269
|
width: opts.width,
|
|
307
270
|
height: opts.height,
|
|
@@ -349,17 +312,8 @@ export function registerGenerationCommands(tools) {
|
|
|
349
312
|
savedPaths.push(resolvedPath);
|
|
350
313
|
const sidecar = {
|
|
351
314
|
prompt: opts.prompt,
|
|
352
|
-
model:
|
|
353
|
-
requestedModel: opts.imageModel ?? null,
|
|
315
|
+
model: opts.imageModel ?? 'default',
|
|
354
316
|
provider: result.provider ?? null,
|
|
355
|
-
transparentBackground: result.transparentBackground ?? null,
|
|
356
|
-
transparencySource: result.transparencySource ?? null,
|
|
357
|
-
requestId: result.requestId ?? null,
|
|
358
|
-
attempts: result.attempts ?? null,
|
|
359
|
-
targetAssetPath: assetContext?.targetAssetPath ?? null,
|
|
360
|
-
outputFormat: opts.outputFormat ?? null,
|
|
361
|
-
fitMode: opts.fitMode ?? null,
|
|
362
|
-
paddingColor: opts.paddingColor ?? null,
|
|
363
317
|
aspectRatio: opts.aspectRatio,
|
|
364
318
|
imageSize: opts.imageSize ?? null,
|
|
365
319
|
requestedWidth: opts.width ?? stripTarget?.width ?? null,
|
|
@@ -377,6 +331,9 @@ export function registerGenerationCommands(tools) {
|
|
|
377
331
|
};
|
|
378
332
|
const sidecarPath = resolvedPath.replace(/\.[^.]+$/, '.json');
|
|
379
333
|
writeFileSync(sidecarPath, JSON.stringify(sidecar, null, 2));
|
|
334
|
+
await captureGeneratedArtifact(resolvedPath, 'generate-image', {
|
|
335
|
+
...(actualWidth ? { width: actualWidth } : {}), ...(actualHeight ? { height: actualHeight } : {}),
|
|
336
|
+
});
|
|
380
337
|
console.log(` └─ Generation metadata: ${sidecarPath}`);
|
|
381
338
|
}
|
|
382
339
|
if (!isBatch && opts.output !== savedPaths[0] && savedPaths[0]) {
|
|
@@ -424,6 +381,7 @@ export function registerGenerationCommands(tools) {
|
|
|
424
381
|
output: opts.output, generatedAt: new Date().toISOString(),
|
|
425
382
|
};
|
|
426
383
|
writeFileSync(opts.output.replace(/\.[^.]+$/, '.json'), JSON.stringify(sfxSidecar, null, 2));
|
|
384
|
+
await captureGeneratedArtifact(opts.output, 'generate-sfx', { duration: result.duration });
|
|
427
385
|
console.log(` └─ Generation metadata: ${opts.output.replace(/\.[^.]+$/, '.json')}`);
|
|
428
386
|
}
|
|
429
387
|
catch (e) {
|
|
@@ -456,6 +414,7 @@ export function registerGenerationCommands(tools) {
|
|
|
456
414
|
output: opts.output, generatedAt: new Date().toISOString(),
|
|
457
415
|
};
|
|
458
416
|
writeFileSync(opts.output.replace(/\.[^.]+$/, '.json'), JSON.stringify(bgmSidecar, null, 2));
|
|
417
|
+
await captureGeneratedArtifact(opts.output, 'generate-bgm', { duration: result.duration });
|
|
459
418
|
console.log(` └─ Generation metadata: ${opts.output.replace(/\.[^.]+$/, '.json')}`);
|
|
460
419
|
}
|
|
461
420
|
catch (e) {
|
package/dist/commands/tools.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
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';
|
|
5
6
|
import { registerVideoReviewCommand } from './tools-video-review.js';
|
|
6
|
-
import { registerImageContextCommand } from './tools-image-context.js';
|
|
7
7
|
export function registerToolsCommands(program) {
|
|
8
8
|
const tools = program
|
|
9
9
|
.command('tools')
|
|
10
10
|
.description('后端 /api/agent/tools;运行 playcraft login 后即可直接使用,也支持 .playcraft.json 或 PLAYCRAFT_SANDBOX_TOKEN 环境变量');
|
|
11
11
|
registerGenerationCommands(tools);
|
|
12
|
-
registerImageContextCommand(tools);
|
|
13
12
|
registerProjectCommands(tools);
|
|
13
|
+
registerBatchCommands(tools);
|
|
14
14
|
register3DCommands(tools);
|
|
15
15
|
registerDetectCommand(tools);
|
|
16
16
|
registerVideoReviewCommand(tools);
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { videoAnalysisRunSchema, videoModelCatalogSchema } from '@playcraft/common/agent-tools';
|
|
2
|
+
import { AgentApiClient } from '../utils/agent-api-client.js';
|
|
3
|
+
import { executeVideoUnderstanding, readVideoRun, requireVideoId, resolveVideoWorkspace, videoTimeout, } from '../video-replication/client.js';
|
|
4
|
+
import { materializeVideoEvidence } from '../video-replication/evidence.js';
|
|
5
|
+
import { probeVideo } from '../video-replication/media.js';
|
|
6
|
+
import { VideoCliError, videoMessage as t } from '../video-replication/messages.js';
|
|
7
|
+
import { comparisonMessage as c } from '../video-replication/comparison-messages.js';
|
|
8
|
+
import { executeVideoComparison, readComparisonRun, comparisonRunResult } from '../video-replication/comparison.js';
|
|
9
|
+
import { materializeComparisonEvidence } from '../video-replication/comparison-evidence.js';
|
|
10
|
+
/** One final structured stdout record; progress and diagnostics stay on stderr. */
|
|
11
|
+
export async function runVideoCliAction(command, options, action) {
|
|
12
|
+
const interrupt = new AbortController();
|
|
13
|
+
const onInterrupt = () => interrupt.abort(new DOMException('Interrupted', 'AbortError'));
|
|
14
|
+
process.once('SIGINT', onInterrupt);
|
|
15
|
+
let timer;
|
|
16
|
+
const deadline = new AbortController();
|
|
17
|
+
try {
|
|
18
|
+
timer = setTimeout(() => deadline.abort(new DOMException('Timed out', 'TimeoutError')), videoTimeout(options));
|
|
19
|
+
const result = await action(AbortSignal.any([interrupt.signal, deadline.signal]));
|
|
20
|
+
process.stdout.write(`${JSON.stringify(result.envelope)}\n`);
|
|
21
|
+
process.exitCode = result.exitCode;
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
const key = error instanceof VideoCliError ? error.code : interrupt.signal.aborted ? 'interrupted' : deadline.signal.aborted ? 'timeoutReached' : 'failed';
|
|
25
|
+
const envelope = {
|
|
26
|
+
schemaVersion: 'playcraft.video-cli.v1', command, state: interrupt.signal.aborted || deadline.signal.aborted ? 'uncertain' : 'failed', ok: false,
|
|
27
|
+
error: { code: key, message: t(key) },
|
|
28
|
+
};
|
|
29
|
+
process.stdout.write(`${JSON.stringify(envelope)}\n`);
|
|
30
|
+
process.exitCode = error instanceof VideoCliError ? error.exitCode : interrupt.signal.aborted ? 130 : deadline.signal.aborted ? 75 : 4;
|
|
31
|
+
}
|
|
32
|
+
finally {
|
|
33
|
+
if (timer)
|
|
34
|
+
clearTimeout(timer);
|
|
35
|
+
process.removeListener('SIGINT', onInterrupt);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function shared(command) {
|
|
39
|
+
return command.option('--workspace <id>', t('workspace')).option('--timeout <seconds>', t('timeout'));
|
|
40
|
+
}
|
|
41
|
+
export function registerVideoCommands(program) {
|
|
42
|
+
const video = program.command('video').description(t('topic'));
|
|
43
|
+
shared(video.command('models').description(t('models')))
|
|
44
|
+
.option('--json', t('json'))
|
|
45
|
+
.action((options) => runVideoCliAction('video models', options, async (signal) => {
|
|
46
|
+
const workspaceId = await resolveVideoWorkspace(options.workspace);
|
|
47
|
+
const catalog = videoModelCatalogSchema.parse(await new AgentApiClient().get('/video-models', { workspaceId }, { signal }));
|
|
48
|
+
return { exitCode: 0, envelope: { schemaVersion: 'playcraft.video-cli.v1', command: 'video models', state: 'completed', ok: true, result: catalog } };
|
|
49
|
+
}));
|
|
50
|
+
shared(video.command('understand').description(t('understand')))
|
|
51
|
+
.option('--video <path>', t('video')).option('--question <text>', t('question'))
|
|
52
|
+
.option('--context <path>', t('context')).option('--processing <mode>', t('processing'))
|
|
53
|
+
.option('--fps <number>', t('fps')).option('--clip <start:end>', t('clip'))
|
|
54
|
+
.option('--model-config <id>', t('model')).option('--out <directory>', t('out'))
|
|
55
|
+
.option('--resume <runId>', t('resume'))
|
|
56
|
+
.action((options) => runVideoCliAction('video understand', options, signal => executeVideoUnderstanding(options, signal, {
|
|
57
|
+
client: new AgentApiClient(), probe: probeVideo, materialize: materializeVideoEvidence,
|
|
58
|
+
progress: key => process.stderr.write(`[video] ${t(key)}\n`),
|
|
59
|
+
})));
|
|
60
|
+
shared(video.command('compare').description(c('compare')))
|
|
61
|
+
.option('--reference <path>', c('reference')).option('--analysis <path>', c('analysis'))
|
|
62
|
+
.option('--candidate <path>', c('candidate')).option('--recording <path>', c('recording'))
|
|
63
|
+
.option('--policy <path>', c('policy')).option('--processing <mode>', c('processing'))
|
|
64
|
+
.option('--fps <number>', t('fps')).option('--model-config <id>', t('model'))
|
|
65
|
+
.option('--out <directory>', t('out')).option('--resume <runId>', t('resume'))
|
|
66
|
+
.action((options) => runVideoCliAction('video compare', options, signal => executeVideoComparison(options, signal, {
|
|
67
|
+
client: new AgentApiClient(), probe: probeVideo, materialize: materializeComparisonEvidence,
|
|
68
|
+
progress: key => process.stderr.write(`[video] ${t(key)}\n`),
|
|
69
|
+
})));
|
|
70
|
+
const jobs = video.command('jobs').description(t('jobs'));
|
|
71
|
+
shared(jobs.command('get <runId>').description(t('get')))
|
|
72
|
+
.option('--kind <kind>', c('kind'))
|
|
73
|
+
.action((runId, options) => runVideoCliAction('video jobs get', options, async (signal) => {
|
|
74
|
+
const workspaceId = await resolveVideoWorkspace(options.workspace);
|
|
75
|
+
if (options.kind !== undefined && !['understanding', 'comparison'].includes(options.kind))
|
|
76
|
+
throw new VideoCliError('invalidArguments', 64);
|
|
77
|
+
if (options.kind === 'comparison') {
|
|
78
|
+
const run = await readComparisonRun(new AgentApiClient(), requireVideoId(runId), workspaceId, signal);
|
|
79
|
+
return comparisonRunResult('video jobs get', run);
|
|
80
|
+
}
|
|
81
|
+
const run = await readVideoRun(new AgentApiClient(), requireVideoId(runId), workspaceId, signal);
|
|
82
|
+
return { exitCode: run.state === 'uncertain' ? 75 : run.state === 'failed' || run.state === 'cancelled' ? 4 : 0, envelope: {
|
|
83
|
+
schemaVersion: 'playcraft.video-cli.v1', command: 'video jobs get', runId: run.runId, state: run.state,
|
|
84
|
+
ok: !['failed', 'uncertain', 'cancelled'].includes(run.state), result: run,
|
|
85
|
+
} };
|
|
86
|
+
}));
|
|
87
|
+
shared(jobs.command('cancel <runId>').description(t('cancel')))
|
|
88
|
+
.option('--kind <kind>', c('kind'))
|
|
89
|
+
.action((identifier, options) => runVideoCliAction('video jobs cancel', options, async (signal) => {
|
|
90
|
+
const workspaceId = await resolveVideoWorkspace(options.workspace);
|
|
91
|
+
const runId = requireVideoId(identifier);
|
|
92
|
+
if (options.kind !== undefined && !['understanding', 'comparison'].includes(options.kind))
|
|
93
|
+
throw new VideoCliError('invalidArguments', 64);
|
|
94
|
+
if (options.kind === 'comparison') {
|
|
95
|
+
const run = await readComparisonRun(new AgentApiClient(), runId, workspaceId, signal, true);
|
|
96
|
+
const confirmed = run.state === 'cancelled';
|
|
97
|
+
return { exitCode: confirmed ? 0 : 75, envelope: {
|
|
98
|
+
schemaVersion: 'playcraft.video-cli.v1', command: 'video jobs cancel', runId, state: run.state,
|
|
99
|
+
ok: confirmed, result: { ...run, cancellationConfirmed: confirmed },
|
|
100
|
+
} };
|
|
101
|
+
}
|
|
102
|
+
const run = videoAnalysisRunSchema.parse(await new AgentApiClient().post(`/video-analysis/${runId}/cancel`, { workspaceId, runId }, { signal }));
|
|
103
|
+
if (run.runId !== runId || run.workspaceId !== workspaceId)
|
|
104
|
+
throw new VideoCliError('invalidResult');
|
|
105
|
+
const confirmed = run.state === 'cancelled';
|
|
106
|
+
return { exitCode: confirmed ? 0 : 75, envelope: {
|
|
107
|
+
schemaVersion: 'playcraft.video-cli.v1', command: 'video jobs cancel', runId, state: run.state,
|
|
108
|
+
ok: confirmed, result: { ...run, cancellationConfirmed: confirmed },
|
|
109
|
+
} };
|
|
110
|
+
}));
|
|
111
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { INCOMPATIBLE_EXIT_CODE, parseSandboxRuntimeConfig, } from '@playcraft/common/cloud-workspace/runtime';
|
|
3
|
+
import { startWorkspaceRuntime } from '../workspace-runtime/serve.js';
|
|
4
|
+
export async function runWorkspaceRuntimeServe(configPath) {
|
|
5
|
+
process.env.PLAYCRAFT_SKIP_UPDATE_CHECK = '1';
|
|
6
|
+
let config;
|
|
7
|
+
try {
|
|
8
|
+
config = parseSandboxRuntimeConfig(JSON.parse(await readFile(configPath, 'utf8')));
|
|
9
|
+
}
|
|
10
|
+
catch (error) {
|
|
11
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
12
|
+
console.error(`workspace-runtime config invalid: ${message}`);
|
|
13
|
+
process.exitCode = INCOMPATIBLE_EXIT_CODE;
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
const runtime = await startWorkspaceRuntime(config, { configPath });
|
|
18
|
+
console.log(JSON.stringify({
|
|
19
|
+
status: 'listening',
|
|
20
|
+
url: runtime.url,
|
|
21
|
+
phase: 'awaiting_native',
|
|
22
|
+
protocolMajor: runtime.config.protocolMajor,
|
|
23
|
+
protocolMinor: runtime.config.protocolMinor,
|
|
24
|
+
}));
|
|
25
|
+
await new Promise((resolve) => {
|
|
26
|
+
const stop = () => {
|
|
27
|
+
void runtime.close().finally(resolve);
|
|
28
|
+
};
|
|
29
|
+
process.once('SIGINT', stop);
|
|
30
|
+
process.once('SIGTERM', stop);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
const code = error.code;
|
|
35
|
+
if (code === 'protocol_incompatible' || code === 'duplicate_instance') {
|
|
36
|
+
console.error(error instanceof Error ? error.message : code);
|
|
37
|
+
process.exitCode = INCOMPATIBLE_EXIT_CODE;
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
throw error;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export function registerWorkspaceRuntimeCommands(program) {
|
|
44
|
+
const workspaceRuntime = program
|
|
45
|
+
.command('workspace-runtime')
|
|
46
|
+
.description('沙箱内 Sandbox Agent Server(Cloud Workspace 常驻运行入口)');
|
|
47
|
+
workspaceRuntime
|
|
48
|
+
.command('serve')
|
|
49
|
+
.description('前台常驻:读取 Runtime Server 下发的配置,不启动本地 backend 或旧同步')
|
|
50
|
+
.requiredOption('--config <path>', 'Runtime 引导配置 JSON')
|
|
51
|
+
.action(async (options) => {
|
|
52
|
+
await runWorkspaceRuntimeServe(options.config);
|
|
53
|
+
});
|
|
54
|
+
}
|
package/dist/config.js
CHANGED
|
@@ -2,7 +2,8 @@ import { cosmiconfig } from 'cosmiconfig';
|
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import { homedir } from 'os';
|
|
5
|
-
import { mkdirSync, writeFileSync, readFileSync } from 'fs';
|
|
5
|
+
import { chmodSync, mkdirSync, renameSync, rmSync, writeFileSync, readFileSync } from 'fs';
|
|
6
|
+
import { randomUUID } from 'node:crypto';
|
|
6
7
|
import 'dotenv/config';
|
|
7
8
|
export const ConfigSchema = z.object({
|
|
8
9
|
projectId: z.string().optional(),
|
|
@@ -18,6 +19,33 @@ const explorer = cosmiconfig('playcraft', {
|
|
|
18
19
|
});
|
|
19
20
|
const GLOBAL_CONFIG_DIR = path.join(homedir(), '.playcraft');
|
|
20
21
|
const GLOBAL_CONFIG_FILE = path.join(GLOBAL_CONFIG_DIR, 'config.json');
|
|
22
|
+
export function normalizeEnvironmentBaseUrl(raw) {
|
|
23
|
+
try {
|
|
24
|
+
const url = new URL(raw);
|
|
25
|
+
if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password)
|
|
26
|
+
return undefined;
|
|
27
|
+
url.hash = '';
|
|
28
|
+
url.search = '';
|
|
29
|
+
url.pathname = url.pathname.replace(/\/+$/, '').replace(/\/api$/, '');
|
|
30
|
+
return url.toString().replace(/\/+$/, '');
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export function readEnvironmentProfile(config, baseUrl) {
|
|
37
|
+
const key = normalizeEnvironmentBaseUrl(baseUrl);
|
|
38
|
+
const profiles = config.environmentProfiles;
|
|
39
|
+
if (!key || !profiles || typeof profiles !== 'object' || Array.isArray(profiles))
|
|
40
|
+
return undefined;
|
|
41
|
+
const profile = profiles[key];
|
|
42
|
+
if (!profile || typeof profile !== 'object' || Array.isArray(profile))
|
|
43
|
+
return undefined;
|
|
44
|
+
const value = profile;
|
|
45
|
+
if (typeof value.token !== 'string' || !value.token || normalizeEnvironmentBaseUrl(value.backendUrl) !== key)
|
|
46
|
+
return undefined;
|
|
47
|
+
return value;
|
|
48
|
+
}
|
|
21
49
|
export function loadGlobalConfig() {
|
|
22
50
|
try {
|
|
23
51
|
const content = readFileSync(GLOBAL_CONFIG_FILE, 'utf-8');
|
|
@@ -28,7 +56,8 @@ export function loadGlobalConfig() {
|
|
|
28
56
|
}
|
|
29
57
|
}
|
|
30
58
|
export function saveGlobalConfig(partial) {
|
|
31
|
-
mkdirSync(GLOBAL_CONFIG_DIR, { recursive: true });
|
|
59
|
+
mkdirSync(GLOBAL_CONFIG_DIR, { recursive: true, mode: 0o700 });
|
|
60
|
+
chmodSync(GLOBAL_CONFIG_DIR, 0o700);
|
|
32
61
|
let existing = {};
|
|
33
62
|
try {
|
|
34
63
|
existing = JSON.parse(readFileSync(GLOBAL_CONFIG_FILE, 'utf-8'));
|
|
@@ -37,7 +66,25 @@ export function saveGlobalConfig(partial) {
|
|
|
37
66
|
// ignore
|
|
38
67
|
}
|
|
39
68
|
const merged = { ...existing, ...partial };
|
|
40
|
-
|
|
69
|
+
const temporary = path.join(GLOBAL_CONFIG_DIR, `.config-${randomUUID()}.tmp`);
|
|
70
|
+
try {
|
|
71
|
+
writeFileSync(temporary, JSON.stringify(merged, null, 2), { encoding: 'utf-8', flag: 'wx', mode: 0o600 });
|
|
72
|
+
renameSync(temporary, GLOBAL_CONFIG_FILE);
|
|
73
|
+
}
|
|
74
|
+
finally {
|
|
75
|
+
rmSync(temporary, { force: true });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
export function saveEnvironmentProfile(baseUrl, profile) {
|
|
79
|
+
const key = normalizeEnvironmentBaseUrl(baseUrl);
|
|
80
|
+
if (!key)
|
|
81
|
+
throw new Error('Invalid environment address');
|
|
82
|
+
const current = loadGlobalConfig();
|
|
83
|
+
const profiles = current.environmentProfiles && typeof current.environmentProfiles === 'object'
|
|
84
|
+
&& !Array.isArray(current.environmentProfiles) ? current.environmentProfiles : {};
|
|
85
|
+
saveGlobalConfig({ environmentProfiles: { ...profiles,
|
|
86
|
+
[key]: { ...profile, backendUrl: key, updatedAt: new Date().toISOString() } },
|
|
87
|
+
token: profile.token, url: key, backendUrl: key });
|
|
41
88
|
}
|
|
42
89
|
export async function loadConfig(cliOptions) {
|
|
43
90
|
const result = await explorer.search(cliOptions.dir || process.cwd());
|
|
@@ -46,12 +93,20 @@ export async function loadConfig(cliOptions) {
|
|
|
46
93
|
? (fileConfig.agent || {})
|
|
47
94
|
: fileConfig;
|
|
48
95
|
const globalConfig = loadGlobalConfig();
|
|
96
|
+
const explicitTarget = cliOptions.url || process.env.PLAYCRAFT_URL || agentConfig.url;
|
|
97
|
+
const targetUrl = explicitTarget || globalConfig.url;
|
|
98
|
+
const profile = typeof targetUrl === 'string' ? readEnvironmentProfile(globalConfig, targetUrl) : undefined;
|
|
99
|
+
const legacyTarget = typeof globalConfig.backendUrl === 'string' ? globalConfig.backendUrl
|
|
100
|
+
: typeof globalConfig.url === 'string' ? globalConfig.url : undefined;
|
|
101
|
+
const legacyToken = !explicitTarget || (legacyTarget && normalizeEnvironmentBaseUrl(String(targetUrl)) === normalizeEnvironmentBaseUrl(legacyTarget))
|
|
102
|
+
? globalConfig.token : undefined;
|
|
49
103
|
const config = ConfigSchema.parse({
|
|
50
104
|
...globalConfig,
|
|
51
105
|
...agentConfig,
|
|
52
106
|
...cliOptions,
|
|
53
107
|
projectId: cliOptions.projectId || process.env.PLAYCRAFT_PROJECT_ID || agentConfig.projectId || globalConfig.projectId,
|
|
54
|
-
|
|
108
|
+
url: targetUrl,
|
|
109
|
+
token: cliOptions.token || process.env.PLAYCRAFT_TOKEN || agentConfig.token || profile?.token || legacyToken,
|
|
55
110
|
port: cliOptions.port || (process.env.PLAYCRAFT_PORT ? parseInt(process.env.PLAYCRAFT_PORT) : undefined) || agentConfig.port || globalConfig.port,
|
|
56
111
|
mode: cliOptions.mode || process.env.PLAYCRAFT_MODE || agentConfig.mode || globalConfig.mode,
|
|
57
112
|
skillsDir: cliOptions.skillsDir || process.env.AGENT_SKILLS_DIR || agentConfig.skillsDir || globalConfig.skillsDir,
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { Command } from 'commander';
|
|
2
|
+
import { Command, CommanderError } from 'commander';
|
|
3
|
+
import { accountActivationMessage } from './auth/messages.js';
|
|
3
4
|
import { readFileSync } from 'fs';
|
|
4
5
|
import { fileURLToPath } from 'url';
|
|
5
6
|
import { dirname, join } from 'path';
|
|
@@ -27,16 +28,23 @@ import { registerLevelsCommands } from './commands/levels.js';
|
|
|
27
28
|
import { registerPatternsCommands } from './commands/patterns.js';
|
|
28
29
|
import { registerPradCommands } from './commands/prad.js';
|
|
29
30
|
import { register3DCommands } from './commands/3d.js';
|
|
31
|
+
import { registerWorkspaceRuntimeCommands } from './commands/workspace-runtime.js';
|
|
32
|
+
import { registerVideoCommands } from './commands/video.js';
|
|
33
|
+
import { registerPlayableCommands } from './commands/playable.js';
|
|
34
|
+
import { videoMessage } from './video-replication/messages.js';
|
|
30
35
|
import { CLI_ROOT_DESCRIPTION, getCliTopicsHelpText, registerRootProgramHelp, } from './cli-root-help.js';
|
|
31
36
|
const __filename = fileURLToPath(import.meta.url);
|
|
32
37
|
const __dirname = dirname(__filename);
|
|
33
38
|
const packageJson = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'));
|
|
34
39
|
const program = new Command();
|
|
40
|
+
const structuredVideoTopic = ['video', 'playable'].includes(process.argv[2] ?? '');
|
|
41
|
+
if (structuredVideoTopic)
|
|
42
|
+
program.exitOverride();
|
|
35
43
|
program.name('playcraft').description(CLI_ROOT_DESCRIPTION).version(packageJson.version);
|
|
36
44
|
registerRootProgramHelp(program);
|
|
37
45
|
// 版本检查(非阻塞,后台执行)
|
|
38
46
|
// 只在非 upgrade 命令时检查,避免重复提示
|
|
39
|
-
if (!process.argv.includes('upgrade')) {
|
|
47
|
+
if (!structuredVideoTopic && !process.argv.includes('upgrade') && !process.argv.includes('workspace-runtime')) {
|
|
40
48
|
checkForUpdates(packageJson);
|
|
41
49
|
}
|
|
42
50
|
// create 命令
|
|
@@ -65,6 +73,8 @@ program
|
|
|
65
73
|
.command('login')
|
|
66
74
|
.description('登录 PlayCraft 账号并获取 CLI token')
|
|
67
75
|
.option('--url <url>', 'Portal / API 基址(默认 https://playcraft.aix.intlgame.com,可用环境变量 PLAYCRAFT_URL 覆盖)')
|
|
76
|
+
.option('--activate', accountActivationMessage('activateOption'))
|
|
77
|
+
.option('--token <token>', accountActivationMessage('tokenOption'))
|
|
68
78
|
.action(async (options) => {
|
|
69
79
|
const { loginCommand } = await import('./commands/login.js');
|
|
70
80
|
await loginCommand(options);
|
|
@@ -368,4 +378,25 @@ registerPatternsCommands(program);
|
|
|
368
378
|
registerPradCommands(program);
|
|
369
379
|
// 3D model utilities (playcraft 3d <command>)
|
|
370
380
|
register3DCommands(program);
|
|
371
|
-
program
|
|
381
|
+
registerWorkspaceRuntimeCommands(program);
|
|
382
|
+
registerVideoCommands(program);
|
|
383
|
+
registerPlayableCommands(program);
|
|
384
|
+
if (structuredVideoTopic) {
|
|
385
|
+
try {
|
|
386
|
+
await program.parseAsync(process.argv);
|
|
387
|
+
}
|
|
388
|
+
catch (error) {
|
|
389
|
+
if (!(error instanceof CommanderError))
|
|
390
|
+
throw error;
|
|
391
|
+
if (error.exitCode === 0)
|
|
392
|
+
process.exitCode = 0;
|
|
393
|
+
else {
|
|
394
|
+
const command = [process.argv[2], process.argv[3]?.startsWith('-') ? undefined : process.argv[3]].filter(Boolean).join(' ');
|
|
395
|
+
process.stdout.write(`${JSON.stringify({ schemaVersion: 'playcraft.video-cli.v1', command, state: 'failed', ok: false,
|
|
396
|
+
error: { code: 'invalidArguments', message: videoMessage('invalidArguments') } })}\n`);
|
|
397
|
+
process.exitCode = 64;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
else
|
|
402
|
+
program.parse(process.argv);
|