weaver-work-cli 0.1.33 → 0.1.38
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/dist/chunks/{archive-FVFY6GZN.js → archive-PDV724YK.js} +5 -4
- package/dist/chunks/{archive-oa-NW3OD4UV.js → archive-oa-4XE4MNXQ.js} +4 -3
- package/dist/chunks/{asset-7QJKMIDV.js → asset-PDL2QE2N.js} +8 -7
- package/dist/chunks/{attachment-content-OSAVA6K3.js → attachment-content-MDDGOE5E.js} +3 -3
- package/dist/chunks/{blog-I7SDPNW4.js → blog-UYR3EUXW.js} +5 -4
- package/dist/chunks/{caizhilian-O37ZIQNY.js → caizhilian-LG2X5NMF.js} +5 -4
- package/dist/chunks/{calendar-7KXE4ED3.js → calendar-FG4BAZHE.js} +4 -3
- package/dist/chunks/{chunk-Q6LWB5HX.js → chunk-6FTTSA45.js} +4 -3
- package/dist/chunks/{chunk-MQ2I5AA3.js → chunk-6LTFIFLM.js} +3 -2
- package/dist/chunks/{chunk-WRRJ4GG2.js → chunk-CORFRD7Q.js} +4 -3
- package/dist/chunks/{chunk-WY74UBJ6.js → chunk-DMV2TOMH.js} +2194 -240
- package/dist/chunks/{chunk-MEEFGWTM.js → chunk-ELYDWBZJ.js} +135 -10
- package/dist/chunks/{chunk-EEWPQE3O.js → chunk-FUWVHMVV.js} +7 -6
- package/dist/chunks/{chunk-HEL5XTYI.js → chunk-GGJFMNJM.js} +26 -4
- package/dist/chunks/{chunk-KAS45KNR.js → chunk-IZUDA353.js} +1 -1
- package/dist/chunks/{chunk-2FLWXFVS.js → chunk-J7USS3ED.js} +4 -4
- package/dist/chunks/{chunk-IHUYP6LO.js → chunk-JSMFPO43.js} +1 -1
- package/dist/chunks/{chunk-RBGOMK4Y.js → chunk-KKRL3REV.js} +5 -4
- package/dist/chunks/{chunk-OCU4RLHC.js → chunk-NI6GIPIM.js} +195 -36
- package/dist/chunks/{chunk-PRHJGPMA.js → chunk-NR6RKXPA.js} +2 -2
- package/dist/chunks/{chunk-D223PTXU.js → chunk-OJQUJZQF.js} +33 -7
- package/dist/chunks/{chunk-ILMBNVSG.js → chunk-UJJD4QWF.js} +4 -3
- package/dist/chunks/{chunk-6ATGWRZM.js → chunk-UZZBA6PV.js} +177 -14
- package/dist/chunks/{chunk-GFPXQIY2.js → chunk-VRYHGG6O.js} +2 -2
- package/dist/chunks/{chunk-NHA62QD7.js → chunk-XJY7G4DD.js} +3 -3
- package/dist/chunks/{chunk-VBCFFFGT.js → chunk-Z32KWY6G.js} +4 -1
- package/dist/chunks/{conflict-SFNEVJY5.js → conflict-BQZEQ4SP.js} +2 -2
- package/dist/chunks/contract-XIRAFTEV.js +9111 -0
- package/dist/chunks/{cowork-CKWYHXYB.js → cowork-BLG62BL2.js} +46 -26
- package/dist/chunks/{ebuilder-form-KOLJRVZD.js → ebuilder-form-NUKG5QW4.js} +3 -3
- package/dist/chunks/{ehr-WFOC3B4D.js → ehr-2STW5ILH.js} +174 -10
- package/dist/chunks/{esb-LJZTN663.js → esb-UUDQUNKY.js} +4 -4
- package/dist/chunks/{fna-F7QRIIZC.js → fna-6RCY4NU5.js} +15 -9
- package/dist/chunks/{host-VQ6JJXGQ.js → host-7RXEDMAH.js} +3 -3
- package/dist/chunks/{host-KXPTEWFS.js → host-EJZFROU3.js} +3 -3
- package/dist/chunks/{host-BCGUVEWX.js → host-FWDOSRH5.js} +3 -3
- package/dist/chunks/{host-DGE2TZD4.js → host-RHM3NPXV.js} +3 -3
- package/dist/chunks/{host-BQBRHVR3.js → host-UFF33TWJ.js} +3 -3
- package/dist/chunks/{hrm-NI6XAKW5.js → hrm-PKJ6ELSH.js} +3 -3
- package/dist/chunks/{im-7TRTF3KT.js → im-YJAXS7CO.js} +3 -3
- package/dist/chunks/{invoice-AOXWJKYT.js → invoice-HWVSXGX6.js} +3 -3
- package/dist/chunks/{jiuchuanhui-7NQWEZAW.js → jiuchuanhui-L7VKILZB.js} +4 -3
- package/dist/chunks/{lazy-dispatch-RNS4EE4S.js → lazy-dispatch-NMYEIHRH.js} +4 -4
- package/dist/chunks/{mail-VDHJAB4O.js → mail-B42RRU7G.js} +46 -24
- package/dist/chunks/{meeting-GA7XCNNP.js → meeting-CFYVBJSS.js} +77 -12
- package/dist/chunks/{okr-TQIRERPQ.js → okr-GCLF6V7W.js} +4 -3
- package/dist/chunks/{plan-4RY4LN2P.js → plan-ZVL7GBRI.js} +4 -3
- package/dist/chunks/{project-D6XT2OC5.js → project-4YWAAE2K.js} +4 -3
- package/dist/chunks/{registry-QKMI43Q6.js → registry-EP4VTHZS.js} +8 -8
- package/dist/chunks/{registry-JHRIDQ3A.js → registry-FRJ6H7BT.js} +4 -4
- package/dist/chunks/{registry-VOGKFFTR.js → registry-SG2PGVCF.js} +2 -2
- package/dist/chunks/{room-recommend-D4EUOOWY.js → room-recommend-R3JVUUAC.js} +4 -4
- package/dist/chunks/{schedule-XPFBHPCV.js → schedule-NIJBTYDO.js} +4 -4
- package/dist/chunks/{workflow-HLBZRWIY.js → workflow-BB24DFBY.js} +11 -4
- package/dist/index.js +31 -26
- package/dist/internal/e10/auth-recovery.js +114 -1
- package/dist/internal/e10/client.js +16 -2
- package/dist/internal/e10/fetch-timing.js +31 -0
- package/dist/internal/e10/request-runtime.js +108 -7
- package/dist/shortcuts/archive/host.js +3 -2
- package/dist/shortcuts/archive-oa/host.js +2 -1
- package/dist/shortcuts/asset/host.js +6 -5
- package/dist/shortcuts/blog/host.js +3 -2
- package/dist/shortcuts/caizhilian/host.js +3 -2
- package/dist/shortcuts/calendar/host.js +2 -1
- package/dist/shortcuts/contract/host.js +2 -1
- package/dist/shortcuts/cowork/host.js +10 -1
- package/dist/shortcuts/cowork/operations/types.js +17 -2
- package/dist/shortcuts/ebuilder-form/host.js +3 -1
- package/dist/shortcuts/ehr/host.js +2 -1
- package/dist/shortcuts/ehr/operations/recruit-write.js +35 -0
- package/dist/shortcuts/ehr/operations/recruit.js +147 -0
- package/dist/shortcuts/esb/host.js +2 -1
- package/dist/shortcuts/fna/host.js +2 -1
- package/dist/shortcuts/fna/operations/expense-chart.js +19 -7
- package/dist/shortcuts/hrm/host.js +2 -1
- package/dist/shortcuts/im/host.js +3 -2
- package/dist/shortcuts/index.js +5 -0
- package/dist/shortcuts/invoice/host.js +5 -4
- package/dist/shortcuts/jiuchuanhui/host.js +2 -1
- package/dist/shortcuts/mail/host.js +10 -1
- package/dist/shortcuts/mail/operations/types.js +17 -2
- package/dist/shortcuts/meeting/host.js +3 -2
- package/dist/shortcuts/meeting/index.js +79 -1
- package/dist/shortcuts/meeting/invitation/text.js +71 -0
- package/dist/shortcuts/meeting/manifest.js +10 -0
- package/dist/shortcuts/meeting/operations/eb/catalog.js +5 -0
- package/dist/shortcuts/meeting/operations/eb/conflict.js +6 -1
- package/dist/shortcuts/meeting/operations/eb/create-flow.js +2105 -169
- package/dist/shortcuts/meeting/operations/eb/crm-search.js +478 -0
- package/dist/shortcuts/meeting/operations/eb/factory.js +161 -8
- package/dist/shortcuts/meeting/operations/eb/index.js +2 -0
- package/dist/shortcuts/meeting/operations/eb/room-recommend.js +36 -2
- package/dist/shortcuts/meeting/operations/eb-fallback.js +4 -0
- package/dist/shortcuts/meeting/operations/env.js +6 -0
- package/dist/shortcuts/meeting/operations/invitation.js +200 -28
- package/dist/shortcuts/okr/host.js +2 -1
- package/dist/shortcuts/plan/host.js +2 -1
- package/dist/shortcuts/project/host.js +2 -1
- package/dist/shortcuts/workflow/endpoints.js +8 -3
- package/dist/shortcuts/workflow/operations/form.js +341 -36
- package/dist/shortcuts/workflow/operations.js +23 -1
- package/docs/ehr.md +1 -1
- package/docs/fna.md +2 -1
- package/package.json +1 -1
- package/skill-template/business-info.json +18 -0
- package/skill-template/domains/cowork.md +14 -0
- package/skills/weaver-e10-cowork/SKILL.md +101 -0
- package/skills/weaver-e10-cowork/references/cowork-read.md +90 -0
- package/skills/weaver-e10-cowork/references/cowork-write.md +131 -0
- package/skills/weaver-e10-cowork/references/safety-boundaries.md +45 -0
- package/skills/weaver-e10-jucailin/SKILL.md +21 -3
- package/skills/weaver-e10-jucailin/references/recruit-read.md +25 -0
- package/skills/weaver-e10-jucailin/references/recruit-write.md +13 -0
- package/skills/weaver-e10-jucailin/references/salary.md +15 -11
- package/skills/weaver-e10-mail/SKILL.md +12 -9
- package/skills/weaver-e10-meeting/SKILL.md +499 -300
- package/skills/weaver-e10-meeting/references/meeting-eb.md +334 -0
- package/skills/weaver-e10-meeting/references/meeting-invitation.md +64 -6
- package/skills/weaver-e10-meeting.zip +0 -0
- package/skills/weaver-e10-okr/references/source-manifest.json +3 -3
- package/skills/weaver-e10-qiyecheng/SKILL.md +5 -2
- package/skills/weaver-e10-qiyecheng/references/catalog.md +2 -2
- package/skills/weaver-e10-qiyecheng/references/expense-chart.md +15 -1
- package/skills/weaver-e10-qiyecheng/references/source-manifest.json +22 -21
- package/skills/weaver-e10-yimiaoban/references/source-manifest.json +3 -3
|
@@ -2,6 +2,7 @@ import { E10_SESSION_EXPIRED_MESSAGE } from '../../internal/e10/auth/messages.js
|
|
|
2
2
|
import { requireE10Session } from '../../internal/e10/context.js';
|
|
3
3
|
import { getActiveProfile } from '../../internal/e10/auth/session.js';
|
|
4
4
|
import { E10ApiError, createE10RequestRuntime } from '../../internal/e10/request-runtime.js';
|
|
5
|
+
import { isE10SessionExpiredResponse } from '../../internal/e10/auth-recovery.js';
|
|
5
6
|
import { CaizhilianCliError, caizhilianAuthError, caizhilianNetworkError, caizhilianValidationError, } from './errors.js';
|
|
6
7
|
/** 业务请求默认超时(源 assets/config.json:http.timeout = 12 秒)。 */
|
|
7
8
|
export const CAIZHILAN_TIMEOUT_MS = 12_000;
|
|
@@ -152,7 +153,7 @@ export function createCaizhilianHost(ctx, dependencies = {}) {
|
|
|
152
153
|
}
|
|
153
154
|
async function request(request) {
|
|
154
155
|
const response = await doRequest(request, false).catch((error) => mapRuntimeNetworkError(error));
|
|
155
|
-
if (response
|
|
156
|
+
if (isE10SessionExpiredResponse(response)) {
|
|
156
157
|
throw caizhilianAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
|
|
157
158
|
}
|
|
158
159
|
if (response.parseError) {
|
|
@@ -183,7 +184,7 @@ export function createCaizhilianHost(ctx, dependencies = {}) {
|
|
|
183
184
|
}
|
|
184
185
|
async function requestBinary(request) {
|
|
185
186
|
const response = await doRequest(request, true).catch((error) => mapRuntimeNetworkError(error));
|
|
186
|
-
if (response
|
|
187
|
+
if (isE10SessionExpiredResponse(response)) {
|
|
187
188
|
throw caizhilianAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
|
|
188
189
|
}
|
|
189
190
|
if (response.status < 200 || response.status >= 300) {
|
|
@@ -2,6 +2,7 @@ import { E10_SESSION_EXPIRED_MESSAGE } from '../../internal/e10/auth/messages.js
|
|
|
2
2
|
import { requireE10Session } from '../../internal/e10/context.js';
|
|
3
3
|
import { getActiveProfile } from '../../internal/e10/auth/session.js';
|
|
4
4
|
import { E10ApiError, createE10RequestRuntime } from '../../internal/e10/request-runtime.js';
|
|
5
|
+
import { isE10SessionExpiredResponse } from '../../internal/e10/auth-recovery.js';
|
|
5
6
|
import { calendarAuthError, calendarNetworkError, CalendarCliError, calendarValidationError } from './errors.js';
|
|
6
7
|
function inferProfile(ctx) {
|
|
7
8
|
const options = ctx.getGlobalOptions();
|
|
@@ -110,7 +111,7 @@ export function createCalendarHost(ctx, dependencies = {}) {
|
|
|
110
111
|
catch (error) {
|
|
111
112
|
mapRuntimeNetworkError(error);
|
|
112
113
|
}
|
|
113
|
-
if (response
|
|
114
|
+
if (isE10SessionExpiredResponse(response)) {
|
|
114
115
|
throw calendarAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
|
|
115
116
|
}
|
|
116
117
|
if (response.parseError) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import { basename } from 'node:path';
|
|
3
3
|
import { E10_SESSION_EXPIRED_MESSAGE } from '../../internal/e10/auth/messages.js';
|
|
4
|
+
import { isE10SessionExpiredResponse, } from '../../internal/e10/auth-recovery.js';
|
|
4
5
|
import { requireE10Session } from '../../internal/e10/context.js';
|
|
5
6
|
import { getActiveProfile } from '../../internal/e10/auth/session.js';
|
|
6
7
|
import { E10ApiError, createE10RequestRuntime, } from '../../internal/e10/request-runtime.js';
|
|
@@ -179,7 +180,7 @@ export function createContractHost(ctx, dependencies = {}) {
|
|
|
179
180
|
}
|
|
180
181
|
throw error;
|
|
181
182
|
}
|
|
182
|
-
if (response
|
|
183
|
+
if (isE10SessionExpiredResponse(response)) {
|
|
183
184
|
throw contractAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status, path });
|
|
184
185
|
}
|
|
185
186
|
const { text, value } = response;
|
|
@@ -2,7 +2,9 @@ import { E10_SESSION_EXPIRED_MESSAGE } from '../../internal/e10/auth/messages.js
|
|
|
2
2
|
import { requireE10Session } from '../../internal/e10/context.js';
|
|
3
3
|
import { getActiveProfile } from '../../internal/e10/auth/session.js';
|
|
4
4
|
import { E10ApiError, createE10RequestRuntime } from '../../internal/e10/request-runtime.js';
|
|
5
|
+
import { isE10SessionExpiredResponse } from '../../internal/e10/auth-recovery.js';
|
|
5
6
|
import { CoworkCliError, coworkAuthError, coworkNetworkError, coworkValidationError } from './errors.js';
|
|
7
|
+
import { coworkPerf } from './operations/types.js';
|
|
6
8
|
function inferProfile(ctx) {
|
|
7
9
|
const options = ctx.getGlobalOptions();
|
|
8
10
|
return options.profile || getActiveProfile();
|
|
@@ -97,7 +99,9 @@ export function createCoworkHost(ctx, dependencies = {}) {
|
|
|
97
99
|
|| ((innerCtx) => requireE10Session(innerCtx, { skipProfileEnrichment: true }));
|
|
98
100
|
async function ensureSession() {
|
|
99
101
|
if (!e10Session) {
|
|
102
|
+
const sessionStart = Date.now();
|
|
100
103
|
e10Session = await requireSession(ctx);
|
|
104
|
+
coworkPerf.sessionMs = Date.now() - sessionStart;
|
|
101
105
|
sessionContext = {
|
|
102
106
|
baseUrl: e10Session.getBaseUrl(),
|
|
103
107
|
profile: inferProfile(ctx),
|
|
@@ -123,6 +127,7 @@ export function createCoworkHost(ctx, dependencies = {}) {
|
|
|
123
127
|
assertPath(path);
|
|
124
128
|
const resolvedMethod = method || (body === undefined && form === undefined ? 'GET' : 'POST');
|
|
125
129
|
let response;
|
|
130
|
+
const reqStart = Date.now();
|
|
126
131
|
try {
|
|
127
132
|
response = await (await runtime()).requestJson({
|
|
128
133
|
module: 'cowork',
|
|
@@ -139,7 +144,11 @@ export function createCoworkHost(ctx, dependencies = {}) {
|
|
|
139
144
|
catch (error) {
|
|
140
145
|
mapRuntimeNetworkError(error);
|
|
141
146
|
}
|
|
142
|
-
|
|
147
|
+
finally {
|
|
148
|
+
coworkPerf.requestCount += 1;
|
|
149
|
+
coworkPerf.requestMs += Date.now() - reqStart;
|
|
150
|
+
}
|
|
151
|
+
if (isE10SessionExpiredResponse(response)) {
|
|
143
152
|
throw coworkAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
|
|
144
153
|
}
|
|
145
154
|
if (response.parseError) {
|
|
@@ -1,10 +1,25 @@
|
|
|
1
1
|
export const READ = 'read';
|
|
2
2
|
export const READ_BEFORE_WRITE = 'read-before-write';
|
|
3
3
|
export const WRITE = 'write';
|
|
4
|
+
/** 进程内耗时收集器(周军锋 2026-10-20 要求:全链路耗时可量化)。host 写入、coworkSuccess 注出;内部诊断字段不进回答。 */
|
|
5
|
+
export const coworkPerf = {
|
|
6
|
+
t0: Date.now(),
|
|
7
|
+
sessionMs: 0,
|
|
8
|
+
requestCount: 0,
|
|
9
|
+
requestMs: 0,
|
|
10
|
+
};
|
|
4
11
|
export function coworkSuccess(action, data, meta) {
|
|
5
12
|
const payload = { ok: true, action, data };
|
|
6
|
-
|
|
7
|
-
|
|
13
|
+
const perf = {
|
|
14
|
+
elapsed_ms: Date.now() - coworkPerf.t0,
|
|
15
|
+
session_ms: coworkPerf.sessionMs || undefined,
|
|
16
|
+
requests: coworkPerf.requestCount || undefined,
|
|
17
|
+
network_ms: coworkPerf.requestMs || undefined,
|
|
18
|
+
};
|
|
19
|
+
for (const k of Object.keys(perf))
|
|
20
|
+
if (perf[k] === undefined)
|
|
21
|
+
delete perf[k];
|
|
22
|
+
payload.meta = { ...(meta || {}), ...perf };
|
|
8
23
|
return payload;
|
|
9
24
|
}
|
|
10
25
|
/** 构造「对象类型 + 严格禁止额外字段」的 JSON Schema。 */
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import { requireE10Session } from '../../internal/e10/context.js';
|
|
3
|
+
import { isE10SessionExpiredResponse } from '../../internal/e10/auth-recovery.js';
|
|
3
4
|
import { getActiveProfile } from '../../internal/e10/auth/session.js';
|
|
4
5
|
import { createE10RequestRuntime } from '../../internal/e10/request-runtime.js';
|
|
5
6
|
import { ebuilderAuthError, ebuilderBusinessError, ebuilderNetworkError, ebuilderValidationError, } from './errors.js';
|
|
@@ -231,7 +232,8 @@ export function createEbuilderFormHost(ctx) {
|
|
|
231
232
|
headers: options.headers,
|
|
232
233
|
bigIntStrings: options.bigIntStrings ?? true,
|
|
233
234
|
});
|
|
234
|
-
|
|
235
|
+
// 403 在 eBuilder 这里偏「无权」,3xx 只有跳到登录页才算登录失效(文件服务交接不算)。
|
|
236
|
+
if (isE10SessionExpiredResponse(response) || response.status === 403) {
|
|
235
237
|
throw ebuilderAuthError('session_expired', 'E10 登录态已失效或当前账号无权访问该 eBuilder 数据', { httpStatus: response.status });
|
|
236
238
|
}
|
|
237
239
|
if (response.parseError) {
|
|
@@ -3,6 +3,7 @@ import { E10_SESSION_EXPIRED_MESSAGE } from '../../internal/e10/auth/messages.js
|
|
|
3
3
|
import { requireE10Session } from '../../internal/e10/context.js';
|
|
4
4
|
import { getActiveProfile } from '../../internal/e10/auth/session.js';
|
|
5
5
|
import { E10ApiError, createE10RequestRuntime } from '../../internal/e10/request-runtime.js';
|
|
6
|
+
import { isE10SessionExpiredResponse } from '../../internal/e10/auth-recovery.js';
|
|
6
7
|
import { ehrAuthError, ehrNetworkError, EhrCliError, ehrValidationError } from './errors.js';
|
|
7
8
|
function inferProfile(ctx) {
|
|
8
9
|
const options = ctx.getGlobalOptions();
|
|
@@ -165,7 +166,7 @@ export function createEhrHost(ctx, dependencies = {}) {
|
|
|
165
166
|
catch (error) {
|
|
166
167
|
mapRuntimeNetworkError(error);
|
|
167
168
|
}
|
|
168
|
-
if (response
|
|
169
|
+
if (isE10SessionExpiredResponse(response)) {
|
|
169
170
|
throw ehrAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
|
|
170
171
|
}
|
|
171
172
|
if (response.parseError) {
|
|
@@ -28,6 +28,31 @@ async function requireRecruitCache(env) {
|
|
|
28
28
|
}
|
|
29
29
|
return env.recruitContext.load(env.host);
|
|
30
30
|
}
|
|
31
|
+
// ── 产物链接(2026-10-29 周军锋要求:写操作完成后自动生成链接返回对话框)──
|
|
32
|
+
// URL 模板与 recruit-stage 脚本同源:人才卡片 /sp/ebdpage/view/{ebdDetailPageId}?talentId=(实证);
|
|
33
|
+
// 列表滑出 /sp/ebdfpage/list/{listId}/card/0/{objId}/{dataId}/{listId}?id={dataId}&openSource=field&openSourceId={dataId}&openSourceAppId={appId}(面试管理实证,offer/entry 同构未单独实测);
|
|
34
|
+
// 列表直开 /sp/ebdfpage/list/{listId}[?talentId=](candidate.import 同构)。全部取自环境缓存,id 缺失返回 null 不编造。
|
|
35
|
+
function recruitTalentCardUrl(cache, baseUrl, talentId) {
|
|
36
|
+
if (!cache.ebdDetailPageId || !talentId)
|
|
37
|
+
return null;
|
|
38
|
+
const extra = String(cache.selfUse) === "1" ? "&recruitType=useSelf" : "";
|
|
39
|
+
return `${baseUrl.replace(/\/+$/, "")}/sp/ebdpage/view/${cache.ebdDetailPageId}?talentId=${talentId}${extra}`;
|
|
40
|
+
}
|
|
41
|
+
function recruitSlideLocateUrl(cache, baseUrl, listKey, formKey, dataId) {
|
|
42
|
+
const listId = cache.lists?.[listKey]?.listId;
|
|
43
|
+
const objId = cache.forms?.[formKey]?.objId;
|
|
44
|
+
if (!listId || !objId || !dataId || !cache.appId)
|
|
45
|
+
return null;
|
|
46
|
+
const base = baseUrl.replace(/\/+$/, "");
|
|
47
|
+
return `${base}/sp/ebdfpage/list/${listId}/card/0/${objId}/${dataId}/${listId}?id=${dataId}&openSource=field&openSourceId=${dataId}&openSourceAppId=${cache.appId}`;
|
|
48
|
+
}
|
|
49
|
+
function recruitOpenListUrl(cache, baseUrl, listKey, talentId) {
|
|
50
|
+
const menu = cache.lists?.[listKey];
|
|
51
|
+
if (!menu?.listId)
|
|
52
|
+
return null;
|
|
53
|
+
const extra = talentId ? `?talentId=${talentId}${String(cache.selfUse) === "1" ? "&recruitType=useSelf" : ""}` : "";
|
|
54
|
+
return `${baseUrl.replace(/\/+$/, "")}/sp/ebdfpage/list/${menu.listId}${extra}`;
|
|
55
|
+
}
|
|
31
56
|
/** 建前查重:按手机号搜人才库(total 恒 0 平台缺陷,以 data.data[] 非空为准)。 */
|
|
32
57
|
export async function checkDuplicateByMobile(env, cache, mobile) {
|
|
33
58
|
const talent = cache.lists.talent;
|
|
@@ -501,6 +526,8 @@ const candidateCreateApply = {
|
|
|
501
526
|
talentId: newId,
|
|
502
527
|
initTalentBatch: initMsg,
|
|
503
528
|
searchHint: `可用 ehr.recruit.candidate.search 按 name=${name} 或 mobile=${mobile} 回查`,
|
|
529
|
+
talentCardUrl: recruitTalentCardUrl(cache, lease.baseUrl, newId),
|
|
530
|
+
talentListLocateUrl: recruitOpenListUrl(cache, lease.baseUrl, 'talent', newId),
|
|
504
531
|
});
|
|
505
532
|
},
|
|
506
533
|
};
|
|
@@ -713,6 +740,8 @@ const interviewArrangeApply = {
|
|
|
713
740
|
candidate: `${plan.candidateName}(talentId=${args.talentId})`,
|
|
714
741
|
time: plan.interviewTime,
|
|
715
742
|
interviewer: plan.interviewer,
|
|
743
|
+
talentCardUrl: recruitTalentCardUrl(cache, lease.baseUrl, args.talentId),
|
|
744
|
+
interviewLocateUrl: recruitSlideLocateUrl(cache, lease.baseUrl, 'interview', 'interview_plan', planFormId),
|
|
716
745
|
});
|
|
717
746
|
},
|
|
718
747
|
};
|
|
@@ -962,6 +991,8 @@ const offerSendApply = {
|
|
|
962
991
|
sendOffer: 'SUCCESS',
|
|
963
992
|
runEsb: { successNum: esbResult.successNum, failNum: esbResult.failNum, msg: esbResult.successMsg },
|
|
964
993
|
nextStep: '回查 offer_status_select 应已变更为「已发送待反馈」',
|
|
994
|
+
talentCardUrl: recruitTalentCardUrl(cache, lease.baseUrl, payload.talentId),
|
|
995
|
+
offerListUrl: recruitOpenListUrl(cache, lease.baseUrl, 'offer'),
|
|
965
996
|
});
|
|
966
997
|
},
|
|
967
998
|
};
|
|
@@ -1241,6 +1272,7 @@ const entryProcessApply = {
|
|
|
1241
1272
|
saveEstPreEntry: '已落账号',
|
|
1242
1273
|
runEsb: { successNum: esbResult.successNum, failNum: esbResult.failNum, msg: esbResult.successMsg },
|
|
1243
1274
|
nextStep: '回查入职列表 transact_status 应由 1 变为 2',
|
|
1275
|
+
entryListUrl: recruitOpenListUrl(cache, lease.baseUrl, 'entry'),
|
|
1244
1276
|
});
|
|
1245
1277
|
},
|
|
1246
1278
|
};
|
|
@@ -1463,6 +1495,7 @@ const feedbackEditApply = {
|
|
|
1463
1495
|
? { result: backFeedback.result, comment: backFeedback.judgement, changed }
|
|
1464
1496
|
: '回查未取到该反馈',
|
|
1465
1497
|
...(changed ? {} : { warning: '回查未确认变更生效,请人工在 E10 中核对面试反馈,禁止自动重试' }),
|
|
1498
|
+
talentCardUrl: recruitTalentCardUrl(cache, lease.baseUrl, payload.talentId),
|
|
1466
1499
|
});
|
|
1467
1500
|
},
|
|
1468
1501
|
};
|
|
@@ -1581,6 +1614,7 @@ const feedbackUrgeApply = {
|
|
|
1581
1614
|
requireConfirm(args, 'ehr.recruit.candidate.interview.feedback.urge.apply');
|
|
1582
1615
|
const lease = await verifyLease(args, 'ehr.recruit.candidate.interview.feedback.urge.apply', env);
|
|
1583
1616
|
const payload = (lease.payload || {});
|
|
1617
|
+
const cache = await requireRecruitCache(env);
|
|
1584
1618
|
if (!payload.feedbackRecordId || !payload.esbActionId || !payload.interviewDetailObjId) {
|
|
1585
1619
|
throw ehrValidationError('continuation_payload_invalid', 'continuation 缺少催评参数,请重新执行 prepare');
|
|
1586
1620
|
}
|
|
@@ -1602,6 +1636,7 @@ const feedbackUrgeApply = {
|
|
|
1602
1636
|
status: 'COMPLETE',
|
|
1603
1637
|
feedbackRecordId: payload.feedbackRecordId,
|
|
1604
1638
|
runEsb: { successNum: esbResult.successNum, failNum: esbResult.failNum, msg: esbResult.successMsg },
|
|
1639
|
+
talentCardUrl: recruitTalentCardUrl(cache, lease.baseUrl, payload.talentId),
|
|
1605
1640
|
});
|
|
1606
1641
|
},
|
|
1607
1642
|
};
|
|
@@ -698,6 +698,152 @@ const candidateList = {
|
|
|
698
698
|
});
|
|
699
699
|
},
|
|
700
700
|
};
|
|
701
|
+
/** 当前登录人姓名(teamsCheck → currentUser.username),用于「我作为面试官」匹配 */
|
|
702
|
+
/** 人员字段可能是 JSON 数组串([{name,id,...}])→ 取姓名连接;纯文本原样返回 */
|
|
703
|
+
function personNames(value) {
|
|
704
|
+
const text = String(value ?? '').trim();
|
|
705
|
+
if (!text)
|
|
706
|
+
return '';
|
|
707
|
+
if (text.startsWith('[')) {
|
|
708
|
+
try {
|
|
709
|
+
const arr = JSON.parse(text);
|
|
710
|
+
if (Array.isArray(arr)) {
|
|
711
|
+
const names = arr
|
|
712
|
+
.map((item) => (typeof item === 'string' ? item : item?.name ?? item?.username ?? ''))
|
|
713
|
+
.filter(Boolean);
|
|
714
|
+
if (names.length)
|
|
715
|
+
return names.join('、');
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
catch { /* 非 JSON,按纯文本处理 */ }
|
|
719
|
+
}
|
|
720
|
+
return text;
|
|
721
|
+
}
|
|
722
|
+
/** 展示名兜底:跨基线 displayData 会把关联字段直接给名字(talent_id=人名、position_manage_id=职位名);纯 16 位以上数字视为 id,不当名字用 */
|
|
723
|
+
function nameLike(value) {
|
|
724
|
+
const text = String(value ?? '').trim();
|
|
725
|
+
if (!text || /^\d{16,}$/.test(text))
|
|
726
|
+
return null;
|
|
727
|
+
return text;
|
|
728
|
+
}
|
|
729
|
+
async function resolveCurrentUserName(host) {
|
|
730
|
+
// teamsCheck 的响应不被 isBusinessSuccess 认可(无 status 字段),故直接 fetch,绕开业务判定
|
|
731
|
+
try {
|
|
732
|
+
const session = await host.getSession();
|
|
733
|
+
const baseUrl = String(session?.baseUrl || '');
|
|
734
|
+
if (!baseUrl)
|
|
735
|
+
return '';
|
|
736
|
+
const resp = await fetch(`${baseUrl}/api/baseserver/layout/teamsCheck?clientType=not_xinchuang&client=WEB&domainName=${encodeURIComponent(baseUrl)}`, {
|
|
737
|
+
method: 'POST',
|
|
738
|
+
headers: {
|
|
739
|
+
accept: 'application/json',
|
|
740
|
+
'content-type': 'application/json',
|
|
741
|
+
eteamsid: String(session?.eteamsId || ''),
|
|
742
|
+
cookie: typeof session?.cookieHeader === 'function' ? session.cookieHeader() : '',
|
|
743
|
+
'user-agent': typeof session?.uaHeader === 'function' ? session.uaHeader() : 'weaver-work-cli',
|
|
744
|
+
origin: baseUrl,
|
|
745
|
+
},
|
|
746
|
+
redirect: 'manual',
|
|
747
|
+
});
|
|
748
|
+
const data = await resp.json().catch(() => ({}));
|
|
749
|
+
const cu = data.currentUser ?? data.data?.currentUser ?? null;
|
|
750
|
+
return cu ? String(cu.username ?? cu.id ?? '') : '';
|
|
751
|
+
}
|
|
752
|
+
catch {
|
|
753
|
+
return '';
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
const interviewFeedbackTodo = {
|
|
757
|
+
name: 'ehr.recruit.interview.feedbackTodo',
|
|
758
|
+
resource: 'recruit',
|
|
759
|
+
method: 'interview.feedbackTodo',
|
|
760
|
+
action: 'ehr.recruit.interview.feedbackTodo',
|
|
761
|
+
summary: '面试待办:**只返回我作为面试官的 AI 面试待反馈清单**(候选人已答完、已有录像、我还没填意见);按需翻页 + 客户端筛选,不返回全量面试',
|
|
762
|
+
capability: 'recruit',
|
|
763
|
+
risk: READ,
|
|
764
|
+
inputSchema: schemaOf({
|
|
765
|
+
maxPages: { type: 'integer', minimum: 1, maximum: 50, description: '最多翻页数(默认 5)' },
|
|
766
|
+
pageSize: { type: 'integer', minimum: 1, maximum: 200, description: '单页条数(默认 200,实测服务端上限 200)' },
|
|
767
|
+
}),
|
|
768
|
+
handler: async (args, env) => {
|
|
769
|
+
if (!env.recruitContext)
|
|
770
|
+
throw ehrValidationError('recruit_context_unavailable', '招聘上下文加载器未初始化');
|
|
771
|
+
const cache = await env.recruitContext.load(env.host);
|
|
772
|
+
const listParam = cache.lists.interview;
|
|
773
|
+
const form = cache.forms.interview_plan;
|
|
774
|
+
if (!listParam?.listId || !form?.objId) {
|
|
775
|
+
throw new RecruitContextError('recruit_list_unavailable', '当前环境未启用「面试管理」功能(未找到面试列表/表单参数,按能力探测拦截)。');
|
|
776
|
+
}
|
|
777
|
+
const pageSize = toNumber(args.pageSize, 'pageSize') ?? 200;
|
|
778
|
+
const maxPages = toNumber(args.maxPages, 'maxPages') ?? 5;
|
|
779
|
+
const me = await resolveCurrentUserName(env.host);
|
|
780
|
+
// 枚举可能回退成 optionId:用环境缓存的 field_options 运行时映射回中文(不写死 id)
|
|
781
|
+
const fields = cache.fields[form.objId] || {};
|
|
782
|
+
const fieldOpts = cache.field_options[form.objId] || {};
|
|
783
|
+
const statusOpts = fieldOpts[fields.union_status] || {};
|
|
784
|
+
const typeOpts = fieldOpts[fields.interview_type] || {};
|
|
785
|
+
const toCn = (map, raw) => {
|
|
786
|
+
const text = String(raw ?? '');
|
|
787
|
+
return map[text] ?? text;
|
|
788
|
+
};
|
|
789
|
+
const statusOf = (row) => toCn(statusOpts, row?.union_status_key ?? row?.union_status);
|
|
790
|
+
const typeOf = (row) => toCn(typeOpts, row?.interview_type_key ?? row?.interview_type);
|
|
791
|
+
const interviewerOf = (row) => personNames(row?.interviewer);
|
|
792
|
+
const scanned = [];
|
|
793
|
+
let lastPageCount = 0;
|
|
794
|
+
for (let i = 0; i < maxPages; i++) {
|
|
795
|
+
const body = buildListBody({
|
|
796
|
+
listId: listParam.listId,
|
|
797
|
+
cusMenuId: listParam.cusMenuId,
|
|
798
|
+
menuName: listParam.menuName || '面试管理',
|
|
799
|
+
title: '面试管理',
|
|
800
|
+
current: i + 1,
|
|
801
|
+
pageSize,
|
|
802
|
+
});
|
|
803
|
+
const page = await fetchListRows(env.host, form.objId, listParam.listId, body);
|
|
804
|
+
const use = page.displayRows && page.displayRows.length ? page.displayRows : page.rows;
|
|
805
|
+
scanned.push(...use);
|
|
806
|
+
lastPageCount = use.length;
|
|
807
|
+
if (use.length < pageSize)
|
|
808
|
+
break;
|
|
809
|
+
}
|
|
810
|
+
// 「待反馈」= 后端 InterviewStatusEnum.TO_FEEDBACK:
|
|
811
|
+
// AI 面试结束时间已过 且(有录像附件 或 已开始录制)且 反馈结果为空
|
|
812
|
+
// ⇒ 这个状态本身就等于「已完成 + 有视频 + 未填意见」,无需再读视频字段
|
|
813
|
+
const pending = scanned.filter((row) => /待反馈/.test(statusOf(row)));
|
|
814
|
+
const mineAll = me ? pending.filter((row) => interviewerOf(row).includes(me)) : [];
|
|
815
|
+
const mineAi = mineAll.filter((row) => /AI\s*面试/i.test(typeOf(row)));
|
|
816
|
+
const slim = (row) => ({
|
|
817
|
+
form_data_id: row?.form_data_id ?? row?.id ?? null,
|
|
818
|
+
candidate_name: nameLike(row?.candidate_name) ?? nameLike(row?.talent_id) ?? nameLike(row?.name),
|
|
819
|
+
position_name: nameLike(row?.position_name) ?? nameLike(row?.position_manage_id),
|
|
820
|
+
start_time: row?.start_time ?? null,
|
|
821
|
+
interview_type_key: typeOf(row) || null,
|
|
822
|
+
interview_session: row?.interview_session ?? null,
|
|
823
|
+
interviewer: interviewerOf(row) || null,
|
|
824
|
+
union_status_key: statusOf(row) || null,
|
|
825
|
+
subject: row?.subject ?? null,
|
|
826
|
+
talent_id: row?.talent_id ?? null,
|
|
827
|
+
position_manage_id: row?.position_manage_id ?? null,
|
|
828
|
+
});
|
|
829
|
+
const notes = [];
|
|
830
|
+
if (!me)
|
|
831
|
+
notes.push('未能解析当前登录人姓名(teamsCheck 失败),无法按面试官过滤');
|
|
832
|
+
if (lastPageCount >= pageSize && scanned.length >= pageSize * maxPages) {
|
|
833
|
+
notes.push(`已扫描 ${scanned.length} 条(达 maxPages=${maxPages} 上限),如怀疑遗漏可加大 maxPages`);
|
|
834
|
+
}
|
|
835
|
+
return ehrSuccess('ehr.recruit.interview.feedbackTodo', {
|
|
836
|
+
currentUser: me || null,
|
|
837
|
+
scanned: scanned.length,
|
|
838
|
+
myAiPendingFeedback: { count: mineAi.length, rows: mineAi.map(slim) },
|
|
839
|
+
myOtherPendingFeedback: {
|
|
840
|
+
count: mineAll.length - mineAi.length,
|
|
841
|
+
rows: mineAll.filter((row) => !/AI\s*面试/i.test(typeOf(row))).map(slim),
|
|
842
|
+
},
|
|
843
|
+
...(notes.length ? { note: notes.join(';') } : {}),
|
|
844
|
+
});
|
|
845
|
+
},
|
|
846
|
+
};
|
|
701
847
|
const candidateCard = {
|
|
702
848
|
name: 'ehr.recruit.candidate.card',
|
|
703
849
|
resource: 'recruit',
|
|
@@ -815,6 +961,7 @@ export const recruitOperations = [
|
|
|
815
961
|
candidateSearch,
|
|
816
962
|
candidateList,
|
|
817
963
|
candidateCard,
|
|
964
|
+
interviewFeedbackTodo,
|
|
818
965
|
];
|
|
819
966
|
export const RECRUIT_SECOND_PHASE_WITHHELD = [
|
|
820
967
|
'recruit.candidate.create',
|
|
@@ -2,6 +2,7 @@ import { E10_SESSION_EXPIRED_MESSAGE } from '../../internal/e10/auth/messages.js
|
|
|
2
2
|
import { requireE10Session } from '../../internal/e10/context.js';
|
|
3
3
|
import { getActiveProfile } from '../../internal/e10/auth/session.js';
|
|
4
4
|
import { E10ApiError, createE10RequestRuntime } from '../../internal/e10/request-runtime.js';
|
|
5
|
+
import { isE10SessionExpiredResponse } from '../../internal/e10/auth-recovery.js';
|
|
5
6
|
import { EsbCliError, esbApiError, esbAuthError, esbNetworkError, esbPermissionError, esbValidationError, esbWriteUncertain, } from './errors.js';
|
|
6
7
|
/** 源接口文档给出的去域名路径;域名与认证由 E10 会话层托管。 */
|
|
7
8
|
export const ESB_PATHS = {
|
|
@@ -117,7 +118,7 @@ export function createEsbHost(ctx, dependencies = {}) {
|
|
|
117
118
|
catch (error) {
|
|
118
119
|
mapRuntimeNetworkError(error);
|
|
119
120
|
}
|
|
120
|
-
if (response
|
|
121
|
+
if (isE10SessionExpiredResponse(response)) {
|
|
121
122
|
throw esbAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
|
|
122
123
|
}
|
|
123
124
|
if (response.parseError) {
|
|
@@ -2,6 +2,7 @@ import { E10_SESSION_EXPIRED_MESSAGE } from '../../internal/e10/auth/messages.js
|
|
|
2
2
|
import { requireE10Session } from '../../internal/e10/context.js';
|
|
3
3
|
import { getActiveProfile } from '../../internal/e10/auth/session.js';
|
|
4
4
|
import { E10ApiError, createE10RequestRuntime } from '../../internal/e10/request-runtime.js';
|
|
5
|
+
import { isE10SessionExpiredResponse } from '../../internal/e10/auth-recovery.js';
|
|
5
6
|
import { FnaCliError, fnaAuthError, fnaNetworkError, fnaValidationError } from './errors.js';
|
|
6
7
|
function inferProfile(ctx) {
|
|
7
8
|
const options = ctx.getGlobalOptions();
|
|
@@ -116,7 +117,7 @@ export function createFnaHost(ctx, dependencies = {}) {
|
|
|
116
117
|
catch (error) {
|
|
117
118
|
mapRuntimeNetworkError(error);
|
|
118
119
|
}
|
|
119
|
-
if (response
|
|
120
|
+
if (isE10SessionExpiredResponse(response)) {
|
|
120
121
|
throw fnaAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
|
|
121
122
|
}
|
|
122
123
|
if (response.parseError) {
|
|
@@ -8,6 +8,8 @@ const MAX_ROWS_LIMIT = 5000;
|
|
|
8
8
|
const USER_INPUT_LIMIT = 500;
|
|
9
9
|
const BLOCKED_MESSAGE = /拦截|高危|安全校验/;
|
|
10
10
|
const CLARIFY_MESSAGE = /不明确|请补充|补充查询/;
|
|
11
|
+
/** 权限类文案:命中即视为数据权限不足,Agent 必须停在原地,不得改调其它接口拼数据。 */
|
|
12
|
+
const PERMISSION_DENIED_MESSAGE = /权限|越权/;
|
|
11
13
|
function requireUserInput(value) {
|
|
12
14
|
if (typeof value !== 'string' || value.trim().length === 0) {
|
|
13
15
|
throw fnaValidationError('user_input_required', 'userInput 必填(自然语言问题,不能为空白)');
|
|
@@ -65,16 +67,23 @@ function collectColumns(rows) {
|
|
|
65
67
|
}
|
|
66
68
|
return columns;
|
|
67
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* 按文案推断字符串 data 的语义。
|
|
72
|
+
* 权限类文案优先归类为 clarify:服务端可能只返回「无数据权限」而不带「不明确」,
|
|
73
|
+
* 若落进 unexpected,Agent 会误判为无非预期形态,进而换问法或改调其它接口拼数据。
|
|
74
|
+
*/
|
|
68
75
|
function classifyMessage(message) {
|
|
76
|
+
if (PERMISSION_DENIED_MESSAGE.test(message))
|
|
77
|
+
return { kind: 'clarify', permissionDenied: true };
|
|
69
78
|
if (BLOCKED_MESSAGE.test(message))
|
|
70
|
-
return 'blocked';
|
|
79
|
+
return { kind: 'blocked', permissionDenied: false };
|
|
71
80
|
if (CLARIFY_MESSAGE.test(message))
|
|
72
|
-
return 'clarify';
|
|
81
|
+
return { kind: 'clarify', permissionDenied: false };
|
|
73
82
|
return undefined;
|
|
74
83
|
}
|
|
75
84
|
/**
|
|
76
85
|
* 归一化 getExpenseChart 的 data。code 恒为 200,成败只看 data 形态:
|
|
77
|
-
* 对象数组 → rows/empty
|
|
86
|
+
* 对象数组 → rows/empty;追问/安全拦截/无权限文案 → clarify/blocked(按文案推断,无权限额外标 permissionDenied);其余 → unexpected。
|
|
78
87
|
* maxRows 只截断本地返回,列名仍按服务端全量结果收集。
|
|
79
88
|
*/
|
|
80
89
|
function normalizeChart(data, maxRows) {
|
|
@@ -107,9 +116,12 @@ function normalizeChart(data, maxRows) {
|
|
|
107
116
|
});
|
|
108
117
|
}
|
|
109
118
|
if (typeof data === 'string') {
|
|
110
|
-
const
|
|
111
|
-
if (
|
|
112
|
-
return fnaSuccess('fna.expense.chart', { kind, message: data }, {
|
|
119
|
+
const classified = classifyMessage(data);
|
|
120
|
+
if (classified) {
|
|
121
|
+
return fnaSuccess('fna.expense.chart', { kind: classified.kind, message: data }, {
|
|
122
|
+
messageKindInferred: true,
|
|
123
|
+
permissionDenied: classified.permissionDenied,
|
|
124
|
+
});
|
|
113
125
|
}
|
|
114
126
|
}
|
|
115
127
|
return fnaSuccess('fna.expense.chart', { kind: 'unexpected' }, { dataType: dataTypeOf(data) });
|
|
@@ -124,7 +136,7 @@ const fnaExpenseChart = {
|
|
|
124
136
|
resource: 'expense',
|
|
125
137
|
method: 'expense.chart',
|
|
126
138
|
action: 'fna.expense.chart',
|
|
127
|
-
summary: '报销报表 AI 问答:用自然语言查询分组/排名/趋势,返回 kind=rows/empty/clarify/blocked/unexpected
|
|
139
|
+
summary: '报销报表 AI 问答:用自然语言查询分组/排名/趋势,返回 kind=rows/empty/clarify/blocked/unexpected;无权限文案额外返回 meta.permissionDenied=true,此时必须停住、不得改调其它接口拼数据;maxRows 只截断本地展示',
|
|
128
140
|
capability: 'expense-chart',
|
|
129
141
|
risk: READ,
|
|
130
142
|
inputSchema: schemaOf({
|
|
@@ -2,6 +2,7 @@ import { E10_SESSION_EXPIRED_MESSAGE } from '../../internal/e10/auth/messages.js
|
|
|
2
2
|
import { requireE10Session } from '../../internal/e10/context.js';
|
|
3
3
|
import { getActiveProfile } from '../../internal/e10/auth/session.js';
|
|
4
4
|
import { E10ApiError, createE10RequestRuntime } from '../../internal/e10/request-runtime.js';
|
|
5
|
+
import { isE10SessionExpiredResponse } from '../../internal/e10/auth-recovery.js';
|
|
5
6
|
import { HrmCliError, hrmAuthError, hrmNetworkError, hrmValidationError } from './errors.js';
|
|
6
7
|
function inferProfile(ctx) {
|
|
7
8
|
const options = ctx.getGlobalOptions();
|
|
@@ -114,7 +115,7 @@ export function createHrmHost(ctx, dependencies = {}) {
|
|
|
114
115
|
catch (error) {
|
|
115
116
|
mapRuntimeNetworkError(error);
|
|
116
117
|
}
|
|
117
|
-
if (response
|
|
118
|
+
if (isE10SessionExpiredResponse(response)) {
|
|
118
119
|
throw hrmAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
|
|
119
120
|
}
|
|
120
121
|
if (response.parseError) {
|
|
@@ -3,6 +3,7 @@ import { existsSync } from 'node:fs';
|
|
|
3
3
|
import { mkdir, readFile, stat, writeFile } from 'node:fs/promises';
|
|
4
4
|
import { dirname, extname, resolve } from 'node:path';
|
|
5
5
|
import { E10_SESSION_EXPIRED_MESSAGE } from '../../internal/e10/auth/messages.js';
|
|
6
|
+
import { isE10SessionExpiredResponse, } from '../../internal/e10/auth-recovery.js';
|
|
6
7
|
import { requireE10Session } from '../../internal/e10/context.js';
|
|
7
8
|
import { getActiveProfile } from '../../internal/e10/auth/session.js';
|
|
8
9
|
import { E10ApiError, createE10RequestRuntime } from '../../internal/e10/request-runtime.js';
|
|
@@ -121,7 +122,7 @@ export function createImHost(ctx, dependencies = {}) {
|
|
|
121
122
|
catch (error) {
|
|
122
123
|
mapRuntimeNetworkError(error);
|
|
123
124
|
}
|
|
124
|
-
if (response
|
|
125
|
+
if (isE10SessionExpiredResponse(response)) {
|
|
125
126
|
throw imAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
|
|
126
127
|
}
|
|
127
128
|
if (response.parseError) {
|
|
@@ -182,7 +183,7 @@ export function createImHost(ctx, dependencies = {}) {
|
|
|
182
183
|
catch (error) {
|
|
183
184
|
mapRuntimeNetworkError(error);
|
|
184
185
|
}
|
|
185
|
-
if (response
|
|
186
|
+
if (isE10SessionExpiredResponse(response)) {
|
|
186
187
|
throw imAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE);
|
|
187
188
|
}
|
|
188
189
|
if (response.status !== 200) {
|
package/dist/shortcuts/index.js
CHANGED
|
@@ -130,6 +130,11 @@ export const builtinShortcuts = [
|
|
|
130
130
|
description: '泛微E10采知连(文档中心)CLI',
|
|
131
131
|
load: async () => (await import('./caizhilian/index.js')).caizhilianShortcut,
|
|
132
132
|
}),
|
|
133
|
+
lazyShortcut({
|
|
134
|
+
name: 'contract',
|
|
135
|
+
description: '泛微E10今承达合同管理 CLI',
|
|
136
|
+
load: async () => (await import('./contract/index.js')).contractShortcut,
|
|
137
|
+
}),
|
|
133
138
|
];
|
|
134
139
|
/** 全部 shortcut 的 CLI 元数据(不触发任何实现模块加载)。 */
|
|
135
140
|
export function shortcutMetadata() {
|
|
@@ -4,6 +4,7 @@ import { E10_REAUTH_GUIDANCE, E10_SESSION_EXPIRED_MESSAGE } from '../../internal
|
|
|
4
4
|
import { requireE10Session } from '../../internal/e10/context.js';
|
|
5
5
|
import { getActiveProfile } from '../../internal/e10/auth/session.js';
|
|
6
6
|
import { createE10RequestRuntime } from '../../internal/e10/request-runtime.js';
|
|
7
|
+
import { isE10SessionExpiredResponse } from '../../internal/e10/auth-recovery.js';
|
|
7
8
|
import { InvoiceCliError, invoiceAuthError, invoiceNetworkError, invoiceValidationError, } from './errors.js';
|
|
8
9
|
const MAX_DOWNLOAD_BYTES = 50 * 1024 * 1024;
|
|
9
10
|
const TOKEN_SERVICE = 'cn.weaver.weaver-work-cli.invoice-token';
|
|
@@ -199,7 +200,7 @@ export function createInvoiceHost(ctx, dependencies = {}) {
|
|
|
199
200
|
timeoutMs,
|
|
200
201
|
trustedHeaders: trustedInvoiceHeaders(token),
|
|
201
202
|
});
|
|
202
|
-
if (response
|
|
203
|
+
if (isE10SessionExpiredResponse(response)) {
|
|
203
204
|
throw invoiceAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
|
|
204
205
|
}
|
|
205
206
|
if (response.parseError) {
|
|
@@ -258,7 +259,7 @@ export function createInvoiceHost(ctx, dependencies = {}) {
|
|
|
258
259
|
timeoutMs,
|
|
259
260
|
trustedHeaders: trustedInvoiceHeaders(token),
|
|
260
261
|
});
|
|
261
|
-
if (response
|
|
262
|
+
if (isE10SessionExpiredResponse(response)) {
|
|
262
263
|
throw invoiceAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
|
|
263
264
|
}
|
|
264
265
|
if (response.parseError) {
|
|
@@ -293,7 +294,7 @@ export function createInvoiceHost(ctx, dependencies = {}) {
|
|
|
293
294
|
timeoutMs,
|
|
294
295
|
trustedHeaders: trustedInvoiceHeaders(token),
|
|
295
296
|
});
|
|
296
|
-
if (response
|
|
297
|
+
if (isE10SessionExpiredResponse(response)) {
|
|
297
298
|
throw invoiceAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
|
|
298
299
|
}
|
|
299
300
|
if (response.parseError) {
|
|
@@ -320,7 +321,7 @@ export function createInvoiceHost(ctx, dependencies = {}) {
|
|
|
320
321
|
timeoutMs,
|
|
321
322
|
trustedHeaders: trustedInvoiceHeaders(token),
|
|
322
323
|
});
|
|
323
|
-
if (response
|
|
324
|
+
if (isE10SessionExpiredResponse(response)) {
|
|
324
325
|
throw invoiceAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
|
|
325
326
|
}
|
|
326
327
|
const contentType = response.contentType;
|
|
@@ -2,6 +2,7 @@ import { E10_SESSION_EXPIRED_MESSAGE } from '../../internal/e10/auth/messages.js
|
|
|
2
2
|
import { requireE10Session } from '../../internal/e10/context.js';
|
|
3
3
|
import { getActiveProfile } from '../../internal/e10/auth/session.js';
|
|
4
4
|
import { E10ApiError, createE10RequestRuntime } from '../../internal/e10/request-runtime.js';
|
|
5
|
+
import { isE10SessionExpiredResponse } from '../../internal/e10/auth-recovery.js';
|
|
5
6
|
import { JiuchuanhuiCliError, jiuchuanhuiAuthError, jiuchuanhuiNetworkError, jiuchuanhuiValidationError, } from './errors.js';
|
|
6
7
|
function inferProfile(ctx) {
|
|
7
8
|
const options = ctx.getGlobalOptions();
|
|
@@ -127,7 +128,7 @@ export function createJiuchuanhuiHost(ctx, dependencies = {}) {
|
|
|
127
128
|
catch (error) {
|
|
128
129
|
mapRuntimeNetworkError(error);
|
|
129
130
|
}
|
|
130
|
-
if (response
|
|
131
|
+
if (isE10SessionExpiredResponse(response)) {
|
|
131
132
|
throw jiuchuanhuiAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
|
|
132
133
|
}
|
|
133
134
|
if (response.parseError) {
|