@max1874/feishu 0.2.17 → 0.2.18

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@max1874/feishu",
3
- "version": "0.2.17",
3
+ "version": "0.2.18",
4
4
  "type": "module",
5
5
  "description": "OpenClaw Feishu/Lark channel plugin",
6
6
  "license": "MIT",
package/src/bot.ts CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  type HistoryEntry,
8
8
  } from "openclaw/plugin-sdk";
9
9
  import type { FeishuConfig, FeishuMessageContext, FeishuMediaInfo } from "./types.js";
10
- import { getFeishuRuntime } from "./runtime.js";
10
+ import { getFeishuRuntime, runWithConversationContext } from "./runtime.js";
11
11
  import { createFeishuClient } from "./client.js";
12
12
  import {
13
13
  resolveFeishuGroupConfig,
@@ -763,12 +763,15 @@ export async function handleFeishuMessage(params: {
763
763
 
764
764
  log(`feishu: dispatching to agent (session=${route.sessionKey})`);
765
765
 
766
- const { queuedFinal, counts } = await core.channel.reply.dispatchReplyFromConfig({
767
- ctx: ctxPayload,
768
- cfg,
769
- dispatcher,
770
- replyOptions,
771
- });
766
+ const { queuedFinal, counts } = await runWithConversationContext(
767
+ { senderOpenId: ctx.senderOpenId, chatId: ctx.chatId, chatType: isGroup ? "group" : "p2p" },
768
+ () => core.channel.reply.dispatchReplyFromConfig({
769
+ ctx: ctxPayload,
770
+ cfg,
771
+ dispatcher,
772
+ replyOptions,
773
+ }),
774
+ );
772
775
 
773
776
  markDispatchIdle();
774
777
 
package/src/docx.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { Type } from "@sinclair/typebox";
2
2
  import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
3
3
  import { createFeishuClient } from "./client.js";
4
+ import { getConversationContext } from "./runtime.js";
4
5
  import type { FeishuConfig } from "./types.js";
5
6
  import type * as Lark from "@larksuiteoapi/node-sdk";
6
7
  import { Readable } from "stream";
@@ -490,6 +491,23 @@ async function setDocPermissionTenantEditable(client: Lark.Client, docToken: str
490
491
 
491
492
  export type DocPermission = "private" | "tenant_editable";
492
493
 
494
+ /** Grant full_access to a user (ou_*) or chat group (oc_*) */
495
+ async function grantFullAccess(client: Lark.Client, docToken: string, memberId: string) {
496
+ const isChat = memberId.startsWith("oc_");
497
+ const res = await client.drive.permissionMember.create({
498
+ path: { token: docToken },
499
+ params: { type: "docx", need_notification: false },
500
+ data: {
501
+ member_type: isChat ? "openchat" : "openid",
502
+ member_id: memberId,
503
+ perm: "full_access",
504
+ type: isChat ? "chat" : "user",
505
+ },
506
+ });
507
+ if (res.code !== 0) throw new Error(res.msg);
508
+ return res.data;
509
+ }
510
+
493
511
  async function createDoc(
494
512
  client: Lark.Client,
495
513
  title: string,
@@ -503,16 +521,30 @@ async function createDoc(
503
521
  const doc = res.data?.document;
504
522
  const docId = doc?.document_id;
505
523
 
506
- // Set permission if requested
524
+ // Set link sharing permission if requested
507
525
  if (permission === "tenant_editable" && docId) {
508
526
  await setDocPermissionTenantEditable(client, docId);
509
527
  }
510
528
 
529
+ // Auto-grant full_access to conversation participant (user in DM, chat group in group)
530
+ let grantedTo: string | undefined;
531
+ const convCtx = getConversationContext();
532
+ if (docId && convCtx) {
533
+ const memberId = convCtx.chatType === "group" ? convCtx.chatId : convCtx.senderOpenId;
534
+ try {
535
+ await grantFullAccess(client, docId, memberId);
536
+ grantedTo = memberId;
537
+ } catch {
538
+ // Non-fatal: doc is created, permission grant failed
539
+ }
540
+ }
541
+
511
542
  return {
512
543
  document_id: docId,
513
544
  title: doc?.title,
514
545
  url: `https://feishu.cn/docx/${docId}`,
515
546
  permission: permission ?? "private",
547
+ ...(grantedTo && { granted_full_access_to: grantedTo }),
516
548
  };
517
549
  }
518
550
 
@@ -854,7 +886,7 @@ export function registerFeishuDocTools(api: OpenClawPluginApi) {
854
886
  name: "feishu_doc_create",
855
887
  label: "Feishu Doc Create",
856
888
  description:
857
- "Create a new empty Feishu document. Use permission='tenant_editable' to allow organization members to edit via link.",
889
+ "Create a new empty Feishu document. Automatically grants full_access (manage) permission to the current conversation participant.",
858
890
  parameters: CreateDocSchema,
859
891
  async execute(_toolCallId, params) {
860
892
  const { title, folder_token, permission } = params as {
package/src/runtime.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { PluginRuntime } from "openclaw/plugin-sdk";
2
+ import { AsyncLocalStorage } from "node:async_hooks";
2
3
 
3
4
  let runtime: PluginRuntime | null = null;
4
5
 
@@ -12,3 +13,21 @@ export function getFeishuRuntime(): PluginRuntime {
12
13
  }
13
14
  return runtime;
14
15
  }
16
+
17
+ // --- Conversation context (tracked via AsyncLocalStorage for concurrency safety) ---
18
+
19
+ export interface FeishuConversationContext {
20
+ senderOpenId: string;
21
+ chatId: string;
22
+ chatType: "group" | "p2p";
23
+ }
24
+
25
+ const conversationStore = new AsyncLocalStorage<FeishuConversationContext>();
26
+
27
+ export function runWithConversationContext<T>(ctx: FeishuConversationContext, fn: () => T): T {
28
+ return conversationStore.run(ctx, fn);
29
+ }
30
+
31
+ export function getConversationContext(): FeishuConversationContext | undefined {
32
+ return conversationStore.getStore();
33
+ }