weaver-work-cli 0.1.36 → 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-XBVH7JU7.js → archive-PDV724YK.js} +5 -4
- package/dist/chunks/{archive-oa-MZM3EKFQ.js → archive-oa-4XE4MNXQ.js} +4 -3
- package/dist/chunks/{asset-4XO7UOUW.js → asset-PDL2QE2N.js} +8 -7
- package/dist/chunks/{attachment-content-VXSWANAC.js → attachment-content-MDDGOE5E.js} +3 -3
- package/dist/chunks/{blog-PGZKOPSC.js → blog-UYR3EUXW.js} +5 -4
- package/dist/chunks/{caizhilian-BH4ZVGUN.js → caizhilian-LG2X5NMF.js} +5 -4
- package/dist/chunks/{calendar-MXEMTIO4.js → calendar-FG4BAZHE.js} +4 -3
- package/dist/chunks/{chunk-NCOVUAZK.js → chunk-6FTTSA45.js} +4 -3
- package/dist/chunks/{chunk-HXJQI5NM.js → chunk-6LTFIFLM.js} +3 -2
- package/dist/chunks/{chunk-QYGO5EE2.js → chunk-CORFRD7Q.js} +4 -3
- package/dist/chunks/{chunk-QHFANTX7.js → chunk-DMV2TOMH.js} +1348 -90
- package/dist/chunks/{chunk-TC6MZAO3.js → chunk-ELYDWBZJ.js} +52 -2
- package/dist/chunks/{chunk-JCSN3JGM.js → chunk-FUWVHMVV.js} +7 -6
- package/dist/chunks/{chunk-BPE37FPB.js → chunk-GGJFMNJM.js} +3 -3
- package/dist/chunks/{chunk-6LLJFKJE.js → chunk-IZUDA353.js} +1 -1
- package/dist/chunks/{chunk-XMGSYD6S.js → chunk-J7USS3ED.js} +4 -4
- package/dist/chunks/{chunk-F746NKNF.js → chunk-JSMFPO43.js} +1 -1
- package/dist/chunks/{chunk-T72N74ZT.js → chunk-KKRL3REV.js} +5 -4
- package/dist/chunks/{chunk-VPUTVEMC.js → chunk-NI6GIPIM.js} +98 -3
- package/dist/chunks/{chunk-VPXOPQJI.js → chunk-NR6RKXPA.js} +2 -2
- package/dist/chunks/{chunk-JNG42JKG.js → chunk-OJQUJZQF.js} +4 -6
- package/dist/chunks/{chunk-I6QGIBDM.js → chunk-UJJD4QWF.js} +4 -3
- package/dist/chunks/{chunk-JUXVYKII.js → chunk-UZZBA6PV.js} +174 -14
- package/dist/chunks/{chunk-JYKABYCV.js → chunk-VRYHGG6O.js} +2 -2
- package/dist/chunks/{chunk-WXFPWCXG.js → chunk-XJY7G4DD.js} +3 -3
- package/dist/chunks/{chunk-5DQEEGCC.js → chunk-Z32KWY6G.js} +1 -1
- package/dist/chunks/{conflict-JAB2LJEZ.js → conflict-BQZEQ4SP.js} +2 -2
- package/dist/chunks/{contract-XI2EEIZU.js → contract-XIRAFTEV.js} +4 -3
- package/dist/chunks/{cowork-SCPGLBA4.js → cowork-BLG62BL2.js} +4 -3
- package/dist/chunks/{ebuilder-form-EYLHNOTA.js → ebuilder-form-NUKG5QW4.js} +3 -3
- package/dist/chunks/{ehr-EAP77IMT.js → ehr-2STW5ILH.js} +4 -3
- package/dist/chunks/{esb-2TGGXEE7.js → esb-UUDQUNKY.js} +4 -4
- package/dist/chunks/{fna-2X7ZE7BU.js → fna-6RCY4NU5.js} +4 -3
- package/dist/chunks/{host-C6EL2RKL.js → host-7RXEDMAH.js} +3 -3
- package/dist/chunks/{host-XCLDDXKQ.js → host-EJZFROU3.js} +3 -3
- package/dist/chunks/{host-I4NRK4MX.js → host-FWDOSRH5.js} +3 -3
- package/dist/chunks/{host-Q4LZGCRU.js → host-RHM3NPXV.js} +3 -3
- package/dist/chunks/{host-UEI5FGDM.js → host-UFF33TWJ.js} +3 -3
- package/dist/chunks/{hrm-YXERJM76.js → hrm-PKJ6ELSH.js} +3 -3
- package/dist/chunks/{im-A4VZN2TQ.js → im-YJAXS7CO.js} +3 -3
- package/dist/chunks/{invoice-2Z6YPMIS.js → invoice-HWVSXGX6.js} +3 -3
- package/dist/chunks/{jiuchuanhui-AR5GMKZB.js → jiuchuanhui-L7VKILZB.js} +4 -3
- package/dist/chunks/{lazy-dispatch-QXW7P4X3.js → lazy-dispatch-NMYEIHRH.js} +4 -4
- package/dist/chunks/{mail-7GZ6CPBJ.js → mail-B42RRU7G.js} +4 -3
- package/dist/chunks/{meeting-3NMGA3MB.js → meeting-CFYVBJSS.js} +13 -12
- package/dist/chunks/{okr-BZ6XUOAG.js → okr-GCLF6V7W.js} +4 -3
- package/dist/chunks/{plan-DR5CMUYP.js → plan-ZVL7GBRI.js} +4 -3
- package/dist/chunks/{project-6J63MK33.js → project-4YWAAE2K.js} +4 -3
- package/dist/chunks/{registry-VRTNC6UR.js → registry-EP4VTHZS.js} +8 -8
- package/dist/chunks/{registry-KDD7DHNK.js → registry-FRJ6H7BT.js} +4 -4
- package/dist/chunks/{registry-TKX2E7C7.js → registry-SG2PGVCF.js} +2 -2
- package/dist/chunks/{room-recommend-XCWRMB5P.js → room-recommend-R3JVUUAC.js} +4 -4
- package/dist/chunks/{schedule-IVBICP32.js → schedule-NIJBTYDO.js} +4 -4
- package/dist/chunks/{workflow-D74NFK3C.js → workflow-BB24DFBY.js} +4 -4
- package/dist/index.js +27 -27
- package/dist/internal/e10/auth-recovery.js +114 -1
- package/dist/internal/e10/client.js +16 -2
- package/dist/internal/e10/request-runtime.js +103 -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 +2 -1
- package/dist/shortcuts/ebuilder-form/host.js +3 -1
- package/dist/shortcuts/ehr/host.js +2 -1
- package/dist/shortcuts/esb/host.js +2 -1
- package/dist/shortcuts/fna/host.js +2 -1
- package/dist/shortcuts/hrm/host.js +2 -1
- package/dist/shortcuts/im/host.js +3 -2
- package/dist/shortcuts/invoice/host.js +5 -4
- package/dist/shortcuts/jiuchuanhui/host.js +2 -1
- package/dist/shortcuts/mail/host.js +2 -1
- package/dist/shortcuts/meeting/host.js +3 -2
- package/dist/shortcuts/meeting/invitation/text.js +71 -0
- package/dist/shortcuts/meeting/operations/eb/create-flow.js +1073 -50
- package/dist/shortcuts/meeting/operations/eb/crm-search.js +478 -0
- package/dist/shortcuts/meeting/operations/eb/factory.js +60 -1
- package/dist/shortcuts/meeting/operations/eb/index.js +2 -0
- package/dist/shortcuts/meeting/operations/invitation.js +122 -9
- 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/operations/form.js +160 -2
- package/package.json +1 -1
- package/skills/weaver-e10-meeting/SKILL.md +250 -301
- package/skills/weaver-e10-meeting/references/meeting-eb.md +203 -0
- package/skills/weaver-e10-meeting/references/meeting-invitation.md +37 -5
- package/skills/weaver-e10-meeting.zip +0 -0
- package/skills/weaver-e10-okr/references/source-manifest.json +3 -3
- package/skills/weaver-e10-yimiaoban/references/source-manifest.json +3 -3
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { CliError, ExitCode } from '../../core/errors.js';
|
|
2
|
-
import { isE10AuthenticationRequired, recoverXiaoEAuthSession, sameE10SessionIdentity, } from './auth-recovery.js';
|
|
2
|
+
import { classifyE10Redirect, isE10AuthenticationRequired, recoverXiaoEAuthSession, sameE10SessionIdentity, } from './auth-recovery.js';
|
|
3
3
|
import { startFetchTiming } from './fetch-timing.js';
|
|
4
4
|
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
5
5
|
const DEFAULT_BIG_INT_STRINGS = true;
|
|
6
|
+
/** 下载直传最多跟几跳:E10 -> 对象存储通常 1 跳,留一点余量但不允许成环。 */
|
|
7
|
+
const MAX_DOWNLOAD_REDIRECTS = 3;
|
|
6
8
|
const RESERVED_HEADERS = [
|
|
7
9
|
'authorization',
|
|
8
10
|
'cookie',
|
|
@@ -63,16 +65,18 @@ export class E10RequestRuntime {
|
|
|
63
65
|
}
|
|
64
66
|
stripReservedHeaders(mutable.headers);
|
|
65
67
|
const requestEteamsId = this.session.eteamsId;
|
|
66
|
-
let
|
|
68
|
+
let attempt = await this.executeRequest(mutable, request.trustedHeaders);
|
|
67
69
|
if (!mutable.streamBody
|
|
68
|
-
|
|
70
|
+
// 已交接给文件服务的响应不是 E10 的接口回答,登录态不该由它判定。
|
|
71
|
+
&& !attempt.fromFileRedirect
|
|
72
|
+
&& isE10AuthenticationRequired(authenticationProbe(mutable, attempt.parsed))
|
|
69
73
|
&& await this.recoverExpiredSession(requestEteamsId)) {
|
|
70
74
|
// Authentication rejection is a definitive pre-business failure. Retry
|
|
71
75
|
// exactly once with the new session; never retry transport/partial errors.
|
|
72
|
-
|
|
76
|
+
attempt = await this.executeRequest(mutable, request.trustedHeaders);
|
|
73
77
|
}
|
|
74
|
-
await this.runAfterResponse(mutable, this.sessionContext(), parsed);
|
|
75
|
-
return parsed;
|
|
78
|
+
await this.runAfterResponse(mutable, this.sessionContext(), attempt.parsed);
|
|
79
|
+
return attempt.parsed;
|
|
76
80
|
}
|
|
77
81
|
catch (error) {
|
|
78
82
|
await this.runOnError(mutable, this.sessionContext(), error);
|
|
@@ -84,7 +88,74 @@ export class E10RequestRuntime {
|
|
|
84
88
|
const headers = this.composeHeaders(request, trustedHeaders);
|
|
85
89
|
const init = this.buildFetchInit(request, headers);
|
|
86
90
|
const response = await this.fetchWithTimeout(url, init, request.timeoutMs);
|
|
87
|
-
|
|
91
|
+
// 二进制下载:E10 常以 302 把文件交接给文件服务/对象存储。这里直接跟过去
|
|
92
|
+
// 取字节,既不会把「文件流」当成登录失效,也不会触发无谓的重新登录。
|
|
93
|
+
const followed = request.responseType === 'binary'
|
|
94
|
+
? await this.followDownloadRedirect(response, request, url)
|
|
95
|
+
: { response, followed: false };
|
|
96
|
+
if (followed.followed && (followed.response.status === 401 || followed.response.status === 403)) {
|
|
97
|
+
// 这是文件服务对直链的回答(签名过期/无权限),与 E10 登录态无关:
|
|
98
|
+
// 报清楚,别让调用方误以为要重新登录。
|
|
99
|
+
throw new E10ApiError('network', 'download_file_service_denied', `文件服务拒绝下载(HTTP ${followed.response.status}),直链可能已过期,请重试下载`, {
|
|
100
|
+
retryable: true,
|
|
101
|
+
exitCode: ExitCode.NetworkError,
|
|
102
|
+
details: { status: followed.response.status },
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
parsed: await this.readResponse(followed.response, request.responseType, request.bigIntStrings, request.maxResponseBytes, request.timeoutMs),
|
|
107
|
+
fromFileRedirect: followed.followed,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* 跟随下载直传的 3xx。仅当跳转目标是文件服务(非登录页)时才跟随,最多
|
|
112
|
+
* `MAX_DOWNLOAD_REDIRECTS` 跳;登录类跳转原样返回,交给上层认证恢复处理。
|
|
113
|
+
* 凭证规则:同源才继续带 Cookie/eteamsid,跨域(对象存储)不带任何鉴权头。
|
|
114
|
+
*/
|
|
115
|
+
async followDownloadRedirect(response, request, requestUrl) {
|
|
116
|
+
const baseOrigin = safeOrigin(this.session.getBaseUrl());
|
|
117
|
+
let current = response;
|
|
118
|
+
let currentUrl = requestUrl;
|
|
119
|
+
let followed = false;
|
|
120
|
+
for (let hop = 0; hop < MAX_DOWNLOAD_REDIRECTS; hop += 1) {
|
|
121
|
+
const kind = classifyE10Redirect({
|
|
122
|
+
status: current.status,
|
|
123
|
+
headers: current.headers,
|
|
124
|
+
requestPath: request.path,
|
|
125
|
+
});
|
|
126
|
+
if (kind !== 'file')
|
|
127
|
+
return { response: current, followed };
|
|
128
|
+
const location = current.headers.get('location');
|
|
129
|
+
if (!location)
|
|
130
|
+
return { response: current, followed };
|
|
131
|
+
let next;
|
|
132
|
+
try {
|
|
133
|
+
next = new URL(location, currentUrl);
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
return { response: current, followed };
|
|
137
|
+
}
|
|
138
|
+
if (!/^https?:$/u.test(next.protocol))
|
|
139
|
+
return { response: current, followed };
|
|
140
|
+
const sameOrigin = baseOrigin !== '' && safeOrigin(next.toString()) === baseOrigin;
|
|
141
|
+
current = await this.fetchWithTimeout(next, this.buildDownloadRedirectInit(sameOrigin), request.timeoutMs);
|
|
142
|
+
currentUrl = next;
|
|
143
|
+
followed = true;
|
|
144
|
+
}
|
|
145
|
+
return { response: current, followed };
|
|
146
|
+
}
|
|
147
|
+
buildDownloadRedirectInit(sameOrigin) {
|
|
148
|
+
const headers = {
|
|
149
|
+
Accept: 'application/octet-stream, */*',
|
|
150
|
+
'User-Agent': this.session.uaHeader(),
|
|
151
|
+
};
|
|
152
|
+
if (sameOrigin) {
|
|
153
|
+
headers.Cookie = this.attachSessionTicket
|
|
154
|
+
? this.session.cookieHeaderWithSessionTicket()
|
|
155
|
+
: this.session.cookieHeader();
|
|
156
|
+
headers.eteamsid = this.session.eteamsId;
|
|
157
|
+
}
|
|
158
|
+
return { method: 'GET', headers, redirect: 'manual' };
|
|
88
159
|
}
|
|
89
160
|
async recoverExpiredSession(requestEteamsId) {
|
|
90
161
|
// Another concurrent request may already have refreshed the shared session
|
|
@@ -276,6 +347,31 @@ export class E10RequestRuntime {
|
|
|
276
347
|
export function parseJsonPreservingBigInts(text) {
|
|
277
348
|
return JSON.parse(quoteLargeJsonIntegers(text));
|
|
278
349
|
}
|
|
350
|
+
/**
|
|
351
|
+
* 组装认证探测入参。二进制响应不能把文件字节交给关键词扫描(乱码里出现
|
|
352
|
+
* 「login」会误判成登录失效),只保留状态码与跳转目标;同时带上请求路径,
|
|
353
|
+
* 让「下载接口 -> 文件服务」的 3xx 能被识别为交接而不是失效。
|
|
354
|
+
*/
|
|
355
|
+
function authenticationProbe(request, response) {
|
|
356
|
+
if ('content' in response) {
|
|
357
|
+
return { status: response.status, headers: response.headers, requestPath: request.path };
|
|
358
|
+
}
|
|
359
|
+
return {
|
|
360
|
+
status: response.status,
|
|
361
|
+
headers: response.headers,
|
|
362
|
+
requestPath: request.path,
|
|
363
|
+
text: response.text,
|
|
364
|
+
value: response.value,
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
function safeOrigin(rawUrl) {
|
|
368
|
+
try {
|
|
369
|
+
return new URL(rawUrl).origin.toLowerCase();
|
|
370
|
+
}
|
|
371
|
+
catch {
|
|
372
|
+
return '';
|
|
373
|
+
}
|
|
374
|
+
}
|
|
279
375
|
const LONG_INTEGER_MIN_DIGITS = 16;
|
|
280
376
|
// 一趟扫描把 JSON 拆成「字符串字面量」与「数字」两类 token:字符串整体跳过,
|
|
281
377
|
// 数字只在它是纯整数且位数够长时补引号。逐字符拼串在 MB 级响应上要 100 ms 以上,
|
|
@@ -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 { ArchiveCliError, archiveAuthError, archiveNetworkError, archiveValidationError, } from './errors.js';
|
|
6
7
|
function inferProfile(ctx) {
|
|
7
8
|
const options = ctx.getGlobalOptions();
|
|
@@ -151,7 +152,7 @@ export function createArchiveHost(ctx, dependencies = {}) {
|
|
|
151
152
|
}
|
|
152
153
|
async function request(request) {
|
|
153
154
|
const response = await doRequest(request, false).catch((error) => mapRuntimeNetworkError(error));
|
|
154
|
-
if (response
|
|
155
|
+
if (isE10SessionExpiredResponse(response)) {
|
|
155
156
|
throw archiveAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
|
|
156
157
|
}
|
|
157
158
|
if (response.parseError) {
|
|
@@ -182,7 +183,7 @@ export function createArchiveHost(ctx, dependencies = {}) {
|
|
|
182
183
|
}
|
|
183
184
|
async function requestBinary(request) {
|
|
184
185
|
const response = await doRequest(request, true).catch((error) => mapRuntimeNetworkError(error));
|
|
185
|
-
if (response
|
|
186
|
+
if (isE10SessionExpiredResponse(response)) {
|
|
186
187
|
throw archiveAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
|
|
187
188
|
}
|
|
188
189
|
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 { ArchiveOaCliError, archiveOaAuthError, archiveOaNetworkError, archiveOaValidationError, } from './errors.js';
|
|
6
7
|
/** 本模块允许调用的路径前缀:ebuilder 业务接口与 OpenESB 开放服务。 */
|
|
7
8
|
const ALLOWED_PATH_PREFIXES = ['/api/', '/openserver/'];
|
|
@@ -122,7 +123,7 @@ export function createArchiveOaHost(ctx, dependencies = {}) {
|
|
|
122
123
|
headers: { 'X-Requested-With': 'XMLHttpRequest', ...extraHeaders },
|
|
123
124
|
timeoutMs,
|
|
124
125
|
}).catch((error) => mapRuntimeNetworkError(error));
|
|
125
|
-
if (response
|
|
126
|
+
if (isE10SessionExpiredResponse(response)) {
|
|
126
127
|
throw archiveOaAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
|
|
127
128
|
}
|
|
128
129
|
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 { createE10RequestRuntime } from '../../internal/e10/request-runtime.js';
|
|
5
|
+
import { isE10SessionExpiredResponse } from '../../internal/e10/auth-recovery.js';
|
|
5
6
|
import { ziguanjiaAuthError, ziguanjiaBusinessError, ziguanjiaNetworkError, ziguanjiaValidationError, } from './errors.js';
|
|
6
7
|
const EBUILDER_PREFIX = process.env.WEAVER_EBUILDER_PREFIX || '/api/ebuilder';
|
|
7
8
|
const ESB_TRIGGER_PATH = '/api/esb/server/event/triggerActionFlow';
|
|
@@ -176,7 +177,7 @@ export function createZiguanjiaHost(ctx) {
|
|
|
176
177
|
query,
|
|
177
178
|
bigIntStrings: true,
|
|
178
179
|
});
|
|
179
|
-
if (response
|
|
180
|
+
if (isE10SessionExpiredResponse(response)) {
|
|
180
181
|
throw ziguanjiaAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
|
|
181
182
|
}
|
|
182
183
|
if (response.parseError) {
|
|
@@ -242,7 +243,7 @@ export function createZiguanjiaHost(ctx) {
|
|
|
242
243
|
formData: fd,
|
|
243
244
|
bigIntStrings: false,
|
|
244
245
|
});
|
|
245
|
-
if (response
|
|
246
|
+
if (isE10SessionExpiredResponse(response)) {
|
|
246
247
|
throw ziguanjiaAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
|
|
247
248
|
}
|
|
248
249
|
if (response.parseError) {
|
|
@@ -315,7 +316,7 @@ export function createZiguanjiaHost(ctx) {
|
|
|
315
316
|
},
|
|
316
317
|
bigIntStrings: true,
|
|
317
318
|
});
|
|
318
|
-
if (response
|
|
319
|
+
if (isE10SessionExpiredResponse(response)) {
|
|
319
320
|
throw ziguanjiaAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
|
|
320
321
|
}
|
|
321
322
|
if (response.parseError) {
|
|
@@ -363,7 +364,7 @@ export function createZiguanjiaHost(ctx) {
|
|
|
363
364
|
body: fullBody,
|
|
364
365
|
bigIntStrings: true,
|
|
365
366
|
});
|
|
366
|
-
if (response
|
|
367
|
+
if (isE10SessionExpiredResponse(response)) {
|
|
367
368
|
throw ziguanjiaAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
|
|
368
369
|
}
|
|
369
370
|
if (response.parseError) {
|
|
@@ -393,7 +394,7 @@ export function createZiguanjiaHost(ctx) {
|
|
|
393
394
|
body: payload,
|
|
394
395
|
bigIntStrings: true,
|
|
395
396
|
});
|
|
396
|
-
if (response
|
|
397
|
+
if (isE10SessionExpiredResponse(response)) {
|
|
397
398
|
throw ziguanjiaAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
|
|
398
399
|
}
|
|
399
400
|
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 { createE10RequestRuntime } from '../../internal/e10/request-runtime.js';
|
|
6
|
+
import { isE10SessionExpiredResponse } from '../../internal/e10/auth-recovery.js';
|
|
6
7
|
import { BlogCliError, blogAuthError, blogNetworkError, blogValidationError, } from './errors.js';
|
|
7
8
|
const MAX_EXPORT_BYTES = 50 * 1024 * 1024;
|
|
8
9
|
/** E10 登录失效在业务响应体里的 code(实测 -2 既可能是数字也可能是字符串) */
|
|
@@ -91,7 +92,7 @@ export function createBlogHost(ctx, dependencies = {}) {
|
|
|
91
92
|
body,
|
|
92
93
|
timeoutMs,
|
|
93
94
|
});
|
|
94
|
-
if (response
|
|
95
|
+
if (isE10SessionExpiredResponse(response)) {
|
|
95
96
|
throw blogAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
|
|
96
97
|
}
|
|
97
98
|
if (response.parseError) {
|
|
@@ -134,7 +135,7 @@ export function createBlogHost(ctx, dependencies = {}) {
|
|
|
134
135
|
timeoutMs,
|
|
135
136
|
headers: { Accept: 'application/octet-stream, */*' },
|
|
136
137
|
});
|
|
137
|
-
if (response
|
|
138
|
+
if (isE10SessionExpiredResponse(response)) {
|
|
138
139
|
throw blogAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
|
|
139
140
|
}
|
|
140
141
|
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 { 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,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 { CoworkCliError, coworkAuthError, coworkNetworkError, coworkValidationError } from './errors.js';
|
|
6
7
|
import { coworkPerf } from './operations/types.js';
|
|
7
8
|
function inferProfile(ctx) {
|
|
@@ -147,7 +148,7 @@ export function createCoworkHost(ctx, dependencies = {}) {
|
|
|
147
148
|
coworkPerf.requestCount += 1;
|
|
148
149
|
coworkPerf.requestMs += Date.now() - reqStart;
|
|
149
150
|
}
|
|
150
|
-
if (response
|
|
151
|
+
if (isE10SessionExpiredResponse(response)) {
|
|
151
152
|
throw coworkAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
|
|
152
153
|
}
|
|
153
154
|
if (response.parseError) {
|
|
@@ -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) {
|
|
@@ -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) {
|
|
@@ -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) {
|
|
@@ -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) {
|
|
@@ -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 { mailAuthError, mailNetworkError, MailCliError, mailValidationError } from './errors.js';
|
|
7
8
|
import { mailPerf } from './operations/types.js';
|
|
8
9
|
function inferProfile(ctx) {
|
|
@@ -160,7 +161,7 @@ export function createMailHost(ctx, dependencies = {}) {
|
|
|
160
161
|
mailPerf.requestCount += 1;
|
|
161
162
|
mailPerf.requestMs += Date.now() - reqStart;
|
|
162
163
|
}
|
|
163
|
-
if (response
|
|
164
|
+
if (isE10SessionExpiredResponse(response)) {
|
|
164
165
|
throw mailAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
|
|
165
166
|
}
|
|
166
167
|
if (response.parseError) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { E10_SESSION_EXPIRED_MESSAGE } from '../../internal/e10/auth/messages.js';
|
|
2
|
+
import { isE10SessionExpiredResponse, } from '../../internal/e10/auth-recovery.js';
|
|
2
3
|
import { requireE10Session } from '../../internal/e10/context.js';
|
|
3
4
|
import { getActiveProfile } from '../../internal/e10/auth/session.js';
|
|
4
5
|
import { E10ApiError, createE10RequestRuntime } from '../../internal/e10/request-runtime.js';
|
|
@@ -127,7 +128,7 @@ export function createMeetingHost(ctx, dependencies = {}) {
|
|
|
127
128
|
catch (error) {
|
|
128
129
|
mapRuntimeNetworkError(error);
|
|
129
130
|
}
|
|
130
|
-
if (response
|
|
131
|
+
if (isE10SessionExpiredResponse(response)) {
|
|
131
132
|
throw meetingAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
|
|
132
133
|
}
|
|
133
134
|
if (response.status !== 200) {
|
|
@@ -177,7 +178,7 @@ export function createMeetingHost(ctx, dependencies = {}) {
|
|
|
177
178
|
catch (error) {
|
|
178
179
|
mapRuntimeNetworkError(error);
|
|
179
180
|
}
|
|
180
|
-
if (response
|
|
181
|
+
if (isE10SessionExpiredResponse(response)) {
|
|
181
182
|
throw meetingAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
|
|
182
183
|
}
|
|
183
184
|
if (response.parseError) {
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 邀请函的**对话内文本版**(Markdown),与 docx 版同源同字段。
|
|
3
|
+
*
|
|
4
|
+
* 为什么要有这一份(2026-10-31 用户明确要求):
|
|
5
|
+
* 「邀请函不是在对话里给个文件让我自己下载,而是**直接展示在对话里**」。
|
|
6
|
+
* docx 是给人打印/发出去的成品,但小E 会话里用户看不到 docx 内容 ——
|
|
7
|
+
* 只回一个 filePath,用户还得去本地打开才知道写了什么。
|
|
8
|
+
* 所以渲染 docx 的同时产出一份纯文本,模型可以把正文直接贴进回复。
|
|
9
|
+
*
|
|
10
|
+
* 版式与 `templates/meeting/invitation.docx` 一致:标题 / 称呼 / 邀请语 /
|
|
11
|
+
* 会议要素 / 议程表 / 落款 / 备注。取不到的字段统一写【待补充】,与 docx 的黄色高亮同义。
|
|
12
|
+
*/
|
|
13
|
+
import { PENDING_MARK } from './docx.js';
|
|
14
|
+
/** 单元格里的 `|` 会破坏 Markdown 表格,换行会破坏行结构 —— 统一折成空格。 */
|
|
15
|
+
function cell(value) {
|
|
16
|
+
return String(value ?? '').replace(/\|/g, '/').replace(/\s*\r?\n\s*/g, ' ').trim();
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* 生成邀请函的对话文本版。
|
|
20
|
+
*
|
|
21
|
+
* @param resolver 与 docx 共用的占位符解析器(保证两版内容一字不差)
|
|
22
|
+
* @param agendaRows 议程行(来自会议议题 / extra.agenda / 模板示例行)
|
|
23
|
+
* @param options.pendingHint 是否在末尾附「【待补充】需人工补」的提示(默认附)
|
|
24
|
+
*/
|
|
25
|
+
export function renderInvitationMarkdown(resolver, agendaRows, options = {}) {
|
|
26
|
+
const pending = new Set();
|
|
27
|
+
const val = (key) => {
|
|
28
|
+
const rendered = resolver(key);
|
|
29
|
+
if (rendered.pending) {
|
|
30
|
+
pending.add(key);
|
|
31
|
+
return PENDING_MARK;
|
|
32
|
+
}
|
|
33
|
+
return String(rendered.text ?? '').trim();
|
|
34
|
+
};
|
|
35
|
+
const lines = [];
|
|
36
|
+
lines.push('# 邀请函');
|
|
37
|
+
lines.push('');
|
|
38
|
+
lines.push(`**尊敬的 ${val('guest_org')}:**`);
|
|
39
|
+
lines.push('');
|
|
40
|
+
lines.push(`${val('caller')} 诚邀您参加「${val('mt_name')}」,现将会议安排通知如下:`);
|
|
41
|
+
lines.push('');
|
|
42
|
+
lines.push(`- **会议名称**:${val('mt_name')}`);
|
|
43
|
+
lines.push(`- **会议时间**:${val('begin_datetime')}~${val('end_datetime')}`);
|
|
44
|
+
lines.push(`- **会议地点**:${val('address')}`);
|
|
45
|
+
lines.push(`- **参会人员**:${val('hrm_members')}`);
|
|
46
|
+
lines.push(`- **会议内容**:${val('mt_desc')}`);
|
|
47
|
+
lines.push('');
|
|
48
|
+
lines.push(`**${val('begin_date')}会议议程安排:**`);
|
|
49
|
+
lines.push('');
|
|
50
|
+
lines.push('| 议程安排 | 议程内容 | 议程时间 |');
|
|
51
|
+
lines.push('| --- | --- | --- |');
|
|
52
|
+
if (agendaRows.length === 0) {
|
|
53
|
+
lines.push('| — | 暂无议程 | — |');
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
for (const [name, content, timeRange] of agendaRows) {
|
|
57
|
+
lines.push(`| ${cell(name) || '—'} | ${cell(content) || '—'} | ${cell(timeRange) || '—'} |`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
lines.push('');
|
|
61
|
+
lines.push(val('company_name'));
|
|
62
|
+
lines.push('');
|
|
63
|
+
lines.push(val('issue_date'));
|
|
64
|
+
lines.push('');
|
|
65
|
+
lines.push('> 注:以上为拟定会议安排,具体会议时间与议程需根据实际情况确认。');
|
|
66
|
+
if (options.pendingHint !== false && pending.size > 0) {
|
|
67
|
+
lines.push('');
|
|
68
|
+
lines.push(`> ⚠️ 标 ${PENDING_MARK} 的字段会议里没有值,需要人工补;补完可用 mtId 重新生成。`);
|
|
69
|
+
}
|
|
70
|
+
return { markdown: lines.join('\n'), pendingFields: [...pending] };
|
|
71
|
+
}
|