e10-data 0.1.9 → 0.1.10

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 CHANGED
@@ -9,7 +9,7 @@ npm i -g e10-data # 全局安装
9
9
  e10-data --version # 验证
10
10
  ```
11
11
 
12
- > 依赖:认证复用 `weaver-e10-login` 的登录态。**认证单一权威源 = `~/.e10-cli/profiles/<active>/auth`**(e10-login 统一认证,与所有专家/工具共享);CLI 进程内读取并解密(AES-256-GCM,seed 与 e10-login 一致),随后自动回写同步明文镜像 `~/.workbuddy/e10-login-cache.md`(仅供查看/兜底)。请先安装并登录 weaver-e10-login(`e10-login oidc`),登录态失效时 CLI 会提示重新登录。
12
+ > 依赖:认证复用 `weaver-e10-login` 的登录态。**加解密全部由 `e10-login` 负责,本 CLI 不实现任何加解密**(auth 文件由 e10-login 自己的密钥体系加密:1.5.0 起为「每机随机密钥 + OS keychain」,早期版本为固定 seed,只有 e10-login 解得开)。CLI 通过 e10-login 的命令出口取明文凭据,优先级:**① `e10-login auth --json`**(推荐的正式契约)→ **② `e10-login whoami --json`**(其 JSON 带 `cookies` 时)→ **③ `e10-login whoami`**(文本解析,当前实际路径)→ **④ 明文镜像 `~/.workbuddy/e10-login-cache.md`**(兜底,命中时打印告警)。请先安装并登录 weaver-e10-login(`e10-login oidc`)。
13
13
 
14
14
  ## 命令总览
15
15
 
@@ -29,7 +29,7 @@ e10-data attend statis 考勤假期余额统计 getAttendInfoStatis(hr-atte
29
29
  - `--json`:原样输出接口 JSON(供脚本/Agent 消费);默认人读输出。
30
30
  - 复杂 JSON/中文参数:推荐 `@文件` 承载(如 `--dsl @q.json`、`--where @cond.json`)。
31
31
  - ⚠️ 大整数精度(自动处理):E10 的 ID 多为 19 位,超过 JS 安全整数(2^53-1≈9e15)。CLI 解析 JSON 时**自动把超范围裸整数转成字符串**再发请求/输出(实测后端接受字符串做 =/IN/BETWEEN 数值比较),无需手动加引号,也不误伤字符串正文。
32
- - 认证:优先读权威源 `~/.e10-cli/profiles/<active>/auth`(进程内 AES-256-GCM 解密)并自动同步明文镜像 `~/.workbuddy/e10-login-cache.md`;两者皆无提示先执行 `e10-login oidc`。
32
+ - 认证:**加解密由 `e10-login` 负责(本 CLI 不参与)**。凭据出口优先级:`e10-login auth --json` → `e10-login whoami --json` → `e10-login whoami`(文本)→ 明文镜像 `~/.workbuddy/e10-login-cache.md`(兜底,命中时告警)。命中的出口会记入 `~/.e10-cli/.e10-data-auth-mode`(只存模式名、不存凭据),下次优先复用。环境变量:`E10_DATA_AUTH_PROBE=1` 强制重新探测、`E10_DATA_AUTH_MODE=<模式名>` 手动指定、`E10_DATA_QUIET=1` 抑制告警(告警只走 stderr)。
33
33
  - 版本更新:`npm i -g e10-data@latest`;查看源上最新 `npm view e10-data version`。
34
34
 
35
35
  ## 用法示例
package/bin/e10-data.mjs CHANGED
@@ -15,7 +15,9 @@
15
15
  * e10-data attend statis 考勤假期余额统计 getAttendInfoStatis(hr-attendance 病假/事假,2026-09-07)
16
16
  *
17
17
  * 通用:--json 原样输出;复杂 JSON 参数支持 @文件 或 内联(中文走 @文件 更稳)
18
- * 认证:进程内读取权威源 ~/.e10-cli/profiles/<active>/auth(e10-login 统一认证,AES-256-GCM seed 硬编码纯 JS 解密,v0.1.7 起),并自动回写同步明文镜像 ~/.workbuddy/e10-login-cache.md;两者皆无时提示先 e10-login
18
+ * 认证:加解密全部由 e10-login 负责(本 CLI 不实现任何加解密)。凭据出口优先级:
19
+ * ① e10-login auth --json → ② e10-login whoami --json → ③ e10-login whoami(文本解析)
20
+ * → ④ 明文镜像 ~/.workbuddy/e10-login-cache.md(兜底,命中时告警);均不可用时提示先 e10-login oidc 登录
19
21
  *
20
22
  * 用法示例:
21
23
  * node e10-data.mjs query --conn 1144919996804022272 --model dw --schema dw --dsl @q.json
@@ -29,6 +31,7 @@ import fs from "node:fs";
29
31
  import os from "node:os";
30
32
  import path from "node:path";
31
33
  import crypto from "node:crypto";
34
+ import { spawnSync } from "node:child_process";
32
35
  import { fileURLToPath } from "node:url";
33
36
 
34
37
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
@@ -40,70 +43,148 @@ function cliVersion() {
40
43
  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";
41
44
  const EB_BIZ_ID = "10000000000000000";
42
45
 
43
- // ---------------- 认证 ----------------
44
- // 认证单一权威源:~/.e10-cli/profiles/<active>/auth(e10-login / e10-cli 统一认证,与所有专家共享,无需各自存登录信息)。
45
- // AES-256-GCM 加密,seed 与 e10-login 一致(ccw_eteams_auth_v1_8a3f2c1b9e4d → sha256),纯 JS 进程内可解、零外部依赖。
46
- // 明文镜像:~/.workbuddy/e10-login-cache.md(历史遗留,仅供查看/兜底;读到权威源后自动回写同步,不再需要手工维护)。
47
- const E10_AUTH_SEED = "ccw_eteams_auth_v1_8a3f2c1b9e4d";
48
- function decryptAuthWrapper(wrapper) {
46
+ // ---------------- 认证(加解密全部交由 e10-login,本 CLI 不再实现任何加解密)----------------
47
+ // 背景:auth 文件由 e10-login 自己的密钥体系加密 —— e10-login 1.5.0 起为「每机随机密钥 + OS keychain
48
+ // (@napi-rs/keyring,service=e10-cli / account=auth-key;keychain 不可用才落 ~/.e10-cli/.key)」,
49
+ // 早期版本为固定 seed。**只有 e10-login 解得开**,所以本 CLI 只负责「向 e10-login 要明文凭据」,
50
+ // 不再自行解密 —— e10-login 以后更换密钥体系 / 算法 / profile 机制,本 CLI 都无需改动。
51
+ // 凭据出口优先级(命中即返回;命中的"模式名"会记入 ~/.e10-cli/.e10-data-auth-mode,只存模式、不存凭据):
52
+ // ① e10-login auth --json 正式契约(推荐 e10-login 侧新增;一旦可用自动优先)
53
+ // ② e10-login whoami --json 其 JSON 输出带 cookies 字段时可用
54
+ // ③ e10-login whoami 文本输出解析(当前 e10-login 1.5.0 的实际可用路径)
55
+ // ④ ~/.workbuddy/e10-login-cache.md 明文镜像 —— 最后兜底,命中时打印告警(该文件可能已过期)
56
+ // 环境变量:E10_DATA_AUTH_PROBE=1 强制重新探测优先级;E10_DATA_AUTH_MODE=<模式名> 手动指定;
57
+ // E10_DATA_QUIET=1 抑制告警输出(告警一律走 stderr,不污染 --json 的 stdout)。
58
+ const AUTH_MODE_FILE = path.join(os.homedir(), ".e10-cli", ".e10-data-auth-mode");
59
+
60
+ /** 定位当前 profile 的 auth 文件(仅用于"是否已登录"的判断与报错提示,本 CLI 不读其内容做解密) */
61
+ function authFilePath() {
62
+ const home = os.homedir();
63
+ let profile = "default";
49
64
  try {
50
- const combined = Buffer.from(wrapper.data, "base64");
51
- const IV = 12, TAG = 16;
52
- if (combined.length < IV + TAG + 1) return null;
53
- const key = crypto.createHash("sha256").update(E10_AUTH_SEED).digest();
54
- const decipher = crypto.createDecipheriv("aes-256-gcm", key, combined.subarray(0, IV), { authTagLength: TAG });
55
- decipher.setAuthTag(combined.subarray(combined.length - TAG));
56
- return JSON.parse(Buffer.concat([decipher.update(combined.subarray(IV, combined.length - TAG)), decipher.final()]).toString("utf-8"));
57
- } catch { return null; }
58
- }
59
- function parseAuthPayload(raw) {
60
- try { // 兼容两种形态:加密 wrapper({enc:true,data:base64})与明文 JSON({cookies,userId,...})
61
- const obj = JSON.parse(raw);
62
- if (obj && obj.enc === true && typeof obj.data === "string") return decryptAuthWrapper(obj);
63
- if (obj && obj.cookies && (obj.userId || obj.cookies.ETEAMSID)) return obj;
65
+ const activeFile = path.join(home, ".e10-cli", "profile");
66
+ if (fs.existsSync(activeFile)) { const n = fs.readFileSync(activeFile, "utf-8").trim(); if (n) profile = n; }
64
67
  } catch {}
65
- return null;
66
- }
67
- function cookieFromAuthData(data) { // 组装完整 cookie 串:cookies.raw 优先,逐项补齐 langType/agentType/isAgent
68
- const eteamsid = (data && data.cookies && data.cookies.ETEAMSID) || "";
69
- const agent = (data && (data.agentType || data.agent_type)) || "workbuddy";
70
- let c = (data && data.cookies && data.cookies.raw) || "";
71
- if (c && !/langType=/.test(c)) c = "langType=zh_CN; " + c;
72
- if (c && agent && !/agentType=/.test(c)) c += "; agentType=" + agent;
73
- if (c && !/isAgent=/.test(c)) c += "; isAgent=true";
74
- if (!c && eteamsid) c = "langType=zh_CN; ETEAMSID=" + eteamsid + "; agentType=" + agent + "; isAgent=true";
75
- return { eteamsid, cookie: c };
76
- }
77
- function authFromE10Cli() { // ① 权威源:~/.e10-cli/profiles/<active>/auth
68
+ return { profile, file: path.join(home, ".e10-cli", "profiles", profile, "auth") };
69
+ }
70
+
71
+ let lastLoginDiag = "";
72
+ /** 调用 e10-login(凭据的唯一来源);失败返回 null,并把诊断信息留在 lastLoginDiag */
73
+ function runLoginCli(args) {
78
74
  try {
79
- const home = os.homedir();
80
- let profile = "default";
81
- try {
82
- const activeFile = path.join(home, ".e10-cli", "profile");
83
- if (fs.existsSync(activeFile)) { const n = fs.readFileSync(activeFile, "utf-8").trim(); if (n) profile = n; }
84
- } catch {}
85
- const p = path.join(home, ".e10-cli", "profiles", profile, "auth");
86
- if (!fs.existsSync(p)) return null;
87
- const data = parseAuthPayload(fs.readFileSync(p, "utf-8"));
88
- if (!data || !data.cookies || !data.cookies.ETEAMSID || !data.baseUrl) return null;
89
- const { eteamsid, cookie } = cookieFromAuthData(data);
90
- return { baseUrl: data.baseUrl, eteamsid, cookie, userId: data.userId || "", tenantKey: data.tenantKey || "", profile };
91
- } catch { return null; }
75
+ const r = spawnSync("e10-login", args, { shell: true, encoding: "utf-8", timeout: 20000, windowsHide: true });
76
+ const out = (r.stdout || "").trim();
77
+ if (r.error || r.status !== 0) {
78
+ lastLoginDiag = ((r.stderr || "") + out).trim().split(/\r?\n/).slice(0, 2).join(" / ") || (r.error ? String(r.error.message) : "exit=" + r.status);
79
+ return null;
80
+ }
81
+ return out || null;
82
+ } catch (e) { lastLoginDiag = String((e && e.message) || e); return null; }
83
+ }
84
+
85
+ /** 归一化 + 校验(缺 baseUrl / eteamsid / cookie 一律视为该出口不可用,继续下一个出口) */
86
+ function normalizeAuth(o) {
87
+ if (!o) return null;
88
+ const baseUrl = String(o.baseUrl || "").trim().replace(/\/+$/, "");
89
+ const eteamsid = String(o.eteamsid || "").trim();
90
+ const cookie = String(o.cookie || "").trim();
91
+ if (!baseUrl || !eteamsid || !cookie) return null;
92
+ if (/^\((未知|空)\)$/.test(eteamsid) || /^\((未知|空)\)$/.test(cookie)) return null;
93
+ return { baseUrl, eteamsid, cookie, userId: String(o.userId || "").trim(), tenantKey: String(o.tenantKey || "").trim(), source: o.source };
94
+ }
95
+
96
+ /** ① e10-login auth --json(正式契约:{v,baseUrl,eteamsId,cookie,userAgent,userId,tenantKey,agentType,profile}) */
97
+ function providerAuthJson() {
98
+ const out = runLoginCli(["auth", "--json"]);
99
+ if (!out) return null;
100
+ let j; try { j = JSON.parse(out); } catch { return null; }
101
+ if (!j || typeof j !== "object" || Array.isArray(j)) return null;
102
+ return normalizeAuth({
103
+ baseUrl: j.baseUrl || j.base_url,
104
+ eteamsid: j.eteamsId || j.eteamsid,
105
+ cookie: typeof j.cookie === "string" ? j.cookie : "",
106
+ userId: j.userId || j.employeeId,
107
+ tenantKey: j.tenantKey,
108
+ source: "e10-login auth --json",
109
+ });
110
+ }
111
+
112
+ /** ② e10-login whoami --json(需其 JSON 带 cookies 字符串;当前 1.5.0 未带 → 自动落到 ③) */
113
+ function providerWhoamiJson() {
114
+ const out = runLoginCli(["whoami", "--json"]);
115
+ if (!out) return null;
116
+ let j; try { j = JSON.parse(out); } catch { return null; }
117
+ if (!j || typeof j !== "object" || Array.isArray(j)) return null;
118
+ const cookie = typeof j.cookies === "string" ? j.cookies : (typeof j.cookie === "string" ? j.cookie : "");
119
+ return normalizeAuth({
120
+ baseUrl: j.baseUrl,
121
+ eteamsid: j.eteamsId || j.eteamsid,
122
+ cookie,
123
+ userId: j.employeeId || j.userId,
124
+ tenantKey: j.tenantKey,
125
+ source: "e10-login whoami --json",
126
+ });
127
+ }
128
+
129
+ /** ③ e10-login whoami(文本输出解析:employeeId / tenantKey / baseUrl / ETEAMSID / cookies / User-Agent) */
130
+ function providerWhoamiText() {
131
+ const out = runLoginCli(["whoami"]);
132
+ if (!out) return null;
133
+ const m = {};
134
+ for (const line of out.split(/\r?\n/)) {
135
+ const x = line.match(/^\s*([A-Za-z][A-Za-z0-9_-]*)\s*:\s*(.+?)\s*$/);
136
+ if (x) m[x[1].toLowerCase()] = x[2];
137
+ }
138
+ return normalizeAuth({
139
+ baseUrl: m["baseurl"],
140
+ eteamsid: m["eteamsid"],
141
+ cookie: m["cookies"],
142
+ userId: m["employeeid"] || m["userid"],
143
+ tenantKey: m["tenantkey"],
144
+ source: "e10-login whoami(文本)",
145
+ });
92
146
  }
93
- function authFromCacheMd() { // ② 兜底:历史明文缓存 ~/.workbuddy/e10-login-cache.md
147
+
148
+ /** ④ 明文镜像(最后兜底:不涉及任何解密,但可能已过期 → 命中时告警) */
149
+ function providerCacheMd() {
94
150
  const p = path.join(os.homedir(), ".workbuddy", "e10-login-cache.md");
95
151
  if (!fs.existsSync(p)) return null;
96
152
  const txt = fs.readFileSync(p, "utf-8");
97
153
  const get = (key) => {
98
- const m = txt.match(new RegExp("\\*\\*" + key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "\\*\\*\\s*[::]\\s*`?([^`\\n]+)`?"));
99
- return m ? m[1].trim() : "";
154
+ const mm = txt.match(new RegExp("\\*\\*" + key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "\\*\\*\\s*[::]\\s*`?([^`\\n]+)`?"));
155
+ return mm ? mm[1].trim() : "";
100
156
  };
101
- const auth = { baseUrl: get("baseUrl"), eteamsid: get("ETEAMSID"), cookie: get("完整 Cookie 串"), userId: get("userId") || get("employeeId"), tenantKey: get("tenantKey") };
102
- if (!auth.baseUrl || !auth.eteamsid) return null;
103
- if (!auth.cookie) auth.cookie = "langType=zh_CN; ETEAMSID=" + auth.eteamsid + "; agentType=workbuddy; isAgent=true";
104
- return auth;
157
+ const eteamsid = get("ETEAMSID");
158
+ let cookie = get("完整 Cookie 串");
159
+ if (!cookie && eteamsid) cookie = "langType=zh_CN; ETEAMSID=" + eteamsid + "; agentType=workbuddy; isAgent=true";
160
+ return normalizeAuth({
161
+ baseUrl: get("baseUrl"), eteamsid, cookie,
162
+ userId: get("userId") || get("employeeId"), tenantKey: get("tenantKey"),
163
+ source: "明文镜像 ~/.workbuddy/e10-login-cache.md(可能已过期)",
164
+ });
105
165
  }
106
- function syncCacheMd(auth) { // 读到权威源后自动回写明文镜像(内容无变化不写盘),根治"重登后手工同步"问题
166
+
167
+ const AUTH_PROVIDERS = [
168
+ ["auth-json", providerAuthJson],
169
+ ["whoami-json", providerWhoamiJson],
170
+ ["whoami-text", providerWhoamiText],
171
+ ["cache-md", providerCacheMd],
172
+ ];
173
+ /** 兜底出口不进记忆:否则 e10-login 偶发不可用一次,就会把"用镜像"永久设为首选、次次告警 */
174
+ const AUTH_FALLBACK_PROVIDERS = new Set(["cache-md"]);
175
+
176
+ function readAuthModeMemo() {
177
+ try {
178
+ const s = fs.readFileSync(AUTH_MODE_FILE, "utf-8").trim();
179
+ return AUTH_PROVIDERS.some(([n]) => n === s) ? s : null;
180
+ } catch { return null; }
181
+ }
182
+ function writeAuthModeMemo(name) {
183
+ try { fs.mkdirSync(path.dirname(AUTH_MODE_FILE), { recursive: true }); fs.writeFileSync(AUTH_MODE_FILE, name, "utf-8"); } catch {}
184
+ }
185
+
186
+ /** 凭据取自 e10-login 时,顺手把明文镜像刷新到最新(保持镜像可用;镜像不参与正常取数) */
187
+ function syncCacheMd(auth) {
107
188
  try {
108
189
  const p = path.join(os.homedir(), ".workbuddy", "e10-login-cache.md");
109
190
  if (!fs.existsSync(p) || !/🔐 登录信息缓存/.test(fs.readFileSync(p, "utf-8"))) return; // 非标准缓存文件不动它
@@ -117,16 +198,47 @@ function syncCacheMd(auth) { // 读到权威源后自动回写明文镜像(内
117
198
  txt = n;
118
199
  };
119
200
  rep("baseUrl", auth.baseUrl); rep("ETEAMSID", auth.eteamsid); rep("完整 Cookie 串", auth.cookie); rep("userId", auth.userId || ""); rep("tenantKey", auth.tenantKey || "");
120
- rep("缓存记录时间", ts + "(e10-data 自动同步自 ~/.e10-cli/profiles/" + (auth.profile || "") + "/auth)");
201
+ rep("缓存记录时间", ts + "(e10-data 自动同步,凭据来源:" + (auth.source || "") + ")");
121
202
  if (changed) fs.writeFileSync(p, txt, "utf-8");
122
203
  } catch {}
123
204
  }
205
+
206
+ /** 取凭据:按优先级依次尝试各出口;命中的模式会被记住(下次优先尝试它,省一次进程调用) */
124
207
  function loadAuth() {
125
- const cli = authFromE10Cli(); // ① 权威源优先(e10-login 统一认证,多专家共享)
126
- if (cli) { syncCacheMd(cli); return cli; }
127
- const cached = authFromCacheMd(); // ② 权威源缺失时回退旧明文缓存
128
- if (cached) return cached;
129
- throw new Error("未找到登录认证(~/.e10-cli/profiles 与 ~/.workbuddy/e10-login-cache.md 均无),请先执行 e10-login oidc / set 登录");
208
+ const { profile, file } = authFilePath();
209
+ const override = (process.env.E10_DATA_AUTH_MODE || "").trim();
210
+ const memo = override || (process.env.E10_DATA_AUTH_PROBE ? null : readAuthModeMemo());
211
+ const order = memo
212
+ ? AUTH_PROVIDERS.filter(([n]) => n === memo).concat(AUTH_PROVIDERS.filter(([n]) => n !== memo))
213
+ : AUTH_PROVIDERS;
214
+
215
+ const diag = [];
216
+ for (const [name, fn] of order) {
217
+ let got = null;
218
+ lastLoginDiag = "";
219
+ try { got = fn(); } catch (e) { diag.push(name + ": " + ((e && e.message) || e)); }
220
+ if (got) {
221
+ if (name !== memo && !AUTH_FALLBACK_PROVIDERS.has(name)) writeAuthModeMemo(name);
222
+ got.profile = profile;
223
+ if (name === "cache-md") {
224
+ if (!process.env.E10_DATA_QUIET) {
225
+ console.error("[e10-data][warn] 凭据来源=" + got.source + ";正常应由 e10-login 提供凭据,请检查 `e10-login whoami` 是否能输出 cookies。");
226
+ }
227
+ } else {
228
+ syncCacheMd(got); // 凭据来自 e10-login → 顺便把镜像刷新到最新
229
+ }
230
+ return got;
231
+ }
232
+ if (lastLoginDiag) diag.push(name + ": " + lastLoginDiag);
233
+ }
234
+
235
+ throw new Error(
236
+ "未取得登录凭据(解密由 e10-login 负责,本 CLI 不参与)。请依次检查:\n" +
237
+ " ① e10-login 是否可用:`e10-login --version`(缺失 → `npm i -g e10-login`);`e10-login whoami` 应能输出 cookies\n" +
238
+ " ② 是否已登录 / 登录态是否失效:`e10-login oidc` 重新登录(当前 profile: " + profile + ")\n" +
239
+ " ③ auth 文件:" + (fs.existsSync(file) ? "存在" : "不存在") + "(" + file + ")—— 其加解密由 e10-login 负责\n" +
240
+ (diag.length ? " 探测诊断:" + diag.join(" | ") + "\n" : "")
241
+ );
130
242
  }
131
243
  const auth = loadAuth();
132
244
  const BASE = auth.baseUrl.replace(/\/+$/, "");
@@ -135,7 +247,11 @@ const USER = { id: auth.userId || "", tenantKey: auth.tenantKey || "" };
135
247
  // ---------------- HTTP ----------------
136
248
  function checkAuthFail(resp, text, j) {
137
249
  if (resp.status === 401 || (j && String(j.code) === "-1" && /token|登录|失效/i.test(String(j.msg || "")))) {
138
- throw new Error("登录态失效(" + (j.msg || resp.status) + "),请重新 e10-login oidc 登录");
250
+ const from = (auth && auth.source) || "未知";
251
+ const hint = /镜像/.test(from)
252
+ ? "当前用的是明文镜像兜底凭据(可能已过期),请先确认 `e10-login whoami` 能正常导出 cookies,或执行 `e10-login oidc` 重新登录"
253
+ : "凭据由 e10-login 提供,请执行 `e10-login oidc` 重新登录";
254
+ throw new Error("登录态失效(" + (j.msg || resp.status) + "):凭据来源=" + from + ";" + hint);
139
255
  }
140
256
  }
141
257
  async function postJson(endpoint, bodyObj, extraHeaders = {}) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "e10-data",
3
- "version": "0.1.9",
4
- "description": "泛微 E10 数据接口统一 CLI(data-assistant 专家配套):DSL 查询/数据源/文档/词库/关联字段翻译/环境参数配置中心(conf,支持 confGroup 分组)/专家包版本管理,认证复用 weaver-e10-login 登录态",
3
+ "version": "0.1.10",
4
+ "description": "泛微 E10 数据接口统一 CLI(data-assistant 专家配套):DSL 查询/数据源/文档/词库/关联字段翻译/环境参数配置中心(conf,支持 confGroup 分组)/专家包版本管理;认证加解密全部交由 weaver-e10-login(本 CLI 不再自行解密,通过 e10-login 命令出口获取凭据)",
5
5
  "author": "e10-dw",
6
6
  "license": "MIT",
7
7
  "type": "module",