weaver-work-cli 0.1.5 → 0.1.7
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 +14 -9
- package/dist/cmd/skills/index.js +60 -0
- package/dist/internal/e10/auth/commands.js +48 -32
- package/dist/internal/e10/auth/session.js +44 -3
- package/dist/internal/e10/auth/xiaoe.js +363 -11
- package/dist/internal/e10/context.js +4 -2
- package/dist/internal/skills/detector.js +42 -0
- package/dist/internal/skills/install.js +50 -3
- package/dist/shortcuts/archive/render/search-format.js +12 -3
- package/dist/shortcuts/ebuilder-form/continuation.js +70 -0
- package/dist/shortcuts/ebuilder-form/errors.js +54 -0
- package/dist/shortcuts/ebuilder-form/host.js +1238 -0
- package/dist/shortcuts/ebuilder-form/index.js +108 -0
- package/dist/shortcuts/ebuilder-form/manifest.js +347 -0
- package/dist/shortcuts/ebuilder-form/operations/read.js +157 -0
- package/dist/shortcuts/ebuilder-form/operations/registry.js +56 -0
- package/dist/shortcuts/ebuilder-form/operations/shared.js +132 -0
- package/dist/shortcuts/ebuilder-form/operations/types.js +19 -0
- package/dist/shortcuts/ebuilder-form/operations/write.js +277 -0
- package/dist/shortcuts/ebuilder-form/operations.js +8 -0
- package/dist/shortcuts/ehr/operations/salary.js +2 -140
- package/dist/shortcuts/im/continuation.js +70 -0
- package/dist/shortcuts/{yimiaoban → im}/errors.js +14 -14
- package/dist/shortcuts/{yimiaoban → im}/host.js +26 -26
- package/dist/shortcuts/{yimiaoban → im}/index.js +42 -42
- package/dist/shortcuts/{yimiaoban → im}/manifest.js +7 -6
- package/dist/shortcuts/{yimiaoban → im}/operations/file-user.js +54 -41
- package/dist/shortcuts/{yimiaoban → im}/operations/group-read.js +46 -44
- package/dist/shortcuts/{yimiaoban → im}/operations/group-write.js +113 -66
- package/dist/shortcuts/{yimiaoban → im}/operations/msg-sync.js +164 -141
- package/dist/shortcuts/{yimiaoban → im}/operations/msg-write.js +47 -47
- package/dist/shortcuts/{yimiaoban → im}/operations/person.js +10 -10
- package/dist/shortcuts/{yimiaoban → im}/operations/registry.js +10 -8
- package/dist/shortcuts/{yimiaoban → im}/operations/session.js +82 -83
- package/dist/shortcuts/im/operations/shared.js +442 -0
- package/dist/shortcuts/{yimiaoban → im}/operations/types.js +1 -1
- package/dist/shortcuts/im/operations/user-remind.js +201 -0
- package/dist/shortcuts/{yimiaoban → im}/operations/write-util.js +7 -7
- package/dist/shortcuts/{yimiaoban → im}/operations.js +3 -3
- package/dist/shortcuts/index.js +10 -2
- package/dist/shortcuts/invoice/manifest.js +113 -10
- package/dist/shortcuts/invoice/operations/import.js +39 -14
- package/dist/shortcuts/invoice/operations/reim.js +38 -18
- package/dist/shortcuts/invoice/operations/shared.js +35 -6
- package/dist/shortcuts/jiuchuanhui/manifest.js +3 -3
- package/dist/shortcuts/jiuchuanhui/operations/registry.js +2 -2
- package/dist/shortcuts/{yimiaoban → okr}/continuation.js +10 -10
- package/dist/shortcuts/okr/errors.js +70 -0
- package/dist/shortcuts/okr/host.js +169 -0
- package/dist/shortcuts/okr/index.js +155 -0
- package/dist/shortcuts/okr/manifest.js +70 -0
- package/dist/shortcuts/okr/operations/link.js +305 -0
- package/dist/shortcuts/okr/operations/read.js +481 -0
- package/dist/shortcuts/okr/operations/registry.js +31 -0
- package/dist/shortcuts/okr/operations/shared.js +381 -0
- package/dist/shortcuts/okr/operations/types.js +88 -0
- package/dist/shortcuts/okr/operations/write.js +838 -0
- package/dist/shortcuts/okr/operations.js +8 -0
- package/dist/shortcuts/project/continuation.js +70 -0
- package/dist/shortcuts/project/errors.js +53 -0
- package/dist/shortcuts/project/host.js +160 -0
- package/dist/shortcuts/project/index.js +106 -0
- package/dist/shortcuts/project/manifest.js +36 -0
- package/dist/shortcuts/project/operations/read.js +551 -0
- package/dist/shortcuts/project/operations/registry.js +31 -0
- package/dist/shortcuts/project/operations/shared.js +68 -0
- package/dist/shortcuts/project/operations/types.js +39 -0
- package/dist/shortcuts/project/operations/write.js +266 -0
- package/dist/shortcuts/project/operations.js +8 -0
- package/dist/shortcuts/workflow/continuation.js +508 -0
- package/dist/shortcuts/workflow/endpoints.js +149 -0
- package/dist/shortcuts/workflow/errors.js +112 -0
- package/dist/shortcuts/workflow/host.js +224 -0
- package/dist/shortcuts/workflow/index.js +116 -0
- package/dist/shortcuts/workflow/manifest.js +30 -0
- package/dist/shortcuts/workflow/operations/form.js +4159 -0
- package/dist/shortcuts/workflow/operations/read.js +3778 -0
- package/dist/shortcuts/workflow/operations/registry.js +33 -0
- package/dist/shortcuts/workflow/operations/shared.js +167 -0
- package/dist/shortcuts/workflow/operations/types.js +44 -0
- package/dist/shortcuts/workflow/operations/write.js +1991 -0
- package/dist/shortcuts/workflow/operations.js +119 -0
- package/dist/shortcuts/workflow/state.js +182 -0
- package/docs/SKILL.md +5 -3
- package/docs/_catalog.md +10 -1
- package/docs/agent-invoice.md +2 -2
- package/docs/agent-skill-install.md +2 -2
- package/docs/e10-auth.md +3 -1
- package/docs/ebuilder-form.md +38 -0
- package/docs/im.md +104 -0
- package/docs/invoice.md +5 -5
- package/docs/okr.md +122 -0
- package/docs/operation-manual.md +10 -7
- package/docs/project.md +69 -0
- package/docs/skill-review-2026-09-19.md +68 -0
- package/package.json +3 -2
- package/scripts/package-skill.mjs +38 -11
- package/scripts/sync-connector-skills.mjs +172 -0
- package/skill-template/business-info.json +357 -16
- package/skill-template/domains/ebuilder-form.md +5 -0
- package/skill-template/domains/okr.md +18 -0
- package/skill-template/domains/shijingran.md +3 -0
- package/skill-template/domains/skill-maker.md +4 -4
- package/skill-template/domains/workflow.md +22 -0
- package/skill-template/domains/yepiaotong.md +4 -2
- package/skill-template/domains/yimiaoban.md +13 -10
- package/skill-template/product.json +5 -0
- package/skill-template/skill-template.md +1 -0
- package/skills/weaver-e10-calendar/SKILL.md +5 -1
- package/skills/weaver-e10-calendar/references/calendar-query.md +7 -0
- package/skills/weaver-e10-calendar/references/source-manifest.json +314 -32
- package/skills/weaver-e10-ebuilder-form/SKILL.md +123 -0
- package/skills/weaver-e10-ebuilder-form/references/context-routing.md +90 -0
- package/skills/weaver-e10-ebuilder-form/references/custom-api.md +86 -0
- package/skills/weaver-e10-ebuilder-form/references/disabled-capabilities.md +50 -0
- package/skills/weaver-e10-ebuilder-form/references/fields-browser.md +100 -0
- package/skills/weaver-e10-ebuilder-form/references/list-nlist.md +91 -0
- package/skills/weaver-e10-ebuilder-form/references/openapi-read.md +74 -0
- package/skills/weaver-e10-ebuilder-form/references/openapi-write.md +86 -0
- package/skills/weaver-e10-ebuilder-form/references/source-manifest.json +107 -0
- package/skills/weaver-e10-esb/SKILL.md +1 -1
- package/skills/weaver-e10-esb/references/source-manifest.json +15 -14
- package/skills/weaver-e10-hrm/SKILL.md +1 -1
- package/skills/weaver-e10-hrm/references/source-manifest.json +357 -38
- package/skills/weaver-e10-jiuchuanhui/SKILL.md +22 -4
- package/skills/weaver-e10-jiuchuanhui/references/clue.md +4 -2
- package/skills/weaver-e10-jiuchuanhui/references/contact.md +1 -1
- package/skills/weaver-e10-jiuchuanhui/references/customer.md +3 -3
- package/skills/weaver-e10-jiuchuanhui/references/disabled-capabilities.md +1 -1
- package/skills/weaver-e10-jiuchuanhui/references/discovery-config.md +10 -10
- package/skills/weaver-e10-jiuchuanhui/references/general-helpers.md +3 -3
- package/skills/weaver-e10-jiuchuanhui/references/sale.md +7 -7
- package/skills/weaver-e10-jiuchuanhui/references/source-manifest.json +3 -3
- package/skills/weaver-e10-jucailin/SKILL.md +2 -2
- package/skills/weaver-e10-mail/SKILL.md +22 -6
- package/skills/weaver-e10-meeting/SKILL.md +22 -1
- package/skills/weaver-e10-okr/SKILL.md +139 -0
- package/skills/weaver-e10-okr/references/okr-align.md +59 -0
- package/skills/weaver-e10-okr/references/okr-keyresult.md +68 -0
- package/skills/weaver-e10-okr/references/okr-link-routing.md +67 -0
- package/skills/weaver-e10-okr/references/okr-query.md +105 -0
- package/skills/weaver-e10-okr/references/okr-write.md +94 -0
- package/skills/weaver-e10-okr/references/source-manifest.json +672 -0
- package/skills/weaver-e10-plan/SKILL.md +3 -3
- package/skills/weaver-e10-plan/references/plan-report-write.md +1 -1
- package/skills/weaver-e10-plan/references/source-manifest.json +20 -20
- package/skills/weaver-e10-qiyecheng/SKILL.md +21 -1
- package/skills/weaver-e10-qiyecheng/references/source-manifest.json +17 -17
- package/skills/weaver-e10-shared/SKILL.md +79 -11
- package/skills/weaver-e10-shared/references/e10-auth-and-session.md +41 -8
- package/skills/weaver-e10-shared/references/non-interactive-environment.md +78 -0
- package/skills/weaver-e10-shared/references/weaver-e10-installation.md +39 -0
- package/skills/weaver-e10-shijingran/SKILL.md +83 -0
- package/skills/weaver-e10-shijingran/references/field-resolution.md +105 -0
- package/skills/weaver-e10-shijingran/references/operations.md +154 -0
- package/skills/weaver-e10-shijingran/references/source-manifest.json +1380 -0
- package/skills/weaver-e10-shijingran/references/write-operations.md +90 -0
- package/skills/weaver-e10-skill-maker/SKILL.md +26 -9
- package/skills/weaver-e10-skill-maker/references/business-skill-generation.md +39 -8
- package/skills/weaver-e10-skill-maker/references/detector-validation.md +25 -8
- package/skills/weaver-e10-skill-maker/references/module-cli-generation.md +66 -5
- package/skills/weaver-e10-skill-maker/references/module-gap-audit-and-repair.md +86 -0
- package/skills/weaver-e10-skill-maker/references/source-update-detection.md +59 -5
- package/skills/weaver-e10-skill-maker/references/weaver-skill-style.md +16 -8
- package/skills/weaver-e10-wenshuding/SKILL.md +28 -4
- package/skills/weaver-e10-wenshuding/references/archive-search.md +14 -0
- package/skills/weaver-e10-wenshuding/references/source-manifest.json +27 -26
- package/skills/weaver-e10-workflow/SKILL.md +250 -0
- package/skills/weaver-e10-workflow/references/source-manifest.json +1062 -0
- package/skills/weaver-e10-workflow/references/workflow-agent-entry.md +96 -0
- package/skills/weaver-e10-workflow/references/workflow-batch.md +275 -0
- package/skills/weaver-e10-workflow/references/workflow-create.md +182 -0
- package/skills/weaver-e10-workflow/references/workflow-edit.md +106 -0
- package/skills/weaver-e10-workflow/references/workflow-memory.md +165 -0
- package/skills/weaver-e10-workflow/references/workflow-operate.md +153 -0
- package/skills/weaver-e10-workflow/references/workflow-query.md +821 -0
- package/skills/weaver-e10-workflow/references/workflow-share.md +117 -0
- package/skills/weaver-e10-workflow/references/workflow-view.md +189 -0
- package/skills/weaver-e10-yepiaotong/SKILL.md +28 -28
- package/skills/weaver-e10-yepiaotong/references/invoice-disabled-capabilities.md +11 -5
- package/skills/weaver-e10-yepiaotong/references/invoice-import.md +14 -8
- package/skills/weaver-e10-yepiaotong/references/invoice-issuing-examples.md +9 -43
- package/skills/weaver-e10-yepiaotong/references/invoice-issuing.md +8 -0
- package/skills/weaver-e10-yepiaotong/references/invoice-reim.md +13 -17
- package/skills/weaver-e10-yepiaotong/references/reim-api-reference.md +6 -7
- package/skills/weaver-e10-yepiaotong/references/reim-examples.md +2 -2
- package/skills/weaver-e10-yepiaotong/references/source-manifest.json +1155 -553
- package/skills/weaver-e10-yimiaoban/SKILL.md +56 -38
- package/skills/weaver-e10-yimiaoban/references/ding-write.md +8 -8
- package/skills/weaver-e10-yimiaoban/references/error-codes.md +16 -0
- package/skills/weaver-e10-yimiaoban/references/field-resolution.md +10 -10
- package/skills/weaver-e10-yimiaoban/references/file-user-i18n.md +12 -12
- package/skills/weaver-e10-yimiaoban/references/group-read.md +16 -16
- package/skills/weaver-e10-yimiaoban/references/group-write.md +17 -15
- package/skills/weaver-e10-yimiaoban/references/msg-read.md +21 -17
- package/skills/weaver-e10-yimiaoban/references/msg-write.md +10 -10
- package/skills/weaver-e10-yimiaoban/references/person.md +6 -6
- package/skills/weaver-e10-yimiaoban/references/safety-boundaries.md +15 -11
- package/skills/weaver-e10-yimiaoban/references/session-sysmsg.md +11 -10
- package/skills/weaver-e10-yimiaoban/references/source-manifest.json +2164 -154
- package/skills/weaver-e10-yimiaoban/references/user-remind.md +75 -0
- package/skills/weaver-e10-ziguanjia/SKILL.md +4 -2
- package/skills/weaver-e10-ziguanjia/references/source-manifest.json +99 -29
- package/tools/weaver-skill-detector-1.0.13/SKILL.md +253 -0
- package/tools/weaver-skill-detector-1.0.13/_meta.json +6 -0
- package/tools/weaver-skill-detector-1.0.13/references/checklist.md +298 -0
- package/tools/weaver-skill-detector-1.0.13/scripts/validate_skill.py +4190 -0
- package/dist/shortcuts/yimiaoban/operations/shared.js +0 -236
- package/docs/yimiaoban.md +0 -86
- package/skills/weaver-e10-calendar/product.json +0 -8
- package/skills/weaver-e10-esb/product.json +0 -8
- package/skills/weaver-e10-hrm/product.json +0 -8
- package/skills/weaver-e10-jiuchuanhui/product.json +0 -8
- package/skills/weaver-e10-jucailin/product.json +0 -8
- package/skills/weaver-e10-mail/product.json +0 -8
- package/skills/weaver-e10-meeting/product.json +0 -8
- package/skills/weaver-e10-plan/product.json +0 -8
- package/skills/weaver-e10-qiyecheng/product.json +0 -8
- package/skills/weaver-e10-skill-maker/product.json +0 -8
- package/skills/weaver-e10-wenshuding/product.json +0 -8
- package/skills/weaver-e10-yepiaotong/product.json +0 -8
- package/skills/weaver-e10-yepiaotong/references/invoice-red.md +0 -261
- package/skills/weaver-e10-yimiaoban/product.json +0 -8
- package/skills/weaver-e10-ziguanjia/product.json +0 -8
- /package/dist/shortcuts/{yimiaoban → im}/operations/msg-parse.js +0 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { CliError, ExitCode } from '../../core/errors.js';
|
|
2
|
+
export class OkrCliError 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 = 'OkrCliError';
|
|
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 okrValidationError(subtype, message, details) {
|
|
23
|
+
return new OkrCliError('validation', subtype, message, {
|
|
24
|
+
details,
|
|
25
|
+
exitCode: ExitCode.UsageError,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
export function okrAuthError(subtype, message, details) {
|
|
29
|
+
return new OkrCliError('authentication', subtype, message, {
|
|
30
|
+
details,
|
|
31
|
+
exitCode: ExitCode.AuthError,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
export function okrNetworkError(subtype, message, options = {}) {
|
|
35
|
+
return new OkrCliError('network', subtype, message, {
|
|
36
|
+
...options,
|
|
37
|
+
retryable: options.retryable ?? true,
|
|
38
|
+
exitCode: ExitCode.NetworkError,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
export function okrApiError(subtype, message, details) {
|
|
42
|
+
return new OkrCliError('api', subtype, message, {
|
|
43
|
+
details,
|
|
44
|
+
exitCode: ExitCode.GeneralError,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
export function okrConfirmationError(action, details) {
|
|
48
|
+
return new OkrCliError('confirmation', 'required', '该操作需要明确确认', {
|
|
49
|
+
details: { action, ...details },
|
|
50
|
+
exitCode: ExitCode.ConfirmationRequired,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/** 当前接口链路不支持该能力:源资料未提供该链路的实现依据,而不是输入错误。 */
|
|
54
|
+
export function okrUnsupportedError(subtype, message, details) {
|
|
55
|
+
return new OkrCliError('policy', subtype, message, {
|
|
56
|
+
details,
|
|
57
|
+
exitCode: ExitCode.PolicyError,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
export function okrPartialError(message, partialData, details) {
|
|
61
|
+
return new OkrCliError('partial', 'write_uncertain', message, {
|
|
62
|
+
details,
|
|
63
|
+
partialData,
|
|
64
|
+
exitCode: ExitCode.Partial,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
/** 按结构化错误的 type 判定类型(不依赖类实例,兼容包装过的错误对象)。 */
|
|
68
|
+
export function isOkrErrorType(error, type) {
|
|
69
|
+
return Boolean(error && typeof error === 'object' && error.type === type);
|
|
70
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { resolve } from 'node:path';
|
|
5
|
+
import { getActiveProfile } from '../../internal/e10/auth/session.js';
|
|
6
|
+
import { requireE10Session } from '../../internal/e10/context.js';
|
|
7
|
+
import { E10ApiError, createE10RequestRuntime } from '../../internal/e10/request-runtime.js';
|
|
8
|
+
import { okrStateRoot } from './continuation.js';
|
|
9
|
+
import { OkrCliError, okrAuthError, okrNetworkError, okrValidationError } from './errors.js';
|
|
10
|
+
function inferProfile(ctx) {
|
|
11
|
+
const options = ctx.getGlobalOptions();
|
|
12
|
+
return options.profile || getActiveProfile();
|
|
13
|
+
}
|
|
14
|
+
function cacheFilename(root, key) {
|
|
15
|
+
const digest = createHash('sha256').update(key).digest('hex').slice(0, 40);
|
|
16
|
+
return resolve(root, 'cache', 'okr', `${digest}.json`);
|
|
17
|
+
}
|
|
18
|
+
async function readCacheFile(root, key) {
|
|
19
|
+
const file = cacheFilename(root, key);
|
|
20
|
+
if (!existsSync(file))
|
|
21
|
+
return undefined;
|
|
22
|
+
try {
|
|
23
|
+
const parsed = JSON.parse(await readFile(file, 'utf8'));
|
|
24
|
+
if (parsed.key !== key)
|
|
25
|
+
return undefined;
|
|
26
|
+
if (typeof parsed.expiresAt === 'number' && parsed.expiresAt < Date.now())
|
|
27
|
+
return undefined;
|
|
28
|
+
return parsed.value;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async function writeCacheFile(root, key, value, ttlMs) {
|
|
35
|
+
const file = cacheFilename(root, key);
|
|
36
|
+
await mkdir(resolve(file, '..'), { recursive: true });
|
|
37
|
+
const payload = {
|
|
38
|
+
key,
|
|
39
|
+
value,
|
|
40
|
+
expiresAt: ttlMs === undefined ? null : Date.now() + ttlMs,
|
|
41
|
+
updatedAt: new Date().toISOString(),
|
|
42
|
+
};
|
|
43
|
+
await writeFile(file, `${JSON.stringify(payload)}\n`, 'utf8');
|
|
44
|
+
}
|
|
45
|
+
/** 统一业务错误消息提取。 */
|
|
46
|
+
export function businessMessage(value) {
|
|
47
|
+
if (!value || typeof value !== 'object')
|
|
48
|
+
return '泛微接口返回业务失败';
|
|
49
|
+
const candidates = [
|
|
50
|
+
value.msg,
|
|
51
|
+
value.message,
|
|
52
|
+
value.resultMsg,
|
|
53
|
+
value.datajson?.message,
|
|
54
|
+
value.datajson?.msg,
|
|
55
|
+
];
|
|
56
|
+
for (const item of candidates) {
|
|
57
|
+
if (typeof item === 'string' && item.trim())
|
|
58
|
+
return item;
|
|
59
|
+
}
|
|
60
|
+
return '泛微接口返回业务失败';
|
|
61
|
+
}
|
|
62
|
+
/** ebuilder datajson 结构成功判定:`datajson.status === true`(兼容字符串 "true")。 */
|
|
63
|
+
export function isDatajsonSuccess(value) {
|
|
64
|
+
const status = value?.datajson?.status;
|
|
65
|
+
return status === true || status === 'true';
|
|
66
|
+
}
|
|
67
|
+
/** 标准版标记型成功判定:`status` 为 "1" / 1 / true。 */
|
|
68
|
+
export function isFlagSuccess(value) {
|
|
69
|
+
const status = value?.status;
|
|
70
|
+
return status === '1' || status === 1 || status === true;
|
|
71
|
+
}
|
|
72
|
+
function assertPath(requestPath) {
|
|
73
|
+
if (typeof requestPath !== 'string' || !requestPath.startsWith('/') || requestPath.startsWith('//')) {
|
|
74
|
+
throw okrValidationError('path_invalid', '请求路径必须是相对绝对路径');
|
|
75
|
+
}
|
|
76
|
+
if (/^https?:/i.test(requestPath) || /[\r\n]/.test(requestPath) || requestPath.includes('/../')) {
|
|
77
|
+
throw okrValidationError('path_invalid', '请求路径不允许跨域或路径逃逸');
|
|
78
|
+
}
|
|
79
|
+
if (!requestPath.startsWith('/api/')) {
|
|
80
|
+
throw okrValidationError('path_not_allowed', 'OKR 目标模块只能调用 /api/ 路径');
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function omitEmptyQueryValues(query) {
|
|
84
|
+
if (!query)
|
|
85
|
+
return undefined;
|
|
86
|
+
return Object.fromEntries(Object.entries(query).filter(([, value]) => value !== '' && value !== undefined && value !== null));
|
|
87
|
+
}
|
|
88
|
+
function mapRuntimeNetworkError(error) {
|
|
89
|
+
if (error instanceof E10ApiError && error.type === 'network') {
|
|
90
|
+
throw okrNetworkError(error.subtype, error.message, {
|
|
91
|
+
retryable: error.retryable,
|
|
92
|
+
details: error.details,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
throw error;
|
|
96
|
+
}
|
|
97
|
+
export function createOkrHost(ctx, dependencies = {}) {
|
|
98
|
+
let e10Session = null;
|
|
99
|
+
let sessionContext = null;
|
|
100
|
+
const requireSession = dependencies.requireSession || requireE10Session;
|
|
101
|
+
const cacheRoot = okrStateRoot(ctx);
|
|
102
|
+
async function ensureSession() {
|
|
103
|
+
if (!e10Session) {
|
|
104
|
+
e10Session = await requireSession(ctx);
|
|
105
|
+
sessionContext = {
|
|
106
|
+
baseUrl: e10Session.getBaseUrl(),
|
|
107
|
+
profile: inferProfile(ctx),
|
|
108
|
+
userId: e10Session.userId,
|
|
109
|
+
tenantKey: e10Session.tenantKey,
|
|
110
|
+
eteamsId: e10Session.eteamsId,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
return e10Session;
|
|
114
|
+
}
|
|
115
|
+
async function getSession() {
|
|
116
|
+
await ensureSession();
|
|
117
|
+
return sessionContext;
|
|
118
|
+
}
|
|
119
|
+
async function runtime() {
|
|
120
|
+
return createE10RequestRuntime(ctx, await ensureSession(), {
|
|
121
|
+
interceptors: dependencies.interceptors,
|
|
122
|
+
fetch: dependencies.fetch,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
async function request(request) {
|
|
126
|
+
const { method, path, query, body, operation, timeoutMs = 30_000 } = request;
|
|
127
|
+
assertPath(path);
|
|
128
|
+
let response;
|
|
129
|
+
try {
|
|
130
|
+
response = await (await runtime()).requestJson({
|
|
131
|
+
module: 'okr',
|
|
132
|
+
operation,
|
|
133
|
+
method: (method || 'POST').toUpperCase(),
|
|
134
|
+
path,
|
|
135
|
+
query: omitEmptyQueryValues(query),
|
|
136
|
+
body,
|
|
137
|
+
headers: { 'X-Requested-With': 'XMLHttpRequest' },
|
|
138
|
+
timeoutMs,
|
|
139
|
+
bigIntStrings: true,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
mapRuntimeNetworkError(error);
|
|
144
|
+
}
|
|
145
|
+
if (response.status === 401 || response.status === 302) {
|
|
146
|
+
throw okrAuthError('session_expired', 'E10 登录态已失效,请先向用户确认 E10 登录域名,再运行 weaver-work-cli auth login --base-url E10_DOMAIN --agent_type AGENT_NAME 完成登录', { httpStatus: response.status });
|
|
147
|
+
}
|
|
148
|
+
if (response.parseError) {
|
|
149
|
+
throw okrNetworkError('protocol', `接口返回不是合法 JSON(HTTP ${response.status})`, { retryable: false });
|
|
150
|
+
}
|
|
151
|
+
if (response.status === 404) {
|
|
152
|
+
throw new OkrCliError('api', 'endpoint_not_found', `接口返回 404,当前环境可能未安装「OKR 目标」相关应用或未部署该接口(${path})`, { code: response.status, details: { path }, exitCode: 1 });
|
|
153
|
+
}
|
|
154
|
+
if (response.status < 200 || response.status >= 300) {
|
|
155
|
+
throw new OkrCliError('api', 'http_error', `泛微接口 HTTP ${response.status}`, {
|
|
156
|
+
code: response.status,
|
|
157
|
+
details: { status: response.status, message: businessMessage(response.value) },
|
|
158
|
+
exitCode: 1,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
return response.value;
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
request,
|
|
165
|
+
getSession,
|
|
166
|
+
getCache: (key) => readCacheFile(cacheRoot, key),
|
|
167
|
+
setCache: (key, value, ttlMs) => writeCacheFile(cacheRoot, key, value, ttlMs),
|
|
168
|
+
};
|
|
169
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { ExitCode, isCliError, toErrorEnvelope } from '../../core/errors.js';
|
|
3
|
+
import { createOkrContinuationSigner } from './continuation.js';
|
|
4
|
+
import { okrValidationError } from './errors.js';
|
|
5
|
+
import { createOkrHost } from './host.js';
|
|
6
|
+
import { okrManifest } from './manifest.js';
|
|
7
|
+
import { executeOkrOperation } from './operations.js';
|
|
8
|
+
function asOptions(value) {
|
|
9
|
+
return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
10
|
+
}
|
|
11
|
+
async function readStdin() {
|
|
12
|
+
const chunks = [];
|
|
13
|
+
for await (const chunk of process.stdin) {
|
|
14
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
15
|
+
}
|
|
16
|
+
return Buffer.concat(chunks).toString('utf8');
|
|
17
|
+
}
|
|
18
|
+
function stripBom(value) {
|
|
19
|
+
return value.replace(/^\uFEFF+/, '');
|
|
20
|
+
}
|
|
21
|
+
export async function inputFrom(options, extra = {}) {
|
|
22
|
+
const opts = asOptions(options);
|
|
23
|
+
let input = {};
|
|
24
|
+
if (opts.input && opts.inputJson) {
|
|
25
|
+
throw okrValidationError('input_conflict', '不能同时传 --input 和 --input-json');
|
|
26
|
+
}
|
|
27
|
+
if (opts.inputJson) {
|
|
28
|
+
input = parseJsonInput(String(opts.inputJson), '--input-json');
|
|
29
|
+
}
|
|
30
|
+
if (opts.input) {
|
|
31
|
+
let raw;
|
|
32
|
+
try {
|
|
33
|
+
raw = stripBom(opts.input === '-' ? await readStdin() : await readFile(String(opts.input), 'utf8'));
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
throw okrValidationError('input_unreadable', `无法读取 --input:${String(opts.input)}`);
|
|
37
|
+
}
|
|
38
|
+
input = parseJsonInput(raw, '--input');
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
...input,
|
|
42
|
+
...Object.fromEntries(Object.entries(extra).filter(([, value]) => value !== undefined && value !== '')),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function parseJsonInput(raw, label) {
|
|
46
|
+
try {
|
|
47
|
+
const parsed = JSON.parse(stripBom(raw));
|
|
48
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
49
|
+
throw new Error('input must be an object');
|
|
50
|
+
}
|
|
51
|
+
return parsed;
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
throw okrValidationError('input_invalid', `${label} 不是合法 JSON 对象`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export function successEnvelope(operation, result) {
|
|
58
|
+
return {
|
|
59
|
+
schemaVersion: 1,
|
|
60
|
+
ok: true,
|
|
61
|
+
operation,
|
|
62
|
+
data: result.data === undefined ? null : result.data,
|
|
63
|
+
...(result.meta ? { meta: result.meta } : {}),
|
|
64
|
+
warnings: [],
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function failureEnvelope(error) {
|
|
68
|
+
const structured = error;
|
|
69
|
+
return {
|
|
70
|
+
schemaVersion: 1,
|
|
71
|
+
ok: false,
|
|
72
|
+
...(structured?.partialData === undefined ? {} : { data: structured.partialData }),
|
|
73
|
+
error: toErrorEnvelope(error),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
async function executeAndPrint(ctx, operation, input) {
|
|
77
|
+
try {
|
|
78
|
+
const result = await executeOkrOperation(operation, input, {
|
|
79
|
+
ctx,
|
|
80
|
+
host: createOkrHost(ctx),
|
|
81
|
+
continuation: createOkrContinuationSigner(ctx),
|
|
82
|
+
});
|
|
83
|
+
ctx.output.write(successEnvelope(operation, result), { json: true });
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
console.error(JSON.stringify(failureEnvelope(error), null, 2));
|
|
87
|
+
process.exitCode = isCliError(error) ? error.exitCode : ExitCode.GeneralError;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
export const okrShortcut = {
|
|
91
|
+
name: 'okr',
|
|
92
|
+
description: '泛微E10 OKR 目标管理 CLI(目标查询/新建/编辑、关键成果、目标对齐、评论)',
|
|
93
|
+
category: 'shortcut',
|
|
94
|
+
register({ program, action }) {
|
|
95
|
+
const okr = program.command('okr').description('泛微E10 OKR 目标管理(目标列表/详情/新建/编辑、关键成果、目标对齐、评论、链路判定)');
|
|
96
|
+
okr.command('schema')
|
|
97
|
+
.description('Print okr operation manifest for agents')
|
|
98
|
+
.action(action((ctx) => {
|
|
99
|
+
ctx.output.write(okrManifest, { json: true });
|
|
100
|
+
}));
|
|
101
|
+
okr.command('run')
|
|
102
|
+
.description('Run an okr operation with JSON input')
|
|
103
|
+
.argument('<operation>', 'operation name, e.g. okr.list')
|
|
104
|
+
.option('--input <path>', 'JSON input file, or - for stdin')
|
|
105
|
+
.option('--input-json <json>', 'inline JSON input object')
|
|
106
|
+
.action(action(async (ctx, operation, options) => {
|
|
107
|
+
await executeAndPrint(ctx, String(operation), await inputFrom(options));
|
|
108
|
+
}));
|
|
109
|
+
// 高频便捷命令
|
|
110
|
+
okr.command('route')
|
|
111
|
+
.description('判定当前租户 OKR 接口链路(okr.version.check 薄包装)')
|
|
112
|
+
.option('--refresh', '忽略 1 天租户缓存,强制重新判定')
|
|
113
|
+
.action(action(async (ctx, options) => {
|
|
114
|
+
const opts = asOptions(options);
|
|
115
|
+
await executeAndPrint(ctx, 'okr.version.check', opts.refresh ? { refresh: true } : {});
|
|
116
|
+
}));
|
|
117
|
+
okr.command('get')
|
|
118
|
+
.description('查询单个目标详情(okr.get 薄包装)')
|
|
119
|
+
.option('--input <path>', 'JSON input file, or - for stdin')
|
|
120
|
+
.option('--input-json <json>', 'inline JSON input object')
|
|
121
|
+
.option('--id <id>', '目标 ID')
|
|
122
|
+
.action(action(async (ctx, options) => {
|
|
123
|
+
const opts = asOptions(options);
|
|
124
|
+
await executeAndPrint(ctx, 'okr.get', await inputFrom(options, { id: opts.id }));
|
|
125
|
+
}));
|
|
126
|
+
okr.command('list')
|
|
127
|
+
.description('分页查询目标列表(okr.list 薄包装)')
|
|
128
|
+
.option('--input <path>', 'JSON input file, or - for stdin')
|
|
129
|
+
.option('--input-json <json>', 'inline JSON input object')
|
|
130
|
+
.option('--period-type <type>', '周期类型:1-年度 2-季度 3-月度 4-自定义')
|
|
131
|
+
.option('--period-range <range>', '周期范围:年度/季度为 yyyy,月度为 yyyy-MM,自定义为「开始日期,结束日期」')
|
|
132
|
+
.option('--period-quarter <quarter>', '季度值 1-4')
|
|
133
|
+
.option('--page-no <n>', '页码')
|
|
134
|
+
.option('--page-size <n>', '每页条数')
|
|
135
|
+
.action(action(async (ctx, options) => {
|
|
136
|
+
const opts = asOptions(options);
|
|
137
|
+
await executeAndPrint(ctx, 'okr.list', await inputFrom(options, {
|
|
138
|
+
period_type: opts.periodType,
|
|
139
|
+
period_range: opts.periodRange,
|
|
140
|
+
period_quarter: opts.periodQuarter,
|
|
141
|
+
page_no: opts.pageNo,
|
|
142
|
+
page_size: opts.pageSize,
|
|
143
|
+
}));
|
|
144
|
+
}));
|
|
145
|
+
okr.command('viewlink')
|
|
146
|
+
.description('为已有目标 ID 生成详情页链接(okr.viewlink 薄包装)')
|
|
147
|
+
.option('--input <path>', 'JSON input file, or - for stdin')
|
|
148
|
+
.option('--input-json <json>', 'inline JSON input object')
|
|
149
|
+
.option('--ids <ids>', '目标 ID,英文逗号拼接')
|
|
150
|
+
.action(action(async (ctx, options) => {
|
|
151
|
+
const opts = asOptions(options);
|
|
152
|
+
await executeAndPrint(ctx, 'okr.viewlink', await inputFrom(options, { ids: opts.ids }));
|
|
153
|
+
}));
|
|
154
|
+
},
|
|
155
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { okrOperationDefinitions } from './operations/registry.js';
|
|
2
|
+
/**
|
|
3
|
+
* 禁用边界:源资料未提供以下能力的实现依据,因此不生成对应 operation,或只能限定在某一链路。
|
|
4
|
+
* - e10 标准版没有独立的关键成果分页接口:关键成果随目标列表/详情返回。
|
|
5
|
+
* - ebuilder 版没有目标评论接口:评论能力只在标准版链路可用。
|
|
6
|
+
* - e10 标准版没有「添加目标对齐」接口:目标对齐只在 ebuilder 链路可用。
|
|
7
|
+
*/
|
|
8
|
+
const WITHHELD = [
|
|
9
|
+
{
|
|
10
|
+
name: 'okr.kr.list.standard',
|
|
11
|
+
reason: 'e10 标准版源资料未提供独立的关键成果分页接口,关键成果通过 okr.list 的 goals[].keyresultList 或 okr.get 的 goal.keyresultList 获取;okr.kr.list 仅支持 ebuilder 链路',
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
name: 'okr.comment.list.eb',
|
|
15
|
+
reason: 'ebuilder 版源资料未提供目标评论接口,okr.comment.list 仅支持 e10 标准版链路',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
name: 'okr.align.create.standard',
|
|
19
|
+
reason: 'e10 标准版源资料未提供「添加目标对齐」接口,okr.align.create.* 仅支持 ebuilder 链路',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: 'okr.eb.kr.delete',
|
|
23
|
+
reason: '源资料只提供关键成果新建与编辑接口,没有删除关键成果接口,故不生成删除 operation',
|
|
24
|
+
},
|
|
25
|
+
];
|
|
26
|
+
function buildOperations() {
|
|
27
|
+
return okrOperationDefinitions().map((definition) => ({
|
|
28
|
+
name: definition.name,
|
|
29
|
+
resource: definition.resource,
|
|
30
|
+
summary: definition.summary,
|
|
31
|
+
...(definition.capability ? { capability: definition.capability } : {}),
|
|
32
|
+
risk: definition.risk,
|
|
33
|
+
...(definition.requiresConfirmation ? { requiresConfirmation: true } : {}),
|
|
34
|
+
inputSchema: definition.inputSchema,
|
|
35
|
+
}));
|
|
36
|
+
}
|
|
37
|
+
export const okrManifest = {
|
|
38
|
+
schemaVersion: 1,
|
|
39
|
+
name: 'okr',
|
|
40
|
+
title: '泛微E10 OKR 目标管理(目标查询/新建/编辑、关键成果、目标对齐、评论)',
|
|
41
|
+
version: '0.1.0',
|
|
42
|
+
host: 'e10-okr',
|
|
43
|
+
status: 'builtin',
|
|
44
|
+
sourceSkill: 'weaver-e10-okr',
|
|
45
|
+
capabilities: ['okr'],
|
|
46
|
+
/**
|
|
47
|
+
* OKR 目标接口链路路由:先读租户隔离、有效期 1 天的版本判定缓存,未命中才调用 okr_searchAppver
|
|
48
|
+
* 读取 ver_name;`ver_name >= V2` 走 e10-ebuilder 链路,其它情况走 e10 标准版链路。
|
|
49
|
+
* ebuilder 链路命中后严禁混用标准版接口,反之亦然。
|
|
50
|
+
*/
|
|
51
|
+
linkRouting: {
|
|
52
|
+
versionInterface: 'okr_searchAppver',
|
|
53
|
+
versionField: 'ver_name',
|
|
54
|
+
ebWhen: '>=V2',
|
|
55
|
+
cacheKeyPrefix: 'okr:version:',
|
|
56
|
+
cacheTtlSeconds: 86400,
|
|
57
|
+
ebAppTag: 'weaver-wr-goal-eb',
|
|
58
|
+
ebFormTag: 'uf_wrgm_baseinfo',
|
|
59
|
+
ebFormIdFixedTenants: { tqasclatif: '1027322801024458753' },
|
|
60
|
+
ebFormIdCache: 'permanent-per-tenant',
|
|
61
|
+
standardDetailUrl: '/sp/goal/openDetail?id={goalId}',
|
|
62
|
+
ebDetailUrl: '/sp/ebdfpage/card/0/{okr目标详情eb表单id}/{goalId}',
|
|
63
|
+
},
|
|
64
|
+
/** 源资料强制规则:分页目标列表固定列,目标名称可点击跳转详情。 */
|
|
65
|
+
goalTableColumns: ['目标名称', '目标责任人名称', '目标周期类型', '目标周期范围'],
|
|
66
|
+
/** 源资料强制规则:人员 ID 必须先由 weaver-e10-hrm skill 解析,禁止直接传人员名称,禁止取 userId 字段。 */
|
|
67
|
+
personIdSource: 'weaver-e10-hrm',
|
|
68
|
+
operations: buildOperations(),
|
|
69
|
+
withheldOperations: WITHHELD,
|
|
70
|
+
};
|