@botanee/skillhub-cli 1.0.6

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/bin/run ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env node
2
+ // eslint-disable-next-line node/shebang
3
+ import { run, Errors } from '@oclif/core'
4
+
5
+ run(undefined, import.meta.url)
6
+ .then(async () => {
7
+ process.exit(0)
8
+ })
9
+ .catch(async (err) => {
10
+ // 简化错误输出,只显示消息
11
+ if (err instanceof Errors.CLIError) {
12
+ console.error(err.message)
13
+ process.exit(1)
14
+ }
15
+ // 其他错误显示完整堆栈
16
+ console.error(err)
17
+ process.exit(1)
18
+ })
@@ -0,0 +1,16 @@
1
+ import { BaseCommand } from '../index';
2
+ export default class AgentLogin extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ help: import("@oclif/core/lib/interfaces").BooleanFlag<void>;
7
+ 'agent-id': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
8
+ 'workspace-id': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
9
+ 'deployment-id': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
10
+ environment: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
11
+ 'agent-type': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
12
+ timeout: import("@oclif/core/lib/interfaces").OptionFlag<number, import("@oclif/core/lib/interfaces").CustomOptions>;
13
+ };
14
+ run(): Promise<void>;
15
+ private sleep;
16
+ }
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const core_1 = require("@oclif/core");
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const cli_ux_1 = __importDefault(require("cli-ux"));
9
+ const axios_1 = __importDefault(require("axios"));
10
+ const index_1 = require("../index");
11
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
12
+ const qrcodeTerminal = require('qrcode-terminal');
13
+ class AgentLogin extends index_1.BaseCommand {
14
+ static description = '远程 Agent 登录 BtnSkillHub 平台';
15
+ static examples = [
16
+ '$ btnskill agent-login --agent-id openclaw-prod-01',
17
+ '$ btnskill agent-login --agent-id codex-prod-01 --agent-type codex',
18
+ ];
19
+ static flags = {
20
+ help: core_1.Flags.help({ char: 'h' }),
21
+ 'agent-id': core_1.Flags.string({
22
+ description: 'Agent 唯一标识',
23
+ required: true,
24
+ }),
25
+ 'workspace-id': core_1.Flags.string({
26
+ description: 'Agent 所属工作区标识',
27
+ }),
28
+ 'deployment-id': core_1.Flags.string({
29
+ description: 'Agent 部署实例标识',
30
+ }),
31
+ environment: core_1.Flags.string({
32
+ description: '运行环境,例如 prod/staging',
33
+ }),
34
+ 'agent-type': core_1.Flags.string({
35
+ description: 'Agent 类型,例如 openclaw/claude-code/codex',
36
+ default: 'openclaw',
37
+ }),
38
+ timeout: core_1.Flags.integer({
39
+ description: '最长等待授权时间(秒)',
40
+ default: 600,
41
+ }),
42
+ };
43
+ async run() {
44
+ const { flags } = await this.parse(AgentLogin);
45
+ this.log(chalk_1.default.blue('\n🤖 BtnSkillHub Agent 登录\n'));
46
+ const startResponse = await axios_1.default.post(`${this.apiBaseUrl}/auth/agent/start`, {
47
+ agentId: flags['agent-id'],
48
+ workspaceId: flags['workspace-id'],
49
+ deploymentId: flags['deployment-id'],
50
+ environment: flags.environment,
51
+ agentType: flags['agent-type'],
52
+ });
53
+ const loginData = startResponse.data.data;
54
+ this.log(`请让用户完成授权:\n`);
55
+ this.log(`链接:\n${chalk_1.default.cyan(loginData.verificationUriComplete)}\n`);
56
+ this.log(`授权码: ${chalk_1.default.bold(loginData.userCode)}`);
57
+ this.log(`二维码内容: ${chalk_1.default.gray(loginData.qrCodePayload)}\n`);
58
+ qrcodeTerminal.generate(loginData.qrCodePayload, { small: true }, (qr) => {
59
+ this.log(qr);
60
+ });
61
+ const expiresAt = Date.now() + Math.min(flags.timeout, loginData.expiresIn) * 1000;
62
+ const pollIntervalMs = Math.max(loginData.interval, 1) * 1000;
63
+ cli_ux_1.default.action.start('等待用户完成 Agent 授权');
64
+ while (Date.now() < expiresAt) {
65
+ await this.sleep(pollIntervalMs);
66
+ const pollResponse = await axios_1.default.post(`${this.apiBaseUrl}/auth/agent/poll`, {
67
+ agentCode: loginData.agentCode,
68
+ });
69
+ const result = pollResponse.data?.data;
70
+ if (result?.status === 'pending') {
71
+ continue;
72
+ }
73
+ if (result?.status === 'expired') {
74
+ cli_ux_1.default.action.stop('失败');
75
+ this.error('Agent 授权已过期,请重新执行 btnskill agent-login');
76
+ }
77
+ if (result?.status === 'revoked') {
78
+ cli_ux_1.default.action.stop('失败');
79
+ this.error('Agent 授权已被撤销');
80
+ }
81
+ if (result?.accessToken) {
82
+ this.storeAuthSession({
83
+ accessToken: result.accessToken,
84
+ refreshToken: result.refreshToken,
85
+ agentId: flags['agent-id'],
86
+ authMode: 'agent',
87
+ agentType: flags['agent-type'],
88
+ });
89
+ cli_ux_1.default.action.stop();
90
+ this.log(chalk_1.default.green('\n✅ Agent 登录成功!'));
91
+ this.log('现在可以使用 btnskill 命令了\n');
92
+ return;
93
+ }
94
+ }
95
+ cli_ux_1.default.action.stop('失败');
96
+ this.error('等待授权超时,请重新执行 btnskill agent-login');
97
+ }
98
+ sleep(ms) {
99
+ return new Promise((resolve) => setTimeout(resolve, ms));
100
+ }
101
+ }
102
+ exports.default = AgentLogin;
@@ -0,0 +1,19 @@
1
+ import { BaseCommand } from '../index';
2
+ export default class Config extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ help: import("@oclif/core/lib/interfaces").BooleanFlag<void>;
7
+ };
8
+ static args: {
9
+ action: import("@oclif/core/lib/interfaces").Arg<string | undefined, Record<string, unknown>>;
10
+ key: import("@oclif/core/lib/interfaces").Arg<string | undefined, Record<string, unknown>>;
11
+ value: import("@oclif/core/lib/interfaces").Arg<string | undefined, Record<string, unknown>>;
12
+ };
13
+ run(): Promise<void>;
14
+ private resolveKey;
15
+ private printConfig;
16
+ private getConfig;
17
+ private setConfig;
18
+ private unsetConfig;
19
+ }
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const core_1 = require("@oclif/core");
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const index_1 = require("../index");
9
+ const KEY_ALIASES = {
10
+ registry: 'registry',
11
+ api: 'registry',
12
+ apiBaseUrl: 'registry',
13
+ };
14
+ class Config extends index_1.BaseCommand {
15
+ static description = '查看或设置 CLI 配置';
16
+ static examples = [
17
+ '$ btnskill config list',
18
+ '$ btnskill config get registry',
19
+ '$ btnskill config set registry https://skillhub.botanee.com.cn',
20
+ '$ btnskill config unset registry',
21
+ ];
22
+ static flags = {
23
+ help: core_1.Flags.help({ char: 'h' }),
24
+ };
25
+ static args = {
26
+ action: core_1.Args.string({ description: 'list | get | set | unset', required: false }),
27
+ key: core_1.Args.string({ description: '配置项名称', required: false }),
28
+ value: core_1.Args.string({ description: '配置项值', required: false }),
29
+ };
30
+ async run() {
31
+ const { args } = await this.parse(Config);
32
+ const action = args.action || 'list';
33
+ switch (action) {
34
+ case 'list':
35
+ this.printConfig();
36
+ return;
37
+ case 'get':
38
+ this.getConfig(args.key);
39
+ return;
40
+ case 'set':
41
+ this.setConfig(args.key, args.value);
42
+ return;
43
+ case 'unset':
44
+ this.unsetConfig(args.key);
45
+ return;
46
+ default:
47
+ this.error(`不支持的操作: ${action}`);
48
+ }
49
+ }
50
+ resolveKey(key) {
51
+ if (!key) {
52
+ this.error('请指定配置项,例如: btnskill config get registry');
53
+ }
54
+ const resolved = KEY_ALIASES[key];
55
+ if (!resolved) {
56
+ this.error(`不支持的配置项: ${key}`);
57
+ }
58
+ return resolved;
59
+ }
60
+ printConfig() {
61
+ const storedRegistry = this.getConfigValue('registry');
62
+ const envRegistry = process.env.API_BASE_URL;
63
+ this.log(chalk_1.default.blue('\n当前 CLI 配置:\n'));
64
+ this.log(` registry (effective): ${chalk_1.default.cyan(this.apiBaseUrl)}`);
65
+ this.log(` registry (stored): ${storedRegistry ? chalk_1.default.cyan(storedRegistry) : chalk_1.default.gray('(未设置)')}`);
66
+ this.log(` API_BASE_URL (env): ${envRegistry ? chalk_1.default.cyan(envRegistry) : chalk_1.default.gray('(未设置)')}`);
67
+ if (envRegistry) {
68
+ this.log(chalk_1.default.yellow('\n环境变量 API_BASE_URL 优先级高于持久化 registry 配置。\n'));
69
+ }
70
+ else {
71
+ this.log();
72
+ }
73
+ }
74
+ getConfig(key) {
75
+ const resolvedKey = this.resolveKey(key);
76
+ if (resolvedKey === 'registry') {
77
+ this.log(this.apiBaseUrl);
78
+ }
79
+ }
80
+ setConfig(key, value) {
81
+ const resolvedKey = this.resolveKey(key);
82
+ if (!value) {
83
+ this.error('请提供配置值,例如: btnskill config set registry https://skillhub.botanee.com.cn');
84
+ }
85
+ if (resolvedKey === 'registry') {
86
+ const normalized = this.normalizeApiBaseUrl(value);
87
+ this.setConfigValue('registry', normalized);
88
+ this.log(chalk_1.default.green(`已保存 registry: ${normalized}`));
89
+ }
90
+ }
91
+ unsetConfig(key) {
92
+ const resolvedKey = this.resolveKey(key);
93
+ if (resolvedKey === 'registry') {
94
+ this.deleteConfigValue('registry');
95
+ this.log(chalk_1.default.green('已删除 registry 配置'));
96
+ }
97
+ }
98
+ }
99
+ exports.default = Config;
@@ -0,0 +1,12 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class HelpCommand extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ 'nested-commands': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
7
+ };
8
+ static args: {
9
+ command: import("@oclif/core/lib/interfaces").Arg<string | undefined, Record<string, unknown>>;
10
+ };
11
+ run(): Promise<void>;
12
+ }
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const core_1 = require("@oclif/core");
4
+ class HelpCommand extends core_1.Command {
5
+ static description = '显示帮助信息';
6
+ static examples = ['$ btnskill help', '$ btnskill help search'];
7
+ static flags = {
8
+ 'nested-commands': core_1.Flags.boolean({
9
+ description: 'Include all nested commands in the output.',
10
+ }),
11
+ };
12
+ static args = {
13
+ command: core_1.Args.string({ description: '要查看帮助的命令' }),
14
+ };
15
+ async run() {
16
+ const { argv, flags } = await this.parse(HelpCommand);
17
+ const help = new core_1.Help(this.config, { all: flags['nested-commands'] });
18
+ help.showHelp(argv);
19
+ }
20
+ }
21
+ exports.default = HelpCommand;
@@ -0,0 +1,17 @@
1
+ import { BaseCommand } from '../index';
2
+ export default class Install extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ help: import("@oclif/core/lib/interfaces").BooleanFlag<void>;
7
+ 'agent-type': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
8
+ global: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
9
+ target: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
10
+ };
11
+ static args: {
12
+ skill: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
13
+ };
14
+ run(): Promise<void>;
15
+ private normalizeAxiosError;
16
+ private registerSkillToOpenClaw;
17
+ }
@@ -0,0 +1,244 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ const core_1 = require("@oclif/core");
40
+ const chalk_1 = __importDefault(require("chalk"));
41
+ const cli_ux_1 = __importDefault(require("cli-ux"));
42
+ const axios_1 = __importDefault(require("axios"));
43
+ const fs = __importStar(require("fs"));
44
+ const path = __importStar(require("path"));
45
+ const os = __importStar(require("os"));
46
+ const adm_zip_1 = __importDefault(require("adm-zip"));
47
+ const index_1 = require("../index");
48
+ const install_paths_1 = require("../lib/install-paths");
49
+ class Install extends index_1.BaseCommand {
50
+ static description = '安装一个 Skill';
51
+ static examples = [
52
+ '$ btnskill install my-skill',
53
+ '$ btnskill install my-skill@1.0.0',
54
+ '$ btnskill install my-skill --agent-type claude-code',
55
+ '$ btnskill install my-skill --agent-type openclaw',
56
+ '$ btnskill install my-skill --agent-type codex',
57
+ '$ btnskill install my-skill --target ~/.custom-agent/skills',
58
+ ];
59
+ static flags = {
60
+ help: core_1.Flags.help({ char: 'h' }),
61
+ 'agent-type': core_1.Flags.string({ description: `安装到指定 Agent 类型的 Skills 目录,支持 ${install_paths_1.SUPPORTED_INSTALL_AGENT_TYPES.join('/')}` }),
62
+ global: core_1.Flags.boolean({ char: 'g', description: '安装到默认 Agent 目录(兼容旧参数)' }),
63
+ target: core_1.Flags.string({ char: 't', description: '指定自定义 Skills 根目录,优先级高于 --agent-type' }),
64
+ };
65
+ static args = {
66
+ skill: core_1.Args.string({ description: 'Skill 名称 (可带版本号)', required: true }),
67
+ };
68
+ async run() {
69
+ const { args, flags } = await this.parse(Install);
70
+ const installTarget = (0, install_paths_1.resolveSkillsDir)({ target: flags.target, agentType: flags['agent-type'] });
71
+ if (installTarget.unsupportedAgentType) {
72
+ this.error(`不支持的 Agent 类型: ${installTarget.unsupportedAgentType}。当前支持: ${install_paths_1.SUPPORTED_INSTALL_AGENT_TYPES.join(', ')}`);
73
+ }
74
+ const token = await this.requireAuth();
75
+ // 解析 skill 名称和版本
76
+ const [name, version] = args.skill.split('@');
77
+ cli_ux_1.default.action.start(`正在安装 ${name}${version ? `@${version}` : ''}`);
78
+ try {
79
+ // 1. 获取 skill 基本信息
80
+ const response = await axios_1.default.get(`${this.apiBaseUrl}/skills/${encodeURIComponent(name)}`, {
81
+ headers: { Authorization: `Bearer ${token}` },
82
+ });
83
+ const skill = response.data.data;
84
+ const installedVersion = version || skill.version;
85
+ if (version) {
86
+ const versionsResponse = await axios_1.default.get(`${this.apiBaseUrl}/skills/${skill.id}/versions`, {
87
+ headers: { Authorization: `Bearer ${token}` },
88
+ });
89
+ const matchedVersion = versionsResponse.data.data.find((item) => item.version === version);
90
+ if (!matchedVersion) {
91
+ this.error(`未找到版本: ${version}`);
92
+ }
93
+ }
94
+ // 2. 确定安装目录(默认安装到通用 Agent 目录)
95
+ const installDir = installTarget.dir;
96
+ // 3. 通过受保护接口下载 skill 包
97
+ const skillDir = path.join(installDir, skill.name);
98
+ const tmpFile = path.join(os.tmpdir(), `${skill.name}-${installedVersion}-${Date.now()}.zip`);
99
+ const fileResponse = await axios_1.default.get(`${this.apiBaseUrl}/skills/${skill.id}/download${version ? `?version=${encodeURIComponent(version)}` : ''}`, {
100
+ headers: { Authorization: `Bearer ${token}` },
101
+ responseType: 'arraybuffer',
102
+ });
103
+ fs.writeFileSync(tmpFile, fileResponse.data);
104
+ // 4. 解压并安装
105
+ if (!fs.existsSync(installDir)) {
106
+ fs.mkdirSync(installDir, { recursive: true });
107
+ }
108
+ // 如果已存在,先删除
109
+ if (fs.existsSync(skillDir)) {
110
+ fs.rmSync(skillDir, { recursive: true });
111
+ }
112
+ // 解压到临时目录
113
+ const extractDir = path.join(os.tmpdir(), `extract-${Date.now()}`);
114
+ const zip = new adm_zip_1.default(tmpFile);
115
+ zip.extractAllTo(extractDir, true);
116
+ // 检查是否需要"提升"目录(如果解压后只有一个子目录)
117
+ const extractedItems = fs.readdirSync(extractDir);
118
+ let sourceDir = extractDir;
119
+ // 过滤掉 __MACOSX 等隐藏目录
120
+ const validItems = extractedItems.filter(item => !item.startsWith('.') && !item.startsWith('__MACOSX'));
121
+ if (validItems.length === 1 && !validItems[0].includes('.')) {
122
+ // 只有一个子目录,提升一层
123
+ sourceDir = path.join(extractDir, validItems[0]);
124
+ }
125
+ // 移动到最终目录
126
+ fs.mkdirSync(skillDir, { recursive: true });
127
+ const items = fs.readdirSync(sourceDir);
128
+ for (const item of items) {
129
+ if (item.startsWith('__MACOSX'))
130
+ continue;
131
+ fs.renameSync(path.join(sourceDir, item), path.join(skillDir, item));
132
+ }
133
+ // 清理临时目录
134
+ fs.rmSync(extractDir, { recursive: true });
135
+ // 5. 生成元数据文件
136
+ // _meta.json - 技能元数据
137
+ const metaData = {
138
+ name: skill.name,
139
+ version: installedVersion,
140
+ description: skill.description,
141
+ author: skill.authorId,
142
+ tags: skill.tags || [],
143
+ publishedAt: skill.createdAt,
144
+ };
145
+ fs.writeFileSync(path.join(skillDir, '_meta.json'), JSON.stringify(metaData, null, 2));
146
+ // .btnskill/origin.json - 安装来源信息
147
+ const btnskillDir = path.join(skillDir, '.btnskill');
148
+ if (!fs.existsSync(btnskillDir)) {
149
+ fs.mkdirSync(btnskillDir, { recursive: true });
150
+ }
151
+ const originData = {
152
+ version: 1,
153
+ registry: this.registryOrigin,
154
+ name: skill.name,
155
+ id: skill.id,
156
+ installedVersion,
157
+ installedAt: Date.now(),
158
+ };
159
+ fs.writeFileSync(path.join(btnskillDir, 'origin.json'), JSON.stringify(originData, null, 2));
160
+ // 6. 清理临时文件
161
+ fs.unlinkSync(tmpFile);
162
+ // 7. 安装到 OpenClaw 时同步注册 OpenClaw 配置
163
+ if (installTarget.agentType === 'openclaw') {
164
+ await this.registerSkillToOpenClaw(skill.name, skillDir);
165
+ }
166
+ cli_ux_1.default.action.stop();
167
+ this.log(chalk_1.default.green(`\n✅ 成功安装 ${skill.name}@${skill.version}`));
168
+ this.log(`安装路径: ${skillDir}\n`);
169
+ }
170
+ catch (error) {
171
+ cli_ux_1.default.action.stop('失败');
172
+ const normalizedError = this.normalizeAxiosError(error);
173
+ if (normalizedError.status === 401) {
174
+ this.error(`登录已过期,请重新登录: ${this.loginHint}`);
175
+ }
176
+ this.error(normalizedError.message || '安装失败');
177
+ }
178
+ }
179
+ normalizeAxiosError(error) {
180
+ const status = error?.response?.status;
181
+ const data = error?.response?.data;
182
+ if (typeof data?.message === 'string') {
183
+ return { status, message: data.message };
184
+ }
185
+ if (Buffer.isBuffer(data)) {
186
+ try {
187
+ const parsed = JSON.parse(data.toString('utf8'));
188
+ if (typeof parsed?.message === 'string') {
189
+ return { status, message: parsed.message };
190
+ }
191
+ }
192
+ catch {
193
+ // Ignore malformed response bodies and fall through to generic handling.
194
+ }
195
+ }
196
+ if (data instanceof ArrayBuffer) {
197
+ try {
198
+ const parsed = JSON.parse(Buffer.from(data).toString('utf8'));
199
+ if (typeof parsed?.message === 'string') {
200
+ return { status, message: parsed.message };
201
+ }
202
+ }
203
+ catch {
204
+ // Ignore malformed response bodies and fall through to generic handling.
205
+ }
206
+ }
207
+ return { status, message: error?.message };
208
+ }
209
+ async registerSkillToOpenClaw(skillName, skillDir) {
210
+ const openclawConfigPath = path.join(os.homedir(), '.openclaw', 'openclaw.json');
211
+ let config = {};
212
+ // 读取现有配置
213
+ if (fs.existsSync(openclawConfigPath)) {
214
+ try {
215
+ const content = fs.readFileSync(openclawConfigPath, 'utf-8');
216
+ config = JSON.parse(content);
217
+ }
218
+ catch {
219
+ // 配置文件格式错误,重置为空对象
220
+ config = {};
221
+ }
222
+ }
223
+ // 确保 skills 结构存在
224
+ if (!config.skills) {
225
+ config.skills = {};
226
+ }
227
+ if (!config.skills.entries) {
228
+ config.skills.entries = {};
229
+ }
230
+ // 添加 skill 条目
231
+ const skillKey = skillName.toLowerCase();
232
+ config.skills.entries[skillKey] = {
233
+ enabled: true,
234
+ };
235
+ // 确保目录存在
236
+ const openclawDir = path.dirname(openclawConfigPath);
237
+ if (!fs.existsSync(openclawDir)) {
238
+ fs.mkdirSync(openclawDir, { recursive: true });
239
+ }
240
+ // 写入配置
241
+ fs.writeFileSync(openclawConfigPath, JSON.stringify(config, null, 2));
242
+ }
243
+ }
244
+ exports.default = Install;
@@ -0,0 +1,11 @@
1
+ import { BaseCommand } from '../index';
2
+ export default class List extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ help: import("@oclif/core/lib/interfaces").BooleanFlag<void>;
7
+ 'agent-type': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
8
+ local: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
9
+ };
10
+ run(): Promise<void>;
11
+ }