@dofe/infra-clients 0.1.38 → 0.1.41
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/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/internal/mlflow/mlflow.client.d.ts +44 -0
- package/dist/internal/mlflow/mlflow.client.d.ts.map +1 -0
- package/dist/internal/mlflow/mlflow.client.js +184 -0
- package/dist/internal/mlflow/mlflow.client.js.map +1 -0
- package/dist/internal/mlflow/mlflow.module.d.ts +3 -0
- package/dist/internal/mlflow/mlflow.module.d.ts.map +1 -0
- package/dist/internal/mlflow/mlflow.module.js +23 -0
- package/dist/internal/mlflow/mlflow.module.js.map +1 -0
- package/dist/internal/model-research-proxy/anthropic-proxy-research.client.d.ts +52 -0
- package/dist/internal/model-research-proxy/anthropic-proxy-research.client.d.ts.map +1 -0
- package/dist/internal/model-research-proxy/anthropic-proxy-research.client.js +174 -0
- package/dist/internal/model-research-proxy/anthropic-proxy-research.client.js.map +1 -0
- package/dist/internal/model-research-proxy/anthropic-proxy-research.module.d.ts +3 -0
- package/dist/internal/model-research-proxy/anthropic-proxy-research.module.d.ts.map +1 -0
- package/dist/internal/model-research-proxy/anthropic-proxy-research.module.js +30 -0
- package/dist/internal/model-research-proxy/anthropic-proxy-research.module.js.map +1 -0
- package/dist/internal/model-research-proxy/index.d.ts +6 -0
- package/dist/internal/model-research-proxy/index.d.ts.map +1 -0
- package/dist/internal/model-research-proxy/index.js +24 -0
- package/dist/internal/model-research-proxy/index.js.map +1 -0
- package/dist/internal/model-verify/index.d.ts +4 -0
- package/dist/internal/model-verify/index.d.ts.map +1 -0
- package/dist/internal/model-verify/index.js +26 -0
- package/dist/internal/model-verify/index.js.map +1 -0
- package/dist/internal/model-verify/model-verify.client.d.ts +77 -0
- package/dist/internal/model-verify/model-verify.client.d.ts.map +1 -0
- package/dist/internal/model-verify/model-verify.client.js +324 -0
- package/dist/internal/model-verify/model-verify.client.js.map +1 -0
- package/dist/internal/openclaw/docker-exec.service.d.ts +132 -0
- package/dist/internal/openclaw/docker-exec.service.d.ts.map +1 -0
- package/dist/internal/openclaw/docker-exec.service.js +544 -0
- package/dist/internal/openclaw/docker-exec.service.js.map +1 -0
- package/dist/internal/openclaw/index.d.ts +14 -0
- package/dist/internal/openclaw/index.d.ts.map +1 -0
- package/dist/internal/openclaw/index.js +32 -0
- package/dist/internal/openclaw/index.js.map +1 -0
- package/dist/internal/openclaw/openclaw-agent-coordination.client.d.ts +73 -0
- package/dist/internal/openclaw/openclaw-agent-coordination.client.d.ts.map +1 -0
- package/dist/internal/openclaw/openclaw-agent-coordination.client.js +249 -0
- package/dist/internal/openclaw/openclaw-agent-coordination.client.js.map +1 -0
- package/dist/internal/openclaw/openclaw-context-status.client.d.ts +66 -0
- package/dist/internal/openclaw/openclaw-context-status.client.d.ts.map +1 -0
- package/dist/internal/openclaw/openclaw-context-status.client.js +164 -0
- package/dist/internal/openclaw/openclaw-context-status.client.js.map +1 -0
- package/dist/internal/openclaw/openclaw-cron.client.d.ts +61 -0
- package/dist/internal/openclaw/openclaw-cron.client.d.ts.map +1 -0
- package/dist/internal/openclaw/openclaw-cron.client.js +416 -0
- package/dist/internal/openclaw/openclaw-cron.client.js.map +1 -0
- package/dist/internal/openclaw/openclaw-gateway.client.d.ts +41 -0
- package/dist/internal/openclaw/openclaw-gateway.client.d.ts.map +1 -0
- package/dist/internal/openclaw/openclaw-gateway.client.js +175 -0
- package/dist/internal/openclaw/openclaw-gateway.client.js.map +1 -0
- package/dist/internal/openclaw/openclaw-skill-sync.client.d.ts +222 -0
- package/dist/internal/openclaw/openclaw-skill-sync.client.d.ts.map +1 -0
- package/dist/internal/openclaw/openclaw-skill-sync.client.js +720 -0
- package/dist/internal/openclaw/openclaw-skill-sync.client.js.map +1 -0
- package/dist/internal/openclaw/openclaw.client.d.ts +602 -0
- package/dist/internal/openclaw/openclaw.client.d.ts.map +1 -0
- package/dist/internal/openclaw/openclaw.client.js +3062 -0
- package/dist/internal/openclaw/openclaw.client.js.map +1 -0
- package/dist/internal/openclaw/openclaw.module.d.ts +3 -0
- package/dist/internal/openclaw/openclaw.module.d.ts.map +1 -0
- package/dist/internal/openclaw/openclaw.module.js +62 -0
- package/dist/internal/openclaw/openclaw.module.js.map +1 -0
- package/dist/internal/openclaw/skill-translation.service.d.ts +39 -0
- package/dist/internal/openclaw/skill-translation.service.d.ts.map +1 -0
- package/dist/internal/openclaw/skill-translation.service.js +217 -0
- package/dist/internal/openclaw/skill-translation.service.js.map +1 -0
- package/dist/internal/openclaw/types/cron.types.d.ts +112 -0
- package/dist/internal/openclaw/types/cron.types.d.ts.map +1 -0
- package/dist/internal/openclaw/types/cron.types.js +9 -0
- package/dist/internal/openclaw/types/cron.types.js.map +1 -0
- package/dist/internal/provider-verify/index.d.ts +6 -0
- package/dist/internal/provider-verify/index.d.ts.map +1 -0
- package/dist/internal/provider-verify/index.js +24 -0
- package/dist/internal/provider-verify/index.js.map +1 -0
- package/dist/internal/provider-verify/provider-verify.client.d.ts +55 -0
- package/dist/internal/provider-verify/provider-verify.client.d.ts.map +1 -0
- package/dist/internal/provider-verify/provider-verify.client.js +284 -0
- package/dist/internal/provider-verify/provider-verify.client.js.map +1 -0
- package/dist/internal/provider-verify/provider-verify.module.d.ts +3 -0
- package/dist/internal/provider-verify/provider-verify.module.d.ts.map +1 -0
- package/dist/internal/provider-verify/provider-verify.module.js +28 -0
- package/dist/internal/provider-verify/provider-verify.module.js.map +1 -0
- package/dist/internal/sso/index.d.ts +5 -0
- package/dist/internal/sso/index.d.ts.map +1 -0
- package/dist/internal/sso/index.js +12 -0
- package/dist/internal/sso/index.js.map +1 -0
- package/dist/internal/sso/sso-auth.client.d.ts +33 -0
- package/dist/internal/sso/sso-auth.client.d.ts.map +1 -0
- package/dist/internal/sso/sso-auth.client.js +89 -0
- package/dist/internal/sso/sso-auth.client.js.map +1 -0
- package/dist/internal/sso/sso-message-proxy.service.d.ts +11 -0
- package/dist/internal/sso/sso-message-proxy.service.d.ts.map +1 -0
- package/dist/internal/sso/sso-message-proxy.service.js +51 -0
- package/dist/internal/sso/sso-message-proxy.service.js.map +1 -0
- package/dist/internal/sso/sso-message.client.d.ts +12 -0
- package/dist/internal/sso/sso-message.client.d.ts.map +1 -0
- package/dist/internal/sso/sso-message.client.js +62 -0
- package/dist/internal/sso/sso-message.client.js.map +1 -0
- package/dist/internal/sso/sso.module.d.ts +3 -0
- package/dist/internal/sso/sso.module.d.ts.map +1 -0
- package/dist/internal/sso/sso.module.js +26 -0
- package/dist/internal/sso/sso.module.js.map +1 -0
- package/package.json +110 -6
|
@@ -0,0 +1,720 @@
|
|
|
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 __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
19
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
20
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
21
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
22
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
23
|
+
};
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
42
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
43
|
+
};
|
|
44
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
45
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
46
|
+
};
|
|
47
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
+
exports.OpenClawSkillSyncClient = void 0;
|
|
49
|
+
/**
|
|
50
|
+
* OpenClaw Skill Sync Client
|
|
51
|
+
*
|
|
52
|
+
* 职责:
|
|
53
|
+
* - 从 GitHub 仓库同步 OpenClaw 技能库
|
|
54
|
+
* - 解析 README.md 提取技能信息
|
|
55
|
+
* - 支持增量同步和全量同步
|
|
56
|
+
* - 支持本地文件缓存(GitHub 不可用时的降级方案)
|
|
57
|
+
*/
|
|
58
|
+
const common_1 = require("@nestjs/common");
|
|
59
|
+
const axios_1 = require("@nestjs/axios");
|
|
60
|
+
const config_1 = require("@nestjs/config");
|
|
61
|
+
const nest_winston_1 = require("nest-winston");
|
|
62
|
+
const winston_1 = require("winston");
|
|
63
|
+
const rxjs_1 = require("rxjs");
|
|
64
|
+
const fs = __importStar(require("fs"));
|
|
65
|
+
const path = __importStar(require("path"));
|
|
66
|
+
const yaml = __importStar(require("js-yaml"));
|
|
67
|
+
const infra_contracts_1 = require("@dofe/infra-contracts");
|
|
68
|
+
/**
|
|
69
|
+
* 分类映射(将 README 中的分类名转换为 slug)
|
|
70
|
+
*/
|
|
71
|
+
const CATEGORY_MAP = {
|
|
72
|
+
'Coding Agents & IDEs': 'coding-agents',
|
|
73
|
+
'Git & GitHub': 'git-github',
|
|
74
|
+
Moltbook: 'moltbook',
|
|
75
|
+
'Web & Frontend Development': 'web-frontend',
|
|
76
|
+
'DevOps & Cloud': 'devops-cloud',
|
|
77
|
+
'Browser & Automation': 'browser-automation',
|
|
78
|
+
'Image & Video Generation': 'image-video-gen',
|
|
79
|
+
'Apple Apps & Services': 'apple-apps',
|
|
80
|
+
'Search & Research': 'search-research',
|
|
81
|
+
'Clawdbot Tools': 'clawdbot-tools',
|
|
82
|
+
'CLI Utilities': 'cli-utilities',
|
|
83
|
+
'Marketing & Sales': 'marketing-sales',
|
|
84
|
+
'Productivity & Tasks': 'productivity-tasks',
|
|
85
|
+
'AI & LLMs': 'ai-llms',
|
|
86
|
+
'Data & Analytics': 'data-analytics',
|
|
87
|
+
Finance: 'finance',
|
|
88
|
+
'Media & Streaming': 'media-streaming',
|
|
89
|
+
'Notes & PKM': 'notes-pkm',
|
|
90
|
+
'iOS & macOS Development': 'ios-macos-dev',
|
|
91
|
+
Transportation: 'transportation',
|
|
92
|
+
'Personal Development': 'personal-dev',
|
|
93
|
+
'Health & Fitness': 'health-fitness',
|
|
94
|
+
Communication: 'communication',
|
|
95
|
+
'Speech & Transcription': 'speech-transcription',
|
|
96
|
+
'Smart Home & IoT': 'smart-home-iot',
|
|
97
|
+
'Shopping & E-commerce': 'shopping-ecommerce',
|
|
98
|
+
'Calendar & Scheduling': 'calendar-scheduling',
|
|
99
|
+
'PDF & Documents': 'pdf-documents',
|
|
100
|
+
'Self-Hosted & Automation': 'self-hosted',
|
|
101
|
+
'Security & Passwords': 'security-passwords',
|
|
102
|
+
Gaming: 'gaming',
|
|
103
|
+
'Agent-to-Agent Protocols': 'agent-protocols',
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* 分类中文名称映射
|
|
107
|
+
*/
|
|
108
|
+
const CATEGORY_ZH_MAP = {
|
|
109
|
+
'coding-agents': '编程代理与IDE',
|
|
110
|
+
'git-github': 'Git与GitHub',
|
|
111
|
+
moltbook: 'Moltbook',
|
|
112
|
+
'web-frontend': 'Web与前端开发',
|
|
113
|
+
'devops-cloud': 'DevOps与云服务',
|
|
114
|
+
'browser-automation': '浏览器与自动化',
|
|
115
|
+
'image-video-gen': '图像与视频生成',
|
|
116
|
+
'apple-apps': 'Apple应用与服务',
|
|
117
|
+
'search-research': '搜索与研究',
|
|
118
|
+
'clawdbot-tools': 'Clawdbot工具',
|
|
119
|
+
'cli-utilities': '命令行工具',
|
|
120
|
+
'marketing-sales': '营销与销售',
|
|
121
|
+
'productivity-tasks': '生产力与任务',
|
|
122
|
+
'ai-llms': 'AI与大语言模型',
|
|
123
|
+
'data-analytics': '数据与分析',
|
|
124
|
+
finance: '金融',
|
|
125
|
+
'media-streaming': '媒体与流媒体',
|
|
126
|
+
'notes-pkm': '笔记与知识管理',
|
|
127
|
+
'ios-macos-dev': 'iOS与macOS开发',
|
|
128
|
+
transportation: '交通出行',
|
|
129
|
+
'personal-dev': '个人发展',
|
|
130
|
+
'health-fitness': '健康与健身',
|
|
131
|
+
communication: '通讯',
|
|
132
|
+
'speech-transcription': '语音与转录',
|
|
133
|
+
'smart-home-iot': '智能家居与物联网',
|
|
134
|
+
'shopping-ecommerce': '购物与电商',
|
|
135
|
+
'calendar-scheduling': '日历与日程',
|
|
136
|
+
'pdf-documents': 'PDF与文档',
|
|
137
|
+
'self-hosted': '自托管与自动化',
|
|
138
|
+
'security-passwords': '安全与密码',
|
|
139
|
+
gaming: '游戏',
|
|
140
|
+
'agent-protocols': '代理间协议',
|
|
141
|
+
};
|
|
142
|
+
/**
|
|
143
|
+
* 分类图标映射
|
|
144
|
+
*/
|
|
145
|
+
const CATEGORY_ICON_MAP = {
|
|
146
|
+
'coding-agents': '💻',
|
|
147
|
+
'git-github': '🔀',
|
|
148
|
+
moltbook: '📓',
|
|
149
|
+
'web-frontend': '🌐',
|
|
150
|
+
'devops-cloud': '☁️',
|
|
151
|
+
'browser-automation': '🤖',
|
|
152
|
+
'image-video-gen': '🎨',
|
|
153
|
+
'apple-apps': '🍎',
|
|
154
|
+
'search-research': '🔍',
|
|
155
|
+
'clawdbot-tools': '🔧',
|
|
156
|
+
'cli-utilities': '⌨️',
|
|
157
|
+
'marketing-sales': '📈',
|
|
158
|
+
'productivity-tasks': '✅',
|
|
159
|
+
'ai-llms': '🧠',
|
|
160
|
+
'data-analytics': '📊',
|
|
161
|
+
finance: '💰',
|
|
162
|
+
'media-streaming': '🎬',
|
|
163
|
+
'notes-pkm': '📝',
|
|
164
|
+
'ios-macos-dev': '📱',
|
|
165
|
+
transportation: '🚗',
|
|
166
|
+
'personal-dev': '🌱',
|
|
167
|
+
'health-fitness': '💪',
|
|
168
|
+
communication: '💬',
|
|
169
|
+
'speech-transcription': '🎤',
|
|
170
|
+
'smart-home-iot': '🏠',
|
|
171
|
+
'shopping-ecommerce': '🛒',
|
|
172
|
+
'calendar-scheduling': '📅',
|
|
173
|
+
'pdf-documents': '📄',
|
|
174
|
+
'self-hosted': '🖥️',
|
|
175
|
+
'security-passwords': '🔐',
|
|
176
|
+
gaming: '🎮',
|
|
177
|
+
'agent-protocols': '🔗',
|
|
178
|
+
};
|
|
179
|
+
let OpenClawSkillSyncClient = class OpenClawSkillSyncClient {
|
|
180
|
+
logger;
|
|
181
|
+
httpService;
|
|
182
|
+
configService;
|
|
183
|
+
repoUrl = 'https://raw.githubusercontent.com/VoltAgent/awesome-openclaw-skills/main/README.md';
|
|
184
|
+
tosSkillsUrl = process.env.SKILLS_URL ||
|
|
185
|
+
'https://pardx.tos-cn-shanghai.volces.com/clawskills/skills.json';
|
|
186
|
+
requestTimeout = 60000; // 60 秒超时
|
|
187
|
+
localCachePath;
|
|
188
|
+
githubToken;
|
|
189
|
+
githubProxyUrl;
|
|
190
|
+
constructor(logger, httpService, configService) {
|
|
191
|
+
this.logger = logger;
|
|
192
|
+
this.httpService = httpService;
|
|
193
|
+
this.configService = configService;
|
|
194
|
+
// 本地缓存文件路径:apps/api/openclaw-skills/README.md
|
|
195
|
+
this.localCachePath = path.resolve(__dirname, '../../../../../openclaw-skills/README.md');
|
|
196
|
+
// GitHub Token 和代理 URL(可选)
|
|
197
|
+
this.githubToken = this.configService.get('GITHUB_TOKEN');
|
|
198
|
+
this.githubProxyUrl = this.configService.get('GITHUB_PROXY_URL');
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* 从 GitHub 获取 README 内容,失败时从本地缓存读取
|
|
202
|
+
*/
|
|
203
|
+
async fetchReadme() {
|
|
204
|
+
this.logger.info('OpenClawSkillSyncClient: 开始获取 README');
|
|
205
|
+
try {
|
|
206
|
+
// 尝试从 GitHub 获取
|
|
207
|
+
const content = await this.fetchFromGitHub();
|
|
208
|
+
// 成功获取后,保存到本地缓存
|
|
209
|
+
await this.saveToLocalCache(content);
|
|
210
|
+
return content;
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
this.logger.warn('OpenClawSkillSyncClient: GitHub 获取失败,尝试从本地缓存读取', {
|
|
214
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
215
|
+
});
|
|
216
|
+
// 从本地缓存读取
|
|
217
|
+
return this.readFromLocalCache();
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* 构建 GitHub 请求的 headers 和 URL
|
|
222
|
+
*/
|
|
223
|
+
getGitHubRequestConfig(url) {
|
|
224
|
+
const headers = {};
|
|
225
|
+
if (this.githubToken) {
|
|
226
|
+
headers['Authorization'] = `token ${this.githubToken}`;
|
|
227
|
+
}
|
|
228
|
+
// 如果配置了代理 URL,替换 GitHub 域名
|
|
229
|
+
let finalUrl = url;
|
|
230
|
+
if (this.githubProxyUrl) {
|
|
231
|
+
finalUrl = url.replace('https://raw.githubusercontent.com', this.githubProxyUrl);
|
|
232
|
+
}
|
|
233
|
+
return { url: finalUrl, headers };
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* 检查 GitHub API Rate Limit 响应头
|
|
237
|
+
* remaining < 10 时告警,429 时抛出友好错误
|
|
238
|
+
*/
|
|
239
|
+
checkRateLimit(headers) {
|
|
240
|
+
const remaining = Number(headers['x-ratelimit-remaining']);
|
|
241
|
+
const reset = Number(headers['x-ratelimit-reset']);
|
|
242
|
+
if (isNaN(remaining))
|
|
243
|
+
return;
|
|
244
|
+
if (remaining === 0) {
|
|
245
|
+
const resetTime = reset
|
|
246
|
+
? new Date(reset * 1000).toLocaleTimeString()
|
|
247
|
+
: 'unknown';
|
|
248
|
+
throw new Error(`GitHub API 请求频率超限,请配置 GITHUB_TOKEN 环境变量以提高限额(60→5000次/小时)。重置时间:${resetTime}`);
|
|
249
|
+
}
|
|
250
|
+
if (remaining < 10) {
|
|
251
|
+
this.logger.warn('OpenClawSkillSyncClient: GitHub API Rate Limit 即将耗尽', {
|
|
252
|
+
remaining,
|
|
253
|
+
resetAt: reset ? new Date(reset * 1000).toISOString() : 'unknown',
|
|
254
|
+
hasToken: !!this.githubToken,
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* 从 AxiosError 中检测 429 状态码并抛出友好错误
|
|
260
|
+
*/
|
|
261
|
+
handleGitHubError(error) {
|
|
262
|
+
const axiosError = error;
|
|
263
|
+
if (axiosError?.response?.status === 429) {
|
|
264
|
+
const reset = Number(axiosError.response.headers?.['x-ratelimit-reset']);
|
|
265
|
+
const resetTime = reset
|
|
266
|
+
? new Date(reset * 1000).toLocaleTimeString()
|
|
267
|
+
: 'unknown';
|
|
268
|
+
throw new Error(`GitHub API 请求频率超限(429),请配置 GITHUB_TOKEN 环境变量以提高限额(60→5000次/小时)。重置时间:${resetTime}`);
|
|
269
|
+
}
|
|
270
|
+
throw error;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* 从 GitHub 获取 README 内容
|
|
274
|
+
*/
|
|
275
|
+
async fetchFromGitHub() {
|
|
276
|
+
const { url, headers } = this.getGitHubRequestConfig(this.repoUrl);
|
|
277
|
+
const response = await (0, rxjs_1.firstValueFrom)(this.httpService.get(url, { headers }).pipe((0, rxjs_1.timeout)(this.requestTimeout), (0, rxjs_1.catchError)((error) => {
|
|
278
|
+
this.logger.error('OpenClawSkillSyncClient: GitHub 请求失败', {
|
|
279
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
280
|
+
});
|
|
281
|
+
throw error;
|
|
282
|
+
})));
|
|
283
|
+
this.logger.info('OpenClawSkillSyncClient: GitHub README 获取成功', {
|
|
284
|
+
contentLength: response.data.length,
|
|
285
|
+
});
|
|
286
|
+
return response.data;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* 保存内容到本地缓存
|
|
290
|
+
*/
|
|
291
|
+
async saveToLocalCache(content) {
|
|
292
|
+
try {
|
|
293
|
+
// 确保目录存在
|
|
294
|
+
const dir = path.dirname(this.localCachePath);
|
|
295
|
+
if (!fs.existsSync(dir)) {
|
|
296
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
297
|
+
}
|
|
298
|
+
fs.writeFileSync(this.localCachePath, content, 'utf-8');
|
|
299
|
+
this.logger.info('OpenClawSkillSyncClient: 已保存到本地缓存', {
|
|
300
|
+
path: this.localCachePath,
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
catch (error) {
|
|
304
|
+
this.logger.warn('OpenClawSkillSyncClient: 保存本地缓存失败', {
|
|
305
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
306
|
+
});
|
|
307
|
+
// 保存失败不影响主流程
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* 从本地缓存读取 README 内容
|
|
312
|
+
*/
|
|
313
|
+
readFromLocalCache() {
|
|
314
|
+
if (!fs.existsSync(this.localCachePath)) {
|
|
315
|
+
this.logger.error('OpenClawSkillSyncClient: 本地缓存文件不存在', {
|
|
316
|
+
path: this.localCachePath,
|
|
317
|
+
});
|
|
318
|
+
throw new Error(`本地缓存文件不存在: ${this.localCachePath}`);
|
|
319
|
+
}
|
|
320
|
+
const content = fs.readFileSync(this.localCachePath, 'utf-8');
|
|
321
|
+
this.logger.info('OpenClawSkillSyncClient: 从本地缓存读取成功', {
|
|
322
|
+
path: this.localCachePath,
|
|
323
|
+
contentLength: content.length,
|
|
324
|
+
});
|
|
325
|
+
return content;
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* 解析 README 内容,提取技能信息
|
|
329
|
+
* 只处理 URL 以 SKILL.md 结尾的技能
|
|
330
|
+
* 自动去重:相同 slug 只保留第一个
|
|
331
|
+
*/
|
|
332
|
+
parseReadme(content) {
|
|
333
|
+
const skillsMap = new Map();
|
|
334
|
+
let currentCategory = '';
|
|
335
|
+
let skippedCount = 0;
|
|
336
|
+
let duplicateCount = 0;
|
|
337
|
+
// 按行解析
|
|
338
|
+
const lines = content.split('\n');
|
|
339
|
+
for (const line of lines) {
|
|
340
|
+
// 检测分类标题(<summary><h3>...)
|
|
341
|
+
const categoryMatch = line.match(/<summary><h3[^>]*>([^<]+)<\/h3><\/summary>/);
|
|
342
|
+
if (categoryMatch) {
|
|
343
|
+
currentCategory = categoryMatch[1].trim();
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
// 检测技能条目(- [slug](url) - description)
|
|
347
|
+
const skillMatch = line.match(/^- \[([^\]]+)\]\(([^)]+)\)\s*-\s*(.+)$/);
|
|
348
|
+
if (skillMatch && currentCategory) {
|
|
349
|
+
const [, slug, sourceUrl, description] = skillMatch;
|
|
350
|
+
// 只处理以 SKILL.md 结尾的 URL
|
|
351
|
+
if (!sourceUrl.trim().endsWith('SKILL.md')) {
|
|
352
|
+
skippedCount++;
|
|
353
|
+
continue;
|
|
354
|
+
}
|
|
355
|
+
const trimmedSlug = slug.trim();
|
|
356
|
+
// 检查是否已存在相同 slug(去重)
|
|
357
|
+
if (skillsMap.has(trimmedSlug)) {
|
|
358
|
+
duplicateCount++;
|
|
359
|
+
this.logger.debug('OpenClawSkillSyncClient: 跳过重复 slug', {
|
|
360
|
+
slug: trimmedSlug,
|
|
361
|
+
sourceUrl: sourceUrl.trim(),
|
|
362
|
+
});
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
// 从 URL 提取作者
|
|
366
|
+
// URL 格式: https://github.com/openclaw/skills/tree/main/skills/{author}/{slug}/SKILL.md
|
|
367
|
+
const authorMatch = sourceUrl.match(/\/skills\/([^/]+)\/[^/]+\/SKILL\.md/);
|
|
368
|
+
const author = authorMatch ? authorMatch[1] : 'unknown';
|
|
369
|
+
skillsMap.set(trimmedSlug, {
|
|
370
|
+
slug: trimmedSlug,
|
|
371
|
+
name: this.slugToName(trimmedSlug),
|
|
372
|
+
description: description.trim(),
|
|
373
|
+
category: CATEGORY_MAP[currentCategory] || this.slugify(currentCategory),
|
|
374
|
+
author,
|
|
375
|
+
sourceUrl: sourceUrl.trim(),
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
const skills = Array.from(skillsMap.values());
|
|
380
|
+
this.logger.info('OpenClawSkillSyncClient: README 解析完成', {
|
|
381
|
+
totalSkills: skills.length,
|
|
382
|
+
skippedSkills: skippedCount,
|
|
383
|
+
duplicateSkills: duplicateCount,
|
|
384
|
+
categories: [...new Set(skills.map((s) => s.category))].length,
|
|
385
|
+
});
|
|
386
|
+
return skills;
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* 将 slug 转换为可读名称
|
|
390
|
+
*/
|
|
391
|
+
slugToName(slug) {
|
|
392
|
+
return slug
|
|
393
|
+
.split('-')
|
|
394
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
395
|
+
.join(' ');
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* 将字符串转换为 slug
|
|
399
|
+
*/
|
|
400
|
+
slugify(text) {
|
|
401
|
+
return text
|
|
402
|
+
.toLowerCase()
|
|
403
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
404
|
+
.replace(/^-|-$/g, '');
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* 获取所有分类(包含中英文名称和图标)
|
|
408
|
+
*/
|
|
409
|
+
getCategories() {
|
|
410
|
+
return Object.entries(CATEGORY_MAP).map(([name, slug], index) => ({
|
|
411
|
+
slug,
|
|
412
|
+
name,
|
|
413
|
+
nameZh: CATEGORY_ZH_MAP[slug] || name,
|
|
414
|
+
icon: CATEGORY_ICON_MAP[slug] || '📦',
|
|
415
|
+
sortOrder: index + 1,
|
|
416
|
+
}));
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* 获取单个技能的 SKILL.md 内容
|
|
420
|
+
* 优先使用 raw URL,失败时 fallback 到 GitHub API(适用于 raw.githubusercontent.com 被屏蔽的环境)
|
|
421
|
+
* @param sourceUrl GitHub tree URL (如 https://github.com/openclaw/skills/tree/main/skills/author/slug/SKILL.md)
|
|
422
|
+
*/
|
|
423
|
+
async fetchSkillDefinition(sourceUrl) {
|
|
424
|
+
this.logger.info('OpenClawSkillSyncClient: 获取 SKILL.md 内容', {
|
|
425
|
+
sourceUrl,
|
|
426
|
+
});
|
|
427
|
+
// 优先尝试 raw URL
|
|
428
|
+
const rawUrl = this.convertToRawUrl(sourceUrl);
|
|
429
|
+
const { url, headers } = this.getGitHubRequestConfig(rawUrl);
|
|
430
|
+
try {
|
|
431
|
+
const response = await (0, rxjs_1.firstValueFrom)(this.httpService.get(url, { headers }).pipe((0, rxjs_1.timeout)(this.requestTimeout), (0, rxjs_1.catchError)((error) => {
|
|
432
|
+
throw error;
|
|
433
|
+
})));
|
|
434
|
+
const content = response.data;
|
|
435
|
+
this.logger.info('OpenClawSkillSyncClient: SKILL.md 获取成功 (raw)', {
|
|
436
|
+
contentLength: content.length,
|
|
437
|
+
});
|
|
438
|
+
return this.parseSkillMd(content, sourceUrl);
|
|
439
|
+
}
|
|
440
|
+
catch (rawError) {
|
|
441
|
+
this.logger.warn('OpenClawSkillSyncClient: raw URL 获取失败,尝试 GitHub API', {
|
|
442
|
+
rawUrl,
|
|
443
|
+
error: rawError instanceof Error ? rawError.message : 'Unknown error',
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
// Fallback: 使用 GitHub API(api.github.com 通常不被屏蔽)
|
|
447
|
+
const apiUrl = this.convertToApiUrl(sourceUrl);
|
|
448
|
+
if (!apiUrl) {
|
|
449
|
+
throw new Error(`无法将 sourceUrl 转换为 GitHub API URL: ${sourceUrl}`);
|
|
450
|
+
}
|
|
451
|
+
const apiHeaders = {
|
|
452
|
+
Accept: 'application/vnd.github.v3+json',
|
|
453
|
+
};
|
|
454
|
+
if (this.githubToken) {
|
|
455
|
+
apiHeaders['Authorization'] = `token ${this.githubToken}`;
|
|
456
|
+
}
|
|
457
|
+
try {
|
|
458
|
+
const response = await (0, rxjs_1.firstValueFrom)(this.httpService
|
|
459
|
+
.get(apiUrl, {
|
|
460
|
+
headers: apiHeaders,
|
|
461
|
+
})
|
|
462
|
+
.pipe((0, rxjs_1.timeout)(this.requestTimeout), (0, rxjs_1.catchError)((error) => {
|
|
463
|
+
this.handleGitHubError(error);
|
|
464
|
+
})));
|
|
465
|
+
this.checkRateLimit(response.headers);
|
|
466
|
+
const content = Buffer.from(response.data.content, 'base64').toString('utf-8');
|
|
467
|
+
this.logger.info('OpenClawSkillSyncClient: SKILL.md 获取成功 (GitHub API)', { contentLength: content.length });
|
|
468
|
+
return this.parseSkillMd(content, sourceUrl);
|
|
469
|
+
}
|
|
470
|
+
catch (error) {
|
|
471
|
+
this.logger.error('OpenClawSkillSyncClient: 获取 SKILL.md 全部失败', {
|
|
472
|
+
sourceUrl,
|
|
473
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
474
|
+
});
|
|
475
|
+
throw error;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* 将 GitHub tree URL 转换为 raw URL
|
|
480
|
+
*/
|
|
481
|
+
convertToRawUrl(treeUrl) {
|
|
482
|
+
// https://github.com/openclaw/skills/tree/main/skills/author/slug/SKILL.md
|
|
483
|
+
// -> https://raw.githubusercontent.com/openclaw/skills/main/skills/author/slug/SKILL.md
|
|
484
|
+
return treeUrl
|
|
485
|
+
.replace('github.com', 'raw.githubusercontent.com')
|
|
486
|
+
.replace('/tree/', '/');
|
|
487
|
+
}
|
|
488
|
+
/**
|
|
489
|
+
* 将 GitHub tree URL 转换为 GitHub API URL
|
|
490
|
+
* https://github.com/{owner}/{repo}/tree/{ref}/{path}
|
|
491
|
+
* -> https://api.github.com/repos/{owner}/{repo}/contents/{path}?ref={ref}
|
|
492
|
+
*/
|
|
493
|
+
convertToApiUrl(treeUrl) {
|
|
494
|
+
const match = treeUrl.match(/github\.com\/([^/]+)\/([^/]+)\/tree\/([^/]+)\/(.+)/);
|
|
495
|
+
if (!match)
|
|
496
|
+
return null;
|
|
497
|
+
const [, owner, repo, ref, filePath] = match;
|
|
498
|
+
return `https://api.github.com/repos/${owner}/${repo}/contents/${filePath}?ref=${ref}`;
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* 将 SKILL.md 的 GitHub tree URL 转换为其所在目录的 API URL
|
|
502
|
+
*/
|
|
503
|
+
convertToDirApiUrl(sourceUrl) {
|
|
504
|
+
const dirUrl = sourceUrl.replace(/\/SKILL\.md$/, '');
|
|
505
|
+
return this.convertToApiUrl(dirUrl);
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* 解析 SKILL.md 内容
|
|
509
|
+
* 提取 YAML frontmatter 和 Markdown 内容
|
|
510
|
+
*/
|
|
511
|
+
parseSkillMd(content, sourceUrl) {
|
|
512
|
+
// 匹配 YAML frontmatter (--- ... ---)
|
|
513
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
514
|
+
if (!frontmatterMatch) {
|
|
515
|
+
// 没有 frontmatter,整个内容作为 Markdown
|
|
516
|
+
return {
|
|
517
|
+
name: '',
|
|
518
|
+
version: '1.0.0',
|
|
519
|
+
description: '',
|
|
520
|
+
content: content,
|
|
521
|
+
frontmatter: {},
|
|
522
|
+
sourceUrl,
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
const [, yamlContent, markdownContent] = frontmatterMatch;
|
|
526
|
+
// 使用 js-yaml 解析 YAML frontmatter
|
|
527
|
+
let frontmatter = {};
|
|
528
|
+
try {
|
|
529
|
+
const parsed = yaml.load(yamlContent);
|
|
530
|
+
frontmatter =
|
|
531
|
+
parsed && typeof parsed === 'object'
|
|
532
|
+
? parsed
|
|
533
|
+
: {};
|
|
534
|
+
}
|
|
535
|
+
catch (error) {
|
|
536
|
+
this.logger.warn('OpenClawSkillSyncClient: YAML 解析失败,使用空对象', {
|
|
537
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
return {
|
|
541
|
+
name: frontmatter.name || '',
|
|
542
|
+
version: String(frontmatter.version || '1.0.0'),
|
|
543
|
+
description: frontmatter.description || '',
|
|
544
|
+
homepage: frontmatter.homepage,
|
|
545
|
+
repository: frontmatter.repository,
|
|
546
|
+
userInvocable: frontmatter['user-invocable'],
|
|
547
|
+
tags: frontmatter.tags,
|
|
548
|
+
metadata: frontmatter.metadata,
|
|
549
|
+
content: markdownContent.trim(),
|
|
550
|
+
frontmatter,
|
|
551
|
+
sourceUrl,
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* 获取技能的完整目录内容
|
|
556
|
+
* 使用 GitHub Contents API 递归获取目录下所有文件
|
|
557
|
+
*/
|
|
558
|
+
async fetchSkillDirectory(sourceUrl) {
|
|
559
|
+
const apiUrl = this.convertToDirApiUrl(sourceUrl);
|
|
560
|
+
if (!apiUrl) {
|
|
561
|
+
throw new Error(`无法解析目录 URL: ${sourceUrl}`);
|
|
562
|
+
}
|
|
563
|
+
const files = [];
|
|
564
|
+
const state = { totalSize: 0 };
|
|
565
|
+
await this.fetchDirectoryRecursive(apiUrl, '', files, state);
|
|
566
|
+
this.logger.info('OpenClawSkillSyncClient: 目录获取完成', {
|
|
567
|
+
sourceUrl,
|
|
568
|
+
fileCount: files.length,
|
|
569
|
+
totalSize: state.totalSize,
|
|
570
|
+
});
|
|
571
|
+
return files;
|
|
572
|
+
}
|
|
573
|
+
/**
|
|
574
|
+
* 递归获取 GitHub 目录内容
|
|
575
|
+
*/
|
|
576
|
+
async fetchDirectoryRecursive(apiUrl, basePath, files, state) {
|
|
577
|
+
const apiHeaders = {
|
|
578
|
+
Accept: 'application/vnd.github.v3+json',
|
|
579
|
+
};
|
|
580
|
+
if (this.githubToken) {
|
|
581
|
+
apiHeaders['Authorization'] = `token ${this.githubToken}`;
|
|
582
|
+
}
|
|
583
|
+
const response = await (0, rxjs_1.firstValueFrom)(this.httpService
|
|
584
|
+
.get(apiUrl, { headers: apiHeaders })
|
|
585
|
+
.pipe((0, rxjs_1.timeout)(this.requestTimeout), (0, rxjs_1.catchError)((e) => {
|
|
586
|
+
this.handleGitHubError(e);
|
|
587
|
+
})));
|
|
588
|
+
this.checkRateLimit(response.headers);
|
|
589
|
+
for (const item of response.data) {
|
|
590
|
+
if (infra_contracts_1.SKILL_LIMITS.EXCLUDED_FILES.includes(item.name))
|
|
591
|
+
continue;
|
|
592
|
+
if (item.type === 'file') {
|
|
593
|
+
if (files.length >= infra_contracts_1.SKILL_LIMITS.MAX_FILE_COUNT) {
|
|
594
|
+
throw new Error(`文件数量超过限制: ${infra_contracts_1.SKILL_LIMITS.MAX_FILE_COUNT}`);
|
|
595
|
+
}
|
|
596
|
+
state.totalSize += item.size;
|
|
597
|
+
if (state.totalSize > infra_contracts_1.SKILL_LIMITS.MAX_DIR_SIZE) {
|
|
598
|
+
throw new Error(`目录总大小超过限制: ${infra_contracts_1.SKILL_LIMITS.MAX_DIR_SIZE} bytes`);
|
|
599
|
+
}
|
|
600
|
+
const relativePath = basePath ? `${basePath}/${item.name}` : item.name;
|
|
601
|
+
let content;
|
|
602
|
+
if (item.content && item.encoding === 'base64') {
|
|
603
|
+
content = Buffer.from(item.content, 'base64').toString('utf-8');
|
|
604
|
+
}
|
|
605
|
+
else if (item.download_url) {
|
|
606
|
+
const { url, headers } = this.getGitHubRequestConfig(item.download_url);
|
|
607
|
+
const fileResponse = await (0, rxjs_1.firstValueFrom)(this.httpService
|
|
608
|
+
.get(url, { headers, responseType: 'text' })
|
|
609
|
+
.pipe((0, rxjs_1.timeout)(15000), (0, rxjs_1.catchError)((e) => {
|
|
610
|
+
throw e;
|
|
611
|
+
})));
|
|
612
|
+
content =
|
|
613
|
+
typeof fileResponse.data === 'string'
|
|
614
|
+
? fileResponse.data
|
|
615
|
+
: JSON.stringify(fileResponse.data);
|
|
616
|
+
}
|
|
617
|
+
else {
|
|
618
|
+
continue;
|
|
619
|
+
}
|
|
620
|
+
files.push({ relativePath, content, size: item.size });
|
|
621
|
+
}
|
|
622
|
+
else if (item.type === 'dir') {
|
|
623
|
+
const subDirUrl = apiUrl.replace(/\/contents\/[^?]+/, `/contents/${item.path}`);
|
|
624
|
+
await this.fetchDirectoryRecursive(subDirUrl, basePath ? `${basePath}/${item.name}` : item.name, files, state);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
/**
|
|
629
|
+
* 获取 Skill 的 _meta.json 版本信息
|
|
630
|
+
* @param sourceUrl GitHub tree URL for SKILL.md
|
|
631
|
+
* @returns SkillMetaInfo or null if not found
|
|
632
|
+
*/
|
|
633
|
+
async fetchSkillMeta(sourceUrl) {
|
|
634
|
+
const metaUrl = sourceUrl.replace(/SKILL\.md$/, '_meta.json');
|
|
635
|
+
// 尝试 raw URL
|
|
636
|
+
const rawUrl = this.convertToRawUrl(metaUrl);
|
|
637
|
+
const { url, headers } = this.getGitHubRequestConfig(rawUrl);
|
|
638
|
+
try {
|
|
639
|
+
const response = await (0, rxjs_1.firstValueFrom)(this.httpService.get(url, { headers }).pipe((0, rxjs_1.timeout)(15000), (0, rxjs_1.catchError)((e) => {
|
|
640
|
+
throw e;
|
|
641
|
+
})));
|
|
642
|
+
const data = typeof response.data === 'string'
|
|
643
|
+
? JSON.parse(response.data)
|
|
644
|
+
: response.data;
|
|
645
|
+
return data;
|
|
646
|
+
}
|
|
647
|
+
catch {
|
|
648
|
+
// Fallback: GitHub API
|
|
649
|
+
}
|
|
650
|
+
const apiUrl = this.convertToApiUrl(metaUrl);
|
|
651
|
+
if (!apiUrl)
|
|
652
|
+
return null;
|
|
653
|
+
const apiHeaders = {
|
|
654
|
+
Accept: 'application/vnd.github.v3+json',
|
|
655
|
+
};
|
|
656
|
+
if (this.githubToken) {
|
|
657
|
+
apiHeaders['Authorization'] = `token ${this.githubToken}`;
|
|
658
|
+
}
|
|
659
|
+
try {
|
|
660
|
+
const response = await (0, rxjs_1.firstValueFrom)(this.httpService
|
|
661
|
+
.get(apiUrl, {
|
|
662
|
+
headers: apiHeaders,
|
|
663
|
+
})
|
|
664
|
+
.pipe((0, rxjs_1.timeout)(15000), (0, rxjs_1.catchError)((e) => {
|
|
665
|
+
this.handleGitHubError(e);
|
|
666
|
+
})));
|
|
667
|
+
this.checkRateLimit(response.headers);
|
|
668
|
+
const content = Buffer.from(response.data.content, 'base64').toString('utf-8');
|
|
669
|
+
return JSON.parse(content);
|
|
670
|
+
}
|
|
671
|
+
catch {
|
|
672
|
+
return null;
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
/**
|
|
676
|
+
* 从 TOS 获取技能列表
|
|
677
|
+
* 数据源: https://pardx.tos-cn-shanghai.volces.com/clawskills/skills.json
|
|
678
|
+
*/
|
|
679
|
+
async fetchTosSkills() {
|
|
680
|
+
this.logger.info('OpenClawSkillSyncClient: 开始从 TOS 获取技能列表');
|
|
681
|
+
try {
|
|
682
|
+
const response = await (0, rxjs_1.firstValueFrom)(this.httpService.get(this.tosSkillsUrl).pipe((0, rxjs_1.timeout)(this.requestTimeout), (0, rxjs_1.catchError)((error) => {
|
|
683
|
+
this.logger.error('OpenClawSkillSyncClient: TOS 请求失败', {
|
|
684
|
+
url: this.tosSkillsUrl,
|
|
685
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
686
|
+
});
|
|
687
|
+
throw error;
|
|
688
|
+
})));
|
|
689
|
+
// 防御性检查:确保 response.data.skills 存在
|
|
690
|
+
if (!response.data || !Array.isArray(response.data.skills)) {
|
|
691
|
+
this.logger.error('OpenClawSkillSyncClient: TOS 响应格式无效', {
|
|
692
|
+
receivedData: response.data,
|
|
693
|
+
expectedStructure: '{ skills: [], updatedAt, total, baseUrl }',
|
|
694
|
+
});
|
|
695
|
+
throw new Error('Invalid TOS response structure: skills array is missing');
|
|
696
|
+
}
|
|
697
|
+
this.logger.info('OpenClawSkillSyncClient: TOS 技能列表获取成功', {
|
|
698
|
+
total: response.data.total,
|
|
699
|
+
skillsCount: response.data.skills.length,
|
|
700
|
+
updatedAt: response.data.updatedAt,
|
|
701
|
+
});
|
|
702
|
+
return response.data;
|
|
703
|
+
}
|
|
704
|
+
catch (error) {
|
|
705
|
+
this.logger.error('OpenClawSkillSyncClient: 从 TOS 获取技能列表失败', {
|
|
706
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
707
|
+
});
|
|
708
|
+
throw error;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
};
|
|
712
|
+
exports.OpenClawSkillSyncClient = OpenClawSkillSyncClient;
|
|
713
|
+
exports.OpenClawSkillSyncClient = OpenClawSkillSyncClient = __decorate([
|
|
714
|
+
(0, common_1.Injectable)(),
|
|
715
|
+
__param(0, (0, common_1.Inject)(nest_winston_1.WINSTON_MODULE_PROVIDER)),
|
|
716
|
+
__metadata("design:paramtypes", [winston_1.Logger,
|
|
717
|
+
axios_1.HttpService,
|
|
718
|
+
config_1.ConfigService])
|
|
719
|
+
], OpenClawSkillSyncClient);
|
|
720
|
+
//# sourceMappingURL=openclaw-skill-sync.client.js.map
|