weaver-work-cli 0.1.6 → 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 +44 -31
- 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,8 @@
|
|
|
1
|
+
import { getOkrOperation } from './operations/registry.js';
|
|
2
|
+
import { assertNoForbiddenKeys, asObject } from './operations/shared.js';
|
|
3
|
+
export async function executeOkrOperation(operation, rawInput, context) {
|
|
4
|
+
const definition = getOkrOperation(operation);
|
|
5
|
+
const input = asObject(rawInput);
|
|
6
|
+
assertNoForbiddenKeys(input);
|
|
7
|
+
return definition.handler(input, context);
|
|
8
|
+
}
|
|
@@ -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 { projectValidationError } from './errors.js';
|
|
7
|
+
export function projectStateRoot(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(projectStateRoot(ctx), 'keys');
|
|
15
|
+
const filename = resolve(keysDir, 'project-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 createProjectContinuationSigner(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 projectValidationError('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 projectValidationError('continuation_invalid', 'continuation 签名校验失败');
|
|
54
|
+
let value;
|
|
55
|
+
try {
|
|
56
|
+
value = JSON.parse(Buffer.from(body, 'base64url').toString('utf8'));
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
throw projectValidationError('continuation_invalid', 'continuation 内容无法解析');
|
|
60
|
+
}
|
|
61
|
+
if (value.operation !== expectedOperation) {
|
|
62
|
+
throw projectValidationError('continuation_mismatch', 'continuation 不属于当前操作');
|
|
63
|
+
}
|
|
64
|
+
if (!Number.isSafeInteger(value.expiresAt) || value.expiresAt < Date.now()) {
|
|
65
|
+
throw projectValidationError('continuation_expired', 'continuation 已过期');
|
|
66
|
+
}
|
|
67
|
+
return value;
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { CliError, ExitCode } from '../../core/errors.js';
|
|
2
|
+
export class ProjectCliError 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 = 'ProjectCliError';
|
|
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 projectValidationError(subtype, message, details) {
|
|
23
|
+
return new ProjectCliError('validation', subtype, message, {
|
|
24
|
+
details,
|
|
25
|
+
exitCode: ExitCode.UsageError,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
export function projectAuthError(subtype, message, details) {
|
|
29
|
+
return new ProjectCliError('authentication', subtype, message, {
|
|
30
|
+
details,
|
|
31
|
+
exitCode: ExitCode.AuthError,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
export function projectNetworkError(subtype, message, options = {}) {
|
|
35
|
+
return new ProjectCliError('network', subtype, message, {
|
|
36
|
+
...options,
|
|
37
|
+
retryable: options.retryable ?? true,
|
|
38
|
+
exitCode: ExitCode.NetworkError,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
export function projectApiError(subtype, message, details) {
|
|
42
|
+
return new ProjectCliError('api', subtype, message, {
|
|
43
|
+
details,
|
|
44
|
+
exitCode: ExitCode.GeneralError,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
export function projectPartialError(message, partialData, details) {
|
|
48
|
+
return new ProjectCliError('partial', 'write_uncertain', message, {
|
|
49
|
+
details,
|
|
50
|
+
partialData,
|
|
51
|
+
exitCode: ExitCode.Partial,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { getActiveProfile } from '../../internal/e10/auth/session.js';
|
|
2
|
+
import { requireE10Session } from '../../internal/e10/context.js';
|
|
3
|
+
import { E10ApiError, createE10RequestRuntime } from '../../internal/e10/request-runtime.js';
|
|
4
|
+
import { ProjectCliError, projectApiError, projectAuthError, projectNetworkError, projectValidationError, } from './errors.js';
|
|
5
|
+
const PROJECT_PATH_PREFIXES = [
|
|
6
|
+
'/api/ebuilder/form/formdata/v2/',
|
|
7
|
+
'/api/ebuilder/form/dataset/v2/',
|
|
8
|
+
'/api/ebuilder/common/ds/',
|
|
9
|
+
'/api/ebproject/backendConsole/',
|
|
10
|
+
'/api/bs/ebuilder/app/tags/',
|
|
11
|
+
'/api/hrm/common/browser/',
|
|
12
|
+
'/api/installer/public/',
|
|
13
|
+
];
|
|
14
|
+
function inferProfile(ctx) {
|
|
15
|
+
const options = ctx.getGlobalOptions();
|
|
16
|
+
return options.profile || getActiveProfile();
|
|
17
|
+
}
|
|
18
|
+
export function businessMessage(value) {
|
|
19
|
+
if (!value || typeof value !== 'object')
|
|
20
|
+
return '泛微接口返回业务失败';
|
|
21
|
+
const candidates = [
|
|
22
|
+
value.msg,
|
|
23
|
+
value.message,
|
|
24
|
+
value.resultMsg,
|
|
25
|
+
value.datajson?.message,
|
|
26
|
+
value.datajson?.msg,
|
|
27
|
+
value.data?.message,
|
|
28
|
+
];
|
|
29
|
+
for (const item of candidates) {
|
|
30
|
+
if (typeof item === 'string' && item.trim())
|
|
31
|
+
return item;
|
|
32
|
+
}
|
|
33
|
+
return '泛微接口返回业务失败';
|
|
34
|
+
}
|
|
35
|
+
export function isDatajsonSuccess(value) {
|
|
36
|
+
const status = value?.datajson?.status;
|
|
37
|
+
return status === true || status === 'true';
|
|
38
|
+
}
|
|
39
|
+
export function isWeaverSuccess(value) {
|
|
40
|
+
if (!value || typeof value !== 'object')
|
|
41
|
+
return false;
|
|
42
|
+
return Number(value.code) === 200 && value.fail !== true && value.status !== false && value.status !== 'false';
|
|
43
|
+
}
|
|
44
|
+
export function assertDatajsonSuccess(value, operation) {
|
|
45
|
+
if (!isDatajsonSuccess(value)) {
|
|
46
|
+
throw projectApiError('business_failed', businessMessage(value), { operation, response: value });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export function assertWeaverSuccess(value, operation) {
|
|
50
|
+
if (!isWeaverSuccess(value)) {
|
|
51
|
+
throw projectApiError('business_failed', businessMessage(value), { operation, response: value });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function assertPath(requestPath) {
|
|
55
|
+
if (typeof requestPath !== 'string' || !requestPath.startsWith('/') || requestPath.startsWith('//')) {
|
|
56
|
+
throw projectValidationError('path_invalid', '请求路径必须是相对绝对路径');
|
|
57
|
+
}
|
|
58
|
+
if (/^https?:/i.test(requestPath) || /[\r\n]/.test(requestPath) || requestPath.includes('/../')) {
|
|
59
|
+
throw projectValidationError('path_invalid', '请求路径不允许跨域或路径逃逸');
|
|
60
|
+
}
|
|
61
|
+
if (!PROJECT_PATH_PREFIXES.some((prefix) => requestPath.startsWith(prefix))) {
|
|
62
|
+
throw projectValidationError('path_not_allowed', '事井然项目模块只能调用源资料允许的 /api/ 路径');
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function omitEmptyQueryValues(query) {
|
|
66
|
+
if (!query)
|
|
67
|
+
return undefined;
|
|
68
|
+
return Object.fromEntries(Object.entries(query).filter(([, value]) => value !== '' && value !== undefined && value !== null));
|
|
69
|
+
}
|
|
70
|
+
function mapRuntimeNetworkError(error) {
|
|
71
|
+
if (error instanceof E10ApiError && error.type === 'network') {
|
|
72
|
+
throw projectNetworkError(error.subtype, error.message, {
|
|
73
|
+
retryable: error.retryable,
|
|
74
|
+
details: error.details,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
export function createProjectHost(ctx, dependencies = {}) {
|
|
80
|
+
let e10Session = null;
|
|
81
|
+
let sessionContext = null;
|
|
82
|
+
const requireSession = dependencies.requireSession || requireE10Session;
|
|
83
|
+
async function ensureSession() {
|
|
84
|
+
if (!e10Session) {
|
|
85
|
+
e10Session = await requireSession(ctx);
|
|
86
|
+
sessionContext = {
|
|
87
|
+
baseUrl: e10Session.getBaseUrl(),
|
|
88
|
+
profile: inferProfile(ctx),
|
|
89
|
+
userId: e10Session.userId,
|
|
90
|
+
tenantKey: e10Session.tenantKey,
|
|
91
|
+
eteamsId: e10Session.eteamsId,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
return e10Session;
|
|
95
|
+
}
|
|
96
|
+
async function getSession() {
|
|
97
|
+
await ensureSession();
|
|
98
|
+
return sessionContext;
|
|
99
|
+
}
|
|
100
|
+
async function runtime() {
|
|
101
|
+
return createE10RequestRuntime(ctx, await ensureSession(), {
|
|
102
|
+
interceptors: dependencies.interceptors,
|
|
103
|
+
fetch: dependencies.fetch,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
async function request(request) {
|
|
107
|
+
const { method, path, query, body, formData, operation, timeoutMs = 30_000 } = request;
|
|
108
|
+
assertPath(path);
|
|
109
|
+
let effectiveBody = body;
|
|
110
|
+
let effectiveFormData;
|
|
111
|
+
if (formData) {
|
|
112
|
+
effectiveFormData = new FormData();
|
|
113
|
+
for (const [key, value] of Object.entries(formData)) {
|
|
114
|
+
if (value !== undefined)
|
|
115
|
+
effectiveFormData.append(key, String(value));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
let response;
|
|
119
|
+
try {
|
|
120
|
+
response = await (await runtime()).requestJson({
|
|
121
|
+
module: 'project',
|
|
122
|
+
operation,
|
|
123
|
+
method: (method || 'POST').toUpperCase(),
|
|
124
|
+
path,
|
|
125
|
+
query: omitEmptyQueryValues(query),
|
|
126
|
+
body: effectiveBody,
|
|
127
|
+
formData: effectiveFormData,
|
|
128
|
+
headers: { 'X-Requested-With': 'XMLHttpRequest' },
|
|
129
|
+
timeoutMs,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
mapRuntimeNetworkError(error);
|
|
134
|
+
}
|
|
135
|
+
if (response.status === 401 || response.status === 302) {
|
|
136
|
+
throw projectAuthError('session_expired', 'E10 登录态已失效,请先完成登录或重新登录', {
|
|
137
|
+
httpStatus: response.status,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
if (response.parseError) {
|
|
141
|
+
throw projectNetworkError('protocol', `接口返回不是合法 JSON(HTTP ${response.status})`, { retryable: false });
|
|
142
|
+
}
|
|
143
|
+
if (response.status === 404) {
|
|
144
|
+
throw new ProjectCliError('api', 'endpoint_not_found', `接口返回 404,当前环境可能未安装事井然项目应用或接口不存在(${path})`, {
|
|
145
|
+
code: response.status,
|
|
146
|
+
details: { path },
|
|
147
|
+
exitCode: 1,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
if (response.status < 200 || response.status >= 300) {
|
|
151
|
+
throw new ProjectCliError('api', 'http_error', `泛微接口 HTTP ${response.status}`, {
|
|
152
|
+
code: response.status,
|
|
153
|
+
details: { status: response.status, message: businessMessage(response.value) },
|
|
154
|
+
exitCode: 1,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
return response.value;
|
|
158
|
+
}
|
|
159
|
+
return { request, getSession };
|
|
160
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { ExitCode, isCliError, toErrorEnvelope } from '../../core/errors.js';
|
|
3
|
+
import { createProjectContinuationSigner } from './continuation.js';
|
|
4
|
+
import { createProjectHost } from './host.js';
|
|
5
|
+
import { projectManifest } from './manifest.js';
|
|
6
|
+
import { executeProjectOperation } from './operations.js';
|
|
7
|
+
import { projectValidationError } from './errors.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+/u, '');
|
|
20
|
+
}
|
|
21
|
+
export async function inputFrom(options) {
|
|
22
|
+
const opts = asOptions(options);
|
|
23
|
+
let input = {};
|
|
24
|
+
if (opts.input && opts.inputJson) {
|
|
25
|
+
throw projectValidationError('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 = opts.input === '-' ? await readStdin() : await readFile(String(opts.input), 'utf8');
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
throw projectValidationError('input_unreadable', `无法读取 --input:${String(opts.input)}`);
|
|
37
|
+
}
|
|
38
|
+
input = parseJsonInput(raw, '--input');
|
|
39
|
+
}
|
|
40
|
+
return input;
|
|
41
|
+
}
|
|
42
|
+
function parseJsonInput(raw, label) {
|
|
43
|
+
try {
|
|
44
|
+
const parsed = JSON.parse(stripBom(raw));
|
|
45
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
46
|
+
throw new Error('input must be an object');
|
|
47
|
+
}
|
|
48
|
+
return parsed;
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
throw projectValidationError('input_invalid', `${label} 不是合法 JSON 对象`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function successEnvelope(operation, result) {
|
|
55
|
+
return {
|
|
56
|
+
schemaVersion: 1,
|
|
57
|
+
ok: true,
|
|
58
|
+
operation,
|
|
59
|
+
data: result.data === undefined ? null : result.data,
|
|
60
|
+
...(result.meta ? { meta: result.meta } : {}),
|
|
61
|
+
warnings: [],
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function failureEnvelope(error) {
|
|
65
|
+
const structured = error;
|
|
66
|
+
return {
|
|
67
|
+
schemaVersion: 1,
|
|
68
|
+
ok: false,
|
|
69
|
+
...(structured?.partialData === undefined ? {} : { data: structured.partialData }),
|
|
70
|
+
error: toErrorEnvelope(error),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
async function executeAndPrint(ctx, operation, input) {
|
|
74
|
+
try {
|
|
75
|
+
const result = await executeProjectOperation(operation, input, {
|
|
76
|
+
host: createProjectHost(ctx),
|
|
77
|
+
continuation: createProjectContinuationSigner(ctx),
|
|
78
|
+
});
|
|
79
|
+
ctx.output.write(successEnvelope(operation, result), { json: true });
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
console.error(JSON.stringify(failureEnvelope(error), null, 2));
|
|
83
|
+
process.exitCode = isCliError(error) ? error.exitCode : ExitCode.GeneralError;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
export const projectShortcut = {
|
|
87
|
+
name: 'project',
|
|
88
|
+
description: '泛微E10事井然项目管理 CLI(项目/任务查询与确认写入)',
|
|
89
|
+
category: 'shortcut',
|
|
90
|
+
register({ program, action }) {
|
|
91
|
+
const project = program.command('project').description('泛微E10事井然项目管理(项目/任务查询与确认写入)');
|
|
92
|
+
project.command('schema')
|
|
93
|
+
.description('Print project operation manifest for agents')
|
|
94
|
+
.action(action((ctx) => {
|
|
95
|
+
ctx.output.write(projectManifest, { json: true });
|
|
96
|
+
}));
|
|
97
|
+
project.command('run')
|
|
98
|
+
.description('Run a project operation with JSON input')
|
|
99
|
+
.argument('<operation>', 'operation name, e.g. project.task.list')
|
|
100
|
+
.option('--input <path>', 'JSON input file, or - for stdin')
|
|
101
|
+
.option('--input-json <json>', 'inline JSON input object')
|
|
102
|
+
.action(action(async (ctx, operation, options) => {
|
|
103
|
+
await executeAndPrint(ctx, String(operation), await inputFrom(options));
|
|
104
|
+
}));
|
|
105
|
+
},
|
|
106
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { projectOperationDefinitions } from './operations/registry.js';
|
|
2
|
+
function buildOperations() {
|
|
3
|
+
return projectOperationDefinitions().map((definition) => ({
|
|
4
|
+
name: definition.name,
|
|
5
|
+
resource: definition.resource,
|
|
6
|
+
summary: definition.summary,
|
|
7
|
+
risk: definition.risk,
|
|
8
|
+
...(definition.requiresConfirmation ? { requiresConfirmation: true } : {}),
|
|
9
|
+
inputSchema: definition.inputSchema,
|
|
10
|
+
}));
|
|
11
|
+
}
|
|
12
|
+
export const projectManifest = {
|
|
13
|
+
schemaVersion: 1,
|
|
14
|
+
name: 'project',
|
|
15
|
+
title: '泛微E10事井然项目管理',
|
|
16
|
+
version: '0.1.0',
|
|
17
|
+
host: 'e10-project',
|
|
18
|
+
status: 'builtin',
|
|
19
|
+
sourceSkill: 'weaver-e10-shijingran',
|
|
20
|
+
capabilities: ['project', 'task'],
|
|
21
|
+
fixed: {
|
|
22
|
+
appTag: 'weaver-project-eb',
|
|
23
|
+
minimumAppVersion: '20.6.11',
|
|
24
|
+
},
|
|
25
|
+
operations: buildOperations(),
|
|
26
|
+
withheldOperations: [
|
|
27
|
+
{
|
|
28
|
+
name: 'project.attachment.upload',
|
|
29
|
+
reason: '源资料仅说明 attachment 字段接受已上传文件 ID,未提供事井然模块自己的上传接口与文件安全确认链。',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: 'project.field.auto-resolve',
|
|
33
|
+
reason: '源资料要求按字段类型组合多个接口解析名称到 ID;当前 CLI 暴露分步解析 operation,由 Agent 按字段类型显式编排,避免替用户猜测关联对象。',
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
};
|