@drawcall/market 0.1.25 → 0.1.27

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/resolve.js CHANGED
@@ -2,9 +2,10 @@
2
2
  * Dependency resolver for market assets.
3
3
  *
4
4
  * 1. Collects the target assets and all transitive asset dependencies.
5
- * 2. For each asset, finds a version that satisfies ALL constraints from
6
- * every dependent.
7
- * 3. Merges npm dependency ranges across all resolved assets and checks
5
+ * 2. For each asset, takes the latest published version and verifies it
6
+ * satisfies ALL constraints from every dependent. (Only the latest
7
+ * version is resolved; older versions are not considered.)
8
+ * 3. Intersects npm dependency ranges across all resolved assets and checks
8
9
  * that they are compatible.
9
10
  */
10
11
  import * as semver from 'semver';
@@ -29,18 +30,22 @@ export async function resolve(client, requests, opts = {}) {
29
30
  throw new ResolutionError(`Asset "${assetName}" not found.`);
30
31
  }
31
32
  if (!opts.includeUnapproved && !meta.approved) {
32
- throw new ResolutionError(`Asset "${assetName}" has no ${opts.includeUnapproved ? '' : 'approved '}versions.`);
33
+ throw new ResolutionError(`Asset "${assetName}" has no approved versions.`);
33
34
  }
35
+ // The market publishes immutable versions and resolution targets the
36
+ // latest published version of each asset. A range that excludes the
37
+ // latest version is therefore unsatisfiable (older versions are not
38
+ // resolved), which this message makes explicit.
34
39
  const assetConstraints = constraints.get(assetName) ?? [];
35
40
  if (!assetConstraints.every((c) => semver.satisfies(meta.latestVersion, c.range))) {
36
41
  const constraintDesc = assetConstraints
37
42
  .map((c) => ` ${c.range} (from ${c.from})`)
38
43
  .join('\n');
39
- throw new ResolutionError(`No version of "${assetName}" satisfies all constraints:\n${constraintDesc}\n` +
40
- `Available${opts.includeUnapproved ? '' : ' approved'}: ${meta.latestVersion}`);
44
+ throw new ResolutionError(`The latest${opts.includeUnapproved ? '' : ' approved'} version of "${assetName}" ` +
45
+ `(${meta.latestVersion}) does not satisfy all constraints:\n${constraintDesc}`);
41
46
  }
42
- const npmDeps = JSON.parse(meta.npmDependencies);
43
- const assetDeps = JSON.parse(meta.assetDependencies);
47
+ const npmDeps = parseDependencies(meta.npmDependencies, assetName, 'npm');
48
+ const assetDeps = parseDependencies(meta.assetDependencies, assetName, 'asset');
44
49
  resolved.set(assetName, {
45
50
  name: assetName,
46
51
  version: meta.latestVersion,
@@ -71,6 +76,18 @@ export async function resolve(client, requests, opts = {}) {
71
76
  npmDependencies: mergedNpm,
72
77
  };
73
78
  }
79
+ function parseDependencies(value, assetName, kind) {
80
+ try {
81
+ const parsed = JSON.parse(value || '{}');
82
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
83
+ return parsed;
84
+ }
85
+ }
86
+ catch {
87
+ // fall through to a clear error below
88
+ }
89
+ throw new ResolutionError(`Asset "${assetName}" has malformed ${kind} dependency metadata.`);
90
+ }
74
91
  function addConstraint(constraints, name, range, from) {
75
92
  const existing = constraints.get(name) ?? [];
76
93
  existing.push({ range, from });
@@ -93,7 +110,8 @@ async function fetchMeta(client, cache, name, includeUnapproved) {
93
110
  /**
94
111
  * Merge npm dependency ranges from all resolved assets.
95
112
  * For each package, check that all declared ranges are compatible
96
- * (using semver.intersects). Return the narrowest range.
113
+ * (using semver.intersects), then return the single declared range that is a
114
+ * subset of every other — the true intersection.
97
115
  */
98
116
  function mergeNpmDependencies(assets) {
99
117
  // Collect all ranges per package
@@ -117,17 +135,18 @@ function mergeNpmDependencies(assets) {
117
135
  }
118
136
  }
119
137
  }
120
- // Use the narrowest (most constrained) range.
121
- // Simple heuristic: pick the range with the highest minimum version.
122
- let narrowest = ranges[0].range;
123
- for (const r of ranges.slice(1)) {
124
- const minCurrent = semver.minVersion(narrowest);
125
- const minNew = semver.minVersion(r.range);
126
- if (minCurrent && minNew && semver.gt(minNew, minCurrent)) {
127
- narrowest = r.range;
128
- }
138
+ // Use the single declared range that is contained in every other declared
139
+ // range the true intersection of all constraints. Picking by "highest
140
+ // minimum version" is unsound: a range with a higher minimum can also have
141
+ // a higher (or open) upper bound, silently dropping another dependent's
142
+ // upper bound. If no single range represents the intersection, it cannot be
143
+ // expressed as one package.json range, so fail loudly.
144
+ const narrowest = ranges.find((candidate) => ranges.every((other) => semver.subset(candidate.range, other.range)));
145
+ if (!narrowest) {
146
+ throw new ResolutionError(`npm dependency ranges for "${pkg}" overlap but cannot be combined into a single range:\n` +
147
+ ranges.map((r) => ` ${r.range} (from ${r.from})`).join('\n'));
129
148
  }
130
- merged[pkg] = narrowest;
149
+ merged[pkg] = narrowest.range;
131
150
  }
132
151
  return merged;
133
152
  }
@@ -1 +1 @@
1
- {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"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"}
package/dist/skill.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const marketSkill = "---\nname: market\ndescription: Find, preview, install, generate, and publish Drawcall Market assets from a coding agent.\n---\n\n# Drawcall Market\n\nUse the `market` CLI. Keep commands short and read the summary line plus asset bullets.\n\n## Quick Start\n\n```sh\nmarket search \"wooden chair\" --type model --limit 3\nmarket install wooden-chair --type model --cwd \"$PWD\"\nmarket preview wooden-chair --out /tmp/wooden-chair.png\n```\n\n## Workflow\n\n1. Search first unless the user gave an exact asset name.\n2. Search requires `--type`; use `model` unless the user names another supported type: `humanoid-model`, `texture`, `humanoid-animation`, `template`, `sound-effect`, `background-music`, or `environment`.\n3. Use `--limit 1` for lookup, `--limit 3` for choice. Search caps at 5.\n4. Use `--verbose` only when the one-line descriptions are not enough.\n5. Use `--unapproved` only when the user asks for unapproved/private/admin assets. Do not install unapproved assets without explicit acceptance.\n6. Preview before installing when visual fit matters: `market preview <name> --out <file>`. Preview is not supported for `humanoid-animation`, `template`, `sound-effect`, or `background-music`.\n7. Generate only when the user wants a new asset, auth is available, and the requested asset type supports generation. No asset type currently supports generation.\n8. Upload only when publishing is requested.\n9. Installed `environment` assets contain `public/environment/<name>.hdr` for Three.js IBL lighting and `public/environment/<name>-background.webp` for the visible equirectangular background. Use `market preview` to fetch the preview image separately.\n\n## Output\n\nCommands print concise summaries:\n\n```text\nResults: 2/8 query=\"wooden chair\" type=model approval=approved\n- wooden-chair@1.0.0 | model | approved | Low-poly wooden chair\nInstalled 1 asset: wooden-chair@1.0.0\nSaved preview for wooden-chair@1.0.0: /tmp/wooden-chair.png\n```\n\nIf search returns no results, try one broader noun phrase. If a command returns `Error: Not logged in...`, ask before running `market login`.\n";
1
+ export declare const marketSkill = "---\nname: market\ndescription: Find, preview, install, generate, and publish Drawcall Market assets from a coding agent.\n---\n\n# Drawcall Market\n\nUse the `market` CLI. Keep commands short and read the summary lines.\n\n## Quick Start\n\n```sh\nmarket search \"wooden chair\" --type model --limit 3\nmarket install wooden-chair --cwd \"$PWD\"\nmarket preview wooden-chair --out /tmp/wooden-chair.png\n```\n\n## Workflow\n\n1. Search first unless the user already gave an exact asset name. `search` requires `--type`; use `model` unless the user names another supported type: `humanoid-model`, `texture`, `humanoid-animation`, `template`, `sound-effect`, `background-music`, or `environment`.\n2. Use `--limit 1` for lookup, `--limit 3` for choice. Search caps at 5. Add `--verbose` only when the one-line descriptions are not enough.\n3. `install` takes one or more exact asset names (optionally `name@range`); it does not search or generate. Find names with `search` first. No `--type` is needed \u2014 asset names are unique.\n4. `preview <name>` saves the preview image; no `--type` is needed. Not every type has previews (e.g. `humanoid-animation`, `template`, `sound-effect`, `background-music`); the CLI reports when one is unavailable.\n5. Use `--unapproved` only when the user asks for unapproved/private/admin assets. Do not install unapproved assets without explicit acceptance.\n6. `generate --type <type> \"<prompt>\"` creates a new asset; it requires login and a type that supports generation. No asset type currently supports generation.\n7. Upload only when publishing is requested: `market upload <name> <zip> \"<description>\" --type <type>`.\n8. Installed `environment` assets contain `public/environment/<name>.hdr` for Three.js IBL lighting and `public/environment/<name>-background.webp` for the visible equirectangular background. Use `market preview` to fetch the preview image separately.\n\n## Output\n\nCommands print concise, line-oriented summaries:\n\n```text\nResults: 2/8 query=\"wooden chair\" type=model approval=approved\n- wooden-chair@1.0.0 | model | approved | Low-poly wooden chair\nInstalled:\n- wooden-chair@1.0.0 (asset)\n files:\n public/model\n \u2514\u2500 wooden-chair.glb\n- three@^0.178.0 (npm)\nSaved preview for wooden-chair@1.0.0: /tmp/wooden-chair.png\n```\n\nIf search returns no results, try one broader noun phrase. If a command returns `Error: Not logged in...`, ask before running `market login`.\n";
2
2
  //# sourceMappingURL=skill.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"skill.d.ts","sourceRoot":"","sources":["../src/skill.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,wkEAyCvB,CAAA"}
1
+ {"version":3,"file":"skill.d.ts","sourceRoot":"","sources":["../src/skill.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,s6EA6CvB,CAAA"}
package/dist/skill.js CHANGED
@@ -5,36 +5,40 @@ description: Find, preview, install, generate, and publish Drawcall Market asset
5
5
 
6
6
  # Drawcall Market
7
7
 
8
- Use the \`market\` CLI. Keep commands short and read the summary line plus asset bullets.
8
+ Use the \`market\` CLI. Keep commands short and read the summary lines.
9
9
 
10
10
  ## Quick Start
11
11
 
12
12
  \`\`\`sh
13
13
  market search "wooden chair" --type model --limit 3
14
- market install wooden-chair --type model --cwd "$PWD"
14
+ market install wooden-chair --cwd "$PWD"
15
15
  market preview wooden-chair --out /tmp/wooden-chair.png
16
16
  \`\`\`
17
17
 
18
18
  ## Workflow
19
19
 
20
- 1. Search first unless the user gave an exact asset name.
21
- 2. Search requires \`--type\`; use \`model\` unless the user names another supported type: \`humanoid-model\`, \`texture\`, \`humanoid-animation\`, \`template\`, \`sound-effect\`, \`background-music\`, or \`environment\`.
22
- 3. Use \`--limit 1\` for lookup, \`--limit 3\` for choice. Search caps at 5.
23
- 4. Use \`--verbose\` only when the one-line descriptions are not enough.
20
+ 1. Search first unless the user already gave an exact asset name. \`search\` requires \`--type\`; use \`model\` unless the user names another supported type: \`humanoid-model\`, \`texture\`, \`humanoid-animation\`, \`template\`, \`sound-effect\`, \`background-music\`, or \`environment\`.
21
+ 2. Use \`--limit 1\` for lookup, \`--limit 3\` for choice. Search caps at 5. Add \`--verbose\` only when the one-line descriptions are not enough.
22
+ 3. \`install\` takes one or more exact asset names (optionally \`name@range\`); it does not search or generate. Find names with \`search\` first. No \`--type\` is needed — asset names are unique.
23
+ 4. \`preview <name>\` saves the preview image; no \`--type\` is needed. Not every type has previews (e.g. \`humanoid-animation\`, \`template\`, \`sound-effect\`, \`background-music\`); the CLI reports when one is unavailable.
24
24
  5. Use \`--unapproved\` only when the user asks for unapproved/private/admin assets. Do not install unapproved assets without explicit acceptance.
25
- 6. Preview before installing when visual fit matters: \`market preview <name> --out <file>\`. Preview is not supported for \`humanoid-animation\`, \`template\`, \`sound-effect\`, or \`background-music\`.
26
- 7. Generate only when the user wants a new asset, auth is available, and the requested asset type supports generation. No asset type currently supports generation.
27
- 8. Upload only when publishing is requested.
28
- 9. Installed \`environment\` assets contain \`public/environment/<name>.hdr\` for Three.js IBL lighting and \`public/environment/<name>-background.webp\` for the visible equirectangular background. Use \`market preview\` to fetch the preview image separately.
25
+ 6. \`generate --type <type> "<prompt>"\` creates a new asset; it requires login and a type that supports generation. No asset type currently supports generation.
26
+ 7. Upload only when publishing is requested: \`market upload <name> <zip> "<description>" --type <type>\`.
27
+ 8. Installed \`environment\` assets contain \`public/environment/<name>.hdr\` for Three.js IBL lighting and \`public/environment/<name>-background.webp\` for the visible equirectangular background. Use \`market preview\` to fetch the preview image separately.
29
28
 
30
29
  ## Output
31
30
 
32
- Commands print concise summaries:
31
+ Commands print concise, line-oriented summaries:
33
32
 
34
33
  \`\`\`text
35
34
  Results: 2/8 query="wooden chair" type=model approval=approved
36
35
  - wooden-chair@1.0.0 | model | approved | Low-poly wooden chair
37
- Installed 1 asset: wooden-chair@1.0.0
36
+ Installed:
37
+ - wooden-chair@1.0.0 (asset)
38
+ files:
39
+ public/model
40
+ └─ wooden-chair.glb
41
+ - three@^0.178.0 (npm)
38
42
  Saved preview for wooden-chair@1.0.0: /tmp/wooden-chair.png
39
43
  \`\`\`
40
44
 
package/dist/skill.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"skill.js","sourceRoot":"","sources":["../src/skill.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyC1B,CAAA"}
1
+ {"version":3,"file":"skill.js","sourceRoot":"","sources":["../src/skill.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6C1B,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drawcall/market",
3
- "version": "0.1.25",
3
+ "version": "0.1.27",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/drawcall-ai/market",
package/src/cli-client.ts CHANGED
@@ -25,8 +25,7 @@ export interface CliClient {
25
25
 
26
26
  export async function getCliClient(opts: CliClientOptions = {}): Promise<CliClient> {
27
27
  const config = await loadConfig()
28
- const authToken =
29
- process.env.DRAWCALL_AUTH_TOKEN ?? process.env.MARKET_API_KEY ?? config?.authToken
28
+ const authToken = process.env.DRAWCALL_AUTH_TOKEN ?? config?.authToken
30
29
  const baseUrl = opts.baseUrl ?? process.env.MARKET_API_URL ?? config?.baseUrl ?? DEFAULT_BASE_URL
31
30
 
32
31
  if (opts.requireAuth && !authToken) {
package/src/cli.ts CHANGED
@@ -56,7 +56,10 @@ program
56
56
  const client = createMarketClient({ baseUrl, authToken: token })
57
57
  const profile = await client.user.getProfile()
58
58
  if (!profile) throw new Error('The Market API did not accept the auth token.')
59
- await saveConfig({ authToken: token, baseUrl })
59
+ // Only pin baseUrl in config when the user explicitly chose one. Persisting
60
+ // the default would freeze logged-in users to a host the CLI can no longer
61
+ // change in a future release.
62
+ await saveConfig(opts.api ? { authToken: token, baseUrl: opts.api } : { authToken: token })
60
63
  console.log(loginResult(profile.email, getConfigPath()))
61
64
  })
62
65
 
@@ -69,25 +72,18 @@ program
69
72
 
70
73
  program
71
74
  .command('install')
72
- .description('Install by name or prompt')
73
- .argument('<assets...>', 'Names or prompts')
74
- .addOption(typeOption)
75
+ .description('Install assets by name')
76
+ .argument('<assets...>', 'Asset names, optionally with @range')
75
77
  .addOption(apiOption)
76
78
  .option('--unapproved', 'Include unapproved versions', false)
77
79
  .option('--cwd <dir>', 'Project directory')
78
- .action(
79
- async (
80
- args: string[],
81
- opts: { type?: AssetType; api?: string; unapproved: boolean; cwd?: string },
82
- ) => {
83
- await installCommand(args, {
84
- type: opts.type,
85
- baseUrl: opts.api,
86
- unapproved: opts.unapproved,
87
- cwd: opts.cwd,
88
- })
89
- },
90
- )
80
+ .action(async (args: string[], opts: { api?: string; unapproved: boolean; cwd?: string }) => {
81
+ await installCommand(args, {
82
+ baseUrl: opts.api,
83
+ unapproved: opts.unapproved,
84
+ cwd: opts.cwd,
85
+ })
86
+ })
91
87
 
92
88
  program
93
89
  .command('search')
@@ -109,12 +105,7 @@ program
109
105
  verbose: boolean
110
106
  },
111
107
  ) => {
112
- if (!opts.type) {
113
- console.error(
114
- errorResult(`Search requires --type. Available types: ${ASSET_TYPES.join(', ')}`),
115
- )
116
- process.exit(1)
117
- }
108
+ requireType(opts.type, 'Search')
118
109
  await searchCommand(query, {
119
110
  type: opts.type,
120
111
  baseUrl: opts.api,
@@ -142,12 +133,7 @@ program
142
133
  description: string,
143
134
  opts: { type?: AssetType; api?: string; version?: string; cwd?: string },
144
135
  ) => {
145
- if (!opts.type) {
146
- console.error(
147
- errorResult(`Upload requires --type. Available types: ${ASSET_TYPES.join(', ')}`),
148
- )
149
- process.exit(1)
150
- }
136
+ requireType(opts.type, 'Upload')
151
137
  await uploadCommand(name, zipFilter, description, {
152
138
  type: opts.type,
153
139
  version: opts.version,
@@ -162,7 +148,6 @@ program
162
148
  .description('Save preview image')
163
149
  .argument('<name>', 'Asset name')
164
150
  .argument('[version]', 'Semver version')
165
- .addOption(typeOption)
166
151
  .addOption(apiOption)
167
152
  .option('--unapproved', 'Include unapproved versions', false)
168
153
  .option('--out <file>', 'Output image path')
@@ -170,10 +155,9 @@ program
170
155
  async (
171
156
  name: string,
172
157
  version: string | undefined,
173
- opts: { type?: AssetType; api?: string; unapproved: boolean; out?: string },
158
+ opts: { api?: string; unapproved: boolean; out?: string },
174
159
  ) => {
175
160
  await previewCommand(name, version, {
176
- type: opts.type,
177
161
  baseUrl: opts.api,
178
162
  unapproved: opts.unapproved,
179
163
  out: opts.out,
@@ -189,6 +173,7 @@ program
189
173
  .addOption(apiOption)
190
174
  .option('--cwd <dir>', 'Project directory')
191
175
  .action(async (description: string, opts: { type?: AssetType; api?: string; cwd?: string }) => {
176
+ requireType(opts.type, 'Generate')
192
177
  await generateCommand(description, {
193
178
  type: opts.type,
194
179
  baseUrl: opts.api,
@@ -207,6 +192,15 @@ program.parseAsync().catch((err) => {
207
192
  process.exit(1)
208
193
  })
209
194
 
195
+ function requireType(type: AssetType | undefined, command: string): asserts type is AssetType {
196
+ if (!type) {
197
+ console.error(
198
+ errorResult(`${command} requires --type. Available types: ${ASSET_TYPES.join(', ')}`),
199
+ )
200
+ process.exit(1)
201
+ }
202
+ }
203
+
210
204
  function parseSearchLimit(value: string): number {
211
205
  const parsed = Number.parseInt(value, 10)
212
206
  if (!Number.isInteger(parsed) || parsed < 1) {
package/src/client.ts CHANGED
@@ -11,7 +11,6 @@ export interface MarketClientOptions {
11
11
  baseUrl?: string
12
12
  fetch?: typeof globalThis.fetch
13
13
  authToken?: string
14
- apiKey?: string
15
14
  }
16
15
 
17
16
  function buildHeaders(authToken?: string): Record<string, string> | undefined {
@@ -20,11 +19,10 @@ function buildHeaders(authToken?: string): Record<string, string> | undefined {
20
19
 
21
20
  export function createMarketClient(opts: MarketClientOptions = {}): MarketClient {
22
21
  const baseUrl = opts.baseUrl || DEFAULT_BASE_URL
23
- const authToken = opts.authToken ?? opts.apiKey
24
22
  const link = new RPCLink({
25
23
  url: new URL('/api/rpc', baseUrl).href,
26
24
  fetch: opts.fetch,
27
- headers: buildHeaders(authToken),
25
+ headers: buildHeaders(opts.authToken),
28
26
  })
29
27
  return createORPCClient<MarketClient>(link)
30
28
  }
@@ -1,14 +1,12 @@
1
- import ora, { type Ora } from 'ora'
1
+ import ora from 'ora'
2
2
  import { resolve } from '../resolve.js'
3
3
  import { install as runInstall } from '../install.js'
4
- import { generateAndWait } from '../generate.js'
5
- import { assetNameSchema, type AssetType } from '../schemas.js'
4
+ import { assetNameSchema } from '../schemas.js'
6
5
  import { getCliClient } from '../cli-client.js'
7
- import { compact, generatedInstallResult, installResult } from '../output.js'
6
+ import { installResult } from '../output.js'
8
7
  import type { MarketClient } from '../client.js'
9
8
 
10
9
  export interface InstallCommandOptions {
11
- type?: AssetType
12
10
  unapproved?: boolean
13
11
  cwd?: string
14
12
  baseUrl?: string
@@ -20,8 +18,8 @@ interface AssetRequest {
20
18
  }
21
19
 
22
20
  export async function installCommand(args: string[], opts: InstallCommandOptions): Promise<void> {
23
- const { client, authToken } = await getCliClient({ baseUrl: opts.baseUrl })
24
- const canGenerate = Boolean(authToken)
21
+ const { client } = await getCliClient({ baseUrl: opts.baseUrl })
22
+ const includeUnapproved = opts.unapproved ?? false
25
23
 
26
24
  const spinner = ora({
27
25
  isEnabled: Boolean(process.stderr.isTTY),
@@ -31,15 +29,11 @@ export async function installCommand(args: string[], opts: InstallCommandOptions
31
29
  const requests: AssetRequest[] = []
32
30
  for (const arg of args) {
33
31
  spinner.text = `Resolving "${truncate(arg, 40)}"`
34
- requests.push(
35
- await resolveArg(client, arg, opts.type, spinner, canGenerate, opts.unapproved ?? false),
36
- )
32
+ requests.push(await resolveArg(client, arg, includeUnapproved))
37
33
  }
38
34
 
39
35
  spinner.text = 'Resolving dependency tree'
40
- const resolution = await resolve(client.asset, requests, {
41
- includeUnapproved: opts.unapproved ?? false,
42
- })
36
+ const resolution = await resolve(client.asset, requests, { includeUnapproved })
43
37
 
44
38
  const result = await runInstall(client, resolution, {
45
39
  cwd: opts.cwd,
@@ -56,82 +50,41 @@ export async function installCommand(args: string[], opts: InstallCommandOptions
56
50
  }
57
51
  }
58
52
 
53
+ /**
54
+ * Resolve a single `name` or `name@range` argument to an exact asset request.
55
+ * Asset names are globally unique, so the name fully determines the asset — no
56
+ * `--type` is needed. install never fuzzy-searches or generates: it points the
57
+ * caller at `market search` / `market generate` instead, which keeps behavior
58
+ * predictable for agents.
59
+ */
59
60
  export async function resolveArg(
60
61
  client: MarketClient,
61
62
  arg: string,
62
- type: AssetType | undefined,
63
- spinner: Ora,
64
- canGenerate: boolean,
65
63
  includeUnapproved: boolean,
66
64
  ): Promise<AssetRequest> {
67
65
  const parsed = parseNameAndRange(arg)
68
- if (parsed) {
69
- const existing = await client.asset.exact({
70
- name: parsed.name,
71
- type,
72
- includeUnapproved,
73
- })
74
- if (existing) return parsed
75
-
76
- if (!includeUnapproved) {
77
- const hidden = await client.asset.exact({
78
- name: parsed.name,
79
- type,
80
- includeUnapproved: true,
81
- })
82
- if (hidden) {
83
- throw new Error(
84
- `Asset "${parsed.name}" exists but has no approved versions. Re-run with \`--unapproved\` if you have access.`,
85
- )
86
- }
87
- }
88
-
89
- if (arg.includes('@')) {
90
- return parsed
91
- }
66
+ if (!parsed) {
67
+ throw new Error(
68
+ `Invalid asset name "${arg}". Use \`market search --type <type> <query>\` to find assets.`,
69
+ )
92
70
  }
93
71
 
94
- spinner.text = `Searching "${truncate(arg, 40)}"`
95
- const results = await client.asset.search({
96
- query: arg,
97
- type,
98
- includeUnapproved,
99
- limit: 1,
100
- page: 1,
101
- sort: 'relevance',
102
- })
103
- if (results.items.length > 0) {
104
- const hit = results.items[0]
105
- if (process.stderr.isTTY) {
106
- spinner.info(`Matched "${compact(arg, 48)}" to ${hit.name} (${hit.type})`)
107
- spinner.start()
108
- }
109
- return { name: hit.name, range: '*' }
110
- }
72
+ const existing = await client.asset.exact({ name: parsed.name, includeUnapproved })
73
+ if (existing) return parsed
111
74
 
112
- if (!canGenerate) {
113
- throw new Error(
114
- `No matches for "${arg}". Run \`market login\` to enable auto-generation of missing assets.`,
115
- )
75
+ if (!includeUnapproved) {
76
+ const hidden = await client.asset.exact({ name: parsed.name, includeUnapproved: true })
77
+ if (hidden) {
78
+ throw new Error(
79
+ `Asset "${parsed.name}" exists but has no approved versions. Re-run with \`--unapproved\` if you have access.`,
80
+ )
81
+ }
116
82
  }
117
83
 
118
- spinner.text = `No matches; generating "${truncate(arg, 40)}"`
119
- const generated = await generateAndWait(
120
- client,
121
- { description: arg, type },
122
- {
123
- onProgress: (msg) => {
124
- spinner.text = msg
125
- },
126
- },
84
+ throw new Error(
85
+ `Asset "${parsed.name}" not found. Use \`market search\` to find assets or ` +
86
+ `\`market generate --type <type>\` to create one.`,
127
87
  )
128
- if (process.stderr.isTTY) {
129
- spinner.info(
130
- `${generatedInstallResult(generated.assetName, generated.version)} for "${compact(arg, 48)}"`,
131
- )
132
- spinner.start()
133
- }
134
- return { name: generated.assetName, range: generated.version }
135
88
  }
136
89
 
137
90
  function parseNameAndRange(arg: string): AssetRequest | null {
@@ -3,10 +3,9 @@ import * as path from 'path'
3
3
  import ora from 'ora'
4
4
  import { getCliClient } from '../cli-client.js'
5
5
  import { previewResult } from '../output.js'
6
- import { assetNameSchema, semverSchema, type AssetType } from '../schemas.js'
6
+ import { assetNameSchema, semverSchema } from '../schemas.js'
7
7
 
8
8
  export interface PreviewCommandOptions {
9
- type?: AssetType
10
9
  unapproved?: boolean
11
10
  out?: string
12
11
  baseUrl?: string
@@ -19,13 +18,10 @@ export async function previewCommand(
19
18
  ): Promise<void> {
20
19
  const parsedName = assetNameSchema.parse(name)
21
20
  const parsedVersion = version ? semverSchema.parse(version) : undefined
22
- if (
23
- opts.type === 'humanoid-animation' ||
24
- opts.type === 'template' ||
25
- opts.type === 'sound-effect'
26
- ) {
27
- throw new Error(`Preview images are not supported for ${opts.type}`)
28
- }
21
+ // The asset name is globally unique, so the server resolves the type. Whether
22
+ // that type supports preview images is also a server concern: the API checks
23
+ // for a provider `downloadPreviewImage` capability and returns a clear,
24
+ // type-specific error rather than us mirroring a list that can drift.
29
25
 
30
26
  const spinner = ora({
31
27
  text: `Resolving preview for ${parsedName}`,
@@ -40,7 +36,6 @@ export async function previewCommand(
40
36
  (
41
37
  await client.asset.exact({
42
38
  name: parsedName,
43
- type: opts.type,
44
39
  includeUnapproved: opts.unapproved ?? false,
45
40
  })
46
41
  )?.latestVersion
package/src/config.ts CHANGED
@@ -5,7 +5,6 @@ import { z } from 'zod'
5
5
 
6
6
  const rawConfigSchema = z.object({
7
7
  authToken: z.string().min(1).optional(),
8
- apiKey: z.string().min(1).optional(),
9
8
  baseUrl: z.string().url().optional(),
10
9
  })
11
10
 
@@ -31,8 +30,9 @@ export async function loadConfig(): Promise<Config | null> {
31
30
  const raw = await fs.readFile(configPath(), 'utf-8')
32
31
  const parsed = rawConfigSchema.safeParse(JSON.parse(raw))
33
32
  if (!parsed.success) return null
34
- const authToken = parsed.data.authToken ?? parsed.data.apiKey
35
- return authToken ? { authToken, baseUrl: parsed.data.baseUrl } : null
33
+ return parsed.data.authToken
34
+ ? { authToken: parsed.data.authToken, baseUrl: parsed.data.baseUrl }
35
+ : null
36
36
  } catch {
37
37
  return null
38
38
  }
@@ -41,8 +41,14 @@ export async function loadConfig(): Promise<Config | null> {
41
41
  export async function saveConfig(config: Config): Promise<void> {
42
42
  const dir = configDir()
43
43
  await fs.mkdir(dir, { recursive: true })
44
+ // `mode` on writeFile/mkdir only applies when the path is created, so chmod
45
+ // explicitly to protect a pre-existing (possibly world-readable) token file
46
+ // and its directory. A chmod failure means we could not secure the token, so
47
+ // let it surface rather than silently leaving it exposed.
48
+ await fs.chmod(dir, 0o700)
44
49
  const file = configPath()
45
50
  await fs.writeFile(file, JSON.stringify(config, null, 2) + '\n', { mode: 0o600 })
51
+ await fs.chmod(file, 0o600)
46
52
  }
47
53
 
48
54
  export async function clearConfig(): Promise<boolean> {
package/src/install.ts CHANGED
@@ -147,19 +147,18 @@ async function installNpmDeps(
147
147
  if (Object.keys(deps).length === 0) return
148
148
 
149
149
  const pkgPath = path.join(projectRoot, 'package.json')
150
- let pkg: PackageJson
151
- try {
152
- pkg = JSON.parse(await fs.readFile(pkgPath, 'utf-8')) as PackageJson
153
- } catch {
154
- pkg = { name: 'my-project', private: true, dependencies: {} }
155
- }
150
+ // Only synthesize a package.json when none exists. A malformed existing file
151
+ // is a real error that must surface — never silently overwrite it.
152
+ const pkg: PackageJson = (await isFile(pkgPath))
153
+ ? (JSON.parse(await fs.readFile(pkgPath, 'utf-8')) as PackageJson)
154
+ : { name: 'my-project', private: true, dependencies: {} }
156
155
 
157
156
  pkg.dependencies = { ...pkg.dependencies, ...deps }
158
157
  await fs.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + '\n')
159
158
 
160
159
  log(`Installing npm dependencies: ${Object.keys(deps).join(', ')}`)
161
160
 
162
- const pm = await detectPackageManager(projectRoot).catch(() => null)
161
+ const pm = await detectPackageManager(projectRoot)
163
162
  const pmName = pm?.name ?? 'npm'
164
163
 
165
164
  log(`Using ${pmName}...`)