oasis_test_v2 2.2.110 → 2.2.111

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +193 -81
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -19978,14 +19978,27 @@ async function sendFeishuBotTextToUser(input) {
19978
19978
  const body2 = await resp.json();
19979
19979
  if (body2.code !== 0) throw new Error(`feishu bot \u79C1\u4FE1\u5931\u8D25: code=${String(body2.code)} ${body2.msg ?? ""}`.trim());
19980
19980
  }
19981
+ async function lookupFeishuOpenIdByEmail(input) {
19982
+ const token2 = await (input.fetchTenantToken ?? fetchFeishuTenantAccessToken)(input.appId, input.appSecret);
19983
+ if (!token2) throw new Error("\u6362\u4F01\u4E1A token \u5931\u8D25\uFF0C\u672A\u80FD\u6309\u90AE\u7BB1\u67E5\u98DE\u4E66\u7528\u6237");
19984
+ const resp = await (input.fetchImpl ?? fetch)(`${FEISHU_BATCH_GET_ID_URL}?user_id_type=open_id`, {
19985
+ method: "POST",
19986
+ headers: { "content-type": "application/json; charset=utf-8", authorization: `Bearer ${token2}` },
19987
+ body: JSON.stringify({ emails: [input.email], include_resigned: false })
19988
+ });
19989
+ const body2 = await resp.json();
19990
+ if (body2.code !== 0) throw new Error(`feishu \u6309\u90AE\u7BB1\u67E5\u7528\u6237\u5931\u8D25: code=${String(body2.code)} ${body2.msg ?? ""}`.trim());
19991
+ const hit = body2.data?.user_list?.find((u) => u.email?.toLowerCase() === input.email.toLowerCase() && u.user_id);
19992
+ return hit?.user_id ?? null;
19993
+ }
19981
19994
  function feishuUserAuthInvalidText(input) {
19982
19995
  const head = `\u300C${input.agentName}\u300D\u7684\u98DE\u4E66\u7528\u6237\u6388\u6743\u5DF2\u5931\u6548\uFF08\u6388\u6743\u88AB\u64A4\u9500\u6216\u957F\u65F6\u95F4\u65E0\u6CD5\u7EED\u671F\uFF09\uFF0C\u4EE5\u4F60\u8EAB\u4EFD\u8C03\u7528\u98DE\u4E66\u7684\u529F\u80FD\u6682\u65F6\u7528\u4E0D\u4E86\uFF0Cbot \u8EAB\u4EFD\u4E0D\u53D7\u5F71\u54CD\u3002`;
19983
19996
  return input.link ? `${head}
19984
- \u70B9\u8FD9\u4E2A\u94FE\u63A5\u91CD\u65B0\u6388\u6743\uFF08\u9700\u7528\u539F\u6388\u6743\u4EBA\u7684\u98DE\u4E66\u8D26\u53F7\u767B\u5F55\uFF0C7 \u5929\u5185\u6709\u6548\uFF09\uFF1A
19997
+ \u70B9\u8FD9\u4E2A\u94FE\u63A5\u91CD\u65B0\u6388\u6743\uFF08\u9700\u7528\u6536\u5230\u8FD9\u6761\u79C1\u4FE1\u7684\u98DE\u4E66\u8D26\u53F7\u767B\u5F55\uFF0C7 \u5929\u5185\u6709\u6548\uFF09\uFF1A
19985
19998
  ${input.link}` : `${head}
19986
19999
  \u8BF7\u5230 Oasis \u8BE5 Agent \u7684\u300C\u8FDE\u63A5\u5668\u300D\u9875\u91CD\u65B0\u6388\u6743\u98DE\u4E66\u3002`;
19987
20000
  }
19988
- var FEISHU_DEVICE_AUTHORIZATION_URL, FEISHU_USER_INFO_URL, FEISHU_SEND_MESSAGE_URL;
20001
+ var FEISHU_DEVICE_AUTHORIZATION_URL, FEISHU_USER_INFO_URL, FEISHU_SEND_MESSAGE_URL, FEISHU_BATCH_GET_ID_URL;
19989
20002
  var init_user_auth = __esm({
19990
20003
  "../connectors/src/feishu/user-auth.ts"() {
19991
20004
  "use strict";
@@ -19993,6 +20006,7 @@ var init_user_auth = __esm({
19993
20006
  FEISHU_DEVICE_AUTHORIZATION_URL = "https://accounts.feishu.cn/oauth/v1/device_authorization";
19994
20007
  FEISHU_USER_INFO_URL = "https://open.feishu.cn/open-apis/authen/v1/user_info";
19995
20008
  FEISHU_SEND_MESSAGE_URL = "https://open.feishu.cn/open-apis/im/v1/messages";
20009
+ FEISHU_BATCH_GET_ID_URL = "https://open.feishu.cn/open-apis/contact/v3/users/batch_get_id";
19996
20010
  }
19997
20011
  });
19998
20012
 
@@ -20569,6 +20583,16 @@ async function persistFeishuUserAuthorization(store, actorId, data, nowMs, who =
20569
20583
  await put3(FEISHU_AUTHORIZED_BY_VAR, who.authorizedBy);
20570
20584
  await store.deleteVariable(FEISHU_USER_AUTH_INVALID_VAR, actorId);
20571
20585
  }
20586
+ async function readFeishuUserAuthInvalid(store, actorId) {
20587
+ const raw = await store.revealVariable(FEISHU_USER_AUTH_INVALID_VAR, actorId).catch(() => null);
20588
+ if (!raw) return null;
20589
+ try {
20590
+ const m2 = JSON.parse(raw);
20591
+ return typeof m2?.at === "string" ? m2 : null;
20592
+ } catch {
20593
+ return null;
20594
+ }
20595
+ }
20572
20596
  async function markFeishuUserAuthInvalid(store, actorId, marker) {
20573
20597
  await store.putVariable({
20574
20598
  key: FEISHU_USER_AUTH_INVALID_VAR,
@@ -23421,6 +23445,7 @@ var init_scheduled_refresh = __esm({
23421
23445
  counts2[outcome.status] = (counts2[outcome.status] ?? 0) + 1;
23422
23446
  }
23423
23447
  }
23448
+ await this.deps.afterCompany?.(companyId, store).catch((error2) => this.deps.warn(new Error(`\u5B9A\u65F6\u7EED\u671F\u6536\u5C3E company=${companyId}: ${String(error2)}`)));
23424
23449
  } catch (error2) {
23425
23450
  this.deps.warn(new Error(`\u5B9A\u65F6\u7EED\u671F company=${companyId}: ${String(error2)}`));
23426
23451
  }
@@ -24524,7 +24549,7 @@ function feishuReauthUrl(apiBase, token2) {
24524
24549
  return `${apiBase.replace(/\/+$/, "")}/api/connectors/feishu/reauth?t=${encodeURIComponent(token2)}`;
24525
24550
  }
24526
24551
  async function issueFeishuReauthToken(links, store, claims) {
24527
- const openId = await store.revealVariable(FEISHU_USER_OPEN_ID_VAR, claims.actorId).catch(() => null);
24552
+ const openId = await store.revealVariable(FEISHU_USER_OPEN_ID_VAR, claims.actorId).catch(() => null) || claims.fallbackSubject;
24528
24553
  if (!openId) return null;
24529
24554
  return links.issue({
24530
24555
  companyId: claims.companyId,
@@ -218072,6 +218097,12 @@ ${composed}`;
218072
218097
  res.writeHead(400).end(JSON.stringify({ error: "\u8BF7\u81F3\u5C11\u9009\u62E9\u4E00\u4E2A\u529F\u80FD\u6A21\u5757" }));
218073
218098
  return;
218074
218099
  }
218100
+ const devicePending = opts.connectorAuthPending?.githubApp;
218101
+ if (!devicePending) {
218102
+ console.error("[feishu-oauth] connectorAuthPending store unavailable \u2014 refusing to start device auth");
218103
+ res.writeHead(503).end(JSON.stringify({ error: "\u6388\u6743\u767B\u8BB0\u670D\u52A1\u6682\u4E0D\u53EF\u7528\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5" }));
218104
+ return;
218105
+ }
218075
218106
  try {
218076
218107
  const startSvc = await connectorActorsService();
218077
218108
  const appSecret = startSvc ? await startSvc.revealResolvedVariable("FEISHU_APP_SECRET", actorId) : null;
@@ -218095,26 +218126,21 @@ ${composed}`;
218095
218126
  scopes: scopesForSelection(selection, sendAsUserRequested)
218096
218127
  });
218097
218128
  if (!deviceCode) throw new Error("no device_code in response");
218098
- const devicePending = opts.connectorAuthPending?.githubApp;
218099
- if (devicePending) {
218100
- const now = Date.now();
218101
- await devicePending.put({
218102
- state: feishuDevicePendingKey(deviceCode),
218103
- ...currentCompanyId ? { companyId: currentCompanyId } : {},
218104
- value: {
218105
- connector: "feishu",
218106
- appId,
218107
- actorId: actorId ?? "",
218108
- modules: selection.modules,
218109
- sendAsUser: selection.sendAsUser,
218110
- sendAsUserRequested
218111
- },
218112
- createdAtMs: now,
218113
- expiresAtMs: now + FEISHU_DEVICE_PENDING_TTL_MS
218114
- });
218115
- } else {
218116
- console.error("[feishu-oauth] connectorAuthPending store unavailable \u2014 auth selection will not be saved");
218117
- }
218129
+ const now = Date.now();
218130
+ await devicePending.put({
218131
+ state: feishuDevicePendingKey(deviceCode),
218132
+ ...currentCompanyId ? { companyId: currentCompanyId } : {},
218133
+ value: {
218134
+ connector: "feishu",
218135
+ appId,
218136
+ actorId: actorId ?? "",
218137
+ modules: selection.modules,
218138
+ sendAsUser: selection.sendAsUser,
218139
+ sendAsUserRequested
218140
+ },
218141
+ createdAtMs: now,
218142
+ expiresAtMs: now + FEISHU_DEVICE_PENDING_TTL_MS
218143
+ });
218118
218144
  res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({
218119
218145
  verificationUrl,
218120
218146
  deviceCode,
@@ -218137,18 +218163,22 @@ ${composed}`;
218137
218163
  try {
218138
218164
  const devicePending = opts.connectorAuthPending?.githubApp;
218139
218165
  const started = devicePending ? await devicePending.get(feishuDevicePendingKey(deviceCode), Date.now()) : { status: "missing" };
218140
- let startedSelection = null;
218141
- if (started.status === "found") {
218142
- const v2 = started.value.value;
218143
- if (v2["appId"] !== appId || v2["actorId"] !== (actorId ?? "") || (started.value.companyId ?? "") !== (currentCompanyId ?? "")) {
218144
- res.writeHead(400).end(JSON.stringify({ error: "\u8FD9\u6B21\u6388\u6743\u4E0D\u662F\u7531\u5F53\u524D\u5E94\u7528\u6216\u5458\u5DE5\u53D1\u8D77\u7684\uFF0C\u8BF7\u91CD\u65B0\u53D1\u8D77\u6388\u6743" }));
218145
- return;
218146
- }
218147
- const selection = normalizeFeishuAuthSelection({ modules: v2["modules"], sendAsUser: v2["sendAsUser"] });
218148
- if (selection) {
218149
- startedSelection = { selection, sendAsUserRequested: selection.sendAsUser && v2["sendAsUserRequested"] === true };
218150
- }
218166
+ if (started.status !== "found") {
218167
+ console.error(`[feishu-oauth] no login/start registration for this device code (${started.status}) \u2014 refusing token exchange`);
218168
+ res.writeHead(409).end(JSON.stringify({ ok: false, restart: true, error: "\u8FD9\u6B21\u6388\u6743\u7684\u767B\u8BB0\u5DF2\u5931\u6548\uFF08\u8D85\u65F6\u6216\u670D\u52A1\u5DF2\u66F4\u65B0\uFF09\uFF0C\u8BF7\u91CD\u65B0\u53D1\u8D77\u6388\u6743" }));
218169
+ return;
218151
218170
  }
218171
+ const v2 = started.value.value;
218172
+ if (v2["appId"] !== appId || v2["actorId"] !== (actorId ?? "") || (started.value.companyId ?? "") !== (currentCompanyId ?? "")) {
218173
+ res.writeHead(400).end(JSON.stringify({ ok: false, restart: true, error: "\u8FD9\u6B21\u6388\u6743\u4E0D\u662F\u7531\u5F53\u524D\u5E94\u7528\u6216\u5458\u5DE5\u53D1\u8D77\u7684\uFF0C\u8BF7\u91CD\u65B0\u53D1\u8D77\u6388\u6743" }));
218174
+ return;
218175
+ }
218176
+ const selection = normalizeFeishuAuthSelection({ modules: v2["modules"], sendAsUser: v2["sendAsUser"] });
218177
+ if (!selection) {
218178
+ res.writeHead(409).end(JSON.stringify({ ok: false, restart: true, error: "\u8FD9\u6B21\u6388\u6743\u7684\u767B\u8BB0\u5DF2\u635F\u574F\uFF0C\u8BF7\u91CD\u65B0\u53D1\u8D77\u6388\u6743" }));
218179
+ return;
218180
+ }
218181
+ const startedSelection = { selection, sendAsUserRequested: selection.sendAsUser && v2["sendAsUserRequested"] === true };
218152
218182
  const pollSvc = await connectorActorsService();
218153
218183
  const appSecret = pollSvc ? await pollSvc.revealResolvedVariable("FEISHU_APP_SECRET", actorId) : null;
218154
218184
  if (!appSecret) throw new Error("FEISHU_APP_SECRET not found \u2014 please complete app setup first");
@@ -218174,18 +218204,14 @@ ${composed}`;
218174
218204
  ...isHumanActorId(actor) ? { authorizedBy: actor } : {}
218175
218205
  }));
218176
218206
  const nameScope = actorId ? { scope: "personal", actorId } : { scope: "global" };
218177
- if (startedSelection) {
218178
- const record10 = feishuAuthSelectionRecord(startedSelection.selection, startedSelection.sendAsUserRequested, data);
218179
- await svc.putVariable({
218180
- key: FEISHU_AUTH_SELECTION_VAR,
218181
- value: JSON.stringify(record10),
218182
- ...nameScope,
218183
- connectorId: "feishu",
218184
- encrypted: false
218185
- });
218186
- } else {
218187
- console.error(`[feishu-oauth] no login/start registration for this device code (${started.status}) \u2014 auth selection not saved`);
218188
- }
218207
+ const record10 = feishuAuthSelectionRecord(startedSelection.selection, startedSelection.sendAsUserRequested, data);
218208
+ await svc.putVariable({
218209
+ key: FEISHU_AUTH_SELECTION_VAR,
218210
+ value: JSON.stringify(record10),
218211
+ ...nameScope,
218212
+ connectorId: "feishu",
218213
+ encrypted: false
218214
+ });
218189
218215
  if (!who.openId) console.error("[feishu-oauth] user_info \u672A\u53D6\u5230\u6388\u6743\u4EBA open_id\uFF1A\u6388\u6743\u5931\u6548\u65F6\u65E0\u6CD5\u79C1\u4FE1\u672C\u4EBA\u3001\u4E5F\u7B7E\u4E0D\u51FA\u4E00\u952E\u91CD\u65B0\u6388\u6743\u94FE\u63A5");
218190
218216
  try {
218191
218217
  const secret = await svc.revealResolvedVariable("FEISHU_APP_SECRET", actorId);
@@ -286671,6 +286697,7 @@ function createTodoRuntime(options) {
286671
286697
  init_src();
286672
286698
  init_src3();
286673
286699
  init_src11();
286700
+ var MAX_NOTICE_ATTEMPTS = 36;
286674
286701
  function createConnectorRenewalRuntime(options) {
286675
286702
  const now = () => options.now?.() ?? Date.now();
286676
286703
  const agentName = async (companyId, actorId) => {
@@ -286679,13 +286706,17 @@ function createConnectorRenewalRuntime(options) {
286679
286706
  const hit = (await svc?.listActors().catch(() => []))?.find((a) => a.id === actorId);
286680
286707
  return hit?.name || actorId;
286681
286708
  };
286682
- const notifyFeishuUser = async (store, actorId, text7) => {
286683
- const openId = await store.revealVariable(FEISHU_USER_OPEN_ID_VAR, actorId);
286684
- if (!openId) return "no-open-id";
286709
+ const appCredentials = async (store, actorId) => {
286685
286710
  const appId = await store.revealResolvedVariable("FEISHU_APP_ID", actorId);
286686
286711
  const appSecret = await store.revealResolvedVariable("FEISHU_APP_SECRET", actorId);
286687
- if (!appId || !appSecret) return "no-app";
286688
- await sendFeishuBotTextToUser({ appId, appSecret, openId, text: text7 });
286712
+ return appId && appSecret ? { appId, appSecret } : null;
286713
+ };
286714
+ const notifyFeishuUser = async (store, actorId, text7, openId) => {
286715
+ const to = openId || await store.revealVariable(FEISHU_USER_OPEN_ID_VAR, actorId);
286716
+ if (!to) return "no-open-id";
286717
+ const app = await appCredentials(store, actorId);
286718
+ if (!app) return "no-app";
286719
+ await sendFeishuBotTextToUser({ ...app, openId: to, text: text7 });
286689
286720
  return "sent";
286690
286721
  };
286691
286722
  const recipientsFor = async (companyId, store, actorId) => {
@@ -286701,36 +286732,110 @@ function createConnectorRenewalRuntime(options) {
286701
286732
  }
286702
286733
  return [...out];
286703
286734
  };
286704
- const onFeishuInvalidated = async (e, store) => {
286705
- const at2 = new Date(now()).toISOString();
286706
- await markFeishuUserAuthInvalid(store, e.actorId, { at: at2, reason: e.outcome.detail ?? "\u7EED\u671F\u6750\u6599\u5DF2\u5931\u6548" });
286707
- const name = await agentName(e.companyId, e.actorId);
286708
- const token2 = await issueFeishuReauthToken(options.links, store, { companyId: e.companyId, actorId: e.actorId, purpose: "reauth" });
286709
- const link = token2 && options.apiBase ? feishuReauthUrl(options.apiBase, token2) : void 0;
286710
- const ledger = await options.notifications(e.companyId);
286711
- if (ledger) {
286712
- const fresh = [];
286713
- for (const recipient of await recipientsFor(e.companyId, store, e.actorId)) {
286714
- const row = await ledger.insert({
286715
- recipient,
286716
- category: "connector",
286717
- kind: "feishu-auth-invalid",
286718
- // 每次失效一条:键里带失效时刻,重新授权后再失效还会再提醒;同一次失效重复扫描不重发(唯一约束)。
286719
- sourceKey: `connector:feishu:${e.actorId ?? "org"}@invalid:${at2}`,
286720
- title: `\u300C${name}\u300D\u7684\u98DE\u4E66\u7528\u6237\u6388\u6743\u5DF2\u5931\u6548\uFF0C\u9700\u8981\u91CD\u65B0\u6388\u6743`,
286721
- ...e.actorId ? { actorId: e.actorId } : {},
286722
- enteredAt: at2
286723
- });
286724
- if (row.enteredAt === at2) fresh.push(row);
286735
+ const recoverOpenId = async (companyId, app, recipients) => {
286736
+ for (const human of recipients) {
286737
+ const email2 = await options.memberEmail?.(human, companyId).catch(() => null);
286738
+ if (!email2) continue;
286739
+ const openId = await lookupFeishuOpenIdByEmail({ ...app, email: email2 });
286740
+ if (openId) return openId;
286741
+ }
286742
+ return null;
286743
+ };
286744
+ const deliverInvalidNotice = async (companyId, store, actorId, marker) => {
286745
+ const prev = marker.notice ?? { inApp: false, dm: "pending", attempts: 0 };
286746
+ const state = { ...prev, attempts: prev.attempts + 1 };
286747
+ const who = `actor=${actorId ?? "-"} \u7B2C ${state.attempts}/${MAX_NOTICE_ATTEMPTS} \u6B21`;
286748
+ const name = await agentName(companyId, actorId);
286749
+ const recipients = await recipientsFor(companyId, store, actorId);
286750
+ if (!state.inApp) {
286751
+ try {
286752
+ const ledger = await options.notifications(companyId);
286753
+ if (ledger) {
286754
+ const rows = [];
286755
+ for (const recipient of recipients) {
286756
+ rows.push(await ledger.insert({
286757
+ recipient,
286758
+ category: "connector",
286759
+ kind: "feishu-auth-invalid",
286760
+ // 每次失效一条:键里带失效时刻,重新授权后再失效还会再提醒;同一次失效补发 / 重复扫描不重发(唯一约束)。
286761
+ sourceKey: `connector:feishu:${actorId ?? "org"}@invalid:${marker.at}`,
286762
+ title: `\u300C${name}\u300D\u7684\u98DE\u4E66\u7528\u6237\u6388\u6743\u5DF2\u5931\u6548\uFF0C\u9700\u8981\u91CD\u65B0\u6388\u6743`,
286763
+ ...actorId ? { actorId } : {},
286764
+ enteredAt: marker.at
286765
+ }));
286766
+ }
286767
+ if (rows.length > 0) options.onNotifications?.(companyId, rows);
286768
+ }
286769
+ if (recipients.length === 0) console.warn(`[credential-refresh] \u98DE\u4E66\u6388\u6743\u5931\u6548\u7AD9\u5185\u901A\u77E5\u6CA1\u6709\u6536\u4EF6\u4EBA actor=${actorId ?? "-"}`);
286770
+ state.inApp = true;
286771
+ } catch (err) {
286772
+ options.warn(new Error(`\u98DE\u4E66\u6388\u6743\u5931\u6548\u7AD9\u5185\u901A\u77E5\u672A\u5199\u5165\uFF08${who}\uFF0C\u4E0B\u4E00\u62CD\u91CD\u8BD5\uFF09: ${String(err)}`));
286725
286773
  }
286726
- if (fresh.length > 0) options.onNotifications?.(e.companyId, fresh);
286727
286774
  }
286728
- try {
286729
- const sent = await notifyFeishuUser(store, e.actorId, feishuUserAuthInvalidText({ agentName: name, ...link ? { link } : {} }));
286730
- if (sent !== "sent") console.warn(`[credential-refresh] \u98DE\u4E66\u6388\u6743\u5931\u6548\u79C1\u4FE1\u672A\u53D1\uFF08${sent}\uFF09actor=${e.actorId ?? "-"}`);
286731
- } catch (err) {
286732
- options.warn(new Error(`\u98DE\u4E66\u6388\u6743\u5931\u6548\u79C1\u4FE1\u53D1\u9001\u5931\u8D25 actor=${e.actorId ?? "-"}: ${String(err)}`));
286775
+ if (state.dm === "pending") {
286776
+ try {
286777
+ const app = await appCredentials(store, actorId);
286778
+ const stored = await store.revealVariable(FEISHU_USER_OPEN_ID_VAR, actorId);
286779
+ const recovered = !stored && app ? await recoverOpenId(companyId, app, recipients) : null;
286780
+ const openId = stored || recovered;
286781
+ if (!app || !openId) {
286782
+ state.dm = "unavailable";
286783
+ console.warn(`[credential-refresh] \u98DE\u4E66\u6388\u6743\u5931\u6548\u79C1\u4FE1\u65E0\u4ECE\u53D1\u51FA\uFF08${app ? "\u6CA1\u8BB0\u6388\u6743\u4EBA\u3001\u901A\u8BAF\u5F55\u4E5F\u6CA1\u6309\u6210\u5458\u90AE\u7BB1\u627E\u5230\u672C\u4EBA" : "\u6CA1\u6709\u5E94\u7528\u51ED\u636E"}\uFF09actor=${actorId ?? "-"}\uFF1B\u53EA\u6709\u7AD9\u5185\u901A\u77E5`);
286784
+ } else {
286785
+ const token2 = await issueFeishuReauthToken(options.links, store, {
286786
+ companyId,
286787
+ actorId,
286788
+ purpose: "reauth",
286789
+ ...recovered ? { fallbackSubject: recovered } : {}
286790
+ });
286791
+ const link = token2 && options.apiBase ? feishuReauthUrl(options.apiBase, token2) : void 0;
286792
+ await notifyFeishuUser(store, actorId, feishuUserAuthInvalidText({ agentName: name, ...link ? { link } : {} }), openId);
286793
+ state.dm = "sent";
286794
+ }
286795
+ } catch (err) {
286796
+ options.warn(new Error(`\u98DE\u4E66\u6388\u6743\u5931\u6548\u79C1\u4FE1\u53D1\u9001\u5931\u8D25\uFF08${who}\uFF0C\u4E0B\u4E00\u62CD\u91CD\u8BD5\uFF09: ${String(err)}`));
286797
+ }
286798
+ }
286799
+ if ((!state.inApp || state.dm === "pending") && state.attempts >= MAX_NOTICE_ATTEMPTS) {
286800
+ options.warn(new Error(`\u98DE\u4E66\u6388\u6743\u5931\u6548\u63D0\u9192\u8865\u53D1 ${state.attempts} \u6B21\u4ECD\u672A\u9001\u5230\uFF0C\u653E\u5F03 actor=${actorId ?? "-"} \u7AD9\u5185=${String(state.inApp)} \u79C1\u4FE1=${state.dm}`));
286801
+ }
286802
+ return state;
286803
+ };
286804
+ const saveNoticeState = async (store, actorId, at2, notice) => {
286805
+ await withConnectorCredentialLock("feishu", actorId, async () => {
286806
+ const current = await readFeishuUserAuthInvalid(store, actorId);
286807
+ if (current?.at !== at2) return;
286808
+ await markFeishuUserAuthInvalid(store, actorId, { ...current, notice });
286809
+ });
286810
+ };
286811
+ const handledThisSweep = /* @__PURE__ */ new Set();
286812
+ const sweepKey = (companyId, actorId) => JSON.stringify([companyId, actorId ?? ""]);
286813
+ const onFeishuInvalidated = async (e, store) => {
286814
+ const marker = {
286815
+ at: new Date(now()).toISOString(),
286816
+ reason: e.outcome.detail ?? "\u7EED\u671F\u6750\u6599\u5DF2\u5931\u6548",
286817
+ notice: { inApp: false, dm: "pending", attempts: 0 }
286818
+ };
286819
+ await markFeishuUserAuthInvalid(store, e.actorId, marker);
286820
+ handledThisSweep.add(sweepKey(e.companyId, e.actorId));
286821
+ await saveNoticeState(store, e.actorId, marker.at, await deliverInvalidNotice(e.companyId, store, e.actorId, marker));
286822
+ };
286823
+ const retryPendingNotices = async (companyId, store) => {
286824
+ const svc = await options.actors(companyId);
286825
+ if (!svc) return;
286826
+ const ids2 = [void 0, ...(await svc.listActors({ status: "active" })).map((a) => a.id)];
286827
+ for (const actorId of ids2) {
286828
+ if (handledThisSweep.delete(sweepKey(companyId, actorId))) continue;
286829
+ const marker = await readFeishuUserAuthInvalid(store, actorId);
286830
+ const n = marker?.notice;
286831
+ if (!marker || !n || n.inApp && n.dm !== "pending" || n.attempts >= MAX_NOTICE_ATTEMPTS) continue;
286832
+ try {
286833
+ await saveNoticeState(store, actorId, marker.at, await deliverInvalidNotice(companyId, store, actorId, marker));
286834
+ } catch (err) {
286835
+ options.warn(new Error(`\u98DE\u4E66\u6388\u6743\u5931\u6548\u63D0\u9192\u8865\u53D1\u5931\u8D25 actor=${actorId ?? "-"}: ${String(err)}`));
286836
+ }
286733
286837
  }
286838
+ for (const k2 of handledThisSweep) if (JSON.parse(k2)[0] === companyId) handledThisSweep.delete(k2);
286734
286839
  };
286735
286840
  const onFeishuRenewed = async (e, store) => {
286736
286841
  if (await store.revealVariable(FEISHU_USER_OPEN_ID_VAR, e.actorId)) return;
@@ -286770,6 +286875,7 @@ function createConnectorRenewalRuntime(options) {
286770
286875
  onRenewed: async (e, store) => {
286771
286876
  if (e.connector.config.slug === "feishu") await onFeishuRenewed(e, store);
286772
286877
  },
286878
+ afterCompany: retryPendingNotices,
286773
286879
  warn: options.warn,
286774
286880
  ...options.now ? { now: options.now } : {}
286775
286881
  });
@@ -286790,7 +286896,8 @@ function createConnectorRenewalRuntime(options) {
286790
286896
  await notifyFeishuUser(
286791
286897
  store,
286792
286898
  claims.actorId,
286793
- `\u6709\u4EBA\u7528\u53E6\u4E00\u4E2A\u98DE\u4E66\u8D26\u53F7${who.userName ? `\uFF08${who.userName}\uFF09` : ""}\u6253\u5F00\u4E86\u300C${name}\u300D\u7684\u91CD\u65B0\u6388\u6743\u94FE\u63A5\uFF0C\u5DF2\u88AB\u62D2\u7EDD\uFF0C\u6388\u6743\u672A\u6539\u53D8\u3002`
286899
+ `\u6709\u4EBA\u7528\u53E6\u4E00\u4E2A\u98DE\u4E66\u8D26\u53F7${who.userName ? `\uFF08${who.userName}\uFF09` : ""}\u6253\u5F00\u4E86\u300C${name}\u300D\u7684\u91CD\u65B0\u6388\u6743\u94FE\u63A5\uFF0C\u5DF2\u88AB\u62D2\u7EDD\uFF0C\u6388\u6743\u672A\u6539\u53D8\u3002`,
286900
+ claims.subject
286794
286901
  ).catch(() => void 0);
286795
286902
  }
286796
286903
  });
@@ -290573,6 +290680,11 @@ async function startServe(opts) {
290573
290680
  auditSink.notifyNotificationsChanged(recipients);
290574
290681
  },
290575
290682
  boundHuman: async (agentId, companyId) => (await (await resolveAssistantsCtx(companyId)).service.getByAgent(agentId))?.humanActorId ?? null,
290683
+ // 成员表行上带 actorId;邮箱在控制面账号上(登录时验证过)。
290684
+ memberEmail: async (humanId, companyId) => {
290685
+ const row = (await controlPlaneStore.listMembers(companyId)).find((m2) => m2.actorId === humanId);
290686
+ return row ? (await controlPlaneStore.getAccount(row.accountId))?.email ?? null : null;
290687
+ },
290576
290688
  links: createConnectorReauthLinkIssuer({ secretFile: path30.join(opts.dir, "connector-reauth-link-secret") }),
290577
290689
  apiBase: remoteApiUrl,
290578
290690
  warn: (error2) => console.warn(`[credential-refresh] ${String(error2)}`)
@@ -300664,7 +300776,7 @@ async function waitForCommandAuthorization(base, headers, draftId, fallback) {
300664
300776
  }
300665
300777
  }
300666
300778
  }
300667
- var CLI_VERSION = "2.2.110" ? "2.2.110" : "dev";
300779
+ var CLI_VERSION = "2.2.111" ? "2.2.111" : "dev";
300668
300780
  function apiHeaders(token2, company) {
300669
300781
  const headers = {
300670
300782
  authorization: `Bearer ${token2}`,
@@ -304835,7 +304947,7 @@ function shimScript() {
304835
304947
  }
304836
304948
 
304837
304949
  // src/index.ts
304838
- var PKG_VERSION = true ? "2.2.110" : "dev";
304950
+ var PKG_VERSION = true ? "2.2.111" : "dev";
304839
304951
  var LOCAL_BIN = localBin();
304840
304952
  var NPM_PREFIX = npmPrefix();
304841
304953
  var INSTANCE = DEFAULT_INSTANCE;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oasis_test_v2",
3
- "version": "2.2.110",
3
+ "version": "2.2.111",
4
4
  "description": "Oasis node daemon + CLI — background daemon, auto-start, full server CLI",
5
5
  "bin": {
6
6
  "oasis": "./dist/index.js"
@@ -26,6 +26,6 @@
26
26
  "node": ">=20"
27
27
  },
28
28
  "oasisRelease": {
29
- "sourceHead": "99b875d0c03fed8cad9ca484f28f05e3a304c3f1"
29
+ "sourceHead": "05ed7c1a9a41f19b4919fc9398095d0b3ceefaf7"
30
30
  }
31
31
  }