openclawmp 0.1.2 → 0.1.3

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/lib/api.js CHANGED
@@ -1,6 +1,7 @@
1
1
  // ============================================================================
2
2
  // api.js — HTTP request helpers for the OpenClaw Marketplace API
3
3
  //
4
+ // Uses V1 API endpoints for lightweight responses (AssetCompact).
4
5
  // Uses Node.js built-in fetch (available since Node 18)
5
6
  // ============================================================================
6
7
 
@@ -22,7 +23,7 @@ function authHeaders() {
22
23
 
23
24
  /**
24
25
  * Make a GET request to the API
25
- * @param {string} apiPath - API path (e.g., '/api/assets')
26
+ * @param {string} apiPath - API path (e.g., '/api/v1/assets')
26
27
  * @param {object} [params] - Query parameters
27
28
  * @returns {Promise<object>} Parsed JSON response
28
29
  */
@@ -107,40 +108,53 @@ async function download(apiPath) {
107
108
  }
108
109
 
109
110
  /**
110
- * Search assets
111
+ * Search assets via V1 API (returns lightweight AssetCompact items).
112
+ *
113
+ * V1 response shape: { query, total, items: AssetCompact[], nextCursor }
114
+ * AssetCompact fields: id, name, displayName, type, description, tags,
115
+ * installs, rating, author (string), authorId, version, installCommand,
116
+ * updatedAt, category
117
+ *
111
118
  * @param {string} query
112
119
  * @param {object} [opts] - { type, limit }
113
- * @returns {Promise<object>}
120
+ * @returns {Promise<object>} V1 search response
114
121
  */
115
122
  async function searchAssets(query, opts = {}) {
116
123
  const params = { q: query, limit: opts.limit || 20 };
117
124
  if (opts.type) params.type = opts.type;
118
- return get('/api/assets', params);
125
+ return get('/api/v1/search', params);
119
126
  }
120
127
 
121
128
  /**
122
- * Find an asset by type and slug (with optional author filter)
129
+ * Find an asset by type and slug (with optional author filter).
130
+ * Uses V1 list endpoint for lightweight data.
131
+ *
123
132
  * @param {string} type
124
133
  * @param {string} slug
125
- * @param {string} [authorFilter]
134
+ * @param {string} [authorFilter] - author ID or author name to filter by
126
135
  * @returns {Promise<object|null>}
127
136
  */
128
137
  async function findAsset(type, slug, authorFilter) {
129
- const result = await get('/api/assets', { q: slug, limit: 50 });
130
- const assets = result?.data?.assets || [];
138
+ const result = await get('/api/v1/assets', { q: slug, type, limit: 50 });
139
+ const assets = result?.items || [];
131
140
 
132
- // Exact match on type + name
133
- let matches = assets.filter(a => a.type === type && a.name === slug);
141
+ // Exact match on name
142
+ let matches = assets.filter(a => a.name === slug);
134
143
  if (authorFilter) {
135
- const authorMatches = matches.filter(a => (a.author?.id || '') === authorFilter);
144
+ // authorFilter could be an authorId or author name
145
+ const authorMatches = matches.filter(a =>
146
+ a.authorId === authorFilter || a.author === authorFilter
147
+ );
136
148
  if (authorMatches.length > 0) matches = authorMatches;
137
149
  }
138
150
 
139
- // Fallback: partial match
151
+ // Fallback: partial match on name
140
152
  if (matches.length === 0) {
141
- matches = assets.filter(a => a.type === type && a.name.includes(slug));
153
+ matches = assets.filter(a => a.name.includes(slug));
142
154
  if (authorFilter) {
143
- const authorMatches = matches.filter(a => (a.author?.id || '') === authorFilter);
155
+ const authorMatches = matches.filter(a =>
156
+ a.authorId === authorFilter || a.author === authorFilter
157
+ );
144
158
  if (authorMatches.length > 0) matches = authorMatches;
145
159
  }
146
160
  }
@@ -148,10 +162,26 @@ async function findAsset(type, slug, authorFilter) {
148
162
  if (matches.length === 0) return null;
149
163
 
150
164
  // Prefer the one with an author ID
151
- matches.sort((a, b) => (b.author?.id || '').localeCompare(a.author?.id || ''));
165
+ matches.sort((a, b) => (b.authorId || '').localeCompare(a.authorId || ''));
152
166
  return matches[0];
153
167
  }
154
168
 
169
+ /**
170
+ * Get asset detail (L2) by ID via V1 API.
171
+ * Returns full detail including readme, files, versions.
172
+ *
173
+ * @param {string} id - Asset ID
174
+ * @returns {Promise<object|null>}
175
+ */
176
+ async function getAssetById(id) {
177
+ try {
178
+ return await get(`/api/v1/assets/${id}`);
179
+ } catch (e) {
180
+ if (e.message.includes('404')) return null;
181
+ throw e;
182
+ }
183
+ }
184
+
155
185
  /**
156
186
  * Make a DELETE request to the API
157
187
  * @param {string} apiPath
@@ -178,18 +208,21 @@ async function del(apiPath, body) {
178
208
  /**
179
209
  * Resolve an asset reference to a full asset object.
180
210
  * Accepts:
181
- * - Direct ID: "tr-fc617094de29f938"
211
+ * - Direct ID: "s-abc123", "tr-fc617094de29f938"
182
212
  * - type/@author/slug: "trigger/@xiaoyue/pdf-watcher"
213
+ *
214
+ * Uses V1 API: GET /api/v1/assets/:id for ID lookups,
215
+ * findAsset() (V1 search) for type/slug lookups.
216
+ *
183
217
  * @param {string} ref
184
218
  * @returns {Promise<object>} asset object with at least { id, name, ... }
185
219
  */
186
220
  async function resolveAssetRef(ref) {
187
- // Direct ID pattern: known prefix + dash + hex (sk-/pl-/tr-/ch-/ex-)
188
- if (/^(sk|pl|tr|ch|ex)-[0-9a-f]+$/.test(ref)) {
189
- // Fetch by ID directly
190
- const result = await get(`/api/assets/${ref}`);
191
- // API may return { data: { asset } } or the asset directly
192
- return result?.data?.asset || result?.data || result;
221
+ // Direct ID pattern: prefix + dash + hex
222
+ if (/^[a-z]+-[0-9a-f]{8,}$/.test(ref)) {
223
+ const result = await getAssetById(ref);
224
+ if (!result) throw new Error(`Asset not found: ${ref}`);
225
+ return result;
193
226
  }
194
227
 
195
228
  // type/@author/slug format
@@ -223,5 +256,6 @@ module.exports = {
223
256
  download,
224
257
  searchAssets,
225
258
  findAsset,
259
+ getAssetById,
226
260
  resolveAssetRef,
227
261
  };
@@ -27,8 +27,9 @@ async function run(args) {
27
27
  process.exit(1);
28
28
  }
29
29
 
30
- const authorName = asset.author?.name || 'unknown';
31
- const authorId = asset.author?.id || '';
30
+ // V1 AssetCompact: author is a string, authorId is separate
31
+ const authorName = asset.author || 'unknown';
32
+ const authorId = asset.authorId || '';
32
33
  const tags = (asset.tags || []).join(', ');
33
34
 
34
35
  console.log('');
@@ -38,8 +39,7 @@ async function run(args) {
38
39
  console.log(` Package: ${asset.name}`);
39
40
  console.log(` Version: ${asset.version}`);
40
41
  console.log(` Author: ${c('cyan', authorName)} ${c('dim', `(${authorId})`)}`);
41
- console.log(` Score: ${asset.hubScore || 0}`);
42
- console.log(` Downloads: ${asset.downloads || 0}`);
42
+ console.log(` Installs: ${asset.installs || 0}`);
43
43
  if (tags) {
44
44
  console.log(` Tags: ${tags}`);
45
45
  }
@@ -106,7 +106,8 @@ function writeManifest(asset, targetDir, hasPackage) {
106
106
  name: asset.name,
107
107
  displayName: asset.displayName || '',
108
108
  version: asset.version,
109
- author: asset.author,
109
+ author: asset.author || '',
110
+ authorId: asset.authorId || '',
110
111
  description: asset.description || '',
111
112
  tags: asset.tags || [],
112
113
  category: asset.category || '',
@@ -167,8 +168,8 @@ async function run(args, flags) {
167
168
 
168
169
  const displayName = asset.displayName || asset.name;
169
170
  const version = asset.version;
170
- const authorName = asset.author?.name || 'unknown';
171
- const authorId = asset.author?.id || '';
171
+ const authorName = asset.author || 'unknown';
172
+ const authorId = asset.authorId || '';
172
173
 
173
174
  console.log(` ${c('bold', displayName)} ${c('dim', `v${version}`)}`);
174
175
  console.log(` by ${c('cyan', authorName)} ${c('dim', `(${authorId})`)}`);
@@ -203,7 +204,7 @@ async function run(args, flags) {
203
204
 
204
205
  // Try downloading the actual package
205
206
  let hasPackage = false;
206
- const pkgBuffer = await api.download(`/api/assets/${asset.id}/download`);
207
+ const pkgBuffer = await api.download(`/api/v1/assets/${asset.id}/download`);
207
208
 
208
209
  if (pkgBuffer && pkgBuffer.length > 0) {
209
210
  info('📦 Downloading package from registry...');
@@ -18,8 +18,8 @@ async function run(args) {
18
18
  console.log('');
19
19
 
20
20
  const result = await api.searchAssets(query);
21
- const assets = result?.data?.assets || [];
22
- const total = result?.data?.total || 0;
21
+ const assets = result?.items || [];
22
+ const total = result?.total || 0;
23
23
 
24
24
  if (assets.length === 0) {
25
25
  console.log(' No results found.');
@@ -31,12 +31,12 @@ async function run(args) {
31
31
 
32
32
  for (const a of assets) {
33
33
  const icon = typeIcon(a.type);
34
- const score = a.hubScore || 0;
35
- const author = a.author?.name || 'unknown';
36
- const authorId = a.author?.id || 'unknown';
34
+ const installs = a.installs || 0;
35
+ const author = a.author || 'unknown';
36
+ const authorId = a.authorId || 'unknown';
37
37
 
38
38
  console.log(` ${icon} ${c('bold', a.displayName)}`);
39
- console.log(` ${a.type}/@${authorId}/${a.name} • v${a.version} • by ${c('cyan', author)} • Score: ${score}`);
39
+ console.log(` ${a.type}/@${authorId}/${a.name} • v${a.version} • by ${c('cyan', author)} • Installs: ${installs}`);
40
40
 
41
41
  const desc = (a.description || '').slice(0, 80);
42
42
  if (desc) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclawmp",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "\ud83d\udc1f OpenClaw Marketplace CLI \u2014 \u6c34\u4ea7\u5e02\u573a\u547d\u4ee4\u884c\u5de5\u5177",
5
5
  "bin": {
6
6
  "openclawmp": "./bin/openclawmp.js"