openclawmp 1.0.0-alpha.0 → 1.0.0-alpha.1

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 CHANGED
@@ -18,8 +18,8 @@ npm install -g openclawmp
18
18
  # 搜索市场资产
19
19
  openclawmp search 天气
20
20
 
21
- # 查看资产详情
22
- openclawmp info 7c19dc4c3244418096f1dcb59c93f795
21
+ # 根据 assetId 查看资产详情
22
+ openclawmp info 6c476a36955a4270a3fa303beeeed5ee
23
23
 
24
24
  # 安装资产
25
25
  openclawmp install skill/7c19dc4c3244418096f1dcb59c93f795
@@ -34,13 +34,13 @@ openclawmp list
34
34
  openclawmp uninstall skill/demo-skill
35
35
 
36
36
  # 发布本地资产
37
- openclawmp publish ./my-skill --type skill --version 1.0.0 --yes
37
+ openclawmp publish ./my-asset --yes
38
38
  ```
39
39
 
40
40
  ## 功能概览
41
41
 
42
42
  - `search`:搜索市场资产,按类型、作者、安装量、标签和简介格式化输出。
43
- - `info`:查看资产详情与版本信息。
43
+ - `info`:根据 `assetId` 查看资产详情与版本信息。
44
44
  - `install`:根据 `assetId` 和可选 `semver` 下载归档并安装到本地 OpenClaw 目录。
45
45
  - `uninstall`:删除本地安装目录,并清理对应锁文件记录。
46
46
  - `list`:扫描本地已安装资产,结合 lockfile 和安装元数据展示结果。
@@ -90,18 +90,34 @@ openclawmp publish /path/to/asset
90
90
  示例:
91
91
 
92
92
  ```bash
93
- openclawmp publish ./my-skill \
94
- --type skill \
95
- --category productivity \
96
- --tags agent,automation \
97
- --version 1.0.0 \
98
- --yes
93
+ cat .metadata.json
94
+ ```
95
+
96
+ ```json
97
+ {
98
+ "assetType": "skill",
99
+ "name": "my-skill",
100
+ "displayName": "My Skill",
101
+ "semver": "1.0.0",
102
+ "category": "productivity",
103
+ "tags": ["agent", "automation"]
104
+ }
105
+ ```
106
+
107
+ ```bash
108
+ openclawmp publish ./my-skill --yes
109
+ ```
110
+
111
+ 查看资产详情:
112
+
113
+ ```bash
114
+ openclawmp info 6c476a36955a4270a3fa303beeeed5ee
99
115
  ```
100
116
 
101
117
  更新已有资产版本:
102
118
 
103
119
  ```bash
104
- openclawmp publish ./my-skill --version 1.0.1 --yes
120
+ openclawmp publish ./my-skill --asset-id s-123456 --yes
105
121
  ```
106
122
 
107
123
  如果目录根部已存在 `.assetid`,会自动把该值透传为 `assetId`;也可显式传入 `--asset-id <id>`。
@@ -138,24 +154,16 @@ openclawmp search 天气 --page-size 10
138
154
 
139
155
  - `publish`
140
156
  - `search`
157
+ - `info`
141
158
  - `install`
142
159
  - `uninstall`
143
160
  - `list`
144
161
 
145
- 其中当前可用的是 `publish`、`search`、`install`、`uninstall`、`list`。
162
+ 其中当前可用的是 `publish`、`search`、`info`、`install`、`uninstall`、`list`。
146
163
 
147
164
  ## 发布命令参数
148
165
 
149
- - `--type <skill|experience|plugin|trigger|channel>`
150
166
  - `--asset-id <id>`:未传时会尝试读取目录根部 `.assetid`
151
- - `--name <slug>`
152
- - `--display-name <name>`
153
- - `--description <text>`
154
- - `--version <semver>`
155
- - `--category <category>`
156
- - `--tags <a,b,c>`
157
- - `--tag <value>`:可重复
158
- - `--long-description <text>`
159
167
  - `--base-url <url>`
160
168
  - `--token-file <path>`
161
169
  - `--part-size-mb <number>`
@@ -171,16 +179,15 @@ openclawmp search 天气 --page-size 10
171
179
  - 根目录可通过 `.openclawmpignore` 补充忽略规则,也会读取根目录 `.gitignore` / `.npmignore` 的常见规则
172
180
  - 子路径遇到无权限读取的目录或文件时会自动跳过;使用 `--verbose` 可查看具体跳过项
173
181
 
174
- ## Metadata 提取规则
182
+ ## Publish Metadata
175
183
 
176
- - 优先级:CLI 显式参数 > `.metadata.json` > `README.md` > `package.json` > `openclaw.plugin.json` > `SKILL.md`
177
- - `.metadata.json` 存在时会优先使用,适合显式固定 `type`、`name`、`displayName`、`description`、`version`、`category`、`tags`、`longDescription`
178
- - `SKILL.md` `name`、`display-name`、`description`、`version`、`tags` 只从 frontmatter(`---` 到 `---`)提取,不会把正文内容混进 `description`
179
- - `README.md` 支持 YAML frontmatter 和前置 `key: value` 形式字段提取 `name`、`display-name`、`description`、`version`、`type`、`category`、`tags`
180
- - `trigger` / `experience` 会把 README 标题后的第一段作为描述兜底
181
- - 发布必填规则:`assetType`、`name`、`displayName`、`semver`
182
- - `version` 必须是严格的 `x.x.x` 形式,例如 `1.2.1`、`1.1.10`
183
- - `assetId` 为可选字段;有就透传,没有就省略,由后端判断是新建还是已有资产发新版
184
+ - `publish` 只读取发布目录或归档里的 `.metadata.json`
185
+ - `.metadata.json` 必须包含 `assetType`、`name`、`displayName`、`semver`
186
+ - `semver` 必须是严格的 `x.y.z` 纯数字格式,例如 `1.2.3`
187
+ - 可选字段:`assetId`、`category`、`tags`、`description`、`longDescription`
188
+ - 不再从 `README.md`、`package.json`、`openclaw.plugin.json`、`SKILL.md` 自动推断发布 metadata
189
+ - 不再通过 CLI 参数补录 `assetType`、`name`、`displayName`、`semver`
190
+ - `assetId` 可写在 `.metadata.json`,也可以通过 `--asset-id` 或目录根部 `.assetid` 透传
184
191
  - `objectId` 由上传完成后自动生成,登录态始终必需
185
192
  - 调用发布接口前会打印 `CreateAssetSemver` 请求预览;真正发网请求前会打印对应 request body
186
193
 
package/lib/api.js CHANGED
@@ -115,7 +115,7 @@ function createCatalogClient(options) {
115
115
  "Connect-Protocol-Version": "1",
116
116
  "Content-Type": "application/json",
117
117
  "Accept": "application/json",
118
- "User-Agent": "mpdev/" + (options.cliVersion || "dev")
118
+ "User-Agent": "openclawmp/" + (options.cliVersion || "dev")
119
119
  };
120
120
 
121
121
  if (options.token) {
package/lib/archive.js CHANGED
@@ -879,7 +879,7 @@ async function preparePackage(inputPath) {
879
879
  const inspected = await inspectPublishInput(inputPath);
880
880
 
881
881
  if (inspected.isDirectory) {
882
- const tempDir = await fsp.mkdtemp(path.join(os.tmpdir(), "mpdev-cli-"));
882
+ const tempDir = await fsp.mkdtemp(path.join(os.tmpdir(), "openclawmp-cli-"));
883
883
  const packagePath = path.join(tempDir, inspected.packageFileName);
884
884
  await createTarGzFromEntries(inspected.entries, packagePath);
885
885
  const packageStat = await fsp.stat(packagePath);
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+
3
+ const CLI_NAME = "openclawmp";
4
+
5
+ function buildInstallSpecifier(assetType, assetId, semver) {
6
+ const normalizedAssetType = String(assetType || "").trim();
7
+ const normalizedAssetId = String(assetId || "").trim();
8
+ const normalizedSemver = String(semver || "").trim();
9
+
10
+ if (!normalizedAssetType || !normalizedAssetId) {
11
+ return "";
12
+ }
13
+
14
+ return (
15
+ normalizedAssetType +
16
+ "/" +
17
+ normalizedAssetId +
18
+ (normalizedSemver ? "@" + normalizedSemver : "")
19
+ );
20
+ }
21
+
22
+ function buildInstallCommand(assetType, assetId, semver) {
23
+ const specifier = buildInstallSpecifier(assetType, assetId, semver);
24
+ if (!specifier) {
25
+ return "";
26
+ }
27
+ return CLI_NAME + " install " + specifier;
28
+ }
29
+
30
+ module.exports = {
31
+ CLI_NAME,
32
+ buildInstallSpecifier,
33
+ buildInstallCommand
34
+ };
@@ -1,8 +1,9 @@
1
1
  "use strict";
2
2
 
3
3
  const { parseOptions } = require("../cli-parser");
4
+ const { CLI_NAME } = require("../cli-text");
4
5
  const { createCatalogClient } = require("../api");
5
- const { fromCatalogAssetType, normalizeLocalAssetType, toCatalogAssetType } = require("../asset-types");
6
+ const { fromCatalogAssetType } = require("../asset-types");
6
7
  const { resolveConnectBaseUrl } = require("../config");
7
8
  const { generateRequestId, humanize } = require("../utils");
8
9
 
@@ -15,10 +16,13 @@ const TYPE_ICONS = {
15
16
  };
16
17
 
17
18
  function printHelp() {
18
- console.log("mpdev info");
19
+ console.log(CLI_NAME + " info");
19
20
  console.log("");
20
21
  console.log("用法:");
21
- console.log(" mpdev info <asset-id | type/name | type/@author/name | query> [options]");
22
+ console.log(" " + CLI_NAME + " info <assetId> [options]");
23
+ console.log("");
24
+ console.log("示例:");
25
+ console.log(" " + CLI_NAME + " info 6c476a36955a4270a3fa303beeeed5ee");
22
26
  console.log("");
23
27
  console.log("参数:");
24
28
  console.log(" --base-url <url> API 基地址");
@@ -33,57 +37,28 @@ function pickFirstText(values) {
33
37
  }) || "";
34
38
  }
35
39
 
36
- function normalizeName(input) {
37
- return String(input || "")
38
- .trim()
39
- .toLowerCase()
40
- .replace(/\s+/g, " ");
41
- }
40
+ function parseAssetId(positionals) {
41
+ const args = Array.isArray(positionals) ? positionals.filter(Boolean) : [];
42
+ const usageExample = CLI_NAME + " info 6c476a36955a4270a3fa303beeeed5ee";
42
43
 
43
- function parseAssetReference(raw) {
44
- const text = String(raw || "").trim();
45
- if (!text) {
46
- throw new Error("info 命令缺少资产标识");
44
+ if (args.length === 0) {
45
+ throw new Error("info 命令缺少 assetId");
47
46
  }
48
47
 
49
- if (/^(?:[a-z]{1,4}-[a-z0-9]+|[a-f0-9]{32})$/iu.test(text)) {
50
- return {
51
- kind: "assetId",
52
- assetId: text
53
- };
48
+ if (args.length !== 1) {
49
+ throw new Error("info 命令只支持 <assetId> 用法,例如: " + usageExample);
54
50
  }
55
51
 
56
- let match = text.match(/^([^/]+)\/@([^/]+)\/(.+)$/u);
57
- if (match) {
58
- const assetType = normalizeLocalAssetType(match[1]);
59
- if (!assetType) {
60
- throw new Error("未知资产类型: " + match[1]);
61
- }
62
- return {
63
- kind: "typedAuthorName",
64
- assetType,
65
- authorId: match[2],
66
- query: match[3].trim()
67
- };
52
+ const assetId = String(args[0] || "").trim();
53
+ if (!assetId) {
54
+ throw new Error("info 命令缺少 assetId");
68
55
  }
69
56
 
70
- match = text.match(/^([^/]+)\/(.+)$/u);
71
- if (match) {
72
- const assetType = normalizeLocalAssetType(match[1]);
73
- if (!assetType) {
74
- throw new Error("未知资产类型: " + match[1]);
75
- }
76
- return {
77
- kind: "typedName",
78
- assetType,
79
- query: match[2].trim()
80
- };
57
+ if (/\s/u.test(assetId) || assetId.includes("/")) {
58
+ throw new Error("info 命令只支持 <assetId> 用法,例如: " + usageExample);
81
59
  }
82
60
 
83
- return {
84
- kind: "query",
85
- query: text
86
- };
61
+ return assetId;
87
62
  }
88
63
 
89
64
  function formatTypeLabel(assetType) {
@@ -156,100 +131,6 @@ function normalizeTimestamp(value) {
156
131
  return "";
157
132
  }
158
133
 
159
- async function listAllUserAssets(client, authorId, assetType) {
160
- const items = [];
161
- let pageToken = "";
162
-
163
- do {
164
- const response = await client.listUserAssets({
165
- userId: authorId,
166
- assetType: toCatalogAssetType(assetType),
167
- pageSize: 100,
168
- pageToken
169
- });
170
-
171
- const pageItems = Array.isArray(response.data.items) ? response.data.items : [];
172
- items.push.apply(items, pageItems);
173
- pageToken = response.data.nextPageToken || "";
174
- } while (pageToken);
175
-
176
- return items;
177
- }
178
-
179
- function buildAmbiguousError(items) {
180
- return (
181
- "匹配到多个资产,请改用更精确的 assetId 或 type/@author/name。候选项: " +
182
- items
183
- .map(function mapItem(item) {
184
- return (
185
- (item.displayName || item.name || item.assetId) +
186
- " [" +
187
- (item.assetId || "") +
188
- "] @" +
189
- (item.ownerUserId || "unknown")
190
- );
191
- })
192
- .join(", ")
193
- );
194
- }
195
-
196
- function pickAssetFromItems(items, requestedName) {
197
- const normalizedRequested = normalizeName(requestedName);
198
- const exactMatches = items.filter(function filterItem(item) {
199
- return (
200
- item &&
201
- (normalizeName(item.name) === normalizedRequested ||
202
- normalizeName(item.displayName) === normalizedRequested)
203
- );
204
- });
205
-
206
- if (exactMatches.length === 1) {
207
- return exactMatches[0];
208
- }
209
- if (exactMatches.length > 1) {
210
- throw new Error(buildAmbiguousError(exactMatches));
211
- }
212
-
213
- const fuzzyMatches = items.filter(function filterItem(item) {
214
- return (
215
- item &&
216
- (normalizeName(item.name).includes(normalizedRequested) ||
217
- normalizeName(item.displayName).includes(normalizedRequested))
218
- );
219
- });
220
-
221
- if (fuzzyMatches.length === 1) {
222
- return fuzzyMatches[0];
223
- }
224
- if (fuzzyMatches.length > 1) {
225
- throw new Error(buildAmbiguousError(fuzzyMatches));
226
- }
227
-
228
- throw new Error("未找到资产: " + requestedName);
229
- }
230
-
231
- async function resolveAssetSummary(client, reference) {
232
- if (reference.kind === "assetId") {
233
- return {
234
- assetId: reference.assetId
235
- };
236
- }
237
-
238
- if (reference.kind === "typedAuthorName") {
239
- const items = await listAllUserAssets(client, reference.authorId, reference.assetType);
240
- return pickAssetFromItems(items, reference.query);
241
- }
242
-
243
- const response = await client.searchAssets({
244
- query: reference.query,
245
- assetType: reference.assetType ? toCatalogAssetType(reference.assetType) : 0,
246
- pageSize: 50,
247
- pageToken: ""
248
- });
249
- const items = Array.isArray(response.data.items) ? response.data.items : [];
250
- return pickAssetFromItems(items, reference.query);
251
- }
252
-
253
134
  function printAssetInfo(detail, summary, semvers, verbose) {
254
135
  const localAssetType = fromCatalogAssetType(detail.assetType);
255
136
  const title = pickFirstText([detail.displayName, detail.name, detail.assetId]) || "(未命名资产)";
@@ -309,10 +190,7 @@ async function run(context) {
309
190
  verbose: { type: "boolean" }
310
191
  });
311
192
 
312
- const rawReference = parsed.positionals.join(" ").trim();
313
- if (!rawReference) {
314
- throw new Error("info 命令缺少资产标识");
315
- }
193
+ const assetId = parseAssetId(parsed.positionals);
316
194
 
317
195
  const requestId = parsed.options["request-id"] || generateRequestId();
318
196
  const client = createCatalogClient({
@@ -321,13 +199,6 @@ async function run(context) {
321
199
  requestId
322
200
  });
323
201
 
324
- const reference = parseAssetReference(rawReference);
325
- const summary = await resolveAssetSummary(client, reference);
326
- const assetId = summary.assetId;
327
- if (!assetId) {
328
- throw new Error("未能解析 assetId: " + rawReference);
329
- }
330
-
331
202
  const assetResponse = await client.getAsset({
332
203
  assetId
333
204
  });
@@ -340,13 +211,14 @@ async function run(context) {
340
211
  const semvers = Array.isArray(semverResponse.data.items)
341
212
  ? semverResponse.data.items
342
213
  : [];
214
+ const asset = assetResponse.data || {};
343
215
 
344
- printAssetInfo(assetResponse.data || {}, summary, semvers, Boolean(parsed.options.verbose));
216
+ printAssetInfo(asset, asset, semvers, Boolean(parsed.options.verbose));
345
217
 
346
218
  return {
347
- asset: assetResponse.data || {},
219
+ asset,
348
220
  semvers,
349
- summary
221
+ summary: asset
350
222
  };
351
223
  }
352
224
 
@@ -4,8 +4,9 @@ const fs = require("node:fs/promises");
4
4
  const os = require("node:os");
5
5
  const path = require("node:path");
6
6
 
7
- const { fromCatalogAssetType, normalizeLocalAssetType } = require("../asset-types");
7
+ const { VALID_ASSET_TYPES, fromCatalogAssetType, normalizeLocalAssetType } = require("../asset-types");
8
8
  const { parseOptions } = require("../cli-parser");
9
+ const { CLI_NAME, buildInstallCommand } = require("../cli-text");
9
10
  const { createCatalogClient } = require("../api");
10
11
  const { resolveConnectBaseUrl, getInstallRoot } = require("../config");
11
12
  const { loadHubCredentials } = require("../credentials");
@@ -22,21 +23,32 @@ const {
22
23
  } = require("../utils");
23
24
 
24
25
  function printHelp() {
25
- console.log("mpdev install");
26
+ console.log(CLI_NAME + " install");
26
27
  console.log("");
27
28
  console.log("用法:");
28
- console.log(" mpdev install <type>/<assetId>[@<semver>] [options]");
29
+ console.log(" " + CLI_NAME + " install <type>/<assetId>[@<semver>] [options]");
30
+ console.log("");
31
+ console.log("说明:");
32
+ console.log(" type 支持: " + VALID_ASSET_TYPES.join(", "));
33
+ console.log(" install 需要本地 token,默认读取 ~/.openclaw/hub-credentials.json");
34
+ console.log(" 可通过 --token-file <path> 指定其他凭证文件");
35
+ console.log(" 默认安装目录:");
36
+ console.log(" skill -> ~/.openclaw/skills");
37
+ console.log(" plugin -> ~/.openclaw/extensions");
38
+ console.log(" channel -> ~/.openclaw/extensions");
39
+ console.log(" trigger -> ~/.openclaw/triggers");
40
+ console.log(" experience -> ~/.openclaw/experiences");
29
41
  console.log("");
30
42
  console.log("示例:");
31
- console.log(" mpdev install skill/7c19dc4c3244418096f1dcb59c93f795");
32
- console.log(" mpdev install skill/7c19dc4c3244418096f1dcb59c93f795@1.0.3");
43
+ console.log(" " + buildInstallCommand("skill", "7c19dc4c3244418096f1dcb59c93f795"));
44
+ console.log(" " + buildInstallCommand("skill", "7c19dc4c3244418096f1dcb59c93f795", "1.0.3"));
33
45
  console.log("");
34
46
  console.log("参数:");
35
47
  console.log(" --version <semver> 安装指定版本(也可写在 <assetId>@<semver>)");
36
48
  console.log(" --base-url <url> API 基地址");
37
49
  console.log(" --token-file <path> 本地命令行令牌文件");
38
50
  console.log(" --request-id <id> 手动指定 X-Request-ID");
39
- console.log(" --target-dir <path> 自定义安装目录");
51
+ console.log(" --target-dir <path> 自定义安装根目录,最终会安装到 <target-dir>/<asset-name>");
40
52
  console.log(" --verbose 输出更多上下文");
41
53
  console.log(" -h, --help 查看帮助");
42
54
  }
@@ -116,6 +128,18 @@ function resolveAssetTypeFromDetail(assetDetail) {
116
128
  );
117
129
  }
118
130
 
131
+ function buildReversionDownloadUrlRequest(assetId, semver) {
132
+ const request = {
133
+ assetId
134
+ };
135
+
136
+ if (semver && semver.reversionId) {
137
+ request.reversionId = semver.reversionId;
138
+ }
139
+
140
+ return request;
141
+ }
142
+
119
143
  async function downloadBuffer(url) {
120
144
  printTerminalOperationNotice("下载归档文件", [url]);
121
145
 
@@ -252,14 +276,11 @@ async function run(context) {
252
276
  assetId: assetDetail.assetId || specifier.assetId,
253
277
  latestSemver: assetDetail.latestSemver || ""
254
278
  }, requestedVersion);
279
+ const resolvedAssetId = assetDetail.assetId || specifier.assetId;
255
280
 
256
- if (!semver.reversionId) {
257
- throw new Error("版本缺少 reversionId,无法下载安装");
258
- }
259
-
260
- const downloadInfo = await client.getReversionDownloadUrl({
261
- reversionId: semver.reversionId
262
- });
281
+ const downloadInfo = await client.getReversionDownloadUrl(
282
+ buildReversionDownloadUrlRequest(resolvedAssetId, semver)
283
+ );
263
284
 
264
285
  const downloadUrl = downloadInfo.data.downloadUrl;
265
286
  if (!downloadUrl) {
@@ -267,9 +288,11 @@ async function run(context) {
267
288
  }
268
289
 
269
290
  if (parsed.options.verbose) {
270
- console.log("资产ID: " + (assetDetail.assetId || specifier.assetId));
291
+ console.log("资产ID: " + resolvedAssetId);
271
292
  console.log("下载版本: " + semver.semver);
272
- console.log("reversionId: " + semver.reversionId);
293
+ if (semver.reversionId) {
294
+ console.log("reversionId: " + semver.reversionId);
295
+ }
273
296
  console.log("downloadUrl: " + downloadUrl);
274
297
  }
275
298
 
@@ -288,7 +311,6 @@ async function run(context) {
288
311
  await installDownloadedArchive(downloaded.buffer, targetDir, fileNameHint);
289
312
  await writeInstallMetadata(targetDir, assetDetail);
290
313
 
291
- const resolvedAssetId = assetDetail.assetId || specifier.assetId;
292
314
  const lockKey = specifier.assetType + "/" + resolvedAssetId;
293
315
  await upsertInstalledAsset({
294
316
  key: lockKey,
@@ -311,6 +333,7 @@ async function run(context) {
311
333
  }
312
334
 
313
335
  module.exports = {
336
+ buildReversionDownloadUrlRequest,
314
337
  parseAssetSpecifier,
315
338
  printHelp,
316
339
  resolveRequestedVersion,
@@ -4,6 +4,7 @@ const fs = require("node:fs/promises");
4
4
  const path = require("node:path");
5
5
 
6
6
  const { parseOptions } = require("../cli-parser");
7
+ const { CLI_NAME } = require("../cli-text");
7
8
  const { VALID_ASSET_TYPES, normalizeLocalAssetType } = require("../asset-types");
8
9
  const { getInstallRoot } = require("../config");
9
10
  const { normalizeInstallMetadata, readInstallMetadata } = require("../install-metadata");
@@ -20,10 +21,10 @@ const TYPE_ICONS = {
20
21
  };
21
22
 
22
23
  function printHelp() {
23
- console.log("mpdev list");
24
+ console.log(CLI_NAME + " list");
24
25
  console.log("");
25
26
  console.log("用法:");
26
- console.log(" mpdev list [options]");
27
+ console.log(" " + CLI_NAME + " list [options]");
27
28
  console.log("");
28
29
  console.log("参数:");
29
30
  console.log(" --type <type> skill | experience | plugin | trigger | channel");
@@ -1,14 +1,15 @@
1
1
  "use strict";
2
2
 
3
+ const { CLI_NAME } = require("../cli-text");
3
4
  const { defaultCredentialsPath } = require("../config");
4
5
  const { loadHubCredentials } = require("../credentials");
5
6
  const { parseOptions } = require("../cli-parser");
6
7
 
7
8
  function printHelp() {
8
- console.log("mpdev login");
9
+ console.log(CLI_NAME + " login");
9
10
  console.log("");
10
11
  console.log("用法:");
11
- console.log(" mpdev login [options]");
12
+ console.log(" " + CLI_NAME + " login [options]");
12
13
  console.log("");
13
14
  console.log("参数:");
14
15
  console.log(" --token-file <path> 指定本地凭证文件路径");