@cloudglab/yapi-cli 0.0.8 → 0.0.9
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/CHANGELOG.md +18 -1
- package/README.md +6 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +41 -77
- package/dist/cli.js.map +1 -1
- package/dist/core/changelog.js +7 -14
- package/dist/core/changelog.js.map +1 -1
- package/dist/core/cli-output.js +7 -6
- package/dist/core/cli-output.js.map +1 -1
- package/dist/core/cli-registry.js +28 -6
- package/dist/core/cli-registry.js.map +1 -1
- package/dist/core/manifest.js +14 -4
- package/dist/core/manifest.js.map +1 -1
- package/dist/core/output.d.ts +2 -2
- package/dist/core/output.js +12 -45
- package/dist/core/output.js.map +1 -1
- package/dist/core/tool-registry.js +5 -0
- package/dist/core/tool-registry.js.map +1 -1
- package/dist/core/update-probe.d.ts +3 -1
- package/dist/core/update-probe.js +9 -4
- package/dist/core/update-probe.js.map +1 -1
- package/dist/core/url-parser.js +10 -0
- package/dist/core/url-parser.js.map +1 -1
- package/dist/install.d.ts +9 -1
- package/dist/install.js +74 -20
- package/dist/install.js.map +1 -1
- package/dist/manifest.json +37 -1
- package/dist/services/yapi/api.d.ts +255 -79
- package/dist/services/yapi/api.js +162 -58
- package/dist/services/yapi/api.js.map +1 -1
- package/dist/services/yapi/auth.d.ts +8 -3
- package/dist/services/yapi/auth.js +10 -6
- package/dist/services/yapi/auth.js.map +1 -1
- package/dist/services/yapi/authCache.js +9 -2
- package/dist/services/yapi/authCache.js.map +1 -1
- package/dist/services/yapi/config.d.ts +6 -0
- package/dist/services/yapi/config.js +85 -8
- package/dist/services/yapi/config.js.map +1 -1
- package/dist/services/yapi/index.d.ts +3 -3
- package/dist/services/yapi/index.js +2 -3
- package/dist/services/yapi/index.js.map +1 -1
- package/dist/services/yapi/types.d.ts +38 -0
- package/dist/tools/shared.d.ts +17 -1
- package/dist/tools/shared.js +25 -6
- package/dist/tools/shared.js.map +1 -1
- package/dist/tools/yapi/docs-sync.js +3 -0
- package/dist/tools/yapi/docs-sync.js.map +1 -1
- package/dist/tools/yapi/groups.d.ts +1 -1
- package/dist/tools/yapi/groups.js +1 -1
- package/dist/tools/yapi/groups.js.map +1 -1
- package/dist/tools/yapi/register-auth.js +116 -104
- package/dist/tools/yapi/register-auth.js.map +1 -1
- package/dist/tools/yapi/register-group.js +118 -93
- package/dist/tools/yapi/register-group.js.map +1 -1
- package/dist/tools/yapi/register-interface.js +418 -238
- package/dist/tools/yapi/register-interface.js.map +1 -1
- package/dist/tools/yapi/register-mock.d.ts +2 -0
- package/dist/tools/yapi/register-mock.js +240 -0
- package/dist/tools/yapi/register-mock.js.map +1 -0
- package/dist/tools/yapi/register-project.js +344 -223
- package/dist/tools/yapi/register-project.js.map +1 -1
- package/dist/tools/yapi/register-test.js +33 -25
- package/dist/tools/yapi/register-test.js.map +1 -1
- package/dist/tools/yapi/register-util.js +444 -350
- package/dist/tools/yapi/register-util.js.map +1 -1
- package/dist/tools/yapi/register.js +3 -0
- package/dist/tools/yapi/register.js.map +1 -1
- package/dist/tools/yapi/utils.d.ts +49 -0
- package/dist/tools/yapi/utils.js +124 -2
- package/dist/tools/yapi/utils.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +18 -5
- package/skills/yapi-cli/SKILL.md +22 -12
- package/skills/yapi-cli/reference/auth.md +34 -45
- package/skills/yapi-cli/reference/cheatsheet.md +156 -0
- package/skills/yapi-cli/reference/cli.md +39 -51
- package/skills/yapi-cli/reference/commands.md +35 -125
- package/skills/yapi-cli/reference/group.md +46 -19
- package/skills/yapi-cli/reference/index.md +32 -0
- package/skills/yapi-cli/reference/install.md +30 -6
- package/skills/yapi-cli/reference/interface.md +71 -155
- package/skills/yapi-cli/reference/mock.md +93 -0
- package/skills/yapi-cli/reference/overview.md +7 -5
- package/skills/yapi-cli/reference/project.md +67 -87
- package/skills/yapi-cli/reference/scenarios.md +184 -0
- package/skills/yapi-cli/reference/test.md +20 -17
- package/skills/yapi-cli/reference/tooling.md +89 -0
- package/dist/services/yapi/cache.d.ts +0 -27
- package/dist/services/yapi/cache.js +0 -88
- package/dist/services/yapi/cache.js.map +0 -1
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { isInteractive, resolveAuthModeSelection, resolveRequiredTextInput } from '../../core/auth-mode.js';
|
|
3
3
|
import { CliConfigError, CliValidationError } from '../../core/errors.js';
|
|
4
|
-
import { jsonResult, optionalTrimmedText } from '../shared.js';
|
|
5
|
-
import {
|
|
4
|
+
import { confirmSchema, jsonResult, optionalTrimmedText, runWithPreview } from '../shared.js';
|
|
5
|
+
import { previewOrAssertWriteAllowed } from '../../core/write-guard.js';
|
|
6
|
+
import { saveConfig, hasConfig, loadConfig, YApiService, encryptPassword } from '../../services/yapi/index.js';
|
|
7
|
+
import { getAuthService } from './utils.js';
|
|
6
8
|
export function registerAuthCommands(registry) {
|
|
7
9
|
// ==================== 配置初始化 ====================
|
|
8
10
|
registry.tool('config-init', {
|
|
@@ -10,41 +12,45 @@ export function registerAuthCommands(registry) {
|
|
|
10
12
|
username: optionalTrimmedText.describe('登录用户名,通常是邮箱前缀,会原样回写到配置文件供后续 login 默认使用。空字符串视为未提供。'),
|
|
11
13
|
password: optionalTrimmedText.describe('登录密码,原样写入本地配置文件,明文存储于 ~/.yapi/config.json,仅本机可读。命令行传入时不会回显到终端,但保存后会被后续 login 默认使用,请勿在共享机器使用。'),
|
|
12
14
|
authMode: z.enum(['native', 'ldap']).optional().describe('认证方式,决定后续 login 命令的密码处理逻辑:native 直传明文、ldap 会按内置规则 PBKDF2 加密后再提交。若 YApi 未启用 LDAP 插件,请保持 native。'),
|
|
15
|
+
...confirmSchema,
|
|
13
16
|
}, async (input) => {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
17
|
+
const payload = input;
|
|
18
|
+
return runWithPreview('config-init', payload.confirm, payload, previewOrAssertWriteAllowed, async () => {
|
|
19
|
+
if (hasConfig()) {
|
|
20
|
+
throw new CliConfigError('配置文件已存在,如需重新配置请先删除 ~/.yapi/config.json');
|
|
21
|
+
}
|
|
22
|
+
if (!isInteractive() && (!input.url || !input.username || !input.password || !input.authMode)) {
|
|
23
|
+
throw new CliValidationError('当前不是交互式终端,无法输入配置。请显式传 --url、--username、--password、--auth-mode 后重试。');
|
|
24
|
+
}
|
|
25
|
+
const url = await resolveRequiredTextInput(input.url, {
|
|
26
|
+
context: '配置初始化',
|
|
27
|
+
flag: '--url',
|
|
28
|
+
prompt: '请输入 YApi 服务器地址',
|
|
29
|
+
normalize: (value) => value.trim().replace(/\/+$/, ''),
|
|
30
|
+
});
|
|
31
|
+
const username = await resolveRequiredTextInput(input.username, {
|
|
32
|
+
context: '配置初始化',
|
|
33
|
+
flag: '--username',
|
|
34
|
+
prompt: '请输入登录用户名',
|
|
35
|
+
});
|
|
36
|
+
const password = await resolveRequiredTextInput(input.password, {
|
|
37
|
+
context: '配置初始化',
|
|
38
|
+
flag: '--password',
|
|
39
|
+
prompt: '请输入登录密码(输入可见)',
|
|
40
|
+
});
|
|
41
|
+
const authMode = await resolveAuthModeSelection(input.authMode, '配置初始化');
|
|
42
|
+
const config = {
|
|
43
|
+
url,
|
|
44
|
+
username,
|
|
45
|
+
password,
|
|
46
|
+
authMode,
|
|
47
|
+
};
|
|
48
|
+
saveConfig(config);
|
|
49
|
+
return jsonResult({
|
|
50
|
+
url: config.url,
|
|
51
|
+
username: config.username,
|
|
52
|
+
authMode: config.authMode,
|
|
53
|
+
});
|
|
48
54
|
});
|
|
49
55
|
}, '初始化本地 YApi 登录配置', {
|
|
50
56
|
costHint: 'low',
|
|
@@ -58,83 +64,89 @@ export function registerAuthCommands(registry) {
|
|
|
58
64
|
password: optionalTrimmedText.describe('登录密码,必须配合 --username 或 --email 一起传入(命令行传入时不会回显)。登录成功后会回写到本地配置文件,后续未显式传参时会作为默认密码使用。'),
|
|
59
65
|
uid: optionalTrimmedText.describe('用户 UID。仅在 --token 模式下生效:当 token 是 JWT 但解析不出 uid 字段时,可手动指定。LDAP 登录模式下会由服务端响应自动提取,无需传入。'),
|
|
60
66
|
ldap: z.coerce.boolean().optional().default(false).describe('是否走 LDAP 登录。设为 true 时(或配置文件中 authMode 已经是 ldap),密码会先经内置 PBKDF2 加密再提交给 YApi 服务端。YApi 未启用 LDAP 插件时传 true 会登录失败。'),
|
|
67
|
+
...confirmSchema,
|
|
61
68
|
}, async (input) => {
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const username = input.username ?? input.email ?? config.username;
|
|
77
|
-
const password = input.password ?? config.password;
|
|
78
|
-
if (username && password) {
|
|
79
|
-
const auth = new YApiAuthService(YAPI_HOME);
|
|
80
|
-
const api = new YApiService(config.url, () => auth.getAuthHeaders(), () => auth.getToken() ?? '');
|
|
81
|
-
if (input.ldap || config.authMode === 'ldap') {
|
|
82
|
-
// LDAP 登录:密码 PBKDF2 加密后发送
|
|
83
|
-
const encrypted = encryptPassword(password);
|
|
84
|
-
const ldapResult = await api.loginByLdap(username, encrypted);
|
|
85
|
-
auth.loginWithJwtCookie(ldapResult.token, ldapResult.username, ldapResult.uid);
|
|
86
|
-
config.username = username;
|
|
87
|
-
config.password = password;
|
|
88
|
-
config.authMode = 'ldap';
|
|
89
|
-
saveConfig(config);
|
|
69
|
+
const payload = input;
|
|
70
|
+
return runWithPreview('login', payload.confirm, payload, previewOrAssertWriteAllowed, async () => {
|
|
71
|
+
const config = loadConfig();
|
|
72
|
+
if (!config) {
|
|
73
|
+
throw new CliConfigError('请先运行 `yapi config-init` 初始化配置');
|
|
74
|
+
}
|
|
75
|
+
if (input.token) {
|
|
76
|
+
const auth = getAuthService();
|
|
77
|
+
// 先校验 token 有效性(与 login-by-token 行为对齐),失败则不写入本地缓存
|
|
78
|
+
const verifyApi = new YApiService(config.url, () => ({}), () => input.token, undefined, undefined, auth);
|
|
79
|
+
await verifyApi.getLoginStatus();
|
|
80
|
+
// JWT token 同时以 Authorization 和 _yapi_token/_yapi_uid cookie 形式保存
|
|
81
|
+
const uid = input.uid ?? extractJwtUid(input.token);
|
|
82
|
+
auth.loginWithJwtCookie(input.token, undefined, uid);
|
|
90
83
|
return jsonResult({
|
|
91
|
-
method: '
|
|
92
|
-
username:
|
|
84
|
+
method: 'token',
|
|
85
|
+
username: null,
|
|
93
86
|
});
|
|
94
87
|
}
|
|
95
|
-
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
88
|
+
const username = input.username ?? input.email ?? config.username;
|
|
89
|
+
const password = input.password ?? config.password;
|
|
90
|
+
if (username && password) {
|
|
91
|
+
const auth = getAuthService();
|
|
92
|
+
const api = new YApiService(config.url, () => auth.getAuthHeaders(), () => auth.getToken() ?? '', undefined, undefined, auth);
|
|
93
|
+
if (input.ldap || config.authMode === 'ldap') {
|
|
94
|
+
// LDAP 登录:密码 PBKDF2 加密后发送
|
|
95
|
+
const encrypted = encryptPassword(password);
|
|
96
|
+
const ldapResult = await api.loginByLdap(username, encrypted);
|
|
97
|
+
auth.loginWithJwtCookie(ldapResult.token, ldapResult.username, ldapResult.uid);
|
|
98
|
+
// 登录成功后不再回写明文 password 到配置文件(仅在 config-init 时落盘)
|
|
99
|
+
return jsonResult({
|
|
100
|
+
method: 'ldap',
|
|
101
|
+
username: ldapResult.username,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
// 标准密码登录
|
|
105
|
+
const result = await api.loginWithPassword(username, password);
|
|
106
|
+
auth.loginWithJwtCookie(result.token, result.username, result.uid);
|
|
107
|
+
// 登录成功后不再回写明文 password 到配置文件(仅在 config-init 时落盘)
|
|
108
|
+
return jsonResult({
|
|
109
|
+
method: 'password',
|
|
110
|
+
username: result.username,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
throw new CliValidationError('请提供 --token 或 --username/--email + --password');
|
|
114
|
+
});
|
|
108
115
|
}, '登录 YApi 并写入认证缓存', {
|
|
109
116
|
costHint: 'low',
|
|
110
117
|
nextBestTools: ['whoami', 'project-list', 'group-list'],
|
|
111
118
|
});
|
|
112
119
|
// ==================== 登出 ====================
|
|
113
|
-
registry.tool('logout', {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
120
|
+
registry.tool('logout', {
|
|
121
|
+
...confirmSchema,
|
|
122
|
+
}, async (input) => {
|
|
123
|
+
const payload = input;
|
|
124
|
+
return runWithPreview('logout', payload.confirm, payload, previewOrAssertWriteAllowed, async () => {
|
|
125
|
+
const auth = getAuthService();
|
|
126
|
+
let serverLogoutOk = false;
|
|
127
|
+
let serverLogoutError;
|
|
128
|
+
// 先调用服务端登出(如果已登录)
|
|
129
|
+
const config = loadConfig();
|
|
130
|
+
if (config) {
|
|
131
|
+
try {
|
|
132
|
+
const testApi = new YApiService(config.url, () => auth.getAuthHeaders(), () => auth.getToken() ?? '', undefined, undefined, auth);
|
|
133
|
+
const result = await testApi.userLogout();
|
|
134
|
+
serverLogoutOk = result.errcode === 0;
|
|
135
|
+
if (!serverLogoutOk) {
|
|
136
|
+
serverLogoutError = result.errmsg;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
serverLogoutError = err instanceof Error ? err.message : String(err);
|
|
126
141
|
}
|
|
127
142
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
message: '已登出',
|
|
136
|
-
serverLogout: serverLogoutOk,
|
|
137
|
-
serverLogoutError,
|
|
143
|
+
// 再清理本地认证缓存
|
|
144
|
+
auth.logout();
|
|
145
|
+
return jsonResult({
|
|
146
|
+
message: '已登出',
|
|
147
|
+
serverLogout: serverLogoutOk,
|
|
148
|
+
serverLogoutError,
|
|
149
|
+
});
|
|
138
150
|
});
|
|
139
151
|
}, '登出 YApi(同时清理本地缓存 + 服务端会话。无参数;服务端不可用时不会中断本地清理,服务端失败原因会写入返回结果)。', {
|
|
140
152
|
costHint: 'low',
|
|
@@ -142,12 +154,12 @@ export function registerAuthCommands(registry) {
|
|
|
142
154
|
});
|
|
143
155
|
// ==================== 查看当前用户 ====================
|
|
144
156
|
registry.tool('whoami', {}, async () => {
|
|
145
|
-
const auth =
|
|
157
|
+
const auth = getAuthService();
|
|
146
158
|
const config = loadConfig();
|
|
147
159
|
// 优先尝试服务端验证
|
|
148
160
|
if (config) {
|
|
149
161
|
try {
|
|
150
|
-
const testApi = new YApiService(config.url, () => auth.getAuthHeaders(), () => auth.getToken() ?? '');
|
|
162
|
+
const testApi = new YApiService(config.url, () => auth.getAuthHeaders(), () => auth.getToken() ?? '', undefined, undefined, auth);
|
|
151
163
|
const status = await testApi.getLoginStatus();
|
|
152
164
|
return jsonResult({
|
|
153
165
|
loggedIn: true,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register-auth.js","sourceRoot":"","sources":["../../../src/tools/yapi/register-auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,aAAa,EAAE,wBAAwB,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AAC5G,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1E,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"register-auth.js","sourceRoot":"","sources":["../../../src/tools/yapi/register-auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { CliError, CliValidationError } from '../../core/errors.js';
|
|
3
|
-
import { jsonResult } from '../shared.js';
|
|
3
|
+
import { confirmSchema, jsonResult, runWithPreview } from '../shared.js';
|
|
4
|
+
import { previewOrAssertWriteAllowed } from '../../core/write-guard.js';
|
|
4
5
|
import { createYApiServices } from './utils.js';
|
|
5
6
|
export function registerGroupCommands(registry) {
|
|
6
7
|
registry.tool('group-list', {}, async () => {
|
|
@@ -18,7 +19,7 @@ export function registerGroupCommands(registry) {
|
|
|
18
19
|
nextBestTools: ['group-get', 'project-list'],
|
|
19
20
|
});
|
|
20
21
|
registry.tool('group-get', {
|
|
21
|
-
id: z.
|
|
22
|
+
id: z.number().int().positive().describe('分组 ID,必填。YApi 服务端分配的内部数字 ID,可通过 group-list 获取。'),
|
|
22
23
|
}, async (input) => {
|
|
23
24
|
const { api } = createYApiServices();
|
|
24
25
|
const result = await api.getGroup(input.id);
|
|
@@ -35,23 +36,27 @@ export function registerGroupCommands(registry) {
|
|
|
35
36
|
});
|
|
36
37
|
// ==================== 创建分组 ====================
|
|
37
38
|
registry.tool('group-create', {
|
|
38
|
-
name: z.string().describe('分组名称,必填,前后会做 trim,空字符串会被 CLI 拒绝。建议用团队/业务域命名,便于在 group-list 中区分。'),
|
|
39
|
+
name: z.string().trim().min(1).describe('分组名称,必填,前后会做 trim,空字符串会被 CLI 拒绝。建议用团队/业务域命名,便于在 group-list 中区分。'),
|
|
39
40
|
desc: z.string().optional().describe('分组描述,可选。纯文本,YApi 后端不做格式校验,仅作为展示文案。建议不超过 100 字。'),
|
|
41
|
+
...confirmSchema,
|
|
40
42
|
}, async (input) => {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
43
|
+
const payload = input;
|
|
44
|
+
return runWithPreview('group-create', payload.confirm, payload, previewOrAssertWriteAllowed, async () => {
|
|
45
|
+
if (!input.name.trim()) {
|
|
46
|
+
throw new CliValidationError('分组名称不能为空');
|
|
47
|
+
}
|
|
48
|
+
const { api } = createYApiServices();
|
|
49
|
+
const result = await api.groupAdd({
|
|
50
|
+
group_name: input.name.trim(),
|
|
51
|
+
group_desc: input.desc,
|
|
52
|
+
});
|
|
53
|
+
if (result.errcode !== 0) {
|
|
54
|
+
throw new CliError(result.errmsg ?? '创建分组失败', 14, 'API_ERROR');
|
|
55
|
+
}
|
|
56
|
+
return jsonResult({
|
|
57
|
+
groupId: result.data._id,
|
|
58
|
+
message: `分组「${input.name}」创建成功`,
|
|
59
|
+
});
|
|
55
60
|
});
|
|
56
61
|
}, '创建新分组', {
|
|
57
62
|
costHint: 'medium',
|
|
@@ -59,25 +64,29 @@ export function registerGroupCommands(registry) {
|
|
|
59
64
|
});
|
|
60
65
|
// ==================== 更新分组 ====================
|
|
61
66
|
registry.tool('group-update', {
|
|
62
|
-
groupId: z.
|
|
67
|
+
groupId: z.number().int().positive().describe('目标分组 ID,必填。若不存在会被服务端拒绝,命令会整体报错且不修改任何字段。'),
|
|
63
68
|
name: z.string().optional().describe('新名称,可选。name 和 desc 至少需要传一个,否则 CLI 会拒绝请求(防止空请求)。'),
|
|
64
69
|
desc: z.string().optional().describe('新描述,可选。传空字符串不会清空原 desc,因为空字符串会被 Zod 当作 undefined 处理。'),
|
|
70
|
+
...confirmSchema,
|
|
65
71
|
}, async (input) => {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
72
|
+
const payload = input;
|
|
73
|
+
return runWithPreview('group-update', payload.confirm, payload, previewOrAssertWriteAllowed, async () => {
|
|
74
|
+
if (input.name === undefined && input.desc === undefined) {
|
|
75
|
+
throw new CliValidationError('至少需要提供 name 或 desc 中的一个');
|
|
76
|
+
}
|
|
77
|
+
const { api } = createYApiServices();
|
|
78
|
+
const result = await api.groupUp({
|
|
79
|
+
id: input.groupId,
|
|
80
|
+
group_name: input.name,
|
|
81
|
+
group_desc: input.desc,
|
|
82
|
+
});
|
|
83
|
+
if (result.errcode !== 0) {
|
|
84
|
+
throw new CliError(result.errmsg ?? '更新分组失败', 14, 'API_ERROR');
|
|
85
|
+
}
|
|
86
|
+
return jsonResult({
|
|
87
|
+
groupId: input.groupId,
|
|
88
|
+
message: `分组 ${input.groupId} 已更新`,
|
|
89
|
+
});
|
|
81
90
|
});
|
|
82
91
|
}, '更新分组信息', {
|
|
83
92
|
costHint: 'medium',
|
|
@@ -85,16 +94,20 @@ export function registerGroupCommands(registry) {
|
|
|
85
94
|
});
|
|
86
95
|
// ==================== 删除分组 ====================
|
|
87
96
|
registry.tool('group-delete', {
|
|
88
|
-
groupId: z.
|
|
97
|
+
groupId: z.number().int().positive().describe('目标分组 ID,必填。删除前请先确认其下项目与接口已迁移或备份;具体是否允许删除、是否级联处理取决于服务端版本与权限配置。'),
|
|
98
|
+
...confirmSchema,
|
|
89
99
|
}, async (input) => {
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
100
|
+
const payload = input;
|
|
101
|
+
return runWithPreview('group-delete', payload.confirm, payload, previewOrAssertWriteAllowed, async () => {
|
|
102
|
+
const { api } = createYApiServices();
|
|
103
|
+
const result = await api.groupDel(input.groupId);
|
|
104
|
+
if (result.errcode !== 0) {
|
|
105
|
+
throw new CliError(result.errmsg ?? '删除分组失败', 14, 'API_ERROR');
|
|
106
|
+
}
|
|
107
|
+
return jsonResult({
|
|
108
|
+
groupId: input.groupId,
|
|
109
|
+
message: `分组 ${input.groupId} 已删除`,
|
|
110
|
+
});
|
|
98
111
|
});
|
|
99
112
|
}, '删除分组', {
|
|
100
113
|
costHint: 'medium',
|
|
@@ -102,23 +115,27 @@ export function registerGroupCommands(registry) {
|
|
|
102
115
|
});
|
|
103
116
|
// ==================== 添加分组成员 ====================
|
|
104
117
|
registry.tool('group-member-add', {
|
|
105
|
-
groupId: z.
|
|
106
|
-
uid: z.
|
|
118
|
+
groupId: z.number().int().positive().describe('目标分组 ID,必填。需要分组所有者或管理员权限,否则服务端会拒绝。'),
|
|
119
|
+
uid: z.number().int().positive().describe('要加入的用户 UID,YApi 内部数字 ID。可通过 user-list 或 user-search 获取。'),
|
|
107
120
|
role: z.string().optional().describe('成员角色,可选。常用值 owner / dev / master / reporter / guest。具体取值由服务端插件决定,传未知值通常会被服务端忽略或拒绝。'),
|
|
121
|
+
...confirmSchema,
|
|
108
122
|
}, async (input) => {
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
123
|
+
const payload = input;
|
|
124
|
+
return runWithPreview('group-member-add', payload.confirm, payload, previewOrAssertWriteAllowed, async () => {
|
|
125
|
+
const { api } = createYApiServices();
|
|
126
|
+
const result = await api.groupAddMember({
|
|
127
|
+
id: input.groupId,
|
|
128
|
+
member_uids: [input.uid],
|
|
129
|
+
role: input.role,
|
|
130
|
+
});
|
|
131
|
+
if (result.errcode !== 0) {
|
|
132
|
+
throw new CliError(result.errmsg ?? '添加分组成员失败', 14, 'API_ERROR');
|
|
133
|
+
}
|
|
134
|
+
return jsonResult({
|
|
135
|
+
groupId: input.groupId,
|
|
136
|
+
uid: input.uid,
|
|
137
|
+
message: `用户 ${input.uid} 已加入分组 ${input.groupId}`,
|
|
138
|
+
});
|
|
122
139
|
});
|
|
123
140
|
}, '添加分组成员', {
|
|
124
141
|
costHint: 'medium',
|
|
@@ -126,27 +143,31 @@ export function registerGroupCommands(registry) {
|
|
|
126
143
|
});
|
|
127
144
|
// ==================== 修改成员角色 ====================
|
|
128
145
|
registry.tool('group-member-role', {
|
|
129
|
-
groupId: z.
|
|
130
|
-
memberUid: z.
|
|
131
|
-
role: z.string().describe('新角色,必填。常用值 owner / dev / master / reporter / guest,传值会做 trim。空字符串会被 CLI 直接拒绝。具体合法值由服务端插件决定。'),
|
|
146
|
+
groupId: z.number().int().positive().describe('目标分组 ID,必填。需要分组所有者权限,否则服务端会拒绝。'),
|
|
147
|
+
memberUid: z.number().int().positive().describe('要修改角色的成员 UID,YApi 内部数字 ID。'),
|
|
148
|
+
role: z.string().trim().min(1).describe('新角色,必填。常用值 owner / dev / master / reporter / guest,传值会做 trim。空字符串会被 CLI 直接拒绝。具体合法值由服务端插件决定。'),
|
|
149
|
+
...confirmSchema,
|
|
132
150
|
}, async (input) => {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
151
|
+
const payload = input;
|
|
152
|
+
return runWithPreview('group-member-role', payload.confirm, payload, previewOrAssertWriteAllowed, async () => {
|
|
153
|
+
if (!input.role.trim()) {
|
|
154
|
+
throw new CliValidationError('角色不能为空');
|
|
155
|
+
}
|
|
156
|
+
const { api } = createYApiServices();
|
|
157
|
+
const result = await api.groupChangeMemberRole({
|
|
158
|
+
id: input.groupId,
|
|
159
|
+
member_uid: input.memberUid,
|
|
160
|
+
role: input.role.trim(),
|
|
161
|
+
});
|
|
162
|
+
if (result.errcode !== 0) {
|
|
163
|
+
throw new CliError(result.errmsg ?? '修改成员角色失败', 14, 'API_ERROR');
|
|
164
|
+
}
|
|
165
|
+
return jsonResult({
|
|
166
|
+
groupId: input.groupId,
|
|
167
|
+
memberUid: input.memberUid,
|
|
168
|
+
role: input.role,
|
|
169
|
+
message: `成员 ${input.memberUid} 角色已更新为 ${input.role}`,
|
|
170
|
+
});
|
|
150
171
|
});
|
|
151
172
|
}, '修改分组成员角色', {
|
|
152
173
|
costHint: 'medium',
|
|
@@ -154,25 +175,29 @@ export function registerGroupCommands(registry) {
|
|
|
154
175
|
});
|
|
155
176
|
// ==================== 移除分组成员 ====================
|
|
156
177
|
registry.tool('group-member-remove', {
|
|
157
|
-
groupId: z.
|
|
158
|
-
memberUid: z.
|
|
178
|
+
groupId: z.number().int().positive().describe('目标分组 ID,必填。'),
|
|
179
|
+
memberUid: z.number().int().positive().describe('要移除的成员 UID,YApi 内部数字 ID。注意移除不会删除用户本身,只是解除与分组的关联。'),
|
|
180
|
+
...confirmSchema,
|
|
159
181
|
}, async (input) => {
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
182
|
+
const payload = input;
|
|
183
|
+
return runWithPreview('group-member-remove', payload.confirm, payload, previewOrAssertWriteAllowed, async () => {
|
|
184
|
+
const { api } = createYApiServices();
|
|
185
|
+
const result = await api.groupDelMember({
|
|
186
|
+
id: input.groupId,
|
|
187
|
+
member_uid: input.memberUid,
|
|
188
|
+
});
|
|
189
|
+
if (result.errcode !== 0) {
|
|
190
|
+
throw new CliError(result.errmsg ?? '移除分组成员失败', 14, 'API_ERROR');
|
|
191
|
+
}
|
|
192
|
+
return jsonResult({
|
|
193
|
+
groupId: input.groupId,
|
|
194
|
+
memberUid: input.memberUid,
|
|
195
|
+
message: `成员 ${input.memberUid} 已从分组 ${input.groupId} 中移除`,
|
|
196
|
+
});
|
|
172
197
|
});
|
|
173
198
|
}, '移除分组成员', {
|
|
174
199
|
costHint: 'medium',
|
|
175
|
-
nextBestTools: ['group-
|
|
200
|
+
nextBestTools: ['group-member-add'],
|
|
176
201
|
});
|
|
177
202
|
// ==================== 我的分组 ====================
|
|
178
203
|
registry.tool('group-mine', {}, async () => {
|
|
@@ -191,7 +216,7 @@ export function registerGroupCommands(registry) {
|
|
|
191
216
|
});
|
|
192
217
|
// ==================== 分组成员列表 ====================
|
|
193
218
|
registry.tool('group-member-list', {
|
|
194
|
-
groupId: z.
|
|
219
|
+
groupId: z.number().int().positive().describe('目标分组 ID,必填。返回该分组下所有成员的用户名、角色、邮箱等基础信息。'),
|
|
195
220
|
}, async (input) => {
|
|
196
221
|
const { api } = createYApiServices();
|
|
197
222
|
const result = await api.listGroupMembers(input.groupId);
|