@drawcall/market 0.1.36 → 0.1.38
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 +4 -1
- package/dist/asset-implementation.d.ts +6 -0
- package/dist/asset-implementation.d.ts.map +1 -1
- package/dist/asset-implementation.js +12 -0
- package/dist/asset-implementation.js.map +1 -1
- package/dist/cli.js +10 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/list.d.ts +17 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +26 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/contract.d.ts +2 -0
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/output.d.ts +2 -0
- package/dist/output.d.ts.map +1 -1
- package/dist/output.js +23 -5
- package/dist/output.js.map +1 -1
- package/dist/skill.d.ts +1 -1
- package/dist/skill.d.ts.map +1 -1
- package/dist/skill.js +18 -9
- package/dist/skill.js.map +1 -1
- package/package.json +7 -2
- package/skills/market/SKILL.md +69 -0
- package/src/asset-implementation.ts +22 -0
- package/src/cli.ts +11 -0
- package/src/commands/list.ts +47 -0
- package/src/contract.ts +2 -0
- package/src/index.ts +7 -1
- package/src/output.ts +29 -5
- package/src/skill.ts +18 -9
- package/tests/install-command.test.ts +0 -67
- package/tests/install-layout.test.ts +0 -307
- package/tests/output.test.ts +0 -186
- package/tests/resolve-skills.test.ts +0 -87
- package/tests/upload-deps.test.ts +0 -63
- package/tsconfig.json +0 -9
package/tests/output.test.ts
DELETED
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict'
|
|
2
|
-
import test from 'node:test'
|
|
3
|
-
import { assetSearchResultLine, installResult } from '../src/output.js'
|
|
4
|
-
|
|
5
|
-
test('assetSearchResultLine prints the full description', () => {
|
|
6
|
-
const description =
|
|
7
|
-
'A detailed model description that is intentionally longer than the old one-line search cap, with material notes, scale guidance, and usage context.'
|
|
8
|
-
|
|
9
|
-
assert.equal(
|
|
10
|
-
assetSearchResultLine({
|
|
11
|
-
id: 'asset-1',
|
|
12
|
-
name: 'wooden-chair',
|
|
13
|
-
type: 'model',
|
|
14
|
-
description,
|
|
15
|
-
ownerId: 'user-1',
|
|
16
|
-
createdAt: new Date('2026-01-01T00:00:00.000Z'),
|
|
17
|
-
updatedAt: new Date('2026-01-01T00:00:00.000Z'),
|
|
18
|
-
latestVersion: '1.2.0',
|
|
19
|
-
approved: true,
|
|
20
|
-
npmDependencies: '{}',
|
|
21
|
-
assetDependencies: '{}',
|
|
22
|
-
skillDependencies: '{}',
|
|
23
|
-
previewUrl: null,
|
|
24
|
-
}),
|
|
25
|
-
`- wooden-chair@1.2.0 | model | approved | ${description}`,
|
|
26
|
-
)
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
test('installResult prints asset type, file tree, and npm list', () => {
|
|
30
|
-
assert.equal(
|
|
31
|
-
installResult({
|
|
32
|
-
assets: [
|
|
33
|
-
{
|
|
34
|
-
name: 'wooden-chair',
|
|
35
|
-
type: 'model',
|
|
36
|
-
version: '1.2.0',
|
|
37
|
-
description: null,
|
|
38
|
-
files: ['public/models/wooden-chair.glb'],
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
name: 'oak-material',
|
|
42
|
-
type: 'texture',
|
|
43
|
-
version: '1.0.0',
|
|
44
|
-
description: null,
|
|
45
|
-
files: ['public/textures/oak/basecolor.png', 'public/textures/oak/normal.png'],
|
|
46
|
-
},
|
|
47
|
-
],
|
|
48
|
-
npmDependencies: { '@react-three/drei': 'latest' },
|
|
49
|
-
skillDependencies: {},
|
|
50
|
-
warnings: [],
|
|
51
|
-
}),
|
|
52
|
-
`Installed:
|
|
53
|
-
- wooden-chair@1.2.0 (model)
|
|
54
|
-
files:
|
|
55
|
-
public
|
|
56
|
-
└─ models
|
|
57
|
-
└─ wooden-chair.glb
|
|
58
|
-
- oak-material@1.0.0 (texture)
|
|
59
|
-
files:
|
|
60
|
-
public
|
|
61
|
-
└─ textures
|
|
62
|
-
└─ oak
|
|
63
|
-
├─ basecolor.png
|
|
64
|
-
└─ normal.png
|
|
65
|
-
- @react-three/drei@latest (npm)`,
|
|
66
|
-
)
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
test('installResult attaches each post-install note under its asset', () => {
|
|
70
|
-
assert.equal(
|
|
71
|
-
installResult(
|
|
72
|
-
{
|
|
73
|
-
assets: [
|
|
74
|
-
{
|
|
75
|
-
name: 'wooden-chair',
|
|
76
|
-
type: 'model',
|
|
77
|
-
version: '1.2.0',
|
|
78
|
-
description: null,
|
|
79
|
-
files: [],
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
name: 'beep',
|
|
83
|
-
type: 'sound-effect',
|
|
84
|
-
version: '1.0.0',
|
|
85
|
-
description: null,
|
|
86
|
-
files: [],
|
|
87
|
-
},
|
|
88
|
-
],
|
|
89
|
-
npmDependencies: {},
|
|
90
|
-
skillDependencies: {},
|
|
91
|
-
warnings: [],
|
|
92
|
-
},
|
|
93
|
-
{
|
|
94
|
-
model: {
|
|
95
|
-
installMessage: 'Load the model.',
|
|
96
|
-
saveOnInstall: true,
|
|
97
|
-
readAssetDependenciesFromPackageJson: false,
|
|
98
|
-
omitUnchangedInstalledFilesOnUpload: false,
|
|
99
|
-
},
|
|
100
|
-
// sound-effect intentionally omitted -> no note for `beep`.
|
|
101
|
-
},
|
|
102
|
-
),
|
|
103
|
-
`Installed:
|
|
104
|
-
- wooden-chair@1.2.0 (model)
|
|
105
|
-
note: Load the model.
|
|
106
|
-
- beep@1.0.0 (sound-effect)`,
|
|
107
|
-
)
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
test('installResult word-wraps a long note with a hanging indent', () => {
|
|
111
|
-
const message = Array(20).fill('word').join(' ')
|
|
112
|
-
const output = installResult(
|
|
113
|
-
{
|
|
114
|
-
assets: [{ name: 'thing', type: 'model', version: '1.0.0', description: null, files: [] }],
|
|
115
|
-
npmDependencies: {},
|
|
116
|
-
skillDependencies: {},
|
|
117
|
-
warnings: [],
|
|
118
|
-
},
|
|
119
|
-
{
|
|
120
|
-
model: {
|
|
121
|
-
installMessage: message,
|
|
122
|
-
saveOnInstall: true,
|
|
123
|
-
readAssetDependenciesFromPackageJson: false,
|
|
124
|
-
omitUnchangedInstalledFilesOnUpload: false,
|
|
125
|
-
},
|
|
126
|
-
},
|
|
127
|
-
)
|
|
128
|
-
|
|
129
|
-
assert.equal(
|
|
130
|
-
output,
|
|
131
|
-
`Installed:
|
|
132
|
-
- thing@1.0.0 (model)
|
|
133
|
-
note: ${Array(13).fill('word').join(' ')}
|
|
134
|
-
${Array(7).fill('word').join(' ')}`,
|
|
135
|
-
)
|
|
136
|
-
})
|
|
137
|
-
|
|
138
|
-
test('installResult lists installed skills with their source', () => {
|
|
139
|
-
assert.equal(
|
|
140
|
-
installResult({
|
|
141
|
-
assets: [
|
|
142
|
-
{
|
|
143
|
-
name: 'wooden-chair',
|
|
144
|
-
type: 'model',
|
|
145
|
-
version: '1.2.0',
|
|
146
|
-
description: null,
|
|
147
|
-
files: [],
|
|
148
|
-
},
|
|
149
|
-
],
|
|
150
|
-
npmDependencies: {},
|
|
151
|
-
skillDependencies: {
|
|
152
|
-
'web-design-guidelines': 'vercel-labs/agent-skills/tree/main/skills/web-design-guidelines',
|
|
153
|
-
'local-skill': '/abs/public/skills/local-skill',
|
|
154
|
-
},
|
|
155
|
-
warnings: [],
|
|
156
|
-
}),
|
|
157
|
-
`Installed:
|
|
158
|
-
- wooden-chair@1.2.0 (model)
|
|
159
|
-
- local-skill ← /abs/public/skills/local-skill (skill)
|
|
160
|
-
- web-design-guidelines ← vercel-labs/agent-skills/tree/main/skills/web-design-guidelines (skill)`,
|
|
161
|
-
)
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
test('installResult shows each installed asset description', () => {
|
|
165
|
-
assert.equal(
|
|
166
|
-
installResult({
|
|
167
|
-
assets: [
|
|
168
|
-
{
|
|
169
|
-
name: 'wooden-chair',
|
|
170
|
-
type: 'model',
|
|
171
|
-
version: '1.2.0',
|
|
172
|
-
description:
|
|
173
|
-
'A game-ready chair with baked texture detail and clean origin placement for room scenes.',
|
|
174
|
-
files: [],
|
|
175
|
-
},
|
|
176
|
-
],
|
|
177
|
-
npmDependencies: {},
|
|
178
|
-
skillDependencies: {},
|
|
179
|
-
warnings: [],
|
|
180
|
-
}),
|
|
181
|
-
`Installed:
|
|
182
|
-
- wooden-chair@1.2.0 (model)
|
|
183
|
-
description: A game-ready chair with baked texture detail and clean origin
|
|
184
|
-
placement for room scenes.`,
|
|
185
|
-
)
|
|
186
|
-
})
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict'
|
|
2
|
-
import test from 'node:test'
|
|
3
|
-
import { resolve, ResolutionError } from '../src/resolve.js'
|
|
4
|
-
|
|
5
|
-
interface FakeAsset {
|
|
6
|
-
latestVersion: string
|
|
7
|
-
approved?: boolean
|
|
8
|
-
npmDependencies?: Record<string, string>
|
|
9
|
-
assetDependencies?: Record<string, string>
|
|
10
|
-
skillDependencies?: Record<string, string>
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function clientWith(assets: Record<string, FakeAsset>) {
|
|
14
|
-
return {
|
|
15
|
-
exact: async ({ name }: { name: string }) => {
|
|
16
|
-
const asset = assets[name]
|
|
17
|
-
if (!asset) return null
|
|
18
|
-
return {
|
|
19
|
-
name,
|
|
20
|
-
latestVersion: asset.latestVersion,
|
|
21
|
-
approved: asset.approved ?? true,
|
|
22
|
-
npmDependencies: JSON.stringify(asset.npmDependencies ?? {}),
|
|
23
|
-
assetDependencies: JSON.stringify(asset.assetDependencies ?? {}),
|
|
24
|
-
skillDependencies: JSON.stringify(asset.skillDependencies ?? {}),
|
|
25
|
-
}
|
|
26
|
-
},
|
|
27
|
-
} as never
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
test('resolve collects skill dependencies across transitive assets', async () => {
|
|
31
|
-
const client = clientWith({
|
|
32
|
-
root: {
|
|
33
|
-
latestVersion: '1.0.0',
|
|
34
|
-
assetDependencies: { dep: '*' },
|
|
35
|
-
skillDependencies: { 'web-design-guidelines': 'vercel-labs/agent-skills' },
|
|
36
|
-
},
|
|
37
|
-
dep: {
|
|
38
|
-
latestVersion: '2.0.0',
|
|
39
|
-
skillDependencies: { 'local-skill': './public/skills/local-skill' },
|
|
40
|
-
},
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
const result = await resolve(client, [{ name: 'root', range: '*' }])
|
|
44
|
-
|
|
45
|
-
assert.deepEqual(result.skillDependencies, {
|
|
46
|
-
'web-design-guidelines': 'vercel-labs/agent-skills',
|
|
47
|
-
'local-skill': './public/skills/local-skill',
|
|
48
|
-
})
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
test('resolve unions identical skill sources without complaint', async () => {
|
|
52
|
-
const client = clientWith({
|
|
53
|
-
root: {
|
|
54
|
-
latestVersion: '1.0.0',
|
|
55
|
-
assetDependencies: { dep: '*' },
|
|
56
|
-
skillDependencies: { shared: 'vercel-labs/agent-skills' },
|
|
57
|
-
},
|
|
58
|
-
dep: {
|
|
59
|
-
latestVersion: '1.0.0',
|
|
60
|
-
skillDependencies: { shared: 'vercel-labs/agent-skills' },
|
|
61
|
-
},
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
const result = await resolve(client, [{ name: 'root', range: '*' }])
|
|
65
|
-
|
|
66
|
-
assert.deepEqual(result.skillDependencies, { shared: 'vercel-labs/agent-skills' })
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
test('resolve rejects the same skill label with conflicting sources', async () => {
|
|
70
|
-
const client = clientWith({
|
|
71
|
-
root: {
|
|
72
|
-
latestVersion: '1.0.0',
|
|
73
|
-
assetDependencies: { dep: '*' },
|
|
74
|
-
skillDependencies: { shared: 'vercel-labs/agent-skills' },
|
|
75
|
-
},
|
|
76
|
-
dep: {
|
|
77
|
-
latestVersion: '1.0.0',
|
|
78
|
-
skillDependencies: { shared: 'other-org/other-skills' },
|
|
79
|
-
},
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
await assert.rejects(
|
|
83
|
-
resolve(client, [{ name: 'root', range: '*' }]),
|
|
84
|
-
(err: unknown) =>
|
|
85
|
-
err instanceof ResolutionError && /skill dependency conflict for "shared"/u.test(err.message),
|
|
86
|
-
)
|
|
87
|
-
})
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict'
|
|
2
|
-
import test from 'node:test'
|
|
3
|
-
import {
|
|
4
|
-
parseSkillDeps,
|
|
5
|
-
parseUploadDescription,
|
|
6
|
-
parseVersionedDeps,
|
|
7
|
-
} from '../src/commands/upload.js'
|
|
8
|
-
import { MAX_ASSET_DESCRIPTION_LENGTH } from '../src/schemas.js'
|
|
9
|
-
|
|
10
|
-
test('parseVersionedDeps parses name@range and defaults a missing range to *', () => {
|
|
11
|
-
assert.deepEqual(parseVersionedDeps(['three@^0.178.0', 'gsap'], 'npm'), {
|
|
12
|
-
three: '^0.178.0',
|
|
13
|
-
gsap: '*',
|
|
14
|
-
})
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
test('parseVersionedDeps keeps the scope marker on scoped npm packages', () => {
|
|
18
|
-
assert.deepEqual(parseVersionedDeps(['@react-three/drei@^9.0.0', '@types/node'], 'npm'), {
|
|
19
|
-
'@react-three/drei': '^9.0.0',
|
|
20
|
-
'@types/node': '*',
|
|
21
|
-
})
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
test('parseVersionedDeps rejects duplicates', () => {
|
|
25
|
-
assert.throws(
|
|
26
|
-
() => parseVersionedDeps(['three@^1', 'three@^2'], 'npm'),
|
|
27
|
-
/Duplicate npm dependency/u,
|
|
28
|
-
)
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
test('parseSkillDeps splits label=source on the first = only', () => {
|
|
32
|
-
assert.deepEqual(
|
|
33
|
-
parseSkillDeps([
|
|
34
|
-
'web-design=vercel-labs/agent-skills/tree/main/skills/web-design-guidelines',
|
|
35
|
-
'local=./public/skills/local',
|
|
36
|
-
]),
|
|
37
|
-
{
|
|
38
|
-
'web-design': 'vercel-labs/agent-skills/tree/main/skills/web-design-guidelines',
|
|
39
|
-
local: './public/skills/local',
|
|
40
|
-
},
|
|
41
|
-
)
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
test('parseSkillDeps rejects specs without a usable label=source', () => {
|
|
45
|
-
assert.throws(() => parseSkillDeps(['no-separator']), /Use label=source/u)
|
|
46
|
-
assert.throws(() => parseSkillDeps(['=missing-label']), /Use label=source/u)
|
|
47
|
-
assert.throws(() => parseSkillDeps(['missing-source=']), /Use label=source/u)
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
test('parseSkillDeps rejects duplicate labels', () => {
|
|
51
|
-
assert.throws(() => parseSkillDeps(['a=one', 'a=two']), /Duplicate skill dependency "a"/u)
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
test('parseUploadDescription rejects descriptions beyond the shared max length', () => {
|
|
55
|
-
assert.equal(
|
|
56
|
-
parseUploadDescription('A compact asset description.'),
|
|
57
|
-
'A compact asset description.',
|
|
58
|
-
)
|
|
59
|
-
assert.throws(
|
|
60
|
-
() => parseUploadDescription('x'.repeat(MAX_ASSET_DESCRIPTION_LENGTH + 1)),
|
|
61
|
-
/1000 characters or fewer/u,
|
|
62
|
-
)
|
|
63
|
-
})
|