openclawmp 1.0.1 → 1.0.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/README.md +14 -41
- package/bin/openclawmp.js +3 -0
- package/lib/api.js +75 -3
- package/lib/commands/help.js +3 -0
- package/lib/commands/info.js +2 -1
- package/lib/commands/install.js +2 -1
- package/lib/commands/search.js +5 -1
- package/lib/commands/users.js +247 -0
- package/lib/config.js +1 -1
- package/lib/help.js +6 -0
- package/lib/publish-flow.js +4 -2
- package/lib/utils.js +2 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**OpenClaw Marketplace CLI** — 水产市场命令行工具
|
|
4
4
|
|
|
5
|
-
A command-line client for the [OpenClaw Marketplace](https://openclawmp.
|
|
5
|
+
A command-line client for the [OpenClaw Marketplace](https://openclawmp.stepfun.com), allowing you to search, install, publish, and manage agent assets (skills, plugins, triggers, channels, and more).
|
|
6
6
|
|
|
7
7
|
## 安装
|
|
8
8
|
|
|
@@ -21,15 +21,15 @@ openclawmp oauth <your-token>
|
|
|
21
21
|
# 搜索市场资产
|
|
22
22
|
openclawmp search 天气
|
|
23
23
|
|
|
24
|
+
# 搜索用户
|
|
25
|
+
openclawmp users 张三
|
|
26
|
+
|
|
24
27
|
# 根据 assetId 查看资产详情
|
|
25
28
|
openclawmp info 6c476a36955a4270a3fa303beeeed5ee
|
|
26
29
|
|
|
27
30
|
# 安装资产
|
|
28
31
|
openclawmp install skill/7c19dc4c3244418096f1dcb59c93f795
|
|
29
32
|
|
|
30
|
-
# 安装指定版本
|
|
31
|
-
openclawmp install skill/7c19dc4c3244418096f1dcb59c93f795@1.0.3
|
|
32
|
-
|
|
33
33
|
# 列出本地已安装资产
|
|
34
34
|
openclawmp list
|
|
35
35
|
|
|
@@ -43,6 +43,7 @@ openclawmp publish ./my-asset --yes
|
|
|
43
43
|
## 功能概览
|
|
44
44
|
|
|
45
45
|
- `search`:搜索市场资产,按类型、作者、安装量、标签和简介格式化输出。
|
|
46
|
+
- `users`:搜索用户,默认展示前 10 条,并显示总结果数与下一页游标。
|
|
46
47
|
- `oauth`:将 token 写入本地凭证文件。
|
|
47
48
|
- `info`:根据 `assetId` 查看资产详情与版本信息。
|
|
48
49
|
- `install`:根据 `assetId` 和可选 `semver` 下载归档并安装到本地 OpenClaw 目录。
|
|
@@ -106,12 +107,12 @@ cat .metadata.json
|
|
|
106
107
|
|
|
107
108
|
```json
|
|
108
109
|
{
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
110
|
+
"assetType": "skill",
|
|
111
|
+
"name": "my-skill",
|
|
112
|
+
"displayName": "My Skill",
|
|
113
|
+
"semver": "1.0.0",
|
|
114
|
+
"category": "productivity",
|
|
115
|
+
"tags": ["agent", "automation"]
|
|
115
116
|
}
|
|
116
117
|
```
|
|
117
118
|
|
|
@@ -157,6 +158,7 @@ openclawmp uninstall skill/demo-skill
|
|
|
157
158
|
|
|
158
159
|
```bash
|
|
159
160
|
openclawmp search 天气 --page-size 10
|
|
161
|
+
openclawmp users openclawmp.cc
|
|
160
162
|
```
|
|
161
163
|
|
|
162
164
|
## 命令结构
|
|
@@ -166,12 +168,13 @@ openclawmp search 天气 --page-size 10
|
|
|
166
168
|
- `publish`
|
|
167
169
|
- `oauth`
|
|
168
170
|
- `search`
|
|
171
|
+
- `users`
|
|
169
172
|
- `info`
|
|
170
173
|
- `install`
|
|
171
174
|
- `uninstall`
|
|
172
175
|
- `list`
|
|
173
176
|
|
|
174
|
-
其中当前可用的是 `publish`、`oauth`、`search`、`info`、`install`、`uninstall`、`list`。
|
|
177
|
+
其中当前可用的是 `publish`、`oauth`、`search`、`users`、`info`、`install`、`uninstall`、`list`。
|
|
175
178
|
|
|
176
179
|
## 发布命令参数
|
|
177
180
|
|
|
@@ -184,33 +187,3 @@ openclawmp search 天气 --page-size 10
|
|
|
184
187
|
- `--dry-run`
|
|
185
188
|
- `--yes, -y`
|
|
186
189
|
- `--verbose`
|
|
187
|
-
|
|
188
|
-
## 目录发布打包规则
|
|
189
|
-
|
|
190
|
-
- 目录发布会默认跳过 `.git`、`node_modules`、`__MACOSX`、`.DS_Store`、`Thumbs.db`
|
|
191
|
-
- 根目录可通过 `.openclawmpignore` 补充忽略规则,也会读取根目录 `.gitignore` / `.npmignore` 的常见规则
|
|
192
|
-
- 子路径遇到无权限读取的目录或文件时会自动跳过;使用 `--verbose` 可查看具体跳过项
|
|
193
|
-
|
|
194
|
-
## Publish Metadata
|
|
195
|
-
|
|
196
|
-
- `publish` 只读取发布目录或归档里的 `.metadata.json`
|
|
197
|
-
- `.metadata.json` 必须包含 `assetType`、`name`、`displayName`、`semver`
|
|
198
|
-
- `semver` 必须是严格的 `x.y.z` 纯数字格式,例如 `1.2.3`
|
|
199
|
-
- 可选字段:`assetId`、`category`、`tags`、`description`、`longDescription`
|
|
200
|
-
- 不再从 `README.md`、`package.json`、`openclaw.plugin.json`、`SKILL.md` 自动推断发布 metadata
|
|
201
|
-
- 不再通过 CLI 参数补录 `assetType`、`name`、`displayName`、`semver`
|
|
202
|
-
- `assetId` 可写在 `.metadata.json`,也可以通过 `--asset-id` 或目录根部 `.assetid` 透传
|
|
203
|
-
- `objectId` 由上传完成后自动生成,登录态始终必需
|
|
204
|
-
- 发布流程不再打印请求预览或请求头明细;终端仅显示操作类型与 request-id
|
|
205
|
-
|
|
206
|
-
## 默认配置来源
|
|
207
|
-
|
|
208
|
-
`baseUrl` 优先级:
|
|
209
|
-
|
|
210
|
-
1. `--base-url`
|
|
211
|
-
2. `OPENCLAWMP_API_BASE_URL`
|
|
212
|
-
3. `OPENCLAWMP_BASE_URL`
|
|
213
|
-
4. `API_BASE_URL`
|
|
214
|
-
5. `NEXT_PUBLIC_API_BASE_URL`
|
|
215
|
-
6. 仓库根目录 `.env.local` / `.env` / `.env.prod`
|
|
216
|
-
7. `https://seafood.c.stepfun-inc.net`
|
package/bin/openclawmp.js
CHANGED
|
@@ -24,6 +24,9 @@ const COMMAND_LOADERS = {
|
|
|
24
24
|
search: function loadSearch() {
|
|
25
25
|
return require("../lib/commands/search");
|
|
26
26
|
},
|
|
27
|
+
users: function loadUsers() {
|
|
28
|
+
return require("../lib/commands/users");
|
|
29
|
+
},
|
|
27
30
|
info: function loadInfo() {
|
|
28
31
|
return require("../lib/commands/info");
|
|
29
32
|
},
|
package/lib/api.js
CHANGED
|
@@ -10,6 +10,7 @@ const {
|
|
|
10
10
|
} = require("./utils");
|
|
11
11
|
|
|
12
12
|
const CATALOG_SERVICE = "step.seafood.catalog.CatalogService";
|
|
13
|
+
const IDENTITY_SERVICE = "step.seafood.identity.IdentityService";
|
|
13
14
|
|
|
14
15
|
class ApiError extends Error {
|
|
15
16
|
constructor(message, details) {
|
|
@@ -79,14 +80,68 @@ function detectTokenPrefix(token) {
|
|
|
79
80
|
return text;
|
|
80
81
|
}
|
|
81
82
|
|
|
83
|
+
function stringifyVerboseValue(value) {
|
|
84
|
+
if (value === undefined) {
|
|
85
|
+
return "";
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (typeof value === "string") {
|
|
89
|
+
const text = value.trim();
|
|
90
|
+
if (!text) {
|
|
91
|
+
return "\"\"";
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (text.startsWith("{") || text.startsWith("[") || text === "null") {
|
|
95
|
+
try {
|
|
96
|
+
return JSON.stringify(JSON.parse(text), null, 2);
|
|
97
|
+
} catch {
|
|
98
|
+
return value;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return value;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
const serialized = JSON.stringify(value, jsonReplacer, 2);
|
|
107
|
+
if (serialized !== undefined) {
|
|
108
|
+
return serialized;
|
|
109
|
+
}
|
|
110
|
+
} catch {
|
|
111
|
+
return String(value);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return String(value);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function printVerboseValue(label, value, writer) {
|
|
118
|
+
const serialized = stringifyVerboseValue(value);
|
|
119
|
+
if (!serialized) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const output = typeof writer === "function" ? writer : console.log;
|
|
124
|
+
const lines = serialized.split("\n");
|
|
125
|
+
if (lines.length === 1) {
|
|
126
|
+
output(" " + label + ": " + lines[0]);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
output(" " + label + ":");
|
|
131
|
+
lines.forEach(function eachLine(line) {
|
|
132
|
+
output(" " + line);
|
|
133
|
+
});
|
|
134
|
+
}
|
|
82
135
|
|
|
83
136
|
function createCatalogClient(options) {
|
|
84
137
|
const defaultRequestId = normalizeRequestId(options.requestId) || generateRequestId();
|
|
138
|
+
const verbose = Boolean(options.verbose);
|
|
85
139
|
|
|
86
|
-
async function
|
|
140
|
+
async function unaryForService(serviceName, methodName, body, extra) {
|
|
87
141
|
const requestId = normalizeRequestId((extra && extra.requestId) || defaultRequestId);
|
|
88
142
|
const operationName = humanizeApiMethodName(methodName) || methodName || "接口调用";
|
|
89
|
-
const requestUrl = String(buildServiceUrl(options.baseUrl,
|
|
143
|
+
const requestUrl = String(buildServiceUrl(options.baseUrl, serviceName, methodName));
|
|
144
|
+
const requestBody = JSON.stringify(body || {}, jsonReplacer);
|
|
90
145
|
const headers = {
|
|
91
146
|
"Connect-Protocol-Version": "1",
|
|
92
147
|
"Content-Type": "application/json",
|
|
@@ -107,13 +162,17 @@ function createCatalogClient(options) {
|
|
|
107
162
|
printTerminalOperationNotice(operationName, [
|
|
108
163
|
requestId ? "request-id: " + requestId : ""
|
|
109
164
|
]);
|
|
165
|
+
if (verbose) {
|
|
166
|
+
printVerboseValue("请求地址", requestUrl);
|
|
167
|
+
printVerboseValue("入参", requestBody);
|
|
168
|
+
}
|
|
110
169
|
|
|
111
170
|
let response;
|
|
112
171
|
try {
|
|
113
172
|
response = await fetch(requestUrl, {
|
|
114
173
|
method: "POST",
|
|
115
174
|
headers,
|
|
116
|
-
body:
|
|
175
|
+
body: requestBody
|
|
117
176
|
});
|
|
118
177
|
} catch (error) {
|
|
119
178
|
printTerminalOperationFailure(operationName, [
|
|
@@ -149,6 +208,9 @@ function createCatalogClient(options) {
|
|
|
149
208
|
responseRequestId ? "request-id: " + responseRequestId : "",
|
|
150
209
|
message
|
|
151
210
|
]);
|
|
211
|
+
if (verbose) {
|
|
212
|
+
printVerboseValue("接口返回", payload, console.error);
|
|
213
|
+
}
|
|
152
214
|
throw new ApiError(message, {
|
|
153
215
|
status: response.status,
|
|
154
216
|
code: payload && payload.code,
|
|
@@ -162,6 +224,9 @@ function createCatalogClient(options) {
|
|
|
162
224
|
"HTTP " + response.status,
|
|
163
225
|
responseRequestId ? "request-id: " + responseRequestId : ""
|
|
164
226
|
]);
|
|
227
|
+
if (verbose) {
|
|
228
|
+
printVerboseValue("接口返回", payload);
|
|
229
|
+
}
|
|
165
230
|
|
|
166
231
|
return {
|
|
167
232
|
data: payload || {},
|
|
@@ -169,10 +234,17 @@ function createCatalogClient(options) {
|
|
|
169
234
|
};
|
|
170
235
|
}
|
|
171
236
|
|
|
237
|
+
async function unary(methodName, body, extra) {
|
|
238
|
+
return unaryForService(CATALOG_SERVICE, methodName, body, extra);
|
|
239
|
+
}
|
|
240
|
+
|
|
172
241
|
return {
|
|
173
242
|
searchAssets: function searchAssets(body, extra) {
|
|
174
243
|
return unary("SearchAssets", body, extra);
|
|
175
244
|
},
|
|
245
|
+
searchUsers: function searchUsers(body, extra) {
|
|
246
|
+
return unaryForService(IDENTITY_SERVICE, "SearchUsers", body, extra);
|
|
247
|
+
},
|
|
176
248
|
listUserAssets: function listUserAssets(body, extra) {
|
|
177
249
|
return unary("ListUserAssets", body, extra);
|
|
178
250
|
},
|
package/lib/commands/help.js
CHANGED
package/lib/commands/info.js
CHANGED
|
@@ -196,7 +196,8 @@ async function run(context) {
|
|
|
196
196
|
const client = createCatalogClient({
|
|
197
197
|
baseUrl: resolveConnectBaseUrl(parsed.options["base-url"]),
|
|
198
198
|
cliVersion: context.cliVersion,
|
|
199
|
-
requestId
|
|
199
|
+
requestId,
|
|
200
|
+
verbose: Boolean(parsed.options.verbose)
|
|
200
201
|
});
|
|
201
202
|
|
|
202
203
|
const assetResponse = await client.getAsset({
|
package/lib/commands/install.js
CHANGED
|
@@ -246,7 +246,8 @@ async function run(context) {
|
|
|
246
246
|
baseUrl: resolveConnectBaseUrl(parsed.options["base-url"]),
|
|
247
247
|
token: loadedCredentials.credentials.token,
|
|
248
248
|
cliVersion: context.cliVersion,
|
|
249
|
-
requestId
|
|
249
|
+
requestId,
|
|
250
|
+
verbose: Boolean(parsed.options.verbose)
|
|
250
251
|
});
|
|
251
252
|
|
|
252
253
|
if (parsed.options.verbose) {
|
package/lib/commands/search.js
CHANGED
|
@@ -138,6 +138,9 @@ function printSearchResult(item, index) {
|
|
|
138
138
|
if (item.latestSemver) {
|
|
139
139
|
console.log(" 版本: " + item.latestSemver);
|
|
140
140
|
}
|
|
141
|
+
if (item.assetId) {
|
|
142
|
+
console.log(" 资产ID: " + item.assetId);
|
|
143
|
+
}
|
|
141
144
|
if (item.name && item.name !== title) {
|
|
142
145
|
console.log(" 标识: " + item.name);
|
|
143
146
|
}
|
|
@@ -167,7 +170,8 @@ async function run(context) {
|
|
|
167
170
|
const client = createCatalogClient({
|
|
168
171
|
baseUrl: resolveConnectBaseUrl(parsed.options["base-url"]),
|
|
169
172
|
cliVersion: context.cliVersion,
|
|
170
|
-
requestId
|
|
173
|
+
requestId,
|
|
174
|
+
verbose: Boolean(parsed.options.verbose)
|
|
171
175
|
});
|
|
172
176
|
|
|
173
177
|
const response = await client.searchAssets({
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const { parseOptions } = require("../cli-parser");
|
|
4
|
+
const { createCatalogClient } = require("../api");
|
|
5
|
+
const { CLI_NAME } = require("../cli-text");
|
|
6
|
+
const { resolveApiBaseUrl, resolveConnectBaseUrl } = require("../config");
|
|
7
|
+
const { generateRequestId } = require("../utils");
|
|
8
|
+
|
|
9
|
+
const DEFAULT_PAGE_SIZE = 10;
|
|
10
|
+
const DEFAULT_SORT_BY = "SEARCH_USERS_SORT_BY_BEST_MATCH";
|
|
11
|
+
|
|
12
|
+
function printHelp() {
|
|
13
|
+
console.log(CLI_NAME + " users");
|
|
14
|
+
console.log("");
|
|
15
|
+
console.log("用法:");
|
|
16
|
+
console.log(" " + CLI_NAME + " users <keyword> [options]");
|
|
17
|
+
console.log("");
|
|
18
|
+
console.log("参数:");
|
|
19
|
+
console.log(" --page-size <number> 每页条数,默认 10");
|
|
20
|
+
console.log(" --page-token <token> 分页游标");
|
|
21
|
+
console.log(" --request-id <id> 手动指定 X-Request-ID");
|
|
22
|
+
console.log(" --verbose 输出更多上下文");
|
|
23
|
+
console.log(" -h, --help 查看帮助");
|
|
24
|
+
console.log("");
|
|
25
|
+
console.log("示例:");
|
|
26
|
+
console.log(" " + CLI_NAME + " users openclawmp.cc");
|
|
27
|
+
console.log(" " + CLI_NAME + " users 窦窦 --page-size 20");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function pickFirstText(values) {
|
|
31
|
+
return values.find(function findValue(value) {
|
|
32
|
+
return typeof value === "string" && value.trim();
|
|
33
|
+
}) || "";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function pickFirstScalar(values) {
|
|
37
|
+
return values.find(function findValue(value) {
|
|
38
|
+
if (typeof value === "string") {
|
|
39
|
+
return value.trim();
|
|
40
|
+
}
|
|
41
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function normalizePageSize(value) {
|
|
46
|
+
if (value === undefined) {
|
|
47
|
+
return DEFAULT_PAGE_SIZE;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!Number.isInteger(value) || value <= 0) {
|
|
51
|
+
throw new Error("--page-size 需要是大于 0 的整数");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return value;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function formatCount(value) {
|
|
58
|
+
const numeric = Number(value);
|
|
59
|
+
if (Number.isFinite(numeric)) {
|
|
60
|
+
return numeric.toLocaleString("zh-CN");
|
|
61
|
+
}
|
|
62
|
+
return String(value || "0");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function resolveItems(data) {
|
|
66
|
+
if (Array.isArray(data.items)) {
|
|
67
|
+
return data.items;
|
|
68
|
+
}
|
|
69
|
+
if (Array.isArray(data.users)) {
|
|
70
|
+
return data.users;
|
|
71
|
+
}
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function resolveTotalSize(data, items) {
|
|
76
|
+
const total = pickFirstScalar([
|
|
77
|
+
data.totalSize,
|
|
78
|
+
data.total_size,
|
|
79
|
+
data.totalCount,
|
|
80
|
+
data.total_count,
|
|
81
|
+
data.total,
|
|
82
|
+
data.count
|
|
83
|
+
]);
|
|
84
|
+
if (total !== undefined) {
|
|
85
|
+
return String(total);
|
|
86
|
+
}
|
|
87
|
+
return String(items.length);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function resolveNextPageToken(data) {
|
|
91
|
+
return pickFirstText([data.nextPageToken, data.next_page_token]);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function resolveUser(item) {
|
|
95
|
+
if (!item || typeof item !== "object") {
|
|
96
|
+
return {};
|
|
97
|
+
}
|
|
98
|
+
if (item.user && typeof item.user === "object") {
|
|
99
|
+
return item.user;
|
|
100
|
+
}
|
|
101
|
+
if (item.profile && typeof item.profile === "object") {
|
|
102
|
+
return item.profile;
|
|
103
|
+
}
|
|
104
|
+
return item;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function resolveDisplayName(user) {
|
|
108
|
+
return pickFirstText([
|
|
109
|
+
user.displayName,
|
|
110
|
+
user.name,
|
|
111
|
+
user.nickname,
|
|
112
|
+
user.userId
|
|
113
|
+
]) || "(未命名用户)";
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function formatDescription(user) {
|
|
117
|
+
const raw = pickFirstText([
|
|
118
|
+
user.introduction,
|
|
119
|
+
user.description,
|
|
120
|
+
user.bio,
|
|
121
|
+
user.summary
|
|
122
|
+
]);
|
|
123
|
+
const singleLine = raw.replace(/\s+/g, " ").trim();
|
|
124
|
+
if (!singleLine) {
|
|
125
|
+
return "(无描述)";
|
|
126
|
+
}
|
|
127
|
+
if (singleLine.length <= 120) {
|
|
128
|
+
return singleLine;
|
|
129
|
+
}
|
|
130
|
+
return singleLine.slice(0, 117) + "...";
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function buildHomepageUrl(user, siteBaseUrl) {
|
|
134
|
+
const explicitUrl = pickFirstText([
|
|
135
|
+
user.homepageUrl,
|
|
136
|
+
user.homePageUrl,
|
|
137
|
+
user.profileUrl,
|
|
138
|
+
user.publicProfileUrl,
|
|
139
|
+
user.url,
|
|
140
|
+
user.link
|
|
141
|
+
]);
|
|
142
|
+
if (explicitUrl) {
|
|
143
|
+
return String(new URL(explicitUrl, String(siteBaseUrl).replace(/\/+$/u, "") + "/"));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const userId = pickFirstText([user.userId, user.uid, user.id]);
|
|
147
|
+
if (!userId) {
|
|
148
|
+
return "(无主页链接)";
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return String(siteBaseUrl).replace(/\/+$/u, "") + "/user/" + encodeURIComponent(userId);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function printUserSearchResult(item, index, siteBaseUrl) {
|
|
155
|
+
const user = resolveUser(item);
|
|
156
|
+
|
|
157
|
+
console.log(index + ". " + resolveDisplayName(user));
|
|
158
|
+
console.log(" 描述: " + formatDescription(user));
|
|
159
|
+
console.log(" 主页: " + buildHomepageUrl(user, siteBaseUrl));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async function run(context) {
|
|
163
|
+
const parsed = parseOptions(context.argv, {
|
|
164
|
+
"page-size": { type: "number" },
|
|
165
|
+
"page-token": { type: "string" },
|
|
166
|
+
"request-id": { type: "string" },
|
|
167
|
+
verbose: { type: "boolean" }
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const query = parsed.positionals.join(" ").trim();
|
|
171
|
+
if (!query) {
|
|
172
|
+
throw new Error("users 命令缺少关键词");
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const pageSize = normalizePageSize(parsed.options["page-size"]);
|
|
176
|
+
const requestId = parsed.options["request-id"] || generateRequestId();
|
|
177
|
+
const apiBaseUrl = resolveApiBaseUrl();
|
|
178
|
+
const client = createCatalogClient({
|
|
179
|
+
baseUrl: resolveConnectBaseUrl(),
|
|
180
|
+
cliVersion: context.cliVersion,
|
|
181
|
+
requestId,
|
|
182
|
+
verbose: Boolean(parsed.options.verbose)
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
const response = await client.searchUsers({
|
|
186
|
+
query,
|
|
187
|
+
sortBy: DEFAULT_SORT_BY,
|
|
188
|
+
pageSize,
|
|
189
|
+
pageToken: parsed.options["page-token"] || ""
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
const items = resolveItems(response.data);
|
|
193
|
+
const totalSize = resolveTotalSize(response.data, items);
|
|
194
|
+
const nextPageToken = resolveNextPageToken(response.data);
|
|
195
|
+
|
|
196
|
+
if (parsed.options.verbose) {
|
|
197
|
+
console.log("request-id: " + response.requestId);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (items.length === 0) {
|
|
201
|
+
if (Number(totalSize) > 0) {
|
|
202
|
+
console.log("当前页没有可展示的用户结果。");
|
|
203
|
+
} else {
|
|
204
|
+
console.log("没有找到与 \"" + query + "\" 相关的用户。");
|
|
205
|
+
}
|
|
206
|
+
console.log("总结果数: " + formatCount(totalSize));
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
items: [],
|
|
210
|
+
nextPageToken,
|
|
211
|
+
totalSize
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
console.log("用户搜索结果:共 " + formatCount(totalSize) + " 项,本页 " + items.length + " 项");
|
|
216
|
+
console.log("");
|
|
217
|
+
|
|
218
|
+
items.forEach(function eachItem(item, index) {
|
|
219
|
+
printUserSearchResult(item, index + 1, apiBaseUrl);
|
|
220
|
+
if (index < items.length - 1) {
|
|
221
|
+
console.log("");
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
if (nextPageToken) {
|
|
226
|
+
let nextPageCommand = " " + CLI_NAME + " users " + JSON.stringify(query);
|
|
227
|
+
if (pageSize !== DEFAULT_PAGE_SIZE) {
|
|
228
|
+
nextPageCommand += " --page-size " + pageSize;
|
|
229
|
+
}
|
|
230
|
+
nextPageCommand += " --page-token " + nextPageToken;
|
|
231
|
+
|
|
232
|
+
console.log("");
|
|
233
|
+
console.log("下一页:");
|
|
234
|
+
console.log(nextPageCommand);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return {
|
|
238
|
+
items,
|
|
239
|
+
nextPageToken,
|
|
240
|
+
totalSize
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
module.exports = {
|
|
245
|
+
printHelp,
|
|
246
|
+
run
|
|
247
|
+
};
|
package/lib/config.js
CHANGED
|
@@ -5,7 +5,7 @@ const path = require("node:path");
|
|
|
5
5
|
const os = require("node:os");
|
|
6
6
|
|
|
7
7
|
// const DEFAULT_API_BASE_URL = "https://seafood.c.stepfun-inc.net";
|
|
8
|
-
const DEFAULT_API_BASE_URL = "https://openclawmp.stepfun.com";
|
|
8
|
+
const DEFAULT_API_BASE_URL = "https://openclawmp.stepfun.com/";
|
|
9
9
|
|
|
10
10
|
const projectRoot = path.resolve(__dirname, "..");
|
|
11
11
|
const repoRoot = path.resolve(projectRoot, "..");
|
package/lib/help.js
CHANGED
|
@@ -28,6 +28,11 @@ const COMMAND_SECTIONS = [
|
|
|
28
28
|
description: "搜索市场资产",
|
|
29
29
|
usage: CLI_NAME + " search <query> [options]"
|
|
30
30
|
},
|
|
31
|
+
{
|
|
32
|
+
name: "users",
|
|
33
|
+
description: "搜索用户",
|
|
34
|
+
usage: CLI_NAME + " users <keyword> [options]"
|
|
35
|
+
},
|
|
31
36
|
{
|
|
32
37
|
name: "info",
|
|
33
38
|
description: "查看某个资产的详细信息",
|
|
@@ -76,6 +81,7 @@ function printGlobalHelp() {
|
|
|
76
81
|
console.log(" " + CLI_NAME + " oauth <your-token>");
|
|
77
82
|
console.log(" " + CLI_NAME + " publish ./my-asset --yes");
|
|
78
83
|
console.log(" " + CLI_NAME + " search 天气");
|
|
84
|
+
console.log(" " + CLI_NAME + " users openclawmp.cc");
|
|
79
85
|
console.log(" " + CLI_NAME + " info 6c476a36955a4270a3fa303beeeed5ee");
|
|
80
86
|
console.log(" " + buildInstallCommand("skill", "7c19dc4c3244418096f1dcb59c93f795"));
|
|
81
87
|
console.log(" " + buildInstallCommand("skill", "7c19dc4c3244418096f1dcb59c93f795", "1.0.3"));
|
package/lib/publish-flow.js
CHANGED
|
@@ -211,7 +211,8 @@ async function publishAsset(options) {
|
|
|
211
211
|
baseUrl: connectBaseUrl,
|
|
212
212
|
token: loadedCredentials.credentials.token,
|
|
213
213
|
cliVersion: options.cliVersion,
|
|
214
|
-
requestId: options.requestId
|
|
214
|
+
requestId: options.requestId,
|
|
215
|
+
verbose: Boolean(options.verbose)
|
|
215
216
|
});
|
|
216
217
|
|
|
217
218
|
if (options.verbose) {
|
|
@@ -331,7 +332,8 @@ async function publishAsset(options) {
|
|
|
331
332
|
baseUrl: resolveConnectBaseUrl(options.baseUrl),
|
|
332
333
|
token: loadedCredentials.credentials.token,
|
|
333
334
|
cliVersion: options.cliVersion,
|
|
334
|
-
requestId: options.requestId
|
|
335
|
+
requestId: options.requestId,
|
|
336
|
+
verbose: Boolean(options.verbose)
|
|
335
337
|
});
|
|
336
338
|
const abortUploadRequest = { uploadId };
|
|
337
339
|
await client.abortUpload(abortUploadRequest);
|
package/lib/utils.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openclawmp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "OpenClaw Marketplace CLI client",
|
|
5
5
|
"bin": {
|
|
6
6
|
"openclawmp": "bin/openclawmp.js"
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
},
|
|
16
16
|
"scripts": {
|
|
17
17
|
"start": "node bin/openclawmp.js",
|
|
18
|
-
"publish:dry-run": "node bin/openclawmp.js publish . --dry-run"
|
|
18
|
+
"publish:dry-run": "node bin/openclawmp.js publish . --dry-run",
|
|
19
|
+
"pack:local": "npm pack"
|
|
19
20
|
},
|
|
20
21
|
"directories": {
|
|
21
22
|
"lib": "lib",
|