@drawcall/market 0.1.98 → 0.2.0

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.
Files changed (104) hide show
  1. package/README.md +7 -4
  2. package/dist/asset-implementation.d.ts +3 -3
  3. package/dist/asset-implementation.d.ts.map +1 -1
  4. package/dist/cli.js +15 -3
  5. package/dist/cli.js.map +1 -1
  6. package/dist/client.d.ts +14 -0
  7. package/dist/client.d.ts.map +1 -1
  8. package/dist/client.js +18 -0
  9. package/dist/client.js.map +1 -1
  10. package/dist/commands/install.js +10 -7
  11. package/dist/commands/install.js.map +1 -1
  12. package/dist/commands/list.d.ts +5 -5
  13. package/dist/commands/list.d.ts.map +1 -1
  14. package/dist/commands/list.js +22 -9
  15. package/dist/commands/list.js.map +1 -1
  16. package/dist/commands/pack.d.ts.map +1 -1
  17. package/dist/commands/pack.js +2 -2
  18. package/dist/commands/pack.js.map +1 -1
  19. package/dist/commands/search.d.ts +2 -0
  20. package/dist/commands/search.d.ts.map +1 -1
  21. package/dist/commands/search.js +14 -2
  22. package/dist/commands/search.js.map +1 -1
  23. package/dist/commands/sync.d.ts +6 -0
  24. package/dist/commands/sync.d.ts.map +1 -0
  25. package/dist/commands/sync.js +22 -0
  26. package/dist/commands/sync.js.map +1 -0
  27. package/dist/commands/upload.d.ts.map +1 -1
  28. package/dist/commands/upload.js +20 -10
  29. package/dist/commands/upload.js.map +1 -1
  30. package/dist/contract.d.ts +4 -6
  31. package/dist/contract.d.ts.map +1 -1
  32. package/dist/contract.js +1 -2
  33. package/dist/contract.js.map +1 -1
  34. package/dist/index.d.ts +5 -4
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +5 -2
  37. package/dist/index.js.map +1 -1
  38. package/dist/install.d.ts.map +1 -1
  39. package/dist/install.js +69 -27
  40. package/dist/install.js.map +1 -1
  41. package/dist/output.d.ts +3 -4
  42. package/dist/output.d.ts.map +1 -1
  43. package/dist/output.js +40 -7
  44. package/dist/output.js.map +1 -1
  45. package/dist/pack.d.ts +36 -6
  46. package/dist/pack.d.ts.map +1 -1
  47. package/dist/pack.js +230 -57
  48. package/dist/pack.js.map +1 -1
  49. package/dist/package-json.d.ts +4 -3
  50. package/dist/package-json.d.ts.map +1 -1
  51. package/dist/package-json.js +3 -1
  52. package/dist/package-json.js.map +1 -1
  53. package/dist/resolve.d.ts +8 -1
  54. package/dist/resolve.d.ts.map +1 -1
  55. package/dist/resolve.js +35 -3
  56. package/dist/resolve.js.map +1 -1
  57. package/dist/schemas.d.ts +19 -7
  58. package/dist/schemas.d.ts.map +1 -1
  59. package/dist/schemas.js +22 -7
  60. package/dist/schemas.js.map +1 -1
  61. package/dist/skill.d.ts +1 -1
  62. package/dist/skill.d.ts.map +1 -1
  63. package/dist/skill.js +4 -2
  64. package/dist/skill.js.map +1 -1
  65. package/dist/v1/client.d.ts +15 -0
  66. package/dist/v1/client.d.ts.map +1 -0
  67. package/dist/v1/client.js +18 -0
  68. package/dist/v1/client.js.map +1 -0
  69. package/dist/v1/contract.d.ts +206 -0
  70. package/dist/v1/contract.d.ts.map +1 -0
  71. package/dist/v1/contract.js +77 -0
  72. package/dist/v1/contract.js.map +1 -0
  73. package/dist/v1/index.d.ts +3 -0
  74. package/dist/v1/index.d.ts.map +1 -0
  75. package/dist/v1/index.js +3 -0
  76. package/dist/v1/index.js.map +1 -0
  77. package/package.json +2 -1
  78. package/skills/market/SKILL.md +4 -2
  79. package/src/asset-implementation.ts +3 -3
  80. package/src/cli.ts +20 -4
  81. package/src/client.ts +26 -0
  82. package/src/commands/install.ts +11 -8
  83. package/src/commands/list.ts +28 -14
  84. package/src/commands/pack.ts +7 -2
  85. package/src/commands/search.ts +21 -2
  86. package/src/commands/sync.ts +30 -0
  87. package/src/commands/upload.ts +26 -11
  88. package/src/contract.ts +0 -3
  89. package/src/index.ts +13 -3
  90. package/src/install.ts +89 -35
  91. package/src/output.ts +52 -11
  92. package/src/pack.ts +317 -70
  93. package/src/package-json.ts +7 -4
  94. package/src/resolve.ts +47 -4
  95. package/src/schemas.ts +32 -8
  96. package/src/skill.ts +4 -2
  97. package/src/v1/client.ts +28 -0
  98. package/src/v1/contract.ts +140 -0
  99. package/src/v1/index.ts +2 -0
  100. package/dist/market-lock.d.ts +0 -16
  101. package/dist/market-lock.d.ts.map +0 -1
  102. package/dist/market-lock.js +0 -48
  103. package/dist/market-lock.js.map +0 -1
  104. package/src/market-lock.ts +0 -68
@@ -0,0 +1,77 @@
1
+ import { oc } from '@orpc/contract';
2
+ import { z } from 'zod';
3
+ import { MAX_UPLOAD_ZIP_SIZE_BYTES, agentRunStatusSchema, agentStartSchema, agentStatusSchema, assetFilesSchema, exactAssetSchema, fileManifestSchema, generateAssetSchema, generateJobStatusSchema, generateResponseSchema, generateStatusInputSchema, listAssetsSchema, updateProfileSchema, uploadZipSchema, } from '../schemas.js';
4
+ /**
5
+ * The v1 contract: the same procedure tree as the legacy RPC surface (so call sites migrate by
6
+ * swapping only the client), with HTTP routes attached — served as plain REST under `/api/v1`.
7
+ * The route annotations here ARE the control-plane URL contract.
8
+ */
9
+ export const contract = {
10
+ asset: {
11
+ search: oc
12
+ .route({ method: 'GET', path: '/assets' })
13
+ .input(listAssetsSchema)
14
+ .output(z.custom()),
15
+ exact: oc
16
+ .route({ method: 'GET', path: '/assets/{name}' })
17
+ .input(exactAssetSchema)
18
+ .output(z.custom()),
19
+ // The manifest is new in v1 — it merges the legacy `files` + metadata into one GET.
20
+ manifest: oc
21
+ .route({ method: 'GET', path: '/assets/{name}/{version}' })
22
+ .input(assetFilesSchema)
23
+ .output(z.custom()),
24
+ files: oc
25
+ .route({ method: 'GET', path: '/assets/{name}/{version}/files' })
26
+ .input(assetFilesSchema)
27
+ .output(z.custom()),
28
+ fileManifest: oc
29
+ .route({ method: 'GET', path: '/assets/{name}/{version}/file-manifest' })
30
+ .input(fileManifestSchema)
31
+ .output(z.custom()),
32
+ uploadZip: oc
33
+ .route({ method: 'PUT', path: '/assets/{name}/{version}' })
34
+ .input(uploadZipSchema
35
+ .extend({ zip: z.instanceof(File) })
36
+ .refine((input) => input.zip.size < MAX_UPLOAD_ZIP_SIZE_BYTES, {
37
+ message: 'Upload zip must be smaller than 1 GB',
38
+ path: ['zip'],
39
+ }))
40
+ .output(z.custom()),
41
+ installMetadata: oc
42
+ .route({ method: 'GET', path: '/types' })
43
+ .output(z.custom()),
44
+ generate: oc
45
+ .route({ method: 'POST', path: '/generations' })
46
+ .input(generateAssetSchema)
47
+ .output(generateResponseSchema),
48
+ generateStatus: oc
49
+ .route({ method: 'GET', path: '/generations/{jobId}' })
50
+ .input(generateStatusInputSchema)
51
+ .output(generateJobStatusSchema),
52
+ agent: {
53
+ start: oc
54
+ .route({ method: 'POST', path: '/agent-runs' })
55
+ .input(agentStartSchema)
56
+ .output(z.object({ runId: z.string() })),
57
+ status: oc
58
+ .route({ method: 'GET', path: '/agent-runs/{runId}' })
59
+ .input(agentStatusSchema)
60
+ .output(agentRunStatusSchema),
61
+ },
62
+ },
63
+ user: {
64
+ getProfile: oc.route({ method: 'GET', path: '/me' }).output(z.custom()),
65
+ updateProfile: oc
66
+ .route({ method: 'PATCH', path: '/me' })
67
+ .input(updateProfileSchema)
68
+ .output(z.custom()),
69
+ getAuthToken: oc
70
+ .route({ method: 'GET', path: '/me/auth-token' })
71
+ .output(z.custom()),
72
+ createAuthToken: oc
73
+ .route({ method: 'POST', path: '/me/auth-token' })
74
+ .output(z.object({ token: z.string(), prefix: z.string() })),
75
+ },
76
+ };
77
+ //# sourceMappingURL=contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.js","sourceRoot":"","sources":["../../src/v1/contract.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export { contract, type V1Contract, type AssetVersionManifest } from './contract.js';
2
+ export { createClient, type MarketV1Client, type MarketV1ClientOptions } from './client.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/v1/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,KAAK,UAAU,EAAE,KAAK,oBAAoB,EAAE,MAAM,eAAe,CAAA;AACpF,OAAO,EAAE,YAAY,EAAE,KAAK,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,aAAa,CAAA"}
@@ -0,0 +1,3 @@
1
+ export { contract } from './contract.js';
2
+ export { createClient } from './client.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/v1/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAA8C,MAAM,eAAe,CAAA;AACpF,OAAO,EAAE,YAAY,EAAmD,MAAM,aAAa,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drawcall/market",
3
- "version": "0.1.98",
3
+ "version": "0.2.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/drawcall-ai/market",
@@ -37,6 +37,7 @@
37
37
  "dependencies": {
38
38
  "@orpc/client": "^1.0.0",
39
39
  "@orpc/contract": "^1.0.0",
40
+ "@orpc/openapi-client": "1.13.14",
40
41
  "chalk": "^5.6.2",
41
42
  "commander": "^14.0.3",
42
43
  "fflate": "^0.8.2",
@@ -31,7 +31,9 @@ Install exact refs together in one call; names need no `--type`:
31
31
 
32
32
  Install prints the exact files it wrote and records the dependency so packing and later rebuilds can restore it. It never searches or generates. With no refs it installs `assetDependencies` from the nearest `package.json`. Assets may install declared skill dependencies with `npx skills add`. Use `--force` only with permission to overwrite changed local files.
33
33
 
34
- `list --cwd "$PWD"` is an offline, compact inventory from `.drawcall/market-lock.json`. Add `--files` when exact paths for previously installed assets are needed; fresh install and generation already print their paths. When selected refs are already installed, read this inventory instead of reinstalling them.
34
+ `list --cwd "$PWD"` is an offline, compact inventory of the `assetDependencies` in the nearest `package.json`, including file aliases. When selected refs are already installed, read this inventory instead of reinstalling them.
35
+
36
+ An `assetDependencies` entry is either a range string or `{ "version": "<range>", "alias": { "<canonical path>": "<current path>" } }`. An alias records where the project keeps a renamed installed file: install writes the file at the aliased path, and packing omits exactly that one copy (extra duplicates ship as project content). Run `sync --cwd "$PWD"` after renaming installed files to record aliases in `package.json`; template packing runs the same update automatically.
35
37
 
36
38
  ## Generate
37
39
 
@@ -47,6 +49,6 @@ Add `--access public` or `--access private` when visibility matters. Omitted acc
47
49
 
48
50
  ## Publish only when asked
49
51
 
50
- Use `pack <source> --out <zip>` to create a Market zip and `upload <name> <source> "<description>" --type <type>` to publish it. Both accept repeatable `--npm name@range`, `--asset name@range`, and `--skill label=source` dependencies. Template packing reads root `assetDependencies` and may use the API to omit unchanged installed dependency files. A skill source is `owner/repo`, a git URL, a full GitHub `tree/<branch>/<subpath>` URL, or a local skill directory inside the zip. Use the command's `--help` for syntax.
52
+ Use `pack <source> --out <zip>` to create a Market zip and `upload <name> <source> "<description>" --type <type>` to publish it. Both accept repeatable `--npm name@range`, `--asset name@range`, and `--skill label=source` dependencies. Template packing reads root `assetDependencies` and may use the API to omit installed dependency files matched by content hash — even when renamed — recording renames as aliases. A skill source is `owner/repo`, a git URL, a full GitHub `tree/<branch>/<subpath>` URL, or a local skill directory inside the zip. Use the command's `--help` for syntax.
51
53
 
52
54
  Use `--unapproved` only when explicitly requested. Do not install an unapproved asset without acceptance. If login is required, ask before running `npx @drawcall/market login`.
@@ -6,7 +6,7 @@ import type {
6
6
  GenerateAssetResult,
7
7
  PaginatedList,
8
8
  } from './contract.js'
9
- import type { AssetAccess, AssetType, GenerateJobStatus } from './schemas.js'
9
+ import type { AssetAccess, AssetDependencies, AssetType, GenerateJobStatus } from './schemas.js'
10
10
 
11
11
  export const ASSET_RELEVANCE_SCORE: unique symbol = Symbol('assetRelevanceScore')
12
12
 
@@ -38,7 +38,7 @@ export interface AssetUploadZipInput {
38
38
  version: string
39
39
  description?: string
40
40
  npmDependencies: Record<string, string>
41
- assetDependencies: Record<string, string>
41
+ assetDependencies: AssetDependencies
42
42
  skillDependencies: Record<string, string>
43
43
  tags: string[]
44
44
  zip: File
@@ -46,7 +46,7 @@ export interface AssetUploadZipInput {
46
46
  access?: AssetAccess
47
47
  }
48
48
 
49
- export interface AssetDownloadZipInput {
49
+ export interface AssetVersionRef {
50
50
  name: string
51
51
  version: string
52
52
  /** The asset's capability key, an alternative credential to identity for private/unapproved assets. */
package/src/cli.ts CHANGED
@@ -13,6 +13,7 @@ import { generateCommand } from './commands/generate.js'
13
13
  import { generateInstallCommand } from './commands/generate-install.js'
14
14
  import { listCommand } from './commands/list.js'
15
15
  import { packCommand } from './commands/pack.js'
16
+ import { syncCommand } from './commands/sync.js'
16
17
  import { previewCommand } from './commands/preview.js'
17
18
  import { uploadCommand } from './commands/upload.js'
18
19
  import { typesCommand } from './commands/types.js'
@@ -125,13 +126,25 @@ program
125
126
 
126
127
  program
127
128
  .command('list')
128
- .description('List locally installed assets')
129
- .option('--files', 'Include installed file paths', false)
129
+ .description('List asset dependencies declared in package.json, including file aliases')
130
130
  .option('--cwd <dir>', 'Project directory')
131
- .action(async (opts: { cwd?: string; files: boolean }) => {
131
+ .action(async (opts: { cwd?: string }) => {
132
132
  await listCommand({
133
133
  cwd: opts.cwd,
134
- files: opts.files,
134
+ })
135
+ })
136
+
137
+ program
138
+ .command('sync')
139
+ .description(
140
+ 'Update package.json assetDependencies aliases to match where installed files live now',
141
+ )
142
+ .addOption(apiOption)
143
+ .option('--cwd <dir>', 'Project directory')
144
+ .action(async (opts: { api?: string; cwd?: string }) => {
145
+ await syncCommand({
146
+ baseUrl: opts.api,
147
+ cwd: opts.cwd,
135
148
  })
136
149
  })
137
150
 
@@ -176,6 +189,7 @@ program
176
189
  .addOption(apiOption)
177
190
  .option('--unapproved', 'Include unapproved versions', false)
178
191
  .option('--limit <n>', 'Max results, 1-5', parseSearchLimit, 5)
192
+ .option('--json', 'Emit a machine-readable JSON array instead of the summary lines', false)
179
193
  .action(
180
194
  async (
181
195
  query: string,
@@ -184,6 +198,7 @@ program
184
198
  api?: string
185
199
  unapproved: boolean
186
200
  limit: number
201
+ json: boolean
187
202
  },
188
203
  ) => {
189
204
  requireType(opts.type, 'Search')
@@ -192,6 +207,7 @@ program
192
207
  baseUrl: opts.api,
193
208
  unapproved: opts.unapproved,
194
209
  limit: opts.limit,
210
+ json: opts.json,
195
211
  })
196
212
  },
197
213
  )
package/src/client.ts CHANGED
@@ -26,3 +26,29 @@ export function createMarketClient(opts: MarketClientOptions = {}): MarketClient
26
26
  })
27
27
  return createORPCClient<MarketClient>(link)
28
28
  }
29
+
30
+ /**
31
+ * The `GET /download` zip view of a version as bytes. The stream is deterministic (store-mode
32
+ * entries, fixed mtimes), so two calls for the same immutable version are byte-identical — which is
33
+ * what upload's "unchanged, skip publishing" check compares against.
34
+ */
35
+ export async function downloadAssetZipBytes(opts: {
36
+ name: string
37
+ version: string
38
+ baseUrl?: string
39
+ authToken?: string
40
+ /** The asset's capability key, an alternative credential to identity for private/unapproved assets. */
41
+ key?: string
42
+ fetch?: typeof globalThis.fetch
43
+ }): Promise<Uint8Array> {
44
+ const url = new URL('/download', opts.baseUrl ?? DEFAULT_BASE_URL)
45
+ url.searchParams.set('name', opts.name)
46
+ url.searchParams.set('version', opts.version)
47
+ if (opts.key) url.searchParams.set('key', opts.key)
48
+ const doFetch = opts.fetch ?? globalThis.fetch
49
+ const response = await doFetch(url.toString(), { headers: buildHeaders(opts.authToken) })
50
+ if (!response.ok) {
51
+ throw new Error(`Download of ${opts.name}@${opts.version} failed (${response.status})`)
52
+ }
53
+ return new Uint8Array(await response.arrayBuffer())
54
+ }
@@ -4,7 +4,7 @@ import ora from 'ora'
4
4
  import { resolve } from '../resolve.js'
5
5
  import { findInstallRoot, install as runInstall, type InstallRootRequest } from '../install.js'
6
6
  import { packageJsonAssetDependencies } from '../package-json.js'
7
- import { assetNameSchema } from '../schemas.js'
7
+ import { assetDependencyRange, assetNameSchema, type AssetDependencies } from '../schemas.js'
8
8
  import { getCliClient } from '../cli-client.js'
9
9
  import { installResult } from '../output.js'
10
10
  import type { MarketClient } from '../client.js'
@@ -125,12 +125,15 @@ async function packageJsonRequests(cwd: string | undefined): Promise<AssetReques
125
125
  const installRoot = await findInstallRoot(cwd ?? process.cwd())
126
126
  const pkgPath = path.join(installRoot, 'package.json')
127
127
  const dependencies = await readPackageJsonAssetDependencies(pkgPath)
128
- const requests = Object.entries(dependencies).map(([name, range]) => ({
129
- name,
130
- range,
131
- saveRange: range,
132
- save: false,
133
- }))
128
+ const requests = Object.entries(dependencies).map(([name, value]) => {
129
+ const range = assetDependencyRange(value)
130
+ return {
131
+ name,
132
+ range,
133
+ saveRange: range,
134
+ save: false,
135
+ }
136
+ })
134
137
 
135
138
  if (requests.length === 0) {
136
139
  throw new Error(
@@ -141,7 +144,7 @@ async function packageJsonRequests(cwd: string | undefined): Promise<AssetReques
141
144
  return requests
142
145
  }
143
146
 
144
- async function readPackageJsonAssetDependencies(pkgPath: string): Promise<Record<string, string>> {
147
+ async function readPackageJsonAssetDependencies(pkgPath: string): Promise<AssetDependencies> {
145
148
  try {
146
149
  return packageJsonAssetDependencies(await fs.readFile(pkgPath, 'utf-8'), pkgPath)
147
150
  } catch (error) {
@@ -1,48 +1,62 @@
1
+ import * as fs from 'fs/promises'
1
2
  import * as path from 'path'
2
3
  import { findInstallRoot } from '../install.js'
3
- import { MARKET_LOCK_PATH, readMarketLock } from '../market-lock.js'
4
+ import { packageJsonAssetDependencies } from '../package-json.js'
5
+ import { assetDependencyAlias, assetDependencyRange } from '../schemas.js'
4
6
  import { listResult } from '../output.js'
5
7
 
6
8
  export interface ListCommandOptions {
7
9
  cwd?: string
8
- files?: boolean
9
10
  }
10
11
 
11
12
  export interface InstalledAssetListing {
12
13
  name: string
13
- version: string
14
- type: string
15
- files: string[]
14
+ range: string
15
+ /** Canonical install path → the project's current path, for files renamed after install. */
16
+ alias: Record<string, string>
16
17
  }
17
18
 
18
19
  export interface ListInstalledAssetsResult {
19
20
  projectRoot: string
20
- lockPath: string
21
+ packageJsonPath: string
21
22
  assets: InstalledAssetListing[]
22
23
  }
23
24
 
24
25
  export async function listCommand(opts: ListCommandOptions): Promise<void> {
25
26
  const result = await listInstalledAssets(opts.cwd)
26
- console.log(listResult(result, { files: opts.files }))
27
+ console.log(listResult(result))
27
28
  }
28
29
 
30
+ /** The project's declared asset dependencies — package.json is the single source of truth. */
29
31
  export async function listInstalledAssets(
30
32
  cwd: string | undefined,
31
33
  ): Promise<ListInstalledAssetsResult> {
32
34
  const projectRoot = await findInstallRoot(cwd ?? process.cwd())
33
- const lock = await readMarketLock(projectRoot)
34
- const assets = Object.entries(lock.assets)
35
- .map(([name, asset]) => ({
35
+ const packageJsonPath = path.join(projectRoot, 'package.json')
36
+ const dependencies = await readAssetDependencies(packageJsonPath)
37
+ const assets = Object.entries(dependencies)
38
+ .map(([name, value]) => ({
36
39
  name,
37
- version: asset.version,
38
- type: asset.type,
39
- files: [...asset.files].sort((a, b) => a.localeCompare(b)),
40
+ range: assetDependencyRange(value),
41
+ alias: assetDependencyAlias(value),
40
42
  }))
41
43
  .sort((a, b) => a.name.localeCompare(b.name))
42
44
 
43
45
  return {
44
46
  projectRoot,
45
- lockPath: path.join(projectRoot, MARKET_LOCK_PATH),
47
+ packageJsonPath,
46
48
  assets,
47
49
  }
48
50
  }
51
+
52
+ async function readAssetDependencies(packageJsonPath: string) {
53
+ try {
54
+ return packageJsonAssetDependencies(
55
+ await fs.readFile(packageJsonPath, 'utf-8'),
56
+ packageJsonPath,
57
+ )
58
+ } catch (error) {
59
+ if (error instanceof Error && 'code' in error && error.code === 'ENOENT') return {}
60
+ throw error
61
+ }
62
+ }
@@ -2,7 +2,12 @@ import * as fs from 'fs/promises'
2
2
  import * as path from 'path'
3
3
  import ora from 'ora'
4
4
  import { getCliClient } from '../cli-client.js'
5
- import { packAsset, packPolicyForType, parsePackDependencies } from '../pack.js'
5
+ import {
6
+ fileManifestForRange,
7
+ packAsset,
8
+ packPolicyForType,
9
+ parsePackDependencies,
10
+ } from '../pack.js'
6
11
  import { packResult } from '../output.js'
7
12
  import type { AssetType } from '../schemas.js'
8
13
 
@@ -39,7 +44,7 @@ export async function packCommand(zipFilter: string, opts: PackCommandOptions):
39
44
  skill: opts.skill,
40
45
  }),
41
46
  policy: packPolicyForType(opts.type),
42
- fetchFileManifest: (name, version) => client.asset.fileManifest({ name, version }),
47
+ fetchFileManifest: fileManifestForRange(client.asset),
43
48
  })
44
49
  const out = opts.out ? path.resolve(cwd, opts.out) : defaultPackPath(packed.sourcePath)
45
50
 
@@ -10,6 +10,8 @@ export interface SearchCommandOptions {
10
10
  unapproved?: boolean
11
11
  baseUrl?: string
12
12
  limit?: number
13
+ /** Emit a machine-readable JSON array instead of the human-readable lines. */
14
+ json?: boolean
13
15
  }
14
16
 
15
17
  export async function searchCommand(query: string, opts: SearchCommandOptions): Promise<void> {
@@ -17,8 +19,8 @@ export async function searchCommand(query: string, opts: SearchCommandOptions):
17
19
 
18
20
  const spinner = ora({
19
21
  text: `Searching "${query}"`,
20
- isEnabled: Boolean(process.stderr.isTTY),
21
- isSilent: !process.stderr.isTTY,
22
+ isEnabled: Boolean(process.stderr.isTTY) && !opts.json,
23
+ isSilent: !process.stderr.isTTY || Boolean(opts.json),
22
24
  }).start()
23
25
  const results = await client.asset
24
26
  .search({
@@ -31,6 +33,23 @@ export async function searchCommand(query: string, opts: SearchCommandOptions):
31
33
  })
32
34
  .finally(() => spinner.stop())
33
35
 
36
+ if (opts.json) {
37
+ // Stable machine-readable shape for scripts; the human-readable lines below may change freely.
38
+ console.log(
39
+ JSON.stringify(
40
+ results.items.map((item) => ({
41
+ name: item.name,
42
+ version: item.latestVersion,
43
+ type: item.type,
44
+ approved: item.approved,
45
+ access: item.access,
46
+ description: item.description,
47
+ })),
48
+ ),
49
+ )
50
+ return
51
+ }
52
+
34
53
  console.log(
35
54
  searchSummary({
36
55
  query,
@@ -0,0 +1,30 @@
1
+ import ora from 'ora'
2
+ import { getCliClient } from '../cli-client.js'
3
+ import { fileManifestForRange, syncAssetDependencies } from '../pack.js'
4
+ import { syncResult } from '../output.js'
5
+
6
+ export interface SyncCommandOptions {
7
+ cwd?: string
8
+ baseUrl?: string
9
+ }
10
+
11
+ export async function syncCommand(opts: SyncCommandOptions): Promise<void> {
12
+ const spinner = ora({
13
+ text: 'Syncing assetDependencies',
14
+ isEnabled: Boolean(process.stderr.isTTY),
15
+ isSilent: !process.stderr.isTTY,
16
+ }).start()
17
+
18
+ try {
19
+ const { client } = await getCliClient({ baseUrl: opts.baseUrl })
20
+ const result = await syncAssetDependencies(
21
+ opts.cwd ?? process.cwd(),
22
+ fileManifestForRange(client.asset),
23
+ )
24
+ spinner.stop()
25
+ console.log(syncResult(result))
26
+ } catch (err) {
27
+ spinner.stop()
28
+ throw err
29
+ }
30
+ }
@@ -1,7 +1,13 @@
1
1
  import ora from 'ora'
2
2
  import semver from 'semver'
3
+ import { downloadAssetZipBytes } from '../client.js'
3
4
  import { getCliClient } from '../cli-client.js'
4
- import { packAsset, packPolicyFromInstallMetadata, parsePackDependencies } from '../pack.js'
5
+ import {
6
+ fileManifestForRange,
7
+ packAsset,
8
+ packPolicyFromInstallMetadata,
9
+ parsePackDependencies,
10
+ } from '../pack.js'
5
11
  import { unchangedUploadResult, uploadResult } from '../output.js'
6
12
  import {
7
13
  assetDescriptionSchema,
@@ -51,13 +57,16 @@ export async function uploadCommand(
51
57
  })
52
58
  const cwd = opts.cwd ?? process.cwd()
53
59
  const type = opts.type
54
- const { client } = await getCliClient({ baseUrl: opts.baseUrl, requireAuth: true })
60
+ const { client, baseUrl, authToken } = await getCliClient({
61
+ baseUrl: opts.baseUrl,
62
+ requireAuth: true,
63
+ })
55
64
  const installMetadata = await client.asset.installMetadata()
56
65
  const packed = await packAsset(zipFilter, {
57
66
  cwd,
58
67
  dependencies,
59
68
  policy: packPolicyFromInstallMetadata(installMetadata[type]),
60
- fetchFileManifest: (name, version) => client.asset.fileManifest({ name, version }),
69
+ fetchFileManifest: fileManifestForRange(client.asset),
61
70
  })
62
71
  const { zip, npmDependencies, assetDependencies, skillDependencies } = packed
63
72
  const profile = await client.user.getProfile()
@@ -75,11 +84,12 @@ export async function uploadCommand(
75
84
  }
76
85
 
77
86
  if (existing) {
78
- const latestZip = await client.asset.downloadZip({
87
+ const latestBytes = await downloadAssetZipBytes({
79
88
  name: parsedName,
80
89
  version: existing.latestVersion,
90
+ baseUrl,
91
+ authToken,
81
92
  })
82
- const latestBytes = new Uint8Array(await latestZip.arrayBuffer())
83
93
  if (
84
94
  existing.description === parsedDescription &&
85
95
  depsEqual(existing.npmDependencies, npmDependencies) &&
@@ -128,16 +138,21 @@ export function parseUploadDescription(description: string): string {
128
138
  }
129
139
  }
130
140
 
131
- /** Compare a stored dependency JSON string against a freshly parsed record. */
132
- function depsEqual(storedJson: string, next: Record<string, string>): boolean {
133
- let stored: Record<string, string>
141
+ /** Compare a stored dependency JSON string against a freshly parsed record. Values may be nested
142
+ * (asset dependencies carry `{ version, alias }`), so equality is structural with sorted keys. */
143
+ function depsEqual(storedJson: string, next: Record<string, unknown>): boolean {
134
144
  try {
135
- stored = JSON.parse(storedJson || '{}') as Record<string, string>
145
+ return stableJson(JSON.parse(storedJson || '{}')) === stableJson(next)
136
146
  } catch {
137
147
  return false
138
148
  }
139
- const keys = Object.keys(stored)
140
- return keys.length === Object.keys(next).length && keys.every((key) => stored[key] === next[key])
149
+ }
150
+
151
+ function stableJson(value: unknown): string {
152
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return JSON.stringify(value)
153
+ const record = value as Record<string, unknown>
154
+ const keys = Object.keys(record).sort()
155
+ return `{${keys.map((key) => `${JSON.stringify(key)}:${stableJson(record[key])}`).join(',')}}`
141
156
  }
142
157
 
143
158
  function nextVersion(latest?: string): string {
package/src/contract.ts CHANGED
@@ -7,7 +7,6 @@ import {
7
7
  agentStartSchema,
8
8
  agentStatusSchema,
9
9
  assetFilesSchema,
10
- downloadZipSchema,
11
10
  exactAssetSchema,
12
11
  fileManifestSchema,
13
12
  generateAssetSchema,
@@ -147,8 +146,6 @@ export const contract = {
147
146
  )
148
147
  .output(z.custom<AssetVersion>()),
149
148
 
150
- downloadZip: oc.input(downloadZipSchema).output(z.instanceof(Blob)),
151
-
152
149
  // The derived file index of a version — every stored file's path, size and md5 etag plus the
153
150
  // fetch-ready data-plane URLs, so installs fetch files in parallel through the same edge cache
154
151
  // browsers warm.
package/src/index.ts CHANGED
@@ -1,5 +1,9 @@
1
+ // The v1 REST surface (`/api/v1`): routed contract + typed client. Prefer this; the legacy RPC
2
+ // client below is deprecated and retires together with `/api/rpc`.
3
+ export * as v1 from './v1/index.js'
4
+
1
5
  // Client
2
- export { createMarketClient } from './client.js'
6
+ export { createMarketClient, downloadAssetZipBytes } from './client.js'
3
7
  export type { MarketClient, MarketClientOptions } from './client.js'
4
8
 
5
9
  // Contracts
@@ -21,7 +25,7 @@ export type {
21
25
  } from './contract.js'
22
26
 
23
27
  export type {
24
- AssetDownloadZipInput,
28
+ AssetVersionRef,
25
29
  AssetExactInput,
26
30
  AssetGenerateInput,
27
31
  AssetImplementation,
@@ -62,9 +66,13 @@ export {
62
66
  assetAccessSchema,
63
67
  npmDependenciesSchema,
64
68
  assetDependenciesSchema,
69
+ assetDependencyAliasSchema,
70
+ assetDependencyValueSchema,
71
+ assetDependencyRange,
72
+ assetDependencyAlias,
73
+ assetDependencyValue,
65
74
  skillDependenciesSchema,
66
75
  uploadZipSchema,
67
- downloadZipSchema,
68
76
  exactAssetSchema,
69
77
  fileManifestSchema,
70
78
  generateAssetSchema,
@@ -83,6 +91,8 @@ export {
83
91
  export type {
84
92
  AssetType,
85
93
  AssetAccess,
94
+ AssetDependencies,
95
+ AssetDependencyValue,
86
96
  AssetFileManifest,
87
97
  AgentResultAsset,
88
98
  AgentResult,