e10-data 0.1.25 → 0.1.26

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 CHANGED
@@ -1,5 +1,87 @@
1
1
  # 变更记录
2
2
 
3
+ ## 0.1.26(2026-10-15)—— 新增 `refusal rules`:拒答话术规则
4
+
5
+ ### 1. `refusal` 族新增子命令 `rules`
6
+
7
+ - 端点:`/api/dw/newetl/chatBi/getRefusalSpeechRules`,**无入参**(CLI 不传任何参数,也不提供任何入参开关 —— 参数面比 `refusal get` 更窄);
8
+ 返回对象的 `data` = **需要使用的对象数组**(拒答话术规则清单,字段由后端定义、**CLI 不裁剪不改写**;允许为空数组 `[]`,**空 ≠ 失败**)。
9
+ - 调用方式**完全比照 `refusal get`**(同一族、同一策略,排障口径统一):
10
+ - 默认 `--http auto` = 先发 **POST 空 multipart 表单** → 判定拿不到 `data` 才回退 **`GET`(不带 query)**,任一条拿到 `data` 即止
11
+ (正常路径只发 1 次请求;回退仅在首条拿不到 `data` 时触发);
12
+ - `--http post` = 仅 POST 空表单;`--http get` = 仅 GET;取值非法直接报用法错误。
13
+ - 输出:人读直接打印 `data` 数组(漂亮 JSON,缩进 1;空数组打印 `[]` 并向 stderr 提示「拒答话术规则 0 条」);
14
+ `--json` = 原样输出整个响应对象(成功标记看 `actionMsg.code`,无顶层 `code`/`msg`)。
15
+ - 排障:`E10_DATA_DEBUG=1` 向 stderr 打印实际生效通道;两种通道均未拿到 `data` → 抛错并列出每条通道的 `code/msg`;
16
+ 凭据失效(401)**不换通道重试**,直接报登录态失效(与 `refusal get` 同口径)。
17
+ - 与 `refusal get` 的**差异仅两点**:① 无入参(表单不带字段 / GET 不带 query);② `data` 是数组而非字符串。
18
+
19
+ ```bash
20
+ e10-data refusal rules # 人读:直接打印规则数组
21
+ e10-data refusal rules --json # 原样输出整个响应对象(data 数组 + actionMsg)
22
+ e10-data refusal rules --http get # 锁定 GET 单通道
23
+ ```
24
+
25
+ ### 2. 同步改动
26
+
27
+ - `CMD_SUBS` 白名单 `refusal: ["get"]` → `refusal: ["get", "rules"]`;
28
+ `main()` 的 `refusal` 分支新增 `rules`;`usage()` 的 `refusal` 帮助段新增 `rules` 条目;
29
+ 头部注释新增 `refusal rules` 一行;`README.md` / `CHANGELOG.md` / `package.json`(版本 + description)同步。
30
+ 另见 §4:`refusal get` / `refusal rules` 的通道循环抽出为共用实现 `refusalTryModes`(含错误归因,两条命令口径同源)。
31
+
32
+ ### 3. 实测确认(2026-10-15)
33
+
34
+ **最终结论(12:43,服务端修复后复测通过)**:
35
+
36
+ - ✅ **POST 空 multipart 表单(不带任何字段)即命中**:`POST /api/dw/newetl/chatBi/getRefusalSpeechRules` →
37
+ `{"data":[…6 条规则…],"actionMsg":{"code":0,"message":"成功"}}` ⇒ **`auto` 正常路径只发 1 次请求**(POST 先命中)。
38
+ - ✅ **GET 同样可用**,且实测 `--http get` 与 `--http post` 的返回体**逐字相同**(`stdout` 全等)⇒ 两条通道均可,
39
+ 不存在"只吃其中一条"的情况。
40
+ - **实测数据形状(`data` = 对象数组,6 条)**:元素字段 = `id` / `createTime` / `updateTime` / `creator` / `tenantKey` /
41
+ `deleteType` / `confKey` / `confValue`(话术正文)/ `confAttrs`(命中条件)[/ `confPlan`];
42
+ `confKey` 实测清单 = `加载完成_1`、`交付前_1`、`交付前_2`、`业务域确认完成_1`(带 `confPlan`)、`交付前_3`、`取数前_1`。
43
+ - **人读输出**实测:stderr `# 规则 6 条` + stdout 漂亮 JSON 数组(stdout 纯净,可重定向)。
44
+
45
+ **排障时间线(保留记录,非当前状态)**:本次实测跨了三个阶段,前两阶段的结论**已被推翻**,留档用于复现排障思路 ——
46
+
47
+ 1. **401(登录态整体过期)**:首次测试时请求在网关层即被拒 ——
48
+ `HTTP 401 {"error":"authentication required","code":-2,"logout_url":"/_zt/logout?…"}`;
49
+ 同一时刻 `getRefusalSpeechTechnique` / `getSceneGroups` 乃至配置中心接口**全部返回同一个 401** ⇒ 属**登录态整体过期**
50
+ (`weaver-work-cli auth status` 报 `loggedIn:true, valid:false`、凭据文件停留在前一日),非本命令特有问题。
51
+ 重新登录(`weaver-work-cli auth login` / `auth xiaoe`)后即恢复。
52
+ 2. **业务 500(服务端未就绪)**:登录态恢复后,请求能被业务逻辑受理,但**6 种请求形态全部返回同一个笼统业务错误** ——
53
+ 空 multipart 表单 / multipart 带 `type` / JSON `{}` / JSON 带 `type` / urlencoded 空 / GET(无 query)/ GET 带 `type`、`skillKey`
54
+ ⇒ 一律 `HTTP 200 {"code":500,"msg":"请求失败,请稍后再试","status":false,"fail":true}`;
55
+ 追加 POST 上下文维度(AgentType UA、`agentType`/`isAgent` 头、表单 `datasetClient=agent`、`referer=/sp/ebddesigner/…`、
56
+ JSON `{datasetClient:"agent"}`)**响应逐字相同** ⇒ 与客户端可变的任何维度都无关。
57
+ 同时确认**路由存在**(同前缀错误路径返回 `HTTP 404 Not Found`,与业务 500 可区分)⇒ 非路径错、非认证问题、非缺参。
58
+ 该阶段催生了 §4 的错误归因改进(当时报错只写"已尝试 N 种通道",容易把排障方向带偏成"通道选错了")。
59
+ 3. **↗ 服务端修复后(12:43)复测通过** —— 即本节的最终结论;**CLI 侧自始至终未做任何针对性改动**,
60
+ 第 2 阶段的失败纯属服务端未就绪(或内部异常被折叠成该笼统错误)。
61
+
62
+ ### 4. 错误归因改进(2026-10-15,未发布即改,不新开版本)
63
+
64
+ **问题**(由 §3 的实测暴露):`refusal get` / `refusal rules` 在"各通道都没拿到 `data`"时,只报
65
+ `已尝试 N 种通道 → post: code=500 … | get: code=500 …`。这句话把**两种性质完全不同**的失败混为一谈,
66
+ 容易把排障方向带偏成"通道选错了",而实际可能是"请求已被业务逻辑受理、只是业务上失败"(如本接口的笼统 500)。
67
+
68
+ **改法**:
69
+
70
+ - 把通道循环从 `refusalCall` 中**抽出为共用实现 `refusalTryModes({label, ctx, modes, request, hasData})`**,
71
+ `refusal get` 与 `refusal rules` **共用同一份**(口径不会悄悄漂移);`refusalRequest` / `refusalRulesRequest` 保留为各自的取数函数。
72
+ - 新增 `refusalBusinessFail(r)`:识别**业务错误**(HTTP 200 但 `fail:true` / `status:false` / `code` 非 0),
73
+ 非业务错误则维持原「`code=… msg=(无 data 字段)`」写法。
74
+ - 仅当**所有**失败都是业务错误时,在报错末尾补一句:
75
+ `;提示:以上均为业务错误(HTTP 200 + 业务码失败)—— 非通道问题,请以 msg 为准排查服务端/配置`;
76
+ 若混有网络异常 / 无 `data` 字段,则**不补**(那种情况换通道仍有意义)。
77
+ - **行为不变**:分类只影响报错文案,**不改变「auto 两条通道都试」**、也不改变 401 立即中止的既有逻辑。
78
+
79
+ **验证(隔离单元测试,7/7 通过)**:把本文件复制一份、把末尾 `main()` 换成 `export { refusalTryModes }` 后,
80
+ 用假 `request` 函数覆盖 ① 首条命中只发 1 次请求、② 首条业务错误仍回退且第二条命中、③ 全业务错误含归因提示、
81
+ ④ 混合失败(网络异常 + 业务错误)不误报归因、⑤ 无 `data` 且无错误码如实提示、⑥ 401 立即中止不换通道、
82
+ ⑦ 单通道 + 自定义上下文文案正确。真实接口侧同时确认:所有 `type` 目前均返回 `配置不存在`,
83
+ 即 `refusal get` 在本环境暂无可用样本(与 2026-10-13 记录的 `type=Extraterritorial` 可命中**已不一致**,疑为该租户配置被移除)。
84
+
3
85
  ## 0.1.25(2026-10-14)—— 新增 `scene groups|items`:场景定位元数据
4
86
 
5
87
  ### 1. 新命令族 `scene`(子命令 `groups` / `items`)
package/README.md CHANGED
@@ -27,6 +27,7 @@ e10-data exp status|manifest|commits|file|pull|init|commit|lock|release|roll
27
27
  e10-data attend statis 考勤假期余额统计 getAttendInfoStatis(hr-attendance 病假/事假等,v0.1.5 新增)
28
28
  e10-data module call 模块配置接口调用(配置中心驱动;已确定「哪个模块的哪条具体接口」之后的统一取数入口,v0.1.21 新增)
29
29
  e10-data refusal get --type <type> 拒答话术 getRefusalSpeechTechnique(入参 type → data 话术字符串,v0.1.23 新增)
30
+ e10-data refusal rules 拒答话术规则 getRefusalSpeechRules(**无入参** → data 对象数组,v0.1.26 新增)
30
31
  e10-data scene groups|items 场景定位元数据 getSceneGroups(① 分组骨架 ② 组内条目;**不取业务数据**,v0.1.25 新增)
31
32
  ```
32
33
 
@@ -95,6 +96,8 @@ e10-data attend statis --user 6264843388578732287 --begin 2026-09-01 --end 2026-
95
96
  e10-data refusal get --type <type> # 拒答话术(人读:直接打印话术正文)
96
97
  e10-data refusal get --type <type> --json # 原样输出整个响应对象(data + actionMsg)
97
98
  e10-data refusal get --type <type> --http get # 强制走 GET ?type=(默认 auto:POST 表单优先、失败回退 GET)
99
+ e10-data refusal rules # 拒答话术规则(无入参;人读:直接打印 data 数组)
100
+ e10-data refusal rules --json # 原样输出整个响应对象(data 数组 + actionMsg)
98
101
  # 无凭据时先登录(环境变量里的 XiaoE 全局 token 换 ETEAMSID)
99
102
  XIAOE_USER_TOKEN=<XiaoE token> XIAOE_DOMAIN=<erpa域名> e10-data login xiaoe-env --domain https://www.e-cology.com.cn
100
103
  ```
@@ -341,6 +344,10 @@ e10-data refusal get --type <type> [--http auto|post|get] [--json]
341
344
  - **`--json`**:原样输出整个响应对象(`{"data":"…","actionMsg":{"code":0,"message":"成功"}}`;**成功标记看 `actionMsg.code`,无顶层 `code`/`msg`**),供脚本 / Agent 消费。
342
345
  - **排障**:`E10_DATA_DEBUG=1` 时向 **stderr** 打印实际生效的通道(`post` / `get`),stdout 保持纯净。
343
346
  - **两种通道均未拿到 `data`**:抛错并列出每条通道的 `code/msg`;凭据失效(401)时**不换通道重试**,直接报登录态失效。
347
+ - **报错归因(v0.1.26 起)**:若各通道返回的都是**业务错误**(HTTP 200 + 业务码失败,如 `{"code":500,"fail":true}`),
348
+ 报错末尾会补一句 `;提示:以上均为业务错误(HTTP 200 + 业务码失败)—— 非通道问题,请以 msg 为准排查服务端/配置`,
349
+ 避免误判成通道选择问题;若失败里**混有网络异常 / 无 `data` 字段**等情况,则不补该句(此时换通道仍有意义)。
350
+ **注意:该分类只影响报错文案,不改变「auto 两条通道都试」的行为。**
344
351
 
345
352
  ```bash
346
353
  # 人读:直接拿到话术正文
@@ -353,6 +360,55 @@ e10-data refusal get --type 1 --json
353
360
  e10-data refusal get --type 1 --http get
354
361
  ```
355
362
 
363
+ ## refusal rules 拒答话术规则(v0.1.26 新增)
364
+
365
+ **用途**:查询**拒答话术规则清单**(命中拒答场景时要使用的规则集合)。接口 `/api/dw/newetl/chatBi/getRefusalSpeechRules`,
366
+ **无入参**,返回对象中的 `data` 即**需要使用的对象数组**(规则清单)。
367
+
368
+ ```bash
369
+ e10-data refusal rules [--http auto|post|get] [--json]
370
+ ```
371
+
372
+ | 参数 | 说明 |
373
+ |---|---|
374
+ | (无位置参数) | 该接口**不接收任何入参**,CLI 也不提供任何入参开关(参数面比 `refusal get` 更窄) |
375
+ | `--http` | 通道,**语义与 `refusal get` 完全一致**:`auto`(默认)= 先 POST **空** multipart 表单 → 返回不含 `data` 时**自动回退** `GET`(不带 query),任一条拿到 `data` 即止;`post` = 仅 POST 空表单;`get` = 仅 `GET` |
376
+
377
+ - **返回契约**:`data` 为**对象数组**(每个元素一条规则,字段由后端定义、**CLI 不裁剪不改写**);允许为空数组 `[]`(语义 = 当前无规则,**空 ≠ 调用失败**)。
378
+ - **已实测的接口契约**(2026-10-15,泛微网络租户 `t7akvdnf84`):**POST 空 multipart 表单即命中** ——
379
+ `POST /api/dw/newetl/chatBi/getRefusalSpeechRules`(表单不带任何字段)→
380
+ `{"data":[…6 条规则…],"actionMsg":{"code":0,"message":"成功"}}`;**GET 同样可用,且返回体与 POST 逐字相同**
381
+ ⇒ `auto` 正常路径**只发 1 次请求**。
382
+ - **规则元素字段**(接口原样下发,CLI **不裁剪不改写**):
383
+
384
+ | 字段 | 含义 |
385
+ |---|---|
386
+ | `confKey` | 规则标识,如 `加载完成_1`、`交付前_1/2/3`、`业务域确认完成_1`、`取数前_1` |
387
+ | `confValue` | **话术正文**(含 `<datasetName>` 等占位符,交付前需替换) |
388
+ | `confAttrs` | **命中条件**(判定说明) |
389
+ | `confPlan` | 可选;带选项计划时出现(如 `业务域确认完成_1` 会给出后续问题选项) |
390
+ | `id` / `createTime` / `updateTime` / `creator` / `tenantKey` / `deleteType` | 记录元信息 |
391
+
392
+ - **人读输出**:直接打印 `data` 数组(漂亮 JSON,缩进 1);空数组打印 `[]` 并向 **stderr** 提示「拒答话术规则 0 条」,**stdout 保持纯净**(可安全重定向/管道)。
393
+ - **`--json`**:原样输出整个响应对象(`{"data":[…],"actionMsg":{"code":0,"message":"成功"}}`;**成功标记看 `actionMsg.code`,无顶层 `code`/`msg`**),供脚本 / Agent 消费。
394
+ - **排障**:`E10_DATA_DEBUG=1` 时向 **stderr** 打印实际生效的通道(`post` / `get`)。
395
+ - **两种通道均未拿到 `data`**:抛错并列出每条通道的 `code/msg`;凭据失效(401)时**不换通道重试**,直接报登录态失效。
396
+ - **报错归因(v0.1.26 起)**:若各通道返回的都是**业务错误**(HTTP 200 + 业务码失败,如 `{"code":500,"fail":true}`),
397
+ 报错末尾会补一句 `;提示:以上均为业务错误(HTTP 200 + 业务码失败)—— 非通道问题,请以 msg 为准排查服务端/配置`,
398
+ 避免误判成通道选择问题;若失败里**混有网络异常 / 无 `data` 字段**等情况,则不补该句(此时换通道仍有意义)。
399
+ **注意:该分类只影响报错文案,不改变「auto 两条通道都试」的行为。**
400
+
401
+ ```bash
402
+ # 人读:直接打印规则数组
403
+ e10-data refusal rules
404
+
405
+ # 取结构化结果(Agent / 脚本)
406
+ e10-data refusal rules --json
407
+
408
+ # 该端点确认走 GET 时,锁定单通道(少一次探测请求)
409
+ e10-data refusal rules --http get
410
+ ```
411
+
356
412
  ## scene groups|items 场景定位元数据(v0.1.25 新增)
357
413
 
358
414
  **用途**:E10「场景分组 → 接口/数据源」两级**定位**(**不取业务数据**)。端点 `/api/dw/newetl/chatBi/getSceneGroups`
package/bin/e10-data.mjs CHANGED
@@ -30,6 +30,8 @@
30
30
  * 复用本 CLI 凭据缓存与 conf 通道、零临时文件
31
31
  * e10-data refusal get 拒答话术 getRefusalSpeechTechnique(入参 type → data 话术字符串;
32
32
  * auto 通道 = POST 表单优先、GET ?type= 回退,2026-10-13)
33
+ * e10-data refusal rules 拒答话术规则 getRefusalSpeechRules(**无入参** → data 对象数组;
34
+ * auto 通道 = POST 空表单优先、GET 回退;2026-10-15 实测 POST 空表单即命中(6 条规则))
33
35
  * e10-data scene groups|items 场景定位元数据 getSceneGroups(2026-10-14 新增)
34
36
  * groups 分组骨架(每个分组带 groupDesc =【场景】【触发词】【取数顺序】)
35
37
  * items 组内条目(interface=接口 / datasource=数据源,每条带 datasetDesc)
@@ -714,7 +716,7 @@ const CMD_SUBS = {
714
716
  exp: ["status", "manifest", "commits", "file", "pull", "init", "commit", "lock", "release", "rollback"],
715
717
  attend: ["statis"],
716
718
  module: ["call"],
717
- refusal: ["get"],
719
+ refusal: ["get", "rules"],
718
720
  scene: ["groups", "items"],
719
721
  };
720
722
  function commandIsKnown(pos) {
@@ -2700,27 +2702,58 @@ async function refusalRequest(mode, type) {
2700
2702
  if (mode === "get") return getUrl(REFUSAL_PATH + "?type=" + encodeURIComponent(type));
2701
2703
  return postMultipart(REFUSAL_PATH, { type }); // 与 fieldsForDSL 同口径:multipart 表单字段 type
2702
2704
  }
2703
- // 返回 { r, mode }:r = 命中的响应体,mode = 实际生效的通道(post/get),供排障展示
2704
- async function refusalCall(type, httpMode) {
2705
- const modes = httpMode === "auto" ? ["post", "get"] : [httpMode];
2705
+ // ---- 通道选择与错误分类(refusal get / refusal rules 共用,2026-10-15 抽出)----
2706
+ // 为什么共用:两条命令的通道策略必须**逐字同源**,否则口径会悄悄漂移(一个改了另一个忘记改)。
2707
+ // 「没拿到 data」其实是两种性质完全不同的情况,必须分开报,否则排障方向会跑偏:
2708
+ // ① **业务错误**:HTTP 200 但业务码失败(fail:true / status:false / code 非 0)⇒ 请求**已被业务逻辑受理**,
2709
+ // 换通道不会改变结果。2026-10-15 实测 getRefusalSpeechRules 的 6 种请求形态返回**同一个** code=500,
2710
+ // 就是典型情形(详见 CHANGELOG 0.1.26 §3)——此时应照 msg 查服务端/配置,而不是继续换通道。
2711
+ // ② **形态不符**:响应正常但就是没有 data 字段(如裸 ResultBean、非 JSON 包装)⇒ 才值得换通道再试。
2712
+ // 注意:分类只影响**报错文案**,不改变「auto 两条通道都试」的行为(避免误杀"第一条通道不支持、第二条能用"的端点)。
2713
+ function refusalBusinessFail(r) {
2714
+ if (!r || typeof r !== "object") return null;
2715
+ const code = r.code;
2716
+ const bad = r.fail === true || r.status === false || (code !== undefined && Number(code) !== 0);
2717
+ if (!bad) return null;
2718
+ return "code=" + code + " msg=" + (r.msg || r.message || r.error || "(无 msg)");
2719
+ }
2720
+ /** 依序试通道:拿到 data 即返回 { r, mode };全失败则抛出「含每通道明细 + 业务错误归因」的错误 */
2721
+ async function refusalTryModes({ label, ctx, modes, request, hasData }) {
2706
2722
  const tried = [];
2723
+ let allBusiness = true; // 「全部失败都是业务错误」时才敢断言"换通道没用"
2707
2724
  for (const mode of modes) {
2708
2725
  let r;
2709
2726
  try {
2710
- r = await refusalRequest(mode, type);
2727
+ r = await request(mode);
2711
2728
  } catch (e) {
2712
2729
  const msg = (e && e.message) || String(e);
2713
2730
  if (/登录态失效|401/.test(msg)) throw e; // 凭据问题与「通道选哪条」无关,不换通道重试(避免重复报错)
2714
2731
  tried.push(mode + ": " + msg);
2732
+ allBusiness = false;
2715
2733
  continue;
2716
2734
  }
2717
- if (refusalHasData(r)) {
2718
- if (process.env.E10_DATA_DEBUG) console.error("[debug] getRefusalSpeechTechnique 命中通道=" + mode);
2735
+ if (hasData(r)) {
2736
+ if (process.env.E10_DATA_DEBUG) console.error("[debug] " + label + " 命中通道=" + mode);
2719
2737
  return { r, mode };
2720
2738
  }
2721
- tried.push(mode + ": code=" + (r && r.code) + " msg=" + ((r && r.msg) || "(无 data 字段)"));
2739
+ const biz = refusalBusinessFail(r);
2740
+ if (biz) tried.push(mode + ": " + biz);
2741
+ else { tried.push(mode + ": code=" + (r && r.code) + " msg=" + ((r && r.msg) || "(无 data 字段)")); allBusiness = false; }
2722
2742
  }
2723
- throw new Error("getRefusalSpeechTechnique 调用失败(type=" + type + ")已尝试 " + modes.length + " 种通道 → " + tried.join(" | "));
2743
+ throw new Error(label + " 调用失败(" + ctx + ")已尝试 " + modes.length + " 种通道 → " + tried.join(" | ")
2744
+ + (allBusiness && tried.length
2745
+ ? ";提示:以上均为业务错误(HTTP 200 + 业务码失败)—— 非通道问题,请以 msg 为准排查服务端/配置"
2746
+ : ""));
2747
+ }
2748
+ // 返回 { r, mode }:r = 命中的响应体,mode = 实际生效的通道(post/get),供排障展示
2749
+ async function refusalCall(type, httpMode) {
2750
+ return refusalTryModes({
2751
+ label: "getRefusalSpeechTechnique",
2752
+ ctx: "type=" + type,
2753
+ modes: httpMode === "auto" ? ["post", "get"] : [httpMode],
2754
+ request: (mode) => refusalRequest(mode, type),
2755
+ hasData: refusalHasData,
2756
+ });
2724
2757
  }
2725
2758
  async function cmdRefusalGet(args, json) {
2726
2759
  const type = need("type", args);
@@ -2737,6 +2770,55 @@ async function cmdRefusalGet(args, json) {
2737
2770
  console.log(typeof d === "object" ? JSON.stringify(d, null, 1) : String(d));
2738
2771
  }
2739
2772
 
2773
+ // ---------------- refusal rules:拒答话术规则 getRefusalSpeechRules(2026-10-15 新增)----------------
2774
+ // 端点:/api/dw/newetl/chatBi/getRefusalSpeechRules
2775
+ // 入参:**无**(CLI 不传任何参数,也不提供任何入参开关 —— 参数面比 refusal get 更窄)
2776
+ // 返回:对象,其中 data = **需要使用的对象数组**(拒答话术规则清单;可能为空数组 [],空 ≠ 失败)
2777
+ // 通道选择:与 refusal get **完全同口径**(同一族、同一策略,便于排障时口径统一):
2778
+ // auto(默认)= 先 POST 空 multipart 表单 → 判定拿不到 data 才回退 GET;任一条拿到 data 即止
2779
+ // (正常路径只发 1 次请求,回退仅在首条拿不到 data 时触发);可 --http post|get 强制单一通道。
2780
+ // 与 refusal get 的差异仅两点:① 无入参(表单不带字段 / GET 不带 query);② data 是数组而非字符串。
2781
+ // ✅ 2026-10-15 实测确认(泛微网络租户 t7akvdnf84):**POST 空 multipart 表单即命中** ——
2782
+ // 返回 { data: [6 条规则], actionMsg: { code: 0, message: "成功" } };GET 同样可用且**返回体与 POST 逐字相同**
2783
+ // (实测 `--http get` 与 `--http post` 的 stdout 完全一致)⇒ auto 正常路径**只发 1 次请求**(POST 先命中)。
2784
+ // 规则元素字段(接口原样下发,CLI 不裁剪):id / createTime / updateTime / creator / tenantKey /
2785
+ // deleteType / confKey(如「加载完成_1」「交付前_1」「取数前_1」)/ confValue(话术正文)/
2786
+ // confAttrs(命中条件)/ confPlan(可选,如「业务域确认完成_1」带选项计划)。
2787
+ const REFUSAL_RULES_PATH = "/api/dw/newetl/chatBi/getRefusalSpeechRules";
2788
+ // 有效返回判定:对象且带 data 字段(data 允许为空数组 —— 语义是「当前无规则」,不等于失败)
2789
+ function refusalRulesHasData(r) {
2790
+ return !!r && typeof r === "object" && r.data !== undefined && r.data !== null;
2791
+ }
2792
+ async function refusalRulesRequest(mode) {
2793
+ if (mode === "get") return getUrl(REFUSAL_RULES_PATH);
2794
+ return postMultipart(REFUSAL_RULES_PATH, {}); // 与 refusal get 同口径:multipart 表单,但无字段
2795
+ }
2796
+ // 返回 { r, mode }:r = 命中的响应体,mode = 实际生效的通道(post/get),供排障展示。
2797
+ // 通道循环与错误分类统一走 refusalTryModes —— 与 refusal get **同一份实现**(口径不会漂移)。
2798
+ async function refusalRulesCall(httpMode) {
2799
+ return refusalTryModes({
2800
+ label: "getRefusalSpeechRules",
2801
+ ctx: "该接口无入参",
2802
+ modes: httpMode === "auto" ? ["post", "get"] : [httpMode],
2803
+ request: (mode) => refusalRulesRequest(mode),
2804
+ hasData: refusalRulesHasData,
2805
+ });
2806
+ }
2807
+ async function cmdRefusalRules(args, json) {
2808
+ const httpMode = args.http === undefined ? "auto" : String(args.http).toLowerCase();
2809
+ if (!REFUSAL_HTTP_MODES.includes(httpMode)) throw new Error("--http 值必须为 " + REFUSAL_HTTP_MODES.join("|") + "(当前: " + args.http + ")");
2810
+ const { r } = await refusalRulesCall(httpMode);
2811
+ if (json) return r; // 原样输出整个响应对象(含 data)
2812
+ const d = r.data;
2813
+ if (Array.isArray(d)) {
2814
+ if (!d.length) { console.log("[]"); process.stderr.write("# 空:拒答话术规则 0 条\n"); return; }
2815
+ process.stderr.write("# 规则 " + d.length + " 条\n");
2816
+ console.log(JSON.stringify(d, null, 1));
2817
+ return;
2818
+ }
2819
+ console.log(typeof d === "object" ? JSON.stringify(d, null, 1) : String(d));
2820
+ }
2821
+
2740
2822
  // ---------------- scene:场景定位元数据 getSceneGroups(2026-10-14 新增)----------------
2741
2823
  // 端点:/api/dw/newetl/chatBi/getSceneGroups(别名 /getAgentSceneGroups,等价)
2742
2824
  // ① scene groups → 分组骨架(不传 groupId;每个分组带 groupDesc 三段式)
@@ -2958,6 +3040,11 @@ refusal 拒答话术(chatBi,2026-10-13 新增)
2958
3040
  # --http:auto(默认)= 先 POST multipart 表单(同 ds fields-dsl 口径),
2959
3041
  # 返回不含 data 时自动回退 GET ?type=;post = 仅 POST 表单;get = 仅 GET query
2960
3042
  # 人读输出直接打印 data 话术正文(空串 → 提示该 type 无话术);--json 原样输出整个响应对象
3043
+ refusal rules [--http auto|post|get] [--json] # 接口 /api/dw/newetl/chatBi/getRefusalSpeechRules
3044
+ # **无入参**(参数面比 get 更窄,CLI 不传任何参数);返回对象的 data = **对象数组**(规则清单)
3045
+ # --http 语义同 refusal get:auto(默认)= 先 POST 空 multipart 表单,返回不含 data 时回退 GET;
3046
+ # post = 仅 POST 空表单;get = 仅 GET(不带 query)
3047
+ # 人读输出直接打印 data 数组(漂亮 JSON;空数组打印 []);--json 原样输出整个响应对象
2961
3048
  scene 场景定位元数据(chatBi getSceneGroups,2026-10-14 新增;**不取业务数据**,只做「分组 → 条目」定位)
2962
3049
  scene groups [--client agent|all|connector] # ① 分组骨架(groupDesc =【场景】【触发词】【取数顺序】)
2963
3050
  scene items --group <groupId> [--client …] # ② 组内条目(每组带 datasetDesc;datasetType 决定分流)
@@ -3033,6 +3120,7 @@ async function main() {
3033
3120
  }
3034
3121
  } else if (family === "refusal") {
3035
3122
  if (sub === "get") result = await cmdRefusalGet(args, json);
3123
+ else if (sub === "rules") result = await cmdRefusalRules(args, json);
3036
3124
  else { usage(); process.exit(1); }
3037
3125
  } else if (family === "scene") {
3038
3126
  if (sub === "groups") result = await cmdSceneGroups(args, json);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "e10-data",
3
- "version": "0.1.25",
4
- "description": "泛微 E10 数据接口统一 CLI(data-assistant 专家配套):登录(login xiaoe-env,环境变量 XiaoE 全局 token 换 ETEAMSID,免浏览器/免 python)/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 不实现任何加解密;仅 login xiaoe-env 在无凭据时自行换 token,写 profile 仍交 e10-login set)。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。",
3
+ "version": "0.1.26",
4
+ "description": "泛微 E10 数据接口统一 CLI(data-assistant 专家配套):登录(login xiaoe-env,环境变量 XiaoE 全局 token 换 ETEAMSID,免浏览器/免 python)/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 不实现任何加解密;仅 login xiaoe-env 在无凭据时自行换 token,写 profile 仍交 e10-login set)。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 原样输出整个响应对象)。",
5
5
  "author": "e10-dw",
6
6
  "license": "MIT",
7
7
  "type": "module",