e10-data 0.1.0

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.
Files changed (3) hide show
  1. package/README.md +44 -0
  2. package/bin/e10-data.mjs +747 -0
  3. package/package.json +30 -0
package/README.md ADDED
@@ -0,0 +1,44 @@
1
+ # e10-data
2
+
3
+ 泛微 E10 数据接口统一命令行工具(「数据助手」data-assistant 专家配套)。将专家内散落的 HTTP 接口调用收敛为统一的 CLI 命令面。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ npm i -g e10-data # 全局安装
9
+ e10-data --version # 验证
10
+ ```
11
+
12
+ > 依赖:认证复用 `weaver-e10-login` 的登录态(用户级缓存 `~/.workbuddy/e10-login-cache.md`)。请先安装并登录 weaver-e10-login(`e10-login oidc`),登录态失效时 CLI 会提示重新登录。
13
+
14
+ ## 命令总览
15
+
16
+ ```
17
+ e10-data query <--conn <connId> --model <ebuilder|flow|eteams|attend|hr|document|dw|blog> --schema <数据库> [--table-id <流程ID>] --dsl @file.json>
18
+ e10-data ds tables|fields|list|chart|stat|count 数据源(业务模块)取数与元数据
19
+ e10-data doc count|list 文档查询(documentListCount + documentList)
20
+ e10-data noun list --skill <skillKey> 词库查询(可带 --source-type/--source-group/--source-table 三参数)
21
+ e10-data field id2name|name2id 关联字段翻译(testRemoteBrowser / complete)
22
+ e10-data exp status|manifest|commits|file|pull|init|commit|lock|release|rollback 专家包版本管理
23
+ ```
24
+
25
+ ## 通用约定
26
+
27
+ - `--json`:原样输出接口 JSON(供脚本/Agent 消费);默认人读输出。
28
+ - 复杂 JSON/中文参数:推荐 `@文件` 承载(如 `--dsl @q.json`、`--where @cond.json`)。
29
+ - 认证:进程内读取 `~/.workbuddy/e10-login-cache.md`;失效提示重新执行 `e10-login oidc`。
30
+ - 版本更新:`npm i -g e10-data@latest`;查看源上最新 `npm view e10-data version`。
31
+
32
+ ## 用法示例
33
+
34
+ ```bash
35
+ e10-data query --conn 1144919996804022272 --model dw --schema dw --dsl @q.json
36
+ e10-data ds count --group weaver-project-servicetask --table 715738355537494016
37
+ e10-data ds fields --group weaver-formreport-service --table 1307487628041044060
38
+ e10-data doc count --name 智能问数
39
+ e10-data noun list --skill rd-department
40
+ e10-data field id2name --module ebuilder/form --type ebuilder --ids 3194494277170470571 \
41
+ --form-param '{"fieldId":"966849910772850766","module":"ebuilderform"}'
42
+ e10-data exp status --expert data-assistant
43
+ e10-data exp pull --expert data-assistant --dir <本地专家包目录> --dry-run
44
+ ```
@@ -0,0 +1,747 @@
1
+ #!/usr/bin/env node
2
+ // -*- coding: utf-8 -*-
3
+ /**
4
+ * e10-data CLI —— data-assistant 专家统一数据接口命令行(2026-09-03 扩展)
5
+ *
6
+ * 覆盖(数据查询链路全部家族;登录/HRM 复用 e10-login / hrm-cli,checkEbModulePermission 已废弃不提供):
7
+ * e10-data query run|... DSL 查询 executeQueryDsl(conn/model/schema/tableId/dslQuery)
8
+ * e10-data ds tables|fields|list|chart|stat|count 数据源(业务模块)取数与元数据
9
+ * e10-data doc count|list 文档查询 documentListCount / documentList
10
+ * e10-data noun list 词库 getChatDocsBySkillKey
11
+ * e10-data field id2name|name2id 关联字段翻译 testRemoteBrowser / complete
12
+ *
13
+ * 通用:--json 原样输出;复杂 JSON 参数支持 @文件 或 内联(中文走 @文件 更稳)
14
+ * 认证:进程内读取用户级登录缓存 ~/.workbuddy/e10-login-cache.md;不存在/失效提示先 e10-login
15
+ *
16
+ * 用法示例:
17
+ * node e10-data.mjs query --conn 1144919996804022272 --model dw --schema dw --dsl @q.json
18
+ * node e10-data.mjs ds count --group weaver-project-servicetask --table 715738355537494016
19
+ * node e10-data.mjs doc count --name 测试
20
+ * node e10-data.mjs noun list --skill rd-department
21
+ * node e10-data.mjs field id2name --module ebuilder/form --type ebuilder \
22
+ * --ids 3194494277170470571 --form-param '{"fieldId":"966849910772850766","module":"ebuilderform"}'
23
+ */
24
+ import fs from "node:fs";
25
+ import os from "node:os";
26
+ import path from "node:path";
27
+ import crypto from "node:crypto";
28
+ import { fileURLToPath } from "node:url";
29
+
30
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
31
+ function cliVersion() {
32
+ try { return JSON.parse(fs.readFileSync(path.join(__dirname, "..", "package.json"), "utf-8")).version; }
33
+ catch { return "dev"; }
34
+ }
35
+
36
+ const UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36";
37
+ const EB_BIZ_ID = "10000000000000000";
38
+
39
+ // ---------------- 认证 ----------------
40
+ function loadAuth() {
41
+ const p = path.join(os.homedir(), ".workbuddy", "e10-login-cache.md");
42
+ if (!fs.existsSync(p)) throw new Error("未找到登录缓存 " + p + ",请先执行 e10-login oidc / set 登录");
43
+ const txt = fs.readFileSync(p, "utf-8");
44
+ const get = (key) => {
45
+ const m = txt.match(new RegExp("\\*\\*" + key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "\\*\\*\\s*[::]\\s*`?([^`\\n]+)`?"));
46
+ return m ? m[1].trim() : "";
47
+ };
48
+ const auth = { baseUrl: get("baseUrl"), eteamsid: get("ETEAMSID"), cookie: get("完整 Cookie 串"), userId: get("userId") || get("employeeId"), tenantKey: get("tenantKey") };
49
+ if (!auth.baseUrl || !auth.eteamsid) throw new Error("登录缓存缺少 baseUrl/ETEAMSID,请重新登录(e10-login oidc)");
50
+ if (!auth.cookie) auth.cookie = "langType=zh_CN; ETEAMSID=" + auth.eteamsid + "; agentType=workbuddy; isAgent=true";
51
+ return auth;
52
+ }
53
+ const auth = loadAuth();
54
+ const BASE = auth.baseUrl.replace(/\/+$/, "");
55
+ const USER = { id: auth.userId || "", tenantKey: auth.tenantKey || "" };
56
+
57
+ // ---------------- HTTP ----------------
58
+ function checkAuthFail(resp, text, j) {
59
+ if (resp.status === 401 || (j && String(j.code) === "-1" && /token|登录|失效/i.test(String(j.msg || "")))) {
60
+ throw new Error("登录态失效(" + (j.msg || resp.status) + "),请重新 e10-login oidc 登录");
61
+ }
62
+ }
63
+ async function postJson(endpoint, bodyObj, extraHeaders = {}) {
64
+ const resp = await fetch(BASE + endpoint, {
65
+ method: "POST",
66
+ headers: {
67
+ "Content-Type": "application/json;charset=UTF-8",
68
+ "accept": "application/json, text/plain, */*",
69
+ "langType": "zh_CN",
70
+ "cookie": auth.cookie,
71
+ "eteamsid": auth.eteamsid,
72
+ "ebbusinessid": EB_BIZ_ID,
73
+ "user-agent": UA,
74
+ "timezoneoffset": "-480",
75
+ ...extraHeaders,
76
+ },
77
+ body: JSON.stringify(bodyObj),
78
+ });
79
+ const text = await resp.text();
80
+ let j; try { j = JSON.parse(text); } catch { throw new Error("HTTP " + resp.status + " 非JSON响应: " + text.slice(0, 300)); }
81
+ checkAuthFail(resp, text, j);
82
+ return j;
83
+ }
84
+ async function getUrl(url) {
85
+ const resp = await fetch(BASE + url, {
86
+ method: "GET",
87
+ headers: {
88
+ "accept": "application/json, text/plain, */*",
89
+ "langType": "zh_CN",
90
+ "cookie": auth.cookie,
91
+ "eteamsid": auth.eteamsid,
92
+ "ebbusinessid": EB_BIZ_ID,
93
+ "user-agent": UA,
94
+ },
95
+ });
96
+ const text = await resp.text();
97
+ let j; try { j = JSON.parse(text); } catch { throw new Error("HTTP " + resp.status + " 非JSON响应: " + text.slice(0, 300)); }
98
+ checkAuthFail(resp, text, j);
99
+ return j;
100
+ }
101
+ async function postMultipart(endpoint, formObj) {
102
+ const fd = new FormData();
103
+ for (const [k, v] of Object.entries(formObj)) fd.append(k, typeof v === "string" ? v : JSON.stringify(v));
104
+ const resp = await fetch(BASE + endpoint, {
105
+ method: "POST",
106
+ headers: {
107
+ "accept": "application/json, text/plain, */*",
108
+ "langType": "zh_CN",
109
+ "cookie": auth.cookie,
110
+ "eteamsid": auth.eteamsid,
111
+ "ebbusinessid": EB_BIZ_ID,
112
+ "user-agent": UA,
113
+ "timezoneoffset": "-480",
114
+ "referer": BASE + "/sp/ebddesigner/1282331905881874436",
115
+ },
116
+ body: fd,
117
+ });
118
+ const text = await resp.text();
119
+ let j; try { j = JSON.parse(text); } catch { throw new Error("HTTP " + resp.status + " 非JSON响应: " + text.slice(0, 300)); }
120
+ checkAuthFail(resp, text, j);
121
+ return j;
122
+ }
123
+
124
+ // ---------------- 参数解析 ----------------
125
+ function parseArgs(argv) {
126
+ const args = {};
127
+ const pos = [];
128
+ for (let i = 0; i < argv.length; i++) {
129
+ const a = argv[i];
130
+ if (a.startsWith("--")) {
131
+ const key = a.slice(2);
132
+ const v = argv[i + 1];
133
+ if (v === undefined || v.startsWith("--")) { args[key] = true; }
134
+ else { args[key] = v; i++; }
135
+ } else pos.push(a);
136
+ }
137
+ return { args, pos };
138
+ }
139
+ function readJsonArg(val, label) {
140
+ let raw = val;
141
+ if (val.startsWith("@")) raw = fs.readFileSync(val.slice(1), "utf-8");
142
+ try { return JSON.parse(raw); } catch (e) { throw new Error(label + " JSON 解析失败: " + e.message); }
143
+ }
144
+ function need(arg, args) { if (!args[arg]) throw new Error("缺少必填参数 --" + arg); return args[arg]; }
145
+
146
+ // ---------------- 展示 ----------------
147
+ function fmt(v) {
148
+ if (v === null || v === undefined) return "";
149
+ if (typeof v === "object") { const s = JSON.stringify(v); return s.length > 80 ? s.slice(0, 80) + "…" : s; }
150
+ return String(v);
151
+ }
152
+ function printHuman(title, rows, cols) {
153
+ if (title) console.log("== " + title + " ==");
154
+ if (!rows || !rows.length) { console.log("(空)"); return; }
155
+ if (cols) {
156
+ console.log(cols.join("\t"));
157
+ for (const r of rows) console.log(cols.map((c) => fmt(r[c])).join("\t"));
158
+ } else {
159
+ for (const r of rows.slice(0, 100)) console.log(JSON.stringify(r));
160
+ if (rows.length > 100) console.log("... 共 " + rows.length + " 行(--json 取全量)");
161
+ }
162
+ }
163
+
164
+ // ---------------- query:DSL 查询 ----------------
165
+ async function cmdQuery(args, json) {
166
+ const conn = need("conn", args), model = need("model", args), schema = need("schema", args);
167
+ const tableId = args["table-id"] || "";
168
+ const dslVal = need("dsl", args);
169
+ const dslQuery = dslVal.startsWith("@") ? readJsonArg(dslVal, "--dsl") : JSON.parse(dslVal);
170
+ const r = await postJson("/api/dw/dsl/executeQueryDsl", { connId: conn, model, schema, tableId, dslQuery });
171
+ if (json) return r;
172
+ if (String(r.code) !== "200" && r.code !== undefined && String(r.code) !== "0") {
173
+ console.log("code=" + r.code + " msg=" + (r.msg || ""));
174
+ if (/没有权限|无权限|权限不足|未授权|无权访问|无查询权限/i.test(String(r.msg || ""))) {
175
+ throw new Error("当前用户对该数据无查询权限,请检查 ETEAMSID 是否有效或联系管理员开通");
176
+ }
177
+ return;
178
+ }
179
+ const rows = r.data || [];
180
+ if (!rows.length) { console.log("(空:0 行)"); return; }
181
+ printHuman("结果 " + rows.length + " 行", rows);
182
+ }
183
+
184
+ // ---------------- ds:数据源 ----------------
185
+ function dyn() {
186
+ return { eteamsId: auth.eteamsid, userId: auth.userId || "", tenantKey: auth.tenantKey || "", langId: 7, formatDate: "yyyy-MM-dd HH:mm:ss", isPublic: false };
187
+ }
188
+ function baseDto(tableId, groupId, pageNo, pageSize, queryType) {
189
+ return { tableId: String(tableId), groupId, pageNo, pageSize, queryType, containDetailTable: false, needFormat: true, dynamicParams: dyn() };
190
+ }
191
+ function parseWhere(val) {
192
+ if (!val) return null;
193
+ const arr = readJsonArg(val, "--where");
194
+ const list = Array.isArray(arr) ? arr : (arr.conditionList || []);
195
+ if (!list.length) return null;
196
+ const ts = Date.now(), rootKey = "root_" + ts;
197
+ return {
198
+ key: rootKey, relationship: "1", isParent: true, parentNode: "", mainField: true,
199
+ conditionList: list.map((c, i) => ({
200
+ key: rootKey + "_" + i, relationship: "1", isParent: false, parentNode: rootKey,
201
+ objId: c.objId || "", conditionId: c.fieldId, fieldName: c.fieldName || c.fieldId,
202
+ conditionType: c.conditionType || "", compareType: c.compareType, conditionValue: c.conditionValue ?? "",
203
+ fieldValue: c.fieldValue || "", mainField: c.mainField !== false, ignoreCaseMark: c.ignoreCaseMark || false,
204
+ })),
205
+ };
206
+ }
207
+ function parseStat(val) {
208
+ const arr = readJsonArg(val, "--stat");
209
+ const list = Array.isArray(arr) ? arr : [arr];
210
+ return list.map((s) => ({ fieldId: s.fieldId, fieldName: s.fieldId, objId: "", mainField: true, aggregator: s.aggregator || "SUM", ...(s.fieldAlias ? { fieldAlias: s.fieldAlias } : {}) }));
211
+ }
212
+ function parseOrder(val) {
213
+ if (!val) return [];
214
+ return val.split(",").map((s) => {
215
+ const [f, dir] = s.trim().split(/\s+/);
216
+ return { fieldId: f, fieldName: f, objId: "", isAsc: (dir || "asc").toLowerCase() !== "desc", mainField: true };
217
+ });
218
+ }
219
+ async function cmdTables(args, json) {
220
+ const gid = need("group", args);
221
+ const form = { name: args.name || "", pageNo: args.page || "1", pageSize: args.size || "100", groupId: gid, sourceType: args["source-type"] || "LOGIC", dataUsage: "" };
222
+ const r = await postMultipart("/api/ebuilder/common/ds/listByPage", form);
223
+ if (json) return r;
224
+ const recs = ((r.data || {}).records) || [];
225
+ printHuman("分组表清单 (total=" + ((r.data || {}).total) + ")", recs, ["id", "name", "tableText", "detailTable", "usage"]);
226
+ }
227
+ async function cmdFields(args, json) {
228
+ const gid = need("group", args), tid = need("table", args);
229
+ const form = { groupId: gid, sourceId: tid, sourceType: args["source-type"] || "LOGIC", detailFieldsGroup: "true", enableMerge: "true", needTableName: "true", params: "[]" };
230
+ const r = await postMultipart("/api/ebuilder/common/ds/fields", form);
231
+ if (json) return r;
232
+ for (const d of (r.data || [])) {
233
+ console.log("== 表: " + d.name + " (id=" + d.id + ") 字段 " + (d.fields || []).length + " 个 ==");
234
+ for (const f of (d.fields || [])) console.log([f.name, f.text, f.type, f.mainPrimaryKey ? "PK" : ""].filter(Boolean).join("\t"));
235
+ }
236
+ }
237
+ async function cmdList(args, json) {
238
+ const gid = need("group", args), tid = need("table", args), fld = need("fields", args);
239
+ const fields = fld.split(",").map((f) => ({ fieldId: f, fieldName: f, objId: String(tid), mainField: true }));
240
+ const dto = baseDto(tid, gid, parseInt(args.page || "1", 10), parseInt(args.size || "20", 10), "getListData");
241
+ dto.queryFields = fields;
242
+ dto.orderFields = parseOrder(args.order);
243
+ const ct = parseWhere(args.where); if (ct) dto.conditionTrees = ct;
244
+ dto.queryCount = true;
245
+ const r = await postMultipart("/api/dw/newetl/chatBi/ds/getListDatas", { sourceType: args["source-type"] || "LOGIC", queryDto: dto });
246
+ if (json) return r;
247
+ const data = r.data || {};
248
+ console.log("== 数据列表 count=" + data.count + " 行=" + (data.list || []).length + " ==");
249
+ printHuman("", data.list || [], fields.map((f) => f.fieldId));
250
+ }
251
+ async function cmdChart(args, json) {
252
+ const gid = need("group", args), tid = need("table", args), gb = need("group-by", args);
253
+ const dto = baseDto(tid, gid, parseInt(args.page || "1", 10), parseInt(args.size || "100", 10), "getChartData");
254
+ dto.groupFields = gb.split(",").map((f) => ({ fieldId: f, fieldName: f, objId: String(tid), mainField: true }));
255
+ dto.groupStatFields = parseStat(need("stat", args)).map((s) => ({ ...s, objId: String(tid) }));
256
+ const ct = parseWhere(args.where); if (ct) dto.conditionTrees = ct;
257
+ const r = await postMultipart("/api/dw/newetl/chatBi/ds/getChartDatas", { sourceType: args["source-type"] || "LOGIC", queryDto: dto });
258
+ if (json) return r;
259
+ printHuman("分组聚合 rows=" + (r.data || []).length, r.data || []);
260
+ }
261
+ async function cmdStat(args, json) {
262
+ const gid = need("group", args), tid = need("table", args);
263
+ const dto = baseDto(tid, gid, 1, 10, "getStatData");
264
+ dto.statFields = parseStat(need("stat", args)).map((s) => ({ ...s, objId: String(tid) }));
265
+ const ct = parseWhere(args.where); if (ct) dto.conditionTrees = ct;
266
+ const r = await postMultipart("/api/dw/newetl/chatBi/ds/getStatDatas", { sourceType: args["source-type"] || "LOGIC", queryDto: dto });
267
+ if (json) return r;
268
+ const data = r.data || {};
269
+ if (data.fields && data.fields.length) { for (const f of data.fields) console.log(JSON.stringify(f)); }
270
+ else console.log("(无统计结果)");
271
+ }
272
+ async function cmdCount(args, json) {
273
+ const gid = need("group", args), tid = need("table", args);
274
+ const dto = baseDto(tid, gid, 1, 10, "getCountData");
275
+ const ct = parseWhere(args.where); if (ct) dto.conditionTrees = ct;
276
+ const r = await postMultipart("/api/dw/newetl/chatBi/ds/getCountData", { sourceType: args["source-type"] || "LOGIC", queryDto: dto });
277
+ if (json) return r;
278
+ const data = r.data || {};
279
+ console.log("totalCount = " + (data.totalCount ?? "-"));
280
+ }
281
+
282
+ // ---------------- doc:文档查询 ----------------
283
+ function docBaseBody(pageNo, pageSize) {
284
+ return {
285
+ curMenuType: "", limitedFolderIncludeSub: "", limitedFolderIds: "", docAppConsole: "",
286
+ viewType: "DOC_PAGINATION", pageNo, pageSize, morePageId: "", dimension: "ALL", includeBelongFolders: true,
287
+ order: { property: "default", direction: "DESC" }, searchType: "all", conditions: [], customConditions: [],
288
+ userId: auth.userId || "", current: pageNo,
289
+ columnOrders: [{ orderKey: "default", orderType: "" }], getColumn: false, quickSearchType: "normal",
290
+ withFullSearch: false, fetchFullSearchParam: true,
291
+ };
292
+ }
293
+ function buildDocConditions(args) {
294
+ const conds = [];
295
+ if (args.name) conds.push({ id: "name", type: "string", value: args.name });
296
+ if (args.creator) conds.push({ id: "creator", operate: args["creator-neq"] ? "neq" : "eq", type: "array", value: args.creator });
297
+ if (args.author) conds.push({ id: "author", operate: args["author-neq"] ? "neq" : "eq", type: "array", value: args.author });
298
+ if (args["content-type"]) conds.push({ id: "contentType", operate: "eq", type: "array", value: args["content-type"] });
299
+ if (args.time) conds.push({ id: "create_time", type: "date", value: args.time });
300
+ if (args["last-time"]) conds.push({ id: "last_update_time", type: "date", value: args["last-time"] });
301
+ if (args.folder) conds.push({ id: "folder", operate: "eq", type: "folder", value: args.folder });
302
+ if (args["folder-neq"]) conds.push({ id: "folder", operate: "neq", type: "folder", value: args["folder-neq"] });
303
+ return conds;
304
+ }
305
+ async function cmdDocCount(args, json) {
306
+ const body = docBaseBody(1, parseInt(args.size || "20", 10));
307
+ body.conditions = buildDocConditions(args);
308
+ const r = await postJson("/api/doc/documents/documentListCount", body);
309
+ if (json) return r;
310
+ const data = r.data || {};
311
+ console.log("total = " + (data.total ?? "-"));
312
+ }
313
+ async function cmdDocList(args, json) {
314
+ const pageSize = parseInt(args.size || "100", 10);
315
+ const body = docBaseBody(1, pageSize);
316
+ body.conditions = buildDocConditions(args);
317
+ const r1 = await postJson("/api/doc/documents/documentListCount", body);
318
+ if (json) return r1;
319
+ const total = (r1.data || {}).total || 0;
320
+ if (!total) { console.log("total = 0(未查询到符合条件的文档)"); return; }
321
+ const totalPages = Math.ceil(total / pageSize);
322
+ const maxPages = args.all ? totalPages : Math.min(parseInt(args.page || "1", 10), totalPages);
323
+ const startPage = args.all ? 1 : parseInt(args.page || "1", 10);
324
+ const allRows = [];
325
+ for (let pno = startPage; pno <= maxPages; pno++) {
326
+ const b = docBaseBody(pno, pageSize);
327
+ b.conditions = buildDocConditions(args);
328
+ const rr = await postJson("/api/doc/documents/documentList", b);
329
+ const rows = ((rr.data || {}).data) || [];
330
+ for (const row of rows) if (!allRows.some((x) => x.id === row.id)) allRows.push(row);
331
+ if (!args.all && pno === maxPages) break;
332
+ }
333
+ console.log("== 文档列表 total=" + total + " 本批=" + allRows.length + " 行 ==");
334
+ printHuman("", allRows, ["id", "name", "docCreatorName", "createTime", "folder"]);
335
+ }
336
+
337
+ // ---------------- noun:词库 ----------------
338
+ async function cmdNoun(args, json) {
339
+ const skill = need("skill", args);
340
+ const q = new URLSearchParams({ skillKey: skill });
341
+ if (args["source-type"]) q.append("sourceType", args["source-type"]);
342
+ if (args["source-group"]) q.append("sourceGroupId", args["source-group"]);
343
+ if (args["source-table"]) q.append("sourceTableId", args["source-table"]);
344
+ const r = await getUrl("/api/dw/newetl/chatBi/skill/getChatDocsBySkillKey?" + q.toString());
345
+ if (json) return r;
346
+ const data = r.data;
347
+ if (!data || !data.nouns) {
348
+ console.log("(该 skill 词库不存在或为空) msg=" + (r.msg || ""));
349
+ return;
350
+ }
351
+ console.log("== 名词解释 " + data.nouns.length + " 条 (skill=" + skill + ") ==");
352
+ for (const n of data.nouns) {
353
+ console.log("【" + (n.keyword || "") + "】" + (n.synonyms ? " 同义:" + n.synonyms : ""));
354
+ if (n.wordExplain) console.log(" " + String(n.wordExplain).replace(/\n/g, " "));
355
+ }
356
+ }
357
+
358
+ // ---------------- field:关联字段翻译 ----------------
359
+ function customParamFromArgs(args) {
360
+ if (args["form-param"]) return { formParam: args["form-param"] };
361
+ if (args["eb-browser-param"]) return { ebBrowserForEditParam: readJsonArg(args["eb-browser-param"], "--eb-browser-param") };
362
+ if (args["property-type"]) return { propertyType: args["property-type"] };
363
+ return {};
364
+ }
365
+ function extractRemoteRows(r, browserType) {
366
+ const remote = r.remote || {};
367
+ const keys = Object.keys(remote);
368
+ const out = [];
369
+ for (const k of keys) {
370
+ if (Array.isArray(remote[k])) for (const it of remote[k]) out.push({ key: k, id: it.id, content: it.content ?? it.name ?? "" });
371
+ }
372
+ return out;
373
+ }
374
+ async function cmdFieldId2name(args, json) {
375
+ const module = need("module", args), type = need("type", args), idsVal = need("ids", args);
376
+ const dataIds = idsVal.split(",").map((s) => s.trim()).filter(Boolean);
377
+ const body = [{ module, browserType: type, dataIds, user: USER, customParam: customParamFromArgs(args) }];
378
+ const r = await postJson("/api/bcw/test/testRemoteBrowser", body);
379
+ if (json) return r;
380
+ const rows = extractRemoteRows(r, type);
381
+ if (!rows.length) {
382
+ const nf = (r.notFound || {});
383
+ const list = nf && nf[module + "_" + type] && nf[module + "_" + type].ids ? nf[module + "_" + type].ids : [];
384
+ console.log("(未翻译到名称) notFound ids: " + (list.join(",") || "全部"));
385
+ return;
386
+ }
387
+ printHuman("id→name (" + rows.length + "/" + dataIds.length + ")", rows, ["id", "content"]);
388
+ }
389
+ async function cmdFieldName2id(args, json) {
390
+ const module = need("module", args), type = need("type", args), name = need("name", args);
391
+ const body = { newly: 1, searchValue: name, ...customParamFromArgs(args) };
392
+ const url = "/api/" + module + "/common/browser/complete/" + type;
393
+ const r = await postJson(url, body);
394
+ if (json) return r;
395
+ const rows = ((r.data || {}).data) || [];
396
+ if (!rows.length) { console.log("(未匹配到名称对应 ID)"); return; }
397
+ printHuman("name→id 候选 " + rows.length + " 条", rows.map((x) => ({ id: x.id, name: x.name ?? x.title ?? "" })), ["id", "name"]);
398
+ }
399
+
400
+ // ---------------- exp:专家包版本管理(expert-repository) ----------------
401
+ async function expGet(url) {
402
+ // 返回 {raw, j}:raw 用于正则提取大整数 id(commitId 19 位超 JS Number 精度,JSON.parse 会丢精度!)
403
+ const resp = await fetch(BASE + url, {
404
+ method: "GET",
405
+ headers: { "accept": "application/json, text/plain, */*", "langType": "zh_CN", "cookie": auth.cookie, "eteamsid": auth.eteamsid, "ebbusinessid": EB_BIZ_ID, "user-agent": UA },
406
+ });
407
+ const raw = await resp.text();
408
+ let j; try { j = JSON.parse(raw); } catch { throw new Error("HTTP " + resp.status + " 非JSON响应: " + raw.slice(0, 300)); }
409
+ if (resp.status === 401 || (j && String(j.code) === "-1" && /token|登录|失效/i.test(String(j.msg || "")))) throw new Error("登录态失效,请重新 e10-login oidc 登录");
410
+ return { raw, j };
411
+ }
412
+ function rawNum(raw, key) {
413
+ // 从原始 JSON 文本提取整数字段为精确字符串(避免 JSON.parse 大数丢精度,如 commitId)
414
+ const m = raw.match(new RegExp('"' + key + '":\\s*(\\d+)'));
415
+ return m ? m[1] : "";
416
+ }
417
+ function rawNumSeq(raw, key) {
418
+ // 提取某 key 全部整数序列(按出现顺序),如提交历史里的 commitId
419
+ const re = new RegExp('"' + key + '":\\s*(\\d+)', "g");
420
+ const out = []; let m;
421
+ while ((m = re.exec(raw)) !== null) out.push(m[1]);
422
+ return out;
423
+ }
424
+ const EXP_ALLOWED_DIRS = [".codebuddy-plugin", "agents", "avatars", "skills"];
425
+ const EXP_SKIP_DIRS = [".git", ".workbuddy", "node_modules", "__pycache__"];
426
+ function sha256(buf) { return crypto.createHash("sha256").update(buf).digest("hex"); }
427
+ function walkDir(dir, root) {
428
+ const out = [];
429
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
430
+ const abs = path.join(dir, entry.name);
431
+ const rel = path.relative(root, abs).split(path.sep).join("/");
432
+ if (entry.isDirectory()) {
433
+ if (EXP_SKIP_DIRS.includes(entry.name)) continue;
434
+ const top = rel.split("/")[0];
435
+ if (rel.includes("/") && !EXP_ALLOWED_DIRS.includes(top)) continue; // 仅允许根文件+四大目录
436
+ walkDir(abs, root).forEach((x) => out.push(x));
437
+ } else if (entry.isFile()) {
438
+ const top = rel.split("/")[0];
439
+ if (!rel.includes("/") || EXP_ALLOWED_DIRS.includes(top)) out.push({ rel, abs });
440
+ }
441
+ }
442
+ return out;
443
+ }
444
+ function collectPackage(dir) {
445
+ if (!fs.existsSync(dir)) throw new Error("本地专家包目录不存在: " + dir);
446
+ return walkDir(dir, dir).map(({ rel, abs }) => {
447
+ const buf = fs.readFileSync(abs);
448
+ if (buf.length > 10 * 1024 * 1024) throw new Error("文件超 10MB 上限: " + rel);
449
+ return { relativePath: rel, contentBase64: buf.toString("base64"), sha256: sha256(buf), abs };
450
+ });
451
+ }
452
+ function localShaMap(dir) {
453
+ const map = {};
454
+ for (const f of walkDir(dir, dir)) map[f.rel] = sha256(fs.readFileSync(f.abs));
455
+ return map;
456
+ }
457
+ function expOk(r) {
458
+ if (!r) return false;
459
+ if (r.actionMsg) return String(r.actionMsg.code) === "0";
460
+ const code = r.code;
461
+ if (code === undefined) return !r.fail;
462
+ return String(code) === "0" || String(code) === "200";
463
+ }
464
+ function expFailMsg(r) {
465
+ if (r && r.actionMsg && String(r.actionMsg.code) !== "0") return r.actionMsg.message;
466
+ if (r && r.msg) return r.msg;
467
+ return "请求失败";
468
+ }
469
+ function printExpData(r, title) {
470
+ if (!expOk(r)) throw new Error(expFailMsg(r));
471
+ if (title) console.log("== " + title + " ==");
472
+ console.log(JSON.stringify(r.data ?? {}, null, 1));
473
+ }
474
+ async function rawGet(url) {
475
+ const resp = await fetch(BASE + url, {
476
+ method: "GET",
477
+ headers: { "accept": "*/*", "langType": "zh_CN", "cookie": auth.cookie, "eteamsid": auth.eteamsid, "ebbusinessid": EB_BIZ_ID, "user-agent": UA },
478
+ });
479
+ if (resp.status === 401) throw new Error("登录态失效,请重新 e10-login oidc 登录");
480
+ return Buffer.from(await resp.arrayBuffer());
481
+ }
482
+ async function expStatus(args, json) {
483
+ const expert = need("expert", args);
484
+ const { raw, j: r } = await expGet("/api/dw/expert-repository/v1/repositories/status?expertCode=" + encodeURIComponent(expert) + "&branchName=" + (args.branch || "main"));
485
+ if (json) return r;
486
+ if (!expOk(r)) throw new Error(expFailMsg(r));
487
+ const d = r.data || {};
488
+ console.log("== 仓库状态 " + expert + " ==");
489
+ console.log("expertCode = " + (d.expertCode ?? ""));
490
+ console.log("displayName = " + (d.displayName ?? ""));
491
+ console.log("branchName = " + (d.branchName ?? ""));
492
+ console.log("headCommitId = " + (rawNum(raw, "headCommitId") || d.headCommitId));
493
+ console.log("headRevision = " + (d.headRevision ?? ""));
494
+ for (const k of ["headCommitHash", "headCommitMessage", "headCommitTime", "headAuthor", "protected"]) if (d[k] !== undefined) console.log(k + " = " + d[k]);
495
+ if (Array.isArray(d.locks) && d.locks.length) { console.log("有效锁:"); for (const l of d.locks) console.log(" " + JSON.stringify(l)); }
496
+ }
497
+ async function expManifest(args, json) {
498
+ const expert = need("expert", args);
499
+ let url = "/api/dw/expert-repository/v1/repositories/manifest?expertCode=" + encodeURIComponent(expert) + "&branchName=" + (args.branch || "main");
500
+ if (args.commit) url += "&commitId=" + encodeURIComponent(args.commit);
501
+ const r = await getUrl(url);
502
+ if (json) return r;
503
+ if (!expOk(r)) throw new Error(expFailMsg(r));
504
+ const items = r.data || [];
505
+ console.log("== 文件清单 " + items.length + " 项 ==");
506
+ printHuman("", items, ["relativePath", "sha256", "size", "action"]);
507
+ }
508
+ async function expCommits(args, json) {
509
+ const expert = need("expert", args);
510
+ const { raw, j: r } = await expGet("/api/dw/expert-repository/v1/repositories/commits?expertCode=" + encodeURIComponent(expert) + "&branchName=" + (args.branch || "main"));
511
+ if (json) return r;
512
+ if (!expOk(r)) throw new Error(expFailMsg(r));
513
+ const rows = (r.data || []).map((x, i) => ({ ...x, commitId: (rawNumSeq(raw, "commitId")[i] || x.commitId) }));
514
+ console.log("== 提交历史 " + rows.length + " 条(commitId 为精确值,可复制用于 file/release/rollback)==");
515
+ printHuman("", rows, ["commitId", "submitMode", "author", "createTime", "message"]);
516
+ }
517
+ async function expFile(args, json) {
518
+ const expert = need("expert", args), commit = need("commit", args), rp = need("path", args);
519
+ const buf = await rawGet("/api/dw/expert-repository/v1/repositories/file?expertCode=" + encodeURIComponent(expert) + "&commitId=" + encodeURIComponent(commit) + "&relativePath=" + encodeURIComponent(rp));
520
+ if (args.out) { fs.writeFileSync(args.out, buf); console.log("已下载 " + buf.length + " 字节 -> " + args.out); return; }
521
+ if (json) { console.log(JSON.stringify({ relativePath: rp, size: buf.length, base64: buf.toString("base64") })); return; }
522
+ process.stdout.write(buf.toString("utf-8"));
523
+ }
524
+ async function expPull(args, json) {
525
+ const expert = need("expert", args), dir = need("dir", args);
526
+ const dry = !!args["dry-run"];
527
+ const st = await expGet("/api/dw/expert-repository/v1/repositories/status?expertCode=" + encodeURIComponent(expert) + "&branchName=" + (args.branch || "main"));
528
+ if (!expOk(st.j)) throw new Error(expFailMsg(st.j));
529
+ const headId = rawNum(st.raw, "headCommitId") || String((st.j.data || {}).headCommitId);
530
+ const mf = await expGet("/api/dw/expert-repository/v1/repositories/manifest?expertCode=" + encodeURIComponent(expert) + "&branchName=" + (args.branch || "main"));
531
+ if (!expOk(mf.j)) throw new Error(expFailMsg(mf.j));
532
+ const items = (mf.j.data || []).filter((f) => f.relativePath && f.relativePath !== "DELETE");
533
+ const local = fs.existsSync(dir) ? localShaMap(dir) : {};
534
+ let downloaded = 0, unchanged = 0, changed = 0, missing = 0;
535
+ const plan = [];
536
+ for (const it of items) {
537
+ const lp = path.join(dir, it.relativePath.split("/").join(path.sep));
538
+ const ls = local[it.relativePath];
539
+ if (ls === it.sha256) { unchanged++; continue; }
540
+ changed++;
541
+ if (ls === undefined) missing++;
542
+ plan.push({ rel: it.relativePath, lp, sha: it.sha256, size: it.size });
543
+ if (dry) continue;
544
+ fs.mkdirSync(path.dirname(lp), { recursive: true });
545
+ const buf = await rawGet("/api/dw/expert-repository/v1/repositories/file?expertCode=" + encodeURIComponent(expert) + "&commitId=" + encodeURIComponent(headId) + "&relativePath=" + encodeURIComponent(it.relativePath));
546
+ fs.writeFileSync(lp, buf);
547
+ downloaded++;
548
+ }
549
+ const out = { expert, headCommitId: headId, total: items.length, unchanged, changed, downloaded, missing, dryRun: !!dry };
550
+ if (json) { console.log(JSON.stringify(out)); return; }
551
+ console.log("== pull " + expert + "@" + headId + (dry ? " (dry-run)" : "") + " ==");
552
+ console.log("清单 " + out.total + " 项 | 未变 " + unchanged + " | 需更新 " + changed + "(缺失 " + missing + ")" + (dry ? "(dry-run 未下载)" : " | 已下载 " + downloaded));
553
+ for (const p of plan.slice(0, 30)) console.log(" " + (dry ? "[待下载]" : "[已下载]") + " " + p.rel + " (" + p.sha.slice(0, 12) + "…)");
554
+ if (plan.length > 30) console.log(" ... 共 " + plan.length + " 个文件待更新");
555
+ }
556
+ async function expInit(args, json) {
557
+ const expert = need("expert", args), dir = need("dir", args);
558
+ const files = collectPackage(dir);
559
+ const body = { expertCode: expert, displayName: args["display-name"] || expert, branchName: args.branch || "main",
560
+ description: args.description || "", initialCommitMessage: args.message || "初始化导入", files };
561
+ const r = await postJson("/api/dw/expert-repository/v1/repositories", body);
562
+ if (json) return r;
563
+ if (!expOk(r)) throw new Error(expFailMsg(r));
564
+ console.log("== 仓库已创建 =="); console.log(JSON.stringify(r.data ?? {}, null, 1));
565
+ }
566
+ async function expCommit(args, json) {
567
+ const expert = need("expert", args), dir = need("dir", args), msg = need("message", args);
568
+ const mode = (args.mode || "FULL_SNAPSHOT").toUpperCase();
569
+ const st = await expGet("/api/dw/expert-repository/v1/repositories/status?expertCode=" + encodeURIComponent(expert) + "&branchName=" + (args.branch || "main"));
570
+ if (!expOk(st.j)) throw new Error("无法获取基线: " + expFailMsg(st.j));
571
+ const base = st.j.data || {};
572
+ const body = {
573
+ branchName: base.branchName || "main",
574
+ baseCommitId: rawNum(st.raw, "headCommitId") || String(base.headCommitId), // 精确字符串,避免大数丢精度
575
+ baseRevision: base.headRevision,
576
+ commitMessage: msg,
577
+ };
578
+ if (mode === "FILE_PATCH") {
579
+ const mf = await expGet("/api/dw/expert-repository/v1/repositories/manifest?expertCode=" + encodeURIComponent(expert) + "&branchName=" + (args.branch || "main"));
580
+ if (!expOk(mf.j)) throw new Error("无法获取 manifest: " + expFailMsg(mf.j));
581
+ const remoteSha = {}; for (const f of (mf.j.data || [])) remoteSha[f.relativePath] = f.sha256;
582
+ const local = collectPackage(dir);
583
+ const localSha = {}; for (const f of local) localSha[f.relativePath] = f.sha256;
584
+ const changes = [];
585
+ for (const f of local) {
586
+ const rs = remoteSha[f.relativePath];
587
+ if (rs === undefined) changes.push({ operation: "ADD", relativePath: f.relativePath, contentBase64: f.contentBase64, sha256: f.sha256, baseFileSha256: "" });
588
+ else if (rs !== f.sha256) changes.push({ operation: "MODIFY", relativePath: f.relativePath, contentBase64: f.contentBase64, sha256: f.sha256, baseFileSha256: rs });
589
+ }
590
+ for (const rp of Object.keys(remoteSha)) if (localSha[rp] === undefined) changes.push({ operation: "DELETE", relativePath: rp, baseFileSha256: remoteSha[rp] });
591
+ if (!changes.length) throw new Error("本地与远端一致,无变更可提交");
592
+ body.submitMode = "FILE_PATCH"; body.changes = changes;
593
+ } else {
594
+ body.submitMode = "FULL_SNAPSHOT"; body.files = collectPackage(dir);
595
+ }
596
+ const r = await postJson("/api/dw/expert-repository/v1/repositories/commits?expertCode=" + encodeURIComponent(expert), body);
597
+ if (json) return r;
598
+ if (!expOk(r)) {
599
+ const m = expFailMsg(r);
600
+ if (/基线|过期|revision|冲突/i.test(m)) throw new Error("提交冲突: " + m + " —— 请先 exp pull 拉取最新并合并后重试");
601
+ throw new Error("提交失败: " + m);
602
+ }
603
+ console.log("== 提交成功 =="); console.log(JSON.stringify(r.data ?? {}, null, 1));
604
+ }
605
+ async function expLock(args, json) {
606
+ const expert = need("expert", args);
607
+ const act = (args.action || "").toLowerCase();
608
+ const branch = args.branch || "main";
609
+ if (act === "query") {
610
+ const r = await getUrl("/api/dw/expert-repository/v1/repositories/locks?expertCode=" + encodeURIComponent(expert) + "&branchName=" + encodeURIComponent(branch));
611
+ if (json) return r;
612
+ if (!expOk(r)) throw new Error(expFailMsg(r));
613
+ printExpData(r, "有效锁");
614
+ return;
615
+ }
616
+ const token = need("lock-token", args);
617
+ if (act === "heartbeat") {
618
+ const r = await postJson("/api/dw/expert-repository/v1/repositories/locks/heartbeat?lockToken=" + encodeURIComponent(token), {});
619
+ if (json) return r;
620
+ if (!expOk(r)) throw new Error(expFailMsg(r));
621
+ console.log("锁已续期: " + JSON.stringify(r.data ?? {}));
622
+ return;
623
+ }
624
+ if (act === "release") {
625
+ const req = new Request(BASE + "/api/dw/expert-repository/v1/repositories/locks?lockToken=" + encodeURIComponent(token), { method: "DELETE" });
626
+ const resp = await fetch(req, { headers: { "cookie": auth.cookie, "eteamsid": auth.eteamsid, "accept": "application/json" } });
627
+ const text = await resp.text();
628
+ let r; try { r = JSON.parse(text); } catch { r = { data: { raw: text } }; }
629
+ if (json) return r;
630
+ if (!expOk(r)) throw new Error(expFailMsg(r));
631
+ console.log("锁已释放");
632
+ return;
633
+ }
634
+ // acquire(默认)
635
+ const body = { branchName: branch, componentKey: args.component || "ROOT", ttlMinutes: parseInt(args.ttl || "30", 10) };
636
+ const r = await postJson("/api/dw/expert-repository/v1/repositories/locks?expertCode=" + encodeURIComponent(expert), body);
637
+ if (json) return r;
638
+ if (!expOk(r)) throw new Error(expFailMsg(r));
639
+ console.log("== 锁已获取(记录 lockToken 供 heartbeat/release/commit 使用)==");
640
+ console.log(JSON.stringify(r.data ?? {}, null, 1));
641
+ }
642
+ async function expRelease(args, json) {
643
+ const expert = need("expert", args);
644
+ const r = await postJson("/api/dw/expert-repository/v1/repositories/releases?expertCode=" + encodeURIComponent(expert),
645
+ { commitId: need("commit", args), versionTag: need("version", args), releaseNote: args.note || "" });
646
+ if (json) return r;
647
+ if (!expOk(r)) throw new Error(expFailMsg(r));
648
+ console.log("== 发布成功 =="); console.log(JSON.stringify(r.data ?? {}, null, 1));
649
+ }
650
+ async function expRollback(args, json) {
651
+ const expert = need("expert", args);
652
+ const r = await postJson("/api/dw/expert-repository/v1/repositories/releases/rollback?expertCode=" + encodeURIComponent(expert) + "&versionTag=" + encodeURIComponent(need("version", args)), {});
653
+ if (json) return r;
654
+ if (!expOk(r)) throw new Error(expFailMsg(r));
655
+ console.log("== 回滚成功 =="); console.log(JSON.stringify(r.data ?? {}, null, 1));
656
+ }
657
+
658
+ // ---------------- main ----------------
659
+ function usage() {
660
+ console.log(`e10-data CLI(data-assistant 数据接口统一命令行,2026-09-03 扩展)
661
+ 用法: node e10-data.mjs <族> <子命令> [--参数] 通用 --json 原样输出
662
+
663
+ query DSL 数据查询 executeQueryDsl
664
+ query --conn <connId> --model <ebuilder|flow|eteams|attend|hr|document|dw|blog> --schema <数据库> [--table-id <流程ID>] --dsl @file.json|'{...}'
665
+ ds 数据源(业务模块)取数与元数据
666
+ ds tables --group <gid> [--name 关键词]
667
+ ds fields --group <gid> --table <tid>
668
+ ds list --group <gid> --table <tid> --fields a,b [--where @f|json] [--order "f asc"] [--page N] [--size N]
669
+ ds chart --group <gid> --table <tid> --group-by g1,g2 --stat '[{"fieldId":"x","aggregator":"SUM"}]' [--where ...]
670
+ ds stat --group <gid> --table <tid> --stat '[...]' [--where ...]
671
+ ds count --group <gid> --table <tid> [--where ...]
672
+ (ds 通用 --source-type LOGIC|ETEAMS,默认 LOGIC)
673
+ doc 文档查询
674
+ doc count [--name 关键词] [--creator id1,id2] [--author id1,id2] [--content-type office] [--time "起~止"] [--last-time "起~止"] [--folder id,id|--folder-neq id,id]
675
+ doc list [同上条件] [--all 翻页到底] [--page N] [--size 100]
676
+ noun 词库查询
677
+ noun list --skill <skillKey> [--source-type LOGIC] [--source-group gid] [--source-table tid](数据源场景带三参数)
678
+ field 关联字段翻译
679
+ field id2name --module <m> --type <t> --ids id1,id2 [--form-param '{"fieldId":"..","module":"ebuilderform"}'|--eb-browser-param '{..}'|--property-type x]
680
+ field name2id --module <m> --type <t> --name 名称 [同上附加参数]
681
+ exp 专家包版本管理(expert-repository/v1,响应以 data.actionMsg.code=0 判成功)
682
+ exp status --expert <code> [--branch main] # 仓库状态(headCommitId/headRevision=提交基线)
683
+ exp manifest --expert <code> [--commit <id>] # 文件清单(sha256)
684
+ exp commits --expert <code> # 提交历史
685
+ exp file --expert <code> --commit <id> --path <相对路径> [--out 本地文件]
686
+ exp pull --expert <code> --dir <本地专家包目录> [--dry-run] # 按 manifest sha 差异下载缺失/不一致文件
687
+ exp init --expert <code> --dir <本地目录> [--display-name X] [--message 初始化导入]
688
+ exp commit --expert <code> --dir <本地目录> --message "..." [--mode FULL_SNAPSHOT|FILE_PATCH]
689
+ exp lock --expert <code> --action query|acquire(默认)|heartbeat|release --component ROOT --ttl 30 --lock-token <token>
690
+ exp release --expert <code> --commit <id> --version vX.Y.Z [--note 说明]
691
+ exp rollback --expert <code> --version vX.Y.Z
692
+ 认证: 读 ~/.workbuddy/e10-login-cache.md;失效请先 e10-login oidc`);
693
+ }
694
+
695
+ async function main() {
696
+ const rawArgv = process.argv.slice(2);
697
+ if (rawArgv.includes("--version") || rawArgv.includes("-v") || rawArgv.includes("version")) {
698
+ console.log("e10-data " + cliVersion());
699
+ process.exit(0);
700
+ }
701
+ const { args, pos } = parseArgs(rawArgv);
702
+ if (!pos.length) { usage(); process.exit(1); }
703
+ const family = pos[0], sub = pos[1], json = !!args.json;
704
+ let result;
705
+ if (family === "ds") {
706
+ if (!sub) { usage(); process.exit(1); }
707
+ switch (sub) {
708
+ case "tables": result = await cmdTables(args, json); break;
709
+ case "fields": result = await cmdFields(args, json); break;
710
+ case "list": result = await cmdList(args, json); break;
711
+ case "chart": result = await cmdChart(args, json); break;
712
+ case "stat": result = await cmdStat(args, json); break;
713
+ case "count": result = await cmdCount(args, json); break;
714
+ default: usage(); process.exit(1);
715
+ }
716
+ } else if (family === "query") {
717
+ result = await cmdQuery(args, json);
718
+ } else if (family === "doc") {
719
+ if (sub === "count") result = await cmdDocCount(args, json);
720
+ else if (sub === "list") result = await cmdDocList(args, json);
721
+ else { usage(); process.exit(1); }
722
+ } else if (family === "noun") {
723
+ if (sub === "list") result = await cmdNoun(args, json);
724
+ else { usage(); process.exit(1); }
725
+ } else if (family === "field") {
726
+ if (sub === "id2name") result = await cmdFieldId2name(args, json);
727
+ else if (sub === "name2id") result = await cmdFieldName2id(args, json);
728
+ else { usage(); process.exit(1); }
729
+ } else if (family === "exp") {
730
+ if (!sub) { usage(); process.exit(1); }
731
+ switch (sub) {
732
+ case "status": result = await expStatus(args, json); break;
733
+ case "manifest": result = await expManifest(args, json); break;
734
+ case "commits": result = await expCommits(args, json); break;
735
+ case "file": result = await expFile(args, json); break;
736
+ case "pull": result = await expPull(args, json); break;
737
+ case "init": result = await expInit(args, json); break;
738
+ case "commit": result = await expCommit(args, json); break;
739
+ case "lock": result = await expLock(args, json); break;
740
+ case "release": result = await expRelease(args, json); break;
741
+ case "rollback": result = await expRollback(args, json); break;
742
+ default: usage(); process.exit(1);
743
+ }
744
+ } else { usage(); process.exit(1); }
745
+ if (json && result !== undefined) console.log(JSON.stringify(result, null, 1));
746
+ }
747
+ main().catch((e) => { console.error("ERROR: " + e.message); process.exit(1); });
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "e10-data",
3
+ "version": "0.1.0",
4
+ "description": "泛微 E10 数据接口统一 CLI(data-assistant 专家配套):DSL 查询/数据源/文档/词库/关联字段翻译/专家包版本管理,认证复用 weaver-e10-login 登录态",
5
+ "author": "e10-dw",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "bin": {
9
+ "e10-data": "bin/e10-data.mjs"
10
+ },
11
+ "main": "bin/e10-data.mjs",
12
+ "files": [
13
+ "bin/",
14
+ "README.md"
15
+ ],
16
+ "engines": {
17
+ "node": ">=18.17.0"
18
+ },
19
+ "keywords": [
20
+ "weaver",
21
+ "e10",
22
+ "ecology",
23
+ "cli",
24
+ "data",
25
+ "泛微"
26
+ ],
27
+ "scripts": {
28
+ "pack": "npm pack"
29
+ }
30
+ }