oasis_test_v2 2.2.113 → 2.2.115

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 +472 -21
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -20479,7 +20479,8 @@ var init_feishu = __esm({
20479
20479
  "FEISHU_REFRESH_TOKEN_EXPIRES_AT",
20480
20480
  FEISHU_USER_OPEN_ID_VAR,
20481
20481
  FEISHU_AUTHORIZED_BY_VAR,
20482
- FEISHU_USER_AUTH_INVALID_VAR
20482
+ FEISHU_USER_AUTH_INVALID_VAR,
20483
+ "FEISHU_AUTH_PENDING_APPROVAL"
20483
20484
  ];
20484
20485
  /** 宿主机登录态隔离,理由同 github(见接口注释):由通用装配层无条件设。 */
20485
20486
  hostCredentialIsolationEnvKeys = ["LARKSUITE_CLI_CONFIG_DIR"];
@@ -20671,7 +20672,8 @@ async function pollFeishuDeviceToken(input) {
20671
20672
  const data = await resp.json();
20672
20673
  const errStr = data["error"];
20673
20674
  if (errStr === "authorization_pending" || errStr === "slow_down") return { status: "pending" };
20674
- if (!resp.ok || errStr) throw new Error(String(data["error_description"] ?? errStr ?? "login failed"));
20675
+ if (errStr) throw new FeishuDeviceFlowError(errStr, String(data["error_description"] ?? errStr));
20676
+ if (!resp.ok) throw new Error(String(data["error_description"] ?? "login failed"));
20675
20677
  if (!data["access_token"]) throw new Error("no access_token in response");
20676
20678
  return { status: "ok", data };
20677
20679
  }
@@ -20720,7 +20722,7 @@ function feishuUserAuthInvalidText(input) {
20720
20722
  ${input.link}` : `${head}
20721
20723
  \u8BF7\u5230 Oasis \u8BE5 Agent \u7684\u300C\u8FDE\u63A5\u5668\u300D\u9875\u91CD\u65B0\u6388\u6743\u98DE\u4E66\u3002`;
20722
20724
  }
20723
- var FEISHU_DEVICE_AUTHORIZATION_URL, FEISHU_USER_INFO_URL, FEISHU_SEND_MESSAGE_URL, FEISHU_BATCH_GET_ID_URL;
20725
+ var FEISHU_DEVICE_AUTHORIZATION_URL, FEISHU_USER_INFO_URL, FEISHU_SEND_MESSAGE_URL, FEISHU_BATCH_GET_ID_URL, FeishuDeviceFlowError;
20724
20726
  var init_user_auth = __esm({
20725
20727
  "../connectors/src/feishu/user-auth.ts"() {
20726
20728
  "use strict";
@@ -20729,6 +20731,81 @@ var init_user_auth = __esm({
20729
20731
  FEISHU_USER_INFO_URL = "https://open.feishu.cn/open-apis/authen/v1/user_info";
20730
20732
  FEISHU_SEND_MESSAGE_URL = "https://open.feishu.cn/open-apis/im/v1/messages";
20731
20733
  FEISHU_BATCH_GET_ID_URL = "https://open.feishu.cn/open-apis/contact/v3/users/batch_get_id";
20734
+ FeishuDeviceFlowError = class extends Error {
20735
+ constructor(code2, message) {
20736
+ super(message);
20737
+ this.code = code2;
20738
+ this.name = "FeishuDeviceFlowError";
20739
+ }
20740
+ };
20741
+ }
20742
+ });
20743
+
20744
+ // ../connectors/src/feishu/pending-approval.ts
20745
+ function feishuPendingApprovalScopes(input) {
20746
+ const granted = input.granted ? new Set(input.granted) : null;
20747
+ const missing = input.requested.filter((s2) => s2 !== "offline_access" && !granted?.has(s2));
20748
+ if (!input.appScopes) return missing.filter((s2) => !AUTO_APPROVED2.has(s2));
20749
+ const byName = userScopeIndex(input.appScopes);
20750
+ return missing.filter((s2) => byName.has(s2) && !isGranted(byName.get(s2)));
20751
+ }
20752
+ function feishuPendingApprovalSettled(pending, appScopes) {
20753
+ const byName = userScopeIndex(appScopes);
20754
+ return pending.pendingScopes.every((s2) => isGranted(byName.get(s2)));
20755
+ }
20756
+ function feishuPendingApprovalModules(pending) {
20757
+ const waiting = new Set(pending.pendingScopes);
20758
+ return pending.modules.filter((m2) => (FEISHU_DOMAIN_SCOPES[m2]?.scopes ?? []).some((s2) => waiting.has(s2)));
20759
+ }
20760
+ async function readFeishuPendingApproval(store, actorId) {
20761
+ const raw = await store.revealVariable(FEISHU_AUTH_PENDING_APPROVAL_VAR, actorId).catch(() => null);
20762
+ if (!raw) return null;
20763
+ try {
20764
+ const p2 = JSON.parse(raw);
20765
+ return typeof p2?.at === "string" && typeof p2.appId === "string" && Array.isArray(p2.modules) && Array.isArray(p2.pendingScopes) ? p2 : null;
20766
+ } catch {
20767
+ return null;
20768
+ }
20769
+ }
20770
+ async function writeFeishuPendingApproval(store, actorId, pending) {
20771
+ await store.putVariable({
20772
+ key: FEISHU_AUTH_PENDING_APPROVAL_VAR,
20773
+ value: JSON.stringify(pending),
20774
+ ...actorId ? { scope: "personal", actorId } : { scope: "global" },
20775
+ connectorId: "feishu",
20776
+ encrypted: false
20777
+ });
20778
+ }
20779
+ async function clearFeishuPendingApproval(store, actorId) {
20780
+ await store.deleteVariable(FEISHU_AUTH_PENDING_APPROVAL_VAR, actorId);
20781
+ }
20782
+ function feishuApprovalGrantedText(input) {
20783
+ const head = `\u7BA1\u7406\u5458\u5DF2\u6279\u51C6\u300C${input.agentName}\u300D\u7533\u8BF7\u7684\u98DE\u4E66\u6743\u9650\uFF08${moduleNames(input.modules)}\uFF09\u3002\u8FD8\u5DEE\u4F60\u70B9\u4E00\u6B21\u6388\u6743\uFF0C\u8FD9\u90E8\u5206\u529F\u80FD\u624D\u80FD\u4EE5\u4F60\u7684\u8EAB\u4EFD\u4F7F\u7528\u3002`;
20784
+ return input.link ? `${head}
20785
+ \u70B9\u8FD9\u4E2A\u94FE\u63A5\u8865\u4E0A\u6743\u9650\uFF08\u9700\u7528\u6536\u5230\u8FD9\u6761\u79C1\u4FE1\u7684\u98DE\u4E66\u8D26\u53F7\u767B\u5F55\uFF0C7 \u5929\u5185\u6709\u6548\uFF09\uFF1A
20786
+ ${input.link}` : `${head}
20787
+ \u8BF7\u5230 Oasis \u8BE5 Agent \u7684\u300C\u8FDE\u63A5\u5668\u300D\u9875\u91CD\u65B0\u8FDE\u63A5\u98DE\u4E66\u3002`;
20788
+ }
20789
+ function feishuPendingApprovalNotice(modules) {
20790
+ return `${moduleNames(modules)}\u542B\u9700\u98DE\u4E66\u7BA1\u7406\u5458\u5BA1\u6279\u7684\u6743\u9650\uFF0C\u5DF2\u63D0\u4EA4\u5BA1\u6279\u3002\u6279\u51C6\u540E\u4F1A\u5728\u98DE\u4E66\u79C1\u4FE1\u901A\u77E5\u4F60\uFF0C\u70B9\u4E00\u4E0B\u5373\u53EF\u8865\u4E0A\u8FD9\u90E8\u5206\u6743\u9650\uFF1B\u5DF2\u6709\u7684\u98DE\u4E66\u80FD\u529B\u4E0D\u53D7\u5F71\u54CD\u3002`;
20791
+ }
20792
+ function feishuApprovalUnconfirmedNotice(modules, expired) {
20793
+ return `${expired ? "\u6388\u6743\u9875\u5DF2\u8D85\u65F6\uFF0C" : ""}\u98DE\u4E66\u8FD9\u4E00\u8F6E\u6CA1\u6709\u7ED9\u51FA\u6388\u6743\uFF0COasis \u65E0\u6CD5\u786E\u8BA4\u4F60\u662F\u5426\u5728\u6388\u6743\u9875\u5BF9${moduleNames(modules)}\u7684\u6743\u9650\u70B9\u4E86\u300C\u7533\u8BF7\u300D\u3002\u5982\u679C\u70B9\u4E86\uFF0C\u8BF7\u9009\u300C\u6211\u70B9\u4E86\u7533\u8BF7\u300D\uFF0C\u6279\u51C6\u540E\u4F1A\u5728\u98DE\u4E66\u79C1\u4FE1\u901A\u77E5\u4F60\u8865\u6388\u6743\uFF1B\u6CA1\u70B9\uFF08\u6216\u70B9\u7684\u662F\u62D2\u7EDD\uFF09\u7684\u8BDD\u91CD\u65B0\u53D1\u8D77\u6388\u6743\u5373\u53EF\u3002\u5DF2\u6709\u7684\u98DE\u4E66\u80FD\u529B\u4E0D\u53D7\u5F71\u54CD\u3002`;
20794
+ }
20795
+ var FEISHU_AUTH_PENDING_APPROVAL_VAR, FEISHU_PENDING_APPROVAL_WATCH_MS, FEISHU_APPROVED_SUPPLEMENT_KEEP_MS, AUTO_APPROVED2, userScopeIndex, isGranted, moduleNames;
20796
+ var init_pending_approval = __esm({
20797
+ "../connectors/src/feishu/pending-approval.ts"() {
20798
+ "use strict";
20799
+ init_scopes();
20800
+ FEISHU_AUTH_PENDING_APPROVAL_VAR = "FEISHU_AUTH_PENDING_APPROVAL";
20801
+ FEISHU_PENDING_APPROVAL_WATCH_MS = 30 * 24 * 36e5;
20802
+ FEISHU_APPROVED_SUPPLEMENT_KEEP_MS = 7 * 24 * 36e5;
20803
+ AUTO_APPROVED2 = new Set(FEISHU_BASIC_SCOPES);
20804
+ userScopeIndex = (appScopes) => new Map(
20805
+ appScopes.filter((s2) => s2.scope_name && (!s2.scope_type || s2.scope_type === "user")).map((s2) => [s2.scope_name, s2])
20806
+ );
20807
+ isGranted = (s2) => !!s2 && (s2.grant_status === void 0 || s2.grant_status === 1);
20808
+ moduleNames = (modules) => modules.map((m2) => FEISHU_MODULE_LABELS[m2] ?? m2).join("\u3001") || "\u6240\u7533\u8BF7\u7684";
20732
20809
  }
20733
20810
  });
20734
20811
 
@@ -24653,6 +24730,7 @@ var init_src3 = __esm({
24653
24730
  init_host_isolation();
24654
24731
  init_feishu();
24655
24732
  init_user_auth();
24733
+ init_pending_approval();
24656
24734
  init_scopes();
24657
24735
  init_oauth();
24658
24736
  init_scheduled_refresh();
@@ -24674,6 +24752,14 @@ var init_src3 = __esm({
24674
24752
  });
24675
24753
 
24676
24754
  // ../server/src/feishu-reauth.ts
24755
+ async function supplementSelection(store, actorId) {
24756
+ const pending = await readFeishuPendingApproval(store, actorId);
24757
+ if (!pending) return null;
24758
+ const appId = await store.revealResolvedVariable("FEISHU_APP_ID", actorId).catch(() => null);
24759
+ if (appId !== pending.appId) return null;
24760
+ const sel = normalizeFeishuAuthSelection({ modules: pending.modules, sendAsUser: pending.sendAsUser });
24761
+ return sel ? { sel, sendAsUser: pending.sendAsUserRequested, pending } : null;
24762
+ }
24677
24763
  async function reauthSelection(store, actorId) {
24678
24764
  const raw = await store.revealVariable(FEISHU_AUTH_SELECTION_VAR, actorId).catch(() => null);
24679
24765
  if (raw) {
@@ -24713,20 +24799,63 @@ function createFeishuReauth(deps) {
24713
24799
  message: `\u8FD9\u4E2A\u94FE\u63A5\u53EA\u80FD\u7531\u539F\u6388\u6743\u4EBA\u672C\u4EBA\u4F7F\u7528\u3002\u4F60${who.name ? `\uFF08${who.name}\uFF09` : ""}\u4E0D\u662F\u539F\u6388\u6743\u4EBA\uFF0C\u672C\u6B21\u6388\u6743\u672A\u4FDD\u5B58\uFF0C\u539F\u6388\u6743\u4FDD\u6301\u4E0D\u53D8\u3002`
24714
24800
  };
24715
24801
  }
24716
- await withConnectorCredentialLock("feishu", claims.actorId, async () => {
24802
+ const supplement = await withConnectorCredentialLock("feishu", claims.actorId, async () => {
24803
+ const pending2 = claims.purpose === "supplement" ? await readFeishuPendingApproval(store, claims.actorId) : null;
24717
24804
  await persistFeishuUserAuthorization(store, claims.actorId, data, now(), {
24718
24805
  openId: who.openId,
24719
- ...who.name ? { userName: who.name } : {}
24806
+ ...who.name ? { userName: who.name } : {},
24807
+ ...pending2?.applicant ? { authorizedBy: pending2.applicant } : {}
24720
24808
  });
24809
+ const record10 = feishuAuthSelectionRecord(sel, sendAsUserRequested, data);
24721
24810
  await store.putVariable({
24722
24811
  key: FEISHU_AUTH_SELECTION_VAR,
24723
- value: JSON.stringify(feishuAuthSelectionRecord(sel, sendAsUserRequested, data)),
24812
+ value: JSON.stringify(record10),
24724
24813
  ...claims.actorId ? { scope: "personal", actorId: claims.actorId } : { scope: "global" },
24725
24814
  connectorId: "feishu",
24726
24815
  encrypted: false
24727
24816
  });
24728
- });
24729
- await deps.onRestored?.(claims, { openId: who.openId, ...who.name ? { userName: who.name } : {} }).catch((e) => warn(`onRestored: ${String(e)}`));
24817
+ if (claims.purpose !== "supplement") return null;
24818
+ if (!record10.grantedScopes) return "unknown";
24819
+ if (!pending2) {
24820
+ const granted2 = new Set(record10.grantedScopes);
24821
+ const wanted = scopesForSelection(sel, sendAsUserRequested).filter((x2) => x2 !== "offline_access");
24822
+ return wanted.every((x2) => granted2.has(x2)) ? "complete" : "short";
24823
+ }
24824
+ const granted = new Set(record10.grantedScopes);
24825
+ const still = pending2.pendingScopes.filter((x2) => !granted.has(x2));
24826
+ if (still.length === 0) {
24827
+ await clearFeishuPendingApproval(store, claims.actorId);
24828
+ return "complete";
24829
+ }
24830
+ const next = { ...pending2, pendingScopes: still };
24831
+ delete next.approvedAt;
24832
+ delete next.notice;
24833
+ await writeFeishuPendingApproval(store, claims.actorId, next);
24834
+ return "partial";
24835
+ });
24836
+ const userName = who.name ? { userName: who.name } : {};
24837
+ if (supplement === "partial") {
24838
+ return {
24839
+ state: "incomplete",
24840
+ ...userName,
24841
+ message: "\u6388\u6743\u5DF2\u4FDD\u5B58\uFF0C\u4F46\u98DE\u4E66\u8FD9\u6B21\u6CA1\u6709\u7ED9\u9F50\u7BA1\u7406\u5458\u6279\u51C6\u7684\u6743\u9650\uFF0C\u8FD8\u5DEE\u7684\u90E8\u5206\u7EE7\u7EED\u7B49\u5F85\u5BA1\u6279\uFF1B\u6279\u51C6\u540E\u4F1A\u518D\u79C1\u4FE1\u901A\u77E5\u4F60\u3002\u5DF2\u6709\u7684\u98DE\u4E66\u80FD\u529B\u4E0D\u53D7\u5F71\u54CD\u3002"
24842
+ };
24843
+ }
24844
+ if (supplement === "short") {
24845
+ return {
24846
+ state: "incomplete",
24847
+ ...userName,
24848
+ message: "\u6388\u6743\u5DF2\u4FDD\u5B58\uFF0C\u4F46\u98DE\u4E66\u8FD9\u6B21\u6CA1\u6709\u7ED9\u9F50\u6240\u7533\u8BF7\u7684\u6743\u9650\u3002\u53EF\u5728 Oasis \u8FDE\u63A5\u5668\u8BE6\u60C5\u67E5\u770B\u5DF2\u5F00\u901A\u7684\u529F\u80FD\uFF0C\u9700\u8981\u65F6\u91CD\u65B0\u8FDE\u63A5\u98DE\u4E66\u3002"
24849
+ };
24850
+ }
24851
+ if (supplement === "unknown") {
24852
+ return {
24853
+ state: "incomplete",
24854
+ ...userName,
24855
+ message: "\u6388\u6743\u5DF2\u4FDD\u5B58\uFF0C\u4F46\u98DE\u4E66\u6CA1\u6709\u56DE\u62A5\u8FD9\u6B21\u6388\u4E88\u4E86\u54EA\u4E9B\u6743\u9650\uFF0C\u65E0\u6CD5\u786E\u8BA4\u7BA1\u7406\u5458\u6279\u51C6\u7684\u6743\u9650\u662F\u5426\u5DF2\u8865\u4E0A\u3002\u53EF\u5728 Oasis \u8FDE\u63A5\u5668\u8BE6\u60C5\u67E5\u770B\uFF1B\u4ECD\u663E\u793A\u5F85\u8865\u6388\u6743\u7684\u8BDD\uFF0C\u518D\u70B9\u4E00\u6B21\u94FE\u63A5\u5373\u53EF\u3002"
24856
+ };
24857
+ }
24858
+ await deps.onRestored?.(claims, { openId: who.openId, ...userName }).catch((e) => warn(`onRestored: ${String(e)}`));
24730
24859
  return { state: "restored", ...who.name ? { userName: who.name } : {} };
24731
24860
  };
24732
24861
  const watch = async (deviceCode, entry, ctx) => {
@@ -24756,7 +24885,15 @@ function createFeishuReauth(deps) {
24756
24885
  if (!store) return { ok: false, status: 404, message: "\u627E\u4E0D\u5230\u8FD9\u4E2A\u94FE\u63A5\u6240\u5C5E\u7684\u7EC4\u7EC7\u3002" };
24757
24886
  const app = await appCredentials(store, claims.actorId);
24758
24887
  if (!app) return { ok: false, status: 409, message: "\u8FD9\u540D Agent \u7684\u98DE\u4E66\u5E94\u7528\u51ED\u636E\u5DF2\u4E0D\u5B58\u5728\uFF0C\u65E0\u6CD5\u91CD\u65B0\u6388\u6743\u3002\u8BF7\u5230 Oasis \u8FDE\u63A5\u5668\u9875\u91CD\u65B0\u8FDE\u63A5\u98DE\u4E66\u3002" };
24759
- const { sel, sendAsUser } = await reauthSelection(store, claims.actorId);
24888
+ let picked;
24889
+ if (claims.purpose === "supplement") {
24890
+ const sup = await supplementSelection(store, claims.actorId);
24891
+ if (!sup) return { ok: false, status: 410, message: "\u8FD9\u6B21\u8865\u6388\u6743\u5DF2\u7ECF\u5B8C\u6210\u6216\u5DF2\u5931\u6548\u3002\u8BF7\u5230 Oasis \u8BE5 Agent \u7684\u300C\u8FDE\u63A5\u5668\u300D\u9875\u67E5\u770B\uFF0C\u9700\u8981\u65F6\u91CD\u65B0\u8FDE\u63A5\u98DE\u4E66\u3002" };
24892
+ picked = sup;
24893
+ } else {
24894
+ picked = await reauthSelection(store, claims.actorId);
24895
+ }
24896
+ const { sel, sendAsUser } = picked;
24760
24897
  const auth = await (deps.start ?? startFeishuDeviceAuthorization)({
24761
24898
  appId: app.appId,
24762
24899
  appSecret: app.appSecret,
@@ -24771,7 +24908,7 @@ function createFeishuReauth(deps) {
24771
24908
  const intervalSec = auth.interval ?? DEFAULT_POLL_INTERVAL_SEC;
24772
24909
  void watch(auth.deviceCode, entry, { store, ...app, sel, sendAsUser, intervalSec });
24773
24910
  const agentName = await deps.agentName?.(claims.companyId, claims.actorId).catch(() => void 0) ?? claims.actorId ?? "\u7EC4\u7EC7";
24774
- return { ok: true, verificationUrl: auth.verificationUrl, deviceCode: auth.deviceCode, agentName, intervalSec };
24911
+ return { ok: true, verificationUrl: auth.verificationUrl, deviceCode: auth.deviceCode, agentName, intervalSec, purpose: claims.purpose };
24775
24912
  },
24776
24913
  /** 页面轮询结果。票与 deviceCode 必须是同一次 begin 发出来的。 */
24777
24914
  status(token2, deviceCode) {
@@ -24788,17 +24925,18 @@ function createFeishuReauth(deps) {
24788
24925
  function feishuReauthPageHtml(input) {
24789
24926
  const head = `<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>\u91CD\u65B0\u6388\u6743\u98DE\u4E66</title>
24790
24927
  <style>body{font-family:system-ui,-apple-system,sans-serif;max-width:32rem;margin:3rem auto;padding:0 1.25rem;color:#1f2329;line-height:1.6}a.btn{display:inline-block;background:#1f2329;color:#fff;padding:.6rem 1.2rem;border-radius:.5rem;text-decoration:none}#s{margin-top:1.25rem}</style></head><body>`;
24791
- if (!input.ok) return `${head}<h2>\u65E0\u6CD5\u91CD\u65B0\u6388\u6743</h2><p>${escapeHtml(input.message)}</p></body></html>`;
24792
- const cfg = JSON.stringify({ t: input.token, d: input.deviceCode, i: Math.max(2, input.intervalSec) }).replace(/</g, "\\u003c");
24793
- return `${head}<h2>\u91CD\u65B0\u6388\u6743\u98DE\u4E66\uFF1A${escapeHtml(input.agentName)}</h2>
24794
- <p>\u8BF7\u7528<strong>\u539F\u6388\u6743\u4EBA</strong>\u7684\u98DE\u4E66\u8D26\u53F7\u5B8C\u6210\u6388\u6743\u3002\u6362\u6210\u522B\u7684\u8D26\u53F7\u6388\u6743\u4F1A\u88AB\u62D2\u7EDD\uFF0C\u539F\u6388\u6743\u4FDD\u6301\u4E0D\u53D8\u3002</p>
24928
+ if (!input.ok) return `${head}<h2>\u65E0\u6CD5\u5B8C\u6210\u6388\u6743</h2><p>${escapeHtml(input.message)}</p></body></html>`;
24929
+ const supplement = input.purpose === "supplement";
24930
+ const cfg = JSON.stringify({ t: input.token, d: input.deviceCode, i: Math.max(2, input.intervalSec), s: supplement }).replace(/</g, "\\u003c");
24931
+ return `${head}<h2>${supplement ? "\u8865\u6388\u6743\u98DE\u4E66\uFF08\u7BA1\u7406\u5458\u5DF2\u6279\u51C6\uFF09" : "\u91CD\u65B0\u6388\u6743\u98DE\u4E66"}\uFF1A${escapeHtml(input.agentName)}</h2>
24932
+ <p>\u8BF7\u7528<strong>${supplement ? "\u7533\u8BF7\u4EBA" : "\u539F\u6388\u6743\u4EBA"}</strong>\u7684\u98DE\u4E66\u8D26\u53F7\u5B8C\u6210\u6388\u6743\u3002\u6362\u6210\u522B\u7684\u8D26\u53F7\u6388\u6743\u4F1A\u88AB\u62D2\u7EDD\uFF0C\u539F\u6388\u6743\u4FDD\u6301\u4E0D\u53D8\u3002</p>
24795
24933
  <p><a class="btn" href="${escapeHtml(input.verificationUrl)}" target="_blank" rel="noopener">\u53BB\u98DE\u4E66\u6388\u6743</a></p>
24796
24934
  <p id="s">\u7B49\u5F85\u4F60\u5728\u98DE\u4E66\u70B9\u300C\u6388\u6743\u300D\u2026</p>
24797
24935
  <script>(function(){var c=${cfg};var el=document.getElementById("s");
24798
24936
  function tick(){fetch("reauth/status",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({t:c.t,deviceCode:c.d})})
24799
24937
  .then(function(r){return r.json()}).then(function(b){
24800
24938
  if(b.state==="pending"){setTimeout(tick,c.i*1000);return}
24801
- if(b.state==="restored"){el.textContent="\u5DF2\u6062\u590D\uFF1A"+(b.userName?b.userName+" \u7684":"")+"\u98DE\u4E66\u7528\u6237\u6388\u6743\u91CD\u65B0\u751F\u6548\uFF0C\u53EF\u4EE5\u5173\u6389\u8FD9\u4E2A\u9875\u9762\u4E86\u3002";return}
24939
+ if(b.state==="restored"){el.textContent=(c.s?"\u5DF2\u8865\u4E0A\uFF1A":"\u5DF2\u6062\u590D\uFF1A")+(b.userName?b.userName+" \u7684":"")+(c.s?"\u98DE\u4E66\u7528\u6237\u6388\u6743\u5DF2\u5305\u542B\u7BA1\u7406\u5458\u6279\u51C6\u7684\u6743\u9650":"\u98DE\u4E66\u7528\u6237\u6388\u6743\u91CD\u65B0\u751F\u6548")+"\uFF0C\u53EF\u4EE5\u5173\u6389\u8FD9\u4E2A\u9875\u9762\u4E86\u3002";return}
24802
24940
  el.textContent=b.message||"\u6388\u6743\u6CA1\u6709\u5B8C\u6210\uFF0C\u53EF\u4EE5\u91CD\u65B0\u70B9\u94FE\u63A5\u518D\u8BD5\u3002"}).catch(function(){setTimeout(tick,c.i*1000)})}
24803
24941
  setTimeout(tick,c.i*1000)})();</script></body></html>`;
24804
24942
  }
@@ -212190,6 +212328,35 @@ async function feishuModuleAccess(store, actorId, fetchScopes = fetchFeishuAppSc
212190
212328
  ...botCheckError ? { botCheckError } : {}
212191
212329
  };
212192
212330
  }
212331
+ async function saveFeishuModuleSelection(store, actorId, body2) {
212332
+ const read3 = (key2) => store.revealResolvedVariable(key2, actorId).catch(() => null);
212333
+ if (!await read3("FEISHU_APP_ID") || !await read3("FEISHU_APP_SECRET")) return { ok: false, error: "no_app" };
212334
+ const sel = normalizeFeishuAuthSelection({ modules: Array.isArray(body2.modules) ? body2.modules : [] });
212335
+ if (!sel) return { ok: false, error: "empty" };
212336
+ let prev = {};
212337
+ const raw = await read3(FEISHU_AUTH_SELECTION_VAR);
212338
+ try {
212339
+ prev = raw ? JSON.parse(raw) ?? {} : {};
212340
+ } catch {
212341
+ prev = {};
212342
+ }
212343
+ const grantedScopes = Array.isArray(prev.grantedScopes) ? prev.grantedScopes.filter((s2) => typeof s2 === "string") : void 0;
212344
+ const sendAsUser = prev.sendAsUser === true;
212345
+ const record10 = {
212346
+ modules: sel.modules,
212347
+ sendAsUser,
212348
+ sendAsUserRequested: sendAsUser && prev.sendAsUserRequested === true,
212349
+ ...grantedScopes ? { grantedScopes } : {}
212350
+ };
212351
+ await store.putVariable({
212352
+ key: FEISHU_AUTH_SELECTION_VAR,
212353
+ value: JSON.stringify(record10),
212354
+ ...actorId ? { scope: "personal", actorId } : { scope: "global" },
212355
+ connectorId: "feishu",
212356
+ encrypted: false
212357
+ });
212358
+ return { ok: true, record: record10 };
212359
+ }
212193
212360
  var init_feishu_module_access = __esm({
212194
212361
  "../server/src/feishu-module-access.ts"() {
212195
212362
  "use strict";
@@ -212197,6 +212364,51 @@ var init_feishu_module_access = __esm({
212197
212364
  }
212198
212365
  });
212199
212366
 
212367
+ // ../server/src/feishu-pending-approval.ts
212368
+ async function recordFeishuPendingApproval(input) {
212369
+ const pendingScopes = await pendingApprovalScopes(input);
212370
+ if (pendingScopes.length === 0) {
212371
+ if (input.granted) {
212372
+ await withConnectorCredentialLock("feishu", input.actorId, () => clearFeishuPendingApproval(input.store, input.actorId));
212373
+ }
212374
+ return null;
212375
+ }
212376
+ const record10 = {
212377
+ at: new Date(input.now).toISOString(),
212378
+ appId: input.appId,
212379
+ ...input.applicant ? { applicant: input.applicant } : {},
212380
+ modules: input.selection.modules,
212381
+ sendAsUser: input.selection.sendAsUser,
212382
+ sendAsUserRequested: input.sendAsUserRequested,
212383
+ pendingScopes
212384
+ };
212385
+ await withConnectorCredentialLock("feishu", input.actorId, () => writeFeishuPendingApproval(input.store, input.actorId, record10));
212386
+ const modules = feishuPendingApprovalModules(record10);
212387
+ return { modules, scopes: pendingScopes, notice: feishuPendingApprovalNotice(modules) };
212388
+ }
212389
+ async function pendingApprovalScopes(input) {
212390
+ const requested = scopesForSelection(input.selection, input.sendAsUserRequested);
212391
+ let appScopes = null;
212392
+ try {
212393
+ appScopes = await (input.fetchScopes ?? fetchFeishuAppScopes)(input.appId, input.appSecret);
212394
+ } catch (e) {
212395
+ (input.warn ?? ((m2) => console.warn(`[feishu-oauth] ${m2}`)))(`\u5E94\u7528\u6743\u9650\u5217\u8868\u67E5\u8BE2\u5931\u8D25\uFF0C\u5F85\u5BA1\u6279\u6309\u514D\u5BA1\u96C6\u5408\u4F30\u7B97: ${String(e)}`);
212396
+ }
212397
+ return feishuPendingApprovalScopes({ requested, granted: input.granted, appScopes });
212398
+ }
212399
+ async function assessFeishuApprovalUnconfirmed(input) {
212400
+ const scopes = await pendingApprovalScopes(input);
212401
+ if (scopes.length === 0) return null;
212402
+ const modules = feishuPendingApprovalModules({ modules: input.selection.modules, pendingScopes: scopes });
212403
+ return { modules, scopes, notice: feishuApprovalUnconfirmedNotice(modules, input.expired) };
212404
+ }
212405
+ var init_feishu_pending_approval = __esm({
212406
+ "../server/src/feishu-pending-approval.ts"() {
212407
+ "use strict";
212408
+ init_src3();
212409
+ }
212410
+ });
212411
+
212200
212412
  // ../server/src/github-app-inventory.ts
212201
212413
  async function collectOasisGithubApps(svc) {
212202
212414
  const rows = (await svc.listVariables()).filter((r) => r.scope === "global" || r.scope === "personal");
@@ -218401,6 +218613,27 @@ ${composed}`;
218401
218613
  }
218402
218614
  return;
218403
218615
  }
218616
+ if (url.pathname === "/api/connectors/feishu/modules" && req.method === "POST") {
218617
+ try {
218618
+ const body2 = JSON.parse(rawBody || "{}");
218619
+ const svc = await connectorActorsService();
218620
+ if (!svc) throw new Error("actors service unavailable");
218621
+ const denied = await connectorWriteDenied(svc, body2.actorId, "\u8C03\u6574\u98DE\u4E66\u63A5\u5165\u529F\u80FD");
218622
+ if (denied) {
218623
+ writeApiError(res, denied);
218624
+ return;
218625
+ }
218626
+ const saved = await saveFeishuModuleSelection(svc, body2.actorId, body2);
218627
+ if (!saved.ok) {
218628
+ res.writeHead(saved.error === "no_app" ? 409 : 400, { "content-type": "application/json" }).end(JSON.stringify({ error: saved.error === "no_app" ? "\u8FD8\u6CA1\u6709\u914D\u7F6E\u98DE\u4E66\u5E94\u7528\uFF0C\u8BF7\u5148\u5B8C\u6210\u8FDE\u63A5" : "\u8BF7\u81F3\u5C11\u9009\u62E9\u4E00\u4E2A\u529F\u80FD\u6A21\u5757" }));
218629
+ return;
218630
+ }
218631
+ res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ ok: true, modules: saved.record.modules }));
218632
+ } catch (e) {
218633
+ res.writeHead(500).end(JSON.stringify({ error: String(e) }));
218634
+ }
218635
+ return;
218636
+ }
218404
218637
  if (url.pathname === "/api/connectors/feishu/login/start" && req.method === "POST") {
218405
218638
  const startBody = JSON.parse(rawBody);
218406
218639
  const { appId, actorId } = startBody;
@@ -218470,6 +218703,51 @@ ${composed}`;
218470
218703
  }
218471
218704
  return;
218472
218705
  }
218706
+ if (url.pathname === "/api/connectors/feishu/login/confirm-application" && req.method === "POST") {
218707
+ const { appId, deviceCode, actorId } = JSON.parse(rawBody);
218708
+ if (!appId || !deviceCode) {
218709
+ res.writeHead(400).end(JSON.stringify({ error: "appId and deviceCode required" }));
218710
+ return;
218711
+ }
218712
+ try {
218713
+ const devicePending = opts.connectorAuthPending?.githubApp;
218714
+ const started = devicePending ? await devicePending.get(feishuDevicePendingKey(deviceCode), Date.now()) : { status: "missing" };
218715
+ if (started.status !== "found") {
218716
+ 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" }));
218717
+ return;
218718
+ }
218719
+ const v2 = started.value.value;
218720
+ if (v2["appId"] !== appId || v2["actorId"] !== (actorId ?? "") || (started.value.companyId ?? "") !== (currentCompanyId ?? "")) {
218721
+ 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" }));
218722
+ return;
218723
+ }
218724
+ if (v2["ended"] !== "no_token") {
218725
+ res.writeHead(409).end(JSON.stringify({ ok: false, error: "\u8FD9\u6B21\u6388\u6743\u6CA1\u6709\u5904\u5728\u300C\u5F85\u786E\u8BA4\u662F\u5426\u5DF2\u7533\u8BF7\u300D\u7684\u72B6\u6001\uFF0C\u65E0\u9700\u786E\u8BA4" }));
218726
+ return;
218727
+ }
218728
+ const selection = normalizeFeishuAuthSelection({ modules: v2["modules"], sendAsUser: v2["sendAsUser"] });
218729
+ const confirmSvc = await connectorActorsService();
218730
+ const appSecret = confirmSvc ? await confirmSvc.revealResolvedVariable("FEISHU_APP_SECRET", actorId) : null;
218731
+ if (!selection || !confirmSvc || !appSecret) {
218732
+ res.writeHead(409).end(JSON.stringify({ ok: false, restart: true, error: "\u8FD9\u6B21\u6388\u6743\u7684\u767B\u8BB0\u5DF2\u635F\u574F\u6216\u5E94\u7528\u51ED\u636E\u5DF2\u4E0D\u5B58\u5728\uFF0C\u8BF7\u91CD\u65B0\u53D1\u8D77\u6388\u6743" }));
218733
+ return;
218734
+ }
218735
+ const pendingApproval = await recordFeishuPendingApproval({
218736
+ store: confirmSvc,
218737
+ actorId,
218738
+ appId,
218739
+ appSecret,
218740
+ selection,
218741
+ sendAsUserRequested: selection.sendAsUser && v2["sendAsUserRequested"] === true,
218742
+ ...isHumanActorId(actor) ? { applicant: actor } : {},
218743
+ now: Date.now()
218744
+ });
218745
+ res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify(pendingApproval ? { ok: true, pendingApproval: { modules: pendingApproval.modules, scopes: pendingApproval.scopes }, notice: pendingApproval.notice } : { ok: false, error: "\u8FD9\u4E9B\u6743\u9650\u73B0\u5728\u5DF2\u7ECF\u53EF\u7528\uFF08\u7BA1\u7406\u5458\u53EF\u80FD\u5DF2\u6279\u51C6\uFF09\uFF0C\u8BF7\u91CD\u65B0\u53D1\u8D77\u6388\u6743\u5B8C\u6210\u8FDE\u63A5" }));
218746
+ } catch (e) {
218747
+ res.writeHead(500).end(JSON.stringify({ error: String(e) }));
218748
+ }
218749
+ return;
218750
+ }
218473
218751
  if (url.pathname === "/api/connectors/feishu/login/poll" && req.method === "POST") {
218474
218752
  const { appId, deviceCode, actorId } = JSON.parse(rawBody);
218475
218753
  if (!appId || !deviceCode) {
@@ -218498,12 +218776,40 @@ ${composed}`;
218498
218776
  const pollSvc = await connectorActorsService();
218499
218777
  const appSecret = pollSvc ? await pollSvc.revealResolvedVariable("FEISHU_APP_SECRET", actorId) : null;
218500
218778
  if (!appSecret) throw new Error("FEISHU_APP_SECRET not found \u2014 please complete app setup first");
218501
- const polled = await pollFeishuDeviceToken({ appId, appSecret, deviceCode });
218779
+ let polled;
218780
+ try {
218781
+ polled = await pollFeishuDeviceToken({ appId, appSecret, deviceCode });
218782
+ } catch (e) {
218783
+ if (!(e instanceof FeishuDeviceFlowError) || !pollSvc) throw e;
218784
+ const expired = e.code === "expired_token" || e.code === "invalid_grant";
218785
+ const unconfirmed = await assessFeishuApprovalUnconfirmed({
218786
+ appId,
218787
+ appSecret,
218788
+ selection: startedSelection.selection,
218789
+ sendAsUserRequested: startedSelection.sendAsUserRequested,
218790
+ expired
218791
+ });
218792
+ if (unconfirmed && devicePending) {
218793
+ await devicePending.patch(feishuDevicePendingKey(deviceCode), { ended: "no_token", endedCode: e.code });
218794
+ }
218795
+ res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify(unconfirmed ? {
218796
+ ok: false,
218797
+ final: true,
218798
+ approvalUnconfirmed: { modules: unconfirmed.modules, scopes: unconfirmed.scopes },
218799
+ notice: unconfirmed.notice
218800
+ } : {
218801
+ ok: false,
218802
+ final: true,
218803
+ error: expired ? "\u6388\u6743\u8D85\u65F6\uFF08\u8BBE\u5907\u7801\u5DF2\u8FC7\u671F\uFF09\uFF0C\u8BF7\u91CD\u65B0\u53D1\u8D77\u6388\u6743" : `\u98DE\u4E66\u6CA1\u6709\u5B8C\u6210\u6388\u6743\uFF08${e.message.slice(0, 200)}\uFF09\uFF0C\u53EF\u4EE5\u91CD\u65B0\u53D1\u8D77\u6388\u6743`
218804
+ }));
218805
+ return;
218806
+ }
218502
218807
  if (polled.status === "pending") {
218503
218808
  res.writeHead(200).end(JSON.stringify({ ok: false, error: "authorization_pending" }));
218504
218809
  return;
218505
218810
  }
218506
218811
  const data = polled.data;
218812
+ let pendingApproval = null;
218507
218813
  const svc = await connectorActorsService();
218508
218814
  if (svc) {
218509
218815
  if (actorId) {
@@ -218528,6 +218834,23 @@ ${composed}`;
218528
218834
  connectorId: "feishu",
218529
218835
  encrypted: false
218530
218836
  });
218837
+ if (record10.grantedScopes) {
218838
+ try {
218839
+ pendingApproval = await recordFeishuPendingApproval({
218840
+ store: svc,
218841
+ actorId,
218842
+ appId,
218843
+ appSecret,
218844
+ selection: startedSelection.selection,
218845
+ sendAsUserRequested: startedSelection.sendAsUserRequested,
218846
+ ...isHumanActorId(actor) ? { applicant: actor } : {},
218847
+ granted: record10.grantedScopes,
218848
+ now: Date.now()
218849
+ });
218850
+ } catch (e) {
218851
+ console.error(`[feishu-oauth] \u5F85\u5BA1\u6279\u8BB0\u5F55\u5199\u5165\u5931\u8D25: ${String(e)}`);
218852
+ }
218853
+ }
218531
218854
  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");
218532
218855
  try {
218533
218856
  const secret = await svc.revealResolvedVariable("FEISHU_APP_SECRET", actorId);
@@ -218552,7 +218875,12 @@ ${composed}`;
218552
218875
  }
218553
218876
  }
218554
218877
  }
218555
- res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ ok: true, appId, stored: true }));
218878
+ res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({
218879
+ ok: true,
218880
+ appId,
218881
+ stored: true,
218882
+ ...pendingApproval ? { pendingApproval: { modules: pendingApproval.modules, scopes: pendingApproval.scopes }, notice: pendingApproval.notice } : {}
218883
+ }));
218556
218884
  } catch (e) {
218557
218885
  res.writeHead(500).end(JSON.stringify({ error: String(e) }));
218558
218886
  }
@@ -218895,6 +219223,7 @@ var init_server3 = __esm({
218895
219223
  init_child_live_turn();
218896
219224
  init_registry_audit_sink();
218897
219225
  init_feishu_module_access();
219226
+ init_feishu_pending_approval();
218898
219227
  init_src3();
218899
219228
  init_github_app_inventory();
218900
219229
  init_github_app_timing();
@@ -287145,6 +287474,122 @@ function createConnectorRenewalRuntime(options) {
287145
287474
  }
287146
287475
  for (const k2 of handledThisSweep) if (JSON.parse(k2)[0] === companyId) handledThisSweep.delete(k2);
287147
287476
  };
287477
+ const applicantOpenId = async (companyId, store, actorId, app, applicant) => {
287478
+ if (!applicant || !isHumanActorId(applicant)) return null;
287479
+ const by = await store.revealVariable(FEISHU_AUTHORIZED_BY_VAR, actorId).catch(() => null);
287480
+ const stored = by === applicant ? await store.revealVariable(FEISHU_USER_OPEN_ID_VAR, actorId).catch(() => null) : null;
287481
+ return stored || recoverOpenId(companyId, app, applicant);
287482
+ };
287483
+ const deliverApprovalNotice = async (companyId, store, actorId, pending, app) => {
287484
+ const prev = pending.notice ?? { inApp: false, dm: "pending", attempts: 0 };
287485
+ const state = { ...prev, attempts: prev.attempts + 1 };
287486
+ const who = `actor=${actorId ?? "-"} \u7B2C ${state.attempts}/${MAX_NOTICE_ATTEMPTS} \u6B21`;
287487
+ const name = await agentName(companyId, actorId);
287488
+ const modules = feishuPendingApprovalModules(pending);
287489
+ if (!state.inApp) {
287490
+ try {
287491
+ const ledger = await options.notifications(companyId);
287492
+ if (!ledger) throw new Error("\u8BE5\u516C\u53F8\u7684\u901A\u77E5\u53F0\u8D26\u4E0D\u53EF\u7528");
287493
+ const recipients = pending.applicant && isHumanActorId(pending.applicant) ? [pending.applicant] : (await recipientsFor(companyId, store, actorId)).recipients;
287494
+ if (recipients.length === 0) throw new Error("\u6CA1\u6709\u6536\u4EF6\u4EBA\uFF08\u6CA1\u8BB0\u7533\u8BF7\u4EBA\u3001\u5458\u5DE5\u4E5F\u6CA1\u6709\u7ED1\u5B9A\u771F\u4EBA\uFF09");
287495
+ const rows = [];
287496
+ for (const recipient of recipients) {
287497
+ rows.push(await ledger.insert({
287498
+ recipient,
287499
+ category: "connector",
287500
+ kind: "feishu-approval-granted",
287501
+ // 每次申请一条(键里带提交时刻);同一次批准补发 / 重复扫描不重发(唯一约束)。
287502
+ sourceKey: `connector:feishu:${actorId ?? "org"}@approved:${pending.at}`,
287503
+ title: `\u7BA1\u7406\u5458\u5DF2\u6279\u51C6\u300C${name}\u300D\u7533\u8BF7\u7684\u98DE\u4E66\u6743\u9650\uFF0C\u8865\u6388\u6743\u540E\u5373\u53EF\u4F7F\u7528`,
287504
+ ...actorId ? { actorId } : {},
287505
+ enteredAt: pending.approvedAt ?? new Date(now()).toISOString()
287506
+ }));
287507
+ }
287508
+ options.onNotifications?.(companyId, rows);
287509
+ state.inApp = true;
287510
+ } catch (err) {
287511
+ options.warn(new Error(`\u98DE\u4E66\u6743\u9650\u6279\u51C6\u7AD9\u5185\u901A\u77E5\u672A\u5199\u5165\uFF08${who}\uFF0C\u4E0B\u4E00\u62CD\u91CD\u8BD5\uFF09: ${String(err)}`));
287512
+ }
287513
+ }
287514
+ if (state.dm === "pending") {
287515
+ try {
287516
+ const openId = await applicantOpenId(companyId, store, actorId, app, pending.applicant);
287517
+ if (!openId) {
287518
+ state.dm = "unavailable";
287519
+ console.warn(`[credential-refresh] \u98DE\u4E66\u6743\u9650\u6279\u51C6\u79C1\u4FE1\u65E0\u4ECE\u53D1\u51FA\uFF08${pending.applicant ? "\u901A\u8BAF\u5F55\u6CA1\u6309\u7533\u8BF7\u4EBA\u7684\u8D26\u53F7\u90AE\u7BB1\u627E\u5230\u4ED6" : "\u6CA1\u8BB0\u7533\u8BF7\u4EBA"}\uFF09actor=${actorId ?? "-"}\uFF1B\u53EA\u6709\u7AD9\u5185\u901A\u77E5\uFF0C\u9700\u5230\u8FDE\u63A5\u5668\u9875\u91CD\u65B0\u8FDE\u63A5`);
287520
+ } else {
287521
+ const token2 = options.links.issue({
287522
+ companyId,
287523
+ ...actorId ? { actorId } : {},
287524
+ connectorId: "feishu",
287525
+ purpose: "supplement",
287526
+ subject: openId
287527
+ });
287528
+ const link = options.apiBase ? feishuReauthUrl(options.apiBase, token2) : void 0;
287529
+ await notifyFeishuUser(store, actorId, feishuApprovalGrantedText({ agentName: name, modules, ...link ? { link } : {} }), openId);
287530
+ state.dm = "sent";
287531
+ }
287532
+ } catch (err) {
287533
+ options.warn(new Error(`\u98DE\u4E66\u6743\u9650\u6279\u51C6\u79C1\u4FE1\u53D1\u9001\u5931\u8D25\uFF08${who}\uFF0C\u4E0B\u4E00\u62CD\u91CD\u8BD5\uFF09: ${String(err)}`));
287534
+ }
287535
+ }
287536
+ if ((!state.inApp || state.dm === "pending") && state.attempts >= MAX_NOTICE_ATTEMPTS) {
287537
+ options.warn(new Error(`\u98DE\u4E66\u6743\u9650\u6279\u51C6\u901A\u77E5\u8865\u53D1 ${state.attempts} \u6B21\u4ECD\u672A\u9001\u5230\uFF0C\u653E\u5F03 actor=${actorId ?? "-"} \u7AD9\u5185=${String(state.inApp)} \u79C1\u4FE1=${state.dm}`));
287538
+ }
287539
+ return state;
287540
+ };
287541
+ const savePendingApproval = async (store, actorId, at2, next) => {
287542
+ await withConnectorCredentialLock("feishu", actorId, async () => {
287543
+ const current = await readFeishuPendingApproval(store, actorId);
287544
+ if (current?.at !== at2) return;
287545
+ if (next) await writeFeishuPendingApproval(store, actorId, next);
287546
+ else await clearFeishuPendingApproval(store, actorId);
287547
+ });
287548
+ };
287549
+ const checkPendingApproval = async (companyId, store, actorId) => {
287550
+ const pending = await readFeishuPendingApproval(store, actorId);
287551
+ if (!pending) return;
287552
+ const t = now();
287553
+ const app = await appCredentials(store, actorId);
287554
+ if (!app || app.appId !== pending.appId) {
287555
+ console.warn(`[credential-refresh] \u98DE\u4E66\u5F85\u5BA1\u6279\u8BB0\u5F55\u4F5C\u5E9F\uFF08\u5E94\u7528\u51ED\u636E${app ? "\u5DF2\u6362\u6210\u522B\u7684\u5E94\u7528" : "\u4E0D\u5B58\u5728"}\uFF09actor=${actorId ?? "-"}`);
287556
+ await savePendingApproval(store, actorId, pending.at, null);
287557
+ return;
287558
+ }
287559
+ let next;
287560
+ if (!pending.approvedAt) {
287561
+ if (t - Date.parse(pending.at) > FEISHU_PENDING_APPROVAL_WATCH_MS) {
287562
+ options.warn(new Error(`\u98DE\u4E66\u5F85\u5BA1\u6279 ${pending.at} \u8D77 30 \u5929\u672A\u68C0\u6D4B\u5230\u6279\u51C6\uFF0C\u4E0D\u518D\u68C0\u6D4B actor=${actorId ?? "-"} \u6743\u9650=${pending.pendingScopes.join(",")}`));
287563
+ await savePendingApproval(store, actorId, pending.at, null);
287564
+ return;
287565
+ }
287566
+ const scopes = await (options.fetchAppScopes ?? fetchFeishuAppScopes)(app.appId, app.appSecret);
287567
+ if (!feishuPendingApprovalSettled(pending, scopes)) return;
287568
+ next = { ...pending, approvedAt: new Date(t).toISOString(), notice: { inApp: false, dm: "pending", attempts: 0 } };
287569
+ console.warn(`[credential-refresh] \u98DE\u4E66\u5F85\u5BA1\u6279\u5DF2\u6279\u51C6 actor=${actorId ?? "-"} \u6743\u9650=${pending.pendingScopes.join(",")}`);
287570
+ } else {
287571
+ if (t - Date.parse(pending.approvedAt) > FEISHU_APPROVED_SUPPLEMENT_KEEP_MS) {
287572
+ await savePendingApproval(store, actorId, pending.at, null);
287573
+ return;
287574
+ }
287575
+ const n = pending.notice;
287576
+ if (!n || n.inApp && n.dm !== "pending" || n.attempts >= MAX_NOTICE_ATTEMPTS) return;
287577
+ next = pending;
287578
+ }
287579
+ await savePendingApproval(store, actorId, pending.at, { ...next, notice: await deliverApprovalNotice(companyId, store, actorId, next, app) });
287580
+ };
287581
+ const checkPendingApprovals = async (companyId, store) => {
287582
+ const svc = await options.actors(companyId);
287583
+ if (!svc) return;
287584
+ const ids2 = [void 0, ...(await svc.listActors({ status: "active" })).map((a) => a.id)];
287585
+ for (const actorId of ids2) {
287586
+ try {
287587
+ await checkPendingApproval(companyId, store, actorId);
287588
+ } catch (err) {
287589
+ options.warn(new Error(`\u98DE\u4E66\u5F85\u5BA1\u6279\u68C0\u6D4B\u5931\u8D25\uFF08\u4E0B\u4E00\u62CD\u91CD\u8BD5\uFF09actor=${actorId ?? "-"}: ${String(err)}`));
287590
+ }
287591
+ }
287592
+ };
287148
287593
  const onFeishuRenewed = async (e, store) => {
287149
287594
  if (await store.revealVariable(FEISHU_USER_OPEN_ID_VAR, e.actorId)) return;
287150
287595
  const accessToken = e.outcome.credentials?.["user_access_token"];
@@ -287183,7 +287628,13 @@ function createConnectorRenewalRuntime(options) {
287183
287628
  onRenewed: async (e, store) => {
287184
287629
  if (e.connector.config.slug === "feishu") await onFeishuRenewed(e, store);
287185
287630
  },
287186
- afterCompany: retryPendingNotices,
287631
+ afterCompany: async (companyId, store) => {
287632
+ try {
287633
+ await retryPendingNotices(companyId, store);
287634
+ } finally {
287635
+ await checkPendingApprovals(companyId, store);
287636
+ }
287637
+ },
287187
287638
  warn: options.warn,
287188
287639
  ...options.now ? { now: options.now } : {}
287189
287640
  });
@@ -287195,7 +287646,7 @@ function createConnectorRenewalRuntime(options) {
287195
287646
  const store = await options.actors(claims.companyId);
287196
287647
  if (!store) return;
287197
287648
  const name = await agentName(claims.companyId, claims.actorId);
287198
- await notifyFeishuUser(store, claims.actorId, `\u300C${name}\u300D\u7684\u98DE\u4E66\u7528\u6237\u6388\u6743\u5DF2\u6062\u590D\u3002`).catch(() => void 0);
287649
+ await notifyFeishuUser(store, claims.actorId, claims.purpose === "supplement" ? `\u300C${name}\u300D\u7684\u98DE\u4E66\u6743\u9650\u5DF2\u8865\u4E0A\uFF0C\u7BA1\u7406\u5458\u6279\u51C6\u7684\u529F\u80FD\u73B0\u5728\u53EF\u4EE5\u4F7F\u7528\u4E86\u3002` : `\u300C${name}\u300D\u7684\u98DE\u4E66\u7528\u6237\u6388\u6743\u5DF2\u6062\u590D\u3002`).catch(() => void 0);
287199
287650
  },
287200
287651
  onRejected: async (claims, who) => {
287201
287652
  const store = await options.actors(claims.companyId);
@@ -301084,7 +301535,7 @@ async function waitForCommandAuthorization(base, headers, draftId, fallback) {
301084
301535
  }
301085
301536
  }
301086
301537
  }
301087
- var CLI_VERSION = "2.2.113" ? "2.2.113" : "dev";
301538
+ var CLI_VERSION = "2.2.115" ? "2.2.115" : "dev";
301088
301539
  function apiHeaders(token2, company) {
301089
301540
  const headers = {
301090
301541
  authorization: `Bearer ${token2}`,
@@ -305255,7 +305706,7 @@ function shimScript() {
305255
305706
  }
305256
305707
 
305257
305708
  // src/index.ts
305258
- var PKG_VERSION = true ? "2.2.113" : "dev";
305709
+ var PKG_VERSION = true ? "2.2.115" : "dev";
305259
305710
  var LOCAL_BIN = localBin();
305260
305711
  var NPM_PREFIX = npmPrefix();
305261
305712
  var INSTANCE = DEFAULT_INSTANCE;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oasis_test_v2",
3
- "version": "2.2.113",
3
+ "version": "2.2.115",
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": "590c9d80c3286fa10fdb0eb14de96140b1520130"
29
+ "sourceHead": "9c025c6edd39870a4e40a29026007109bbec04cf"
30
30
  }
31
31
  }