e10-data 0.1.0 → 0.1.3
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 +8 -1
- package/bin/e10-data.mjs +307 -11
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -14,11 +14,12 @@ e10-data --version # 验证
|
|
|
14
14
|
## 命令总览
|
|
15
15
|
|
|
16
16
|
```
|
|
17
|
-
e10-data query <--conn <connId> --model <ebuilder|flow|eteams|attend|hr|document|dw|blog> --schema <数据库> [--table-id <流程ID>] --dsl @file.json>
|
|
17
|
+
e10-data query <--conn <connId> --model <ebuilder|flow|eteams|attend|hr|document|dw|blog> --schema <数据库> [--table-id <流程ID>] [--debug true|false] --dsl @file.json>
|
|
18
18
|
e10-data ds tables|fields|list|chart|stat|count 数据源(业务模块)取数与元数据
|
|
19
19
|
e10-data doc count|list 文档查询(documentListCount + documentList)
|
|
20
20
|
e10-data noun list --skill <skillKey> 词库查询(可带 --source-type/--source-group/--source-table 三参数)
|
|
21
21
|
e10-data field id2name|name2id 关联字段翻译(testRemoteBrowser / complete)
|
|
22
|
+
e10-data conf get|batch|batch-file|resolve|resolve-file|add|add-file|update|delete 环境参数配置中心(chatBi config map,替代 config-resolve 脚本 URL 直连;confType 默认按 CONN_/DS_/WF_ 前缀推导,可 --type 显式)
|
|
22
23
|
e10-data exp status|manifest|commits|file|pull|init|commit|lock|release|rollback 专家包版本管理
|
|
23
24
|
```
|
|
24
25
|
|
|
@@ -26,6 +27,7 @@ e10-data exp status|manifest|commits|file|pull|init|commit|lock|release|roll
|
|
|
26
27
|
|
|
27
28
|
- `--json`:原样输出接口 JSON(供脚本/Agent 消费);默认人读输出。
|
|
28
29
|
- 复杂 JSON/中文参数:推荐 `@文件` 承载(如 `--dsl @q.json`、`--where @cond.json`)。
|
|
30
|
+
- ⚠️ 大整数精度(自动处理):E10 的 ID 多为 19 位,超过 JS 安全整数(2^53-1≈9e15)。CLI 解析 JSON 时**自动把超范围裸整数转成字符串**再发请求/输出(实测后端接受字符串做 =/IN/BETWEEN 数值比较),无需手动加引号,也不误伤字符串正文。
|
|
29
31
|
- 认证:进程内读取 `~/.workbuddy/e10-login-cache.md`;失效提示重新执行 `e10-login oidc`。
|
|
30
32
|
- 版本更新:`npm i -g e10-data@latest`;查看源上最新 `npm view e10-data version`。
|
|
31
33
|
|
|
@@ -33,12 +35,17 @@ e10-data exp status|manifest|commits|file|pull|init|commit|lock|release|roll
|
|
|
33
35
|
|
|
34
36
|
```bash
|
|
35
37
|
e10-data query --conn 1144919996804022272 --model dw --schema dw --dsl @q.json
|
|
38
|
+
e10-data query --conn 1144919996804022272 --model dw --schema dw --debug true --dsl @q.json # debug 模式:额外打印实际执行 SQL
|
|
36
39
|
e10-data ds count --group weaver-project-servicetask --table 715738355537494016
|
|
37
40
|
e10-data ds fields --group weaver-formreport-service --table 1307487628041044060
|
|
38
41
|
e10-data doc count --name 智能问数
|
|
39
42
|
e10-data noun list --skill rd-department
|
|
40
43
|
e10-data field id2name --module ebuilder/form --type ebuilder --ids 3194494277170470571 \
|
|
41
44
|
--form-param '{"fieldId":"966849910772850766","module":"ebuilderform"}'
|
|
45
|
+
e10-data conf get CONN_EC_EDCAPP # 读配置中心单条
|
|
46
|
+
e10-data conf resolve '{"connId":"#{CONN_EC_EDCAPP}"}' # 占位符替换为真实值
|
|
47
|
+
e10-data conf add CONN_EC_EDCAPP --value 10 --name "说明" --attrs '{"schema":"ec_edcapp"}'
|
|
48
|
+
e10-data conf delete CONN_EC_EDCAPP
|
|
42
49
|
e10-data exp status --expert data-assistant
|
|
43
50
|
e10-data exp pull --expert data-assistant --dir <本地专家包目录> --dry-run
|
|
44
51
|
```
|
package/bin/e10-data.mjs
CHANGED
|
@@ -4,11 +4,13 @@
|
|
|
4
4
|
* e10-data CLI —— data-assistant 专家统一数据接口命令行(2026-09-03 扩展)
|
|
5
5
|
*
|
|
6
6
|
* 覆盖(数据查询链路全部家族;登录/HRM 复用 e10-login / hrm-cli,checkEbModulePermission 已废弃不提供):
|
|
7
|
-
* e10-data query
|
|
7
|
+
* e10-data query DSL 查询 executeQueryDsl(conn/model/schema/tableId/dslQuery/debug)
|
|
8
8
|
* e10-data ds tables|fields|list|chart|stat|count 数据源(业务模块)取数与元数据
|
|
9
9
|
* e10-data doc count|list 文档查询 documentListCount / documentList
|
|
10
10
|
* e10-data noun list 词库 getChatDocsBySkillKey
|
|
11
11
|
* e10-data field id2name|name2id 关联字段翻译 testRemoteBrowser / complete
|
|
12
|
+
* e10-data conf get|batch|batch-file|resolve|resolve-file|add|add-file|update|delete
|
|
13
|
+
* 环境参数配置中心(chatBi config map,替代 config-resolve 脚本 URL 直连)
|
|
12
14
|
*
|
|
13
15
|
* 通用:--json 原样输出;复杂 JSON 参数支持 @文件 或 内联(中文走 @文件 更稳)
|
|
14
16
|
* 认证:进程内读取用户级登录缓存 ~/.workbuddy/e10-login-cache.md;不存在/失效提示先 e10-login
|
|
@@ -77,7 +79,7 @@ async function postJson(endpoint, bodyObj, extraHeaders = {}) {
|
|
|
77
79
|
body: JSON.stringify(bodyObj),
|
|
78
80
|
});
|
|
79
81
|
const text = await resp.text();
|
|
80
|
-
let j; try { j =
|
|
82
|
+
let j; try { j = parseJsonSafe(text); } catch { throw new Error("HTTP " + resp.status + " 非JSON响应: " + text.slice(0, 300)); }
|
|
81
83
|
checkAuthFail(resp, text, j);
|
|
82
84
|
return j;
|
|
83
85
|
}
|
|
@@ -94,7 +96,7 @@ async function getUrl(url) {
|
|
|
94
96
|
},
|
|
95
97
|
});
|
|
96
98
|
const text = await resp.text();
|
|
97
|
-
let j; try { j =
|
|
99
|
+
let j; try { j = parseJsonSafe(text); } catch { throw new Error("HTTP " + resp.status + " 非JSON响应: " + text.slice(0, 300)); }
|
|
98
100
|
checkAuthFail(resp, text, j);
|
|
99
101
|
return j;
|
|
100
102
|
}
|
|
@@ -116,7 +118,7 @@ async function postMultipart(endpoint, formObj) {
|
|
|
116
118
|
body: fd,
|
|
117
119
|
});
|
|
118
120
|
const text = await resp.text();
|
|
119
|
-
let j; try { j =
|
|
121
|
+
let j; try { j = parseJsonSafe(text); } catch { throw new Error("HTTP " + resp.status + " 非JSON响应: " + text.slice(0, 300)); }
|
|
120
122
|
checkAuthFail(resp, text, j);
|
|
121
123
|
return j;
|
|
122
124
|
}
|
|
@@ -136,10 +138,56 @@ function parseArgs(argv) {
|
|
|
136
138
|
}
|
|
137
139
|
return { args, pos };
|
|
138
140
|
}
|
|
141
|
+
// ⚠️ 大整数精度保护(2026-09-03):E10 主键/ID 多为 19 位大整数,超过 JS 安全整数 2^53-1
|
|
142
|
+
// (9007199254740991)。JSON.parse 会将其舍入为近似 double(如 1297901170990161977 → ...1920),
|
|
143
|
+
// 导致 DSL 条件查不到数据(静默空结果)或返回 ID 失真。此处把 JSON 中 >2^53 的裸数字字面量
|
|
144
|
+
// 加引号转字符串(后端实测接受字符串形式做 = / IN / BETWEEN 数值比较),请求与响应两侧统一保护。
|
|
145
|
+
function protectBigInts(raw) {
|
|
146
|
+
// 逐字符扫描:跟踪 JSON 字符串状态(含转义),仅在引号外的结构层把 >2^53 的裸整数加引号,
|
|
147
|
+
// 避免误伤字符串内容(如 avatar JSON、文档正文里含 ",大数字" 片段)。
|
|
148
|
+
let out = "", inStr = false, esc = false;
|
|
149
|
+
for (let i = 0; i < raw.length; i++) {
|
|
150
|
+
const ch = raw[i];
|
|
151
|
+
if (inStr) {
|
|
152
|
+
out += ch;
|
|
153
|
+
if (esc) esc = false;
|
|
154
|
+
else if (ch === "\\") esc = true;
|
|
155
|
+
else if (ch === '"') inStr = false;
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
if (ch === '"') { inStr = true; out += ch; continue; }
|
|
159
|
+
if (ch === "-" || (ch >= "0" && ch <= "9")) {
|
|
160
|
+
let j = i + 1;
|
|
161
|
+
while (j < raw.length && raw[j] >= "0" && raw[j] <= "9") j++; // 纯整数 token(无小数点/指数)
|
|
162
|
+
let k = i - 1;
|
|
163
|
+
while (k >= 0 && /\s/.test(raw[k])) k--;
|
|
164
|
+
const prev = k < 0 ? "" : raw[k];
|
|
165
|
+
let n = j;
|
|
166
|
+
while (n < raw.length && /\s/.test(raw[n])) n++;
|
|
167
|
+
const next = n < raw.length ? raw[n] : "";
|
|
168
|
+
const token = raw.slice(i, j);
|
|
169
|
+
if ((prev === ":" || prev === "," || prev === "[") && (next === "," || next === "}" || next === "]")) {
|
|
170
|
+
try {
|
|
171
|
+
const bi = BigInt(token);
|
|
172
|
+
if (bi > 9007199254740991n || bi < -9007199254740991n) { out += '"' + token + '"'; i = j - 1; continue; }
|
|
173
|
+
} catch { /* 非纯整数不转换 */ }
|
|
174
|
+
}
|
|
175
|
+
out += raw.slice(i, j);
|
|
176
|
+
i = j - 1;
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
out += ch;
|
|
180
|
+
}
|
|
181
|
+
return out;
|
|
182
|
+
}
|
|
183
|
+
function parseJsonSafe(raw, label) {
|
|
184
|
+
try { return JSON.parse(protectBigInts(raw)); }
|
|
185
|
+
catch (e) { throw new Error((label || "JSON") + " 解析失败: " + e.message); }
|
|
186
|
+
}
|
|
139
187
|
function readJsonArg(val, label) {
|
|
140
188
|
let raw = val;
|
|
141
189
|
if (val.startsWith("@")) raw = fs.readFileSync(val.slice(1), "utf-8");
|
|
142
|
-
|
|
190
|
+
return parseJsonSafe(raw, label);
|
|
143
191
|
}
|
|
144
192
|
function need(arg, args) { if (!args[arg]) throw new Error("缺少必填参数 --" + arg); return args[arg]; }
|
|
145
193
|
|
|
@@ -166,8 +214,15 @@ async function cmdQuery(args, json) {
|
|
|
166
214
|
const conn = need("conn", args), model = need("model", args), schema = need("schema", args);
|
|
167
215
|
const tableId = args["table-id"] || "";
|
|
168
216
|
const dslVal = need("dsl", args);
|
|
169
|
-
const dslQuery = dslVal.startsWith("@") ? readJsonArg(dslVal, "--dsl") :
|
|
170
|
-
const
|
|
217
|
+
const dslQuery = dslVal.startsWith("@") ? readJsonArg(dslVal, "--dsl") : parseJsonSafe(dslVal, "--dsl");
|
|
218
|
+
const body = { connId: conn, model, schema, tableId, dslQuery };
|
|
219
|
+
if (args.debug !== undefined) { // --debug true|false,与 conn/model 同层级
|
|
220
|
+
const dbg = String(args.debug).toLowerCase();
|
|
221
|
+
if (dbg === "true" || dbg === "1") body.debug = true;
|
|
222
|
+
else if (dbg === "false" || dbg === "0") body.debug = false;
|
|
223
|
+
else throw new Error("--debug 值必须为 true 或 false(当前: " + args.debug + ")");
|
|
224
|
+
}
|
|
225
|
+
const r = await postJson("/api/dw/dsl/executeQueryDsl", body);
|
|
171
226
|
if (json) return r;
|
|
172
227
|
if (String(r.code) !== "200" && r.code !== undefined && String(r.code) !== "0") {
|
|
173
228
|
console.log("code=" + r.code + " msg=" + (r.msg || ""));
|
|
@@ -176,7 +231,10 @@ async function cmdQuery(args, json) {
|
|
|
176
231
|
}
|
|
177
232
|
return;
|
|
178
233
|
}
|
|
179
|
-
const
|
|
234
|
+
const d = r.data;
|
|
235
|
+
// debug=true 时 data 为 { sql, records };否则为行数组
|
|
236
|
+
const rows = Array.isArray(d) ? d : (d && Array.isArray(d.records) ? d.records : []);
|
|
237
|
+
if (d && !Array.isArray(d) && d.sql) console.log("SQL: " + d.sql);
|
|
180
238
|
if (!rows.length) { console.log("(空:0 行)"); return; }
|
|
181
239
|
printHuman("结果 " + rows.length + " 行", rows);
|
|
182
240
|
}
|
|
@@ -397,6 +455,230 @@ async function cmdFieldName2id(args, json) {
|
|
|
397
455
|
printHuman("name→id 候选 " + rows.length + " 条", rows.map((x) => ({ id: x.id, name: x.name ?? x.title ?? "" })), ["id", "name"]);
|
|
398
456
|
}
|
|
399
457
|
|
|
458
|
+
// ---------------- conf:环境参数配置中心(chatBi config map) ----------------
|
|
459
|
+
// 覆盖 config-resolve 原 resolve_config.py 的 URL 直连调用:get/batch/batch-file/resolve/resolve-file
|
|
460
|
+
// 与管理 add/add-file/update/delete。认证复用进程内登录缓存(完整 Cookie + eteamsid,同 query/ds/doc),
|
|
461
|
+
// 无内置令牌;baseUrl 取登录缓存(与 executeQueryDsl 同域)。confType 默认由 key 前缀推导
|
|
462
|
+
// (CONN_→conn / DS_→ds / WF_→wf),可用 --type 显式指定任意大类(如 data-source-query / system-config)。
|
|
463
|
+
const CONF_PREFIX_TYPE = [["CONN_", "conn"], ["DS_", "ds"], ["WF_", "wf"]];
|
|
464
|
+
const CONF_GET_PATH = "/api/dw/newetl/chatBi/getChatBiConfigMap";
|
|
465
|
+
const CONF_BATCH_PATH = "/api/dw/newetl/chatBi/batchGetChatBiConfigMap";
|
|
466
|
+
const CONF_ADD_PATH = "/api/dw/newetl/chatBi/batchAddChatBiConfigMap";
|
|
467
|
+
const CONF_UPDATE_PATH = "/api/dw/newetl/chatBi/updateChatBiConfigMap";
|
|
468
|
+
const CONF_DELETE_PATH = "/api/dw/newetl/chatBi/deleteChatBiConfigMap";
|
|
469
|
+
const CONF_TOKEN_RE = /#\{([A-Z][A-Z0-9_]{2,})\}/g;
|
|
470
|
+
function confKeyType(key, explicitType) {
|
|
471
|
+
if (explicitType) return explicitType;
|
|
472
|
+
for (const [p, t] of CONF_PREFIX_TYPE) if (key.startsWith(p)) return t;
|
|
473
|
+
throw new Error("无法由 confKey 推导 confType(需 CONN_/DS_/WF_ 前缀或显式 --type): " + key);
|
|
474
|
+
}
|
|
475
|
+
function confFail(j, url) {
|
|
476
|
+
const m = (j && (j.msg || (j.actionMsg && j.actionMsg.message))) || "";
|
|
477
|
+
throw new Error("配置服务请求失败 [" + url + "]" + (m ? ": " + m : ""));
|
|
478
|
+
}
|
|
479
|
+
async function confRequest(method, url, bodyObj) {
|
|
480
|
+
const resp = await fetch(BASE + url, {
|
|
481
|
+
method,
|
|
482
|
+
headers: {
|
|
483
|
+
"accept": "application/json, text/plain, */*",
|
|
484
|
+
"langType": "zh_CN",
|
|
485
|
+
"cookie": auth.cookie,
|
|
486
|
+
"eteamsid": auth.eteamsid,
|
|
487
|
+
"ebbusinessid": EB_BIZ_ID,
|
|
488
|
+
"user-agent": UA,
|
|
489
|
+
...(bodyObj !== undefined ? { "Content-Type": "application/json;charset=UTF-8" } : {}),
|
|
490
|
+
},
|
|
491
|
+
...(bodyObj !== undefined ? { body: JSON.stringify(bodyObj) } : {}),
|
|
492
|
+
});
|
|
493
|
+
const text = await resp.text();
|
|
494
|
+
let j; try { j = parseJsonSafe(text); } catch { throw new Error("HTTP " + resp.status + " 非JSON响应: " + text.slice(0, 300)); }
|
|
495
|
+
checkAuthFail(resp, text, j);
|
|
496
|
+
return j;
|
|
497
|
+
}
|
|
498
|
+
async function confGetData(ctype, key, silent) {
|
|
499
|
+
const url = CONF_GET_PATH + "?confType=" + encodeURIComponent(ctype) + "&confKey=" + encodeURIComponent(key);
|
|
500
|
+
let r;
|
|
501
|
+
try { r = await confRequest("GET", url); }
|
|
502
|
+
catch (e) { if (silent) return null; throw e; }
|
|
503
|
+
const data = (r && r.data) || null;
|
|
504
|
+
if (!silent && (!data || data.confValue === undefined || data.confValue === null || data.confValue === "")) {
|
|
505
|
+
throw new Error("配置不存在或为空: confType=" + ctype + " confKey=" + key);
|
|
506
|
+
}
|
|
507
|
+
return data;
|
|
508
|
+
}
|
|
509
|
+
function confNormalizeBatch(payload) {
|
|
510
|
+
const data = (payload && payload.data) || null;
|
|
511
|
+
const out = {};
|
|
512
|
+
if (Array.isArray(data)) {
|
|
513
|
+
for (const row of data) {
|
|
514
|
+
if (!row || typeof row !== "object") continue;
|
|
515
|
+
const k = row.confKey || row.conf_key;
|
|
516
|
+
if (k) out[k] = row;
|
|
517
|
+
}
|
|
518
|
+
} else if (data && typeof data === "object") {
|
|
519
|
+
for (const [k, v] of Object.entries(data)) {
|
|
520
|
+
if (v && typeof v === "object" && !v.confKey) v.confKey = k;
|
|
521
|
+
out[k] = v;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
return out;
|
|
525
|
+
}
|
|
526
|
+
async function cmdConfGet(args, json, keys) {
|
|
527
|
+
const key = keys[0];
|
|
528
|
+
if (!key) throw new Error("conf get 缺少 confKey(用法: conf get <key> [--type t])");
|
|
529
|
+
const ctype = confKeyType(key, args.type);
|
|
530
|
+
const data = await confGetData(ctype, key);
|
|
531
|
+
if (json) return data;
|
|
532
|
+
console.log(JSON.stringify(data, null, 1));
|
|
533
|
+
}
|
|
534
|
+
async function cmdConfBatch(args, json, keys) {
|
|
535
|
+
if (!keys.length) throw new Error("conf batch 缺少 confKey 列表(用法: conf batch <key1> [key2 ...])");
|
|
536
|
+
const items = keys.map((k) => ({ confType: confKeyType(k, args.type), confKey: k }));
|
|
537
|
+
let map = {};
|
|
538
|
+
try {
|
|
539
|
+
const r = await confRequest("POST", CONF_BATCH_PATH, items);
|
|
540
|
+
map = confNormalizeBatch(r);
|
|
541
|
+
for (const it of items) {
|
|
542
|
+
const c = map[it.confKey];
|
|
543
|
+
if (!c || c.confValue === undefined || c.confValue === null || c.confValue === "") {
|
|
544
|
+
throw new Error("批量响应缺失 confKey=" + it.confKey);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
} catch (e) {
|
|
548
|
+
if (String(e.message).indexOf("批量响应缺失") === 0) throw e;
|
|
549
|
+
console.error("[config-resolve] 批量接口不可用,降级逐条单查: " + e.message);
|
|
550
|
+
map = {};
|
|
551
|
+
for (const it of items) map[it.confKey] = await confGetData(it.confType, it.confKey);
|
|
552
|
+
}
|
|
553
|
+
if (json) return map;
|
|
554
|
+
printHuman("配置 " + items.length + " 条", keys.map((k) => ({ key: k, confType: items.find((x) => x.confKey === k).confType, confValue: (map[k] || {}).confValue ?? "", confName: (map[k] || {}).confName ?? "" })), ["key", "confType", "confValue", "confName"]);
|
|
555
|
+
}
|
|
556
|
+
async function cmdConfBatchFile(args, json) {
|
|
557
|
+
if (!args["batch-file"]) throw new Error("conf batch-file 缺少参数(用法: conf batch-file @items.json)");
|
|
558
|
+
const items = readJsonArg(args["batch-file"], "--batch-file");
|
|
559
|
+
const list = Array.isArray(items) ? items : [items];
|
|
560
|
+
const map = {};
|
|
561
|
+
for (const it of list) {
|
|
562
|
+
if (!it.confKey || !it.confType) continue;
|
|
563
|
+
map[it.confKey] = await confGetData(it.confType, it.confKey);
|
|
564
|
+
}
|
|
565
|
+
if (json) return map;
|
|
566
|
+
printHuman("配置 " + Object.keys(map).length + " 条", Object.keys(map).map((k) => ({ key: k, confValue: (map[k] || {}).confValue ?? "", confName: (map[k] || {}).confName ?? "" })), ["key", "confValue", "confName"]);
|
|
567
|
+
}
|
|
568
|
+
async function confResolveAll(keys) {
|
|
569
|
+
// 多占位符:先批量预取一轮(失败降级逐条单查),返回 {key: data}
|
|
570
|
+
const items = keys.map((k) => ({ confType: confKeyType(k, ""), confKey: k }));
|
|
571
|
+
const map = {};
|
|
572
|
+
try {
|
|
573
|
+
const r = await confRequest("POST", CONF_BATCH_PATH, items);
|
|
574
|
+
const got = confNormalizeBatch(r);
|
|
575
|
+
for (const it of items) {
|
|
576
|
+
const c = got[it.confKey];
|
|
577
|
+
if (!c || c.confValue === undefined || c.confValue === null || c.confValue === "") throw new Error("缺失 " + it.confKey);
|
|
578
|
+
map[it.confKey] = c;
|
|
579
|
+
}
|
|
580
|
+
} catch {
|
|
581
|
+
for (const it of items) map[it.confKey] = await confGetData(it.confType, it.confKey);
|
|
582
|
+
}
|
|
583
|
+
return map;
|
|
584
|
+
}
|
|
585
|
+
async function confResolveText(text, json) {
|
|
586
|
+
const found = [...new Set([...text.matchAll(CONF_TOKEN_RE)].map((m) => m[1]))];
|
|
587
|
+
const map = found.length ? await confResolveAll(found) : {};
|
|
588
|
+
const out = text.replace(CONF_TOKEN_RE, (m0, k) => {
|
|
589
|
+
const cfg = map[k];
|
|
590
|
+
if (!cfg) throw new Error("配置不存在或为空: " + k);
|
|
591
|
+
return cfg.confValue ?? "";
|
|
592
|
+
});
|
|
593
|
+
if (json) return { input: text, output: out, resolved: Object.fromEntries(Object.entries(map).map(([k, v]) => [k, v.confValue ?? ""])) };
|
|
594
|
+
return out;
|
|
595
|
+
}
|
|
596
|
+
async function cmdConfResolve(args, json, keys) {
|
|
597
|
+
let text = keys[0];
|
|
598
|
+
if (!text) throw new Error("conf resolve 缺少文本(用法: conf resolve '<含#{KEY}文本>' | @file.txt)");
|
|
599
|
+
if (text.startsWith("@")) text = fs.readFileSync(text.slice(1), "utf-8");
|
|
600
|
+
const out = await confResolveText(text, json);
|
|
601
|
+
if (json) { console.log(JSON.stringify(out, null, 1)); return out; }
|
|
602
|
+
process.stdout.write(out + "\n");
|
|
603
|
+
}
|
|
604
|
+
async function cmdConfResolveFile(args, json) {
|
|
605
|
+
const p = args["resolve-file"];
|
|
606
|
+
if (!p) throw new Error("conf resolve-file 缺少文件路径(用法: conf resolve-file <文件路径>)");
|
|
607
|
+
if (!fs.existsSync(p)) throw new Error("文件不存在: " + p);
|
|
608
|
+
const text = fs.readFileSync(p, "utf-8");
|
|
609
|
+
const out = await confResolveText(text, json);
|
|
610
|
+
if (json) { console.log(JSON.stringify(out, null, 1)); return out; }
|
|
611
|
+
process.stdout.write(out + "\n");
|
|
612
|
+
}
|
|
613
|
+
async function cmdConfAdd(args, json, keys) {
|
|
614
|
+
const key = keys[0];
|
|
615
|
+
if (!key || args.value === undefined) throw new Error("conf add 缺少 confKey/confValue(用法: conf add <key> --value <真实值> [--name] [--attrs] [--type] [--force])");
|
|
616
|
+
const ctype = confKeyType(key, args.type);
|
|
617
|
+
if (!args.force && await confGetData(ctype, key, true)) {
|
|
618
|
+
console.error("[config-resolve] 配置已存在,跳过新增(如需修改请用 conf update;确需重建可先 conf delete): " + key);
|
|
619
|
+
return null;
|
|
620
|
+
}
|
|
621
|
+
const item = { confType: ctype, confKey: key, confValue: String(args.value), confAttrs: args.attrs || "", confName: args.name || "" };
|
|
622
|
+
const r = await confRequest("POST", CONF_ADD_PATH, [item]);
|
|
623
|
+
if (json) return r;
|
|
624
|
+
console.log("新增成功: " + key + " = " + item.confValue);
|
|
625
|
+
}
|
|
626
|
+
async function cmdConfAddFile(args, json) {
|
|
627
|
+
if (args["add-file"] === undefined) throw new Error("conf add-file 缺少参数(用法: conf add-file @items.json)");
|
|
628
|
+
const items = readJsonArg(args["add-file"], "--add-file");
|
|
629
|
+
const list = Array.isArray(items) ? items : [items];
|
|
630
|
+
const toAdd = [];
|
|
631
|
+
for (const it of list) {
|
|
632
|
+
if (!it.confKey || it.confValue === undefined) continue;
|
|
633
|
+
const ctype = it.confType || confKeyType(it.confKey, args.type);
|
|
634
|
+
const dup = await confGetData(ctype, it.confKey, true);
|
|
635
|
+
if (dup) { console.error("[config-resolve] 已存在跳过: " + it.confKey); continue; }
|
|
636
|
+
toAdd.push({ confType: ctype, confKey: it.confKey, confValue: String(it.confValue), confAttrs: it.confAttrs || "", confName: it.confName || "" });
|
|
637
|
+
}
|
|
638
|
+
if (!toAdd.length) { console.log("全部已存在,无新增"); return null; }
|
|
639
|
+
const r = await confRequest("POST", CONF_ADD_PATH, toAdd);
|
|
640
|
+
if (json) return r;
|
|
641
|
+
console.log("批量新增 " + toAdd.length + " 条成功");
|
|
642
|
+
}
|
|
643
|
+
async function cmdConfUpdate(args, json, keys) {
|
|
644
|
+
const key = keys[0];
|
|
645
|
+
if (!key) throw new Error("conf update 缺少 confKey(用法: conf update <key> [--value v] [--name n] [--attrs a] [--type t])");
|
|
646
|
+
if (args.value === undefined && args.name === undefined && args.attrs === undefined) {
|
|
647
|
+
throw new Error("conf update 至少需提供 --value / --name / --attrs 之一");
|
|
648
|
+
}
|
|
649
|
+
const body = { confType: confKeyType(key, args.type), confKey: key };
|
|
650
|
+
if (args.value !== undefined) body.confValue = String(args.value);
|
|
651
|
+
if (args.name !== undefined) body.confName = args.name;
|
|
652
|
+
if (args.attrs !== undefined) body.confAttrs = args.attrs;
|
|
653
|
+
const r = await confRequest("POST", CONF_UPDATE_PATH, body);
|
|
654
|
+
if (json) return r;
|
|
655
|
+
console.log("更新成功: " + key);
|
|
656
|
+
}
|
|
657
|
+
async function cmdConfDelete(args, json, keys) {
|
|
658
|
+
const key = keys[0];
|
|
659
|
+
if (!key) throw new Error("conf delete 缺少 confKey(用法: conf delete <key> [--type t])");
|
|
660
|
+
const ctype = confKeyType(key, args.type);
|
|
661
|
+
const url = CONF_DELETE_PATH + "?confType=" + encodeURIComponent(ctype) + "&confKey=" + encodeURIComponent(key);
|
|
662
|
+
const r = await confRequest("DELETE", url);
|
|
663
|
+
if (json) return r;
|
|
664
|
+
console.log("删除成功: " + key);
|
|
665
|
+
}
|
|
666
|
+
async function cmdConf(sub, args, json, pos) {
|
|
667
|
+
const keys = pos.slice(2);
|
|
668
|
+
switch (sub) {
|
|
669
|
+
case "get": return cmdConfGet(args, json, keys);
|
|
670
|
+
case "batch": return cmdConfBatch(args, json, keys);
|
|
671
|
+
case "batch-file": return cmdConfBatchFile(args, json);
|
|
672
|
+
case "resolve": return cmdConfResolve(args, json, keys);
|
|
673
|
+
case "resolve-file": return cmdConfResolveFile(args, json);
|
|
674
|
+
case "add": return cmdConfAdd(args, json, keys);
|
|
675
|
+
case "add-file": return cmdConfAddFile(args, json);
|
|
676
|
+
case "update": return cmdConfUpdate(args, json, keys);
|
|
677
|
+
case "delete": return cmdConfDelete(args, json, keys);
|
|
678
|
+
default: usage(); process.exit(1);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
|
|
400
682
|
// ---------------- exp:专家包版本管理(expert-repository) ----------------
|
|
401
683
|
async function expGet(url) {
|
|
402
684
|
// 返回 {raw, j}:raw 用于正则提取大整数 id(commitId 19 位超 JS Number 精度,JSON.parse 会丢精度!)
|
|
@@ -405,7 +687,7 @@ async function expGet(url) {
|
|
|
405
687
|
headers: { "accept": "application/json, text/plain, */*", "langType": "zh_CN", "cookie": auth.cookie, "eteamsid": auth.eteamsid, "ebbusinessid": EB_BIZ_ID, "user-agent": UA },
|
|
406
688
|
});
|
|
407
689
|
const raw = await resp.text();
|
|
408
|
-
let j; try { j =
|
|
690
|
+
let j; try { j = parseJsonSafe(raw); } catch { throw new Error("HTTP " + resp.status + " 非JSON响应: " + raw.slice(0, 300)); }
|
|
409
691
|
if (resp.status === 401 || (j && String(j.code) === "-1" && /token|登录|失效/i.test(String(j.msg || "")))) throw new Error("登录态失效,请重新 e10-login oidc 登录");
|
|
410
692
|
return { raw, j };
|
|
411
693
|
}
|
|
@@ -661,7 +943,7 @@ function usage() {
|
|
|
661
943
|
用法: node e10-data.mjs <族> <子命令> [--参数] 通用 --json 原样输出
|
|
662
944
|
|
|
663
945
|
query DSL 数据查询 executeQueryDsl
|
|
664
|
-
query --conn <connId> --model <ebuilder|flow|eteams|attend|hr|document|dw|blog> --schema <数据库> [--table-id <流程ID>] --dsl @file.json|'{...}'
|
|
946
|
+
query --conn <connId> --model <ebuilder|flow|eteams|attend|hr|document|dw|blog> --schema <数据库> [--table-id <流程ID>] [--debug true|false] --dsl @file.json|'{...}'
|
|
665
947
|
ds 数据源(业务模块)取数与元数据
|
|
666
948
|
ds tables --group <gid> [--name 关键词]
|
|
667
949
|
ds fields --group <gid> --table <tid>
|
|
@@ -678,6 +960,16 @@ noun 词库查询
|
|
|
678
960
|
field 关联字段翻译
|
|
679
961
|
field id2name --module <m> --type <t> --ids id1,id2 [--form-param '{"fieldId":"..","module":"ebuilderform"}'|--eb-browser-param '{..}'|--property-type x]
|
|
680
962
|
field name2id --module <m> --type <t> --name 名称 [同上附加参数]
|
|
963
|
+
conf 环境参数配置中心(chatBi config map,替代 config-resolve 脚本 URL 直连;confType 默认由 CONN_/DS_/WF_ 前缀推导,可 --type 显式)
|
|
964
|
+
conf get <key> [--type t] # 单查(confValue/confAttrs/confName 全量 JSON)
|
|
965
|
+
conf batch <key1> [key2 ...] # 批量查(批量接口异常自动降级逐条单查)
|
|
966
|
+
conf batch-file @items.json # 按 [{confType,confKey}..] 显式批量查(支持任意 confType)
|
|
967
|
+
conf resolve '<含#{KEY}文本>'|@file.txt # 文本占位符替换为真实值(含中文建议 @file)
|
|
968
|
+
conf resolve-file <文件路径> # 读文件替换全部占位符并打印(不改写原文件)
|
|
969
|
+
conf add <key> --value <真实值> [--name 说明] [--attrs '{"schema":..}'] [--type t] [--force] # 新增(默认判重,已存在跳过)
|
|
970
|
+
conf add-file @items.json # JSON 数组批量新增(逐条判重)
|
|
971
|
+
conf update <key> [--value v] [--name n] [--attrs a] [--type t] # 更新(confType+confKey 定位,传哪个字段更哪个)
|
|
972
|
+
conf delete <key> [--type t] # 删除
|
|
681
973
|
exp 专家包版本管理(expert-repository/v1,响应以 data.actionMsg.code=0 判成功)
|
|
682
974
|
exp status --expert <code> [--branch main] # 仓库状态(headCommitId/headRevision=提交基线)
|
|
683
975
|
exp manifest --expert <code> [--commit <id>] # 文件清单(sha256)
|
|
@@ -694,7 +986,8 @@ exp 专家包版本管理(expert-repository/v1,响应以 data.actionMsg.
|
|
|
694
986
|
|
|
695
987
|
async function main() {
|
|
696
988
|
const rawArgv = process.argv.slice(2);
|
|
697
|
-
|
|
989
|
+
// 仅当顶层显式 -v/--version(无子命令)时打印版本;exp release/rollback 的 --version 是版本号参数、conf 文本可能含 version 字样,一律放行
|
|
990
|
+
if (rawArgv.length === 1 && (rawArgv[0] === "--version" || rawArgv[0] === "-v")) {
|
|
698
991
|
console.log("e10-data " + cliVersion());
|
|
699
992
|
process.exit(0);
|
|
700
993
|
}
|
|
@@ -726,6 +1019,9 @@ async function main() {
|
|
|
726
1019
|
if (sub === "id2name") result = await cmdFieldId2name(args, json);
|
|
727
1020
|
else if (sub === "name2id") result = await cmdFieldName2id(args, json);
|
|
728
1021
|
else { usage(); process.exit(1); }
|
|
1022
|
+
} else if (family === "conf") {
|
|
1023
|
+
if (!sub) { usage(); process.exit(1); }
|
|
1024
|
+
result = await cmdConf(sub, args, json, pos);
|
|
729
1025
|
} else if (family === "exp") {
|
|
730
1026
|
if (!sub) { usage(); process.exit(1); }
|
|
731
1027
|
switch (sub) {
|
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 专家配套):DSL
|
|
3
|
+
"version": "0.1.3",
|
|
4
|
+
"description": "泛微 E10 数据接口统一 CLI(data-assistant 专家配套):DSL 查询/数据源/文档/词库/关联字段翻译/环境参数配置中心(conf)/专家包版本管理,认证复用 weaver-e10-login 登录态",
|
|
5
5
|
"author": "e10-dw",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"type": "module",
|
|
@@ -27,4 +27,4 @@
|
|
|
27
27
|
"scripts": {
|
|
28
28
|
"pack": "npm pack"
|
|
29
29
|
}
|
|
30
|
-
}
|
|
30
|
+
}
|