e10-data 0.1.6 → 0.1.7
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 +3 -3
- package/bin/e10-data.mjs +79 -5
- package/package.json +1 -1
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`
|
|
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 会提示重新登录。
|
|
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
|
-
-
|
|
32
|
+
- 认证:优先读权威源 `~/.e10-cli/profiles/<active>/auth`(进程内 AES-256-GCM 解密)并自动同步明文镜像 `~/.workbuddy/e10-login-cache.md`;两者皆无提示先执行 `e10-login oidc`。
|
|
33
33
|
- 版本更新:`npm i -g e10-data@latest`;查看源上最新 `npm view e10-data version`。
|
|
34
34
|
|
|
35
35
|
## 用法示例
|
|
@@ -69,7 +69,7 @@ e10-data attend statis --user 6264843388578732287 --begin 2026-09-01 --end 2026-
|
|
|
69
69
|
|
|
70
70
|
## attend statis 考勤假期余额(v0.1.5 新增)
|
|
71
71
|
|
|
72
|
-
封装 E10 考勤 REST 接口 `POST /api/attend/web/attendInfoV2/getAttendInfoStatis`(hr-attendance 场景六:病假/事假/年假余额统计,替代原手工 curl)。认证/绕代理由 CLI
|
|
72
|
+
封装 E10 考勤 REST 接口 `POST /api/attend/web/attendInfoV2/getAttendInfoStatis`(hr-attendance 场景六:病假/事假/年假余额统计,替代原手工 curl)。认证/绕代理由 CLI 进程内自动完成(权威源 `~/.e10-cli/profiles/<active>/auth`)。
|
|
73
73
|
|
|
74
74
|
```bash
|
|
75
75
|
e10-data attend statis [--user <人员ID>] [--begin 2026-09-01] [--end 2026-09-30]
|
package/bin/e10-data.mjs
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* e10-data attend statis 考勤假期余额统计 getAttendInfoStatis(hr-attendance 病假/事假,2026-09-07)
|
|
15
15
|
*
|
|
16
16
|
* 通用:--json 原样输出;复杂 JSON 参数支持 @文件 或 内联(中文走 @文件 更稳)
|
|
17
|
-
*
|
|
17
|
+
* 认证:进程内读取权威源 ~/.e10-cli/profiles/<active>/auth(e10-login 统一认证,AES-256-GCM seed 硬编码纯 JS 解密,v0.1.7 起),并自动回写同步明文镜像 ~/.workbuddy/e10-login-cache.md;两者皆无时提示先 e10-login
|
|
18
18
|
*
|
|
19
19
|
* 用法示例:
|
|
20
20
|
* node e10-data.mjs query --conn 1144919996804022272 --model dw --schema dw --dsl @q.json
|
|
@@ -40,19 +40,93 @@ const UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,
|
|
|
40
40
|
const EB_BIZ_ID = "10000000000000000";
|
|
41
41
|
|
|
42
42
|
// ---------------- 认证 ----------------
|
|
43
|
-
|
|
43
|
+
// 认证单一权威源:~/.e10-cli/profiles/<active>/auth(e10-login / e10-cli 统一认证,与所有专家共享,无需各自存登录信息)。
|
|
44
|
+
// AES-256-GCM 加密,seed 与 e10-login 一致(ccw_eteams_auth_v1_8a3f2c1b9e4d → sha256),纯 JS 进程内可解、零外部依赖。
|
|
45
|
+
// 明文镜像:~/.workbuddy/e10-login-cache.md(历史遗留,仅供查看/兜底;读到权威源后自动回写同步,不再需要手工维护)。
|
|
46
|
+
const E10_AUTH_SEED = "ccw_eteams_auth_v1_8a3f2c1b9e4d";
|
|
47
|
+
function decryptAuthWrapper(wrapper) {
|
|
48
|
+
try {
|
|
49
|
+
const combined = Buffer.from(wrapper.data, "base64");
|
|
50
|
+
const IV = 12, TAG = 16;
|
|
51
|
+
if (combined.length < IV + TAG + 1) return null;
|
|
52
|
+
const key = crypto.createHash("sha256").update(E10_AUTH_SEED).digest();
|
|
53
|
+
const decipher = crypto.createDecipheriv("aes-256-gcm", key, combined.subarray(0, IV), { authTagLength: TAG });
|
|
54
|
+
decipher.setAuthTag(combined.subarray(combined.length - TAG));
|
|
55
|
+
return JSON.parse(Buffer.concat([decipher.update(combined.subarray(IV, combined.length - TAG)), decipher.final()]).toString("utf-8"));
|
|
56
|
+
} catch { return null; }
|
|
57
|
+
}
|
|
58
|
+
function parseAuthPayload(raw) {
|
|
59
|
+
try { // 兼容两种形态:加密 wrapper({enc:true,data:base64})与明文 JSON({cookies,userId,...})
|
|
60
|
+
const obj = JSON.parse(raw);
|
|
61
|
+
if (obj && obj.enc === true && typeof obj.data === "string") return decryptAuthWrapper(obj);
|
|
62
|
+
if (obj && obj.cookies && (obj.userId || obj.cookies.ETEAMSID)) return obj;
|
|
63
|
+
} catch {}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
function cookieFromAuthData(data) { // 组装完整 cookie 串:cookies.raw 优先,逐项补齐 langType/agentType/isAgent
|
|
67
|
+
const eteamsid = (data && data.cookies && data.cookies.ETEAMSID) || "";
|
|
68
|
+
const agent = (data && (data.agentType || data.agent_type)) || "workbuddy";
|
|
69
|
+
let c = (data && data.cookies && data.cookies.raw) || "";
|
|
70
|
+
if (c && !/langType=/.test(c)) c = "langType=zh_CN; " + c;
|
|
71
|
+
if (c && agent && !/agentType=/.test(c)) c += "; agentType=" + agent;
|
|
72
|
+
if (c && !/isAgent=/.test(c)) c += "; isAgent=true";
|
|
73
|
+
if (!c && eteamsid) c = "langType=zh_CN; ETEAMSID=" + eteamsid + "; agentType=" + agent + "; isAgent=true";
|
|
74
|
+
return { eteamsid, cookie: c };
|
|
75
|
+
}
|
|
76
|
+
function authFromE10Cli() { // ① 权威源:~/.e10-cli/profiles/<active>/auth
|
|
77
|
+
try {
|
|
78
|
+
const home = os.homedir();
|
|
79
|
+
let profile = "default";
|
|
80
|
+
try {
|
|
81
|
+
const activeFile = path.join(home, ".e10-cli", "profile");
|
|
82
|
+
if (fs.existsSync(activeFile)) { const n = fs.readFileSync(activeFile, "utf-8").trim(); if (n) profile = n; }
|
|
83
|
+
} catch {}
|
|
84
|
+
const p = path.join(home, ".e10-cli", "profiles", profile, "auth");
|
|
85
|
+
if (!fs.existsSync(p)) return null;
|
|
86
|
+
const data = parseAuthPayload(fs.readFileSync(p, "utf-8"));
|
|
87
|
+
if (!data || !data.cookies || !data.cookies.ETEAMSID || !data.baseUrl) return null;
|
|
88
|
+
const { eteamsid, cookie } = cookieFromAuthData(data);
|
|
89
|
+
return { baseUrl: data.baseUrl, eteamsid, cookie, userId: data.userId || "", tenantKey: data.tenantKey || "", profile };
|
|
90
|
+
} catch { return null; }
|
|
91
|
+
}
|
|
92
|
+
function authFromCacheMd() { // ② 兜底:历史明文缓存 ~/.workbuddy/e10-login-cache.md
|
|
44
93
|
const p = path.join(os.homedir(), ".workbuddy", "e10-login-cache.md");
|
|
45
|
-
if (!fs.existsSync(p))
|
|
94
|
+
if (!fs.existsSync(p)) return null;
|
|
46
95
|
const txt = fs.readFileSync(p, "utf-8");
|
|
47
96
|
const get = (key) => {
|
|
48
97
|
const m = txt.match(new RegExp("\\*\\*" + key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "\\*\\*\\s*[::]\\s*`?([^`\\n]+)`?"));
|
|
49
98
|
return m ? m[1].trim() : "";
|
|
50
99
|
};
|
|
51
100
|
const auth = { baseUrl: get("baseUrl"), eteamsid: get("ETEAMSID"), cookie: get("完整 Cookie 串"), userId: get("userId") || get("employeeId"), tenantKey: get("tenantKey") };
|
|
52
|
-
if (!auth.baseUrl || !auth.eteamsid)
|
|
101
|
+
if (!auth.baseUrl || !auth.eteamsid) return null;
|
|
53
102
|
if (!auth.cookie) auth.cookie = "langType=zh_CN; ETEAMSID=" + auth.eteamsid + "; agentType=workbuddy; isAgent=true";
|
|
54
103
|
return auth;
|
|
55
104
|
}
|
|
105
|
+
function syncCacheMd(auth) { // 读到权威源后自动回写明文镜像(内容无变化不写盘),根治"重登后手工同步"问题
|
|
106
|
+
try {
|
|
107
|
+
const p = path.join(os.homedir(), ".workbuddy", "e10-login-cache.md");
|
|
108
|
+
if (!fs.existsSync(p) || !/🔐 登录信息缓存/.test(fs.readFileSync(p, "utf-8"))) return; // 非标准缓存文件不动它
|
|
109
|
+
const d = new Date();
|
|
110
|
+
const ts = d.getFullYear() + "-" + String(d.getMonth() + 1).padStart(2, "0") + "-" + String(d.getDate()).padStart(2, "0") + " " + String(d.getHours()).padStart(2, "0") + ":" + String(d.getMinutes()).padStart(2, "0");
|
|
111
|
+
let txt = fs.readFileSync(p, "utf-8");
|
|
112
|
+
let changed = false;
|
|
113
|
+
const rep = (key, val) => {
|
|
114
|
+
const re = new RegExp("(\\*\\*" + key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "\\*\\*\\s*[::]\\s*`?)([^`\\n]*)(`?)");
|
|
115
|
+
const n = txt.replace(re, (m, a, b, c) => { if (b === val && c === "`") return m; changed = true; return a + val + (c || "`"); });
|
|
116
|
+
txt = n;
|
|
117
|
+
};
|
|
118
|
+
rep("baseUrl", auth.baseUrl); rep("ETEAMSID", auth.eteamsid); rep("完整 Cookie 串", auth.cookie); rep("userId", auth.userId || ""); rep("tenantKey", auth.tenantKey || "");
|
|
119
|
+
rep("缓存记录时间", ts + "(e10-data 自动同步自 ~/.e10-cli/profiles/" + (auth.profile || "") + "/auth)");
|
|
120
|
+
if (changed) fs.writeFileSync(p, txt, "utf-8");
|
|
121
|
+
} catch {}
|
|
122
|
+
}
|
|
123
|
+
function loadAuth() {
|
|
124
|
+
const cli = authFromE10Cli(); // ① 权威源优先(e10-login 统一认证,多专家共享)
|
|
125
|
+
if (cli) { syncCacheMd(cli); return cli; }
|
|
126
|
+
const cached = authFromCacheMd(); // ② 权威源缺失时回退旧明文缓存
|
|
127
|
+
if (cached) return cached;
|
|
128
|
+
throw new Error("未找到登录认证(~/.e10-cli/profiles 与 ~/.workbuddy/e10-login-cache.md 均无),请先执行 e10-login oidc / set 登录");
|
|
129
|
+
}
|
|
56
130
|
const auth = loadAuth();
|
|
57
131
|
const BASE = auth.baseUrl.replace(/\/+$/, "");
|
|
58
132
|
const USER = { id: auth.userId || "", tenantKey: auth.tenantKey || "" };
|
|
@@ -1090,7 +1164,7 @@ exp 专家包版本管理(expert-repository/v1,响应以 data.actionMsg.
|
|
|
1090
1164
|
exp rollback --expert <code> --version vX.Y.Z
|
|
1091
1165
|
attend 考勤 REST 接口(2026-09-07 新增,hr-attendance 病假/事假等假期余额)
|
|
1092
1166
|
attend statis [--user <人员ID>] [--begin 2026-09-01] [--end 2026-09-30] # getAttendInfoStatis 假期余额统计;--user 默认当前登录用户,--begin/--end 默认当月
|
|
1093
|
-
认证:
|
|
1167
|
+
认证: 优先读权威源 ~/.e10-cli/profiles/<active>/auth(自动同步镜像 ~/.workbuddy/e10-login-cache.md);皆无请先 e10-login oidc`);
|
|
1094
1168
|
}
|
|
1095
1169
|
|
|
1096
1170
|
async function main() {
|
package/package.json
CHANGED