@playcraft/cli 0.1.68 → 0.1.69
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/dist/asset-library/provenance.js +169 -0
- package/dist/commands/library-messages.js +27 -9
- package/dist/commands/library.js +62 -10
- package/dist/model-adaptation/project-template.js +56 -3
- package/dist/model-adaptation/project.js +15 -1
- package/dist/workspace-runtime/batch/freeze.js +15 -2
- package/dist/workspace-runtime/batch/git.js +16 -4
- package/dist/workspace-runtime/batch/publication.js +96 -0
- package/dist/workspace-runtime/batch/source-candidate.js +2 -1
- package/dist/workspace-runtime/batch/source-publish.js +26 -15
- package/dist/workspace-runtime/persistence/sqlite-worker.js +2 -1
- package/dist/workspace-runtime/serve.js +50 -18
- package/dist/workspace-runtime/server/dispatch.js +42 -9
- package/dist/workspace-runtime/server/http.js +12 -2
- package/dist/workspace-runtime/verify-artifact.js +2 -1
- package/dist/workspace-runtime/workspaces/archive.js +28 -3
- package/dist/workspace-runtime/workspaces/asset-library-import.js +33 -2
- package/dist/workspace-runtime/workspaces/asset-provenance-verifier.js +36 -0
- package/dist/workspace-runtime/workspaces/registry.js +3 -5
- package/dist/workspace-runtime/workspaces/remote-attach-marker.js +55 -0
- package/package.json +3 -3
- package/project-template/.claude/skills/playcraft-workspace-assets/SKILL.md +15 -4
- package/project-template-v2/.claude/skills/playcraft-workspace-assets/SKILL.md +15 -4
- package/project-template-v2/.playcraft/skills.lock.json +2 -2
- package/project-templates/phaser/.claude/skills/playcraft-workspace-assets/SKILL.md +15 -4
- package/project-templates/threejs/.claude/skills/playcraft-workspace-assets/SKILL.md +15 -4
|
@@ -6,6 +6,7 @@ import { BINARY_WORKSPACE_FILE_MAX_BYTES, WORKSPACE_LIBRARY_ATTACHMENT_MAX_BYTES
|
|
|
6
6
|
import { observeWorkspaceBinaryFile, writeWorkspaceBinaryFile } from './binary-write.js';
|
|
7
7
|
import { shareWorkspaceAssetDirectory } from './asset-publication-access.js';
|
|
8
8
|
import { ensureSharedDirectory } from './shared-write.js';
|
|
9
|
+
import { publishLibraryModels } from '../../asset-library/provenance.js';
|
|
9
10
|
import { isEditableAudioLibraryAsset, prepareAudioLibraryImport } from './audio-assets/library-import.js';
|
|
10
11
|
import { readAudioArtifactPackage } from './audio-assets/artifact-package.js';
|
|
11
12
|
const RESERVED = new Set(['.git', 'node_modules', '.playcraft-temp', '.tmp']);
|
|
@@ -341,6 +342,8 @@ export async function importWorkspaceLibraryAssets(root, selection, snapshot, as
|
|
|
341
342
|
await createPublicDirectory(rootReal, payload.targetDirectory);
|
|
342
343
|
const result = { paths: [], skippedPaths: [], failures: [...transport.failures] };
|
|
343
344
|
const processed = new Set(transport.failures.map(failure => failure.assetId));
|
|
345
|
+
const curated = [];
|
|
346
|
+
const plannedTargets = new Set();
|
|
344
347
|
const progress = () => options.onProgress?.({ ...result, failures: [...result.failures,
|
|
345
348
|
...payload.assetIds.filter(id => !processed.has(id)).map(assetId => ({ assetId, error: 'asset_library_import_interrupted' }))] });
|
|
346
349
|
await progress();
|
|
@@ -361,7 +364,7 @@ export async function importWorkspaceLibraryAssets(root, selection, snapshot, as
|
|
|
361
364
|
let name = originalName;
|
|
362
365
|
let target = validPath([payload.targetDirectory, name].filter(Boolean).join('/'));
|
|
363
366
|
let existing = await statTarget(rootReal, target);
|
|
364
|
-
if (existing && payload.collision === 'skip') {
|
|
367
|
+
if ((existing || plannedTargets.has(target)) && payload.collision === 'skip') {
|
|
365
368
|
result.skippedPaths.push(target);
|
|
366
369
|
processed.add(assetId);
|
|
367
370
|
await progress();
|
|
@@ -370,7 +373,7 @@ export async function importWorkspaceLibraryAssets(root, selection, snapshot, as
|
|
|
370
373
|
if (payload.collision === 'rename') {
|
|
371
374
|
const extension = asset.kind === 'bundle' ? '' : extname(originalName);
|
|
372
375
|
const stem = originalName.slice(0, originalName.length - extension.length);
|
|
373
|
-
for (let suffix = 1; existing; suffix++) {
|
|
376
|
+
for (let suffix = 1; existing || plannedTargets.has(target); suffix++) {
|
|
374
377
|
if (suffix > 10_000)
|
|
375
378
|
throw new AssetLibraryImportError('content_conflict');
|
|
376
379
|
name = `${stem} (${suffix})${extension}`;
|
|
@@ -378,6 +381,14 @@ export async function importWorkspaceLibraryAssets(root, selection, snapshot, as
|
|
|
378
381
|
existing = await statTarget(rootReal, target);
|
|
379
382
|
}
|
|
380
383
|
}
|
|
384
|
+
if (asset.provenance) {
|
|
385
|
+
const observed = existing ? await observeWorkspaceBinaryFile(rootReal, target) : undefined;
|
|
386
|
+
curated.push({ assetId, path: target, bytes: Buffer.from(asset.files[0].content, 'base64'),
|
|
387
|
+
provenance: asset.provenance, expectedContentHash: observed?.contentHash ?? null });
|
|
388
|
+
plannedTargets.add(target);
|
|
389
|
+
await progress();
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
381
392
|
if (asset.kind === 'bundle') {
|
|
382
393
|
const audio = isEditableAudioLibraryAsset(asset) ? await prepareAudioLibraryImport(rootReal, target, asset, assertCurrent) : undefined;
|
|
383
394
|
const incoming = audio?.asset ?? asset;
|
|
@@ -396,6 +407,8 @@ export async function importWorkspaceLibraryAssets(root, selection, snapshot, as
|
|
|
396
407
|
}
|
|
397
408
|
}
|
|
398
409
|
catch (error) {
|
|
410
|
+
if (error instanceof Error && ['stale_lease', 'batch_workspace_readonly'].includes(error.message))
|
|
411
|
+
throw error;
|
|
399
412
|
const code = error instanceof AssetLibraryImportError ? error.message
|
|
400
413
|
: error instanceof Error && ['invalid_audio_library_package', 'audio_library_engine_unavailable', 'audio_library_source_unverified', 'audio_representation_conflict'].includes(error.message) ? error.message
|
|
401
414
|
: error?.code === 'content_conflict' ? 'content_conflict'
|
|
@@ -408,5 +421,23 @@ export async function importWorkspaceLibraryAssets(root, selection, snapshot, as
|
|
|
408
421
|
if (publishedTransaction)
|
|
409
422
|
await removeTransaction(rootReal, publishedTransaction).catch(() => undefined);
|
|
410
423
|
}
|
|
424
|
+
if (curated.length) {
|
|
425
|
+
try {
|
|
426
|
+
const receipt = await publishLibraryModels(rootReal, curated, { operationId: options.operationId,
|
|
427
|
+
scope: options.scope, assertCurrent });
|
|
428
|
+
result.bundleReceipt = receipt;
|
|
429
|
+
result.paths.push(...receipt.files.map(file => file.path));
|
|
430
|
+
}
|
|
431
|
+
catch (error) {
|
|
432
|
+
if (error instanceof Error && ['stale_lease', 'batch_workspace_readonly'].includes(error.message))
|
|
433
|
+
throw error;
|
|
434
|
+
const code = error.code ?? (error instanceof Error ? error.message : 'asset_library_import_failed');
|
|
435
|
+
for (const model of curated)
|
|
436
|
+
result.failures.push({ assetId: model.assetId, error: code.slice(0, 256) });
|
|
437
|
+
}
|
|
438
|
+
for (const model of curated)
|
|
439
|
+
processed.add(model.assetId);
|
|
440
|
+
await progress();
|
|
441
|
+
}
|
|
411
442
|
return workspaceAssetLibraryImportResultSchema.parse(result);
|
|
412
443
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { assetLibraryModelProvenanceSchema, canonicalAssetLibraryModelProvenance } from '@playcraft/common/cloud-workspace/runtime';
|
|
4
|
+
import { businessEndpoint } from '../../utils/environment-endpoints.js';
|
|
5
|
+
import { ensureCliCredentials } from './cli-credentials.js';
|
|
6
|
+
/** Compare the portable source record with the current user's immutable server revision. */
|
|
7
|
+
export async function verifyRuntimeAssetProvenance(state, provenance) {
|
|
8
|
+
await ensureCliCredentials(state);
|
|
9
|
+
const credential = JSON.parse(await readFile(join(state.config.workspaceRoot, '.playcraft.json'), 'utf8'));
|
|
10
|
+
if (typeof credential.apiUrl !== 'string' || typeof credential.token !== 'string' || !credential.token)
|
|
11
|
+
throw new Error('asset_provenance_authority_unavailable');
|
|
12
|
+
const url = businessEndpoint(credential.apiUrl, `/api/agent/tools/asset-library/${encodeURIComponent(provenance.assetId)}`);
|
|
13
|
+
url.searchParams.set('revisionId', provenance.revisionId);
|
|
14
|
+
const response = await fetch(url, { redirect: 'error', signal: AbortSignal.timeout(15_000),
|
|
15
|
+
headers: { 'X-Sandbox-Token': credential.token, 'playcraft-env': state.config.environment } });
|
|
16
|
+
if (!response.ok) {
|
|
17
|
+
await response.body?.cancel();
|
|
18
|
+
throw new Error('asset_provenance_authority_unavailable');
|
|
19
|
+
}
|
|
20
|
+
const maximum = 256 * 1024, chunks = [];
|
|
21
|
+
let length = 0;
|
|
22
|
+
if (!response.body)
|
|
23
|
+
throw new Error('asset_provenance_authority_unavailable');
|
|
24
|
+
for await (const chunk of response.body) {
|
|
25
|
+
length += chunk.byteLength;
|
|
26
|
+
if (length > maximum) {
|
|
27
|
+
await response.body.cancel().catch(() => undefined);
|
|
28
|
+
throw new Error('asset_provenance_authority_response_bound');
|
|
29
|
+
}
|
|
30
|
+
chunks.push(chunk);
|
|
31
|
+
}
|
|
32
|
+
const result = JSON.parse(Buffer.concat(chunks).toString('utf8'));
|
|
33
|
+
const authoritative = assetLibraryModelProvenanceSchema.parse(result.provenance);
|
|
34
|
+
if (canonicalAssetLibraryModelProvenance(authoritative) !== canonicalAssetLibraryModelProvenance(provenance))
|
|
35
|
+
throw new Error('asset_provenance_authority_mismatch');
|
|
36
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { execFile } from 'node:child_process';
|
|
2
|
-
import { lstat, mkdir, realpath, rm
|
|
2
|
+
import { lstat, mkdir, realpath, rm } from 'node:fs/promises';
|
|
3
3
|
import { dirname, join, relative } from 'node:path';
|
|
4
4
|
import { promisify } from 'node:util';
|
|
5
5
|
import { isLocalWorkspaceGitAuthMode, sameWorkspaceRegistration, workspaceRegistrationSchema, batchSourceSnapshotSchema, } from '@playcraft/common/cloud-workspace/runtime';
|
|
@@ -10,6 +10,7 @@ import { ensureWorkspaceSkills, readWorkspaceSkills } from './skills.js';
|
|
|
10
10
|
import { loadWorkspaceSkillArtifact, workspacePlatformSkillAccess } from './skill-artifact-loader.js';
|
|
11
11
|
import { writeWorkspaceCliContext } from './cli-context.js';
|
|
12
12
|
import { attachWorkspaceRemote } from './git.js';
|
|
13
|
+
import { markRemoteAttachPending } from './remote-attach-marker.js';
|
|
13
14
|
import { ensureWorkspaceArtifactInstructions } from './instructions.js';
|
|
14
15
|
import { repairWorkspaceShares } from './workspace-shares.js';
|
|
15
16
|
export { WorkspaceContextError } from './context-error.js';
|
|
@@ -183,10 +184,7 @@ export async function registerWorkspace(state, input, leaseFence, preparation, r
|
|
|
183
184
|
&& (needsRemote || remoteUpgrade || await lstat(attachMarker).then(() => true, () => false))) {
|
|
184
185
|
if (preparation?.mode !== 'clone' || !state.gitCredentials)
|
|
185
186
|
throw new WorkspaceContextError('workspace_git_gateway_unavailable');
|
|
186
|
-
|
|
187
|
-
await mkdir(join(state.config.stateDir, 'workspace-remote-attach'), { recursive: true, mode: 0o700 });
|
|
188
|
-
// private
|
|
189
|
-
await writeFile(attachMarker, JSON.stringify({ repositoryUrl: binding.repositoryUrl, branchName: binding.branchName }), { mode: 0o600 });
|
|
187
|
+
await markRemoteAttachPending(state, binding.workspaceId, binding.repositoryUrl, binding.branchName);
|
|
190
188
|
// The credential helper only serves a workspace the journal knows to be bound.
|
|
191
189
|
await state.journal.putWorkspace({ ...binding, directories });
|
|
192
190
|
try {
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { link, mkdir, open, readFile, unlink } from 'node:fs/promises';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
function markerPath(state, workspaceId) {
|
|
5
|
+
return join(state.config.stateDir, 'workspace-remote-attach', `${workspaceId}.json`);
|
|
6
|
+
}
|
|
7
|
+
/** Caller holds the workspace journal lock. Never replace a different unfinished attach. */
|
|
8
|
+
export async function markRemoteAttachPending(state, workspaceId, repositoryUrl, branchName) {
|
|
9
|
+
const path = markerPath(state, workspaceId);
|
|
10
|
+
const directory = join(state.config.stateDir, 'workspace-remote-attach');
|
|
11
|
+
// private: the attach marker is controller state outside the shared workspace tree.
|
|
12
|
+
await mkdir(directory, { recursive: true, mode: 0o700 });
|
|
13
|
+
const expected = { repositoryUrl, branchName };
|
|
14
|
+
const temporary = `${path}.${randomUUID()}.tmp`;
|
|
15
|
+
// private: only the controller reads an unfinished attach back.
|
|
16
|
+
const handle = await open(temporary, 'wx', 0o600);
|
|
17
|
+
try {
|
|
18
|
+
await handle.writeFile(JSON.stringify(expected));
|
|
19
|
+
await handle.sync();
|
|
20
|
+
await handle.close();
|
|
21
|
+
try {
|
|
22
|
+
await link(temporary, path);
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
if (error.code !== 'EEXIST')
|
|
26
|
+
throw error;
|
|
27
|
+
const previous = JSON.parse(await readFile(path, 'utf8'));
|
|
28
|
+
if (previous.repositoryUrl !== repositoryUrl || previous.branchName !== branchName)
|
|
29
|
+
throw new Error('batch_source_identity_mismatch');
|
|
30
|
+
}
|
|
31
|
+
const folder = await open(directory, 'r');
|
|
32
|
+
try {
|
|
33
|
+
await folder.sync();
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
await folder.close();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
finally {
|
|
40
|
+
await handle.close();
|
|
41
|
+
await unlink(temporary).catch(() => undefined);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/** Caller holds the workspace journal lock to serialize with attach and marker removal. */
|
|
45
|
+
export async function remoteAttachPending(state, workspaceId) {
|
|
46
|
+
try {
|
|
47
|
+
await readFile(markerPath(state, workspaceId));
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
if (error.code === 'ENOENT')
|
|
52
|
+
return false;
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@playcraft/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.69",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -56,8 +56,8 @@
|
|
|
56
56
|
"@gltf-transform/core": "^4.3.0",
|
|
57
57
|
"@gltf-transform/extensions": "^4.3.0",
|
|
58
58
|
"@gltf-transform/functions": "^4.3.0",
|
|
59
|
-
"@playcraft/build": "0.1.
|
|
60
|
-
"@playcraft/common": "0.1.
|
|
59
|
+
"@playcraft/build": "0.1.69",
|
|
60
|
+
"@playcraft/common": "0.1.69",
|
|
61
61
|
"better-sqlite3-pinned": "npm:better-sqlite3@13.0.3",
|
|
62
62
|
"chokidar": "^4.0.3",
|
|
63
63
|
"commander": "^13.1.0",
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: playcraft-workspace-assets
|
|
3
|
-
description: Use when finding or downloading shared library assets, or
|
|
3
|
+
description: Use when selecting 3D asset kits, finding or downloading shared library assets, or managing durable media generation tasks in a PlayCraft cloud workspace.
|
|
4
4
|
compatibility: agent,opencode
|
|
5
|
-
version: 1.
|
|
5
|
+
version: 1.1.0
|
|
6
6
|
surfaces:
|
|
7
7
|
- sandbox-agent
|
|
8
8
|
---
|
|
@@ -13,6 +13,17 @@ surfaces:
|
|
|
13
13
|
|
|
14
14
|
## 查找并复用素材
|
|
15
15
|
|
|
16
|
+
一组三维物件的检索入口为素材包;包内覆盖结果给出逐项可用模型及缺口:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
playcraft library kits --search "家具" --json
|
|
20
|
+
playcraft library kit kenney-furniture-kit --needs "椅子,桌子,打印机" --json
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
素材包是主题与风格的逻辑集合,区别于一起下载的文件集合及版权来源归档。`coverageScope: partial` 表示仅部分收录;`members` 才是当前可用模型及确切版本,`needCoverage` 返回同包每项需求的候选或缺口。`inventoryRevision` 只标识当前库存,不是持久保存的历史整包版本。`styleGuidance` 提供设计参照,`compatibility` 未验证的单位、朝向和连接规则不能用于兼容性承诺;生成供应商不会自动匹配该风格。
|
|
24
|
+
|
|
25
|
+
包内模型和独立素材都使用以下单素材入口。模型下载加 `--revision-id <revisionId>`,保留命令生成的 `.playcraft/asset-provenance.json`、许可和来源说明;工程文件路径、模型版本与来源记录共同标识实际使用实例。
|
|
26
|
+
|
|
16
27
|
```bash
|
|
17
28
|
playcraft library search "角色" --type texture --tags character --limit 20 --json
|
|
18
29
|
playcraft library facets --json
|
|
@@ -21,9 +32,9 @@ playcraft library files <assetId> --json
|
|
|
21
32
|
playcraft library download <assetId> --output ./assets/character
|
|
22
33
|
```
|
|
23
34
|
|
|
24
|
-
|
|
35
|
+
检索字段是名称、素材库路径和标签,已审核模型还支持别名、用途、许可及能力筛选;也可按分类、子分类、团队和分页筛选。使用检索结果返回的素材库编号,与 `tools list-assets` 的项目资产编号分别处理。预览图不能代替模型或其依赖。
|
|
25
36
|
|
|
26
|
-
`--output`
|
|
37
|
+
`--output` 对单文件表示文件路径,对文件集合表示目录;`--output-dir` 始终表示目录。目标路径必须尚不存在。文件集合包含清单、嵌套目录和模型贴图等依赖,下载后保留其相对路径。用户要求修改已有素材时,先下载到新路径并核对,再按用户要求替换。
|
|
27
38
|
|
|
28
39
|
## 创建持久化生成任务
|
|
29
40
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: playcraft-workspace-assets
|
|
3
|
-
description: Use when finding or downloading shared library assets, or
|
|
3
|
+
description: Use when selecting 3D asset kits, finding or downloading shared library assets, or managing durable media generation tasks in a PlayCraft cloud workspace.
|
|
4
4
|
compatibility: agent,opencode
|
|
5
|
-
version: 1.
|
|
5
|
+
version: 1.1.0
|
|
6
6
|
surfaces:
|
|
7
7
|
- sandbox-agent
|
|
8
8
|
---
|
|
@@ -13,6 +13,17 @@ surfaces:
|
|
|
13
13
|
|
|
14
14
|
## 查找并复用素材
|
|
15
15
|
|
|
16
|
+
一组三维物件的检索入口为素材包;包内覆盖结果给出逐项可用模型及缺口:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
playcraft library kits --search "家具" --json
|
|
20
|
+
playcraft library kit kenney-furniture-kit --needs "椅子,桌子,打印机" --json
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
素材包是主题与风格的逻辑集合,区别于一起下载的文件集合及版权来源归档。`coverageScope: partial` 表示仅部分收录;`members` 才是当前可用模型及确切版本,`needCoverage` 返回同包每项需求的候选或缺口。`inventoryRevision` 只标识当前库存,不是持久保存的历史整包版本。`styleGuidance` 提供设计参照,`compatibility` 未验证的单位、朝向和连接规则不能用于兼容性承诺;生成供应商不会自动匹配该风格。
|
|
24
|
+
|
|
25
|
+
包内模型和独立素材都使用以下单素材入口。模型下载加 `--revision-id <revisionId>`,保留命令生成的 `.playcraft/asset-provenance.json`、许可和来源说明;工程文件路径、模型版本与来源记录共同标识实际使用实例。
|
|
26
|
+
|
|
16
27
|
```bash
|
|
17
28
|
playcraft library search "角色" --type texture --tags character --limit 20 --json
|
|
18
29
|
playcraft library facets --json
|
|
@@ -21,9 +32,9 @@ playcraft library files <assetId> --json
|
|
|
21
32
|
playcraft library download <assetId> --output ./assets/character
|
|
22
33
|
```
|
|
23
34
|
|
|
24
|
-
|
|
35
|
+
检索字段是名称、素材库路径和标签,已审核模型还支持别名、用途、许可及能力筛选;也可按分类、子分类、团队和分页筛选。使用检索结果返回的素材库编号,与 `tools list-assets` 的项目资产编号分别处理。预览图不能代替模型或其依赖。
|
|
25
36
|
|
|
26
|
-
`--output`
|
|
37
|
+
`--output` 对单文件表示文件路径,对文件集合表示目录;`--output-dir` 始终表示目录。目标路径必须尚不存在。文件集合包含清单、嵌套目录和模型贴图等依赖,下载后保留其相对路径。用户要求修改已有素材时,先下载到新路径并核对,再按用户要求替换。
|
|
27
38
|
|
|
28
39
|
## 创建持久化生成任务
|
|
29
40
|
|
|
@@ -171,8 +171,8 @@
|
|
|
171
171
|
},
|
|
172
172
|
{
|
|
173
173
|
"skillRef": "remix/playcraft-workspace-assets",
|
|
174
|
-
"version": "1.
|
|
175
|
-
"artifactHash": "
|
|
174
|
+
"version": "1.1.0",
|
|
175
|
+
"artifactHash": "bb8d3c17953e85e006f5de75bd267d29ea9cef329298d169ba57e11f59f4f21e",
|
|
176
176
|
"surface": "sandbox-agent",
|
|
177
177
|
"mountPath": ".playcraft/skills/remix__playcraft-workspace-assets"
|
|
178
178
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: playcraft-workspace-assets
|
|
3
|
-
description: Use when finding or downloading shared library assets, or
|
|
3
|
+
description: Use when selecting 3D asset kits, finding or downloading shared library assets, or managing durable media generation tasks in a PlayCraft cloud workspace.
|
|
4
4
|
compatibility: agent,opencode
|
|
5
|
-
version: 1.
|
|
5
|
+
version: 1.1.0
|
|
6
6
|
surfaces:
|
|
7
7
|
- sandbox-agent
|
|
8
8
|
---
|
|
@@ -13,6 +13,17 @@ surfaces:
|
|
|
13
13
|
|
|
14
14
|
## 查找并复用素材
|
|
15
15
|
|
|
16
|
+
一组三维物件的检索入口为素材包;包内覆盖结果给出逐项可用模型及缺口:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
playcraft library kits --search "家具" --json
|
|
20
|
+
playcraft library kit kenney-furniture-kit --needs "椅子,桌子,打印机" --json
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
素材包是主题与风格的逻辑集合,区别于一起下载的文件集合及版权来源归档。`coverageScope: partial` 表示仅部分收录;`members` 才是当前可用模型及确切版本,`needCoverage` 返回同包每项需求的候选或缺口。`inventoryRevision` 只标识当前库存,不是持久保存的历史整包版本。`styleGuidance` 提供设计参照,`compatibility` 未验证的单位、朝向和连接规则不能用于兼容性承诺;生成供应商不会自动匹配该风格。
|
|
24
|
+
|
|
25
|
+
包内模型和独立素材都使用以下单素材入口。模型下载加 `--revision-id <revisionId>`,保留命令生成的 `.playcraft/asset-provenance.json`、许可和来源说明;工程文件路径、模型版本与来源记录共同标识实际使用实例。
|
|
26
|
+
|
|
16
27
|
```bash
|
|
17
28
|
playcraft library search "角色" --type texture --tags character --limit 20 --json
|
|
18
29
|
playcraft library facets --json
|
|
@@ -21,9 +32,9 @@ playcraft library files <assetId> --json
|
|
|
21
32
|
playcraft library download <assetId> --output ./assets/character
|
|
22
33
|
```
|
|
23
34
|
|
|
24
|
-
|
|
35
|
+
检索字段是名称、素材库路径和标签,已审核模型还支持别名、用途、许可及能力筛选;也可按分类、子分类、团队和分页筛选。使用检索结果返回的素材库编号,与 `tools list-assets` 的项目资产编号分别处理。预览图不能代替模型或其依赖。
|
|
25
36
|
|
|
26
|
-
`--output`
|
|
37
|
+
`--output` 对单文件表示文件路径,对文件集合表示目录;`--output-dir` 始终表示目录。目标路径必须尚不存在。文件集合包含清单、嵌套目录和模型贴图等依赖,下载后保留其相对路径。用户要求修改已有素材时,先下载到新路径并核对,再按用户要求替换。
|
|
27
38
|
|
|
28
39
|
## 创建持久化生成任务
|
|
29
40
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: playcraft-workspace-assets
|
|
3
|
-
description: Use when finding or downloading shared library assets, or
|
|
3
|
+
description: Use when selecting 3D asset kits, finding or downloading shared library assets, or managing durable media generation tasks in a PlayCraft cloud workspace.
|
|
4
4
|
compatibility: agent,opencode
|
|
5
|
-
version: 1.
|
|
5
|
+
version: 1.1.0
|
|
6
6
|
surfaces:
|
|
7
7
|
- sandbox-agent
|
|
8
8
|
---
|
|
@@ -13,6 +13,17 @@ surfaces:
|
|
|
13
13
|
|
|
14
14
|
## 查找并复用素材
|
|
15
15
|
|
|
16
|
+
一组三维物件的检索入口为素材包;包内覆盖结果给出逐项可用模型及缺口:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
playcraft library kits --search "家具" --json
|
|
20
|
+
playcraft library kit kenney-furniture-kit --needs "椅子,桌子,打印机" --json
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
素材包是主题与风格的逻辑集合,区别于一起下载的文件集合及版权来源归档。`coverageScope: partial` 表示仅部分收录;`members` 才是当前可用模型及确切版本,`needCoverage` 返回同包每项需求的候选或缺口。`inventoryRevision` 只标识当前库存,不是持久保存的历史整包版本。`styleGuidance` 提供设计参照,`compatibility` 未验证的单位、朝向和连接规则不能用于兼容性承诺;生成供应商不会自动匹配该风格。
|
|
24
|
+
|
|
25
|
+
包内模型和独立素材都使用以下单素材入口。模型下载加 `--revision-id <revisionId>`,保留命令生成的 `.playcraft/asset-provenance.json`、许可和来源说明;工程文件路径、模型版本与来源记录共同标识实际使用实例。
|
|
26
|
+
|
|
16
27
|
```bash
|
|
17
28
|
playcraft library search "角色" --type texture --tags character --limit 20 --json
|
|
18
29
|
playcraft library facets --json
|
|
@@ -21,9 +32,9 @@ playcraft library files <assetId> --json
|
|
|
21
32
|
playcraft library download <assetId> --output ./assets/character
|
|
22
33
|
```
|
|
23
34
|
|
|
24
|
-
|
|
35
|
+
检索字段是名称、素材库路径和标签,已审核模型还支持别名、用途、许可及能力筛选;也可按分类、子分类、团队和分页筛选。使用检索结果返回的素材库编号,与 `tools list-assets` 的项目资产编号分别处理。预览图不能代替模型或其依赖。
|
|
25
36
|
|
|
26
|
-
`--output`
|
|
37
|
+
`--output` 对单文件表示文件路径,对文件集合表示目录;`--output-dir` 始终表示目录。目标路径必须尚不存在。文件集合包含清单、嵌套目录和模型贴图等依赖,下载后保留其相对路径。用户要求修改已有素材时,先下载到新路径并核对,再按用户要求替换。
|
|
27
38
|
|
|
28
39
|
## 创建持久化生成任务
|
|
29
40
|
|