@gakr-gakr/feishu 0.1.0

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 (133) hide show
  1. package/api.ts +32 -0
  2. package/autobot.plugin.json +180 -0
  3. package/channel-entry.ts +20 -0
  4. package/channel-plugin-api.ts +1 -0
  5. package/contract-api.ts +16 -0
  6. package/index.ts +82 -0
  7. package/package.json +62 -0
  8. package/runtime-api.ts +55 -0
  9. package/secret-contract-api.ts +5 -0
  10. package/security-contract-api.ts +1 -0
  11. package/session-key-api.ts +1 -0
  12. package/setup-api.ts +3 -0
  13. package/setup-entry.ts +13 -0
  14. package/skills/feishu-doc/SKILL.md +211 -0
  15. package/skills/feishu-doc/references/block-types.md +103 -0
  16. package/skills/feishu-drive/SKILL.md +97 -0
  17. package/skills/feishu-perm/SKILL.md +119 -0
  18. package/skills/feishu-wiki/SKILL.md +113 -0
  19. package/src/accounts.ts +333 -0
  20. package/src/agent-config.ts +21 -0
  21. package/src/app-registration.ts +331 -0
  22. package/src/approval-auth.ts +25 -0
  23. package/src/async.ts +104 -0
  24. package/src/audio-preflight.runtime.ts +9 -0
  25. package/src/bitable.ts +762 -0
  26. package/src/bot-content.ts +485 -0
  27. package/src/bot-runtime-api.ts +12 -0
  28. package/src/bot-sender-name.ts +125 -0
  29. package/src/bot.ts +1703 -0
  30. package/src/card-action.ts +447 -0
  31. package/src/card-interaction.ts +159 -0
  32. package/src/card-test-helpers.ts +54 -0
  33. package/src/card-ux-approval.ts +65 -0
  34. package/src/card-ux-launcher.ts +121 -0
  35. package/src/card-ux-shared.ts +33 -0
  36. package/src/channel-runtime-api.ts +16 -0
  37. package/src/channel.runtime.ts +47 -0
  38. package/src/channel.ts +1423 -0
  39. package/src/chat-schema.ts +25 -0
  40. package/src/chat.ts +188 -0
  41. package/src/client-timeout.ts +42 -0
  42. package/src/client.ts +262 -0
  43. package/src/comment-dispatcher-runtime-api.ts +6 -0
  44. package/src/comment-dispatcher.ts +107 -0
  45. package/src/comment-handler-runtime-api.ts +3 -0
  46. package/src/comment-handler.ts +303 -0
  47. package/src/comment-reaction.ts +259 -0
  48. package/src/comment-shared.ts +406 -0
  49. package/src/comment-target.ts +44 -0
  50. package/src/config-schema.ts +335 -0
  51. package/src/conversation-id.ts +199 -0
  52. package/src/dedup-runtime-api.ts +1 -0
  53. package/src/dedup.ts +141 -0
  54. package/src/dedupe-key.ts +72 -0
  55. package/src/directory.static.ts +61 -0
  56. package/src/directory.ts +124 -0
  57. package/src/doc-schema.ts +182 -0
  58. package/src/docx-batch-insert.ts +223 -0
  59. package/src/docx-color-text.ts +154 -0
  60. package/src/docx-table-ops.ts +316 -0
  61. package/src/docx-types.ts +38 -0
  62. package/src/docx.ts +1596 -0
  63. package/src/drive-schema.ts +92 -0
  64. package/src/drive.ts +829 -0
  65. package/src/dynamic-agent.ts +143 -0
  66. package/src/event-types.ts +45 -0
  67. package/src/external-keys.ts +19 -0
  68. package/src/lifecycle.test-support.ts +220 -0
  69. package/src/media.ts +1105 -0
  70. package/src/mention-target.types.ts +5 -0
  71. package/src/mention.ts +114 -0
  72. package/src/message-action-contract.ts +13 -0
  73. package/src/monitor-state-runtime-api.ts +7 -0
  74. package/src/monitor-transport-runtime-api.ts +10 -0
  75. package/src/monitor.account.ts +492 -0
  76. package/src/monitor.acp-init-failure.lifecycle.test-support.ts +219 -0
  77. package/src/monitor.bot-identity.ts +86 -0
  78. package/src/monitor.bot-menu-handler.ts +165 -0
  79. package/src/monitor.bot-menu.lifecycle.test-support.ts +224 -0
  80. package/src/monitor.broadcast.reply-once.lifecycle.test-support.ts +264 -0
  81. package/src/monitor.card-action.lifecycle.test-support.ts +421 -0
  82. package/src/monitor.comment-notice-handler.ts +105 -0
  83. package/src/monitor.comment.ts +1386 -0
  84. package/src/monitor.message-handler.ts +350 -0
  85. package/src/monitor.reaction.lifecycle.test-support.ts +68 -0
  86. package/src/monitor.startup.ts +74 -0
  87. package/src/monitor.state.ts +170 -0
  88. package/src/monitor.synthetic-error.ts +18 -0
  89. package/src/monitor.test-mocks.ts +46 -0
  90. package/src/monitor.transport.ts +451 -0
  91. package/src/monitor.ts +100 -0
  92. package/src/outbound-runtime-api.ts +1 -0
  93. package/src/outbound.ts +785 -0
  94. package/src/perm-schema.ts +52 -0
  95. package/src/perm.ts +170 -0
  96. package/src/pins.ts +108 -0
  97. package/src/policy.ts +321 -0
  98. package/src/post.ts +275 -0
  99. package/src/probe.ts +166 -0
  100. package/src/processing-claims.ts +59 -0
  101. package/src/qr-terminal.ts +1 -0
  102. package/src/reactions.ts +123 -0
  103. package/src/reasoning-preview.ts +28 -0
  104. package/src/reply-dispatcher-runtime-api.ts +7 -0
  105. package/src/reply-dispatcher.ts +748 -0
  106. package/src/runtime.ts +9 -0
  107. package/src/secret-contract.ts +145 -0
  108. package/src/secret-input.ts +1 -0
  109. package/src/security-audit-shared.ts +69 -0
  110. package/src/security-audit.ts +1 -0
  111. package/src/send-result.ts +80 -0
  112. package/src/send-target.ts +35 -0
  113. package/src/send.ts +861 -0
  114. package/src/sequential-key.ts +28 -0
  115. package/src/sequential-queue.ts +86 -0
  116. package/src/session-conversation.ts +42 -0
  117. package/src/session-route.ts +48 -0
  118. package/src/setup-core.ts +51 -0
  119. package/src/setup-surface.ts +618 -0
  120. package/src/streaming-card.ts +571 -0
  121. package/src/subagent-hooks.ts +413 -0
  122. package/src/targets.ts +97 -0
  123. package/src/thread-bindings.ts +331 -0
  124. package/src/tool-account.ts +93 -0
  125. package/src/tool-factory-test-harness.ts +79 -0
  126. package/src/tool-result.ts +16 -0
  127. package/src/tools-config.ts +22 -0
  128. package/src/types.ts +106 -0
  129. package/src/typing.ts +214 -0
  130. package/src/wiki-schema.ts +69 -0
  131. package/src/wiki.ts +270 -0
  132. package/subagent-hooks-api.ts +31 -0
  133. package/tsconfig.json +16 -0
package/src/runtime.ts ADDED
@@ -0,0 +1,9 @@
1
+ import type { PluginRuntime } from "autobot/plugin-sdk/core";
2
+ import { createPluginRuntimeStore } from "autobot/plugin-sdk/runtime-store";
3
+
4
+ const { setRuntime: setFeishuRuntime, getRuntime: getFeishuRuntime } =
5
+ createPluginRuntimeStore<PluginRuntime>({
6
+ pluginId: "feishu",
7
+ errorMessage: "Feishu runtime not initialized",
8
+ });
9
+ export { getFeishuRuntime, setFeishuRuntime };
@@ -0,0 +1,145 @@
1
+ import {
2
+ collectConditionalChannelFieldAssignments,
3
+ collectSimpleChannelFieldAssignments,
4
+ getChannelSurface,
5
+ hasOwnProperty,
6
+ normalizeSecretStringValue,
7
+ type ResolverContext,
8
+ type SecretDefaults,
9
+ type SecretTargetRegistryEntry,
10
+ } from "autobot/plugin-sdk/channel-secret-basic-runtime";
11
+
12
+ export const secretTargetRegistryEntries: SecretTargetRegistryEntry[] = [
13
+ {
14
+ id: "channels.feishu.accounts.*.appSecret",
15
+ targetType: "channels.feishu.accounts.*.appSecret",
16
+ configFile: "autobot.json",
17
+ pathPattern: "channels.feishu.accounts.*.appSecret",
18
+ secretShape: "secret_input",
19
+ expectedResolvedValue: "string",
20
+ includeInPlan: true,
21
+ includeInConfigure: true,
22
+ includeInAudit: true,
23
+ },
24
+ {
25
+ id: "channels.feishu.accounts.*.encryptKey",
26
+ targetType: "channels.feishu.accounts.*.encryptKey",
27
+ configFile: "autobot.json",
28
+ pathPattern: "channels.feishu.accounts.*.encryptKey",
29
+ secretShape: "secret_input",
30
+ expectedResolvedValue: "string",
31
+ includeInPlan: true,
32
+ includeInConfigure: true,
33
+ includeInAudit: true,
34
+ },
35
+ {
36
+ id: "channels.feishu.accounts.*.verificationToken",
37
+ targetType: "channels.feishu.accounts.*.verificationToken",
38
+ configFile: "autobot.json",
39
+ pathPattern: "channels.feishu.accounts.*.verificationToken",
40
+ secretShape: "secret_input",
41
+ expectedResolvedValue: "string",
42
+ includeInPlan: true,
43
+ includeInConfigure: true,
44
+ includeInAudit: true,
45
+ },
46
+ {
47
+ id: "channels.feishu.appSecret",
48
+ targetType: "channels.feishu.appSecret",
49
+ configFile: "autobot.json",
50
+ pathPattern: "channels.feishu.appSecret",
51
+ secretShape: "secret_input",
52
+ expectedResolvedValue: "string",
53
+ includeInPlan: true,
54
+ includeInConfigure: true,
55
+ includeInAudit: true,
56
+ },
57
+ {
58
+ id: "channels.feishu.encryptKey",
59
+ targetType: "channels.feishu.encryptKey",
60
+ configFile: "autobot.json",
61
+ pathPattern: "channels.feishu.encryptKey",
62
+ secretShape: "secret_input",
63
+ expectedResolvedValue: "string",
64
+ includeInPlan: true,
65
+ includeInConfigure: true,
66
+ includeInAudit: true,
67
+ },
68
+ {
69
+ id: "channels.feishu.verificationToken",
70
+ targetType: "channels.feishu.verificationToken",
71
+ configFile: "autobot.json",
72
+ pathPattern: "channels.feishu.verificationToken",
73
+ secretShape: "secret_input",
74
+ expectedResolvedValue: "string",
75
+ includeInPlan: true,
76
+ includeInConfigure: true,
77
+ includeInAudit: true,
78
+ },
79
+ ];
80
+
81
+ export function collectRuntimeConfigAssignments(params: {
82
+ config: { channels?: Record<string, unknown> };
83
+ defaults?: SecretDefaults;
84
+ context: ResolverContext;
85
+ }): void {
86
+ const resolved = getChannelSurface(params.config, "feishu");
87
+ if (!resolved) {
88
+ return;
89
+ }
90
+ const { channel: feishu, surface } = resolved;
91
+ collectSimpleChannelFieldAssignments({
92
+ channelKey: "feishu",
93
+ field: "appSecret",
94
+ channel: feishu,
95
+ surface,
96
+ defaults: params.defaults,
97
+ context: params.context,
98
+ topInactiveReason: "no enabled account inherits this top-level Feishu appSecret.",
99
+ accountInactiveReason: "Feishu account is disabled.",
100
+ });
101
+ const baseConnectionMode =
102
+ normalizeSecretStringValue(feishu.connectionMode) === "webhook" ? "webhook" : "websocket";
103
+ const resolveAccountMode = (account: Record<string, unknown>) =>
104
+ hasOwnProperty(account, "connectionMode")
105
+ ? normalizeSecretStringValue(account.connectionMode)
106
+ : baseConnectionMode;
107
+ collectConditionalChannelFieldAssignments({
108
+ channelKey: "feishu",
109
+ field: "encryptKey",
110
+ channel: feishu,
111
+ surface,
112
+ defaults: params.defaults,
113
+ context: params.context,
114
+ topLevelActiveWithoutAccounts: baseConnectionMode === "webhook",
115
+ topLevelInheritedAccountActive: ({ account, enabled }) =>
116
+ enabled &&
117
+ !hasOwnProperty(account, "encryptKey") &&
118
+ resolveAccountMode(account) === "webhook",
119
+ accountActive: ({ account, enabled }) => enabled && resolveAccountMode(account) === "webhook",
120
+ topInactiveReason: "no enabled Feishu webhook-mode surface inherits this top-level encryptKey.",
121
+ accountInactiveReason: "Feishu account is disabled or not running in webhook mode.",
122
+ });
123
+ collectConditionalChannelFieldAssignments({
124
+ channelKey: "feishu",
125
+ field: "verificationToken",
126
+ channel: feishu,
127
+ surface,
128
+ defaults: params.defaults,
129
+ context: params.context,
130
+ topLevelActiveWithoutAccounts: baseConnectionMode === "webhook",
131
+ topLevelInheritedAccountActive: ({ account, enabled }) =>
132
+ enabled &&
133
+ !hasOwnProperty(account, "verificationToken") &&
134
+ resolveAccountMode(account) === "webhook",
135
+ accountActive: ({ account, enabled }) => enabled && resolveAccountMode(account) === "webhook",
136
+ topInactiveReason:
137
+ "no enabled Feishu webhook-mode surface inherits this top-level verificationToken.",
138
+ accountInactiveReason: "Feishu account is disabled or not running in webhook mode.",
139
+ });
140
+ }
141
+
142
+ export const channelSecrets = {
143
+ secretTargetRegistryEntries,
144
+ collectRuntimeConfigAssignments,
145
+ };
@@ -0,0 +1 @@
1
+ export { buildSecretInputSchema, hasConfiguredSecretInput } from "autobot/plugin-sdk/secret-input";
@@ -0,0 +1,69 @@
1
+ import { hasConfiguredSecretInput } from "autobot/plugin-sdk/secret-input";
2
+ import type { AutoBotConfig } from "../runtime-api.js";
3
+
4
+ function asRecord(value: unknown): Record<string, unknown> | undefined {
5
+ return value && typeof value === "object" && !Array.isArray(value)
6
+ ? (value as Record<string, unknown>)
7
+ : undefined;
8
+ }
9
+
10
+ function hasNonEmptyString(value: unknown): boolean {
11
+ return typeof value === "string" && value.trim().length > 0;
12
+ }
13
+
14
+ function isFeishuDocToolEnabled(cfg: AutoBotConfig): boolean {
15
+ const channels = asRecord(cfg.channels);
16
+ const feishu = asRecord(channels?.feishu);
17
+ if (!feishu || feishu.enabled === false) {
18
+ return false;
19
+ }
20
+
21
+ const baseTools = asRecord(feishu.tools);
22
+ const baseDocEnabled = baseTools?.doc !== false;
23
+ const baseAppId = hasNonEmptyString(feishu.appId);
24
+ const baseAppSecret = hasConfiguredSecretInput(feishu.appSecret, cfg.secrets?.defaults);
25
+ const baseConfigured = baseAppId && baseAppSecret;
26
+
27
+ const accounts = asRecord(feishu.accounts);
28
+ if (!accounts || Object.keys(accounts).length === 0) {
29
+ return baseDocEnabled && baseConfigured;
30
+ }
31
+
32
+ for (const accountValue of Object.values(accounts)) {
33
+ const account = asRecord(accountValue) ?? {};
34
+ if (account.enabled === false) {
35
+ continue;
36
+ }
37
+ const accountTools = asRecord(account.tools);
38
+ const effectiveTools = accountTools ?? baseTools;
39
+ const docEnabled = effectiveTools?.doc !== false;
40
+ if (!docEnabled) {
41
+ continue;
42
+ }
43
+ const accountConfigured =
44
+ (hasNonEmptyString(account.appId) || baseAppId) &&
45
+ (hasConfiguredSecretInput(account.appSecret, cfg.secrets?.defaults) || baseAppSecret);
46
+ if (accountConfigured) {
47
+ return true;
48
+ }
49
+ }
50
+
51
+ return false;
52
+ }
53
+
54
+ export function collectFeishuSecurityAuditFindings(params: { cfg: AutoBotConfig }) {
55
+ if (!isFeishuDocToolEnabled(params.cfg)) {
56
+ return [];
57
+ }
58
+ return [
59
+ {
60
+ checkId: "channels.feishu.doc_owner_open_id",
61
+ severity: "warn" as const,
62
+ title: "Feishu doc create can grant requester permissions",
63
+ detail:
64
+ 'channels.feishu tools include "doc"; feishu_doc action "create" can grant document access to the trusted requesting Feishu user.',
65
+ remediation:
66
+ "Disable channels.feishu.tools.doc when not needed, and restrict tool access for untrusted prompts.",
67
+ },
68
+ ];
69
+ }
@@ -0,0 +1 @@
1
+ export { collectFeishuSecurityAuditFindings } from "./security-audit-shared.js";
@@ -0,0 +1,80 @@
1
+ import {
2
+ createMessageReceiptFromOutboundResults,
3
+ type MessageReceipt,
4
+ type MessageReceiptPartKind,
5
+ } from "autobot/plugin-sdk/channel-message";
6
+
7
+ type FeishuMessageApiResponse = {
8
+ code?: number;
9
+ msg?: string;
10
+ data?: {
11
+ message_id?: string;
12
+ };
13
+ };
14
+
15
+ export function resolveFeishuReceiptKind(msgType?: string): MessageReceiptPartKind {
16
+ switch (msgType) {
17
+ case "audio":
18
+ return "voice";
19
+ case "image":
20
+ case "media":
21
+ case "file":
22
+ return "media";
23
+ case "interactive":
24
+ return "card";
25
+ case "post":
26
+ case "text":
27
+ return "text";
28
+ default:
29
+ return "unknown";
30
+ }
31
+ }
32
+
33
+ export function createFeishuSendReceipt(params: {
34
+ messageId?: string;
35
+ chatId: string;
36
+ kind?: MessageReceiptPartKind;
37
+ }): MessageReceipt {
38
+ const messageId = params.messageId?.trim();
39
+ const chatId = params.chatId.trim();
40
+ return createMessageReceiptFromOutboundResults({
41
+ results: messageId
42
+ ? [
43
+ {
44
+ channel: "feishu",
45
+ messageId,
46
+ chatId,
47
+ conversationId: chatId,
48
+ },
49
+ ]
50
+ : [],
51
+ ...(chatId ? { threadId: chatId } : {}),
52
+ kind: params.kind ?? "unknown",
53
+ });
54
+ }
55
+
56
+ export function assertFeishuMessageApiSuccess(
57
+ response: FeishuMessageApiResponse,
58
+ errorPrefix: string,
59
+ ) {
60
+ if (response.code !== 0) {
61
+ throw new Error(`${errorPrefix}: ${response.msg || `code ${response.code}`}`);
62
+ }
63
+ }
64
+
65
+ export function toFeishuSendResult(
66
+ response: FeishuMessageApiResponse,
67
+ chatId: string,
68
+ kind?: MessageReceiptPartKind,
69
+ ): {
70
+ messageId: string;
71
+ chatId: string;
72
+ receipt: MessageReceipt;
73
+ } {
74
+ const messageId = response.data?.message_id ?? "unknown";
75
+ return {
76
+ messageId,
77
+ chatId,
78
+ receipt: createFeishuSendReceipt({ messageId, chatId, kind }),
79
+ };
80
+ }
@@ -0,0 +1,35 @@
1
+ import type { ClawdbotConfig } from "../runtime-api.js";
2
+ import { resolveFeishuRuntimeAccount } from "./accounts.js";
3
+ import { createFeishuClient } from "./client.js";
4
+ import { resolveReceiveIdType, normalizeFeishuTarget } from "./targets.js";
5
+
6
+ type FeishuSendTarget = {
7
+ client: ReturnType<typeof createFeishuClient>;
8
+ receiveId: string;
9
+ receiveIdType: ReturnType<typeof resolveReceiveIdType>;
10
+ };
11
+
12
+ export function resolveFeishuSendTarget(params: {
13
+ cfg: ClawdbotConfig;
14
+ to: string;
15
+ accountId?: string;
16
+ }): FeishuSendTarget {
17
+ const target = params.to.trim();
18
+ const account = resolveFeishuRuntimeAccount({ cfg: params.cfg, accountId: params.accountId });
19
+ if (!account.configured) {
20
+ throw new Error(`Feishu account "${account.accountId}" not configured`);
21
+ }
22
+ const client = createFeishuClient(account);
23
+ const receiveId = normalizeFeishuTarget(target);
24
+ if (!receiveId) {
25
+ throw new Error(`Invalid Feishu target: ${params.to}`);
26
+ }
27
+ // Preserve explicit routing prefixes (chat/group/user/dm/open_id) when present.
28
+ // normalizeFeishuTarget strips these prefixes, so infer type from the raw target first.
29
+ const withoutProviderPrefix = target.replace(/^(feishu|lark):/i, "");
30
+ return {
31
+ client,
32
+ receiveId,
33
+ receiveIdType: resolveReceiveIdType(withoutProviderPrefix),
34
+ };
35
+ }