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 +56 -22
- package/lib/commands/info.js +4 -4
- package/lib/commands/install.js +5 -4
- package/lib/commands/search.js +6 -6
- package/package.json +1 -1
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/
|
|
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?.
|
|
138
|
+
const result = await get('/api/v1/assets', { q: slug, type, limit: 50 });
|
|
139
|
+
const assets = result?.items || [];
|
|
131
140
|
|
|
132
|
-
// Exact match on
|
|
133
|
-
let matches = assets.filter(a => a.
|
|
141
|
+
// Exact match on name
|
|
142
|
+
let matches = assets.filter(a => a.name === slug);
|
|
134
143
|
if (authorFilter) {
|
|
135
|
-
|
|
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.
|
|
153
|
+
matches = assets.filter(a => a.name.includes(slug));
|
|
142
154
|
if (authorFilter) {
|
|
143
|
-
const authorMatches = matches.filter(a =>
|
|
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.
|
|
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:
|
|
188
|
-
if (/^
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
};
|
package/lib/commands/info.js
CHANGED
|
@@ -27,8 +27,9 @@ async function run(args) {
|
|
|
27
27
|
process.exit(1);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
const
|
|
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(`
|
|
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
|
}
|
package/lib/commands/install.js
CHANGED
|
@@ -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
|
|
171
|
-
const authorId = asset.
|
|
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...');
|
package/lib/commands/search.js
CHANGED
|
@@ -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?.
|
|
22
|
-
const total = result?.
|
|
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
|
|
35
|
-
const author = a.author
|
|
36
|
-
const authorId = a.
|
|
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)} •
|
|
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) {
|