weaver-work-cli 0.1.8 → 0.1.21
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 +25 -17
- package/dist/cmd/action.js +76 -0
- package/dist/cmd/index.js +2 -0
- package/dist/cmd/refs/index.js +103 -0
- package/dist/cmd/register.js +31 -0
- package/dist/cmd/root.js +1 -0
- package/dist/cmd/setup/index.js +25 -5
- package/dist/cmd/skills/index.js +37 -5
- package/dist/core/connector.js +54 -0
- package/dist/core/context.js +2 -1
- package/dist/core/input.js +48 -0
- package/dist/core/output.js +298 -3
- package/dist/core/refs.js +435 -0
- package/dist/core/state.js +26 -0
- package/dist/internal/e10/auth/commands.js +34 -19
- package/dist/internal/e10/auth/session.js +74 -2
- package/dist/internal/e10/auth/xiaoe.js +359 -94
- package/dist/internal/e10/context.js +18 -6
- package/dist/internal/e10/request-runtime.js +8 -1
- package/dist/internal/skills/install.js +148 -28
- package/dist/internal/skills/reader.js +12 -9
- package/dist/shortcuts/blog/continuation.js +74 -0
- package/dist/shortcuts/blog/errors.js +60 -0
- package/dist/shortcuts/blog/host.js +175 -0
- package/dist/shortcuts/blog/index.js +324 -0
- package/dist/shortcuts/blog/manifest.js +415 -0
- package/dist/shortcuts/blog/operations/comment.js +125 -0
- package/dist/shortcuts/blog/operations/comments.js +31 -0
- package/dist/shortcuts/blog/operations/edit.js +131 -0
- package/dist/shortcuts/blog/operations/export.js +46 -0
- package/dist/shortcuts/blog/operations/like.js +80 -0
- package/dist/shortcuts/blog/operations/publish.js +157 -0
- package/dist/shortcuts/blog/operations/query-user.js +58 -0
- package/dist/shortcuts/blog/operations/query.js +96 -0
- package/dist/shortcuts/blog/operations/registry.js +54 -0
- package/dist/shortcuts/blog/operations/remind.js +176 -0
- package/dist/shortcuts/blog/operations/shared.js +330 -0
- package/dist/shortcuts/blog/operations/types.js +16 -0
- package/dist/shortcuts/blog/operations.js +9 -0
- package/dist/shortcuts/caizhilian/continuation.js +70 -0
- package/dist/shortcuts/caizhilian/errors.js +53 -0
- package/dist/shortcuts/caizhilian/host.js +190 -0
- package/dist/shortcuts/caizhilian/index.js +186 -0
- package/dist/shortcuts/caizhilian/manifest.js +42 -0
- package/dist/shortcuts/caizhilian/operations/api.js +801 -0
- package/dist/shortcuts/caizhilian/operations/confirm.js +75 -0
- package/dist/shortcuts/caizhilian/operations/files.js +306 -0
- package/dist/shortcuts/caizhilian/operations/form.js +170 -0
- package/dist/shortcuts/caizhilian/operations/handlers/collab.js +611 -0
- package/dist/shortcuts/caizhilian/operations/handlers/document.js +890 -0
- package/dist/shortcuts/caizhilian/operations/handlers/file-collection.js +1227 -0
- package/dist/shortcuts/caizhilian/operations/handlers/file-upload.js +192 -0
- package/dist/shortcuts/caizhilian/operations/handlers/fileops.js +570 -0
- package/dist/shortcuts/caizhilian/operations/handlers/folder.js +1024 -0
- package/dist/shortcuts/caizhilian/operations/handlers/search.js +318 -0
- package/dist/shortcuts/caizhilian/operations/handlers/setting-attach.js +294 -0
- package/dist/shortcuts/caizhilian/operations/handlers/setting-backup.js +423 -0
- package/dist/shortcuts/caizhilian/operations/handlers/setting-encrypt.js +238 -0
- package/dist/shortcuts/caizhilian/operations/handlers/setting-storage.js +364 -0
- package/dist/shortcuts/caizhilian/operations/handlers/system.js +106 -0
- package/dist/shortcuts/caizhilian/operations/registry.js +151 -0
- package/dist/shortcuts/caizhilian/operations/shared.js +408 -0
- package/dist/shortcuts/caizhilian/operations/specs.js +698 -0
- package/dist/shortcuts/caizhilian/operations/types.js +57 -0
- package/dist/shortcuts/caizhilian/operations.js +21 -0
- package/dist/shortcuts/contract/continuation.js +70 -0
- package/dist/shortcuts/contract/errors.js +62 -0
- package/dist/shortcuts/contract/host.js +205 -0
- package/dist/shortcuts/contract/index.js +318 -0
- package/dist/shortcuts/contract/manifest.js +596 -0
- package/dist/shortcuts/contract/operations/clause-reference.js +78 -0
- package/dist/shortcuts/contract/operations/form-info.js +371 -0
- package/dist/shortcuts/contract/operations/read.js +118 -0
- package/dist/shortcuts/contract/operations/registry.js +43 -0
- package/dist/shortcuts/contract/operations/request.js +300 -0
- package/dist/shortcuts/contract/operations/shared.js +133 -0
- package/dist/shortcuts/contract/operations/subject.js +157 -0
- package/dist/shortcuts/contract/operations/template.js +81 -0
- package/dist/shortcuts/contract/operations/types.js +7 -0
- package/dist/shortcuts/contract/operations/workflow.js +52 -0
- package/dist/shortcuts/contract/operations/write.js +131 -0
- package/dist/shortcuts/contract/operations.js +8 -0
- package/dist/shortcuts/hrm/index.js +83 -2
- package/dist/shortcuts/hrm/manifest.js +16 -9
- package/dist/shortcuts/hrm/operations/employee.js +160 -30
- package/dist/shortcuts/hrm/operations/org.js +17 -3
- package/dist/shortcuts/hrm/operations/position.js +18 -2
- package/dist/shortcuts/hrm/operations/registry.js +2 -0
- package/dist/shortcuts/hrm/operations/resolve.js +449 -0
- package/dist/shortcuts/index.js +5 -1
- package/dist/shortcuts/invoice/index.js +74 -36
- package/dist/shortcuts/invoice/manifest.js +674 -4
- package/dist/shortcuts/invoice/markdown.js +172 -0
- package/dist/shortcuts/invoice/operations/add.js +3 -3
- package/dist/shortcuts/invoice/operations/delete.js +2 -2
- package/dist/shortcuts/invoice/operations/get.js +10 -2
- package/dist/shortcuts/invoice/operations/import.js +2 -2
- package/dist/shortcuts/invoice/operations/registry.js +8 -1
- package/dist/shortcuts/invoice/operations/reim-auto.js +719 -0
- package/dist/shortcuts/invoice/operations/reim.js +85 -2
- package/dist/shortcuts/invoice/operations/shared.js +75 -8
- package/dist/shortcuts/invoice/operations/update.js +2 -2
- package/dist/shortcuts/invoice/operations/upload-and-validate.js +93 -0
- package/dist/shortcuts/invoice/operations/validate-preview.js +2 -1
- package/dist/shortcuts/invoice/projection.js +328 -0
- package/dist/shortcuts/meeting/host.js +18 -1
- package/dist/shortcuts/meeting/manifest.js +30 -18
- package/dist/shortcuts/meeting/operations/eb/catalog.js +390 -0
- package/dist/shortcuts/meeting/operations/eb/conflict.js +389 -0
- package/dist/shortcuts/meeting/operations/eb/factory.js +389 -0
- package/dist/shortcuts/meeting/operations/eb/index.js +22 -0
- package/dist/shortcuts/meeting/operations/env.js +8 -1
- package/dist/shortcuts/meeting/operations/registry.js +2 -0
- package/dist/shortcuts/meeting/operations/signs.js +216 -2
- package/dist/shortcuts/workflow/endpoints.js +2 -1
- package/dist/shortcuts/workflow/errors.js +8 -2
- package/dist/shortcuts/workflow/host.js +11 -4
- package/dist/shortcuts/workflow/operations/form.js +1016 -590
- package/dist/shortcuts/workflow/operations/read.js +913 -231
- package/dist/shortcuts/workflow/operations/shared.js +21 -0
- package/dist/shortcuts/workflow/operations/write.js +750 -52
- package/dist/shortcuts/workflow/operations.js +36 -10
- package/docs/_catalog.md +6 -0
- package/docs/agent-invoice.md +5 -1
- package/docs/agent-io-channel-design.md +468 -0
- package/docs/agent-skill-install.md +9 -4
- package/docs/blog.md +123 -0
- package/docs/caizhilian.md +304 -0
- package/docs/contract.md +91 -0
- package/docs/e10-auth.md +4 -2
- package/docs/hrm.md +53 -11
- package/docs/invoice.md +5 -1
- package/docs/meeting.md +1 -1
- package/docs/operation-manual.md +15 -3
- package/docs/skill-review-2026-09-19.md +2 -2
- package/package.json +1 -1
- package/scripts/package-skill.mjs +32 -20
- package/scripts/xiaoe_env.py +118 -0
- package/skill-template/business-info.json +113 -14
- package/skill-template/domains/blog.md +24 -0
- package/skill-template/domains/caizhilian.md +22 -0
- package/skill-template/domains/hrm.md +2 -1
- package/skill-template/domains/jinchengda.md +23 -0
- package/skill-template/domains/meeting.md +1 -1
- package/skill-template/domains/workflow.md +1 -1
- package/skill-template/skill-template.md +1 -1
- package/skills/weaver-e10-blog/SKILL.md +111 -0
- package/skills/weaver-e10-blog/references/blog-comment.md +51 -0
- package/skills/weaver-e10-blog/references/blog-comments.md +39 -0
- package/skills/weaver-e10-blog/references/blog-default-permission.md +38 -0
- package/skills/weaver-e10-blog/references/blog-disabled-capabilities.md +11 -0
- package/skills/weaver-e10-blog/references/blog-edit.md +51 -0
- package/skills/weaver-e10-blog/references/blog-export.md +42 -0
- package/skills/weaver-e10-blog/references/blog-like.md +50 -0
- package/skills/weaver-e10-blog/references/blog-publish.md +52 -0
- package/skills/weaver-e10-blog/references/blog-query-user.md +45 -0
- package/skills/weaver-e10-blog/references/blog-query.md +49 -0
- package/skills/weaver-e10-blog/references/blog-remind.md +51 -0
- package/skills/weaver-e10-blog/references/blog-resolve-users.md +38 -0
- package/skills/weaver-e10-blog/references/source-manifest.json +1540 -0
- package/skills/weaver-e10-caizhilian/SKILL.md +217 -0
- package/skills/weaver-e10-caizhilian/references/agent-entry.md +106 -0
- package/skills/weaver-e10-caizhilian/references/collaboration.md +112 -0
- package/skills/weaver-e10-caizhilian/references/document.md +111 -0
- package/skills/weaver-e10-caizhilian/references/file-collection.md +173 -0
- package/skills/weaver-e10-caizhilian/references/file-upload.md +309 -0
- package/skills/weaver-e10-caizhilian/references/files.md +120 -0
- package/skills/weaver-e10-caizhilian/references/folder.md +122 -0
- package/skills/weaver-e10-caizhilian/references/org-browser.md +104 -0
- package/skills/weaver-e10-caizhilian/references/setting-attach.md +440 -0
- package/skills/weaver-e10-caizhilian/references/setting-backup.md +372 -0
- package/skills/weaver-e10-caizhilian/references/setting-encrypt.md +399 -0
- package/skills/weaver-e10-caizhilian/references/setting-storage.md +603 -0
- package/skills/weaver-e10-caizhilian/references/source-manifest.json +5694 -0
- package/skills/weaver-e10-caizhilian/references/withheld.md +93 -0
- package/skills/weaver-e10-caizhilian/references/write-protocol.md +120 -0
- package/skills/weaver-e10-calendar/SKILL.md +4 -3
- package/skills/weaver-e10-ebuilder-form/SKILL.md +4 -9
- package/skills/weaver-e10-ebuilder-form/references/context-routing.md +0 -9
- package/skills/weaver-e10-ebuilder-form/references/custom-api.md +1 -5
- package/skills/weaver-e10-ebuilder-form/references/fields-browser.md +1 -5
- package/skills/weaver-e10-ebuilder-form/references/list-nlist.md +1 -5
- package/skills/weaver-e10-ebuilder-form/references/openapi-read.md +1 -5
- package/skills/weaver-e10-ebuilder-form/references/openapi-write.md +1 -5
- package/skills/weaver-e10-esb/SKILL.md +2 -2
- package/skills/weaver-e10-esb/references/source-manifest.json +9 -9
- package/skills/weaver-e10-hrm/SKILL.md +104 -9
- package/skills/weaver-e10-hrm/references/employee.md +89 -15
- package/skills/weaver-e10-hrm/references/org.md +114 -5
- package/skills/weaver-e10-hrm/references/position.md +14 -7
- package/skills/weaver-e10-hrm/references/source-manifest.json +1 -1
- package/skills/weaver-e10-jinchengda/SKILL.md +167 -0
- package/skills/weaver-e10-jinchengda/references/contract-agent-entry.md +82 -0
- package/skills/weaver-e10-jinchengda/references/contract-clause-recommender.md +69 -0
- package/skills/weaver-e10-jinchengda/references/contract-clause-reference-save.md +78 -0
- package/skills/weaver-e10-jinchengda/references/contract-disabled-capabilities.md +27 -0
- package/skills/weaver-e10-jinchengda/references/contract-request-status.md +71 -0
- package/skills/weaver-e10-jinchengda/references/contract-request-write.md +94 -0
- package/skills/weaver-e10-jinchengda/references/contract-signing-subjects.md +89 -0
- package/skills/weaver-e10-jinchengda/references/contract-subject-create.md +77 -0
- package/skills/weaver-e10-jinchengda/references/contract-template-selector.md +66 -0
- package/skills/weaver-e10-jinchengda/references/contract-workflow-discovery.md +94 -0
- package/skills/weaver-e10-jinchengda/references/source-manifest.json +259 -0
- package/skills/weaver-e10-jiuchuanhui/SKILL.md +5 -11
- package/skills/weaver-e10-jucailin/SKILL.md +4 -3
- package/skills/weaver-e10-mail/SKILL.md +4 -11
- package/skills/weaver-e10-meeting/SKILL.md +66 -32
- package/skills/weaver-e10-meeting/references/meeting-change.md +5 -2
- package/skills/weaver-e10-meeting/references/meeting-create.md +2 -1
- package/skills/weaver-e10-meeting/references/meeting-eb.md +114 -0
- package/skills/weaver-e10-meeting/references/meeting-lifecycle.md +9 -8
- package/skills/weaver-e10-meeting/references/meeting-receipts-signs.md +35 -7
- package/skills/weaver-e10-meeting/references/meeting-update.md +1 -1
- package/skills/weaver-e10-okr/SKILL.md +6 -11
- package/skills/weaver-e10-okr/references/source-manifest.json +3 -3
- package/skills/weaver-e10-plan/SKILL.md +3 -3
- package/skills/weaver-e10-plan/references/plan-collect-and-compose.md +1 -1
- package/skills/weaver-e10-plan/references/plan-overview-and-routing.md +1 -1
- package/skills/weaver-e10-plan/references/source-manifest.json +13 -13
- package/skills/weaver-e10-qiyecheng/SKILL.md +4 -10
- package/skills/weaver-e10-qiyecheng/references/source-manifest.json +9 -9
- package/skills/weaver-e10-shared/SKILL.md +42 -34
- package/skills/weaver-e10-shared/references/e10-auth-and-session.md +16 -43
- package/skills/weaver-e10-shared/references/json-output-contract.md +20 -11
- package/skills/weaver-e10-shared/references/non-interactive-environment.md +21 -14
- package/skills/weaver-e10-shijingran/SKILL.md +2 -1
- package/skills/weaver-e10-shijingran/references/source-manifest.json +9 -9
- package/skills/weaver-e10-skill-maker/SKILL.md +4 -10
- package/skills/weaver-e10-skill-maker/references/business-skill-generation.md +5 -3
- package/skills/weaver-e10-skill-maker/references/module-cli-generation.md +12 -27
- package/skills/weaver-e10-skill-maker/references/module-gap-audit-and-repair.md +17 -5
- package/skills/weaver-e10-skill-maker/references/source-update-detection.md +4 -27
- package/skills/weaver-e10-skill-maker/references/weaver-skill-style.md +1 -1
- package/skills/weaver-e10-wenshuding/SKILL.md +5 -16
- package/skills/weaver-e10-wenshuding/references/source-manifest.json +7 -7
- package/skills/weaver-e10-workflow/SKILL.md +41 -46
- package/skills/weaver-e10-workflow/references/source-manifest.json +39 -75
- package/skills/weaver-e10-workflow/references/workflow-batch.md +90 -29
- package/skills/weaver-e10-workflow/references/workflow-create.md +83 -109
- package/skills/weaver-e10-workflow/references/workflow-memory.md +19 -9
- package/skills/weaver-e10-workflow/references/workflow-operate.md +2 -2
- package/skills/weaver-e10-workflow/references/workflow-query.md +40 -27
- package/skills/weaver-e10-workflow/references/workflow-share.md +18 -2
- package/skills/weaver-e10-workflow/references/workflow-view.md +3 -4
- package/skills/weaver-e10-yepiaotong/SKILL.md +74 -39
- package/skills/weaver-e10-yepiaotong/references/invoice-detail.md +22 -1
- package/skills/weaver-e10-yepiaotong/references/invoice-enterprise-list.md +3 -1
- package/skills/weaver-e10-yepiaotong/references/invoice-output-view.md +135 -0
- package/skills/weaver-e10-yepiaotong/references/invoice-personal-list.md +15 -3
- package/skills/weaver-e10-yepiaotong/references/invoice-reim.md +1 -1
- package/skills/weaver-e10-yepiaotong/references/invoice-summarize.py +217 -0
- package/skills/weaver-e10-yepiaotong/references/invoice-upload-and-validate.md +88 -0
- package/skills/weaver-e10-yepiaotong/references/invoice-validation-preview.md +7 -2
- package/skills/weaver-e10-yepiaotong/references/reim-api-reference.md +89 -9
- package/skills/weaver-e10-yepiaotong/references/reim-examples.md +1 -0
- package/skills/weaver-e10-yepiaotong/references/reim-expense-subject-rules.md +70 -49
- package/skills/weaver-e10-yepiaotong/references/reim-fast-path.md +119 -0
- package/skills/weaver-e10-yepiaotong/references/reim-form-fill-rules.md +55 -36
- package/skills/weaver-e10-yepiaotong/references/reim-matching-rules.md +4 -0
- package/skills/weaver-e10-yepiaotong/references/reim-phone-subsidy-rules.md +149 -0
- package/skills/weaver-e10-yepiaotong/references/reim-workflow.md +125 -5
- package/skills/weaver-e10-yepiaotong/references/source-manifest.json +430 -63
- package/skills/weaver-e10-yimiaoban/SKILL.md +4 -12
- package/skills/weaver-e10-yimiaoban/references/source-manifest.json +3 -3
- package/skills/weaver-e10-ziguanjia/SKILL.md +2 -1
- package/skills/weaver-e10-ziguanjia/references/ziguanjia-agent-entry.md +3 -3
- package/tools/weaver-skill-detector-1.0.13/SKILL.md +9 -9
- package/tools/weaver-skill-detector-1.0.13/references/checklist.md +3 -3
- package/tools/weaver-skill-detector-1.0.13/scripts/__pycache__/validate_skill.cpython-313.pyc +0 -0
- package/tools/weaver-skill-detector-1.0.13/scripts/validate_skill.py +98 -91
- package/scripts/sync-connector-skills.mjs +0 -172
|
@@ -1,43 +1,56 @@
|
|
|
1
1
|
import { execFile } from 'node:child_process';
|
|
2
|
-
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
2
|
+
import { accessSync, constants as fsConstants, existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
|
-
import { join } from 'node:path';
|
|
4
|
+
import { dirname, join, resolve } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
5
6
|
import { CliError, ExitCode } from '../../../core/errors.js';
|
|
6
|
-
import { buildE10CookieHeader, E10AuthSession, profileNameFromUrl, readTeamsCheckUserInfo, saveAuthFile, saveProfileConfig, setActiveProfile, } from './session.js';
|
|
7
|
+
import { buildE10CookieHeader, E10AuthSession, extractRawSetCookie, extractSessionTicket, profileNameFromUrl, readTeamsCheckUserInfo, saveAuthFile, saveProfileConfig, setActiveProfile, } from './session.js';
|
|
7
8
|
export const DEFAULT_E10_BASE_URL = 'https://www.e-cology.com.cn';
|
|
8
9
|
export const XIAOE_AGENT_TYPE = 'XiaoE';
|
|
10
|
+
// `XIAOE_` 是宿主/平台保留前缀(XIAOE_DOMAIN、XIAOE_USER_TOKEN、XIAOE_SKILL_* 等)。
|
|
11
|
+
// 本 CLI 自定义的宿主集成变量一律使用自有前缀 `WEAVER_XIAOE_*`;历史 `XIAOE_*`
|
|
12
|
+
// 名称仅作兼容别名排在后面,读取优先级低于新名称。
|
|
9
13
|
const XIAOE_UNIFIED_LOGIN_MAP_ENV_KEYS = [
|
|
14
|
+
'WEAVER_XIAOE_AUTH_MAP',
|
|
15
|
+
'WEAVER_XIAOE_E10_AUTH_MAP',
|
|
16
|
+
'WEAVER_XIAOE_DOMAIN_ETEAMSID_MAP',
|
|
17
|
+
'WEAVER_XIAOE_UNIFIED_LOGIN_MAP',
|
|
10
18
|
'XIAOE_E10_AUTH_MAP',
|
|
11
19
|
'XIAOE_E10_DOMAIN_ETEAMSID_MAP',
|
|
12
20
|
'XIAOE_DOMAIN_ETEAMSID_MAP',
|
|
13
21
|
'XIAOE_UNIFIED_LOGIN_MAP',
|
|
14
22
|
];
|
|
15
23
|
const XIAOE_SINGLE_DOMAIN_ENV_KEYS = [
|
|
16
|
-
'
|
|
24
|
+
'WEAVER_XIAOE_E10_DOMAIN',
|
|
25
|
+
'WEAVER_XIAOE_BASE_URL',
|
|
26
|
+
'WEAVER_XIAOE_E10_BASE_URL',
|
|
17
27
|
'XIAOE_E10_DOMAIN',
|
|
18
28
|
'XIAOE_BASE_URL',
|
|
19
29
|
'XIAOE_E10_BASE_URL',
|
|
20
30
|
];
|
|
21
31
|
const XIAOE_DEFAULT_DOMAIN_ENV_KEYS = [
|
|
32
|
+
'WEAVER_XIAOE_DEFAULT_DOMAIN',
|
|
33
|
+
'WEAVER_XIAOE_DEFAULT_BASE_URL',
|
|
34
|
+
'WEAVER_XIAOE_E10_DEFAULT_DOMAIN',
|
|
35
|
+
'WEAVER_XIAOE_E10_DEFAULT_BASE_URL',
|
|
22
36
|
'XIAOE_DEFAULT_DOMAIN',
|
|
23
37
|
'XIAOE_DEFAULT_BASE_URL',
|
|
24
38
|
'XIAOE_E10_DEFAULT_DOMAIN',
|
|
25
39
|
'XIAOE_E10_DEFAULT_BASE_URL',
|
|
26
40
|
];
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const XIAOE_TOKEN_SCRIPT_ENV_KEY = 'XIAOE_TOKEN_SCRIPT_PATH';
|
|
35
|
-
const XIAOE_TOKEN_SCRIPT_PYTHON_ENV_KEY = 'XIAOE_TOKEN_SCRIPT_PYTHON';
|
|
41
|
+
// The XiaoE host only injects domain variables (XIAOE_DOMAIN etc.) together
|
|
42
|
+
// with XIAOE_USER_TOKEN; it never injects an ETEAMSID environment variable, so
|
|
43
|
+
// no `XIAOE_*ETEAMSID*` credential channel exists here on purpose.
|
|
44
|
+
const XIAOE_TOKEN_SCRIPT_ENV_KEY = 'WEAVER_XIAOE_TOKEN_SCRIPT_PATH';
|
|
45
|
+
const XIAOE_TOKEN_SCRIPT_PYTHON_ENV_KEY = 'WEAVER_XIAOE_TOKEN_SCRIPT_PYTHON';
|
|
46
|
+
const XIAOE_TOKEN_SCRIPT_LEGACY_ENV_KEY = 'XIAOE_TOKEN_SCRIPT_PATH';
|
|
47
|
+
const XIAOE_TOKEN_SCRIPT_PYTHON_LEGACY_ENV_KEY = 'XIAOE_TOKEN_SCRIPT_PYTHON';
|
|
36
48
|
const XIAOE_TOKEN_SCRIPT_TIMEOUT_MS = 15_000;
|
|
37
49
|
const XIAOE_CLIENT_TOKEN_TIMEOUT_MS = 15_000;
|
|
38
50
|
export const XIAOE_SKILL_ETEAMID_API_PATH = '/spec-apps/api/app/rpachat/skill/extend/eteamId';
|
|
39
51
|
export const XIAOE_CLIENT_SETTINGS_SOURCE = 'XiaoE/Robots/user_settings.json';
|
|
40
52
|
export const XIAOE_USER_TOKEN_SOURCE = 'XIAOE_USER_TOKEN';
|
|
53
|
+
export const XIAOE_BUNDLED_ENV_SCRIPT_SOURCE = 'bundled scripts/xiaoe_env.py';
|
|
41
54
|
function envString(env, ...keys) {
|
|
42
55
|
for (const key of keys) {
|
|
43
56
|
const value = env[key];
|
|
@@ -50,29 +63,40 @@ function hasEnvPrefix(env, prefix) {
|
|
|
50
63
|
return Object.keys(env).some((key) => key === prefix || key.startsWith(`${prefix}_`));
|
|
51
64
|
}
|
|
52
65
|
export function hasXiaoEEnv(env = process.env) {
|
|
53
|
-
|
|
66
|
+
// `XIAOE_*` are the platform-injected variables; `WEAVER_XIAOE_*` are this
|
|
67
|
+
// CLI's own host-integration variables. Either one marks a XiaoE host.
|
|
68
|
+
return hasEnvPrefix(env, 'XIAOE') || hasEnvPrefix(env, 'WEAVER_XIAOE');
|
|
54
69
|
}
|
|
55
70
|
export function describeXiaoECredentialSource(source) {
|
|
56
|
-
|
|
71
|
+
// Step 1 is the platform-injected credential (XIAOE_USER_TOKEN + XIAOE_DOMAIN):
|
|
72
|
+
// it is available in every sandbox run and needs no local desktop client.
|
|
73
|
+
if (source === XIAOE_USER_TOKEN_SOURCE || source === XIAOE_BUNDLED_ENV_SCRIPT_SOURCE) {
|
|
57
74
|
return {
|
|
58
75
|
credentialSourceStep: 1,
|
|
76
|
+
credentialSourceType: 'xiaoe_platform',
|
|
77
|
+
credentialSourceLabel: source === XIAOE_BUNDLED_ENV_SCRIPT_SOURCE
|
|
78
|
+
? 'XiaoE platform user token (bundled Python fallback)'
|
|
79
|
+
: 'XiaoE platform user token',
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
if (source === 'xiaoeUserData/tokens.py') {
|
|
83
|
+
return {
|
|
84
|
+
credentialSourceStep: 2,
|
|
59
85
|
credentialSourceType: 'token_script',
|
|
60
86
|
credentialSourceLabel: 'xiaoeUserData/tokens.py',
|
|
61
87
|
};
|
|
62
88
|
}
|
|
63
|
-
if (source === XIAOE_CLIENT_SETTINGS_SOURCE
|
|
89
|
+
if (source === XIAOE_CLIENT_SETTINGS_SOURCE) {
|
|
64
90
|
return {
|
|
65
|
-
credentialSourceStep:
|
|
91
|
+
credentialSourceStep: 3,
|
|
66
92
|
credentialSourceType: 'xiaoe_client',
|
|
67
|
-
credentialSourceLabel:
|
|
68
|
-
? 'XiaoE client user token'
|
|
69
|
-
: 'XiaoE client settings',
|
|
93
|
+
credentialSourceLabel: 'XiaoE client settings',
|
|
70
94
|
};
|
|
71
95
|
}
|
|
72
96
|
return {
|
|
73
|
-
credentialSourceStep:
|
|
97
|
+
credentialSourceStep: 4,
|
|
74
98
|
credentialSourceType: 'xiaoe_env',
|
|
75
|
-
credentialSourceLabel: '
|
|
99
|
+
credentialSourceLabel: 'WEAVER_XIAOE_* environment variables',
|
|
76
100
|
};
|
|
77
101
|
}
|
|
78
102
|
function normalizeUrl(value) {
|
|
@@ -223,28 +247,36 @@ function listXiaoEUnifiedLoginCredentials(env = process.env) {
|
|
|
223
247
|
if (raw)
|
|
224
248
|
candidates.push(...parseXiaoEUnifiedLoginMap(raw, key));
|
|
225
249
|
}
|
|
226
|
-
const singleDomain = envString(env, ...XIAOE_SINGLE_DOMAIN_ENV_KEYS);
|
|
227
|
-
const singleEteamsId = envString(env, ...XIAOE_SINGLE_ETEAMSID_ENV_KEYS);
|
|
228
|
-
if (singleDomain && singleEteamsId) {
|
|
229
|
-
candidates.push({
|
|
230
|
-
baseUrl: normalizeUrl(singleDomain),
|
|
231
|
-
eteamsId: singleEteamsId,
|
|
232
|
-
source: `${XIAOE_SINGLE_DOMAIN_ENV_KEYS[0]}+${XIAOE_SINGLE_ETEAMSID_ENV_KEYS[0]}`,
|
|
233
|
-
});
|
|
234
|
-
}
|
|
235
250
|
return candidates;
|
|
236
251
|
}
|
|
237
252
|
export function resolveXiaoEUnifiedLoginCredential(baseUrl, env = process.env) {
|
|
238
253
|
const candidates = listXiaoEUnifiedLoginCredentials(env);
|
|
239
254
|
return candidates.find((candidate) => domainMatches(baseUrl, candidate.baseUrl)) || null;
|
|
240
255
|
}
|
|
241
|
-
|
|
256
|
+
function normalizeBaseUrlValue(value) {
|
|
257
|
+
const raw = normalizeUrl(value.trim());
|
|
258
|
+
if (!raw)
|
|
259
|
+
return '';
|
|
260
|
+
return /^https?:\/\//i.test(raw) ? raw : `https://${raw}`;
|
|
261
|
+
}
|
|
262
|
+
function resolveXiaoEConfiguredBaseUrl(env) {
|
|
242
263
|
const explicitDefault = envString(env, ...XIAOE_DEFAULT_DOMAIN_ENV_KEYS);
|
|
243
264
|
if (explicitDefault)
|
|
244
|
-
return
|
|
265
|
+
return normalizeBaseUrlValue(explicitDefault);
|
|
266
|
+
// XIAOE_DOMAIN is the XiaoE/erpa service address used to exchange
|
|
267
|
+
// XIAOE_USER_TOKEN. Only dedicated E10 variables may select the target E10.
|
|
268
|
+
const singleDomain = envString(env, ...XIAOE_SINGLE_DOMAIN_ENV_KEYS);
|
|
269
|
+
if (singleDomain)
|
|
270
|
+
return normalizeBaseUrlValue(singleDomain);
|
|
245
271
|
const firstCredential = listXiaoEUnifiedLoginCredentials(env)[0];
|
|
246
272
|
if (firstCredential?.baseUrl)
|
|
247
273
|
return normalizeUrl(firstCredential.baseUrl);
|
|
274
|
+
return '';
|
|
275
|
+
}
|
|
276
|
+
export function resolveXiaoEDefaultBaseUrl(env = process.env) {
|
|
277
|
+
const configured = resolveXiaoEConfiguredBaseUrl(env);
|
|
278
|
+
if (configured)
|
|
279
|
+
return configured;
|
|
248
280
|
return hasXiaoEEnv(env) ? DEFAULT_E10_BASE_URL : '';
|
|
249
281
|
}
|
|
250
282
|
function uniqueStrings(values) {
|
|
@@ -259,7 +291,7 @@ function uniqueStrings(values) {
|
|
|
259
291
|
return result;
|
|
260
292
|
}
|
|
261
293
|
function xiaoETokenScriptCandidatePaths(env = process.env) {
|
|
262
|
-
const explicit = envString(env, XIAOE_TOKEN_SCRIPT_ENV_KEY);
|
|
294
|
+
const explicit = envString(env, XIAOE_TOKEN_SCRIPT_ENV_KEY, XIAOE_TOKEN_SCRIPT_LEGACY_ENV_KEY);
|
|
263
295
|
if (explicit)
|
|
264
296
|
return [explicit];
|
|
265
297
|
if (process.platform === 'win32') {
|
|
@@ -271,7 +303,7 @@ function xiaoETokenScriptCandidatePaths(env = process.env) {
|
|
|
271
303
|
]);
|
|
272
304
|
}
|
|
273
305
|
const envHome = envString(env, 'HOME');
|
|
274
|
-
const nodeHome = homedir();
|
|
306
|
+
const nodeHome = envHome ? '' : homedir();
|
|
275
307
|
return uniqueStrings([
|
|
276
308
|
envHome ? join(envHome, 'Library', 'Application Support', 'xiaoeUserData', 'tokens.py') : '',
|
|
277
309
|
nodeHome ? join(nodeHome, 'Library', 'Application Support', 'xiaoeUserData', 'tokens.py') : '',
|
|
@@ -280,29 +312,125 @@ function xiaoETokenScriptCandidatePaths(env = process.env) {
|
|
|
280
312
|
function xiaoETokenScriptPath(env = process.env) {
|
|
281
313
|
return xiaoETokenScriptCandidatePaths(env).find((path) => existsSync(path)) || '';
|
|
282
314
|
}
|
|
315
|
+
export function xiaoEBundledEnvScriptPath() {
|
|
316
|
+
const scriptPath = resolve(dirname(fileURLToPath(import.meta.url)), '../../../../scripts/xiaoe_env.py');
|
|
317
|
+
return existsSync(scriptPath) ? scriptPath : '';
|
|
318
|
+
}
|
|
283
319
|
export function hasXiaoETokenScript(env = process.env) {
|
|
284
|
-
const explicit = envString(env, XIAOE_TOKEN_SCRIPT_ENV_KEY);
|
|
320
|
+
const explicit = envString(env, XIAOE_TOKEN_SCRIPT_ENV_KEY, XIAOE_TOKEN_SCRIPT_LEGACY_ENV_KEY);
|
|
285
321
|
if (explicit)
|
|
286
322
|
return existsSync(explicit);
|
|
287
323
|
const scriptPath = xiaoETokenScriptPath(env);
|
|
288
324
|
return Boolean(scriptPath && existsSync(scriptPath));
|
|
289
325
|
}
|
|
290
|
-
function
|
|
291
|
-
|
|
326
|
+
function isExecutableFile(path) {
|
|
327
|
+
try {
|
|
328
|
+
if (!statSync(path).isFile())
|
|
329
|
+
return false;
|
|
330
|
+
accessSync(path, fsConstants.X_OK);
|
|
331
|
+
return true;
|
|
332
|
+
}
|
|
333
|
+
catch {
|
|
334
|
+
return false;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
function listXiaoEPythonRuntimeDirectories(resourcesRoot) {
|
|
338
|
+
if (!resourcesRoot || !existsSync(resourcesRoot))
|
|
339
|
+
return [];
|
|
340
|
+
try {
|
|
341
|
+
return readdirSync(resourcesRoot, { withFileTypes: true })
|
|
342
|
+
.filter((entry) => entry.isDirectory() && /^Python3(?:[-_.]\d[\d.]*)?$/iu.test(entry.name))
|
|
343
|
+
.map((entry) => join(resourcesRoot, entry.name))
|
|
344
|
+
.sort((left, right) => right.localeCompare(left, undefined, { numeric: true }));
|
|
345
|
+
}
|
|
346
|
+
catch {
|
|
347
|
+
return [];
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
function listExecutablePythonFiles(directory, platform) {
|
|
351
|
+
if (!directory || !existsSync(directory))
|
|
352
|
+
return [];
|
|
353
|
+
try {
|
|
354
|
+
const pattern = platform === 'win32' ? /^python(?:3(?:\.\d+)*)?\.exe$/iu : /^python3(?:\.\d+)*$/u;
|
|
355
|
+
return readdirSync(directory, { withFileTypes: true })
|
|
356
|
+
.filter((entry) => entry.isFile() && pattern.test(entry.name))
|
|
357
|
+
.map((entry) => join(directory, entry.name))
|
|
358
|
+
.filter(isExecutableFile)
|
|
359
|
+
.sort((left, right) => right.localeCompare(left, undefined, { numeric: true }));
|
|
360
|
+
}
|
|
361
|
+
catch {
|
|
362
|
+
return [];
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Locate the Python runtime bundled with the desktop XiaoE client. The client
|
|
367
|
+
* ships the runtime expected by xiaoeUserData/tokens.py; the system Python may
|
|
368
|
+
* still be unavailable or incompatible with a host-provided script.
|
|
369
|
+
*/
|
|
370
|
+
export function xiaoEClientPythonCandidates(env = process.env, platform = process.platform) {
|
|
371
|
+
const appRoots = [];
|
|
372
|
+
if (platform === 'darwin') {
|
|
373
|
+
const envHome = envString(env, 'HOME');
|
|
374
|
+
const nodeHome = homedir();
|
|
375
|
+
for (const home of uniqueStrings([envHome, nodeHome])) {
|
|
376
|
+
appRoots.push(join(home, 'Applications', '智能小e.app'));
|
|
377
|
+
}
|
|
378
|
+
appRoots.push('/Applications/智能小e.app');
|
|
379
|
+
}
|
|
380
|
+
else if (platform === 'win32') {
|
|
381
|
+
const localAppData = envString(env, 'LOCALAPPDATA');
|
|
382
|
+
const programFiles = uniqueStrings([
|
|
383
|
+
envString(env, 'ProgramW6432'),
|
|
384
|
+
envString(env, 'ProgramFiles'),
|
|
385
|
+
envString(env, 'ProgramFiles(x86)'),
|
|
386
|
+
]);
|
|
387
|
+
const appNames = ['智能小e', 'XiaoE', 'XiaoEClient', 'XiaoEAI'];
|
|
388
|
+
if (localAppData) {
|
|
389
|
+
for (const appName of appNames) {
|
|
390
|
+
appRoots.push(join(localAppData, 'Programs', appName), join(localAppData, appName));
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
for (const programFilesRoot of programFiles) {
|
|
394
|
+
for (const appName of appNames)
|
|
395
|
+
appRoots.push(join(programFilesRoot, appName));
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
const candidates = [];
|
|
399
|
+
for (const appRoot of uniqueStrings(appRoots)) {
|
|
400
|
+
const resourcesRoots = platform === 'darwin'
|
|
401
|
+
? [join(appRoot, 'Contents', 'Resources')]
|
|
402
|
+
: [join(appRoot, 'Resources'), appRoot];
|
|
403
|
+
for (const resourcesRoot of resourcesRoots) {
|
|
404
|
+
for (const runtimeRoot of listXiaoEPythonRuntimeDirectories(resourcesRoot)) {
|
|
405
|
+
const executableDirectories = platform === 'win32'
|
|
406
|
+
? [runtimeRoot, join(runtimeRoot, 'bin')]
|
|
407
|
+
: [join(runtimeRoot, 'bin')];
|
|
408
|
+
for (const executableDirectory of executableDirectories) {
|
|
409
|
+
candidates.push(...listExecutablePythonFiles(executableDirectory, platform));
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
return uniqueStrings(candidates);
|
|
415
|
+
}
|
|
416
|
+
export function xiaoETokenScriptPythonCandidates(env = process.env, platform = process.platform) {
|
|
417
|
+
const explicit = envString(env, XIAOE_TOKEN_SCRIPT_PYTHON_ENV_KEY, XIAOE_TOKEN_SCRIPT_PYTHON_LEGACY_ENV_KEY);
|
|
292
418
|
if (explicit)
|
|
293
419
|
return [explicit];
|
|
294
|
-
return uniqueStrings(
|
|
295
|
-
? ['python', 'python3', 'py']
|
|
296
|
-
: ['python3', '/usr/bin/python3', 'python']);
|
|
420
|
+
return uniqueStrings(platform === 'win32'
|
|
421
|
+
? [...xiaoEClientPythonCandidates(env, platform), 'python', 'python3', 'py']
|
|
422
|
+
: [...xiaoEClientPythonCandidates(env, platform), 'python3', '/usr/bin/python3', 'python']);
|
|
297
423
|
}
|
|
298
424
|
function isXiaoETokenScriptEnvFailure(message) {
|
|
299
|
-
return /No module named|ModuleNotFoundError|ENOENT|command not found|not recognized as an internal|cannot find the file/i
|
|
425
|
+
return /No module named|ModuleNotFoundError|ENOENT|EACCES|permission denied|command not found|not recognized as an internal|cannot find the file/i
|
|
300
426
|
.test(message);
|
|
301
427
|
}
|
|
302
|
-
async function runXiaoETokenScriptOnce(pythonCommand, scriptPath, env) {
|
|
428
|
+
async function runXiaoETokenScriptOnce(pythonCommand, scriptPath, baseUrl, env) {
|
|
303
429
|
const wrapper = [
|
|
304
430
|
'import ast,contextlib,io,json,runpy,sys',
|
|
305
431
|
`path = ${JSON.stringify(scriptPath)}`,
|
|
432
|
+
`base_url = ${JSON.stringify(baseUrl)}`,
|
|
433
|
+
'sys.argv = [path, base_url]',
|
|
306
434
|
'buf = io.StringIO()',
|
|
307
435
|
'data = None',
|
|
308
436
|
'with contextlib.redirect_stdout(buf):',
|
|
@@ -324,9 +452,9 @@ async function runXiaoETokenScriptOnce(pythonCommand, scriptPath, env) {
|
|
|
324
452
|
' except Exception as exc:',
|
|
325
453
|
' last_error = exc',
|
|
326
454
|
' if data is None:',
|
|
327
|
-
' raise SystemExit("
|
|
455
|
+
' raise SystemExit("XiaoE credential script stdout is not valid JSON object")',
|
|
328
456
|
'else:',
|
|
329
|
-
' raise SystemExit("
|
|
457
|
+
' raise SystemExit("XiaoE credential script produced empty stdout")',
|
|
330
458
|
'print(json.dumps(data, ensure_ascii=False))',
|
|
331
459
|
].join('\n');
|
|
332
460
|
return await new Promise((resolve, reject) => {
|
|
@@ -337,12 +465,12 @@ async function runXiaoETokenScriptOnce(pythonCommand, scriptPath, env) {
|
|
|
337
465
|
}, (error, stdout, stderr) => {
|
|
338
466
|
if (error) {
|
|
339
467
|
const detail = (stderr || error.message || 'unknown error').trim().split('\n').slice(-1)[0];
|
|
340
|
-
reject(new CliError('XiaoE
|
|
468
|
+
reject(new CliError('XiaoE credential script failed: ' + detail, ExitCode.AuthError));
|
|
341
469
|
return;
|
|
342
470
|
}
|
|
343
471
|
const text = stdout.trim();
|
|
344
472
|
if (!text) {
|
|
345
|
-
reject(new CliError('XiaoE
|
|
473
|
+
reject(new CliError('XiaoE credential script returned empty stdout.', ExitCode.AuthError));
|
|
346
474
|
return;
|
|
347
475
|
}
|
|
348
476
|
try {
|
|
@@ -350,19 +478,18 @@ async function runXiaoETokenScriptOnce(pythonCommand, scriptPath, env) {
|
|
|
350
478
|
resolve(parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : null);
|
|
351
479
|
}
|
|
352
480
|
catch {
|
|
353
|
-
reject(new CliError('XiaoE
|
|
481
|
+
reject(new CliError('XiaoE credential script stdout is not valid JSON.', ExitCode.AuthError));
|
|
354
482
|
}
|
|
355
483
|
});
|
|
356
484
|
});
|
|
357
485
|
}
|
|
358
|
-
async function
|
|
359
|
-
const scriptPath = xiaoETokenScriptPath(env);
|
|
486
|
+
async function runXiaoEPythonCredentialScript(scriptPath, baseUrl, env) {
|
|
360
487
|
if (!scriptPath || !existsSync(scriptPath))
|
|
361
488
|
return null;
|
|
362
489
|
let lastError = null;
|
|
363
490
|
for (const pythonCommand of xiaoETokenScriptPythonCandidates(env)) {
|
|
364
491
|
try {
|
|
365
|
-
return await runXiaoETokenScriptOnce(pythonCommand, scriptPath, env);
|
|
492
|
+
return await runXiaoETokenScriptOnce(pythonCommand, scriptPath, baseUrl, env);
|
|
366
493
|
}
|
|
367
494
|
catch (error) {
|
|
368
495
|
lastError = error;
|
|
@@ -373,14 +500,26 @@ async function runXiaoETokenScript(env = process.env) {
|
|
|
373
500
|
break;
|
|
374
501
|
}
|
|
375
502
|
}
|
|
376
|
-
throw lastError instanceof Error ? lastError : new CliError('XiaoE
|
|
503
|
+
throw lastError instanceof Error ? lastError : new CliError('XiaoE credential script failed.', ExitCode.AuthError);
|
|
504
|
+
}
|
|
505
|
+
async function runXiaoETokenScript(baseUrl, env = process.env) {
|
|
506
|
+
return runXiaoEPythonCredentialScript(xiaoETokenScriptPath(env), baseUrl, env);
|
|
377
507
|
}
|
|
378
508
|
async function resolveXiaoETokenScriptCredential(baseUrl, env = process.env) {
|
|
379
|
-
const tokenData = await runXiaoETokenScript(env);
|
|
509
|
+
const tokenData = await runXiaoETokenScript(baseUrl, env);
|
|
380
510
|
if (!tokenData)
|
|
381
511
|
return null;
|
|
382
512
|
return readXiaoECredentialRecord(tokenData, 'xiaoeUserData/tokens.py', baseUrl);
|
|
383
513
|
}
|
|
514
|
+
async function resolveXiaoEBundledEnvScriptCredential(baseUrl, env = process.env) {
|
|
515
|
+
const scriptPath = xiaoEBundledEnvScriptPath();
|
|
516
|
+
if (!scriptPath)
|
|
517
|
+
return null;
|
|
518
|
+
const tokenData = await runXiaoEPythonCredentialScript(scriptPath, baseUrl, env);
|
|
519
|
+
if (!tokenData)
|
|
520
|
+
return null;
|
|
521
|
+
return readXiaoECredentialRecord(tokenData, XIAOE_BUNDLED_ENV_SCRIPT_SOURCE, baseUrl);
|
|
522
|
+
}
|
|
384
523
|
export function xiaoEClientRobotsRoot(env = process.env) {
|
|
385
524
|
if (process.platform === 'win32') {
|
|
386
525
|
const localAppData = envString(env, 'LOCALAPPDATA');
|
|
@@ -415,17 +554,30 @@ export function findXiaoEClientSettingsFile(env = process.env) {
|
|
|
415
554
|
return files.sort((left, right) => statSync(right).mtimeMs - statSync(left).mtimeMs)[0];
|
|
416
555
|
}
|
|
417
556
|
/**
|
|
418
|
-
*
|
|
419
|
-
*
|
|
557
|
+
* Platform-injected credential: XIAOE_USER_TOKEN (+ the XiaoE service address).
|
|
558
|
+
* This is the only source that works in a Linux sandbox with no desktop client
|
|
559
|
+
* and no Python runtime, so it is tried first.
|
|
420
560
|
*/
|
|
421
|
-
export function
|
|
561
|
+
export function readXiaoEPlatformCredential(env = process.env) {
|
|
422
562
|
const envToken = envString(env, 'XIAOE_USER_TOKEN');
|
|
423
|
-
if (envToken)
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
563
|
+
if (!envToken)
|
|
564
|
+
return null;
|
|
565
|
+
const rawErpa = envString(env, 'WEAVER_XIAOE_ERP', 'WEAVER_XIAOE_ERPA', 'WEAVER_XIAOE_ERP_URL', 'XIAOE_ERP', 'XIAOE_ERPA', 'XIAOE_ERP_URL', 'XIAOE_DOMAIN');
|
|
566
|
+
// Hosts may inject the domain without a scheme (e.g. "weapp.example.cn");
|
|
567
|
+
// default to https instead of rejecting the credential outright.
|
|
568
|
+
const erpaUrl = normalizeBaseUrlValue(rawErpa);
|
|
569
|
+
if (!erpaUrl)
|
|
570
|
+
return null;
|
|
571
|
+
return { authToken: envToken, erpaUrl, source: XIAOE_USER_TOKEN_SOURCE };
|
|
572
|
+
}
|
|
573
|
+
export function hasXiaoEPlatformCredential(env = process.env) {
|
|
574
|
+
return Boolean(readXiaoEPlatformCredential(env));
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* Desktop XiaoE client session: the newest XiaoE/Robots/<id>/user_settings.json.
|
|
578
|
+
* Kept for local desktop hosts only; sandboxes must rely on the platform credential.
|
|
579
|
+
*/
|
|
580
|
+
export function readXiaoEClientCredential(env = process.env) {
|
|
429
581
|
const settingsFile = findXiaoEClientSettingsFile(env);
|
|
430
582
|
if (!settingsFile)
|
|
431
583
|
return null;
|
|
@@ -442,49 +594,119 @@ export function readXiaoEClientCredential(env = process.env) {
|
|
|
442
594
|
}
|
|
443
595
|
}
|
|
444
596
|
export function hasXiaoEClientCredential(env = process.env) {
|
|
445
|
-
return Boolean(
|
|
597
|
+
return Boolean(findXiaoEClientSettingsFile(env));
|
|
446
598
|
}
|
|
447
599
|
async function fetchXiaoEClientEteamId(credential, domain) {
|
|
448
600
|
const controller = new AbortController();
|
|
449
601
|
const timer = setTimeout(() => controller.abort(), XIAOE_CLIENT_TOKEN_TIMEOUT_MS);
|
|
450
602
|
try {
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
603
|
+
let endpoint = new URL(`${credential.erpaUrl}${XIAOE_SKILL_ETEAMID_API_PATH}`);
|
|
604
|
+
let resp = null;
|
|
605
|
+
for (let redirects = 0; redirects <= 5; redirects += 1) {
|
|
606
|
+
resp = await fetch(endpoint, {
|
|
607
|
+
method: 'POST',
|
|
608
|
+
headers: {
|
|
609
|
+
'content-type': 'application/json',
|
|
610
|
+
authorization: credential.authToken,
|
|
611
|
+
},
|
|
612
|
+
body: JSON.stringify({ domain: domain || DEFAULT_E10_BASE_URL }),
|
|
613
|
+
redirect: 'manual',
|
|
614
|
+
signal: controller.signal,
|
|
615
|
+
});
|
|
616
|
+
if (resp.status < 300 || resp.status >= 400)
|
|
617
|
+
break;
|
|
618
|
+
if (redirects === 5) {
|
|
619
|
+
throw new CliError('XiaoE client token endpoint redirected too many times.', ExitCode.AuthError);
|
|
620
|
+
}
|
|
621
|
+
const location = resp.headers.get('location');
|
|
622
|
+
if (!location) {
|
|
623
|
+
throw new CliError('XiaoE client token endpoint returned a redirect without Location.', ExitCode.AuthError);
|
|
624
|
+
}
|
|
625
|
+
const next = new URL(location, endpoint);
|
|
626
|
+
const sameOrigin = next.origin === endpoint.origin;
|
|
627
|
+
const sameHostHttpsUpgrade = endpoint.protocol === 'http:'
|
|
628
|
+
&& next.protocol === 'https:'
|
|
629
|
+
&& next.hostname === endpoint.hostname;
|
|
630
|
+
const safeProtocol = next.protocol === 'http:' || next.protocol === 'https:';
|
|
631
|
+
if (!safeProtocol || next.username || next.password || (!sameOrigin && !sameHostHttpsUpgrade)) {
|
|
632
|
+
throw new CliError('XiaoE client token endpoint attempted an unsafe cross-origin redirect.', ExitCode.AuthError);
|
|
633
|
+
}
|
|
634
|
+
await resp.body?.cancel().catch(() => undefined);
|
|
635
|
+
endpoint = next;
|
|
636
|
+
}
|
|
637
|
+
if (!resp?.ok) {
|
|
638
|
+
throw new CliError(`XiaoE client token endpoint returned HTTP ${resp?.status || 0}.`, ExitCode.AuthError);
|
|
463
639
|
}
|
|
464
640
|
const data = await resp.json().catch(() => null);
|
|
465
|
-
|
|
641
|
+
if (!data || typeof data !== 'object' || Array.isArray(data)) {
|
|
642
|
+
throw new CliError('XiaoE client token endpoint returned invalid JSON.', ExitCode.AuthError);
|
|
643
|
+
}
|
|
644
|
+
const payload = data;
|
|
645
|
+
const businessCode = payload.code;
|
|
646
|
+
const businessData = payload.data && typeof payload.data === 'object' && !Array.isArray(payload.data)
|
|
647
|
+
? payload.data
|
|
648
|
+
: {};
|
|
649
|
+
if ((businessCode !== '0000' && businessCode !== 0 && businessCode !== 200) || !readString(businessData, 'eteamId')) {
|
|
650
|
+
const codeText = businessCode === undefined ? 'missing' : String(businessCode);
|
|
651
|
+
throw new CliError(`XiaoE client token exchange failed with business code ${codeText}.`, ExitCode.AuthError);
|
|
652
|
+
}
|
|
653
|
+
return payload;
|
|
466
654
|
}
|
|
467
655
|
finally {
|
|
468
656
|
clearTimeout(timer);
|
|
469
657
|
}
|
|
470
658
|
}
|
|
659
|
+
async function resolveXiaoETokenCredential(credential, baseUrl) {
|
|
660
|
+
const targetBaseUrl = baseUrl || DEFAULT_E10_BASE_URL;
|
|
661
|
+
const payload = await fetchXiaoEClientEteamId(credential, targetBaseUrl);
|
|
662
|
+
// The legacy gateway contract uses numeric code 0 and serves the E10 APIs
|
|
663
|
+
// from the same origin as the token exchange. Newer 0000/200 responses keep
|
|
664
|
+
// the XiaoE service origin separate and use the requested E10 target.
|
|
665
|
+
const resolvedBaseUrl = payload.code === 0 ? credential.erpaUrl : targetBaseUrl;
|
|
666
|
+
return readXiaoECredentialRecord(payload, credential.source, resolvedBaseUrl);
|
|
667
|
+
}
|
|
668
|
+
async function resolveXiaoEPlatformCredential(baseUrl, env = process.env) {
|
|
669
|
+
const credential = readXiaoEPlatformCredential(env);
|
|
670
|
+
if (!credential)
|
|
671
|
+
return null;
|
|
672
|
+
return resolveXiaoETokenCredential(credential, baseUrl);
|
|
673
|
+
}
|
|
471
674
|
async function resolveXiaoEClientCredential(baseUrl, env = process.env) {
|
|
472
675
|
const clientCredential = readXiaoEClientCredential(env);
|
|
473
676
|
if (!clientCredential)
|
|
474
677
|
return null;
|
|
475
|
-
|
|
476
|
-
const payload = await fetchXiaoEClientEteamId(clientCredential, targetBaseUrl);
|
|
477
|
-
return readXiaoECredentialRecord(payload, clientCredential.source, targetBaseUrl);
|
|
678
|
+
return resolveXiaoETokenCredential(clientCredential, baseUrl);
|
|
478
679
|
}
|
|
479
680
|
export function resolveXiaoEPreferredBaseUrl(options) {
|
|
480
681
|
const env = options.env || process.env;
|
|
481
682
|
const explicit = normalizeUrl(options.explicitBaseUrl || '');
|
|
482
683
|
if (explicit)
|
|
483
684
|
return explicit;
|
|
484
|
-
const
|
|
485
|
-
if (
|
|
486
|
-
return
|
|
487
|
-
|
|
685
|
+
const configured = resolveXiaoEConfiguredBaseUrl(env);
|
|
686
|
+
if (configured)
|
|
687
|
+
return configured;
|
|
688
|
+
const profileBaseUrl = normalizeUrl(options.profileBaseUrl || '');
|
|
689
|
+
if (profileBaseUrl)
|
|
690
|
+
return profileBaseUrl;
|
|
691
|
+
return hasXiaoEEnv(env) ? DEFAULT_E10_BASE_URL : '';
|
|
692
|
+
}
|
|
693
|
+
export function shouldUseXiaoEBrowserFallback(platform = process.platform) {
|
|
694
|
+
return platform === 'darwin' || platform === 'win32';
|
|
695
|
+
}
|
|
696
|
+
export function createXiaoEUnifiedLoginUnavailableError(baseUrl = '') {
|
|
697
|
+
const target = normalizeUrl(baseUrl);
|
|
698
|
+
const message = [
|
|
699
|
+
'XiaoE unified login did not return a usable E10 session.',
|
|
700
|
+
target ? `Resolved E10 baseUrl: ${target}.` : 'No E10 baseUrl could be resolved.',
|
|
701
|
+
'This web/Linux host cannot complete a localhost browser OAuth callback.',
|
|
702
|
+
'Refresh the XiaoE web session or restore host credential injection before retrying.',
|
|
703
|
+
].join(' ');
|
|
704
|
+
return Object.assign(new CliError(message, ExitCode.AuthError), {
|
|
705
|
+
type: 'authentication',
|
|
706
|
+
subtype: 'xiaoe_web_credential_unavailable',
|
|
707
|
+
retryable: false,
|
|
708
|
+
stage: 'xiaoe_unified_login',
|
|
709
|
+
});
|
|
488
710
|
}
|
|
489
711
|
async function resolveUserInfo(baseUrl, eteamsId, rawCookie = '', agentType = '') {
|
|
490
712
|
const resp = await fetch(`${baseUrl}/api/baseserver/layout/teamsCheck?clientType=not_xinchuang&client=WEB&domainName=${encodeURIComponent(baseUrl)}`, {
|
|
@@ -500,11 +722,11 @@ async function resolveUserInfo(baseUrl, eteamsId, rawCookie = '', agentType = ''
|
|
|
500
722
|
redirect: 'manual',
|
|
501
723
|
});
|
|
502
724
|
const data = await resp.json().catch(() => ({}));
|
|
503
|
-
return readTeamsCheckUserInfo(resp, data);
|
|
725
|
+
return { userInfo: readTeamsCheckUserInfo(resp, data), setCookie: extractRawSetCookie(resp) };
|
|
504
726
|
}
|
|
505
727
|
async function saveXiaoEUnifiedLogin(input) {
|
|
506
728
|
const baseUrl = normalizeUrl(input.baseUrl);
|
|
507
|
-
const userInfo = await resolveUserInfo(baseUrl, input.eteamsId, input.rawCookie || '', XIAOE_AGENT_TYPE);
|
|
729
|
+
const { userInfo, setCookie } = await resolveUserInfo(baseUrl, input.eteamsId, input.rawCookie || '', XIAOE_AGENT_TYPE);
|
|
508
730
|
if (!userInfo.userId) {
|
|
509
731
|
throw new CliError('Could not resolve userId. Check whether XiaoE unified login is valid for this E10 domain.', ExitCode.AuthError);
|
|
510
732
|
}
|
|
@@ -514,6 +736,7 @@ async function saveXiaoEUnifiedLogin(input) {
|
|
|
514
736
|
const cookies = { ETEAMSID: input.eteamsId };
|
|
515
737
|
if (input.rawCookie)
|
|
516
738
|
cookies.raw = input.rawCookie;
|
|
739
|
+
const ztSession = extractSessionTicket(setCookie, input.rawCookie || '');
|
|
517
740
|
const authData = {
|
|
518
741
|
cookies,
|
|
519
742
|
userId: userInfo.userId,
|
|
@@ -531,6 +754,7 @@ async function saveXiaoEUnifiedLogin(input) {
|
|
|
531
754
|
credentialSourceStep: input.credentialSource.credentialSourceStep,
|
|
532
755
|
credentialSourceType: input.credentialSource.credentialSourceType,
|
|
533
756
|
credentialSourceLabel: input.credentialSource.credentialSourceLabel,
|
|
757
|
+
...(ztSession ? { ztSession } : {}),
|
|
534
758
|
};
|
|
535
759
|
const authFile = saveAuthFile(authData, profile);
|
|
536
760
|
saveProfileConfig(baseUrl, baseUrl, profile, tenantKey);
|
|
@@ -557,43 +781,84 @@ export async function tryXiaoEUnifiedLogin(options = {}) {
|
|
|
557
781
|
const explicitBaseUrl = normalizeUrl(options.baseUrl || '');
|
|
558
782
|
const baseUrl = resolveXiaoEPreferredBaseUrl({
|
|
559
783
|
explicitBaseUrl,
|
|
784
|
+
profileBaseUrl: options.profileBaseUrl,
|
|
560
785
|
env,
|
|
561
786
|
});
|
|
562
787
|
const mayUseTokenScript = options.allowTokenScript || hasXiaoEEnv(env);
|
|
563
788
|
if (!baseUrl && !mayUseTokenScript)
|
|
564
789
|
return null;
|
|
565
790
|
let credential = null;
|
|
566
|
-
|
|
791
|
+
// Step 1 — platform-injected credential (XIAOE_USER_TOKEN + XIAOE_DOMAIN).
|
|
792
|
+
// Works in sandboxes: no desktop client, no Python, no developer-machine paths.
|
|
793
|
+
if (hasXiaoEPlatformCredential(env)) {
|
|
794
|
+
options.logger?.info('Trying XiaoE platform credential from XIAOE_USER_TOKEN...');
|
|
795
|
+
try {
|
|
796
|
+
credential = await resolveXiaoEPlatformCredential(baseUrl || DEFAULT_E10_BASE_URL, env);
|
|
797
|
+
}
|
|
798
|
+
catch (error) {
|
|
799
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
800
|
+
options.logger?.warn('XiaoE platform credential failed. ' + message);
|
|
801
|
+
}
|
|
802
|
+
if (!credential) {
|
|
803
|
+
options.logger?.warn(options.allowBundledEnvScript
|
|
804
|
+
? 'XiaoE platform credential did not return usable ETEAMSID; will try the CLI-bundled xiaoe_env.py compatibility path next.'
|
|
805
|
+
: 'XiaoE platform credential did not return usable ETEAMSID; will try xiaoeUserData/tokens.py next.');
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
// Step 1b — compatibility path used by the reference login package. The
|
|
809
|
+
// script is shipped with the CLI npm package (never with a Skill) and its
|
|
810
|
+
// stdout stays inside this process so credentials are not exposed to agents.
|
|
811
|
+
if (!credential && options.allowBundledEnvScript && hasXiaoEPlatformCredential(env)) {
|
|
812
|
+
const scriptPath = xiaoEBundledEnvScriptPath();
|
|
813
|
+
if (!scriptPath) {
|
|
814
|
+
options.logger?.warn('CLI-bundled xiaoe_env.py was not found; will try desktop XiaoE credential sources next.');
|
|
815
|
+
}
|
|
816
|
+
else {
|
|
817
|
+
options.logger?.info('Trying CLI-bundled xiaoe_env.py compatibility credential...');
|
|
818
|
+
try {
|
|
819
|
+
credential = await resolveXiaoEBundledEnvScriptCredential(baseUrl || DEFAULT_E10_BASE_URL, env);
|
|
820
|
+
}
|
|
821
|
+
catch (error) {
|
|
822
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
823
|
+
options.logger?.warn('CLI-bundled xiaoe_env.py failed. ' + message);
|
|
824
|
+
}
|
|
825
|
+
if (!credential) {
|
|
826
|
+
options.logger?.warn('CLI-bundled xiaoe_env.py did not return usable ETEAMSID; will try xiaoeUserData/tokens.py next.');
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
// Step 2 — host tokens.py (desktop host only; Linux sandboxes have no such path).
|
|
831
|
+
if (!credential && mayUseTokenScript) {
|
|
567
832
|
const tokenScriptPath = xiaoETokenScriptPath(env);
|
|
568
833
|
if (!tokenScriptPath) {
|
|
569
|
-
options.logger?.warn('XiaoE token script not found. Expected xiaoeUserData/tokens.py; will try the
|
|
834
|
+
options.logger?.warn('XiaoE token script not found. Expected xiaoeUserData/tokens.py; will try the local XiaoE client session next.');
|
|
570
835
|
}
|
|
571
836
|
else {
|
|
572
837
|
options.logger?.info('Trying XiaoE token script credential...');
|
|
573
838
|
try {
|
|
574
|
-
credential = await resolveXiaoETokenScriptCredential(
|
|
839
|
+
credential = await resolveXiaoETokenScriptCredential(baseUrl || DEFAULT_E10_BASE_URL, env);
|
|
575
840
|
}
|
|
576
841
|
catch (error) {
|
|
577
842
|
const message = error instanceof Error ? error.message : String(error);
|
|
578
843
|
options.logger?.warn('XiaoE token script failed. ' + message);
|
|
579
844
|
}
|
|
580
845
|
if (!credential) {
|
|
581
|
-
options.logger?.warn('XiaoE token script did not return usable ETEAMSID; will try the
|
|
846
|
+
options.logger?.warn('XiaoE token script did not return usable ETEAMSID; will try the local XiaoE client session next.');
|
|
582
847
|
}
|
|
583
848
|
}
|
|
584
|
-
//
|
|
585
|
-
// session and exchanges it for an eteamId, so
|
|
586
|
-
//
|
|
849
|
+
// Step 3 — built-in Node equivalent of tokens.py: reads the local XiaoE
|
|
850
|
+
// client session and exchanges it for an eteamId, so Python availability
|
|
851
|
+
// no longer blocks unified login.
|
|
587
852
|
if (!credential && hasXiaoEClientCredential(env)) {
|
|
588
853
|
try {
|
|
589
|
-
credential = await resolveXiaoEClientCredential(
|
|
854
|
+
credential = await resolveXiaoEClientCredential(baseUrl || DEFAULT_E10_BASE_URL, env);
|
|
590
855
|
}
|
|
591
856
|
catch (error) {
|
|
592
857
|
const message = error instanceof Error ? error.message : String(error);
|
|
593
858
|
options.logger?.warn('Built-in XiaoE client credential failed. ' + message);
|
|
594
859
|
}
|
|
595
860
|
if (!credential) {
|
|
596
|
-
options.logger?.warn('Built-in XiaoE client credential did not return usable ETEAMSID; will try
|
|
861
|
+
options.logger?.warn('Built-in XiaoE client credential did not return usable ETEAMSID; will try WEAVER_XIAOE_* credentials next.');
|
|
597
862
|
}
|
|
598
863
|
}
|
|
599
864
|
}
|