openclawmp 1.0.3 → 1.0.5

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
@@ -1,8 +1,8 @@
1
1
  # 🐟 openclawmp
2
2
 
3
- **OpenClaw Marketplace CLI** — 水产市场命令行工具
3
+ **OpenClaw Marketplace CLI** — 面向 OpenClaw Marketplace 的命令行工具。
4
4
 
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).
5
+ 用于在终端中完成资产搜索、用户搜索、详情查看、评论、安装、卸载、列表查看和发布等常用操作。当前支持的资产类型包括 `skill`、`experience`、`plugin`、`trigger`、`channel`。
6
6
 
7
7
  ## 安装
8
8
 
@@ -10,7 +10,7 @@ A command-line client for the [OpenClaw Marketplace](https://openclawmp.stepfun.
10
10
  npm install -g openclawmp
11
11
  ```
12
12
 
13
- 需要 Node.js `>=18.0.0`。项目使用 Node 内置 `fetch`,无额外运行时依赖。
13
+ 运行要求:Node.js `>=18.0.0`。项目使用 Node 内置 `fetch`,无额外运行时依赖。
14
14
 
15
15
  ## 快速开始
16
16
 
@@ -18,13 +18,13 @@ npm install -g openclawmp
18
18
  # 写入本地 token
19
19
  openclawmp oauth <your-token>
20
20
 
21
+ # 查看本地登录状态
22
+ openclawmp login
23
+
21
24
  # 搜索市场资产
22
25
  openclawmp search 天气
23
26
 
24
- # 搜索用户
25
- openclawmp users 张三
26
-
27
- # 根据 assetId 查看资产详情
27
+ # 查看资产详情
28
28
  openclawmp info 6c476a36955a4270a3fa303beeeed5ee
29
29
 
30
30
  # 安装资产
@@ -40,150 +40,174 @@ openclawmp uninstall skill/demo-skill
40
40
  openclawmp publish ./my-asset --yes
41
41
  ```
42
42
 
43
- ## 功能概览
44
-
45
- - `search`:搜索市场资产,按类型、作者、安装量、标签和简介格式化输出。
46
- - `users`:搜索用户,默认展示前 10 条,并显示总结果数与下一页游标。
47
- - `oauth`:将 token 写入本地凭证文件。
48
- - `info`:根据 `assetId` 查看资产详情与版本信息。
49
- - `install`:根据 `assetId` 和可选 `semver` 下载归档并安装到本地 OpenClaw 目录。
50
- - `uninstall`:删除本地安装目录,并清理对应锁文件记录。
51
- - `list`:扫描本地已安装资产,结合 lockfile 和安装元数据展示结果。
52
- - `publish`:打包本地目录并上传发布,串联 `createUpload`、`signUploadPartUrl`、`completeUpload`、`createAssetSemver`。
43
+ ## 命令概览
53
44
 
54
- ## 支持的资产类型
45
+ | 命令 | 说明 |
46
+ | --- | --- |
47
+ | `login` | 查看当前本地 token 状态和凭证文件位置。 |
48
+ | `oauth` | 将 token 写入本地凭证文件。 |
49
+ | `search` | 按关键词搜索市场资产,并展示类型、作者、安装量、标签和简介等信息。 |
50
+ | `users` | 按关键词搜索用户,默认展示前 10 条,并显示总结果数与下一页游标。 |
51
+ | `info` | 根据 `assetId` 查看资产详情和版本信息。 |
52
+ | `createComment` | 给指定资产发送文本评论;当前用法为 `openclawmp createComment <assetId> <content>`,需要默认本地 token。 |
53
+ | `install` | 根据 `type/assetId` 和可选版本号下载并安装资产到本地目录。 |
54
+ | `list` | 扫描本地已安装资产,并结合锁文件和安装元数据展示结果。 |
55
+ | `uninstall` | 删除本地安装目录,并清理对应锁文件记录。 |
56
+ | `publish` | 校验、打包并发布本地资产目录或归档。 |
55
57
 
56
- - `skill`
57
- - `experience`
58
- - `plugin`
59
- - `trigger`
60
- - `channel`
61
-
62
- ## 运行要求
63
-
64
- - Node.js `>=18.0.0`
65
- - 零运行时依赖
58
+ > 查看某个命令的完整参数:`openclawmp help <command>` 或 `openclawmp <command> --help`。
66
59
 
67
60
  ## 凭证
68
61
 
69
- 默认读取:
62
+ 默认凭证文件:
70
63
 
71
- - macOS / Linux: `~/.openclaw/hub-credentials.json`
72
- - Windows: `%USERPROFILE%\\.openclaw\\hub-credentials.json`
64
+ - macOS / Linux:`~/.openclaw/hub-credentials.json`
65
+ - Windows:`%USERPROFILE%\.openclaw\hub-credentials.json`
73
66
 
74
- 文件最少需要:
67
+ 文件最少需要包含:
75
68
 
76
69
  ```json
77
70
  {
78
- "token": "your-credential-token"
71
+ "token": "your-credential-token"
79
72
  }
80
73
  ```
81
74
 
82
- 也可以直接通过命令写入:
75
+ 写入或查看凭证:
83
76
 
84
77
  ```bash
85
78
  openclawmp oauth <your-token>
79
+ openclawmp login
86
80
  ```
87
81
 
88
- 请求头约定:
89
-
90
- - `Authorization: Bearer <credential token>`:受保护接口必需;`search` 可匿名调用
91
- - `X-Request-ID`:自动生成,也可通过 `--request-id` 显式指定
92
- - `Cli-Version`:自动带当前 package 版本
82
+ 需要登录态的命令会读取本地 token;部分命令也支持通过 `--token-file <path>` 指定其他凭证文件,具体以命令帮助为准。
93
83
 
94
84
  ## 常用示例
95
85
 
96
- ```bash
97
- npm install -g openclawmp
98
- openclawmp oauth <your-token>
99
- openclawmp publish /path/to/asset
100
- ```
101
-
102
- 示例:
86
+ ### 搜索资产和用户
103
87
 
104
88
  ```bash
105
- cat .metadata.json
89
+ openclawmp search 天气 --page-size 10
90
+ openclawmp search "stock realtime" --page-size 10
91
+ openclawmp users openclawmp.cc
92
+ openclawmp users 窦窦 --page-size 20
106
93
  ```
107
94
 
108
- ```json
109
- {
110
- "assetType": "skill",
111
- "name": "my-skill",
112
- "displayName": "My Skill",
113
- "semver": "1.0.0",
114
- "category": "productivity",
115
- "tags": ["agent", "automation"]
116
- }
117
- ```
95
+ ### 查看资产详情
118
96
 
119
97
  ```bash
120
- openclawmp publish ./my-skill --yes
98
+ openclawmp info 6c476a36955a4270a3fa303beeeed5ee
121
99
  ```
122
100
 
123
- 查看资产详情:
101
+ ### 发送资产评论
124
102
 
125
103
  ```bash
126
- openclawmp info 6c476a36955a4270a3fa303beeeed5ee
104
+ openclawmp createComment 8ec77f07430644bc3a848235d5c8d911 "这个资产很有帮助"
127
105
  ```
128
106
 
129
- 更新已有资产版本:
107
+ ### 安装资产
130
108
 
131
109
  ```bash
132
- openclawmp publish ./my-skill --asset-id s-123456 --yes
110
+ openclawmp install skill/7c19dc4c3244418096f1dcb59c93f795
111
+ openclawmp install skill/7c19dc4c3244418096f1dcb59c93f795@1.0.3
112
+ openclawmp install skill/7c19dc4c3244418096f1dcb59c93f795 --version 1.0.3
133
113
  ```
134
114
 
135
- 如果目录根部已存在 `.assetid`,会自动把该值透传为 `assetId`;也可显式传入 `--asset-id <id>`。
115
+ 默认安装根会优先跟随当前 OpenClaw / StepClaw state dir:
136
116
 
137
- 只做本地校验和打包:
117
+ 1. `OPENCLAWMP_INSTALL_ROOT`
118
+ 2. `OPENCLAW_STATE_DIR` 或 `STEPCLAW_STATE_DIR`
119
+ 3. `~/.stepclaw/stepclaw-install-state.json` 中记录的 state dir
120
+ 4. 检测到 `~/.stepclaw` 时使用该目录
121
+ 5. 否则回退到 `~/.openclaw`
138
122
 
139
- ```bash
140
- openclawmp publish ./my-skill --dry-run
141
- ```
123
+ 不同资产类型的默认子目录:
142
124
 
143
- 安装示例:
125
+ | 类型 | 默认子目录 |
126
+ | --- | --- |
127
+ | `skill` | `skills` |
128
+ | `plugin` / `channel` | `extensions` |
129
+ | `trigger` | `triggers` |
130
+ | `experience` | `experiences` |
144
131
 
145
- ```bash
146
- openclawmp install skill/7c19dc4c3244418096f1dcb59c93f795
147
- openclawmp install skill/7c19dc4c3244418096f1dcb59c93f795@1.0.3
148
- ```
132
+ 也可以通过 `--target-dir <path>` 显式指定安装根目录。
149
133
 
150
- 卸载示例:
134
+ ### 列出和卸载本地资产
151
135
 
152
136
  ```bash
137
+ openclawmp list
138
+ openclawmp list --type skill
153
139
  openclawmp uninstall skill/7c19dc4c3244418096f1dcb59c93f795
154
140
  openclawmp uninstall skill/demo-skill
141
+ openclawmp uninstall skill/@author/name
142
+ ```
143
+
144
+ ### 发布资产
145
+
146
+ 发布目录或归档中必须包含 `.metadata.json`:
147
+
148
+ ```json
149
+ {
150
+ "assetType": "skill",
151
+ "name": "my-skill",
152
+ "displayName": "My Skill",
153
+ "semver": "1.0.0",
154
+ "category": "productivity",
155
+ "tags": ["agent", "automation"],
156
+ "description": "A short description"
157
+ }
155
158
  ```
156
159
 
157
- 搜索示例:
160
+ 发布示例:
158
161
 
159
162
  ```bash
160
- openclawmp search 天气 --page-size 10
161
- openclawmp users openclawmp.cc
163
+ openclawmp publish ./my-skill --yes
164
+ openclawmp publish ./my-skill --asset-id s-123456 --yes
165
+ openclawmp publish ./my-skill --dry-run
162
166
  ```
163
167
 
164
- ## 命令结构
168
+ 如果目录根部存在 `.assetid`,发布时会自动作为目标资产 ID 使用;也可通过 `--asset-id <id>` 显式指定。
169
+
170
+ `--dry-run` 只做本地校验和打包,不提交发布。
171
+
172
+ ## 发布 Metadata 规则
173
+
174
+ - `publish` 只读取发布目录或归档里的 `.metadata.json`。
175
+ - `.metadata.json` 必须包含 `assetType`、`name`、`displayName`、`semver`。
176
+ - `assetType` 支持:`skill`、`experience`、`plugin`、`trigger`、`channel`。
177
+ - `semver` 必须是严格的 `x.y.z` 纯数字格式,例如 `1.2.3`。
178
+ - 可选字段:`assetId`、`category`、`tags`、`description`、`longDescription`。
179
+ - 不再从 `README.md`、`package.json`、`openclaw.plugin.json`、`SKILL.md` 自动推断发布 metadata。
180
+ - 不再通过 CLI 参数补录 `assetType`、`name`、`displayName`、`semver`。
181
+ - `assetId` 可写在 `.metadata.json`,也可以通过 `--asset-id` 或目录根部 `.assetid` 传入。
182
+ - 默认不打印请求细节;加 `--verbose` 会额外输出调试信息。
183
+
184
+ ## 发布打包规则
185
+
186
+ - 目录发布会默认跳过 `.git`、`node_modules`、`__MACOSX`、`.DS_Store`、`Thumbs.db`。
187
+ - 根目录可通过 `.openclawmpignore` 补充忽略规则,也会读取根目录 `.gitignore` / `.npmignore` 的常见规则。
188
+ - 子路径遇到无权限读取的目录或文件时会自动跳过;使用 `--verbose` 可查看具体跳过项。
165
189
 
166
- 入口采用懒加载:
190
+ ## 常用参数
167
191
 
168
- - `publish`
169
- - `oauth`
170
- - `search`
171
- - `users`
172
- - `info`
173
- - `install`
174
- - `uninstall`
175
- - `list`
192
+ 不同命令支持的参数略有差异,请以 `--help` 输出为准。常见参数包括:
176
193
 
177
- 其中当前可用的是 `publish`、`oauth`、`search`、`users`、`info`、`install`、`uninstall`、`list`。
194
+ | 参数 | 说明 |
195
+ | --- | --- |
196
+ | `--base-url <url>` | 指定服务基地址。 |
197
+ | `--token-file <path>` | 指定本地凭证文件。 |
198
+ | `--request-id <id>` | 指定请求追踪 ID。 |
199
+ | `--page-size <number>` | 指定搜索分页大小。 |
200
+ | `--page-token <token>` | 指定下一页游标。 |
201
+ | `--verbose` | 输出更多调试信息。 |
202
+ | `-h, --help` | 查看帮助。 |
203
+ | `-v, --version` | 查看版本。 |
178
204
 
179
- ## 发布命令参数
205
+ 服务基地址优先级:
180
206
 
181
- - `--asset-id <id>`:未传时会尝试读取目录根部 `.assetid`
182
- - `--base-url <url>`
183
- - `--token-file <path>`
184
- - `--part-size-mb <number>`
185
- - `--ttl-seconds <number>`
186
- - `--request-id <id>`
187
- - `--dry-run`
188
- - `--yes, -y`
189
- - `--verbose`
207
+ 1. `--base-url`
208
+ 2. `OPENCLAWMP_API_BASE_URL`
209
+ 3. `OPENCLAWMP_BASE_URL`
210
+ 4. `API_BASE_URL`
211
+ 5. `NEXT_PUBLIC_API_BASE_URL`
212
+ 6. 仓库根目录 `.env.local` / `.env` / `.env.prod`
213
+ 7. `https://openclawmp.stepfun.com`
package/bin/openclawmp.js CHANGED
@@ -27,6 +27,9 @@ const COMMAND_LOADERS = {
27
27
  users: function loadUsers() {
28
28
  return require("../lib/commands/users");
29
29
  },
30
+ createComment: function loadCreateComment() {
31
+ return require("../lib/commands/create-comment");
32
+ },
30
33
  info: function loadInfo() {
31
34
  return require("../lib/commands/info");
32
35
  },
package/lib/api.js CHANGED
@@ -11,6 +11,7 @@ const {
11
11
 
12
12
  const CATALOG_SERVICE = "step.seafood.catalog.CatalogService";
13
13
  const IDENTITY_SERVICE = "step.seafood.identity.IdentityService";
14
+ const ENGAGEMENT_SERVICE = "step.seafood.engagement.EngagementService";
14
15
 
15
16
  class ApiError extends Error {
16
17
  constructor(message, details) {
@@ -245,6 +246,9 @@ function createCatalogClient(options) {
245
246
  searchUsers: function searchUsers(body, extra) {
246
247
  return unaryForService(IDENTITY_SERVICE, "SearchUsers", body, extra);
247
248
  },
249
+ createComment: function createComment(body, extra) {
250
+ return unaryForService(ENGAGEMENT_SERVICE, "CreateComment", body, extra);
251
+ },
248
252
  listUserAssets: function listUserAssets(body, extra) {
249
253
  return unary("ListUserAssets", body, extra);
250
254
  },
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+
3
+ const { parseOptions } = require("../cli-parser");
4
+ const { CLI_NAME } = require("../cli-text");
5
+ const { createCatalogClient } = require("../api");
6
+ const { loadHubCredentials } = require("../credentials");
7
+ const { defaultCredentialsPath, resolveConnectBaseUrl } = require("../config");
8
+ const { generateRequestId, trimOrUndefined } = require("../utils");
9
+
10
+ function printHelp() {
11
+ console.log(CLI_NAME + " createComment");
12
+ console.log("");
13
+ console.log("用法:");
14
+ console.log(" " + CLI_NAME + " createComment <assetId> <content>");
15
+ console.log("");
16
+ console.log("说明:");
17
+ console.log(" 调用 step.seafood.engagement.EngagementService/CreateComment 发送文本评论。");
18
+ console.log(" 命令只支持最简单的 assetId + 评论内容用法。");
19
+ console.log(" baseUrl 和 token 读取方式与 publish 命令一致。");
20
+ console.log("");
21
+ console.log("示例:");
22
+ console.log(" " + CLI_NAME + " createComment 8ec77f07430644bc3a848235d5c8d911 \"agrent\"");
23
+ }
24
+
25
+ function firstText(values) {
26
+ return values.find(function findValue(value) {
27
+ return typeof value === "string" && value.trim();
28
+ }) || "";
29
+ }
30
+
31
+ function parseArguments(positionals) {
32
+ const args = Array.isArray(positionals) ? positionals : [];
33
+ const assetId = trimOrUndefined(args[0]) || "";
34
+ const content = args.slice(1).join(" ").trim();
35
+
36
+ if (!assetId) {
37
+ throw new Error("createComment 命令缺少 assetId");
38
+ }
39
+ if (/\s/u.test(assetId) || assetId.includes("/")) {
40
+ throw new Error("createComment 命令的 assetId 格式不正确");
41
+ }
42
+ if (!content) {
43
+ throw new Error("createComment 命令缺少评论内容");
44
+ }
45
+
46
+ return {
47
+ assetId,
48
+ content
49
+ };
50
+ }
51
+
52
+ function buildCreateCommentRequest(assetId, content) {
53
+ return {
54
+ assetId,
55
+ contentSegments: [
56
+ {
57
+ type: "COMMENT_CONTENT_TYPE_TEXT",
58
+ text: content
59
+ }
60
+ ]
61
+ };
62
+ }
63
+
64
+ function resolveCommentId(data) {
65
+ if (!data || typeof data !== "object") {
66
+ return "";
67
+ }
68
+
69
+ return firstText([
70
+ data.commentId,
71
+ data.id,
72
+ data.comment && data.comment.commentId,
73
+ data.comment && data.comment.id
74
+ ]);
75
+ }
76
+
77
+ function loadCommentCredentials() {
78
+ try {
79
+ return loadHubCredentials();
80
+ } catch {
81
+ throw new Error(
82
+ [
83
+ "未检测到可用于发送评论的登录态。",
84
+ "请先通过 " + CLI_NAME + " oauth <token> 写入 token,或手动写入 " + defaultCredentialsPath() + "。"
85
+ ].join("\n")
86
+ );
87
+ }
88
+ }
89
+
90
+ async function run(context) {
91
+ const parsed = parseOptions(context.argv, {});
92
+ const args = parseArguments(parsed.positionals);
93
+ const loadedCredentials = loadCommentCredentials();
94
+ const requestId = generateRequestId();
95
+ const client = createCatalogClient({
96
+ baseUrl: resolveConnectBaseUrl(),
97
+ token: loadedCredentials.credentials.token,
98
+ cliVersion: context.cliVersion,
99
+ requestId
100
+ });
101
+
102
+ const response = await client.createComment(
103
+ buildCreateCommentRequest(args.assetId, args.content)
104
+ );
105
+ const data = response.data || {};
106
+ const commentId = resolveCommentId(data);
107
+
108
+ console.log("");
109
+ console.log("评论发送成功");
110
+ console.log("- assetId: " + args.assetId);
111
+ if (commentId) {
112
+ console.log("- commentId: " + commentId);
113
+ }
114
+ console.log("- 内容: " + args.content);
115
+
116
+ return {
117
+ assetId: args.assetId,
118
+ content: args.content,
119
+ commentId,
120
+ data,
121
+ requestId: response.requestId
122
+ };
123
+ }
124
+
125
+ module.exports = {
126
+ buildCreateCommentRequest,
127
+ parseArguments,
128
+ printHelp,
129
+ run
130
+ };
@@ -22,6 +22,9 @@ const COMMAND_LOADERS = {
22
22
  users: function loadUsers() {
23
23
  return require("./users");
24
24
  },
25
+ createComment: function loadCreateComment() {
26
+ return require("./create-comment");
27
+ },
25
28
  info: function loadInfo() {
26
29
  return require("./info");
27
30
  },
@@ -33,11 +33,12 @@ function printHelp() {
33
33
  console.log(" install 需要本地 token,默认读取 ~/.openclaw/hub-credentials.json");
34
34
  console.log(" 可通过 --token-file <path> 指定其他凭证文件");
35
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");
36
+ console.log(" 优先跟随当前 OpenClaw/StepClaw state dir(OPENCLAW_STATE_DIR、STEPCLAW_STATE_DIR 或 ~/.stepclaw)");
37
+ console.log(" 未检测到 StepClaw 时回退到 ~/.openclaw");
38
+ console.log(" skill -> <state-dir>/skills 或 ~/.openclaw/skills");
39
+ console.log(" plugin/channel -> <state-dir>/extensions 或 ~/.openclaw/extensions");
40
+ console.log(" trigger -> <state-dir>/triggers 或 ~/.openclaw/triggers");
41
+ console.log(" experience -> <state-dir>/experiences 或 ~/.openclaw/experiences");
41
42
  console.log("");
42
43
  console.log("示例:");
43
44
  console.log(" " + buildInstallCommand("skill", "7c19dc4c3244418096f1dcb59c93f795"));
package/lib/config.js CHANGED
@@ -17,6 +17,12 @@ const INSTALL_ROOT_BY_TYPE = {
17
17
  experience: ["experiences"],
18
18
  template: ["templates"],
19
19
  };
20
+ const OPENCLAWMP_INSTALL_ROOT_ENV = "OPENCLAWMP_INSTALL_ROOT";
21
+ const OPENCLAW_STATE_DIR_ENV = "OPENCLAW_STATE_DIR";
22
+ const STEPCLAW_STATE_DIR_ENV = "STEPCLAW_STATE_DIR";
23
+ const STEPCLAW_STATE_DIR_NAME = ".stepclaw";
24
+ const STEPCLAW_INSTALL_STATE_FILE = "stepclaw-install-state.json";
25
+ const STEPCLAW_RUNTIME_STATE_FILE = "stepclaw-state.json";
20
26
 
21
27
  function parseEnvFile(content) {
22
28
  const result = {};
@@ -68,6 +74,26 @@ function firstDefined(values) {
68
74
  });
69
75
  }
70
76
 
77
+ function trimString(value) {
78
+ return typeof value === "string" ? value.trim() : "";
79
+ }
80
+
81
+ function expandHomePath(value) {
82
+ const trimmed = trimString(value);
83
+ if (trimmed === "~") {
84
+ return os.homedir();
85
+ }
86
+ if (trimmed.startsWith("~/") || trimmed.startsWith("~\\")) {
87
+ return path.join(os.homedir(), trimmed.slice(2));
88
+ }
89
+ return trimmed;
90
+ }
91
+
92
+ function resolvePathValue(value) {
93
+ const expanded = expandHomePath(value);
94
+ return expanded ? path.resolve(expanded) : "";
95
+ }
96
+
71
97
  function normalizeBaseUrl(value) {
72
98
  return String(value)
73
99
  .trim()
@@ -100,6 +126,53 @@ function getOpenClawHome() {
100
126
  return path.join(os.homedir(), ".openclaw");
101
127
  }
102
128
 
129
+ function getDefaultStepClawStateDir() {
130
+ return path.join(os.homedir(), STEPCLAW_STATE_DIR_NAME);
131
+ }
132
+
133
+ function readJsonFileIfExists(filePath) {
134
+ try {
135
+ return JSON.parse(fs.readFileSync(filePath, "utf8"));
136
+ } catch (error) {
137
+ return null;
138
+ }
139
+ }
140
+
141
+ function looksLikeStepClawStateDir(stateDir) {
142
+ return (
143
+ fs.existsSync(path.join(stateDir, "openclaw.json")) ||
144
+ fs.existsSync(path.join(stateDir, STEPCLAW_INSTALL_STATE_FILE)) ||
145
+ fs.existsSync(
146
+ path.join(stateDir, "runtime", STEPCLAW_RUNTIME_STATE_FILE),
147
+ )
148
+ );
149
+ }
150
+
151
+ function resolveStepClawStateDir() {
152
+ const envStateDir = firstDefined([
153
+ process.env[OPENCLAW_STATE_DIR_ENV],
154
+ process.env[STEPCLAW_STATE_DIR_ENV],
155
+ ]);
156
+ if (envStateDir) {
157
+ return resolvePathValue(envStateDir);
158
+ }
159
+
160
+ const defaultStateDir = getDefaultStepClawStateDir();
161
+ const presence = readJsonFileIfExists(
162
+ path.join(defaultStateDir, STEPCLAW_INSTALL_STATE_FILE),
163
+ );
164
+ const redirectedStateDir = trimString(presence && presence.stateDir);
165
+ if (redirectedStateDir) {
166
+ return resolvePathValue(redirectedStateDir);
167
+ }
168
+
169
+ if (looksLikeStepClawStateDir(defaultStateDir)) {
170
+ return defaultStateDir;
171
+ }
172
+
173
+ return "";
174
+ }
175
+
103
176
  function defaultCredentialsPath() {
104
177
  return path.join(getOpenClawHome(), "hub-credentials.json");
105
178
  }
@@ -109,7 +182,14 @@ function getLockfilePath() {
109
182
  }
110
183
 
111
184
  function getManagedAssetsRoot() {
112
- return getOpenClawHome();
185
+ const explicitInstallRoot = firstDefined([
186
+ process.env[OPENCLAWMP_INSTALL_ROOT_ENV],
187
+ ]);
188
+ if (explicitInstallRoot) {
189
+ return resolvePathValue(explicitInstallRoot);
190
+ }
191
+
192
+ return resolveStepClawStateDir() || getOpenClawHome();
113
193
  }
114
194
 
115
195
  function getInstallRoot(assetType) {
@@ -126,6 +206,8 @@ module.exports = {
126
206
  resolveApiBaseUrl,
127
207
  resolveConnectBaseUrl,
128
208
  getOpenClawHome,
209
+ getDefaultStepClawStateDir,
210
+ resolveStepClawStateDir,
129
211
  defaultCredentialsPath,
130
212
  getLockfilePath,
131
213
  getManagedAssetsRoot,
package/lib/help.js CHANGED
@@ -33,6 +33,11 @@ const COMMAND_SECTIONS = [
33
33
  description: "搜索用户",
34
34
  usage: CLI_NAME + " users <keyword> [options]"
35
35
  },
36
+ {
37
+ name: "createComment",
38
+ description: "发送资产评论",
39
+ usage: CLI_NAME + " createComment <assetId> <content>"
40
+ },
36
41
  {
37
42
  name: "info",
38
43
  description: "查看某个资产的详细信息",
@@ -63,7 +68,7 @@ function printGlobalHelp() {
63
68
  console.log("");
64
69
  console.log("命令:");
65
70
  COMMAND_SECTIONS.forEach(function eachSection(section) {
66
- const paddedName = (section.name + " ").slice(0, 12);
71
+ const paddedName = (section.name + " ").slice(0, 15);
67
72
  console.log(" " + paddedName + section.description);
68
73
  });
69
74
  console.log("");
@@ -82,6 +87,7 @@ function printGlobalHelp() {
82
87
  console.log(" " + CLI_NAME + " publish ./my-asset --yes");
83
88
  console.log(" " + CLI_NAME + " search 天气");
84
89
  console.log(" " + CLI_NAME + " users openclawmp.cc");
90
+ console.log(" " + CLI_NAME + " createComment 8ec77f07430644bc3a848235d5c8d911 \"agrent\"");
85
91
  console.log(" " + CLI_NAME + " info 6c476a36955a4270a3fa303beeeed5ee");
86
92
  console.log(" " + buildInstallCommand("skill", "7c19dc4c3244418096f1dcb59c93f795"));
87
93
  console.log(" " + buildInstallCommand("skill", "7c19dc4c3244418096f1dcb59c93f795", "1.0.3"));
package/lib/utils.js CHANGED
@@ -199,6 +199,8 @@ function humanizeApiMethodName(methodName) {
199
199
  return "搜索资产";
200
200
  case "SearchUsers":
201
201
  return "搜索用户";
202
+ case "CreateComment":
203
+ return "发送评论";
202
204
  case "ListUserAssets":
203
205
  return "查询用户资产";
204
206
  case "GetAsset":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclawmp",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "OpenClaw Marketplace CLI client",
5
5
  "bin": {
6
6
  "openclawmp": "bin/openclawmp.js"