skill-market-cli 1.0.0 → 1.1.0
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/examples/test-skill.yaml +78 -0
- package/package.json +5 -5
- package/src/api/client.js +6 -2
- package/src/auth/oauth.js +161 -113
- package/src/auth/token-store.js +1 -1
- package/src/commands/login.js +4 -5
- package/src/commands/logout.js +5 -5
- package/src/commands/run-example.js +1 -56
- package/src/commands/update.js +36 -7
- package/src/commands/upload.js +229 -105
- package/src/config/server-modes.js +43 -0
- package/src/index.js +42 -13
- package/src/lib/run-example-collect.js +44 -0
- package/src/lib/skill-upload-helpers.js +77 -0
- package/src/skills/skill-market-upload/SKILL.md +53 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: code-reviewer
|
|
3
|
+
purpose: Review code changes and provide constructive feedback on quality, style, and potential issues
|
|
4
|
+
rootUrl: https://raw.githubusercontent.com/LSTM-Kirigaya/jinhui-skills/main/skills/code-reviewer/SKILL.md
|
|
5
|
+
tags: ["code-review", "development", "quality"]
|
|
6
|
+
model: claude-3-5-sonnet
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Code Reviewer Skill
|
|
10
|
+
|
|
11
|
+
This skill helps review code changes and provide detailed feedback.
|
|
12
|
+
|
|
13
|
+
## Usage Examples
|
|
14
|
+
|
|
15
|
+
### Review a pull request
|
|
16
|
+
|
|
17
|
+
Please review this pull request for code quality and potential issues:
|
|
18
|
+
|
|
19
|
+
```diff
|
|
20
|
+
+ function calculateTotal(items) {
|
|
21
|
+
+ let total = 0;
|
|
22
|
+
+ for (let i = 0; i < items.length; i++) {
|
|
23
|
+
+ total += items[i].price;
|
|
24
|
+
+ }
|
|
25
|
+
+ return total;
|
|
26
|
+
+ }
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Check for security issues
|
|
30
|
+
|
|
31
|
+
Review this code for potential security vulnerabilities:
|
|
32
|
+
|
|
33
|
+
```javascript
|
|
34
|
+
app.post('/login', (req, res) => {
|
|
35
|
+
const { username, password } = req.body;
|
|
36
|
+
const query = `SELECT * FROM users WHERE username = '${username}'`;
|
|
37
|
+
db.query(query, (err, results) => {
|
|
38
|
+
// ...
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## AI Execution Output Format
|
|
44
|
+
|
|
45
|
+
When reviewing code, provide output in this structured format:
|
|
46
|
+
|
|
47
|
+
1. **Summary** - Brief overview of the code changes
|
|
48
|
+
2. **Quality Assessment** - Code quality rating (1-5 stars)
|
|
49
|
+
3. **Issues Found** - List of specific issues with:
|
|
50
|
+
- Severity (Critical/High/Medium/Low)
|
|
51
|
+
- Location (line numbers)
|
|
52
|
+
- Description
|
|
53
|
+
- Suggested fix
|
|
54
|
+
4. **Positive Aspects** - What was done well
|
|
55
|
+
5. **Recommendations** - Actionable improvement suggestions
|
|
56
|
+
|
|
57
|
+
## Required Fields
|
|
58
|
+
|
|
59
|
+
Based on the skill format, the following fields are required when uploading:
|
|
60
|
+
|
|
61
|
+
| Field | Type | Description |
|
|
62
|
+
|-------|------|-------------|
|
|
63
|
+
| name | string | Unique skill identifier (e.g., "code-reviewer") |
|
|
64
|
+
| purpose | string | What this skill does |
|
|
65
|
+
| rootUrl | string | GitHub raw URL to the SKILL.md file |
|
|
66
|
+
| tags | array | Categories for the skill |
|
|
67
|
+
| usageExamples | array | Example prompts with AI responses |
|
|
68
|
+
| model | string | Recommended AI model (optional) |
|
|
69
|
+
|
|
70
|
+
## Usage Example Structure
|
|
71
|
+
|
|
72
|
+
Each usage example should include:
|
|
73
|
+
- `prompt`: The user's input/query
|
|
74
|
+
- `aiResponses`: Array of AI response items with:
|
|
75
|
+
- `type`: "thinking", "toolcall", or "message"
|
|
76
|
+
- `content`: The response content
|
|
77
|
+
- `toolName`: For toolcall type, the tool name
|
|
78
|
+
- `toolInput`: For toolcall type, the input parameters
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skill-market-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "CLI tool for managing skills on Skill Market",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"skill-market-cli": "
|
|
8
|
-
"smcli": "
|
|
7
|
+
"skill-market-cli": "bin/skill-market-cli.js",
|
|
8
|
+
"smcli": "bin/skill-market-cli.js"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
@@ -35,10 +35,10 @@
|
|
|
35
35
|
},
|
|
36
36
|
"repository": {
|
|
37
37
|
"type": "git",
|
|
38
|
-
"url": "https://github.com/
|
|
38
|
+
"url": "git+https://github.com/LSTM-Kirigaya/skill-market-cli.git"
|
|
39
39
|
},
|
|
40
40
|
"homepage": "https://kirigaya.cn/ktools/skillmanager",
|
|
41
41
|
"bugs": {
|
|
42
|
-
"url": "https://github.com/
|
|
42
|
+
"url": "https://github.com/LSTM-Kirigaya/skill-market-cli/issues"
|
|
43
43
|
}
|
|
44
44
|
}
|
package/src/api/client.js
CHANGED
|
@@ -9,6 +9,10 @@ class ApiClient {
|
|
|
9
9
|
this.init();
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
reinit() {
|
|
13
|
+
this.init();
|
|
14
|
+
}
|
|
15
|
+
|
|
12
16
|
init() {
|
|
13
17
|
const serverConfig = getServerConfig();
|
|
14
18
|
this.client = axios.create({
|
|
@@ -101,12 +105,12 @@ class ApiClient {
|
|
|
101
105
|
}
|
|
102
106
|
|
|
103
107
|
async uploadSkill(data) {
|
|
104
|
-
const response = await this.client.post('/skill/upload', data);
|
|
108
|
+
const response = await this.client.post('/skill/ai/upload', data);
|
|
105
109
|
return response.data;
|
|
106
110
|
}
|
|
107
111
|
|
|
108
112
|
async updateSkill(id, data) {
|
|
109
|
-
const response = await this.client.post(`/skill/update/${id}`, data);
|
|
113
|
+
const response = await this.client.post(`/skill/ai/update/${id}`, data);
|
|
110
114
|
return response.data;
|
|
111
115
|
}
|
|
112
116
|
|
package/src/auth/oauth.js
CHANGED
|
@@ -1,155 +1,196 @@
|
|
|
1
1
|
const http = require('http');
|
|
2
|
-
const url = require('url');
|
|
3
2
|
const chalk = require('chalk');
|
|
4
3
|
const open = require('open');
|
|
5
4
|
const axios = require('axios');
|
|
6
|
-
const { saveToken, getServerConfig
|
|
5
|
+
const { saveToken, getServerConfig } = require('./token-store');
|
|
7
6
|
|
|
8
|
-
const DEFAULT_SERVER_URL = 'https://kirigaya.cn';
|
|
9
7
|
const CLIENT_ID = 'skill-market-cli';
|
|
10
8
|
|
|
11
|
-
|
|
9
|
+
function getApiRoot() {
|
|
10
|
+
const serverConfig = getServerConfig();
|
|
11
|
+
return serverConfig.apiBase || `${serverConfig.baseURL}/api`;
|
|
12
|
+
}
|
|
13
|
+
|
|
12
14
|
async function getOAuthConfig() {
|
|
15
|
+
const serverConfig = getServerConfig();
|
|
16
|
+
const apiRoot = getApiRoot();
|
|
17
|
+
|
|
13
18
|
try {
|
|
14
|
-
const
|
|
15
|
-
const response = await axios.get(`${serverConfig.baseURL}/oauth/config`);
|
|
19
|
+
const response = await axios.get(`${apiRoot}/oauth/config`);
|
|
16
20
|
if (response.data && response.data.code === 200) {
|
|
17
|
-
|
|
21
|
+
const data = response.data.data || {};
|
|
22
|
+
return {
|
|
23
|
+
...data,
|
|
24
|
+
authorizeURL: `${serverConfig.baseURL}/oauth/authorize`,
|
|
25
|
+
tokenURL: `${apiRoot}/oauth/token`,
|
|
26
|
+
userinfoURL: `${apiRoot}/oauth/userinfo`
|
|
27
|
+
};
|
|
18
28
|
}
|
|
19
29
|
} catch (e) {
|
|
20
|
-
//
|
|
30
|
+
// 使用下方默认配置
|
|
21
31
|
}
|
|
32
|
+
|
|
22
33
|
return {
|
|
23
34
|
clientId: CLIENT_ID,
|
|
24
35
|
redirectUri: 'http://localhost:0/callback',
|
|
25
|
-
authorizeURL: `${
|
|
26
|
-
tokenURL: `${
|
|
36
|
+
authorizeURL: `${serverConfig.baseURL}/oauth/authorize`,
|
|
37
|
+
tokenURL: `${apiRoot}/oauth/token`,
|
|
38
|
+
userinfoURL: `${apiRoot}/oauth/userinfo`,
|
|
27
39
|
scopes: ['skill:read', 'skill:write', 'user:read']
|
|
28
40
|
};
|
|
29
41
|
}
|
|
30
42
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const server = http.createServer((req, res) => {
|
|
35
|
-
const parsedUrl = url.parse(req.url, true);
|
|
36
|
-
const { code, error, error_description } = parsedUrl.query;
|
|
37
|
-
|
|
38
|
-
if (error) {
|
|
39
|
-
res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
40
|
-
res.end(`
|
|
41
|
-
<!DOCTYPE html>
|
|
42
|
-
<html>
|
|
43
|
-
<head><title>授权失败</title></head>
|
|
44
|
-
<body style="text-align:center;padding:50px;font-family:sans-serif;">
|
|
45
|
-
<h1 style="color:#f44336;">❌ 授权失败</h1>
|
|
46
|
-
<p>${error_description || error}</p>
|
|
47
|
-
<p>请关闭此窗口并返回命令行</p>
|
|
48
|
-
</body>
|
|
49
|
-
</html>
|
|
50
|
-
`);
|
|
51
|
-
server.close();
|
|
52
|
-
reject(new Error(error_description || error));
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
43
|
+
function startCallbackServer() {
|
|
44
|
+
let resolveCode;
|
|
45
|
+
let rejectCode;
|
|
55
46
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
|
|
47
|
+
const codePromise = new Promise((resolve, reject) => {
|
|
48
|
+
resolveCode = resolve;
|
|
49
|
+
rejectCode = reject;
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const server = http.createServer((req, res) => {
|
|
53
|
+
let pathname;
|
|
54
|
+
let code;
|
|
55
|
+
let error;
|
|
56
|
+
let error_description;
|
|
57
|
+
try {
|
|
58
|
+
const base = `http://127.0.0.1:${server.address().port}`;
|
|
59
|
+
const u = new URL(req.url || '/', base);
|
|
60
|
+
pathname = u.pathname;
|
|
61
|
+
code = u.searchParams.get('code');
|
|
62
|
+
error = u.searchParams.get('error');
|
|
63
|
+
error_description = u.searchParams.get('error_description');
|
|
64
|
+
} catch {
|
|
65
|
+
res.writeHead(400, { 'Content-Type': 'text/plain; charset=utf-8' });
|
|
66
|
+
res.end('Bad request');
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (pathname === '/favicon.ico') {
|
|
71
|
+
res.writeHead(204);
|
|
72
|
+
res.end();
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (error) {
|
|
77
|
+
res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
78
|
+
res.end(`<!DOCTYPE html><html><head><meta charset="utf-8"><title>授权未通过</title></head>
|
|
79
|
+
<body style="font-family:sans-serif;text-align:center;padding:48px;">
|
|
80
|
+
<h1>授权未通过</h1><p>${error_description || error}</p><p>请关闭此窗口,回到终端继续操作。</p>
|
|
81
|
+
</body></html>`);
|
|
82
|
+
server.close();
|
|
83
|
+
rejectCode(new Error(error_description || error));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
76
86
|
|
|
77
|
-
|
|
87
|
+
if (code) {
|
|
88
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
89
|
+
res.end(`<!DOCTYPE html><html><head><meta charset="utf-8"><title>授权完成</title></head>
|
|
90
|
+
<body style="font-family:sans-serif;text-align:center;padding:48px;">
|
|
91
|
+
<h1>授权完成</h1><p>本窗口可关闭,请回到终端查看登录结果。</p>
|
|
92
|
+
</body></html>`);
|
|
93
|
+
server.close();
|
|
94
|
+
resolveCode(code);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
|
99
|
+
res.end('Not found');
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
return new Promise((resolve, reject) => {
|
|
103
|
+
server.listen(0, () => {
|
|
78
104
|
const actualPort = server.address().port;
|
|
79
|
-
console.log(chalk.gray(
|
|
105
|
+
console.log(chalk.gray(`本地回调服务已启动,端口:${actualPort}`));
|
|
106
|
+
resolve({ port: actualPort, codePromise });
|
|
80
107
|
});
|
|
81
108
|
|
|
82
109
|
server.on('error', reject);
|
|
83
110
|
});
|
|
84
111
|
}
|
|
85
112
|
|
|
86
|
-
|
|
113
|
+
function formatApiError(err) {
|
|
114
|
+
const d = err.response && err.response.data;
|
|
115
|
+
if (!d) return err.message || String(err);
|
|
116
|
+
if (typeof d === 'string') return d;
|
|
117
|
+
if (d.error_description) return d.error_description;
|
|
118
|
+
if (d.error) return d.error;
|
|
119
|
+
try {
|
|
120
|
+
return JSON.stringify(d);
|
|
121
|
+
} catch {
|
|
122
|
+
return err.message;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
87
126
|
async function login(options = {}) {
|
|
88
|
-
|
|
127
|
+
const serverConfig = getServerConfig();
|
|
128
|
+
|
|
129
|
+
console.log(chalk.bold('Skill Market 登录'));
|
|
130
|
+
console.log(chalk.gray(`站点地址:${serverConfig.baseURL}`));
|
|
89
131
|
|
|
90
|
-
// 获取 OAuth 配置
|
|
91
132
|
const oauthConfig = await getOAuthConfig();
|
|
92
|
-
console.log(chalk.gray(`Server: ${oauthConfig.authorizeURL}`));
|
|
93
133
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const callbackPromise = startCallbackServer(callbackPort);
|
|
134
|
+
const { port, codePromise } = await startCallbackServer();
|
|
135
|
+
const redirectUri = `http://localhost:${port}/callback`;
|
|
97
136
|
|
|
98
|
-
// 构造授权 URL
|
|
99
137
|
const state = generateRandomString(16);
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
const authUrl = new URL(oauthConfig.authorizeURL);
|
|
138
|
+
const authorizeURL = oauthConfig.authorizeURL || `${serverConfig.baseURL}/oauth/authorize`;
|
|
139
|
+
const authUrl = new URL(authorizeURL);
|
|
103
140
|
authUrl.searchParams.set('response_type', 'code');
|
|
104
141
|
authUrl.searchParams.set('client_id', oauthConfig.clientId);
|
|
105
142
|
authUrl.searchParams.set('redirect_uri', redirectUri);
|
|
106
|
-
|
|
143
|
+
const scopeVal = oauthConfig.scopes;
|
|
144
|
+
const scopeStr = Array.isArray(scopeVal)
|
|
145
|
+
? scopeVal.join(' ')
|
|
146
|
+
: (scopeVal || 'skill:read skill:write user:read');
|
|
147
|
+
authUrl.searchParams.set('scope', scopeStr);
|
|
107
148
|
authUrl.searchParams.set('state', state);
|
|
108
149
|
|
|
109
|
-
console.log(
|
|
110
|
-
console.log(
|
|
111
|
-
console.log(chalk.
|
|
112
|
-
console.log();
|
|
150
|
+
console.log('');
|
|
151
|
+
console.log('请在浏览器中完成授权(将自动打开页面;若未打开,请复制下方链接到浏览器):');
|
|
152
|
+
console.log(chalk.cyan(authUrl.toString()));
|
|
153
|
+
console.log('');
|
|
113
154
|
|
|
114
|
-
// 自动打开浏览器
|
|
115
155
|
if (options.open !== false) {
|
|
116
156
|
try {
|
|
117
157
|
await open(authUrl.toString());
|
|
118
|
-
console.log(chalk.gray('
|
|
158
|
+
console.log(chalk.gray('已尝试打开系统默认浏览器。'));
|
|
119
159
|
} catch (e) {
|
|
120
|
-
console.log(chalk.yellow('
|
|
121
|
-
console.log(chalk.yellow('Please open the URL manually.\n'));
|
|
160
|
+
console.log(chalk.yellow('无法自动打开浏览器,请手动访问上方链接。'));
|
|
122
161
|
}
|
|
123
162
|
}
|
|
124
163
|
|
|
125
|
-
// 等待回调
|
|
126
164
|
try {
|
|
127
|
-
const code = await
|
|
128
|
-
console.log(chalk.
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
165
|
+
const code = await codePromise;
|
|
166
|
+
console.log(chalk.gray('已收到授权码,正在换取访问令牌…'));
|
|
167
|
+
|
|
168
|
+
const tokenURL = oauthConfig.tokenURL;
|
|
169
|
+
const tokenResponse = await axios.post(
|
|
170
|
+
tokenURL,
|
|
171
|
+
{
|
|
172
|
+
grant_type: 'authorization_code',
|
|
173
|
+
code: code,
|
|
174
|
+
redirect_uri: redirectUri,
|
|
175
|
+
client_id: oauthConfig.clientId,
|
|
176
|
+
client_secret: 'dummy-secret'
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
headers: { 'Content-Type': 'application/json' }
|
|
180
|
+
}
|
|
181
|
+
);
|
|
140
182
|
|
|
141
183
|
const { access_token, refresh_token, expires_in } = tokenResponse.data;
|
|
142
184
|
|
|
143
|
-
|
|
144
|
-
const userResponse = await axios.get(
|
|
185
|
+
const userinfoURL = oauthConfig.userinfoURL;
|
|
186
|
+
const userResponse = await axios.get(userinfoURL, {
|
|
145
187
|
headers: {
|
|
146
|
-
|
|
188
|
+
Authorization: `Bearer ${access_token}`
|
|
147
189
|
}
|
|
148
190
|
});
|
|
149
191
|
|
|
150
192
|
const user = userResponse.data;
|
|
151
193
|
|
|
152
|
-
// 保存 Token
|
|
153
194
|
const expiresAt = Date.now() + (expires_in * 1000);
|
|
154
195
|
saveToken(access_token, refresh_token, expiresAt, {
|
|
155
196
|
name: user.name,
|
|
@@ -157,38 +198,46 @@ async function login(options = {}) {
|
|
|
157
198
|
picture: user.picture
|
|
158
199
|
});
|
|
159
200
|
|
|
160
|
-
console.log();
|
|
161
|
-
console.log(chalk.green('
|
|
162
|
-
console.log(chalk.
|
|
163
|
-
console.log();
|
|
201
|
+
console.log('');
|
|
202
|
+
console.log(chalk.green('登录成功'));
|
|
203
|
+
console.log(chalk.gray(`当前用户:${user.name || user.sub || '(未返回名称)'}`));
|
|
204
|
+
console.log('');
|
|
164
205
|
|
|
165
206
|
return true;
|
|
166
207
|
} catch (error) {
|
|
167
|
-
console.
|
|
168
|
-
console.
|
|
169
|
-
|
|
170
|
-
|
|
208
|
+
console.log('');
|
|
209
|
+
console.log(chalk.red('登录失败'));
|
|
210
|
+
console.log(chalk.red(formatApiError(error)));
|
|
211
|
+
if (error.response && error.response.data && process.env.DEBUG) {
|
|
212
|
+
console.log(chalk.gray(JSON.stringify(error.response.data, null, 2)));
|
|
171
213
|
}
|
|
214
|
+
console.log('');
|
|
172
215
|
return false;
|
|
173
216
|
}
|
|
174
217
|
}
|
|
175
218
|
|
|
176
|
-
// 刷新 Token
|
|
177
219
|
async function refreshAccessToken() {
|
|
178
220
|
const { refreshToken } = require('./token-store').getToken();
|
|
179
221
|
if (!refreshToken) {
|
|
180
|
-
throw new Error('
|
|
222
|
+
throw new Error('无刷新令牌,请重新登录');
|
|
181
223
|
}
|
|
182
224
|
|
|
183
225
|
const oauthConfig = await getOAuthConfig();
|
|
184
|
-
|
|
226
|
+
const tokenURL = oauthConfig.tokenURL;
|
|
227
|
+
|
|
185
228
|
try {
|
|
186
|
-
const response = await axios.post(
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
229
|
+
const response = await axios.post(
|
|
230
|
+
tokenURL,
|
|
231
|
+
{
|
|
232
|
+
grant_type: 'refresh_token',
|
|
233
|
+
refresh_token: refreshToken,
|
|
234
|
+
client_id: oauthConfig.clientId,
|
|
235
|
+
client_secret: 'dummy-secret'
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
headers: { 'Content-Type': 'application/json' }
|
|
239
|
+
}
|
|
240
|
+
);
|
|
192
241
|
|
|
193
242
|
const { access_token, refresh_token, expires_in } = response.data;
|
|
194
243
|
const expiresAt = Date.now() + (expires_in * 1000);
|
|
@@ -198,11 +247,10 @@ async function refreshAccessToken() {
|
|
|
198
247
|
|
|
199
248
|
return access_token;
|
|
200
249
|
} catch (error) {
|
|
201
|
-
throw new Error('
|
|
250
|
+
throw new Error('刷新令牌失败:' + formatApiError(error));
|
|
202
251
|
}
|
|
203
252
|
}
|
|
204
253
|
|
|
205
|
-
// 生成随机字符串
|
|
206
254
|
function generateRandomString(length) {
|
|
207
255
|
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
208
256
|
let result = '';
|
package/src/auth/token-store.js
CHANGED
package/src/commands/login.js
CHANGED
|
@@ -3,16 +3,15 @@ const { isLoggedIn } = require('../auth/token-store');
|
|
|
3
3
|
const { login: oauthLogin } = require('../auth/oauth');
|
|
4
4
|
|
|
5
5
|
async function login(options) {
|
|
6
|
-
// 检查是否已登录
|
|
7
6
|
if (isLoggedIn()) {
|
|
8
|
-
console.log(chalk.yellow('
|
|
9
|
-
console.log(chalk.gray('
|
|
7
|
+
console.log(chalk.yellow('当前已处于登录状态。'));
|
|
8
|
+
console.log(chalk.gray('如需重新登录,请先执行:skill-market-cli logout'));
|
|
9
|
+
console.log('');
|
|
10
10
|
return;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
// 执行登录
|
|
14
13
|
const success = await oauthLogin(options);
|
|
15
|
-
|
|
14
|
+
|
|
16
15
|
if (!success) {
|
|
17
16
|
process.exit(1);
|
|
18
17
|
}
|
package/src/commands/logout.js
CHANGED
|
@@ -4,21 +4,21 @@ const apiClient = require('../api/client');
|
|
|
4
4
|
|
|
5
5
|
async function logout() {
|
|
6
6
|
if (!isLoggedIn()) {
|
|
7
|
-
console.log(chalk.yellow('
|
|
7
|
+
console.log(chalk.yellow('当前未登录,无需登出。'));
|
|
8
|
+
console.log('');
|
|
8
9
|
return;
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
try {
|
|
12
|
-
// 通知服务器登出(撤销 Token)
|
|
13
13
|
await apiClient.client.post('/oauth/logout');
|
|
14
14
|
} catch (e) {
|
|
15
|
-
//
|
|
15
|
+
// 忽略网络错误,仍清除本地凭证
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
// 清除本地 Token
|
|
19
18
|
clearToken();
|
|
20
19
|
|
|
21
|
-
console.log(chalk.green('
|
|
20
|
+
console.log(chalk.green('已登出本地凭证'));
|
|
21
|
+
console.log('');
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
module.exports = logout;
|
|
@@ -3,6 +3,7 @@ const path = require('path');
|
|
|
3
3
|
const chalk = require('chalk');
|
|
4
4
|
const inquirer = require('inquirer');
|
|
5
5
|
const YAML = require('yaml');
|
|
6
|
+
const { runExampleAndCollect } = require('../lib/run-example-collect');
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Run user examples and collect AI responses
|
|
@@ -213,60 +214,4 @@ async function addExampleInteractively(skillFilePath, skillContent, model) {
|
|
|
213
214
|
}
|
|
214
215
|
}
|
|
215
216
|
|
|
216
|
-
/**
|
|
217
|
-
* 运行示例并收集 AI 响应
|
|
218
|
-
* 这里需要集成 AI API,目前使用模拟数据
|
|
219
|
-
*/
|
|
220
|
-
async function runExampleAndCollect(prompt, model) {
|
|
221
|
-
console.log(chalk.blue('\n🤖 Running example with AI...'));
|
|
222
|
-
console.log(chalk.gray(`Model: ${model}\n`));
|
|
223
|
-
|
|
224
|
-
// 这里应该调用实际的 AI API
|
|
225
|
-
// 目前模拟一个典型的 AI 响应流程
|
|
226
|
-
|
|
227
|
-
console.log(chalk.gray('Simulating AI response collection...'));
|
|
228
|
-
console.log(chalk.gray('In production, this would call the AI API and capture:'));
|
|
229
|
-
console.log(chalk.gray(' 1. Thinking steps'));
|
|
230
|
-
console.log(chalk.gray(' 2. Tool calls'));
|
|
231
|
-
console.log(chalk.gray(' 3. Final message\n'));
|
|
232
|
-
|
|
233
|
-
// 模拟 AI 响应
|
|
234
|
-
const simulatedResponses = [
|
|
235
|
-
{
|
|
236
|
-
type: 'thinking',
|
|
237
|
-
content: `Let me analyze this request: "${prompt}". The user wants me to help them with a specific task. I'll break this down into steps and execute the appropriate tools.`
|
|
238
|
-
},
|
|
239
|
-
{
|
|
240
|
-
type: 'toolcall',
|
|
241
|
-
toolName: 'read_file',
|
|
242
|
-
toolInput: { path: './README.md' }
|
|
243
|
-
},
|
|
244
|
-
{
|
|
245
|
-
type: 'toolcall',
|
|
246
|
-
toolName: 'write_file',
|
|
247
|
-
toolInput: {
|
|
248
|
-
path: './output.txt',
|
|
249
|
-
content: `Processed: ${prompt}`
|
|
250
|
-
}
|
|
251
|
-
},
|
|
252
|
-
{
|
|
253
|
-
type: 'message',
|
|
254
|
-
content: `I've processed your request: "${prompt}". Here's what I did:\n\n1. Analyzed the requirements\n2. Read the relevant files\n3. Generated the output\n\nThe task is now complete!`
|
|
255
|
-
}
|
|
256
|
-
];
|
|
257
|
-
|
|
258
|
-
// 模拟处理时间
|
|
259
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
260
|
-
|
|
261
|
-
console.log(chalk.gray('Collected responses:'));
|
|
262
|
-
simulatedResponses.forEach((resp, i) => {
|
|
263
|
-
const icon = resp.type === 'thinking' ? '💭' :
|
|
264
|
-
resp.type === 'toolcall' ? '🔧' : '💬';
|
|
265
|
-
console.log(chalk.gray(` ${icon} [${resp.type}]`));
|
|
266
|
-
});
|
|
267
|
-
console.log();
|
|
268
|
-
|
|
269
|
-
return simulatedResponses;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
217
|
module.exports = runExample;
|