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
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
1
5
|
import { CliError, ExitCode } from '../../../core/errors.js';
|
|
2
6
|
import { buildE10CookieHeader, E10AuthSession, profileNameFromUrl, readTeamsCheckUserInfo, saveAuthFile, saveProfileConfig, setActiveProfile, } from './session.js';
|
|
3
7
|
export const DEFAULT_E10_BASE_URL = 'https://www.e-cology.com.cn';
|
|
4
|
-
export const XIAOE_AGENT_TYPE = '
|
|
8
|
+
export const XIAOE_AGENT_TYPE = 'XiaoE';
|
|
5
9
|
const XIAOE_UNIFIED_LOGIN_MAP_ENV_KEYS = [
|
|
6
10
|
'XIAOE_E10_AUTH_MAP',
|
|
7
11
|
'XIAOE_E10_DOMAIN_ETEAMSID_MAP',
|
|
@@ -27,6 +31,13 @@ const XIAOE_SINGLE_ETEAMSID_ENV_KEYS = [
|
|
|
27
31
|
'XIAOE_E10_ETEAMSID',
|
|
28
32
|
'XIAOE_E10_ETEAM_ID',
|
|
29
33
|
];
|
|
34
|
+
const XIAOE_TOKEN_SCRIPT_ENV_KEY = 'XIAOE_TOKEN_SCRIPT_PATH';
|
|
35
|
+
const XIAOE_TOKEN_SCRIPT_PYTHON_ENV_KEY = 'XIAOE_TOKEN_SCRIPT_PYTHON';
|
|
36
|
+
const XIAOE_TOKEN_SCRIPT_TIMEOUT_MS = 15_000;
|
|
37
|
+
const XIAOE_CLIENT_TOKEN_TIMEOUT_MS = 15_000;
|
|
38
|
+
export const XIAOE_SKILL_ETEAMID_API_PATH = '/spec-apps/api/app/rpachat/skill/extend/eteamId';
|
|
39
|
+
export const XIAOE_CLIENT_SETTINGS_SOURCE = 'XiaoE/Robots/user_settings.json';
|
|
40
|
+
export const XIAOE_USER_TOKEN_SOURCE = 'XIAOE_USER_TOKEN';
|
|
30
41
|
function envString(env, ...keys) {
|
|
31
42
|
for (const key of keys) {
|
|
32
43
|
const value = env[key];
|
|
@@ -41,6 +52,29 @@ function hasEnvPrefix(env, prefix) {
|
|
|
41
52
|
export function hasXiaoEEnv(env = process.env) {
|
|
42
53
|
return hasEnvPrefix(env, 'XIAOE');
|
|
43
54
|
}
|
|
55
|
+
export function describeXiaoECredentialSource(source) {
|
|
56
|
+
if (source === 'xiaoeUserData/tokens.py') {
|
|
57
|
+
return {
|
|
58
|
+
credentialSourceStep: 1,
|
|
59
|
+
credentialSourceType: 'token_script',
|
|
60
|
+
credentialSourceLabel: 'xiaoeUserData/tokens.py',
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
if (source === XIAOE_CLIENT_SETTINGS_SOURCE || source === XIAOE_USER_TOKEN_SOURCE) {
|
|
64
|
+
return {
|
|
65
|
+
credentialSourceStep: 2,
|
|
66
|
+
credentialSourceType: 'xiaoe_client',
|
|
67
|
+
credentialSourceLabel: source === XIAOE_USER_TOKEN_SOURCE
|
|
68
|
+
? 'XiaoE client user token'
|
|
69
|
+
: 'XiaoE client settings',
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
credentialSourceStep: 3,
|
|
74
|
+
credentialSourceType: 'xiaoe_env',
|
|
75
|
+
credentialSourceLabel: 'XIAOE_* environment variables',
|
|
76
|
+
};
|
|
77
|
+
}
|
|
44
78
|
function normalizeUrl(value) {
|
|
45
79
|
return value.replace(/\/+$/, '');
|
|
46
80
|
}
|
|
@@ -54,6 +88,36 @@ function readString(data, ...keys) {
|
|
|
54
88
|
}
|
|
55
89
|
return '';
|
|
56
90
|
}
|
|
91
|
+
function cookieString(value) {
|
|
92
|
+
if (!value)
|
|
93
|
+
return '';
|
|
94
|
+
if (typeof value === 'string')
|
|
95
|
+
return value;
|
|
96
|
+
if (typeof value === 'object' && !Array.isArray(value)) {
|
|
97
|
+
return Object.entries(value)
|
|
98
|
+
.filter(([, cookieValue]) => cookieValue !== undefined && cookieValue !== null && cookieValue !== '')
|
|
99
|
+
.map(([name, cookieValue]) => `${name}=${String(cookieValue)}`)
|
|
100
|
+
.join('; ');
|
|
101
|
+
}
|
|
102
|
+
return '';
|
|
103
|
+
}
|
|
104
|
+
function cookieValue(rawCookie, name) {
|
|
105
|
+
const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
106
|
+
const match = rawCookie.match(new RegExp(`(?:^|;\\s*)${escaped}=([^;]+)`));
|
|
107
|
+
return match?.[1] || '';
|
|
108
|
+
}
|
|
109
|
+
function recordCandidates(value) {
|
|
110
|
+
return [
|
|
111
|
+
value,
|
|
112
|
+
value.data,
|
|
113
|
+
value.result,
|
|
114
|
+
value.auth,
|
|
115
|
+
value.session,
|
|
116
|
+
value.e10,
|
|
117
|
+
value.e10Auth,
|
|
118
|
+
value.e10_auth,
|
|
119
|
+
].filter((entry) => Boolean(entry) && typeof entry === 'object' && !Array.isArray(entry));
|
|
120
|
+
}
|
|
57
121
|
function normalizeDomainKey(value) {
|
|
58
122
|
const raw = normalizeUrl(value.trim());
|
|
59
123
|
if (!raw)
|
|
@@ -84,11 +148,22 @@ function domainMatches(baseUrl, candidate) {
|
|
|
84
148
|
|| Boolean(base.host && target.host && base.host === target.host);
|
|
85
149
|
}
|
|
86
150
|
function readXiaoECredentialRecord(value, source, fallbackBaseUrl = '') {
|
|
87
|
-
const
|
|
88
|
-
const
|
|
151
|
+
const candidates = recordCandidates(value);
|
|
152
|
+
const baseUrl = candidates
|
|
153
|
+
.map((entry) => readString(entry, 'domain', 'baseUrl', 'base_url', 'url', 'host', 'e10BaseUrl', 'e10_base_url'))
|
|
154
|
+
.find(Boolean)
|
|
155
|
+
|| fallbackBaseUrl;
|
|
156
|
+
const rawCookie = candidates
|
|
157
|
+
.map((entry) => cookieString(entry.cookies || entry.cookie || entry.rawCookie || entry.raw_cookie))
|
|
158
|
+
.find(Boolean)
|
|
159
|
+
|| '';
|
|
160
|
+
const eteamsId = candidates
|
|
161
|
+
.map((entry) => readString(entry, 'eteamsId', 'eteamsid', 'ETEAMSID', 'eteamId', 'eteam_id', 'eTeamId', 'eteamID', 'eteam_id'))
|
|
162
|
+
.find(Boolean)
|
|
163
|
+
|| cookieValue(rawCookie, 'ETEAMSID');
|
|
89
164
|
if (!baseUrl || !eteamsId)
|
|
90
165
|
return null;
|
|
91
|
-
return { baseUrl: normalizeUrl(baseUrl), eteamsId, source };
|
|
166
|
+
return { baseUrl: normalizeUrl(baseUrl), eteamsId, source, rawCookie };
|
|
92
167
|
}
|
|
93
168
|
function parseXiaoEUnifiedLoginMap(raw, source) {
|
|
94
169
|
const value = raw.trim();
|
|
@@ -172,6 +247,235 @@ export function resolveXiaoEDefaultBaseUrl(env = process.env) {
|
|
|
172
247
|
return normalizeUrl(firstCredential.baseUrl);
|
|
173
248
|
return hasXiaoEEnv(env) ? DEFAULT_E10_BASE_URL : '';
|
|
174
249
|
}
|
|
250
|
+
function uniqueStrings(values) {
|
|
251
|
+
const seen = new Set();
|
|
252
|
+
const result = [];
|
|
253
|
+
for (const value of values) {
|
|
254
|
+
if (!value || seen.has(value))
|
|
255
|
+
continue;
|
|
256
|
+
seen.add(value);
|
|
257
|
+
result.push(value);
|
|
258
|
+
}
|
|
259
|
+
return result;
|
|
260
|
+
}
|
|
261
|
+
function xiaoETokenScriptCandidatePaths(env = process.env) {
|
|
262
|
+
const explicit = envString(env, XIAOE_TOKEN_SCRIPT_ENV_KEY);
|
|
263
|
+
if (explicit)
|
|
264
|
+
return [explicit];
|
|
265
|
+
if (process.platform === 'win32') {
|
|
266
|
+
const localAppData = envString(env, 'LOCALAPPDATA');
|
|
267
|
+
const userProfile = envString(env, 'USERPROFILE');
|
|
268
|
+
return uniqueStrings([
|
|
269
|
+
localAppData ? join(localAppData, 'xiaoeUserData', 'tokens.py') : '',
|
|
270
|
+
userProfile ? join(userProfile, 'AppData', 'Local', 'xiaoeUserData', 'tokens.py') : '',
|
|
271
|
+
]);
|
|
272
|
+
}
|
|
273
|
+
const envHome = envString(env, 'HOME');
|
|
274
|
+
const nodeHome = homedir();
|
|
275
|
+
return uniqueStrings([
|
|
276
|
+
envHome ? join(envHome, 'Library', 'Application Support', 'xiaoeUserData', 'tokens.py') : '',
|
|
277
|
+
nodeHome ? join(nodeHome, 'Library', 'Application Support', 'xiaoeUserData', 'tokens.py') : '',
|
|
278
|
+
]);
|
|
279
|
+
}
|
|
280
|
+
function xiaoETokenScriptPath(env = process.env) {
|
|
281
|
+
return xiaoETokenScriptCandidatePaths(env).find((path) => existsSync(path)) || '';
|
|
282
|
+
}
|
|
283
|
+
export function hasXiaoETokenScript(env = process.env) {
|
|
284
|
+
const explicit = envString(env, XIAOE_TOKEN_SCRIPT_ENV_KEY);
|
|
285
|
+
if (explicit)
|
|
286
|
+
return existsSync(explicit);
|
|
287
|
+
const scriptPath = xiaoETokenScriptPath(env);
|
|
288
|
+
return Boolean(scriptPath && existsSync(scriptPath));
|
|
289
|
+
}
|
|
290
|
+
function xiaoETokenScriptPythonCandidates(env = process.env) {
|
|
291
|
+
const explicit = envString(env, XIAOE_TOKEN_SCRIPT_PYTHON_ENV_KEY);
|
|
292
|
+
if (explicit)
|
|
293
|
+
return [explicit];
|
|
294
|
+
return uniqueStrings(process.platform === 'win32'
|
|
295
|
+
? ['python', 'python3', 'py']
|
|
296
|
+
: ['python3', '/usr/bin/python3', 'python']);
|
|
297
|
+
}
|
|
298
|
+
function isXiaoETokenScriptEnvFailure(message) {
|
|
299
|
+
return /No module named|ModuleNotFoundError|ENOENT|command not found|not recognized as an internal|cannot find the file/i
|
|
300
|
+
.test(message);
|
|
301
|
+
}
|
|
302
|
+
async function runXiaoETokenScriptOnce(pythonCommand, scriptPath, env) {
|
|
303
|
+
const wrapper = [
|
|
304
|
+
'import ast,contextlib,io,json,runpy,sys',
|
|
305
|
+
`path = ${JSON.stringify(scriptPath)}`,
|
|
306
|
+
'buf = io.StringIO()',
|
|
307
|
+
'data = None',
|
|
308
|
+
'with contextlib.redirect_stdout(buf):',
|
|
309
|
+
' runpy.run_path(path, run_name="__main__")',
|
|
310
|
+
'text = buf.getvalue().strip()',
|
|
311
|
+
'if text:',
|
|
312
|
+
' lines = [line.strip() for line in text.splitlines() if line.strip()]',
|
|
313
|
+
' candidates = [text] + (lines[-1:] if lines else [])',
|
|
314
|
+
' last_error = None',
|
|
315
|
+
' for candidate in candidates:',
|
|
316
|
+
' try:',
|
|
317
|
+
' data = json.loads(candidate)',
|
|
318
|
+
' break',
|
|
319
|
+
' except Exception as exc:',
|
|
320
|
+
' last_error = exc',
|
|
321
|
+
' try:',
|
|
322
|
+
' data = ast.literal_eval(candidate)',
|
|
323
|
+
' break',
|
|
324
|
+
' except Exception as exc:',
|
|
325
|
+
' last_error = exc',
|
|
326
|
+
' if data is None:',
|
|
327
|
+
' raise SystemExit("tokens.py stdout is not valid JSON object")',
|
|
328
|
+
'else:',
|
|
329
|
+
' raise SystemExit("tokens.py produced empty stdout")',
|
|
330
|
+
'print(json.dumps(data, ensure_ascii=False))',
|
|
331
|
+
].join('\n');
|
|
332
|
+
return await new Promise((resolve, reject) => {
|
|
333
|
+
execFile(pythonCommand, ['-c', wrapper], {
|
|
334
|
+
env,
|
|
335
|
+
timeout: XIAOE_TOKEN_SCRIPT_TIMEOUT_MS,
|
|
336
|
+
maxBuffer: 1024 * 1024,
|
|
337
|
+
}, (error, stdout, stderr) => {
|
|
338
|
+
if (error) {
|
|
339
|
+
const detail = (stderr || error.message || 'unknown error').trim().split('\n').slice(-1)[0];
|
|
340
|
+
reject(new CliError('XiaoE token script failed: ' + detail, ExitCode.AuthError));
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
const text = stdout.trim();
|
|
344
|
+
if (!text) {
|
|
345
|
+
reject(new CliError('XiaoE token script returned empty stdout.', ExitCode.AuthError));
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
try {
|
|
349
|
+
const parsed = JSON.parse(text);
|
|
350
|
+
resolve(parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : null);
|
|
351
|
+
}
|
|
352
|
+
catch {
|
|
353
|
+
reject(new CliError('XiaoE token script stdout is not valid JSON.', ExitCode.AuthError));
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
async function runXiaoETokenScript(env = process.env) {
|
|
359
|
+
const scriptPath = xiaoETokenScriptPath(env);
|
|
360
|
+
if (!scriptPath || !existsSync(scriptPath))
|
|
361
|
+
return null;
|
|
362
|
+
let lastError = null;
|
|
363
|
+
for (const pythonCommand of xiaoETokenScriptPythonCandidates(env)) {
|
|
364
|
+
try {
|
|
365
|
+
return await runXiaoETokenScriptOnce(pythonCommand, scriptPath, env);
|
|
366
|
+
}
|
|
367
|
+
catch (error) {
|
|
368
|
+
lastError = error;
|
|
369
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
370
|
+
// Only keep probing other interpreters when this one could not even load
|
|
371
|
+
// the script's dependencies; a real script error would repeat everywhere.
|
|
372
|
+
if (!isXiaoETokenScriptEnvFailure(message))
|
|
373
|
+
break;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
throw lastError instanceof Error ? lastError : new CliError('XiaoE token script failed.', ExitCode.AuthError);
|
|
377
|
+
}
|
|
378
|
+
async function resolveXiaoETokenScriptCredential(baseUrl, env = process.env) {
|
|
379
|
+
const tokenData = await runXiaoETokenScript(env);
|
|
380
|
+
if (!tokenData)
|
|
381
|
+
return null;
|
|
382
|
+
return readXiaoECredentialRecord(tokenData, 'xiaoeUserData/tokens.py', baseUrl);
|
|
383
|
+
}
|
|
384
|
+
export function xiaoEClientRobotsRoot(env = process.env) {
|
|
385
|
+
if (process.platform === 'win32') {
|
|
386
|
+
const localAppData = envString(env, 'LOCALAPPDATA');
|
|
387
|
+
return localAppData ? join(localAppData, 'XiaoE', 'Robots') : '';
|
|
388
|
+
}
|
|
389
|
+
const home = envString(env, 'HOME') || homedir();
|
|
390
|
+
return home ? join(home, 'Library', 'Application Support', 'XiaoE', 'Robots') : '';
|
|
391
|
+
}
|
|
392
|
+
export function findXiaoEClientSettingsFile(env = process.env) {
|
|
393
|
+
const root = xiaoEClientRobotsRoot(env);
|
|
394
|
+
if (!root || !existsSync(root))
|
|
395
|
+
return '';
|
|
396
|
+
let entries = [];
|
|
397
|
+
try {
|
|
398
|
+
entries = readdirSync(root);
|
|
399
|
+
}
|
|
400
|
+
catch {
|
|
401
|
+
return '';
|
|
402
|
+
}
|
|
403
|
+
const files = entries
|
|
404
|
+
.map((name) => join(root, name, 'user_settings.json'))
|
|
405
|
+
.filter((path) => {
|
|
406
|
+
try {
|
|
407
|
+
return statSync(path).isFile();
|
|
408
|
+
}
|
|
409
|
+
catch {
|
|
410
|
+
return false;
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
if (files.length === 0)
|
|
414
|
+
return '';
|
|
415
|
+
return files.sort((left, right) => statSync(right).mtimeMs - statSync(left).mtimeMs)[0];
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Mirror of the host `xiaoeUserData/tokens.py` credential chain, implemented in
|
|
419
|
+
* Node so the CLI does not depend on a Python runtime that has `requests`.
|
|
420
|
+
*/
|
|
421
|
+
export function readXiaoEClientCredential(env = process.env) {
|
|
422
|
+
const envToken = envString(env, 'XIAOE_USER_TOKEN');
|
|
423
|
+
if (envToken) {
|
|
424
|
+
const erpaUrl = normalizeUrl(envString(env, 'XIAOE_ERP', 'XIAOE_ERPA', 'XIAOE_ERP_URL', 'XIAOE_DOMAIN'));
|
|
425
|
+
if (/^https?:\/\//i.test(erpaUrl)) {
|
|
426
|
+
return { authToken: envToken, erpaUrl, source: XIAOE_USER_TOKEN_SOURCE };
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
const settingsFile = findXiaoEClientSettingsFile(env);
|
|
430
|
+
if (!settingsFile)
|
|
431
|
+
return null;
|
|
432
|
+
try {
|
|
433
|
+
const parsed = JSON.parse(readFileSync(settingsFile, 'utf8'));
|
|
434
|
+
const authToken = readString(parsed, 'session_id', 'sessionId');
|
|
435
|
+
const erpaUrl = normalizeUrl(readString(parsed, 'erpa', 'erpa_url', 'erpaUrl'));
|
|
436
|
+
if (!authToken || !erpaUrl)
|
|
437
|
+
return null;
|
|
438
|
+
return { authToken, erpaUrl, source: XIAOE_CLIENT_SETTINGS_SOURCE };
|
|
439
|
+
}
|
|
440
|
+
catch {
|
|
441
|
+
return null;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
export function hasXiaoEClientCredential(env = process.env) {
|
|
445
|
+
return Boolean(envString(env, 'XIAOE_USER_TOKEN')) || Boolean(findXiaoEClientSettingsFile(env));
|
|
446
|
+
}
|
|
447
|
+
async function fetchXiaoEClientEteamId(credential, domain) {
|
|
448
|
+
const controller = new AbortController();
|
|
449
|
+
const timer = setTimeout(() => controller.abort(), XIAOE_CLIENT_TOKEN_TIMEOUT_MS);
|
|
450
|
+
try {
|
|
451
|
+
const resp = await fetch(`${credential.erpaUrl}${XIAOE_SKILL_ETEAMID_API_PATH}`, {
|
|
452
|
+
method: 'POST',
|
|
453
|
+
headers: {
|
|
454
|
+
'content-type': 'application/json',
|
|
455
|
+
authorization: credential.authToken,
|
|
456
|
+
},
|
|
457
|
+
body: JSON.stringify({ domain: domain || DEFAULT_E10_BASE_URL }),
|
|
458
|
+
redirect: 'manual',
|
|
459
|
+
signal: controller.signal,
|
|
460
|
+
});
|
|
461
|
+
if (!resp.ok) {
|
|
462
|
+
throw new CliError(`XiaoE client token endpoint returned HTTP ${resp.status}.`, ExitCode.AuthError);
|
|
463
|
+
}
|
|
464
|
+
const data = await resp.json().catch(() => null);
|
|
465
|
+
return data && typeof data === 'object' && !Array.isArray(data) ? data : {};
|
|
466
|
+
}
|
|
467
|
+
finally {
|
|
468
|
+
clearTimeout(timer);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
async function resolveXiaoEClientCredential(baseUrl, env = process.env) {
|
|
472
|
+
const clientCredential = readXiaoEClientCredential(env);
|
|
473
|
+
if (!clientCredential)
|
|
474
|
+
return null;
|
|
475
|
+
const targetBaseUrl = baseUrl || DEFAULT_E10_BASE_URL;
|
|
476
|
+
const payload = await fetchXiaoEClientEteamId(clientCredential, targetBaseUrl);
|
|
477
|
+
return readXiaoECredentialRecord(payload, clientCredential.source, targetBaseUrl);
|
|
478
|
+
}
|
|
175
479
|
export function resolveXiaoEPreferredBaseUrl(options) {
|
|
176
480
|
const env = options.env || process.env;
|
|
177
481
|
const explicit = normalizeUrl(options.explicitBaseUrl || '');
|
|
@@ -200,15 +504,18 @@ async function resolveUserInfo(baseUrl, eteamsId, rawCookie = '', agentType = ''
|
|
|
200
504
|
}
|
|
201
505
|
async function saveXiaoEUnifiedLogin(input) {
|
|
202
506
|
const baseUrl = normalizeUrl(input.baseUrl);
|
|
203
|
-
const userInfo = await resolveUserInfo(baseUrl, input.eteamsId, '', XIAOE_AGENT_TYPE);
|
|
507
|
+
const userInfo = await resolveUserInfo(baseUrl, input.eteamsId, input.rawCookie || '', XIAOE_AGENT_TYPE);
|
|
204
508
|
if (!userInfo.userId) {
|
|
205
509
|
throw new CliError('Could not resolve userId. Check whether XiaoE unified login is valid for this E10 domain.', ExitCode.AuthError);
|
|
206
510
|
}
|
|
207
511
|
const tenantKey = userInfo.tenantKey || input.tenantKeyFallback || '';
|
|
208
512
|
const profile = input.profileHint || profileNameFromUrl(baseUrl);
|
|
209
513
|
setActiveProfile(profile);
|
|
514
|
+
const cookies = { ETEAMSID: input.eteamsId };
|
|
515
|
+
if (input.rawCookie)
|
|
516
|
+
cookies.raw = input.rawCookie;
|
|
210
517
|
const authData = {
|
|
211
|
-
cookies
|
|
518
|
+
cookies,
|
|
212
519
|
userId: userInfo.userId,
|
|
213
520
|
tenantKey,
|
|
214
521
|
tenantName: userInfo.tenantName,
|
|
@@ -221,6 +528,9 @@ async function saveXiaoEUnifiedLogin(input) {
|
|
|
221
528
|
passportUrl: baseUrl,
|
|
222
529
|
cookieSavedAt: Date.now(),
|
|
223
530
|
loginSource: 'xiaoe-unified',
|
|
531
|
+
credentialSourceStep: input.credentialSource.credentialSourceStep,
|
|
532
|
+
credentialSourceType: input.credentialSource.credentialSourceType,
|
|
533
|
+
credentialSourceLabel: input.credentialSource.credentialSourceLabel,
|
|
224
534
|
};
|
|
225
535
|
const authFile = saveAuthFile(authData, profile);
|
|
226
536
|
saveProfileConfig(baseUrl, baseUrl, profile, tenantKey);
|
|
@@ -237,32 +547,74 @@ async function saveXiaoEUnifiedLogin(input) {
|
|
|
237
547
|
authFile,
|
|
238
548
|
agentType: XIAOE_AGENT_TYPE,
|
|
239
549
|
version: '',
|
|
550
|
+
loginSource: 'xiaoe-unified',
|
|
551
|
+
...input.credentialSource,
|
|
240
552
|
},
|
|
241
553
|
};
|
|
242
554
|
}
|
|
243
555
|
export async function tryXiaoEUnifiedLogin(options = {}) {
|
|
244
556
|
const env = options.env || process.env;
|
|
557
|
+
const explicitBaseUrl = normalizeUrl(options.baseUrl || '');
|
|
245
558
|
const baseUrl = resolveXiaoEPreferredBaseUrl({
|
|
246
|
-
explicitBaseUrl
|
|
559
|
+
explicitBaseUrl,
|
|
247
560
|
env,
|
|
248
561
|
});
|
|
249
|
-
|
|
562
|
+
const mayUseTokenScript = options.allowTokenScript || hasXiaoEEnv(env);
|
|
563
|
+
if (!baseUrl && !mayUseTokenScript)
|
|
250
564
|
return null;
|
|
251
|
-
|
|
565
|
+
let credential = null;
|
|
566
|
+
if (mayUseTokenScript) {
|
|
567
|
+
const tokenScriptPath = xiaoETokenScriptPath(env);
|
|
568
|
+
if (!tokenScriptPath) {
|
|
569
|
+
options.logger?.warn('XiaoE token script not found. Expected xiaoeUserData/tokens.py; will try the built-in XiaoE client credential next.');
|
|
570
|
+
}
|
|
571
|
+
else {
|
|
572
|
+
options.logger?.info('Trying XiaoE token script credential...');
|
|
573
|
+
try {
|
|
574
|
+
credential = await resolveXiaoETokenScriptCredential(explicitBaseUrl || DEFAULT_E10_BASE_URL, env);
|
|
575
|
+
}
|
|
576
|
+
catch (error) {
|
|
577
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
578
|
+
options.logger?.warn('XiaoE token script failed. ' + message);
|
|
579
|
+
}
|
|
580
|
+
if (!credential) {
|
|
581
|
+
options.logger?.warn('XiaoE token script did not return usable ETEAMSID; will try the built-in XiaoE client credential next.');
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
// Built-in Node equivalent of tokens.py: reads the local XiaoE client
|
|
585
|
+
// session and exchanges it for an eteamId, so a Python runtime without
|
|
586
|
+
// `requests` no longer blocks unified login.
|
|
587
|
+
if (!credential && hasXiaoEClientCredential(env)) {
|
|
588
|
+
try {
|
|
589
|
+
credential = await resolveXiaoEClientCredential(explicitBaseUrl || DEFAULT_E10_BASE_URL, env);
|
|
590
|
+
}
|
|
591
|
+
catch (error) {
|
|
592
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
593
|
+
options.logger?.warn('Built-in XiaoE client credential failed. ' + message);
|
|
594
|
+
}
|
|
595
|
+
if (!credential) {
|
|
596
|
+
options.logger?.warn('Built-in XiaoE client credential did not return usable ETEAMSID; will try XIAOE_* credentials next.');
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
if (!credential && baseUrl)
|
|
601
|
+
credential = resolveXiaoEUnifiedLoginCredential(baseUrl, env);
|
|
252
602
|
if (!credential)
|
|
253
603
|
return null;
|
|
254
604
|
options.logger?.info('Detected XiaoE unified login for this E10 domain. Trying XiaoE auth...');
|
|
255
605
|
try {
|
|
256
606
|
return await saveXiaoEUnifiedLogin({
|
|
257
|
-
baseUrl,
|
|
607
|
+
baseUrl: credential.baseUrl || baseUrl || DEFAULT_E10_BASE_URL,
|
|
258
608
|
eteamsId: credential.eteamsId,
|
|
609
|
+
rawCookie: credential.rawCookie,
|
|
259
610
|
tenantKeyFallback: options.tenantKeyFallback,
|
|
260
611
|
profileHint: options.profileHint,
|
|
612
|
+
credentialSource: describeXiaoECredentialSource(credential.source),
|
|
261
613
|
});
|
|
262
614
|
}
|
|
263
615
|
catch (error) {
|
|
264
616
|
const message = error instanceof Error ? error.message : String(error);
|
|
265
|
-
options.logger?.warn('XiaoE unified login failed
|
|
617
|
+
options.logger?.warn('XiaoE unified login failed. ' + message);
|
|
266
618
|
return null;
|
|
267
619
|
}
|
|
268
620
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { CliError, ExitCode } from '../../core/errors.js';
|
|
2
2
|
import { E10AuthSession } from './auth/session.js';
|
|
3
|
-
import { hasXiaoEEnv, tryXiaoEUnifiedLogin } from './auth/xiaoe.js';
|
|
3
|
+
import { hasXiaoEEnv, hasXiaoETokenScript, tryXiaoEUnifiedLogin } from './auth/xiaoe.js';
|
|
4
4
|
import { E10ApiClient } from './client.js';
|
|
5
5
|
function shouldPreferXiaoEUnifiedLogin(options, env) {
|
|
6
|
-
return
|
|
6
|
+
return !options.auth && !options.profile && (hasXiaoEEnv(env) || hasXiaoETokenScript(env));
|
|
7
7
|
}
|
|
8
8
|
export async function getE10Session(ctx) {
|
|
9
9
|
const options = ctx.getGlobalOptions();
|
|
@@ -11,10 +11,12 @@ export async function getE10Session(ctx) {
|
|
|
11
11
|
const xiaoELogin = await tryXiaoEUnifiedLogin({
|
|
12
12
|
env: ctx.env,
|
|
13
13
|
baseUrl: options.baseUrl,
|
|
14
|
+
allowTokenScript: true,
|
|
14
15
|
logger: ctx.logger,
|
|
15
16
|
});
|
|
16
17
|
if (xiaoELogin)
|
|
17
18
|
return xiaoELogin.session;
|
|
19
|
+
return null;
|
|
18
20
|
}
|
|
19
21
|
return E10AuthSession.fromOptions(options, ctx.cwd);
|
|
20
22
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { join, resolve } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { CliError, ExitCode } from '../../core/errors.js';
|
|
5
|
+
export const PACKAGED_DETECTOR_VERSION = '1.0.13';
|
|
6
|
+
export const PACKAGED_DETECTOR_NAME = `weaver-skill-detector-${PACKAGED_DETECTOR_VERSION}`;
|
|
7
|
+
export function packagedDetectorDir() {
|
|
8
|
+
return fileURLToPath(new URL(`../../../tools/${PACKAGED_DETECTOR_NAME}/`, import.meta.url));
|
|
9
|
+
}
|
|
10
|
+
function detectorScriptPath(detectorDir) {
|
|
11
|
+
return join(detectorDir, 'scripts', 'validate_skill.py');
|
|
12
|
+
}
|
|
13
|
+
function legacySiblingDetectorDir() {
|
|
14
|
+
return resolve(packagedDetectorDir(), '..', '..', '..', PACKAGED_DETECTOR_NAME);
|
|
15
|
+
}
|
|
16
|
+
function detectorVersion(detectorDir) {
|
|
17
|
+
let version = PACKAGED_DETECTOR_VERSION;
|
|
18
|
+
const metaPath = join(detectorDir, '_meta.json');
|
|
19
|
+
if (existsSync(metaPath)) {
|
|
20
|
+
try {
|
|
21
|
+
const meta = JSON.parse(readFileSync(metaPath, 'utf8'));
|
|
22
|
+
if (typeof meta.version === 'string' && meta.version)
|
|
23
|
+
version = meta.version;
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
version = PACKAGED_DETECTOR_VERSION;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return version;
|
|
30
|
+
}
|
|
31
|
+
export function resolveDetectorInfo(detectorDir) {
|
|
32
|
+
const candidates = detectorDir
|
|
33
|
+
? [resolve(detectorDir)]
|
|
34
|
+
: [packagedDetectorDir(), legacySiblingDetectorDir()].map((candidate) => resolve(candidate));
|
|
35
|
+
for (const candidate of candidates) {
|
|
36
|
+
const scriptPath = detectorScriptPath(candidate);
|
|
37
|
+
if (existsSync(scriptPath)) {
|
|
38
|
+
return { detectorDir: candidate, scriptPath, version: detectorVersion(candidate) };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
throw new CliError(`weaver skill detector is not available; checked: ${candidates.map(detectorScriptPath).join(', ')}`, ExitCode.UsageError);
|
|
42
|
+
}
|
|
@@ -63,6 +63,7 @@ export function installPackagedSkills(sourceRoot, targetRoot, requested = 'all')
|
|
|
63
63
|
const resolvedSourceRoot = resolve(sourceRoot);
|
|
64
64
|
const resolvedTargetRoot = resolve(targetRoot);
|
|
65
65
|
const skillNames = resolvePackagedSkillNames(resolvedSourceRoot, requested);
|
|
66
|
+
const productContent = productMetadataContent(resolvedSourceRoot);
|
|
66
67
|
mkdirSync(resolvedTargetRoot, { recursive: true, mode: 0o700 });
|
|
67
68
|
const installed = skillNames.flatMap((name) => {
|
|
68
69
|
const source = resolveInside(resolvedSourceRoot, name);
|
|
@@ -71,8 +72,9 @@ export function installPackagedSkills(sourceRoot, targetRoot, requested = 'all')
|
|
|
71
72
|
cpSync(source, target, {
|
|
72
73
|
recursive: true,
|
|
73
74
|
force: true,
|
|
74
|
-
filter: (entry) => shouldCopy(entry, source),
|
|
75
|
+
filter: (entry) => shouldCopy(entry, source, Boolean(productContent)),
|
|
75
76
|
});
|
|
77
|
+
writeProductMetadata(target, productContent);
|
|
76
78
|
const current = {
|
|
77
79
|
name,
|
|
78
80
|
source,
|
|
@@ -86,9 +88,10 @@ export function installPackagedSkills(sourceRoot, targetRoot, requested = 'all')
|
|
|
86
88
|
cpSync(source, legacyTarget, {
|
|
87
89
|
recursive: true,
|
|
88
90
|
force: true,
|
|
89
|
-
filter: (entry) => shouldCopy(entry, source),
|
|
91
|
+
filter: (entry) => shouldCopy(entry, source, Boolean(productContent)),
|
|
90
92
|
});
|
|
91
93
|
rewriteSkillName(resolve(legacyTarget, 'SKILL.md'), LEGACY_SHARED_SKILL_NAME);
|
|
94
|
+
writeProductMetadata(legacyTarget, productContent);
|
|
92
95
|
return [current, {
|
|
93
96
|
name: LEGACY_SHARED_SKILL_NAME,
|
|
94
97
|
source,
|
|
@@ -107,11 +110,55 @@ function rewriteSkillName(skillPath, name) {
|
|
|
107
110
|
const content = readFileSync(skillPath, 'utf8');
|
|
108
111
|
writeFileSync(skillPath, content.replace(/^name:\s*weaver-e10-shared\s*$/mu, `name: ${name}`), 'utf8');
|
|
109
112
|
}
|
|
110
|
-
function
|
|
113
|
+
function productMetadataContent(sourceRoot) {
|
|
114
|
+
const productPath = productMetadataPath(sourceRoot);
|
|
115
|
+
if (!existsSync(productPath))
|
|
116
|
+
return null;
|
|
117
|
+
const product = JSON.parse(readFileSync(productPath, 'utf8'));
|
|
118
|
+
const packageInfo = readPackageInfo(resolve(sourceRoot, '..', 'package.json'));
|
|
119
|
+
const metadata = {
|
|
120
|
+
schemaVersion: typeof product.schemaVersion === 'number' ? product.schemaVersion : 1,
|
|
121
|
+
product: stringOrEmpty(product.product),
|
|
122
|
+
productVersion: stringOrEmpty(packageInfo?.version) || stringOrEmpty(product.productVersion),
|
|
123
|
+
packageName: stringOrEmpty(packageInfo?.name) || stringOrEmpty(product.packageName),
|
|
124
|
+
command: stringOrEmpty(product.command),
|
|
125
|
+
nodeEngine: stringOrEmpty(packageInfo?.engines?.node) || stringOrEmpty(product.nodeEngine),
|
|
126
|
+
};
|
|
127
|
+
if (metadata.product !== 'weaver-work-cli' ||
|
|
128
|
+
metadata.packageName !== 'weaver-work-cli' ||
|
|
129
|
+
metadata.command !== 'weaver-work-cli' ||
|
|
130
|
+
!metadata.productVersion ||
|
|
131
|
+
!metadata.nodeEngine) {
|
|
132
|
+
throw new Error('skill-template/product.json or package.json is invalid for weaver-work-cli skills');
|
|
133
|
+
}
|
|
134
|
+
return `${JSON.stringify(metadata, null, 2)}\n`;
|
|
135
|
+
}
|
|
136
|
+
function productMetadataPath(sourceRoot) {
|
|
137
|
+
const templatePath = resolve(sourceRoot, '..', 'skill-template', 'product.json');
|
|
138
|
+
if (existsSync(templatePath))
|
|
139
|
+
return templatePath;
|
|
140
|
+
return resolve(sourceRoot, 'product.json');
|
|
141
|
+
}
|
|
142
|
+
function readPackageInfo(packagePath) {
|
|
143
|
+
if (!existsSync(packagePath))
|
|
144
|
+
return null;
|
|
145
|
+
return JSON.parse(readFileSync(packagePath, 'utf8'));
|
|
146
|
+
}
|
|
147
|
+
function stringOrEmpty(value) {
|
|
148
|
+
return typeof value === 'string' ? value : '';
|
|
149
|
+
}
|
|
150
|
+
function writeProductMetadata(target, productContent) {
|
|
151
|
+
if (!productContent)
|
|
152
|
+
return;
|
|
153
|
+
writeFileSync(resolve(target, 'product.json'), productContent, 'utf8');
|
|
154
|
+
}
|
|
155
|
+
function shouldCopy(entry, sourceRoot, skipSkillProduct) {
|
|
111
156
|
const relativePath = relative(sourceRoot, entry);
|
|
112
157
|
const name = basename(entry);
|
|
113
158
|
if (!relativePath)
|
|
114
159
|
return true;
|
|
160
|
+
if (skipSkillProduct && relativePath === 'product.json')
|
|
161
|
+
return false;
|
|
115
162
|
if (name === '.DS_Store' || name.startsWith('.'))
|
|
116
163
|
return false;
|
|
117
164
|
const parts = relativePath.split(sep);
|
|
@@ -195,7 +195,16 @@ export function formatSearchText(rows, meta = undefined) {
|
|
|
195
195
|
*
|
|
196
196
|
* 全宗:xxx 分类:xxx 文件形成时间:2026-01-23 ← 第三段
|
|
197
197
|
* 注意:三段间必须空行,单换行在 Markdown 是软换行会被折叠成空格。
|
|
198
|
+
* 第二、三段必须缩进 3 空格(对齐 `1. ` 后的内容列):不缩进会被判定为列表外段落,
|
|
199
|
+
* 渲染器会把下一条当作新列表起点,导致所有条目的序号都显示为 1。
|
|
198
200
|
* ------------------------------------------------------------------ */
|
|
201
|
+
/** 卡片第二、三段缩进 3 空格,保持属于同一条有序列表项;不缩进会被渲染器重排成新列表(序号全变 1)。 */
|
|
202
|
+
function indentContinuation(text) {
|
|
203
|
+
return text
|
|
204
|
+
.split('\n')
|
|
205
|
+
.map((line) => (line.trim() ? ` ${line}` : ''))
|
|
206
|
+
.join('\n');
|
|
207
|
+
}
|
|
199
208
|
export function formatSearchCard(rows, options = {}) {
|
|
200
209
|
const baseUrl = String(options.baseUrl ?? '').trim().replace(/\/+$/, '');
|
|
201
210
|
const items = rows.map((row) => {
|
|
@@ -214,10 +223,10 @@ export function formatSearchCard(rows, options = {}) {
|
|
|
214
223
|
const highlight = markdownHighlight(highlightSourceOf(raw, view));
|
|
215
224
|
if (highlight) {
|
|
216
225
|
parts.push('');
|
|
217
|
-
parts.push(highlight);
|
|
226
|
+
parts.push(indentContinuation(highlight));
|
|
218
227
|
}
|
|
219
228
|
parts.push('');
|
|
220
|
-
parts.push(textMetaItems(view).join(' '));
|
|
229
|
+
parts.push(indentContinuation(textMetaItems(view).join(' ')));
|
|
221
230
|
blocks.push(parts.join('\n'));
|
|
222
231
|
});
|
|
223
232
|
return blocks.join('\n\n');
|
|
@@ -261,7 +270,7 @@ h1{font-size:20px;margin-bottom:4px}
|
|
|
261
270
|
.trow{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap}
|
|
262
271
|
.idx{color:#94a3b8;font-weight:600}
|
|
263
272
|
.lvl{flex:none;background:#eef4ff;color:#2563eb;border:1px solid #bfdbfe;border-radius:4px;padding:0 8px;font-size:12px}
|
|
264
|
-
.tlink{font-size:15px;font-weight:600;color:#0f2c5c;text-decoration:none;word-break:break-
|
|
273
|
+
.tlink{font-size:15px;font-weight:600;color:#0f2c5c;text-decoration:none;word-wrap:break-word;overflow-wrap:break-word}
|
|
265
274
|
.tlink:hover{color:#1d6ae5;text-decoration:underline}
|
|
266
275
|
.tlink.plain{cursor:default}
|
|
267
276
|
.hl{margin:8px 0 6px;color:#475569;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;font-size:13px}
|