@app-public/weaverbird-debug 1.0.0 → 1.0.2
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 +18 -4
- package/api.js +2 -2
- package/index.js +76 -31
- package/login.js +10 -2
- package/npm-packages.js +21 -0
- package/package.json +1 -1
- package/token.js +55 -0
- package/update.js +44 -0
- package/user.js +47 -0
package/README.md
CHANGED
|
@@ -34,13 +34,17 @@ npm install -g @app-public/weaverbird-test # 测试环境
|
|
|
34
34
|
## CLI 命令
|
|
35
35
|
|
|
36
36
|
```bash
|
|
37
|
-
weaverbird
|
|
37
|
+
weaverbird --version # 显示 CLI 版本号(顶层)
|
|
38
|
+
weaverbird --help # 显示顶层帮助
|
|
39
|
+
weaverbird cli login # 浏览器 OAuth 登录
|
|
38
40
|
weaverbird-debug cli login # 本地调试
|
|
39
41
|
weaverbird-test cli login # 测试环境
|
|
40
42
|
weaverbird cli logout # 清除当前环境本地 token
|
|
41
43
|
weaverbird cli status # 查询登录状态及 token 信息
|
|
42
|
-
weaverbird cli --
|
|
43
|
-
weaverbird
|
|
44
|
+
weaverbird cli token set --type figma <token> # 设置 Figma token(本地 + 服务端)
|
|
45
|
+
weaverbird update # 更新当前环境 CLI 到最新版本
|
|
46
|
+
weaverbird cli update # 同上(子命令形式)
|
|
47
|
+
weaverbird cli --help # 显示子命令帮助
|
|
44
48
|
```
|
|
45
49
|
|
|
46
50
|
### login 流程
|
|
@@ -61,11 +65,18 @@ weaverbird cli --help # 显示命令帮助
|
|
|
61
65
|
"env": "debug",
|
|
62
66
|
"ci_base_url": "http://10.25.72.141:16644",
|
|
63
67
|
"token_path": "C:\\Users\\xxx\\.weaverbird\\token.debug.json",
|
|
68
|
+
"user_id": "abc123",
|
|
69
|
+
"user_name": "张三",
|
|
64
70
|
"token": {
|
|
65
71
|
"access_token": "eyJxxx",
|
|
66
72
|
"token_type": "Bearer",
|
|
67
73
|
"expires_in": 7200,
|
|
68
|
-
"refresh_token": "xxx"
|
|
74
|
+
"refresh_token": "xxx",
|
|
75
|
+
"user": {
|
|
76
|
+
"id": "abc123",
|
|
77
|
+
"name": "zhangsan",
|
|
78
|
+
"realname": "张三"
|
|
79
|
+
}
|
|
69
80
|
}
|
|
70
81
|
}
|
|
71
82
|
```
|
|
@@ -78,6 +89,8 @@ weaverbird cli --help # 显示命令帮助
|
|
|
78
89
|
"env": "debug",
|
|
79
90
|
"ci_base_url": "http://10.25.72.141:16644",
|
|
80
91
|
"token_path": "C:\\Users\\xxx\\.weaverbird\\token.debug.json",
|
|
92
|
+
"user_id": null,
|
|
93
|
+
"user_name": null,
|
|
81
94
|
"token": null
|
|
82
95
|
}
|
|
83
96
|
```
|
|
@@ -258,6 +271,7 @@ CLI 依赖服务端 OAuth 接口(均为 POST JSON,响应格式与项目统
|
|
|
258
271
|
| `/v1/api/ci/oauth/getAuthorizeSession` | Web 查询授权会话状态(pending/expired/invalid) |
|
|
259
272
|
| `/v1/api/ci/oauth/authorize` | 签发 authorization code(需 session_token + header `token`) |
|
|
260
273
|
| `/v1/api/ci/oauth/token` | 换取/刷新 access_token |
|
|
274
|
+
| `/v1/api/ci/open/setToken` | CLI 设置第三方 token(如 figma),需 `cli_token` header |
|
|
261
275
|
|
|
262
276
|
授权页由前端 `{WEAVERBIRD_URL}/weaverbird/oauth/authorize?session=...` 实现;会话 **5 分钟过期、一次性使用**,过期或已用后 Web 会拦截并提示重新执行 `weaverbird cli login`。
|
|
263
277
|
|
package/api.js
CHANGED
|
@@ -20,7 +20,7 @@ async function refreshAccessToken(refreshToken) {
|
|
|
20
20
|
if (result.code !== 200 || !result.data?.access_token) {
|
|
21
21
|
throw new Error(result.msg || '刷新 token 失败');
|
|
22
22
|
}
|
|
23
|
-
saveToken(result.data);
|
|
23
|
+
saveToken({ ...getToken(), ...result.data });
|
|
24
24
|
return result.data;
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -38,9 +38,9 @@ async function request(path, options = {}) {
|
|
|
38
38
|
const response = await fetch(url, {
|
|
39
39
|
...options,
|
|
40
40
|
headers: {
|
|
41
|
-
cli_token: accessToken,
|
|
42
41
|
'Content-Type': 'application/json',
|
|
43
42
|
...(options.headers || {}),
|
|
43
|
+
cli_token: accessToken,
|
|
44
44
|
},
|
|
45
45
|
});
|
|
46
46
|
const data = await response.json().catch(() => ({}));
|
package/index.js
CHANGED
|
@@ -4,64 +4,109 @@ const yargs = require('yargs/yargs');
|
|
|
4
4
|
const { hideBin } = require('yargs/helpers');
|
|
5
5
|
const { startLogin } = require('./login');
|
|
6
6
|
const { getToken, clearToken, getTokenPath } = require('./tokenStore');
|
|
7
|
+
const { getDisplayName } = require('./user');
|
|
7
8
|
const { CI_BASE_URL, ENV } = require('./config');
|
|
8
9
|
const { resolveScriptName } = require('./cli-env');
|
|
10
|
+
const { runUpdate } = require('./update');
|
|
11
|
+
const { setServiceToken } = require('./token');
|
|
9
12
|
const pkg = require('./package.json');
|
|
10
13
|
|
|
11
14
|
const scriptName = resolveScriptName();
|
|
12
15
|
|
|
13
16
|
function printStatus() {
|
|
14
17
|
const token = getToken();
|
|
18
|
+
const user = token?.user;
|
|
15
19
|
const result = {
|
|
16
20
|
logged_in: !!(token && token.access_token),
|
|
17
21
|
env: ENV,
|
|
18
22
|
ci_base_url: CI_BASE_URL,
|
|
19
23
|
token_path: getTokenPath(),
|
|
24
|
+
user_id: user?.id || null,
|
|
25
|
+
user_name: getDisplayName(user),
|
|
20
26
|
token: token || null,
|
|
21
27
|
};
|
|
22
28
|
console.log(JSON.stringify(result, null, 2));
|
|
23
29
|
}
|
|
24
30
|
|
|
31
|
+
function registerTokenCommands(yargs) {
|
|
32
|
+
return yargs
|
|
33
|
+
.command(
|
|
34
|
+
'set <token>',
|
|
35
|
+
'设置第三方 token 并同步到服务端',
|
|
36
|
+
(yargs) =>
|
|
37
|
+
yargs.option('type', {
|
|
38
|
+
alias: 't',
|
|
39
|
+
type: 'string',
|
|
40
|
+
choices: ['figma'],
|
|
41
|
+
demandOption: true,
|
|
42
|
+
description: 'token 类型,当前支持 figma',
|
|
43
|
+
}),
|
|
44
|
+
async (argv) => {
|
|
45
|
+
await setServiceToken(argv.type, argv.token);
|
|
46
|
+
}
|
|
47
|
+
)
|
|
48
|
+
.demandCommand(1, '请使用 token set --type <type> <token>')
|
|
49
|
+
.help();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function registerCliSubcommands(yargs) {
|
|
53
|
+
return yargs
|
|
54
|
+
.command(
|
|
55
|
+
'login',
|
|
56
|
+
`浏览器 OAuth 登录,token 保存至 ~/.weaverbird/token.${ENV}.json`,
|
|
57
|
+
() => {},
|
|
58
|
+
async () => {
|
|
59
|
+
await startLogin();
|
|
60
|
+
}
|
|
61
|
+
)
|
|
62
|
+
.command(
|
|
63
|
+
'logout',
|
|
64
|
+
'清除本地 token',
|
|
65
|
+
() => {},
|
|
66
|
+
() => {
|
|
67
|
+
clearToken();
|
|
68
|
+
console.log('✅ 已退出登录');
|
|
69
|
+
}
|
|
70
|
+
)
|
|
71
|
+
.command(
|
|
72
|
+
'status',
|
|
73
|
+
'查询登录状态及 token 信息',
|
|
74
|
+
() => {},
|
|
75
|
+
() => {
|
|
76
|
+
printStatus();
|
|
77
|
+
}
|
|
78
|
+
)
|
|
79
|
+
.command(
|
|
80
|
+
'update',
|
|
81
|
+
'从 npm 更新当前环境 CLI 到最新版本',
|
|
82
|
+
() => {},
|
|
83
|
+
() => {
|
|
84
|
+
runUpdate();
|
|
85
|
+
}
|
|
86
|
+
)
|
|
87
|
+
.command('token', '管理第三方 token', (yargs) => registerTokenCommands(yargs));
|
|
88
|
+
}
|
|
89
|
+
|
|
25
90
|
async function main() {
|
|
26
91
|
await yargs(hideBin(process.argv))
|
|
27
92
|
.scriptName(scriptName)
|
|
28
93
|
.usage('$0 <command> [options]')
|
|
29
|
-
.version(
|
|
94
|
+
.version(pkg.version)
|
|
95
|
+
.command(
|
|
96
|
+
'update',
|
|
97
|
+
'从 npm 更新当前环境 CLI 到最新版本',
|
|
98
|
+
() => {},
|
|
99
|
+
() => {
|
|
100
|
+
runUpdate();
|
|
101
|
+
}
|
|
102
|
+
)
|
|
30
103
|
.command(
|
|
31
104
|
'cli',
|
|
32
105
|
'Weaverbird CLI 子命令',
|
|
33
106
|
(yargs) =>
|
|
34
|
-
yargs
|
|
35
|
-
.version(pkg.version)
|
|
36
|
-
.command(
|
|
37
|
-
'login',
|
|
38
|
-
`浏览器 OAuth 登录,token 保存至 ~/.weaverbird/token.${ENV}.json`,
|
|
39
|
-
() => {},
|
|
40
|
-
async () => {
|
|
41
|
-
await startLogin();
|
|
42
|
-
}
|
|
43
|
-
)
|
|
44
|
-
.command(
|
|
45
|
-
'logout',
|
|
46
|
-
'清除本地 token',
|
|
47
|
-
() => {},
|
|
48
|
-
() => {
|
|
49
|
-
clearToken();
|
|
50
|
-
console.log('✅ 已退出登录');
|
|
51
|
-
}
|
|
52
|
-
)
|
|
53
|
-
.command(
|
|
54
|
-
'status',
|
|
55
|
-
'查询登录状态及 token 信息',
|
|
56
|
-
() => {},
|
|
57
|
-
() => {
|
|
58
|
-
printStatus();
|
|
59
|
-
}
|
|
60
|
-
)
|
|
61
|
-
.demandCommand(0)
|
|
62
|
-
.help()
|
|
107
|
+
registerCliSubcommands(yargs.version(pkg.version)).demandCommand(0).help()
|
|
63
108
|
)
|
|
64
|
-
.demandCommand(1, `请使用 ${scriptName}
|
|
109
|
+
.demandCommand(1, `请使用 ${scriptName} <command>,执行 ${scriptName} --help 查看帮助`)
|
|
65
110
|
.help()
|
|
66
111
|
.parseAsync();
|
|
67
112
|
}
|
package/login.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
const express = require('express');
|
|
2
2
|
const open = require('open');
|
|
3
3
|
const fetch = require('cross-fetch');
|
|
4
|
-
const {saveToken} = require('./tokenStore');
|
|
4
|
+
const { saveToken } = require('./tokenStore');
|
|
5
|
+
const { fetchAndSaveCurrentUser, getDisplayName } = require('./user');
|
|
5
6
|
const {
|
|
6
7
|
CLIENT_ID,
|
|
7
8
|
CLIENT_SECRET,
|
|
@@ -219,13 +220,20 @@ async function startLogin() {
|
|
|
219
220
|
try {
|
|
220
221
|
const token = await exchangeCodeForToken(code);
|
|
221
222
|
saveToken(token);
|
|
223
|
+
let userName = null;
|
|
224
|
+
try {
|
|
225
|
+
const user = await fetchAndSaveCurrentUser();
|
|
226
|
+
userName = getDisplayName(user);
|
|
227
|
+
} catch (userErr) {
|
|
228
|
+
console.warn(`⚠️ 获取用户信息失败:${userErr.message}`);
|
|
229
|
+
}
|
|
222
230
|
res.send(renderCallbackPage({
|
|
223
231
|
type: 'success',
|
|
224
232
|
title: '登录成功',
|
|
225
233
|
message: 'CLI 已完成授权,token 已保存到本地。',
|
|
226
234
|
hint: '可以关闭此页面',
|
|
227
235
|
}));
|
|
228
|
-
console.log('✅ 登录成功');
|
|
236
|
+
console.log(userName ? `✅ 登录成功:${userName}` : '✅ 登录成功');
|
|
229
237
|
resolve(token);
|
|
230
238
|
} catch (err) {
|
|
231
239
|
res.status(500).send(renderCallbackPage({
|
package/npm-packages.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const NPM_SCOPE = '@app-public';
|
|
2
|
+
|
|
3
|
+
const ENV_NPM_PACKAGES = {
|
|
4
|
+
release: `${NPM_SCOPE}/weaverbird`,
|
|
5
|
+
debug: `${NPM_SCOPE}/weaverbird-debug`,
|
|
6
|
+
test: `${NPM_SCOPE}/weaverbird-test`,
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
function getNpmPackageName(env) {
|
|
10
|
+
const name = ENV_NPM_PACKAGES[env];
|
|
11
|
+
if (!name) {
|
|
12
|
+
throw new Error(`未知环境 "${env}",无法确定 npm 包名`);
|
|
13
|
+
}
|
|
14
|
+
return name;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
module.exports = {
|
|
18
|
+
NPM_SCOPE,
|
|
19
|
+
ENV_NPM_PACKAGES,
|
|
20
|
+
getNpmPackageName,
|
|
21
|
+
};
|
package/package.json
CHANGED
package/token.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
const { getToken, saveToken } = require('./tokenStore');
|
|
2
|
+
const { request, getValidAccessToken } = require('./api');
|
|
3
|
+
|
|
4
|
+
const SET_TOKEN_PATH = '/v1/api/ci/open/setToken';
|
|
5
|
+
|
|
6
|
+
const SUPPORTED_TYPES = {
|
|
7
|
+
figma: {
|
|
8
|
+
localKey: 'figma_token',
|
|
9
|
+
label: 'Figma',
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
async function setServiceToken(type, tokenValue) {
|
|
14
|
+
const config = SUPPORTED_TYPES[type];
|
|
15
|
+
if (!config) {
|
|
16
|
+
throw new Error(`不支持的 type: ${type},当前支持: ${Object.keys(SUPPORTED_TYPES).join(', ')}`);
|
|
17
|
+
}
|
|
18
|
+
if (!tokenValue || !String(tokenValue).trim()) {
|
|
19
|
+
throw new Error('token 不能为空');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const stored = getToken();
|
|
23
|
+
if (!stored?.access_token) {
|
|
24
|
+
throw new Error('未登录,请先执行 weaverbird cli login');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const value = String(tokenValue).trim();
|
|
28
|
+
const cliToken = await getValidAccessToken();
|
|
29
|
+
saveToken({
|
|
30
|
+
...stored,
|
|
31
|
+
[config.localKey]: value,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const result = await request(SET_TOKEN_PATH, {
|
|
35
|
+
method: 'POST',
|
|
36
|
+
headers: {
|
|
37
|
+
'Content-Type': 'application/json',
|
|
38
|
+
'cli_token': cliToken,
|
|
39
|
+
},
|
|
40
|
+
body: JSON.stringify({
|
|
41
|
+
type,
|
|
42
|
+
token: value,
|
|
43
|
+
}),
|
|
44
|
+
});
|
|
45
|
+
if (result.code !== 200) {
|
|
46
|
+
throw new Error(result.msg || '同步 token 到服务端失败');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
console.log(`✅ ${config.label} token 已保存到本地并同步至服务端`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
module.exports = {
|
|
53
|
+
SUPPORTED_TYPES,
|
|
54
|
+
setServiceToken,
|
|
55
|
+
};
|
package/update.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const { spawnSync } = require('child_process');
|
|
2
|
+
const { ENV } = require('./config');
|
|
3
|
+
const { getNpmPackageName } = require('./npm-packages');
|
|
4
|
+
const pkg = require('./package.json');
|
|
5
|
+
|
|
6
|
+
function queryLatestVersion(packageName) {
|
|
7
|
+
const result = spawnSync('npm', ['view', packageName, 'version'], {
|
|
8
|
+
encoding: 'utf8',
|
|
9
|
+
shell: true,
|
|
10
|
+
});
|
|
11
|
+
if (result.status !== 0) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
return result.stdout.trim() || null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function runUpdate() {
|
|
18
|
+
const packageName = getNpmPackageName(ENV);
|
|
19
|
+
const latest = queryLatestVersion(packageName);
|
|
20
|
+
|
|
21
|
+
console.log(`📦 包名: ${packageName}`);
|
|
22
|
+
console.log(`📌 当前版本: ${pkg.version}`);
|
|
23
|
+
if (latest) {
|
|
24
|
+
console.log(`🌐 最新版本: ${latest}`);
|
|
25
|
+
if (latest === pkg.version) {
|
|
26
|
+
console.log('✅ 已是最新版本');
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
console.log(`⬇️ 正在更新 ${packageName}@latest ...`);
|
|
32
|
+
const result = spawnSync('npm', ['install', '-g', `${packageName}@latest`], {
|
|
33
|
+
stdio: 'inherit',
|
|
34
|
+
shell: true,
|
|
35
|
+
});
|
|
36
|
+
if (result.status !== 0) {
|
|
37
|
+
throw new Error('更新失败,请检查网络或 npm 登录状态');
|
|
38
|
+
}
|
|
39
|
+
console.log('✅ 更新完成');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = {
|
|
43
|
+
runUpdate,
|
|
44
|
+
};
|
package/user.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const { getToken, saveToken } = require('./tokenStore');
|
|
2
|
+
const { request, getValidAccessToken } = require('./api');
|
|
3
|
+
const GET_USER_PATH = '/v1/api/ci/open/getUser';
|
|
4
|
+
|
|
5
|
+
function normalizeUser(user) {
|
|
6
|
+
if (!user) {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
return {
|
|
10
|
+
id: user.id,
|
|
11
|
+
name: user.name,
|
|
12
|
+
realname: user.realname || null,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function getDisplayName(user) {
|
|
17
|
+
if (!user) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
return user.realname || user.name || null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function fetchAndSaveCurrentUser() {
|
|
24
|
+
const cliToken = await getValidAccessToken();
|
|
25
|
+
const result = await request(GET_USER_PATH, {
|
|
26
|
+
method: 'GET',
|
|
27
|
+
headers: {
|
|
28
|
+
cli_token: cliToken,
|
|
29
|
+
},
|
|
30
|
+
}); if (result.code !== 200 || !result.data?.user) {
|
|
31
|
+
throw new Error(result.msg || '获取用户信息失败');
|
|
32
|
+
}
|
|
33
|
+
const user = normalizeUser(result.data.user);
|
|
34
|
+
const stored = getToken() || {};
|
|
35
|
+
saveToken({
|
|
36
|
+
...stored,
|
|
37
|
+
user,
|
|
38
|
+
});
|
|
39
|
+
return user;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = {
|
|
43
|
+
GET_USER_PATH,
|
|
44
|
+
normalizeUser,
|
|
45
|
+
getDisplayName,
|
|
46
|
+
fetchAndSaveCurrentUser,
|
|
47
|
+
};
|