@nextclaw/channel-plugin-feishu 0.2.29-beta.0 → 0.2.29-beta.1

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 (114) hide show
  1. package/dist/index.d.ts +23 -0
  2. package/dist/index.js +45 -0
  3. package/dist/src/accounts.js +141 -0
  4. package/dist/src/app-scope-checker.js +36 -0
  5. package/dist/src/async.js +34 -0
  6. package/dist/src/auth-errors.js +72 -0
  7. package/dist/src/bitable.js +495 -0
  8. package/dist/src/bot.d.ts +35 -0
  9. package/dist/src/bot.js +941 -0
  10. package/dist/src/calendar-calendar.js +54 -0
  11. package/dist/src/calendar-event-attendee.js +98 -0
  12. package/dist/src/calendar-event.js +193 -0
  13. package/dist/src/calendar-freebusy.js +40 -0
  14. package/dist/src/calendar-shared.js +23 -0
  15. package/dist/src/calendar.js +16 -0
  16. package/dist/src/card-action.js +49 -0
  17. package/dist/src/channel.d.ts +7 -0
  18. package/dist/src/channel.js +413 -0
  19. package/dist/src/chat-schema.js +25 -0
  20. package/dist/src/chat.js +87 -0
  21. package/dist/src/client.d.ts +16 -0
  22. package/dist/src/client.js +112 -0
  23. package/dist/src/config-schema.d.ts +357 -0
  24. package/dist/src/dedup.js +126 -0
  25. package/dist/src/device-flow.js +109 -0
  26. package/dist/src/directory.js +101 -0
  27. package/dist/src/doc-schema.js +148 -0
  28. package/dist/src/docx-batch-insert.js +104 -0
  29. package/dist/src/docx-color-text.js +80 -0
  30. package/dist/src/docx-table-ops.js +197 -0
  31. package/dist/src/docx.js +858 -0
  32. package/dist/src/domains.js +14 -0
  33. package/dist/src/drive-schema.js +41 -0
  34. package/dist/src/drive.js +126 -0
  35. package/dist/src/dynamic-agent.js +93 -0
  36. package/dist/src/external-keys.js +13 -0
  37. package/dist/src/feishu-fetch.js +12 -0
  38. package/dist/src/identity.js +92 -0
  39. package/dist/src/lark-ticket.js +11 -0
  40. package/dist/src/media.d.ts +75 -0
  41. package/dist/src/media.js +304 -0
  42. package/dist/src/mention.d.ts +52 -0
  43. package/dist/src/mention.js +82 -0
  44. package/dist/src/monitor.account.d.ts +1 -0
  45. package/dist/src/monitor.account.js +393 -0
  46. package/dist/src/monitor.d.ts +11 -0
  47. package/dist/src/monitor.js +58 -0
  48. package/dist/src/monitor.startup.js +24 -0
  49. package/dist/src/monitor.state.d.ts +1 -0
  50. package/dist/src/monitor.state.js +80 -0
  51. package/dist/src/monitor.transport.js +167 -0
  52. package/dist/src/nextclaw-sdk/account-id.js +15 -0
  53. package/dist/src/nextclaw-sdk/core-channel.js +150 -0
  54. package/dist/src/nextclaw-sdk/core-pairing.js +151 -0
  55. package/dist/src/nextclaw-sdk/dedupe.js +164 -0
  56. package/dist/src/nextclaw-sdk/feishu.d.ts +1 -0
  57. package/dist/src/nextclaw-sdk/feishu.js +14 -0
  58. package/dist/src/nextclaw-sdk/history.js +69 -0
  59. package/dist/src/nextclaw-sdk/network-body.js +180 -0
  60. package/dist/src/nextclaw-sdk/network-fetch.js +63 -0
  61. package/dist/src/nextclaw-sdk/network-webhook.js +126 -0
  62. package/dist/src/nextclaw-sdk/network.js +4 -0
  63. package/dist/src/nextclaw-sdk/runtime-store.js +21 -0
  64. package/dist/src/nextclaw-sdk/secrets-config.js +65 -0
  65. package/dist/src/nextclaw-sdk/secrets-core.d.ts +1 -0
  66. package/dist/src/nextclaw-sdk/secrets-core.js +68 -0
  67. package/dist/src/nextclaw-sdk/secrets-prompt.js +193 -0
  68. package/dist/src/nextclaw-sdk/secrets.d.ts +1 -0
  69. package/dist/src/nextclaw-sdk/secrets.js +4 -0
  70. package/dist/src/nextclaw-sdk/types.d.ts +242 -0
  71. package/dist/src/oauth.js +171 -0
  72. package/dist/src/onboarding.js +381 -0
  73. package/dist/src/outbound.js +150 -0
  74. package/dist/src/perm-schema.js +49 -0
  75. package/dist/src/perm.js +90 -0
  76. package/dist/src/policy.js +61 -0
  77. package/dist/src/post.js +160 -0
  78. package/dist/src/probe.d.ts +11 -0
  79. package/dist/src/probe.js +85 -0
  80. package/dist/src/raw-request.js +24 -0
  81. package/dist/src/reactions.d.ts +67 -0
  82. package/dist/src/reactions.js +91 -0
  83. package/dist/src/reply-dispatcher.js +250 -0
  84. package/dist/src/runtime.js +5 -0
  85. package/dist/src/secret-input.js +3 -0
  86. package/dist/src/send-result.js +12 -0
  87. package/dist/src/send-target.js +22 -0
  88. package/dist/src/send.d.ts +51 -0
  89. package/dist/src/send.js +265 -0
  90. package/dist/src/sheets-shared.js +193 -0
  91. package/dist/src/sheets.js +95 -0
  92. package/dist/src/streaming-card.js +263 -0
  93. package/dist/src/targets.js +39 -0
  94. package/dist/src/task-comment.js +76 -0
  95. package/dist/src/task-shared.js +13 -0
  96. package/dist/src/task-subtask.js +79 -0
  97. package/dist/src/task-task.js +144 -0
  98. package/dist/src/task-tasklist.js +136 -0
  99. package/dist/src/task.js +16 -0
  100. package/dist/src/token-store.js +154 -0
  101. package/dist/src/tool-account.js +65 -0
  102. package/dist/src/tool-result.js +18 -0
  103. package/dist/src/tool-scopes.js +62 -0
  104. package/dist/src/tools-config.js +30 -0
  105. package/dist/src/types.d.ts +43 -0
  106. package/dist/src/typing.js +145 -0
  107. package/dist/src/uat-client.js +102 -0
  108. package/dist/src/user-tool-client.js +132 -0
  109. package/dist/src/user-tool-helpers.js +110 -0
  110. package/dist/src/user-tool-result.js +10 -0
  111. package/dist/src/wiki-schema.js +45 -0
  112. package/dist/src/wiki.js +144 -0
  113. package/package.json +8 -4
  114. package/index.ts +0 -75
@@ -0,0 +1,79 @@
1
+ import { StringEnum, assertLarkOk, createToolContext, handleInvokeError, json, registerTool } from "./user-tool-helpers.js";
2
+ import { normalizeTaskTime } from "./task-shared.js";
3
+ import { Type } from "@sinclair/typebox";
4
+ //#region src/task-subtask.ts
5
+ const SubtaskSchema = Type.Union([Type.Object({
6
+ action: Type.Literal("create"),
7
+ task_guid: Type.String(),
8
+ summary: Type.String(),
9
+ description: Type.Optional(Type.String()),
10
+ due: Type.Optional(Type.Object({
11
+ timestamp: Type.String(),
12
+ is_all_day: Type.Optional(Type.Boolean())
13
+ })),
14
+ start: Type.Optional(Type.Object({
15
+ timestamp: Type.String(),
16
+ is_all_day: Type.Optional(Type.Boolean())
17
+ })),
18
+ members: Type.Optional(Type.Array(Type.Object({
19
+ id: Type.String(),
20
+ role: Type.Optional(StringEnum(["assignee", "follower"]))
21
+ })))
22
+ }), Type.Object({
23
+ action: Type.Literal("list"),
24
+ task_guid: Type.String(),
25
+ page_size: Type.Optional(Type.Number()),
26
+ page_token: Type.Optional(Type.String())
27
+ })]);
28
+ function registerFeishuTaskSubtaskTool(api) {
29
+ const { toolClient } = createToolContext(api, "feishu_task_subtask");
30
+ registerTool(api, {
31
+ name: "feishu_task_subtask",
32
+ label: "Feishu Task Subtask",
33
+ description: "按本人身份创建、列出任务的子任务。",
34
+ parameters: SubtaskSchema,
35
+ async execute(_toolCallId, params) {
36
+ const payload = params;
37
+ try {
38
+ const client = toolClient();
39
+ if (payload.action === "create") {
40
+ const response = await client.invoke("feishu_task_subtask.create", (sdk, opts) => sdk.task.v2.taskSubtask.create({
41
+ path: { task_guid: payload.task_guid },
42
+ params: { user_id_type: "open_id" },
43
+ data: {
44
+ summary: payload.summary,
45
+ description: payload.description,
46
+ due: normalizeTaskTime(payload.due),
47
+ start: normalizeTaskTime(payload.start),
48
+ members: payload.members?.map((member) => ({
49
+ id: member.id,
50
+ type: "user",
51
+ role: member.role ?? "assignee"
52
+ }))
53
+ }
54
+ }, opts), { as: "user" });
55
+ assertLarkOk(response);
56
+ return json({ subtask: response.data?.subtask });
57
+ }
58
+ const response = await client.invoke("feishu_task_subtask.list", (sdk, opts) => sdk.task.v2.taskSubtask.list({
59
+ path: { task_guid: payload.task_guid },
60
+ params: {
61
+ page_size: payload.page_size,
62
+ page_token: payload.page_token,
63
+ user_id_type: "open_id"
64
+ }
65
+ }, opts), { as: "user" });
66
+ assertLarkOk(response);
67
+ return json({
68
+ subtasks: response.data?.items ?? [],
69
+ has_more: response.data?.has_more ?? false,
70
+ page_token: response.data?.page_token
71
+ });
72
+ } catch (error) {
73
+ return handleInvokeError(error, api);
74
+ }
75
+ }
76
+ }, { name: "feishu_task_subtask" });
77
+ }
78
+ //#endregion
79
+ export { registerFeishuTaskSubtaskTool };
@@ -0,0 +1,144 @@
1
+ import { StringEnum, assertLarkOk, createToolContext, handleInvokeError, json, parseTimeToTimestampMs, registerTool } from "./user-tool-helpers.js";
2
+ import { normalizeTaskTime } from "./task-shared.js";
3
+ import { Type } from "@sinclair/typebox";
4
+ //#region src/task-task.ts
5
+ const TaskSchema = Type.Union([
6
+ Type.Object({
7
+ action: Type.Literal("create"),
8
+ summary: Type.String(),
9
+ current_user_id: Type.Optional(Type.String()),
10
+ description: Type.Optional(Type.String()),
11
+ due: Type.Optional(Type.Object({
12
+ timestamp: Type.String(),
13
+ is_all_day: Type.Optional(Type.Boolean())
14
+ })),
15
+ start: Type.Optional(Type.Object({
16
+ timestamp: Type.String(),
17
+ is_all_day: Type.Optional(Type.Boolean())
18
+ })),
19
+ members: Type.Optional(Type.Array(Type.Object({
20
+ id: Type.String(),
21
+ role: Type.Optional(StringEnum(["assignee", "follower"]))
22
+ }))),
23
+ tasklists: Type.Optional(Type.Array(Type.Object({
24
+ tasklist_guid: Type.String(),
25
+ section_guid: Type.Optional(Type.String())
26
+ }))),
27
+ repeat_rule: Type.Optional(Type.String())
28
+ }),
29
+ Type.Object({
30
+ action: Type.Literal("get"),
31
+ task_guid: Type.String()
32
+ }),
33
+ Type.Object({
34
+ action: Type.Literal("list"),
35
+ page_size: Type.Optional(Type.Number()),
36
+ page_token: Type.Optional(Type.String()),
37
+ completed: Type.Optional(Type.Boolean())
38
+ }),
39
+ Type.Object({
40
+ action: Type.Literal("patch"),
41
+ task_guid: Type.String(),
42
+ summary: Type.Optional(Type.String()),
43
+ description: Type.Optional(Type.String()),
44
+ due: Type.Optional(Type.Object({
45
+ timestamp: Type.String(),
46
+ is_all_day: Type.Optional(Type.Boolean())
47
+ })),
48
+ start: Type.Optional(Type.Object({
49
+ timestamp: Type.String(),
50
+ is_all_day: Type.Optional(Type.Boolean())
51
+ })),
52
+ completed_at: Type.Optional(Type.String()),
53
+ members: Type.Optional(Type.Array(Type.Object({
54
+ id: Type.String(),
55
+ role: Type.Optional(StringEnum(["assignee", "follower"]))
56
+ }))),
57
+ repeat_rule: Type.Optional(Type.String())
58
+ })
59
+ ]);
60
+ function registerFeishuTaskTaskTool(api) {
61
+ const { toolClient } = createToolContext(api, "feishu_task_task");
62
+ registerTool(api, {
63
+ name: "feishu_task_task",
64
+ label: "Feishu Task",
65
+ description: "按本人身份创建、查询、列出、更新飞书任务。",
66
+ parameters: TaskSchema,
67
+ async execute(_toolCallId, params) {
68
+ const payload = params;
69
+ try {
70
+ const client = toolClient();
71
+ if (payload.action === "create") {
72
+ const members = [...payload.members ?? []];
73
+ if (payload.current_user_id && !members.some((member) => member.id === payload.current_user_id)) members.push({
74
+ id: payload.current_user_id,
75
+ role: "follower"
76
+ });
77
+ const response = await client.invoke("feishu_task_task.create", (sdk, opts) => sdk.task.v2.task.create({
78
+ params: { user_id_type: "open_id" },
79
+ data: {
80
+ summary: payload.summary,
81
+ description: payload.description,
82
+ due: normalizeTaskTime(payload.due),
83
+ start: normalizeTaskTime(payload.start),
84
+ repeat_rule: payload.repeat_rule,
85
+ members: members.length ? members.map((member) => ({
86
+ id: member.id,
87
+ type: "user",
88
+ role: member.role ?? "assignee"
89
+ })) : void 0,
90
+ tasklists: payload.tasklists
91
+ }
92
+ }, opts), { as: "user" });
93
+ assertLarkOk(response);
94
+ return json({ task: response.data?.task });
95
+ }
96
+ if (payload.action === "get") {
97
+ const response = await client.invoke("feishu_task_task.get", (sdk, opts) => sdk.task.v2.task.get({
98
+ path: { task_guid: payload.task_guid },
99
+ params: { user_id_type: "open_id" }
100
+ }, opts), { as: "user" });
101
+ assertLarkOk(response);
102
+ return json({ task: response.data?.task });
103
+ }
104
+ if (payload.action === "list") {
105
+ const response = await client.invoke("feishu_task_task.list", (sdk, opts) => sdk.task.v2.task.list({ params: {
106
+ page_size: payload.page_size,
107
+ page_token: payload.page_token,
108
+ completed: payload.completed,
109
+ user_id_type: "open_id"
110
+ } }, opts), { as: "user" });
111
+ assertLarkOk(response);
112
+ return json({
113
+ tasks: response.data?.items ?? [],
114
+ has_more: response.data?.has_more ?? false,
115
+ page_token: response.data?.page_token
116
+ });
117
+ }
118
+ const patchData = {};
119
+ if (payload.summary) patchData.summary = payload.summary;
120
+ if (payload.description) patchData.description = payload.description;
121
+ if (payload.due) patchData.due = normalizeTaskTime(payload.due);
122
+ if (payload.start) patchData.start = normalizeTaskTime(payload.start);
123
+ if (payload.repeat_rule) patchData.repeat_rule = payload.repeat_rule;
124
+ if (payload.completed_at !== void 0) patchData.completed_at = payload.completed_at === "0" ? "0" : /^\d+$/.test(payload.completed_at) ? payload.completed_at : parseTimeToTimestampMs(payload.completed_at);
125
+ if (payload.members) patchData.members = payload.members.map((member) => ({
126
+ id: member.id,
127
+ type: "user",
128
+ role: member.role ?? "assignee"
129
+ }));
130
+ const response = await client.invoke("feishu_task_task.patch", (sdk, opts) => sdk.task.v2.task.patch({
131
+ path: { task_guid: payload.task_guid },
132
+ params: { user_id_type: "open_id" },
133
+ data: patchData
134
+ }, opts), { as: "user" });
135
+ assertLarkOk(response);
136
+ return json({ task: response.data?.task });
137
+ } catch (error) {
138
+ return handleInvokeError(error, api);
139
+ }
140
+ }
141
+ }, { name: "feishu_task_task" });
142
+ }
143
+ //#endregion
144
+ export { registerFeishuTaskTaskTool };
@@ -0,0 +1,136 @@
1
+ import { StringEnum, assertLarkOk, createToolContext, handleInvokeError, json, registerTool } from "./user-tool-helpers.js";
2
+ import { Type } from "@sinclair/typebox";
3
+ //#region src/task-tasklist.ts
4
+ const TasklistSchema = Type.Union([
5
+ Type.Object({
6
+ action: Type.Literal("create"),
7
+ name: Type.String(),
8
+ members: Type.Optional(Type.Array(Type.Object({
9
+ id: Type.String(),
10
+ role: Type.Optional(StringEnum(["editor", "viewer"]))
11
+ })))
12
+ }),
13
+ Type.Object({
14
+ action: Type.Literal("get"),
15
+ tasklist_guid: Type.String()
16
+ }),
17
+ Type.Object({
18
+ action: Type.Literal("list"),
19
+ page_size: Type.Optional(Type.Number()),
20
+ page_token: Type.Optional(Type.String())
21
+ }),
22
+ Type.Object({
23
+ action: Type.Literal("tasks"),
24
+ tasklist_guid: Type.String(),
25
+ page_size: Type.Optional(Type.Number()),
26
+ page_token: Type.Optional(Type.String()),
27
+ completed: Type.Optional(Type.Boolean())
28
+ }),
29
+ Type.Object({
30
+ action: Type.Literal("patch"),
31
+ tasklist_guid: Type.String(),
32
+ name: Type.Optional(Type.String())
33
+ }),
34
+ Type.Object({
35
+ action: Type.Literal("add_members"),
36
+ tasklist_guid: Type.String(),
37
+ members: Type.Array(Type.Object({
38
+ id: Type.String(),
39
+ role: Type.Optional(StringEnum(["editor", "viewer"]))
40
+ }))
41
+ })
42
+ ]);
43
+ function registerFeishuTaskTasklistTool(api) {
44
+ const { toolClient } = createToolContext(api, "feishu_task_tasklist");
45
+ registerTool(api, {
46
+ name: "feishu_task_tasklist",
47
+ label: "Feishu Tasklist",
48
+ description: "按本人身份创建、查询和管理飞书任务清单。",
49
+ parameters: TasklistSchema,
50
+ async execute(_toolCallId, params) {
51
+ const payload = params;
52
+ try {
53
+ const client = toolClient();
54
+ if (payload.action === "create") {
55
+ const response = await client.invoke("feishu_task_tasklist.create", (sdk, opts) => sdk.task.v2.tasklist.create({
56
+ params: { user_id_type: "open_id" },
57
+ data: {
58
+ name: payload.name,
59
+ members: payload.members?.map((member) => ({
60
+ id: member.id,
61
+ type: "user",
62
+ role: member.role ?? "editor"
63
+ }))
64
+ }
65
+ }, opts), { as: "user" });
66
+ assertLarkOk(response);
67
+ return json({ tasklist: response.data?.tasklist });
68
+ }
69
+ if (payload.action === "get") {
70
+ const response = await client.invoke("feishu_task_tasklist.get", (sdk, opts) => sdk.task.v2.tasklist.get({
71
+ path: { tasklist_guid: payload.tasklist_guid },
72
+ params: { user_id_type: "open_id" }
73
+ }, opts), { as: "user" });
74
+ assertLarkOk(response);
75
+ return json({ tasklist: response.data?.tasklist });
76
+ }
77
+ if (payload.action === "list") {
78
+ const response = await client.invoke("feishu_task_tasklist.list", (sdk, opts) => sdk.task.v2.tasklist.list({ params: {
79
+ page_size: payload.page_size,
80
+ page_token: payload.page_token,
81
+ user_id_type: "open_id"
82
+ } }, opts), { as: "user" });
83
+ assertLarkOk(response);
84
+ return json({
85
+ tasklists: response.data?.items ?? [],
86
+ has_more: response.data?.has_more ?? false,
87
+ page_token: response.data?.page_token
88
+ });
89
+ }
90
+ if (payload.action === "tasks") {
91
+ const response = await client.invoke("feishu_task_tasklist.tasks", (sdk, opts) => sdk.task.v2.tasklist.tasks({
92
+ path: { tasklist_guid: payload.tasklist_guid },
93
+ params: {
94
+ page_size: payload.page_size,
95
+ page_token: payload.page_token,
96
+ completed: payload.completed,
97
+ user_id_type: "open_id"
98
+ }
99
+ }, opts), { as: "user" });
100
+ assertLarkOk(response);
101
+ return json({
102
+ tasks: response.data?.items ?? [],
103
+ has_more: response.data?.has_more ?? false,
104
+ page_token: response.data?.page_token
105
+ });
106
+ }
107
+ if (payload.action === "patch") {
108
+ const response = await client.invoke("feishu_task_tasklist.patch", (sdk, opts) => sdk.task.v2.tasklist.patch({
109
+ path: { tasklist_guid: payload.tasklist_guid },
110
+ params: { user_id_type: "open_id" },
111
+ data: { name: payload.name }
112
+ }, opts), { as: "user" });
113
+ assertLarkOk(response);
114
+ return json({ tasklist: response.data?.tasklist });
115
+ }
116
+ assertLarkOk(await client.invoke("feishu_task_tasklist.add_members", (sdk, opts) => sdk.task.v2.tasklist.addMembers({
117
+ path: { tasklist_guid: payload.tasklist_guid },
118
+ params: { user_id_type: "open_id" },
119
+ data: { members: (payload.members ?? []).map((member) => ({
120
+ id: member.id,
121
+ type: "user",
122
+ role: member.role ?? "editor"
123
+ })) }
124
+ }, opts), { as: "user" }));
125
+ return json({
126
+ success: true,
127
+ tasklist_guid: payload.tasklist_guid
128
+ });
129
+ } catch (error) {
130
+ return handleInvokeError(error, api);
131
+ }
132
+ }
133
+ }, { name: "feishu_task_tasklist" });
134
+ }
135
+ //#endregion
136
+ export { registerFeishuTaskTasklistTool };
@@ -0,0 +1,16 @@
1
+ import { resolveRegisteredFeishuToolsConfig } from "./tool-account.js";
2
+ import { registerFeishuTaskCommentTool } from "./task-comment.js";
3
+ import { registerFeishuTaskSubtaskTool } from "./task-subtask.js";
4
+ import { registerFeishuTaskTaskTool } from "./task-task.js";
5
+ import { registerFeishuTaskTasklistTool } from "./task-tasklist.js";
6
+ //#region src/task.ts
7
+ function registerFeishuTaskTools(api) {
8
+ if (!api.config) return;
9
+ if (!resolveRegisteredFeishuToolsConfig(api.config).task) return;
10
+ registerFeishuTaskTaskTool(api);
11
+ registerFeishuTaskTasklistTool(api);
12
+ registerFeishuTaskCommentTool(api);
13
+ registerFeishuTaskSubtaskTool(api);
14
+ }
15
+ //#endregion
16
+ export { registerFeishuTaskTools };
@@ -0,0 +1,154 @@
1
+ import { join } from "node:path";
2
+ import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
3
+ import { homedir } from "node:os";
4
+ import { chmod, mkdir, readFile, unlink, writeFile } from "node:fs/promises";
5
+ import { execFile } from "node:child_process";
6
+ import { promisify } from "node:util";
7
+ //#region src/token-store.ts
8
+ const execFile$1 = promisify(execFile);
9
+ const KEYCHAIN_SERVICE = "nextclaw-feishu-uat";
10
+ const REFRESH_AHEAD_MS = 300 * 1e3;
11
+ function accountKey(appId, userOpenId) {
12
+ return `${appId}:${userOpenId}`;
13
+ }
14
+ const darwinBackend = {
15
+ async get(service, account) {
16
+ try {
17
+ const { stdout } = await execFile$1("security", [
18
+ "find-generic-password",
19
+ "-s",
20
+ service,
21
+ "-a",
22
+ account,
23
+ "-w"
24
+ ]);
25
+ return stdout.trim() || null;
26
+ } catch {
27
+ return null;
28
+ }
29
+ },
30
+ async set(service, account, data) {
31
+ try {
32
+ await execFile$1("security", [
33
+ "delete-generic-password",
34
+ "-s",
35
+ service,
36
+ "-a",
37
+ account
38
+ ]);
39
+ } catch {}
40
+ await execFile$1("security", [
41
+ "add-generic-password",
42
+ "-s",
43
+ service,
44
+ "-a",
45
+ account,
46
+ "-w",
47
+ data
48
+ ]);
49
+ },
50
+ async remove(service, account) {
51
+ try {
52
+ await execFile$1("security", [
53
+ "delete-generic-password",
54
+ "-s",
55
+ service,
56
+ "-a",
57
+ account
58
+ ]);
59
+ } catch {}
60
+ }
61
+ };
62
+ const STORAGE_DIR = join(process.platform === "win32" ? process.env.LOCALAPPDATA ?? join(process.env.USERPROFILE ?? homedir(), "AppData", "Local") : process.env.XDG_DATA_HOME ?? join(homedir(), ".local", "share"), KEYCHAIN_SERVICE);
63
+ const MASTER_KEY_PATH = join(STORAGE_DIR, "master.key");
64
+ const MASTER_KEY_BYTES = 32;
65
+ const IV_BYTES = 12;
66
+ const TAG_BYTES = 16;
67
+ function safeFileName(account) {
68
+ return account.replace(/[^a-zA-Z0-9._-]/g, "_") + ".enc";
69
+ }
70
+ async function ensureStorageDir() {
71
+ await mkdir(STORAGE_DIR, {
72
+ recursive: true,
73
+ mode: 448
74
+ });
75
+ }
76
+ async function getMasterKey() {
77
+ try {
78
+ const key = await readFile(MASTER_KEY_PATH);
79
+ if (key.length === MASTER_KEY_BYTES) return key;
80
+ } catch {}
81
+ await ensureStorageDir();
82
+ const key = randomBytes(MASTER_KEY_BYTES);
83
+ await writeFile(MASTER_KEY_PATH, key, { mode: 384 });
84
+ if (process.platform !== "win32") await chmod(MASTER_KEY_PATH, 384);
85
+ return key;
86
+ }
87
+ function encryptData(plaintext, key) {
88
+ const iv = randomBytes(IV_BYTES);
89
+ const cipher = createCipheriv("aes-256-gcm", key, iv);
90
+ const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
91
+ return Buffer.concat([
92
+ iv,
93
+ cipher.getAuthTag(),
94
+ encrypted
95
+ ]);
96
+ }
97
+ function decryptData(data, key) {
98
+ if (data.length < IV_BYTES + TAG_BYTES) return null;
99
+ try {
100
+ const iv = data.subarray(0, IV_BYTES);
101
+ const tag = data.subarray(IV_BYTES, IV_BYTES + TAG_BYTES);
102
+ const encrypted = data.subarray(IV_BYTES + TAG_BYTES);
103
+ const decipher = createDecipheriv("aes-256-gcm", key, iv);
104
+ decipher.setAuthTag(tag);
105
+ return Buffer.concat([decipher.update(encrypted), decipher.final()]).toString("utf8");
106
+ } catch {
107
+ return null;
108
+ }
109
+ }
110
+ const backend = process.platform === "darwin" ? darwinBackend : {
111
+ async get(_service, account) {
112
+ try {
113
+ const key = await getMasterKey();
114
+ return decryptData(await readFile(join(STORAGE_DIR, safeFileName(account))), key);
115
+ } catch {
116
+ return null;
117
+ }
118
+ },
119
+ async set(_service, account, data) {
120
+ const key = await getMasterKey();
121
+ await ensureStorageDir();
122
+ const filePath = join(STORAGE_DIR, safeFileName(account));
123
+ await writeFile(filePath, encryptData(data, key), { mode: 384 });
124
+ if (process.platform !== "win32") await chmod(filePath, 384);
125
+ },
126
+ async remove(_service, account) {
127
+ try {
128
+ await unlink(join(STORAGE_DIR, safeFileName(account)));
129
+ } catch {}
130
+ }
131
+ };
132
+ async function getStoredToken(appId, userOpenId) {
133
+ try {
134
+ const json = await backend.get(KEYCHAIN_SERVICE, accountKey(appId, userOpenId));
135
+ return json ? JSON.parse(json) : null;
136
+ } catch {
137
+ return null;
138
+ }
139
+ }
140
+ async function setStoredToken(token) {
141
+ const payload = JSON.stringify(token);
142
+ await backend.set(KEYCHAIN_SERVICE, accountKey(token.appId, token.userOpenId), payload);
143
+ }
144
+ async function removeStoredToken(appId, userOpenId) {
145
+ await backend.remove(KEYCHAIN_SERVICE, accountKey(appId, userOpenId));
146
+ }
147
+ function tokenStatus(token) {
148
+ const now = Date.now();
149
+ if (now < token.expiresAt - REFRESH_AHEAD_MS) return "valid";
150
+ if (now < token.refreshExpiresAt) return "needs_refresh";
151
+ return "expired";
152
+ }
153
+ //#endregion
154
+ export { getStoredToken, removeStoredToken, setStoredToken, tokenStatus };
@@ -0,0 +1,65 @@
1
+ import { listFeishuAccountIds, resolveFeishuAccount } from "./accounts.js";
2
+ import { createFeishuClient } from "./client.js";
3
+ import { getTicket } from "./lark-ticket.js";
4
+ import { resolveToolsConfig } from "./tools-config.js";
5
+ //#region src/tool-account.ts
6
+ function normalizeOptionalAccountId(value) {
7
+ const trimmed = value?.trim();
8
+ return trimmed ? trimmed : void 0;
9
+ }
10
+ function readConfiguredDefaultAccountId(config) {
11
+ const value = (config?.channels?.feishu)?.defaultAccount;
12
+ if (typeof value !== "string") return;
13
+ return normalizeOptionalAccountId(value);
14
+ }
15
+ function readTicketAccountId() {
16
+ return normalizeOptionalAccountId(getTicket()?.accountId);
17
+ }
18
+ function resolveFeishuToolAccount(params) {
19
+ if (!params.api.config) throw new Error("Feishu config unavailable");
20
+ return resolveFeishuAccount({
21
+ cfg: params.api.config,
22
+ accountId: normalizeOptionalAccountId(params.executeParams?.accountId) ?? readTicketAccountId() ?? readConfiguredDefaultAccountId(params.api.config) ?? normalizeOptionalAccountId(params.defaultAccountId)
23
+ });
24
+ }
25
+ function createFeishuToolClient(params) {
26
+ return createFeishuClient(resolveFeishuToolAccount(params));
27
+ }
28
+ function resolveAnyEnabledFeishuToolsConfig(accounts) {
29
+ const merged = {
30
+ doc: false,
31
+ chat: false,
32
+ wiki: false,
33
+ drive: false,
34
+ perm: false,
35
+ scopes: false,
36
+ calendar: false,
37
+ task: false,
38
+ sheets: false,
39
+ oauth: false,
40
+ identity: false
41
+ };
42
+ for (const account of accounts) {
43
+ const cfg = resolveToolsConfig(account.config.tools);
44
+ merged.doc = merged.doc || cfg.doc;
45
+ merged.chat = merged.chat || cfg.chat;
46
+ merged.wiki = merged.wiki || cfg.wiki;
47
+ merged.drive = merged.drive || cfg.drive;
48
+ merged.perm = merged.perm || cfg.perm;
49
+ merged.scopes = merged.scopes || cfg.scopes;
50
+ merged.calendar = merged.calendar || cfg.calendar;
51
+ merged.task = merged.task || cfg.task;
52
+ merged.sheets = merged.sheets || cfg.sheets;
53
+ merged.oauth = merged.oauth || cfg.oauth;
54
+ merged.identity = merged.identity || cfg.identity;
55
+ }
56
+ return merged;
57
+ }
58
+ function resolveRegisteredFeishuToolsConfig(config) {
59
+ return resolveAnyEnabledFeishuToolsConfig(listFeishuAccountIds(config).map((accountId) => resolveFeishuAccount({
60
+ cfg: config,
61
+ accountId
62
+ })));
63
+ }
64
+ //#endregion
65
+ export { createFeishuToolClient, resolveFeishuToolAccount, resolveRegisteredFeishuToolsConfig };
@@ -0,0 +1,18 @@
1
+ //#region src/tool-result.ts
2
+ function jsonToolResult(data) {
3
+ return {
4
+ content: [{
5
+ type: "text",
6
+ text: JSON.stringify(data, null, 2)
7
+ }],
8
+ details: data
9
+ };
10
+ }
11
+ function unknownToolActionResult(action) {
12
+ return jsonToolResult({ error: `Unknown action: ${String(action)}` });
13
+ }
14
+ function toolExecutionErrorResult(error) {
15
+ return jsonToolResult({ error: error instanceof Error ? error.message : String(error) });
16
+ }
17
+ //#endregion
18
+ export { jsonToolResult, toolExecutionErrorResult, unknownToolActionResult };
@@ -0,0 +1,62 @@
1
+ //#region src/tool-scopes.ts
2
+ const TOOL_SCOPES = {
3
+ "feishu_get_user.default": ["contact:contact.base:readonly", "contact:user.base:readonly"],
4
+ "feishu_search_user.default": ["contact:user:search"],
5
+ "feishu_calendar_calendar.list": ["calendar:calendar:read"],
6
+ "feishu_calendar_calendar.get": ["calendar:calendar:read"],
7
+ "feishu_calendar_calendar.primary": ["calendar:calendar:read"],
8
+ "feishu_calendar_event.create": ["calendar:calendar.event:create", "calendar:calendar.event:update"],
9
+ "feishu_calendar_event.list": ["calendar:calendar.event:read"],
10
+ "feishu_calendar_event.get": ["calendar:calendar.event:read"],
11
+ "feishu_calendar_event.patch": ["calendar:calendar.event:update"],
12
+ "feishu_calendar_event.delete": ["calendar:calendar.event:delete"],
13
+ "feishu_calendar_event.instance_view": ["calendar:calendar.event:read"],
14
+ "feishu_calendar_event_attendee.create": ["calendar:calendar.event:update"],
15
+ "feishu_calendar_event_attendee.list": ["calendar:calendar.event:read"],
16
+ "feishu_calendar_freebusy.list": ["calendar:calendar.free_busy:read"],
17
+ "feishu_task_task.create": ["task:task:write", "task:task:writeonly"],
18
+ "feishu_task_task.get": ["task:task:read", "task:task:write"],
19
+ "feishu_task_task.list": ["task:task:read", "task:task:write"],
20
+ "feishu_task_task.patch": ["task:task:write", "task:task:writeonly"],
21
+ "feishu_task_tasklist.create": ["task:tasklist:write"],
22
+ "feishu_task_tasklist.get": ["task:tasklist:read", "task:tasklist:write"],
23
+ "feishu_task_tasklist.list": ["task:tasklist:read", "task:tasklist:write"],
24
+ "feishu_task_tasklist.tasks": ["task:tasklist:read", "task:tasklist:write"],
25
+ "feishu_task_tasklist.patch": ["task:tasklist:write"],
26
+ "feishu_task_tasklist.add_members": ["task:tasklist:write"],
27
+ "feishu_task_comment.create": ["task:comment:write"],
28
+ "feishu_task_comment.get": ["task:comment:read", "task:comment:write"],
29
+ "feishu_task_comment.list": ["task:comment:read", "task:comment:write"],
30
+ "feishu_task_subtask.create": ["task:task:write"],
31
+ "feishu_task_subtask.list": ["task:task:read", "task:task:write"],
32
+ "feishu_sheet.info": ["sheets:spreadsheet.meta:read", "sheets:spreadsheet:read"],
33
+ "feishu_sheet.read": ["sheets:spreadsheet.meta:read", "sheets:spreadsheet:read"],
34
+ "feishu_sheet.write": [
35
+ "sheets:spreadsheet.meta:read",
36
+ "sheets:spreadsheet:read",
37
+ "sheets:spreadsheet:create",
38
+ "sheets:spreadsheet:write_only"
39
+ ],
40
+ "feishu_sheet.append": [
41
+ "sheets:spreadsheet.meta:read",
42
+ "sheets:spreadsheet:read",
43
+ "sheets:spreadsheet:create",
44
+ "sheets:spreadsheet:write_only"
45
+ ],
46
+ "feishu_sheet.find": ["sheets:spreadsheet.meta:read", "sheets:spreadsheet:read"],
47
+ "feishu_sheet.create": [
48
+ "sheets:spreadsheet.meta:read",
49
+ "sheets:spreadsheet:read",
50
+ "sheets:spreadsheet:create",
51
+ "sheets:spreadsheet:write_only"
52
+ ],
53
+ "feishu_sheet.export": ["docs:document:export"]
54
+ };
55
+ function getRequiredScopes(toolAction) {
56
+ return TOOL_SCOPES[toolAction] ?? [];
57
+ }
58
+ function getAllKnownScopes() {
59
+ return Array.from(new Set(Object.values(TOOL_SCOPES).flat())).sort();
60
+ }
61
+ //#endregion
62
+ export { getAllKnownScopes, getRequiredScopes };