weaver-work-cli 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/internal/e10/auth/commands.js +4 -1
- package/dist/internal/e10/request-runtime.js +3 -2
- package/dist/shortcuts/index.js +4 -0
- package/dist/shortcuts/meeting/continuation.js +70 -0
- package/dist/shortcuts/meeting/errors.js +63 -0
- package/dist/shortcuts/meeting/host.js +167 -0
- package/dist/shortcuts/meeting/index.js +123 -0
- package/dist/shortcuts/meeting/manifest.js +64 -0
- package/dist/shortcuts/meeting/operations/catalogs.js +58 -0
- package/dist/shortcuts/meeting/operations/change.js +242 -0
- package/dist/shortcuts/meeting/operations/conflicts.js +142 -0
- package/dist/shortcuts/meeting/operations/create.js +349 -0
- package/dist/shortcuts/meeting/operations/env.js +191 -0
- package/dist/shortcuts/meeting/operations/lifecycle.js +139 -0
- package/dist/shortcuts/meeting/operations/meetings.js +73 -0
- package/dist/shortcuts/meeting/operations/receipts.js +195 -0
- package/dist/shortcuts/meeting/operations/registry.js +47 -0
- package/dist/shortcuts/meeting/operations/rooms.js +132 -0
- package/dist/shortcuts/meeting/operations/shared.js +112 -0
- package/dist/shortcuts/meeting/operations/signs.js +41 -0
- package/dist/shortcuts/meeting/operations/types.js +25 -0
- package/dist/shortcuts/meeting/operations.js +19 -0
- package/dist/shortcuts/yimiaoban/continuation.js +70 -0
- package/dist/shortcuts/yimiaoban/errors.js +61 -0
- package/dist/shortcuts/yimiaoban/host.js +469 -0
- package/dist/shortcuts/yimiaoban/index.js +271 -0
- package/dist/shortcuts/yimiaoban/manifest.js +28 -0
- package/dist/shortcuts/yimiaoban/operations/file-user.js +288 -0
- package/dist/shortcuts/yimiaoban/operations/group-read.js +477 -0
- package/dist/shortcuts/yimiaoban/operations/group-write.js +591 -0
- package/dist/shortcuts/yimiaoban/operations/msg-parse.js +429 -0
- package/dist/shortcuts/yimiaoban/operations/msg-sync.js +519 -0
- package/dist/shortcuts/yimiaoban/operations/msg-write.js +593 -0
- package/dist/shortcuts/yimiaoban/operations/person.js +102 -0
- package/dist/shortcuts/yimiaoban/operations/registry.js +38 -0
- package/dist/shortcuts/yimiaoban/operations/session.js +437 -0
- package/dist/shortcuts/yimiaoban/operations/shared.js +236 -0
- package/dist/shortcuts/yimiaoban/operations/types.js +27 -0
- package/dist/shortcuts/yimiaoban/operations/write-util.js +78 -0
- package/dist/shortcuts/yimiaoban/operations.js +8 -0
- package/docs/_catalog.md +6 -0
- package/docs/meeting.md +48 -0
- package/docs/yimiaoban.md +86 -0
- package/package.json +1 -1
- package/skill-template/business-info.json +4 -1
- package/skill-template/domains/meeting.md +3 -0
- package/skill-template/domains/yimiaoban.md +21 -0
- package/skills/weaver-e10-calendar/SKILL.md +1 -0
- package/skills/weaver-e10-esb/SKILL.md +1 -0
- package/skills/weaver-e10-esb/references/source-manifest.json +3 -3
- package/skills/weaver-e10-hrm/SKILL.md +1 -0
- package/skills/weaver-e10-jiuchuanhui/SKILL.md +1 -0
- package/skills/weaver-e10-jucailin/SKILL.md +1 -0
- package/skills/weaver-e10-mail/SKILL.md +1 -0
- package/skills/weaver-e10-meeting/SKILL.md +106 -0
- package/skills/weaver-e10-meeting/product.json +8 -0
- package/skills/weaver-e10-meeting/references/meeting-change.md +67 -0
- package/skills/weaver-e10-meeting/references/meeting-create.md +52 -0
- package/skills/weaver-e10-meeting/references/meeting-lifecycle.md +57 -0
- package/skills/weaver-e10-meeting/references/meeting-queries.md +61 -0
- package/skills/weaver-e10-meeting/references/meeting-receipts-signs.md +48 -0
- package/skills/weaver-e10-meeting/references/meeting-update.md +7 -0
- package/skills/weaver-e10-meeting/references/source-manifest.json +50 -0
- package/skills/weaver-e10-plan/SKILL.md +1 -0
- package/skills/weaver-e10-plan/references/source-manifest.json +3 -3
- package/skills/weaver-e10-qiyecheng/SKILL.md +1 -0
- package/skills/weaver-e10-qiyecheng/references/source-manifest.json +3 -3
- package/skills/weaver-e10-shared/SKILL.md +4 -0
- package/skills/weaver-e10-skill-maker/SKILL.md +1 -0
- package/skills/weaver-e10-skill-maker/references/weaver-skill-style.md +1 -1
- package/skills/weaver-e10-wenshuding/SKILL.md +1 -0
- package/skills/weaver-e10-wenshuding/references/source-manifest.json +3 -3
- package/skills/weaver-e10-yepiaotong/references/source-manifest.json +5 -5
- package/skills/weaver-e10-yimiaoban/SKILL.md +104 -0
- package/skills/weaver-e10-yimiaoban/product.json +8 -0
- package/skills/weaver-e10-yimiaoban/references/ding-write.md +68 -0
- package/skills/weaver-e10-yimiaoban/references/error-codes.md +85 -0
- package/skills/weaver-e10-yimiaoban/references/field-resolution.md +46 -0
- package/skills/weaver-e10-yimiaoban/references/file-user-i18n.md +54 -0
- package/skills/weaver-e10-yimiaoban/references/group-read.md +55 -0
- package/skills/weaver-e10-yimiaoban/references/group-write.md +72 -0
- package/skills/weaver-e10-yimiaoban/references/msg-read.md +59 -0
- package/skills/weaver-e10-yimiaoban/references/msg-write.md +64 -0
- package/skills/weaver-e10-yimiaoban/references/person.md +44 -0
- package/skills/weaver-e10-yimiaoban/references/safety-boundaries.md +61 -0
- package/skills/weaver-e10-yimiaoban/references/session-sysmsg.md +47 -0
- package/skills/weaver-e10-yimiaoban/references/source-manifest.json +485 -0
- package/skills/weaver-e10-ziguanjia/SKILL.md +1 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/** 保留凭据头:输入里出现即拒绝(认证由 E10 runtime 托管)。 */
|
|
2
|
+
const FORBIDDEN_KEYS = new Set([
|
|
3
|
+
'cookie',
|
|
4
|
+
'eteamsid',
|
|
5
|
+
'eteam_sid',
|
|
6
|
+
'token',
|
|
7
|
+
'authorization',
|
|
8
|
+
'appkey',
|
|
9
|
+
'app_secret',
|
|
10
|
+
'appsecret',
|
|
11
|
+
]);
|
|
12
|
+
export function asObject(value) {
|
|
13
|
+
return value && typeof value === 'object' && !Array.isArray(value)
|
|
14
|
+
? { ...value }
|
|
15
|
+
: {};
|
|
16
|
+
}
|
|
17
|
+
export function assertNoForbiddenKeys(input) {
|
|
18
|
+
for (const key of Object.keys(input)) {
|
|
19
|
+
if (FORBIDDEN_KEYS.has(key.toLowerCase())) {
|
|
20
|
+
throw meetingValidationError('forbidden_input_key', `输入不允许携带认证字段:${key}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
import { meetingValidationError } from '../errors.js';
|
|
25
|
+
/** 校验 yyyy-MM-dd HH:mm(标准版 @DateTimeFormat 契约),返回原串。 */
|
|
26
|
+
export function assertDateTimeMinute(value, label) {
|
|
27
|
+
if (typeof value !== 'string' || !/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/.test(value)) {
|
|
28
|
+
throw meetingValidationError('datetime_invalid', `${label} 必须是 yyyy-MM-dd HH:mm 格式`);
|
|
29
|
+
}
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
/** 校验 yyyy-MM-dd,返回原串。 */
|
|
33
|
+
export function assertDate(value, label) {
|
|
34
|
+
if (typeof value !== 'string' || !/^\d{4}-\d{2}-\d{2}$/.test(value)) {
|
|
35
|
+
throw meetingValidationError('date_invalid', `${label} 必须是 yyyy-MM-dd 格式`);
|
|
36
|
+
}
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
/** 长 id 一律转字符串(超 2^53 数字会失真)。 */
|
|
40
|
+
export function toIdString(value, label) {
|
|
41
|
+
if (typeof value === 'number') {
|
|
42
|
+
if (!Number.isSafeInteger(value)) {
|
|
43
|
+
throw meetingValidationError('id_invalid', `${label} 超过安全整数范围,必须用字符串传`);
|
|
44
|
+
}
|
|
45
|
+
return String(value);
|
|
46
|
+
}
|
|
47
|
+
if (typeof value === 'string' && /^\d+$/.test(value))
|
|
48
|
+
return value;
|
|
49
|
+
throw meetingValidationError('id_invalid', `${label} 必须是数字 id(超长 id 用字符串)`);
|
|
50
|
+
}
|
|
51
|
+
/** 逗号分隔 id 串:接受数组或字符串,输出字符串。 */
|
|
52
|
+
export function toIdCsv(value, label) {
|
|
53
|
+
if (value === undefined || value === null || value === '')
|
|
54
|
+
return undefined;
|
|
55
|
+
const parts = Array.isArray(value)
|
|
56
|
+
? value
|
|
57
|
+
: String(value).split(',');
|
|
58
|
+
return parts.map((item) => toIdString(item, label).trim()).filter(Boolean).join(',');
|
|
59
|
+
}
|
|
60
|
+
/** 标准版列表响应归一化:提取 rows/displayData/data 数组。 */
|
|
61
|
+
export function extractRows(value) {
|
|
62
|
+
const data = value?.data ?? value;
|
|
63
|
+
if (Array.isArray(data?.displayData)) {
|
|
64
|
+
return { rows: data.displayData, total: data.total };
|
|
65
|
+
}
|
|
66
|
+
if (Array.isArray(data?.data?.rows)) {
|
|
67
|
+
return { rows: data.data.rows, total: data.data.count ?? data.total };
|
|
68
|
+
}
|
|
69
|
+
if (Array.isArray(data?.rows)) {
|
|
70
|
+
return { rows: data.rows, total: data.count ?? data.total };
|
|
71
|
+
}
|
|
72
|
+
if (Array.isArray(data?.data)) {
|
|
73
|
+
return { rows: data.data, total: data.total };
|
|
74
|
+
}
|
|
75
|
+
if (Array.isArray(data?.datas)) {
|
|
76
|
+
return { rows: data.datas, total: data.total };
|
|
77
|
+
}
|
|
78
|
+
return { rows: [], total: 0 };
|
|
79
|
+
}
|
|
80
|
+
/** 冲突接口返回归一化:提取 cansub(是否可提交)与冲突描述。 */
|
|
81
|
+
export function extractConflict(value) {
|
|
82
|
+
const payload = value?.data ?? value ?? {};
|
|
83
|
+
const cansubRaw = payload.cansub ?? payload.canSub;
|
|
84
|
+
const cansub = typeof cansubRaw === 'boolean' ? cansubRaw : undefined;
|
|
85
|
+
const message = [payload.conflictMsg, payload.conflictmsg, payload.message, payload.msg]
|
|
86
|
+
.find((item) => typeof item === 'string' && item.trim()) || '';
|
|
87
|
+
return { cansub, message };
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* 回查会议是否存在且可读(getMeetingDetailField)。
|
|
91
|
+
*
|
|
92
|
+
* 注意:**权限判定不在 CLI 侧做**。源码依据:
|
|
93
|
+
* - MeetingPermissionEvent 用 MeetingBaseServiceUtils.getShowButtons(会议, operateType, tenant, user)
|
|
94
|
+
* 按会议状态 + 会议基础设置 + 权限判定按钮,并在不通过时返回本地化提示
|
|
95
|
+
* (如"您没有变更权限!"/"您没有取消权限!"/"您没有提前结束权限!");
|
|
96
|
+
* - getMeetingDetailField 返回的 showButtons 是页面新建按钮配置(实测为 ["save","submit"]),
|
|
97
|
+
* 其中 canChange/canCancel/canDel 等键并不存在,不能作为权限依据。
|
|
98
|
+
* 因此 CLI 只确认目标存在,权限交给服务端,并把服务端返回的提示原样透传给用户。
|
|
99
|
+
*/
|
|
100
|
+
export async function assertMeetingExists(id, host, operation) {
|
|
101
|
+
try {
|
|
102
|
+
await host.request({
|
|
103
|
+
method: 'GET',
|
|
104
|
+
path: '/api/meeting/detail/getMeetingDetailField',
|
|
105
|
+
operation,
|
|
106
|
+
query: { id },
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
throw meetingValidationError('meeting_not_operable', `未能确认会议 ${id} 存在或可读(getMeetingDetailField 查询失败):${error instanceof Error ? error.message : String(error)}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { meetingSuccess, SCHEMA_ID } from './types.js';
|
|
2
|
+
import { extractRows, toIdString } from './shared.js';
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// 签到列表(getMeetingSignInfoList)
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
const meetingSignList = {
|
|
7
|
+
name: 'meeting.sign.list',
|
|
8
|
+
resource: 'sign',
|
|
9
|
+
method: 'sign.list',
|
|
10
|
+
action: 'meeting.sign.list',
|
|
11
|
+
summary: '查询会议签到记录(getMeetingSignInfoList)并输出已签/未签统计',
|
|
12
|
+
capability: 'sign',
|
|
13
|
+
risk: 'read',
|
|
14
|
+
inputSchema: {
|
|
15
|
+
type: 'object',
|
|
16
|
+
additionalProperties: false,
|
|
17
|
+
properties: {
|
|
18
|
+
mtId: { ...SCHEMA_ID, description: '会议 id(必填)' },
|
|
19
|
+
},
|
|
20
|
+
required: ['mtId'],
|
|
21
|
+
},
|
|
22
|
+
handler: async (args, { host }) => {
|
|
23
|
+
// 源码依据(MeetingSignController.getMeetingSignInfoList):body 是 Map,
|
|
24
|
+
// 方法体读取的键名是 "meetingid"(不是 mtId);缺失会 Long.parseLong("") 抛异常。
|
|
25
|
+
// 对外 CLI 参数保持 mtId 统一,此处仅做键名适配。
|
|
26
|
+
const response = await host.request({
|
|
27
|
+
method: 'POST',
|
|
28
|
+
path: '/api/meeting/sign/getMeetingSignInfoList',
|
|
29
|
+
operation: 'meeting.sign.list',
|
|
30
|
+
body: { meetingid: toIdString(args.mtId, 'mtId') },
|
|
31
|
+
});
|
|
32
|
+
const { rows, total } = extractRows(response);
|
|
33
|
+
const signed = rows.filter((row) => row?.signtime !== undefined && row?.signtime !== null && row?.signtime !== '').length;
|
|
34
|
+
return meetingSuccess('meeting.sign.list', {
|
|
35
|
+
total,
|
|
36
|
+
summary: { total: rows.length, signed, unsigned: Math.max(rows.length - signed, 0) },
|
|
37
|
+
signs: rows,
|
|
38
|
+
});
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
export const meetingSignOperations = [meetingSignList];
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export function meetingSuccess(action, data, options = {}) {
|
|
2
|
+
const payload = { ok: true, action, data };
|
|
3
|
+
if (options.meta && Object.keys(options.meta).length > 0)
|
|
4
|
+
payload.meta = options.meta;
|
|
5
|
+
if (options.warnings && options.warnings.length > 0)
|
|
6
|
+
payload.warnings = options.warnings;
|
|
7
|
+
return payload;
|
|
8
|
+
}
|
|
9
|
+
/** 构造「对象类型 + 严格禁止额外字段」的 JSON Schema。 */
|
|
10
|
+
export function schemaOf(properties, options = {}) {
|
|
11
|
+
return {
|
|
12
|
+
type: 'object',
|
|
13
|
+
additionalProperties: false,
|
|
14
|
+
properties,
|
|
15
|
+
...(options.required?.length ? { required: options.required } : {}),
|
|
16
|
+
...(options.description ? { description: options.description } : {}),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/** E10 长 ID:源资料返回字符串,请求也接受数字或数字字符串(超 2^53 必须字符串)。 */
|
|
20
|
+
export const SCHEMA_ID = { oneOf: [{ type: 'integer' }, { type: 'string', pattern: '^\\d+$' }] };
|
|
21
|
+
export const SCHEMA_ID_LIST = { type: 'array', items: SCHEMA_ID };
|
|
22
|
+
/** 会议时间格式 yyyy-MM-dd HH:mm(标准版 @DateTimeFormat 契约)。 */
|
|
23
|
+
export const SCHEMA_DATETIME_MINUTE = { type: 'string', pattern: '^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}$' };
|
|
24
|
+
/** 日期格式 yyyy-MM-dd。 */
|
|
25
|
+
export const SCHEMA_DATE = { type: 'string', pattern: '^\\d{4}-\\d{2}-\\d{2}$' };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { getMeetingOperation } from './operations/registry.js';
|
|
2
|
+
import { assertNoForbiddenKeys, asObject } from './operations/shared.js';
|
|
3
|
+
export async function executeMeetingOperation(operation, rawInput, context) {
|
|
4
|
+
const definition = getMeetingOperation(operation);
|
|
5
|
+
const input = asObject(rawInput);
|
|
6
|
+
assertNoForbiddenKeys(input);
|
|
7
|
+
const host = {
|
|
8
|
+
request(request) {
|
|
9
|
+
return context.host.request({
|
|
10
|
+
...request,
|
|
11
|
+
operation: request.operation || operation,
|
|
12
|
+
});
|
|
13
|
+
},
|
|
14
|
+
getSession() {
|
|
15
|
+
return context.host.getSession();
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
return definition.handler(input, { ...context, host });
|
|
19
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { createHmac, randomBytes, timingSafeEqual } from 'node:crypto';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { chmod, mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { homedir } from 'node:os';
|
|
5
|
+
import { resolve } from 'node:path';
|
|
6
|
+
import { ymbValidationError } from './errors.js';
|
|
7
|
+
function stateRoot(ctx) {
|
|
8
|
+
const configured = ctx?.config?.path();
|
|
9
|
+
if (configured)
|
|
10
|
+
return resolve(configured, '..');
|
|
11
|
+
return process.env.WEAVER_WORK_CLI_HOME || process.env.WORK_CLI_HOME || resolve(homedir(), '.weaver-work-cli');
|
|
12
|
+
}
|
|
13
|
+
async function loadKey(ctx) {
|
|
14
|
+
const keysDir = resolve(stateRoot(ctx), 'keys');
|
|
15
|
+
const filename = resolve(keysDir, 'yimiaoban-continuation.key');
|
|
16
|
+
await mkdir(keysDir, { recursive: true, mode: 0o700 });
|
|
17
|
+
if (existsSync(filename))
|
|
18
|
+
return readFile(filename);
|
|
19
|
+
const key = randomBytes(32);
|
|
20
|
+
try {
|
|
21
|
+
await writeFile(filename, key, { flag: 'wx', mode: 0o600 });
|
|
22
|
+
if (process.platform !== 'win32')
|
|
23
|
+
await chmod(filename, 0o600).catch(() => undefined);
|
|
24
|
+
return key;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return readFile(filename);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function encode(value) {
|
|
31
|
+
return Buffer.from(JSON.stringify(value)).toString('base64url');
|
|
32
|
+
}
|
|
33
|
+
function signature(payload, key) {
|
|
34
|
+
return createHmac('sha256', key).update(payload).digest('base64url');
|
|
35
|
+
}
|
|
36
|
+
export function createYmbContinuationSigner(ctx) {
|
|
37
|
+
return {
|
|
38
|
+
async issue(payload, ttlMs = 10 * 60_000) {
|
|
39
|
+
const issuedAt = Date.now();
|
|
40
|
+
const body = encode({ ...payload, issuedAt, expiresAt: issuedAt + ttlMs });
|
|
41
|
+
const sig = signature(body, await loadKey(ctx));
|
|
42
|
+
return `${body}.${sig}`;
|
|
43
|
+
},
|
|
44
|
+
async verify(token, expectedOperation) {
|
|
45
|
+
if (typeof token !== 'string' || !token.includes('.')) {
|
|
46
|
+
throw ymbValidationError('continuation_invalid', 'continuation 无效');
|
|
47
|
+
}
|
|
48
|
+
const [body, provided] = token.split('.');
|
|
49
|
+
const expected = signature(body, await loadKey(ctx));
|
|
50
|
+
const valid = provided.length === expected.length
|
|
51
|
+
&& timingSafeEqual(Buffer.from(provided), Buffer.from(expected));
|
|
52
|
+
if (!valid)
|
|
53
|
+
throw ymbValidationError('continuation_invalid', 'continuation 签名校验失败');
|
|
54
|
+
let value;
|
|
55
|
+
try {
|
|
56
|
+
value = JSON.parse(Buffer.from(body, 'base64url').toString('utf8'));
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
throw ymbValidationError('continuation_invalid', 'continuation 内容无法解析');
|
|
60
|
+
}
|
|
61
|
+
if (value.operation !== expectedOperation) {
|
|
62
|
+
throw ymbValidationError('continuation_mismatch', 'continuation 不属于当前操作');
|
|
63
|
+
}
|
|
64
|
+
if (!Number.isSafeInteger(value.expiresAt) || value.expiresAt < Date.now()) {
|
|
65
|
+
throw ymbValidationError('continuation_expired', 'continuation 已过期');
|
|
66
|
+
}
|
|
67
|
+
return value;
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { CliError, ExitCode } from '../../core/errors.js';
|
|
2
|
+
export class YmbCliError extends CliError {
|
|
3
|
+
type;
|
|
4
|
+
subtype;
|
|
5
|
+
code;
|
|
6
|
+
retryable;
|
|
7
|
+
stage;
|
|
8
|
+
details;
|
|
9
|
+
partialData;
|
|
10
|
+
constructor(type, subtype, message, options = {}) {
|
|
11
|
+
super(message, options.exitCode ?? ExitCode.InternalError);
|
|
12
|
+
this.name = 'YmbCliError';
|
|
13
|
+
this.type = type;
|
|
14
|
+
this.subtype = subtype;
|
|
15
|
+
this.code = options.code;
|
|
16
|
+
this.retryable = options.retryable ?? false;
|
|
17
|
+
this.stage = options.stage;
|
|
18
|
+
this.details = options.details;
|
|
19
|
+
this.partialData = options.partialData;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export function ymbValidationError(subtype, message, details) {
|
|
23
|
+
return new YmbCliError('validation', subtype, message, {
|
|
24
|
+
details,
|
|
25
|
+
exitCode: ExitCode.UsageError,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
export function ymbAuthError(subtype, message, details) {
|
|
29
|
+
return new YmbCliError('authentication', subtype, message, {
|
|
30
|
+
details,
|
|
31
|
+
exitCode: ExitCode.AuthError,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
export function ymbNetworkError(subtype, message, options = {}) {
|
|
35
|
+
return new YmbCliError('network', subtype, message, {
|
|
36
|
+
...options,
|
|
37
|
+
retryable: options.retryable ?? true,
|
|
38
|
+
exitCode: ExitCode.NetworkError,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
export function ymbConfirmationError(action, details) {
|
|
42
|
+
return new YmbCliError('confirmation', 'required', '该操作需要明确确认', {
|
|
43
|
+
details: { action, ...details },
|
|
44
|
+
exitCode: ExitCode.ConfirmationRequired,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
export function ymbPartialError(message, partialData, details) {
|
|
48
|
+
return new YmbCliError('partial', 'write_uncertain', message, {
|
|
49
|
+
details,
|
|
50
|
+
partialData,
|
|
51
|
+
exitCode: ExitCode.Partial,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
/** 业务失败(HTTP 200 但接口 code 非 0 / actionMsg.code 非 0 等)。 */
|
|
55
|
+
export function ymbBusinessError(subtype, message, options = {}) {
|
|
56
|
+
return new YmbCliError('api', subtype, message, {
|
|
57
|
+
...options,
|
|
58
|
+
code: options.code,
|
|
59
|
+
exitCode: 1,
|
|
60
|
+
});
|
|
61
|
+
}
|