e10-data 0.1.32 → 0.1.34
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/CHANGELOG.md +88 -0
- package/README.md +10 -25
- package/bin/e10-data.mjs +166 -157
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,93 @@
|
|
|
1
1
|
# 变更记录
|
|
2
2
|
|
|
3
|
+
## 0.1.34(2026-10-29)—— `ds fields` / `ds describe` 改走 `getFields`(扁平返回)
|
|
4
|
+
|
|
5
|
+
### 1. 背景
|
|
6
|
+
|
|
7
|
+
取字段端点在后端已有统一实现 `GET /api/dw/newetl/chatBi/ds/getFields`(与 `ds/listByPage`、
|
|
8
|
+
`getListDatas` 同族),旧的 `POST /api/ebuilder/common/ds/fields` 不再是唯一入口。本次按用户
|
|
9
|
+
指定切到新端点,并适配其**扁平返回结构**。
|
|
10
|
+
|
|
11
|
+
### 2. 改动(**输出结构变化**)
|
|
12
|
+
|
|
13
|
+
- **`ds fields` 端点**:`POST /api/ebuilder/common/ds/fields`(multipart 表单)→
|
|
14
|
+
**`GET /api/dw/newetl/chatBi/ds/getFields`**(query 参数),四个参数全部取自本次调用:
|
|
15
|
+
|
|
16
|
+
| 参数 | 来源 |
|
|
17
|
+
| --- | --- |
|
|
18
|
+
| `groupId` | `--group` |
|
|
19
|
+
| `sourceId` | `--table` |
|
|
20
|
+
| `sourceType` | `--source-type`(缺省 `LOGIC`) |
|
|
21
|
+
| `appId` | `--app-id`(显式传入才拼;`auto` / 空则不出现) |
|
|
22
|
+
|
|
23
|
+
- **返回结构(不兼容)**:`data` 由「表节点数组」变为**扁平字段数组** ——
|
|
24
|
+
不再有 `data[0].fields[]` 这一层,也不再有 `isDetailtable` 子表节点与表名。
|
|
25
|
+
字段本身不丢(实测同一流程表新旧均为 126 个字段),子表归属只能看字段的
|
|
26
|
+
`config.formTableId`。
|
|
27
|
+
- **`ds describe`**:字段段同步走 `getFields`;`fields.main` = 全部字段、`fields.detail` 恒为空,
|
|
28
|
+
并新增 `fields.shapeNote` 说明该端点不返回表名与子表分组。
|
|
29
|
+
- **文本模式**:`ds fields` 不加 `--json` 时按扁平数组打印(`字段 N 个(getFields 扁平返回)`)。
|
|
30
|
+
- **`getUrl()` 支持 `E10_DEBUG_URL`**:与 `postJson` 对齐,设 `E10_DEBUG_URL=1` 时打印真实
|
|
31
|
+
`GET` 路径(含 query),便于核对参数是否按预期拼装。
|
|
32
|
+
|
|
33
|
+
### 3. 影响 / 兼容
|
|
34
|
+
|
|
35
|
+
- 只读字段清单的调用方无需改命令;**自己解析 `data[0].fields` 的脚本必须改成遍历 `data`**。
|
|
36
|
+
- `ds fields-dsl` **未改**,仍走 `/api/dw/newetl/chatBi/ds/fieldsForDSL`(返回仍为嵌套结构,
|
|
37
|
+
DSL 需要密文列名 `f_*` 与 `compType` 回填)。
|
|
38
|
+
|
|
39
|
+
### 4. 实测
|
|
40
|
+
|
|
41
|
+
- EB 表单(`846297265322573830`):137 个字段;需求库(`840720984867880961`):106 个字段;
|
|
42
|
+
流程表(`100003460000000478`):126 个字段(主表 62 + 7 张子表 20/7/6/6/5/5/2 + 13 个无 formTableId)。
|
|
43
|
+
- 新旧字段集合逐项比对一致,`compType` / `browserParams` 等属性比旧端点更全。
|
|
44
|
+
|
|
45
|
+
## 0.1.33(2026-10-23)—— 移除 `login xiaoe-env`(登录交 weaver-work-cli)+ 新增 `scene search`
|
|
46
|
+
|
|
47
|
+
### 1. 背景
|
|
48
|
+
|
|
49
|
+
登录链路已统一由 `weaver-work-cli`(`auth login` / `auth status` / `auth export-weaver-env`)负责,
|
|
50
|
+
本 CLI 侧再保留一套「环境变量 XiaoE 全局 token 换 ETEAMSID」的登录入口已无意义,且它是唯一
|
|
51
|
+
绕过「凭据唯一来源」的旁路,容易造成两套 profile 认知分裂。
|
|
52
|
+
|
|
53
|
+
### 2. 改动(**不兼容**)
|
|
54
|
+
|
|
55
|
+
- **删除 `e10-data login xiaoe-env`**(含别名 `e10-data xiaoe-env`):
|
|
56
|
+
随之移除 `cmdLoginXiaoeEnv` / `xiaoeEnvFetchEteamsId` / `resolveLoginBaseUrl` 及
|
|
57
|
+
`LOGIN_DEFAULT_BASE_URL` / `XIAOE_ENV_ETEAMID_PATH` / `ETEAMSID_SAFE_RE` 常量,
|
|
58
|
+
以及 `loadAuth()` 之前的 login 拦截块;
|
|
59
|
+
- 命令白名单 `commandIsKnown()` 去掉 `login` / `xiaoe-env` 两个特例 ⇒ 现在这两个词走
|
|
60
|
+
「未知命令 → 打印用法」,不再触发任何网络请求;
|
|
61
|
+
- `usage()` 与文件头注释同步:删掉 login 族说明,改为一句「本 CLI 不含登录命令,
|
|
62
|
+
未登录请执行 `weaver-work-cli auth login --base-url <域名>`」;
|
|
63
|
+
- 未登录报错文案仍指向 `weaver-work-cli auth status --no-check` / `auth login`(原本即如此)。
|
|
64
|
+
|
|
65
|
+
### 3. 迁移
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
旧:XIAOE_USER_TOKEN=<t> XIAOE_DOMAIN=<d> e10-data login xiaoe-env --domain <域名>
|
|
69
|
+
新:weaver-work-cli auth login --base-url <域名> # 浏览器 OAuth,回调后自动落 profile
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
> 注:`e10-login` 相关函数(`runLoginCli` / `providerAuthJson` 等)在本命令移除前就已
|
|
73
|
+
> 「保留定义但不挂载」,本次未动;`E10_LOGIN_ENTRY` 环境变量随之失去使用方。
|
|
74
|
+
|
|
75
|
+
### 4. 同版本新增 `scene search --keywords`
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
scene search --keywords <词1,词2> [--client agent|all|connector]
|
|
79
|
+
[--max-groups 3] [--max-items 12] [--scope-group-id <gid>]
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
- **后端无对应接口**,属本地补丁:先拉一次 `getSceneGroups` 分组骨架,按
|
|
83
|
+
`groupName` 命中 ×3 + `groupDesc` 命中 ×1 打分,取前 `--max-groups`(默认 3);
|
|
84
|
+
仅对入选分组再各拉一次 items,条目按 `datasetName` ×3 + `datasetDesc` ×1 打分,
|
|
85
|
+
全 0 分时回退「原序前 `--max-items` 条」(覆盖关键词只落在组描述里的情况);
|
|
86
|
+
- 输出与 `scene items` **同构**(分组 + `datasets`),`--json` 同结构(非原样透传);
|
|
87
|
+
计数走 stderr,stdout 保持纯净;
|
|
88
|
+
- 关键词按 `,,、;;空白` 分词,**字面包含**匹配(忽略大小写),不做语义裁决 ——
|
|
89
|
+
候选仍需模型判断;`--scope-group-id` 可把召回限定在指定分组内。
|
|
90
|
+
|
|
3
91
|
## 0.1.32(2026-10-22)—— 新增 `field options`:Select/下拉框字段选项值清单
|
|
4
92
|
|
|
5
93
|
### 1. 背景
|
package/README.md
CHANGED
|
@@ -9,14 +9,13 @@ npm i -g e10-data # 全局安装
|
|
|
9
9
|
e10-data --version # 验证
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
> 依赖:认证**唯一来源 = `weaver-work-cli
|
|
12
|
+
> 依赖:认证**唯一来源 = `weaver-work-cli`(不设回退)**,**登录也全部交给它**。本 CLI **不含任何登录命令**、不实现任何加解密:凭据由 `weaver-work-cli auth export-weaver-env`(解析 `WEAVER_BASE_URL` / `WEAVER_ETEAMSID` / `WEAVER_COOKIE`)或直读其 auth 文件取得。使用前请先 `weaver-work-cli auth login --base-url <域名>`。
|
|
13
13
|
>
|
|
14
|
-
>
|
|
14
|
+
> 历史说明:早期移植的 `e10-data login xiaoe-env`(环境变量 XiaoE 全局 token 换 ETEAMSID)已于 **2026-10-23 移除**,请统一用 `weaver-work-cli auth login`。
|
|
15
15
|
|
|
16
16
|
## 命令总览
|
|
17
17
|
|
|
18
18
|
```
|
|
19
|
-
e10-data login xiaoe-env [--domain <url>] 登录:环境变量 XiaoE token 换 ETEAMSID(免浏览器/免 python)
|
|
20
19
|
e10-data query <--conn <connId> --model <ebuilder|flow|eteams|attend|hr|document|dw|blog> --schema <数据库> [--table-id <流程ID>] [--debug true|false] --dsl @file.json>
|
|
21
20
|
[--dimension <维度>] [--conf-group <配置分组>] [--username <人员名称>] [--start-date <YYYY-MM>]
|
|
22
21
|
后四项 = 后端 4 个 @RequestParam(dimension 即后端 confKey / confGroup / username / startDate):
|
|
@@ -31,7 +30,7 @@ e10-data attend statis 考勤假期余额统计 getAttendInfoStatis(hr-atte
|
|
|
31
30
|
e10-data module call 模块配置接口调用(配置中心驱动;已确定「哪个模块的哪条具体接口」之后的统一取数入口,v0.1.21 新增)
|
|
32
31
|
e10-data refusal get --type <type> 拒答话术 getRefusalSpeechTechnique(入参 type → data 话术字符串,v0.1.23 新增)
|
|
33
32
|
e10-data refusal rules 拒答话术规则 getRefusalSpeechRules(**无入参** → data 对象数组,v0.1.26 新增)
|
|
34
|
-
e10-data scene groups|items
|
|
33
|
+
e10-data scene groups|items|search 场景定位元数据 getSceneGroups(① 分组骨架 ② 组内条目 ③ 关键词候选召回;**不取业务数据**,v0.1.25 新增,search 为本地补丁)
|
|
35
34
|
```
|
|
36
35
|
|
|
37
36
|
## 通用约定
|
|
@@ -40,7 +39,7 @@ e10-data scene groups|items 场景定位元数据 getSceneGroups(①
|
|
|
40
39
|
- 复杂 JSON/中文参数:推荐 `@文件` 承载(如 `--dsl @q.json`、`--where @cond.json`)。
|
|
41
40
|
- **逗号列表参数**(`--fields` / `--group-by` / `--order`):**一律走 `@文件`** —— 逗号不出现在命令行上,避免被 shell(尤其 Windows 交互式 PowerShell 把未加引号的 `a,b,c` 当数组拆开)拆分后**静默丢列 / 静默 0 行**。文件内容两种形态均可:逗号字符串 `a,b,c` 或 JSON 数组 `["a","b","c"]`。
|
|
42
41
|
- ⚠️ 大整数精度(自动处理):E10 的 ID 多为 19 位,超过 JS 安全整数(2^53-1≈9e15)。CLI 解析 JSON 时**自动把超范围裸整数转成字符串**再发请求/输出(实测后端接受字符串做 =/IN/BETWEEN 数值比较),无需手动加引号,也不误伤字符串正文。
|
|
43
|
-
-
|
|
42
|
+
- 认证:**凭据唯一来源 = `weaver-work-cli`**(本 CLI 不参与加解密,**已移除 e10-login 回退**)。凭据出口:`weaver-work-cli auth export-weaver-env`(解析 `WEAVER_BASE_URL` / `WEAVER_ETEAMSID` / `WEAVER_COOKIE`,另补一次 `auth status` 取 `userId` / `tenantKey`),以及直读其 auth 文件的快路径。命中的出口会记入 `~/.e10-cli/.e10-data-auth-mode`(只存模式名、不存凭据),下次优先复用。环境变量:`E10_DATA_AUTH_PROBE=1` 强制重新探测、`E10_DATA_AUTH_MODE=<模式名>` 手动指定、`E10_DATA_QUIET=1` 抑制告警(告警只走 stderr)。**没有任何可用凭据时**执行 `weaver-work-cli auth login --base-url <域名>`(本 CLI 不提供登录命令)。
|
|
44
43
|
- 版本更新:`npm i -g e10-data@latest`;查看源上最新 `npm view e10-data version`。
|
|
45
44
|
|
|
46
45
|
### 平台范围提示四字段统一透出(2026-10-19 本地增强)
|
|
@@ -82,7 +81,7 @@ e10-data scene groups|items 场景定位元数据 getSceneGroups(①
|
|
|
82
81
|
| `E10_DATA_NO_CRED_CACHE=1` | 整体关闭凭据缓存(每次仍直连 weaver-work-cli) |
|
|
83
82
|
| `E10_DATA_AUTH_PROBE=1` / `E10_DATA_AUTH_MODE=…` | 存在时**绕过缓存**,强制重新解析凭据 |
|
|
84
83
|
| `WEAVER_WORK_CLI_ENTRY` | 显式指定 `weaver-work-cli` 的 JS 入口路径(凭据出口排障用) |
|
|
85
|
-
| `E10_LOGIN_ENTRY` |
|
|
84
|
+
| `E10_LOGIN_ENTRY` | **已废弃**(`e10-login` 出口与 `login xiaoe-env` 均已移除,当前无使用方) |
|
|
86
85
|
|
|
87
86
|
> ⚠️ TTL 采用「先到先得」:若已有一个 TTL=300 的持有者在服务,此时以更短的 `E10_DATA_CRED_TTL` 再调用不会立刻缩短它的寿命(新持有者因通道被占而静默退出)。日常使用无需关注。
|
|
88
87
|
|
|
@@ -122,32 +121,18 @@ e10-data refusal get --type <type> --json # 原样
|
|
|
122
121
|
e10-data refusal get --type <type> --http get # 强制走 GET ?type=(默认 auto:POST 表单优先、失败回退 GET)
|
|
123
122
|
e10-data refusal rules # 拒答话术规则(无入参;人读:直接打印 data 数组)
|
|
124
123
|
e10-data refusal rules --json # 原样输出整个响应对象(data 数组 + actionMsg)
|
|
125
|
-
# 无凭据时先登录(环境变量里的 XiaoE 全局 token 换 ETEAMSID)
|
|
126
|
-
XIAOE_USER_TOKEN=<XiaoE token> XIAOE_DOMAIN=<erpa域名> e10-data login xiaoe-env --domain https://www.e-cology.com.cn
|
|
127
124
|
```
|
|
128
125
|
|
|
129
|
-
##
|
|
126
|
+
## 登录(不在本 CLI 内)
|
|
130
127
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
这是唯一**不需要已有凭据**的命令族(其余命令都要求先有登录态),因此它在 `loadAuth()` 之前被拦截执行。
|
|
134
|
-
|
|
135
|
-
| 步骤 | 做什么 | 谁做 |
|
|
136
|
-
|---|---|---|
|
|
137
|
-
| ① 换 token | `POST {$XIAOE_DOMAIN}/spec-apps/api/app/rpachat/skill/extend/eteamId`,请求头 `authorization: <XIAOE_USER_TOKEN>`,body `{"domain":"<--domain 或当前 profile 的 baseUrl>"}`,取回 `data.eteamId`(code 取 `0000`/`200` 为成功) | **本 CLI 原生实现**(无 python 依赖) |
|
|
138
|
-
| ② 落 profile | `e10-login set --eteamsid <id> --base-url <url>`:teamsCheck 解析用户信息 + 写加密 auth/config + 切换 active profile | `e10-login`(auth 文件加密只有它解得开) |
|
|
139
|
-
| ③ 自检 | 立刻用取数链路把凭据读回来(`loadAuth`),读不回就报错退出(**不假成功**) | 本 CLI |
|
|
128
|
+
本 CLI **不提供任何登录命令**。所有命令都要求先有登录态,凭据一律由 `weaver-work-cli` 管理:
|
|
140
129
|
|
|
141
130
|
```bash
|
|
142
|
-
#
|
|
143
|
-
|
|
144
|
-
# bash / git-bash
|
|
145
|
-
XIAOE_USER_TOKEN=<token> XIAOE_DOMAIN=<erpa域名> e10-data login xiaoe-env --domain https://www.e-cology.com.cn
|
|
131
|
+
weaver-work-cli auth status --no-check # 看登录态
|
|
132
|
+
weaver-work-cli auth login --base-url https://www.e-cology.com.cn # 登录(浏览器 OAuth,回调自动落 profile)
|
|
146
133
|
```
|
|
147
134
|
|
|
148
|
-
-
|
|
149
|
-
- `--json`:stdout 只输出结果 JSON(`baseUrl`/`profile`/`credentialSource`/`writtenBy`/`readback`),其余信息走 stderr,便于脚本消费;**ETEAMSID 一律不回显**;
|
|
150
|
-
- 与原版差异:e10-login 的实现是 `spawn python scripts/xiaoe_env.py`(需本机 python 3),本 CLI 直接原生发请求,**不需要 python**;请求形状与 code 判定逐字段对齐。
|
|
135
|
+
历史上移植的 `e10-data login xiaoe-env`(环境变量 `XIAOE_USER_TOKEN` + `XIAOE_DOMAIN` 换 ETEAMSID)已于 **2026-10-23 移除**,登录链路全部交给 `weaver-work-cli`。
|
|
151
136
|
|
|
152
137
|
## conf 配置中心与 confGroup 分组(v0.1.9 新增)
|
|
153
138
|
|
package/bin/e10-data.mjs
CHANGED
|
@@ -3,9 +3,8 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* e10-data CLI —— data-assistant 专家统一数据接口命令行(2026-09-03 扩展)
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* (XiaoE 全局 token)换 ETEAMSID 并落 profile(免浏览器/免 python;2026-09-17 移植)
|
|
6
|
+
* 覆盖(数据查询链路全部家族;登录一律交 weaver-work-cli,HRM 复用 hrm-cli,
|
|
7
|
+
* checkEbModulePermission 已废弃不提供):
|
|
9
8
|
* e10-data query DSL 查询 executeQueryDsl(conn/model/schema/tableId/dslQuery/debug
|
|
10
9
|
* + URL query: dimension(=后端 confKey)/confGroup/username/startDate,
|
|
11
10
|
* 四个键恒定发送、值允许为空)
|
|
@@ -43,8 +42,9 @@
|
|
|
43
42
|
* 通用:--json 原样输出;复杂 JSON 参数支持 @文件 或 内联(中文走 @文件 更稳)
|
|
44
43
|
* 认证:凭据**唯一来源 = weaver-work-cli**(本 CLI 不实现任何加解密,**不设 e10-login 回退**):
|
|
45
44
|
* weaver-work-cli auth export-weaver-env —— 解析 WEAVER_BASE_URL / WEAVER_ETEAMSID / WEAVER_COOKIE
|
|
46
|
-
* e10-login 系出口 / 明文镜像已于 2026-09-24
|
|
47
|
-
*
|
|
45
|
+
* e10-login 系出口 / 明文镜像已于 2026-09-24 停用;本 CLI **不提供登录命令**,
|
|
46
|
+
* 未登录 / 凭据失效时一律执行 `weaver-work-cli auth login --base-url <域名>`
|
|
47
|
+
* (历史上移植的 `login xiaoe-env` 已于 2026-10-23 移除,登录全部交给 weaver-work-cli)
|
|
48
48
|
*
|
|
49
49
|
* 用法示例:
|
|
50
50
|
* node e10-data.mjs query --conn 1144919996804022272 --model dw --schema dw --dsl @q.json
|
|
@@ -643,134 +643,6 @@ function credCachePublish(auth) {
|
|
|
643
643
|
setTimeout(done, 1000); // 兜底:不让发布环节拖住主命令
|
|
644
644
|
});
|
|
645
645
|
}
|
|
646
|
-
// ---------------- login(唯一"不需要已有凭据"的命令族:xiaoe-env,2026-09-17 移植)----------------
|
|
647
|
-
// 来源:e10-login 1.6.4-xiaoe 的 `e10-login xiaoe-env [--domain <url>]`
|
|
648
|
-
// (lib/xiaoe.js 命令定义 + lib/login-flow.js 收尾 + scripts/xiaoe_env.py 换 token)。
|
|
649
|
-
// 原实现是「Node 里 spawn python scripts/xiaoe_env.py 换 ETEAMSID」,而本 CLI 是零依赖 Node,
|
|
650
|
-
// **不需要 python**:换 token 本身就是一次 POST,此处原生复刻(逐字段对齐:URL 拼接、请求头
|
|
651
|
-
// authorization、body {domain}、code 判定 '0000'/200、取 data.eteamId、错误信息取 msg)。
|
|
652
|
-
// 分工(与本 CLI 既有的认证边界一致,不越界):
|
|
653
|
-
// ① 换 token → 本 CLI 原生实现(无 python 依赖);
|
|
654
|
-
// ② 落 profile → 仍交 e10-login `set --eteamsid --base-url`(auth 文件由它的密钥体系加密,
|
|
655
|
-
// 只有它解得开;`set` 与原命令的收尾 finishLoginByEteamsId 是同一条路径:
|
|
656
|
-
// teamsCheck 解析用户信息 + 写 auth/config + 切换 active profile)。
|
|
657
|
-
// ⚠️ 本段必须在 loadAuth() 之前执行 —— 该命令存在的意义就是"还没有凭据时把凭据造出来",
|
|
658
|
-
// 因此它在 main() 之前拦截(见下方拦截块),不走常规 main() 分发。
|
|
659
|
-
const LOGIN_DEFAULT_BASE_URL = "https://www.e-cology.com.cn";
|
|
660
|
-
const XIAOE_ENV_ETEAMID_PATH = "/spec-apps/api/app/rpachat/skill/extend/eteamId";
|
|
661
|
-
const ETEAMSID_SAFE_RE = /^[A-Za-z0-9_.\-]+$/; // 透传给 e10-login 的参数白名单(该调用走 shell,防注入)
|
|
662
|
-
|
|
663
|
-
/** --domain → 当前 profile 的 config.json.baseUrl → 默认平台地址(对齐 e10-login 的 resolveBaseUrl;
|
|
664
|
-
* config.json 是明文,读它不涉及任何解密) */
|
|
665
|
-
function resolveLoginBaseUrl(optDomain) {
|
|
666
|
-
if (optDomain !== undefined && typeof optDomain !== "string") throw new Error("--domain 需要跟一个平台地址,如 --domain https://www.e-cology.com.cn");
|
|
667
|
-
let cfgBase = "";
|
|
668
|
-
try {
|
|
669
|
-
const cfgFile = path.join(path.dirname(authFilePath().file), "config.json");
|
|
670
|
-
if (fs.existsSync(cfgFile)) cfgBase = String(JSON.parse(fs.readFileSync(cfgFile, "utf-8")).baseUrl || "").trim();
|
|
671
|
-
} catch {}
|
|
672
|
-
return String(optDomain || cfgBase || LOGIN_DEFAULT_BASE_URL).trim().replace(/\/+$/, "");
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
/** 复刻 scripts/xiaoe_env.py:POST {XIAOE_DOMAIN}/spec-apps/api/app/rpachat/skill/extend/eteamId
|
|
676
|
-
* 请求头 authorization=<XIAOE_USER_TOKEN>;body {"domain": <目标平台域名>} → 取 data.eteamId */
|
|
677
|
-
async function xiaoeEnvFetchEteamsId(domain) {
|
|
678
|
-
const token = String(process.env.XIAOE_USER_TOKEN || "").trim();
|
|
679
|
-
const erpa = String(process.env.XIAOE_DOMAIN || "").trim();
|
|
680
|
-
if (!token || !erpa) {
|
|
681
|
-
throw new Error("缺少环境变量 XIAOE_USER_TOKEN / XIAOE_DOMAIN(请先设置后重试,如 export XIAOE_USER_TOKEN=<token> XIAOE_DOMAIN=<erpa域名>)");
|
|
682
|
-
}
|
|
683
|
-
const url = erpa.replace(/\/+$/, "") + XIAOE_ENV_ETEAMID_PATH;
|
|
684
|
-
let resp, text;
|
|
685
|
-
try {
|
|
686
|
-
resp = await fetch(url, {
|
|
687
|
-
method: "POST",
|
|
688
|
-
headers: { authorization: token, "content-type": "application/json", accept: "application/json" },
|
|
689
|
-
body: JSON.stringify({ domain }),
|
|
690
|
-
});
|
|
691
|
-
text = await resp.text();
|
|
692
|
-
} catch (e) {
|
|
693
|
-
throw new Error("请求 XiaoE 换 token 接口失败(" + url + "):" + ((e && e.message) || e));
|
|
694
|
-
}
|
|
695
|
-
let j = null;
|
|
696
|
-
try { j = JSON.parse(text); } catch {}
|
|
697
|
-
if (!j) throw new Error("XiaoE 接口返回非 JSON(HTTP " + resp.status + "):" + text.slice(0, 200));
|
|
698
|
-
const code = j.code;
|
|
699
|
-
const eteamId = String((j.data && j.data.eteamId) || "").trim();
|
|
700
|
-
if (!(code === "0000" || code === 200 || code === "200") || !eteamId) {
|
|
701
|
-
throw new Error("XiaoE 换取失败:" + (j.msg || ("HTTP " + resp.status + " code=" + code)));
|
|
702
|
-
}
|
|
703
|
-
return eteamId;
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
/** e10-data login xiaoe-env:环境变量全局 token → ETEAMSID → 写 profile(走 e10-login set 收尾) */
|
|
707
|
-
async function cmdLoginXiaoeEnv(args, json) {
|
|
708
|
-
const say = json ? console.error : console.log; // --json 时人读信息一律走 stderr,保持 stdout 只有 JSON
|
|
709
|
-
const baseUrl = resolveLoginBaseUrl(args.domain);
|
|
710
|
-
say("ℹ️ 目标平台: " + baseUrl + (args.domain ? "(--domain)" : baseUrl === LOGIN_DEFAULT_BASE_URL ? "(默认平台地址)" : "(当前 profile 的 config.json)"));
|
|
711
|
-
|
|
712
|
-
const eteamsId = await xiaoeEnvFetchEteamsId(baseUrl);
|
|
713
|
-
say("✅ 已取得 ETEAMSID(来源: env,不回显)");
|
|
714
|
-
|
|
715
|
-
if (!ETEAMSID_SAFE_RE.test(eteamsId)) throw new Error("换回的 ETEAMSID 含异常字符,已拒绝透传 e10-login(安全防护)");
|
|
716
|
-
// v0.1.17:改为数组直传 + shell:false(不再手工拼 shell 字符串、不再需要 shq 转义),
|
|
717
|
-
// 既省掉 cmd.exe 那一层开销,也彻底消除 shell 注入面。ETEAMSID_SAFE_RE 仍作为二道防线保留。
|
|
718
|
-
const r = spawnLoginCli(["set", "--eteamsid", eteamsId, "--base-url", baseUrl], 60000);
|
|
719
|
-
const out = (r.stdout || "").trim(), err = (r.stderr || "").trim();
|
|
720
|
-
if (r.error || r.status !== 0) {
|
|
721
|
-
throw new Error("换 token 成功,但 e10-login 写 profile 失败(auth 文件的加密写入只有 e10-login 能做):"
|
|
722
|
-
+ (err || out || (r.error && r.error.message) || ("exit=" + r.status)));
|
|
723
|
-
}
|
|
724
|
-
for (const line of (out + (err ? "\n" + err : "")).split(/\r?\n/)) if (line.trim()) say(" [e10-login] " + line.trim());
|
|
725
|
-
|
|
726
|
-
const profile = authFilePath().profile;
|
|
727
|
-
// 自检:立刻用取数链路(loadAuth)把凭据读回来,确认"真能读到"而不是"只写了文件"
|
|
728
|
-
// (历史坑:e10-login 旧版 whoami 不带 cookies → 登录成功但 e10-data 取不到凭据;此处必须报出来)
|
|
729
|
-
// 强制重新探测(E10_DATA_AUTH_PROBE),并拒绝把"明文镜像兜底"当成自检通过 —— 镜像可能是旧环境的
|
|
730
|
-
let readBack = "";
|
|
731
|
-
try {
|
|
732
|
-
process.env.E10_DATA_AUTH_PROBE = "1";
|
|
733
|
-
const a = loadAuth();
|
|
734
|
-
const viaMirror = /镜像/.test(String(a.source || ""));
|
|
735
|
-
readBack = (viaMirror ? "⚠️ 仅命中明文镜像:" : "ok(") + (a.source || "?") + ";baseUrl: " + a.baseUrl + (a.userId ? ";userId: " + a.userId : "") + (viaMirror ? "" : ")");
|
|
736
|
-
if (viaMirror) throw new Error("凭据来自明文镜像而非新写入的 profile(镜像可能是旧环境/已过期)");
|
|
737
|
-
say("✅ 凭据自检通过:" + readBack);
|
|
738
|
-
} catch (e) {
|
|
739
|
-
if (!readBack) readBack = "失败: " + String((e && e.message) || e).split(/\r?\n/)[0];
|
|
740
|
-
throw new Error("profile 已写入 ~/.e10-cli/profiles/" + profile + "/auth,但本 CLI 读不回凭据(" + readBack
|
|
741
|
-
+ ")—— 取数仍会失败。请确认 `e10-login whoami` 能输出 cookies(缺则升级 e10-login),或改用 `e10-login oidc`。");
|
|
742
|
-
}
|
|
743
|
-
return {
|
|
744
|
-
ok: true,
|
|
745
|
-
baseUrl,
|
|
746
|
-
profile,
|
|
747
|
-
credentialSource: "env(XIAOE_USER_TOKEN + XIAOE_DOMAIN)",
|
|
748
|
-
writtenBy: "e10-login set",
|
|
749
|
-
eteamsId: "(已写入,不回显)",
|
|
750
|
-
readback: readBack,
|
|
751
|
-
};
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
// ---- 拦截(必须早于 loadAuth()):login 族 = 唯一不需要已有凭据的命令族 ----
|
|
755
|
-
{
|
|
756
|
-
const a2 = process.argv[2] || "";
|
|
757
|
-
const bare = a2 === "xiaoe-env"; // 别名:与 e10-login 同名命令保持一致的肌肉记忆
|
|
758
|
-
if (a2 === "login" || bare) {
|
|
759
|
-
const sub = bare ? a2 : process.argv[3] || "";
|
|
760
|
-
const { args } = parseArgs(process.argv.slice(bare ? 3 : 4));
|
|
761
|
-
const json = !!args.json;
|
|
762
|
-
try {
|
|
763
|
-
if (sub !== "xiaoe-env") throw new Error("未知子命令 `login " + (sub || "(空)") + "`(当前仅移植 xiaoe-env)");
|
|
764
|
-
const r = await cmdLoginXiaoeEnv(args, json);
|
|
765
|
-
if (json) console.log(JSON.stringify(r, null, 1));
|
|
766
|
-
process.exit(0);
|
|
767
|
-
} catch (e) {
|
|
768
|
-
console.error("ERROR: " + ((e && e.message) || e));
|
|
769
|
-
process.exit(1);
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
|
-
|
|
774
646
|
// ---- 命令白名单(早期校验用;必须与下方 main() 的分发表保持一致)----
|
|
775
647
|
// 返回 true = 该命令存在、可以继续(可能还需要凭据);false = 未知命令,直接给用法提示。
|
|
776
648
|
// 为什么需要:loadAuth() 写在模块顶层,过去"未知命令"也要先付一整笔取凭据的开销
|
|
@@ -785,13 +657,12 @@ const CMD_SUBS = {
|
|
|
785
657
|
attend: ["statis"],
|
|
786
658
|
module: ["call"],
|
|
787
659
|
refusal: ["get", "rules"],
|
|
788
|
-
scene: ["groups", "items"],
|
|
660
|
+
scene: ["groups", "items", "search"],
|
|
789
661
|
};
|
|
790
662
|
function commandIsKnown(pos) {
|
|
791
663
|
const family = pos[0], sub = pos[1];
|
|
792
664
|
if (!family) return false; // 无命令 → 用法
|
|
793
665
|
if (family === "query") return true; // query 无子命令(cmdQuery 自行处理入参)
|
|
794
|
-
if (family === "login" || family === "xiaoe-env") return true; // 已由上方拦截块接管
|
|
795
666
|
const subs = CMD_SUBS[family];
|
|
796
667
|
if (!subs) return false;
|
|
797
668
|
return subs.includes(sub);
|
|
@@ -799,7 +670,7 @@ function commandIsKnown(pos) {
|
|
|
799
670
|
|
|
800
671
|
// ---- 拦截(必须早于 loadAuth()):免凭据命令族 ----
|
|
801
672
|
// ① 版本 / ② 帮助 / ③ 未知命令 —— 这三类都不需要凭据,故在取凭据之前 return。
|
|
802
|
-
// (
|
|
673
|
+
// (exp release/rollback 的 --version 是版本号参数、长度 >1 不受影响)
|
|
803
674
|
{
|
|
804
675
|
const rawArgv = process.argv.slice(2);
|
|
805
676
|
const { args: earlyArgs, pos: earlyPos } = parseArgs(rawArgv);
|
|
@@ -874,6 +745,9 @@ async function postJson(endpoint, bodyObj, extraHeaders = {}) {
|
|
|
874
745
|
return j;
|
|
875
746
|
}
|
|
876
747
|
async function getUrl(url) {
|
|
748
|
+
if (process.env.E10_DEBUG_URL) {
|
|
749
|
+
console.error("[http] GET " + url);
|
|
750
|
+
}
|
|
877
751
|
const resp = await fetch(BASE + url, {
|
|
878
752
|
method: "GET",
|
|
879
753
|
headers: {
|
|
@@ -1227,6 +1101,24 @@ function parseOrder(val) {
|
|
|
1227
1101
|
function dsFieldsForm(gid, tid, st) {
|
|
1228
1102
|
return { groupId: gid, sourceId: tid, sourceType: st, detailFieldsGroup: "true", enableMerge: "true", needTableName: "true", params: "[]" };
|
|
1229
1103
|
}
|
|
1104
|
+
// ---- 获取字段端点(2026-10-29 用户指定):GET /api/dw/newetl/chatBi/ds/getFields ----
|
|
1105
|
+
// 四个参数全部取自本次调用,不写死:
|
|
1106
|
+
// groupId ← --group
|
|
1107
|
+
// sourceId ← --table(数据集 / EB 表 id)
|
|
1108
|
+
// sourceType ← --source-type(缺省 LOGIC)
|
|
1109
|
+
// appId ← --app-id(仅显式传入时才带;EB 建模表单才需要)
|
|
1110
|
+
const DS_FIELDS_PATH = "/api/dw/newetl/chatBi/ds/getFields";
|
|
1111
|
+
function dsFieldsQuery(gid, tid, st, appId) {
|
|
1112
|
+
const q = new URLSearchParams();
|
|
1113
|
+
q.set("groupId", String(gid));
|
|
1114
|
+
q.set("sourceId", String(tid));
|
|
1115
|
+
q.set("sourceType", String(st || "LOGIC"));
|
|
1116
|
+
if (typeof appId === "string" && appId !== "" && appId !== "auto") q.set("appId", appId);
|
|
1117
|
+
return q.toString();
|
|
1118
|
+
}
|
|
1119
|
+
async function dsGetFields(gid, tid, st, appId) {
|
|
1120
|
+
return getUrl(DS_FIELDS_PATH + "?" + dsFieldsQuery(gid, tid, st, appId));
|
|
1121
|
+
}
|
|
1230
1122
|
// ---- ds listByPage 端点(唯一):/api/dw/newetl/chatBi/ds/listByPage ----
|
|
1231
1123
|
// ⚠️ 该端点**当前会忽略 name**(传 name/tableName/keyword/title 等任意字段名,都返回该分组全量,实测 8531 条)。
|
|
1232
1124
|
// 后端后续会升级支持 name 过滤(用户 2026-09-24 确认)——升级后 `ds find` / `ds tables --name` **无需改代码**
|
|
@@ -1253,16 +1145,17 @@ async function cmdTables(args, json) {
|
|
|
1253
1145
|
const recs = ((r.data || {}).records) || [];
|
|
1254
1146
|
printHuman("分组表清单 (total=" + ((r.data || {}).total) + ")", recs, ["id", "name", "appId", "tableText", "detailTable", "usage"]);
|
|
1255
1147
|
}
|
|
1148
|
+
// 取字段:GET /api/dw/newetl/chatBi/ds/getFields(2026-10-29 用户指定端点)
|
|
1149
|
+
// 返回形态 = **扁平字段数组**(data[] 里每个元素就是一个字段,没有 table node、没有子表节点)
|
|
1150
|
+
// ⇒ 与旧 /api/ebuilder/common/ds/fields 的 data[0].fields[] 不同,故 --json 原样透传、
|
|
1151
|
+
// 文本模式按扁平数组打印;下游(describe / 过滤脚本)按扁平形态归一。
|
|
1256
1152
|
async function cmdFields(args, json) {
|
|
1257
1153
|
const gid = need("group", args), tid = need("table", args);
|
|
1258
|
-
const
|
|
1259
|
-
if (typeof args["app-id"] === "string" && args["app-id"] !== "" && args["app-id"] !== "auto") form.appId = args["app-id"];
|
|
1260
|
-
const r = await postMultipart("/api/ebuilder/common/ds/fields", form);
|
|
1154
|
+
const r = await dsGetFields(gid, tid, args["source-type"] || "LOGIC", args["app-id"]);
|
|
1261
1155
|
if (json) return r;
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
}
|
|
1156
|
+
const fields = Array.isArray(r.data) ? r.data : [];
|
|
1157
|
+
console.log("== 字段 " + fields.length + " 个(getFields 扁平返回)==");
|
|
1158
|
+
for (const f of fields) console.log([f.name, f.text, f.type || f.compType, f.mainPrimaryKey ? "PK" : ""].filter(Boolean).join("\t"));
|
|
1266
1159
|
}
|
|
1267
1160
|
// fieldsForDSL:参数与返回结构同 cmdFields,仅接口换为 /api/dw/newetl/chatBi/ds/fieldsForDSL
|
|
1268
1161
|
async function cmdFieldsForDSL(args, json) {
|
|
@@ -1684,7 +1577,24 @@ function collectFieldNodes(list, out, subName) {
|
|
|
1684
1577
|
}
|
|
1685
1578
|
}
|
|
1686
1579
|
function normalizeFieldsResp(r) {
|
|
1687
|
-
const
|
|
1580
|
+
const data = Array.isArray(r.data) ? r.data : [];
|
|
1581
|
+
// ④ getFields 扁平形态(2026-10-29):data 直接是字段数组 —— 没有 table node、没有子表节点、没有表名。
|
|
1582
|
+
// 字段本身不丢(与旧端点字段名集合一致),但 isDetailtable 无从判断,只能靠 config.formTableId 区分归属。
|
|
1583
|
+
if (data.length && !data.some((d) => Array.isArray(d && d.fields))) {
|
|
1584
|
+
const all = data.map((f) => ({
|
|
1585
|
+
name: f.name ?? null, text: f.text ?? null, type: f.type || f.compType || null,
|
|
1586
|
+
isPK: !!f.mainPrimaryKey, subTable: null,
|
|
1587
|
+
dataKey: (f.config && f.config.dataKey) || null,
|
|
1588
|
+
formTableId: (f.config && f.config.formTableId) || null,
|
|
1589
|
+
}));
|
|
1590
|
+
return {
|
|
1591
|
+
tableName: null, tableId: null,
|
|
1592
|
+
mainCount: all.length, detailCount: 0,
|
|
1593
|
+
main: all, detail: [],
|
|
1594
|
+
shapeNote: "端点 getFields 返回扁平字段数组:不含表名与子表分组(isDetailtable),子表归属只能看 formTableId",
|
|
1595
|
+
};
|
|
1596
|
+
}
|
|
1597
|
+
const node = data[0] || {};
|
|
1688
1598
|
const flat = [];
|
|
1689
1599
|
collectFieldNodes(node.fields, flat, null);
|
|
1690
1600
|
const main = flat.filter((x) => !x.__detail);
|
|
@@ -1715,9 +1625,7 @@ async function cmdDsDescribe(args, json) {
|
|
|
1715
1625
|
const tasks = [
|
|
1716
1626
|
(async () => {
|
|
1717
1627
|
try {
|
|
1718
|
-
|
|
1719
|
-
if (typeof args["app-id"] === "string" && args["app-id"] !== "" && args["app-id"] !== "auto") form.appId = args["app-id"];
|
|
1720
|
-
return { key: "fields", value: normalizeFieldsResp(await postMultipart("/api/ebuilder/common/ds/fields", form)) };
|
|
1628
|
+
return { key: "fields", value: normalizeFieldsResp(await dsGetFields(gid, tid, sourceType, args["app-id"])) };
|
|
1721
1629
|
} catch (e) { return { key: "fields", error: String((e && e.message) || e) }; }
|
|
1722
1630
|
})(),
|
|
1723
1631
|
(async () => {
|
|
@@ -3148,6 +3056,7 @@ async function cmdRefusalRules(args, json) {
|
|
|
3148
3056
|
// 端点:/api/dw/newetl/chatBi/getSceneGroups(别名 /getAgentSceneGroups,等价)
|
|
3149
3057
|
// ① scene groups → 分组骨架(不传 groupId;每个分组带 groupDesc 三段式)
|
|
3150
3058
|
// ② scene items --group <groupId> → 该分组的条目(传 groupId;datasetType = interface / datasource)
|
|
3059
|
+
// ③ scene search --keywords <词> → 关键词候选召回(2026-10-23 本地补丁;本地打分,非后端接口)
|
|
3151
3060
|
// 来源:由技能 `references/scene-locate/scripts/locate.mjs` **整体迁入**(scene-locate.md §6 原计划的 CLI 替换),
|
|
3152
3061
|
// 迁入后技能侧不再直连接口。参数与输出口径与脚本保持一致(`--client` 默认 agent、精简字段集、计数走 stderr)。
|
|
3153
3062
|
// 形状口径(2026-10-14 修订):**默认输出顶层一律为数组** —— ① groups = 分组数组;② items = 分组数组
|
|
@@ -3255,19 +3164,113 @@ async function cmdSceneItems(args, json) {
|
|
|
3255
3164
|
console.log(JSON.stringify(out, null, 1));
|
|
3256
3165
|
}
|
|
3257
3166
|
|
|
3167
|
+
/** ③ 关键词候选召回(2026-10-23 本地补丁;后端无对应接口,故在本地打分实现)
|
|
3168
|
+
* 数据源 = getSceneGroups 元数据:
|
|
3169
|
+
* ① 拉一次分组骨架,按 groupName 命中×3 + groupDesc 命中×1 打分,取前 --max-groups(默认 3);
|
|
3170
|
+
* ② 仅对入选分组再各拉一次条目(1 个分组 = 1 次请求);
|
|
3171
|
+
* ③ 条目按 datasetName 命中×3 + datasetDesc 命中×1 打分;组命中但条目全 0 分时,
|
|
3172
|
+
* 回退「条目原序前 --max-items 条」(覆盖「关键词只落在组描述里」的情况)。
|
|
3173
|
+
* 形状:顶层数组,与 `scene items` 同构(分组 + datasets);`--json` 输出同一结构
|
|
3174
|
+
* (本命令无「原样透传对象」可言,--json 与默认输出一致)。
|
|
3175
|
+
* 注意:关键词是**字面包含**匹配(忽略大小写),不做语义裁决 —— 候选仍需模型判断。
|
|
3176
|
+
*/
|
|
3177
|
+
function sceneKeywords(raw) {
|
|
3178
|
+
return String(raw ?? "")
|
|
3179
|
+
.split(/[,,、;;\s]+/)
|
|
3180
|
+
.map((s) => s.trim())
|
|
3181
|
+
.filter(Boolean);
|
|
3182
|
+
}
|
|
3183
|
+
|
|
3184
|
+
function sceneHitScore(text, keywords) {
|
|
3185
|
+
const hay = String(text ?? "").toLowerCase();
|
|
3186
|
+
if (!hay) return 0;
|
|
3187
|
+
let n = 0;
|
|
3188
|
+
for (const k of keywords) {
|
|
3189
|
+
if (hay.includes(k.toLowerCase())) n += 1;
|
|
3190
|
+
}
|
|
3191
|
+
return n;
|
|
3192
|
+
}
|
|
3193
|
+
|
|
3194
|
+
function sceneSlimItem(d) {
|
|
3195
|
+
const o = {
|
|
3196
|
+
id: String(d.id ?? ""),
|
|
3197
|
+
datasetName: d.datasetName ?? "",
|
|
3198
|
+
datasetType: d.datasetType ?? "",
|
|
3199
|
+
sourceId: d.sourceId ?? "",
|
|
3200
|
+
};
|
|
3201
|
+
if (d.moduleName) o.moduleName = d.moduleName;
|
|
3202
|
+
if (d.sourceType) o.sourceType = d.sourceType;
|
|
3203
|
+
if (d.sourceGroupId) o.sourceGroupId = d.sourceGroupId;
|
|
3204
|
+
if (d.appId) o.appId = d.appId;
|
|
3205
|
+
o.datasetDesc = d.datasetDesc ?? "";
|
|
3206
|
+
// 前向兼容:后端一旦下发 datasetLogic,自动带出(技能侧按 datasetLogic 优先消费)
|
|
3207
|
+
if (d.datasetLogic !== undefined) o.datasetLogic = d.datasetLogic;
|
|
3208
|
+
return o;
|
|
3209
|
+
}
|
|
3210
|
+
|
|
3211
|
+
async function cmdSceneSearch(args, json) {
|
|
3212
|
+
const client = sceneClient(args);
|
|
3213
|
+
const keywords = sceneKeywords(args.keywords ?? args.keyword);
|
|
3214
|
+
if (!keywords.length) throw new Error("缺少必填参数 --keywords <词1,词2>(也可用空格 / 、 分隔)");
|
|
3215
|
+
const maxGroups = Math.max(1, Number(args["max-groups"] ?? 3) || 3);
|
|
3216
|
+
const maxItems = Math.max(1, Number(args["max-items"] ?? 12) || 12);
|
|
3217
|
+
const scope = args["scope-group-id"] == null ? "" : String(args["scope-group-id"]).trim();
|
|
3218
|
+
|
|
3219
|
+
const raw = await sceneRequest({ datasetClient: client });
|
|
3220
|
+
let requests = 1;
|
|
3221
|
+
const all = Array.isArray(raw) ? raw : (raw == null ? [] : [raw]);
|
|
3222
|
+
const pool = all.filter((g) => g && g.groupId !== undefined);
|
|
3223
|
+
|
|
3224
|
+
const scored = pool
|
|
3225
|
+
.filter((g) => !scope || String(g.groupId) === scope)
|
|
3226
|
+
.map((g) => ({ g, score: sceneHitScore(g.groupName, keywords) * 3 + sceneHitScore(g.groupDesc, keywords) }))
|
|
3227
|
+
.filter((x) => x.score > 0)
|
|
3228
|
+
.sort((a, b) => b.score - a.score)
|
|
3229
|
+
.slice(0, maxGroups);
|
|
3230
|
+
|
|
3231
|
+
const out = [];
|
|
3232
|
+
for (const { g } of scored) {
|
|
3233
|
+
let items = Array.isArray(g.datasets) ? g.datasets : null;
|
|
3234
|
+
if (!items) {
|
|
3235
|
+
const one = await sceneRequest({ datasetClient: client, groupId: String(g.groupId) });
|
|
3236
|
+
requests += 1;
|
|
3237
|
+
const arr = Array.isArray(one) ? one : (one == null ? [] : [one]);
|
|
3238
|
+
const hit = arr.find((x) => x && String(x.groupId) === String(g.groupId)) || arr[0] || {};
|
|
3239
|
+
items = Array.isArray(hit.datasets) ? hit.datasets : [];
|
|
3240
|
+
}
|
|
3241
|
+
const ranked = items
|
|
3242
|
+
.map((d) => ({ d, score: sceneHitScore(d.datasetName, keywords) * 3 + sceneHitScore(d.datasetDesc, keywords) }))
|
|
3243
|
+
.sort((a, b) => b.score - a.score);
|
|
3244
|
+
const matched = ranked.filter((x) => x.score > 0);
|
|
3245
|
+
const keep = (matched.length ? matched : ranked).slice(0, maxItems);
|
|
3246
|
+
const group = {
|
|
3247
|
+
groupId: String(g.groupId),
|
|
3248
|
+
groupName: g.groupName ?? "",
|
|
3249
|
+
groupDesc: g.groupDesc ?? "",
|
|
3250
|
+
itemCount: Number(g.itemCount ?? items.length),
|
|
3251
|
+
datasets: keep.map((x) => sceneSlimItem(x.d)),
|
|
3252
|
+
};
|
|
3253
|
+
// 前向兼容:后端下发 groupLogic 时自动带出
|
|
3254
|
+
if (g.groupLogic !== undefined) group.groupLogic = g.groupLogic;
|
|
3255
|
+
out.push(group);
|
|
3256
|
+
}
|
|
3257
|
+
|
|
3258
|
+
const itemTotal = out.reduce((n, g) => n + g.datasets.length, 0);
|
|
3259
|
+
if (!out.length) {
|
|
3260
|
+
process.stderr.write("# 未命中任何分组(关键词:" + keywords.join(",") + ";候选池 " + pool.length + " 组)\n");
|
|
3261
|
+
}
|
|
3262
|
+
process.stderr.write("# 关键词 " + keywords.join(",")
|
|
3263
|
+
+ " | 候选分组 " + out.length + " 个 | 条目 " + itemTotal + " 条 | 请求 " + requests + " 次\n");
|
|
3264
|
+
return out;
|
|
3265
|
+
}
|
|
3266
|
+
|
|
3258
3267
|
// ---------------- main ----------------
|
|
3259
3268
|
function usage() {
|
|
3260
3269
|
console.log(`e10-data CLI(data-assistant 数据接口统一命令行,2026-09-03 扩展)
|
|
3261
3270
|
用法: node e10-data.mjs <族> <子命令> [--参数] 通用 --json 原样输出
|
|
3262
3271
|
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
# 换 ETEAMSID 并写入 profile(免浏览器;不依赖 python)
|
|
3266
|
-
# --domain 缺省 = 当前 profile 的 config.json.baseUrl → 默认平台地址
|
|
3267
|
-
# 依赖环境变量:XIAOE_USER_TOKEN(XiaoE token)、XIAOE_DOMAIN(erpa 域名)
|
|
3268
|
-
# 别名 e10-data xiaoe-env(与 e10-login 同名命令一致)
|
|
3269
|
-
# 说明:换 token 由本 CLI 原生完成;写 profile(auth 加密)仍交 e10-login set
|
|
3270
|
-
# 成功后自动用取数链路自检读回凭据,读不回即报错(不假成功)
|
|
3272
|
+
# 本 CLI **不含任何登录命令**(登录一律交 weaver-work-cli),所有命令都要求先有登录态;
|
|
3273
|
+
# 未登录 / 凭据失效 → weaver-work-cli auth login --base-url <域名>(详见文末「认证」)
|
|
3271
3274
|
|
|
3272
3275
|
query DSL 数据查询 executeQueryDsl
|
|
3273
3276
|
query --conn <connId> --model <ebuilder|flow|eteams|attend|hr|document|dw|blog> --schema <数据库> [--table-id <流程ID>] [--debug true|false] --dsl @file.json|'{...}'
|
|
@@ -3393,6 +3396,10 @@ refusal 拒答话术(chatBi,2026-10-13 新增)
|
|
|
3393
3396
|
scene 场景定位元数据(chatBi getSceneGroups,2026-10-14 新增;**不取业务数据**,只做「分组 → 条目」定位)
|
|
3394
3397
|
scene groups [--client agent|all|connector] # ① 分组骨架(groupDesc =【场景】【触发词】【取数顺序】)
|
|
3395
3398
|
scene items --group <groupId> [--client …] # ② 组内条目(每组带 datasetDesc;datasetType 决定分流)
|
|
3399
|
+
scene search --keywords <词1,词2> [--client …] # ③ 关键词候选召回(本地打分,2026-10-23 本地补丁)
|
|
3400
|
+
[--max-groups 3] [--max-items 12] [--scope-group-id <gid>]
|
|
3401
|
+
# 后端无此接口:拉一次 groups + 每个入选分组各拉一次 items,本地按关键词打分
|
|
3402
|
+
# 输出与 ② items 同构(分组 + datasets);--json 同结构(非原样透传)
|
|
3396
3403
|
# 端点 /api/dw/newetl/chatBi/getSceneGroups(别名 getAgentSceneGroups,等价)
|
|
3397
3404
|
# 默认输出**精简 JSON**(groups: groupId/groupName/itemCount/groupDesc;
|
|
3398
3405
|
# items: id/datasetName/datasetType/sourceId[/moduleName/sourceType/sourceGroupId/appId]/datasetDesc);
|
|
@@ -3402,8 +3409,9 @@ scene 场景定位元数据(chatBi getSceneGroups,2026-10-14 新增;**
|
|
|
3402
3409
|
# 分流提示:items 里 datasetType=interface → module call(--conf-key sourceId,--group 取 moduleName);
|
|
3403
3410
|
# datasetType=datasource → query(DSL)/ ds(数据源,--source-type/--group/--app-id 见条目字段)
|
|
3404
3411
|
# 示例: scene groups | scene items --group 1060565684758507520
|
|
3405
|
-
认证: 凭据**唯一来源 = weaver-work-cli**(本 CLI 不实现任何加解密,**不设 e10-login
|
|
3406
|
-
|
|
3412
|
+
认证: 凭据**唯一来源 = weaver-work-cli**(本 CLI 不实现任何加解密,**不设 e10-login 回退**,
|
|
3413
|
+
**也不提供登录命令**);未登录 / 凭据失效时执行
|
|
3414
|
+
weaver-work-cli auth login --base-url <域名>(浏览器 OAuth,回调后自动落 profile)
|
|
3407
3415
|
平台范围提示(2026-10-19 本地增强): 响应任意层级出现 tipTimelimit / tipTimelimitMsg /
|
|
3408
3416
|
tipTimeout / tipTimeoutMsg 时,一律提到**输出顶层**原名原值透出(--json 顶层带这四个键;
|
|
3409
3417
|
人读模式打 "[!] tipXxx=true|平台提示:<原文>")。tipTimelimit=数据已返回但实际范围被收窄;
|
|
@@ -3476,6 +3484,7 @@ async function main() {
|
|
|
3476
3484
|
} else if (family === "scene") {
|
|
3477
3485
|
if (sub === "groups") result = await cmdSceneGroups(args, json);
|
|
3478
3486
|
else if (sub === "items") result = await cmdSceneItems(args, json);
|
|
3487
|
+
else if (sub === "search") result = await cmdSceneSearch(args, json);
|
|
3479
3488
|
else { usage(); process.exit(1); }
|
|
3480
3489
|
} else if (family === "attend") {
|
|
3481
3490
|
if (sub === "statis") result = await cmdAttendStatis(args, json);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "e10-data",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "泛微 E10 数据接口统一 CLI(data-assistant
|
|
3
|
+
"version": "0.1.34",
|
|
4
|
+
"description": "泛微 E10 数据接口统一 CLI(data-assistant 专家配套):DSL 查询/数据源/文档/词库/关联字段翻译/环境参数配置中心(conf,支持 confGroup 分组)/专家包版本管理;ds 取数(list/chart/stat/count)的 appId 由调用方显式传入 --app-id(v0.1.14 起 CLI 不再自动反查;EB 建模表单 weaver-ebuilder-form-service 必须带,appId 取自 ebdf_obj.app_id;2026-10-09 起不再做 EB 权限前置校验);认证凭据**唯一来源 = weaver-work-cli**(通过 `auth export-weaver-env` 命令出口取明文凭据,**不设 e10-login 回退**;加解密由该 CLI 自行负责,本 CLI 不实现任何加解密;登录命令不在本 CLI 内,一律用 weaver-work-cli auth login)。v0.1.17 性能改造:免凭据命令(--version/--help/未知命令)提前返回、直连 e10-login JS 入口(去掉 shell:true 的 cmd.exe 开销)、新增凭据进程间 TTL 缓存(不落明文);v0.1.19(2026-09-24)新增 ds 发现层聚合 groups/find/describe(进程内并发,全 58 分组扫表实测 ~5.5s),并修复 ds tables --name 因 listByPage 端点切换导致的关键词过滤静默失效;v0.1.18(2026-09-24)新增 ds fields-dsl(接口 /api/dw/newetl/chatBi/ds/fieldsForDSL),并移除凭据明文镜像同步(不再写 ~/.workbuddy/e10-login-cache.md);v0.1.20(2026-09-24)凭据来源切换为 weaver-work-cli 唯一来源、移除 e10-login 回退,并补齐 userId/tenantKey 传递(修复 attend statis 等命令报「缺少查询人员」的问题)。v0.1.21(2026-09-28)新增 module call —— 模块配置接口调用(配置中心驱动):已确定「哪个模块的哪条具体接口」之后的一条命令取数入口(精取该条 confAttrs → 按 http_method/input_params 组参 → 发请求(自动禁代理 + Cookie/eteamsid/UA 三件套)→ 提取精简结果),与技能自带 script/call_module.py 等价(参数名/退出码/提取口径全对齐),复用本 CLI 凭据缓存与 conf 通道、零临时文件;同时 auth 归一化与凭据缓存补齐 userAgent(供 AgentType UA 使用,缺失时从 cookie 的 agentType= 推导)。v0.1.22(2026-10-10):移除 `eb perm-page` 命令(EB 建模表单取数不再做权限前置校验,appId 直接取 ebdf_obj.app_id);`--fields`/`--group-by`/`--order` 等逗号列表支持 `@文件`(逗号字符串或 JSON 数组,与 --where/--stat/--dsl 一致,规避 shell 拆分导致的静默丢列/0 行)。v0.1.23(2026-10-13)新增 `refusal get` —— 拒答话术 getRefusalSpeechTechnique(入参仅 type,返回对象的 data = 话术字符串;默认 auto 通道 = POST multipart 表单优先、返回不含 data 时自动回退 GET ?type=,可用 --http post|get 锁定单通道)。v0.1.25(2026-10-14)新增 `scene groups|items` —— 场景定位元数据 getSceneGroups(① groups 分组骨架 / ② items --group <groupId> 组内条目),由技能 references/scene-locate/scripts/locate.mjs 整体迁入(属元数据定位接口、不取业务数据):默认输出精简 JSON(groups: groupId/groupName/itemCount/groupDesc;items: id/datasetName/datasetType/sourceId[/moduleName/sourceType/sourceGroupId/appId]/datasetDesc),--json 原样输出接口返回,计数走 stderr;--client agent|all|connector 默认 agent。v0.1.26(2026-10-15)新增 `refusal rules` —— 拒答话术规则 getRefusalSpeechRules(**无入参**,返回对象的 data = 对象数组;调用方式与 refusal get 同口径:默认 auto 通道 = POST 空 multipart 表单优先、返回不含 data 时回退 GET,可用 --http post|get 锁定单通道;人读直接打印 data 数组,--json 原样输出整个响应对象)。v0.1.27(2026-10-15)新增 `noun scope --ids <id1,id2|@ids.json>` —— 词库**按生效范围批量检索**(端点 /api/dw/newetl/chatBi/doc/getChatBiDocByScopeId,POST body = 数据集 id 的 JSON 数组;入参 = 数据集 id 列表 = `scene items --group <分组id>` 里每个条目的 `id`;命中 = allScope=1 全局词库 ∪ scopeEffectId 含任一传入 id;id 按字符串下发,避免 19 位雪花号超 2^53 丢精度)。属**新增通道**,原 `noun list --skill <skillKey>`(getChatDocsBySkillKey)行为**完全不变**。 v0.1.28(2026-10-17):`module call` 支持 `--type <confType>`(自定义 confType / 域级配置分组,默认 `api`,不传时行为与旧版一致)与 `--show-params` 精简模式(默认省略 `request_example`,`--full` / `--with-example` 回显,`--no-example` 显式精简),`--show-params` 输出新增 `conf_type` 字段。v0.1.29(2026-10-19)本地增强:`module call` 新增 `--conf-file <文件|@文件>`——直接读本地 confAttrs 跳过 `conf get`(同一 confKey 一次问数只取一次配置),兼容模板缓存与 `conf get --json` 原样输出,带该参数时 `--group`/`--conf-key` 可从文件回填,stderr 提示「未请求配置中心」;缓存缺失/过期时去掉该参数即回退原路径。 v0.1.30(2026-10-19):query 透传后端 executeQueryDsl 新增的 4 个 @RequestParam(dimension=后端 confKey / confGroup / username / startDate,恒定发送、值允许为空,缺键即失败),并显式声明 Content-Type=application/json;charset=utf-8。v0.1.31(2026-10-21)凭据直读快路径(providerAuthFileDirect 直读 weaver-work-cli auth 文件解密,省 2 个子进程约 5.6s→~0.1s)+ cred-helper stale socket 自愈。v0.1.32(2026-10-22)新增 `field options` —— Select/下拉框字段的选项值清单 {id,name,archive}(GET /api/ebuilder/common/ds/options?fieldName=&objId=&sourceType=&groupId=,通用下拉框选项方法,不限 DSL/宽表:EB 表单与 ETEAMS 宽表均适用;--field-name 用 ds fields 的 name,--source-type 需显式传参 LOGIC|ETEAMS、代码缺省 LOGIC 兜底;Select 列存 value_key 时用它做显示映射,替代已停用的 id2name/字典表直查)。v0.1.33(2026-10-23):**移除 `login xiaoe-env`**(含别名 `xiaoe-env`)——登录链路统一交 weaver-work-cli(`auth login/status/export-weaver-env`),本 CLI 不再提供任何登录命令,未登录时提示执行 `weaver-work-cli auth login --base-url <域名>`;同版本新增 `scene search --keywords <词1,词2>` —— 场景关键词候选召回(本地打分:拉一次 groups + 每个入选分组各拉一次 items,按 groupName/datasetName 命中×3 + 描述命中×1 排序,输出与 `scene items` 同构,支持 `--max-groups 3` / `--max-items 12` / `--scope-group-id`)。",
|
|
5
5
|
"author": "e10-dw",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"type": "module",
|